Skip to content
5 changes: 3 additions & 2 deletions packages/cli/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
15 changes: 11 additions & 4 deletions packages/cli/src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
async run(options: CommandOptions, config: ProjectConfig): Promise<void> {
// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really do this in a unified place

options.npm = config.npm;
}

await install(options);
}
}
34 changes: 27 additions & 7 deletions packages/cli/src/install/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 {
Expand All @@ -35,25 +44,36 @@ interface JsonArray extends Array<JsonValue> {}
export interface Options {
variants?: boolean;
offline?: boolean;
npm?: boolean;
}

export async function install(options?: Options): Promise<void> {
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,
Expand Down Expand Up @@ -90,13 +110,13 @@ async function _install(
await project.install([], {save: false, offline}, config);
}

async function installDefault(offline: boolean): Promise<void> {
async function bowerInstallDefault(offline: boolean): Promise<void> {
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<void> {
async function bowerInstallVariants(offline: boolean): Promise<void> {
const bowerJson = await new Promise<any>((resolve, reject) => {
const config = defaultBowerConfig({
save: false,
Expand All @@ -120,7 +140,7 @@ async function installVariants(offline: boolean): Promise<void> {
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}`);
}));
}
Expand Down
13 changes: 1 addition & 12 deletions packages/cli/src/test/integration/integration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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');
Expand All @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions packages/polyserve/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to think that the --npm flag / config option description should be made more generic because it has to match across all commands ("Sets npm mode.") and each command's help description might be a better place to put specific information about how it reacts to the flag.

Copy link
Contributor

@rictic rictic Apr 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we need to do some unification to the way we handle options/config in the CLI

type: Boolean,
},
{
Expand Down