Compare commits

...

26 Commits

Author SHA1 Message Date
79fb9d449c build(docs-infra): use pinned dependencies when possible in ng-packages-installer (#28510)
Previously, `ng-packages-installer` would replace the version ranges for
all dependencies that were peer dependencies of an Angular package with
the version range used in the Angular package. This effectively meant
that the pinned version (from `yarn.lock`) for that dependency was
ignored (even if the pinned version satisfied the new version range).

This commit reduces non-determinism in CI jobs using the locally built
Angular packages by always using pinned versions of dependencies for
Angular package peer dependencies if possible.

For example, assuming the following versions for the RxJS dependency:

- **aio/package.json**: `rxjs: ^6.3.0`
- **aio/yarn.lock**: `rxjs@^6.3.0: 6.3.3`
- **@angular/core#peerDependencies**: `rxjs: ^6.0.0`

...the following versions would be used with `ng-packages-installer`:

- Before this commit:
  - **aio/package.json**: `rxjs: ^6.0.0`
  - **node_modules/rxjs/**: `6.4.0` (latest version satisfying `^6.0.0`)
- After this commit:
  - **aio/package.json**: `rxjs: ^6.3.0`
  - **node_modules/rxjs/**: `6.3.3` (because it satisfies `^6.0.0`)

PR Close #28510
2019-03-06 15:03:43 -08:00
73a93d3ab6 build(docs-infra): keep other dependencies pinned when installing local Angular packages (#28510)
`ng-packages-installer` can be used to replace Angular packages with
locally built ones (from `dist/packages-dist/`) along with their peer
dependencies.

Previously, in order to achieve this, `yarn install` was called with the
`--no-lockfile` option, which resulted in installing the latest versions
of all dependencies (including transitive ones) permitted by the
corresponding version ranges in `package.json` files. As a result, newly
released versions would be picked, resulting in unexpected,
non-deterministic breakages in CI.

This commit calls `yarn install` with the `--pure-lockfile` option
instead. As a result, only the Angular packages (for which the locally
built ones are used) and their peer dependencies are unpinned; the
pinned versions from `yarn.lock` are used for all other (direct and
transitive) dependencies.

While this does not eliminate non-determinism across builds, it
significantly reduces it.

PR Close #28510
2019-03-06 15:03:37 -08:00
8eda5a152b perf(docs-infra): avoid unnecessary I/O operation in ng-packages-installer (#28510)
PR Close #28510
2019-03-06 15:03:29 -08:00
7b82ce0c67 refactor(docs-infra): format package.json for readability in ng-packages-installer (#28510)
PR Close #28510
2019-03-06 15:03:19 -08:00
2eb5fe699f build(docs-infra): remove unnecessary workaround for RxJS in ng-packages-installer (#28510)
Since b43f8bc7d, RxJS does not need to be patched any more in the
top-level `node_modules/`, so we don't need to special-case RxJS in
`ng-package-installer` and use `node_modules/rxjs/`.

PR Close #28510
2019-03-06 15:03:12 -08:00
f99febcdf9 ci(docs-infra): fix deployment to Firebase
This is a backport of f1a860fbf to 6.1.x.
Related to #29029.
2019-03-06 15:03:00 -08:00
36cbfb1771 build(compiler-cli): upgrade chokidar to latest version
This is a backport of 745c9c5ca to 6.1.x.
Related to #28771.
2019-02-28 18:54:13 +02:00
1f5315f6f7 build(docs-infra): upgrade npm-run-all to latest version for security
This is a backport of f45aedcbf to 6.2.x.
See the original commit for details.
2019-02-28 18:54:13 +02:00
eeebe621fe docs: Indicate that PRs should have an associated issue (#25436)
PR Close #25436
2018-10-15 16:51:46 -07:00
05f279df49 test(docs-infra): improve logging output in test-pwa-score[-localhost] (#26459)
PR Close #26459
2018-10-15 15:23:37 -07:00
485d67bfed build(docs-infra): upgrade lighthouse to 3.2.1 (#26459)
PR Close #26459
2018-10-15 15:23:37 -07:00
a1592f5a20 ci(docs-infra): reduce flakyness (#26459)
PR Close #26459
2018-10-15 15:23:37 -07:00
a251374ecd docs: update process for cli tool and restructure doc (#25752)
PR Close #25752
2018-10-15 11:22:18 -07:00
2b00c17091 docs: minor wording correction. "use" to "user". (#26452)
PR Close #26452
2018-10-15 11:19:47 -07:00
81724f5790 ci(docs-infra): allow aio_local builds to fail on Travis
This job is flaky (up to 50%!) so let's allow it to fail while
we investigate the reason.
2018-10-15 10:28:09 -07:00
1f06b6c99b build: upgrade @types/jasminewd2 to 2.0.4 (#26432)
This commit also removes the extra jasminewd2 typings, since the
changes have been merged in the official typings with
DefinitelyTyped/DefinitelyTyped#28957.
2018-10-13 21:09:41 -07:00
6790709b93 fix(docs-infra): prevent unnecessary SideNav scrollbar (#26416)
Fixes #21508

PR Close #26416
2018-10-12 14:09:09 -07:00
9f7f67121c build(docs-infra): only show name in 'inherited from' section (#26387)
Closes #26181

PR Close #26387
2018-10-12 09:13:02 -07:00
db49beae15 build(docs-infra): upgrade @angular/material to 7.0.0-rc.1 (#26394)
PR Close #26394
2018-10-12 08:57:50 -07:00
97609daea9 build(docs-infra): upgrade @angular/* to 7.0.0-rc.1 (#26394)
PR Close #26394
2018-10-12 08:57:50 -07:00
abcb03cb82 build(docs-infra): upgrade @angular/cli to 7.0.0-rc.2 (#26394)
PR Close #26394
2018-10-12 08:57:47 -07:00
4f09f7db73 fix(upgrade): properly destroy upgraded component elements and descendants (#26209)
Fixes #26208

PR Close #26209
2018-10-11 21:07:49 -07:00
f1e14a3224 docs: add angularmix to events page (#26374)
PR Close #26374
2018-10-11 14:16:02 -07:00
50de03a83a docs: fix transpiles link in dependency injection (#26250)
fixed a double bracket that broke the link

PR Close #26250
2018-10-11 14:11:46 -07:00
65555fe35d Revert "fix(upgrade): properly destroy upgraded component elements and descendants (#26209)"
This reverts commit 6da3867d63. Revert is needed due to compilation failures due to this PR inside Google.
2018-10-10 14:46:20 -07:00
6da3867d63 fix(upgrade): properly destroy upgraded component elements and descendants (#26209)
Fixes #26208

PR Close #26209
2018-10-10 14:19:01 -07:00
34 changed files with 5774 additions and 2518 deletions

View File

@ -56,6 +56,7 @@ env:
matrix: matrix:
fast_finish: true fast_finish: true
allow_failures: allow_failures:
- env: "CI_MODE=aio_local"
- env: "CI_MODE=saucelabs_optional" - env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional" - env: "CI_MODE=browserstack_optional"

View File

@ -71,6 +71,8 @@ Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR 1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort. that relates to your submission. You don't want to duplicate effort.
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discussing the design up front helps to ensure that we're ready to accept your work.
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs. 1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository. We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
1. Fork the angular/angular repo. 1. Fork the angular/angular repo.

View File

@ -46,7 +46,7 @@
"@types/shelljs": "^0.8.0", "@types/shelljs": "^0.8.0",
"@types/supertest": "^2.0.5", "@types/supertest": "^2.0.5",
"nodemon": "^1.18.3", "nodemon": "^1.18.3",
"npm-run-all": "^4.1.3", "npm-run-all": "^4.1.5",
"supertest": "^3.1.0", "supertest": "^3.1.0",
"tslint": "^5.11.0", "tslint": "^5.11.0",
"tslint-jasmine-noSkipOrFocus": "^1.0.9", "tslint-jasmine-noSkipOrFocus": "^1.0.9",

View File

@ -129,7 +129,7 @@ ansi-styles@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
ansi-styles@^3.2.0, ansi-styles@^3.2.1: ansi-styles@^3.2.1:
version "3.2.1" version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
dependencies: dependencies:
@ -384,7 +384,7 @@ chalk@^1.1.3:
strip-ansi "^3.0.0" strip-ansi "^3.0.0"
supports-color "^2.0.0" supports-color "^2.0.0"
chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0: chalk@^2.0.1, chalk@^2.3.0:
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
dependencies: dependencies:
@ -392,6 +392,15 @@ chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
escape-string-regexp "^1.0.5" escape-string-regexp "^1.0.5"
supports-color "^5.3.0" supports-color "^5.3.0"
chalk@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
check-error@^1.0.1: check-error@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
@ -532,9 +541,10 @@ cross-spawn@^5.0.1:
shebang-command "^1.2.0" shebang-command "^1.2.0"
which "^1.2.9" which "^1.2.9"
cross-spawn@^6.0.4: cross-spawn@^6.0.5:
version "6.0.5" version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
dependencies: dependencies:
nice-try "^1.0.4" nice-try "^1.0.4"
path-key "^2.0.1" path-key "^2.0.1"
@ -1630,16 +1640,17 @@ npm-packlist@^1.1.6:
ignore-walk "^3.0.1" ignore-walk "^3.0.1"
npm-bundled "^1.0.1" npm-bundled "^1.0.1"
npm-run-all@^4.1.3: npm-run-all@^4.1.5:
version "4.1.3" version "4.1.5"
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.3.tgz#49f15b55a66bb4101664ce270cb18e7103f8f185" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==
dependencies: dependencies:
ansi-styles "^3.2.0" ansi-styles "^3.2.1"
chalk "^2.1.0" chalk "^2.4.1"
cross-spawn "^6.0.4" cross-spawn "^6.0.5"
memorystream "^0.3.1" memorystream "^0.3.1"
minimatch "^3.0.4" minimatch "^3.0.4"
ps-tree "^1.1.0" pidtree "^0.3.0"
read-pkg "^3.0.0" read-pkg "^3.0.0"
shell-quote "^1.6.1" shell-quote "^1.6.1"
string.prototype.padend "^3.0.0" string.prototype.padend "^3.0.0"
@ -1786,6 +1797,11 @@ pause-stream@0.0.11:
dependencies: dependencies:
through "~2.3" through "~2.3"
pidtree@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b"
integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==
pify@^2.3.0: pify@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"

View File

@ -17,7 +17,7 @@ Here are some key features.
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers. * [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
* [Service Workers](guide/service-worker-intro): Use a service worker to reduce dependency on the network * [Service Workers](guide/service-worker-intro): Use a service worker to reduce dependency on the network
significantly improving the use experience. significantly improving the user experience.
## Domain-specific libraries ## Domain-specific libraries

View File

@ -240,7 +240,7 @@ The `@Injectable()` decorator is the standard decorator for service classes.
<div class="alert-is-helpful"> <div class="alert-is-helpful">
The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles]((guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`. The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles](guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`.
This means you're responsible for putting `@Injectable()` on your service classes. This means you're responsible for putting `@Injectable()` on your service classes.

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,12 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- AngularMix -->
<tr>
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
<td>Orlando, Florida</td>
<td>October 10-12, 2018</td>
</tr>
<!-- ReactiveConf --> <!-- ReactiveConf -->
<tr> <tr>
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th> <th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
@ -59,13 +65,13 @@
<!-- ngconf 2018--> <!-- ngconf 2018-->
<tr> <tr>
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th> <th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
<td>Salt Lake City, UT</td> <td>Salt Lake City, Utah</td>
<td>April 18-20, 2018</td> <td>April 18-20, 2018</td>
</tr> </tr>
<!-- WeRDevs--> <!-- WeRDevs-->
<tr> <tr>
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th> <th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
<td>Vienna</td> <td>Vienna, Austria</td>
<td>May 16-18, 2018</td> <td>May 16-18, 2018</td>
</tr> </tr>
<!-- ngJapan--> <!-- ngJapan-->

View File

@ -30,7 +30,7 @@
"postsetup-local": "yarn postsetup", "postsetup-local": "yarn postsetup",
"set-opensearch-url": "node --eval \"const sh = require('shelljs'); sh.set('-e'); sh.sed('-i', /PLACEHOLDER_URL/g, process.argv[1], 'dist/assets/opensearch.xml');\"", "set-opensearch-url": "node --eval \"const sh = require('shelljs'); sh.set('-e'); sh.sed('-i', /PLACEHOLDER_URL/g, process.argv[1], 'dist/assets/opensearch.xml');\"",
"test-pwa-score": "node scripts/test-pwa-score", "test-pwa-score": "node scripts/test-pwa-score",
"test-pwa-score-localhost": "run-p --race \"~~http-server dist -p 4200 --silent\" \"test-pwa-score http://localhost:4200 {1}\" --", "test-pwa-score-localhost": "run-p --race \"~~http-server dist -p 4200 --silent\" \"test-pwa-score http://localhost:4200 {1} {2}\" --",
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e", "example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"", "example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug", "example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug",
@ -71,17 +71,17 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^7.0.0-rc.0", "@angular/animations": "^7.0.0-rc.1",
"@angular/cdk": "6.0.2", "@angular/cdk": "7.0.0-rc.1",
"@angular/common": "^7.0.0-rc.0", "@angular/common": "^7.0.0-rc.1",
"@angular/core": "^7.0.0-rc.0", "@angular/core": "^7.0.0-rc.1",
"@angular/elements": "^7.0.0-rc.0", "@angular/elements": "^7.0.0-rc.1",
"@angular/forms": "^7.0.0-rc.0", "@angular/forms": "^7.0.0-rc.1",
"@angular/material": "6.0.2", "@angular/material": "7.0.0-rc.1",
"@angular/platform-browser": "^7.0.0-rc.0", "@angular/platform-browser": "^7.0.0-rc.1",
"@angular/platform-browser-dynamic": "^7.0.0-rc.0", "@angular/platform-browser-dynamic": "^7.0.0-rc.1",
"@angular/router": "^7.0.0-rc.0", "@angular/router": "^7.0.0-rc.1",
"@angular/service-worker": "^7.0.0-rc.0", "@angular/service-worker": "^7.0.0-rc.1",
"@webcomponents/custom-elements": "^1.2.0", "@webcomponents/custom-elements": "^1.2.0",
"classlist.js": "^1.1.20150312", "classlist.js": "^1.1.20150312",
"core-js": "^2.4.1", "core-js": "^2.4.1",
@ -91,14 +91,15 @@
"zone.js": "^0.8.26" "zone.js": "^0.8.26"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^0.8.3", "@angular-devkit/build-angular": "~0.9.0-rc.1",
"@angular/cli": "^6.2.3", "@angular/cli": "^7.0.0-rc.2",
"@angular/compiler": "^7.0.0-rc.0", "@angular/compiler": "^7.0.0-rc.1",
"@angular/compiler-cli": "^7.0.0-rc.0", "@angular/compiler-cli": "^7.0.0-rc.1",
"@angular/language-service": "^7.0.0-rc.0", "@angular/language-service": "^7.0.0-rc.1",
"@types/jasmine": "^2.5.52", "@types/jasmine": "^2.5.52",
"@types/jasminewd2": "^2.0.3", "@types/jasminewd2": "^2.0.4",
"@types/node": "~6.0.60", "@types/node": "~6.0.60",
"@yarnpkg/lockfile": "^1.1.0",
"archiver": "^1.3.0", "archiver": "^1.3.0",
"canonical-path": "^0.0.2", "canonical-path": "^0.0.2",
"chalk": "^2.1.0", "chalk": "^2.1.0",
@ -111,7 +112,7 @@
"entities": "^1.1.1", "entities": "^1.1.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0", "eslint-plugin-jasmine": "^2.2.0",
"firebase-tools": "^3.2.1", "firebase-tools": "^5.1.1",
"fs-extra": "^2.1.2", "fs-extra": "^2.1.2",
"globby": "^6.1.0", "globby": "^6.1.0",
"hast-util-is-element": "^1.0.0", "hast-util-is-element": "^1.0.0",
@ -132,10 +133,10 @@
"karma-coverage-istanbul-reporter": "^1.3.0", "karma-coverage-istanbul-reporter": "^1.3.0",
"karma-jasmine": "^1.1.0", "karma-jasmine": "^1.1.0",
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^0.2.2",
"lighthouse": "^2.5.0", "lighthouse": "^3.2.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lunr": "^2.1.0", "lunr": "^2.1.0",
"npm-run-all": "^4.1.3", "npm-run-all": "^4.1.5",
"protractor": "^5.2.0", "protractor": "^5.2.0",
"rehype": "^4.0.0", "rehype": "^4.0.0",
"rehype-slug": "^2.0.0", "rehype-slug": "^2.0.0",
@ -147,7 +148,7 @@
"tree-kill": "^1.1.0", "tree-kill": "^1.1.0",
"ts-node": "^3.3.0", "ts-node": "^3.3.0",
"tslint": "~5.9.1", "tslint": "~5.9.1",
"typescript": "^3.1.1", "typescript": "^3.1.2",
"uglify-js": "^3.0.15", "uglify-js": "^3.0.15",
"unist-util-filter": "^0.2.1", "unist-util-filter": "^0.2.1",
"unist-util-source": "^1.0.1", "unist-util-source": "^1.0.1",
@ -159,4 +160,4 @@
"xregexp": "^4.0.0", "xregexp": "^4.0.0",
"yargs": "^7.0.2" "yargs": "^7.0.2"
} }
} }

View File

@ -3,9 +3,9 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime": 3173, "runtime": 3173,
"main": 483428, "main": 494475,
"polyfills": 53920, "polyfills": 53926,
"prettify": 14913 "prettify": 14917
} }
} }
} }

View File

@ -100,8 +100,8 @@ fi
yarn payload-size yarn payload-size
# Deploy to Firebase # Deploy to Firebase
firebase use "$projectId" --token "$firebaseToken" yarn firebase use "$projectId" --token "$firebaseToken"
firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken" yarn firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken"
# Run PWA-score tests # Run PWA-score tests
yarn test-pwa-score "$deployedUrl" "$AIO_MIN_PWA_SCORE" yarn test-pwa-score "$deployedUrl" "$AIO_MIN_PWA_SCORE"

View File

@ -11,13 +11,15 @@
*/ */
// Imports // Imports
const chromeLauncher = require('chrome-launcher');
const lighthouse = require('lighthouse'); const lighthouse = require('lighthouse');
const chromeLauncher = require('lighthouse/chrome-launcher');
const printer = require('lighthouse/lighthouse-cli/printer'); const printer = require('lighthouse/lighthouse-cli/printer');
const config = require('lighthouse/lighthouse-core/config/default.js'); const config = require('lighthouse/lighthouse-core/config/default-config.js');
const logger = require('lighthouse-logger');
// Constants // Constants
const CHROME_LAUNCH_OPTS = {}; const CHROME_LAUNCH_OPTS = {};
const LIGHTHOUSE_FLAGS = {logLevel: 'info'};
const SKIPPED_HTTPS_AUDITS = ['redirects-http']; const SKIPPED_HTTPS_AUDITS = ['redirects-http'];
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/'; const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
@ -26,6 +28,7 @@ const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
if (process.env.TRAVIS) { if (process.env.TRAVIS) {
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN; process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
CHROME_LAUNCH_OPTS.chromeFlags = ['--no-sandbox']; CHROME_LAUNCH_OPTS.chromeFlags = ['--no-sandbox'];
LIGHTHOUSE_FLAGS.logLevel = 'error';
} }
// Run // Run
@ -42,18 +45,20 @@ function _main(args) {
skipHttpsAudits(config); skipHttpsAudits(config);
} }
launchChromeAndRunLighthouse(url, {}, config). logger.setLevel(LIGHTHOUSE_FLAGS.logLevel);
launchChromeAndRunLighthouse(url, LIGHTHOUSE_FLAGS, config).
then(results => processResults(results, logFile)). then(results => processResults(results, logFile)).
then(score => evaluateScore(minScore, score)). then(score => evaluateScore(minScore, score)).
catch(onError); catch(onError);
} }
function evaluateScore(expectedScore, actualScore) { function evaluateScore(expectedScore, actualScore) {
console.log('Lighthouse PWA score:'); console.log('\nLighthouse PWA score:');
console.log(` - Expected: ${expectedScore} / 100 (or higher)`); console.log(` - Expected: ${expectedScore.toFixed(0).padStart(3)} / 100 (or higher)`);
console.log(` - Actual: ${actualScore} / 100`); console.log(` - Actual: ${actualScore.toFixed(0).padStart(3)} / 100\n`);
if (actualScore < expectedScore) { if (isNaN(actualScore) || (actualScore < expectedScore)) {
throw new Error(`PWA score is too low. (${actualScore} < ${expectedScore})`); throw new Error(`PWA score is too low. (${actualScore} < ${expectedScore})`);
} }
} }
@ -87,20 +92,30 @@ function parseInput(args) {
} }
function processResults(results, logFile) { function processResults(results, logFile) {
let promise = Promise.resolve(); const categories = results.lhr.categories;
const report = results.report;
if (logFile) { return Promise.resolve().
console.log(`Saving results in '${logFile}'...`); then(() => {
console.log(`(LightHouse viewer: ${VIEWER_URL})`); if (logFile) {
console.log(`Saving results in '${logFile}'...`);
console.log(`(LightHouse viewer: ${VIEWER_URL})`);
// Remove the artifacts, which are not necessary for the report. return printer.write(report, printer.OutputMode.json, logFile);
// (Saves ~1,500,000 lines of formatted JSON output \o/) }
results.artifacts = undefined; }).
then(() => {
const categoryData = Object.keys(categories).map(name => categories[name]);
const maxTitleLen = Math.max(...categoryData.map(({title}) => title.length));
promise = printer.write(results, 'json', logFile); console.log('\nAudit scores:');
} categoryData.forEach(({title, score}) => {
const paddedTitle = `${title}:`.padEnd(maxTitleLen + 1);
return promise.then(() => Math.round(results.score)); const paddedScore = (score * 100).toFixed(0).padStart(3);
console.log(` - ${paddedTitle} ${paddedScore} / 100`);
});
}).
then(() => categories.pwa.score * 100);
} }
function skipHttpsAudits(config) { function skipHttpsAudits(config) {

View File

@ -164,10 +164,10 @@ describe('AppComponent', () => {
describe('onScroll', () => { describe('onScroll', () => {
it('should update `tocMaxHeight` accordingly', () => { it('should update `tocMaxHeight` accordingly', () => {
expect(component.tocMaxHeight).toBeUndefined(); component.tocMaxHeight = '';
component.onScroll(); component.onScroll();
expect(component.tocMaxHeight).toBeGreaterThan(0);
expect(component.tocMaxHeight).toMatch(/^\d+\.\d{2}$/);
}); });
}); });
@ -654,12 +654,13 @@ describe('AppComponent', () => {
it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => { it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => {
setHasFloatingToc(true); setHasFloatingToc(true);
expect(tocContainer!.style['max-height']).toBe('');
component.tocMaxHeight = '100'; component.tocMaxHeight = '100';
fixture.detectChanges(); fixture.detectChanges();
expect(tocContainer!.style['max-height']).toBe('100px'); expect(tocContainer!.style['max-height']).toBe('100px');
component.tocMaxHeight = '200';
fixture.detectChanges();
expect(tocContainer!.style['max-height']).toBe('200px');
}); });
it('should restrain scrolling inside the ToC container', () => { it('should restrain scrolling inside the ToC container', () => {

View File

@ -349,12 +349,17 @@ export class AppComponent implements OnInit {
@HostListener('window:scroll') @HostListener('window:scroll')
onScroll() { onScroll() {
if (!this.tocMaxHeightOffset) { if (!this.tocMaxHeightOffset) {
// Must wait until now for mat-toolbar to be measurable. // Must wait until `mat-toolbar` is measurable.
const el = this.hostElement.nativeElement as Element; const el = this.hostElement.nativeElement as Element;
this.tocMaxHeightOffset = const headerEl = el.querySelector('.app-toolbar');
el.querySelector('footer')!.clientHeight + const footerEl = el.querySelector('footer');
el.querySelector('.app-toolbar')!.clientHeight +
24; // fudge margin if (headerEl && footerEl) {
this.tocMaxHeightOffset =
headerEl.clientHeight +
footerEl.clientHeight +
24; // fudge margin
}
} }
this.tocMaxHeight = (document.body.scrollHeight - window.pageYOffset - this.tocMaxHeightOffset).toFixed(2); this.tocMaxHeight = (document.body.scrollHeight - window.pageYOffset - this.tocMaxHeightOffset).toFixed(2);

View File

@ -115,6 +115,7 @@ button.vertical-menu-item {
} }
.heading-children.collapsed { .heading-children.collapsed {
overflow: hidden; // Needed to prevent unnecessary sidenav scrollbar.
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
max-height: 1px; // Must have measurement to transition height. max-height: 1px; // Must have measurement to transition height.

View File

@ -53,7 +53,7 @@
"@types/angular-route": "^1.3.5", "@types/angular-route": "^1.3.5",
"@types/express": "^4.0.35", "@types/express": "^4.0.35",
"@types/jasmine": "~2.8.0", "@types/jasmine": "~2.8.0",
"@types/jasminewd2": "^2.0.3", "@types/jasminewd2": "^2.0.4",
"@types/jquery": "^3.3.4", "@types/jquery": "^3.3.4",
"@types/node": "^6.0.45", "@types/node": "^6.0.45",
"canonical-path": "0.0.2", "canonical-path": "0.0.2",

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,14 @@
const chalk = require('chalk'); const chalk = require('chalk');
const fs = require('fs-extra'); const fs = require('fs-extra');
const lockfile = require('@yarnpkg/lockfile');
const path = require('canonical-path'); const path = require('canonical-path');
const semver = require('semver');
const shelljs = require('shelljs'); const shelljs = require('shelljs');
const yargs = require('yargs'); const yargs = require('yargs');
const PACKAGE_JSON = 'package.json'; const PACKAGE_JSON = 'package.json';
const YARN_LOCK = 'yarn.lock';
const LOCAL_MARKER_PATH = 'node_modules/_local_.json'; const LOCAL_MARKER_PATH = 'node_modules/_local_.json';
const PACKAGE_JSON_REGEX = /^[^/]+\/package\.json$/; const PACKAGE_JSON_REGEX = /^[^/]+\/package\.json$/;
@ -62,8 +65,10 @@ class NgPackagesInstaller {
* contents and acts as an indicator that dependencies have been overridden. * contents and acts as an indicator that dependencies have been overridden.
*/ */
installLocalDependencies() { installLocalDependencies() {
if (this._checkLocalMarker() !== true || this.force) { if (this.force || !this._checkLocalMarker()) {
const pathToPackageConfig = path.resolve(this.projectDir, PACKAGE_JSON); const pathToPackageConfig = path.resolve(this.projectDir, PACKAGE_JSON);
const pathToLockfile = path.resolve(this.projectDir, YARN_LOCK);
const parsedLockfile = this._parseLockfile(pathToLockfile);
const packages = this._getDistPackages(); const packages = this._getDistPackages();
try { try {
@ -88,17 +93,17 @@ class NgPackagesInstaller {
}); });
}); });
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(tmpConfig)); fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(tmpConfig, null, 2));
}); });
const packageConfigFile = fs.readFileSync(pathToPackageConfig); const packageConfigFile = fs.readFileSync(pathToPackageConfig, 'utf8');
const packageConfig = JSON.parse(packageConfigFile); const packageConfig = JSON.parse(packageConfigFile);
const [dependencies, peers] = this._collectDependencies(packageConfig.dependencies || {}, packages); const [dependencies, peers] = this._collectDependencies(packageConfig.dependencies || {}, packages);
const [devDependencies, devPeers] = this._collectDependencies(packageConfig.devDependencies || {}, packages); const [devDependencies, devPeers] = this._collectDependencies(packageConfig.devDependencies || {}, packages);
this._assignPeerDependencies(peers, dependencies, devDependencies); this._assignPeerDependencies(peers, dependencies, devDependencies, parsedLockfile);
this._assignPeerDependencies(devPeers, dependencies, devDependencies); this._assignPeerDependencies(devPeers, dependencies, devDependencies, parsedLockfile);
const localPackageConfig = Object.assign(Object.create(null), packageConfig, { dependencies, devDependencies }); const localPackageConfig = Object.assign(Object.create(null), packageConfig, { dependencies, devDependencies });
localPackageConfig.__angular = { local: true }; localPackageConfig.__angular = { local: true };
@ -107,7 +112,7 @@ class NgPackagesInstaller {
try { try {
this._log(`Writing temporary local ${PACKAGE_JSON} to ${pathToPackageConfig}`); this._log(`Writing temporary local ${PACKAGE_JSON} to ${pathToPackageConfig}`);
fs.writeFileSync(pathToPackageConfig, localPackageConfigJson); fs.writeFileSync(pathToPackageConfig, localPackageConfigJson);
this._installDeps('--no-lockfile', '--check-files'); this._installDeps('--pure-lockfile', '--check-files');
this._setLocalMarker(localPackageConfigJson); this._setLocalMarker(localPackageConfigJson);
} finally { } finally {
this._log(`Restoring original ${PACKAGE_JSON} to ${pathToPackageConfig}`); this._log(`Restoring original ${PACKAGE_JSON} to ${pathToPackageConfig}`);
@ -118,7 +123,7 @@ class NgPackagesInstaller {
this._log(`Restoring original ${PACKAGE_JSON} for local Angular packages.`); this._log(`Restoring original ${PACKAGE_JSON} for local Angular packages.`);
Object.keys(packages).forEach(key => { Object.keys(packages).forEach(key => {
const pkg = packages[key]; const pkg = packages[key];
fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(pkg.config)); fs.writeFileSync(pkg.packageJsonPath, JSON.stringify(pkg.config, null, 2));
}); });
} }
} }
@ -134,15 +139,23 @@ class NgPackagesInstaller {
// Protected helpers // Protected helpers
_assignPeerDependencies(peerDependencies, dependencies, devDependencies) { _assignPeerDependencies(peerDependencies, dependencies, devDependencies, parsedLockfile) {
Object.keys(peerDependencies).forEach(key => { Object.keys(peerDependencies).forEach(key => {
const peerDepRange = peerDependencies[key];
// Ignore peerDependencies whose range is already satisfied by current version in lockfile.
const originalRange = dependencies[key] || devDependencies[key];
const lockfileVersion = originalRange && parsedLockfile[`${key}@${originalRange}`].version;
if (lockfileVersion && semver.satisfies(lockfileVersion, peerDepRange)) return;
// If there is already an equivalent dependency then override it - otherwise assign/override the devDependency // If there is already an equivalent dependency then override it - otherwise assign/override the devDependency
if (dependencies[key]) { if (dependencies[key]) {
this._log(`Overriding dependency with peerDependency: ${key}: ${peerDependencies[key]}`); this._log(`Overriding dependency with peerDependency: ${key}: ${peerDepRange}`);
dependencies[key] = peerDependencies[key]; dependencies[key] = peerDepRange;
} else { } else {
this._log(`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ${key}: ${peerDependencies[key]}`); this._log(`${devDependencies[key] ? 'Overriding' : 'Assigning'} devDependency with peerDependency: ${key}: ${peerDepRange}`);
devDependencies[key] = peerDependencies[key]; devDependencies[key] = peerDepRange;
} }
}); });
} }
@ -166,11 +179,6 @@ class NgPackagesInstaller {
} }
}); });
// FIXME: Temporarily use RxJS from root `node_modules/`.
if (peerDependencies.rxjs) {
peerDependencies.rxjs = `file:${ANGULAR_ROOT_DIR}/node_modules/rxjs`;
}
return [mergedDependencies, peerDependencies]; return [mergedDependencies, peerDependencies];
} }
@ -226,6 +234,20 @@ class NgPackagesInstaller {
} }
} }
/**
* Parse and return a `yarn.lock` file.
*/
_parseLockfile(lockfilePath) {
const lockfileContent = fs.readFileSync(lockfilePath, 'utf8');
const parsed = lockfile.parse(lockfileContent);
if (parsed.type !== 'success') {
throw new Error(`[${NgPackagesInstaller.name}]: Error parsing lockfile '${lockfilePath}' (result type: ${parsed.type}).`);
}
return parsed.object;
}
_printWarning() { _printWarning() {
const relativeScriptPath = path.relative('.', __filename.replace(/\.js$/, '')); const relativeScriptPath = path.relative('.', __filename.replace(/\.js$/, ''));
const absoluteProjectDir = path.resolve(this.projectDir); const absoluteProjectDir = path.resolve(this.projectDir);

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const fs = require('fs-extra'); const fs = require('fs-extra');
const lockfile = require('@yarnpkg/lockfile');
const path = require('canonical-path'); const path = require('canonical-path');
const shelljs = require('shelljs'); const shelljs = require('shelljs');
@ -11,6 +12,7 @@ describe('NgPackagesInstaller', () => {
const absoluteRootDir = path.resolve(rootDir); const absoluteRootDir = path.resolve(rootDir);
const nodeModulesDir = path.resolve(absoluteRootDir, 'node_modules'); const nodeModulesDir = path.resolve(absoluteRootDir, 'node_modules');
const packageJsonPath = path.resolve(absoluteRootDir, 'package.json'); const packageJsonPath = path.resolve(absoluteRootDir, 'package.json');
const yarnLockPath = path.resolve(absoluteRootDir, 'yarn.lock');
const packagesDir = path.resolve(path.resolve(__dirname, '../../../dist/packages-dist')); const packagesDir = path.resolve(path.resolve(__dirname, '../../../dist/packages-dist'));
const toolsDir = path.resolve(path.resolve(__dirname, '../../../dist/tools/@angular')); const toolsDir = path.resolve(path.resolve(__dirname, '../../../dist/tools/@angular'));
let installer; let installer;
@ -52,13 +54,26 @@ describe('NgPackagesInstaller', () => {
beforeEach(() => { beforeEach(() => {
spyOn(installer, '_checkLocalMarker'); spyOn(installer, '_checkLocalMarker');
spyOn(installer, '_installDeps');
spyOn(installer, '_setLocalMarker');
spyOn(installer, '_parseLockfile').and.returnValue({
'rxjs@^6.3.0': {version: '6.3.3'},
'zone.js@^0.8.26': {version: '0.8.27'}
});
// These are the packages that are "found" in the dist directory // These are the packages that are "found" in the dist directory
dummyNgPackages = { dummyNgPackages = {
'@angular/core': { '@angular/core': {
parentDir: packagesDir, parentDir: packagesDir,
packageJsonPath: `${packagesDir}/core/package.json`, packageJsonPath: `${packagesDir}/core/package.json`,
config: { peerDependencies: { 'some-package': '5.0.1' } } config: {
peerDependencies: {
'rxjs': '^6.4.0',
'some-package': '5.0.1',
'zone.js': '~0.8.26'
}
}
}, },
'@angular/common': { '@angular/common': {
parentDir: packagesDir, parentDir: packagesDir,
@ -93,10 +108,12 @@ describe('NgPackagesInstaller', () => {
dummyPackage = { dummyPackage = {
dependencies: { dependencies: {
'@angular/core': '4.4.1', '@angular/core': '4.4.1',
'@angular/common': '4.4.1' '@angular/common': '4.4.1',
rxjs: '^6.3.0'
}, },
devDependencies: { devDependencies: {
'@angular/compiler-cli': '4.4.1' '@angular/compiler-cli': '4.4.1',
'zone.js': '^0.8.26'
} }
}; };
dummyPackageJson = JSON.stringify(dummyPackage); dummyPackageJson = JSON.stringify(dummyPackage);
@ -104,14 +121,23 @@ describe('NgPackagesInstaller', () => {
// This is the package.json that is temporarily written to the "test" folder // This is the package.json that is temporarily written to the "test" folder
// Note that the Angular (dev)dependencies have been modified to use a "file:" path // Note that the Angular (dev)dependencies have been modified to use a "file:" path
// And that the peerDependencies from `dummyNgPackages` have been added as (dev)dependencies. // And that the peerDependencies from `dummyNgPackages` have been updated or added as
// (dev)dependencies (unless the current version in lockfile satisfies semver).
//
// For example, `zone.js@0.8.27` (from lockfile) satisfies `zone.js@~0.8.26` (from
// `@angular/core`), thus `zone.js: ^0.8.26` (from original `package.json`) is retained.
// In contrast, `rxjs@6.3.3` (from lockfile) does not satisfy `rxjs@^6.4.0 (from
// `@angular/core`), thus `rxjs: ^6.3.0` (from original `package.json`) is replaced with
// `rxjs: ^6.4.0` (from `@angular/core`).
expectedModifiedPackage = { expectedModifiedPackage = {
dependencies: { dependencies: {
'@angular/core': `file:${packagesDir}/core`, '@angular/core': `file:${packagesDir}/core`,
'@angular/common': `file:${packagesDir}/common` '@angular/common': `file:${packagesDir}/common`,
'rxjs': '^6.4.0'
}, },
devDependencies: { devDependencies: {
'@angular/compiler-cli': `file:${toolsDir}/compiler-cli`, '@angular/compiler-cli': `file:${toolsDir}/compiler-cli`,
'zone.js': '^0.8.26',
'some-package': '5.0.1', 'some-package': '5.0.1',
typescript: '^2.4.2' typescript: '^2.4.2'
}, },
@ -121,12 +147,20 @@ describe('NgPackagesInstaller', () => {
}); });
describe('when there is a local package marker', () => { describe('when there is a local package marker', () => {
beforeEach(() => installer._checkLocalMarker.and.returnValue(true));
it('should not continue processing', () => { it('should not continue processing', () => {
installer._checkLocalMarker.and.returnValue(true);
installer.installLocalDependencies(); installer.installLocalDependencies();
expect(installer._checkLocalMarker).toHaveBeenCalled(); expect(installer._checkLocalMarker).toHaveBeenCalled();
expect(installer._getDistPackages).not.toHaveBeenCalled(); expect(installer._getDistPackages).not.toHaveBeenCalled();
}); });
it('should continue processing (without checking for local marker) if `force` is true', () => {
installer.force = true;
installer.installLocalDependencies();
expect(installer._checkLocalMarker).not.toHaveBeenCalled();
expect(installer._getDistPackages).toHaveBeenCalled();
});
}); });
describe('when there is no local package marker', () => { describe('when there is no local package marker', () => {
@ -135,14 +169,14 @@ describe('NgPackagesInstaller', () => {
beforeEach(() => { beforeEach(() => {
log = []; log = [];
fs.writeFileSync.and.callFake((filePath, contents) => filePath === packageJsonPath && log.push(`writeFile: ${contents}`)); fs.writeFileSync.and.callFake((filePath, contents) => filePath === packageJsonPath && log.push(`writeFile: ${contents}`));
spyOn(installer, '_installDeps').and.callFake(() => log.push('installDeps:')); installer._installDeps.and.callFake((...args) => log.push(`installDeps: ${args.join(' ')}`));
spyOn(installer, '_setLocalMarker');
installer._checkLocalMarker.and.returnValue(false); installer._checkLocalMarker.and.returnValue(false);
installer.installLocalDependencies(); installer.installLocalDependencies();
}); });
it('should get the dist packages', () => { it('should parse the lockfile and get the dist packages', () => {
expect(installer._checkLocalMarker).toHaveBeenCalled(); expect(installer._checkLocalMarker).toHaveBeenCalled();
expect(installer._parseLockfile).toHaveBeenCalledWith(yarnLockPath);
expect(installer._getDistPackages).toHaveBeenCalled(); expect(installer._getDistPackages).toHaveBeenCalled();
}); });
@ -150,31 +184,32 @@ describe('NgPackagesInstaller', () => {
const pkgJsonFor = pkgName => dummyNgPackages[`@angular/${pkgName}`].packageJsonPath; const pkgJsonFor = pkgName => dummyNgPackages[`@angular/${pkgName}`].packageJsonPath;
const pkgConfigFor = pkgName => copyJsonObj(dummyNgPackages[`@angular/${pkgName}`].config); const pkgConfigFor = pkgName => copyJsonObj(dummyNgPackages[`@angular/${pkgName}`].config);
const overwriteConfigFor = (pkgName, newProps) => Object.assign(pkgConfigFor(pkgName), newProps); const overwriteConfigFor = (pkgName, newProps) => Object.assign(pkgConfigFor(pkgName), newProps);
const stringifyConfig = config => JSON.stringify(config, null, 2);
const allArgs = fs.writeFileSync.calls.allArgs(); const allArgs = fs.writeFileSync.calls.allArgs();
const firstFiveArgs = allArgs.slice(0, 5); const firstFiveArgs = allArgs.slice(0, 5);
const lastFiveArgs = allArgs.slice(-5); const lastFiveArgs = allArgs.slice(-5);
expect(firstFiveArgs).toEqual([ expect(firstFiveArgs).toEqual([
[pkgJsonFor('core'), JSON.stringify(overwriteConfigFor('core', {private: true}))], [pkgJsonFor('core'), stringifyConfig(overwriteConfigFor('core', {private: true}))],
[pkgJsonFor('common'), JSON.stringify(overwriteConfigFor('common', {private: true}))], [pkgJsonFor('common'), stringifyConfig(overwriteConfigFor('common', {private: true}))],
[pkgJsonFor('compiler'), JSON.stringify(overwriteConfigFor('compiler', {private: true}))], [pkgJsonFor('compiler'), stringifyConfig(overwriteConfigFor('compiler', {private: true}))],
[pkgJsonFor('compiler-cli'), JSON.stringify(overwriteConfigFor('compiler-cli', { [pkgJsonFor('compiler-cli'), stringifyConfig(overwriteConfigFor('compiler-cli', {
private: true, private: true,
dependencies: { '@angular/tsc-wrapped': `file:${toolsDir}/tsc-wrapped` } dependencies: { '@angular/tsc-wrapped': `file:${toolsDir}/tsc-wrapped` }
}))], }))],
[pkgJsonFor('tsc-wrapped'), JSON.stringify(overwriteConfigFor('tsc-wrapped', { [pkgJsonFor('tsc-wrapped'), stringifyConfig(overwriteConfigFor('tsc-wrapped', {
private: true, private: true,
devDependencies: { '@angular/common': `file:${packagesDir}/common` } devDependencies: { '@angular/common': `file:${packagesDir}/common` }
}))], }))],
]); ]);
expect(lastFiveArgs).toEqual(['core', 'common', 'compiler', 'compiler-cli', 'tsc-wrapped'] expect(lastFiveArgs).toEqual(['core', 'common', 'compiler', 'compiler-cli', 'tsc-wrapped']
.map(pkgName => [pkgJsonFor(pkgName), JSON.stringify(pkgConfigFor(pkgName))])); .map(pkgName => [pkgJsonFor(pkgName), stringifyConfig(pkgConfigFor(pkgName))]));
}); });
it('should load the package.json', () => { it('should load the package.json', () => {
expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath); expect(fs.readFileSync).toHaveBeenCalledWith(packageJsonPath, 'utf8');
}); });
it('should overwrite package.json with modified config', () => { it('should overwrite package.json with modified config', () => {
@ -188,7 +223,7 @@ describe('NgPackagesInstaller', () => {
it('should overwrite package.json, then install deps, then restore original package.json', () => { it('should overwrite package.json, then install deps, then restore original package.json', () => {
expect(log).toEqual([ expect(log).toEqual([
`writeFile: ${expectedModifiedPackageJson}`, `writeFile: ${expectedModifiedPackageJson}`,
`installDeps:`, `installDeps: --pure-lockfile --check-files`,
`writeFile: ${dummyPackageJson}` `writeFile: ${dummyPackageJson}`
]); ]);
}); });
@ -264,6 +299,42 @@ describe('NgPackagesInstaller', () => {
}); });
}); });
describe('_parseLockfile()', () => {
let originalLockfileParseDescriptor;
beforeEach(() => {
// Workaround for `lockfile.parse()` being non-writable.
let parse = lockfile.parse;
originalLockfileParseDescriptor = Object.getOwnPropertyDescriptor(lockfile, 'parse');
Object.defineProperty(lockfile, 'parse', {
get() { return parse; },
set(newParse) { parse = newParse; },
});
fs.readFileSync.and.returnValue('mock content');
spyOn(lockfile, 'parse').and.returnValue({type: 'success', object: {foo: {version: 'bar'}}});
});
afterEach(() => Object.defineProperty(lockfile, 'parse', originalLockfileParseDescriptor));
it('should parse the specified lockfile', () => {
installer._parseLockfile('/foo/bar/yarn.lock');
expect(fs.readFileSync).toHaveBeenCalledWith('/foo/bar/yarn.lock', 'utf8');
expect(lockfile.parse).toHaveBeenCalledWith('mock content');
});
it('should throw if parsing the lockfile fails', () => {
lockfile.parse.and.returnValue({type: 'not success'});
expect(() => installer._parseLockfile('/foo/bar/yarn.lock')).toThrowError(
'[NgPackagesInstaller]: Error parsing lockfile \'/foo/bar/yarn.lock\' (result type: not success).');
});
it('should return the parsed lockfile content as an object', () => {
const parsed = installer._parseLockfile('/foo/bar/yarn.lock');
expect(parsed).toEqual({foo: {version: 'bar'}});
});
});
describe('_printWarning()', () => { describe('_printWarning()', () => {
it('should mention the message passed in the warning', () => { it('should mention the message passed in the warning', () => {
installer._printWarning(); installer._printWarning();

View File

@ -208,7 +208,7 @@
{% set nonInternalMembers = ancestor.doc[collectionName] | filterByPropertyValue('internal', undefined) %} {% set nonInternalMembers = ancestor.doc[collectionName] | filterByPropertyValue('internal', undefined) %}
{% if nonInternalMembers.length -%} {% if nonInternalMembers.length -%}
<section class="inherited-members-list"> <section class="inherited-members-list">
<h3>Inherited from <code>{$ ancestor.doc.id $}</code></h3> <h3>Inherited from <code><a class="code-anchor" href="{$ ancestor.doc.path $}">{$ ancestor.doc.name $}</a></code></h3>
<ul> <ul>
{% for member in nonInternalMembers %} {% for member in nonInternalMembers %}
<li> <li>

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/language-service": "file:../../dist/packages-dist/language-service", "@angular/language-service": "file:../../dist/packages-dist/language-service",
"@types/jasmine": "~2.8.3", "@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2", "@types/jasminewd2": "~2.0.4",
"@types/node": "~6.0.60", "@types/node": "~6.0.60",
"codelyzer": "^4.3.0", "codelyzer": "^4.3.0",
"jasmine-core": "~2.8.0", "jasmine-core": "~2.8.0",

View File

@ -206,9 +206,9 @@
version "2.8.6" version "2.8.6"
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e"
"@types/jasminewd2@~2.0.2": "@types/jasminewd2@~2.0.4":
version "2.0.3" version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.3.tgz#0d2886b0cbdae4c0eeba55e30792f584bf040a95" resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.4.tgz#12422ee719f372d30c3cc7d99cc72dadba6ace01"
dependencies: dependencies:
"@types/jasmine" "*" "@types/jasmine" "*"

1
modules/types.d.ts vendored
View File

@ -13,6 +13,5 @@
/// <reference types="jasminewd2" /> /// <reference types="jasminewd2" />
/// <reference types="node" /> /// <reference types="node" />
/// <reference types="zone.js" /> /// <reference types="zone.js" />
/// <reference path="../tools/types-ext/jasminewd2.d.ts" />
/// <reference path="./es6-subset.d.ts" /> /// <reference path="./es6-subset.d.ts" />
/// <reference path="./system.d.ts" /> /// <reference path="./system.d.ts" />

View File

@ -43,12 +43,12 @@
"@types/angular": "^1.6.47", "@types/angular": "^1.6.47",
"@types/base64-js": "1.2.5", "@types/base64-js": "1.2.5",
"@types/chai": "^4.1.2", "@types/chai": "^4.1.2",
"@types/chokidar": "1.7.3", "@types/chokidar": "^1.7.5",
"@types/diff": "^3.2.2", "@types/diff": "^3.2.2",
"@types/fs-extra": "4.0.2", "@types/fs-extra": "4.0.2",
"@types/hammerjs": "2.0.35", "@types/hammerjs": "2.0.35",
"@types/jasmine": "^2.8.8", "@types/jasmine": "^2.8.8",
"@types/jasminewd2": "^2.0.3", "@types/jasminewd2": "^2.0.4",
"@types/minimist": "^1.2.0", "@types/minimist": "^1.2.0",
"@types/node": "6.0.88", "@types/node": "6.0.88",
"@types/selenium-webdriver": "3.0.7", "@types/selenium-webdriver": "3.0.7",
@ -66,7 +66,7 @@
"bower": "1.8.2", "bower": "1.8.2",
"browserstacktunnel-wrapper": "2.0.1", "browserstacktunnel-wrapper": "2.0.1",
"canonical-path": "0.0.2", "canonical-path": "0.0.2",
"chokidar": "1.7.0", "chokidar": "^2.1.1",
"clang-format": "1.0.41", "clang-format": "1.0.41",
"cldr": "4.10.0", "cldr": "4.10.0",
"cldr-data-downloader": "0.3.2", "cldr-data-downloader": "0.3.2",

View File

@ -12,7 +12,7 @@
"reflect-metadata": "^0.1.2", "reflect-metadata": "^0.1.2",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"tsickle": "^0.32.1", "tsickle": "^0.32.1",
"chokidar": "^1.4.2" "chokidar": "^2.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": ">=2.7.2 <2.10", "typescript": ">=2.7.2 <2.10",

View File

@ -16,7 +16,6 @@
"include": [ "include": [
"../../node_modules/@types/jasminewd2/index.d.ts", "../../node_modules/@types/jasminewd2/index.d.ts",
"../../tools/types-ext/jasminewd2.d.ts",
"../types.d.ts", "../types.d.ts",
"**/*.ts" "**/*.ts"
] ]

View File

@ -10,7 +10,6 @@
"include": [ "include": [
"../../node_modules/@types/jasminewd2/index.d.ts", "../../node_modules/@types/jasminewd2/index.d.ts",
"../../tools/types-ext/jasminewd2.d.ts",
"../types.d.ts", "../types.d.ts",
"**/e2e_test/*" "**/e2e_test/*"
], ],

View File

@ -214,24 +214,29 @@ export interface INgModelController {
$name: string; $name: string;
} }
function noNg() { function noNg(): never {
throw new Error('AngularJS v1.x is not loaded!'); throw new Error('AngularJS v1.x is not loaded!');
} }
const noNgElement: typeof angular.element = (() => noNg()) as any;
noNgElement.cleanData = noNg;
let angular: { let angular: {
bootstrap: (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) => bootstrap: (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
IInjectorService, IInjectorService,
module: (prefix: string, dependencies?: string[]) => IModule, module: (prefix: string, dependencies?: string[]) => IModule,
element: (e: string | Element | Document | IAugmentedJQuery) => IAugmentedJQuery, element: {
(e: string | Element | Document | IAugmentedJQuery): IAugmentedJQuery;
cleanData: (nodes: Node[] | NodeList) => void;
},
version: {major: number}, version: {major: number},
resumeBootstrap: () => void, resumeBootstrap: () => void,
getTestability: (e: Element) => ITestabilityService getTestability: (e: Element) => ITestabilityService
} = <any>{ } = {
bootstrap: noNg, bootstrap: noNg,
module: noNg, module: noNg,
element: noNg, element: noNgElement,
version: undefined, version: undefined as any,
resumeBootstrap: noNg, resumeBootstrap: noNg,
getTestability: noNg getTestability: noNg
}; };
@ -281,7 +286,8 @@ export const bootstrap: typeof angular.bootstrap = (e, modules, config?) =>
export const module: typeof angular.module = (prefix, dependencies?) => export const module: typeof angular.module = (prefix, dependencies?) =>
angular.module(prefix, dependencies); angular.module(prefix, dependencies);
export const element: typeof angular.element = e => angular.element(e); export const element: typeof angular.element = (e => angular.element(e)) as typeof angular.element;
element.cleanData = nodes => angular.element.cleanData(nodes);
export const resumeBootstrap: typeof angular.resumeBootstrap = () => angular.resumeBootstrap(); export const resumeBootstrap: typeof angular.resumeBootstrap = () => angular.resumeBootstrap();

View File

@ -124,7 +124,15 @@ export class UpgradeHelper {
controllerInstance.$onDestroy(); controllerInstance.$onDestroy();
} }
$scope.$destroy(); $scope.$destroy();
this.$element.triggerHandler !('$destroy');
// Clean the jQuery/jqLite data on the component+child elements.
// Equivelent to how jQuery/jqLite invoke `cleanData` on an Element (this.element)
// https://github.com/jquery/jquery/blob/e743cbd28553267f955f71ea7248377915613fd9/src/manipulation.js#L223
// https://github.com/angular/angular.js/blob/26ddc5f830f902a3d22f4b2aab70d86d4d688c82/src/jqLite.js#L306-L312
// `cleanData` will invoke the AngularJS `$destroy` DOM event
// https://github.com/angular/angular.js/blob/26ddc5f830f902a3d22f4b2aab70d86d4d688c82/src/Angular.js#L1911-L1924
angular.element.cleanData([this.element]);
angular.element.cleanData(this.element.querySelectorAll('*'));
} }
prepareTransclusion(): angular.ILinkFn|undefined { prepareTransclusion(): angular.ILinkFn|undefined {

View File

@ -12,6 +12,7 @@ import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as angular from '@angular/upgrade/src/common/angular1'; import * as angular from '@angular/upgrade/src/common/angular1';
import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter'; import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter';
import {$apply, $digest, html, multiTrim, withEachNg1Version} from './test_helpers'; import {$apply, $digest, html, multiTrim, withEachNg1Version} from './test_helpers';
declare global { declare global {
@ -2177,9 +2178,10 @@ withEachNg1Version(() => {
}); });
})); }));
it('should emit `$destroy` on `$element`', fakeAsync(() => { it('should emit `$destroy` on `$element` and descendants', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const elementDestroyListener = jasmine.createSpy('elementDestroyListener'); const elementDestroyListener = jasmine.createSpy('elementDestroyListener');
const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener');
let ng2ComponentInstance: Ng2Component; let ng2ComponentInstance: Ng2Component;
@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'}) @Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
@ -2194,9 +2196,13 @@ withEachNg1Version(() => {
// Mocking animations (via `ngAnimateMock`) avoids the issue. // Mocking animations (via `ngAnimateMock`) avoids the issue.
angular.module('ng1', ['ngAnimateMock']) angular.module('ng1', ['ngAnimateMock'])
.component('ng1', { .component('ng1', {
controller: function($element: angular.IAugmentedJQuery) { controller: class {
$element.on !('$destroy', elementDestroyListener); constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
this.$element.on !('$destroy', elementDestroyListener);
this.$element.contents !().on !('$destroy', descendantDestroyListener);
}
}, },
template: '<div></div>'
}) })
.directive('ng2', adapter.downgradeNg2Component(Ng2Component)); .directive('ng2', adapter.downgradeNg2Component(Ng2Component));
@ -2210,14 +2216,150 @@ withEachNg1Version(() => {
const element = html('<ng2></ng2>'); const element = html('<ng2></ng2>');
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any; const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();
expect(elementDestroyListener).not.toHaveBeenCalled(); expect(elementDestroyListener).not.toHaveBeenCalled();
expect(descendantDestroyListener).not.toHaveBeenCalled();
ng2ComponentInstance.ng2Destroy = true; ng2ComponentInstance.ng2Destroy = true;
tick(); tick();
$rootScope.$digest(); $rootScope.$digest();
expect(elementDestroyListener).toHaveBeenCalledTimes(1); expect(elementDestroyListener).toHaveBeenCalledTimes(1);
expect(descendantDestroyListener).toHaveBeenCalledTimes(1);
});
}));
it('should clear data on `$element` and descendants', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
let ng1ComponentElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
this.$element.data !('test', 1);
this.$element.contents !().data !('test', 2);
ng1ComponentElement = this.$element;
}
},
template: '<div></div>'
};
// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}
// Define `ng1Module`
angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));
// Define `Ng2Module`
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(`<ng2-a></ng2-a>`);
adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();
expect(ng1ComponentElement.data !('test')).toBe(1);
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
ng2ComponentAInstance.destroyIt = true;
tick();
$rootScope.$digest();
expect(ng1ComponentElement.data !('test')).toBeUndefined();
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
});
}));
it('should clear dom listeners on `$element` and descendants`', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const elementClickListener = jasmine.createSpy('elementClickListener');
const descendantClickListener = jasmine.createSpy('descendantClickListener');
let ng1DescendantElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
ng1DescendantElement = this.$element.contents !();
this.$element.on !('click', elementClickListener);
ng1DescendantElement.on !('click', descendantClickListener);
}
},
template: '<div></div>'
};
// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}
// Define `ng1Module`
angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));
// Define `Ng2Module`
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(`<ng2-a></ng2-a>`);
adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
ng2ComponentAInstance.destroyIt = true;
tick();
$rootScope.$digest();
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
}); });
})); }));
}); });

