diff --git a/README.md b/README.md index 799874f7e763..d8069cd4d5a6 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,10 @@ ng serve ``` Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. -You can configure the default HTTP port and the one used by the LiveReload server with two command-line options : +You can configure the default HTTP host and port used by the development server with two command-line options : ```bash -ng serve --host 0.0.0.0 --port 4201 --live-reload-port 49153 +ng serve --host 0.0.0.0 --port 4201 ``` ### Generating Components, Directives, Pipes and Services @@ -88,6 +88,7 @@ Directive | `ng g directive my-new-directive` Pipe | `ng g pipe my-new-pipe` Service | `ng g service my-new-service` Class | `ng g class my-new-class` +Guard | `ng g guard my-new-guard` Interface | `ng g interface my-new-interface` Enum | `ng g enum my-new-enum` Module | `ng g module my-module` @@ -111,7 +112,7 @@ npm install -g @angular/cli@latest Local project package: ```bash -rm -rf node_modules dist # use rmdir on Windows +rm -rf node_modules dist # use rmdir /S/Q node_modules dist in Windows Command Prompt; use rm -r -fo node_modules,dist in Windows PowerShell npm install --save-dev @angular/cli@latest npm install ``` diff --git a/docs/documentation/build.md b/docs/documentation/build.md index 67f0ac7c8f80..31f41dd2a9f0 100644 --- a/docs/documentation/build.md +++ b/docs/documentation/build.md @@ -103,8 +103,8 @@ or `ng serve --prod` will also make use of uglifying and tree-shaking functional `--target` (`-t`) Defines the build target. -`--vendor-chunk` (`-vb`) Use a separate bundle containing only vendor libraries. +`--vendor-chunk` (`-vc`) Use a separate bundle containing only vendor libraries. `--verbose` (`-v`) Adds more details to output logging. -`--watch` (`-w`) Run build when files change. \ No newline at end of file +`--watch` (`-w`) Run build when files change. diff --git a/docs/documentation/generate/interface.md b/docs/documentation/generate/interface.md index 7884e716e428..57a798de5d74 100644 --- a/docs/documentation/generate/interface.md +++ b/docs/documentation/generate/interface.md @@ -8,4 +8,4 @@ ## Options `--app` Specifies app name or index to use. -`type` Pptional String to specify the type of interface. +`type` Optional String to specify the type of interface. diff --git a/docs/documentation/generate/service.md b/docs/documentation/generate/service.md index 5bdad34d299e..9d7d3a02af85 100644 --- a/docs/documentation/generate/service.md +++ b/docs/documentation/generate/service.md @@ -10,6 +10,6 @@ `--flat` Flag to indicate if a dir is created. -`--module` (`-m`) Allows specification of the declaring module. +`--module` (`-m`) Allows you to specify the module where the service should be provided -`--spec` Specifies if a spec file is generated. \ No newline at end of file +`--spec` Specifies if a spec file is generated. diff --git a/docs/documentation/home.md b/docs/documentation/home.md index c56fee1226b4..de912fc3a752 100644 --- a/docs/documentation/home.md +++ b/docs/documentation/home.md @@ -50,9 +50,10 @@ End-to-end tests are run via [Protractor](https://angular.github.io/protractor/) * [ng e2e](e2e) * [ng build](build) * [ng get/ng set](config) -* [ng docs](docs) +* [ng doc](doc) * [ng eject](eject) +* [ng xi18n](xi18n) ### Additional Information There are several [stories](stories) which will walk you through setting up -additional aspects of Angular applciations. +additional aspects of Angular applications. diff --git a/docs/documentation/serve.md b/docs/documentation/serve.md index 253a6af29c27..6d722294bdc7 100644 --- a/docs/documentation/serve.md +++ b/docs/documentation/serve.md @@ -5,7 +5,7 @@ ## Overview `ng serve` builds the application and starts a web server -All the build Options are available in serve below are the additional options. +All the build Options are available in serve, below are the additional options. ## Options `--host` (`-H`) Listens only on localhost by default. @@ -24,4 +24,7 @@ All the build Options are available in serve below are the additional options. `--ssl-cert` SSL certificate to use for serving HTTPS. -`--ssl-key` SSL key to use for serving HTTPS. \ No newline at end of file +`--ssl-key` SSL key to use for serving HTTPS. + +## Note +When running `ng serve`, the compiled output is served from memory, not from disk. This means that the application being served is not located on disk in the `dist` folder. \ No newline at end of file diff --git a/docs/documentation/stories.md b/docs/documentation/stories.md index d3faa8d93447..558df442ccf9 100644 --- a/docs/documentation/stories.md +++ b/docs/documentation/stories.md @@ -20,3 +20,5 @@ - [Routing](stories/routing) - [3rd Party Lib](stories/third-party-lib) - [Corporate Proxy](stories/using-corporate-proxy) + - [Internationalization (i18n)](stories/internationalization) + - [Serve from Disk](stories/disk-serve) diff --git a/docs/documentation/stories/disk-serve.md b/docs/documentation/stories/disk-serve.md new file mode 100644 index 000000000000..b3f4663b630b --- /dev/null +++ b/docs/documentation/stories/disk-serve.md @@ -0,0 +1,23 @@ +# Serve from Disk + +The CLI supports running a live browser reload experience to users by running `ng serve`. This will compile the application upon file saves and reload the browser with the newly compiled application. This is done by hosting the application in memory and serving it via [webpack-dev-server](https://webpack.js.org/guides/development/#webpack-dev-server). + +If you wish to get a similar experience with the application output to disk please use the steps below. This practice will allow you to ensure that serving the contents of your `dist` dir will be closer to how your application will behave when it is deployed. + +## Environment Setup +### Install a web server +You will not be using webpack-dev-server, so you will need to install a web server for the browser to request the application. There are many to choose from but a good one to try is [lite-server](https://github.com/johnpapa/lite-server) as it will auto-reload your browser when new files are output. + +## Usage +You will need two terminals to get the live-reload experience. The first will run the build in a watch mode to compile the application to the `dist` folder. The second will run the web server against the `dist` folder. The combination of these two processes will mimic the same behavior of ng serve. + +### 1st terminal - Start the build +```bash +ng build --watch +``` + +### 2nd terminal - Start the web server +```bash +lite-server --baseDir="dist" +``` +When using `lite-server` the default browser will open to the appropriate URL. diff --git a/docs/documentation/stories/include-angular-material.md b/docs/documentation/stories/include-angular-material.md index d2e30afe514b..83ab16131cf1 100644 --- a/docs/documentation/stories/include-angular-material.md +++ b/docs/documentation/stories/include-angular-material.md @@ -24,7 +24,7 @@ import { MaterialModule } from '@angular/material'; @NgModule({ imports: [ ... - MaterialModule.forRoot() + MaterialModule ], ... }) diff --git a/docs/documentation/stories/internationalization.md b/docs/documentation/stories/internationalization.md new file mode 100644 index 000000000000..259d90d048fd --- /dev/null +++ b/docs/documentation/stories/internationalization.md @@ -0,0 +1,72 @@ +# Internationalization (i18n) + +If you are working on internationalization, the CLI can help you with the following steps: +- extraction +- serve +- build + +The first thing that you have to do is to setup your application to use i18n. +To do that you can follow [the cookbook on angular.io](https://angular.io/docs/ts/latest/cookbook/i18n.html). + +### Extraction +When your app is ready, you can extract the strings to translate from your templates with the +`ng xi18n` command. + +By default it will create a file named `messages.xlf` in your `src` folder. +You can use [parameters from the xi18n command](../xi18n) to change the format, +the name, the location and the source locale of the extracted file. + +For example to create a file in the `src/locale` folder you would use: +```sh +ng xi18n --output-path src/locale +``` + +### Serve +Now that you have generated a messages bundle source file, you can translate it. +Let's say that your file containing the french translations is named `messages.fr.xlf` +and is located in the `src/locale` folder. +If you want to use it when you serve your application you can use the 3 following commands: +- `--i18n-file` Localization file to use for i18n. +- `--i18n-format` Format of the localization file specified with --i18n-file. +- `--locale` Locale to use for i18n. + +In our case we can load the french translations with the following command: +```sh +ng serve --aot --locale fr --i18n-format xlf --i18n-file src/locale/messages.fr.xlf +``` + +Our application is exactly the same but the `LOCALE_ID` has been provided with "fr", +`TRANSLATIONS_FORMAT` with "xlf" and `TRANSLATIONS` with the content of `messages.fr.xlf`. +All the strings flagged for i18n have been replaced with their french translations. + +Note: this only works for AOT, if you want to use i18n in JIT you will have to update +your bootstrap file yourself. + +### Build +To build your application with a specific locale you can use the exact same commands +that you used for `serve`: +```sh +ng build --aot --locale fr --i18n-format xlf --i18n-file src/i18n/messages.fr.xlf +``` + +When you build your application for a specific locale, it is probably a good idea to change +the output path with the command `--output-path` in order to save the files to a different location. + +```sh +ng build --aot --output-path dist/fr --locale fr --i18n-format xlf --i18n-file src/i18n/messages.fr.xlf +``` + +If you end up serving this specific version from a subdirectory, you can also change +the base url used by your application with the command `--base-href`. + +For example if the french version of your application is served from https://myapp.com/fr/ +then you would build the french version like this: + +```sh +ng build --aot --output-path dist/fr --base-href fr --locale fr --i18n-format xlf --i18n-file src/i18n/messages.fr.xlf +``` + +If you need more details about how to create scripts to generate the app in multiple +languages and how to setup Apache 2 to serve them from different subdirectories, +you can read [this great tutorial](https://medium.com/@feloy/deploying-an-i18n-angular-app-with-angular-cli-fc788f17e358#.1xq4iy6fp) +by Philippe Martin. \ No newline at end of file diff --git a/docs/documentation/stories/rc-update.md b/docs/documentation/stories/rc-update.md index c366384e8360..697f2e016def 100644 --- a/docs/documentation/stories/rc-update.md +++ b/docs/documentation/stories/rc-update.md @@ -320,16 +320,17 @@ Packages in `dependencies`: - `@angular/*` packages now have a `^2.4.0` minimum (`^3.4.0` for router) - `core-js` remains unchanged at `^2.4.1` - `rxjs` to `^5.1.0` +- `ts-helpers` was **removed** - `zone.js` to `^0.7.6` -Packages in `dependencies`: +Packages in `devDependencies`: - `@angular/cli` at `1.0.0-rc.0` replaces `angular-cli` - `@angular/compiler-cli` is also at `^2.4.0` - `@types/jasmine` remains unchanged and pinned at `2.5.38` - `@types/node` was updated to `~6.0.60` - `codelyzer` was updated to `~2.0.0` - `jasmine-core` was updated to `~2.5.2` -- `jasmine-spec-reporter` was **removed** +- `jasmine-spec-reporter` was updated to `~3.2.0` - `karma` was updated to `~1.4.1` - `karma-chrome-launcher` was updated to `~2.0.0` - `karma-cli` was updated to `~1.0.1` @@ -353,6 +354,7 @@ We also updated the scripts section to make it more simple: "scripts": { "ng": "ng", "start": "ng serve", + "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/docs/documentation/test.md b/docs/documentation/test.md index f3bb95c2a396..18c3c633b282 100644 --- a/docs/documentation/test.md +++ b/docs/documentation/test.md @@ -20,8 +20,6 @@ You can run tests with coverage via `--code-coverage`. The coverage report will `--browsers` Override which browsers tests are run against. -`--build` Build prior to running tests. - `--code-coverage` Coverage report will be in the coverage/ directory. `--colors` Enable or disable colors in the output (reporters and logs). diff --git a/docs/documentation/xi18n.md b/docs/documentation/xi18n.md new file mode 100644 index 000000000000..fa2285d7be78 --- /dev/null +++ b/docs/documentation/xi18n.md @@ -0,0 +1,21 @@ + + +# ng xi18n + +## Overview +`ng xi18n` Extracts i18n messages from the templates. + +## Options +`--i18n-format` (`-f`) Output format for the generated file: either `xmb` or `xlf`. + +`--output-path` (`-op`) Path where output will be placed. + +`--locale` (`-l`) Specifies the source language of the application. + +`--outfile` (`-of`) Name of the file to output. + +`--verbose` Adds more details to output logging. + +`--progress` Log progress to the console while running. + +`--app` (`-a`) Specifies app name to use. \ No newline at end of file diff --git a/lib/bootstrap-local.js b/lib/bootstrap-local.js index a4043c34437f..645c540267e9 100644 --- a/lib/bootstrap-local.js +++ b/lib/bootstrap-local.js @@ -6,6 +6,8 @@ const path = require('path'); const ts = require('typescript'); +Error.stackTraceLimit = Infinity; + global.angularCliIsLocal = true; global.angularCliPackages = require('./packages'); diff --git a/packages/@angular/cli/blueprints/component/index.ts b/packages/@angular/cli/blueprints/component/index.ts index ef5d9bcbd45c..1cef6aa80120 100644 --- a/packages/@angular/cli/blueprints/component/index.ts +++ b/packages/@angular/cli/blueprints/component/index.ts @@ -12,6 +12,27 @@ const getFiles = Blueprint.prototype.files; const stringUtils = require('ember-cli-string-utils'); const astUtils = require('../../utilities/ast-utils'); +const viewEncapsulationMap: any = { + 'emulated': 'Emulated', + 'native': 'Native', + 'none': 'None' +}; + +const changeDetectionMap: any = { + 'default': 'Default', + 'onpush': 'OnPush' +}; + +function correctCase(options: any) { + if (options.viewEncapsulation) { + options.viewEncapsulation = viewEncapsulationMap[options.viewEncapsulation.toLowerCase()]; + } + + if (options.changeDetection) { + options.changeDetection = changeDetectionMap[options.changeDetection.toLowerCase()]; + } +} + export default Blueprint.extend({ description: '', @@ -95,8 +116,7 @@ export default Blueprint.extend({ } } else { try { - this.pathToModule = findParentModule( - this.project.root, appConfig.root, this.dynamicPath.dir); + this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath); } catch (e) { if (!options.skipImport) { throw `Error locating module for declaration\n\t${e}`; @@ -147,6 +167,8 @@ export default Blueprint.extend({ options.changeDetection = options.changeDetection !== undefined ? options.changeDetection : CliConfig.getValue('defaults.component.changeDetection'); + correctCase(options); + return { dynamicPath: this.dynamicPath.dir.replace(this.dynamicPath.appRoot, ''), flat: options.flat, diff --git a/packages/@angular/cli/blueprints/directive/index.ts b/packages/@angular/cli/blueprints/directive/index.ts index d5b02d81d22e..50ddaf00c0d7 100644 --- a/packages/@angular/cli/blueprints/directive/index.ts +++ b/packages/@angular/cli/blueprints/directive/index.ts @@ -70,8 +70,7 @@ export default Blueprint.extend({ } } else { try { - this.pathToModule = findParentModule - (this.project.root, appConfig.root, this.dynamicPath.dir); + this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath); } catch (e) { if (!options.skipImport) { throw `Error locating module for declaration\n\t${e}`; diff --git a/packages/@angular/cli/blueprints/module/files/__path__/__name__-routing.module.ts b/packages/@angular/cli/blueprints/module/files/__path__/__name__-routing.module.ts index 2d4459211035..9d822650edbb 100644 --- a/packages/@angular/cli/blueprints/module/files/__path__/__name__-routing.module.ts +++ b/packages/@angular/cli/blueprints/module/files/__path__/__name__-routing.module.ts @@ -5,7 +5,6 @@ const routes: Routes = []; @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule], - providers: [] + exports: [RouterModule] }) export class <%= classifiedModuleName %>RoutingModule { } diff --git a/packages/@angular/cli/blueprints/ng/files/README.md b/packages/@angular/cli/blueprints/ng/files/README.md index 69fb9c4a7b2b..2280408509ad 100755 --- a/packages/@angular/cli/blueprints/ng/files/README.md +++ b/packages/@angular/cli/blueprints/ng/files/README.md @@ -3,6 +3,7 @@ This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version <%= version %>. ## Development server + Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. ## Code scaffolding diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/app/app-routing.module.ts b/packages/@angular/cli/blueprints/ng/files/__path__/app/app-routing.module.ts index 03f6daa60bec..5b7d25b9a403 100644 --- a/packages/@angular/cli/blueprints/ng/files/__path__/app/app-routing.module.ts +++ b/packages/@angular/cli/blueprints/ng/files/__path__/app/app-routing.module.ts @@ -10,7 +10,6 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule], - providers: [] + exports: [RouterModule] }) export class AppRoutingModule { } diff --git a/packages/@angular/cli/blueprints/ng/files/e2e/tsconfig.e2e.json b/packages/@angular/cli/blueprints/ng/files/e2e/tsconfig.e2e.json index ff0fdbcff9a2..674dd643c913 100644 --- a/packages/@angular/cli/blueprints/ng/files/e2e/tsconfig.e2e.json +++ b/packages/@angular/cli/blueprints/ng/files/e2e/tsconfig.e2e.json @@ -10,7 +10,7 @@ "lib": [ "es2016" ],<% } %> - "outDir": "../dist/out-tsc-e2e", + "outDir": "../out-tsc/e2e", "module": "commonjs", "target": "es5", "types":[ diff --git a/packages/@angular/cli/blueprints/ng/files/gitignore b/packages/@angular/cli/blueprints/ng/files/gitignore index 8ce873857363..6708ebc05606 100755 --- a/packages/@angular/cli/blueprints/ng/files/gitignore +++ b/packages/@angular/cli/blueprints/ng/files/gitignore @@ -3,6 +3,7 @@ # compiled output /dist /tmp +/out-tsc # dependencies /node_modules @@ -36,6 +37,6 @@ testem.log /e2e/*.js /e2e/*.map -#System Files +# System Files .DS_Store Thumbs.db diff --git a/packages/@angular/cli/blueprints/ng/files/package.json b/packages/@angular/cli/blueprints/ng/files/package.json index f35a9e6c93a6..fc2f3609964d 100644 --- a/packages/@angular/cli/blueprints/ng/files/package.json +++ b/packages/@angular/cli/blueprints/ng/files/package.json @@ -40,7 +40,7 @@ "karma-coverage-istanbul-reporter": "^0.2.0", "protractor": "~5.1.0", "ts-node": "~2.0.0", - "tslint": "~4.4.2", + "tslint": "~4.5.0", "typescript": "<%= ng4 ? '~2.1.0' : '~2.0.0' %>" } } diff --git a/packages/@angular/cli/blueprints/pipe/index.ts b/packages/@angular/cli/blueprints/pipe/index.ts index 9cf81773a420..99c50281a8fe 100644 --- a/packages/@angular/cli/blueprints/pipe/index.ts +++ b/packages/@angular/cli/blueprints/pipe/index.ts @@ -65,8 +65,7 @@ export default Blueprint.extend({ } } else { try { - this.pathToModule = findParentModule - (this.project.root, appConfig.root, this.dynamicPath.dir); + this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath); } catch (e) { if (!options.skipImport) { throw `Error locating module for declaration\n\t${e}`; diff --git a/packages/@angular/cli/commands/completion.ts b/packages/@angular/cli/commands/completion.ts index 1147c7e48f6b..b755ab49ee0d 100644 --- a/packages/@angular/cli/commands/completion.ts +++ b/packages/@angular/cli/commands/completion.ts @@ -78,7 +78,7 @@ const CompletionCommand = Command.extend({ commandOptions.all = !commandOptions.bash && !commandOptions.zsh; const commandFiles = fs.readdirSync(__dirname) - .filter(file => file.match(/\.ts$/) && !file.match(/\.run.ts$/)) + .filter(file => file.match(/\.(j|t)s$/) && !file.match(/\.d.ts$/)) .map(file => path.parse(file).name) .filter(file => { return commandsToIgnore.indexOf(file) < 0; diff --git a/packages/@angular/cli/commands/generate.ts b/packages/@angular/cli/commands/generate.ts index 830a93a2d35d..ac3f3208d7b4 100644 --- a/packages/@angular/cli/commands/generate.ts +++ b/packages/@angular/cli/commands/generate.ts @@ -57,6 +57,7 @@ const aliasMap: { [alias: string]: string } = { 'c': 'component', 'd': 'directive', 'e': 'enum', + 'g': 'guard', 'i': 'interface', 'm': 'module', 'p': 'pipe', diff --git a/packages/@angular/cli/commands/set.ts b/packages/@angular/cli/commands/set.ts index f5de2ba9f056..f916aaf60615 100644 --- a/packages/@angular/cli/commands/set.ts +++ b/packages/@angular/cli/commands/set.ts @@ -64,7 +64,7 @@ const SetCommand = Command.extend({ case 'number': value = this.asNumber(rawValue); break; case 'string': value = rawValue; break; - default: value = JSON.parse(rawValue); + default: value = parseValue(rawValue, jsonPath); } config.set(jsonPath, value); @@ -74,4 +74,12 @@ const SetCommand = Command.extend({ } }); +function parseValue(rawValue: string, path: string) { + try { + return JSON.parse(rawValue); + } catch (error) { + throw new SilentError(`No node found at path ${path}`); + } +} + export default SetCommand; diff --git a/packages/@angular/cli/commands/test.ts b/packages/@angular/cli/commands/test.ts index 22c1f745dc02..655612009d3a 100644 --- a/packages/@angular/cli/commands/test.ts +++ b/packages/@angular/cli/commands/test.ts @@ -15,7 +15,6 @@ export interface TestOptions { log?: string; port?: number; reporters?: string; - build?: boolean; sourcemap?: boolean; progress?: boolean; config: string; @@ -85,12 +84,6 @@ const TestCommand = EmberTestCommand.extend({ type: String, description: 'List of reporters to use.' }, - { - name: 'build', - type: Boolean, - default: true, - description: 'Build prior to running tests.' - }, { name: 'sourcemap', type: Boolean, diff --git a/packages/@angular/cli/ember-cli/lib/ui/index.js b/packages/@angular/cli/ember-cli/lib/ui/index.js index 7837f04e0f52..3e749d1a0166 100644 --- a/packages/@angular/cli/ember-cli/lib/ui/index.js +++ b/packages/@angular/cli/ember-cli/lib/ui/index.js @@ -174,12 +174,10 @@ UI.prototype.prompt = function(questions, callback) { // If no callback was provided, automatically return a promise if (callback) { - inquirer.prompt(questions, callback); - } else { - return new Promise(function(resolve) { - inquirer.prompt(questions, resolve); - }); + return inquirer.prompt(questions, callback); } + + return inquirer.prompt(questions); }; /** diff --git a/packages/@angular/cli/lib/cli/index.js b/packages/@angular/cli/lib/cli/index.js index 10952734a829..3e0f3c056be9 100644 --- a/packages/@angular/cli/lib/cli/index.js +++ b/packages/@angular/cli/lib/cli/index.js @@ -12,7 +12,6 @@ const UI = require('../../ember-cli/lib/ui'); const Watcher = require('../../ember-cli/lib/models/watcher'); const path = require('path'); -Error.stackTraceLimit = Infinity; module.exports = function(options) { diff --git a/packages/@angular/cli/models/webpack-configs/styles.ts b/packages/@angular/cli/models/webpack-configs/styles.ts index fa53e3165b85..f3666c8b7545 100644 --- a/packages/@angular/cli/models/webpack-configs/styles.ts +++ b/packages/@angular/cli/models/webpack-configs/styles.ts @@ -48,8 +48,8 @@ export function getStylesConfig(wco: WebpackConfigOptions) { const deployUrl = wco.buildOptions.deployUrl; const postcssUrlOptions = { url: (URL: string) => { - // Only convert absolute URLs, which CSS-Loader won't process into require(). - if (!URL.startsWith('/')) { + // Only convert root relative URLs, which CSS-Loader won't process into require(). + if (!URL.startsWith('/') || URL.startsWith('//')) { return URL; } // Join together base-href, deploy-url and the original URL. diff --git a/packages/@angular/cli/tasks/extract-i18n.ts b/packages/@angular/cli/tasks/extract-i18n.ts index 278223162a35..eac76cb69b56 100644 --- a/packages/@angular/cli/tasks/extract-i18n.ts +++ b/packages/@angular/cli/tasks/extract-i18n.ts @@ -54,7 +54,6 @@ export const Extracti18nTask = Task.extend({ this.ui.writeError('\nAn error occured during the i18n extraction:\n' + ((err && err.stack) || err)); } - throw err; }); } }); diff --git a/packages/@angular/cli/tasks/lint.ts b/packages/@angular/cli/tasks/lint.ts index ef63b2dc3d4e..12cf4c4c9725 100644 --- a/packages/@angular/cli/tasks/lint.ts +++ b/packages/@angular/cli/tasks/lint.ts @@ -33,11 +33,8 @@ export default Task.extend({ const Linter = tslint.Linter; const Configuration = tslint.Configuration; - let errors = 0; - let results = ''; - - lintConfigs - .forEach((config) => { + const result = lintConfigs + .map((config) => { const program: ts.Program = Linter.createProgram(config.project); const files = getFilesToLint(program, config, Linter); @@ -56,19 +53,37 @@ export default Task.extend({ linter.lint(file, fileContents, configLoad.results); }); - const result = linter.getResult(); - errors += result.failureCount; - results = results.concat(result.output.trim().concat('\n')); + return linter.getResult(); + }) + .reduce((total, current) => { + const failures = current.failures + .filter((cf: any) => !total.failures.some((ef: any) => ef.equals(cf))); + total.failures = total.failures.concat(...failures); + + if (current.fixes) { + total.fixes = (total.fixes || []).concat(...current.fixes); + } + return total; + }, { + failures: [], + fixes: undefined }); + const Formatter = tslint.findFormatter(commandOptions.format); + const formatter = new Formatter(); + + const output = formatter.format(result.failures, result.fixes); + if (output) { + ui.writeLine(output); + } + // print formatter output directly for non human-readable formats if (['prose', 'verbose', 'stylish'].indexOf(commandOptions.format) == -1) { - ui.writeLine(results.trim()); - return (errors == 0 || commandOptions.force) ? Promise.resolve(0) : Promise.resolve(2); + return (result.failures.length == 0 || commandOptions.force) + ? Promise.resolve(0) : Promise.resolve(2); } - if (errors > 0) { - ui.writeLine(results.trim()); + if (result.failures.length > 0) { ui.writeLine(chalk.red('Lint errors found in the listed files.')); return commandOptions.force ? Promise.resolve(0) : Promise.resolve(2); } diff --git a/packages/@angular/cli/tasks/npm-install.ts b/packages/@angular/cli/tasks/npm-install.ts index e362b2a65d4d..c7a846590cb7 100644 --- a/packages/@angular/cli/tasks/npm-install.ts +++ b/packages/@angular/cli/tasks/npm-install.ts @@ -13,7 +13,11 @@ export default Task.extend({ return new Promise(function(resolve, reject) { ui.writeLine(chalk.green(`Installing packages for tooling via ${packageManager}.`)); - exec(`${packageManager} --quiet install`, + let installCommand = `${packageManager} --quiet install`; + if (packageManager === 'yarn') { + installCommand = `${packageManager} install`; + } + exec(installCommand, (err: NodeJS.ErrnoException, _stdout: string, stderr: string) => { if (err) { ui.writeLine(stderr); diff --git a/packages/@angular/cli/utilities/find-parent-module.ts b/packages/@angular/cli/utilities/find-parent-module.ts index 3236167121cf..61be86780b5e 100644 --- a/packages/@angular/cli/utilities/find-parent-module.ts +++ b/packages/@angular/cli/utilities/find-parent-module.ts @@ -13,6 +13,10 @@ export default function findParentModule( let pathToCheck = path.join(sourceRoot, currentDir); while (pathToCheck.length >= sourceRoot.length) { + if (!fs.existsSync(pathToCheck)) { + pathToCheck = path.dirname(pathToCheck); + continue; + } // TODO: refactor to not be based upon file name const files = fs.readdirSync(pathToCheck) .filter(fileName => !fileName.endsWith('routing.module.ts')) diff --git a/packages/@ngtools/json-schema/src/schema-class-factory.ts b/packages/@ngtools/json-schema/src/schema-class-factory.ts index eebde99abf8c..effe0b0ab903 100644 --- a/packages/@ngtools/json-schema/src/schema-class-factory.ts +++ b/packages/@ngtools/json-schema/src/schema-class-factory.ts @@ -51,7 +51,13 @@ function _getSchemaNodeForPath(rootMetaData: SchemaTreeNode, let fragments = _parseJsonPath(path); // TODO: make this work with union (oneOf) schemas return fragments.reduce((md: SchemaTreeNode, current: string) => { - return md && md.children && md.children[current]; + if (md && md.children) { + return md.children[current]; + } else if (md && md.items) { + return md.items[parseInt(current, 10)]; + } else { + return md; + } }, rootMetaData); } diff --git a/packages/@ngtools/webpack/src/extract_i18n_plugin.ts b/packages/@ngtools/webpack/src/extract_i18n_plugin.ts index 10a087343abd..06168b9b3f24 100644 --- a/packages/@ngtools/webpack/src/extract_i18n_plugin.ts +++ b/packages/@ngtools/webpack/src/extract_i18n_plugin.ts @@ -181,8 +181,8 @@ export class ExtractI18nPlugin implements Tapable { }); }) .then(() => cb(), (err: any) => { - compilation.errors.push(err); - cb(); + this._compilation.errors.push(err); + cb(err); }); } diff --git a/tests/e2e/tests/commands/completion/completion.ts b/tests/e2e/tests/commands/completion/completion.ts new file mode 100644 index 000000000000..b88821e8c3ab --- /dev/null +++ b/tests/e2e/tests/commands/completion/completion.ts @@ -0,0 +1,9 @@ +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/get/get.ts b/tests/e2e/tests/commands/get/get.ts new file mode 100644 index 000000000000..b66fb2e9ceb1 --- /dev/null +++ b/tests/e2e/tests/commands/get/get.ts @@ -0,0 +1,13 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => ng('get', 'apps.zzz.prefix'))) + .then(() => ng('get', 'apps.0.prefix')) + .then(({ stdout }) => { + if (!stdout.match(/app/)) { + throw new Error(`Expected "app", received "${JSON.stringify(stdout)}".`); + } + }); +} diff --git a/tests/e2e/tests/commands/set/set.ts b/tests/e2e/tests/commands/set/set.ts new file mode 100644 index 000000000000..da82c129e46b --- /dev/null +++ b/tests/e2e/tests/commands/set/set.ts @@ -0,0 +1,14 @@ +import {ng} from '../../../utils/process'; +import {expectToFail} from '../../../utils/utils'; + +export default function() { + return Promise.resolve() + .then(() => expectToFail(() => ng('set', 'apps.zzz.prefix'))) + .then(() => ng('set', 'apps.0.prefix' , 'new-prefix')) + .then(() => ng('get', 'apps.0.prefix')) + .then(({ stdout }) => { + if (!stdout.match(/new-prefix/)) { + throw new Error(`Expected "new-prefix", received "${JSON.stringify(stdout)}".`); + } + }); +} diff --git a/tests/e2e/tests/generate/component/component-flag-case.ts b/tests/e2e/tests/generate/component/component-flag-case.ts new file mode 100644 index 000000000000..dbbee3525756 --- /dev/null +++ b/tests/e2e/tests/generate/component/component-flag-case.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {ng} from '../../../utils/process'; +import {expectFileToMatch} from '../../../utils/fs'; + + +export default function() { + const compDir = join('src', 'app', 'test'); + + return Promise.resolve() + .then(() => ng('generate', 'component', 'test', '-cd', 'onpush', '-ve', 'emulated')) + .then(() => expectFileToMatch(join(compDir, 'test.component.ts'), + /changeDetection: ChangeDetectionStrategy.OnPush/)) + .then(() => expectFileToMatch(join(compDir, 'test.component.ts'), + /encapsulation: ViewEncapsulation.Emulated/)); +} diff --git a/tests/e2e/tests/generate/component/component-in-existing-module-dir.ts b/tests/e2e/tests/generate/component/component-in-existing-module-dir.ts new file mode 100644 index 000000000000..bb98447bb4a1 --- /dev/null +++ b/tests/e2e/tests/generate/component/component-in-existing-module-dir.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { ng } from '../../../utils/process'; +import { expectFileToMatch } from '../../../utils/fs'; + +export default function () { + const modulePath = join('src', 'app', 'foo', 'foo.module.ts'); + + return Promise.resolve() + .then(() => ng('generate', 'module', 'foo')) + .then(() => ng('generate', 'component', 'foo')) + .then(() => expectFileToMatch(modulePath, /import { FooComponent } from '.\/foo.component'/)); +} diff --git a/tests/e2e/tests/generate/directive/directive-in-existing-module-dir.ts b/tests/e2e/tests/generate/directive/directive-in-existing-module-dir.ts new file mode 100644 index 000000000000..7dd270fe81a1 --- /dev/null +++ b/tests/e2e/tests/generate/directive/directive-in-existing-module-dir.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { ng } from '../../../utils/process'; +import { expectFileToMatch } from '../../../utils/fs'; + +export default function () { + const modulePath = join('src', 'app', 'foo', 'foo.module.ts'); + + return Promise.resolve() + .then(() => ng('generate', 'module', 'foo')) + .then(() => ng('generate', 'directive', 'foo', '--no-flat')) + .then(() => expectFileToMatch(modulePath, /import { FooDirective } from '.\/foo.directive'/)); +} diff --git a/tests/e2e/tests/generate/pipe/pipe-in-existing-module-dir.ts b/tests/e2e/tests/generate/pipe/pipe-in-existing-module-dir.ts new file mode 100644 index 000000000000..2055643a1b5b --- /dev/null +++ b/tests/e2e/tests/generate/pipe/pipe-in-existing-module-dir.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { ng } from '../../../utils/process'; +import { expectFileToMatch } from '../../../utils/fs'; + +export default function () { + const modulePath = join('src', 'app', 'foo', 'foo.module.ts'); + + return Promise.resolve() + .then(() => ng('generate', 'module', 'foo')) + .then(() => ng('generate', 'pipe', 'foo', '--no-flat')) + .then(() => expectFileToMatch(modulePath, /import { FooPipe } from '.\/foo.pipe'/)); +}