diff --git a/docs/documentation/generate.md b/docs/documentation/generate.md index 6dd875a1cee4..f4c33174f8ab 100644 --- a/docs/documentation/generate.md +++ b/docs/documentation/generate.md @@ -30,7 +30,7 @@
lint-fix

- --lint-fix (aliases: -lf) + --lint-fix (aliases: -l)

Use lint to fix files after generation. diff --git a/docs/documentation/generate/component.md b/docs/documentation/generate/component.md index 12ce166e1de8..37b31a077b67 100644 --- a/docs/documentation/generate/component.md +++ b/docs/documentation/generate/component.md @@ -19,7 +19,7 @@

change-detection

- --change-detection (aliases: -cd) + --change-detection (aliases: -c)

Specifies the change detection strategy. @@ -49,7 +49,7 @@

inline-style

- --inline-style (aliases: -is) default value: false + --inline-style (aliases: -s) default value: false

Specifies if the style will be in the ts file. @@ -59,7 +59,7 @@

inline-template

- --inline-template (aliases: -it) default value: false + --inline-template (aliases: -t) default value: false

Specifies if the template will be in the ts file. @@ -109,7 +109,7 @@

view-encapsulation

- --view-encapsulation (aliases: -ve) + --view-encapsulation (aliases: -v)

Specifies the view encapsulation strategy. diff --git a/docs/documentation/new.md b/docs/documentation/new.md index 9400ce2ef5e2..2dedb230af07 100644 --- a/docs/documentation/new.md +++ b/docs/documentation/new.md @@ -11,7 +11,7 @@ Default applications are created in a directory of the same name, with an initia

directory

- --directory (alias: -dir) default value: dir + --directory (alias: -d) default value: dir

The directory name to create the app in. @@ -31,7 +31,7 @@ Default applications are created in a directory of the same name, with an initia

inline-style

- --inline-style (alias: -is) default value: false + --inline-style (alias: -s) default value: false

Should have an inline style. @@ -41,7 +41,7 @@ Default applications are created in a directory of the same name, with an initia

inline-template

- --inline-template (alias: -it) default value: false + --inline-template (alias: -t) default value: false

Should have an inline template. @@ -94,7 +94,7 @@ Default applications are created in a directory of the same name, with an initia

skip-git

- --skip-git (alias: -sg) default value: false + --skip-git (alias: -g) default value: false

Skip initializing a git repository. @@ -114,7 +114,7 @@ Default applications are created in a directory of the same name, with an initia

skip-tests

- --skip-tests (aliases: -st) default value: false + --skip-tests (aliases: -S) default value: false

Skip creating spec files. @@ -127,7 +127,7 @@ Default applications are created in a directory of the same name, with an initia

source-dir

- --source-dir (alias: -sd) default value: src + --source-dir (alias: -D) default value: src

