diff --git a/packages/cli/src/args.ts b/packages/cli/src/args.ts index f75dda67b..d20457cf8 100644 --- a/packages/cli/src/args.ts +++ b/packages/cli/src/args.ts @@ -70,8 +70,9 @@ export const globalArguments: ArgDescriptor[] = [ { name: 'npm', type: Boolean, - description: 'Sets npm mode: component directory is "node_modules" ' + - 'and the package name is read from package.json', + description: 'Sets npm mode: dependencies are installed from npm, ' + + 'component directory is "node_modules" and the package name is read ' + + 'from package.json', }, { name: 'module-resolution', diff --git a/packages/cli/src/commands/install.ts b/packages/cli/src/commands/install.ts index 873d0ea6f..7b92d61ac 100644 --- a/packages/cli/src/commands/install.ts +++ b/packages/cli/src/commands/install.ts @@ -28,27 +28,34 @@ export class InstallCommand implements Command { aliases = ['i']; // TODO(justinfagnani): Expand and link to eventual doc on variants. - description = 'installs Bower dependencies, optionally installing "variants"'; + description = 'Installs project dependencies from npm or Bower (optionally ' + + 'installing "variants").'; args = [ { name: 'variants', type: Boolean, defaultValue: false, - description: 'Whether to install variants' + description: 'Whether to install Bower variants' }, { name: 'offline', type: Boolean, defaultValue: false, - description: 'Don\'t hit the network' + description: 'Don\'t hit the network when installing Bower dependencies' }, ]; - async run(options: CommandOptions, _config: ProjectConfig): Promise { + async run(options: CommandOptions, config: ProjectConfig): Promise { // Defer dependency loading until this specific command is run const install = require('../install/install').install as typeof installTypeOnly; + + // Use `npm` from the config, if available and not passed as a CLI arg. + if (options.npm === undefined && config.npm !== undefined) { + options.npm = config.npm; + } + await install(options); } } diff --git a/packages/cli/src/install/install.ts b/packages/cli/src/install/install.ts index 973207750..e5505fc78 100644 --- a/packages/cli/src/install/install.ts +++ b/packages/cli/src/install/install.ts @@ -14,6 +14,7 @@ import * as bower from 'bower'; import {read as readBowerJson} from 'bower-json'; +import * as child_process from 'child_process'; import * as path from 'path'; import * as logging from 'plylog'; @@ -24,6 +25,14 @@ import BowerProject = require('bower/lib/core/Project'); const logger = logging.getLogger('cli.install'); +async function exec(command: string, opts: child_process.ExecOptions) { + return new Promise<[string, string]>((resolve, reject) => { + child_process.exec(command, opts, (err, stdout, stderr) => { + err ? reject(err) : resolve([stdout, stderr]); + }); + }); +} + type JsonValue = string|number|boolean|null|JsonObject|JsonArray; interface JsonObject { @@ -35,25 +44,36 @@ interface JsonArray extends Array {} export interface Options { variants?: boolean; offline?: boolean; + npm?: boolean; } export async function install(options?: Options): Promise { + if (options && options.npm) { + return npmInstall(); + } + // default to false const offline = options == null ? false : options.offline === true; // default to false const variants = options == null ? false : options.variants === true; await Promise.all([ - installDefault(offline), - variants ? installVariants(offline) : Promise.resolve(), + bowerInstallDefault(offline), + variants ? bowerInstallVariants(offline) : Promise.resolve(), ]); } +async function npmInstall() { + logger.info('Installing npm dependencies...'); + await exec('npm install', {cwd: process.cwd()}); + logger.info('Finished installing npm dependencies.'); +} + /** * Performs a Bower install, optionally with a specific JSON configuration and * output directory. */ -async function _install( +async function _bowerInstall( offline: boolean, bowerJson?: JsonObject, componentDirectory?: string, @@ -90,13 +110,13 @@ async function _install( await project.install([], {save: false, offline}, config); } -async function installDefault(offline: boolean): Promise { +async function bowerInstallDefault(offline: boolean): Promise { logger.info(`Installing default Bower components...`); - await _install(offline); + await _bowerInstall(offline); logger.info(`Finished installing default Bower components`); } -async function installVariants(offline: boolean): Promise { +async function bowerInstallVariants(offline: boolean): Promise { const bowerJson = await new Promise((resolve, reject) => { const config = defaultBowerConfig({ save: false, @@ -120,7 +140,7 @@ async function installVariants(offline: boolean): Promise { const variantDirectory = `bower_components-${variantName}`; logger.info( `Installing variant ${variantName} to ${variantDirectory}...`); - await _install(offline, variantBowerJson, variantDirectory, variantName); + await _bowerInstall(offline, variantBowerJson, variantDirectory, variantName); logger.info(`Finished installing variant ${variantName}`); })); } diff --git a/packages/cli/src/test/integration/integration_test.ts b/packages/cli/src/test/integration/integration_test.ts index 4bd0079ac..6d5e27877 100644 --- a/packages/cli/src/test/integration/integration_test.ts +++ b/packages/cli/src/test/integration/integration_test.ts @@ -16,7 +16,6 @@ import {createApplicationGenerator} from '../../init/application/application'; import {runCommand} from './run-command'; import {createElementGenerator} from '../../init/element/element'; import {createGithubGenerator} from '../../init/github'; -import * as child_process from 'child_process'; // A zero priveledge github token of a nonce account, used for quota. const githubToken = '8d8622bf09bb1d85cb411b5e475a35e742a7ce35'; @@ -26,14 +25,6 @@ const githubToken = '8d8622bf09bb1d85cb411b5e475a35e742a7ce35'; const isWindows = process.platform === 'win32'; const skipOnWindows = isWindows ? test.skip : test; -async function exec(command: string, opts: child_process.ExecOptions) { - return new Promise<[string, string]>((resolve, reject) => { - child_process.exec(command, opts, (err, stdout, stderr) => { - err ? reject(err) : resolve([stdout, stderr]); - }); - }); -} - suite('integration tests', function() { const binPath = path.join(__dirname, '../../../', 'bin', 'polymer.js'); @@ -48,9 +39,7 @@ suite('integration tests', function() { await runGenerator(createElementGenerator('polymer-3.x')) .withPrompts({name: 'my-element'}) // Mock the prompt answers .toPromise(); - // TODO(#118): Use `polymer install` once it supports installing npm - // packages. - await exec('npm install', {cwd: dir}); + await runCommand(binPath, ['install'], {cwd: dir}); // TODO(#130): Add this back in when `polymer lint` has a Polymer 3 // option. diff --git a/packages/polyserve/src/args.ts b/packages/polyserve/src/args.ts index 4d12904f8..9bc02680d 100644 --- a/packages/polyserve/src/args.ts +++ b/packages/polyserve/src/args.ts @@ -91,8 +91,9 @@ export const args: ArgDescriptor[] = [ }, { name: 'npm', - description: 'Sets npm mode: component directory is "node_modules" and ' + - 'the package name is read from package.json', + description: 'Sets npm mode: dependencies are installed from npm, ' + + 'component directory is "node_modules" and the package name is read ' + + 'from package.json', type: Boolean, }, {