diff --git a/.appveyor.yml b/.appveyor.yml index eb3e089fd20b..20d541241449 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,13 +7,16 @@ matrix: install: - ps: Install-Product node $env:nodejs_version - - npm install -g npm@~5.3.0 + - npm install -g npm@~5.6.0 - npm install + - choco install googlechrome test_script: - node --version - npm --version - - node tests\run_e2e.js --appveyor + - npm run test:packages + - npm run test:cli + - node tests\run_e2e.js --appveyor "--glob=tests/{basic,commands,generate,build/styles}/**" build: off diff --git a/.circleci/config.yml b/.circleci/config.yml index 85d7be1c8ca1..c460fbb14fec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ version: 2 anchor_1: &defaults working_directory: ~/angular-cli docker: - - image: angular/ngcontainer + - image: angular/ngcontainer:0.0.5 # Restore cache based on package-lock.json checksum for branch. anchor_2: &restore_cache_defaults @@ -25,7 +25,7 @@ jobs: <<: *restore_cache_defaults - run: node --version - run: npm --version - - run: npm install --quiet + - run: npm install --no-save - run: npm run build - save_cache: key: angular-cli-{{ checksum "package-lock.json" }} @@ -48,7 +48,7 @@ jobs: <<: *restore_cache_defaults - run: npm run test -# E2E test for current Angular. + # E2E test for current Angular. e2e-setup: <<: *defaults steps: @@ -110,67 +110,30 @@ jobs: - run: cp -r /workspace/dist/ ./ - run: xvfb-run -a node tests/run_e2e.js --nobuild --reuse=/workspace/angular-cli-e2e-default/test-project --nb-shards=4 --shard=3 --nosilent -# E2E test for Angular nightly. - e2e-setup-nightly: + # Master only E2E. + ng2-e2e: <<: *defaults steps: - checkout - restore_cache: <<: *restore_cache_defaults - - run: mkdir /workspace - - run: mkdir /workspace/angular-cli-e2e-nightly - # Ignore all tests, we just want the setup step to persist it to the workspace. - - run: node tests/run_e2e.js --tmpdir=/workspace/angular-cli-e2e-nightly --ignore=**/* --nightly - - run: mv dist /workspace/ - - persist_to_workspace: - root: /workspace - paths: - - dist/ - - angular-cli-e2e-nightly/ - - e2e-0-nightly: - <<: *defaults - steps: - - checkout - - restore_cache: - <<: *restore_cache_defaults - - attach_workspace: - <<: *attach_workspace_defaults - - run: cp -r /workspace/dist/ ./ - - run: xvfb-run -a node tests/run_e2e.js --nobuild --reuse=/workspace/angular-cli-e2e-nightly/test-project --nb-shards=4 --shard=0 --nosilent --nightly + - run: xvfb-run -a node tests/run_e2e.js --ng2 "--glob=tests/build/**" - e2e-1-nightly: + ng4-e2e: <<: *defaults steps: - checkout - restore_cache: <<: *restore_cache_defaults - - attach_workspace: - <<: *attach_workspace_defaults - - run: cp -r /workspace/dist/ ./ - - run: xvfb-run -a node tests/run_e2e.js --nobuild --reuse=/workspace/angular-cli-e2e-nightly/test-project --nb-shards=4 --shard=1 --nosilent --nightly + - run: xvfb-run -a node tests/run_e2e.js --ng4 "--glob=tests/build/**" - e2e-2-nightly: + nightly-e2e: <<: *defaults steps: - checkout - restore_cache: <<: *restore_cache_defaults - - attach_workspace: - <<: *attach_workspace_defaults - - run: cp -r /workspace/dist/ ./ - - run: xvfb-run -a node tests/run_e2e.js --nobuild --reuse=/workspace/angular-cli-e2e-nightly/test-project --nb-shards=4 --shard=2 --nosilent --nightly - - e2e-3-nightly: - <<: *defaults - steps: - - checkout - - restore_cache: - <<: *restore_cache_defaults - - attach_workspace: - <<: *attach_workspace_defaults - - run: cp -r /workspace/dist/ ./ - - run: xvfb-run -a node tests/run_e2e.js --nobuild --reuse=/workspace/angular-cli-e2e-nightly/test-project --nb-shards=4 --shard=3 --nosilent --nightly + - run: xvfb-run -a node tests/run_e2e.js --nightly "--glob=tests/build/**" workflows: version: 2 @@ -199,23 +162,25 @@ workflows: - e2e-3: requires: - e2e-setup - # E2E test for Angular nightly. - - e2e-setup-nightly: + # Master only E2E. + - ng2-e2e: requires: - build filters: branches: only: - master - - e2e-0-nightly: - requires: - - e2e-setup-nightly - - e2e-1-nightly: - requires: - - e2e-setup-nightly - - e2e-2-nightly: + - ng4-e2e: requires: - - e2e-setup-nightly - - e2e-3-nightly: + - build + filters: + branches: + only: + - master + - nightly-e2e: requires: - - e2e-setup-nightly + - build + filters: + branches: + only: + - master diff --git a/.travis.yml b/.travis.yml index 9b50b6185d4c..2d1b4286561a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,13 @@ cache: directories: - ./node_modules +stages: + - build + - test + - name: optional + if: (NOT type IN (pull_request)) AND (branch = master) + - deploy + matrix: fast_finish: true allow_failures: @@ -18,12 +25,14 @@ matrix: - node_js: "7" - node_js: "8" include: + # Build stage - stage: build script: npm run lint env: lint - script: npm run build env: build + # Test stage - stage: test script: npm run test env: test @@ -43,46 +52,35 @@ matrix: os: linux script: node tests/run_e2e.js --nb-shards=4 --shard=3 --nosilent env: e2e-3 - - - node_js: "6" - os: linux - script: node tests/run_e2e.js --nb-shards=4 --shard=0 --nosilent --nightly - env: nightly-0 - - node_js: "6" - os: linux - script: node tests/run_e2e.js --nb-shards=4 --shard=1 --nosilent --nightly - env: nightly-1 - - node_js: "6" - os: linux - script: node tests/run_e2e.js --nb-shards=4 --shard=2 --nosilent --nightly - env: nightly-2 - - node_js: "6" - os: linux - script: node tests/run_e2e.js --nb-shards=4 --shard=3 --nosilent --nightly - env: nightly-3 - - node_js: "6" os: linux script: node tests/run_e2e.js --eject "--glob=tests/build/**" env: eject - # Optional builds. - - node_js: "6" + # Optional stage. + - stage: optional + node_js: "6" os: linux - before_script: if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then exit 0; fi - script: node tests/run_e2e.js --ng2 "--glob=tests/{build,test,misc}/**" + script: node tests/run_e2e.js --ng2 "--glob=tests/build/**" env: ng2 + - node_js: "6" + os: linux + script: node tests/run_e2e.js --ng4 "--glob=tests/build/**" + env: ng4 + - node_js: "6" + os: linux + script: node tests/run_e2e.js --nightly "--glob=tests/build/**" + env: nightly - node_js: "7" os: linux - before_script: if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then exit 0; fi - script: node tests/run_e2e.js "--glob=tests/{build,test,misc}/**" + script: node tests/run_e2e.js "--glob=tests/build/**" env: node7 - node_js: "8" os: linux - before_script: if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then exit 0; fi script: node tests/run_e2e.js "--glob=tests/build/**" env: node8 + # Deploy stage - stage: deploy script: skip env: builds @@ -92,8 +90,7 @@ matrix: skip_cleanup: true on: all_branches: true - - stage: deploy - script: skip + - script: skip env: publish deploy: - provider: script diff --git a/docs/documentation/build.md b/docs/documentation/build.md index 2aa151aff024..19fe3371f891 100644 --- a/docs/documentation/build.md +++ b/docs/documentation/build.md @@ -83,13 +83,20 @@ Flag | `--dev` | `--prod` `--named-chunks`   | `true` | `false` `--build-optimizer` | `false` | `true` with AOT and Angular 5 -`--extract-licenses` Extract all licenses in a separate file, in the case of production builds only. -`--i18n-file` Localization file to use for i18n. `--prod` also sets the following non-flaggable settings: - Adds service worker if configured in `.angular-cli.json`. - Replaces `process.env.NODE_ENV` in modules with the `production` value (this is needed for some libraries, like react). - Runs UglifyJS on the code. +### `--build-optimizer` and `--vendor-chunk` + +When using Build Optimizer the vendor chunk will be disabled by default. +You can override this with `--vendor-chunk=true`. + +Total bundle sizes with Build Optimizer are smaller if there is no separate vendor chunk because +having vendor code in the same chunk as app code makes it possible for Uglify to remove more unused +code. + ### CSS resources Resources in CSS, such as images and fonts, will be copied over automatically as part of a build. @@ -111,6 +118,16 @@ Remember to disable the service worker while developing to avoid stale code. Note: service worker support is experimental and subject to change. +### ES2015 support + +To build in ES2015 mode, edit `./tsconfig.json` to use `"target": "es2015"` (instead of `es5`). + +This will cause application TypeScript and Uglify be output as ES2015, and third party libraries +to be loaded through the `es2015` entry in `package.json` if available. + +Be aware that JIT does not support ES2015 and so you should build/serve your app with `--aot`. +See https://github.com/angular/angular-cli/issues/7797 for details. + ## Options
aot @@ -377,3 +394,14 @@ Note: service worker support is experimental and subject to change. In a server build, state whether `all` or `none` dependencies should be bundles in the output.

+ + +
+ extract-licenses +

+ --extract-licenses default value: true +

+

+ Extract all licenses in a separate file, in the case of production builds only. +

+
diff --git a/docs/documentation/e2e.md b/docs/documentation/e2e.md index c78606d3ae8c..ebfaf64e8d96 100644 --- a/docs/documentation/e2e.md +++ b/docs/documentation/e2e.md @@ -23,7 +23,7 @@ Please note that options that are supported by `ng serve` are also supported by --config (aliases: -c)

- Use a specific config file. Defaults to the protractor config file in `.angular-cli.json`. + Use a specific config file. Defaults to the protractor config file in .angular-cli.json.

@@ -33,7 +33,7 @@ Please note that options that are supported by `ng serve` are also supported by --element-explorer (aliases: -ee) default value: false

- Start Protractor's [Element Explorer](https://github.com/angular/protractor/blob/master/docs/debugging.md#testing-out-protractor-interactively) for debugging. + Start Protractor's Element Explorer for debugging.

@@ -56,7 +56,17 @@ Please note that options that are supported by `ng serve` are also supported by --specs (aliases: -sp) default value: []

- Override specs in the protractor config. Can send in multiple specs by repeating flag (ng e2e --specs=spec1.ts --specs=spec2.ts). + Override specs in the protractor config. Can send in multiple specs by repeating flag (ng e2e --specs=spec1.ts --specs=spec2.ts). +

+ + +
+ suite +

+ --suite (aliases: -su) +

+

+ Override suite in the protractor config. Can send in multiple suite by comma separated values (ng e2e --suite=suiteA,suiteB).