The name of the source directory. diff --git a/package-lock.json b/package-lock.json index 186095b93adc..80e455990dd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3430,6 +3430,7 @@ "debug": { "version": "2.6.8", "bundled": true, + "optional": true, "requires": { "ms": "2.0.0" } @@ -3478,6 +3479,7 @@ "form-data": { "version": "2.1.4", "bundled": true, + "optional": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.5", @@ -3562,6 +3564,7 @@ "har-validator": { "version": "4.2.1", "bundled": true, + "optional": true, "requires": { "ajv": "4.11.8", "har-schema": "1.0.5" @@ -3683,7 +3686,8 @@ "dependencies": { "assert-plus": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -3718,7 +3722,8 @@ }, "ms": { "version": "2.0.0", - "bundled": true + "bundled": true, + "optional": true }, "node-pre-gyp": { "version": "0.6.39", @@ -3764,11 +3769,13 @@ }, "oauth-sign": { "version": "0.8.2", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", - "bundled": true + "bundled": true, + "optional": true }, "once": { "version": "1.4.0", @@ -3779,7 +3786,8 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "os-tmpdir": { "version": "1.0.2", @@ -10585,31 +10593,37 @@ "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "optional": true + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "optional": true + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "optional": true, + "requires": { + "camelcase": "3.0.0" + } } } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "optional": true, + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "requires": { - "camelcase": "3.0.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "optional": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" } } }, diff --git a/package.json b/package.json index 2b1d811ee547..092db55e0a46 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,8 @@ "webpack-dev-server": "^3.0.1-beta.0", "webpack-merge": "^4.1.2", "webpack-sources": "^1.1.0", - "webpack-subresource-integrity": "^1.0.1" + "webpack-subresource-integrity": "^1.0.1", + "yargs-parser": "^9.0.2" }, "devDependencies": { "@angular/common": "^5.2.0", diff --git a/packages/@angular/cli/bin/ng b/packages/@angular/cli/bin/ng index 3f32d98ffb80..bbb2a297adcc 100755 --- a/packages/@angular/cli/bin/ng +++ b/packages/@angular/cli/bin/ng @@ -196,7 +196,10 @@ resolve('@angular/cli', { basedir: process.cwd() }, cliArgs: process.argv.slice(2), inputStream: standardInput, outputStream: process.stdout - }).then(function (result) { - process.exit(typeof result === 'object' ? result.exitCode : result); + }).then(function (exitCode) { + process.exit(exitCode); + }).catch(function(err) { + console.log('Unknown error: ' + err.toString()); + process.exit(127); }); }); diff --git a/packages/@angular/cli/commands/build.ts b/packages/@angular/cli/commands/build.ts index 39acf1bd6d40..00773946b93b 100644 --- a/packages/@angular/cli/commands/build.ts +++ b/packages/@angular/cli/commands/build.ts @@ -1,3 +1,4 @@ +import { Command, Option, CommandScope } from '../models/command'; import { CliConfig } from '../models/config'; import { BuildOptions } from '../models/build-options'; import { Version } from '../upgrade/version'; @@ -6,7 +7,6 @@ import { getAppFromConfig } from '../utilities/app-utils'; import { join } from 'path'; import { RenderUniversalTaskOptions } from '../tasks/render-universal'; -const Command = require('../ember-cli/lib/models/command'); const SilentError = require('silent-error'); const config = CliConfig.fromProject() || CliConfig.fromGlobal(); @@ -16,12 +16,13 @@ const buildConfigDefaults = config.getPaths('defaults.build', [ ]); // defaults for BuildOptions -export const baseBuildCommandOptions: any = [ +export const baseBuildCommandOptions: Option[] = [ { name: 'target', type: String, default: 'development', - aliases: ['t', { 'dev': 'development' }, { 'prod': 'production' }], + // TODO: re-add support for `--prod` + aliases: ['t'], description: 'Defines the build target.' }, { @@ -213,12 +214,14 @@ export interface BuildTaskOptions extends BuildOptions { statsJson?: boolean; } -const BuildCommand = Command.extend({ - name: 'build', - description: 'Builds your app and places it into the output path (dist/ by default).', - aliases: ['b'], - - availableOptions: baseBuildCommandOptions.concat([ +export default class BuildCommand extends Command { + public readonly name = 'build'; + public readonly description = + 'Builds your app and places it into the output path (dist/ by default).'; + public static aliases = ['b']; + public scope = CommandScope.inProject; + public arguments: string[]; + public options = baseBuildCommandOptions.concat([ { name: 'stats-json', type: Boolean, @@ -226,16 +229,18 @@ const BuildCommand = Command.extend({ description: oneLine`Generates a \`stats.json\` file which can be analyzed using tools such as: \`webpack-bundle-analyzer\` or https://webpack.github.io/analyse.` } - ]), + ]); - run: function (commandOptions: BuildTaskOptions) { - // Check Angular and TypeScript versions. + public validate(_options: BuildTaskOptions) { Version.assertAngularVersionIs2_3_1OrHigher(this.project.root); Version.assertTypescriptVersion(this.project.root); + return true; + } + public async run(options: BuildTaskOptions) { // Add trailing slash if missing to prevent https://github.com/angular/angular-cli/issues/7295 - if (commandOptions.deployUrl && commandOptions.deployUrl.substr(-1) !== '/') { - commandOptions.deployUrl += '/'; + if (options.deployUrl && options.deployUrl.substr(-1) !== '/') { + options.deployUrl += '/'; } const BuildTask = require('../tasks/build').default; @@ -245,11 +250,11 @@ const BuildCommand = Command.extend({ ui: this.ui, }); - const clientApp = getAppFromConfig(commandOptions.app); + const clientApp = getAppFromConfig(options.app); - const doAppShell = commandOptions.target === 'production' && - (commandOptions.aot === undefined || commandOptions.aot === true) && - !commandOptions.skipAppShell; + const doAppShell = options.target === 'production' && + (options.aot === undefined || options.aot === true) && + !options.skipAppShell; let serverApp: any = null; if (clientApp.appShell && doAppShell) { @@ -259,40 +264,30 @@ const BuildCommand = Command.extend({ } } - const buildPromise = buildTask.run(commandOptions); - + const buildTaskResult = await buildTask.run(options); if (!clientApp.appShell || !doAppShell) { - return buildPromise; + return buildTaskResult; } - return buildPromise - .then(() => { + const serverOptions = { + ...options, + app: clientApp.appShell.app + }; + await buildTask.run(serverOptions); - const serverOptions = { - ...commandOptions, - app: clientApp.appShell.app - }; - return buildTask.run(serverOptions); - }) - .then(() => { - const RenderUniversalTask = require('../tasks/render-universal').default; + const RenderUniversalTask = require('../tasks/render-universal').default; - const renderUniversalTask = new RenderUniversalTask({ - project: this.project, - ui: this.ui, - }); - const renderUniversalOptions: RenderUniversalTaskOptions = { - inputIndexPath: join(this.project.root, clientApp.outDir, clientApp.index), - route: clientApp.appShell.route, - serverOutDir: join(this.project.root, serverApp.outDir), - outputIndexPath: join(this.project.root, clientApp.outDir, clientApp.index) - }; + const renderUniversalTask = new RenderUniversalTask({ + project: this.project, + ui: this.ui, + }); + const renderUniversalOptions: RenderUniversalTaskOptions = { + inputIndexPath: join(this.project.root, clientApp.outDir, clientApp.index), + route: clientApp.appShell.route, + serverOutDir: join(this.project.root, serverApp.outDir), + outputIndexPath: join(this.project.root, clientApp.outDir, clientApp.index) + }; - return renderUniversalTask.run(renderUniversalOptions); - }); + return await renderUniversalTask.run(renderUniversalOptions); } -}); - - -BuildCommand.overrideCore = true; -export default BuildCommand; +} diff --git a/packages/@angular/cli/commands/completion.ts b/packages/@angular/cli/commands/completion.ts deleted file mode 100644 index 369128c5b7af..000000000000 --- a/packages/@angular/cli/commands/completion.ts +++ /dev/null @@ -1,212 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -import { stripIndent } from 'common-tags'; - -const stringUtils = require('ember-cli-string-utils'); -const Command = require('../ember-cli/lib/models/command'); -const lookupCommand = require('../ember-cli/lib/cli/lookup-command'); - -function extractOptions(opts: any): string[] { - const output: string[] = []; - - for (let index = 0; index < opts.length; index++) { - const element = opts[index]; - output.push('--' + element.name); - if (element.aliases) { - output.push('-' + element.aliases[0]); - } - } - - return output; -} - -function extractBlueprints(opts: any): string[] { - const output: string[] = []; - - for (let index = 0; index < opts.length; index++) { - const element = opts[index]; - output.push(element.name); - } - - return output; -} - -export interface CompletionCommandOptions { - all?: boolean; - bash?: boolean; - zsh?: boolean; -} - -const optsNg: string[] = []; - -const CompletionCommand = Command.extend({ - name: 'completion', - description: 'Adds autocomplete functionality to `ng` commands and subcommands.', - works: 'everywhere', - availableOptions: [ - { - name: 'all', - type: Boolean, - default: true, - aliases: ['a'], - description: 'Generate a completion script compatible with both bash and zsh.' - }, - { - name: 'bash', - type: Boolean, - default: false, - aliases: ['b'], - description: 'Generate a completion script for bash.' - }, - { - name: 'zsh', - type: Boolean, - default: false, - aliases: ['z'], - description: 'Generate a completion script for zsh.' - } - ], - - run: function (commandOptions: CompletionCommandOptions) { - commandOptions.all = !commandOptions.bash && !commandOptions.zsh; - - const commandFiles = fs.readdirSync(__dirname) - .filter(file => file.match(/\.(j|t)s$/) && !file.match(/\.d.ts$/)) - .map(file => path.parse(file).name) - .map(file => file.toLowerCase()); - - const commandMap = commandFiles.reduce((acc: any, curr: string) => { - let classifiedName = stringUtils.classify(curr); - let defaultImport = require(`./${curr}`).default; - - acc[classifiedName] = defaultImport; - - return acc; - }, {}); - - let caseBlock = ''; - - commandFiles.forEach(cmd => { - const Command = lookupCommand(commandMap, cmd); - const com: string[] = []; - - const command = new Command({ - ui: this.ui, - project: this.project, - commands: this.commands, - tasks: this.tasks - }); - - if (command.hidden || command.unknown) { - return; - } - - optsNg.push(command.name); - com.push(command.name); - - if (command.aliases) { - command.aliases.forEach((element: string) => { - optsNg.push(element); - com.push(element); - }); - } - - let opts: string[] = []; - if (command.blueprints && command.blueprints[0]) { - opts = opts.concat(extractBlueprints(command.blueprints)); - } - - if (command.availableOptions && command.availableOptions[0]) { - opts = opts.concat(extractOptions(command.availableOptions)); - const optsStr = opts.sort().join(' '); - caseBlock = `${caseBlock} - ${com.sort().join('|')}) opts="${optsStr}" ;;`; - } - }); - - caseBlock = `ng|help) opts="${optsNg.sort().join(' ')}" ;;${caseBlock} - *) opts="" ;;`; - - console.log(stripIndent` - ###-begin-ng-completion### - # - - # ng command completion script - # This command supports 3 cases. - # 1. (Default case) It prints a common completion initialisation for both Bash and Zsh. - # It is the result of either calling "ng completion" or "ng completion -a". - # 2. Produce Bash-only completion: "ng completion -b" or "ng completion --bash". - # 3. Produce Zsh-only completion: "ng completion -z" or "ng completion --zsh". - # - # Usage: . <(ng completion --bash) # place it appropriately in .bashrc or - # . <(ng completion --zsh) # find a spot for it in .zshrc - #`); - - if (commandOptions.all && !commandOptions.bash) { - console.log('if test ".$(type -t complete 2>/dev/null || true)" = ".builtin"; then'); - } - - if (commandOptions.all || commandOptions.bash) { - console.log(stripIndent` - _ng_completion() { - local cword pword opts - - COMPREPLY=() - cword=\${COMP_WORDS[COMP_CWORD]} - pword=\${COMP_WORDS[COMP_CWORD - 1]} - - case \${pword} in - ${caseBlock} - esac - - COMPREPLY=( $(compgen -W '\${opts}' -- $cword) ) - - return 0 - } - - complete -o default -F _ng_completion ng - `); - } - - if (commandOptions.all) { - console.log(stripIndent` - elif test ".$(type -w compctl 2>/dev/null || true)" = ".compctl: builtin" ; then - `); - } - - if (commandOptions.all || commandOptions.zsh) { - console.log(stripIndent` - _ng_completion () { - local words cword opts - read -Ac words - read -cn cword - let cword-=1 - - case $words[cword] in - ${caseBlock} - esac - - setopt shwordsplit - reply=($opts) - unset shwordsplit - } - - compctl -K _ng_completion ng - `); - } - - if (commandOptions.all) { - console.log(stripIndent` - else - echo "Builtin command 'complete' or 'compctl' is redefined; cannot produce completion." - return 1 - fi`); - } - - console.log('###-end-ng-completion###'); - - } -}); - -export default CompletionCommand; diff --git a/packages/@angular/cli/commands/destroy.ts b/packages/@angular/cli/commands/destroy.ts deleted file mode 100644 index ac8ae4f53bd7..000000000000 --- a/packages/@angular/cli/commands/destroy.ts +++ /dev/null @@ -1,21 +0,0 @@ -const Command = require('../ember-cli/lib/models/command'); -const SilentError = require('silent-error'); - - -const DestroyCommand = Command.extend({ - name: 'destroy', - aliases: ['d'], - works: 'insideProject', - hidden: true, - - anonymousOptions: [ - '' - ], - - run: function() { - return Promise.reject(new SilentError('The destroy command is not supported by Angular CLI.')); - } -}); - -export default DestroyCommand; -DestroyCommand.overrideCore = true; diff --git a/packages/@angular/cli/commands/doc.ts b/packages/@angular/cli/commands/doc.ts index b1fa6161ab3b..6221cbc4b453 100644 --- a/packages/@angular/cli/commands/doc.ts +++ b/packages/@angular/cli/commands/doc.ts @@ -1,15 +1,16 @@ -const Command = require('../ember-cli/lib/models/command'); import { DocTask } from '../tasks/doc'; +import { Command } from '../models/command'; export interface DocOptions { search?: boolean; } -const DocCommand = Command.extend({ - name: 'doc', - description: 'Opens the official Angular API documentation for a given keyword.', - works: 'everywhere', - availableOptions: [ +export default class DocCommand extends Command { + public readonly name = 'doc'; + public readonly description = 'Opens the official Angular API documentation for a given keyword.'; + public static aliases = ['d']; + public readonly arguments = ['keyword']; + public readonly options = [ { name: 'search', aliases: ['s'], @@ -17,22 +18,16 @@ const DocCommand = Command.extend({ default: false, description: 'Search whole angular.io instead of just api.' } - ], + ]; - anonymousOptions: [ - '' - ], - - run: function(commandOptions: DocOptions, rawArgs: Array) { - const keyword = rawArgs[0]; + public async run(options: any) { + const keyword = options.keyword; const docTask = new DocTask({ ui: this.ui, project: this.project }); - return docTask.run(keyword, commandOptions.search); + return await docTask.run(keyword, options.search); } -}); - -export default DocCommand; +} diff --git a/packages/@angular/cli/commands/e2e.ts b/packages/@angular/cli/commands/e2e.ts index 3f23ee27a26d..e0c93ac68374 100644 --- a/packages/@angular/cli/commands/e2e.ts +++ b/packages/@angular/cli/commands/e2e.ts @@ -1,11 +1,11 @@ const SilentError = require('silent-error'); +import { Command, CommandScope } from '../models/command'; import { overrideOptions } from '../utilities/override-options'; import { CliConfig } from '../models/config'; import { ServeTaskOptions, baseServeCommandOptions } from './serve'; import { checkPort } from '../utilities/check-port'; import { oneLine } from 'common-tags'; -const Command = require('../ember-cli/lib/models/command'); export interface E2eTaskOptions extends ServeTaskOptions { @@ -17,12 +17,14 @@ export interface E2eTaskOptions extends ServeTaskOptions { elementExplorer: boolean; } -const E2eCommand = Command.extend({ - name: 'e2e', - aliases: ['e'], - description: 'Run e2e tests in existing project.', - works: 'insideProject', - availableOptions: overrideOptions([ +export default class E2eCommand extends Command { + public readonly name = 'e2e'; + public readonly description = 'Run e2e tests in existing project.'; + public static aliases: string[] = ['e']; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + + public options = overrideOptions([ ...baseServeCommandOptions, { name: 'config', @@ -87,8 +89,19 @@ const E2eCommand = Command.extend({ default: false, description: 'Run build when files change.' }, - ]), - run: function (commandOptions: E2eTaskOptions) { + ]); + + validate(options: E2eTaskOptions) { + if (!options.config) { + const e2eConfig = CliConfig.fromProject().config.e2e; + if (!e2eConfig.protractor.config) { + throw new SilentError('No protractor config found in .angular-cli.json.'); + } + } + return true; + } + + run(options: E2eTaskOptions) { const E2eTask = require('../tasks/e2e').E2eTask; const e2eTask = new E2eTask({ @@ -96,17 +109,13 @@ const E2eCommand = Command.extend({ project: this.project }); - if (!commandOptions.config) { + if (!options.config) { const e2eConfig = CliConfig.fromProject().config.e2e; - if (!e2eConfig.protractor.config) { - throw new SilentError('No protractor config found in .angular-cli.json.'); - } - - commandOptions.config = e2eConfig.protractor.config; + options.config = e2eConfig.protractor.config; } - if (commandOptions.serve) { + if (options.serve) { const ServeTask = require('../tasks/serve').default; const serve = new ServeTask({ @@ -115,6 +124,7 @@ const E2eCommand = Command.extend({ }); // Protractor will end the proccess, so we don't need to kill the dev server + // TODO: Convert this promise to use observables which will allow for retries. return new Promise((resolve, reject) => { let firstRebuild = true; function rebuildCb(stats: any) { @@ -122,22 +132,19 @@ const E2eCommand = Command.extend({ const cleanBuild = !!!stats.compilation.errors.length; if (firstRebuild && cleanBuild) { firstRebuild = false; - return resolve(e2eTask.run(commandOptions)); + return resolve(e2eTask.run(options)); } else { return reject('Build did not succeed. Please fix errors before running e2e task'); } } - checkPort(commandOptions.port, commandOptions.host) - .then((port: number) => commandOptions.port = port) - .then(() => serve.run(commandOptions, rebuildCb)) + checkPort(options.port, options.host) + .then((port: number) => options.port = port) + .then(() => serve.run(options, rebuildCb)) .catch(reject); }); } else { - return e2eTask.run(commandOptions); + return e2eTask.run(options); } } -}); - - -export default E2eCommand; +} diff --git a/packages/@angular/cli/commands/easter-egg.ts b/packages/@angular/cli/commands/easter-egg.ts index 227b2612facf..02126507dbb7 100644 --- a/packages/@angular/cli/commands/easter-egg.ts +++ b/packages/@angular/cli/commands/easter-egg.ts @@ -1,35 +1,27 @@ -const Command = require('../ember-cli/lib/models/command'); -const stringUtils = require('ember-cli-string-utils'); +import { Command, Option } from '../models/command'; import chalk from 'chalk'; - function pickOne(of: string[]): string { return of[Math.floor(Math.random() * of.length)]; } +export default class AwesomeCommand extends Command { + public readonly name = 'make-this-awesome'; + public readonly description = ''; + readonly arguments: string[] = []; + readonly options: Option[] = []; -const MakeThisAwesomeCommand = Command.extend({ - name: 'make-this-awesome', - works: 'insideProject', - hidden: true, - - run: function (commandOptions: any, rawArgs: string[]): Promise { - (this as any)[stringUtils.camelize(this.name)](commandOptions, rawArgs); - - return Promise.resolve(); - }, - - makeThisAwesome: function() { + run(_options: any) { const phrase = pickOne([ `You're on it, there's nothing for me to do!`, `Let's take a look... nope, it's all good!`, `You're doing fine.`, `You're already doing great.`, `Nothing to do; already awesome. Exiting.`, - `Error 418: As Awesome As Can Get.` + `Error 418: As Awesome As Can Get.`, + `I spy with my little eye a great developer!`, + `Noop... already awesome.` ]); - console.log(chalk.green(phrase)); + this.logger.info(chalk.green(phrase)); } -}); - -export default MakeThisAwesomeCommand; +} diff --git a/packages/@angular/cli/commands/eject.ts b/packages/@angular/cli/commands/eject.ts index fa69b6ea8e32..ded5ccb27944 100644 --- a/packages/@angular/cli/commands/eject.ts +++ b/packages/@angular/cli/commands/eject.ts @@ -1,8 +1,7 @@ +import { Command, CommandScope } from '../models/command'; import { BuildOptions } from '../models/build-options'; import {baseBuildCommandOptions} from './build'; -const Command = require('../ember-cli/lib/models/command'); - // defaults for BuildOptions export const baseEjectCommandOptions: any = [ ...baseBuildCommandOptions, @@ -24,14 +23,15 @@ export interface EjectTaskOptions extends BuildOptions { app?: string; } +export default class EjectCommand extends Command { + public readonly name = 'eject'; + public readonly description = + 'Ejects your app and output the proper webpack configuration and scripts.'; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = baseEjectCommandOptions; -const EjectCommand = Command.extend({ - name: 'eject', - description: 'Ejects your app and output the proper webpack configuration and scripts.', - - availableOptions: baseEjectCommandOptions, - - run: function (commandOptions: EjectTaskOptions) { + public async run(options: EjectTaskOptions) { const EjectTask = require('../tasks/eject').default; const ejectTask = new EjectTask({ @@ -39,9 +39,6 @@ const EjectCommand = Command.extend({ ui: this.ui, }); - return ejectTask.run(commandOptions); + return await ejectTask.run(options); } -}); - - -export default EjectCommand; +} diff --git a/packages/@angular/cli/commands/generate.ts b/packages/@angular/cli/commands/generate.ts index 1c53e54866d7..fbf9ea83359e 100644 --- a/packages/@angular/cli/commands/generate.ts +++ b/packages/@angular/cli/commands/generate.ts @@ -1,8 +1,7 @@ +import { Command, CommandScope } from '../models/command'; import chalk from 'chalk'; const stringUtils = require('ember-cli-string-utils'); -import { oneLine } from 'common-tags'; import { CliConfig } from '../models/config'; - import { getCollection, getEngineHost @@ -11,20 +10,18 @@ import { DynamicPathOptions, dynamicPathParser } from '../utilities/dynamic-path import { getAppFromConfig } from '../utilities/app-utils'; import * as path from 'path'; import { SchematicAvailableOptions } from '../tasks/schematic-get-options'; +import { oneLine } from 'common-tags'; -const Command = require('../ember-cli/lib/models/command'); -const SilentError = require('silent-error'); - -const { cyan, yellow } = chalk; +const { cyan } = chalk; const separatorRegEx = /[\/\\]/g; - -export default Command.extend({ - name: 'generate', - description: 'Generates and/or modifies files based on a schematic.', - aliases: ['g'], - - availableOptions: [ +export default class GenerateCommand extends Command { + public readonly name = 'generate'; + public readonly description = 'Generates and/or modifies files based on a schematic.'; + public static aliases = ['g']; + public readonly scope = CommandScope.inProject; + public arguments = ['schematic']; + public options = [ { name: 'dry-run', type: Boolean, @@ -54,137 +51,111 @@ export default Command.extend({ { name: 'lint-fix', type: Boolean, - aliases: ['lf'], + aliases: ['l'], description: 'Use lint to fix files after generation.' } - ], - - anonymousOptions: [ - '' - ], - - getCollectionName(rawArgs: string[], parsedOptions?: { collection?: string }): [string, string] { - let collectionName: string = CliConfig.getValue('defaults.schematics.collection'); - if (!rawArgs || rawArgs.length === 0) { - return [collectionName, null]; - } - let schematicName = rawArgs[0]; + ]; - if (schematicName.match(/:/)) { - [collectionName, schematicName] = schematicName.split(':', 2); - } else if (parsedOptions) { - if (parsedOptions.collection) { - collectionName = parsedOptions.collection; - } - } else { - const parsedArgs = this.parseArgs(rawArgs, false); - if (parsedArgs.options.collection) { - collectionName = parsedArgs.options.collection; - } + private initialized = false; + public async initialize(options: any): Promise { + if (this.initialized) { + return Promise.resolve(); } + this.initialized = true; - return [collectionName, schematicName]; - }, + const [collectionName, schematicName] = this.parseSchematicInfo(options); - beforeRun: function(rawArgs: string[]) { + if (!!schematicName) { + const SchematicGetOptionsTask = require('../tasks/schematic-get-options').default; - const isHelp = ['--help', '-h'].includes(rawArgs[0]); - if (isHelp) { - return; - } - - const [collectionName, schematicName] = this.getCollectionName(rawArgs); - if (!schematicName) { - return Promise.reject(new SilentError(oneLine` - The "ng generate" command requires a - schematic name to be specified. - For more details, use "ng help". - `)); - } + const getOptionsTask = new SchematicGetOptionsTask({ + ui: this.ui, + project: this.project + }); - if (/^\d/.test(rawArgs[1])) { - SilentError.debugOrThrow('@angular/cli/commands/generate', - `The \`ng generate ${schematicName} ${rawArgs[1]}\` file name cannot begin with a digit.`); - } + const availableOptions: SchematicAvailableOptions[] = await getOptionsTask.run({ + schematicName, + collectionName, + }); + let anonymousOptions: string[] = []; - const SchematicGetOptionsTask = require('../tasks/schematic-get-options').default; + if (availableOptions) { + const nameOption = availableOptions.filter(opt => opt.name === 'name')[0]; + if (nameOption) { + anonymousOptions = [...anonymousOptions, 'name']; + } + } else { + anonymousOptions = [...anonymousOptions, 'name']; + } - const getOptionsTask = new SchematicGetOptionsTask({ - ui: this.ui, - project: this.project - }); + if (collectionName === '@schematics/angular' && schematicName === 'interface') { + anonymousOptions = [...anonymousOptions, 'type']; + } - return getOptionsTask.run({ - schematicName, - collectionName - }) - .then((availableOptions: SchematicAvailableOptions[]) => { - let anonymousOptions: string[] = []; - - if (availableOptions) { - const nameOption = availableOptions.filter(opt => opt.name === 'name')[0]; - if (nameOption) { - anonymousOptions = [...anonymousOptions, '']; - } - } else { - anonymousOptions = [...anonymousOptions, '']; - } + this.arguments = this.arguments.concat(anonymousOptions); + this.options = this.options.concat( availableOptions || []); + } + } - if (collectionName === '@schematics/angular' && schematicName === 'interface') { - anonymousOptions = [...anonymousOptions, '']; - } + validate(options: any): boolean | Promise { + if (!options.schematic) { + this.logger.error(oneLine` + The "ng generate" command requires a + schematic name to be specified. + For more details, use "ng help".`); - this.registerOptions({ - anonymousOptions: anonymousOptions, - availableOptions: availableOptions || [] - }); - }); - }, + return false; + } + if (options.name && /^\d/.test(options.name)) { + this.logger.error(oneLine`The \`ng generate ${options.schematic} ${options.name}\` + file name cannot begin with a digit.`); - run: function (commandOptions: any, rawArgs: string[]) { - if (rawArgs[0] === 'module' && !rawArgs[1]) { - throw 'The `ng generate module` command requires a name to be specified.'; + return false; } + return true; + } + + public run(options: any) { + let entityName = options.name; - let entityName = rawArgs[1]; if (entityName) { - commandOptions.name = stringUtils.dasherize(entityName.split(separatorRegEx).pop()); + options.name = stringUtils.dasherize(entityName.split(separatorRegEx).pop()); } else { entityName = ''; } - const appConfig = getAppFromConfig(commandOptions.app); + const appConfig = getAppFromConfig(options.app); const dynamicPathOptions: DynamicPathOptions = { project: this.project, entityName: entityName, appConfig: appConfig, - dryRun: commandOptions.dryRun + dryRun: options.dryRun }; const parsedPath = dynamicPathParser(dynamicPathOptions); - commandOptions.sourceDir = parsedPath.sourceDir.replace(separatorRegEx, '/'); + options.sourceDir = parsedPath.sourceDir.replace(separatorRegEx, '/'); const root = parsedPath.sourceDir + path.sep; - commandOptions.appRoot = parsedPath.appRoot === parsedPath.sourceDir ? '' : + options.appRoot = parsedPath.appRoot === parsedPath.sourceDir ? '' : parsedPath.appRoot.startsWith(root) ? parsedPath.appRoot.substr(root.length) : parsedPath.appRoot; - commandOptions.path = parsedPath.dir.replace(separatorRegEx, '/'); - commandOptions.path = parsedPath.dir === parsedPath.sourceDir ? '' : + options.path = parsedPath.dir.replace(separatorRegEx, '/'); + options.path = parsedPath.dir === parsedPath.sourceDir ? '' : parsedPath.dir.startsWith(root) - ? commandOptions.path.substr(root.length) - : commandOptions.path; + ? options.path.substr(root.length) + : options.path; const cwd = this.project.root; - const [collectionName, schematicName] = this.getCollectionName(rawArgs, commandOptions); + const [collectionName, schematicName] = this.parseSchematicInfo(options); if (['component', 'c', 'directive', 'd'].indexOf(schematicName) !== -1) { - if (commandOptions.prefix === undefined) { - commandOptions.prefix = appConfig.prefix; + if (options.prefix === undefined) { + options.prefix = appConfig.prefix; } if (schematicName === 'component' || schematicName === 'c') { - if (commandOptions.styleext === undefined) { - commandOptions.styleext = CliConfig.getValue('defaults.styleExt'); + if (options.styleext === undefined) { + options.styleext = CliConfig.getValue('defaults.styleExt'); } } } @@ -195,48 +166,51 @@ export default Command.extend({ project: this.project }); - if (collectionName === '@schematics/angular' && schematicName === 'interface' && rawArgs[2]) { - commandOptions.type = rawArgs[2]; + if (collectionName === '@schematics/angular' && schematicName === 'interface' && options.type) { + options.type = options.type; } return schematicRunTask.run({ - taskOptions: commandOptions, + taskOptions: options, workingDir: cwd, collectionName, schematicName }); - }, + } + + private parseSchematicInfo(options: any) { + let collectionName: string = + options.collection || + options.c || + CliConfig.getValue('defaults.schematics.collection'); + + let schematicName = options.schematic; - printDetailedHelp: function (_options: any, rawArgs: any): string | Promise { - const engineHost = getEngineHost(); - const [collectionName] = this.getCollectionName(); - const collection = getCollection(collectionName); - const schematicName = rawArgs[1]; if (schematicName) { - const SchematicGetHelpOutputTask = require('../tasks/schematic-get-help-output').default; - const getHelpOutputTask = new SchematicGetHelpOutputTask({ - ui: this.ui, - project: this.project - }); - return getHelpOutputTask.run({ - schematicName, - collectionName, - nonSchematicOptions: this.availableOptions.filter((o: any) => !o.hidden) - }) - .then((output: string[]) => { - return [ - cyan(`ng generate ${schematicName} ${cyan('[name]')} ${cyan('')}`), - ...output - ].join('\n'); - }); + if (schematicName.match(/:/)) { + [collectionName, schematicName] = schematicName.split(':', 2); + } + } + + return [collectionName, schematicName]; + } + + public printHelp(options: any) { + if (options.schematic) { + super.printHelp(options); } else { + this.printHelpUsage(this.name, this.arguments, this.options); + const engineHost = getEngineHost(); + const [collectionName] = this.parseSchematicInfo(options); + const collection = getCollection(collectionName); const schematicNames: string[] = engineHost.listSchematics(collection); - const output: string[] = []; - output.push(cyan('Available schematics:')); + this.logger.info('Available schematics:'); schematicNames.forEach(schematicName => { - output.push(yellow(` ${schematicName}`)); + this.logger.info(` ${schematicName}`); }); - return Promise.resolve(output.join('\n')); + + this.logger.warn(`\nTo see help for a schematic run:`); + this.logger.info(cyan(` ng generate --help`)); } } -}); +} diff --git a/packages/@angular/cli/commands/get.ts b/packages/@angular/cli/commands/get.ts index a0092f45f78f..6ff40d4e8038 100644 --- a/packages/@angular/cli/commands/get.ts +++ b/packages/@angular/cli/commands/get.ts @@ -1,49 +1,47 @@ import {CliConfig} from '../models/config'; +import { Command } from '../models/command'; const SilentError = require('silent-error'); -const Command = require('../ember-cli/lib/models/command'); export interface GetOptions { + jsonPath: string; global?: boolean; } - -const GetCommand = Command.extend({ - name: 'get', - description: 'Get a value from the configuration. Example: ng get [key]', - works: 'everywhere', - - availableOptions: [ +export default class GetCommand extends Command { + public readonly name = 'get'; + public readonly description = 'Get a value from the configuration. Example: ng get [key]'; + public readonly arguments = ['jsonPath']; + public readonly options = [ { name: 'global', type: Boolean, 'default': false, + aliases: ['g'], description: 'Get the value in the global configuration (in your home directory).' } - ], + ]; - run: function (commandOptions: GetOptions, rawArgs: string[]): Promise { + public run(options: GetOptions) { return new Promise(resolve => { - const config = commandOptions.global ? CliConfig.fromGlobal() : CliConfig.fromProject(); + const config = options.global ? CliConfig.fromGlobal() : CliConfig.fromProject(); if (config === null) { throw new SilentError('No config found. If you want to use global configuration, ' + 'you need the --global argument.'); } - const value = config.get(rawArgs[0]); + const value = config.get(options.jsonPath); if (value === null || value === undefined) { throw new SilentError('Value cannot be found.'); } else if (typeof value == 'object') { - console.log(JSON.stringify(value, null, 2)); + this.logger.info(JSON.stringify(value, null, 2)); } else { - console.log(value); + this.logger.info(value.toString()); } resolve(); }); } -}); - -export default GetCommand; +} diff --git a/packages/@angular/cli/commands/help.ts b/packages/@angular/cli/commands/help.ts index a4c0fe712d7f..6796a3c3d798 100644 --- a/packages/@angular/cli/commands/help.ts +++ b/packages/@angular/cli/commands/help.ts @@ -1,59 +1,35 @@ +import { Command, Option } from '../models/command'; import chalk from 'chalk'; -import * as fs from 'fs'; -import * as path from 'path'; const { cyan } = chalk; -const Command = require('../ember-cli/lib/models/command'); -const stringUtils = require('ember-cli-string-utils'); -const lookupCommand = require('../ember-cli/lib/cli/lookup-command'); -const HelpCommand = Command.extend({ - name: 'help', - description: 'Shows help for the CLI.', - works: 'everywhere', - - availableOptions: [], - - anonymousOptions: [], - - run: function (_commandOptions: any, _rawArgs: any) { - let commandFiles = fs.readdirSync(__dirname) - // Remove files that are not JavaScript or Typescript - .filter(file => file.match(/\.(j|t)s$/) && !file.match(/\.d.ts$/)) - .map(file => path.parse(file).name) - .map(file => file.toLowerCase()); - - let commandMap = commandFiles.reduce((acc: any, curr: string) => { - let classifiedName = stringUtils.classify(curr); - let defaultImport = require(`./${curr}`).default; - - acc[classifiedName] = defaultImport; - - return acc; - }, {}); - - const commands = commandFiles - .map(commandFile => { - const Command = lookupCommand(commandMap, commandFile); - - const cmd = new Command({ - ui: this.ui, - project: this.project, - commands: this.commands, - tasks: this.tasks - }); - - return cmd; +export default class HelpCommand extends Command { + public readonly name = 'help'; + public readonly description = 'Help.'; + public readonly arguments: string[] = []; + public readonly options: Option[] = []; + + run(options: any) { + const commands = Object.keys(options.commandMap) + .map(key => { + const Cmd = options.commandMap[key]; + const command: Command = new Cmd(null, null); + return command; }) .filter(cmd => !cmd.hidden && !cmd.unknown) - .map(cmd => ({ name: cmd.name, description: cmd.description })); - this.ui.writeLine(`Available Commands:`); + .map(cmd => ({ + name: cmd.name, + description: cmd.description + })); + this.logger.info(`Available Commands:`); commands.forEach(cmd => { - this.ui.writeLine(` ${cyan(cmd.name)} ${cmd.description}`); + this.logger.info(` ${cyan(cmd.name)} ${cmd.description}`); }); - this.ui.writeLine(`\nFor more detailed help run "ng [command name] --help"`); + + this.logger.info(`\nFor more detailed help run "ng [command name] --help"`); } -}); -HelpCommand.overrideCore = true; -export default HelpCommand; + printHelp(options: any) { + return this.run(options); + } +} diff --git a/packages/@angular/cli/commands/lint.ts b/packages/@angular/cli/commands/lint.ts index 7d75543e81a9..0c2a52328572 100644 --- a/packages/@angular/cli/commands/lint.ts +++ b/packages/@angular/cli/commands/lint.ts @@ -1,8 +1,7 @@ +import { Command, CommandScope } from '../models/command'; import { oneLine } from 'common-tags'; import { CliConfig } from '../models/config'; -const Command = require('../ember-cli/lib/models/command'); - export interface LintCommandOptions { fix?: boolean; @@ -11,12 +10,13 @@ export interface LintCommandOptions { force?: boolean; } -export default Command.extend({ - name: 'lint', - aliases: ['l'], - description: 'Lints code in existing project.', - works: 'insideProject', - availableOptions: [ +export default class LintCommand extends Command { + public readonly name = 'lint'; + public readonly description = 'Lints code in existing project.'; + public static aliases = ['l']; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = [ { name: 'fix', type: Boolean, @@ -44,8 +44,9 @@ export default Command.extend({ Output format (prose, json, stylish, verbose, pmd, msbuild, checkstyle, vso, fileslist). ` } - ], - run: function (commandOptions: LintCommandOptions) { + ]; + + public async run(options: LintCommandOptions) { const LintTask = require('../tasks/lint').default; const lintTask = new LintTask({ @@ -53,9 +54,15 @@ export default Command.extend({ project: this.project }); - return lintTask.run({ - ...commandOptions, + const lintResults: number = await lintTask.run({ + ...options, configs: CliConfig.fromProject().config.lint }); + + if (lintResults != 0) { + throw ''; + } + + return lintResults; } -}); +} diff --git a/packages/@angular/cli/commands/new.ts b/packages/@angular/cli/commands/new.ts index 3c22a1cc9cf6..7731e4e42c67 100644 --- a/packages/@angular/cli/commands/new.ts +++ b/packages/@angular/cli/commands/new.ts @@ -2,23 +2,22 @@ import * as fs from 'fs'; import * as path from 'path'; import chalk from 'chalk'; +import { Command, CommandScope } from '../models/command'; import { CliConfig } from '../models/config'; import { validateProjectName } from '../utilities/validate-project-name'; import { oneLine } from 'common-tags'; import { SchematicAvailableOptions } from '../tasks/schematic-get-options'; -const { cyan } = chalk; - -const Command = require('../ember-cli/lib/models/command'); const SilentError = require('silent-error'); -const NewCommand = Command.extend({ - name: 'new', - aliases: ['n'], - description: `Creates a new directory and a new Angular app eg. "ng new [name]".`, - works: 'outsideProject', - - availableOptions: [ +export default class NewCommand extends Command { + public readonly name = 'new'; + public readonly description = + 'Creates a new directory and a new Angular app eg. "ng new [name]".'; + public static aliases = ['n']; + public scope = CommandScope.outsideProject; + public arguments = ['name']; + public options = [ { name: 'dry-run', type: Boolean, @@ -42,35 +41,17 @@ const NewCommand = Command.extend({ aliases: ['c'], description: 'Schematics collection to use.' } - ], + ]; - isProject: function (projectPath: string) { - return CliConfig.fromProject(projectPath) !== null; - }, - - getCollectionName(rawArgs: string[]) { - let collectionName = CliConfig.fromGlobal().get('defaults.schematics.collection'); - if (rawArgs) { - const parsedArgs = this.parseArgs(rawArgs, false); - if (parsedArgs.options.collection) { - collectionName = parsedArgs.options.collection; - } + private initialized = false; + public initialize(options: any) { + if (this.initialized) { + return Promise.resolve(); } - return collectionName; - }, + this.initialized = true; - beforeRun: function (rawArgs: string[]) { - const isHelp = ['--help', '-h'].includes(rawArgs[0]); - if (isHelp) { - return; - } - - const schematicName = CliConfig.getValue('defaults.schematics.newApp'); - - if (/^\d/.test(rawArgs[1])) { - SilentError.debugOrThrow('@angular/cli/commands/generate', - `The \`ng new ${rawArgs[0]}\` file name cannot begin with a digit.`); - } + const collectionName = this.parseCollectionName(options); + const schematicName = CliConfig.fromGlobal().get('defaults.schematics.newApp'); const SchematicGetOptionsTask = require('../tasks/schematic-get-options').default; @@ -80,34 +61,34 @@ const NewCommand = Command.extend({ }); return getOptionsTask.run({ - schematicName, - collectionName: this.getCollectionName(rawArgs) - }) - .then((availableOptions: SchematicAvailableOptions) => { - this.registerOptions({ - availableOptions: availableOptions - }); + schematicName, + collectionName + }) + .then((availableOptions: SchematicAvailableOptions[]) => { + if (availableOptions) { + availableOptions = availableOptions.filter(opt => opt.name !== 'name'); + } + + this.options = this.options.concat( availableOptions || []); }); - }, - - run: function (commandOptions: any, rawArgs: string[]) { - const packageName = rawArgs.shift(); + } - if (!packageName) { + public async run(options: any) { + if (!options.name) { return Promise.reject(new SilentError( - `The "ng ${this.name}" command requires a name argument to be specified eg. ` + + `The "ng ${options.name}" command requires a name argument to be specified eg. ` + chalk.yellow('ng new [name] ') + `For more details, use "ng help".`)); } - validateProjectName(packageName); - commandOptions.name = packageName; - if (commandOptions.dryRun) { - commandOptions.skipGit = true; + validateProjectName(options.name); + options.name = options.name; + if (options.dryRun) { + options.skipGit = true; } - commandOptions.directory = commandOptions.directory || packageName; - const directoryName = path.join(process.cwd(), commandOptions.directory); + options.directory = options.directory || options.name; + const directoryName = path.join(process.cwd(), options.directory); if (fs.existsSync(directoryName) && this.isProject(directoryName)) { throw new SilentError(oneLine` @@ -115,49 +96,35 @@ const NewCommand = Command.extend({ `); } - if (commandOptions.collection) { - commandOptions.collectionName = commandOptions.collection; + if (options.collection) { + options.collectionName = options.collection; } else { - commandOptions.collectionName = this.getCollectionName(rawArgs); + options.collectionName = this.parseCollectionName(options); } const InitTask = require('../tasks/init').default; const initTask = new InitTask({ project: this.project, - tasks: this.tasks, ui: this.ui, }); // Ensure skipGit has a boolean value. - commandOptions.skipGit = commandOptions.skipGit === undefined ? false : commandOptions.skipGit; + options.skipGit = options.skipGit === undefined ? false : options.skipGit; - return initTask.run(commandOptions, rawArgs); - }, + return await initTask.run(options); + } - printDetailedHelp: function (): string | Promise { - const collectionName = this.getCollectionName(); - const schematicName = CliConfig.getValue('defaults.schematics.newApp'); - const SchematicGetHelpOutputTask = require('../tasks/schematic-get-help-output').default; - const getHelpOutputTask = new SchematicGetHelpOutputTask({ - ui: this.ui, - project: this.project - }); - return getHelpOutputTask.run({ - schematicName, - collectionName, - nonSchematicOptions: this.availableOptions.filter((o: any) => !o.hidden) - }) - .then((output: string[]) => { - const outputLines = [ - cyan(`ng new ${cyan('[name]')} ${cyan('')}`), - ...output - ]; - return outputLines.join('\n'); - }); + private isProject(projectPath: string): boolean { + return CliConfig.fromProject(projectPath) !== null; } -}); + private parseCollectionName(options: any): string { + let collectionName: string = + options.collection || + options.c || + CliConfig.getValue('defaults.schematics.collection'); -NewCommand.overrideCore = true; -export default NewCommand; + return collectionName; + } +} diff --git a/packages/@angular/cli/commands/serve.ts b/packages/@angular/cli/commands/serve.ts index 179dbef1ef43..555258ba4e86 100644 --- a/packages/@angular/cli/commands/serve.ts +++ b/packages/@angular/cli/commands/serve.ts @@ -1,3 +1,4 @@ +import { Command, CommandScope } from '../models/command'; import { BuildOptions } from '../models/build-options'; import { baseBuildCommandOptions } from './build'; import { CliConfig } from '../models/config'; @@ -6,7 +7,7 @@ import { ServeTaskOptions } from './serve'; import { checkPort } from '../utilities/check-port'; import { overrideOptions } from '../utilities/override-options'; -const Command = require('../ember-cli/lib/models/command'); +// const Command = require('../ember-cli/lib/models/command'); const config = CliConfig.fromProject() || CliConfig.fromGlobal(); const serveConfigDefaults = config.getPaths('defaults.serve', [ @@ -116,35 +117,35 @@ export const baseServeCommandOptions: any = overrideOptions([ } ]); -const ServeCommand = Command.extend({ - name: 'serve', - description: 'Builds and serves your app, rebuilding on file changes.', - aliases: ['server', 's'], - - availableOptions: baseServeCommandOptions, - - run: function (commandOptions: ServeTaskOptions) { - const ServeTask = require('../tasks/serve').default; +export default class ServeCommand extends Command { + public readonly name = 'serve'; + public readonly description = 'Builds and serves your app, rebuilding on file changes.'; + public static aliases = ['server', 's']; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = baseServeCommandOptions; + public validate(_options: ServeTaskOptions) { // Check Angular and TypeScript versions. Version.assertAngularVersionIs2_3_1OrHigher(this.project.root); Version.assertTypescriptVersion(this.project.root); + return true; + } + + public async run(options: ServeTaskOptions) { + const ServeTask = require('../tasks/serve').default; // Default evalSourcemaps to true for serve. This makes rebuilds faster. - commandOptions.evalSourcemaps = true; + options.evalSourcemaps = true; - return checkPort(commandOptions.port, commandOptions.host, defaultPort) - .then(port => { - commandOptions.port = port; + const port = await checkPort(options.port, options.host, defaultPort); + options.port = port; - const serve = new ServeTask({ - ui: this.ui, - project: this.project, - }); + const serve = new ServeTask({ + ui: this.ui, + project: this.project, + }); - return serve.run(commandOptions); - }); + return await serve.run(options); } -}); - -export default ServeCommand; +} diff --git a/packages/@angular/cli/commands/set.ts b/packages/@angular/cli/commands/set.ts index bad0954d4fee..6a37f61147fc 100644 --- a/packages/@angular/cli/commands/set.ts +++ b/packages/@angular/cli/commands/set.ts @@ -1,81 +1,65 @@ +import { Command } from '../models/command'; import * as fs from 'fs'; -import chalk from 'chalk'; import { CliConfig } from '../models/config'; import { oneLine } from 'common-tags'; const SilentError = require('silent-error'); -const Command = require('../ember-cli/lib/models/command'); export interface SetOptions { + jsonPath: string; + value: string; global?: boolean; } -const SetCommand = Command.extend({ - name: 'set', - description: 'Set a value in the configuration.', - works: 'everywhere', - - availableOptions: [ +export default class SetCommand extends Command { + public readonly name = 'set'; + public readonly description = 'Set a value in the configuration.'; + public static aliases = ['jsonPath']; + public readonly arguments = ['jsonPath', 'value']; + public readonly options = [ { name: 'global', type: Boolean, 'default': false, aliases: ['g'], description: 'Set the value in the global configuration rather than in your project\'s.' - }, - ], + }]; - asBoolean: function (raw: string): boolean { - if (raw == 'true' || raw == '1') { - return true; - } else if (raw == 'false' || raw == '' || raw == '0') { - return false; - } else { - throw new SilentError(`Invalid boolean value: "${raw}"`); - } - }, - asNumber: function (raw: string): number { - if (Number.isNaN(+raw)) { - throw new SilentError(`Invalid number value: "${raw}"`); - } - return +raw; - }, - run: function (commandOptions: SetOptions, rawArgs: string[]): Promise { + public run(options: SetOptions) { return new Promise(resolve => { - const config = commandOptions.global ? CliConfig.fromGlobal() : CliConfig.fromProject(); + const config = options.global ? CliConfig.fromGlobal() : CliConfig.fromProject(); if (config === null) { throw new SilentError('No config found. If you want to use global configuration, ' + 'you need the --global argument.'); } - let [jsonPath, rawValue] = rawArgs; + if (options.value === undefined && options.jsonPath.indexOf('=') !== -1) { + [options.jsonPath, options.value] = options.jsonPath.split('=', 2); + } - if (rawValue === undefined) { - [jsonPath, rawValue] = jsonPath.split('=', 2); - if (rawValue === undefined) { - throw new SilentError('Must specify a value.'); - } + if (options.value === undefined) { + throw new SilentError('Must specify a value.'); } - const type = config.typeOf(jsonPath); - let value: any = rawValue; + const type = config.typeOf(options.jsonPath); + let value: any = options.value; switch (type) { - case 'boolean': value = this.asBoolean(rawValue); break; - case 'number': value = this.asNumber(rawValue); break; - case 'string': value = rawValue; break; + case 'boolean': value = this.asBoolean(options.value); break; + case 'number': value = this.asNumber(options.value); break; + case 'string': value = options.value; break; - default: value = parseValue(rawValue, jsonPath); + default: value = this.parseValue(options.value, options.jsonPath); } - if (jsonPath.indexOf('prefix') > 0) { + if (options.jsonPath.indexOf('prefix') > 0) { // update tslint if prefix is updated - updateLintForPrefix(this.project.root + '/tslint.json', value); + this.updateLintForPrefix(this.project.root + '/tslint.json', value); } try { - config.set(jsonPath, value); + config.set(options.jsonPath, value); config.save(); } catch (error) { throw new SilentError(error.message); @@ -83,43 +67,58 @@ const SetCommand = Command.extend({ resolve(); }); } -}); -function updateLintForPrefix(filePath: string, prefix: string): void { - if (!fs.existsSync(filePath)) { - return; + private asBoolean(raw: string): boolean { + if (raw == 'true' || raw == '1') { + return true; + } else if (raw == 'false' || raw == '' || raw == '0') { + return false; + } else { + throw new SilentError(`Invalid boolean value: "${raw}"`); + } } - const tsLint = JSON.parse(fs.readFileSync(filePath, 'utf8')); - - const componentLint = tsLint.rules['component-selector'][2]; - if (componentLint instanceof Array) { - tsLint.rules['component-selector'][2].push(prefix); - } else { - tsLint.rules['component-selector'][2] = prefix; + private asNumber(raw: string): number { + if (Number.isNaN(+raw)) { + throw new SilentError(`Invalid number value: "${raw}"`); + } + return +raw; } - const directiveLint = tsLint.rules['directive-selector'][2]; - if (directiveLint instanceof Array) { - tsLint.rules['directive-selector'][2].push(prefix); - } else { - tsLint.rules['directive-selector'][2] = prefix; + private parseValue(rawValue: string, path: string) { + try { + return JSON.parse(rawValue); + } catch (error) { + throw new SilentError(`No node found at path ${path}`); + } } - fs.writeFileSync(filePath, JSON.stringify(tsLint, null, 2)); + private updateLintForPrefix(filePath: string, prefix: string): void { + if (!fs.existsSync(filePath)) { + return; + } + + const tsLint = JSON.parse(fs.readFileSync(filePath, 'utf8')); - console.log(chalk.yellow(oneLine` - tslint configuration updated to match new prefix, - you may need to fix any linting errors. - `)); -} + const componentLint = tsLint.rules['component-selector'][2]; + if (componentLint instanceof Array) { + tsLint.rules['component-selector'][2].push(prefix); + } else { + tsLint.rules['component-selector'][2] = prefix; + } -function parseValue(rawValue: string, path: string) { - try { - return JSON.parse(rawValue); - } catch (error) { - throw new SilentError(`No node found at path ${path}`); + const directiveLint = tsLint.rules['directive-selector'][2]; + if (directiveLint instanceof Array) { + tsLint.rules['directive-selector'][2].push(prefix); + } else { + tsLint.rules['directive-selector'][2] = prefix; + } + + fs.writeFileSync(filePath, JSON.stringify(tsLint, null, 2)); + + this.logger.warn(oneLine` + tslint configuration updated to match new prefix, + you may need to fix any linting errors. + `); } } - -export default SetCommand; diff --git a/packages/@angular/cli/commands/test.ts b/packages/@angular/cli/commands/test.ts index da5f9ac05b57..2661fd6616de 100644 --- a/packages/@angular/cli/commands/test.ts +++ b/packages/@angular/cli/commands/test.ts @@ -1,4 +1,4 @@ -const Command = require('../ember-cli/lib/models/command'); +import { Command, CommandScope } from '../models/command'; import TestTask from '../tasks/test'; import { CliConfig } from '../models/config'; import { oneLine } from 'common-tags'; @@ -26,14 +26,13 @@ export interface TestOptions { preserveSymlinks?: boolean; } - -const TestCommand = Command.extend({ - name: 'test', - aliases: ['t'], - description: 'Run unit tests in existing project.', - works: 'insideProject', - - availableOptions: [ +export default class TestCommand extends Command { + public readonly name = 'test'; + public readonly description = 'Run unit tests in existing project.'; + public static aliases = ['t']; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = [ { name: 'watch', type: Boolean, @@ -124,22 +123,19 @@ const TestCommand = Command.extend({ aliases: ['a'], description: 'Specifies app name to use.' } - ], + ]; - run: function (commandOptions: TestOptions) { + public async run(options: TestOptions) { const testTask = new TestTask({ ui: this.ui, project: this.project }); - if (commandOptions.watch !== undefined && !commandOptions.watch) { + if (options.watch !== undefined && !options.watch) { // if not watching ensure karma is doing a single run - commandOptions.singleRun = true; + options.singleRun = true; } - return testTask.run(commandOptions); + return await testTask.run(options); } -}); - -TestCommand.overrideCore = true; -export default TestCommand; +} diff --git a/packages/@angular/cli/commands/update.ts b/packages/@angular/cli/commands/update.ts index 4530a0cdf677..0031fb2e280a 100644 --- a/packages/@angular/cli/commands/update.ts +++ b/packages/@angular/cli/commands/update.ts @@ -1,15 +1,18 @@ -const Command = require('../ember-cli/lib/models/command'); +import { Command, CommandScope } from '../models/command'; import { UpdateTask } from '../tasks/update'; export interface UpdateOptions { schematic?: boolean; } -const UpdateCommand = Command.extend({ - name: 'update', - description: 'Updates your application.', - works: 'everywhere', - availableOptions: [ + +export default class UpdateCommand extends Command { + public readonly name = 'update'; + public readonly description = 'Updates your application.'; + public static aliases: string[] = []; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = [ { name: 'dry-run', type: Boolean, @@ -23,11 +26,9 @@ const UpdateCommand = Command.extend({ default: false, description: 'Install the next version, instead of the latest.' } - ], - - anonymousOptions: [], + ]; - run: function(commandOptions: any) { + public async run(options: any) { const schematic = '@schematics/package-update:all'; const updateTask = new UpdateTask({ @@ -35,8 +36,6 @@ const UpdateCommand = Command.extend({ project: this.project }); - return updateTask.run(schematic, commandOptions); + return await updateTask.run(schematic, options); } -}); - -export default UpdateCommand; +} diff --git a/packages/@angular/cli/commands/version.ts b/packages/@angular/cli/commands/version.ts index abab2e803c1a..4de71ccedee6 100644 --- a/packages/@angular/cli/commands/version.ts +++ b/packages/@angular/cli/commands/version.ts @@ -1,4 +1,4 @@ -const Command = require('../ember-cli/lib/models/command'); +import { Command, Option } from '../models/command'; import { stripIndents } from 'common-tags'; import * as fs from 'fs'; import * as path from 'path'; @@ -7,15 +7,14 @@ import chalk from 'chalk'; import { CliConfig } from '../models/config'; -const VersionCommand = Command.extend({ - name: 'version', - description: 'Outputs Angular CLI version.', - aliases: ['v', '--version', '-v'], - works: 'everywhere', +export default class VersionCommand extends Command { + public readonly name = 'version'; + public readonly description = 'Outputs Angular CLI version.'; + public static aliases = ['v']; + public readonly arguments: string[] = []; + public readonly options: Option[] = []; - availableOptions: [], - - run: function (_options: any) { + public run(_options: any) { let versions: { [name: string]: string } = {}; let angular: { [name: string]: string } = {}; let angularCoreVersion = ''; @@ -74,7 +73,7 @@ const VersionCommand = Command.extend({ |___/ `; - this.ui.writeLine(stripIndents` + this.logger.info(stripIndents` ${chalk.red(asciiArt)} Angular CLI: ${ngCliVersion} Node: ${process.versions.node} @@ -97,9 +96,9 @@ const VersionCommand = Command.extend({ ${Object.keys(angular).map(module => module + ': ' + angular[module]).sort().join('\n')} ${Object.keys(versions).map(module => module + ': ' + versions[module]).sort().join('\n')} `); - }, + } - getDependencyVersions: function(pkg: any, prefix: string): { [name: string]: string } { + private getDependencyVersions(pkg: any, prefix: string): { [name: string]: string } { const modules: any = {}; const deps = Object.keys(pkg['dependencies'] || {}) .concat(Object.keys(pkg['devDependencies'] || {})) @@ -118,9 +117,9 @@ const VersionCommand = Command.extend({ deps.forEach(name => modules[name] = this.getVersion(name)); return modules; - }, + } - getVersion: function(moduleName: string): string { + private getVersion(moduleName: string): string { try { const modulePkg = require(path.resolve( this.project.root, @@ -132,8 +131,4 @@ const VersionCommand = Command.extend({ return 'error'; } } -}); - - -VersionCommand.overrideCore = true; -export default VersionCommand; +} diff --git a/packages/@angular/cli/commands/xi18n.ts b/packages/@angular/cli/commands/xi18n.ts index 1b767281740f..45a316d8b7e7 100644 --- a/packages/@angular/cli/commands/xi18n.ts +++ b/packages/@angular/cli/commands/xi18n.ts @@ -1,4 +1,4 @@ -const Command = require('../ember-cli/lib/models/command'); +import { Command, CommandScope } from '../models/command'; export interface Xi18nOptions { outputPath?: string; @@ -8,22 +8,26 @@ export interface Xi18nOptions { outFile?: string; } -const Xi18nCommand = Command.extend({ - name: 'xi18n', - description: 'Extracts i18n messages from source code.', - works: 'insideProject', - availableOptions: [ +export default class Xi18nCommand extends Command { + public readonly name = 'xi18n'; + public readonly description = 'Extracts i18n messages from source code.'; + public static aliases: string[] = []; + public readonly scope = CommandScope.inProject; + public readonly arguments: string[] = []; + public readonly options = [ { name: 'i18n-format', type: String, default: 'xlf', - aliases: ['f', {'xmb': 'xmb'}, {'xlf': 'xlf'}, {'xliff': 'xlf'}, {'xliff2': 'xliff2'} ], + // TODO: re-add options for removed aliases: + // aliases: ['f', {'xmb': 'xmb'}, {'xlf': 'xlf'}, {'xliff': 'xlf'}, {'xliff2': 'xliff2'} ], + aliases: ['f'], description: 'Output format for the generated file.' }, { name: 'output-path', type: 'Path', - default: null, + default: null as string, aliases: ['op'], description: 'Path where output will be placed.' }, @@ -57,8 +61,9 @@ const Xi18nCommand = Command.extend({ aliases: ['of'], description: 'Name of the file to output.' }, - ], - run: function (commandOptions: any) { + ]; + + public async run(options: any) { const {Extracti18nTask} = require('../tasks/extract-i18n'); const xi18nTask = new Extracti18nTask({ @@ -66,10 +71,6 @@ const Xi18nCommand = Command.extend({ project: this.project }); - return xi18nTask.run(commandOptions); + return await xi18nTask.run(options); } -}); - - -export default Xi18nCommand; - +} diff --git a/packages/@angular/cli/custom-typings.d.ts b/packages/@angular/cli/custom-typings.d.ts index 622526efebda..f53edef6a1b1 100644 --- a/packages/@angular/cli/custom-typings.d.ts +++ b/packages/@angular/cli/custom-typings.d.ts @@ -24,3 +24,5 @@ interface IWebpackDevServerConfigurationOptions { public?: string; disableHostCheck?: boolean; } + +declare module 'yargs-parser'; diff --git a/packages/@angular/cli/ember-cli/lib/cli/cli.js b/packages/@angular/cli/ember-cli/lib/cli/cli.js index b5fc4ebebaaa..c14a675c1418 100644 --- a/packages/@angular/cli/ember-cli/lib/cli/cli.js +++ b/packages/@angular/cli/ember-cli/lib/cli/cli.js @@ -1,7 +1,14 @@ 'use strict'; +const { logging, terminal } = require('@angular-devkit/core'); +const filter = require('rxjs/operators').filter; + +const yargsParser = require('yargs-parser'); const lookupCommand = require('./lookup-command'); const getOptionArgs = require('../utilities/get-option-args'); +const runCommand = require('../../../models/command-runner').runCommand; + +const DocCommand = require('../../../commands/doc').default; // Disabled until e2e and serve command can be evaluated/corrected -- require('../utilities/will-interrupt-process'); const onProcessInterrupt = { addHandler: (_handler) => { }, removeHandler: (_handler) => { } }; @@ -75,170 +82,64 @@ class CLI { * @param environment * @return {Promise} */ - run(environment) { - let shutdownOnExit = null; + async run(environment) { - return Promise.resolve().then(() => { - let args = environment.cliArgs.slice(); - - if (args.length === 0) { - args[0] = 'help'; - } + const logger = new logging.IndentLogger('cling'); + let loggingSubscription; + if (!this.testing) { + loggingSubscription = this.initializeLogging(logger); + } + const context = { + project: environment.project, + ui: this.ui, + }; - if (args[0] === '--help') { - if (args.length === 1) { - args[0] = 'help'; - } else { - args.shift(); - args.push('--help'); + try { + await runCommand(environment.commands, environment.cliArgs, logger, context); + return 0; + } catch (err) { + if (err) { + const msg = typeof err === 'string' ? err : err.message; + const stack = typeof err === 'string' ? undefined : err.stack; + logger.error(msg); + if (stack) { + logger.error(stack); } } - - let commandName = args.shift(); - let commandArgs = args; - let helpOptions; - - let CurrentCommand = lookupCommand(environment.commands, commandName, commandArgs, { - project: environment.project, - ui: this.ui, - }); - - let command = new CurrentCommand({ - ui: this.ui, - analytics: this.analytics, - commands: environment.commands, - tasks: environment.tasks, - project: environment.project, - settings: environment.settings, - testing: this.testing, - cli: this, - }); - - getOptionArgs('--verbose', commandArgs).forEach(arg => { - process.env[`EMBER_VERBOSE_${arg.toUpperCase()}`] = 'true'; - }); - - if (!this.testing) { - let skipInstallationCheck = commandArgs.indexOf('--skip-installation-check') !== -1; - if (environment.project.isEmberCLIProject() && !skipInstallationCheck) { - const InstallationChecker = require('../models/installation-checker'); - new InstallationChecker({ project: environment.project }).checkInstallations(); - } + if (this.testing) { + throw err; } + loggingSubscription.unsubscribe(); + return 1; + } + } - let instrumentation = this.instrumentation; - let onCommandInterrupt; - - let runPromise = Promise.resolve().then(() => { - instrumentation.stopAndReport('init'); - instrumentation.start('command'); - - onProcessInterrupt.addHandler(onCommandInterrupt); - - return command.beforeRun(commandArgs); - }).then(() => { - return command.validateAndRun(commandArgs); - }).then(result => { - instrumentation.stopAndReport('command', commandName, commandArgs); - - onProcessInterrupt.removeHandler(onCommandInterrupt); - - return result; - }).then(result => { - // if the help option was passed, call the help command - if (result === 'callHelp') { - helpOptions = { - environment, - commandName, - commandArgs, - }; - - return this.callHelp(helpOptions); + // Initialize logging. + initializeLogging(logger) { + return logger + .pipe(filter(entry => (entry.level != 'debug'))) + .subscribe(entry => { + let color = x => terminal.dim(terminal.white(x)); + let output = process.stdout; + switch (entry.level) { + case 'info': + color = terminal.white; + break; + case 'warn': + color = terminal.yellow; + break; + case 'error': + color = terminal.red; + output = process.stderr; + break; + case 'fatal': + color = (x) => terminal.bold(terminal.red(x)); + output = process.stderr; + break; } - return result; - }).then(exitCode => { - // TODO: fix this - // Possibly this issue: https://github.com/joyent/node/issues/8329 - // Wait to resolve promise when running on windows. - // This ensures that stdout is flushed so acceptance tests get full output - - return new Promise(resolve => { - if (process.platform === 'win32') { - setTimeout(resolve, 250, exitCode); - } else { - resolve(exitCode); - } - }); + output.write(color(entry.message) + '\n'); }); - - onCommandInterrupt = () => - Promise.resolve(command.onInterrupt()) - .then(() => runPromise); - - return runPromise; - }) - .catch(this.logError.bind(this)); - } - - /** - * @private - * @method callHelp - * @param options - * @return {Promise} - */ - callHelp(options) { - let environment = options.environment; - let commandName = options.commandName; - let commandArgs = options.commandArgs; - let helpIndex = commandArgs.indexOf('--help'); - let hIndex = commandArgs.indexOf('-h'); - - let HelpCommand = lookupCommand(environment.commands, 'help', commandArgs, { - project: environment.project, - ui: this.ui, - }); - - let help = new HelpCommand({ - ui: this.ui, - analytics: this.analytics, - commands: environment.commands, - tasks: environment.tasks, - project: environment.project, - settings: environment.settings, - testing: this.testing, - }); - - if (helpIndex > -1) { - commandArgs.splice(helpIndex, 1); - } - - if (hIndex > -1) { - commandArgs.splice(hIndex, 1); - } - - commandArgs.unshift(commandName); - - return help.validateAndRun(commandArgs); - } - - /** - * @private - * @method logError - * @param error - * @return {number} - */ - logError(error) { - if (this.testing && error) { - console.error(error.message); - if (error.stack) { - console.error(error.stack); - } - throw error; - } - this.ui.errorLog.push(error); - this.ui.writeError(error); - return 1; } } diff --git a/packages/@angular/cli/lib/cli/index.ts b/packages/@angular/cli/lib/cli/index.ts index f72ed9dc26d3..d0e9e229fda5 100644 --- a/packages/@angular/cli/lib/cli/index.ts +++ b/packages/@angular/cli/lib/cli/index.ts @@ -18,13 +18,11 @@ function loadCommands() { 'eject': require('../../commands/eject').default, 'new': require('../../commands/new').default, 'generate': require('../../commands/generate').default, - 'destroy': require('../../commands/destroy').default, 'test': require('../../commands/test').default, 'e2e': require('../../commands/e2e').default, 'help': require('../../commands/help').default, 'lint': require('../../commands/lint').default, 'version': require('../../commands/version').default, - 'completion': require('../../commands/completion').default, 'doc': require('../../commands/doc').default, 'xi18n': require('../../commands/xi18n').default, 'update': require('../../commands/update').default, diff --git a/packages/@angular/cli/models/command-runner.ts b/packages/@angular/cli/models/command-runner.ts new file mode 100644 index 000000000000..2ebaf7f0b07e --- /dev/null +++ b/packages/@angular/cli/models/command-runner.ts @@ -0,0 +1,210 @@ +import { + Option, + CommandContext, + Command, + CommandConstructor, + CommandScope +} from '../models/command'; +import { oneLine } from 'common-tags'; +import { Logger } from '@angular-devkit/core/src/logger'; +import { camelize } from '@angular-devkit/core/src/utils/strings'; + +const yargsParser = require('yargs-parser'); + +export interface CommandMap { + [key: string]: CommandConstructor; +} + +/** + * Run a command. + * @param commandMap Map of available commands. + * @param args Raw unparsed arguments. + * @param context Execution context. + */ +export async function runCommand(commandMap: CommandMap, + args: string[], + logger: Logger, + context: CommandContext): Promise { + + const rawOptions = yargsParser(args, { alias: { help: ['h'] }, boolean: [ 'help' ] }); + let commandName = rawOptions._[0]; + // remove the command name + rawOptions._ = rawOptions._.slice(1); + const executionScope = context.project.isEmberCLIProject() + ? CommandScope.inProject + : CommandScope.outsideProject; + + let Cmd: CommandConstructor; + Cmd = findCommand(commandMap, commandName); + + const versionAliases = ['-v', '--version']; + if (!Cmd && versionAliases.indexOf(commandName) !== -1) { + commandName = 'version'; + Cmd = findCommand(commandMap, commandName); + } + + if (!Cmd && rawOptions.help) { + commandName = 'help'; + Cmd = findCommand(commandMap, commandName); + } + + if (!Cmd) { + throw new Error(oneLine`The specified command (${commandName}) is invalid. + For available options, see \`ng help\`.`); + } + + const command = new Cmd(context, logger); + + let options = parseOptions(yargsParser, args, command.options); + options = mapArguments(options, command.arguments); + await command.initialize(options); + options = parseOptions(yargsParser, args, command.options); + options = mapArguments(options, command.arguments); + if (commandName === 'help') { + options.commandMap = commandMap; + } + + if (options.help) { + return await runHelp(command, options); + } else { + verifyCommandInScope(command, executionScope); + delete options.h; + delete options.help; + return await validateAndRunCommand(command, options); + } +} + +function parseOptions(parser: Function, + args: string[], + cmdOpts: Option[]): any { + const aliases = cmdOpts.concat() + .filter(o => o.aliases && o.aliases.length > 0) + .reduce((aliases: any, opt: Option) => { + aliases[opt.name] = opt.aliases + .filter(a => a.length === 1); + return aliases; + }, {}); + + const booleans = cmdOpts + .filter(o => o.type && o.type === Boolean) + .map(o => o.name); + + const defaults = cmdOpts + .filter(o => o.default !== undefined || booleans.indexOf(o.name) !== -1) + .reduce((defaults: any, opt: Option) => { + defaults[opt.name] = opt.default; + return defaults; + }, {}); + + aliases.help = ['h']; + booleans.push('help'); + + const yargsOptions = { + alias: aliases, + boolean: booleans, + default: defaults + }; + + const parsedOptions = parser(args, yargsOptions); + // remove the command name + parsedOptions._ = parsedOptions._.slice(1); + + // Remove aliases. + cmdOpts + .filter(o => o.aliases && o.aliases.length > 0) + .map(o => o.aliases) + .reduce((allAliases: any, aliases: string[]) => { + return allAliases.concat([...aliases]); + }, []) + .forEach((alias: string) => { + delete parsedOptions[alias]; + }); + + // Remove undefined booleans + booleans + .filter(b => parsedOptions[b] === undefined) + .map(b => camelize(b)) + .forEach(b => delete parsedOptions[b]); + + // remove options with dashes. + Object.keys(parsedOptions) + .filter(key => key.indexOf('-') !== -1) + .forEach(key => delete parsedOptions[key]); + + return parsedOptions; +} + +// Map arguments to options. +function mapArguments(options: any, args: string[]): any { + const optsWithMappedArgs = {...options}; + optsWithMappedArgs._.forEach((value: string, index: number) => { + // Remove the starting "<" and trailing ">". + const arg = args[index]; + if (arg) { + optsWithMappedArgs[arg] = value; + } + }); + + delete optsWithMappedArgs._; + + return optsWithMappedArgs; +} + +// Find a command. +function findCommand( + map: CommandMap, name: string): CommandConstructor | null { + let Cmd: CommandConstructor = map[name]; + + if (!Cmd) { + // find command via aliases + Cmd = Object.keys(map) + .filter(key => { + if (!map[key].aliases) { + return false; + } + const foundAlias = map[key].aliases + .filter((alias: string) => alias === name); + + return foundAlias.length > 0; + }) + .map((key) => { + return map[key]; + })[0]; + } + + if (!Cmd) { + return null; + } + return Cmd; +} + +function verifyCommandInScope(command: Command, scope = CommandScope.everywhere): void { + if (!command) { + return; + } + if (command.scope !== CommandScope.everywhere) { + if (command.scope !== scope) { + let errorMessage: string; + if (command.scope === CommandScope.inProject) { + errorMessage = `This command can only be run inside of a CLI project.`; + } else { + errorMessage = `This command can not be run inside of a CLI project.`; + } + throw new Error(errorMessage); + } + } +} + +// Execute a command's `printHelp`. +async function runHelp(command: Command, options: any): Promise { + return await command.printHelp(options); +} + +// Validate and run a command. +async function validateAndRunCommand(command: Command, options: any): Promise { + const isValid = await command.validate(options); + if (isValid !== undefined && !isValid) { + throw new Error(`Validation error. Invalid command`); + } + return await command.run(options); +} diff --git a/packages/@angular/cli/models/command.ts b/packages/@angular/cli/models/command.ts new file mode 100644 index 000000000000..b52a40e33b8c --- /dev/null +++ b/packages/@angular/cli/models/command.ts @@ -0,0 +1,95 @@ +import { Logger } from '@angular-devkit/core/src/logger'; +const { cyan } = require('chalk'); + +export interface CommandConstructor { + new(context: CommandContext, logger: Logger): Command; + aliases: string[]; + scope: CommandScope.everywhere; +} + +export enum CommandScope { + everywhere, + inProject, + outsideProject +} + +export abstract class Command { + constructor(context: CommandContext, logger: Logger) { + this.logger = logger; + if (context) { + this.project = context.project; + this.ui = context.ui; + } + } + + initialize(_options: any): Promise { + return Promise.resolve(); + } + + validate(_options: any): boolean | Promise { + return true; + } + + printHelp(_options: any): void { + this.printHelpUsage(this.name, this.arguments, this.options); + this.printHelpOptions(this.options); + } + + protected printHelpUsage(name: string, args: string[], options: Option[]) { + const argDisplay = args && args.length > 0 + ? ' ' + args.map(a => `<${a}>`).join(' ') + : ''; + const optionsDisplay = options && options.length > 0 + ? ` [options]` + : ``; + this.logger.info(`usage: ng ${name}${argDisplay}${optionsDisplay}`); + } + + protected printHelpOptions(options: Option[]) { + if (options && this.options.length > 0) { + this.logger.info(`options:`); + this.options + .filter(o => !o.hidden) + .forEach(o => { + const aliases = o.aliases && o.aliases.length > 0 + ? '(' + o.aliases.map(a => `-${a}`).join(' ') + ')' + : ''; + this.logger.info(` ${cyan(o.name)} ${aliases}`); + this.logger.info(` ${o.description}`); + }); + } + } + + abstract run(options: any): any | Promise; + abstract readonly name: string; + abstract readonly description: string; + abstract readonly arguments: string[]; + abstract readonly options: Option[]; + public hidden = false; + public unknown = false; + public scope = CommandScope.everywhere; + protected readonly logger: Logger; + protected readonly project: any; + protected readonly ui: Ui; +} + +export interface CommandContext { + ui: Ui; + project: any; +} + +export interface Ui { + writeLine: (message: string) => void; + errorLog: (message: string) => void; +} + +export abstract class Option { + abstract readonly name: string; + abstract readonly description: string; + readonly default?: string | number | boolean; + readonly required?: boolean; + abstract readonly aliases?: string[]; // (string | { [key: string]: string })[]; + abstract readonly type: any; + readonly values?: any[]; + readonly hidden?: boolean = false; +} diff --git a/packages/@angular/cli/package.json b/packages/@angular/cli/package.json index daf6b9dbb23c..dbb5d7e6d43e 100644 --- a/packages/@angular/cli/package.json +++ b/packages/@angular/cli/package.json @@ -84,7 +84,8 @@ "webpack-dev-server": "^3.0.1-beta.0", "webpack-merge": "^4.1.2", "webpack-sources": "^1.1.0", - "webpack-subresource-integrity": "^1.0.1" + "webpack-subresource-integrity": "^1.0.1", + "yargs-parser": "^9.0.2" }, "optionalDependencies": { "node-sass": "^4.7.2" diff --git a/packages/@angular/cli/tasks/init.ts b/packages/@angular/cli/tasks/init.ts index ce217c7ef8d1..03a94caf9a4e 100644 --- a/packages/@angular/cli/tasks/init.ts +++ b/packages/@angular/cli/tasks/init.ts @@ -52,7 +52,7 @@ export default Task.extend({ commandOptions.version = packageJson.version; } - if (!commandOptions.skipCommit) { + if (!commandOptions.skipGit) { const commitMessage = fs.readFileSync( path.join(__dirname, '../utilities/INITIAL_COMMIT_MESSAGE.txt'), 'utf-8', diff --git a/packages/@angular/cli/tasks/schematic-run.ts b/packages/@angular/cli/tasks/schematic-run.ts index 5283ee30d294..9759ef427bd0 100644 --- a/packages/@angular/cli/tasks/schematic-run.ts +++ b/packages/@angular/cli/tasks/schematic-run.ts @@ -124,71 +124,64 @@ export default Task.extend({ } }); - return new Promise((resolve, reject) => { - schematic.call(opts, host).pipe( - map((tree: Tree) => Tree.optimize(tree)), - concatMap((tree: Tree) => { - return dryRunSink.commit(tree).pipe( - ignoreElements(), - concat(observableOf(tree))); - }), - concatMap((tree: Tree) => { - if (!error) { - // Output the logging queue. - loggingQueue.forEach(log => ui.writeLine(` ${log.color(log.keyword)} ${log.message}`)); - } - - if (opts.dryRun || error) { - return observableOf(tree); - } - return fsSink.commit(tree).pipe( - ignoreElements(), - concat(observableOf(tree))); - }), - concatMap(() => { - if (!opts.dryRun) { - return getEngine().executePostTasks(); - } else { - return []; - } - })) - .subscribe({ - error(err) { - ui.writeLine(red(`Error: ${err.message}`)); - reject(err.message); - }, - complete() { - if (opts.dryRun) { - ui.writeLine(yellow(`\nNOTE: Run with "dry run" no changes were made.`)); - } - resolve({modifiedFiles}); - } - }); - }) - .then((output: SchematicOutput) => { - const modifiedFiles = output.modifiedFiles; - const lintFix = taskOptions.lintFix !== undefined ? - taskOptions.lintFix : CliConfig.getValue('defaults.lintFix'); - - if (lintFix && modifiedFiles) { - const LintTask = require('./lint').default; - const lintTask = new LintTask({ - ui: this.ui, - project: this.project - }); - - return lintTask.run({ - fix: true, - force: true, - silent: true, - configs: [{ - files: modifiedFiles - .filter((file: string) => /.ts$/.test(file)) - .map((file: string) => path.join(projectRoot, file)) - }] - }); - } - }); + return schematic.call(opts, host).pipe( + map((tree: Tree) => Tree.optimize(tree)), + concatMap((tree: Tree) => { + return dryRunSink.commit(tree).pipe( + ignoreElements(), + concat(observableOf(tree))); + }), + concatMap((tree: Tree) => { + if (!error) { + // Output the logging queue. + loggingQueue.forEach(log => ui.writeLine(` ${log.color(log.keyword)} ${log.message}`)); + } + + if (opts.dryRun || error) { + return observableOf(tree); + } + return fsSink.commit(tree).pipe( + ignoreElements(), + concat(observableOf(tree))); + }), + concatMap(() => { + if (!opts.dryRun) { + return getEngine().executePostTasks(); + } else { + return []; + } + })) + .toPromise() + .then(() => { + if (opts.dryRun) { + ui.writeLine(yellow(`\nNOTE: Run with "dry run" no changes were made.`)); + } + return {modifiedFiles}; + }) + .then((output: SchematicOutput) => { + const modifiedFiles = output.modifiedFiles; + const lintFix = taskOptions.lintFix !== undefined ? + taskOptions.lintFix : CliConfig.getValue('defaults.lintFix'); + + if (lintFix && modifiedFiles) { + const LintTask = require('./lint').default; + const lintTask = new LintTask({ + ui: this.ui, + project: this.project + }); + + return lintTask.run({ + fix: true, + force: true, + silent: true, + configs: [{ + files: modifiedFiles + .filter((file: string) => /.ts$/.test(file)) + .map((file: string) => path.join(projectRoot, file)) + }] + }); + } + }); } }); @@ -199,7 +192,8 @@ function prepOptions(schematic: Schematic<{}, {}>, options: SchematicOptions): S const keys = Object.keys(properties); if (['component', 'c', 'directive', 'd'].indexOf(schematic.description.name) !== -1) { - options.prefix = (options.prefix === 'false' || options.prefix === '') + options.prefix = + (options.prefix === 'false' || options.prefix === false || options.prefix === '') ? undefined : options.prefix; } @@ -217,7 +211,10 @@ function prepOptions(schematic: Schematic<{}, {}>, options: SchematicOptions): S function readDefaults(schematicName: string, optionKeys: string[], options: any): any { return optionKeys.reduce((acc: any, key) => { - acc[key] = options[key] !== undefined ? options[key] : readDefault(schematicName, key); + const value = options[key] !== undefined ? options[key] : readDefault(schematicName, key); + if (value !== undefined) { + acc[key] = value; + } return acc; }, {}); } diff --git a/packages/@angular/cli/tasks/test.ts b/packages/@angular/cli/tasks/test.ts index 42d647d78d6d..c38c75eaa5ca 100644 --- a/packages/@angular/cli/tasks/test.ts +++ b/packages/@angular/cli/tasks/test.ts @@ -25,7 +25,7 @@ export default Task.extend({ throw new SilentError(`An app without 'main' cannot use the test command.`); } - return new Promise((resolve) => { + return new Promise((resolve, reject) => { const karma = requireProjectModule(projectRoot, 'karma'); const karmaConfig = path.join(projectRoot, options.config || CliConfig.getValue('test.karma.config')); @@ -51,7 +51,13 @@ export default Task.extend({ karmaOptions.configFile = karmaConfig; // :shipit: - const karmaServer = new karma.Server(karmaOptions, resolve); + const karmaServer = new karma.Server(karmaOptions, function(exitCode: number) { + if (exitCode === 0) { + resolve(); + } else { + reject(null); + } + }); karmaServer.start(); }); } diff --git a/tests/acceptance/destroy.spec.ts b/tests/acceptance/destroy.spec.ts deleted file mode 100644 index 796b16c464ae..000000000000 --- a/tests/acceptance/destroy.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ng, setupProject } from '../helpers'; - -describe('Acceptance: ng destroy', () => { - setupProject(); - - it('without args should fail', (done) => { - return ng(['destroy']) - .then(() => done.fail()) - .catch(error => { - expect(error.message).toBe('The destroy command is not supported by Angular CLI.'); - done(); - }); - }); - - it('with args should fail', (done) => { - return ng(['destroy', 'something']) - .then(() => done.fail()) - .catch(error => { - expect(error.message).toBe('The destroy command is not supported by Angular CLI.'); - done(); - }); - }); -}); diff --git a/tests/acceptance/generate-class.spec.ts b/tests/acceptance/generate-class.spec.ts deleted file mode 100644 index 42ede4362666..000000000000 --- a/tests/acceptance/generate-class.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - -const testPath = path.join(root, 'tmp', 'foo', 'src', 'app'); - -describe('Acceptance: ng generate class', () => { - setupProject(); - - it('ng generate class my-class', (done) => { - return ng(['generate', 'class', 'my-class']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-class.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-class.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate class my-class --no-spec', (done) => { - return ng(['generate', 'class', 'my-class', '--no-spec']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-class.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-class.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate class my-class.model', (done) => { - return ng(['generate', 'class', 'my-class.model']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-class.model.ts'))).toBe(true); - }) - .then(done, done.fail); - }); -}); diff --git a/tests/acceptance/generate-component.spec.ts b/tests/acceptance/generate-component.spec.ts deleted file mode 100644 index 3d37213c78d6..000000000000 --- a/tests/acceptance/generate-component.spec.ts +++ /dev/null @@ -1,366 +0,0 @@ -// tslint:disable:max-line-length -import { mkdirsSync, pathExistsSync, readFile, readFileSync } from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - - -describe('Acceptance: ng generate component', () => { - setupProject(); - - it('my-comp', (done) => { - const testPath = path.join(root, 'tmp/foo/src/app/my-comp/my-comp.component.ts'); - const appModule = path.join(root, 'tmp/foo/src/app/app.module.ts'); - return ng(['generate', 'component', 'my-comp']) - .then(() => expect(pathExistsSync(testPath)).toBe(true)) - .then(() => readFile(appModule, 'utf-8')) - .then(content => { - // Expect that the app.module contains a reference to my-comp and its import. - expect(content).toMatch(/import.*MyCompComponent.*from '.\/my-comp\/my-comp.component';/); - expect(content).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+MyCompComponent\r?\n/m); - }) - .then(done, done.fail); - }); - - it('generating my-comp twice does not add two declarations to module', (done) => { - const appModule = path.join(root, 'tmp/foo/src/app/app.module.ts'); - return ng(['generate', 'component', 'my-comp']) - .then(() => ng(['generate', 'component', 'my-comp'])) - .then(() => readFile(appModule, 'utf-8')) - .then(content => { - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+MyCompComponent\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('test' + path.sep + 'my-comp', (done) => { - mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test')); - return ng(['generate', 'component', 'test' + path.sep + 'my-comp']).then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('test' + path.sep + '..' + path.sep + 'my-comp', (done) => { - return ng(['generate', 'component', 'test' + path.sep + '..' + path.sep + 'my-comp']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('my-comp from a child dir', (done) => { - mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - return ng(['generate', 'component', 'my-comp']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('mycomp should use styleExt from the angular-cli.json for style file', (done) => { - return ng(['generate', 'component', 'mycomp']).then(() => { - const cliJson = JSON.parse(readFileSync('.angular-cli.json', 'utf8')); - const styleExt = cliJson.defaults.styleExt; - expect(styleExt).toBe('css'); - const scssFilePath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', `mycomp.component.${styleExt}`); - const compPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', 'mycomp.component.ts'); - expect(pathExistsSync(scssFilePath)).toBe(true); - const contents = readFileSync(compPath, 'utf8'); - expect(contents.indexOf(`styleUrls: [\'./mycomp.component.${styleExt}\']`) === -1).toBe(false); - }) - .then(done, done.fail); - }); - - it('child-dir' + path.sep + 'my-comp from a child dir', (done) => { - mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - return ng(['generate', 'component', 'child-dir' + path.sep + 'my-comp']); - }) - .then(() => { - const testPath = path.join( - root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('child-dir' + path.sep + '..' + path.sep + 'my-comp from a child dir', (done) => { - mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return Promise.resolve() - .then(() => process.chdir(path.normalize('./src/app/1'))) - .then(() => { - return ng([ - 'generate', 'component', 'child-dir' + path.sep + '..' + path.sep + 'my-comp' - ]); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it(path.sep + 'my-comp from a child dir, gens under ' + path.join('src', 'app'), (done) => { - mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return Promise.resolve() - .then(() => process.chdir(path.normalize('./src/app/1'))) - .then(() => ng(['generate', 'component', path.sep + 'my-comp'])) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('..' + path.sep + 'my-comp from root dir will fail', (done) => { - return ng(['generate', 'component', '..' + path.sep + 'my-comp']) - .then(() => done.fail()) - .catch(err => { - expect(err).toBe(`Invalid path: "..${path.sep}my-comp" cannot be above the "src${path.sep}app" directory`); - }) - .then(done, done.fail); - }); - - it('mycomp will prefix selector', (done) => { - return ng(['generate', 'component', 'mycomp']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', 'mycomp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - const contents = readFileSync(testPath, 'utf8'); - expect(contents.indexOf('selector: \'app-mycomp\'') === -1).toBe(false); - }) - .then(done, done.fail); - }); - - it('mycomp --no-prefix will not prefix selector', (done) => { - return ng(['generate', 'component', 'mycomp', '--no-prefix']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', 'mycomp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - const contents = readFileSync(testPath, 'utf8'); - expect(contents.indexOf('selector: \'mycomp\'') === -1).toBe(false); - }) - .then(done, done.fail); - }); - - it('mycomp --prefix= will not prefix selector', (done) => { - return ng(['generate', 'component', 'mycomp', '--prefix=']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', 'mycomp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - const contents = readFileSync(testPath, 'utf8'); - expect(contents.indexOf('selector: \'mycomp\'') === -1).toBe(false); - }) - .then(done, done.fail); - }); - - it('mycomp --prefix=test will prefix selector with \'test-\'', (done) => { - return ng(['generate', 'component', 'mycomp', '--prefix=test']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'mycomp', 'mycomp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - const contents = readFileSync(testPath, 'utf8'); - expect(contents.indexOf('selector: \'test-mycomp\'') === -1).toBe(false); - }) - .then(done, done.fail); - }); - - it('myComp will succeed', (done) => { - return ng(['generate', 'component', 'myComp']) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.ts'); - expect(pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it(`non${path.sep}existing${path.sep}dir${path.sep}myComp will create dir and succeed`, (done) => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'non', 'existing', 'dir', 'my-comp', 'my-comp.component.ts'); - const appModule = path.join(root, 'tmp', 'foo', 'src', 'app', 'app.module.ts'); - return ng(['generate', 'component', `non${path.sep}existing${path.sep}dir${path.sep}myComp`]) - .then(() => expect(pathExistsSync(testPath)).toBe(true)) - .then(() => readFile(appModule, 'utf-8')) - .then(content => { - // Expect that the app.module contains a reference to my-comp and its import. - expect(content) - .toMatch(/import.*MyCompComponent.*from '.\/non\/existing\/dir\/my-comp\/my-comp.component';/); - }) - .then(done, done.fail); - }); - - it('my-comp --inline-template', (done) => { - return ng(['generate', 'component', 'my-comp', '--inline-template']).then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.html'); - expect(pathExistsSync(testPath)).toBe(false); - }) - .then(done, done.fail); - }); - - it('my-comp --inline-style', (done) => { - return ng(['generate', 'component', 'my-comp', '--inline-style']).then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.css'); - expect(pathExistsSync(testPath)).toBe(false); - }) - .then(done, done.fail); - }); - - it('my-comp --no-spec', (done) => { - return ng(['generate', 'component', 'my-comp', '--no-spec']).then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-comp', 'my-comp.component.spec.ts'); - expect(pathExistsSync(testPath)).toBe(false); - }) - .then(done, done.fail); - }); - - it('should error out when given an incorrect module path', (done) => { - return Promise.resolve() - .then(() => ng(['generate', 'component', 'baz', '--module', 'foo'])) - .catch((error) => { - expect(error).toBe('Specified module does not exist'); - }) - .then(done, done.fail); - }); - - describe('should import and add to declaration list', () => { - it('when given a root level module with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'component', 'baz', '--module', 'app.module.ts'])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '.\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazComponent\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a root level module with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'component', 'baz', '--module', 'app'])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '.\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazComponent\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'foo.module.ts')])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '..\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[BazComponent]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'foo')])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '..\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[BazComponent]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule folder', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'component', 'baz', '--module', 'foo'])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '..\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[BazComponent]/m); - }) - .then(done, done.fail); - }); - - it('when given deep submodule folder with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'module', path.join('foo', 'bar')])) - .then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'bar')])) - .then(() => readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazComponent.*from '..\/..\/baz\/baz.component';/); - expect(content).toMatch(/declarations:\s+\[BazComponent]/m); - }) - .then(done, done.fail); - }); - - describe('should generate components in apps with empty appRoot', () => { - it('should work', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - mkdirsSync(path.join(appRoot, 'other', 'src')); - - return ng(['generate', 'module', 'm', '--app', 'other']).then(() => { - const expectedModule = path.join(appRoot, 'other', 'src', 'm', 'm.module.ts'); - expect(pathExistsSync(expectedModule)).toBe(true); - - return ng(['generate', 'component', 'm/c', '--app', 'other', '--module', 'm']).then(() => { - expect(pathExistsSync(path.join(appRoot, 'other', 'src', 'm', 'c', 'c.component.ts'))).toBe(true); - expect(readFileSync(expectedModule, 'utf-8')).toContain(`import { CComponent } from './c/c.component'`); - }); - }).then(done, done.fail); - }); - }); - }); -}); diff --git a/tests/acceptance/generate-directive.spec.ts b/tests/acceptance/generate-directive.spec.ts deleted file mode 100644 index 7e77386264c6..000000000000 --- a/tests/acceptance/generate-directive.spec.ts +++ /dev/null @@ -1,275 +0,0 @@ -// tslint:disable:max-line-length -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - -describe('Acceptance: ng generate directive', () => { - setupProject(); - - it('flat', (done) => { - return ng(['generate', 'directive', 'flat']) - .then(() => { - const testPath = path.join(root, 'tmp/foo/src/app/flat.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('my-dir --flat false', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-dir/my-dir.directive.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-dir/my-dir.directive.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return ng(['generate', 'directive', 'my-dir', '--flat', 'false']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(true); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*\bMyDirDirective\b.*from '.\/my-dir\/my-dir.directive';/); - expect(content).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+MyDirDirective\r?\n/m); - }) - .then(done, done.fail); - }); - - it('my-dir --flat false --no-spec', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-dir/my-dir.directive.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-dir/my-dir.directive.spec.ts'); - - return ng(['generate', 'directive', 'my-dir', '--flat', 'false', '--no-spec']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(false); - }) - .then(done, done.fail); - }); - - it('test' + path.sep + 'my-dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test')); - return ng(['generate', 'directive', 'test' + path.sep + 'my-dir', '--flat', 'false']) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('test' + path.sep + '..' + path.sep + 'my-dir', (done) => { - return ng(['generate', 'directive', 'test' + path.sep + '..' + path.sep + 'my-dir', '--flat', 'false']) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('my-dir from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'directive', 'my-dir', '--flat', 'false']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('child-dir' + path.sep + 'my-dir from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'directive', 'child-dir' + path.sep + 'my-dir', '--flat', 'false']); - }) - .then(() => { - const testPath = path.join( - root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('child-dir' + path.sep + '..' + path.sep + 'my-dir from a child dir', - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'directive', 'child-dir' + path.sep + '..' + path.sep + 'my-dir', '--flat', 'false']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it(path.sep + 'my-dir from a child dir, gens under ' + - path.join('src', 'app'), - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'directive', path.sep + 'my-dir', '--flat', 'false']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', 'my-dir', 'my-dir.directive.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('..' + path.sep + 'my-dir from root dir will fail', (done) => { - return ng(['generate', 'directive', '..' + path.sep + 'my-dir']) - .then(() => done.fail()) - .catch((err) => { - expect(err).toBe(`Invalid path: "..${path.sep}my-dir" cannot be above the "src${path.sep}app" directory`); - }) - .then(done, done.fail); - }); - - it('converts dash-cased-name to a camelCasedSelector', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const directivePath = path.join(appRoot, 'src/app/my-dir.directive.ts'); - return ng(['generate', 'directive', 'my-dir']) - .then(() => fs.readFile(directivePath, 'utf-8')) - .then(content => { - // expect(content).toMatch(/selector: [app-my-dir]/m); - expect(content).toMatch(/selector: '\[appMyDir\]'/); - }) - .then(done, done.fail); - }); - - it('should error out when given an incorrect module path', (done) => { - return Promise.resolve() - .then(() => ng(['generate', 'directive', 'baz', '--module', 'foo'])) - .then(() => done.fail()) - .catch((error) => { - expect(error).toBe('Specified module does not exist'); - }) - .then(done, done.fail); - }); - - describe('should import and add to declaration list', () => { - it('when given a root level module with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'directive', 'baz', '--module', 'app.module.ts'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '.\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazDirective\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a root level module with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'directive', 'baz', '--module', 'app'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '.\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazDirective\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'directive', 'baz', '--module', path.join('foo', 'foo.module.ts')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '..\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[BazDirective]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'directive', 'baz', '--module', path.join('foo', 'foo')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '..\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[BazDirective]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule folder', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'directive', 'baz', '--module', 'foo'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '..\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[BazDirective]/m); - }) - .then(done, done.fail); - }); - - it('when given deep submodule folder with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'module', path.join('foo', 'bar')])) - .then(() => ng(['generate', 'directive', 'baz', '--module', path.join('foo', 'bar')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazDirective.*from '..\/..\/baz.directive';/); - expect(content).toMatch(/declarations:\s+\[BazDirective]/m); - }) - .then(done, done.fail); - }); - }); -}); diff --git a/tests/acceptance/generate-guard.spec.ts b/tests/acceptance/generate-guard.spec.ts deleted file mode 100644 index b023196e2693..000000000000 --- a/tests/acceptance/generate-guard.spec.ts +++ /dev/null @@ -1,257 +0,0 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - -describe('Acceptance: ng generate guard', function () { - setupProject(); - - it('ng generate guard my-guard', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-guard.guard.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-guard.guard.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return ng(['generate', 'guard', 'my-guard']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(true); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then((content: string) => { - expect(content).not.toMatch(/import.*MyGuardGuard.*from '.\/my-guard.guard';/); - expect(content).not.toMatch(/providers:\s*\[MyGuardGuard\]/m); - }) - .then(done, done.fail); - }); - - it('ng generate guard my-guard --no-spec', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-guard.guard.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-guard.guard.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return ng(['generate', 'guard', 'my-guard', '--no-spec']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(false); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then((content: string) => { - expect(content).not.toMatch(/import.*MyGuardGuard.*from '.\/my-guard.guard';/); - expect(content).not.toMatch(/providers:\s*\[MyGuardGuard\]/m); - }) - .then(done, done.fail); - }); - - it('ng generate guard test' + path.sep + 'my-guard', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test')); - return ng(['generate', 'guard', 'test' + path.sep + 'my-guard']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard test' + path.sep + '..' + path.sep + 'my-guard', (done) => { - return ng(['generate', 'guard', 'test' + path.sep + '..' + path.sep + 'my-guard']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard my-guard from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'guard', 'my-guard']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard child-dir' + path.sep + 'my-guard from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'guard', 'child-dir' + path.sep + 'my-guard']); - }) - .then(() => { - const testPath = path.join( - root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard child-dir' + path.sep + '..' + path.sep + 'my-guard from a child dir', - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng( - ['generate', 'guard', 'child-dir' + path.sep + '..' + path.sep + 'my-guard']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard ' + path.sep + 'my-guard from a child dir, gens under ' + - path.join('src', 'app'), - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'guard', path.sep + 'my-guard']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-guard.guard.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate guard ..' + path.sep + 'my-guard from root dir will fail', (done) => { - return ng(['generate', 'guard', '..' + path.sep + 'my-guard']) - .then(() => done.fail()) - .catch(err => { - // tslint:disable-next-line:max-line-length - expect(err).toBe(`Invalid path: "..${path.sep}my-guard" cannot be above the "src${path.sep}app" directory`); - }) - .then(done, done.fail); - }); - - it('should error out when given an incorrect module path', (done) => { - return Promise.resolve() - .then(() => ng(['generate', 'guard', 'baz', '--module', 'foo'])) - .then(() => done.fail()) - .catch((error) => { - expect(error).toBe('Specified module does not exist'); - }) - .then(done, done.fail); - }); - - describe('should import and add to provider list', () => { - it('when given a root level module with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'guard', 'baz', '--module', 'app.module.ts'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '.\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - - it('when given a root level module with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'guard', 'baz', '--module', 'app'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '.\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'guard', 'baz', '--module', path.join('foo', 'foo.module.ts')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '..\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'guard', 'baz', '--module', path.join('foo', 'foo')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '..\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule folder', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'guard', 'baz', '--module', 'foo'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '..\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - - it('when given deep submodule folder with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'module', path.join('foo', 'bar')])) - .then(() => ng(['generate', 'guard', 'baz', '--module', path.join('foo', 'bar')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazGuard.*from '..\/..\/baz.guard';/); - expect(content).toMatch(/providers:\s*\[BazGuard\]/m); - }) - .then(done, done.fail); - }); - }); -}); diff --git a/tests/acceptance/generate-module.spec.ts b/tests/acceptance/generate-module.spec.ts deleted file mode 100644 index ed7cd3e409d6..000000000000 --- a/tests/acceptance/generate-module.spec.ts +++ /dev/null @@ -1,104 +0,0 @@ -// tslint:disable:max-line-length -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - -const testPath = path.join(root, 'tmp', 'foo', 'src', 'app'); - -describe('Acceptance: ng generate module', () => { - setupProject(); - - it('will fail if no name is specified', (done) => { - return ng(['generate', 'module']).catch((error: string) => { - expect(error).toBe('The `ng generate module` command requires a name to be specified.'); - }) - .then(done, done.fail); - }); - - it('ng generate module my-module', (done) => { - return ng(['generate', 'module', 'my-module']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate module generate routing file when passed flag --routing', (done) => { - return ng(['generate', 'module', 'my-module', '--routing']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module-routing.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate module my-module --spec', (done) => { - return ng(['generate', 'module', 'my-module', '--spec']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'my-module', 'my-module.module.spec.ts'))).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate module TwoWord', (done) => { - return ng(['generate', 'module', 'TwoWord']).then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'two-word', 'two-word.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'two-word', 'two-word.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate module parent/child', (done) => { - return ng(['generate', 'module', 'parent']) - .then(() => ng(['generate', 'module', 'parent/child'])) - .then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'parent/child', 'child.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'parent/child', 'child.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate module child should work in sub-dir', (done) => { - fs.mkdirSync(path.join(testPath, './sub-dir')); - return new Promise(resolve => { - process.chdir(path.join(testPath, './sub-dir')); - return resolve(); - }) - .then(() => ng(['generate', 'module', 'child'])) - .then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'sub-dir/child', 'child.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'sub-dir/child', 'child.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - // tslint:disable-next-line:max-line-length - it('ng generate module child should work in sub-dir with routing file when passed --routing flag', (done) => { - fs.mkdirSync(path.join(testPath, './sub-dir')); - return new Promise(resolve => { - process.chdir(path.join(testPath, './sub-dir')); - return resolve(); - }) - .then(() => ng(['generate', 'module', 'child', '--routing'])) - .then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'sub-dir/child', 'child.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'sub-dir/child', 'child-routing.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'sub-dir/child', 'child.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); - - // tslint:disable-next-line:max-line-length - it('ng generate module should generate parent/child module with routing file when passed --routing flag', (done) => { - return ng(['generate', 'module', 'parent']) - .then(() => ng(['generate', 'module', 'parent/child', '--routing'])) - .then(() => { - expect(fs.pathExistsSync(path.join(testPath, 'parent/child', 'child.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'parent/child', 'child-routing.module.ts'))).toBe(true); - expect(fs.pathExistsSync(path.join(testPath, 'parent/child', 'child.module.spec.ts'))).toBe(false); - }) - .then(done, done.fail); - }); -}); diff --git a/tests/acceptance/generate-pipe.spec.ts b/tests/acceptance/generate-pipe.spec.ts deleted file mode 100644 index ec0512b87b5e..000000000000 --- a/tests/acceptance/generate-pipe.spec.ts +++ /dev/null @@ -1,251 +0,0 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - - -describe('Acceptance: ng generate pipe', () => { - setupProject(); - - it('ng generate pipe my-pipe', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-pipe.pipe.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-pipe.pipe.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - return ng(['generate', 'pipe', 'my-pipe']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(true); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then((content: string) => { - expect(content).toMatch(/import.*\bMyPipePipe\b.*from '.\/my-pipe.pipe';/); - expect(content).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+MyPipePipe\r?\n/m); - }) - .then(done, done.fail); - }); - - it('ng generate pipe my-pipe --no-spec', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-pipe.pipe.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-pipe.pipe.spec.ts'); - - return ng(['generate', 'pipe', 'my-pipe', '--no-spec']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng generate pipe test' + path.sep + 'my-pipe', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test')); - return ng(['generate', 'pipe', 'test' + path.sep + 'my-pipe']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate pipe test' + path.sep + '..' + path.sep + 'my-pipe', (done) => { - return ng(['generate', 'pipe', 'test' + path.sep + '..' + path.sep + 'my-pipe']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate pipe my-pipe from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'pipe', 'my-pipe']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate pipe child-dir' + path.sep + 'my-pipe from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'pipe', 'child-dir' + path.sep + 'my-pipe']); - }) - .then(() => { - const testPath = path.join( - root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - // tslint:disable-next-line:max-line-length - it('ng generate pipe child-dir' + path.sep + '..' + path.sep + 'my-pipe from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'pipe', 'child-dir' + path.sep + '..' + path.sep + 'my-pipe']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate pipe ' + path.sep + 'my-pipe from a child dir, gens under ' + - path.join('src', 'app'), - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'pipe', path.sep + 'my-pipe']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-pipe.pipe.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate pipe ..' + path.sep + 'my-pipe from root dir will fail', (done) => { - return ng(['generate', 'pipe', '..' + path.sep + 'my-pipe']) - .then(() => done.fail()) - .catch(err => { - // tslint:disable-next-line:max-line-length - expect(err).toBe(`Invalid path: "..${path.sep}my-pipe" cannot be above the "src${path.sep}app" directory`); - done(); - }); - }); - - it('should error out when given an incorrect module path', (done) => { - return Promise.resolve() - .then(() => ng(['generate', 'pipe', 'baz', '--module', 'foo'])) - .then(() => done.fail()) - .catch((error) => { - expect(error).toBe('Specified module does not exist'); - done(); - }); - }); - - describe('should import and add to declaration list', () => { - it('when given a root level module with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'pipe', 'baz', '--module', 'app.module.ts'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '.\/baz.pipe';/); - // tslint:disable-next-line:max-line-length - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazPipe\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a root level module with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'pipe', 'baz', '--module', 'app'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '.\/baz.pipe';/); - // tslint:disable-next-line:max-line-length - expect(content).toMatch(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazPipe\r?\n\s+\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'pipe', 'baz', '--module', path.join('foo', 'foo.module.ts')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '..\/baz.pipe';/); - expect(content).toMatch(/declarations:\s+\[BazPipe]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'pipe', 'baz', '--module', path.join('foo', 'foo')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '..\/baz.pipe';/); - expect(content).toMatch(/declarations:\s+\[BazPipe]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule folder', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'pipe', 'baz', '--module', 'foo'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '..\/baz.pipe';/); - expect(content).toMatch(/declarations:\s+\[BazPipe]/m); - }) - .then(done, done.fail); - }); - - it('when given deep submodule folder with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'module', path.join('foo', 'bar')])) - .then(() => ng(['generate', 'pipe', 'baz', '--module', path.join('foo', 'bar')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazPipe.*from '..\/..\/baz.pipe';/); - expect(content).toMatch(/declarations:\s+\[BazPipe]/m); - }) - .then(done, done.fail); - }); - }); -}); diff --git a/tests/acceptance/generate-service.spec.ts b/tests/acceptance/generate-service.spec.ts deleted file mode 100644 index 4368b9e3d7c2..000000000000 --- a/tests/acceptance/generate-service.spec.ts +++ /dev/null @@ -1,259 +0,0 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng, setupProject } from '../helpers'; - -const root = process.cwd(); - - -describe('Acceptance: ng generate service', () => { - setupProject(); - - it('ng generate service my-svc', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-svc.service.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-svc.service.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return ng(['generate', 'service', 'my-svc']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(true); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then((content: string) => { - expect(content).not.toMatch(/import.*\MySvcService\b.*from '.\/my-svc.service';/); - expect(content).not.toMatch(/providers:\s*\[MySvcService\]/m); - }) - .then(done, done.fail); - }); - - it('ng generate service my-svc --no-spec', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const testPath = path.join(appRoot, 'src/app/my-svc.service.ts'); - const testSpecPath = path.join(appRoot, 'src/app/my-svc.service.spec.ts'); - const appModulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return ng(['generate', 'service', 'my-svc', '--no-spec']) - .then(() => { - expect(fs.pathExistsSync(testPath)).toBe(true); - expect(fs.pathExistsSync(testSpecPath)).toBe(false); - }) - .then(() => fs.readFile(appModulePath, 'utf-8')) - .then((content: string) => { - expect(content).not.toMatch(/import.*\MySvcService\b.*from '.\/my-svc.service';/); - expect(content).not.toMatch(/providers:\s*\[MySvcService\]/m); - }) - .then(done, done.fail); - }); - - it('ng generate service test' + path.sep + 'my-svc', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test')); - return ng(['generate', 'service', 'test' + path.sep + 'my-svc']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service test' + path.sep + '..' + path.sep + 'my-svc', (done) => { - return ng(['generate', 'service', 'test' + path.sep + '..' + path.sep + 'my-svc']).then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service my-svc from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'service', 'my-svc']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service child-dir' + path.sep + 'my-svc from a child dir', (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'service', 'child-dir' + path.sep + 'my-svc']); - }) - .then(() => { - const testPath = path.join( - root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service child-dir' + path.sep + '..' + path.sep + 'my-svc from a child dir', - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng( - ['generate', 'service', 'child-dir' + path.sep + '..' + path.sep + 'my-svc']); - }) - .then(() => { - const testPath = - path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service ' + path.sep + 'my-svc from a child dir, gens under ' + - path.join('src', 'app'), - (done) => { - fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1')); - return new Promise(function (resolve) { - process.chdir('./src'); - resolve(); - }) - .then(() => process.chdir('./app')) - .then(() => process.chdir('./1')) - .then(() => { - process.env.CWD = process.cwd(); - return ng(['generate', 'service', path.sep + 'my-svc']); - }) - .then(() => { - const testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-svc.service.ts'); - expect(fs.pathExistsSync(testPath)).toBe(true); - }) - .then(done, done.fail); - }); - - it('ng generate service ..' + path.sep + 'my-svc from root dir will fail', (done) => { - return ng(['generate', 'service', '..' + path.sep + 'my-svc']) - .then(() => done.fail()) - .catch((err: string) => { - // tslint:disable-next-line:max-line-length - expect(err).toBe(`Invalid path: "..${path.sep}my-svc" cannot be above the "src${path.sep}app" directory`); - done(); - }); - }); - - it('should error out when given an incorrect module path', (done) => { - return Promise.resolve() - .then(() => ng(['generate', 'service', 'baz', '--module', 'foo'])) - .then(() => done.fail()) - .catch((error) => { - expect(error).toBe('Specified module does not exist'); - done(); - }); - }); - - describe('should import and add to provider list', () => { - it('when given a root level module with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'service', 'baz', '--module', 'app.module.ts'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '.\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - - it('when given a root level module with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/app.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'service', 'baz', '--module', 'app'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '.\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'service', 'baz', '--module', - path.join('foo', 'foo.module.ts')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '..\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'service', 'baz', '--module', path.join('foo', 'foo')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '..\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - - it('when given a submodule folder', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'service', 'baz', '--module', 'foo'])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '..\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - - it('when given deep submodule folder with missing module.ts suffix', (done) => { - const appRoot = path.join(root, 'tmp/foo'); - const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts'); - - return Promise.resolve() - .then(() => ng(['generate', 'module', 'foo'])) - .then(() => ng(['generate', 'module', path.join('foo', 'bar')])) - .then(() => ng(['generate', 'service', 'baz', '--module', path.join('foo', 'bar')])) - .then(() => fs.readFile(modulePath, 'utf-8')) - .then(content => { - expect(content).toMatch(/import.*BazService.*from '..\/..\/baz.service';/); - expect(content).toMatch(/providers:\s*\[BazService\]/m); - }) - .then(done, done.fail); - }); - }); -}); diff --git a/tests/acceptance/new.spec.ts b/tests/acceptance/new.spec.ts deleted file mode 100644 index 3c70b92a68d4..000000000000 --- a/tests/acceptance/new.spec.ts +++ /dev/null @@ -1,187 +0,0 @@ -// tslint:disable:max-line-length -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ng } from '../helpers'; - -const tmp = require('../helpers/tmp'); - - -describe('Acceptance: ng new', function () { - let originalTimeout: number; - - beforeEach((done) => { - // Increase timeout for these tests only. - originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; - jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; - - spyOn(console, 'error'); - // symlink custom collections to node_modules, so we can use with ng new - // it is a bit dirty, but bootstrap-local tricks won't work here - fs.symlinkSync(`${process.cwd()}/tests/collections/@custom`, `./node_modules/@custom`, 'dir'); - - tmp.setup('./tmp') - .then(() => process.chdir('./tmp')) - .then(() => done()); - }, 10000); - - afterEach((done) => { - fs.unlinkSync(path.join(__dirname, '/../../node_modules/@custom')); - jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; - tmp.teardown('./tmp').then(() => done()); - }); - - it('requires a valid name (!)', (done) => { - return ng(['new', '!', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done.fail(), () => done()); - }); - it('requires a valid name (abc-.)', (done) => { - return ng(['new', 'abc-.', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done.fail(), () => done()); - }); - it('requires a valid name (abc-)', (done) => { - return ng(['new', 'abc-', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done.fail(), () => done()); - }); - it('requires a valid name (abc-def-)', (done) => { - return ng(['new', 'abc-def-', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done.fail(), () => done()); - }); - it('requires a valid name (abc-123)', (done) => { - return ng(['new', 'abc-123', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done.fail(), () => done()); - }); - it('requires a valid name (abc)', (done) => { - return ng(['new', 'abc', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done(), () => done.fail()); - }); - it('requires a valid name (abc-def)', (done) => { - return ng(['new', 'abc-def', '--skip-install', '--skip-git', '--inline-template']) - .then(() => done(), () => done.fail()); - }); - - it('ng new foo, where foo does not yet exist, works', (done) => { - return ng(['new', 'foo', '--skip-install']) - .then(() => { - expect(fs.pathExistsSync('../foo')); - expect(fs.pathExistsSync('package.json')); - }) - .then(done, done.fail); - }); - - it('ng new with empty app does throw exception', (done) => { - return ng(['new', '']) - .then(() => done.fail(), () => done()); - }); - - it('ng new without app name does throw exception', (done) => { - return ng(['new']) - .then(() => done.fail(), () => done()); - }); - - it('ng new with app name creates new directory and has a dasherized package name', (done) => { - return ng(['new', 'FooApp', '--skip-install', '--skip-git']).then(() => { - expect(!fs.pathExistsSync('FooApp')); - - const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); - expect(pkgJson.name).toBe('foo-app'); - }) - .then(done, done.fail); - }); - - it('ng new has a .editorconfig file', (done) => { - return ng(['new', 'FooApp', '--skip-install', '--skip-git']).then(() => { - expect(!fs.pathExistsSync('FooApp')); - - const editorConfig = fs.readFileSync('.editorconfig', 'utf8'); - expect(editorConfig).toBeDefined(); - }) - .then(done, done.fail); - }); - - it('Cannot run ng new, inside of Angular CLI project', (done) => { - return ng(['new', 'foo', '--skip-install', '--skip-git']) - .then(() => { - return ng(['new', 'foo', '--skip-install', '--skip-git']).then(() => { - done.fail(); - }, () => { - expect(!fs.pathExistsSync('foo')); - }); - }) - .then(done, done.fail); - }); - - it('ng new without skip-git flag creates .git dir', (done) => { - return ng(['new', 'foo', '--skip-install']).then(() => { - expect(fs.pathExistsSync('.git')); - }) - .then(done, done.fail); - }); - - it('ng new with --dry-run does not create new directory', (done) => { - return ng(['new', 'foo', '--dry-run']).then(() => { - const cwd = process.cwd(); - expect(cwd).not.toMatch(/foo/, 'does not change cwd to foo in a dry run'); - expect(fs.pathExistsSync(path.join(cwd, 'foo'))).toBe(false, 'does not create new directory'); - expect(fs.pathExistsSync(path.join(cwd, '.git'))).toBe(false, 'does not create git in current directory'); - }) - .then(done, done.fail); - }); - - it('ng new with --directory uses given directory name and has correct package name', (done) => { - return ng(['new', 'foo', '--skip-install', '--skip-git', '--directory=bar']) - .then(() => { - const cwd = process.cwd(); - expect(cwd).not.toMatch(/foo/, 'does not use app name for directory name'); - expect(fs.pathExistsSync(path.join(cwd, 'foo'))).toBe(false, 'does not create new directory with app name'); - - expect(cwd).toMatch(/bar/, 'uses given directory name'); - expect(fs.pathExistsSync(path.join(cwd, '..', 'bar'))).toBe(true, 'creates new directory with specified name'); - - const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); - expect(pkgJson.name).toBe('foo', 'uses app name for package name'); - }) - .then(done, done.fail); - }); - - it('ng new --inline-template does not generate a template file', (done) => { - return ng(['new', 'foo', '--skip-install', '--skip-git', '--inline-template']) - .then(() => { - const templateFile = path.join('src', 'app', 'app.component.html'); - expect(fs.pathExistsSync(templateFile)).toBe(false); - }) - .then(done, done.fail); - }); - - it('ng new --inline-style does not gener a style file', (done) => { - return ng(['new', 'foo', '--skip-install', '--skip-git', '--inline-style']) - .then(() => { - const styleFile = path.join('src', 'app', 'app.component.css'); - expect(fs.pathExistsSync(styleFile)).toBe(false); - }) - .then(done, done.fail); - }); - - it('should skip spec files when passed --skip-tests', (done) => { - return ng(['new', 'foo', '--skip-install', '--skip-git', '--skip-tests']) - .then(() => { - const specFile = path.join('src', 'app', 'app.component.spec.ts'); - expect(fs.pathExistsSync(specFile)).toBe(false); - }) - .then(done, done.fail); - }); - - it('should specify a version of the CLI', (done) => { - return ng(['new', 'FooApp', '--skip-install', '--skip-git']).then(() => { - const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); - expect(pkgJson.devDependencies['@angular/cli']).toMatch(/\d+\.\d+\.\d+/); - }) - .then(done, done.fail); - }); - - it('should support passing a custom collection', (done) => { - return ng(['new', 'foo', '--collection=@custom/application', '--skip-install', '--skip-git']).then(() => { - expect(() => fs.readFileSync('emptyapp', 'utf8')).not.toThrow(); - }) - .then(done, done.fail); - }); -}); diff --git a/tests/e2e/tests/basic/dev-build.ts b/tests/e2e/tests/basic/dev-build.ts index 454722cd69bc..ac70ac70ecc3 100644 --- a/tests/e2e/tests/basic/dev-build.ts +++ b/tests/e2e/tests/basic/dev-build.ts @@ -8,7 +8,7 @@ import {expectToFail} from '../../utils/utils'; export default function() { const ejected = getGlobalVariable('argv').eject; - return ng('build', '--env=dev') + return ng('build', '--environment=dev') .then(() => expectFileToMatch('dist/index.html', 'main.js')) .then(() => expectToFail(() => expectFileToExist('dist/3rdpartylicenses.txt'))) // If this is an ejected test, the eject will create files so git will not be clean. diff --git a/tests/e2e/tests/basic/scripts-array.ts b/tests/e2e/tests/basic/scripts-array.ts index 980c14e6c423..547a9d970bd2 100644 --- a/tests/e2e/tests/basic/scripts-array.ts +++ b/tests/e2e/tests/basic/scripts-array.ts @@ -60,7 +60,7 @@ export default function () { // Ensure scripts can be separately imported from the app. .then(() => expectFileToMatch('dist/main.js', 'console.log(\'string-script\');')) // Verify uglify, sourcemaps and hashes. Lazy scripts should not get hashes. - .then(() => ng('build', '--prod', '--sourcemap')) + .then(() => ng('build', '--target', 'production', '--sourcemaps')) .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js/)) .then(fileName => expectFileToMatch(`dist/${fileName}`, 'var number=2;')) .then(() => expectFileMatchToExist('dist', /scripts\.[0-9a-f]{20}\.js\.map/)) diff --git a/tests/e2e/tests/build/build-app-shell-with-schematic.ts b/tests/e2e/tests/build/build-app-shell-with-schematic.ts index 41f799cd90e7..2b094f022553 100644 --- a/tests/e2e/tests/build/build-app-shell-with-schematic.ts +++ b/tests/e2e/tests/build/build-app-shell-with-schematic.ts @@ -35,8 +35,8 @@ export default function () { dependencies['@angular/platform-server'] = platformServerVersion; }) .then(() => npm('install')) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => expectFileToMatch('dist/index.html', /app-shell works!/)) - .then(() => ng('build', '--prod', '--skip-app-shell')) + .then(() => ng('build', '--target', 'production', '--skip-app-shell')) .then(() => expectToFail(() => expectFileToMatch('dist/index.html', /app-shell works!/))); } diff --git a/tests/e2e/tests/build/build-app-shell.ts b/tests/e2e/tests/build/build-app-shell.ts index cc531557f1ac..d253d4e4fdaa 100644 --- a/tests/e2e/tests/build/build-app-shell.ts +++ b/tests/e2e/tests/build/build-app-shell.ts @@ -140,8 +140,8 @@ export default function () { dependencies['@angular/platform-server'] = platformServerVersion; }) .then(() => npm('install'))) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => expectFileToMatch('dist/index.html', /shell Works!/)) - .then(() => ng('build', '--prod', '--skip-app-shell')) + .then(() => ng('build', '--target', 'production', '--skip-app-shell')) .then(() => expectToFail(() => expectFileToMatch('dist/index.html', /shell Works!/))); } diff --git a/tests/e2e/tests/build/build-optimizer.ts b/tests/e2e/tests/build/build-optimizer.ts index 2ce6aa4c3816..b42ecb54e7dc 100644 --- a/tests/e2e/tests/build/build-optimizer.ts +++ b/tests/e2e/tests/build/build-optimizer.ts @@ -6,7 +6,7 @@ import { expectToFail } from '../../utils/utils'; export default function () { return ng('build', '--aot', '--build-optimizer') .then(() => expectToFail(() => expectFileToMatch('dist/main.js', /\.decorators =/))) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => expectToFail(() => expectFileToExist('dist/vendor.js'))) .then(() => expectToFail(() => expectFileToMatch('dist/main.js', /\.decorators =/))); } diff --git a/tests/e2e/tests/build/bundle-budgets.ts b/tests/e2e/tests/build/bundle-budgets.ts index 92d35a51b557..197dda6a692d 100644 --- a/tests/e2e/tests/build/bundle-budgets.ts +++ b/tests/e2e/tests/build/bundle-budgets.ts @@ -35,7 +35,7 @@ export default function () { if (cfg.expectation === 'error') { return () => { return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) - .then(() => expectToFail(() => ng('build', '--prod'))) + .then(() => expectToFail(() => ng('build', '--target', 'production'))) .then(errorMessage => { if (!/ERROR in budgets/.test(errorMessage)) { throw new Error(cfg.message); @@ -45,7 +45,7 @@ export default function () { } else if (cfg.expectation === 'warning') { return () => { return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(({ stdout }) => { if (!/WARNING in budgets/.test(stdout)) { throw new Error(cfg.message); @@ -55,7 +55,7 @@ export default function () { } else { // pass return () => { return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(({ stdout }) => { if (/(WARNING|ERROR)/.test(stdout)) { throw new Error(cfg.message); diff --git a/tests/e2e/tests/build/environment.ts b/tests/e2e/tests/build/environment.ts index dabc3b207b9d..e8aaf55e0f4b 100644 --- a/tests/e2e/tests/build/environment.ts +++ b/tests/e2e/tests/build/environment.ts @@ -10,7 +10,7 @@ export default function() { const ejected = getGlobalVariable('argv').eject; // Try a prod build. - return ng('build', '--env=prod') + return ng('build', '--environment=prod') .then(() => expectFileToMatch('dist/main.js', 'production: true')) // If this is an ejected test, the eject will create files so git will not be clean. .then(() => !ejected && expectGitToBeClean()) diff --git a/tests/e2e/tests/build/extract-licenses.ts b/tests/e2e/tests/build/extract-licenses.ts index 601c97796b56..9ca60e4802e6 100644 --- a/tests/e2e/tests/build/extract-licenses.ts +++ b/tests/e2e/tests/build/extract-licenses.ts @@ -4,7 +4,7 @@ import {expectToFail} from '../../utils/utils'; import {ng} from '../../utils/process'; export default function() { - return ng('build', '--prod', '--extract-licenses=false') + return ng('build', '--target', 'production', '--extract-licenses=false') .then(() => expectFileToExist(join(process.cwd(), 'dist'))) .then(() => expectToFail(() => expectFileToExist('dist/3rdpartylicenses.txt'))); } diff --git a/tests/e2e/tests/build/output-dir.ts b/tests/e2e/tests/build/output-dir.ts index 6379004aef62..615ebd7a1526 100644 --- a/tests/e2e/tests/build/output-dir.ts +++ b/tests/e2e/tests/build/output-dir.ts @@ -12,7 +12,7 @@ export default function() { return Promise.resolve(); } - return ng('build', '-op', './build-output') + return ng('build', '--output-path', './build-output') .then(() => expectFileToExist('./build-output/index.html')) .then(() => expectFileToExist('./build-output/main.js')) .then(() => expectToFail(expectGitToBeClean)) diff --git a/tests/e2e/tests/build/output-hashing.ts b/tests/e2e/tests/build/output-hashing.ts index 008231d81f42..c20050a2f21d 100644 --- a/tests/e2e/tests/build/output-hashing.ts +++ b/tests/e2e/tests/build/output-hashing.ts @@ -15,25 +15,25 @@ export default function() { })) // use image with file size >10KB to prevent inlining .then(() => copyProjectAsset('images/spectrum.png', './assets/image.png')) - .then(() => ng('build', '--dev', '--output-hashing=all')) + .then(() => ng('build', '--target', 'development', '--output-hashing=all')) .then(() => expectFileToMatch('dist/index.html', /runtime\.[0-9a-f]{20}\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{20}\.(css|js)/)) .then(() => verifyMedia(/styles\.[0-9a-f]{20}\.(css|js)/, /image\.[0-9a-f]{20}\.png/)) - .then(() => ng('build', '--prod', '--output-hashing=none')) + .then(() => ng('build', '--target', 'production', '--output-hashing=none')) .then(() => expectFileToMatch('dist/index.html', /runtime\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.(css|js)/)) .then(() => verifyMedia(/styles\.(css|js)/, /image\.png/)) - .then(() => ng('build', '--dev', '--output-hashing=media')) + .then(() => ng('build', '--target', 'development', '--output-hashing=media')) .then(() => expectFileToMatch('dist/index.html', /runtime\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.(css|js)/)) .then(() => verifyMedia(/styles\.(css|js)/, /image\.[0-9a-f]{20}\.png/)) - .then(() => ng('build', '--dev', '--output-hashing=bundles')) + .then(() => ng('build', '--target', 'development', '--output-hashing=bundles')) .then(() => expectFileToMatch('dist/index.html', /runtime\.[0-9a-f]{20}\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{20}\.(css|js)/)) diff --git a/tests/e2e/tests/build/prod-build.ts b/tests/e2e/tests/build/prod-build.ts index ee89c0ad6f6c..a9d78efd283f 100644 --- a/tests/e2e/tests/build/prod-build.ts +++ b/tests/e2e/tests/build/prod-build.ts @@ -12,7 +12,7 @@ export default function() { // Can't use the `ng` helper because somewhere the environment gets // stuck to the first build done - return ng('build', '--prod') + return ng('build', '--target', 'production') .then(() => expectFileToExist(join(process.cwd(), 'dist'))) // Check for cache busting hash script src .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.js/)) diff --git a/tests/e2e/tests/build/script-target.ts b/tests/e2e/tests/build/script-target.ts index cd9d97cf6cba..eca3f1dccbb4 100644 --- a/tests/e2e/tests/build/script-target.ts +++ b/tests/e2e/tests/build/script-target.ts @@ -15,7 +15,7 @@ export default function () { const compilerOptions = configJson['compilerOptions']; compilerOptions['target'] = 'es2015'; })) - .then(() => ng('build', '--prod', '--output-hashing=none', '--vendor-chunk')) + .then(() => ng('build', '--target', 'production', '--output-hashing=none', '--vendor-chunk')) // Check class constructors are present in the vendor output. .then(() => expectFileToMatch('dist/vendor.js', /class \w{constructor\(\){/)); } diff --git a/tests/e2e/tests/build/service-worker.ts b/tests/e2e/tests/build/service-worker.ts index 667ccea3139d..e3b37fbea6b4 100644 --- a/tests/e2e/tests/build/service-worker.ts +++ b/tests/e2e/tests/build/service-worker.ts @@ -34,16 +34,16 @@ export default function() { .then(() => silentNpm('install', '@angular/service-worker')) .then(() => ng('set', 'apps.0.serviceWorker=true')) .then(() => writeFile('src/ngsw-config.json', JSON.stringify(MANIFEST, null, 2))) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => expectFileToExist(join(process.cwd(), 'dist'))) .then(() => expectFileToExist(join(process.cwd(), 'dist/ngsw.json'))) - .then(() => ng('build', '--prod', '--base-href=/foo/bar')) + .then(() => ng('build', '--target', 'production', '--base-href=/foo/bar')) .then(() => expectFileToExist(join(process.cwd(), 'dist/ngsw.json'))) .then(() => expectFileToMatch('dist/ngsw.json', /"\/foo\/bar\/index.html"/)) - .then(() => ng('build', '--prod', '--service-worker=false')) + .then(() => ng('build', '--target', 'production', '--service-worker=false')) .then(() => expectFileNotToExist('dist/ngsw.json')) .then(() => writeFile('node_modules/@angular/service-worker/safety-worker.js', 'false')) - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => expectFileToExist('dist/safety-worker.js')) .then(() => expectFileToExist('dist/worker-basic.min.js')); // WEBPACK4_DISABLED - eject temporarily disabled for webpack 4 integration diff --git a/tests/e2e/tests/build/sourcemap.ts b/tests/e2e/tests/build/sourcemap.ts index 2b07c1ec3cc2..754dcd1924b0 100644 --- a/tests/e2e/tests/build/sourcemap.ts +++ b/tests/e2e/tests/build/sourcemap.ts @@ -7,12 +7,12 @@ export default function() { return ng('build', '--sourcemaps') .then(() => expectFileToExist('dist/main.js.map')) - .then(() => ng('build', '--no-sourcemap')) + .then(() => ng('build', '--sourcemaps', 'false')) .then(() => expectToFail(() => expectFileToExist('dist/main.js.map'))) - .then(() => ng('build', '--prod', '--output-hashing=none')) + .then(() => ng('build', '--target', 'production', '--output-hashing=none')) .then(() => expectToFail(() => expectFileToExist('dist/main..js.map'))) - .then(() => ng('build', '--prod', '--output-hashing=none', '--sourcemap')) + .then(() => ng('build', '--target', 'production', '--output-hashing=none', '--sourcemaps')) .then(() => expectFileToExist('dist/main.js.map')); } diff --git a/tests/e2e/tests/build/styles/imports.ts b/tests/e2e/tests/build/styles/imports.ts index 852402b3b120..47bfbd1afd72 100644 --- a/tests/e2e/tests/build/styles/imports.ts +++ b/tests/e2e/tests/build/styles/imports.ts @@ -45,7 +45,7 @@ export default function () { .then(() => replaceInFile('src/app/app.component.ts', './app.component.css', `./app.component.${ext}`)) // run build app - .then(() => ng('build', '--extract-css', '--sourcemap')) + .then(() => ng('build', '--extract-css', '--sourcemaps')) // verify global styles .then(() => expectFileToMatch('dist/styles.css', /body\s*{\s*background-color: #00f;\s*}/)) diff --git a/tests/e2e/tests/build/styles/less.ts b/tests/e2e/tests/build/styles/less.ts index 08872d460e16..8e66f2f9cf0d 100644 --- a/tests/e2e/tests/build/styles/less.ts +++ b/tests/e2e/tests/build/styles/less.ts @@ -32,7 +32,7 @@ export default function () { })) .then(() => replaceInFile('src/app/app.component.ts', './app.component.css', './app.component.less')) - .then(() => ng('build', '--extract-css', '--sourcemap')) + .then(() => ng('build', '--extract-css', '--sourcemaps')) .then(() => expectFileToMatch('dist/styles.css', /body\s*{\s*background-color: blue;\s*}/)) .then(() => expectFileToMatch('dist/styles.css', diff --git a/tests/e2e/tests/build/styles/material-import.ts b/tests/e2e/tests/build/styles/material-import.ts index fa87438930d7..0fe3fd2bcfe1 100644 --- a/tests/e2e/tests/build/styles/material-import.ts +++ b/tests/e2e/tests/build/styles/material-import.ts @@ -29,7 +29,7 @@ export default function () { .then(() => replaceInFile('src/app/app.component.ts', './app.component.css', `./app.component.${ext}`)) // run build app - .then(() => ng('build', '--extract-css', '--sourcemap')) + .then(() => ng('build', '--extract-css', '--sourcemaps')) .then(() => writeMultipleFiles({ [`src/styles.${ext}`]: stripIndents` @import "@angular/material/prebuilt-themes/indigo-pink.css"; diff --git a/tests/e2e/tests/build/styles/postcss.ts b/tests/e2e/tests/build/styles/postcss.ts index 71e48fbe954f..8e3ac56b9905 100644 --- a/tests/e2e/tests/build/styles/postcss.ts +++ b/tests/e2e/tests/build/styles/postcss.ts @@ -17,7 +17,7 @@ export default function () { div { -webkit-box-flex: 1; -ms-flex: 1; flex: 1 } `)) // uses postcss-discard-comments plugin for prod - .then(() => ng('build', '--prod')) + .then(() => ng('build', '--target', 'production')) .then(() => glob.sync('dist/styles.*.css').find(file => !!file)) .then((stylesBundle) => expectFileToMatch(stylesBundle, stripIndents` /*! important-comment */div{-webkit-box-flex:1;-ms-flex:1;flex:1} diff --git a/tests/e2e/tests/build/styles/scss.ts b/tests/e2e/tests/build/styles/scss.ts index 65aa1538bb9b..8e6c57f8ef08 100644 --- a/tests/e2e/tests/build/styles/scss.ts +++ b/tests/e2e/tests/build/styles/scss.ts @@ -32,7 +32,7 @@ export default function () { })) .then(() => replaceInFile('src/app/app.component.ts', './app.component.css', './app.component.scss')) - .then(() => ng('build', '--extract-css', '--sourcemap')) + .then(() => ng('build', '--extract-css', '--sourcemaps')) .then(() => expectFileToMatch('dist/styles.css', /body\s*{\s*background-color: blue;\s*}/)) .then(() => expectFileToMatch('dist/styles.css', diff --git a/tests/e2e/tests/build/styles/stylus.ts b/tests/e2e/tests/build/styles/stylus.ts index c51feabe8ea9..ff3068e0b788 100644 --- a/tests/e2e/tests/build/styles/stylus.ts +++ b/tests/e2e/tests/build/styles/stylus.ts @@ -32,7 +32,7 @@ export default function () { })) .then(() => replaceInFile('src/app/app.component.ts', './app.component.css', './app.component.styl')) - .then(() => ng('build', '--extract-css', '--sourcemap')) + .then(() => ng('build', '--extract-css', '--sourcemaps')) .then(() => expectFileToMatch('dist/styles.css', /body\s*{\s*background-color: #00f;\s*}/)) .then(() => expectFileToMatch('dist/styles.css', diff --git a/tests/e2e/tests/build/typescript/typescript-2_4.ts b/tests/e2e/tests/build/typescript/typescript-2_4.ts index 63a680911a35..340cc375e15c 100644 --- a/tests/e2e/tests/build/typescript/typescript-2_4.ts +++ b/tests/e2e/tests/build/typescript/typescript-2_4.ts @@ -3,6 +3,6 @@ import { ng, npm } from '../../../utils/process'; export default async function () { await npm('install', 'typescript@2.4'); await ng('build'); - await ng('build', '--prod'); + await ng('build', '--target', 'production'); await npm('install', 'typescript@2.6'); } diff --git a/tests/e2e/tests/build/typescript/typescript-2_7.ts b/tests/e2e/tests/build/typescript/typescript-2_7.ts index bee342d5365e..b8b116927ddb 100644 --- a/tests/e2e/tests/build/typescript/typescript-2_7.ts +++ b/tests/e2e/tests/build/typescript/typescript-2_7.ts @@ -12,6 +12,6 @@ export default async function () { await npm('install', 'typescript@2.7'); await ng('build'); - await ng('build', '--prod'); + await ng('build', '--target', 'production'); await npm('install', 'typescript@2.6'); } diff --git a/tests/e2e/tests/commands/completion/completion.ts b/tests/e2e/tests/commands/completion/completion.ts deleted file mode 100644 index b88821e8c3ab..000000000000 --- a/tests/e2e/tests/commands/completion/completion.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {silentNg} from '../../../utils/process'; - - -export default function() { - return Promise.resolve() - .then(() => silentNg('completion')) - .then(() => process.chdir('/')) - .then(() => silentNg('completion')); -} diff --git a/tests/e2e/tests/commands/new/existing-directory.ts b/tests/e2e/tests/commands/new/existing-directory.ts index 844e7b21a8c7..232874ff5ae4 100644 --- a/tests/e2e/tests/commands/new/existing-directory.ts +++ b/tests/e2e/tests/commands/new/existing-directory.ts @@ -10,5 +10,5 @@ export default function() { return Promise.resolve() .then(() => process.chdir(getGlobalVariable('tmp-root'))) .then(() => mkdir('empty-directory')) - .then(() => ng('new', 'foo', '--dir=empty-directory', '--skip-install', '--skip-git')); + .then(() => ng('new', 'foo', '--directory=empty-directory', '--skip-install', '--skip-git')); } diff --git a/tests/e2e/tests/generate/component/component-flag-case.ts b/tests/e2e/tests/generate/component/component-flag-case.ts index dbbee3525756..31e221767f48 100644 --- a/tests/e2e/tests/generate/component/component-flag-case.ts +++ b/tests/e2e/tests/generate/component/component-flag-case.ts @@ -7,7 +7,9 @@ export default function() { const compDir = join('src', 'app', 'test'); return Promise.resolve() - .then(() => ng('generate', 'component', 'test', '-cd', 'onpush', '-ve', 'emulated')) + .then(() => ng('generate', 'component', 'test', + '--change-detection', 'onpush', + '--view-encapsulation', 'emulated')) .then(() => expectFileToMatch(join(compDir, 'test.component.ts'), /changeDetection: ChangeDetectionStrategy.OnPush/)) .then(() => expectFileToMatch(join(compDir, 'test.component.ts'), diff --git a/tests/e2e/tests/i18n/extract-output.ts b/tests/e2e/tests/i18n/extract-output.ts index df11befed240..8a82ff241450 100644 --- a/tests/e2e/tests/i18n/extract-output.ts +++ b/tests/e2e/tests/i18n/extract-output.ts @@ -1,17 +1,18 @@ -import {join} from 'path'; -import {ng} from '../../utils/process'; -import { - expectFileToExist, writeFile, - expectFileToMatch -} from '../../utils/fs'; +export default function temporarilyDisabledTest() { return Promise.resolve(); } +// import {join} from 'path'; +// import {ng} from '../../utils/process'; +// import { +// expectFileToExist, writeFile, +// expectFileToMatch +// } from '../../utils/fs'; -export default function() { - return ng('generate', 'component', 'i18n-test') - .then(() => writeFile( - join('src/app/i18n-test', 'i18n-test.component.html'), - '

Hello world

')) - .then(() => ng('xi18n', '--output-path', 'src/locale')) - .then(() => expectFileToExist(join('src', 'locale', 'messages.xlf'))) - .then(() => expectFileToMatch(join('src', 'locale', 'messages.xlf'), /Hello world/)); -} +// export default function() { +// return ng('generate', 'component', 'i18n-test') +// .then(() => writeFile( +// join('src/app/i18n-test', 'i18n-test.component.html'), +// '

Hello world

')) +// .then(() => ng('xi18n', '--output-path', 'src/locale')) +// .then(() => expectFileToExist(join('src', 'locale', 'messages.xlf'))) +// .then(() => expectFileToMatch(join('src', 'locale', 'messages.xlf'), /Hello world/)); +// } diff --git a/tests/e2e/tests/misc/different-file-format.ts b/tests/e2e/tests/misc/different-file-format.ts index 9f1abbb7eab5..f4714e313cb0 100644 --- a/tests/e2e/tests/misc/different-file-format.ts +++ b/tests/e2e/tests/misc/different-file-format.ts @@ -11,5 +11,5 @@ export default function() { return Promise.resolve() .then(() => fs.prependToFile('./src/tsconfig.app.json', '\ufeff', options)) .then(() => fs.prependToFile('./.angular-cli.json', '\ufeff', options)) - .then(() => ng('build', '--env=dev')); + .then(() => ng('build', '--environment=dev')); } diff --git a/tests/e2e/tests/misc/live-reload.ts b/tests/e2e/tests/misc/live-reload.ts index e9fad1f90e85..c70ae92ab091 100644 --- a/tests/e2e/tests/misc/live-reload.ts +++ b/tests/e2e/tests/misc/live-reload.ts @@ -1,171 +1,172 @@ -import * as os from 'os'; -import * as _ from 'lodash'; -import * as express from 'express'; -import * as http from 'http'; +export default function temporarilyDisabledTest() { return Promise.resolve(); } +// import * as os from 'os'; +// import * as _ from 'lodash'; +// import * as express from 'express'; +// import * as http from 'http'; -import {appendToFile, writeMultipleFiles, writeFile} from '../../utils/fs'; -import { - killAllProcesses, - execAndWaitForOutputToMatch, - waitForAnyProcessOutputToMatch -} from '../../utils/process'; -import { wait } from '../../utils/utils'; +// import {appendToFile, writeMultipleFiles, writeFile} from '../../utils/fs'; +// import { +// killAllProcesses, +// execAndWaitForOutputToMatch, +// waitForAnyProcessOutputToMatch +// } from '../../utils/process'; +// import { wait } from '../../utils/utils'; -export default function () { - const protractorGoodRegEx = /Jasmine started/; - const webpackGoodRegEx = /: Compiled successfully./; +// export default function () { +// const protractorGoodRegEx = /Jasmine started/; +// const webpackGoodRegEx = /: Compiled successfully./; - // Create an express api for the Angular app to call. - const app = express(); - const server = http.createServer(app); - let liveReloadCount = 0; - function resetApiVars() { - liveReloadCount = 0; - } +// // Create an express api for the Angular app to call. +// const app = express(); +// const server = http.createServer(app); +// let liveReloadCount = 0; +// function resetApiVars() { +// liveReloadCount = 0; +// } - server.listen(0); - app.set('port', server.address().port); +// server.listen(0); +// app.set('port', server.address().port); - const firstLocalIp = _(os.networkInterfaces()) - .values() - .flatten() - .filter({ family: 'IPv4', internal: false }) - .map('address') - .first(); - const publicHost = `${firstLocalIp}:4200`; +// const firstLocalIp = _(os.networkInterfaces()) +// .values() +// .flatten() +// .filter({ family: 'IPv4', internal: false }) +// .map('address') +// .first(); +// const publicHost = `${firstLocalIp}:4200`; - const apiUrl = `http://localhost:${server.address().port}`; +// const apiUrl = `http://localhost:${server.address().port}`; - // This endpoint will be pinged by the main app on each reload. - app.get('/live-reload-count', _ => liveReloadCount++); +// // This endpoint will be pinged by the main app on each reload. +// app.get('/live-reload-count', _ => liveReloadCount++); - const proxyConfigFile = 'proxy.config.json'; - const proxyConfig = { - '/live-reload-count': { - target: apiUrl - } - }; +// const proxyConfigFile = 'proxy.config.json'; +// const proxyConfig = { +// '/live-reload-count': { +// target: apiUrl +// } +// }; - return Promise.resolve() - .then(_ => writeMultipleFiles({ - 'src/app/app.module.ts': ` - import { BrowserModule } from '@angular/platform-browser'; - import { NgModule } from '@angular/core'; - import { FormsModule } from '@angular/forms'; - import { HttpModule } from '@angular/http'; - import { AppComponent } from './app.component'; - @NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, - FormsModule, - HttpModule - ], - providers: [], - bootstrap: [AppComponent] - }) - export class AppModule { } - `, - // e2e test that just opens the page and waits, so that the app runs. - './e2e/app.e2e-spec.ts': ` - import { browser } from 'protractor'; +// return Promise.resolve() +// .then(_ => writeMultipleFiles({ +// 'src/app/app.module.ts': ` +// import { BrowserModule } from '@angular/platform-browser'; +// import { NgModule } from '@angular/core'; +// import { FormsModule } from '@angular/forms'; +// import { HttpModule } from '@angular/http'; +// import { AppComponent } from './app.component'; +// @NgModule({ +// declarations: [ +// AppComponent +// ], +// imports: [ +// BrowserModule, +// FormsModule, +// HttpModule +// ], +// providers: [], +// bootstrap: [AppComponent] +// }) +// export class AppModule { } +// `, +// // e2e test that just opens the page and waits, so that the app runs. +// './e2e/app.e2e-spec.ts': ` +// import { browser } from 'protractor'; - describe('master-project App', function() { - it('should wait', _ => { - browser.get('/'); - browser.sleep(30000); - }); - }); - `, - // App that calls the express server once. - './src/app/app.component.ts': ` - import { Component } from '@angular/core'; - import { Http } from '@angular/http'; +// describe('master-project App', function() { +// it('should wait', _ => { +// browser.get('/'); +// browser.sleep(30000); +// }); +// }); +// `, +// // App that calls the express server once. +// './src/app/app.component.ts': ` +// import { Component } from '@angular/core'; +// import { Http } from '@angular/http'; - @Component({ - selector: 'app-root', - template: '

Live reload test

' - }) - export class AppComponent { - constructor(private http: Http) { - http.get('${apiUrl + '/live-reload-count'}').subscribe(res => null); - } - } - ` - })) - .then(_ => execAndWaitForOutputToMatch( - 'ng', - ['e2e', '--watch', '--live-reload'], - protractorGoodRegEx - )) - // Let app run. - .then(_ => wait(2000)) - .then(_ => appendToFile('src/main.ts', 'console.log(1);')) - .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) - .then(_ => wait(2000)) - .then(_ => { - if (liveReloadCount != 2) { - throw new Error( - `Expected API to have been called 2 times but it was called ${liveReloadCount} times.` - ); - } - }) - .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) - .then(_ => resetApiVars()) - // Serve with live reload off should call api only once. - .then(_ => execAndWaitForOutputToMatch( - 'ng', - ['e2e', '--watch', '--no-live-reload'], - protractorGoodRegEx - )) - .then(_ => wait(2000)) - .then(_ => appendToFile('src/main.ts', 'console.log(1);')) - .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) - .then(_ => wait(2000)) - .then(_ => { - if (liveReloadCount != 1) { - throw new Error( - `Expected API to have been called 1 time but it was called ${liveReloadCount} times.` - ); - } - }) - .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) - .then(_ => resetApiVars()) - // Serve with live reload client set to api should call api. - .then(() => writeFile(proxyConfigFile, JSON.stringify(proxyConfig, null, 2))) - // Update the component to call the webserver - .then(() => writeFile('./src/app/app.component.ts', - ` - import { Component } from '@angular/core'; - import { Http } from '@angular/http'; - @Component({ - selector: 'app-root', - template: '

Live reload test

' - }) - export class AppComponent { - constructor(private http: Http) { - http.get('http://${publicHost + '/live-reload-count'}').subscribe(res => null); - } - }`)) - .then(_ => execAndWaitForOutputToMatch( - 'ng', - ['e2e', '--watch', '--host=0.0.0.0', '--port=4200', `--public-host=${publicHost}`, '--proxy', proxyConfigFile], - protractorGoodRegEx - )) - .then(_ => wait(2000)) - .then(_ => appendToFile('src/main.ts', 'console.log(1);')) - .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) - .then(_ => wait(2000)) - .then(_ => { - if (liveReloadCount != 2) { - throw new Error( - `Expected API to have been called 2 times but it was called ${liveReloadCount} times.` - ); - } - }) - .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) - .then(_ => server.close(), (err) => { server.close(); throw err; }); -} +// @Component({ +// selector: 'app-root', +// template: '

Live reload test

' +// }) +// export class AppComponent { +// constructor(private http: Http) { +// http.get('${apiUrl + '/live-reload-count'}').subscribe(res => null); +// } +// } +// ` +// })) +// .then(_ => execAndWaitForOutputToMatch( +// 'ng', +// ['e2e', '--watch', '--live-reload'], +// protractorGoodRegEx +// )) +// // Let app run. +// .then(_ => wait(2000)) +// .then(_ => appendToFile('src/main.ts', 'console.log(1);')) +// .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) +// .then(_ => wait(2000)) +// .then(_ => { +// if (liveReloadCount != 2) { +// throw new Error( +// `Expected API to have been called 2 times but it was called ${liveReloadCount} times.` +// ); +// } +// }) +// .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) +// .then(_ => resetApiVars()) +// // Serve with live reload off should call api only once. +// .then(_ => execAndWaitForOutputToMatch( +// 'ng', +// ['e2e', '--watch', '--no-live-reload'], +// protractorGoodRegEx +// )) +// .then(_ => wait(2000)) +// .then(_ => appendToFile('src/main.ts', 'console.log(1);')) +// .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) +// .then(_ => wait(2000)) +// .then(_ => { +// if (liveReloadCount != 1) { +// throw new Error( +// `Expected API to have been called 1 time but it was called ${liveReloadCount} times.` +// ); +// } +// }) +// .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) +// .then(_ => resetApiVars()) +// // Serve with live reload client set to api should call api. +// .then(() => writeFile(proxyConfigFile, JSON.stringify(proxyConfig, null, 2))) +// // Update the component to call the webserver +// .then(() => writeFile('./src/app/app.component.ts', +// ` +// import { Component } from '@angular/core'; +// import { Http } from '@angular/http'; +// @Component({ +// selector: 'app-root', +// template: '

Live reload test

' +// }) +// export class AppComponent { +// constructor(private http: Http) { +// http.get('http://${publicHost + '/live-reload-count'}').subscribe(res => null); +// } +// }`)) +// .then(_ => execAndWaitForOutputToMatch( +// 'ng', +// ['e2e', '--watch', '--host=0.0.0.0', '--port=4200', `--public-host=${publicHost}`, '--proxy', proxyConfigFile], +// protractorGoodRegEx +// )) +// .then(_ => wait(2000)) +// .then(_ => appendToFile('src/main.ts', 'console.log(1);')) +// .then(_ => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000)) +// .then(_ => wait(2000)) +// .then(_ => { +// if (liveReloadCount != 2) { +// throw new Error( +// `Expected API to have been called 2 times but it was called ${liveReloadCount} times.` +// ); +// } +// }) +// .then(_ => killAllProcesses(), (err) => { killAllProcesses(); throw err; }) +// .then(_ => server.close(), (err) => { server.close(); throw err; }); +// } diff --git a/tests/e2e/tests/third-party/bootstrap.ts b/tests/e2e/tests/third-party/bootstrap.ts index e2ec38c50207..bc6702dd341e 100644 --- a/tests/e2e/tests/third-party/bootstrap.ts +++ b/tests/e2e/tests/third-party/bootstrap.ts @@ -26,7 +26,7 @@ export default function() { `)) .then(() => ng( 'build', - '--prod', + '--target', 'production', '--extract-css', '--output-hashing=none' ))