View File

@ -3633,8 +3633,9 @@ withEachNg1Version(() => {
}); });
})); }));
it('should emit `$destroy` on `$element`', async(() => { it('should emit `$destroy` on `$element` and descendants', async(() => {
const elementDestroyListener = jasmine.createSpy('elementDestroyListener'); const elementDestroyListener = jasmine.createSpy('elementDestroyListener');
const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener');
let ng2ComponentAInstance: Ng2ComponentA; let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component` // Define `ng1Component`
@ -3642,8 +3643,10 @@ withEachNg1Version(() => {
controller: class { controller: class {
constructor($element: angular.IAugmentedJQuery) { constructor($element: angular.IAugmentedJQuery) {
$element.on !('$destroy', elementDestroyListener); $element.on !('$destroy', elementDestroyListener);
$element.contents !().on !('$destroy', descendantDestroyListener);
} }
} },
template: '<div></div>'
}; };
// Define `Ng1ComponentFacade` // Define `Ng1ComponentFacade`
@ -3686,11 +3689,151 @@ withEachNg1Version(() => {
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => { bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(elementDestroyListener).not.toHaveBeenCalled(); expect(elementDestroyListener).not.toHaveBeenCalled();
expect(descendantDestroyListener).not.toHaveBeenCalled();
ng2ComponentAInstance.destroyIt = true; ng2ComponentAInstance.destroyIt = true;
$digest(adapter); $digest(adapter);
expect(elementDestroyListener).toHaveBeenCalledTimes(1); expect(elementDestroyListener).toHaveBeenCalledTimes(1);
expect(descendantDestroyListener).toHaveBeenCalledTimes(1);
});
}));
it('should clear data on `$element` and descendants`', async(() => {
let ng1ComponentElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($element: angular.IAugmentedJQuery) {
$element.data !('test', 1);
$element.contents !().data !('test', 2);
ng1ComponentElement = $element;
}
},
template: '<div></div>'
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(`<ng2-a></ng2-a>`);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(ng1ComponentElement.data !('test')).toBe(1);
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
expect(ng1ComponentElement.data !('test')).toBeUndefined();
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
});
}));
it('should clear dom listeners on `$element` and descendants`', async(() => {
const elementClickListener = jasmine.createSpy('elementClickListener');
const descendantClickListener = jasmine.createSpy('descendantClickListener');
let ng1DescendantElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($element: angular.IAugmentedJQuery) {
ng1DescendantElement = $element.contents !();
$element.on !('click', elementClickListener);
ng1DescendantElement.on !('click', descendantClickListener);
}
},
template: '<div></div>'
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(`<ng2-a></ng2-a>`);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
}); });
})); }));

View File

@ -1,30 +0,0 @@
/**
* @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
*/
/// <reference types="jasminewd2" />
/**
* Extended typings for `jasminewd2`.
*
* The currently used `jasminewd2` version (v2.1.0), supports passing a `done` callback to a spec,
* but the latest typings on [DefinitelyTyped][1] do not reflect that.
* Overwrite the relevant function signatures to add a `done` callback.
*
* [1]:
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/566e0394859fdc1dc893658ccec6b06372d56a91/types/jasminewd2/index.d.ts#L9-L15
*/
declare function it(
expectation: string, assertion?: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function fit(
expectation: string, assertion?: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function xit(
expectation: string, assertion?: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function beforeEach(action: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function afterEach(action: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function beforeAll(action: (done: DoneFn) => Promise<void>, timeout?: number): void;
declare function afterAll(action: (done: DoneFn) => Promise<void>, timeout?: number): void;

1294
yarn.lock

File diff suppressed because it is too large Load Diff