diff --git a/docs/documentation/new.md b/docs/documentation/new.md index 37cb262319f0..1dc19d6746fd 100644 --- a/docs/documentation/new.md +++ b/docs/documentation/new.md @@ -10,7 +10,7 @@ Default applications are created in a directory of the same name, with an initia ## Options `--dry-run` (`-d`) run through without making any changes -`--skip-npm` (`-sn`) skip installing npm packages +`--skip-install` (`-si`) skip installing packages `--skip-git` (`-sg`) skip initializing a git repository diff --git a/docs/documentation/update.md b/docs/documentation/update.md index be536099187b..59b36ee5b4f0 100644 --- a/docs/documentation/update.md +++ b/docs/documentation/update.md @@ -10,7 +10,7 @@ Initialization is done in-place, meaning that the generated application is initi ## Options `--dry-run` (`-d`) run through without making any changes -`--skip-npm` (`-sn`) skip installing npm packages +`--skip-install` (`-si`) skip installing packages `--skip-git` (`-sg`) skip initializing a git repository diff --git a/packages/@angular/cli/commands/init.ts b/packages/@angular/cli/commands/init.ts index 5246fb7a374a..4270201a1680 100644 --- a/packages/@angular/cli/commands/init.ts +++ b/packages/@angular/cli/commands/init.ts @@ -10,7 +10,7 @@ const InitCommand: any = Command.extend({ { name: 'dry-run', type: Boolean, default: false, aliases: ['d'] }, { name: 'verbose', type: Boolean, default: false, aliases: ['v'] }, { name: 'link-cli', type: Boolean, default: false, aliases: ['lc'] }, - { name: 'skip-npm', type: Boolean, default: false, aliases: ['sn'] }, + { name: 'skip-install', type: Boolean, default: false, aliases: ['si'] }, { name: 'skip-git', type: Boolean, default: false, aliases: ['sg'] }, { name: 'skip-tests', type: Boolean, default: false, aliases: ['st'] }, { name: 'skip-commit', type: Boolean, default: false, aliases: ['sc'] }, diff --git a/packages/@angular/cli/commands/new.ts b/packages/@angular/cli/commands/new.ts index 2f0f6d4cfa77..68fb795b087a 100644 --- a/packages/@angular/cli/commands/new.ts +++ b/packages/@angular/cli/commands/new.ts @@ -15,7 +15,7 @@ const NewCommand = Command.extend({ { name: 'dry-run', type: Boolean, default: false, aliases: ['d'] }, { name: 'verbose', type: Boolean, default: false, aliases: ['v'] }, { name: 'link-cli', type: Boolean, default: false, aliases: ['lc'] }, - { name: 'skip-npm', type: Boolean, default: false, aliases: ['sn'] }, + { name: 'skip-install', type: Boolean, default: false, aliases: ['si'] }, { name: 'skip-git', type: Boolean, default: false, aliases: ['sg'] }, { name: 'skip-tests', type: Boolean, default: false, aliases: ['st'] }, { name: 'skip-commit', type: Boolean, default: false, aliases: ['sc'] }, diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json index 9c1d753aa09b..3ebdc3a5a476 100644 --- a/packages/@angular/cli/lib/config/schema.json +++ b/packages/@angular/cli/lib/config/schema.json @@ -292,6 +292,11 @@ }, "additionalProperties": false }, + "packageManager": { + "enum": [ "npm", "cnpm", "yarn", "default" ], + "default": "default", + "type": "string" + }, "warnings": { "description": "Allow people to disable console warnings.", "type": "object", diff --git a/packages/@angular/cli/tasks/init.ts b/packages/@angular/cli/tasks/init.ts index d8a8492715f5..72f9ac47253a 100644 --- a/packages/@angular/cli/tasks/init.ts +++ b/packages/@angular/cli/tasks/init.ts @@ -2,6 +2,8 @@ import * as chalk from 'chalk'; import LinkCli from '../tasks/link-cli'; import NpmInstall from '../tasks/npm-install'; import { validateProjectName } from '../utilities/validate-project-name'; +import {checkYarnOrCNPM} from '../utilities/check-package-manager'; +import {CliConfig} from '../models/config'; const Task = require('../ember-cli/lib/models/task'); const Promise = require('../ember-cli/lib/ext/promise'); @@ -13,7 +15,7 @@ const GitInit = require('../tasks/git-init'); export default Task.extend({ run: function (commandOptions: any, rawArgs: string[]) { if (commandOptions.dryRun) { - commandOptions.skipNpm = true; + commandOptions.skipInstall = true; } const installBlueprint = new this.tasks.InstallBlueprint({ @@ -32,10 +34,12 @@ export default Task.extend({ } let npmInstall: any; - if (!commandOptions.skipNpm) { + if (!commandOptions.skipInstall) { + const packageManager = CliConfig.fromGlobal().get('packageManager'); npmInstall = new NpmInstall({ ui: this.ui, - project: this.project + project: this.project, + packageManager }); } @@ -86,7 +90,7 @@ export default Task.extend({ } }) .then(function () { - if (!commandOptions.skipNpm) { + if (!commandOptions.skipInstall) { return npmInstall.run(); } }) @@ -95,6 +99,7 @@ export default Task.extend({ return linkCli.run(); } }) + .then(checkYarnOrCNPM) .then(() => { this.ui.writeLine(chalk.green(`Project '${packageName}' successfully created.`)); }); diff --git a/packages/@angular/cli/tasks/npm-install.ts b/packages/@angular/cli/tasks/npm-install.ts index 02e3f5d1cc27..f0d4f80bfe4d 100644 --- a/packages/@angular/cli/tasks/npm-install.ts +++ b/packages/@angular/cli/tasks/npm-install.ts @@ -6,17 +6,21 @@ import {exec} from 'child_process'; export default Task.extend({ run: function() { const ui = this.ui; + let packageManager = this.packageManager; + if (packageManager === 'default') { + packageManager = 'npm'; + } return new Promise(function(resolve, reject) { - ui.writeLine(chalk.green('Installing packages for tooling via npm.')); - exec('npm install', + ui.writeLine(chalk.green(`Installing packages for tooling via ${packageManager}.`)); + exec(`${packageManager} install`, (err: NodeJS.ErrnoException, stdout: string, stderr: string) => { if (err) { ui.writeLine(stderr); ui.writeLine(chalk.red('Package install failed, see above.')); reject(); } else { - ui.writeLine(chalk.green('Installed packages for tooling via npm.')); + ui.writeLine(chalk.green(`Installed packages for tooling via ${packageManager}.`)); resolve(); } }); diff --git a/packages/@angular/cli/utilities/check-package-manager.ts b/packages/@angular/cli/utilities/check-package-manager.ts new file mode 100644 index 000000000000..654812c8ecb3 --- /dev/null +++ b/packages/@angular/cli/utilities/check-package-manager.ts @@ -0,0 +1,38 @@ +import * as chalk from 'chalk'; +import {exec} from 'child_process'; +import {CliConfig} from '../models/config'; + +const Promise = require('../ember-cli/lib/ext/promise'); +const execPromise = Promise.denodeify(exec); +const packageManager = CliConfig.fromGlobal().get('packageManager'); + + +export function checkYarnOrCNPM() { + if (packageManager !== 'default') { + return Promise.resolve(); + } + + return Promise + .all([checkYarn(), checkCNPM()]) + .then((data: Array) => { + const [isYarnInstalled, isCNPMInstalled] = data; + if (isYarnInstalled && isCNPMInstalled) { + console.log(chalk.yellow('You can `ng set --global packageManager=yarn` ' + + 'or `ng set --global packageManager=cnpm`.')); + } else if (isYarnInstalled) { + console.log(chalk.yellow('You can `ng set --global packageManager=yarn`.')); + } else if (isCNPMInstalled) { + console.log(chalk.yellow('You can `ng set --global packageManager=cnpm`.')); + } + }); +} + +function checkYarn() { + return execPromise('yarn --version') + .then(() => true, () => false); +} + +function checkCNPM() { + return execPromise('cnpm --version') + .then(() => true, () => false); +} diff --git a/packages/@ngtools/json-schema/src/schema-tree.ts b/packages/@ngtools/json-schema/src/schema-tree.ts index a946edb83242..02b425e2824a 100644 --- a/packages/@ngtools/json-schema/src/schema-tree.ts +++ b/packages/@ngtools/json-schema/src/schema-tree.ts @@ -495,7 +495,9 @@ class EnumSchemaTreeNode extends LeafSchemaTreeNode { return v; } - get type() { return 'any'; } + get type() { + return this._schema['type'] || 'any'; + } get tsType(): null { return null; } serialize(serializer: Serializer) { serializer.outputEnum(this); } } diff --git a/tests/acceptance/destroy.spec.js b/tests/acceptance/destroy.spec.js index 5eeed80b2aad..af0e1eb92cf4 100644 --- a/tests/acceptance/destroy.spec.js +++ b/tests/acceptance/destroy.spec.js @@ -11,7 +11,7 @@ describe('Acceptance: ng destroy', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-class.spec.js b/tests/acceptance/generate-class.spec.js index a219cf23981e..fdd75d2e1606 100644 --- a/tests/acceptance/generate-class.spec.js +++ b/tests/acceptance/generate-class.spec.js @@ -16,7 +16,7 @@ describe('Acceptance: ng generate class', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-component.spec.js b/tests/acceptance/generate-component.spec.js index 5f2b6f541798..4b6ce683c90e 100644 --- a/tests/acceptance/generate-component.spec.js +++ b/tests/acceptance/generate-component.spec.js @@ -20,7 +20,7 @@ describe('Acceptance: ng generate component', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-directive.spec.js b/tests/acceptance/generate-directive.spec.js index 089fda0c640e..0f4b214d0d71 100644 --- a/tests/acceptance/generate-directive.spec.js +++ b/tests/acceptance/generate-directive.spec.js @@ -19,7 +19,7 @@ describe('Acceptance: ng generate directive', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-module.spec.js b/tests/acceptance/generate-module.spec.js index 7ba774885ba7..5e2da7c5c84c 100644 --- a/tests/acceptance/generate-module.spec.js +++ b/tests/acceptance/generate-module.spec.js @@ -16,7 +16,7 @@ describe('Acceptance: ng generate module', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-pipe.spec.js b/tests/acceptance/generate-pipe.spec.js index d930f9927875..60e2e68ec388 100644 --- a/tests/acceptance/generate-pipe.spec.js +++ b/tests/acceptance/generate-pipe.spec.js @@ -20,7 +20,7 @@ describe('Acceptance: ng generate pipe', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-route.spec.js b/tests/acceptance/generate-route.spec.js index 99469635b42e..6d6847bc73b6 100644 --- a/tests/acceptance/generate-route.spec.js +++ b/tests/acceptance/generate-route.spec.js @@ -21,7 +21,7 @@ xdescribe('Acceptance: ng generate route', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/generate-service.spec.js b/tests/acceptance/generate-service.spec.js index 1aa55f8210a2..7a34924ab882 100644 --- a/tests/acceptance/generate-service.spec.js +++ b/tests/acceptance/generate-service.spec.js @@ -19,7 +19,7 @@ describe('Acceptance: ng generate service', function () { return tmp.setup('./tmp').then(function () { process.chdir('./tmp'); }).then(function () { - return ng(['new', 'foo', '--skip-npm']); + return ng(['new', 'foo', '--skip-install']); }); }); diff --git a/tests/acceptance/init.spec.js b/tests/acceptance/init.spec.js index ca8a81c7e823..bfef2e731697 100644 --- a/tests/acceptance/init.spec.js +++ b/tests/acceptance/init.spec.js @@ -97,7 +97,7 @@ describe('Acceptance: ng update', function () { it('ng init does the same as ng update', function () { return ng([ 'init', - '--skip-npm' + '--skip-install' ]).then(confirmBlueprinted); }); @@ -109,7 +109,7 @@ describe('Acceptance: ng update', function () { .then(function () { return ng([ 'init', - '--skip-npm', + '--skip-install', '--name', 'tmp' ]); @@ -121,15 +121,15 @@ describe('Acceptance: ng update', function () { }); it('init an already init\'d folder', function () { - return ng(['init', '--skip-npm']) + return ng(['init', '--skip-install']) .then(function () { - return ng(['init', '--skip-npm']); + return ng(['init', '--skip-install']); }) .then(confirmBlueprinted); }); it('init a single file', function () { - return ng(['init', 'package.json', '--skip-npm']) + return ng(['init', 'package.json', '--skip-install']) .then(function () { return 'package.json'; }) @@ -137,15 +137,15 @@ describe('Acceptance: ng update', function () { }); it('init a single file on already init\'d folder', function () { - return ng(['init', '--skip-npm']) + return ng(['init', '--skip-install']) .then(function () { - return ng(['init', 'package.json', '--skip-npm']); + return ng(['init', 'package.json', '--skip-install']); }) .then(confirmBlueprinted); }); it('init multiple files by glob pattern', function () { - return ng(['init', 'src/**', '--skip-npm']) + return ng(['init', 'src/**', '--skip-install']) .then(function () { return 'src/**'; }) @@ -153,15 +153,15 @@ describe('Acceptance: ng update', function () { }); it('init multiple files by glob pattern on already init\'d folder', function () { - return ng(['init', '--skip-npm']) + return ng(['init', '--skip-install']) .then(function () { - return ng(['init', 'src/**', '--skip-npm']); + return ng(['init', 'src/**', '--skip-install']); }) .then(confirmBlueprinted); }); it('init multiple files by glob patterns', function () { - return ng(['init', 'src/**', 'package.json', '--skip-npm']) + return ng(['init', 'src/**', 'package.json', '--skip-install']) .then(function () { return '{src/**,package.json}'; }) @@ -169,15 +169,15 @@ describe('Acceptance: ng update', function () { }); it('init multiple files by glob patterns on already init\'d folder', function () { - return ng(['init', '--skip-npm']) + return ng(['init', '--skip-install']) .then(function () { - return ng(['init', 'src/**', 'package.json', '--skip-npm']); + return ng(['init', 'src/**', 'package.json', '--skip-install']); }) .then(confirmBlueprinted); }); it('ng update --inline-template does not generate a template file', () => { - return ng(['init', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['init', '--skip-install', '--skip-git', '--inline-template']) .then(() => { const templateFile = path.join('src', 'app', 'app.component.html'); expect(existsSync(templateFile)).to.equal(false); @@ -185,7 +185,7 @@ describe('Acceptance: ng update', function () { }); it('ng update --inline-style does not gener a style file', () => { - return ng(['init', '--skip-npm', '--skip-git', '--inline-style']) + return ng(['init', '--skip-install', '--skip-git', '--inline-style']) .then(() => { const styleFile = path.join('src', 'app', 'app.component.css'); expect(existsSync(styleFile)).to.equal(false); @@ -193,7 +193,7 @@ describe('Acceptance: ng update', function () { }); it('should skip spec files when passed --skip-tests', () => { - return ng(['init', '--skip-npm', '--skip-git', '--skip-tests']) + return ng(['init', '--skip-install', '--skip-git', '--skip-tests']) .then(() => { const specFile = path.join('src', 'app', 'app.component.spec.ts'); expect(existsSync(specFile)).to.equal(false); diff --git a/tests/acceptance/new.spec.ts b/tests/acceptance/new.spec.ts index 93c56532b7d7..e50457567045 100644 --- a/tests/acceptance/new.spec.ts +++ b/tests/acceptance/new.spec.ts @@ -55,34 +55,34 @@ describe('Acceptance: ng new', function () { } it('requires a valid name (!)', () => { - return ng(['new', '!', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', '!', '--skip-install', '--skip-git', '--inline-template']) .then(() => { throw new Error(); }, () => {}); }); it('requires a valid name (abc-.)', () => { - return ng(['new', 'abc-.', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', 'abc-.', '--skip-install', '--skip-git', '--inline-template']) .then(() => { throw new Error(); }, () => {}); }); it('requires a valid name (abc-)', () => { - return ng(['new', 'abc-', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', 'abc-', '--skip-install', '--skip-git', '--inline-template']) .then(() => { throw new Error(); }, () => {}); }); it('requires a valid name (abc-def-)', () => { - return ng(['new', 'abc-def-', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', 'abc-def-', '--skip-install', '--skip-git', '--inline-template']) .then(() => { throw new Error(); }, () => {}); }); it('requires a valid name (abc-123)', () => { - return ng(['new', 'abc-123', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', 'abc-123', '--skip-install', '--skip-git', '--inline-template']) .then(() => { throw new Error(); }, () => {}); }); it('requires a valid name (abc)', () => { - return ng(['new', 'abc', '--skip-npm', '--skip-git', '--inline-template']); + return ng(['new', 'abc', '--skip-install', '--skip-git', '--inline-template']); }); it('requires a valid name (abc-def)', () => { - return ng(['new', 'abc-def', '--skip-npm', '--skip-git', '--inline-template']); + return ng(['new', 'abc-def', '--skip-install', '--skip-git', '--inline-template']); }); it('ng new foo, where foo does not yet exist, works', function () { - return ng(['new', 'foo', '--skip-npm']).then(confirmBlueprinted); + return ng(['new', 'foo', '--skip-install']).then(confirmBlueprinted); }); it('ng new with empty app does throw exception', function () { @@ -94,7 +94,7 @@ describe('Acceptance: ng new', function () { }); it('ng new with app name creates new directory and has a dasherized package name', function () { - return ng(['new', 'FooApp', '--skip-npm', '--skip-git']).then(function () { + return ng(['new', 'FooApp', '--skip-install', '--skip-git']).then(function () { expect(!existsSync('FooApp')); const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); @@ -103,7 +103,7 @@ describe('Acceptance: ng new', function () { }); it('ng new has a .editorconfig file', function () { - return ng(['new', 'FooApp', '--skip-npm', '--skip-git']).then(function () { + return ng(['new', 'FooApp', '--skip-install', '--skip-git']).then(function () { expect(!existsSync('FooApp')); const editorConfig = fs.readFileSync('.editorconfig', 'utf8'); @@ -112,9 +112,9 @@ describe('Acceptance: ng new', function () { }); it('Cannot run ng new, inside of Angular CLI project', function () { - return ng(['new', 'foo', '--skip-npm', '--skip-git']) + return ng(['new', 'foo', '--skip-install', '--skip-git']) .then(function () { - return ng(['new', 'foo', '--skip-npm', '--skip-git']).then(() => { + return ng(['new', 'foo', '--skip-install', '--skip-git']).then(() => { throw new SilentError('Cannot run ng new, inside of Angular CLI project should fail.'); }, () => { expect(!existsSync('foo')); @@ -124,7 +124,7 @@ describe('Acceptance: ng new', function () { }); it('ng new without skip-git flag creates .git dir', function () { - return ng(['new', 'foo', '--skip-npm']).then(function () { + return ng(['new', 'foo', '--skip-install']).then(function () { expect(existsSync('.git')); }); }); @@ -139,7 +139,7 @@ describe('Acceptance: ng new', function () { }); it('ng new with --directory uses given directory name and has correct package name', function () { - return ng(['new', 'foo', '--skip-npm', '--skip-git', '--directory=bar']) + return ng(['new', 'foo', '--skip-install', '--skip-git', '--directory=bar']) .then(function () { const cwd = process.cwd(); expect(cwd).to.not.match(/foo/, 'does not use app name for directory name'); @@ -154,7 +154,7 @@ describe('Acceptance: ng new', function () { }); it('ng new --inline-template does not generate a template file', () => { - return ng(['new', 'foo', '--skip-npm', '--skip-git', '--inline-template']) + return ng(['new', 'foo', '--skip-install', '--skip-git', '--inline-template']) .then(() => { const templateFile = path.join('src', 'app', 'app.component.html'); expect(existsSync(templateFile)).to.equal(false); @@ -162,7 +162,7 @@ describe('Acceptance: ng new', function () { }); it('ng new --inline-style does not gener a style file', () => { - return ng(['new', 'foo', '--skip-npm', '--skip-git', '--inline-style']) + return ng(['new', 'foo', '--skip-install', '--skip-git', '--inline-style']) .then(() => { const styleFile = path.join('src', 'app', 'app.component.css'); expect(existsSync(styleFile)).to.equal(false); @@ -170,7 +170,7 @@ describe('Acceptance: ng new', function () { }); it('should skip spec files when passed --skip-tests', () => { - return ng(['new', 'foo', '--skip-npm', '--skip-git', '--skip-tests']) + return ng(['new', 'foo', '--skip-install', '--skip-git', '--skip-tests']) .then(() => { const specFile = path.join('src', 'app', 'app.component.spec.ts'); expect(existsSync(specFile)).to.equal(false); diff --git a/tests/e2e/setup/500-create-project.ts b/tests/e2e/setup/500-create-project.ts index 4e5f0bbe008b..ef859b46d121 100644 --- a/tests/e2e/setup/500-create-project.ts +++ b/tests/e2e/setup/500-create-project.ts @@ -24,7 +24,7 @@ export default function() { } else { // Otherwise create a project from scratch. createProject = Promise.resolve() - .then(() => ng('new', 'test-project', '--skip-npm')) + .then(() => ng('new', 'test-project', '--skip-install')) .then(() => expectFileToExist(join(process.cwd(), 'test-project'))) .then(() => process.chdir('./test-project')); } diff --git a/tests/e2e/tests/commands/new/check-yarn.ts b/tests/e2e/tests/commands/new/check-yarn.ts new file mode 100644 index 000000000000..b6be40b6b20e --- /dev/null +++ b/tests/e2e/tests/commands/new/check-yarn.ts @@ -0,0 +1,17 @@ +import {ng} from '../../../utils/process'; +import {getGlobalVariable} from '../../../utils/env'; + +const yarnRegEx = /You can `ng set --global packageManager=yarn`./; + +export default function() { + return Promise.resolve() + .then(() => process.chdir(getGlobalVariable('tmp-root'))) + .then(() => ng('set', '--global', 'packageManager=default')) + .then(() => ng('new', 'foo')) + .then((stdout) => { + // assuming yarn is installed and checking for message with yarn + if (!stdout.toString().match(yarnRegEx)) { + throw new Error('Should display message to use yarn packageManager'); + } + }); +} diff --git a/tests/e2e/utils/project.ts b/tests/e2e/utils/project.ts index f3b9376ec496..f89dd7726968 100644 --- a/tests/e2e/utils/project.ts +++ b/tests/e2e/utils/project.ts @@ -34,7 +34,7 @@ export function ngServe(...args: string[]) { export function createProject(name: string, ...args: string[]) { return Promise.resolve() .then(() => process.chdir(getGlobalVariable('tmp-root'))) - .then(() => ng('new', name, '--skip-npm', ...args)) + .then(() => ng('new', name, '--skip-install', ...args)) .then(() => process.chdir(name)) .then(() => updateJsonFile('package.json', json => { Object.keys(packages).forEach(pkgName => {