diff --git a/docs/documentation/stories.md b/docs/documentation/stories.md index babf2802e116..7b5d3f6703d0 100644 --- a/docs/documentation/stories.md +++ b/docs/documentation/stories.md @@ -14,6 +14,7 @@ - [Angular Material](stories/include-angular-material) - [AngularFire](stories/include-angularfire) - [Bootstrap](stories/include-bootstrap) + - [Budgets](stories/budgets) - [Font Awesome](stories/include-font-awesome) - [Moving Into the CLI](stories/moving-into-the-cli) - [Moving Out of the CLI](stories/moving-out-of-the-cli) diff --git a/docs/documentation/stories/budgets.md b/docs/documentation/stories/budgets.md new file mode 100644 index 000000000000..33c863465078 --- /dev/null +++ b/docs/documentation/stories/budgets.md @@ -0,0 +1,61 @@ +# Budgets + +As applications grow in functionality, they also grow in size. Budgets is a feature in the +Angular CLI which allows you to set budget thresholds in your configuration to ensure parts +of your application stay within boundries which you set. + +**.angular-cli.json** +``` +{ + ... + apps: [ + { + ... + budgets: [] + } + ] +} +``` + +## Budget Definition + +- type + - The type of budget. + - Possible values: + - bundle - The size of a specific bundle. + - initial - The initial size of the app. + - allScript - The size of all scripts. + - all - The size of the entire app. + - anyScript - The size of any one script. + - any - The size of any file. +- name + - The name of the bundle. + - Required only for type of "bundle" +- baseline + - The baseline size for comparison. +- maximumWarning + - The maximum threshold for warning relative to the baseline. +- maximumError + - The maximum threshold for error relative to the baseline. +- minimumWarning + - The minimum threshold for warning relative to the baseline. +- minimumError + - The minimum threshold for error relative to the baseline. +- warning + - The threshold for warning relative to the baseline (min & max). +- error + - The threshold for error relative to the baseline (min & max). + +## Specifying sizes + +Available formats: +123 - size in bytes +123b - size in bytes +123kb - size in kilobytes +123mb - size in megabytes +12% - percentage + +## NOTES + +All sizes are relative to baseline. +Percentages are not valid for baseline values. diff --git a/docs/documentation/stories/configure-hmr.md b/docs/documentation/stories/configure-hmr.md index 85d21d6dfb77..daa0c3ca56e6 100644 --- a/docs/documentation/stories/configure-hmr.md +++ b/docs/documentation/stories/configure-hmr.md @@ -3,7 +3,7 @@ Hot Module Replacement (HMR) is a WebPack feature to update code in a running app without rebuilding it. This results in faster updates and less full page-reloads. -You can read more about HMR by visiting [this page](https://webpack.github.io/docs/hot-module-replacement.html). +You can read more about HMR by visiting [this page](https://webpack.js.org/guides/hot-module-replacement). In order to get HMR working with Angular CLI we first need to add a new environment and enable it. diff --git a/docs/documentation/stories/continuous-integration.md b/docs/documentation/stories/continuous-integration.md index 1745f8cf4240..ba24aba1a07c 100644 --- a/docs/documentation/stories/continuous-integration.md +++ b/docs/documentation/stories/continuous-integration.md @@ -119,6 +119,13 @@ sudo: false language: node_js node_js: - "6" + +addons: + apt: + sources: + - google-chrome + packages: + - google-chrome-stable cache: directories: diff --git a/docs/documentation/stories/css-preprocessors.md b/docs/documentation/stories/css-preprocessors.md index b897b0893af0..58bd0071deab 100644 --- a/docs/documentation/stories/css-preprocessors.md +++ b/docs/documentation/stories/css-preprocessors.md @@ -30,3 +30,5 @@ Or set the default style on an existing project: ```bash ng set defaults.styleExt scss ``` + +Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a pre-processor to inline styles. \ No newline at end of file diff --git a/docs/documentation/stories/include-bootstrap.md b/docs/documentation/stories/include-bootstrap.md index 3debf5d279f1..b5d244342113 100644 --- a/docs/documentation/stories/include-bootstrap.md +++ b/docs/documentation/stories/include-bootstrap.md @@ -117,7 +117,11 @@ Verify the bootstrap styled button appears. To ensure your variables are used open `_variables.scss` and add the following: ```sass +// version 3 $brand-primary: red; + +// version 4 +$primary: red; ``` Return the browser to see the font color changed. diff --git a/docs/documentation/stories/proxy.md b/docs/documentation/stories/proxy.md index 3298b5f97c79..12e35c9bda14 100644 --- a/docs/documentation/stories/proxy.md +++ b/docs/documentation/stories/proxy.md @@ -1,6 +1,6 @@ # Proxy To Backend -Using the [proxying support](https://webpack.github.io/docs/webpack-dev-server.html#proxy) in webpack's dev server we can highjack certain URLs and send them to a backend server. +Using the [proxying support](https://webpack.js.org/configuration/dev-server/#devserver-proxy) in webpack's dev server we can highjack certain URLs and send them to a backend server. We do this by passing a file to `--proxy-config` Say we have a server running on `http://localhost:3000/api` and we want all calls to `http://localhost:4200/api` to go to that server. @@ -16,7 +16,7 @@ We create a file next to our project's `package.json` called `proxy.conf.json` w } ``` -You can read more about what options are available [here](https://webpack.github.io/docs/webpack-dev-server.html#proxy). +You can read more about what options are available [here](https://webpack.js.org/configuration/dev-server/#devserver-proxy). We can then edit the `package.json` file's start script to be @@ -26,6 +26,8 @@ We can then edit the `package.json` file's start script to be Now in order to run our dev server with our proxy config, we can simply call `npm start`. +**After each edit to the proxy.conf.json file remember to relaunch the `npm start` process to make your changes effective.** + ### Rewriting the URL path One option that comes up a lot is rewriting the URL path for the proxy. This is supported by the `pathRewrite` option. diff --git a/package-lock.json b/package-lock.json index 28e68ef39edb..a5c1e1c01d45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,37 +1,58 @@ { "name": "@angular/cli", - "version": "1.6.0-beta.2", + "version": "1.7.0-beta.1", "lockfileVersion": 1, "requires": true, "dependencies": { "@angular-devkit/build-optimizer": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.28.tgz", - "integrity": "sha512-rG5sGV5a2F3R5E+8WRnvq/UPDY2uN5iQjMBDsId1oILjBSAUSwOZM10zQ3De+sd89CmT/R6aSkr6YtbkYn8G0Q==", + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.0.41.tgz", + "integrity": "sha512-TYolYpR3cb3rZGMqHBc1Q4r6SwhvuTF9sDt3gARy9Tli5R3BwY8bRA3evHQhhM0cb4AbBaJVgQYdJm5FURNn6g==", "requires": { "loader-utils": "1.1.0", "source-map": "0.5.7", - "typescript": "2.4.2", + "typescript": "2.6.2", "webpack-sources": "1.0.1" + }, + "dependencies": { + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=" + } } }, "@angular-devkit/core": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.20.tgz", - "integrity": "sha512-lg5BvMxOfbVD//SOQvpq6TPIKTXYNMj0I9N/kfXbXkUGgiBGFLyFMf2fc+qNvDoa7lulKMPT8OJWS1YlGt93eg==", + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.0.28.tgz", + "integrity": "sha512-rfGRVdpx080zZq9NGZ3RNG+cmoq/ZPaCzpM4dAbosEM46ficUkwr/JKjhjZUUoSyb9ItrT1lp9C33GfE/YpSVQ==", "requires": { + "ajv": "5.5.2", + "chokidar": "1.7.0", + "rxjs": "5.5.6", "source-map": "0.5.7" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + } } }, "@angular-devkit/schematics": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.35.tgz", - "integrity": "sha512-+qGUWhmMpHqHkYKMk1yKQDjXb/vqXGkzbMiRs/u5rSnlrH+/TzkCO0UsM7/p9WPcModuDxkf5FItpw/AgdcPeQ==", + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.0.51.tgz", + "integrity": "sha512-KGYiJpPgLqPvXeJzaHw/9JHAt8dL3EzyeYOAZ/gMKnPDA+7WypAta2n9ZuPVchY3G9p1XcqB5STJLvk7HQw3Cw==", "requires": { - "@angular-devkit/core": "0.0.20", "@ngtools/json-schema": "1.1.0", - "minimist": "1.2.0", - "rxjs": "5.5.2" + "rxjs": "5.5.6" } }, "@angular/compiler": { @@ -79,11 +100,18 @@ "integrity": "sha1-w6DFRNYjkqzCgTpCyKDcb1j4aSI=" }, "@schematics/angular": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.0.tgz", - "integrity": "sha512-+Yy72J55uImsROxwyyEMso+HJIvx7+ffT8o8HzdNOZyLg4jj7G/ZDiCsCmhRtTYOmOof4OqvF2VecJyXVi0oHA==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.1.16.tgz", + "integrity": "sha512-SLzs5TmDLEO2/oU83uDw/x9jgBQTktNlO202kbawcqDKLekqgcZXnsl7UhoNdeCFOU9BE9dw07wWwuIzAhj9gQ==", "requires": { - "@angular-devkit/core": "0.0.20" + "typescript": "2.6.2" + }, + "dependencies": { + "typescript": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=" + } } }, "@types/common-tags": { @@ -161,6 +189,16 @@ "integrity": "sha1-WJKKYh0BTOarWcWpxBBx9zKLDKk=", "dev": true }, + "@types/loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha512-VR4oHG6TzhpemxtBDf0BD8xlOiPo2B6zcFEA2Jjmgf1RqSrHLAiteIksV3YvpVn0Pd4HxV1B3LQ6Mf2pGTyZ7g==", + "dev": true, + "requires": { + "@types/node": "6.0.88", + "@types/webpack": "3.0.11" + } + }, "@types/lodash": { "version": "4.14.74", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.74.tgz", @@ -226,6 +264,12 @@ "@types/mime": "2.0.0" } }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, "@types/source-map": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.1.tgz", @@ -258,6 +302,17 @@ "@types/uglify-js": "2.6.29" } }, + "@types/webpack-sources": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.3.tgz", + "integrity": "sha512-yS052yVjjyIjwcUqIEe2+JxbWsw27OM8UFb1fLUGacGYtqMRwgAx2qk41VTE/nPMjw/xfD0JiHPD0Q99dlrInA==", + "dev": true, + "requires": { + "@types/node": "6.0.88", + "@types/source-list-map": "0.1.2", + "@types/source-map": "0.5.1" + } + }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", @@ -420,6 +475,11 @@ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, "array-filter": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", @@ -520,6 +580,11 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", @@ -544,17 +609,84 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" + }, "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.3.tgz", + "integrity": "sha512-dqzVGiz3v934+s3YZA6nk7tAs9xuTz5wMJbX1M+L4cY/MTNkOUqP61c1GWkEVlUL/PEy1pKRSCFuoRZrXYx9qA==", "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000733", + "browserslist": "2.10.0", + "caniuse-lite": "1.0.30000784", "normalize-range": "0.1.2", "num2fraction": "1.2.2", - "postcss": "5.2.17", + "postcss": "6.0.14", "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "requires": { + "color-convert": "1.9.0" + } + }, + "browserslist": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.10.0.tgz", + "integrity": "sha512-WyvzSLsuAVPOjbljXnyeWl14Ae+ukAT8MUuagKVzIDvwBxl4UAwD1xqtyQs2eWYPGUKMeC3Ol62goqYuKqTTcw==", + "requires": { + "caniuse-lite": "1.0.30000784", + "electron-to-chromium": "1.3.28" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "electron-to-chromium": { + "version": "1.3.28", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.28.tgz", + "integrity": "sha1-jdTmRYCGZE6fnwoc8y4qH53/2e4=" + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "postcss": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", + "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "requires": { + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "4.5.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "2.0.0" + } + } } }, "aws-sign2": { @@ -684,6 +816,20 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.0", + "pascalcase": "0.1.1" + } + }, "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", @@ -732,6 +878,38 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz", "integrity": "sha512-LxFiV5mefv0ley0SzqkOPR1bC4EbpPx8LkOz5vMe/Yi15t5hzwgO/G+tc7wOtL4PZTYjwHu8JnEiSLumuSjSfA==" }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -890,9 +1068,9 @@ "integrity": "sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=" }, "cacache": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.0.tgz", - "integrity": "sha512-s9h6I9NY3KcBjfuS28K6XNmrv/HNFSzlpVD6eYMXugZg3Y8jjI1lUzTeUMa0oKByCDtHfsIy5Ec7KgWRnC5gtg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.1.tgz", + "integrity": "sha512-dRHYcs9LvG9cHgdPzjiI+/eS7e1xRhULrcyOx04RZQsszNJXU2SL9CyG60yLnge282Qq5nwTv+ieK2fH+WPZmA==", "requires": { "bluebird": "3.5.1", "chownr": "1.0.1", @@ -909,6 +1087,22 @@ "y18n": "3.2.1" } }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -963,6 +1157,11 @@ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000733.tgz", "integrity": "sha1-OmJbxBx6n5nVnWRVKFfdGvDt2dQ=" }, + "caniuse-lite": { + "version": "1.0.30000784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000784.tgz", + "integrity": "sha1-EpztdOmhKApEGIC2zSvOMO9Z5sA=" + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1017,11 +1216,6 @@ } } }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" - }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -1029,7 +1223,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", - "fsevents": "1.1.2", + "fsevents": "1.1.3", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -1068,9 +1262,9 @@ } }, "circular-dependency-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-3.0.0.tgz", - "integrity": "sha1-m2hpLjWw41EJmNAWS2rlARvqV2A=" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-4.2.1.tgz", + "integrity": "sha512-Ggm3s8lvFVr2aYqDK6v09D1sJN17n3E7FE3R2khWFHchNop0QLRQvJi4JYsgwX0DNf7gZvXuOQsjTcj2qIVBqg==" }, "circular-json": { "version": "0.3.3", @@ -1105,6 +1299,78 @@ } } }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "clean-css": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", @@ -1172,6 +1438,15 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, "color": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", @@ -1254,6 +1529,11 @@ "dot-prop": "3.0.0" } }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, "compressible": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.11.tgz", @@ -1597,9 +1877,9 @@ } }, "convert-source-map": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", - "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=" + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" }, "cookie": { "version": "0.3.1", @@ -1624,6 +1904,11 @@ "run-queue": "1.0.3" } }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, "copy-webpack-plugin": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.1.1.tgz", @@ -1740,11 +2025,6 @@ "which": "1.3.0" } }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, "cryptiles": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", @@ -1879,6 +2159,21 @@ "postcss-unique-selectors": "2.0.2", "postcss-value-parser": "3.3.0", "postcss-zindex": "2.2.0" + }, + "dependencies": { + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000733", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.17", + "postcss-value-parser": "3.3.0" + } + } } }, "csso": { @@ -1967,6 +2262,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -1987,6 +2287,14 @@ "object-keys": "1.0.11" } }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "1.0.2" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", @@ -2305,12 +2613,6 @@ "tapable": "0.2.8" } }, - "ensure-posix-path": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.0.2.tgz", - "integrity": "sha1-pls+QtC3HPxYXrd0+ZQ8jZuRsMI=", - "dev": true - }, "entities": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", @@ -2685,6 +2987,7 @@ "version": "4.15.4", "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", + "dev": true, "requires": { "accepts": "1.3.4", "array-flatten": "1.1.1", @@ -2719,12 +3022,14 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true }, "qs": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", - "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==" + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==", + "dev": true } } }, @@ -2733,6 +3038,14 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -2749,9 +3062,9 @@ } }, "extract-text-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.0.tgz", - "integrity": "sha1-kMqnkHvESfM1AF46x1MrQbAN5hI=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", + "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", "requires": { "async": "2.5.0", "loader-utils": "1.1.0", @@ -2769,6 +3082,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -2848,6 +3166,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.5.tgz", "integrity": "sha1-pwEwPSV6G8gv6lR6M+WuiVMXI98=", + "dev": true, "requires": { "debug": "2.6.8", "encodeurl": "1.0.1", @@ -2864,7 +3183,7 @@ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "requires": { "commondir": "1.0.1", - "make-dir": "1.0.0", + "make-dir": "1.1.0", "pkg-dir": "2.0.0" } }, @@ -2978,10 +3297,19 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "0.2.2" + } + }, "fresh": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", - "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=", + "dev": true }, "from": { "version": "0.1.7", @@ -3018,6 +3346,15 @@ } } }, + "fs-minipass": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.3.tgz", + "integrity": "sha512-u1pHCXDx+CElfM6CuIeHDTKvb1Ya9ZhsMk7xTHTh6zHSRLK6O0DTVBN+E3wg8fruxAFp4oE07owrrzQfDA0b5Q==", + "dev": true, + "requires": { + "minipass": "2.2.1" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -3035,13 +3372,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", - "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", "optional": true, "requires": { "nan": "2.7.0", - "node-pre-gyp": "0.6.36" + "node-pre-gyp": "0.6.39" }, "dependencies": { "abbrev": { @@ -3175,7 +3512,6 @@ "cryptiles": { "version": "2.0.5", "bundled": true, - "optional": true, "requires": { "boom": "2.10.1" } @@ -3217,6 +3553,11 @@ "bundled": true, "optional": true }, + "detect-libc": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, "ecc-jsbn": { "version": "0.1.1", "bundled": true, @@ -3341,7 +3682,6 @@ "hawk": { "version": "3.1.3", "bundled": true, - "optional": true, "requires": { "boom": "2.10.1", "cryptiles": "2.0.5", @@ -3490,10 +3830,12 @@ "optional": true }, "node-pre-gyp": { - "version": "0.6.36", + "version": "0.6.39", "bundled": true, "optional": true, "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", "mkdirp": "0.5.1", "nopt": "4.0.1", "npmlog": "4.1.0", @@ -3677,7 +4019,6 @@ "sntp": { "version": "1.0.9", "bundled": true, - "optional": true, "requires": { "hoek": "2.16.3" } @@ -3862,14 +4203,12 @@ "generate-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" }, "generate-object-property": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, "requires": { "is-property": "1.0.2" } @@ -3902,6 +4241,11 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -4090,32 +4434,79 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.0.2" + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "requires": { + "inherits": "2.0.3" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.0.2" } }, "he": { @@ -4303,6 +4694,11 @@ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, "icss-replace-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", @@ -4363,15 +4759,6 @@ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", "optional": true }, - "import-local": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-0.1.1.tgz", - "integrity": "sha1-sReVcqrNwRxqkQCftDDbyrX2aKg=", - "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" - } - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4497,13 +4884,29 @@ "ipaddr.js": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", - "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=" + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", + "dev": true }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4535,11 +4938,43 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", @@ -4596,7 +5031,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "dev": true, "requires": { "generate-function": "2.0.0", "generate-object-property": "1.2.0", @@ -4618,6 +5052,24 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-odd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", + "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", + "requires": { + "is-number": "3.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + } + } + } + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -4665,8 +5117,7 @@ "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" }, "is-regex": { "version": "1.0.4", @@ -4754,27 +5205,14 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "istanbul-instrumenter-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-2.0.0.tgz", - "integrity": "sha1-5UkpAKsLuoNe+oAkywC+mz7qJwA=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz", + "integrity": "sha512-alLSEFX06ApU75sm5oWcaVNaiss/bgMRiWTct3g0P0ZZTKjR+6QiCcuVOKDI1kWJgwHEnIXsv/dWm783kPpmtw==", "requires": { - "convert-source-map": "1.5.0", - "istanbul-lib-instrument": "1.8.0", - "loader-utils": "0.2.17", - "object-assign": "4.1.1" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - } + "convert-source-map": "1.5.1", + "istanbul-lib-instrument": "1.9.1", + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" } }, "istanbul-lib-coverage": { @@ -4783,9 +5221,9 @@ "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==" }, "istanbul-lib-instrument": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz", - "integrity": "sha1-ZvbJQhzJ7EcE928tsIS6kHiitTI=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz", + "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", "requires": { "babel-generator": "6.26.0", "babel-template": "6.26.0", @@ -4823,9 +5261,9 @@ } }, "js-base64": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", - "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.0.tgz", + "integrity": "sha512-Wehd+7Pf9tFvGb+ydPm9TjYjV8X1YHOVyG8QyELZxEMqOhemVwGRmoG8iQ/soqI3n8v4xn59zaLxiCJiaaRzKA==" }, "js-tokens": { "version": "3.0.2", @@ -4910,8 +5348,7 @@ "jsonpointer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" }, "jsprim": { "version": "1.4.1", @@ -4932,6 +5369,11 @@ "source-map-support": "0.4.18" } }, + "killable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", + "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4996,9 +5438,9 @@ } }, "license-checker": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-14.0.0.tgz", - "integrity": "sha512-NuPRmB/tICGaRuDwLoGp24oBW0e/dX9jnEYo5lR0hCTkIT1yx+Jm3x3hQ+qkgF+e2UJC59hDSJY1FPkjcxvQ0w==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-15.0.0.tgz", + "integrity": "sha512-HLI3BcrG1mM5Q156DAZxVlK0pE0s/I/EhSaLqWev23e+G5pBIwYXleAhgsja3qJDKcZVBezb7GJwu5pqfmK/Tg==", "dev": true, "requires": { "chalk": "0.5.1", @@ -5284,11 +5726,18 @@ } }, "make-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", - "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", + "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", "requires": { - "pify": "2.3.0" + "pify": "3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } } }, "make-error": { @@ -5297,6 +5746,11 @@ "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", "dev": true }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -5308,13 +5762,12 @@ "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, - "matcher-collection": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.0.5.tgz", - "integrity": "sha512-nUCmzKipcJEwYsBVAFh5P+d7JBuhJaW1xs85Hara9xuMLqtCVUrW6DSC0JVIkluxEH2W45nPBM/wjHtBXa/tYA==", - "dev": true, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "requires": { - "minimatch": "3.0.4" + "object-visit": "1.0.1" } }, "math-expression-evaluator": { @@ -5322,16 +5775,6 @@ "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=" }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "1.1.5" - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5523,15 +5966,6 @@ } } }, - "minizlib": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz", - "integrity": "sha1-1cGr93vhVGGZUuJTM27Mq5sqMvU=", - "dev": true, - "requires": { - "minipass": "2.2.1" - } - }, "mississippi": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-1.3.0.tgz", @@ -5543,12 +5977,31 @@ "flush-write-stream": "1.0.2", "from2": "2.3.0", "parallel-transform": "1.1.0", - "pump": "1.0.2", + "pump": "1.0.3", "pumpify": "1.3.5", - "stream-each": "1.2.0", + "stream-each": "1.2.2", "through2": "2.0.3" } }, + "mixin-deep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", + "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "mixin-object": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", @@ -5636,6 +6089,41 @@ "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", "optional": true }, + "nanomatch": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.7.tgz", + "integrity": "sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "is-odd": "1.0.0", + "kind-of": "5.1.0", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5768,9 +6256,9 @@ "integrity": "sha1-QAlrCM560OoUaAhjr0ScfHWl0cg=" }, "node-sass": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.3.tgz", - "integrity": "sha1-0JydEXlkEjnRuX/8YjH9zsU+FWg=", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz", + "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==", "optional": true, "requires": { "async-foreach": "0.1.3", @@ -5788,16 +6276,42 @@ "nan": "2.7.0", "node-gyp": "3.6.2", "npmlog": "4.1.2", - "request": "2.82.0", + "request": "2.79.0", "sass-graph": "2.2.4", - "stdout-stream": "1.4.0" + "stdout-stream": "1.4.0", + "true-case-path": "1.0.2" }, "dependencies": { + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "optional": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "optional": true + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "optional": true, "requires": { "ansi-styles": "2.2.1", "escape-string-regexp": "1.0.5", @@ -5806,23 +6320,131 @@ "supports-color": "2.0.0" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "optional": true, + "requires": { + "boom": "2.10.1" + } + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "optional": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.11.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", "optional": true - } - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.1.0" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "optional": true, + "requires": { + "hoek": "2.16.3" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "optional": true + } + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -6029,11 +6651,72 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + } + } + }, "object-keys": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "3.0.1" + } + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -6053,6 +6736,14 @@ } } }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "3.0.1" + } + }, "obuf": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.1.tgz", @@ -6269,11 +6960,21 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -6402,13 +7103,18 @@ } } }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, "postcss": { "version": "5.2.17", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", "requires": { "chalk": "1.1.3", - "js-base64": "2.3.2", + "js-base64": "2.4.0", "source-map": "0.5.7", "supports-color": "3.2.3" }, @@ -6463,40 +7169,6 @@ "postcss-value-parser": "3.3.0" } }, - "postcss-custom-properties": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-6.1.0.tgz", - "integrity": "sha1-nK8RUaxBsenmTTov+ezplsoYl30=", - "requires": { - "balanced-match": "1.0.0", - "postcss": "6.0.11" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "postcss": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.11.tgz", - "integrity": "sha512-DsnIzznNRQprsGTALpkC0xjDygo+QcOd+qVjP9+RjyzrPiyYOXBGOwoJ4rAiiE4lu6JggQ/jW4niY24WLxuncg==", - "requires": { - "chalk": "2.2.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, "postcss-discard-comments": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", @@ -6547,6 +7219,75 @@ "uniqid": "4.1.1" } }, + "postcss-import": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.0.0.tgz", + "integrity": "sha1-qWLi34LTvFptpqOGhBdHIE9B71s=", + "requires": { + "postcss": "6.0.15", + "postcss-value-parser": "3.3.0", + "read-cache": "1.0.0", + "resolve": "1.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + }, + "dependencies": { + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "postcss": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.15.tgz", + "integrity": "sha512-v/SpyMzLbtkmh45zUdaqLAaqXqzPdSrw8p4cQVO0/w6YiYfpj4k+Wkzhn68qk9br+H+0qfddhdPEVnbmBPfXVQ==", + "requires": { + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "5.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", + "requires": { + "has-flag": "2.0.0" + } + } + } + }, "postcss-load-config": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", @@ -6577,12 +7318,12 @@ } }, "postcss-loader": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.8.tgz", - "integrity": "sha512-KtXBiQ/r/WYW8LxTSJK7h8wLqvCMSub/BqmRnud/Mu8RzwflW9cmXxwsMwbn15TNv287Hcufdb3ZSs7xHKnG8Q==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.0.10.tgz", + "integrity": "sha512-xQaDcEgJ/2JqFY18zpFkik8vyYs7oS5ZRbrjvDqkP97k2wYWfPT4+qA0m4o3pTSCsz0u26PNqs8ZO9FRUWAqrA==", "requires": { "loader-utils": "1.1.0", - "postcss": "6.0.14", + "postcss": "6.0.15", "postcss-load-config": "1.2.0", "schema-utils": "0.3.0" }, @@ -6603,6 +7344,16 @@ "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", "supports-color": "4.5.0" + }, + "dependencies": { + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "2.0.0" + } + } } }, "has-flag": { @@ -6611,13 +7362,13 @@ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" }, "postcss": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", - "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.15.tgz", + "integrity": "sha512-v/SpyMzLbtkmh45zUdaqLAaqXqzPdSrw8p4cQVO0/w6YiYfpj4k+Wkzhn68qk9br+H+0qfddhdPEVnbmBPfXVQ==", "requires": { "chalk": "2.3.0", "source-map": "0.6.1", - "supports-color": "4.5.0" + "supports-color": "5.1.0" } }, "source-map": { @@ -6626,9 +7377,9 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", "requires": { "has-flag": "2.0.0" } @@ -7072,6 +7823,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", + "dev": true, "requires": { "forwarded": "0.1.2", "ipaddr.js": "1.4.0" @@ -7109,9 +7861,9 @@ } }, "pump": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", - "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", "requires": { "end-of-stream": "1.4.0", "once": "1.4.0" @@ -7124,7 +7876,7 @@ "requires": { "duplexify": "3.5.1", "inherits": "2.0.3", - "pump": "1.0.2" + "pump": "1.0.3" } }, "punycode": { @@ -7216,11 +7968,37 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + } + } + }, "raw-loader": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=" }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "requires": { + "pify": "2.3.0" + } + }, "read-installed": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", @@ -7390,6 +8168,14 @@ "is-equal-shallow": "0.1.3" } }, + "regex-not": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", + "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", + "requires": { + "extend-shallow": "2.0.1" + } + }, "regexpu-core": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", @@ -7557,6 +8343,11 @@ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, "restore-cursor": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", @@ -7567,12 +8358,6 @@ "onetime": "1.1.0" } }, - "rewire": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/rewire/-/rewire-2.5.2.tgz", - "integrity": "sha1-ZCfee3/u+n02QBUH62SlOFvFjcc=", - "dev": true - }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -7622,11 +8407,11 @@ "dev": true }, "rxjs": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz", - "integrity": "sha512-oRYoIKWBU3Ic37fLA5VJu31VqQO4bWubRntcHSJ+cwaDQBwdnZ9x4zmhJfm/nFQ2E82/I4loSioHnACamrKGgA==", + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", + "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", "requires": { - "symbol-observable": "1.0.4" + "symbol-observable": "1.0.1" } }, "safe-buffer": { @@ -7684,7 +8469,7 @@ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "optional": true, "requires": { - "js-base64": "2.3.2", + "js-base64": "2.4.0", "source-map": "0.4.4" }, "dependencies": { @@ -7721,6 +8506,7 @@ "version": "0.15.4", "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", + "dev": true, "requires": { "debug": "2.6.8", "depd": "1.1.1", @@ -7740,10 +8526,16 @@ "mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true } } }, + "serialize-javascript": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", + "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=" + }, "serializerr": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/serializerr/-/serializerr-1.0.3.tgz", @@ -7771,6 +8563,7 @@ "version": "1.12.4", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", + "dev": true, "requires": { "encodeurl": "1.0.1", "escape-html": "1.0.3", @@ -7783,11 +8576,30 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "requires": { + "to-object-path": "0.3.0" + } + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -7894,45 +8706,123 @@ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, - "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "requires": { - "hoek": "4.2.0" - } - }, - "sockjs": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.18.tgz", - "integrity": "sha1-2bKJMWyn33dZXvKZ4HXw+TfrQgc=", - "requires": { - "faye-websocket": "0.10.0", - "uuid": "2.0.3" - }, - "dependencies": { - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" - } - } - }, - "sockjs-client": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", - "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "snapdragon": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", + "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", "requires": { + "base": "0.11.2", "debug": "2.6.8", - "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.1.9" + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "2.0.2" }, "dependencies": { - "faye-websocket": { - "version": "0.11.1", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "3.2.2" + } + }, + "sntp": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", + "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", + "requires": { + "hoek": "4.2.0" + } + }, + "sockjs-client": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", + "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", + "requires": { + "debug": "2.6.8", + "eventsource": "0.1.6", + "faye-websocket": "0.11.1", + "inherits": "2.0.3", + "json3": "3.3.2", + "url-parse": "1.1.9" + }, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "requires": { @@ -7995,6 +8885,18 @@ } } }, + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "requires": { + "atob": "2.0.3", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -8003,6 +8905,11 @@ "source-map": "0.5.7" } }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, "spdx": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/spdx/-/spdx-0.5.1.tgz", @@ -8099,6 +9006,33 @@ "through": "2.3.8" } }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, "split2": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/split2/-/split2-2.1.1.tgz", @@ -8136,6 +9070,76 @@ "safe-buffer": "5.1.1" } }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, "statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", @@ -8169,9 +9173,9 @@ } }, "stream-each": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.0.tgz", - "integrity": "sha1-HpXUdXP1gNgU3A/4zQ9m8c5TyZE=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", + "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "requires": { "end-of-stream": "1.4.0", "stream-shift": "1.0.0" @@ -8269,11 +9273,12 @@ "dev": true }, "style-loader": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.13.2.tgz", - "integrity": "sha1-dFMzhM9pjHEEx5URULSXF63C87s=", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.19.1.tgz", + "integrity": "sha512-IRE+ijgojrygQi3rsqT0U4dd+UcPCqcVvauZpCnQrGAlEe+FUIyrK93bUDScamesjP08JlQNsFJU+KmPedP5Og==", "requires": { - "loader-utils": "1.1.0" + "loader-utils": "1.1.0", + "schema-utils": "0.3.0" } }, "stylus": { @@ -8350,9 +9355,9 @@ } }, "symbol-observable": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", - "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" }, "sync-exec": { "version": "0.6.2", @@ -8450,18 +9455,28 @@ "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" }, "tar": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz", - "integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.1.1.tgz", + "integrity": "sha512-p2lLtRABOEhuC28GDpMYwxcDYRqAFfiz2AIZiQlI+fhrACPKtQ1mcF/bPY8T1h9aKlpDpd+WE33Y2PJ/hWhm8g==", "dev": true, "requires": { "chownr": "1.0.1", + "fs-minipass": "1.2.3", "minipass": "2.2.1", - "minizlib": "1.0.3", + "minizlib": "1.0.4", "mkdirp": "0.5.1", "yallist": "3.0.2" }, "dependencies": { + "minizlib": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.4.tgz", + "integrity": "sha512-sN4U9tIJtBRwKbwgFh9qJfrPIQ/GGTRr1MGqkgOeMTLy8/lM0FcWU//FqlnZ3Vb7gJ+Mxh3FOg1EklibdajbaQ==", + "dev": true, + "requires": { + "minipass": "2.2.1" + } + }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", @@ -8543,6 +9558,104 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "3.2.2" + } + }, + "to-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", + "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", + "requires": { + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "regex-not": "1.0.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + } + } + } + }, "toposort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.3.tgz", @@ -8583,6 +9696,30 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, + "true-case-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", + "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", + "optional": true, + "requires": { + "glob": "6.0.4" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } + } + }, "tryit": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", @@ -8733,33 +9870,61 @@ "optional": true }, "uglifyjs-webpack-plugin": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.0.0.tgz", - "integrity": "sha512-23qmtiLm1X7O0XVSZ54W7XGHykPss+2lo3RYC9zSzK3DDT5W27woZpDFDKguDCnG1RIX8cDnmy5j+dtXxJCA/Q==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.1.5.tgz", + "integrity": "sha512-YBGc9G7dv12Vjx8vUQs54DZgAXVf04LlG6dNNiEbTZjL3PbUqiY4uPB9Kv+fUJaqRskEGva/lS7sh08yJr7jnA==", "requires": { - "cacache": "10.0.0", + "cacache": "10.0.1", "find-cache-dir": "1.0.0", "schema-utils": "0.3.0", - "source-map": "0.5.7", - "uglify-es": "3.1.5", + "serialize-javascript": "1.4.0", + "source-map": "0.6.1", + "uglify-es": "3.2.2", "webpack-sources": "1.0.1", - "worker-farm": "1.4.1" + "worker-farm": "1.5.2" }, "dependencies": { + "commander": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", + "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, "uglify-es": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.1.5.tgz", - "integrity": "sha512-l2PqhvUNmD5pOKiHMuE8TmlvvsghxvLcg+ffcg/obRn/qm0fXf+1Mi8N7tZZIi6zxQS+PbIvq39VCYxmK0QMYA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.2.2.tgz", + "integrity": "sha512-l+s5VLzFwGJfS+fbqaGf/Dfwo1MF13jLOF2ekL0PytzqEqQ6cVppvHf4jquqFok+35USMpKjqkYxy6pQyUcuug==", "requires": { - "commander": "2.11.0", + "commander": "2.12.2", "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + } + } + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -8808,11 +9973,52 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -8862,6 +10068,85 @@ } } }, + "use": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", + "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", + "requires": { + "define-property": "0.2.5", + "isobject": "3.0.1", + "lazy-cache": "2.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "requires": { + "set-getter": "0.1.0" + } + } + } + }, "user-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", @@ -8905,7 +10190,8 @@ "utils-merge": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", - "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "dev": true }, "uuid": { "version": "3.1.0", @@ -8971,16 +10257,6 @@ "indexof": "0.0.1" } }, - "walk-sync": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.2.tgz", - "integrity": "sha512-FMB5VqpLqOCcqrzA9okZFc0wq0Qbmdm396qJxvQZhDpyu0W95G9JCmp74tx7iyYnyOcBtUuKJsgIKAqjozvmmQ==", - "dev": true, - "requires": { - "ensure-posix-path": "1.0.2", - "matcher-collection": "1.0.5" - } - }, "watchpack": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", @@ -9000,9 +10276,9 @@ } }, "webpack": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.8.1.tgz", - "integrity": "sha512-5ZXLWWsMqHKFr5y0N3Eo5IIisxeEeRAajNq4mELb/WELOR7srdbQk2N5XiyNy2A/AgvlR3AmeBCZJW8lHrolbw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz", + "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==", "requires": { "acorn": "5.1.2", "acorn-dynamic-import": "2.0.2", @@ -9255,58 +10531,6 @@ } } }, - "webpack-concat-plugin": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/webpack-concat-plugin/-/webpack-concat-plugin-1.4.0.tgz", - "integrity": "sha512-Ym9Qm5Sw9oXJYChNJk09I/yaXDaV3UDxsa07wcCvILzIeSJTnSUZjhS4y2YkULzgE8VHOv9X04KtlJPZGwXqMg==", - "requires": { - "md5": "2.2.1", - "uglify-js": "2.8.29" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, "webpack-core": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", @@ -9344,43 +10568,109 @@ } }, "webpack-dev-server": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.9.3.tgz", - "integrity": "sha512-bwq7sj452FRH+oVfgOA8xXKkLYPTNsYB4dQ0Jhz3ydjNJ9MvhpGJtehFW8Z0cEcwNkRRiF4aYbReiSGQ4pbS1w==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.0.tgz", + "integrity": "sha512-lXzc36DGjKUVinETNmDWhfZFRbHMhatuF+lKex+czqY+JVe0Qf2V+Ig6/svDdbt/DmXFXuLQmSqhncYCqYf3qA==", "requires": { "ansi-html": "0.0.7", "array-includes": "3.0.3", "bonjour": "3.5.0", - "chokidar": "1.7.0", + "chokidar": "2.0.0", "compression": "1.7.0", "connect-history-api-fallback": "1.3.0", "debug": "3.1.0", "del": "3.0.0", - "express": "4.15.4", + "express": "4.16.2", "html-entities": "1.2.1", "http-proxy-middleware": "0.17.4", - "import-local": "0.1.1", + "import-local": "1.0.0", "internal-ip": "1.2.0", "ip": "1.1.5", + "killable": "1.0.0", "loglevel": "1.4.1", "opn": "5.1.0", "portfinder": "1.0.13", "selfsigned": "1.9.1", "serve-index": "1.9.0", - "sockjs": "0.3.18", + "sockjs": "0.3.19", "sockjs-client": "1.1.4", "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "4.4.0", - "webpack-dev-middleware": "1.12.0", + "strip-ansi": "4.0.0", + "supports-color": "5.1.0", + "webpack-dev-middleware": "1.12.2", "yargs": "6.6.0" }, "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "3.1.5", + "normalize-path": "2.1.1" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", + "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.1" + } + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" }, + "chokidar": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.0.tgz", + "integrity": "sha512-OgXCNv2U6TnG04D3tth0gsvdbV4zdbxFG3sYUqcoQMoEFVd1j1pZR6TZ8iknC45o9IJ6PeQI/J6wT/+cHcniAw==", + "requires": { + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.0", + "fsevents": "1.1.3", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -9389,19 +10679,396 @@ "ms": "2.0.0" } }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "express": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.1", + "serve-static": "1.13.1", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "requires": { + "pkg-dir": "2.0.0", + "resolve-cwd": "2.0.0" + } + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.5.tgz", + "integrity": "sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.0", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.7", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "send": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "serve-static": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.1" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "requires": { + "faye-websocket": "0.10.0", + "uuid": "3.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", "requires": { "has-flag": "2.0.0" } }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "requires": { + "memory-fs": "0.4.1", + "mime": "1.6.0", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + } + } + }, "yargs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", @@ -9513,9 +11180,9 @@ "dev": true }, "worker-farm": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.4.1.tgz", - "integrity": "sha512-tgFAtgOYLPutkAyzgpS6VJFL5HY+0ui1Tvua+fITgz8ByaJTMFGtazR6xxQfwfiAcbwE+2fLG/K49wc2TfwCNw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.5.2.tgz", + "integrity": "sha512-XxiQ9kZN5n6mmnW+mFJ+wXjNNI/Nx4DIdaAKLX1Bn6LYBWlN/zaBhu34DQYPZ1AJobQuu67S2OfDdNSVULvXkQ==", "requires": { "errno": "0.1.4", "xtend": "4.0.1" @@ -9625,9 +11292,10 @@ "dev": true }, "zone.js": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.17.tgz", - "integrity": "sha1-TF5RhahX2o2nk9rzkZNxxaNrKgs=" + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.20.tgz", + "integrity": "sha512-FXlA37ErSXCMy5RNBcGFgCI/Zivqzr0D19GuvDxhcYIJc7xkFp6c29DKyODJu0Zo+EMyur/WPPgcBh1EHjB9jA==", + "dev": true } } } diff --git a/package.json b/package.json index adab4c28470f..000a793edd7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/cli", - "version": "1.6.0-beta.2", + "version": "1.7.0-beta.1", "description": "CLI tool for Angular", "main": "packages/@angular/cli/lib/cli/index.js", "trackingCode": "UA-8594346-19", @@ -41,27 +41,28 @@ }, "homepage": "https://github.com/angular/angular-cli", "dependencies": { - "@angular-devkit/build-optimizer": "~0.0.28", - "@angular-devkit/schematics": "~0.0.35", - "@schematics/angular": "~0.1.0", - "autoprefixer": "^6.5.3", + "@angular-devkit/build-optimizer": "~0.0.41", + "@angular-devkit/core": "~0.0.28", + "@angular-devkit/schematics": "~0.0.51", + "@schematics/angular": "~0.1.16", + "autoprefixer": "^7.2.3", "chalk": "~2.2.0", - "circular-dependency-plugin": "^3.0.0", + "circular-dependency-plugin": "^4.2.1", + "clean-css": "^4.1.9", "common-tags": "^1.3.1", "copy-webpack-plugin": "^4.1.1", "core-object": "^3.1.0", "css-loader": "^0.28.1", - "cssnano": "^3.10.0", "denodeify": "^1.2.1", "ember-cli-string-utils": "^1.0.0", "enhanced-resolve": "^3.4.1", "exports-loader": "^0.6.3", - "extract-text-webpack-plugin": "3.0.0", + "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.5", "fs-extra": "^4.0.0", "glob": "^7.0.3", "html-webpack-plugin": "^2.29.0", - "istanbul-instrumenter-loader": "^2.0.0", + "istanbul-instrumenter-loader": "^3.0.0", "karma-source-map-support": "^1.2.0", "less": "^2.7.2", "less-loader": "^4.0.5", @@ -75,33 +76,31 @@ "nopt": "^4.0.1", "opn": "~5.1.0", "portfinder": "~1.0.12", - "postcss-custom-properties": "^6.1.0", - "postcss-loader": "^2.0.8", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.10", "postcss-url": "^7.1.2", "raw-loader": "^0.5.1", "resolve": "^1.1.7", - "rxjs": "^5.5.2", - "sass-loader": "^6.0.3", + "rxjs": "^5.5.6", + "sass-loader": "^6.0.6", "semver": "^5.3.0", "silent-error": "^1.0.0", "source-map": "^0.5.6", "source-map-loader": "^0.2.0", "source-map-support": "^0.4.1", - "style-loader": "^0.13.1", + "style-loader": "^0.19.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "tree-kill": "^1.0.0", "typescript": "~2.4.2", - "uglifyjs-webpack-plugin": "1.0.0", + "uglifyjs-webpack-plugin": "^1.1.5", "url-loader": "^0.6.2", - "webpack": "~3.8.1", - "webpack-concat-plugin": "1.4.0", + "webpack": "~3.10.0", "webpack-dev-middleware": "~1.12.0", - "webpack-dev-server": "~2.9.3", + "webpack-dev-server": "~2.11.0", "webpack-merge": "^4.1.0", "webpack-sources": "^1.0.0", - "webpack-subresource-integrity": "^1.0.1", - "zone.js": "^0.8.14" + "webpack-subresource-integrity": "^1.0.1" }, "devDependencies": { "@angular/compiler": "^5.0.0", @@ -116,6 +115,7 @@ "@types/glob": "^5.0.29", "@types/jasmine": "2.5.45", "@types/lodash": "~4.14.50", + "@types/loader-utils": "^1.1.0", "@types/minimist": "^1.2.0", "@types/mock-fs": "^3.6.30", "@types/node": "^6.0.84", @@ -123,29 +123,29 @@ "@types/semver": "^5.3.30", "@types/source-map": "^0.5.0", "@types/webpack": "^3.0.5", + "@types/webpack-sources": "^0.1.3", "conventional-changelog": "1.1.0", "dtsgenerator": "^0.9.1", "eslint": "^3.11.0", "express": "^4.14.0", "jasmine": "^2.6.0", "jasmine-spec-reporter": "^4.1.0", - "license-checker": "^14.0.0", + "license-checker": "^15.0.0", "minimist": "^1.2.0", "mock-fs": "^4.0.0", "npm-run": "^4.1.0", "npm-run-all": "^4.0.0", "request": "^2.74.0", "resolve-bin": "^0.4.0", - "rewire": "^2.5.1", "spdx-satisfies": "^0.1.3", - "tar": "^3.1.5", + "tar": "^4.1.1", "temp": "0.8.3", "through": "^2.3.6", "ts-node": "^3.2.0", "tslint": "^5.1.0", - "walk-sync": "^0.3.1" + "zone.js": "^0.8.20" }, "optionalDependencies": { - "node-sass": "^4.3.0" + "node-sass": "^4.7.2" } } diff --git a/packages/@angular/cli/bin/ng b/packages/@angular/cli/bin/ng index 2c265c957d4d..3f32d98ffb80 100755 --- a/packages/@angular/cli/bin/ng +++ b/packages/@angular/cli/bin/ng @@ -151,6 +151,7 @@ resolve('@angular/cli', { basedir: process.cwd() }, localVersion = _fromPackageJson(); shouldWarn = localVersion && globalVersion.compare(localVersion) > 0; } catch (e) { + // eslint-disable-next-line no-console console.error(e); shouldWarn = true; } @@ -163,14 +164,14 @@ resolve('@angular/cli', { basedir: process.cwd() }, To disable this warning use "ng set --global warnings.versionMismatch=false". `); // Don't show warning colorised on `ng completion` - if (process.argv[2] !== 'completion') { - // eslint-disable no-console - console.log(warning); - } else { - // eslint-disable no-console - console.error(warning); - process.exit(1); - } + if (process.argv[2] !== 'completion') { + // eslint-disable-next-line no-console + console.log(warning); + } else { + // eslint-disable-next-line no-console + console.error(warning); + process.exit(1); + } } // No error implies a projectLocalCli, which will load whatever diff --git a/packages/@angular/cli/commands/build.ts b/packages/@angular/cli/commands/build.ts index 64bd4f823461..2611e66b0527 100644 --- a/packages/@angular/cli/commands/build.ts +++ b/packages/@angular/cli/commands/build.ts @@ -2,9 +2,12 @@ import { CliConfig } from '../models/config'; import { BuildOptions } from '../models/build-options'; import { Version } from '../upgrade/version'; import { oneLine } from 'common-tags'; +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(); const buildConfigDefaults = config.getPaths('defaults.build', [ @@ -198,6 +201,12 @@ export const baseBuildCommandOptions: any = [ aliases: ['sw'], description: 'Generates a service worker config for production builds, if the app has ' + 'service worker enabled.' + }, + { + name: 'skip-app-shell', + type: Boolean, + description: 'Flag to prevent building an app shell', + default: false } ]; @@ -230,6 +239,11 @@ const BuildCommand = Command.extend({ commandOptions.forceTsCommonjs = true; } + // Add trailing slash if missing to prevent https://github.com/angular/angular-cli/issues/7295 + if (commandOptions.deployUrl && commandOptions.deployUrl.substr(-1) !== '/') { + commandOptions.deployUrl += '/'; + } + const BuildTask = require('../tasks/build').default; const buildTask = new BuildTask({ @@ -237,7 +251,51 @@ const BuildCommand = Command.extend({ ui: this.ui, }); - return buildTask.run(commandOptions); + const clientApp = getAppFromConfig(commandOptions.app); + + const doAppShell = commandOptions.target === 'production' && + (commandOptions.aot === undefined || commandOptions.aot === true) && + !commandOptions.skipAppShell; + + let serverApp: any = null; + if (clientApp.appShell && doAppShell) { + serverApp = getAppFromConfig(clientApp.appShell.app); + if (serverApp.platform !== 'server') { + throw new SilentError(`Shell app's platform is not "server"`); + } + } + + const buildPromise = buildTask.run(commandOptions); + + if (!clientApp.appShell || !doAppShell) { + return buildPromise; + } + + return buildPromise + .then(() => { + + const serverOptions = { + ...commandOptions, + app: clientApp.appShell.app + }; + return buildTask.run(serverOptions); + }) + .then(() => { + 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) + }; + + return renderUniversalTask.run(renderUniversalOptions); + }); } }); diff --git a/packages/@angular/cli/commands/e2e.ts b/packages/@angular/cli/commands/e2e.ts index fc6d084816a8..3f23ee27a26d 100644 --- a/packages/@angular/cli/commands/e2e.ts +++ b/packages/@angular/cli/commands/e2e.ts @@ -13,6 +13,7 @@ export interface E2eTaskOptions extends ServeTaskOptions { serve: boolean; webdriverUpdate: boolean; specs: string[]; + suite: string; elementExplorer: boolean; } @@ -42,6 +43,15 @@ const E2eCommand = Command.extend({ Can send in multiple specs by repeating flag (ng e2e --specs=spec1.ts --specs=spec2.ts). ` }, + { + name: 'suite', + type: String, + aliases: ['su'], + description: oneLine` + Override suite in the protractor config. + Can send in multiple suite by comma separated values (ng e2e --suite=suiteA,suiteB). + ` + }, { name: 'element-explorer', type: Boolean, diff --git a/packages/@angular/cli/commands/generate.ts b/packages/@angular/cli/commands/generate.ts index e9a4d79b3585..b8337be48a5b 100644 --- a/packages/@angular/cli/commands/generate.ts +++ b/packages/@angular/cli/commands/generate.ts @@ -65,15 +65,24 @@ export default Command.extend({ '' ], - getCollectionName(rawArgs: string[]) { + getCollectionName(rawArgs: string[], parsedOptions?: { collection?: string }): [string, string] { + let schematicName = rawArgs[0]; let collectionName = CliConfig.getValue('defaults.schematics.collection'); - if (rawArgs) { + + 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; } } - return collectionName; + + return [collectionName, schematicName]; }, beforeRun: function(rawArgs: string[]) { @@ -83,7 +92,7 @@ export default Command.extend({ return; } - const schematicName = rawArgs[0]; + const [collectionName, schematicName] = this.getCollectionName(rawArgs); if (!schematicName) { return Promise.reject(new SilentError(oneLine` The "ng generate" command requires a @@ -103,21 +112,30 @@ export default Command.extend({ ui: this.ui, project: this.project }); - const collectionName = this.getCollectionName(rawArgs); return getOptionsTask.run({ schematicName, collectionName }) - .then((availableOptions: SchematicAvailableOptions) => { + .then((availableOptions: SchematicAvailableOptions[]) => { let anonymousOptions: string[] = []; + + if (availableOptions) { + const nameOption = availableOptions.filter(opt => opt.name === 'name')[0]; + if (nameOption) { + anonymousOptions = [...anonymousOptions, '']; + } + } else { + anonymousOptions = [...anonymousOptions, '']; + } + if (collectionName === '@schematics/angular' && schematicName === 'interface') { - anonymousOptions = ['']; + anonymousOptions = [...anonymousOptions, '']; } this.registerOptions({ anonymousOptions: anonymousOptions, - availableOptions: availableOptions + availableOptions: availableOptions || [] }); }); }, @@ -127,8 +145,12 @@ export default Command.extend({ throw 'The `ng generate module` command requires a name to be specified.'; } - const entityName = rawArgs[1]; - commandOptions.name = stringUtils.dasherize(entityName.split(separatorRegEx).pop()); + let entityName = rawArgs[1]; + if (entityName) { + commandOptions.name = stringUtils.dasherize(entityName.split(separatorRegEx).pop()); + } else { + entityName = ''; + } const appConfig = getAppFromConfig(commandOptions.app); const dynamicPathOptions: DynamicPathOptions = { @@ -138,7 +160,7 @@ export default Command.extend({ dryRun: commandOptions.dryRun }; const parsedPath = dynamicPathParser(dynamicPathOptions); - commandOptions.sourceDir = parsedPath.sourceDir; + commandOptions.sourceDir = parsedPath.sourceDir.replace(separatorRegEx, '/'); const root = parsedPath.sourceDir + path.sep; commandOptions.appRoot = parsedPath.appRoot === parsedPath.sourceDir ? '' : parsedPath.appRoot.startsWith(root) @@ -152,7 +174,7 @@ export default Command.extend({ : commandOptions.path; const cwd = this.project.root; - const schematicName = rawArgs[0]; + const [collectionName, schematicName] = this.getCollectionName(rawArgs, commandOptions); if (['component', 'c', 'directive', 'd'].indexOf(schematicName) !== -1) { if (commandOptions.prefix === undefined) { @@ -171,8 +193,6 @@ export default Command.extend({ ui: this.ui, project: this.project }); - const collectionName = commandOptions.collection || - CliConfig.getValue('defaults.schematics.collection'); if (collectionName === '@schematics/angular' && schematicName === 'interface' && rawArgs[2]) { commandOptions.type = rawArgs[2]; diff --git a/packages/@angular/cli/lib/cli/index.ts b/packages/@angular/cli/lib/cli/index.ts index e47fe33117c9..4f6a81d09294 100644 --- a/packages/@angular/cli/lib/cli/index.ts +++ b/packages/@angular/cli/lib/cli/index.ts @@ -1,7 +1,3 @@ -// Prevent the dependency validation from tripping because we don't import these. We need -// it as a peer dependency of @angular/core. -// require('zone.js') - import * as path from 'path'; const cli = require('../../ember-cli/lib/cli'); diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json index 6e38ae682e2f..7359d5d348d8 100644 --- a/packages/@angular/cli/lib/config/schema.json +++ b/packages/@angular/cli/lib/config/schema.json @@ -38,6 +38,20 @@ "description": "Directory where app files are placed.", "default": "app" }, + "appShell": { + "type": "object", + "description": "AppShell configuration.", + "properties": { + "app": { + "type": "string", + "description": "Index or name of the related AppShell app." + }, + "route": { + "type": "string", + "description": "Default AppShell route to render." + } + } + }, "root": { "type": "string", "description": "The root directory of the app." @@ -85,6 +99,53 @@ }, "default": [] }, + "budgets": { + "type": "array", + "description": "Threshold definitions for bundle sizes.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["bundle", "initial", "allScript", "all", "anyScript", "any"], + "description": "The type of budget" + }, + "name": { + "type": "string", + "description": "The name of the bundle" + }, + "baseline": { + "type": "string", + "description": "The baseline size for comparison." + }, + "maximumWarning": { + "type": "string", + "description": "The maximum threshold for warning relative to the baseline." + }, + "maximumError": { + "type": "string", + "description": "The maximum threshold for error relative to the baseline." + }, + "minimumWarning": { + "type": "string", + "description": "The minimum threshold for warning relative to the baseline." + }, + "minimumError": { + "type": "string", + "description": "The minimum threshold for error relative to the baseline." + }, + "warning": { + "type": "string", + "description": "The threshold for warning relative to the baseline (min & max)." + }, + "error": { + "type": "string", + "description": "The threshold for error relative to the baseline (min & max)." + } + } + }, + "default": [] + }, "deployUrl": { "type": "string", "description": "URL where files will be deployed." @@ -600,6 +661,11 @@ "description": "Show a warning when the TypeScript version is incompatible", "type": "boolean", "default": true + }, + "servePathDefault": { + "description": "Show a warning when deploy-url/base-href use unsupported serve path values.", + "type": "boolean", + "default": true } } } diff --git a/packages/@angular/cli/models/build-options.ts b/packages/@angular/cli/models/build-options.ts index 13f7c23324ed..05abcfb3ee23 100644 --- a/packages/@angular/cli/models/build-options.ts +++ b/packages/@angular/cli/models/build-options.ts @@ -32,4 +32,5 @@ export interface BuildOptions { subresourceIntegrity?: boolean; forceTsCommonjs?: boolean; serviceWorker?: boolean; + skipAppShell?: boolean; } diff --git a/packages/@angular/cli/models/webpack-configs/common.ts b/packages/@angular/cli/models/webpack-configs/common.ts index ecb60283a19c..3e24b93a7780 100644 --- a/packages/@angular/cli/models/webpack-configs/common.ts +++ b/packages/@angular/cli/models/webpack-configs/common.ts @@ -2,13 +2,12 @@ import * as webpack from 'webpack'; import * as path from 'path'; import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin'; -import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin'; import { extraEntryParser, getOutputHashFormat, AssetPattern } from './utils'; import { isDirectory } from '../../utilities/is-directory'; import { requireProjectModule } from '../../utilities/require-project-module'; import { WebpackConfigOptions } from '../webpack-config'; +import { ScriptsWebpackPlugin } from '../../plugins/scripts-webpack-plugin'; -const ConcatPlugin = require('webpack-concat-plugin'); const ProgressPlugin = require('webpack/lib/ProgressPlugin'); const CircularDependencyPlugin = require('circular-dependency-plugin'); const SilentError = require('silent-error'); @@ -65,23 +64,16 @@ export function getCommonConfig(wco: WebpackConfigOptions) { // Add a new asset for each entry. globalScriptsByEntry.forEach((script) => { - const hash = hashFormat.chunk !== '' && !script.lazy ? '.[hash]' : ''; - extraPlugins.push(new ConcatPlugin({ - uglify: buildOptions.target === 'production' ? { sourceMapIncludeSources: true } : false, - sourceMap: buildOptions.sourcemaps, + // Lazy scripts don't get a hash, otherwise they can't be loaded by name. + const hash = script.lazy ? '' : hashFormat.script; + extraPlugins.push(new ScriptsWebpackPlugin({ name: script.entry, - // Lazy scripts don't get a hash, otherwise they can't be loaded by name. - fileName: `[name]${script.lazy ? '' : hash}.bundle.js`, - filesToConcat: script.paths + sourceMap: buildOptions.sourcemaps, + filename: `${script.entry}${hash}.bundle.js`, + scripts: script.paths, + basePath: projectRoot, })); }); - - // Insert all the assets created by ConcatPlugin in the right place in index.html. - extraPlugins.push(new InsertConcatAssetsWebpackPlugin( - globalScriptsByEntry - .filter((el) => !el.lazy) - .map((el) => el.entry) - )); } // process asset entries @@ -91,19 +83,25 @@ export function getCommonConfig(wco: WebpackConfigOptions) { asset = typeof asset === 'string' ? { glob: asset } : asset; // Add defaults. // Input is always resolved relative to the appRoot. - asset.input = path.resolve(appRoot, asset.input || ''); + asset.input = path.resolve(appRoot, asset.input || '').replace(/\\/g, '/'); asset.output = asset.output || ''; asset.glob = asset.glob || ''; // Prevent asset configurations from writing outside of the output path, except if the user // specify a configuration flag. // Also prevent writing outside the project path. That is not overridable. - const fullOutputPath = path.resolve(buildOptions.outputPath, asset.output); - if (!fullOutputPath.startsWith(projectRoot)) { - const message = 'An asset cannot be written to a location outside the project.'; - throw new SilentError(message); - } - if (!fullOutputPath.startsWith(path.resolve(buildOptions.outputPath))) { + const absoluteOutputPath = path.resolve(buildOptions.outputPath); + const absoluteAssetOutput = path.resolve(absoluteOutputPath, asset.output); + const outputRelativeOutput = path.relative(absoluteOutputPath, absoluteAssetOutput); + + if (outputRelativeOutput.startsWith('..') || path.isAbsolute(outputRelativeOutput)) { + + const projectRelativeOutput = path.relative(projectRoot, absoluteAssetOutput); + if (projectRelativeOutput.startsWith('..') || path.isAbsolute(projectRelativeOutput)) { + const message = 'An asset cannot be written to a location outside the project.'; + throw new SilentError(message); + } + if (!asset.allowOutsideOutDir) { const message = 'An asset cannot be written to a location outside of the output path. ' + 'You can override this message by setting the `allowOutsideOutDir` ' @@ -113,7 +111,8 @@ export function getCommonConfig(wco: WebpackConfigOptions) { } // Prevent asset configurations from reading files outside of the project. - if (!asset.input.startsWith(projectRoot)) { + const projectRelativeInput = path.relative(projectRoot, asset.input); + if (projectRelativeInput.startsWith('..') || path.isAbsolute(projectRelativeInput)) { const message = 'An asset cannot be read from a location outside the project.'; throw new SilentError(message); } @@ -128,11 +127,15 @@ export function getCommonConfig(wco: WebpackConfigOptions) { asset.glob = asset.glob + '/**/*'; } + // Escape the input in case it has special charaters and use to make glob absolute + const escapedInput = asset.input + .replace(/[\\|\*|\?|\!|\(|\)|\[|\]|\{|\}]/g, (substring) => `\\${substring}`); + return { context: asset.input, to: asset.output, from: { - glob: asset.glob, + glob: path.resolve(escapedInput, asset.glob), dot: true } }; diff --git a/packages/@angular/cli/models/webpack-configs/production.ts b/packages/@angular/cli/models/webpack-configs/production.ts index fddc7f373861..5b6be4dfd754 100644 --- a/packages/@angular/cli/models/webpack-configs/production.ts +++ b/packages/@angular/cli/models/webpack-configs/production.ts @@ -5,6 +5,7 @@ import * as semver from 'semver'; import { stripIndent } from 'common-tags'; import { LicenseWebpackPlugin } from 'license-webpack-plugin'; import { PurifyPlugin } from '@angular-devkit/build-optimizer'; +import { BundleBudgetPlugin } from '../../plugins/bundle-budget'; import { StaticAssetPlugin } from '../../plugins/static-asset'; import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin'; import { WebpackConfigOptions } from '../webpack-config'; @@ -47,7 +48,7 @@ export function getProdConfig(wco: WebpackConfigOptions) { const swVersion = JSON.parse(swPackageJson)['version']; const isLegacySw = semver.satisfies(swVersion, OLD_SW_VERSION); - const isModernSw = semver.satisfies(swVersion, NEW_SW_VERSION); + const isModernSw = semver.gte(swVersion, NEW_SW_VERSION); if (!isLegacySw && !isModernSw) { throw new Error(stripIndent` @@ -108,6 +109,10 @@ export function getProdConfig(wco: WebpackConfigOptions) { } } + extraPlugins.push(new BundleBudgetPlugin({ + budgets: appConfig.budgets + })); + if (buildOptions.extractLicenses) { extraPlugins.push(new LicenseWebpackPlugin({ pattern: /^(MIT|ISC|BSD.*)$/, @@ -117,7 +122,13 @@ export function getProdConfig(wco: WebpackConfigOptions) { })); } - const uglifyCompressOptions: any = {}; + const uglifyCompressOptions: any = { + // Disabled because of an issue with Mapbox GL when using the Webpack node global and UglifyJS: + // https://github.com/mapbox/mapbox-gl-js/issues/4359#issuecomment-303880888 + // https://github.com/angular/angular-cli/issues/5804 + // https://github.com/angular/angular-cli/pull/7931 + typeofs : false + }; if (buildOptions.buildOptimizer) { // This plugin must be before webpack.optimize.UglifyJsPlugin. @@ -136,21 +147,26 @@ export function getProdConfig(wco: WebpackConfigOptions) { }), new webpack.HashedModuleIdsPlugin(), new webpack.optimize.ModuleConcatenationPlugin(), + ...extraPlugins, + // Uglify should be the last plugin as PurifyPlugin needs to be before it. new UglifyJSPlugin({ sourceMap: buildOptions.sourcemaps, + parallel: true, uglifyOptions: { ecma: wco.supportES2015 ? 6 : 5, warnings: buildOptions.verbose, ie8: false, - mangle: true, + mangle: { + safari10: true, + }, compress: uglifyCompressOptions, output: { ascii_only: true, - comments: false + comments: false, + webkit: true, }, } }), - ...extraPlugins ] }; } diff --git a/packages/@angular/cli/models/webpack-configs/styles.ts b/packages/@angular/cli/models/webpack-configs/styles.ts index 83392219cbdb..d85cf3df0ff1 100644 --- a/packages/@angular/cli/models/webpack-configs/styles.ts +++ b/packages/@angular/cli/models/webpack-configs/styles.ts @@ -6,12 +6,12 @@ import { import { extraEntryParser, getOutputHashFormat } from './utils'; import { WebpackConfigOptions } from '../webpack-config'; import { pluginArgs, postcssArgs } from '../../tasks/eject'; +import { CleanCssWebpackPlugin } from '../../plugins/cleancss-webpack-plugin'; -const cssnano = require('cssnano'); const postcssUrl = require('postcss-url'); const autoprefixer = require('autoprefixer'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const customProperties = require('postcss-custom-properties'); +const postcssImports = require('postcss-import'); /** * Enumerate loaders and their dependencies from this file to let the dependency validator @@ -29,6 +29,12 @@ const customProperties = require('postcss-custom-properties'); * require('sass-loader') */ +interface PostcssUrlAsset { + url: string; + hash: string; + absolutePath: string; +} + export function getStylesConfig(wco: WebpackConfigOptions) { const { projectRoot, buildOptions, appConfig } = wco; @@ -36,64 +42,96 @@ export function getStylesConfig(wco: WebpackConfigOptions) { const entryPoints: { [key: string]: string[] } = {}; const globalStylePaths: string[] = []; const extraPlugins: any[] = []; - // style-loader does not support sourcemaps without absolute publicPath, so it's - // better to disable them when not extracting css - // https://github.com/webpack-contrib/style-loader#recommended-configuration - const cssSourceMap = buildOptions.extractCss && buildOptions.sourcemaps; + const cssSourceMap = buildOptions.sourcemaps; + // Maximum resource size to inline (KiB) + const maximumInlineSize = 10; // Minify/optimize css in production. const minimizeCss = buildOptions.target === 'production'; // Convert absolute resource URLs to account for base-href and deploy-url. const baseHref = wco.buildOptions.baseHref || ''; const deployUrl = wco.buildOptions.deployUrl || ''; - const postcssPluginCreator = function() { - // safe settings based on: https://github.com/ben-eb/cssnano/issues/358#issuecomment-283696193 - const importantCommentRe = /@preserve|@license|[@#]\s*source(?:Mapping)?URL|^!/i; - const minimizeOptions = { - autoprefixer: false, // full pass with autoprefixer is run separately - safe: true, - mergeLonghand: false, // version 3+ should be safe; cssnano currently uses 2.x - discardComments : { remove: (comment: string) => !importantCommentRe.test(comment) } - }; - + const postcssPluginCreator = function(loader: webpack.loader.LoaderContext) { return [ - postcssUrl({ - url: (URL: { url: string }) => { - const { url } = URL; - // Only convert root relative URLs, which CSS-Loader won't process into require(). - if (!url.startsWith('/') || url.startsWith('//')) { - return URL.url; - } + postcssImports({ + resolve: (url: string, context: string) => { + return new Promise((resolve, reject) => { + if (url && url.startsWith('~')) { + url = url.substr(1); + } + loader.resolve(context, url, (err: Error, result: string) => { + if (err) { + reject(err); + return; + } - if (deployUrl.match(/:\/\//)) { - // If deployUrl contains a scheme, ignore baseHref use deployUrl as is. - return `${deployUrl.replace(/\/$/, '')}${url}`; - } else if (baseHref.match(/:\/\//)) { - // If baseHref contains a scheme, include it as is. - return baseHref.replace(/\/$/, '') + - `/${deployUrl}/${url}`.replace(/\/\/+/g, '/'); - } else { - // Join together base-href, deploy-url and the original URL. - // Also dedupe multiple slashes into single ones. - return `/${baseHref}/${deployUrl}/${url}`.replace(/\/\/+/g, '/'); - } + resolve(result); + }); + }); + }, + load: (filename: string) => { + return new Promise((resolve, reject) => { + loader.fs.readFile(filename, (err: Error, data: Buffer) => { + if (err) { + reject(err); + return; + } + + const content = data.toString(); + resolve(content); + }); + }); } }), + postcssUrl({ + filter: ({ url }: PostcssUrlAsset) => url.startsWith('~'), + url: ({ url }: PostcssUrlAsset) => { + const fullPath = path.join(projectRoot, 'node_modules', url.substr(1)); + return path.relative(loader.context, fullPath).replace(/\\/g, '/'); + } + }), + postcssUrl([ + { + // Only convert root relative URLs, which CSS-Loader won't process into require(). + filter: ({ url }: PostcssUrlAsset) => url.startsWith('/') && !url.startsWith('//'), + url: ({ url }: PostcssUrlAsset) => { + if (deployUrl.match(/:\/\//) || deployUrl.startsWith('/')) { + // If deployUrl is absolute or root relative, ignore baseHref & use deployUrl as is. + return `${deployUrl.replace(/\/$/, '')}${url}`; + } else if (baseHref.match(/:\/\//)) { + // If baseHref contains a scheme, include it as is. + return baseHref.replace(/\/$/, '') + + `/${deployUrl}/${url}`.replace(/\/\/+/g, '/'); + } else { + // Join together base-href, deploy-url and the original URL. + // Also dedupe multiple slashes into single ones. + return `/${baseHref}/${deployUrl}/${url}`.replace(/\/\/+/g, '/'); + } + } + }, + { + // TODO: inline .cur if not supporting IE (use browserslist to check) + filter: (asset: PostcssUrlAsset) => { + return maximumInlineSize > 0 && !asset.hash && !asset.absolutePath.endsWith('.cur'); + }, + url: 'inline', + // NOTE: maxSize is in KB + maxSize: maximumInlineSize, + fallback: 'rebase', + }, + { url: 'rebase' }, + ]), autoprefixer(), - customProperties({ preserve: true}) - ].concat( - minimizeCss ? [cssnano(minimizeOptions)] : [] - ); + ]; }; (postcssPluginCreator as any)[postcssArgs] = { variableImports: { 'autoprefixer': 'autoprefixer', 'postcss-url': 'postcssUrl', - 'cssnano': 'cssnano', - 'postcss-custom-properties': 'customProperties' + 'postcss-import': 'postcssImports', }, - variables: { minimizeCss, baseHref, deployUrl } + variables: { minimizeCss, baseHref, deployUrl, projectRoot, maximumInlineSize } }; // determine hashing format @@ -164,7 +202,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) { loader: 'css-loader', options: { sourceMap: cssSourceMap, - importLoaders: 1 + import: false, } }, { @@ -172,7 +210,8 @@ export function getStylesConfig(wco: WebpackConfigOptions) { options: { // A non-function property is required to workaround a webpack option handling bug ident: 'postcss', - plugins: postcssPluginCreator + plugins: postcssPluginCreator, + sourceMap: cssSourceMap } } ]; @@ -219,6 +258,10 @@ export function getStylesConfig(wco: WebpackConfigOptions) { extraPlugins.push(new SuppressExtractedTextChunksWebpackPlugin()); } + if (minimizeCss) { + extraPlugins.push(new CleanCssWebpackPlugin({ sourceMap: cssSourceMap })); + } + return { entry: entryPoints, module: { rules }, diff --git a/packages/@angular/cli/models/webpack-configs/utils.ts b/packages/@angular/cli/models/webpack-configs/utils.ts index 8679dc5a81cf..99a6b7b07271 100644 --- a/packages/@angular/cli/models/webpack-configs/utils.ts +++ b/packages/@angular/cli/models/webpack-configs/utils.ts @@ -81,8 +81,8 @@ export function getOutputHashFormat(option: string, length = 20): HashFormat { const hashFormats: { [option: string]: HashFormat } = { none: { chunk: '', extract: '', file: '' , script: '' }, media: { chunk: '', extract: '', file: `.[hash:${length}]`, script: '' }, - bundles: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '' , script: '.[hash]' }, - all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]`, script: '.[hash]' }, + bundles: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: '' , script: `.[hash:${length}]` }, + all: { chunk: `.[chunkhash:${length}]`, extract: `.[contenthash:${length}]`, file: `.[hash:${length}]`, script: `.[hash:${length}]` }, }; /* tslint:enable:max-line-length */ return hashFormats[option] || hashFormats['none']; diff --git a/packages/@angular/cli/package.json b/packages/@angular/cli/package.json index 9de573893fa1..4e4fc1d4bcd5 100644 --- a/packages/@angular/cli/package.json +++ b/packages/@angular/cli/package.json @@ -1,6 +1,6 @@ { "name": "@angular/cli", - "version": "1.6.0-beta.2", + "version": "1.7.0-beta.1", "description": "CLI tool for Angular", "main": "lib/cli/index.js", "trackingCode": "UA-8594346-19", @@ -27,23 +27,24 @@ }, "homepage": "https://github.com/angular/angular-cli", "dependencies": { - "@angular-devkit/build-optimizer": "~0.0.31", - "@angular-devkit/schematics": "~0.0.34", + "@angular-devkit/build-optimizer": "~0.0.41", + "@angular-devkit/core": "~0.0.28", + "@angular-devkit/schematics": "~0.0.51", "@ngtools/json-schema": "1.1.0", - "@ngtools/webpack": "1.9.0-beta.2", - "@schematics/angular": "~0.1.5", - "autoprefixer": "^6.5.3", + "@ngtools/webpack": "1.10.0-beta.1", + "@schematics/angular": "~0.1.16", + "autoprefixer": "^7.2.3", "chalk": "~2.2.0", - "circular-dependency-plugin": "^3.0.0", + "circular-dependency-plugin": "^4.2.1", + "clean-css": "^4.1.9", "common-tags": "^1.3.1", "copy-webpack-plugin": "^4.1.1", "core-object": "^3.1.0", "css-loader": "^0.28.1", - "cssnano": "^3.10.0", "denodeify": "^1.2.1", "ember-cli-string-utils": "^1.0.0", "exports-loader": "^0.6.3", - "extract-text-webpack-plugin": "3.0.0", + "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.5", "fs-extra": "^4.0.0", "glob": "^7.0.3", @@ -53,39 +54,38 @@ "less-loader": "^4.0.5", "license-webpack-plugin": "^1.0.0", "lodash": "^4.11.1", + "loader-utils": "1.1.0", "memory-fs": "^0.4.1", "minimatch": "^3.0.4", "node-modules-path": "^1.0.0", "nopt": "^4.0.1", "opn": "~5.1.0", "portfinder": "~1.0.12", - "postcss-custom-properties": "^6.1.0", - "postcss-loader": "^2.0.8", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.10", "postcss-url": "^7.1.2", "raw-loader": "^0.5.1", "resolve": "^1.1.7", "rxjs": "^5.5.2", - "sass-loader": "^6.0.3", + "sass-loader": "^6.0.6", "semver": "^5.1.0", "silent-error": "^1.0.0", "source-map-loader": "^0.2.0", "source-map-support": "^0.4.1", - "istanbul-instrumenter-loader": "^2.0.0", - "style-loader": "^0.13.1", + "istanbul-instrumenter-loader": "^3.0.0", + "style-loader": "^0.19.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", - "uglifyjs-webpack-plugin": "1.0.0", + "uglifyjs-webpack-plugin": "^1.1.5", "url-loader": "^0.6.2", - "webpack": "~3.8.1", - "webpack-concat-plugin": "1.4.0", + "webpack": "~3.10.0", "webpack-dev-middleware": "~1.12.0", - "webpack-dev-server": "~2.9.3", + "webpack-dev-server": "~2.11.0", "webpack-merge": "^4.1.0", "webpack-sources": "^1.0.0", - "webpack-subresource-integrity": "^1.0.1", - "zone.js": "^0.8.14" + "webpack-subresource-integrity": "^1.0.1" }, "optionalDependencies": { - "node-sass": "^4.3.0" + "node-sass": "^4.7.2" } } diff --git a/packages/@angular/cli/plugins/bundle-budget.ts b/packages/@angular/cli/plugins/bundle-budget.ts new file mode 100644 index 000000000000..3ce0b7f77ee0 --- /dev/null +++ b/packages/@angular/cli/plugins/bundle-budget.ts @@ -0,0 +1,129 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { Budget, calculateBytes, calculateSizes } from '../utilities/bundle-calculator'; + +interface Thresholds { + maximumWarning?: number; + maximumError?: number; + minimumWarning?: number; + minimumError?: number; + warningLow?: number; + warningHigh?: number; + errorLow?: number; + errorHigh?: number; +} + +export interface BundleBudgetPluginOptions { + budgets: Budget[]; +} + +export class BundleBudgetPlugin { + constructor(private options: BundleBudgetPluginOptions) {} + + apply(compiler: any): void { + const { budgets } = this.options; + compiler.plugin('after-emit', (compilation: any, cb: Function) => { + if (!budgets || budgets.length === 0) { + cb(); + return; + } + + budgets.map(budget => { + const thresholds = this.calcualte(budget); + return { + budget, + thresholds, + sizes: calculateSizes(budget, compilation) + }; + }) + .forEach(budgetCheck => { + budgetCheck.sizes.forEach(size => { + if (budgetCheck.thresholds.maximumWarning) { + if (budgetCheck.thresholds.maximumWarning < size.size) { + compilation.warnings.push(`budgets, maximum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.maximumError) { + if (budgetCheck.thresholds.maximumError < size.size) { + compilation.errors.push(`budgets, maximum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.minimumWarning) { + if (budgetCheck.thresholds.minimumWarning > size.size) { + compilation.warnings.push(`budgets, minimum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.minimumError) { + if (budgetCheck.thresholds.minimumError > size.size) { + compilation.errors.push(`budgets, minimum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.warningLow) { + if (budgetCheck.thresholds.warningLow > size.size) { + compilation.warnings.push(`budgets, minimum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.warningHigh) { + if (budgetCheck.thresholds.warningHigh < size.size) { + compilation.warnings.push(`budgets, maximum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.errorLow) { + if (budgetCheck.thresholds.errorLow > size.size) { + compilation.errors.push(`budgets, minimum exceeded for ${size.label}.`); + } + } + if (budgetCheck.thresholds.errorHigh) { + if (budgetCheck.thresholds.errorHigh < size.size) { + compilation.errors.push(`budgets, maximum exceeded for ${size.label}.`); + } + } + }); + + }); + cb(); + }); + } + + private calcualte(budget: Budget): Thresholds { + let thresholds: Thresholds = {}; + if (budget.maximumWarning) { + thresholds.maximumWarning = calculateBytes(budget.maximumWarning, budget.baseline, 'pos'); + } + + if (budget.maximumError) { + thresholds.maximumError = calculateBytes(budget.maximumError, budget.baseline, 'pos'); + } + + if (budget.minimumWarning) { + thresholds.minimumWarning = calculateBytes(budget.minimumWarning, budget.baseline, 'neg'); + } + + if (budget.minimumError) { + thresholds.minimumError = calculateBytes(budget.minimumError, budget.baseline, 'neg'); + } + + if (budget.warning) { + thresholds.warningLow = calculateBytes(budget.warning, budget.baseline, 'neg'); + } + + if (budget.warning) { + thresholds.warningHigh = calculateBytes(budget.warning, budget.baseline, 'pos'); + } + + if (budget.error) { + thresholds.errorLow = calculateBytes(budget.error, budget.baseline, 'neg'); + } + + if (budget.error) { + thresholds.errorHigh = calculateBytes(budget.error, budget.baseline, 'pos'); + } + + return thresholds; + } +} diff --git a/packages/@angular/cli/plugins/cleancss-webpack-plugin.ts b/packages/@angular/cli/plugins/cleancss-webpack-plugin.ts new file mode 100644 index 000000000000..2b469c0264ba --- /dev/null +++ b/packages/@angular/cli/plugins/cleancss-webpack-plugin.ts @@ -0,0 +1,111 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { Compiler } from 'webpack'; +import { RawSource, SourceMapSource } from 'webpack-sources'; + +const CleanCSS = require('clean-css'); + +interface Chunk { + files: string[]; +} + +export interface CleanCssWebpackPluginOptions { + sourceMap: boolean; +} + +export class CleanCssWebpackPlugin { + + constructor(private options: Partial = {}) {} + + apply(compiler: Compiler): void { + compiler.plugin('compilation', (compilation: any) => { + compilation.plugin('optimize-chunk-assets', + (chunks: Array, callback: (err?: Error) => void) => { + + const cleancss = new CleanCSS({ + compatibility: 'ie9', + level: 2, + inline: false, + returnPromise: true, + sourceMap: this.options.sourceMap, + }); + + const files: string[] = [...compilation.additionalChunkAssets]; + + chunks.forEach(chunk => { + if (chunk.files && chunk.files.length > 0) { + files.push(...chunk.files); + } + }); + + const actions = files + .filter(file => file.endsWith('.css')) + .map(file => { + const asset = compilation.assets[file]; + if (!asset) { + return Promise.resolve(); + } + + let content: string; + let map: any; + if (asset.sourceAndMap) { + const sourceAndMap = asset.sourceAndMap(); + content = sourceAndMap.source; + map = sourceAndMap.map; + } else { + content = asset.source(); + } + + if (content.length === 0) { + return Promise.resolve(); + } + + return Promise.resolve() + .then(() => cleancss.minify(content, map)) + .then((output: any) => { + let hasWarnings = false; + if (output.warnings && output.warnings.length > 0) { + compilation.warnings.push(...output.warnings); + hasWarnings = true; + } + + if (output.errors && output.errors.length > 0) { + output.errors + .forEach((error: string) => compilation.errors.push(new Error(error))); + return; + } + + // generally means invalid syntax so bail + if (hasWarnings && output.stats.minifiedSize === 0) { + return; + } + + let newSource; + if (output.sourceMap) { + newSource = new SourceMapSource( + output.styles, + file, + output.sourceMap.toString(), + content, + map, + ); + } else { + newSource = new RawSource(output.styles); + } + + compilation.assets[file] = newSource; + }); + }); + + Promise.all(actions) + .then(() => callback()) + .catch(err => callback(err)); + }); + }); + } +} diff --git a/packages/@angular/cli/plugins/insert-concat-assets-webpack-plugin.ts b/packages/@angular/cli/plugins/insert-concat-assets-webpack-plugin.ts deleted file mode 100644 index 039c0afafd00..000000000000 --- a/packages/@angular/cli/plugins/insert-concat-assets-webpack-plugin.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Add assets from `ConcatPlugin` to index.html. - - -export class InsertConcatAssetsWebpackPlugin { - // Priority list of where to insert asset. - private insertAfter = [ - /polyfills(\.[0-9a-f]{20})?\.bundle\.js/, - /inline(\.[0-9a-f]{20})?\.bundle\.js/, - ]; - - constructor(private entryNames: string[]) { } - - apply(compiler: any): void { - compiler.plugin('compilation', (compilation: any) => { - compilation.plugin('html-webpack-plugin-before-html-generation', - (htmlPluginData: any, callback: any) => { - - const fileNames = this.entryNames.map((entryName) => { - const fileName = htmlPluginData.assets.webpackConcat - && htmlPluginData.assets.webpackConcat[entryName]; - - if (!fileName) { - // Something went wrong and the asset was not correctly added. - throw new Error(`Cannot find file for ${entryName} script.`); - } - - if (htmlPluginData.assets.publicPath) { - if (htmlPluginData.assets.publicPath.endsWith('/')) { - return htmlPluginData.assets.publicPath + fileName; - } - return htmlPluginData.assets.publicPath + '/' + fileName; - } - return fileName; - }); - - let insertAt = 0; - - // TODO: try to figure out if there are duplicate bundle names when adding and throw - for (let el of this.insertAfter) { - const jsIdx = htmlPluginData.assets.js.findIndex((js: string) => js.match(el)); - if (jsIdx !== -1) { - insertAt = jsIdx + 1; - break; - } - } - - htmlPluginData.assets.js.splice(insertAt, 0, ...fileNames); - callback(null, htmlPluginData); - }); - }); - } -} diff --git a/packages/@angular/cli/plugins/karma.ts b/packages/@angular/cli/plugins/karma.ts index ad893a5fadb5..3f813871912d 100644 --- a/packages/@angular/cli/plugins/karma.ts +++ b/packages/@angular/cli/plugins/karma.ts @@ -53,7 +53,7 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => { environment: 'dev', codeCoverage: false, sourcemaps: true, - progress: true, + progress: process.stdout.isTTY === true, preserveSymlinks: false, }, config.angularCli); diff --git a/packages/@angular/cli/plugins/scripts-webpack-plugin.ts b/packages/@angular/cli/plugins/scripts-webpack-plugin.ts new file mode 100644 index 000000000000..e65056264bfa --- /dev/null +++ b/packages/@angular/cli/plugins/scripts-webpack-plugin.ts @@ -0,0 +1,140 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { Compiler, loader } from 'webpack'; +import { CachedSource, ConcatSource, OriginalSource, RawSource, Source } from 'webpack-sources'; +import { interpolateName } from 'loader-utils'; +import * as path from 'path'; + +const Chunk = require('webpack/lib/Chunk'); + +export interface ScriptsWebpackPluginOptions { + name: string; + sourceMap: boolean; + scripts: string[]; + filename: string; + basePath: string; +} + +interface ScriptOutput { + filename: string; + source: CachedSource; +} + +export class ScriptsWebpackPlugin { + private _lastBuildTime?: number; + private _cachedOutput?: ScriptOutput; + + constructor(private options: Partial = {}) {} + + shouldSkip(compilation: any, scripts: string[]): boolean { + if (this._lastBuildTime == undefined) { + this._lastBuildTime = Date.now(); + return false; + } + + for (let i = 0; i < scripts.length; i++) { + const scriptTime = compilation.fileTimestamps[scripts[i]]; + if (!scriptTime || scriptTime > this._lastBuildTime) { + this._lastBuildTime = Date.now(); + return false; + } + } + + return true; + } + + private _insertOutput(compilation: any, { filename, source }: ScriptOutput, cached = false) { + const chunk = new Chunk(); + chunk.rendered = !cached; + chunk.id = this.options.name; + chunk.ids = [chunk.id]; + chunk.name = this.options.name; + chunk.isInitial = () => true; + chunk.files.push(filename); + + compilation.chunks.push(chunk); + compilation.assets[filename] = source; + } + + apply(compiler: Compiler): void { + if (!this.options.scripts || this.options.scripts.length === 0) { + return; + } + + const scripts = this.options.scripts + .filter(script => !!script) + .map(script => path.resolve(this.options.basePath || '', script)); + + compiler.plugin('this-compilation', (compilation: any) => { + compilation.plugin('additional-assets', (callback: (err?: Error) => void) => { + if (this.shouldSkip(compilation, scripts)) { + if (this._cachedOutput) { + this._insertOutput(compilation, this._cachedOutput, true); + } + compilation.fileDependencies.push(...scripts); + + callback(); + + return; + } + + const sourceGetters = scripts.map(fullPath => { + return new Promise((resolve, reject) => { + compilation.inputFileSystem.readFile(fullPath, (err: Error, data: Buffer) => { + if (err) { + reject(err); + return; + } + + const content = data.toString(); + + let source; + if (this.options.sourceMap) { + // TODO: Look for source map file (for '.min' scripts, etc.) + + let adjustedPath = fullPath; + if (this.options.basePath) { + adjustedPath = path.relative(this.options.basePath, fullPath); + } + source = new OriginalSource(content, adjustedPath); + } else { + source = new RawSource(content); + } + + resolve(source); + }); + }); + }); + + Promise.all(sourceGetters) + .then(sources => { + const concatSource = new ConcatSource(); + sources.forEach(source => { + concatSource.add(source); + concatSource.add('\n;'); + }); + + const combinedSource = new CachedSource(concatSource); + const filename = interpolateName( + { resourcePath: 'scripts.js' } as loader.LoaderContext, + this.options.filename, + { content: combinedSource.source() }, + ); + + const output = { filename, source: combinedSource }; + this._insertOutput(compilation, output); + this._cachedOutput = output; + compilation.fileDependencies.push(...scripts); + + callback(); + }) + .catch((err: Error) => callback(err)); + }); + }); + } +} diff --git a/packages/@angular/cli/plugins/webpack.ts b/packages/@angular/cli/plugins/webpack.ts index a51f71eff8d4..c834b494a23d 100644 --- a/packages/@angular/cli/plugins/webpack.ts +++ b/packages/@angular/cli/plugins/webpack.ts @@ -1,6 +1,8 @@ // Exports the webpack plugins we use internally. export { BaseHrefWebpackPlugin } from '../lib/base-href-webpack/base-href-webpack-plugin'; +export { CleanCssWebpackPlugin, CleanCssWebpackPluginOptions } from './cleancss-webpack-plugin'; export { GlobCopyWebpackPlugin, GlobCopyWebpackPluginOptions } from './glob-copy-webpack-plugin'; -export { InsertConcatAssetsWebpackPlugin } from './insert-concat-assets-webpack-plugin'; +export { BundleBudgetPlugin, BundleBudgetPluginOptions } from './bundle-budget'; export { NamedLazyChunksWebpackPlugin } from './named-lazy-chunks-webpack-plugin'; +export { ScriptsWebpackPlugin, ScriptsWebpackPluginOptions } from './scripts-webpack-plugin'; export { SuppressExtractedTextChunksWebpackPlugin } from './suppress-entry-chunks-webpack-plugin'; diff --git a/packages/@angular/cli/tasks/e2e.ts b/packages/@angular/cli/tasks/e2e.ts index 40228aafeb83..f73ab5a0d36e 100644 --- a/packages/@angular/cli/tasks/e2e.ts +++ b/packages/@angular/cli/tasks/e2e.ts @@ -59,6 +59,10 @@ export const E2eTask = Task.extend({ additionalProtractorConfig['specs'] = e2eTaskOptions.specs; } + if (e2eTaskOptions.suite && e2eTaskOptions.suite.length !== 0) { + additionalProtractorConfig['suite'] = e2eTaskOptions.suite; + } + if (e2eTaskOptions.webdriverUpdate) { // The webdriver-manager update command can only be accessed via a deep import. const webdriverDeepImport = 'webdriver-manager/built/lib/cmds/update'; diff --git a/packages/@angular/cli/tasks/eject.ts b/packages/@angular/cli/tasks/eject.ts index a782ac21e640..9f9ac2b171c4 100644 --- a/packages/@angular/cli/tasks/eject.ts +++ b/packages/@angular/cli/tasks/eject.ts @@ -7,6 +7,7 @@ import { getAppFromConfig } from '../utilities/app-utils'; import { EjectTaskOptions } from '../commands/eject'; import { NgCliWebpackConfig } from '../models/webpack-config'; import { CliConfig } from '../models/config'; +import { usesServiceWorker } from '../utilities/service-worker'; import { stripBom } from '../utilities/strip-bom'; import { AotPlugin, AngularCompilerPlugin } from '@ngtools/webpack'; import { PurifyPlugin } from '@angular-devkit/build-optimizer'; @@ -25,7 +26,6 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); const SubresourceIntegrityPlugin = require('webpack-subresource-integrity'); const SilentError = require('silent-error'); const CircularDependencyPlugin = require('circular-dependency-plugin'); -const ConcatPlugin = require('webpack-concat-plugin'); const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const Task = require('../ember-cli/lib/models/task'); @@ -90,6 +90,13 @@ class JsonWebpackSerializer { }; } + private _bundleBudgetPluginSerialize(value: any): any { + let budgets = value.options.budgets; + return { + budgets + }; + } + private _insertConcatAssetsWebpackPluginSerialize(value: any): any { return value.entryNames; } @@ -155,19 +162,7 @@ class JsonWebpackSerializer { } private _licenseWebpackPlugin(plugin: any) { - return plugin.options; - } - - private _concatPlugin(plugin: any) { - const options = plugin.settings; - if (!options || !options.filesToConcat) { - return options; - } - - const filesToConcat = options.filesToConcat - .map((file: string) => path.relative(process.cwd(), file)); - - return { ...options, filesToConcat }; + return this._licenseReplacer(plugin.options); } private _uglifyjsPlugin(plugin: any) { @@ -202,7 +197,9 @@ class JsonWebpackSerializer { this._addImport('webpack.optimize', 'ModuleConcatenationPlugin'); break; case angularCliPlugins.BaseHrefWebpackPlugin: + case angularCliPlugins.CleanCssWebpackPlugin: case angularCliPlugins.NamedLazyChunksWebpackPlugin: + case angularCliPlugins.ScriptsWebpackPlugin: case angularCliPlugins.SuppressExtractedTextChunksWebpackPlugin: this._addImport('@angular/cli/plugins/webpack', plugin.constructor.name); break; @@ -210,6 +207,10 @@ class JsonWebpackSerializer { args = this._globCopyWebpackPluginSerialize(plugin); this._addImport('@angular/cli/plugins/webpack', 'GlobCopyWebpackPlugin'); break; + case angularCliPlugins.BundleBudgetPlugin: + args = this._bundleBudgetPluginSerialize(plugin); + this._addImport('@angular/cli/plugins/webpack', 'BundleBudgetPlugin'); + break; case angularCliPlugins.InsertConcatAssetsWebpackPlugin: args = this._insertConcatAssetsWebpackPluginSerialize(plugin); this._addImport('@angular/cli/plugins/webpack', 'InsertConcatAssetsWebpackPlugin'); @@ -248,10 +249,6 @@ class JsonWebpackSerializer { args = this._licenseWebpackPlugin(plugin); this._addImport('license-webpack-plugin', 'LicenseWebpackPlugin'); break; - case ConcatPlugin: - args = this._concatPlugin(plugin); - this.variableImports['webpack-concat-plugin'] = 'ConcatPlugin'; - break; case UglifyJSPlugin: args = this._uglifyjsPlugin(plugin); this.variableImports['uglifyjs-webpack-plugin'] = 'UglifyJsPlugin'; @@ -409,6 +406,13 @@ class JsonWebpackSerializer { }); } + private _licenseReplacer(value: any) { + return Object.assign({}, value, { + outputTemplate: this._relativePath( + 'process.cwd()', path.relative(this._root, value.outputTemplate)) + }); + } + private _replacer(_key: string, value: any) { if (value === undefined) { return value; @@ -484,7 +488,6 @@ class JsonWebpackSerializer { } } - export default Task.extend({ run: function (runTaskOptions: EjectTaskOptions) { const project = this.project; @@ -521,31 +524,35 @@ export default Task.extend({ .then((packageJson: string) => JSON.parse(packageJson)) .then((packageJson: any) => { const scripts = packageJson['scripts']; - if (scripts['build'] && scripts['build'] !== 'ng build' && !force) { - throw new SilentError(oneLine` - Your package.json scripts must not contain a build script as it will be overwritten. - `); - } - if (scripts['start'] && scripts['start'] !== 'ng serve' && !force) { - throw new SilentError(oneLine` - Your package.json scripts must not contain a start script as it will be overwritten. - `); - } - if (scripts['pree2e'] && scripts['pree2e'] !== pree2eNpmScript && !force) { - throw new SilentError(oneLine` - Your package.json scripts must not contain a pree2e script as it will be - overwritten. - `); - } - if (scripts['e2e'] && scripts['e2e'] !== 'ng e2e' && !force) { - throw new SilentError(oneLine` - Your package.json scripts must not contain a e2e script as it will be overwritten. - `); - } - if (scripts['test'] && scripts['test'] !== 'ng test' && !force) { - throw new SilentError(oneLine` - Your package.json scripts must not contain a test script as it will be overwritten. - `); + if (!force) { + if (scripts['build'] + && scripts['build'] != 'ng build' + && scripts['build'] != 'ng build --prod') { + throw new SilentError(oneLine` + Your package.json scripts must not contain a build script as it will be overwritten. + `); + } + if (scripts['start'] && scripts['start'] !== 'ng serve') { + throw new SilentError(oneLine` + Your package.json scripts must not contain a start script as it will be overwritten. + `); + } + if (scripts['pree2e'] && scripts['pree2e'] !== pree2eNpmScript) { + throw new SilentError(oneLine` + Your package.json scripts must not contain a pree2e script as it will be + overwritten. + `); + } + if (scripts['e2e'] && scripts['e2e'] !== 'ng e2e') { + throw new SilentError(oneLine` + Your package.json scripts must not contain a e2e script as it will be overwritten. + `); + } + if (scripts['test'] && scripts['test'] !== 'ng test') { + throw new SilentError(oneLine` + Your package.json scripts must not contain a test script as it will be overwritten. + `); + } } packageJson['scripts']['build'] = 'webpack'; @@ -554,6 +561,16 @@ export default Task.extend({ packageJson['scripts']['pree2e'] = pree2eNpmScript; packageJson['scripts']['e2e'] = 'protractor ./protractor.conf.js'; + if (!!appConfig.serviceWorker && runTaskOptions.target === 'production' && + usesServiceWorker(project.root) && !!runTaskOptions.serviceWorker) { + packageJson['scripts']['build'] += ' && npm run sw-config && npm run sw-copy'; + packageJson['scripts']['sw-config'] = `ngsw-config ${outputPath} src/ngsw-config.json`; + packageJson['scripts']['sw-copy'] = + `cpx node_modules/@angular/service-worker/ngsw-worker.js ${outputPath}`; + + packageJson['devDependencies']['cpx'] = '^1.5.0'; + } + // Add new dependencies based on our dependencies. const ourPackageJson = require('../package.json'); if (!packageJson['devDependencies']) { @@ -567,13 +584,13 @@ export default Task.extend({ 'webpack', 'autoprefixer', 'css-loader', - 'cssnano', 'exports-loader', 'file-loader', 'html-webpack-plugin', 'json-loader', 'karma-sourcemap-loader', 'less-loader', + 'postcss-import', 'postcss-loader', 'postcss-url', 'raw-loader', @@ -584,7 +601,6 @@ export default Task.extend({ 'stylus-loader', 'url-loader', 'circular-dependency-plugin', - 'webpack-concat-plugin', 'copy-webpack-plugin', 'uglifyjs-webpack-plugin', ].forEach((packageName: string) => { diff --git a/packages/@angular/cli/tasks/lint.ts b/packages/@angular/cli/tasks/lint.ts index 576a2d49d035..4b747ac72bdb 100644 --- a/packages/@angular/cli/tasks/lint.ts +++ b/packages/@angular/cli/tasks/lint.ts @@ -177,7 +177,7 @@ function getFilesToLint( let programFiles = linter.getFileNames(program); if (ignore && ignore.length > 0) { - const ignoreMatchers = ignore.map(pattern => new Minimatch(pattern)); + const ignoreMatchers = ignore.map(pattern => new Minimatch(pattern, { dot: true })); programFiles = programFiles .filter(file => !ignoreMatchers.some(matcher => matcher.match(file))); diff --git a/packages/@angular/cli/tasks/render-universal.ts b/packages/@angular/cli/tasks/render-universal.ts new file mode 100644 index 000000000000..01e3bc1d5eb2 --- /dev/null +++ b/packages/@angular/cli/tasks/render-universal.ts @@ -0,0 +1,33 @@ +import { requireProjectModule } from '../utilities/require-project-module'; +import { join } from 'path'; + +const fs = require('fs'); +const Task = require('../ember-cli/lib/models/task'); + +export interface RenderUniversalTaskOptions { + inputIndexPath: string; + route: string; + serverOutDir: string; + outputIndexPath: string; +} + +export default Task.extend({ + run: function(options: RenderUniversalTaskOptions): Promise { + requireProjectModule(this.project.root, 'zone.js/dist/zone-node'); + + const renderModuleFactory = + requireProjectModule(this.project.root, '@angular/platform-server').renderModuleFactory; + + // Get the main bundle from the server build's output directory. + const serverDir = fs.readdirSync(options.serverOutDir); + const serverMainBundle = serverDir + .filter((file: string) => /main\.(?:[a-zA-Z0-9]{20}\.)?bundle\.js/.test(file))[0]; + const serverBundlePath = join(options.serverOutDir, serverMainBundle); + const AppServerModuleNgFactory = require(serverBundlePath).AppServerModuleNgFactory; + + const index = fs.readFileSync(options.inputIndexPath, 'utf8'); + // Render to HTML and overwrite the client index file. + return renderModuleFactory(AppServerModuleNgFactory, {document: index, url: options.route}) + .then((html: string) => fs.writeFileSync(options.outputIndexPath, html)); + } +}); diff --git a/packages/@angular/cli/tasks/schematic-get-help-output.ts b/packages/@angular/cli/tasks/schematic-get-help-output.ts index 5b3e2b2171f4..2bdd9c054d34 100644 --- a/packages/@angular/cli/tasks/schematic-get-help-output.ts +++ b/packages/@angular/cli/tasks/schematic-get-help-output.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; const Task = require('../ember-cli/lib/models/task'); -const { cyan, grey } = chalk; +const { cyan, green, grey } = chalk; export interface SchematicGetHelpOptions { collectionName: string; @@ -40,7 +40,7 @@ export default Task.extend({ }), nonSchematicOptions]) .then(([availableOptions, nonSchematicOptions]: [SchematicAvailableOptions[], any[]]) => { const output: string[] = []; - [...(nonSchematicOptions || []), ...availableOptions] + [...(nonSchematicOptions || []), ...availableOptions || []] .filter(opt => hiddenOptions.indexOf(opt.name) === -1) .forEach(opt => { let text = cyan(` --${opt.name}`); @@ -63,6 +63,11 @@ export default Task.extend({ output.push(grey(` aliases: ${aliasText}`)); } }); + if (availableOptions === null) { + output.push(green('This schematic accept additional options, but did not provide ' + + 'documentation.')); + } + return output; }); } diff --git a/packages/@angular/cli/tasks/schematic-get-options.ts b/packages/@angular/cli/tasks/schematic-get-options.ts index 77c300150b11..44c28e580270 100644 --- a/packages/@angular/cli/tasks/schematic-get-options.ts +++ b/packages/@angular/cli/tasks/schematic-get-options.ts @@ -18,7 +18,7 @@ export interface SchematicAvailableOptions { } export default Task.extend({ - run: function (options: SchematicGetOptions): Promise { + run: function (options: SchematicGetOptions): Promise { const collectionName = options.collectionName || CliConfig.getValue('defaults.schematics.collection'); @@ -26,10 +26,15 @@ export default Task.extend({ const schematic = getSchematic(collection, options.schematicName); + if (!schematic.description.schemaJson) { + return Promise.resolve(null); + } + const properties = schematic.description.schemaJson.properties; const keys = Object.keys(properties); const availableOptions = keys .map(key => ({...properties[key], ...{name: stringUtils.dasherize(key)}})) + .filter(opt => opt.visible !== false) .map(opt => { let type; const schematicType = opt.type; diff --git a/packages/@angular/cli/tasks/schematic-run.ts b/packages/@angular/cli/tasks/schematic-run.ts index f3cddcc7c49b..b49ddee6fa71 100644 --- a/packages/@angular/cli/tasks/schematic-run.ts +++ b/packages/@angular/cli/tasks/schematic-run.ts @@ -168,12 +168,14 @@ export default Task.extend({ }); function prepOptions(schematic: Schematic<{}, {}>, options: SchematicOptions): SchematicOptions { + const properties = (schematic.description).schemaJson + ? (schematic.description).schemaJson.properties + : options; - const properties = (schematic.description).schemaJson.properties; const keys = Object.keys(properties); if (['component', 'c', 'directive', 'd'].indexOf(schematic.description.name) !== -1) { options.prefix = (options.prefix === 'false' || options.prefix === '') - ? '' : options.prefix; + ? undefined : options.prefix; } let preppedOptions = { diff --git a/packages/@angular/cli/tasks/serve.ts b/packages/@angular/cli/tasks/serve.ts index 18274a5c92c5..698a8fc8211e 100644 --- a/packages/@angular/cli/tasks/serve.ts +++ b/packages/@angular/cli/tasks/serve.ts @@ -73,8 +73,8 @@ export default Task.extend({ } const serveDefaults = { - // default deployUrl to '' on serve to prevent the default from .angular-cli.json - deployUrl: '' + deployUrl: appConfig.deployUrl || '', + baseHref: appConfig.baseHref || '', }; serveTaskOptions = Object.assign({}, serveDefaults, serveTaskOptions); @@ -108,18 +108,18 @@ export default Task.extend({ if (serveTaskOptions.liveReload) { // This allows for live reload of page when changes are made to repo. - // https://webpack.github.io/docs/webpack-dev-server.html#inline-mode + // https://webpack.js.org/configuration/dev-server/#devserver-inline let entryPoints = [ `webpack-dev-server/client?${clientAddress}` ]; if (serveTaskOptions.hmr) { - const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html'; + const webpackHmrLink = 'https://webpack.js.org/guides/hot-module-replacement'; ui.writeLine(oneLine` ${yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server. `); - const showWarning = CliConfig.fromGlobal().get('warnings.hmrWarning'); + const showWarning = CliConfig.fromProject().get('warnings.hmrWarning'); if (showWarning) { ui.writeLine(' The project will still live reload when HMR is enabled,'); ui.writeLine(' but to take advantage of HMR additional application code is required'); @@ -127,7 +127,7 @@ export default Task.extend({ ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`); ui.writeLine(' for information on working with HMR for Webpack.'); ui.writeLine(oneLine` - ${yellow('To disable this warning use "ng set --global warnings.hmrWarning=false"')} + ${yellow('To disable this warning use "ng set warnings.hmrWarning=false"')} `); } entryPoints.push('webpack/hot/dev-server'); @@ -193,7 +193,8 @@ export default Task.extend({ if (!servePath && servePath !== '') { const defaultServePath = findDefaultServePath(serveTaskOptions.baseHref, serveTaskOptions.deployUrl); - if (defaultServePath == null) { + const showWarning = CliConfig.fromProject().get('warnings.servePathDefault'); + if (defaultServePath == null && showWarning) { ui.writeLine(oneLine` ${chalk.yellow('WARNING')} --deploy-url and/or --base-href contain unsupported values for ng serve. Default serve path of '/' used. diff --git a/packages/@angular/cli/upgrade/version.ts b/packages/@angular/cli/upgrade/version.ts index 0fcf5f226642..9234eb3b1908 100644 --- a/packages/@angular/cli/upgrade/version.ts +++ b/packages/@angular/cli/upgrade/version.ts @@ -169,7 +169,9 @@ export class Version { const versionCombos = [ { compiler: '>=2.3.1 <3.0.0', typescript: '>=2.0.2 <2.3.0' }, { compiler: '>=4.0.0 <5.0.0', typescript: '>=2.1.0 <2.4.0' }, - { compiler: '>=5.0.0 <6.0.0', typescript: '>=2.4.2 <2.5.0' } + { compiler: '>=5.0.0 <5.1.0', typescript: '>=2.4.2 <2.5.0' }, + { compiler: '>=5.1.0 <5.2.0', typescript: '>=2.4.2 <2.6.0' }, + { compiler: '>=5.2.0 <6.0.0', typescript: '>=2.4.2 <2.7.0' } ]; const currentCombo = versionCombos.find((combo) => satisfies(compilerVersion, combo.compiler)); diff --git a/packages/@angular/cli/utilities/bundle-calculator.ts b/packages/@angular/cli/utilities/bundle-calculator.ts new file mode 100644 index 000000000000..6a048f6f51ea --- /dev/null +++ b/packages/@angular/cli/utilities/bundle-calculator.ts @@ -0,0 +1,204 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +export type BudgetType = 'all' | 'allScript' | 'any' | 'anyScript' | 'bundle' | 'initial'; + +export interface Budget { + /** + * The type of budget + */ + type: BudgetType; + /** + * The name of the bundle + */ + name?: string; + /** + * The baseline size for comparison. + */ + baseline?: string; + /** + * The maximum threshold for warning relative to the baseline. + */ + maximumWarning?: string; + /** + * The maximum threshold for error relative to the baseline. + */ + maximumError?: string; + /** + * The minimum threshold for warning relative to the baseline. + */ + minimumWarning?: string; + /** + * The minimum threshold for error relative to the baseline. + */ + minimumError?: string; + /** + * The threshold for warning relative to the baseline (min & max). + */ + warning?: string; + /** + * The threshold for error relative to the baseline (min & max). + */ + error?: string; +} + +export interface Compilation { + assets: any; + chunks: any[]; + warnings: string[]; + errors: string[]; +} + +export interface Size { + size: number; + label?: string; +} + +export function calculateSizes(budget: Budget, compilation: Compilation): Size[] { + const calculatorMap = { + all: AllCalculator, + allScript: AllScriptCalculator, + any: AnyCalculator, + anyScript: AnyScriptCalculator, + bundle: BundleCalculator, + initial: InitialCalculator, + }; + const ctor = calculatorMap[budget.type]; + const calculator = new ctor(budget, compilation); + return calculator.calculate(); +} + +export abstract class Calculator { + constructor (protected budget: Budget, protected compilation: Compilation) {} + + abstract calculate(): Size[]; +} + +/** + * A named bundle. + */ +class BundleCalculator extends Calculator { + calculate() { + const size: number = this.compilation.chunks + .filter(chunk => chunk.name === this.budget.name) + .reduce((files, chunk) => [...files, ...chunk.files], []) + .map((file: string) => this.compilation.assets[file].size()) + .reduce((total: number, size: number) => total + size, 0); + return [{size, label: this.budget.name}]; + } +} + +/** + * The sum of all initial chunks (marked as initial by webpack). + */ +class InitialCalculator extends Calculator { + calculate() { + const initialChunks = this.compilation.chunks.filter(chunk => chunk.isInitial); + const size: number = initialChunks + .reduce((files, chunk) => [...files, ...chunk.files], []) + .map((file: string) => this.compilation.assets[file].size()) + .reduce((total: number, size: number) => total + size, 0); + return [{size, label: 'initial'}]; + } +} + +/** + * The sum of all the scripts portions. + */ +class AllScriptCalculator extends Calculator { + calculate() { + const size: number = Object.keys(this.compilation.assets) + .filter(key => /\.js$/.test(key)) + .map(key => this.compilation.assets[key]) + .map(asset => asset.size()) + .reduce((total: number, size: number) => total + size, 0); + return [{size, label: 'total scripts'}]; + } +} + +/** + * All scripts and assets added together. + */ +class AllCalculator extends Calculator { + calculate() { + const size: number = Object.keys(this.compilation.assets) + .map(key => this.compilation.assets[key].size()) + .reduce((total: number, size: number) => total + size, 0); + return [{size, label: 'total'}]; + } +} + +/** + * Any script, individually. + */ +class AnyScriptCalculator extends Calculator { + calculate() { + return Object.keys(this.compilation.assets) + .filter(key => /\.js$/.test(key)) + .map(key => { + const asset = this.compilation.assets[key]; + return { + size: asset.size(), + label: key + }; + }); + } +} + +/** + * Any script or asset (images, css, etc). + */ +class AnyCalculator extends Calculator { + calculate() { + return Object.keys(this.compilation.assets) + .map(key => { + const asset = this.compilation.assets[key]; + return { + size: asset.size(), + label: key + }; + }); + } +} + +/** + * Calculate the bytes given a string value. + */ +export function calculateBytes(val: string, baseline?: string, factor?: ('pos' | 'neg')): number { + if (/^\d+$/.test(val)) { + return parseFloat(val); + } + + if (/^(\d+)%$/.test(val)) { + return calculatePercentBytes(val, baseline, factor); + } + + const multiplier = getMultiplier(val); + + const numberVal = parseFloat(val.replace(/((k|m|M|)b?)$/, '')); + const baselineVal = baseline ? parseFloat(baseline.replace(/((k|m|M|)b?)$/, '')) : 0; + const baselineMultiplier = baseline ? getMultiplier(baseline) : 1; + const factorMultiplier = factor ? (factor === 'pos' ? 1 : -1) : 1; + + return numberVal * multiplier + baselineVal * baselineMultiplier * factorMultiplier; +} + +function getMultiplier(val?: string): number { + if (/^(\d+)b?$/.test(val)) { + return 1; + } else if (/^(\d+)kb$/.test(val)) { + return 1000; + } else if (/^(\d+)(m|M)b$/.test(val)) { + return 1000 * 1000; + } +} + +function calculatePercentBytes(val: string, baseline?: string, factor?: ('pos' | 'neg')): number { + const baselineBytes = calculateBytes(baseline); + const percentage = parseFloat(val.replace(/%/g, '')); + return baselineBytes + baselineBytes * percentage / 100 * (factor === 'pos' ? 1 : -1); +} diff --git a/packages/@angular/cli/utilities/dynamic-path-parser.ts b/packages/@angular/cli/utilities/dynamic-path-parser.ts index b6795b928a09..7835939eb27d 100644 --- a/packages/@angular/cli/utilities/dynamic-path-parser.ts +++ b/packages/@angular/cli/utilities/dynamic-path-parser.ts @@ -12,11 +12,11 @@ export interface DynamicPathOptions { export function dynamicPathParser(options: DynamicPathOptions) { const projectRoot = options.project.root; - const sourceDir = options.appConfig.root.replace('/', path.sep); + const sourceDir = options.appConfig.root.replace(/\//g, path.sep); const p = options.appConfig.appRoot === undefined ? 'app' - : options.appConfig.appRoot.replace('/', path.sep); + : options.appConfig.appRoot.replace(/\//g, path.sep); const appRoot = path.join(sourceDir, p); const cwd = process.env.PWD; diff --git a/packages/@angular/cli/utilities/package-chunk-sort.ts b/packages/@angular/cli/utilities/package-chunk-sort.ts index a7841aeb544c..67322ff20156 100644 --- a/packages/@angular/cli/utilities/package-chunk-sort.ts +++ b/packages/@angular/cli/utilities/package-chunk-sort.ts @@ -15,6 +15,10 @@ export function packageChunkSort(appConfig: any) { extraEntryParser(appConfig.styles, './', 'styles').forEach(pushExtraEntries); } + if (appConfig.scripts) { + extraEntryParser(appConfig.scripts, './', 'scripts').forEach(pushExtraEntries); + } + entryPoints.push(...['vendor', 'main']); function sort(left: any, right: any) { diff --git a/packages/@angular/cli/utilities/schematics.ts b/packages/@angular/cli/utilities/schematics.ts index f94067a8e42c..ff468fba4880 100644 --- a/packages/@angular/cli/utilities/schematics.ts +++ b/packages/@angular/cli/utilities/schematics.ts @@ -5,16 +5,19 @@ * require('@schematics/angular') */ +import { schema } from '@angular-devkit/core'; import { Collection, Engine, Schematic, SchematicEngine, + formats, } from '@angular-devkit/schematics'; import { FileSystemCollectionDesc, FileSystemSchematicDesc, - NodeModulesEngineHost + NodeModulesEngineHost, + validateOptionsWithSchema } from '@angular-devkit/schematics/tools'; import { SchemaClassFactory } from '@ngtools/json-schema'; import 'rxjs/add/operator/concatMap'; @@ -26,6 +29,10 @@ const engineHost = new NodeModulesEngineHost(); const engine: Engine = new SchematicEngine(engineHost); +// Add support for schemaJson. +const registry = new schema.CoreSchemaRegistry(formats.standardFormats); +engineHost.registerOptionsTransform(validateOptionsWithSchema(registry)); + export function getEngineHost() { return engineHost; diff --git a/packages/@angular/cli/utilities/service-worker/index.ts b/packages/@angular/cli/utilities/service-worker/index.ts index 8d8b93ac7f55..0d167b632776 100644 --- a/packages/@angular/cli/utilities/service-worker/index.ts +++ b/packages/@angular/cli/utilities/service-worker/index.ts @@ -1,11 +1,11 @@ import { Filesystem } from '@angular/service-worker/config'; -import { stripIndent } from 'common-tags'; +import { oneLine } from 'common-tags'; import * as crypto from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; -export const NEW_SW_VERSION = '>= 5.0.0-rc.0'; +export const NEW_SW_VERSION = '5.0.0-rc.0'; class CliFilesystem implements Filesystem { constructor(private base: string) {} @@ -58,7 +58,7 @@ export function usesServiceWorker(projectRoot: string): boolean { const swPackageJson = fs.readFileSync(`${swModule}/package.json`).toString(); const swVersion = JSON.parse(swPackageJson)['version']; - return semver.satisfies(swVersion, NEW_SW_VERSION); + return semver.gte(swVersion, NEW_SW_VERSION); } export function augmentAppWithServiceWorker(projectRoot: string, appRoot: string, @@ -71,9 +71,9 @@ export function augmentAppWithServiceWorker(projectRoot: string, appRoot: string const configPath = path.resolve(appRoot, 'ngsw-config.json'); if (!fs.existsSync(configPath)) { - throw new Error(stripIndent`Expected to find an ngsw-config.json configuration file in the - application root. Either provide one or disable Service Worker - build support in angular-cli.json.`); + throw new Error(oneLine`Error: Expected to find an ngsw-config.json configuration + file in the ${appRoot} folder. Either provide one or disable Service Worker + in .angular-cli.json.`); } const config = fs.readFileSync(configPath, 'utf8'); diff --git a/packages/@angular/cli/utilities/stats.ts b/packages/@angular/cli/utilities/stats.ts index be9001019aca..c58f4b0bcc17 100644 --- a/packages/@angular/cli/utilities/stats.ts +++ b/packages/@angular/cli/utilities/stats.ts @@ -26,11 +26,9 @@ export function statsToString(json: any, statsConfig: any) { const g = (x: string) => colors ? bold(green(x)) : x; const y = (x: string) => colors ? bold(yellow(x)) : x; - return rs(stripIndents` - Date: ${w(new Date().toISOString())} - Hash: ${w(json.hash)} - Time: ${w('' + json.time)}ms - ${json.chunks.map((chunk: any) => { + const changedChunksStats = json.chunks + .filter((chunk: any) => chunk.rendered) + .map((chunk: any) => { const asset = json.assets.filter((x: any) => x.name == chunk.files[0])[0]; const size = asset ? ` ${_formatSize(asset.size)}` : ''; const files = chunk.files.join(', '); @@ -41,8 +39,24 @@ export function statsToString(json: any, statsConfig: any) { .join(''); return `chunk {${y(chunk.id)}} ${g(files)}${names}${size} ${initial}${flags}`; - }).join('\n')} - `); + }); + + const unchangedChunkNumber = json.chunks.length - changedChunksStats.length; + + if (unchangedChunkNumber > 0) { + return rs(stripIndents` + Date: ${w(new Date().toISOString())} • Hash: ${w(json.hash)} • Time: ${w('' + json.time)}ms + ${unchangedChunkNumber} unchanged chunks + ${changedChunksStats.join('\n')} + `); + } else { + return rs(stripIndents` + Date: ${w(new Date().toISOString())} + Hash: ${w(json.hash)} + Time: ${w('' + json.time)}ms + ${changedChunksStats.join('\n')} + `); + } } export function statsWarningsToString(json: any, statsConfig: any) { diff --git a/packages/@angular/cli/webpack-custom-typings.d.ts b/packages/@angular/cli/webpack-custom-typings.d.ts deleted file mode 100644 index b57841eb3c17..000000000000 --- a/packages/@angular/cli/webpack-custom-typings.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as webpack from 'webpack'; - -declare module 'webpack' { - export class NamedChunksPlugin { - constructor(nameResolver: (chunk: any) => string | null); - } - export class HashedModuleIdsPlugin { - constructor(); - } -} diff --git a/packages/@ngtools/webpack/README.md b/packages/@ngtools/webpack/README.md index fc3fc2529462..7163bc3bd2ca 100644 --- a/packages/@ngtools/webpack/README.md +++ b/packages/@ngtools/webpack/README.md @@ -64,7 +64,7 @@ The loader works with webpack plugin to compile your TypeScript. It's important * `basePath`. Optional. The root to use by the compiler to resolve file paths. By default, use the `tsConfigPath` root. * `entryModule`. Optional if specified in `angularCompilerOptions`. The path and classname of the main application module. This follows the format `path/to/file#ClassName`. * `mainPath`. Optional if `entryModule` is specified. The `main.ts` file containing the bootstrap code. The plugin will use AST to determine the `entryModule`. -* `skipCodeGeneration`. Optional, defaults to false. Disable code generation and do not refactor the code to bootstrap. This replaces `templateUrl: "string"` with `template: require("string")` (and similar for styles) to allow for webpack to properly link the resources. Only available in `AotPlugin`. +* `skipCodeGeneration`. Optional, defaults to false. Disable code generation and do not refactor the code to bootstrap. This replaces `templateUrl: "string"` with `template: require("string")` (and similar for styles) to allow for webpack to properly link the resources. * `typeChecking`. Optional, defaults to true. Enable type checking through your application. This will slow down compilation, but show syntactic and semantic errors in webpack. Only available in `AotPlugin`. * `exclude`. Optional. Extra files to exclude from TypeScript compilation. Not supported with `AngularCompilerPlugin`. * `sourceMap`. Optional. Include sourcemaps. diff --git a/packages/@ngtools/webpack/package.json b/packages/@ngtools/webpack/package.json index 3b7d315e8138..4b4b155b98ba 100644 --- a/packages/@ngtools/webpack/package.json +++ b/packages/@ngtools/webpack/package.json @@ -1,6 +1,6 @@ { "name": "@ngtools/webpack", - "version": "1.9.0-beta.2", + "version": "1.10.0-beta.1", "description": "Webpack plugin that AoT compiles your Angular components and modules.", "main": "./src/index.js", "typings": "src/index.d.ts", @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/angular/angular-cli/tree/master/packages/@ngtools/webpack", "engines": { - "node": ">= 4.1.0", + "node": ">= 6.9.0", "npm": ">= 3.0.0" }, "dependencies": { @@ -31,7 +31,8 @@ "enhanced-resolve": "^3.1.0", "magic-string": "^0.22.3", "semver": "^5.3.0", - "source-map": "^0.5.6" + "source-map": "^0.5.6", + "webpack-sources": "^1.1.0" }, "peerDependencies": { "webpack": "^2.2.0 || ^3.0.0" diff --git a/packages/@ngtools/webpack/src/angular_compiler_plugin.ts b/packages/@ngtools/webpack/src/angular_compiler_plugin.ts index f0c8342791d6..9aa58816a62e 100644 --- a/packages/@ngtools/webpack/src/angular_compiler_plugin.ts +++ b/packages/@ngtools/webpack/src/angular_compiler_plugin.ts @@ -19,6 +19,7 @@ import { import { resolveEntryModuleFromMain } from './entry_resolver'; import { replaceBootstrap, + replaceServerBootstrap, exportNgFactory, exportLazyModuleMap, removeDecorators, @@ -27,7 +28,7 @@ import { replaceResources, } from './transformers'; import { time, timeEnd } from './benchmark'; -import { InitMessage, UpdateMessage } from './type_checker'; +import { InitMessage, UpdateMessage, AUTO_START_ARG } from './type_checker'; import { gatherDiagnostics, hasErrors } from './gather_diagnostics'; import { CompilerCliIsSupported, @@ -106,8 +107,9 @@ export class AngularCompilerPlugin implements Tapable { // Webpack plugin. private _firstRun = true; private _donePromise: Promise | null; - private _compiler: any = null; - private _compilation: any = null; + private _normalizedLocale: string; + private _warnings: (string | Error)[] = []; + private _errors: (string | Error)[] = []; // TypeChecker process. private _forkTypeChecker = true; @@ -133,9 +135,9 @@ export class AngularCompilerPlugin implements Tapable { if (!this._entryModule) { return undefined; } - const splitted = this._entryModule.split('#'); + const splitted = this._entryModule.split(/(#[a-zA-Z_]([\w]+))$/); const path = splitted[0]; - const className = splitted[1] || 'default'; + const className = !!splitted[1] ? splitted[1].substring(1) : 'default'; return { path, className }; } @@ -173,7 +175,7 @@ export class AngularCompilerPlugin implements Tapable { } this._rootNames = config.rootNames.concat(...this._singleFileIncludes); - this._compilerOptions = config.options; + this._compilerOptions = { ...config.options, ...options.compilerOptions }; this._basePath = config.options.basePath; // Overwrite outDir so we can find generated files next to their .ts origin in compilerHost. @@ -225,7 +227,9 @@ export class AngularCompilerPlugin implements Tapable { this._compilerOptions.i18nOutFormat = options.i18nOutFormat; } if (options.locale !== undefined) { - this._compilerOptions.i18nInLocale = this._validateLocale(options.locale); + this._compilerOptions.i18nInLocale = options.locale; + this._compilerOptions.i18nOutLocale = options.locale; + this._normalizedLocale = this._validateLocale(options.locale); } if (options.missingTranslation !== undefined) { this._compilerOptions.i18nInMissingTranslations = @@ -445,7 +449,7 @@ export class AngularCompilerPlugin implements Tapable { if (moduleKey in this._lazyRoutes) { if (this._lazyRoutes[moduleKey] !== modulePath) { // Found a duplicate, this is an error. - this._compilation.warnings.push( + this._warnings.push( new Error(`Duplicated path in loadChildren detected during a rebuild. ` + `We will take the latest version detected and override it to save rebuild time. ` + `You should perform a full build to validate that your routes don't overlap.`) @@ -463,7 +467,7 @@ export class AngularCompilerPlugin implements Tapable { const g: any = global; const typeCheckerFile: string = g['angularCliIsLocal'] ? './type_checker_bootstrap.js' - : './type_checker.js'; + : './type_checker_worker.js'; const debugArgRegex = /--inspect(?:-brk|-port)?|--debug(?:-brk|-port)/; @@ -472,21 +476,46 @@ export class AngularCompilerPlugin implements Tapable { // Workaround for https://github.com/nodejs/node/issues/9435 return !debugArgRegex.test(arg); }); - + // Signal the process to start listening for messages + // Solves https://github.com/angular/angular-cli/issues/9071 + const forkArgs = [AUTO_START_ARG]; const forkOptions: ForkOptions = { execArgv }; - this._typeCheckerProcess = fork(path.resolve(__dirname, typeCheckerFile), [], forkOptions); + this._typeCheckerProcess = fork( + path.resolve(__dirname, typeCheckerFile), + forkArgs, + forkOptions); this._typeCheckerProcess.send(new InitMessage(this._compilerOptions, this._basePath, this._JitMode, this._rootNames)); // Cleanup. const killTypeCheckerProcess = () => { - treeKill(this._typeCheckerProcess.pid, 'SIGTERM'); + if (this._typeCheckerProcess && this._typeCheckerProcess.pid) { + treeKill(this._typeCheckerProcess.pid, 'SIGTERM'); + this._typeCheckerProcess = undefined; + this._forkTypeChecker = false; + } + }; + + // Handle child process exit. + const handleChildProcessExit = () => { + killTypeCheckerProcess(); + const msg = 'AngularCompilerPlugin: Forked Type Checker exited unexpectedly. ' + + 'Falling back to typechecking on main thread.'; + this._warnings.push(msg); + }; + this._typeCheckerProcess.once('exit', handleChildProcessExit); + this._typeCheckerProcess.once('SIGINT', handleChildProcessExit); + this._typeCheckerProcess.once('uncaughtException', handleChildProcessExit); + + // Handle parent process exit. + const handleParentProcessExit = () => { + killTypeCheckerProcess(); process.exit(); }; - process.once('exit', killTypeCheckerProcess); - process.once('SIGINT', killTypeCheckerProcess); - process.once('uncaughtException', killTypeCheckerProcess); + process.once('exit', handleParentProcessExit); + process.once('SIGINT', handleParentProcessExit); + process.once('uncaughtException', handleParentProcessExit); } private _updateForkedTypeChecker(rootNames: string[], changedCompilationFiles: string[]) { @@ -496,8 +525,6 @@ export class AngularCompilerPlugin implements Tapable { // Registration hook for webpack plugin. apply(compiler: any) { - this._compiler = compiler; - // Decorate inputFileSystem to serve contents of CompilerHost. // Use decorated inputFileSystem in watchFileSystem. compiler.plugin('environment', () => { @@ -571,7 +598,6 @@ export class AngularCompilerPlugin implements Tapable { }); compiler.plugin('done', () => { this._donePromise = null; - this._compilation = null; }); // TODO: consider if it's better to remove this plugin and instead make it wait on the @@ -581,9 +607,9 @@ export class AngularCompilerPlugin implements Tapable { // Wait for the plugin to be done when requesting `.ts` files directly (entry points), or // when the issuer is a `.ts` or `.ngfactory.js` file. compiler.resolvers.normal.plugin('before-resolve', (request: any, cb: () => void) => { - if (request.request.endsWith('.ts') - || (request.context.issuer && /\.ts|ngfactory\.js$/.test(request.context.issuer))) { - this.done!.then(() => cb(), () => cb()); + if (this.done && (request.request.endsWith('.ts') + || (request.context.issuer && /\.ts|ngfactory\.js$/.test(request.context.issuer)))) { + this.done.then(() => cb(), () => cb()); } else { cb(); } @@ -602,14 +628,13 @@ export class AngularCompilerPlugin implements Tapable { private _make(compilation: any, cb: (err?: any, request?: any) => void) { time('AngularCompilerPlugin._make'); - this._compilation = compilation; this._emitSkipped = true; - if (this._compilation._ngToolsWebpackPluginInstance) { + if (compilation._ngToolsWebpackPluginInstance) { return cb(new Error('An @ngtools/webpack plugin already exist for this compilation.')); } // Set a private variable for this plugin instance. - this._compilation._ngToolsWebpackPluginInstance = this; + compilation._ngToolsWebpackPluginInstance = this; // Update the resource loader with the new webpack compilation. this._resourceLoader.update(compilation); @@ -622,15 +647,24 @@ export class AngularCompilerPlugin implements Tapable { this._donePromise = Promise.resolve() .then(() => this._update()) .then(() => { + this.pushCompilationErrors(compilation); timeEnd('AngularCompilerPlugin._make'); cb(); }, (err: any) => { compilation.errors.push(err.stack); + this.pushCompilationErrors(compilation); timeEnd('AngularCompilerPlugin._make'); cb(); }); } + private pushCompilationErrors(compilation: any) { + compilation.errors.push(...this._errors); + compilation.warnings.push(...this._warnings); + this._errors = []; + this._warnings = []; + } + private _makeTransformers() { const isAppPath = (fileName: string) => @@ -652,9 +686,9 @@ export class AngularCompilerPlugin implements Tapable { // If we have a locale, auto import the locale data file. // This transform must go before replaceBootstrap because it looks for the entry module // import, which will be replaced. - if (this._compilerOptions.i18nInLocale) { + if (this._normalizedLocale) { this._transformers.push(registerLocaleData(isAppPath, getEntryModule, - this._compilerOptions.i18nInLocale)); + this._normalizedLocale)); } if (!this._JitMode) { @@ -664,7 +698,9 @@ export class AngularCompilerPlugin implements Tapable { } else if (this._platform === PLATFORM.Server) { this._transformers.push(exportLazyModuleMap(isMainPath, getLazyRoutes)); if (!this._JitMode) { - this._transformers.push(exportNgFactory(isMainPath, getEntryModule)); + this._transformers.push( + exportNgFactory(isMainPath, getEntryModule), + replaceServerBootstrap(isMainPath, getEntryModule, getTypeChecker)); } } } @@ -728,18 +764,18 @@ export class AngularCompilerPlugin implements Tapable { if (errors.length > 0) { const message = formatDiagnostics(errors); - this._compilation.errors.push(message); + this._errors.push(message); } if (warnings.length > 0) { const message = formatDiagnostics(warnings); - this._compilation.warnings.push(message); + this._warnings.push(message); } this._emitSkipped = !emitResult || emitResult.emitSkipped; // Reset changed files on successful compilation. - if (!this._emitSkipped && this._compilation.errors.length === 0) { + if (!this._emitSkipped && this._errors.length === 0) { this._compilerHost.resetChangedFileTracker(); } timeEnd('AngularCompilerPlugin._update'); @@ -788,16 +824,20 @@ export class AngularCompilerPlugin implements Tapable { .map((p) => this._compilerHost.denormalizePath(p)); } } else { - // Check if the TS file exists. - if (fileName.endsWith('.ts') && !this._compilerHost.fileExists(fileName, false)) { - throw new Error(`${fileName} is not part of the compilation. ` - + `Please make sure it is in your tsconfig via the 'files' or 'include' property.`); - } + // Check if the TS input file and the JS output file exist. + if ((fileName.endsWith('.ts') && !this._compilerHost.fileExists(fileName, false)) + || !this._compilerHost.fileExists(outputFile, false)) { + let msg = `${fileName} is missing from the TypeScript compilation. ` + + `Please make sure it is in your tsconfig via the 'files' or 'include' property.`; + + if (/(\\|\/)node_modules(\\|\/)/.test(fileName)) { + msg += '\nThe missing file seems to be part of a third party library. ' + + 'TS files in published libraries are often a sign of a badly packaged library. ' + + 'Please open an issue in the library repository to alert its author and ask them ' + + 'to package the library using the Angular Package Format (https://goo.gl/jB3GVv).'; + } - // Check if the output file exists. - if (!this._compilerHost.fileExists(outputFile, false)) { - throw new Error(`${fileName} is not part of the compilation output. ` - + `Please check the other error messages for details.`); + throw new Error(msg); } outputText = this._compilerHost.readFile(outputFile); diff --git a/packages/@ngtools/webpack/src/compiler_host.ts b/packages/@ngtools/webpack/src/compiler_host.ts index 33cc54fe46d6..d53ba4a110bb 100644 --- a/packages/@ngtools/webpack/src/compiler_host.ts +++ b/packages/@ngtools/webpack/src/compiler_host.ts @@ -97,7 +97,6 @@ export class WebpackCompilerHost implements ts.CompilerHost { private _delegate: ts.CompilerHost; private _files: {[path: string]: VirtualFileStats | null} = Object.create(null); private _directories: {[path: string]: VirtualDirStats | null} = Object.create(null); - private _cachedResources: {[path: string]: string | undefined} = Object.create(null); private _changedFiles: {[path: string]: boolean} = Object.create(null); private _changedDirs: {[path: string]: boolean} = Object.create(null); @@ -174,8 +173,8 @@ export class WebpackCompilerHost implements ts.CompilerHost { fileName = this.resolve(fileName); if (fileName in this._files) { this._files[fileName] = null; - this._changedFiles[fileName] = true; } + this._changedFiles[fileName] = true; } fileExists(fileName: string, delegate = true): boolean { @@ -299,22 +298,7 @@ export class WebpackCompilerHost implements ts.CompilerHost { if (this._resourceLoader) { // These paths are meant to be used by the loader so we must denormalize them. const denormalizedFileName = this.denormalizePath(fileName); - const resourceDeps = this._resourceLoader.getResourceDependencies(denormalizedFileName); - - if (this._cachedResources[fileName] === undefined - || resourceDeps.some((dep) => this._changedFiles[this.resolve(dep)])) { - return this._resourceLoader.get(denormalizedFileName) - .then((resource) => { - // Add resource dependencies to the compiler host file list. - // This way we can check the changed files list to determine whether to use cache. - this._resourceLoader.getResourceDependencies(denormalizedFileName) - .forEach((dep) => this.readFile(dep)); - this._cachedResources[fileName] = resource; - return resource; - }); - } else { - return this._cachedResources[fileName]; - } + return this._resourceLoader.get(denormalizedFileName); } else { return this.readFile(fileName); } diff --git a/packages/@ngtools/webpack/src/index.ts b/packages/@ngtools/webpack/src/index.ts index 6292e36f96f8..25689f51811d 100644 --- a/packages/@ngtools/webpack/src/index.ts +++ b/packages/@ngtools/webpack/src/index.ts @@ -1,12 +1,12 @@ // @ignoreDep typescript -import { satisfies } from 'semver'; +import { gte } from 'semver'; // Test if typescript is available. This is a hack. We should be using peerDependencies instead // but can't until we split global and local packages. // See https://github.com/angular/angular-cli/issues/8107#issuecomment-338185872 try { const version = require('typescript').version; - if (!satisfies(version, '^2.0.2')) { + if (!gte(version, '2.0.2')) { throw new Error(); } } catch (e) { diff --git a/packages/@ngtools/webpack/src/ngtools_api.ts b/packages/@ngtools/webpack/src/ngtools_api.ts index e66ea1a71809..074ab5df2fc6 100644 --- a/packages/@ngtools/webpack/src/ngtools_api.ts +++ b/packages/@ngtools/webpack/src/ngtools_api.ts @@ -144,8 +144,9 @@ export function CompilerCliIsSupported() { + 'Please clean your node_modules and reinstall.'); } - // Throw if we're neither 2.3.1 or more, nor 4.x.y, nor 5.x.y. - if (!(version.major == '5' + // Throw if we're neither 2.3.1 or more, nor 4.x.y, nor 5.x.y, nor 6.x.y. + if (!(version.major == '6' + || version.major == '5' || version.major == '4' || (version.major == '2' && (version.minor == '4' diff --git a/packages/@ngtools/webpack/src/paths-plugin.ts b/packages/@ngtools/webpack/src/paths-plugin.ts index 8f05721669a9..c3e05b00bd53 100644 --- a/packages/@ngtools/webpack/src/paths-plugin.ts +++ b/packages/@ngtools/webpack/src/paths-plugin.ts @@ -124,7 +124,7 @@ export class PathsPlugin implements Tapable { this._nmf.plugin('before-resolve', (request: NormalModuleFactoryRequest, callback: Callback) => { // Only work on TypeScript issuers. - if (!request.contextInfo.issuer || !request.contextInfo.issuer.endsWith('.ts')) { + if (!request.contextInfo.issuer || !request.contextInfo.issuer.match(/\.[jt]s$/)) { return callback(null, request); } diff --git a/packages/@ngtools/webpack/src/resource_loader.ts b/packages/@ngtools/webpack/src/resource_loader.ts index 38dc019c6fbc..cce18e26fa77 100644 --- a/packages/@ngtools/webpack/src/resource_loader.ts +++ b/packages/@ngtools/webpack/src/resource_loader.ts @@ -1,5 +1,6 @@ import * as vm from 'vm'; import * as path from 'path'; +import { RawSource } from 'webpack-sources'; const NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin'); const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin'); @@ -17,6 +18,7 @@ export class WebpackResourceLoader { private _context: string; private _uniqueId = 0; private _resourceDependencies = new Map(); + private _cachedResources = new Map(); constructor() {} @@ -36,6 +38,11 @@ export class WebpackResourceLoader { throw new Error('WebpackResourceLoader cannot be used without parentCompilation'); } + // Simple sanity check. + if (filePath.match(/\.[jt]s$/)) { + return Promise.reject('Cannot use a JavaScript or TypeScript file for styleUrl.'); + } + const compilerName = `compiler(${this._uniqueId++})`; const outputOptions = { filename: filePath }; const relativePath = path.relative(this._context || '', filePath); @@ -48,12 +55,7 @@ export class WebpackResourceLoader { new LoaderTargetPlugin('node') ); - // Store the result of the parent compilation before we start the child compilation - let assetsBeforeCompilation = Object.assign( - {}, - this._parentCompilation.assets[outputOptions.filename] - ); - + // NOTE: This is not needed with webpack 3.6+ // Fix for "Uncaught TypeError: __webpack_require__(...) is not a function" // Hot module replacement requires that every child compiler has its own // cache. @see https://github.com/ampedandwired/html-webpack-plugin/pull/179 @@ -66,9 +68,30 @@ export class WebpackResourceLoader { } }); + childCompiler.plugin('this-compilation', (compilation: any) => { + compilation.plugin('additional-assets', (callback: (err?: Error) => void) => { + if (this._cachedResources.has(compilation.fullHash)) { + callback(); + return; + } + + const asset = compilation.assets[filePath]; + if (asset) { + this._evaluate({ outputName: filePath, source: asset.source() }) + .then(output => { + compilation.assets[filePath] = new RawSource(output); + callback(); + }) + .catch(err => callback(err)); + } else { + callback(); + } + }); + }); + // Compile and return a promise return new Promise((resolve, reject) => { - childCompiler.runAsChild((err: Error, entries: any[], childCompilation: any) => { + childCompiler.compile((err: Error, childCompilation: any) => { // Resolve / reject the promise if (childCompilation && childCompilation.errors && childCompilation.errors.length) { const errorDetails = childCompilation.errors.map(function (error: any) { @@ -78,47 +101,35 @@ export class WebpackResourceLoader { } else if (err) { reject(err); } else { - // Replace [hash] placeholders in filename - const outputName = this._parentCompilation.mainTemplate.applyPluginsWaterfall( - 'asset-path', outputOptions.filename, { - hash: childCompilation.hash, - chunk: entries[0] - }); - - // Restore the parent compilation to the state like it was before the child compilation. - Object.keys(childCompilation.assets).forEach((fileName) => { - // If it wasn't there and it's a source file (absolute path) - delete it. - if (assetsBeforeCompilation[fileName] === undefined && path.isAbsolute(fileName)) { - delete this._parentCompilation.assets[fileName]; - } else { - // Otherwise, add it to the parent compilation. - this._parentCompilation.assets[fileName] = childCompilation.assets[fileName]; + Object.keys(childCompilation.assets).forEach(assetName => { + if (assetName !== filePath && this._parentCompilation.assets[assetName] == undefined) { + this._parentCompilation.assets[assetName] = childCompilation.assets[assetName]; } }); // Save the dependencies for this resource. - this._resourceDependencies.set(outputName, childCompilation.fileDependencies); + this._resourceDependencies.set(filePath, childCompilation.fileDependencies); - resolve({ - // Output name. - outputName, - // Compiled code. - source: childCompilation.assets[outputName].source() - }); + const compilationHash = childCompilation.fullHash; + if (this._cachedResources.has(compilationHash)) { + resolve({ + outputName: filePath, + source: this._cachedResources.get(compilationHash), + }); + } else { + const source = childCompilation.assets[filePath].source(); + this._cachedResources.set(compilationHash, source); + resolve({ outputName: filePath, source }); + } } }); }); } - private _evaluate(output: CompilationOutput): Promise { + private _evaluate({ outputName, source }: CompilationOutput): Promise { try { - const outputName = output.outputName; - const vmContext = vm.createContext(Object.assign({ require: require }, global)); - const vmScript = new vm.Script(output.source, { filename: outputName }); - - // Evaluate code and cast to string - let evaluatedSource: string; - evaluatedSource = vmScript.runInContext(vmContext); + // Evaluate code + const evaluatedSource = vm.runInNewContext(source, undefined, { filename: outputName }); if (typeof evaluatedSource == 'string') { return Promise.resolve(evaluatedSource); @@ -132,6 +143,6 @@ export class WebpackResourceLoader { get(filePath: string): Promise { return this._compile(filePath) - .then((result: CompilationOutput) => this._evaluate(result)); + .then((result: CompilationOutput) => result.source); } } diff --git a/packages/@ngtools/webpack/src/transformers/elide_imports.ts b/packages/@ngtools/webpack/src/transformers/elide_imports.ts index a12bb030f77b..d1cd5b640c34 100644 --- a/packages/@ngtools/webpack/src/transformers/elide_imports.ts +++ b/packages/@ngtools/webpack/src/transformers/elide_imports.ts @@ -99,7 +99,11 @@ export function elideImports( .forEach((id) => { if (removedSymbolMap.has(id.text)) { const symbol = removedSymbolMap.get(id.text); - if (typeChecker.getSymbolAtLocation(id) === symbol.symbol) { + + // Check if the symbol is the same or if it is a named export. + // Named exports don't have the same symbol but will have the same name. + if ((id.parent && id.parent.kind === ts.SyntaxKind.ExportSpecifier) + || typeChecker.getSymbolAtLocation(id) === symbol.symbol) { symbol.all.push(id); } } diff --git a/packages/@ngtools/webpack/src/transformers/index.ts b/packages/@ngtools/webpack/src/transformers/index.ts index 4ee76368978c..e523cc7782e8 100644 --- a/packages/@ngtools/webpack/src/transformers/index.ts +++ b/packages/@ngtools/webpack/src/transformers/index.ts @@ -4,6 +4,7 @@ export * from './make_transform'; export * from './insert_import'; export * from './elide_imports'; export * from './replace_bootstrap'; +export * from './replace_server_bootstrap'; export * from './export_ngfactory'; export * from './export_lazy_module_map'; export * from './register_locale_data'; diff --git a/packages/@ngtools/webpack/src/transformers/make_transform.ts b/packages/@ngtools/webpack/src/transformers/make_transform.ts index ae6befae01bf..c2f6ed85eb59 100644 --- a/packages/@ngtools/webpack/src/transformers/make_transform.ts +++ b/packages/@ngtools/webpack/src/transformers/make_transform.ts @@ -129,11 +129,18 @@ function visitEachChildWorkaround(node: ts.Node, visitor: ts.Visitor, } -// If TS sees an empty decorator array, it will still emit a `__decorate` call. -// This seems to be a TS bug. +// 1) If TS sees an empty decorator array, it will still emit a `__decorate` call. +// This seems to be a TS bug. +// 2) Also ensure nodes with modified decorators have parents +// built in TS transformers assume certain nodes have parents (fixed in TS 2.7+) function cleanupDecorators(node: ts.Node) { - if (node.decorators && node.decorators.length == 0) { - node.decorators = undefined; + if (node.decorators) { + if (node.decorators.length == 0) { + node.decorators = undefined; + } else if (node.parent == undefined) { + const originalNode = ts.getParseTreeNode(node); + node.parent = originalNode.parent; + } } ts.forEachChild(node, node => cleanupDecorators(node)); diff --git a/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts b/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts index 3fbfe4ad7203..fedf80298ca3 100644 --- a/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts +++ b/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts @@ -76,6 +76,54 @@ describe('@ngtools/webpack transformers', () => { expect(oneLine`${result}`).toEqual(oneLine`${output}`); }); + it('should keep other decorators on class member', () => { + const input = stripIndent` + import { Component, HostListener } from '@angular/core'; + import { AnotherDecorator } from 'another-lib'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] + }) + export class AppComponent { + title = 'app'; + + @HostListener('document:keydown.escape') + @AnotherDecorator() + onEscape() { + console.log('run'); + } + } + `; + const output = stripIndent` + import * as tslib_1 from "tslib"; + import { AnotherDecorator } from 'another-lib'; + + export class AppComponent { + constructor() { + this.title = 'app'; + } + + onEscape() { + console.log('run'); + } + } + tslib_1.__decorate([ + AnotherDecorator() + ], AppComponent.prototype, "onEscape", null); + `; + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = removeDecorators( + () => true, + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + it('should remove imports for identifiers within the decorator', () => { const input = stripIndent` import { Component } from '@angular/core'; @@ -111,7 +159,7 @@ describe('@ngtools/webpack transformers', () => { it('should not remove imports from types that are still used', () => { const input = stripIndent` - import { Component, EventEmitter } from '@angular/core'; + import { Component, ChangeDetectionStrategy, EventEmitter } from '@angular/core'; @Component({ selector: 'app-root', @@ -123,9 +171,11 @@ describe('@ngtools/webpack transformers', () => { notify: EventEmitter = new EventEmitter(); title = 'app'; } + + export { ChangeDetectionStrategy }; `; const output = stripIndent` - import { EventEmitter } from '@angular/core'; + import { ChangeDetectionStrategy, EventEmitter } from '@angular/core'; export class AppComponent { constructor() { @@ -133,6 +183,8 @@ describe('@ngtools/webpack transformers', () => { this.title = 'app'; } } + + export { ChangeDetectionStrategy }; `; const { program, compilerHost } = createTypescriptContext(input); diff --git a/packages/@ngtools/webpack/src/transformers/replace_resources.spec.ts b/packages/@ngtools/webpack/src/transformers/replace_resources.spec.ts index 0382d746c96f..d3c454e00d33 100644 --- a/packages/@ngtools/webpack/src/transformers/replace_resources.spec.ts +++ b/packages/@ngtools/webpack/src/transformers/replace_resources.spec.ts @@ -41,6 +41,43 @@ describe('@ngtools/webpack transformers', () => { expect(oneLine`${result}`).toEqual(oneLine`${output}`); }); + it('should replace resources with backticks', () => { + const input = stripIndent` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + templateUrl: \`./app.component.html\`, + styleUrls: [\`./app.component.css\`, \`./app.component.2.css\`] + }) + export class AppComponent { + title = 'app'; + } + `; + const output = stripIndent` + import * as tslib_1 from "tslib"; + import { Component } from '@angular/core'; + let AppComponent = class AppComponent { + constructor() { + this.title = 'app'; + } + }; + AppComponent = tslib_1.__decorate([ + Component({ + selector: 'app-root', + template: require("./app.component.html"), + styles: [require("./app.component.css"), require("./app.component.2.css")] + }) + ], AppComponent); + export { AppComponent }; + `; + + const transformer = replaceResources(() => true); + const result = transformTypescript(input, [transformer]); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + it('should not replace resources if shouldTransform returns false', () => { const input = stripIndent` import { Component } from '@angular/core'; diff --git a/packages/@ngtools/webpack/src/transformers/replace_resources.ts b/packages/@ngtools/webpack/src/transformers/replace_resources.ts index 7d206e809b19..875ee464856e 100644 --- a/packages/@ngtools/webpack/src/transformers/replace_resources.ts +++ b/packages/@ngtools/webpack/src/transformers/replace_resources.ts @@ -131,7 +131,10 @@ function _getContentOfKeyLiteral(node?: ts.Node): string | null { } function _getResourceRequest(element: ts.Expression, sourceFile: ts.SourceFile) { - if (element.kind == ts.SyntaxKind.StringLiteral) { + if ( + element.kind === ts.SyntaxKind.StringLiteral || + element.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral + ) { const url = (element as ts.StringLiteral).text; // If the URL does not start with ./ or ../, prepends ./ to it. return `${/^\.?\.\//.test(url) ? '' : './'}${url}`; diff --git a/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.spec.ts b/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.spec.ts new file mode 100644 index 000000000000..5ca49b0e2ab3 --- /dev/null +++ b/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.spec.ts @@ -0,0 +1,215 @@ +import { oneLine, stripIndent } from 'common-tags'; +import { createTypescriptContext, transformTypescript } from './ast_helpers'; +import { replaceServerBootstrap } from './replace_server_bootstrap'; + +describe('@ngtools/webpack transformers', () => { + describe('replace_server_bootstrap', () => { + it('should replace bootstrap', () => { + const input = stripIndent` + import { enableProdMode } from '@angular/core'; + import { platformDynamicServer } from '@angular/platform-server'; + + import { AppModule } from './app/app.module'; + import { environment } from './environments/environment'; + + if (environment.production) { + enableProdMode(); + } + + platformDynamicServer().bootstrapModule(AppModule); + `; + + // tslint:disable:max-line-length + const output = stripIndent` + import { enableProdMode } from '@angular/core'; + import { environment } from './environments/environment'; + + import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory"; + import * as __NgCli_bootstrap_2 from "@angular/platform-server"; + + if (environment.production) { + enableProdMode(); + } + __NgCli_bootstrap_2.platformServer().bootstrapModuleFactory(__NgCli_bootstrap_1.AppModuleNgFactory); + `; + // tslint:enable:max-line-length + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = replaceServerBootstrap( + () => true, + () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + + it('should replace renderModule', () => { + const input = stripIndent` + import { enableProdMode } from '@angular/core'; + import { renderModule } from '@angular/platform-server'; + + import { AppModule } from './app/app.module'; + import { environment } from './environments/environment'; + + if (environment.production) { + enableProdMode(); + } + + renderModule(AppModule, { + document: '', + url: '/' + }); + `; + + // tslint:disable:max-line-length + const output = stripIndent` + import { enableProdMode } from '@angular/core'; + import { environment } from './environments/environment'; + + import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory"; + import * as __NgCli_bootstrap_2 from "@angular/platform-server"; + + if (environment.production) { + enableProdMode(); + } + __NgCli_bootstrap_2.renderModuleFactory(__NgCli_bootstrap_1.AppModuleNgFactory, { + document: '', + url: '/' + }); + `; + // tslint:enable:max-line-length + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = replaceServerBootstrap( + () => true, + () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + + it('should replace when the module is used in a config object', () => { + const input = stripIndent` + import * as express from 'express'; + + import { enableProdMode } from '@angular/core'; + import { ngExpressEngine } from '@nguniversal/express-engine'; + + import { AppModule } from './app/app.module'; + import { environment } from './environments/environment'; + + if (environment.production) { + enableProdMode(); + } + + const server = express(); + server.engine('html', ngExpressEngine({ + bootstrap: AppModule + })); + `; + + // tslint:disable:max-line-length + const output = stripIndent` + import * as express from 'express'; + + import { enableProdMode } from '@angular/core'; + import { ngExpressEngine } from '@nguniversal/express-engine'; + + import { environment } from './environments/environment'; + + import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory"; + + if (environment.production) { + enableProdMode(); + } + + const server = express(); + server.engine('html', ngExpressEngine({ + bootstrap: __NgCli_bootstrap_1.AppModuleNgFactory + })); + `; + // tslint:enable:max-line-length + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = replaceServerBootstrap( + () => true, + () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + + it('should replace bootstrap when barrel files are used', () => { + const input = stripIndent` + import { enableProdMode } from '@angular/core'; + import { platformDynamicServer } from '@angular/platform-browser-dynamic'; + + import { AppModule } from './app'; + import { environment } from './environments/environment'; + + if (environment.production) { + enableProdMode(); + } + + platformDynamicServer().bootstrapModule(AppModule); + `; + + // tslint:disable:max-line-length + const output = stripIndent` + import { enableProdMode } from '@angular/core'; + import { environment } from './environments/environment'; + + import * as __NgCli_bootstrap_1 from "./app/app.module.ngfactory"; + import * as __NgCli_bootstrap_2 from "@angular/platform-server"; + + if (environment.production) { + enableProdMode(); + } + __NgCli_bootstrap_2.platformServer().bootstrapModuleFactory(__NgCli_bootstrap_1.AppModuleNgFactory); + `; + // tslint:enable:max-line-length + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = replaceServerBootstrap( + () => true, + () => ({ path: '/project/src/app/app.module', className: 'AppModule' }), + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + + it('should not replace bootstrap when there is no entry module', () => { + const input = stripIndent` + import { enableProdMode } from '@angular/core'; + import { platformDynamicServer } from '@angular/platform-browser-dynamic'; + + import { AppModule } from './app/app.module'; + import { environment } from './environments/environment'; + + if (environment.production) { + enableProdMode(); + } + + platformDynamicServer().bootstrapModule(AppModule); + `; + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = replaceServerBootstrap( + () => true, + () => undefined, + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${input}`); + }); + }); +}); diff --git a/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.ts b/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.ts new file mode 100644 index 000000000000..a38ee8982c26 --- /dev/null +++ b/packages/@ngtools/webpack/src/transformers/replace_server_bootstrap.ts @@ -0,0 +1,135 @@ +// @ignoreDep typescript +import * as ts from 'typescript'; +import { relative, dirname } from 'path'; + +import { collectDeepNodes } from './ast_helpers'; +import { insertStarImport } from './insert_import'; +import { StandardTransform, ReplaceNodeOperation, TransformOperation } from './interfaces'; +import { makeTransform } from './make_transform'; + +export function replaceServerBootstrap( + shouldTransform: (fileName: string) => boolean, + getEntryModule: () => { path: string, className: string }, + getTypeChecker: () => ts.TypeChecker, +): ts.TransformerFactory { + + const standardTransform: StandardTransform = function (sourceFile: ts.SourceFile) { + const ops: TransformOperation[] = []; + + const entryModule = getEntryModule(); + + if (!shouldTransform(sourceFile.fileName) || !entryModule) { + return ops; + } + + // Find all identifiers. + const entryModuleIdentifiers = collectDeepNodes(sourceFile, + ts.SyntaxKind.Identifier) + .filter(identifier => identifier.text === entryModule.className); + + if (entryModuleIdentifiers.length === 0) { + return []; + } + + const relativeEntryModulePath = relative(dirname(sourceFile.fileName), entryModule.path); + const normalizedEntryModulePath = `./${relativeEntryModulePath}`.replace(/\\/g, '/'); + const factoryClassName = entryModule.className + 'NgFactory'; + const factoryModulePath = normalizedEntryModulePath + '.ngfactory'; + + // Find the bootstrap calls. + entryModuleIdentifiers.forEach(entryModuleIdentifier => { + if (!entryModuleIdentifier.parent) { + return; + } + + if (entryModuleIdentifier.parent.kind !== ts.SyntaxKind.CallExpression && + entryModuleIdentifier.parent.kind !== ts.SyntaxKind.PropertyAssignment) { + return; + } + + if (entryModuleIdentifier.parent.kind === ts.SyntaxKind.CallExpression) { + // Figure out if it's a `platformDynamicServer().bootstrapModule(AppModule)` call. + + const callExpr = entryModuleIdentifier.parent as ts.CallExpression; + + if (callExpr.expression.kind === ts.SyntaxKind.PropertyAccessExpression) { + + const propAccessExpr = callExpr.expression as ts.PropertyAccessExpression; + + if (!(propAccessExpr.name.text === 'bootstrapModule' + && propAccessExpr.expression.kind === ts.SyntaxKind.CallExpression)) { + return; + } + + const bootstrapModuleIdentifier = propAccessExpr.name; + const innerCallExpr = propAccessExpr.expression as ts.CallExpression; + + if (!( + innerCallExpr.expression.kind === ts.SyntaxKind.Identifier + && (innerCallExpr.expression as ts.Identifier).text === 'platformDynamicServer' + )) { + return; + } + + const platformDynamicServerIdentifier = innerCallExpr.expression as ts.Identifier; + + const idPlatformServer = ts.createUniqueName('__NgCli_bootstrap_'); + const idNgFactory = ts.createUniqueName('__NgCli_bootstrap_'); + + // Add the transform operations. + ops.push( + // Replace the entry module import. + ...insertStarImport(sourceFile, idNgFactory, factoryModulePath), + new ReplaceNodeOperation(sourceFile, entryModuleIdentifier, + ts.createPropertyAccess(idNgFactory, ts.createIdentifier(factoryClassName))), + // Replace the platformBrowserDynamic import. + ...insertStarImport(sourceFile, idPlatformServer, '@angular/platform-server'), + new ReplaceNodeOperation(sourceFile, platformDynamicServerIdentifier, + ts.createPropertyAccess(idPlatformServer, 'platformServer')), + new ReplaceNodeOperation(sourceFile, bootstrapModuleIdentifier, + ts.createIdentifier('bootstrapModuleFactory')), + ); + } else if (callExpr.expression.kind === ts.SyntaxKind.Identifier) { + // Figure out if it is renderModule + + const identifierExpr = callExpr.expression as ts.Identifier; + + if (identifierExpr.text !== 'renderModule') { + return; + } + + const renderModuleIdentifier = identifierExpr as ts.Identifier; + + const idPlatformServer = ts.createUniqueName('__NgCli_bootstrap_'); + const idNgFactory = ts.createUniqueName('__NgCli_bootstrap_'); + + ops.push( + // Replace the entry module import. + ...insertStarImport(sourceFile, idNgFactory, factoryModulePath), + new ReplaceNodeOperation(sourceFile, entryModuleIdentifier, + ts.createPropertyAccess(idNgFactory, ts.createIdentifier(factoryClassName))), + // Replace the renderModule import. + ...insertStarImport(sourceFile, idPlatformServer, '@angular/platform-server'), + new ReplaceNodeOperation(sourceFile, renderModuleIdentifier, + ts.createPropertyAccess(idPlatformServer, 'renderModuleFactory')), + ); + } + } else if (entryModuleIdentifier.parent.kind === ts.SyntaxKind.PropertyAssignment) { + // This is for things that accept a module as a property in a config object + // .ie the express engine + + const idNgFactory = ts.createUniqueName('__NgCli_bootstrap_'); + + ops.push( + ...insertStarImport(sourceFile, idNgFactory, factoryModulePath), + new ReplaceNodeOperation(sourceFile, entryModuleIdentifier, + ts.createPropertyAccess(idNgFactory, ts.createIdentifier(factoryClassName))) + ); + } + }); + + return ops; + }; + + return makeTransform(standardTransform, getTypeChecker); +} diff --git a/packages/@ngtools/webpack/src/type_checker.ts b/packages/@ngtools/webpack/src/type_checker.ts index e17f363b78ff..e14b8a55b88b 100644 --- a/packages/@ngtools/webpack/src/type_checker.ts +++ b/packages/@ngtools/webpack/src/type_checker.ts @@ -1,11 +1,6 @@ // @ignoreDep typescript -import * as process from 'process'; import * as ts from 'typescript'; import chalk from 'chalk'; - -import { WebpackCompilerHost } from './compiler_host'; -import { time, timeEnd } from './benchmark'; -import { CancellationToken, gatherDiagnostics } from './gather_diagnostics'; import { Program, CompilerOptions, @@ -14,12 +9,19 @@ import { createCompilerHost, formatDiagnostics, } from './ngtools_api'; +import { WebpackCompilerHost } from './compiler_host'; +import { time, timeEnd } from './benchmark'; +import { CancellationToken, gatherDiagnostics } from './gather_diagnostics'; + + +// This file should run in a child process with the AUTO_START_ARG argument // Force basic color support on terminals with no color support. // Chalk typings don't have the correct constructor parameters. const chalkCtx = new (chalk.constructor as any)(chalk.supportsColor ? {} : { level: 1 }); const { bold, red, yellow } = chalkCtx; + export enum MESSAGE_KIND { Init, Update @@ -46,43 +48,9 @@ export class UpdateMessage extends TypeCheckerMessage { } } -let typeChecker: TypeChecker; -let lastCancellationToken: CancellationToken; - -process.on('message', (message: TypeCheckerMessage) => { - time('TypeChecker.message'); - switch (message.kind) { - case MESSAGE_KIND.Init: - const initMessage = message as InitMessage; - typeChecker = new TypeChecker( - initMessage.compilerOptions, - initMessage.basePath, - initMessage.jitMode, - initMessage.rootNames, - ); - break; - case MESSAGE_KIND.Update: - if (!typeChecker) { - throw new Error('TypeChecker: update message received before initialization'); - } - if (lastCancellationToken) { - // This cancellation token doesn't seem to do much, messages don't seem to be processed - // before the diagnostics finish. - lastCancellationToken.requestCancellation(); - } - const updateMessage = message as UpdateMessage; - lastCancellationToken = new CancellationToken(); - typeChecker.update(updateMessage.rootNames, updateMessage.changedCompilationFiles, - lastCancellationToken); - break; - default: - throw new Error(`TypeChecker: Unexpected message received: ${message}.`); - } - timeEnd('TypeChecker.message'); -}); - +export const AUTO_START_ARG = '9d93e901-158a-4cf9-ba1b-2f0582ffcfeb'; -class TypeChecker { +export class TypeChecker { private _program: ts.Program | Program; private _compilerHost: WebpackCompilerHost & CompilerHost; @@ -171,3 +139,4 @@ class TypeChecker { this._diagnose(cancellationToken); } } + diff --git a/packages/@ngtools/webpack/src/type_checker_bootstrap.js b/packages/@ngtools/webpack/src/type_checker_bootstrap.js index e5a3ab002ecc..9326a42d9b4f 100644 --- a/packages/@ngtools/webpack/src/type_checker_bootstrap.js +++ b/packages/@ngtools/webpack/src/type_checker_bootstrap.js @@ -1,2 +1,2 @@ require('../../../../lib/bootstrap-local'); -require('./type_checker.ts'); +require('./type_checker_worker.ts'); diff --git a/packages/@ngtools/webpack/src/type_checker_worker.ts b/packages/@ngtools/webpack/src/type_checker_worker.ts new file mode 100644 index 000000000000..ce1cafba57cb --- /dev/null +++ b/packages/@ngtools/webpack/src/type_checker_worker.ts @@ -0,0 +1,53 @@ +import * as process from 'process'; + +import { time, timeEnd } from './benchmark'; +import { CancellationToken } from './gather_diagnostics'; + +import { + AUTO_START_ARG, + TypeCheckerMessage, + InitMessage, + MESSAGE_KIND, + UpdateMessage, + TypeChecker +} from './type_checker'; + +let typeChecker: TypeChecker; +let lastCancellationToken: CancellationToken; + +// only listen to messages if started from the AngularCompilerPlugin +if (process.argv.indexOf(AUTO_START_ARG) >= 0) { + process.on('message', (message: TypeCheckerMessage) => { + time('TypeChecker.message'); + switch (message.kind) { + case MESSAGE_KIND.Init: + const initMessage = message as InitMessage; + typeChecker = new TypeChecker( + initMessage.compilerOptions, + initMessage.basePath, + initMessage.jitMode, + initMessage.rootNames, + ); + break; + case MESSAGE_KIND.Update: + if (!typeChecker) { + throw new Error('TypeChecker: update message received before initialization'); + } + if (lastCancellationToken) { + // This cancellation token doesn't seem to do much, messages don't seem to be processed + // before the diagnostics finish. + lastCancellationToken.requestCancellation(); + } + const updateMessage = message as UpdateMessage; + lastCancellationToken = new CancellationToken(); + typeChecker.update(updateMessage.rootNames, updateMessage.changedCompilationFiles, + lastCancellationToken); + break; + default: + throw new Error(`TypeChecker: Unexpected message received: ${message}.`); + } + timeEnd('TypeChecker.message'); + }); +} + + diff --git a/scripts/test-licenses.js b/scripts/test-licenses.js index b7c700c21c90..ae68d7f75d1e 100644 --- a/scripts/test-licenses.js +++ b/scripts/test-licenses.js @@ -79,6 +79,7 @@ const ignoredPackages = [ 'spdx-license-ids@2.0.1', // CC0 but it's content only (index.json, no code) and not distributed. 'map-stream@0.1.0', // MIT, license but it's not listed in package.json. 'xmldom@0.1.27', // LGPL,MIT but has a broken licenses array. + 'true-case-path@1.0.2', // Apache-2.0 but broken license in package.json 'jsonify@0.0.0', // TODO(hansl): fix this. this is not an acceptable license, but is 8 deps down // so hard to manage. In talk with owner and users to switch over. diff --git a/tests/acceptance/bundle-calculator.spec.ts b/tests/acceptance/bundle-calculator.spec.ts new file mode 100644 index 000000000000..d60ac083ac3a --- /dev/null +++ b/tests/acceptance/bundle-calculator.spec.ts @@ -0,0 +1,86 @@ +import * as path from 'path'; +import { calculateBytes, calculateSizes } from '@angular/cli/utilities/bundle-calculator'; +import mockFs = require('mock-fs'); + + +describe('bundle calculator', () => { + describe('calculateBytes', () => { + const kb = (n: number) => n * 1000; + const mb = (n: number) => n * 1000 * 1000; + const scenarios: any[] = [ + { expect: 1, val: '1' }, + { expect: 1, val: '1b' }, + { expect: kb(1), val: '1kb' }, + { expect: mb(1), val: '1mb' }, + { expect: 110, val: '100b', baseline: '10', factor: 'pos' }, + { expect: 110, val: '100b', baseline: '10b', factor: 'pos' }, + { expect: 90, val: '100b', baseline: '10', factor: 'neg' }, + { expect: 90, val: '100b', baseline: '10b', factor: 'neg' }, + { expect: 15, val: '50%', baseline: '10', factor: 'pos' }, + { expect: 5, val: '50%', baseline: '10', factor: 'neg' }, + { expect: kb(50) + mb(1), val: '50kb', baseline: '1mb', factor: 'pos' }, + { expect: mb(1.25), val: '25%', baseline: '1mb', factor: 'pos' }, + { expect: mb(0.75), val: '25%', baseline: '1mb', factor: 'neg' }, + ]; + scenarios.forEach(s => { + const specMsg = `${s.val} => ${s.expect}`; + const baselineMsg = s.baseline ? ` (baseline: ${s.baseline})` : ``; + const factor = s.factor ? ` (factor: ${s.factor})` : ``; + it(`should calculateBytes ${specMsg}${baselineMsg}${factor}`, () => { + const result = calculateBytes(s.val, s.baseline, s.factor); + expect(s.expect).toEqual(result); + }); + }); + }); + + describe('calculateSizes', () => { + let compilation: any; + beforeEach(() => { + compilation = { + assets: { + 'asset1.js': { size: () => 1 }, + 'asset2': { size: () => 2 }, + 'asset3.js': { size: () => 4 }, + 'asset4': { size: () => 8 }, + 'asset5': { size: () => 16 }, + }, + chunks: [ + { name: 'chunk1', files: ['asset1.js'], isInitial: true }, + { name: 'chunk2', files: ['asset2'], isInitial: false }, + { name: 'chunk3', files: ['asset3.js', 'asset4'], isInitial: false } + ] + }; + }); + + const scenarios: any[] = [ + { expect: [{size: 31, label: 'total'}], budget: { type: 'all' } }, + { expect: [{size: 5, label: 'total scripts'}], budget: { type: 'allScript' } }, + { expect: [ + {size: 1, label: 'asset1.js'}, + {size: 2, label: 'asset2'}, + {size: 4, label: 'asset3.js'}, + {size: 8, label: 'asset4'}, + {size: 16, label: 'asset5'}, + ], budget: { type: 'any' } }, + { expect: [ + {size: 1, label: 'asset1.js'}, + {size: 4, label: 'asset3.js'}, + ], budget: { type: 'anyScript' } }, + { expect: [{size: 2, label: 'chunk2'}], budget: { type: 'bundle', name: 'chunk2' } }, + { expect: [{size: 12, label: 'chunk3'}], budget: { type: 'bundle', name: 'chunk3' } }, + { expect: [{size: 1, label: 'initial'}], budget: { type: 'initial' } }, + ]; + + scenarios.forEach(s => { + const budgetName = s.budget.name ? ` (${s.budget.name})` : ''; + it(`should calulate sizes for ${s.budget.type}${budgetName}`, () => { + const sizes = calculateSizes(s.budget, compilation); + expect(sizes.length).toEqual(s.expect.length); + for (let i = 0; i < sizes.length; i++) { + expect(sizes[i].size).toEqual((s.expect[i].size)); + expect(sizes[i].label).toEqual((s.expect[i].label)); + } + }); + }); + }); +}); diff --git a/tests/e2e/assets/images/spectrum.png b/tests/e2e/assets/images/spectrum.png new file mode 100644 index 000000000000..2a5f123afc84 Binary files /dev/null and b/tests/e2e/assets/images/spectrum.png differ diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.html b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.html new file mode 100644 index 000000000000..5a532db9308f --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.html @@ -0,0 +1,5 @@ +
+

hello world

+ lazy + +
diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.scss b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.scss new file mode 100644 index 000000000000..5cde7b922336 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.scss @@ -0,0 +1,3 @@ +:host { + background-color: blue; +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.ts new file mode 100644 index 000000000000..82a4059565d3 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.component.ts @@ -0,0 +1,15 @@ +import {Component, ViewEncapsulation} from '@angular/core'; +import {MyInjectable} from './injectable'; + + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class AppComponent { + constructor(public inj: MyInjectable) { + console.log(inj); + } +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/app.module.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.module.ts new file mode 100644 index 000000000000..7c8a0c296448 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/app.module.ts @@ -0,0 +1,36 @@ +import { NgModule, Component } from '@angular/core'; +import { ServerModule } from '@angular/platform-server'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; + +import { AppComponent } from './app.component'; +import { MyInjectable } from './injectable'; + +@Component({ + selector: 'home-view', + template: 'home!' +}) +export class HomeView {} + + +@NgModule({ + declarations: [ + AppComponent, + HomeView + ], + imports: [ + BrowserModule.withServerTransition({ + appId: 'app' + }), + ServerModule, + RouterModule.forRoot([ + {path: 'lazy', loadChildren: './lazy.module#LazyModule'}, + {path: '', component: HomeView} + ]) + ], + providers: [MyInjectable], + bootstrap: [AppComponent] +}) +export class AppModule { + static testProp: string; +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/feature.module.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/feature.module.ts new file mode 100644 index 000000000000..f464ca028b05 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/feature.module.ts @@ -0,0 +1,20 @@ +import {NgModule, Component} from '@angular/core'; +import {RouterModule} from '@angular/router'; + +@Component({ + selector: 'feature-component', + template: 'foo.html' +}) +export class FeatureComponent {} + +@NgModule({ + declarations: [ + FeatureComponent + ], + imports: [ + RouterModule.forChild([ + { path: '', component: FeatureComponent} + ]) + ] +}) +export class FeatureModule {} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/lazy-feature.module.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/lazy-feature.module.ts new file mode 100644 index 000000000000..8fafca158b24 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/feature/lazy-feature.module.ts @@ -0,0 +1,23 @@ +import {NgModule, Component} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {HttpModule, Http} from '@angular/http'; + +@Component({ + selector: 'lazy-feature-comp', + template: 'lazy feature!' +}) +export class LazyFeatureComponent {} + +@NgModule({ + imports: [ + RouterModule.forChild([ + {path: '', component: LazyFeatureComponent, pathMatch: 'full'}, + {path: 'feature', loadChildren: './feature.module#FeatureModule'} + ]), + HttpModule + ], + declarations: [LazyFeatureComponent] +}) +export class LazyFeatureModule { + constructor(http: Http) {} +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/injectable.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/injectable.ts new file mode 100644 index 000000000000..b357678ae77a --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/injectable.ts @@ -0,0 +1,8 @@ +import {Injectable, Inject, ViewContainerRef} from '@angular/core'; +import {DOCUMENT} from '@angular/platform-browser'; + + +@Injectable() +export class MyInjectable { + constructor(@Inject(DOCUMENT) public doc) {} +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/lazy.module.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/lazy.module.ts new file mode 100644 index 000000000000..96da4de7515b --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/lazy.module.ts @@ -0,0 +1,26 @@ +import {NgModule, Component} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {HttpModule, Http} from '@angular/http'; + +@Component({ + selector: 'lazy-comp', + template: 'lazy!' +}) +export class LazyComponent {} + +@NgModule({ + imports: [ + RouterModule.forChild([ + {path: '', component: LazyComponent, pathMatch: 'full'}, + {path: 'feature', loadChildren: './feature/feature.module#FeatureModule'}, + {path: 'lazy-feature', loadChildren: './feature/lazy-feature.module#LazyFeatureModule'} + ]), + HttpModule + ], + declarations: [LazyComponent] +}) +export class LazyModule { + constructor(http: Http) {} +} + +export class SecondModule {} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/main.commonjs.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/main.commonjs.ts new file mode 100644 index 000000000000..ce26d93a11de --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/main.commonjs.ts @@ -0,0 +1 @@ +export { AppModule } from './app.module'; diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/app/main.ts b/tests/e2e/assets/webpack/test-server-app-ng5/app/main.ts new file mode 100644 index 000000000000..5d57aafca8ae --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/app/main.ts @@ -0,0 +1,12 @@ +import 'core-js/es7/reflect'; +import {platformDynamicServer, renderModule} from '@angular/platform-server'; +import {AppModule} from './app.module'; + +AppModule.testProp = 'testing'; + +platformDynamicServer().bootstrapModule(AppModule); + +renderModule(AppModule, { + document: '', + url: '/' +}); diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/index.html b/tests/e2e/assets/webpack/test-server-app-ng5/index.html new file mode 100644 index 000000000000..89fb0893c35d --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/index.html @@ -0,0 +1,12 @@ + + + + Document + + + + + + + + diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/index.js b/tests/e2e/assets/webpack/test-server-app-ng5/index.js new file mode 100644 index 000000000000..bdfb2e792acd --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/index.js @@ -0,0 +1,12 @@ +const fs = require('fs'); +const { AppModuleNgFactory } = require('./dist/app.main'); +const { renderModuleFactory } = require('@angular/platform-server'); + +require('zone.js/dist/zone-node'); + +renderModuleFactory(AppModuleNgFactory, { + url: '/', + document: '' +}).then(html => { + fs.writeFileSync('dist/index.html', html); +}) diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/package.json b/tests/e2e/assets/webpack/test-server-app-ng5/package.json new file mode 100644 index 000000000000..b499eaaa4e25 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/package.json @@ -0,0 +1,28 @@ +{ + "name": "test", + "license": "MIT", + "dependencies": { + "@angular/animations": "^5.0.0", + "@angular/common": "^5.0.0", + "@angular/compiler": "^5.0.0", + "@angular/compiler-cli": "^5.0.0", + "@angular/core": "^5.0.0", + "@angular/http": "^5.0.0", + "@angular/platform-browser": "^5.0.0", + "@angular/platform-browser-dynamic": "^5.0.0", + "@angular/platform-server": "^5.0.0", + "@angular/router": "^5.0.0", + "@ngtools/webpack": "0.0.0", + "core-js": "^2.4.1", + "rxjs": "^5.4.2", + "zone.js": "^0.8.14" + }, + "devDependencies": { + "node-sass": "^4.5.0", + "performance-now": "^0.2.0", + "raw-loader": "^0.5.1", + "sass-loader": "^6.0.3", + "typescript": "~2.5.0", + "webpack": "2.2.1" + } +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/tsconfig.json b/tests/e2e/assets/webpack/test-server-app-ng5/tsconfig.json new file mode 100644 index 000000000000..5822e780bfc7 --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": "", + "module": "es2015", + "moduleResolution": "node", + "target": "es5", + "noImplicitAny": false, + "sourceMap": true, + "mapRoot": "", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es2017", + "dom" + ], + "outDir": "lib", + "skipLibCheck": true, + "rootDir": "." + }, + "angularCompilerOptions": { + "genDir": "./app/ngfactory", + "entryModule": "app/app.module#AppModule" + } +} diff --git a/tests/e2e/assets/webpack/test-server-app-ng5/webpack.config.js b/tests/e2e/assets/webpack/test-server-app-ng5/webpack.config.js new file mode 100644 index 000000000000..4764b17b23dc --- /dev/null +++ b/tests/e2e/assets/webpack/test-server-app-ng5/webpack.config.js @@ -0,0 +1,32 @@ +const { AngularCompilerPlugin, PLATFORM } = require('@ngtools/webpack'); + +module.exports = { + resolve: { + extensions: ['.ts', '.js'] + }, + target: 'web', + entry: './app/main.ts', + output: { + path: './dist', + publicPath: 'dist/', + filename: 'app.main.js' + }, + plugins: [ + new AngularCompilerPlugin({ + tsConfigPath: './tsconfig.json', + mainPath: './app/main.ts', + platform: PLATFORM.Server + }) + ], + module: { + loaders: [ + { test: /\.scss$/, loaders: ['raw-loader', 'sass-loader'] }, + { test: /\.css$/, loader: 'raw-loader' }, + { test: /\.html$/, loader: 'raw-loader' }, + { test: /\.ts$/, loader: '@ngtools/webpack' } + ] + }, + devServer: { + historyApiFallback: true + } +}; diff --git a/tests/e2e/setup/100-global-cli.ts b/tests/e2e/setup/100-global-cli.ts index 5f3bc897c7a1..0db3b19c3780 100644 --- a/tests/e2e/setup/100-global-cli.ts +++ b/tests/e2e/setup/100-global-cli.ts @@ -13,7 +13,9 @@ export default function () { } // Install global Angular CLI. - return silentNpm('install', '-g', packages['@angular/cli'].tar); + // --unsafe-perm is needed for circleci + // because of https://github.com/sass/node-sass/issues/2006 + return silentNpm('install', '-g', packages['@angular/cli'].tar, '--unsafe-perm'); }) .then(() => exec(process.platform.startsWith('win') ? 'where' : 'which', 'ng')); } diff --git a/tests/e2e/setup/500-create-project.ts b/tests/e2e/setup/500-create-project.ts index b4aa8f108e99..0e784d13de07 100644 --- a/tests/e2e/setup/500-create-project.ts +++ b/tests/e2e/setup/500-create-project.ts @@ -5,6 +5,7 @@ import { updateTsConfig, updateJsonFile, useNg2, + useNg4, useSha, useCIChrome, useCIDefaults, @@ -45,6 +46,7 @@ export default function() { .then(() => useCIChrome()) .then(() => useCIDefaults()) .then(() => argv['ng2'] ? useNg2() : Promise.resolve()) + .then(() => argv['ng4'] ? useNg4() : Promise.resolve()) .then(() => argv.nightly || argv['ng-sha'] ? useSha() : Promise.resolve()) // npm link on Circle CI is very noisy. .then(() => silentNpm('install')) diff --git a/tests/e2e/tests/build/aot/aot.ts b/tests/e2e/tests/basic/aot.ts similarity index 66% rename from tests/e2e/tests/build/aot/aot.ts rename to tests/e2e/tests/basic/aot.ts index bbeb6c2004d4..5ec34d6d563d 100644 --- a/tests/e2e/tests/build/aot/aot.ts +++ b/tests/e2e/tests/basic/aot.ts @@ -1,5 +1,5 @@ -import {ng} from '../../../utils/process'; -import {expectFileToMatch} from '../../../utils/fs'; +import {ng} from '../../utils/process'; +import {expectFileToMatch} from '../../utils/fs'; export default function() { return ng('build', '--aot') diff --git a/tests/e2e/tests/build/assets.ts b/tests/e2e/tests/basic/assets.ts similarity index 90% rename from tests/e2e/tests/build/assets.ts rename to tests/e2e/tests/basic/assets.ts index 9b7e91941339..4b5a9a07a9d7 100644 --- a/tests/e2e/tests/build/assets.ts +++ b/tests/e2e/tests/basic/assets.ts @@ -1,3 +1,4 @@ +import * as path from 'path'; import { writeMultipleFiles, createDir, @@ -10,6 +11,10 @@ import { expectToFail } from '../../utils/utils'; import {getGlobalVariable} from '../../utils/env'; +const temp = require('temp'); +const tempDir = path.join(temp.mkdirSync('angular-cli-e2e-assets-'), 'out'); + + export default function () { // Disable parts of it in webpack tests. const ejected = getGlobalVariable('argv').eject; @@ -55,6 +60,21 @@ export default function () { })) .then(() => expectToFail(() => ng('build'))) + // This asset will not fail with the exception above. + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['outDir'] = tempDir; + app['assets'] = [ + { 'glob': '**/*', 'input': '../node_modules/some-package/', 'output': tempDir, + 'allowOutsideOutDir': true } + ]; + })) + .then(() => ng('build')) + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['outDir'] = 'dist'; + }) + // This asset should also fail from reading from outside the project. .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson['apps'][0]; diff --git a/tests/e2e/tests/build/dev-build.ts b/tests/e2e/tests/basic/dev-build.ts similarity index 100% rename from tests/e2e/tests/build/dev-build.ts rename to tests/e2e/tests/basic/dev-build.ts diff --git a/tests/e2e/tests/test/e2e.ts b/tests/e2e/tests/basic/e2e.ts similarity index 67% rename from tests/e2e/tests/test/e2e.ts rename to tests/e2e/tests/basic/e2e.ts index aca46ca1f624..6558deb50909 100644 --- a/tests/e2e/tests/test/e2e.ts +++ b/tests/e2e/tests/basic/e2e.ts @@ -4,9 +4,9 @@ import { execAndWaitForOutputToMatch, killAllProcesses } from '../../utils/process'; -import { updateJsonFile } from '../../utils/project'; -import { expectToFail } from '../../utils/utils'; -import { moveFile, copyFile } from '../../utils/fs'; +import {updateJsonFile} from '../../utils/project'; +import {expectToFail} from '../../utils/utils'; +import {moveFile, copyFile, replaceInFile} from '../../utils/fs'; export default function () { @@ -34,6 +34,21 @@ export default function () { .then(() => copyFile('./e2e/renamed-app.e2e-spec.ts', './e2e/another-app.e2e-spec.ts')) .then(() => ng('e2e', '--specs', './e2e/renamed-app.e2e-spec.ts', '--specs', './e2e/another-app.e2e-spec.ts')) + // Suites block need to be added in the protractor.conf.js file to test suites + .then(() => replaceInFile('protractor.conf.js', `allScriptsTimeout: 11000,`, + `allScriptsTimeout: 11000, + suites: { + app: './e2e/app.e2e-spec.ts' + }, + `)) + .then(() => ng('e2e', '--suite=app')) + // remove suites block from protractor.conf.js file after testing suites + .then(() => replaceInFile('protractor.conf.js', `allScriptsTimeout: 11000, + suites: { + app: './e2e/app.e2e-spec.ts' + }, + `, `allScriptsTimeout: 11000,` + )) // Should start up Element Explorer .then(() => execAndWaitForOutputToMatch('ng', ['e2e', '--element-explorer'], /Element Explorer/)) @@ -44,5 +59,9 @@ export default function () { // Should run side-by-side with `ng serve` .then(() => execAndWaitForOutputToMatch('ng', ['serve'], /webpack: Compiled successfully./)) - .then(() => ng('e2e')); + .then(() => ng('e2e')) + .then(() => killAllProcesses(), (err: any) => { + killAllProcesses(); + throw err; + }); } diff --git a/tests/e2e/tests/build/rebuild.ts b/tests/e2e/tests/basic/rebuild.ts similarity index 93% rename from tests/e2e/tests/build/rebuild.ts rename to tests/e2e/tests/basic/rebuild.ts index d253e3632df0..4e4c29609578 100644 --- a/tests/e2e/tests/build/rebuild.ts +++ b/tests/e2e/tests/basic/rebuild.ts @@ -20,14 +20,9 @@ export default function() { return Promise.resolve(); } - let oldNumberOfChunks = 0; - const chunkRegExp = /chunk\s+\{/g; + const lazyChunkRegExp = /lazy\.module\.chunk\.js/g; return execAndWaitForOutputToMatch('ng', ['serve'], validBundleRegEx) - // Count the bundles. - .then(({ stdout }) => { - oldNumberOfChunks = stdout.split(chunkRegExp).length; - }) // Add a lazy module. .then(() => ng('generate', 'module', 'lazy', '--routing')) // Should trigger a rebuild with a new bundle. @@ -65,8 +60,7 @@ export default function() { // Count the bundles. .then((results) => { const stdout = results[0].stdout; - let newNumberOfChunks = stdout.split(chunkRegExp).length; - if (oldNumberOfChunks >= newNumberOfChunks) { + if (!lazyChunkRegExp.test(stdout)) { throw new Error('Expected webpack to create a new chunk, but did not.'); } }) diff --git a/tests/e2e/tests/build/scripts-array.ts b/tests/e2e/tests/basic/scripts-array.ts similarity index 100% rename from tests/e2e/tests/build/scripts-array.ts rename to tests/e2e/tests/basic/scripts-array.ts diff --git a/tests/e2e/tests/build/styles/styles-array.ts b/tests/e2e/tests/basic/styles-array.ts similarity index 93% rename from tests/e2e/tests/build/styles/styles-array.ts rename to tests/e2e/tests/basic/styles-array.ts index 8149363993f3..c7ac5d756e41 100644 --- a/tests/e2e/tests/build/styles/styles-array.ts +++ b/tests/e2e/tests/basic/styles-array.ts @@ -1,9 +1,9 @@ import { writeMultipleFiles, expectFileToMatch -} from '../../../utils/fs'; -import { ng } from '../../../utils/process'; -import { updateJsonFile } from '../../../utils/project'; +} from '../../utils/fs'; +import { ng } from '../../utils/process'; +import { updateJsonFile } from '../../utils/project'; import { oneLineTrim } from 'common-tags'; export default function () { diff --git a/tests/e2e/tests/test/test.ts b/tests/e2e/tests/basic/test.ts similarity index 100% rename from tests/e2e/tests/test/test.ts rename to tests/e2e/tests/basic/test.ts diff --git a/tests/e2e/tests/build/aot/exclude.ts b/tests/e2e/tests/build/aot/exclude.ts index 94b474832f23..3266012f1f35 100644 --- a/tests/e2e/tests/build/aot/exclude.ts +++ b/tests/e2e/tests/build/aot/exclude.ts @@ -7,9 +7,8 @@ export default function () { // Disable parts of it in webpack tests. const ejected = getGlobalVariable('argv').eject; - // Skip this in ng5 tests, it only happens in ng2/4. - // This check should be changed once ng5 because the default. - if (getGlobalVariable('argv').nightly) { + // This test is only for Angular 2/4 projects. + if (!getGlobalVariable('argv').ng2 && !getGlobalVariable('argv').ng4) { return Promise.resolve(); } diff --git a/tests/e2e/tests/build/base-href.ts b/tests/e2e/tests/build/base-href.ts index 1f14735bf2e3..23a1633283dd 100644 --- a/tests/e2e/tests/build/base-href.ts +++ b/tests/e2e/tests/build/base-href.ts @@ -1,15 +1,9 @@ import {ng} from '../../utils/process'; import {expectFileToMatch} from '../../utils/fs'; -import {getGlobalVariable} from '../../utils/env'; import {updateJsonFile} from '../../utils/project'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return ng('build', '--base-href', '/myUrl') .then(() => expectFileToMatch('dist/index.html', //)) .then(() => updateJsonFile('.angular-cli.json', configJson => { diff --git a/tests/e2e/tests/build/build-app-shell-with-schematic.ts b/tests/e2e/tests/build/build-app-shell-with-schematic.ts new file mode 100644 index 000000000000..41f799cd90e7 --- /dev/null +++ b/tests/e2e/tests/build/build-app-shell-with-schematic.ts @@ -0,0 +1,42 @@ +import { ng, npm } from '../../utils/process'; +import { expectFileToMatch, appendToFile } from '../../utils/fs'; +import { getGlobalVariable } from '../../utils/env'; +import { expectToFail } from '../../utils/utils'; +import { updateJsonFile } from '../../utils/project'; +import { readNgVersion } from '../../utils/version'; + + +export default function () { + // Skip this in ejected tests. + if (getGlobalVariable('argv').eject) { + return Promise.resolve(); + } + + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { + return Promise.resolve(); + } + + let platformServerVersion = readNgVersion(); + + if (getGlobalVariable('argv').nightly) { + platformServerVersion = 'github:angular/platform-server-builds'; + } + + + return Promise.resolve() + .then(() => expectToFail(() => { + return ng('generate', 'appShell', '--universal-app', 'universal'); + }) + .then(() => appendToFile('src/app/app.component.html', '')) + .then(() => ng('generate', 'appShell', '--universal-app', 'universal')) + .then(() => updateJsonFile('package.json', packageJson => { + const dependencies = packageJson['dependencies']; + dependencies['@angular/platform-server'] = platformServerVersion; + }) + .then(() => npm('install')) + .then(() => ng('build', '--prod')) + .then(() => expectFileToMatch('dist/index.html', /app-shell works!/)) + .then(() => ng('build', '--prod', '--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 new file mode 100644 index 000000000000..cc531557f1ac --- /dev/null +++ b/tests/e2e/tests/build/build-app-shell.ts @@ -0,0 +1,147 @@ +import { ng, npm } from '../../utils/process'; +import { expectFileToMatch, writeFile } from '../../utils/fs'; +import { getGlobalVariable } from '../../utils/env'; +import { expectToFail } from '../../utils/utils'; +import { updateJsonFile } from '../../utils/project'; +import { readNgVersion } from '../../utils/version'; +import { stripIndent } from 'common-tags'; + + +export default function () { + // Skip this in ejected tests. + if (getGlobalVariable('argv').eject) { + return Promise.resolve(); + } + + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { + return Promise.resolve(); + } + + let platformServerVersion = readNgVersion(); + + if (getGlobalVariable('argv').nightly) { + platformServerVersion = 'github:angular/platform-server-builds'; + } + + return Promise.resolve() + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['appShell'] = { + app: '1', + route: 'shell' + }; + configJson['apps'].push({ + platform: 'server', + root: 'src', + outDir: 'dist-server', + assets: [ + 'assets', + 'favicon.ico' + ], + index: 'index.html', + main: 'main.server.ts', + test: 'test.ts', + tsconfig: 'tsconfig.server.json', + testTsconfig: 'tsconfig.spec.json', + prefix: 'app', + styles: [ + 'styles.css' + ], + scripts: [], + environmentSource: 'environments/environment.ts', + environments: { + dev: 'environments/environment.ts', + prod: 'environments/environment.prod.ts' + } + }); + })) + .then(() => writeFile('src/app/app.module.ts', stripIndent` + import { BrowserModule } from '@angular/platform-browser'; + import { NgModule } from '@angular/core'; + import { RouterModule } from '@angular/router'; + + import { AppComponent } from './app.component'; + + @NgModule({ + imports: [ + BrowserModule.withServerTransition({ appId: 'appshell-play' }), + RouterModule + ], + declarations: [AppComponent], + bootstrap: [AppComponent] + }) + export class AppModule { } + `)) + .then(() => writeFile('src/app/app.component.html', stripIndent` + Hello World + + `)) + .then(() => writeFile('src/tsconfig.server.json', stripIndent` + { + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "baseUrl": "./", + "module": "commonjs", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ], + "angularCompilerOptions": { + "entryModule": "app/app.server.module#AppServerModule" + } + } + `)) + .then(() => writeFile('src/main.server.ts', stripIndent` + export {AppServerModule} from './app/app.server.module'; + `)) + .then(() => writeFile('src/app/app.server.module.ts', stripIndent` + import {NgModule} from '@angular/core'; + import {ServerModule} from '@angular/platform-server'; + import { Routes, RouterModule } from '@angular/router'; + + import { AppModule } from './app.module'; + import { AppComponent } from './app.component'; + import { ShellComponent } from './shell.component'; + + const routes: Routes = [ + { path: 'shell', component: ShellComponent } + ]; + + @NgModule({ + imports: [ + // The AppServerModule should import your AppModule followed + // by the ServerModule from @angular/platform-server. + AppModule, + ServerModule, + RouterModule.forRoot(routes), + ], + // Since the bootstrapped component is not inherited from your + // imported AppModule, it needs to be repeated here. + bootstrap: [AppComponent], + declarations: [ShellComponent], + }) + export class AppServerModule {} + `)) + .then(() => writeFile('src/app/shell.component.ts', stripIndent` + import { Component } from '@angular/core'; + @Component({ + selector: 'app-shell', + template: '

shell Works!

', + styles: [] + }) + export class ShellComponent {} + `)) + .then(() => updateJsonFile('package.json', packageJson => { + const dependencies = packageJson['dependencies']; + dependencies['@angular/platform-server'] = platformServerVersion; + }) + .then(() => npm('install'))) + .then(() => ng('build', '--prod')) + .then(() => expectFileToMatch('dist/index.html', /shell Works!/)) + .then(() => ng('build', '--prod', '--skip-app-shell')) + .then(() => expectToFail(() => expectFileToMatch('dist/index.html', /shell Works!/))); +} diff --git a/tests/e2e/tests/build/build-errors.ts b/tests/e2e/tests/build/build-errors.ts index 341debe750b0..a5a97af7fb49 100644 --- a/tests/e2e/tests/build/build-errors.ts +++ b/tests/e2e/tests/build/build-errors.ts @@ -1,6 +1,6 @@ import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; -import { writeFile, appendToFile, readFile } from '../../utils/fs'; +import { writeFile, appendToFile, readFile, replaceInFile } from '../../utils/fs'; import { getGlobalVariable } from '../../utils/env'; import { expectToFail } from '../../utils/utils'; @@ -15,13 +15,14 @@ export default function () { if (process.platform.startsWith('win')) { return Promise.resolve(); } + // Skip this in ejected tests. if (getGlobalVariable('argv').eject) { return Promise.resolve(); } - // Skip in non-nightly tests. Switch this check around when ng5 is out. - if (!getGlobalVariable('argv').nightly) { + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); } @@ -38,7 +39,7 @@ export default function () { })) .then(() => expectToFail(() => ng('build'))) .then(({ message }) => { - if (!message.includes('polyfills.ts is not part of the compilation')) { + if (!message.includes('polyfills.ts is missing from the TypeScript compilation')) { throw new Error(`Expected missing TS file error, got this instead:\n${message}`); } if (extraErrors.some((e) => message.includes(e))) { @@ -61,11 +62,12 @@ export default function () { } }) .then(() => writeFile('./src/app/app.component.ts', origContent)) - // Check errors when files were not emitted. - .then(() => writeFile('./src/app/app.component.ts', '')) + // Check errors when files were not emitted due to static analysis errors. + .then(() => replaceInFile('./src/app/app.component.ts', `'app-root'`, `(() => 'app-root')()`)) .then(() => expectToFail(() => ng('build', '--aot'))) .then(({ message }) => { - if (!message.includes(`Unexpected value 'AppComponent`)) { + if (!message.includes('Function calls are not supported') + && !message.includes('Function expressions are not supported in decorators')) { throw new Error(`Expected static analysis error, got this instead:\n${message}`); } if (extraErrors.some((e) => message.includes(e))) { diff --git a/tests/e2e/tests/build/build-optimizer.ts b/tests/e2e/tests/build/build-optimizer.ts index 6e0dd82b2bdf..399909bfa986 100644 --- a/tests/e2e/tests/build/build-optimizer.ts +++ b/tests/e2e/tests/build/build-optimizer.ts @@ -9,12 +9,12 @@ export default function () { .then(() => expectToFail(() => expectFileToExist('dist/vendor.js'))) .then(() => expectToFail(() => expectFileToMatch('dist/main.js', /\.decorators =/))) .then(() => { - // Check if build optimizer is on by default in ng5 prod builds - // This check should be changed once ng5 because the default. - if (!getGlobalVariable('argv').nightly) { + // Skip this part of the test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); } + // Check if build optimizer is on by default in ng5 prod builds return Promise.resolve() .then(() => ng('build', '--prod')) .then(() => expectToFail(() => expectFileToExist('dist/vendor.js'))) diff --git a/tests/e2e/tests/build/bundle-budgets.ts b/tests/e2e/tests/build/bundle-budgets.ts new file mode 100644 index 000000000000..92d35a51b557 --- /dev/null +++ b/tests/e2e/tests/build/bundle-budgets.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { getGlobalVariable } from '../../utils/env'; +import { ng } from '../../utils/process'; +import { updateJsonFile } from '../../utils/project'; +import { expectToFail } from '../../utils/utils'; + +// tslint:disable:max-line-length +export default function () { + const budgetConfigs = [ + { + expectation: 'pass', + message: 'BIG max for all, should not error', + budget: { type: 'allScript', maximumError: '100mb' }, + }, + { + expectation: 'error', + message: 'Budget error: all, max error', + budget: { type: 'all', maximumError: '100b' }, + }, + { + expectation: 'warning', + message: 'Budget warning: all, min warning', + budget: { type: 'all', minimumWarning: '100mb' }, + } + ]; + + const promiseFactories = budgetConfigs.map(cfg => { + if (cfg.expectation === 'error') { + return () => { + return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) + .then(() => expectToFail(() => ng('build', '--prod'))) + .then(errorMessage => { + if (!/ERROR in budgets/.test(errorMessage)) { + throw new Error(cfg.message); + } + }); + }; + } else if (cfg.expectation === 'warning') { + return () => { + return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) + .then(() => ng('build', '--prod')) + .then(({ stdout }) => { + if (!/WARNING in budgets/.test(stdout)) { + throw new Error(cfg.message); + } + }); + }; + } else { // pass + return () => { + return updateJsonFile('.angular-cli.json', (json) => { json.apps[0].budgets = [cfg.budget]; }) + .then(() => ng('build', '--prod')) + .then(({ stdout }) => { + if (/(WARNING|ERROR)/.test(stdout)) { + throw new Error(cfg.message); + } + }); + }; + } + }); + + let promiseChain = Promise.resolve(); + for (let i = 0; i < promiseFactories.length; i++) { + promiseChain = promiseChain.then(promiseFactories[i]); + } + + return promiseChain; +} diff --git a/tests/e2e/tests/build/chunk-hash.ts b/tests/e2e/tests/build/chunk-hash.ts index 13e5a2cb8298..57cae6db7d16 100644 --- a/tests/e2e/tests/build/chunk-hash.ts +++ b/tests/e2e/tests/build/chunk-hash.ts @@ -2,7 +2,6 @@ import * as fs from 'fs'; import {ng} from '../../utils/process'; import {writeFile, prependToFile, replaceInFile} from '../../utils/fs'; -import {getGlobalVariable} from '../../utils/env'; const OUTPUT_RE = /(main|polyfills|vendor|inline|styles|\d+)\.[a-z0-9]+\.(chunk|bundle)\.(js|css)$/; @@ -43,12 +42,6 @@ function validateHashes( } export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - - let oldHashes: Map; let newHashes: Map; // First, collect the hashes. diff --git a/tests/e2e/tests/build/css-urls.ts b/tests/e2e/tests/build/css-urls.ts index 9bc565a428d3..e8e3e13223a1 100644 --- a/tests/e2e/tests/build/css-urls.ts +++ b/tests/e2e/tests/build/css-urls.ts @@ -1,4 +1,3 @@ -import * as fs from 'fs'; import { ng } from '../../utils/process'; import { expectFileToMatch, @@ -6,8 +5,8 @@ import { expectFileMatchToExist, writeMultipleFiles } from '../../utils/fs'; +import { copyProjectAsset } from '../../utils/assets'; import { expectToFail } from '../../utils/utils'; -import { getGlobalVariable } from '../../utils/env'; const imgSvg = ` @@ -16,42 +15,39 @@ const imgSvg = ` `; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() // Verify absolute/relative paths in global/component css. .then(() => writeMultipleFiles({ 'src/styles.css': ` h1 { background: url('/assets/global-img-absolute.svg'); } - h2 { background: url('./assets/global-img-relative.svg'); } + h2 { background: url('./assets/global-img-relative.png'); } `, 'src/app/app.component.css': ` h3 { background: url('/assets/component-img-absolute.svg'); } - h4 { background: url('../assets/component-img-relative.svg'); } + h4 { background: url('../assets/component-img-relative.png'); } `, - // Using SVGs because they are loaded via file-loader and thus never inlined. 'src/assets/global-img-absolute.svg': imgSvg, - 'src/assets/global-img-relative.svg': imgSvg, - 'src/assets/component-img-absolute.svg': imgSvg, - 'src/assets/component-img-relative.svg': imgSvg + 'src/assets/component-img-absolute.svg': imgSvg })) + // use image with file size >10KB to prevent inlining + .then(() => copyProjectAsset('images/spectrum.png', './assets/global-img-relative.png')) + .then(() => copyProjectAsset('images/spectrum.png', './assets/component-img-relative.png')) .then(() => ng('build', '--extract-css', '--aot')) // Check paths are correctly generated. .then(() => expectFileToMatch('dist/styles.bundle.css', '/assets/global-img-absolute.svg')) .then(() => expectFileToMatch('dist/styles.bundle.css', - /global-img-relative\.[0-9a-f]{20}\.svg/)) + /url\('\/assets\/global-img-absolute\.svg'\)/)) + .then(() => expectFileToMatch('dist/styles.bundle.css', + /global-img-relative\.[0-9a-f]{20}\.png/)) .then(() => expectFileToMatch('dist/main.bundle.js', '/assets/component-img-absolute.svg')) .then(() => expectFileToMatch('dist/main.bundle.js', - /component-img-relative\.[0-9a-f]{20}\.svg/)) + /component-img-relative\.[0-9a-f]{20}\.png/)) // Check files are correctly created. .then(() => expectToFail(() => expectFileToExist('dist/global-img-absolute.svg'))) .then(() => expectToFail(() => expectFileToExist('dist/component-img-absolute.svg'))) - .then(() => expectFileMatchToExist('./dist', /global-img-relative\.[0-9a-f]{20}\.svg/)) - .then(() => expectFileMatchToExist('./dist', /component-img-relative\.[0-9a-f]{20}\.svg/)) + .then(() => expectFileMatchToExist('./dist', /global-img-relative\.[0-9a-f]{20}\.png/)) + .then(() => expectFileMatchToExist('./dist', /component-img-relative\.[0-9a-f]{20}\.png/)) // Check urls with deploy-url scheme are used as is. .then(() => ng('build', '--base-href=/base/', '--deploy-url=http://deploy.url/', '--extract-css')) @@ -79,9 +75,31 @@ export default function () { .then(() => expectFileToMatch('dist/styles.bundle.css', '/base/deploy/assets/global-img-absolute.svg')) .then(() => expectFileToMatch('dist/styles.bundle.css', - /global-img-relative\.[0-9a-f]{20}\.svg/)) + /global-img-relative\.[0-9a-f]{20}\.png/)) .then(() => expectFileToMatch('dist/main.bundle.js', '/base/deploy/assets/component-img-absolute.svg')) .then(() => expectFileToMatch('dist/main.bundle.js', - /deploy\/component-img-relative\.[0-9a-f]{20}\.svg/)); + /deploy\/component-img-relative\.[0-9a-f]{20}\.png/)) + // Check with identical base-href and deploy-url flags. + .then(() => ng('build', '--base-href=/base/', '--deploy-url=/base/', + '--extract-css', '--aot')) + .then(() => expectFileToMatch('dist/styles.bundle.css', + '/base/assets/global-img-absolute.svg')) + .then(() => expectFileToMatch('dist/styles.bundle.css', + /global-img-relative\.[0-9a-f]{20}\.png/)) + .then(() => expectFileToMatch('dist/main.bundle.js', + '/base/assets/component-img-absolute.svg')) + .then(() => expectFileToMatch('dist/main.bundle.js', + /\/base\/component-img-relative\.[0-9a-f]{20}\.png/)) + // Check with only base-href flag. + .then(() => ng('build', '--base-href=/base/', + '--extract-css', '--aot')) + .then(() => expectFileToMatch('dist/styles.bundle.css', + '/base/assets/global-img-absolute.svg')) + .then(() => expectFileToMatch('dist/styles.bundle.css', + /global-img-relative\.[0-9a-f]{20}\.png/)) + .then(() => expectFileToMatch('dist/main.bundle.js', + '/base/assets/component-img-absolute.svg')) + .then(() => expectFileToMatch('dist/main.bundle.js', + /component-img-relative\.[0-9a-f]{20}\.png/)); } diff --git a/tests/e2e/tests/build/delete-output-path.ts b/tests/e2e/tests/build/delete-output-path.ts index e503a67f6db7..9a5b658f8e7e 100644 --- a/tests/e2e/tests/build/delete-output-path.ts +++ b/tests/e2e/tests/build/delete-output-path.ts @@ -4,11 +4,6 @@ import {deleteFile, expectFileToExist} from '../../utils/fs'; import {getGlobalVariable} from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - // Skip this in ejected tests. if (getGlobalVariable('argv').eject) { return Promise.resolve(); diff --git a/tests/e2e/tests/build/deploy-url.ts b/tests/e2e/tests/build/deploy-url.ts index 5882d2c8e5ba..36acdcfdda98 100644 --- a/tests/e2e/tests/build/deploy-url.ts +++ b/tests/e2e/tests/build/deploy-url.ts @@ -1,29 +1,21 @@ import { ng } from '../../utils/process'; +import { copyProjectAsset } from '../../utils/assets'; import { expectFileToMatch, writeMultipleFiles } from '../../utils/fs'; import { updateJsonFile } from '../../utils/project'; import { getGlobalVariable } from '../../utils/env'; -import { stripIndents } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - - return Promise.resolve() .then(() => writeMultipleFiles({ - 'src/styles.css': 'div { background: url("./assets/more.svg"); }', - 'src/assets/more.svg': stripIndents` - - - - `})) + 'src/styles.css': 'div { background: url("./assets/more.png"); }', + })) + // use image with file size >10KB to prevent inlining + .then(() => copyProjectAsset('images/spectrum.png', './assets/more.png')) .then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css')) .then(() => expectFileToMatch('dist/index.html', 'deployUrl/main.bundle.js')) // verify --deploy-url isn't applied to extracted css urls - .then(() => expectFileToMatch('dist/styles.bundle.css', /url\(more\.[0-9a-f]{20}\.svg\)/)) + .then(() => expectFileToMatch('dist/styles.bundle.css', /url\(more\.[0-9a-f]{20}\.png\)/)) .then(() => ng('build', '--deploy-url=http://example.com/some/path/', '--extract-css')) .then(() => expectFileToMatch('dist/index.html', 'http://example.com/some/path/main.bundle.js')) // verify option also works in config @@ -36,5 +28,13 @@ export default function () { // verify --deploy-url is applied to non-extracted css urls .then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css=false')) .then(() => expectFileToMatch('dist/styles.bundle.js', - /__webpack_require__.p \+ \"more\.[0-9a-f]{20}\.svg\"/)); + /__webpack_require__.p \+ \"more\.[0-9a-f]{20}\.png\"/)) + .then(() => expectFileToMatch('dist/inline.bundle.js', + /__webpack_require__\.p = "deployUrl\/";/)) + // verify slash is appended to the end of --deploy-url if missing + .then(() => ng('build', '--deploy-url=deployUrl', '--extract-css=false')) + // skip this in ejected tests + .then(() => getGlobalVariable('argv').eject + ? Promise.resolve() + : expectFileToMatch('dist/inline.bundle.js', /__webpack_require__\.p = "deployUrl\/";/)); } diff --git a/tests/e2e/tests/build/filename.ts b/tests/e2e/tests/build/filename.ts index 2399e833a803..e8979648f9a6 100644 --- a/tests/e2e/tests/build/filename.ts +++ b/tests/e2e/tests/build/filename.ts @@ -2,15 +2,9 @@ import {ng} from '../../utils/process'; import {expectFileToExist} from '../../utils/fs'; import {updateJsonFile} from '../../utils/project'; import {copyFile} from '../../utils/fs'; -import {getGlobalVariable} from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => copyFile('src/index.html', 'src/config-index.html')) .then(() => updateJsonFile('.angular-cli.json', configJson => { diff --git a/tests/e2e/tests/build/json.ts b/tests/e2e/tests/build/json.ts index 71cfa32f0544..dc116f60719a 100644 --- a/tests/e2e/tests/build/json.ts +++ b/tests/e2e/tests/build/json.ts @@ -5,11 +5,6 @@ import {getGlobalVariable} from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - // Skip this in ejected tests. if (getGlobalVariable('argv').eject) { return Promise.resolve(); diff --git a/tests/e2e/tests/build/module-id.ts b/tests/e2e/tests/build/module-id.ts index 581d8bbddf1d..ad75d230a419 100644 --- a/tests/e2e/tests/build/module-id.ts +++ b/tests/e2e/tests/build/module-id.ts @@ -1,14 +1,8 @@ import { ng } from '../../utils/process'; import { replaceInFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => replaceInFile('src/app/app.component.ts', '@Component({', diff --git a/tests/e2e/tests/build/no-angular-router.ts b/tests/e2e/tests/build/no-angular-router.ts index 84eb22cd14d0..b6680e99d9f9 100644 --- a/tests/e2e/tests/build/no-angular-router.ts +++ b/tests/e2e/tests/build/no-angular-router.ts @@ -1,16 +1,10 @@ import {ng} from '../../utils/process'; import {expectFileToExist, moveFile} from '../../utils/fs'; -import {updateJsonFile} from '../../utils/project'; import {getGlobalVariable} from '../../utils/env'; import * as path from 'path'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - const tmp = getGlobalVariable('tmp-root'); return Promise.resolve() diff --git a/tests/e2e/tests/build/no-implicit-any.ts b/tests/e2e/tests/build/no-implicit-any.ts index 340f7169b698..08927a41467e 100644 --- a/tests/e2e/tests/build/no-implicit-any.ts +++ b/tests/e2e/tests/build/no-implicit-any.ts @@ -1,14 +1,8 @@ import {updateTsConfig} from '../../utils/project'; import {ng} from '../../utils/process'; -import {getGlobalVariable} from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return updateTsConfig(json => { json['compilerOptions']['noImplicitAny'] = true; }) diff --git a/tests/e2e/tests/build/output-hashing.ts b/tests/e2e/tests/build/output-hashing.ts index a7c091c25041..aba9758f6dd5 100644 --- a/tests/e2e/tests/build/output-hashing.ts +++ b/tests/e2e/tests/build/output-hashing.ts @@ -1,7 +1,7 @@ -import {stripIndents} from 'common-tags'; import {ng} from '../../utils/process'; +import { copyProjectAsset } from '../../utils/assets'; import { writeMultipleFiles, expectFileToMatch, expectFileMatchToExist } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; + function verifyMedia(css: RegExp, content: RegExp) { return expectFileMatchToExist('./dist', css) @@ -9,39 +9,33 @@ function verifyMedia(css: RegExp, content: RegExp) { } export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => writeMultipleFiles({ - 'src/styles.css': stripIndents` - body { background-image: url("image.svg"); } - `, - 'src/image.svg': 'I would like to be an image someday.' + 'src/styles.css': 'body { background-image: url("./assets/image.png"); }' })) + // 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(() => expectFileToMatch('dist/index.html', /inline\.[0-9a-f]{20}\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{20}\.bundle\.(css|js)/)) - .then(() => verifyMedia(/styles\.[0-9a-f]{20}\.bundle\.(css|js)/, /image\.[0-9a-f]{20}\.svg/)) + .then(() => verifyMedia(/styles\.[0-9a-f]{20}\.bundle\.(css|js)/, /image\.[0-9a-f]{20}\.png/)) .then(() => ng('build', '--prod', '--output-hashing=none')) .then(() => expectFileToMatch('dist/index.html', /inline\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.bundle\.(css|js)/)) - .then(() => verifyMedia(/styles\.bundle\.(css|js)/, /image\.svg/)) + .then(() => verifyMedia(/styles\.bundle\.(css|js)/, /image\.png/)) .then(() => ng('build', '--dev', '--output-hashing=media')) .then(() => expectFileToMatch('dist/index.html', /inline\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.bundle\.(css|js)/)) - .then(() => verifyMedia(/styles\.bundle\.(css|js)/, /image\.[0-9a-f]{20}\.svg/)) + .then(() => verifyMedia(/styles\.bundle\.(css|js)/, /image\.[0-9a-f]{20}\.png/)) .then(() => ng('build', '--dev', '--output-hashing=bundles')) .then(() => expectFileToMatch('dist/index.html', /inline\.[0-9a-f]{20}\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{20}\.bundle\.(css|js)/)) - .then(() => verifyMedia(/styles\.[0-9a-f]{20}\.bundle\.(css|js)/, /image\.svg/)); + .then(() => verifyMedia(/styles\.[0-9a-f]{20}\.bundle\.(css|js)/, /image\.png/)); } diff --git a/tests/e2e/tests/build/platform-server.ts b/tests/e2e/tests/build/platform-server.ts index 8d7622c90c55..d2943d162070 100644 --- a/tests/e2e/tests/build/platform-server.ts +++ b/tests/e2e/tests/build/platform-server.ts @@ -14,11 +14,6 @@ import { readNgVersion } from '../../utils/version'; import { expectToFail } from '../../utils/utils'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - // Skip this for ejected tests. if (getGlobalVariable('argv').eject) { return Promise.resolve(); @@ -30,6 +25,11 @@ export default function () { platformServerVersion = 'github:angular/platform-server-builds'; } + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { + return Promise.resolve(); + } + return Promise.resolve() .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson['apps'][0]; diff --git a/tests/e2e/tests/build/polyfills.ts b/tests/e2e/tests/build/polyfills.ts index a00445c34843..2fae001ff3e0 100644 --- a/tests/e2e/tests/build/polyfills.ts +++ b/tests/e2e/tests/build/polyfills.ts @@ -1,14 +1,8 @@ import { expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; -import { getGlobalVariable } from '../../utils/env'; import { oneLineTrim } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => ng('build')) // files were created successfully diff --git a/tests/e2e/tests/build/rebuild-error.ts b/tests/e2e/tests/build/rebuild-error.ts index 5da761dd1e76..ea20919d3a88 100644 --- a/tests/e2e/tests/build/rebuild-error.ts +++ b/tests/e2e/tests/build/rebuild-error.ts @@ -26,8 +26,8 @@ export default function () { return Promise.resolve(); } - // Skip in non-nightly tests. Switch this check around when ng5 is out. - if (!getGlobalVariable('argv').nightly) { + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); } @@ -37,13 +37,14 @@ export default function () { // Save the original contents of `./src/app/app.component.ts`. .then(() => readFile('./src/app/app.component.ts')) .then((contents) => origContent = contents) - // Add a major error on a non-main file to the initial build. - .then(() => writeFile('src/app/app.component.ts', '')) + // Add a major static analysis error on a non-main file to the initial build. + .then(() => replaceInFile('./src/app/app.component.ts', `'app-root'`, `(() => 'app-root')()`)) // Should have an error. .then(() => execAndWaitForOutputToMatch('ng', ['serve', '--aot'], failedRe)) .then((results) => { const stderr = results.stderr; - if (!stderr.includes(`Unexpected value 'AppComponent`)) { + if (!stderr.includes('Function calls are not supported') + && !stderr.includes('Function expressions are not supported in decorators')) { throw new Error(`Expected static analysis error, got this instead:\n${stderr}`); } if (extraErrors.some((e) => stderr.includes(e))) { @@ -76,18 +77,19 @@ export default function () { // have an error message in 5s. .then(() => Promise.all([ expectToFail(() => waitForAnyProcessOutputToMatch(errorRe, 5000)), - replaceInFile('src/app/app.component.ts', ']]]]]', '') + writeFile('src/app/app.component.ts', origContent) ])) .then(() => wait(2000)) - // Add a major error on a rebuild. + // Add a major static analysis error on a rebuild. // Should fail the rebuild. .then(() => Promise.all([ waitForAnyProcessOutputToMatch(failedRe, 20000), - writeFile('src/app/app.component.ts', '') + replaceInFile('./src/app/app.component.ts', `'app-root'`, `(() => 'app-root')()`) ])) .then((results) => { const stderr = results[0].stderr; - if (!stderr.includes(`Unexpected value 'AppComponent`)) { + if (!stderr.includes('Function calls are not supported') + && !stderr.includes('Function expressions are not supported in decorators')) { throw new Error(`Expected static analysis error, got this instead:\n${stderr}`); } if (extraErrors.some((e) => stderr.includes(e))) { diff --git a/tests/e2e/tests/build/rebuild-ngfactories.ts b/tests/e2e/tests/build/rebuild-ngfactories.ts index 59969db5ed4b..e03ede0f9f4f 100644 --- a/tests/e2e/tests/build/rebuild-ngfactories.ts +++ b/tests/e2e/tests/build/rebuild-ngfactories.ts @@ -18,6 +18,11 @@ export default function () { return Promise.resolve(); } + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { + return Promise.resolve(); + } + return execAndWaitForOutputToMatch('ng', ['serve', '--aot'], validBundleRegEx) .then(() => writeMultipleFiles({ 'src/app/app.component.css': ` @@ -70,8 +75,8 @@ export default function () { } }) .then(() => { - // Skip in non-nightly tests. Switch this check around when ng5 is out. - if (!getGlobalVariable('argv').nightly) { + // Skip this part of the test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); } diff --git a/tests/e2e/tests/build/script-target.ts b/tests/e2e/tests/build/script-target.ts index f3d847141d30..b3175a2f0ed5 100644 --- a/tests/e2e/tests/build/script-target.ts +++ b/tests/e2e/tests/build/script-target.ts @@ -1,9 +1,15 @@ import { expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; +import { getGlobalVariable } from '../../utils/env'; export default function () { + // Skip this test in Angular 2, it had different bundles. + if (getGlobalVariable('argv').ng2) { + return Promise.resolve(); + } + return Promise.resolve() .then(() => updateJsonFile('tsconfig.json', configJson => { const compilerOptions = configJson['compilerOptions']; diff --git a/tests/e2e/tests/build/service-worker.ts b/tests/e2e/tests/build/service-worker.ts index 8a2c7aebfbae..33c6bf90158a 100644 --- a/tests/e2e/tests/build/service-worker.ts +++ b/tests/e2e/tests/build/service-worker.ts @@ -1,7 +1,7 @@ import {join} from 'path'; import {getGlobalVariable} from '../../utils/env'; import {expectFileNotToExist, expectFileToExist, expectFileToMatch, writeFile} from '../../utils/fs'; -import {ng, silentNpm} from '../../utils/process'; +import {ng, npm, silentNpm} from '../../utils/process'; const MANIFEST = { index: '/index.html', @@ -42,5 +42,11 @@ export default function() { .then(() => expectFileToMatch('dist/ngsw.json', /"\/foo\/bar\/index.html"/)) .then(() => ng('build', '--prod', '--service-worker=false')) .then(() => expectFileNotToExist('dist/ngsw.json')) + .then(() => ng('eject', '--prod')) + .then(() => silentNpm('install')) + .then(() => npm('run', 'build')) + .then(() => expectFileToMatch('package.json', /"sw-config"/)) + .then(() => expectFileToExist(join(process.cwd(), 'dist/ngsw-worker.js'))) + .then(() => expectFileToExist(join(process.cwd(), 'dist/ngsw.json'))) .then(() => ng('set', 'apps.0.serviceWorker=false')); } diff --git a/tests/e2e/tests/build/sourcemap.ts b/tests/e2e/tests/build/sourcemap.ts index 4e831558d9c4..2eb1ee84793c 100644 --- a/tests/e2e/tests/build/sourcemap.ts +++ b/tests/e2e/tests/build/sourcemap.ts @@ -1,15 +1,9 @@ import {ng} from '../../utils/process'; import {expectFileToExist} from '../../utils/fs'; import {expectToFail} from '../../utils/utils'; -import {getGlobalVariable} from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return ng('build', '--sourcemaps') .then(() => expectFileToExist('dist/main.bundle.js.map')) diff --git a/tests/e2e/tests/build/styles/extract-css.ts b/tests/e2e/tests/build/styles/extract-css.ts index 834b16dbba25..828d4cce8fd3 100644 --- a/tests/e2e/tests/build/styles/extract-css.ts +++ b/tests/e2e/tests/build/styles/extract-css.ts @@ -6,15 +6,9 @@ import { import { ng } from '../../../utils/process'; import { updateJsonFile } from '../../../utils/project'; import { expectToFail } from '../../../utils/utils'; -import { getGlobalVariable } from '../../../utils/env'; import { oneLineTrim } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => writeMultipleFiles({ 'src/string-style.css': '.string-style { color: red }', diff --git a/tests/e2e/tests/build/styles/inline-urls.ts b/tests/e2e/tests/build/styles/inline-urls.ts new file mode 100644 index 000000000000..7ba0936832d1 --- /dev/null +++ b/tests/e2e/tests/build/styles/inline-urls.ts @@ -0,0 +1,62 @@ +import { ng, silentNpm } from '../../../utils/process'; +import { + expectFileToMatch, + expectFileToExist, + expectFileMatchToExist, + writeMultipleFiles +} from '../../../utils/fs'; +import { copyProjectAsset } from '../../../utils/assets'; +import { expectToFail } from '../../../utils/utils'; +import { updateJsonFile } from '../../../utils/project'; + +const imgSvg = ` + + + +`; + +export default function () { + return Promise.resolve() + .then(() => silentNpm('install', 'font-awesome@4.7.0')) + .then(() => writeMultipleFiles({ + 'src/styles.scss': ` + $fa-font-path: "~font-awesome/fonts"; + @import "~font-awesome/scss/font-awesome"; + h1 { background: url('./assets/large.png'); } + h2 { background: url('./assets/small.svg'); } + p { background: url('./assets/small-id.svg#testID'); } + `, + 'src/app/app.component.css': ` + h3 { background: url('../assets/small.svg'); } + h4 { background: url('../assets/large.png'); } + `, + 'src/assets/small.svg': imgSvg, + 'src/assets/small-id.svg': imgSvg + })) + .then(() => copyProjectAsset('images/spectrum.png', './assets/large.png')) + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['styles'] = ['styles.scss']; + })) + .then(() => ng('build', '--extract-css', '--aot')) + .then(({ stdout }) => { + if (stdout.match(/postcss-url: \.+: Can't read file '\.+', ignoring/)) { + throw new Error('Expected no postcss-url file read warnings.'); + } + }) + // Check paths are correctly generated. + .then(() => expectFileToMatch('dist/styles.bundle.css', + /url\([\'"]?large\.[0-9a-f]{20}\.png[\'"]?\)/)) + .then(() => expectFileToMatch('dist/styles.bundle.css', + /url\(\\?[\'"]data:image\/svg\+xml/)) + .then(() => expectFileToMatch('dist/styles.bundle.css', + /url\([\'"]?small-id\.[0-9a-f]{20}\.svg#testID[\'"]?\)/)) + .then(() => expectFileToMatch('dist/main.bundle.js', + /url\(\\?[\'"]data:image\/svg\+xml/)) + .then(() => expectFileToMatch('dist/main.bundle.js', + /url\([\'"]?large\.[0-9a-f]{20}\.png[\'"]?\)/)) + // Check files are correctly created. + .then(() => expectToFail(() => expectFileToExist('dist/small.svg'))) + .then(() => expectFileMatchToExist('./dist', /large\.[0-9a-f]{20}\.png/)) + .then(() => expectFileMatchToExist('./dist', /small-id\.[0-9a-f]{20}\.svg/)); +} diff --git a/tests/e2e/tests/build/styles/material-import.ts b/tests/e2e/tests/build/styles/material-import.ts new file mode 100644 index 000000000000..d739502a0bc0 --- /dev/null +++ b/tests/e2e/tests/build/styles/material-import.ts @@ -0,0 +1,37 @@ +import { + writeMultipleFiles, + replaceInFile +} from '../../../utils/fs'; +import { ng, silentNpm } from '../../../utils/process'; +import { stripIndents } from 'common-tags'; +import { updateJsonFile } from '../../../utils/project'; + +export default function () { + const extensions = ['css', 'scss', 'less', 'styl']; + let promise: Promise = Promise.resolve() + .then(() => silentNpm('install', '@angular/material@5.0.4')); + + extensions.forEach(ext => { + promise = promise.then(() => { + return writeMultipleFiles({ + [`src/styles.${ext}`]: stripIndents` + @import "~@angular/material/prebuilt-themes/indigo-pink.css"; + `, + [`src/app/app.component.${ext}`]: stripIndents` + @import "~@angular/material/prebuilt-themes/indigo-pink.css"; + `, + }) + // change files to use preprocessor + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const app = configJson['apps'][0]; + app['styles'] = [`styles.${ext}`]; + })) + .then(() => replaceInFile('src/app/app.component.ts', + './app.component.css', `./app.component.${ext}`)) + // run build app + .then(() => ng('build', '--extract-css', '--sourcemap')); + }); + }); + + return promise; +} diff --git a/tests/e2e/tests/build/styles/postcss.ts b/tests/e2e/tests/build/styles/postcss.ts index 08600f69d6eb..14636a95c7de 100644 --- a/tests/e2e/tests/build/styles/postcss.ts +++ b/tests/e2e/tests/build/styles/postcss.ts @@ -1,15 +1,9 @@ import * as glob from 'glob'; import { writeFile, expectFileToMatch } from '../../../utils/fs'; import { ng } from '../../../utils/process'; -import { getGlobalVariable } from '../../../utils/env'; import { stripIndents } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return writeFile('src/styles.css', stripIndents` /* normal-comment */ /*! important-comment */ diff --git a/tests/e2e/tests/commands/new/new-routing.ts b/tests/e2e/tests/commands/new/new-routing.ts index 953aa16190cc..1726abad6c21 100644 --- a/tests/e2e/tests/commands/new/new-routing.ts +++ b/tests/e2e/tests/commands/new/new-routing.ts @@ -1,14 +1,8 @@ import {ng} from '../../../utils/process'; import {createProject} from '../../../utils/project'; -import { getGlobalVariable } from '../../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => createProject('routing-project', '--routing')) diff --git a/tests/e2e/tests/commands/new/new-skip-commit.ts b/tests/e2e/tests/commands/new/new-skip-commit.ts index c57e34b67f48..95a4328f21f8 100644 --- a/tests/e2e/tests/commands/new/new-skip-commit.ts +++ b/tests/e2e/tests/commands/new/new-skip-commit.ts @@ -2,15 +2,9 @@ import {ng} from '../../../utils/process'; import {createProject} from '../../../utils/project'; import {expectToFail} from '../../../utils/utils'; import {expectGitToBeClean} from '../../../utils/git'; -import {getGlobalVariable} from '../../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => createProject('new-project', '--skip-commit')) .then(() => expectToFail(() => expectGitToBeClean())) diff --git a/tests/e2e/tests/commands/new/new-skip-tests.ts b/tests/e2e/tests/commands/new/new-skip-tests.ts index 62c5328b8575..95dde0a38e88 100644 --- a/tests/e2e/tests/commands/new/new-skip-tests.ts +++ b/tests/e2e/tests/commands/new/new-skip-tests.ts @@ -1,14 +1,8 @@ import {createProject} from '../../../utils/project'; import {expectFileNotToExist} from '../../../utils/fs'; -import {getGlobalVariable} from '../../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => createProject('new-project-skip-tests', '--skip-tests')) .then(() => expectFileNotToExist('src/app/app.component.spec.ts')); diff --git a/tests/e2e/tests/i18n/build-locale.ts b/tests/e2e/tests/i18n/build-locale.ts index 4f68d6ea37d4..033e425a499e 100644 --- a/tests/e2e/tests/i18n/build-locale.ts +++ b/tests/e2e/tests/i18n/build-locale.ts @@ -5,9 +5,8 @@ import { expectToFail } from '../../utils/utils'; export default function () { - // Check if register locale works in ng5 prod builds - // This check should be changed once ng5 because the default. - if (!getGlobalVariable('argv').nightly) { + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { return Promise.resolve(); } @@ -18,5 +17,9 @@ export default function () { .then(() => expectFileToMatch('dist/main.bundle.js', /registerLocaleData/)) .then(() => expectFileToMatch('dist/main.bundle.js', /angular_common_locales_fr/)) .then(() => rimraf('dist')) - .then(() => expectToFail(() => ng('build', '--aot', '--locale=no-locale'))) + .then(() => ng('build', '--aot', '--locale=fr_FR')) + .then(() => expectFileToMatch('dist/main.bundle.js', /registerLocaleData/)) + .then(() => expectFileToMatch('dist/main.bundle.js', /angular_common_locales_fr/)) + .then(() => rimraf('dist')) + .then(() => expectToFail(() => ng('build', '--aot', '--locale=no-locale'))); } diff --git a/tests/e2e/tests/i18n/extract-locale.ts b/tests/e2e/tests/i18n/extract-locale.ts index 16df41a68957..1916973ed244 100644 --- a/tests/e2e/tests/i18n/extract-locale.ts +++ b/tests/e2e/tests/i18n/extract-locale.ts @@ -1,9 +1,6 @@ import { join } from 'path'; import { ng } from '../../utils/process'; -import { - expectFileToExist, writeFile, - expectFileToMatch -} from '../../utils/fs'; +import { writeFile, expectFileToMatch } from '../../utils/fs'; export default function() { @@ -14,8 +11,7 @@ export default function() { .then(() => ng('xi18n', '--locale', 'fr')) .then((output) => { if (!output.stdout.match(/starting from Angular v4/)) { - expectFileToExist(join('src', 'messages.xlf')); - expectFileToMatch(join('src', 'messages.xlf'), /source-language="fr"/); + return expectFileToMatch(join('src', 'messages.xlf'), 'source-language="fr"'); } }); } diff --git a/tests/e2e/tests/i18n/extract-outfile.ts b/tests/e2e/tests/i18n/extract-outfile.ts index 444c0e1c70fc..2948b6a17d85 100644 --- a/tests/e2e/tests/i18n/extract-outfile.ts +++ b/tests/e2e/tests/i18n/extract-outfile.ts @@ -1,9 +1,6 @@ import {join} from 'path'; import {ng} from '../../utils/process'; -import { - expectFileToExist, writeFile, - expectFileToMatch -} from '../../utils/fs'; +import { writeFile, expectFileToMatch } from '../../utils/fs'; export default function() { @@ -14,8 +11,7 @@ export default function() { .then(() => ng('xi18n', '--out-file', 'messages.fr.xlf')) .then((output) => { if (!output.stdout.match(/starting from Angular v4/)) { - expectFileToExist(join('src', 'messages.fr.xlf')); - expectFileToMatch(join('src', 'messages.fr.xlf'), /Hello world/); + return expectFileToMatch(join('src', 'messages.fr.xlf'), 'Hello world'); } }); } diff --git a/tests/e2e/tests/lint/lint-no-config-section.ts b/tests/e2e/tests/lint/lint-no-config-section.ts index 79378681cae1..d2bfc1e4992d 100644 --- a/tests/e2e/tests/lint/lint-no-config-section.ts +++ b/tests/e2e/tests/lint/lint-no-config-section.ts @@ -1,13 +1,7 @@ import { ng } from '../../utils/process'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => ng('set', 'lint', '[]')) .then(() => ng('lint')) diff --git a/tests/e2e/tests/lint/lint-no-project.ts b/tests/e2e/tests/lint/lint-no-project.ts index 9e374ef040e4..d558a76c8d50 100644 --- a/tests/e2e/tests/lint/lint-no-project.ts +++ b/tests/e2e/tests/lint/lint-no-project.ts @@ -1,15 +1,9 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; import { expectToFail } from '../../utils/utils'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => ng('set', 'lint.0.project', '')) .then(() => ng('lint', '--type-check')) diff --git a/tests/e2e/tests/lint/lint-with-exclude.ts b/tests/e2e/tests/lint/lint-with-exclude.ts index be792e2e9530..bcf193acc692 100644 --- a/tests/e2e/tests/lint/lint-with-exclude.ts +++ b/tests/e2e/tests/lint/lint-with-exclude.ts @@ -1,14 +1,8 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - const fileName = 'src/app/foo.ts'; return Promise.resolve() diff --git a/tests/e2e/tests/lint/lint-with-force.ts b/tests/e2e/tests/lint/lint-with-force.ts index ac3f2975e663..1f40b16a2346 100644 --- a/tests/e2e/tests/lint/lint-with-force.ts +++ b/tests/e2e/tests/lint/lint-with-force.ts @@ -1,14 +1,8 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - const fileName = 'src/app/foo.ts'; return Promise.resolve() diff --git a/tests/e2e/tests/lint/lint-with-format-by-aliases.ts b/tests/e2e/tests/lint/lint-with-format-by-aliases.ts index 067adb1640f6..cb0d76c12827 100644 --- a/tests/e2e/tests/lint/lint-with-format-by-aliases.ts +++ b/tests/e2e/tests/lint/lint-with-format-by-aliases.ts @@ -1,14 +1,8 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - const fileName = 'src/app/foo.ts'; return Promise.resolve() diff --git a/tests/e2e/tests/lint/lint-with-format.ts b/tests/e2e/tests/lint/lint-with-format.ts index ed21f1ae87a1..50ee8b5454a9 100644 --- a/tests/e2e/tests/lint/lint-with-format.ts +++ b/tests/e2e/tests/lint/lint-with-format.ts @@ -1,14 +1,8 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; import { oneLine } from 'common-tags'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - const fileName = 'src/app/foo.ts'; return Promise.resolve() diff --git a/tests/e2e/tests/lint/lint-with-nested-configs.ts b/tests/e2e/tests/lint/lint-with-nested-configs.ts index d0580fa8f114..951c9f4aaccd 100644 --- a/tests/e2e/tests/lint/lint-with-nested-configs.ts +++ b/tests/e2e/tests/lint/lint-with-nested-configs.ts @@ -1,14 +1,9 @@ import { createDir, writeFile } from '../../utils/fs'; import { ng } from '../../utils/process'; import { expectToFail } from '../../utils/utils'; -import { getGlobalVariable } from '../../utils/env'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { const fileName = 'src/app/foo/foo.ts'; const nestedConfigContent = ` { diff --git a/tests/e2e/tests/lint/lint-with-type-check-fail.ts b/tests/e2e/tests/lint/lint-with-type-check-fail.ts index 9d545f7cfec0..965a18559d07 100644 --- a/tests/e2e/tests/lint/lint-with-type-check-fail.ts +++ b/tests/e2e/tests/lint/lint-with-type-check-fail.ts @@ -1,14 +1,9 @@ import { ng } from '../../utils/process'; import { expectToFail } from '../../utils/utils'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { const fileName = 'src/app/foo.ts'; const fileContents = ` const ANIMATION_CSS_VALUE_REGEX = 'asda'; diff --git a/tests/e2e/tests/lint/lint-with-type-check.ts b/tests/e2e/tests/lint/lint-with-type-check.ts index 59b8642cf6f8..ac6dfc761ba9 100644 --- a/tests/e2e/tests/lint/lint-with-type-check.ts +++ b/tests/e2e/tests/lint/lint-with-type-check.ts @@ -1,13 +1,8 @@ import { ng } from '../../utils/process'; import { writeFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { const fileName = 'src/app/foo.ts'; const fileContents = ` const ANIMATION_CSS_VALUE_REGEX = 'asda'; diff --git a/tests/e2e/tests/misc/coverage.ts b/tests/e2e/tests/misc/coverage.ts index 280a5cca8458..f7e008b55072 100644 --- a/tests/e2e/tests/misc/coverage.ts +++ b/tests/e2e/tests/misc/coverage.ts @@ -2,15 +2,9 @@ import {expectFileToExist, expectFileToMatch} from '../../utils/fs'; import {updateJsonFile} from '../../utils/project'; import {expectToFail} from '../../utils/utils'; import {ng} from '../../utils/process'; -import {getGlobalVariable} from '../../utils/env'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return ng('test', '--single-run', '--code-coverage') .then(() => expectFileToExist('coverage/src/app')) .then(() => expectFileToExist('coverage/lcov.info')) diff --git a/tests/e2e/tests/misc/default-port.ts b/tests/e2e/tests/misc/default-port.ts index a010173f2907..a9e6ef9bc590 100644 --- a/tests/e2e/tests/misc/default-port.ts +++ b/tests/e2e/tests/misc/default-port.ts @@ -2,14 +2,9 @@ import { request } from '../../utils/http'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; import { updateJsonFile } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; -export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function() { return Promise.resolve() .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson.defaults; diff --git a/tests/e2e/tests/misc/deploy-url.ts b/tests/e2e/tests/misc/deploy-url.ts index ef2393f3b33f..b87f94060480 100644 --- a/tests/e2e/tests/misc/deploy-url.ts +++ b/tests/e2e/tests/misc/deploy-url.ts @@ -1,15 +1,9 @@ import { killAllProcesses } from '../../utils/process'; import { request } from '../../utils/http'; import { ngServe, updateJsonFile } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; import { writeMultipleFiles } from '../../utils/fs'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => writeMultipleFiles({ 'src/string-script.js': 'console.log(\'string-script\'); var number = 1+1;', diff --git a/tests/e2e/tests/misc/different-file-format.ts b/tests/e2e/tests/misc/different-file-format.ts index df2fe4ff4216..9f1abbb7eab5 100644 --- a/tests/e2e/tests/misc/different-file-format.ts +++ b/tests/e2e/tests/misc/different-file-format.ts @@ -1,6 +1,5 @@ import {ng} from '../../utils/process'; import * as fs from '../../utils/fs'; -import {getGlobalVariable} from '../../utils/env'; const options = { @@ -9,11 +8,6 @@ const options = { export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => fs.prependToFile('./src/tsconfig.app.json', '\ufeff', options)) .then(() => fs.prependToFile('./.angular-cli.json', '\ufeff', options)) diff --git a/tests/e2e/tests/misc/fallback.ts b/tests/e2e/tests/misc/fallback.ts index c3cf7067aa6c..6c3cefe86864 100644 --- a/tests/e2e/tests/misc/fallback.ts +++ b/tests/e2e/tests/misc/fallback.ts @@ -3,15 +3,9 @@ import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; import { updateJsonFile } from '../../utils/project'; import { moveFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - // should fallback to config.app[0].index (index.html by default) return Promise.resolve() .then(() => ngServe()) diff --git a/tests/e2e/tests/misc/ssl-default-config.ts b/tests/e2e/tests/misc/ssl-default-config.ts index c87641a54d11..4d77c22d9d88 100644 --- a/tests/e2e/tests/misc/ssl-default-config.ts +++ b/tests/e2e/tests/misc/ssl-default-config.ts @@ -2,14 +2,9 @@ import { request } from '../../utils/http'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; import { updateJsonFile } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; -export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function() { return Promise.resolve() .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson.defaults; diff --git a/tests/e2e/tests/misc/ssl-default.ts b/tests/e2e/tests/misc/ssl-default.ts index 5f32001067ec..80c3952b16ab 100644 --- a/tests/e2e/tests/misc/ssl-default.ts +++ b/tests/e2e/tests/misc/ssl-default.ts @@ -1,15 +1,9 @@ import { request } from '../../utils/http'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => ngServe('--ssl', 'true')) .then(() => request('https://localhost:4200/')) diff --git a/tests/e2e/tests/misc/ssl-with-cert-config.ts b/tests/e2e/tests/misc/ssl-with-cert-config.ts index 50e03f2485ce..7d14478e0a29 100644 --- a/tests/e2e/tests/misc/ssl-with-cert-config.ts +++ b/tests/e2e/tests/misc/ssl-with-cert-config.ts @@ -3,14 +3,9 @@ import { assetDir } from '../../utils/assets'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; import { updateJsonFile } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; -export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function() { return Promise.resolve() .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson.defaults; diff --git a/tests/e2e/tests/misc/ssl-with-cert.ts b/tests/e2e/tests/misc/ssl-with-cert.ts index de2e0c90ca17..e15aaed9d2b8 100644 --- a/tests/e2e/tests/misc/ssl-with-cert.ts +++ b/tests/e2e/tests/misc/ssl-with-cert.ts @@ -2,15 +2,9 @@ import { request } from '../../utils/http'; import { assetDir } from '../../utils/assets'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; export default function() { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => ngServe( '--ssl', 'true', diff --git a/tests/e2e/tests/misc/typescript-warning.ts b/tests/e2e/tests/misc/typescript-warning.ts index 6b8735a3de34..1b247eea264a 100644 --- a/tests/e2e/tests/misc/typescript-warning.ts +++ b/tests/e2e/tests/misc/typescript-warning.ts @@ -3,15 +3,12 @@ import { getGlobalVariable } from '../../utils/env'; export default function () { - // typescript@2.5 is not part of the officially supported range in latest stable. - // Update as needed. - let unsupportedTsVersion = '2.5'; + // typescript@2.7.0-dev.20180104 is not part of the officially supported range in latest stable. + let unsupportedTsVersion = '2.7.0-dev.20180104'; - // TODO: re-enable for ng5, adjust as needed. This test fails on ng5 because the 2.5 is supported. - // When ng5 because the default this test will need to be adjusted to use 2.3 as the unsupported - // version, and to disable the experimental angular compiler (transforms need 2.4 minimum). - if (getGlobalVariable('argv').nightly) { - return; + // Skip this test in Angular 2/4. + if (getGlobalVariable('argv').ng2 || getGlobalVariable('argv').ng4) { + return Promise.resolve(); } return Promise.resolve() diff --git a/tests/e2e/tests/packages/webpack/server-ng5.ts b/tests/e2e/tests/packages/webpack/server-ng5.ts new file mode 100644 index 000000000000..021b984508d3 --- /dev/null +++ b/tests/e2e/tests/packages/webpack/server-ng5.ts @@ -0,0 +1,23 @@ +import { normalize } from 'path'; +import { createProjectFromAsset } from '../../../utils/assets'; +import { exec } from '../../../utils/process'; +import { expectFileToMatch, rimraf } from '../../../utils/fs'; + + +export default function (skipCleaning: () => void) { + return Promise.resolve() + .then(() => createProjectFromAsset('webpack/test-server-app')) + .then(() => exec(normalize('node_modules/.bin/webpack'))) + .then(() => expectFileToMatch('dist/app.main.js', + new RegExp('MyInjectable.ctorParameters = .*' + + 'type: undefined, decorators.*Inject.*args: .*DOCUMENT.*')) + .then(() => expectFileToMatch('dist/app.main.js', + new RegExp('AppComponent.ctorParameters = .*MyInjectable')) + .then(() => expectFileToMatch('dist/app.main.js', + /AppModule \*\/\].*\.testProp = \'testing\'/)) + .then(() => expectFileToMatch('dist/app.main.js', + /platformServer \*\/\]\)\(\)\.bootstrapModuleFactory\(.*\/\* AppModuleNgFactory \*\/\]/)) + .then(() => expectFileToMatch('dist/app.main.js', + /renderModuleFactory \*\/\].*\/\* AppModuleNgFactory \*\/\]/)) + .then(() => skipCleaning()); +} diff --git a/tests/e2e/tests/test/e2e-baseurl.ts b/tests/e2e/tests/test/e2e-baseurl.ts index 8a70d4ef6197..c6a66d23e0d3 100644 --- a/tests/e2e/tests/test/e2e-baseurl.ts +++ b/tests/e2e/tests/test/e2e-baseurl.ts @@ -2,14 +2,9 @@ import { ng, killAllProcesses } from '../../utils/process'; import { expectToFail } from '../../utils/utils'; import { ngServe } from '../../utils/project'; import { updateJsonFile } from '../../utils/project'; -import { getGlobalVariable } from '../../utils/env'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { return Promise.resolve() .then(() => expectToFail(() => ng('e2e', '--no-serve'))) .then(() => updateJsonFile('.angular-cli.json', configJson => { diff --git a/tests/e2e/tests/test/test-assets.ts b/tests/e2e/tests/test/test-assets.ts index adf3ee27328c..12d247aad6ef 100644 --- a/tests/e2e/tests/test/test-assets.ts +++ b/tests/e2e/tests/test/test-assets.ts @@ -2,16 +2,10 @@ import { writeMultipleFiles } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { expectToFail } from '../../utils/utils'; -import { getGlobalVariable } from '../../utils/env'; import { stripIndent } from 'common-tags'; // Make sure asset files are served export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } - return Promise.resolve() .then(() => writeMultipleFiles({ 'src/assets/file.txt': 'assets-folder-content', diff --git a/tests/e2e/tests/test/test-backwards-compat.ts b/tests/e2e/tests/test/test-backwards-compat.ts index 067f19b9c31e..aaac37583e5e 100644 --- a/tests/e2e/tests/test/test-backwards-compat.ts +++ b/tests/e2e/tests/test/test-backwards-compat.ts @@ -1,13 +1,8 @@ import { ng } from '../../utils/process'; import { replaceInFile } from '../../utils/fs'; -import { getGlobalVariable } from '../../utils/env'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { // Old configs (with the cli preprocessor listed) should still supported. return Promise.resolve() .then(() => replaceInFile('karma.conf.js', diff --git a/tests/e2e/tests/test/test-scripts.ts b/tests/e2e/tests/test/test-scripts.ts index 4ca07efd65db..bf42a46d1d42 100644 --- a/tests/e2e/tests/test/test-scripts.ts +++ b/tests/e2e/tests/test/test-scripts.ts @@ -2,15 +2,10 @@ import { writeMultipleFiles } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; import { expectToFail } from '../../utils/utils'; -import { getGlobalVariable } from '../../utils/env'; import { stripIndent } from 'common-tags'; -export default function () { - // Skip this in Appveyor tests. - if (getGlobalVariable('argv').appveyor) { - return Promise.resolve(); - } +export default function () { return Promise.resolve() .then(() => ng('test', '--watch=false')) // prepare global scripts test files diff --git a/tests/e2e/tests/third-party/bootstrap.ts b/tests/e2e/tests/third-party/bootstrap.ts index 9be757f279e9..c3fadd070b7c 100644 --- a/tests/e2e/tests/third-party/bootstrap.ts +++ b/tests/e2e/tests/third-party/bootstrap.ts @@ -6,7 +6,7 @@ import {oneLineTrim} from 'common-tags'; export default function() { return Promise.resolve() - .then(() => silentNpm('install', 'bootstrap@next')) + .then(() => silentNpm('install', 'bootstrap@4.0.0-beta.3')) .then(() => updateJsonFile('.angular-cli.json', configJson => { const app = configJson['apps'][0]; app['styles'].push('../node_modules/bootstrap/dist/css/bootstrap.css'); @@ -23,5 +23,19 @@ export default function() { + `)) + .then(() => ng( + 'build', + '--prod', + '--extract-css', + '--output-hashing=none' + )) + .then(() => expectFileToMatch('dist/scripts.bundle.js', 'jQuery')) + .then(() => expectFileToMatch('dist/styles.bundle.css', '* Bootstrap')) + .then(() => expectFileToMatch('dist/index.html', oneLineTrim` + + + + `)); } diff --git a/tests/e2e/utils/assets.ts b/tests/e2e/utils/assets.ts index d1a8e0a98ce2..72db65f90ef6 100644 --- a/tests/e2e/utils/assets.ts +++ b/tests/e2e/utils/assets.ts @@ -11,6 +11,13 @@ export function assetDir(assetName: string) { return join(__dirname, '../assets', assetName); } +export function copyProjectAsset(assetName: string, to?: string) { + const tempRoot = join(getGlobalVariable('tmp-root'), 'test-project', 'src'); + const sourcePath = assetDir(assetName); + const targetPath = join(tempRoot, to || assetName); + + return copyFile(sourcePath, targetPath); +} export function copyAssets(assetName: string) { const tempRoot = join(getGlobalVariable('tmp-root'), 'assets', assetName); diff --git a/tests/e2e/utils/project.ts b/tests/e2e/utils/project.ts index a11784cfbd12..446375fab4fd 100644 --- a/tests/e2e/utils/project.ts +++ b/tests/e2e/utils/project.ts @@ -42,6 +42,7 @@ export function createProject(name: string, ...args: string[]) { .then(() => useCIChrome()) .then(() => useCIDefaults()) .then(() => argv['ng2'] ? useNg2() : Promise.resolve()) + .then(() => argv['ng4'] ? useNg4() : Promise.resolve()) .then(() => argv.nightly || argv['ng-sha'] ? useSha() : Promise.resolve()) .then(() => console.log(`Project ${name} created... Installing npm.`)) .then(() => silentNpm('install')); @@ -139,7 +140,7 @@ export function useCIDefaults() { sourcemaps: false, progress: false }; - }) + }); } export function useCIChrome() { @@ -172,7 +173,7 @@ export function useCIChrome() { .catch(() => null); } -// Convert a Angular 4 project to Angular 2. +// Convert a Angular 5 project to Angular 2. export function useNg2() { const ng2Deps: any = { 'dependencies': { @@ -275,12 +276,53 @@ export function useNg2() { Object.keys(ng2Deps['devDependencies']).forEach(pkgName => { json['devDependencies'][pkgName] = ng2Deps['devDependencies'][pkgName]; }); - console.log(JSON.stringify(json)) + console.log(JSON.stringify(json)); })) .then(() => updateJsonFile('src/tsconfig.app.json', json => Object.assign(json, tsconfigAppJson))) .then(() => updateJsonFile('src/tsconfig.spec.json', json => Object.assign(json, tsconfigSpecJson))) .then(() => updateJsonFile('e2e/tsconfig.e2e.json', json => - Object.assign(json, tsconfigE2eJson))); + Object.assign(json, tsconfigE2eJson))) + .then(() => replaceInFile('src/test.ts', 'import \'zone.js/dist/zone-testing\';', ` + import 'zone.js/dist/long-stack-trace-zone'; + import 'zone.js/dist/proxy.js'; + import 'zone.js/dist/sync-test'; + import 'zone.js/dist/jasmine-patch'; + import 'zone.js/dist/async-test'; + import 'zone.js/dist/fake-async-test'; + `)); +} + +// Convert a Angular 5 project to Angular 4. +export function useNg4() { + const ng4Deps: any = { + 'dependencies': { + '@angular/common': '^4.4.6', + '@angular/compiler': '^4.4.6', + '@angular/core': '^4.4.6', + '@angular/forms': '^4.4.6', + '@angular/http': '^4.4.6', + '@angular/platform-browser': '^4.4.6', + '@angular/platform-browser-dynamic': '^4.4.6', + '@angular/router': '^4.4.6', + 'zone.js': '^0.8.14' + }, + 'devDependencies': { + '@angular/compiler-cli': '^4.4.6', + 'typescript': '~2.3.3' + } + }; + + + return Promise.resolve() + .then(() => updateJsonFile('package.json', json => { + Object.keys(ng4Deps['dependencies']).forEach(pkgName => { + json['dependencies'][pkgName] = ng4Deps['dependencies'][pkgName]; + }); + Object.keys(ng4Deps['devDependencies']).forEach(pkgName => { + json['devDependencies'][pkgName] = ng4Deps['devDependencies'][pkgName]; + }); + console.log(JSON.stringify(json)); + })); }