mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7677963bd9 | |||
| 03fcb36cfd | |||
| 2f0856070b | |||
| 7b4d3c036d | |||
| c98c6a8452 | |||
| 630193543f | |||
| cff84d00cf | |||
| ba7a757cc5 | |||
| ecb309152f | |||
| 2fd81940d3 | |||
| 0950ef6a49 | |||
| e69ba26c55 | |||
| 61878883ed | |||
| 8ed653b93f | |||
| b76b176733 | |||
| f8a932afbb | |||
| d784986cd3 | |||
| 31a71a0f89 | |||
| 3d8efece03 | |||
| 345817f427 | |||
| 24b6eea0a9 | |||
| d457ffc408 | |||
| a378267117 | |||
| 11f8f8d8e4 | |||
| 4811850607 | |||
| 5d6440947a | |||
| 25e220a23a | |||
| c95766ed12 | |||
| 43e9301783 | |||
| e63fb8031a | |||
| 25bed17c5f | |||
| 117af7d8b5 | |||
| ee7a9248a8 | |||
| e1607cdbd4 | |||
| b06a1d0349 | |||
| a814ed1382 | |||
| 1a7444f99f | |||
| bf4ad38117 | |||
| 86d4016d23 | |||
| 440b51b7af |
@@ -134,9 +134,10 @@ build:remote --project_id=internal-200822
|
||||
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
||||
build:remote --remote_executor=remotebuildexecution.googleapis.com
|
||||
|
||||
# Ensure that tags like "no-remote-exec" get propagated to actions created by rules.
|
||||
# Ensure that tags like "no-remote-exec" get propagated to actions created by rules,
|
||||
# even if the rule implementation does not explicitly pass them to the execution requirements.
|
||||
# https://bazel.build/reference/command-line-reference#flag--experimental_allow_tags_propagation
|
||||
build --experimental_allow_tags_propagation
|
||||
common --experimental_allow_tags_propagation
|
||||
|
||||
# Disable network access in the sandbox by default. To enable network access
|
||||
# for a particular target, use:
|
||||
|
||||
+12
-10
@@ -74,7 +74,7 @@ var_9: &only_release_branches
|
||||
# https://circleci.com/developer/orbs.
|
||||
orbs:
|
||||
node: circleci/node@5.0.1
|
||||
devinfra: angular/dev-infra@1.0.6
|
||||
devinfra: angular/dev-infra@1.0.8
|
||||
|
||||
# Executor Definitions
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
|
||||
@@ -595,22 +595,16 @@ jobs:
|
||||
executor: windows-executor
|
||||
steps:
|
||||
- checkout
|
||||
- save_month_to_file
|
||||
- capture_patches_for_cache_key
|
||||
- init_environment
|
||||
|
||||
# Setup the proper NodeJS version. The image comes pre-installed with `nvm-windows`.
|
||||
# Note: This happens immediately after checkout because environment initialization logic
|
||||
# might rely on Node and shouldn't use the older image-provided NodeJS version.
|
||||
- run:
|
||||
name: Install NodeJS version from `.nvmrc`.
|
||||
command: |
|
||||
nvm on
|
||||
nvm install $(cat .nvmrc)
|
||||
nvm use $(cat .nvmrc)
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *cache_key_win
|
||||
- *cache_key_win_fallback
|
||||
|
||||
- run:
|
||||
name: Debug information for flakiness of Windows job
|
||||
command: |
|
||||
@@ -621,6 +615,14 @@ jobs:
|
||||
echo "Path: $PATH"
|
||||
yarn node -v
|
||||
|
||||
- save_month_to_file
|
||||
- capture_patches_for_cache_key
|
||||
- init_environment
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *cache_key_win
|
||||
- *cache_key_win_fallback
|
||||
|
||||
# Install project dependencies, and install Bazelisk globally. This is necessary as
|
||||
# Windows might error when `bazel` is invoked from the project node modules. The Bazel
|
||||
# invocation might modify the symlinked project `node_modules` again, causing failures.
|
||||
|
||||
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812 # v3
|
||||
- uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3
|
||||
with:
|
||||
path: |
|
||||
./node_modules/
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
- uses: ./.github/actions/yarn-install
|
||||
|
||||
- uses: angular/dev-infra/github-actions/setup-bazel-remote-exec@accc3c96df0d40ff639070132acb0699a58bfcf8
|
||||
- uses: angular/dev-infra/github-actions/setup-bazel-remote-exec@82bf866dd936abf5e6826a282d8da6e720dd1aa2
|
||||
with:
|
||||
bazelrc: ./.bazelrc.user
|
||||
|
||||
|
||||
@@ -350,6 +350,7 @@ groups:
|
||||
'aio/content/guide/singleton-services.md',
|
||||
'aio/content/guide/sharing-ngmodules.md',
|
||||
'aio/content/guide/standalone-components.md',
|
||||
'aio/content/guide/standalone-migration.md',
|
||||
'aio/content/guide/structural-directives.md',
|
||||
'aio/content/examples/structural-directives/**/{*,.*}',
|
||||
'aio/content/guide/svg-in-templates.md',
|
||||
|
||||
+58
-120
@@ -1,16 +1,14 @@
|
||||
<a name="15.1.5"></a>
|
||||
# 15.1.5 (2023-02-15)
|
||||
### forms
|
||||
<a name="15.2.0"></a>
|
||||
# 15.2.0 (2023-02-22)
|
||||
## Deprecations
|
||||
###
|
||||
- Class and `InjectionToken` guards and resolvers are
|
||||
deprecated. Instead, write guards as plain JavaScript functions and
|
||||
inject dependencies with `inject` from `@angular/core`.
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5f2a3edcf2](https://github.com/angular/angular/commit/5f2a3edcf27cfe6ffc6ae4d67ca2b47953f26336) | fix | Make radio buttons respect `[attr.disabled]` ([#48864](https://github.com/angular/angular/pull/48864)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler, PaloMiklo and Paul Gschwendtner
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.2.0-next.4"></a>
|
||||
# 15.2.0-next.4 (2023-02-08)
|
||||
| [926c35f4ac](https://github.com/angular/angular/commit/926c35f4ac70f5e4d142e545d6d056dd67aac97b) | docs | Deprecate class and InjectionToken and resolvers ([#47924](https://github.com/angular/angular/pull/47924)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
@@ -19,27 +17,75 @@ AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler,
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0cf11167f1](https://github.com/angular/angular/commit/0cf11167f13108992ec781e88ab2a7d1fc7f5a0d) | fix | incorrectly detecting forward refs when symbol already exists in file ([#48988](https://github.com/angular/angular/pull/48988)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a154db8a81](https://github.com/angular/angular/commit/a154db8a81cbdfed8c3d0db1e2a5bf43aa3e0bbf) | feat | add ng generate schematic to convert declarations to standalone ([#48790](https://github.com/angular/angular/pull/48790)) |
|
||||
| [345e737daa](https://github.com/angular/angular/commit/345e737daa7b9f635a4c2923358e5e765f716434) | feat | add ng generate schematic to convert to standalone bootstrapping APIs ([#48848](https://github.com/angular/angular/pull/48848)) |
|
||||
| [e7318fc758](https://github.com/angular/angular/commit/e7318fc758d9e64d1a7f60a2c7071a769b73e7d8) | feat | add ng generate schematic to remove unnecessary modules ([#48832](https://github.com/angular/angular/pull/48832)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4ae384fd61](https://github.com/angular/angular/commit/4ae384fd619a13eaadf737d08a97f07e1f6b273c) | feat | Allow auto-imports of a pipe via quick fix when its selector is used, both directly and via reexports. ([#48354](https://github.com/angular/angular/pull/48354)) |
|
||||
| [141333411e](https://github.com/angular/angular/commit/141333411e67769d752c7162e4cb03376022f5e1) | feat | Introduce a new NgModuleIndex, and use it to suggest re-exports. ([#48354](https://github.com/angular/angular/pull/48354)) |
|
||||
| [d0145033bd](https://github.com/angular/angular/commit/d0145033bd11eccd16fa8b61ba9170037d0c62b3) | fix | generate forwardRef for same file imports ([#48898](https://github.com/angular/angular/pull/48898)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [2796230e95](https://github.com/angular/angular/commit/2796230e953eb8c29d6227a1a3858f5f08a8f200) | fix | add `enum` in `mode` option in `standalone` schema ([#48851](https://github.com/angular/angular/pull/48851)) |
|
||||
| [816e76a578](https://github.com/angular/angular/commit/816e76a5789b041fee78ddd278c0e0d19b9a617a) | fix | automatically prune root module after bootstrap step ([#49030](https://github.com/angular/angular/pull/49030)) |
|
||||
| [bdbf21d04b](https://github.com/angular/angular/commit/bdbf21d04ba74a6f73469242076d6ce697c57edf) | fix | avoid generating imports with forward slashes ([#48993](https://github.com/angular/angular/pull/48993)) |
|
||||
| [32cf4e5cb9](https://github.com/angular/angular/commit/32cf4e5cb989f365296d519dddf72fb38ca47c40) | fix | avoid internal modules when generating imports ([#48958](https://github.com/angular/angular/pull/48958)) |
|
||||
| [521ccfbe6c](https://github.com/angular/angular/commit/521ccfbe6ce9af1a7ddd6ab5e70151b7198f82ef) | fix | avoid interrupting the migration if language service lookup fails ([#49010](https://github.com/angular/angular/pull/49010)) |
|
||||
| [a40cd47aa7](https://github.com/angular/angular/commit/a40cd47aa7ebccfbeeb26e397e03f1372aa10a55) | fix | avoid modifying testing modules without declarations ([#48921](https://github.com/angular/angular/pull/48921)) |
|
||||
| [1afa6ed322](https://github.com/angular/angular/commit/1afa6ed3227e784e3fe2b4b31443961589cb6332) | fix | don't add ModuleWithProviders to standalone test components ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [c98c6a8452](https://github.com/angular/angular/commit/c98c6a845286b9b89daf275a9c4a2bdbc7ad77a7) | fix | don't copy animations modules into the imports of test components ([#49147](https://github.com/angular/angular/pull/49147)) |
|
||||
| [8389557848](https://github.com/angular/angular/commit/83895578488bd35c7e47609f092907eb0f53f435) | fix | don't copy unmigrated declarations into imports array ([#48882](https://github.com/angular/angular/pull/48882)) |
|
||||
| [f82bdc4b01](https://github.com/angular/angular/commit/f82bdc4b01f93a7103870449d37da61cc4c4f179) | fix | don't delete classes that may provide dependencies transitively ([#48866](https://github.com/angular/angular/pull/48866)) |
|
||||
| [759db12e0b](https://github.com/angular/angular/commit/759db12e0b618fcb51f4cb141adeb49bfa495a60) | fix | duplicated comments on migrated classes ([#48966](https://github.com/angular/angular/pull/48966)) |
|
||||
| [ba38178d19](https://github.com/angular/angular/commit/ba38178d1918d413f9c2260c40eb6542eadfddba) | fix | generate forwardRef for same file imports ([#48898](https://github.com/angular/angular/pull/48898)) |
|
||||
| [03fcb36cfd](https://github.com/angular/angular/commit/03fcb36cfd36731028bf288f156e16cb8ac4c758) | fix | migrate HttpClientModule to provideHttpClient() ([#48949](https://github.com/angular/angular/pull/48949)) |
|
||||
| [2de6dae16d](https://github.com/angular/angular/commit/2de6dae16d4b0b83f0517a3033cda44ba44154ed) | fix | migrate RouterModule.forRoot with a config object to use features ([#48935](https://github.com/angular/angular/pull/48935)) |
|
||||
| [770191cf1f](https://github.com/angular/angular/commit/770191cf1f1254546625dfa7a882b716c3f0aab3) | fix | migrate tests when switching to standalone bootstrap API ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [c7926b5773](https://github.com/angular/angular/commit/c7926b57730c23f765a00d3dd9f92079c95e87e0) | fix | move standalone migrations into imports ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [65c74ed93e](https://github.com/angular/angular/commit/65c74ed93e04cb560c27838d440c6aa7a9859a4e) | fix | normalize paths to posix ([#48850](https://github.com/angular/angular/pull/48850)) |
|
||||
| [6377487b1a](https://github.com/angular/angular/commit/6377487b1ab7679cef9a44f88440fe5e8eb97480) | fix | only exclude bootstrapped declarations from initial standalone migration ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [e9e4449a43](https://github.com/angular/angular/commit/e9e4449a43430e026e61b0f05ebd32dd830fa916) | fix | preserve tsconfig in standalone migration ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [ffad1b49d9](https://github.com/angular/angular/commit/ffad1b49d95ab90637e7184f92cb5136d490d865) | fix | reduce number of files that need to be checked ([#48987](https://github.com/angular/angular/pull/48987)) |
|
||||
| [ba7a757cc5](https://github.com/angular/angular/commit/ba7a757cc5a2f3f942adcbabdcd5b7aef33ea493) | fix | return correct alias when conflicting import exists ([#49139](https://github.com/angular/angular/pull/49139)) |
|
||||
| [49a7c9f94a](https://github.com/angular/angular/commit/49a7c9f94ae8f89907da8b3620242e62f87ec5a4) | fix | standalone migration incorrectly throwing path error for multi app projects ([#48958](https://github.com/angular/angular/pull/48958)) |
|
||||
| [584976e6c8](https://github.com/angular/angular/commit/584976e6c8a783d40578ab191132673300394a52) | fix | support --defaults in standalone migration ([#48921](https://github.com/angular/angular/pull/48921)) |
|
||||
| [03f47ac901](https://github.com/angular/angular/commit/03f47ac9019eddbcb373b50c41bc6f523293ece1) | fix | use consistent quotes in generated imports ([#48876](https://github.com/angular/angular/pull/48876)) |
|
||||
| [ebae506d89](https://github.com/angular/angular/commit/ebae506d894a90c38e0f2dd1e948acabdb0fdf2e) | fix | use import remapper in root component ([#49046](https://github.com/angular/angular/pull/49046)) |
|
||||
| [40c976c909](https://github.com/angular/angular/commit/40c976c90975878852a87b7722076eb78944098b) | fix | use NgForOf instead of NgFor ([#49022](https://github.com/angular/angular/pull/49022)) |
|
||||
| [4ac25b2aff](https://github.com/angular/angular/commit/4ac25b2affab4f959ad8c111f1e429a05b435422) | perf | avoid re-traversing nodes when resolving bootstrap call dependencies ([#49010](https://github.com/angular/angular/pull/49010)) |
|
||||
| [26cb7ab2e6](https://github.com/angular/angular/commit/26cb7ab2e6ac9b61904361a8a544467b69eef3f3) | perf | speed up language service lookups ([#49010](https://github.com/angular/angular/pull/49010)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [bf4ad38117](https://github.com/angular/angular/commit/bf4ad3811762d9ba43d18c3360d014a9ceb06b4d) | fix | remove styles from DOM of destroyed components ([#48298](https://github.com/angular/angular/pull/48298)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [25e220a23a](https://github.com/angular/angular/commit/25e220a23ab90520efc65f05cd9b7a22db582b87) | fix | avoid duplicate TransferState info after renderApplication call ([#49094](https://github.com/angular/angular/pull/49094)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [31b94c762f](https://github.com/angular/angular/commit/31b94c762fc91ab6cabe08ea6812780fdcf92a64) | feat | Add a withNavigationErrorHandler feature to provideRouter ([#48551](https://github.com/angular/angular/pull/48551)) |
|
||||
| [dedac8d3f7](https://github.com/angular/angular/commit/dedac8d3f73ebf4f05b773454e2a22ab5fa4bf7c) | feat | Add test helper for trigger navigations in tests ([#48552](https://github.com/angular/angular/pull/48552)) |
|
||||
## Special Thanks
|
||||
Alex Castle, Andrew Scott, Dylan Hunn, Jessica Janiuk, Kian Yang Lee, Kristiyan Kostadinov, Matthieu Riegler, Pawel Kozlowski, Redouane Bekkouche, Simona Cotin and Walid Bouguima
|
||||
Alan Agius, Alex Castle, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Dylan Hunn, Ikko Eltociear Ashimine, Ilyass, Jessica Janiuk, Joey Perrott, John Manners, Kalbarczyk, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Pawel Kozlowski, Virginia Dooley, Walid Bouguima, cexbrayat and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.1.5"></a>
|
||||
# 15.1.5 (2023-02-15)
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5f2a3edcf2](https://github.com/angular/angular/commit/5f2a3edcf27cfe6ffc6ae4d67ca2b47953f26336) | fix | Make radio buttons respect `[attr.disabled]` ([#48864](https://github.com/angular/angular/pull/48864)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alvaro Junqueira, Dylan Hunn, Joey Perrott, Matthieu Riegler, PaloMiklo and Paul Gschwendtner
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
@@ -82,73 +128,6 @@ Alan Agius, Besim Gürbüz, Brecht Billiet, Dario Piotrowicz, Dylan Hunn, Iván
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.2.0-next.3"></a>
|
||||
# 15.2.0-next.3 (2023-02-02)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b4187548ca](https://github.com/angular/angular/commit/b4187548cae42f25624bf7ab7ad0cca121242de7) | fix | fix non-animatable warnings for easing ([#48583](https://github.com/angular/angular/pull/48583)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a055196c55](https://github.com/angular/angular/commit/a055196c55c57de281ff7167cd4dfb3df783c337) | fix | warn if using ngSrcset without a configured image loader ([#48804](https://github.com/angular/angular/pull/48804)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [06e161f2dd](https://github.com/angular/angular/commit/06e161f2dd78b42ad1938578f4d730a41404a24f) | fix | incorrect code when non-null assertion is used after a safe access ([#48801](https://github.com/angular/angular/pull/48801)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [345e737daa](https://github.com/angular/angular/commit/345e737daa7b9f635a4c2923358e5e765f716434) | feat | add ng generate schematic to convert to standalone bootstrapping APIs ([#48848](https://github.com/angular/angular/pull/48848)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [2796230e95](https://github.com/angular/angular/commit/2796230e953eb8c29d6227a1a3858f5f08a8f200) | fix | add `enum` in `mode` option in `standalone` schema ([#48851](https://github.com/angular/angular/pull/48851)) |
|
||||
| [a40cd47aa7](https://github.com/angular/angular/commit/a40cd47aa7ebccfbeeb26e397e03f1372aa10a55) | fix | avoid modifying testing modules without declarations ([#48921](https://github.com/angular/angular/pull/48921)) |
|
||||
| [8389557848](https://github.com/angular/angular/commit/83895578488bd35c7e47609f092907eb0f53f435) | fix | don't copy unmigrated declarations into imports array ([#48882](https://github.com/angular/angular/pull/48882)) |
|
||||
| [f82bdc4b01](https://github.com/angular/angular/commit/f82bdc4b01f93a7103870449d37da61cc4c4f179) | fix | don't delete classes that may provide dependencies transitively ([#48866](https://github.com/angular/angular/pull/48866)) |
|
||||
| [04e0ac3d7c](https://github.com/angular/angular/commit/04e0ac3d7ca89bf38d4d2a38b7ca120d9b7b96ae) | fix | migration host incorrectly reading empty files ([#48849](https://github.com/angular/angular/pull/48849)) |
|
||||
| [65c74ed93e](https://github.com/angular/angular/commit/65c74ed93e04cb560c27838d440c6aa7a9859a4e) | fix | normalize paths to posix ([#48850](https://github.com/angular/angular/pull/48850)) |
|
||||
| [584976e6c8](https://github.com/angular/angular/commit/584976e6c8a783d40578ab191132673300394a52) | fix | support --defaults in standalone migration ([#48921](https://github.com/angular/angular/pull/48921)) |
|
||||
| [03f47ac901](https://github.com/angular/angular/commit/03f47ac9019eddbcb373b50c41bc6f523293ece1) | fix | use consistent quotes in generated imports ([#48876](https://github.com/angular/angular/pull/48876)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [2fc5b70fce](https://github.com/angular/angular/commit/2fc5b70fcedb8ac35b825b245c0ae394dc125244) | fix | insert transfer state `script` before other `script` tags ([#48868](https://github.com/angular/angular/pull/48868)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [dedac8d3f7](https://github.com/angular/angular/commit/dedac8d3f73ebf4f05b773454e2a22ab5fa4bf7c) | feat | Add test helper for trigger navigations in tests ([#48552](https://github.com/angular/angular/pull/48552)) |
|
||||
| [16ef770db8](https://github.com/angular/angular/commit/16ef770db803ce4037a90c72477da412642dfb33) | fix | Handle routerLink directive on svg anchors. ([#48857](https://github.com/angular/angular/pull/48857)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Andrew Scott, Besim Gürbüz, Brecht Billiet, Dario Piotrowicz, Dylan Hunn, Iván Navarro, Kristiyan Kostadinov, Matthieu Riegler, Onkar Ruikar, Payam Valadkhan, Santosh Yadav, Virginia Dooley and Walid Bouguima
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.2.0-next.2"></a>
|
||||
# 15.2.0-next.2 (2023-01-25)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [bc8cfa2552](https://github.com/angular/angular/commit/bc8cfa25520c3b8b0a506c69a269e42a63f097d6) | fix | handle css selectors with space after an escaped character. ([#48558](https://github.com/angular/angular/pull/48558)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4da1f2948c](https://github.com/angular/angular/commit/4da1f2948cc67b68527a91579ee14bb1a22b0c58) | fix | resolve deprecation warning ([#48812](https://github.com/angular/angular/pull/48812)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a154db8a81](https://github.com/angular/angular/commit/a154db8a81cbdfed8c3d0db1e2a5bf43aa3e0bbf) | feat | add ng generate schematic to convert declarations to standalone ([#48790](https://github.com/angular/angular/pull/48790)) |
|
||||
| [e7318fc758](https://github.com/angular/angular/commit/e7318fc758d9e64d1a7f60a2c7071a769b73e7d8) | feat | add ng generate schematic to remove unnecessary modules ([#48832](https://github.com/angular/angular/pull/48832)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4dcab333ae](https://github.com/angular/angular/commit/4dcab333ae009f923cf3864e20ee47e43d32dfe6) | fix | 'createUrlTreeFromSnapshot' with empty paths and named outlets ([#48734](https://github.com/angular/angular/pull/48734)) |
|
||||
## Special Thanks
|
||||
Alan Agius, AleksanderBodurri, Andrew Kushnir, Andrew Scott, Charles Lyding, Dylan Hunn, Jessica Janiuk, JoostK, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Payam Valadkhan, Virginia Dooley, Yann Thomas LE MOIGNE and dario-piotrowicz
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.1.2"></a>
|
||||
# 15.1.2 (2023-01-25)
|
||||
### compiler
|
||||
@@ -168,36 +147,6 @@ Alan Agius, AleksanderBodurri, Andrew Kushnir, Andrew Scott, Charles Lyding, Dyl
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.2.0-next.1"></a>
|
||||
# 15.2.0-next.1 (2023-01-18)
|
||||
## Deprecations
|
||||
###
|
||||
- Class and `InjectionToken` guards and resolvers are
|
||||
deprecated. Instead, write guards as plain JavaScript functions and
|
||||
inject dependencies with `inject` from `@angular/core`.
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [926c35f4ac](https://github.com/angular/angular/commit/926c35f4ac70f5e4d142e545d6d056dd67aac97b) | docs | Deprecate class and InjectionToken and resolvers ([#47924](https://github.com/angular/angular/pull/47924)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8802b4aab9](https://github.com/angular/angular/commit/8802b4aab9600870f3e09f198c993845587e9622) | fix | Update `Location` to get a normalized URL valid in case a represented URL starts with the substring equals `APP_BASE_HREF` ([#48489](https://github.com/angular/angular/pull/48489)) |
|
||||
| [5f21c6d627](https://github.com/angular/angular/commit/5f21c6d627892dfad942b55f13861c213f9c0afb) | perf | avoid excessive DOM mutation in NgClass ([#48433](https://github.com/angular/angular/pull/48433)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f00bf71411](https://github.com/angular/angular/commit/f00bf714110100549111bd27345943ab8830128c) | fix | makeEnvironmentProviders should accept EnvironmentProviders ([#48720](https://github.com/angular/angular/pull/48720)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4ae384fd61](https://github.com/angular/angular/commit/4ae384fd619a13eaadf737d08a97f07e1f6b273c) | feat | Allow auto-imports of a pipe via quick fix when its selector is used, both directly and via reexports. ([#48354](https://github.com/angular/angular/pull/48354)) |
|
||||
| [141333411e](https://github.com/angular/angular/commit/141333411e67769d752c7162e4cb03376022f5e1) | feat | Introduce a new NgModuleIndex, and use it to suggest re-exports. ([#48354](https://github.com/angular/angular/pull/48354)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos, Bob Watson, Dylan Hunn, Jens, Konstantin Kharitonov, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Pawel Kozlowski, Vladyslav Slipchenko, ced, dario-piotrowicz, mgechev and ノウラ
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.1.1"></a>
|
||||
# 15.1.1 (2023-01-18)
|
||||
### common
|
||||
@@ -214,17 +163,6 @@ Alan Agius, Alex Rickabaugh, Andrew Scott, Aristeidis Bampakos, Bob Watson, Jens
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.2.0-next.0"></a>
|
||||
# 15.2.0-next.0 (2023-01-11)
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [31b94c762f](https://github.com/angular/angular/commit/31b94c762fc91ab6cabe08ea6812780fdcf92a64) | feat | Add a withNavigationErrorHandler feature to provideRouter ([#48551](https://github.com/angular/angular/pull/48551)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alex Rickabaugh, Andrew Scott, Paul Gschwendtner and angular-robot[bot]
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="15.1.0"></a>
|
||||
# 15.1.0 (2023-01-10)
|
||||
## Deprecations
|
||||
|
||||
@@ -192,10 +192,10 @@ cldr_xml_data_repository(
|
||||
# sass rules
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "38442f09a6f6f4e932e86ed29b86d5026b41d9ebbe9d017fe8a5684e1826b821",
|
||||
strip_prefix = "rules_sass-b603e6f0daf73d5aa9dca768b45dde30e5b288bc",
|
||||
sha256 = "cb2c1e972f6ee57f6c824a336016bd20627b9581d979969ac0dd7a73f7cb27ad",
|
||||
strip_prefix = "rules_sass-a2efcd672812366130ecd27ca91a011fa2b5a726",
|
||||
urls = [
|
||||
"https://github.com/bazelbuild/rules_sass/archive/b603e6f0daf73d5aa9dca768b45dde30e5b288bc.zip",
|
||||
"https://github.com/bazelbuild/rules_sass/archive/a2efcd672812366130ecd27ca91a011fa2b5a726.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~15.0.1",
|
||||
"@angular/cli": "~15.0.1",
|
||||
"@angular/compiler-cli": "~15.0.1",
|
||||
"@angular-devkit/build-angular": "~15.2.0-rc.0",
|
||||
"@angular/cli": "~15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "~15.2.0-rc.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^6.0.0",
|
||||
@@ -38,6 +38,6 @@
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.8.4"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -220,7 +220,7 @@ When you use the Angular CLI command `ng new --strict`, it is set to `true` in t
|
||||
|
||||
When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode).
|
||||
|
||||
The strictness flags that this open enables allow you to turn on and off specific types of strict template type checking.
|
||||
The strictness flags that this option enables allow you to turn on and off specific types of strict template type checking.
|
||||
See [troubleshooting template errors](guide/template-typecheck#troubleshooting-template-errors).
|
||||
|
||||
When you use the Angular CLI command `ng new --strict`, it is set to `true` in the new project's configuration.
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
# Configure HTTP URL parameters
|
||||
# HTTP - Configure URL parameters
|
||||
|
||||
@reviewed 2022-10-06
|
||||
Use the `HttpParams` class with the `params` request option to add URL query strings in your `HttpRequest`.
|
||||
|
||||
## Create URL parameter using the search method
|
||||
|
||||
The following example, the `searchHeroes()` method queries for heroes whose names contain the search term.
|
||||
|
||||
Start by importing `HttpParams` class.
|
||||
|
||||
<code-example hideCopy language="typescript">
|
||||
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example linenums="false" path="http/src/app/heroes/heroes.service.ts" region="searchHeroes"></code-example>
|
||||
|
||||
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter.
|
||||
If the term is "cat", for example, the GET request URL would be `api/heroes?name=cat`.
|
||||
|
||||
The `HttpParams` object is immutable.
|
||||
If you need to update the options, save the returned value of the `.set()` method.
|
||||
|
||||
## Create URL parameters from a query
|
||||
|
||||
You can also create HTTP parameters directly from a query string by using the `fromString` variable:
|
||||
|
||||
<code-example hideCopy language="typescript">
|
||||
|
||||
const params = new HttpParams({fromString: 'name=foo'});
|
||||
|
||||
</code-example>
|
||||
|
||||
<a id="intercepting-requests-and-responses"></a>
|
||||
|
||||
@reviewed 2022-11-08
|
||||
|
||||
@@ -1,3 +1,127 @@
|
||||
# HTTP interceptor use-cases
|
||||
# HTTP - interceptor use-cases
|
||||
|
||||
@reviewed 2022-10-06
|
||||
Following are a number of common uses for interceptors.
|
||||
|
||||
## Set default headers
|
||||
|
||||
Apps often use an interceptor to set default headers on outgoing requests.
|
||||
|
||||
The sample app has an `AuthService` that produces an authorization token.
|
||||
Here is its `AuthInterceptor` that injects that service to get the token and adds an authorization header with that token to every outgoing request:
|
||||
|
||||
<code-example header="app/http-interceptors/auth-interceptor.ts" path="http/src/app/http-interceptors/auth-interceptor.ts"></code-example>
|
||||
|
||||
The practice of cloning a request to set new headers is so common that there's a `setHeaders` shortcut for it:
|
||||
|
||||
<code-example path="http/src/app/http-interceptors/auth-interceptor.ts" region="set-header-shortcut"></code-example>
|
||||
|
||||
An interceptor that alters headers can be used for a number of different operations, including:
|
||||
|
||||
* Authentication/authorization
|
||||
* Caching behavior; for example, `If-Modified-Since`
|
||||
* XSRF protection
|
||||
|
||||
## Log request and response pairs
|
||||
|
||||
Because interceptors can process the request and response *together*, they can perform tasks such as timing and logging an entire HTTP operation.
|
||||
|
||||
Consider the following `LoggingInterceptor`, which captures the time of the request,
|
||||
the time of the response, and logs the outcome with the elapsed time
|
||||
with the injected `MessageService`.
|
||||
|
||||
<code-example header="app/http-interceptors/logging-interceptor.ts)" path="http/src/app/http-interceptors/logging-interceptor.ts" region="excerpt"></code-example>
|
||||
|
||||
The RxJS `tap` operator captures whether the request succeeded or failed.
|
||||
The RxJS `finalize` operator is called when the response observable either returns an error or completes and reports the outcome to the `MessageService`.
|
||||
|
||||
Neither `tap` nor `finalize` touch the values of the observable stream returned to the caller.
|
||||
|
||||
<a id="custom-json-parser"></a>
|
||||
|
||||
## Custom JSON parsing
|
||||
|
||||
Interceptors can be used to replace the built-in JSON parsing with a custom implementation.
|
||||
|
||||
The `CustomJsonInterceptor` in the following example demonstrates how to achieve this.
|
||||
If the intercepted request expects a `'json'` response, the `responseType` is changed to `'text'` to disable the built-in JSON parsing.
|
||||
Then the response is parsed via the injected `JsonParser`.
|
||||
|
||||
<code-example header="app/http-interceptors/custom-json-interceptor.ts" path="http/src/app/http-interceptors/custom-json-interceptor.ts" region="custom-json-interceptor"></code-example>
|
||||
|
||||
You can then implement your own custom `JsonParser`.
|
||||
Here is a custom JsonParser that has a special date reviver.
|
||||
|
||||
<code-example header="app/http-interceptors/custom-json-interceptor.ts" path="http/src/app/http-interceptors/custom-json-interceptor.ts" region="custom-json-parser"></code-example>
|
||||
|
||||
You provide the `CustomParser` along with the `CustomJsonInterceptor`.
|
||||
|
||||
<code-example header="app/http-interceptors/index.ts" path="http/src/app/http-interceptors/index.ts" region="custom-json-interceptor"></code-example>
|
||||
|
||||
<a id="caching"></a>
|
||||
|
||||
## Cache requests
|
||||
|
||||
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
|
||||
|
||||
For example, you might decide to cache certain requests and responses to improve performance.
|
||||
You can delegate caching to an interceptor without disturbing your existing data services.
|
||||
|
||||
The `CachingInterceptor` in the following example demonstrates this approach.
|
||||
|
||||
<code-example header="app/http-interceptors/caching-interceptor.ts)" path="http/src/app/http-interceptors/caching-interceptor.ts" region="v1"></code-example>
|
||||
|
||||
* The `isCacheable()` function determines if the request is cacheable.
|
||||
In this sample, only GET requests to the package search API are cacheable.
|
||||
|
||||
* If the request is not cacheable, the interceptor forwards the request to the next handler in the chain
|
||||
* If a cacheable request is found in the cache, the interceptor returns an `of()` *observable* with the cached response, by-passing the `next` handler and all other interceptors downstream
|
||||
* If a cacheable request is not in cache, the code calls `sendRequest()`.
|
||||
This function forwards the request to `next.handle()` which ultimately calls the server and returns the server's response.
|
||||
|
||||
<a id="send-request"></a>
|
||||
|
||||
<code-example path="http/src/app/http-interceptors/caching-interceptor.ts" region="send-request"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Notice how `sendRequest()` intercepts the response on its way back to the application.
|
||||
This method pipes the response through the `tap()` operator, whose callback adds the response to the cache.
|
||||
|
||||
The original response continues untouched back up through the chain of interceptors to the application caller.
|
||||
|
||||
Data services, such as `PackageSearchService`, are unaware that some of their `HttpClient` requests actually return cached responses.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<a id="cache-refresh"></a>
|
||||
|
||||
## Use interceptors to request multiple values
|
||||
|
||||
The `HttpClient.get()` method normally returns an observable that emits a single value, either the data or an error.
|
||||
An interceptor can change this to an observable that emits [multiple values](guide/observables).
|
||||
|
||||
The following revised version of the `CachingInterceptor` optionally returns an observable that immediately emits the cached response, sends the request on to the package search API, and emits again later with the updated search results.
|
||||
|
||||
<code-example path="http/src/app/http-interceptors/caching-interceptor.ts" region="intercept-refresh"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The *cache-then-refresh* option is triggered by the presence of a custom `x-refresh` header.
|
||||
|
||||
A checkbox on the `PackageSearchComponent` toggles a `withRefresh` flag, which is one of the arguments to `PackageSearchService.search()`.
|
||||
That `search()` method creates the custom `x-refresh` header and adds it to the request before calling `HttpClient.get()`.
|
||||
|
||||
</div>
|
||||
|
||||
The revised `CachingInterceptor` sets up a server request whether there's a cached value or not, using the same `sendRequest()` method described [above](#send-request).
|
||||
The `results$` observable makes the request when subscribed.
|
||||
|
||||
* If there's no cached value, the interceptor returns `results$`.
|
||||
* If there is a cached value, the code *pipes* the cached response onto `results$`. This produces a recomposed observable that emits two responses, so subscribers will see a sequence of these two responses:
|
||||
* The cached response that's emitted immediately
|
||||
* The response from the server, that's emitted later
|
||||
|
||||
<a id="report-progress"></a>
|
||||
|
||||
@reviewed 2022-11-08
|
||||
|
||||
@@ -1,3 +1,47 @@
|
||||
# Make a JSON with Padding request
|
||||
# HTTP: Make a JSONP request
|
||||
|
||||
@reviewed 2022-10-06
|
||||
“JSON with Padding” (JSONP) is a method to deceive a web browser into carrying out requests with a `<script>` tag that uses the SRC attribute to make a special API request.
|
||||
|
||||
Apps can use the `HttpClient` to make [JSONP](https://en.wikipedia.org/wiki/JSONP) requests across domains when a server doesn't support [CORS protocol](https://developer.mozilla.org/docs/Web/HTTP/CORS).
|
||||
|
||||
Angular JSONP requests return an `Observable`.
|
||||
Follow the pattern for subscribing to observables and use the RxJS `map` operator to transform the response before using the [async pipe](api/common/AsyncPipe) to manage the results.
|
||||
|
||||
In Angular, use JSONP by including `HttpClientJsonpModule` in the `NgModule` imports.
|
||||
In the following example, the `searchHeroes()` method uses a JSONP request to query for heroes whose names contain the search term.
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
/* GET heroes whose name contains search term */
|
||||
searchHeroes(term: string): Observable {
|
||||
term = term.trim();
|
||||
|
||||
const heroesURL = `${this.heroesURL}?${term}`;
|
||||
return this.http.jsonp(heroesUrl, 'callback').pipe(
|
||||
catchError(this.handleError('searchHeroes', [])) // then handle the error
|
||||
);
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
This request passes the `heroesURL` as the first parameter and the callback function name as the second parameter.
|
||||
The response is wrapped in the callback function, which takes the observables returned by the JSONP method and pipes them through to the error handler.
|
||||
|
||||
## Request non-JSON data
|
||||
|
||||
Not all APIs return JSON data.
|
||||
In this next example, a `DownloaderService` method reads a text file from the server and logs the file contents, before returning those contents to the caller as an `Observable<string>`.
|
||||
|
||||
<code-example header="app/downloader/downloader.service.ts (getTextFile)" linenums="false" path="http/src/app/downloader/downloader.service.ts" region="getTextFile"></code-example>
|
||||
|
||||
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
|
||||
|
||||
The RxJS `tap` operator lets the code inspect both success and error values passing through the observable without disturbing them.
|
||||
|
||||
A `download()` method in the `DownloaderComponent` initiates the request by subscribing to the service method.
|
||||
|
||||
<code-example header="app/downloader/downloader.component.ts (download)" linenums="false" path="http/src/app/downloader/downloader.component.ts" region="download"></code-example>
|
||||
|
||||
<a id="error-handling"></a>
|
||||
|
||||
@reviewed 2022-11-03
|
||||
|
||||
@@ -1,3 +1,49 @@
|
||||
# Pass metadata to interceptors
|
||||
# HTTP - Pass metadata to interceptors
|
||||
|
||||
@reviewed 2022-10-06
|
||||
Many interceptors require or benefit from configuration.
|
||||
Consider an interceptor that retries failed requests.
|
||||
By default, the interceptor might retry a request three times, but you might want to override this retry count for particularly error-prone or sensitive requests.
|
||||
|
||||
`HttpClient` requests contain a *context* that can carry metadata about the request.
|
||||
This context is available for interceptors to read or modify, though it is not transmitted to the backend server when the request is sent.
|
||||
This lets applications or other interceptors tag requests with configuration parameters, such as how many times to retry a request.
|
||||
|
||||
## Create a context token
|
||||
|
||||
Angular stores and retrieves a value in the context using an `HttpContextToken`.
|
||||
You can create a context token using the `new` operator, as in the following example:
|
||||
|
||||
<code-example header="creating a context token" path="http/src/app/http-interceptors/retry-interceptor.ts" region="context-token"></code-example>
|
||||
|
||||
The lambda function `() => 3` passed during the creation of the `HttpContextToken` serves two purposes:
|
||||
|
||||
1. It lets TypeScript infer the type of this token:
|
||||
`HttpContextToken<number>`
|
||||
The request context is type-safe —reading a token from a request's context returns a value of the appropriate type.
|
||||
|
||||
1. It sets the default value for the token.
|
||||
This is the value that the request context returns if no other value was set for this token.
|
||||
Using a default value avoids the need to check if a particular value is set.
|
||||
|
||||
## Set context values when making a request
|
||||
|
||||
When making a request, you can provide an `HttpContext` instance, in which you have already set the context values.
|
||||
|
||||
<code-example header="setting context values" path="http/src/app/http-interceptors/retry-interceptor.ts" region="set-context"></code-example>
|
||||
|
||||
## Read context values in an interceptor
|
||||
|
||||
Within an interceptor, you can read the value of a token in a given request's context with `HttpContext.get()`.
|
||||
If you have not explicitly set a value for the token, Angular returns the default value specified in the token.
|
||||
|
||||
<code-example header="reading context values in an interceptor" path="http/src/app/http-interceptors/retry-interceptor.ts" region="reading-context"></code-example>
|
||||
|
||||
## Contexts are mutable
|
||||
|
||||
Unlike most other aspects of `HttpRequest` instances, the request context is mutable and persists across other immutable transformations of the request.
|
||||
This lets interceptors coordinate operations through the context.
|
||||
For instance, the `RetryInterceptor` example could use a second context token to track how many errors occur during the execution of a given request:
|
||||
|
||||
<code-example header="coordinating operations through the context" path="http/src/app/http-interceptors/retry-interceptor.ts" region="mutable-context"></code-example>
|
||||
|
||||
@reviewed 2022-11-15
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
# Security: Cross-site request forgery (XSRF) protection
|
||||
# HTTP client - Security: Cross-Site Request Forgery (XSRF) protection
|
||||
|
||||
@reviewed 2022-10-06
|
||||
[Cross-Site Request Forgery (XSRF or CSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website.
|
||||
|
||||
`HttpClient` supports a [common mechanism](https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token) used to prevent XSRF attacks.
|
||||
When performing HTTP requests, an interceptor reads a token from a cookie, by default `XSRF-TOKEN`, and sets it as an HTTP header, `X-XSRF-TOKEN`.
|
||||
Because only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.
|
||||
|
||||
By default, an interceptor sends this header on all mutating requests \(such as POST\)
|
||||
to relative URLs, but not on GET/HEAD requests or on requests with an absolute URL.
|
||||
|
||||
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called `XSRF-TOKEN` on either the page load or the first GET request.
|
||||
On subsequent requests the server can verify that the cookie matches the `X-XSRF-TOKEN` HTTP header, and therefore be sure that only code running on your domain could have sent the request.
|
||||
The token must be unique for each user and must be verifiable by the server; this prevents the client from making up its own tokens.
|
||||
Set the token to a digest of your site's authentication cookie with a salt for added security.
|
||||
|
||||
To prevent collisions in environments where multiple Angular apps share the same domain or subdomain, give each application a unique cookie name.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
*`HttpClient` supports only the client half of the XSRF protection scheme.*
|
||||
Your backend service must be configured to set the cookie for your page, and to verify that the header is present on all eligible requests.
|
||||
Failing to do so renders Angular's default protection ineffective.
|
||||
|
||||
</div>
|
||||
|
||||
## Configure custom cookie/header names
|
||||
|
||||
If your backend service uses different names for the XSRF token cookie or header, use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
|
||||
<code-example path="http/src/app/app.module.ts" region="xsrf"></code-example>
|
||||
|
||||
<a id="testing-requests"></a>
|
||||
|
||||
@reviewed 2022-11-14
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
# Setup for server communication
|
||||
# HTTP: Setup for server communication
|
||||
|
||||
@reviewed 2022-10-06
|
||||
Before you can use `HttpClient`, you need to import the Angular `HttpClientModule`.
|
||||
Most apps do so in the root `AppModule`.
|
||||
|
||||
<code-example header="app/app.module.ts (excerpt)" path="http/src/app/app.module.ts" region="sketch"></code-example>
|
||||
|
||||
You can then inject the `HttpClient` service as a dependency of an application class, as shown in the following `ConfigService` example.
|
||||
|
||||
<code-example header="app/config/config.service.ts (excerpt)" path="http/src/app/config/config.service.ts" region="proto"></code-example>
|
||||
|
||||
The `HttpClient` service makes use of [observables](guide/glossary#observable "Observable definition") for all transactions.
|
||||
You must import the RxJS observable and operator symbols that appear in the example snippets.
|
||||
These `ConfigService` imports are typical.
|
||||
|
||||
<code-example header="app/config/config.service.ts (RxJS imports)" path="http/src/app/config/config.service.ts" region="rxjs-imports"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can run the <live-example></live-example> that accompanies this guide.
|
||||
|
||||
The sample app does not require a data server.
|
||||
It relies on the [Angular *in-memory-web-api*](https://github.com/angular/angular/tree/main/packages/misc/angular-in-memory-web-api), which replaces the *HttpClient* module's `HttpBackend`.
|
||||
The replacement service simulates the behavior of a REST-like backend.
|
||||
|
||||
Look at the `AppModule` *imports* to see how it is configured.
|
||||
|
||||
</div>
|
||||
|
||||
@reviewed 2022-11-03
|
||||
|
||||
@@ -1,3 +1,75 @@
|
||||
# Test HTTP requests
|
||||
# HTTP client - Test requests
|
||||
|
||||
@reviewed 2022-10-06
|
||||
As for any external dependency, you must mock the HTTP backend so your tests can simulate interaction with a remote server.
|
||||
The `@angular/common/http/testing` library makes it straightforward to set up such mocking.
|
||||
|
||||
## HTTP testing library
|
||||
|
||||
Angular's HTTP testing library is designed for a pattern of testing in which the app executes code and makes requests first.
|
||||
The test then expects that certain requests have or have not been made, performs assertions against those requests, and finally provides responses by "flushing" each expected request.
|
||||
|
||||
At the end, tests can verify that the app made no unexpected requests.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can run <live-example stackblitz="specs">these sample tests</live-example> in a live coding environment.
|
||||
|
||||
The tests described in this guide are in `src/testing/http-client.spec.ts`.
|
||||
There are also tests of an application data service that call `HttpClient` in `src/app/heroes/heroes.service.spec.ts`.
|
||||
|
||||
</div>
|
||||
|
||||
## Setup for testing
|
||||
|
||||
To begin testing calls to `HttpClient`, import the `HttpClientTestingModule` and the mocking controller, `HttpTestingController`, along with the other symbols your tests require.
|
||||
|
||||
<code-example header="app/testing/http-client.spec.ts (imports)" path="http/src/testing/http-client.spec.ts" region="imports"></code-example>
|
||||
|
||||
Then add the `HttpClientTestingModule` to the `TestBed` and continue with the setup of the *service-under-test*.
|
||||
|
||||
<code-example header="app/testing/http-client.spec.ts(setup)" path="http/src/testing/http-client.spec.ts" region="setup"></code-example>
|
||||
|
||||
Now requests made in the course of your tests hit the testing backend instead of the normal backend.
|
||||
|
||||
This setup also calls `TestBed.inject()` to inject the `HttpClient` service and the mocking controller so they can be referenced during the tests.
|
||||
|
||||
## Expect and answer requests
|
||||
|
||||
Now you can write a test that expects a GET Request to occur and provides a mock response.
|
||||
|
||||
<code-example header="app/testing/http-client.spec.ts (HttpClient.get)" path="http/src/testing/http-client.spec.ts" region="get-test"></code-example>
|
||||
|
||||
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
|
||||
|
||||
<code-example path="http/src/testing/http-client.spec.ts" region="afterEach"></code-example>
|
||||
|
||||
### Custom request expectations
|
||||
|
||||
If matching by URL isn't sufficient, it's possible to implement your own matching function.
|
||||
For example, you could look for an outgoing request that has an authorization header:
|
||||
|
||||
<code-example path="http/src/testing/http-client.spec.ts" region="predicate"></code-example>
|
||||
|
||||
As with the previous `expectOne()`, the test fails if 0 or 2+ requests satisfy this predicate.
|
||||
|
||||
### Handle more than one request
|
||||
|
||||
If you need to respond to duplicate requests in your test, use the `match()` API instead of `expectOne()`.
|
||||
It takes the same arguments but returns an array of matching requests.
|
||||
Once returned, these requests are removed from future matching and you are responsible for flushing and verifying them.
|
||||
|
||||
<code-example path="http/src/testing/http-client.spec.ts" region="multi-request"></code-example>
|
||||
|
||||
## Test for errors
|
||||
|
||||
You should test the app's defenses against HTTP requests that fail.
|
||||
|
||||
Call `request.flush()` with an error message, as seen in the following example.
|
||||
|
||||
<code-example path="http/src/testing/http-client.spec.ts" region="404"></code-example>
|
||||
|
||||
Alternatively, call `request.error()` with a `ProgressEvent`.
|
||||
|
||||
<code-example path="http/src/testing/http-client.spec.ts" region="network-error"></code-example>
|
||||
|
||||
@reviewed 2022-11-14
|
||||
|
||||
@@ -257,7 +257,7 @@ Because a wildcard route is the least specific route, place it last in the route
|
||||
|
||||
</div>
|
||||
|
||||
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to a non-existant route called `"/sidekicks"`.
|
||||
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to a non-existent route called `"/sidekicks"`.
|
||||
|
||||
<code-example header="src/app/hero-list/hero-list.component.html (excerpt)" path="router/src/app/hero-list/hero-list.component.1.html"></code-example>
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
# Migrate an existing Angular project to standalone
|
||||
|
||||
As of version 15.2.0, Angular offers a [schematic](guide/schematics) to help project authors convert existing projects to [the new standalone APIs](guide/standalone-components). The schematic aims to transform as much code as possible automatically, but it may require some manual fixes by the project author. Run the schematic with the following command:
|
||||
|
||||
<code-example format="shell" language="shell">
|
||||
|
||||
ng generate @angular/core:standalone
|
||||
|
||||
</code-example>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using the schematic, please ensure that the project:
|
||||
1. Is using Angular 15.2.0 or later.
|
||||
2. Builds without any compilation errors.
|
||||
3. Is on a clean Git branch and all work is saved.
|
||||
|
||||
## Schematic options
|
||||
|
||||
| Option | Details |
|
||||
|:--- |:--- |
|
||||
| `mode` | The transformation to perform. See [Migration modes](#migration-modes) below for details on the available options. |
|
||||
| `path` | The path to migrate, relative to the project root. You can use this option to migrate sections of your project incrementally. |
|
||||
|
||||
## Migrations steps
|
||||
|
||||
The migration process is composed of three steps. You'll have to run it multiple times and check manually that the project builds and behaves as expected.
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
<header>Note</header>
|
||||
|
||||
While the schematic can automatically update most code, some edge cases require developer intervention.
|
||||
You should plan to apply manual fixes after each step of the migration. Additionally, the new code generated by the schematic may not match your code's formatting rules.
|
||||
|
||||
</div>
|
||||
|
||||
Run the migration in the order listed below, verifying that your code builds and runs between each step:
|
||||
1. Run `ng g @angular/core:standalone` and select "Convert all components, directives and pipes to standalone"
|
||||
2. Run `ng g @angular/core:standalone` and select "Remove unnecessary NgModule classes"
|
||||
3. Run `ng g @angular/core:standalone` and select "Bootstrap the project using standalone APIs"
|
||||
4. Run any linting and formatting checks, fix any failures, and commit the result
|
||||
|
||||
## After the migration
|
||||
|
||||
Congratulations, your application has been converted to standalone 🎉. These are some optional follow-up steps you may want to take now:
|
||||
* Find and remove any remaining `NgModule` declarations: since the ["Remove unnecessary NgModules" step](#remove-unnecessary-ngmodules) cannot remove all modules automatically, you may have to remove the remaining declarations manually.
|
||||
* Run the project's unit tests and fix any failures.
|
||||
* Run any code formatters, if the project uses automatic formatting.
|
||||
* Run any linters in your project and fix new warnings. Some linters support a `--fix` flag that may resolve so warnings automatically.
|
||||
|
||||
## Migration modes
|
||||
The migration has the following modes:
|
||||
1. Convert declarations to standalone.
|
||||
2. Remove unnecessary NgModules.
|
||||
3. Switch to standalone bootstrapping API.
|
||||
You should run these migrations in the order given.
|
||||
|
||||
### Convert declarations to standalone
|
||||
|
||||
In this mode, the migration converts all components, directives and pipes to standalone by setting `standalone: true` and adding dependencies to their `imports` array.
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
The schematic ignores NgModules which bootstrap a component during this step because they are likely root modules used by `bootstrapModule` rather than the standalone-compatible `bootstrapApplication`. The schematic converts these declarations automatically as a part of the ["Switch to standalone bootstrapping API"](#switch-to-standalone-bootstrapping-api) step.
|
||||
|
||||
</div>
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
// shared.module.ts
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
declarations: [GreeterComponent],
|
||||
exports: [GreeterComponent]
|
||||
})
|
||||
export class SharedModule {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// greeter.component.ts
|
||||
@Component({
|
||||
selector: 'greeter',
|
||||
template: '<div *ngIf="showGreeting">Hello</div>',
|
||||
})
|
||||
export class GreeterComponent {
|
||||
showGreeting = true;
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
// shared.module.ts
|
||||
@NgModule({
|
||||
imports: [CommonModule, GreeterComponent],
|
||||
exports: [GreeterComponent]
|
||||
})
|
||||
export class SharedModule {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// greeter.component.ts
|
||||
@Component({
|
||||
selector: 'greeter',
|
||||
template: '<div *ngIf="showGreeting">Hello</div>',
|
||||
standalone: true,
|
||||
imports: [NgIf]
|
||||
})
|
||||
export class GreeterComponent {
|
||||
showGreeting = true;
|
||||
}
|
||||
```
|
||||
|
||||
### Remove unnecessary NgModules
|
||||
After converting all declarations to standalone, many NgModules can be safely removed. This step deletes such module declarations and as many corresponding references as possible. If the migration cannot delete a reference automatically, it leaves the following TODO comment so that you can delete the NgModule manually:
|
||||
```typescript
|
||||
/* TODO(standalone-migration): clean up removed NgModule reference manually */
|
||||
```
|
||||
|
||||
The migration considers a module safe to remove if that module:
|
||||
* Has no `declarations`.
|
||||
* Has no `providers`.
|
||||
* Has no `bootstrap` components.
|
||||
* Has no `imports` that reference a `ModuleWithProviders` symbol or a module that can't be removed.
|
||||
* Has no class members. Empty constructors are ignored.
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
// importer.module.ts
|
||||
@NgModule({
|
||||
imports: [FooComponent, BarPipe],
|
||||
exports: [FooComponent, BarPipe]
|
||||
})
|
||||
export class ImporterModule {}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
// importer.module.ts
|
||||
// Does not exist!
|
||||
```
|
||||
|
||||
### Switch to standalone bootstrapping API
|
||||
This step converts any usages of `bootstrapModule` to the new, standalone-based `bootstrapApplication`. It also switches the root component to `standalone: true` and deletes the root NgModule. If the root module has any `providers` or `imports`, the migration attempts to copy as much of this configuration as possible into the new bootstrap call.
|
||||
|
||||
**Before:**
|
||||
```typescript
|
||||
// ./app/app.module.ts
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ./app/app.component.ts
|
||||
@Component({ selector: 'app', template: 'hello' })
|
||||
export class AppComponent {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ./main.ts
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
```
|
||||
|
||||
**After:**
|
||||
```typescript
|
||||
// ./app/app.module.ts
|
||||
// Does not exist!
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ./app/app.component.ts
|
||||
@Component({ selector: 'app', template: 'hello', standalone: true })
|
||||
export class AppComponent {}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ./main.ts
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent).catch(e => console.error(e));
|
||||
```
|
||||
|
||||
## Common problems
|
||||
Some common problems that may prevent the schematic from working correctly include:
|
||||
* Compilation errors - if the project has compilation errors, Angular cannot analyze and migrate it correctly.
|
||||
* Files not included in a tsconfig - the schematic determines which files to migrate by analyzing your project's `tsconfig.json` files. The schematic excludes any files not captured by a tsconfig.
|
||||
* Code that cannot be statically analyzed - the schematic uses static analysis to understand your code and determine where to make changes. The migration may skip any classes with metadata that cannot be statically analyzed at build time.
|
||||
|
||||
## Limitations
|
||||
Due to the size and complexity of the migration, there are some cases that the schematic cannot handle:
|
||||
* Because unit tests are not ahead-of-time (AoT) compiled, `imports` added to components in unit tests might not be entirely correct.
|
||||
* The schematic relies on direct calls to Angular APIs. The schematic cannot recognize custom wrappers around Angular APIs. For example, if there you define a custom `customConfigureTestModule` function that wraps `TestBed.configureTestingModule`, components it declares may not be recognized.
|
||||
|
||||
@reviewed 2023-02-15
|
||||
@@ -1132,5 +1132,19 @@
|
||||
"start": "2022-12-06"
|
||||
},
|
||||
"name": "Angular Contributor Days"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2023-11-07"
|
||||
},
|
||||
"linkUrl": "http://ng-poland.pl/",
|
||||
"name": "NG Poland"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2023-11-08"
|
||||
},
|
||||
"linkUrl": "http://js-poland.pl/",
|
||||
"name": "JS Poland"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -312,9 +312,20 @@
|
||||
"tooltip": "A list of developer guides for building Angular applications."
|
||||
},
|
||||
{
|
||||
"url": "guide/standalone-components",
|
||||
"title": "Standalone components",
|
||||
"tooltip": "Standalone components, directives, and pipes"
|
||||
"title": "Standalone",
|
||||
"tooltip": "Learn about Angular's standalone APIs and how to convert your application to them.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/standalone-components",
|
||||
"title": "Standalone components",
|
||||
"tooltip": "Standalone components, directives, and pipes"
|
||||
},
|
||||
{
|
||||
"url": "guide/standalone-migration",
|
||||
"title": "Migrating to standalone",
|
||||
"tooltip": "Migrate your application to Angular's standalone APIs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Change detection",
|
||||
|
||||
@@ -251,7 +251,7 @@ The user should be able to get to these details in three ways.
|
||||
1. By clicking a hero in the heroes list.
|
||||
1. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.
|
||||
|
||||
This section enables navigation to the `HeroDetailComponent` and liberate it from the `HeroesComponent`.
|
||||
This section enables navigation to the `HeroDetailComponent` and liberates it from the `HeroesComponent`.
|
||||
|
||||
### Delete *hero details* from `HeroesComponent`
|
||||
|
||||
@@ -299,7 +299,7 @@ The hero items in the `HeroesComponent` are `<li>` elements whose click events a
|
||||
|
||||
<code-example header="src/app/heroes/heroes.component.html (list with onSelect)" path="toh-pt4/src/app/heroes/heroes.component.html" region="list"></code-example>
|
||||
|
||||
Remove the `<li>` back to just its `*ngFor`.
|
||||
Remove the inner HTML of `<li>`.
|
||||
Wrap the badge and name in an anchor `<a>` element.
|
||||
Add a `routerLink` attribute to the anchor that's the same as in the dashboard template.
|
||||
|
||||
|
||||
+3
-3
@@ -99,8 +99,8 @@
|
||||
"@types/node": "^12.7.9",
|
||||
"@types/trusted-types": "^2.0.2",
|
||||
"@types/xregexp": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.52.0",
|
||||
"@typescript-eslint/parser": "5.52.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.53.0",
|
||||
"@typescript-eslint/parser": "5.53.0",
|
||||
"archiver": "^5.3.0",
|
||||
"assert": "^2.0.0",
|
||||
"canonical-path": "1.0.0",
|
||||
@@ -158,7 +158,7 @@
|
||||
"ts-node": "^10.8.1",
|
||||
"tsec": "^0.2.2",
|
||||
"tslint": "~6.1.3",
|
||||
"typescript": "~4.8.0",
|
||||
"typescript": "~4.9.3",
|
||||
"uglify-js": "^3.13.3",
|
||||
"unist-util-filter": "^2.0.3",
|
||||
"unist-util-source": "^3.0.0",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<header>
|
||||
<mat-toolbar #appToolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning">
|
||||
<mat-toolbar-row class="notification-container">
|
||||
<aio-notification notificationId="survey-january-2023" expirationDate="2023-03-30" [dismissOnContentClick]="true" (dismissed)="notificationDismissed()">
|
||||
<aio-notification notificationId="survey-january-2023" expirationDate="2023-02-15" [dismissOnContentClick]="true" (dismissed)="notificationDismissed()">
|
||||
<a href="https://goo.gle/angular-devsat-q1-2023">
|
||||
<mat-icon class="icon" svgIcon="insert_comment" aria-label="Announcement"></mat-icon>
|
||||
<span class="message">Help Angular by taking a <b>quick survey</b>!</span>
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"angular": "^1.8.3",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"angular-route": "1.8.3",
|
||||
@@ -29,9 +29,9 @@
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@types/angular": "^1.8.4",
|
||||
"@types/angular-route": "^1.7.2",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
@@ -46,6 +46,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,23 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/node": "^16.11.35",
|
||||
"copyfiles": "^2.4.1",
|
||||
@@ -43,6 +43,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,24 +13,24 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/elements": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/elements": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/node": "^16.11.35",
|
||||
"jasmine-core": "~4.5.0",
|
||||
@@ -43,6 +43,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,24 +15,24 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/localize": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/localize": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/node": "^16.11.35",
|
||||
"jasmine-core": "~4.5.0",
|
||||
@@ -45,6 +45,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,24 +13,24 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/service-worker": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.1",
|
||||
"@angular/common": "^15.2.0-rc.1",
|
||||
"@angular/compiler": "^15.2.0-rc.1",
|
||||
"@angular/core": "^15.2.0-rc.1",
|
||||
"@angular/forms": "^15.2.0-rc.1",
|
||||
"@angular/platform-browser": "^15.2.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.1",
|
||||
"@angular/router": "^15.2.0-rc.1",
|
||||
"@angular/service-worker": "^15.2.0-rc.1",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.1",
|
||||
"@angular/cli": "^15.2.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.1",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/node": "^16.11.35",
|
||||
"jasmine-core": "~4.5.0",
|
||||
@@ -43,6 +43,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/upgrade": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"@angular/upgrade": "^15.2.0-rc.0",
|
||||
"core-js": "^2.5.4",
|
||||
"rxjs": "~7.8.0",
|
||||
"systemjs": "~0.19.39",
|
||||
@@ -42,7 +42,7 @@
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@rollup/plugin-commonjs": "^24.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.2.0",
|
||||
"@types/angular": "^1.8.4",
|
||||
@@ -63,6 +63,6 @@
|
||||
"protractor": "~7.0.0",
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/platform-server": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/platform-server": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"@nguniversal/express-engine": "^15.0.0-next.0",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
"express": "^4.18.1",
|
||||
@@ -34,9 +34,9 @@
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@nguniversal/builders": "^15.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
@@ -51,6 +51,6 @@
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,19 @@
|
||||
"license": "MIT",
|
||||
"repository": {},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.0.0-rc.1",
|
||||
"@angular/common": "^15.0.0-rc.1",
|
||||
"@angular/compiler": "^15.0.0-rc.1",
|
||||
"@angular/core": "^15.0.0-rc.1",
|
||||
"@angular/elements": "^15.0.0-rc.1",
|
||||
"@angular/forms": "^15.0.0-rc.1",
|
||||
"@angular/localize": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser": "^15.0.0-rc.1",
|
||||
"@angular/platform-browser-dynamic": "^15.0.0-rc.1",
|
||||
"@angular/platform-server": "^15.0.0-rc.1",
|
||||
"@angular/router": "^15.0.0-rc.1",
|
||||
"@angular/service-worker": "^15.0.0-rc.1",
|
||||
"@angular/upgrade": "^15.0.0-rc.1",
|
||||
"@angular/animations": "^15.2.0-rc.0",
|
||||
"@angular/common": "^15.2.0-rc.0",
|
||||
"@angular/compiler": "^15.2.0-rc.0",
|
||||
"@angular/core": "^15.2.0-rc.0",
|
||||
"@angular/elements": "^15.2.0-rc.0",
|
||||
"@angular/forms": "^15.2.0-rc.0",
|
||||
"@angular/localize": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser": "^15.2.0-rc.0",
|
||||
"@angular/platform-browser-dynamic": "^15.2.0-rc.0",
|
||||
"@angular/platform-server": "^15.2.0-rc.0",
|
||||
"@angular/router": "^15.2.0-rc.0",
|
||||
"@angular/service-worker": "^15.2.0-rc.0",
|
||||
"@angular/upgrade": "^15.2.0-rc.0",
|
||||
"@nguniversal/express-engine": "^15.0.0-next.0",
|
||||
"angular": "^1.8.3",
|
||||
"angular-in-memory-web-api": "~0.15.0",
|
||||
@@ -46,9 +46,9 @@
|
||||
"zone.js": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.0.0-rc.1",
|
||||
"@angular/cli": "^15.0.0-rc.1",
|
||||
"@angular/compiler-cli": "^15.0.0-rc.1",
|
||||
"@angular-devkit/build-angular": "^15.2.0-rc.0",
|
||||
"@angular/cli": "^15.2.0-rc.0",
|
||||
"@angular/compiler-cli": "^15.2.0-rc.0",
|
||||
"@nguniversal/builders": "^15.0.0",
|
||||
"@rollup/plugin-commonjs": "^24.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.2.0",
|
||||
@@ -81,6 +81,6 @@
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"source-map-explorer": "^2.0.0",
|
||||
"ts-node": "~10.9.0",
|
||||
"typescript": "~4.8.0"
|
||||
"typescript": "~4.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
+858
-651
File diff suppressed because it is too large
Load Diff
+54
-49
@@ -3547,14 +3547,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz#5fb0d43574c2411f16ea80f5fc335b8eaa7b28a8"
|
||||
integrity sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==
|
||||
"@typescript-eslint/eslint-plugin@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz#24b8b4a952f3c615fe070e3c461dd852b5056734"
|
||||
integrity sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.52.0"
|
||||
"@typescript-eslint/type-utils" "5.52.0"
|
||||
"@typescript-eslint/utils" "5.52.0"
|
||||
"@typescript-eslint/scope-manager" "5.53.0"
|
||||
"@typescript-eslint/type-utils" "5.53.0"
|
||||
"@typescript-eslint/utils" "5.53.0"
|
||||
debug "^4.3.4"
|
||||
grapheme-splitter "^1.0.4"
|
||||
ignore "^5.2.0"
|
||||
@@ -3563,14 +3563,14 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.52.0.tgz#73c136df6c0133f1d7870de7131ccf356f5be5a4"
|
||||
integrity sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==
|
||||
"@typescript-eslint/parser@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.53.0.tgz#a1f2b9ae73b83181098747e96683f1b249ecab52"
|
||||
integrity sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.52.0"
|
||||
"@typescript-eslint/types" "5.52.0"
|
||||
"@typescript-eslint/typescript-estree" "5.52.0"
|
||||
"@typescript-eslint/scope-manager" "5.53.0"
|
||||
"@typescript-eslint/types" "5.53.0"
|
||||
"@typescript-eslint/typescript-estree" "5.53.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.43.0":
|
||||
@@ -3581,13 +3581,13 @@
|
||||
"@typescript-eslint/types" "5.43.0"
|
||||
"@typescript-eslint/visitor-keys" "5.43.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz#a993d89a0556ea16811db48eabd7c5b72dcb83d1"
|
||||
integrity sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==
|
||||
"@typescript-eslint/scope-manager@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz#42b54f280e33c82939275a42649701024f3fafef"
|
||||
integrity sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.52.0"
|
||||
"@typescript-eslint/visitor-keys" "5.52.0"
|
||||
"@typescript-eslint/types" "5.53.0"
|
||||
"@typescript-eslint/visitor-keys" "5.53.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.43.0":
|
||||
version "5.43.0"
|
||||
@@ -3599,13 +3599,13 @@
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz#9fd28cd02e6f21f5109e35496df41893f33167aa"
|
||||
integrity sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==
|
||||
"@typescript-eslint/type-utils@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz#41665449935ba9b4e6a1ba6e2a3f4b2c31d6cf97"
|
||||
integrity sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "5.52.0"
|
||||
"@typescript-eslint/utils" "5.52.0"
|
||||
"@typescript-eslint/typescript-estree" "5.53.0"
|
||||
"@typescript-eslint/utils" "5.53.0"
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
@@ -3614,10 +3614,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.43.0.tgz#e4ddd7846fcbc074325293515fa98e844d8d2578"
|
||||
integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==
|
||||
|
||||
"@typescript-eslint/types@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.52.0.tgz#19e9abc6afb5bd37a1a9bea877a1a836c0b3241b"
|
||||
integrity sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==
|
||||
"@typescript-eslint/types@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.53.0.tgz#f79eca62b97e518ee124086a21a24f3be267026f"
|
||||
integrity sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.43.0":
|
||||
version "5.43.0"
|
||||
@@ -3632,13 +3632,13 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz#6408cb3c2ccc01c03c278cb201cf07e73347dfca"
|
||||
integrity sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==
|
||||
"@typescript-eslint/typescript-estree@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz#bc651dc28cf18ab248ecd18a4c886c744aebd690"
|
||||
integrity sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.52.0"
|
||||
"@typescript-eslint/visitor-keys" "5.52.0"
|
||||
"@typescript-eslint/types" "5.53.0"
|
||||
"@typescript-eslint/visitor-keys" "5.53.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
@@ -3659,16 +3659,16 @@
|
||||
eslint-utils "^3.0.0"
|
||||
semver "^7.3.7"
|
||||
|
||||
"@typescript-eslint/utils@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.52.0.tgz#b260bb5a8f6b00a0ed51db66bdba4ed5e4845a72"
|
||||
integrity sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==
|
||||
"@typescript-eslint/utils@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.53.0.tgz#e55eaad9d6fffa120575ffaa530c7e802f13bce8"
|
||||
integrity sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@types/semver" "^7.3.12"
|
||||
"@typescript-eslint/scope-manager" "5.52.0"
|
||||
"@typescript-eslint/types" "5.52.0"
|
||||
"@typescript-eslint/typescript-estree" "5.52.0"
|
||||
"@typescript-eslint/scope-manager" "5.53.0"
|
||||
"@typescript-eslint/types" "5.53.0"
|
||||
"@typescript-eslint/typescript-estree" "5.53.0"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
semver "^7.3.7"
|
||||
@@ -3681,12 +3681,12 @@
|
||||
"@typescript-eslint/types" "5.43.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.52.0":
|
||||
version "5.52.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz#e38c971259f44f80cfe49d97dbffa38e3e75030f"
|
||||
integrity sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==
|
||||
"@typescript-eslint/visitor-keys@5.53.0":
|
||||
version "5.53.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz#8a5126623937cdd909c30d8fa72f79fa56cc1a9f"
|
||||
integrity sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.52.0"
|
||||
"@typescript-eslint/types" "5.53.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
@@ -13402,7 +13402,7 @@ typescript@~4.5.4:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
||||
|
||||
typescript@~4.8.0, typescript@~4.8.4:
|
||||
typescript@~4.8.4:
|
||||
version "4.8.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
|
||||
integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
|
||||
@@ -13412,6 +13412,11 @@ typescript@~4.9.0:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
|
||||
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
|
||||
|
||||
typescript@~4.9.3:
|
||||
version "4.9.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
|
||||
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
|
||||
|
||||
ua-parser-js@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.2.tgz#e2976c34dbfb30b15d2c300b2a53eac87c57a775"
|
||||
|
||||
@@ -183,6 +183,9 @@ export const platformBrowser: (extraProviders?: StaticProvider[]) => PlatformRef
|
||||
// @public
|
||||
export function provideProtractorTestingSupport(): Provider[];
|
||||
|
||||
// @public
|
||||
export const REMOVE_STYLES_ON_COMPONENT_DESTROY: InjectionToken<boolean>;
|
||||
|
||||
// @public
|
||||
export interface SafeHtml extends SafeValue {
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"aio-local": {
|
||||
"uncompressed": {
|
||||
"runtime": 4325,
|
||||
"main": 466290,
|
||||
"main": 466694,
|
||||
"polyfills": 33836,
|
||||
"styles": 74561,
|
||||
"light-theme": 92890,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"cli-hello-world": {
|
||||
"uncompressed": {
|
||||
"runtime": 908,
|
||||
"main": 125165,
|
||||
"main": 126185,
|
||||
"polyfills": 33824
|
||||
}
|
||||
},
|
||||
@@ -16,24 +16,17 @@
|
||||
"polyfills": 33848
|
||||
}
|
||||
},
|
||||
"cli-hello-world-ivy-compat": {
|
||||
"uncompressed": {
|
||||
"runtime": 930,
|
||||
"main": 132182,
|
||||
"polyfills": 33957
|
||||
}
|
||||
},
|
||||
"cli-hello-world-ivy-i18n": {
|
||||
"uncompressed": {
|
||||
"runtime": 926,
|
||||
"main": 123543,
|
||||
"main": 125026,
|
||||
"polyfills": 34628
|
||||
}
|
||||
},
|
||||
"cli-hello-world-lazy": {
|
||||
"uncompressed": {
|
||||
"runtime": 2734,
|
||||
"main": 228122,
|
||||
"main": 229111,
|
||||
"polyfills": 33842,
|
||||
"src_app_lazy_lazy_routes_ts": 487
|
||||
}
|
||||
@@ -41,21 +34,21 @@
|
||||
"forms": {
|
||||
"uncompressed": {
|
||||
"runtime": 888,
|
||||
"main": 157604,
|
||||
"main": 158624,
|
||||
"polyfills": 33915
|
||||
}
|
||||
},
|
||||
"animations": {
|
||||
"uncompressed": {
|
||||
"runtime": 898,
|
||||
"main": 156355,
|
||||
"main": 157699,
|
||||
"polyfills": 33897
|
||||
}
|
||||
},
|
||||
"standalone-bootstrap": {
|
||||
"uncompressed": {
|
||||
"runtime": 918,
|
||||
"main": 84527,
|
||||
"main": 85547,
|
||||
"polyfills": 33945
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,46 +0,0 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -1,7 +0,0 @@
|
||||
load("//integration:index.bzl", "ng_integration_test")
|
||||
|
||||
ng_integration_test(
|
||||
name = "test",
|
||||
setup_chromium = True,
|
||||
track_payload_size = "cli-hello-world-ivy-compat",
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
# CliHelloWorldIvy
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.0-next.9.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](https://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/main/README.md).
|
||||
@@ -1,126 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"cli-hello-world-ivy-compat": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": [],
|
||||
"progress": false
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "cli-hello-world-ivy-compat:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "cli-hello-world-ivy-compat:build:production"
|
||||
},
|
||||
"ci": {},
|
||||
"ci-production": {
|
||||
"browserTarget": "cli-hello-world-ivy-compat:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "cli-hello-world-ivy-compat:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": [],
|
||||
"progress": false,
|
||||
"watch": false
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "cli-hello-world-ivy-compat:serve",
|
||||
"webdriverUpdate": false
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "cli-hello-world-ivy-compat:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "cli-hello-world-ivy-compat:serve:ci"
|
||||
},
|
||||
"ci-production": {
|
||||
"devServerTarget": "cli-hello-world-ivy-compat:serve:ci-production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##### Test Debug Utility #####
|
||||
##############################
|
||||
|
||||
# Use this script to run the ngcc integration test locally
|
||||
# in isolation from the other integration tests.
|
||||
# This is useful when debugging the ngcc code-base.
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Go to the project directory and build the release packages.
|
||||
(cd $(pwd)/../../ && yarn build)
|
||||
|
||||
# Workaround https://github.com/yarnpkg/yarn/issues/2165
|
||||
# Yarn will cache file://dist URIs and not update Angular code
|
||||
readonly cache=../.yarn_local_cache
|
||||
function rm_cache {
|
||||
rm -rf $cache
|
||||
}
|
||||
rm_cache
|
||||
mkdir $cache
|
||||
trap rm_cache EXIT
|
||||
|
||||
rm -rf dist
|
||||
rm -rf node_modules
|
||||
yarn install --cache-folder $cache
|
||||
yarn test
|
||||
@@ -1,37 +0,0 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
chromeDriver: process.env.CHROMEDRIVER_BIN,
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN,
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
},
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('cli-hello-world-ivy-compat App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('cli-hello-world-ivy-compat app is running!');
|
||||
});
|
||||
|
||||
it('the percent pipe should work', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getPipeContent()).toEqual('100 % awesome');
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get(browser.baseUrl) as Promise<any>;
|
||||
}
|
||||
|
||||
getTitleText() {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
|
||||
getPipeContent() {
|
||||
return element(by.css('app-root p')).getText();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/latest-app'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "cli-hello-world-ivy-compat",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "ng build --configuration production",
|
||||
"e2e": "ng e2e --port 0",
|
||||
"lint": "ng lint",
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"pretest": "ng version",
|
||||
"test": "ng test && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"tslib": "file:../../node_modules/tslib",
|
||||
"zone.js": "file:../../dist/zone.js-dist/archive/zone.js.tgz"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "file:../../node_modules/@angular-devkit/build-angular",
|
||||
"@angular/cli": "file:../../node_modules/@angular/cli",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||
"@types/jasmine": "file:../../node_modules/@types/jasmine",
|
||||
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
|
||||
"@types/node": "file:../../node_modules/@types/node",
|
||||
"codelyzer": "5.2.0",
|
||||
"jasmine-core": "3.5.0",
|
||||
"jasmine-spec-reporter": "4.2.1",
|
||||
"karma": "4.4.0",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "2.1.0",
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-jasmine-html-reporter": "1.4.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "5.18.0",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
}
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * * -->
|
||||
<!-- * * * * * * * to get started with your project! * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
|
||||
<style>
|
||||
:host {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
height: 60px;
|
||||
margin: -8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #1976d2;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toolbar img {
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.toolbar #twitter-logo {
|
||||
height: 40px;
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.toolbar #twitter-logo:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
margin: 32px auto;
|
||||
padding: 0 16px;
|
||||
max-width: 960px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
svg.material-icons {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
svg.material-icons:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.card svg.material-icons path {
|
||||
fill: #888;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #eee;
|
||||
background-color: #fafafa;
|
||||
height: 40px;
|
||||
width: 200px;
|
||||
margin: 0 8px 16px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.2s ease-in-out;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.card-container .card:not(:last-child) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.card.card-small {
|
||||
height: 16px;
|
||||
width: 168px;
|
||||
}
|
||||
|
||||
.card-container .card:not(.highlight-card) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-container .card:not(.highlight-card):hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 4px 17px rgba(black, 0.35);
|
||||
}
|
||||
|
||||
.card-container .card:not(.highlight-card):hover .material-icons path {
|
||||
fill: rgb(105, 103, 103);
|
||||
}
|
||||
|
||||
.card.highlight-card {
|
||||
background-color: #1976d2;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
width: auto;
|
||||
min-width: 30%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card.card.highlight-card span {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
svg#rocket {
|
||||
width: 80px;
|
||||
position: absolute;
|
||||
left: -10px;
|
||||
top: -24px;
|
||||
}
|
||||
|
||||
svg#rocket-smoke {
|
||||
height: calc(100vh - 95px);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 180px;
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited,
|
||||
a:hover {
|
||||
color: #1976d2;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #125699;
|
||||
}
|
||||
|
||||
.terminal {
|
||||
position: relative;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
border-radius: 6px;
|
||||
padding-top: 45px;
|
||||
margin-top: 8px;
|
||||
overflow: hidden;
|
||||
background-color: rgb(15, 15, 16);
|
||||
}
|
||||
|
||||
.terminal::before {
|
||||
content: "\2022 \2022 \2022";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
background: rgb(58, 58, 58);
|
||||
color: #c2c3c4;
|
||||
width: 100%;
|
||||
font-size: 2rem;
|
||||
line-height: 0;
|
||||
padding: 14px 0;
|
||||
text-indent: 4px;
|
||||
}
|
||||
|
||||
.terminal pre {
|
||||
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
||||
color: white;
|
||||
padding: 0 1rem 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.circle-link {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 40px;
|
||||
margin: 8px;
|
||||
background-color: white;
|
||||
border: 1px solid #eeeeee;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
transition: 1s ease-out;
|
||||
}
|
||||
|
||||
.circle-link:hover {
|
||||
transform: translateY(-0.25rem);
|
||||
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.github-star-badge {
|
||||
color: #24292e;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
padding: 3px 10px;
|
||||
border: 1px solid rgba(27,31,35,.2);
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
|
||||
margin-left: 4px;
|
||||
font-weight: 600;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
|
||||
}
|
||||
|
||||
.github-star-badge:hover {
|
||||
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
|
||||
border-color: rgba(27,31,35,.35);
|
||||
background-position: -.5em;
|
||||
}
|
||||
|
||||
.github-star-badge .material-icons {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
svg#clouds {
|
||||
position: fixed;
|
||||
bottom: -160px;
|
||||
left: -230px;
|
||||
z-index: -10;
|
||||
width: 1920px;
|
||||
}
|
||||
|
||||
|
||||
/* Responsive Styles */
|
||||
@media screen and (max-width: 767px) {
|
||||
|
||||
.card-container > *:not(.circle-link) ,
|
||||
.terminal {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card:not(.highlight-card) {
|
||||
height: 16px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.card.highlight-card span {
|
||||
margin-left: 72px;
|
||||
}
|
||||
|
||||
svg#rocket-smoke {
|
||||
right: 120px;
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 575px) {
|
||||
svg#rocket-smoke {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>{{ 1 | percent }} awesome</p>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="toolbar" role="banner">
|
||||
<img
|
||||
width="40"
|
||||
alt="Angular Logo"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
|
||||
/>
|
||||
<span>Welcome</span>
|
||||
<div class="spacer"></div>
|
||||
<a aria-label="Angular on twitter" target="_blank" rel="noopener" href="https://twitter.com/angular" title="Twitter">
|
||||
<svg id="twitter-logo" height="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
|
||||
<rect width="400" height="400" fill="none"/>
|
||||
<path d="M153.62,301.59c94.34,0,145.94-78.16,145.94-145.94,0-2.22,0-4.43-.15-6.63A104.36,104.36,0,0,0,325,122.47a102.38,102.38,0,0,1-29.46,8.07,51.47,51.47,0,0,0,22.55-28.37,102.79,102.79,0,0,1-32.57,12.45,51.34,51.34,0,0,0-87.41,46.78A145.62,145.62,0,0,1,92.4,107.81a51.33,51.33,0,0,0,15.88,68.47A50.91,50.91,0,0,1,85,169.86c0,.21,0,.43,0,.65a51.31,51.31,0,0,0,41.15,50.28,51.21,51.21,0,0,1-23.16.88,51.35,51.35,0,0,0,47.92,35.62,102.92,102.92,0,0,1-63.7,22A104.41,104.41,0,0,1,75,278.55a145.21,145.21,0,0,0,78.62,23" fill="#fff"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="content" role="main">
|
||||
|
||||
<!-- Highlight Card -->
|
||||
<div class="card highlight-card card-small">
|
||||
|
||||
<svg id="rocket" alt="Rocket Ship" xmlns="http://www.w3.org/2000/svg" width="101.678" height="101.678" viewBox="0 0 101.678 101.678">
|
||||
<g id="Group_83" data-name="Group 83" transform="translate(-141 -696)">
|
||||
<circle id="Ellipse_8" data-name="Ellipse 8" cx="50.839" cy="50.839" r="50.839" transform="translate(141 696)" fill="#dd0031"/>
|
||||
<g id="Group_47" data-name="Group 47" transform="translate(165.185 720.185)">
|
||||
<path id="Path_33" data-name="Path 33" d="M3.4,42.615a3.084,3.084,0,0,0,3.553,3.553,21.419,21.419,0,0,0,12.215-6.107L9.511,30.4A21.419,21.419,0,0,0,3.4,42.615Z" transform="translate(0.371 3.363)" fill="#fff"/>
|
||||
<path id="Path_34" data-name="Path 34" d="M53.3,3.221A3.09,3.09,0,0,0,50.081,0,48.227,48.227,0,0,0,18.322,13.437c-6-1.666-14.991-1.221-18.322,7.218A33.892,33.892,0,0,1,9.439,25.1l-.333.666a3.013,3.013,0,0,0,.555,3.553L23.985,43.641a2.9,2.9,0,0,0,3.553.555l.666-.333A33.892,33.892,0,0,1,32.647,53.3c8.55-3.664,8.884-12.326,7.218-18.322A48.227,48.227,0,0,0,53.3,3.221ZM34.424,9.772a6.439,6.439,0,1,1,9.106,9.106,6.368,6.368,0,0,1-9.106,0A6.467,6.467,0,0,1,34.424,9.772Z" transform="translate(0 0.005)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<span>{{ title }} app is running!</span>
|
||||
|
||||
<svg id="rocket-smoke" alt="Rocket Ship Smoke" xmlns="http://www.w3.org/2000/svg" width="516.119" height="1083.632" viewBox="0 0 516.119 1083.632">
|
||||
<path id="Path_40" data-name="Path 40" d="M644.6,141S143.02,215.537,147.049,870.207s342.774,201.755,342.774,201.755S404.659,847.213,388.815,762.2c-27.116-145.51-11.551-384.124,271.9-609.1C671.15,139.365,644.6,141,644.6,141Z" transform="translate(-147.025 -140.939)" fill="#f5f5f5"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Resources -->
|
||||
<h2>Resources</h2>
|
||||
<p>Here are some links to help you get started:</p>
|
||||
|
||||
<div class="card-container">
|
||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/tutorial">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></svg>
|
||||
|
||||
<span>Learn Angular</span>
|
||||
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a>
|
||||
|
||||
<a class="card" target="_blank" rel="noopener" href="https://angular.io/cli">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
||||
|
||||
<span>CLI Documentation</span>
|
||||
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
||||
</a>
|
||||
|
||||
<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg>
|
||||
|
||||
<span>Angular Blog</span>
|
||||
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Next Steps -->
|
||||
<h2>Next Steps</h2>
|
||||
<p>What do you want to do next with your app?</p>
|
||||
|
||||
<input type="hidden" #selection>
|
||||
|
||||
<div class="card-container">
|
||||
<div class="card card-small" (click)="selection.value = 'component'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>New Component</span>
|
||||
</div>
|
||||
|
||||
<div class="card card-small" (click)="selection.value = 'material'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>Angular Material</span>
|
||||
</div>
|
||||
|
||||
<div class="card card-small" (click)="selection.value = 'pwa'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>Add PWA Support</span>
|
||||
</div>
|
||||
|
||||
<div class="card card-small" (click)="selection.value = 'dependency'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>Add Dependency</span>
|
||||
</div>
|
||||
|
||||
<div class="card card-small" (click)="selection.value = 'test'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>Run and Watch Tests</span>
|
||||
</div>
|
||||
|
||||
<div class="card card-small" (click)="selection.value = 'build'" tabindex="0">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||
|
||||
<span>Build for Production</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terminal -->
|
||||
<div class="terminal" [ngSwitch]="selection.value">
|
||||
<pre *ngSwitchDefault>ng generate component xyz</pre>
|
||||
<pre *ngSwitchCase="'material'">ng add @angular/material</pre>
|
||||
<pre *ngSwitchCase="'pwa'">ng add @angular/pwa</pre>
|
||||
<pre *ngSwitchCase="'dependency'">ng add _____</pre>
|
||||
<pre *ngSwitchCase="'test'">ng test</pre>
|
||||
<pre *ngSwitchCase="'build'">ng build --prod</pre>
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="card-container">
|
||||
<a class="circle-link" title="Animations" href="https://angular.io/guide/animations" target="_blank" rel="noopener">
|
||||
<svg id="Group_20" data-name="Group 20" xmlns="http://www.w3.org/2000/svg" width="21.813" height="23.453" viewBox="0 0 21.813 23.453">
|
||||
<path id="Path_15" data-name="Path 15" d="M4099.584,972.736h0l-10.882,3.9,1.637,14.4,9.245,5.153,9.245-5.153,1.686-14.4Z" transform="translate(-4088.702 -972.736)" fill="#ffa726"/>
|
||||
<path id="Path_16" data-name="Path 16" d="M4181.516,972.736v23.453l9.245-5.153,1.686-14.4Z" transform="translate(-4170.633 -972.736)" fill="#fb8c00"/>
|
||||
<path id="Path_17" data-name="Path 17" d="M4137.529,1076.127l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1058.315)" fill="#ffe0b2"/>
|
||||
<path id="Path_18" data-name="Path 18" d="M4137.529,1051.705l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1036.757)" fill="#fff3e0"/>
|
||||
<path id="Path_19" data-name="Path 19" d="M4137.529,1027.283l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1015.199)" fill="#fff"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a class="circle-link" title="CLI" href="https://cli.angular.io/" target="_blank" rel="noopener">
|
||||
<svg alt="Angular CLI Logo" xmlns="http://www.w3.org/2000/svg" width="21.762" height="23.447" viewBox="0 0 21.762 23.447">
|
||||
<g id="Group_21" data-name="Group 21" transform="translate(0)">
|
||||
<path id="Path_20" data-name="Path 20" d="M2660.313,313.618h0l-10.833,3.9,1.637,14.4,9.2,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-2649.48 -313.618)" fill="#37474f"/>
|
||||
<path id="Path_21" data-name="Path 21" d="M2741.883,313.618v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-2731.05 -313.618)" fill="#263238"/>
|
||||
<path id="Path_22" data-name="Path 22" d="M2692.293,379.169h11.724V368.618h-11.724Zm11.159-.6h-10.608v-9.345h10.621v9.345Z" transform="translate(-2687.274 -362.17)" fill="#fff"/>
|
||||
<path id="Path_23" data-name="Path 23" d="M2709.331,393.688l.4.416,2.265-2.28-2.294-2.294-.4.4,1.893,1.893Z" transform="translate(-2702.289 -380.631)" fill="#fff"/>
|
||||
<rect id="Rectangle_12" data-name="Rectangle 12" width="3.517" height="0.469" transform="translate(9.709 13.744)" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a class="circle-link" title="Protractor" href="https://www.protractortest.org/" target="_blank" rel="noopener">
|
||||
<svg alt="Angular Protractor Logo" xmlns="http://www.w3.org/2000/svg" width="21.81" height="23.447" viewBox="0 0 21.81 23.447">
|
||||
<g id="Group_26" data-name="Group 26" transform="translate(0)">
|
||||
<path id="Path_28" data-name="Path 28" d="M4620.155,311.417h0l-10.881,3.9,1.637,14.4,9.244,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-4609.274 -311.417)" fill="#e13439"/>
|
||||
<path id="Path_29" data-name="Path 29" d="M4702.088,311.417v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-4691.207 -311.417)" fill="#b52f32"/>
|
||||
<path id="Path_30" data-name="Path 30" d="M4651.044,369.58v-.421h1.483a7.6,7.6,0,0,0-2.106-5.052l-1.123,1.123-.3-.3,1.122-1.121a7.588,7.588,0,0,0-4.946-2.055v1.482h-.421v-1.485a7.589,7.589,0,0,0-5.051,2.058l1.122,1.121-.3.3-1.123-1.123a7.591,7.591,0,0,0-2.106,5.052h1.482v.421h-1.489v1.734h15.241V369.58Zm-10.966-.263a4.835,4.835,0,0,1,9.67,0Z" transform="translate(-4634.008 -355.852)" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a class="circle-link" title="Find a Local Meetup" href="https://www.meetup.com/find/?keywords=angular" target="_blank" rel="noopener">
|
||||
<svg alt="Meetup Logo" xmlns="http://www.w3.org/2000/svg" width="24.607" height="23.447" viewBox="0 0 24.607 23.447">
|
||||
<path id="logo--mSwarm" d="M21.221,14.95A4.393,4.393,0,0,1,17.6,19.281a4.452,4.452,0,0,1-.8.069c-.09,0-.125.035-.154.117a2.939,2.939,0,0,1-2.506,2.091,2.868,2.868,0,0,1-2.248-.624.168.168,0,0,0-.245-.005,3.926,3.926,0,0,1-2.589.741,4.015,4.015,0,0,1-3.7-3.347,2.7,2.7,0,0,1-.043-.38c0-.106-.042-.146-.143-.166a3.524,3.524,0,0,1-1.516-.69A3.623,3.623,0,0,1,2.23,14.557a3.66,3.66,0,0,1,1.077-3.085.138.138,0,0,0,.026-.2,3.348,3.348,0,0,1-.451-1.821,3.46,3.46,0,0,1,2.749-3.28.44.44,0,0,0,.355-.281,5.072,5.072,0,0,1,3.863-3,5.028,5.028,0,0,1,3.555.666.31.31,0,0,0,.271.03A4.5,4.5,0,0,1,18.3,4.7a4.4,4.4,0,0,1,1.334,2.751,3.658,3.658,0,0,1,.022.706.131.131,0,0,0,.1.157,2.432,2.432,0,0,1,1.574,1.645,2.464,2.464,0,0,1-.7,2.616c-.065.064-.051.1-.014.166A4.321,4.321,0,0,1,21.221,14.95ZM13.4,14.607a2.09,2.09,0,0,0,1.409,1.982,4.7,4.7,0,0,0,1.275.221,1.807,1.807,0,0,0,.9-.151.542.542,0,0,0,.321-.545.558.558,0,0,0-.359-.534,1.2,1.2,0,0,0-.254-.078c-.262-.047-.526-.086-.787-.138a.674.674,0,0,1-.617-.75,3.394,3.394,0,0,1,.218-1.109c.217-.658.509-1.286.79-1.918a15.609,15.609,0,0,0,.745-1.86,1.95,1.95,0,0,0,.06-1.073,1.286,1.286,0,0,0-1.051-1.033,1.977,1.977,0,0,0-1.521.2.339.339,0,0,1-.446-.042c-.1-.092-.2-.189-.307-.284a1.214,1.214,0,0,0-1.643-.061,7.563,7.563,0,0,1-.614.512A.588.588,0,0,1,10.883,8c-.215-.115-.437-.215-.659-.316a2.153,2.153,0,0,0-.695-.248A2.091,2.091,0,0,0,7.541,8.562a9.915,9.915,0,0,0-.405.986c-.559,1.545-1.015,3.123-1.487,4.7a1.528,1.528,0,0,0,.634,1.777,1.755,1.755,0,0,0,1.5.211,1.35,1.35,0,0,0,.824-.858c.543-1.281,1.032-2.584,1.55-3.875.142-.355.28-.712.432-1.064a.548.548,0,0,1,.851-.24.622.622,0,0,1,.185.539,2.161,2.161,0,0,1-.181.621c-.337.852-.68,1.7-1.018,2.552a2.564,2.564,0,0,0-.173.528.624.624,0,0,0,.333.71,1.073,1.073,0,0,0,.814.034,1.22,1.22,0,0,0,.657-.655q.758-1.488,1.511-2.978.35-.687.709-1.37a1.073,1.073,0,0,1,.357-.434.43.43,0,0,1,.463-.016.373.373,0,0,1,.153.387.7.7,0,0,1-.057.236c-.065.157-.127.316-.2.469-.42.883-.846,1.763-1.262,2.648A2.463,2.463,0,0,0,13.4,14.607Zm5.888,6.508a1.09,1.09,0,0,0-2.179.006,1.09,1.09,0,0,0,2.179-.006ZM1.028,12.139a1.038,1.038,0,1,0,.01-2.075,1.038,1.038,0,0,0-.01,2.075ZM13.782.528a1.027,1.027,0,1,0-.011,2.055A1.027,1.027,0,0,0,13.782.528ZM22.21,6.95a.882.882,0,0,0-1.763.011A.882.882,0,0,0,22.21,6.95ZM4.153,4.439a.785.785,0,1,0,.787-.78A.766.766,0,0,0,4.153,4.439Zm8.221,18.22a.676.676,0,1,0-.677.666A.671.671,0,0,0,12.374,22.658ZM22.872,12.2a.674.674,0,0,0-.665.665.656.656,0,0,0,.655.643.634.634,0,0,0,.655-.644A.654.654,0,0,0,22.872,12.2ZM7.171-.123A.546.546,0,0,0,6.613.43a.553.553,0,1,0,1.106,0A.539.539,0,0,0,7.171-.123ZM24.119,9.234a.507.507,0,0,0-.493.488.494.494,0,0,0,.494.494.48.48,0,0,0,.487-.483A.491.491,0,0,0,24.119,9.234Zm-19.454,9.7a.5.5,0,0,0-.488-.488.491.491,0,0,0-.487.5.483.483,0,0,0,.491.479A.49.49,0,0,0,4.665,18.936Z" transform="translate(0 0.123)" fill="#f64060"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a class="circle-link" title="Join the Conversation on Gitter" href="https://gitter.im/angular/angular" target="_blank" rel="noopener">
|
||||
<svg alt="Gitter Logo" xmlns="http://www.w3.org/2000/svg" width="19.447" height="19.447" viewBox="0 0 19.447 19.447">
|
||||
<g id="Group_40" data-name="Group 40" transform="translate(-1612 -405)">
|
||||
<rect id="Rectangle_19" data-name="Rectangle 19" width="19.447" height="19.447" transform="translate(1612 405)" fill="#e60257"/>
|
||||
<g id="gitter" transform="translate(1617.795 408.636)">
|
||||
<g id="Group_33" data-name="Group 33" transform="translate(0 0)">
|
||||
<rect id="Rectangle_15" data-name="Rectangle 15" width="1.04" height="9.601" transform="translate(2.304 2.324)" fill="#fff"/>
|
||||
<rect id="Rectangle_16" data-name="Rectangle 16" width="1.04" height="9.601" transform="translate(4.607 2.324)" fill="#fff"/>
|
||||
<rect id="Rectangle_17" data-name="Rectangle 17" width="1.04" height="4.648" transform="translate(6.91 2.324)" fill="#fff"/>
|
||||
<rect id="Rectangle_18" data-name="Rectangle 18" width="1.04" height="6.971" transform="translate(0 0)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer>
|
||||
Love Angular?
|
||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener"> Give our repo a star.
|
||||
<div class="github-star-badge">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
|
||||
Star
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
|
||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" fill="#1976d2"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<svg id="clouds" alt="Gray Clouds Background" xmlns="http://www.w3.org/2000/svg" width="2611.084" height="485.677" viewBox="0 0 2611.084 485.677">
|
||||
<path id="Path_39" data-name="Path 39" d="M2379.709,863.793c10-93-77-171-168-149-52-114-225-105-264,15-75,3-140,59-152,133-30,2.83-66.725,9.829-93.5,26.25-26.771-16.421-63.5-23.42-93.5-26.25-12-74-77-130-152-133-39-120-212-129-264-15-54.084-13.075-106.753,9.173-138.488,48.9-31.734-39.726-84.4-61.974-138.487-48.9-52-114-225-105-264,15a162.027,162.027,0,0,0-103.147,43.044c-30.633-45.365-87.1-72.091-145.206-58.044-52-114-225-105-264,15-75,3-140,59-152,133-53,5-127,23-130,83-2,42,35,72,70,86,49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33,61.112,8.015,113.854-5.72,150.492-29.764a165.62,165.62,0,0,0,110.861-3.236c47,94,178,113,251,33,31.385,4.116,60.563,2.495,86.487-3.311,25.924,5.806,55.1,7.427,86.488,3.311,73,80,204,61,251-33a165.625,165.625,0,0,0,120,0c51,13,108,15,157-5a147.188,147.188,0,0,0,33.5-18.694,147.217,147.217,0,0,0,33.5,18.694c49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33C2446.709,1093.793,2554.709,922.793,2379.709,863.793Z" transform="translate(142.69 -634.312)" fill="#eee"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
|
||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
||||
@@ -1,32 +0,0 @@
|
||||
import {TestBed, waitForAsync} from '@angular/core/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed
|
||||
.configureTestingModule({
|
||||
declarations: [AppComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'cli-hello-world-ivy-compat'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('cli-hello-world-ivy-compat');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent)
|
||||
.toContain('cli-hello-world-ivy-compat app is running!');
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'cli-hello-world-ivy-compat';
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { LOCALE_ID, NgModule } from '@angular/core';
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
// adding this code to detect issues like https://github.com/angular/angular-cli/issues/10322
|
||||
registerLocaleData(localeFr);
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
providers: [{ provide: LOCALE_ID, useValue: 'fr' }],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
@@ -1,3 +0,0 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 948 B |
@@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CliHelloWorldIvyCompat</title>
|
||||
<base href="/">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +0,0 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
@@ -1 +0,0 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@@ -1,14 +0,0 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"array-type": false,
|
||||
"arrow-parens": false,
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"interface-name": false,
|
||||
"max-classes-per-file": false,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"static-method",
|
||||
"instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-consecutive-blank-lines": false,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-empty": false,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"object-literal-sort-keys": false,
|
||||
"ordered-imports": false,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"trailing-comma": false,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
"no-inputs-metadata-property": true,
|
||||
"no-output-native": true,
|
||||
"no-output-on-prefix": true,
|
||||
"no-output-rename": true,
|
||||
"no-outputs-metadata-property": true,
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "15.2.0-next.4",
|
||||
"version": "15.2.0",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
|
||||
@@ -83,13 +83,7 @@ export class HttpClientXsrfModule {
|
||||
* with supporting services for HTTP communications.
|
||||
*/
|
||||
providers: [
|
||||
provideHttpClient(
|
||||
withInterceptorsFromDi(),
|
||||
withXsrfConfiguration({
|
||||
cookieName: XSRF_DEFAULT_COOKIE_NAME,
|
||||
headerName: XSRF_DEFAULT_HEADER_NAME,
|
||||
}),
|
||||
),
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
],
|
||||
})
|
||||
export class HttpClientModule {
|
||||
|
||||
@@ -10,6 +10,7 @@ export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAl
|
||||
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
|
||||
export {DefaultImportTracker} from './src/default';
|
||||
export {AbsoluteModuleStrategy, assertSuccessfulReferenceEmit, EmittedReference, FailedEmitResult, ImportedFile, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitKind, ReferenceEmitResult, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter';
|
||||
export {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './src/patch_alias_reference_resolution';
|
||||
export {Reexport} from './src/reexport';
|
||||
export {OwningModule, Reference} from './src/references';
|
||||
export {ModuleResolver} from './src/resolver';
|
||||
|
||||
@@ -11,6 +11,8 @@ import ts from 'typescript';
|
||||
|
||||
import {getSourceFile} from '../../util/src/typescript';
|
||||
|
||||
import {loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution';
|
||||
|
||||
const DefaultImportDeclaration = Symbol('DefaultImportDeclaration');
|
||||
|
||||
interface WithDefaultImportDeclaration {
|
||||
@@ -57,29 +59,31 @@ export function getDefaultImportDeclaration(expr: WrappedNodeExpr<unknown>): ts.
|
||||
* a dangling reference, as TS will elide the import because it was only used in the type position
|
||||
* originally.
|
||||
*
|
||||
* To avoid this, the compiler must "touch" the imports with `ts.getMutableClone`, and should
|
||||
* only do this for imports which are actually consumed. The `DefaultImportTracker` keeps track of
|
||||
* these imports as they're encountered and emitted, and implements a transform which can correctly
|
||||
* flag the imports as required.
|
||||
* To avoid this, the compiler must patch the emit resolver, and should only do this for imports
|
||||
* which are actually consumed. The `DefaultImportTracker` keeps track of these imports as they're
|
||||
* encountered and emitted, and implements a transform which can correctly flag the imports as
|
||||
* required.
|
||||
*
|
||||
* This problem does not exist for non-default imports as the compiler can easily insert
|
||||
* "import * as X" style imports for those, and the "X" identifier survives transformation.
|
||||
*/
|
||||
export class DefaultImportTracker {
|
||||
/**
|
||||
* A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
|
||||
* a given `ts.SourceFile` and need to be preserved.
|
||||
* A `Map` which tracks the `Set` of `ts.ImportClause`s for default imports that were used in
|
||||
* a given file name.
|
||||
*/
|
||||
private sourceFileToUsedImports = new Map<ts.SourceFile, Set<ts.ImportDeclaration>>();
|
||||
private sourceFileToUsedImports = new Map<string, Set<ts.ImportClause>>();
|
||||
|
||||
recordUsedImport(importDecl: ts.ImportDeclaration): void {
|
||||
const sf = getSourceFile(importDecl);
|
||||
if (importDecl.importClause) {
|
||||
const sf = getSourceFile(importDecl);
|
||||
|
||||
// Add the default import declaration to the set of used import declarations for the file.
|
||||
if (!this.sourceFileToUsedImports.has(sf)) {
|
||||
this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>());
|
||||
// Add the default import declaration to the set of used import declarations for the file.
|
||||
if (!this.sourceFileToUsedImports.has(sf.fileName)) {
|
||||
this.sourceFileToUsedImports.set(sf.fileName, new Set<ts.ImportClause>());
|
||||
}
|
||||
this.sourceFileToUsedImports.get(sf.fileName)!.add(importDecl.importClause);
|
||||
}
|
||||
this.sourceFileToUsedImports.get(sf)!.add(importDecl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,65 +93,25 @@ export class DefaultImportTracker {
|
||||
* This transformer must run after any other transformers which call `recordUsedImport`.
|
||||
*/
|
||||
importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> {
|
||||
return (context: ts.TransformationContext) => {
|
||||
return (sf: ts.SourceFile) => {
|
||||
return this.transformSourceFile(sf);
|
||||
return context => {
|
||||
let clausesToPreserve: Set<ts.Declaration>|null = null;
|
||||
|
||||
return sourceFile => {
|
||||
const clausesForFile = this.sourceFileToUsedImports.get(sourceFile.fileName);
|
||||
|
||||
if (clausesForFile !== undefined) {
|
||||
for (const clause of clausesForFile) {
|
||||
// Initialize the patch lazily so that apps that
|
||||
// don't use default imports aren't patched.
|
||||
if (clausesToPreserve === null) {
|
||||
clausesToPreserve = loadIsReferencedAliasDeclarationPatch(context);
|
||||
}
|
||||
clausesToPreserve.add(clause);
|
||||
}
|
||||
}
|
||||
|
||||
return sourceFile;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a `ts.SourceFile` and replace any `ts.ImportDeclaration`s.
|
||||
*/
|
||||
private transformSourceFile(sf: ts.SourceFile): ts.SourceFile {
|
||||
const originalSf = ts.getOriginalNode(sf) as ts.SourceFile;
|
||||
// Take a fast path if no import declarations need to be preserved in the file.
|
||||
if (!this.sourceFileToUsedImports.has(originalSf)) {
|
||||
return sf;
|
||||
}
|
||||
|
||||
// There are declarations that need to be preserved.
|
||||
const importsToPreserve = this.sourceFileToUsedImports.get(originalSf)!;
|
||||
|
||||
// Generate a new statement list which preserves any imports present in `importsToPreserve`.
|
||||
const statements = sf.statements.map(stmt => {
|
||||
if (ts.isImportDeclaration(stmt) && importsToPreserve.has(stmt)) {
|
||||
// Preserving an import that's marked as unreferenced (type-only) is tricky in TypeScript.
|
||||
//
|
||||
// Various approaches have been tried, with mixed success:
|
||||
//
|
||||
// 1. Using `ts.updateImportDeclaration` does not cause the import to be retained.
|
||||
//
|
||||
// 2. Using `ts.factory.createImportDeclaration` with the same `ts.ImportClause` causes the
|
||||
// import to correctly be retained, but when emitting CommonJS module format code,
|
||||
// references to the imported value will not match the import variable.
|
||||
//
|
||||
// 3. Emitting "import * as" imports instead generates the correct import variable, but
|
||||
// references are missing the ".default" access. This happens to work for tsickle code
|
||||
// with goog.module transformations as tsickle strips the ".default" anyway.
|
||||
//
|
||||
// 4. It's possible to trick TypeScript by setting `ts.NodeFlag.Synthesized` on the import
|
||||
// declaration. This causes the import to be correctly retained and generated, but can
|
||||
// violate invariants elsewhere in the compiler and cause crashes.
|
||||
//
|
||||
// 5. Using `ts.getMutableClone` seems to correctly preserve the import and correctly
|
||||
// generate references to the import variable across all module types.
|
||||
//
|
||||
// Therefore, option 5 is the one used here. It seems to be implemented as the correct way
|
||||
// to perform option 4, which preserves all the compiler's invariants.
|
||||
//
|
||||
// TODO(alxhub): discuss with the TypeScript team and determine if there's a better way to
|
||||
// deal with this issue.
|
||||
// tslint:disable-next-line: ban
|
||||
stmt = ts.getMutableClone(stmt);
|
||||
}
|
||||
return stmt;
|
||||
});
|
||||
|
||||
// Save memory - there's no need to keep these around once the transform has run for the given
|
||||
// file.
|
||||
this.sourceFileToUsedImports.delete(originalSf);
|
||||
|
||||
return ts.factory.updateSourceFile(sf, statements);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -11,7 +11,7 @@ import ts from 'typescript';
|
||||
/**
|
||||
* Describes a TypeScript transformation context with the internal emit
|
||||
* resolver exposed. There are requests upstream in TypeScript to expose
|
||||
* that as public API: https://github.com/microsoft/TypeScript/issues/17516..
|
||||
* that as public API: https://github.com/microsoft/TypeScript/issues/17516.
|
||||
*/
|
||||
interface TransformationContextWithResolver extends ts.TransformationContext {
|
||||
getEmitResolver: () => EmitResolver;
|
||||
@@ -61,7 +61,7 @@ interface EmitResolver {
|
||||
* The patch we apply to tell TypeScript about actual referenced aliases (i.e. imported symbols),
|
||||
* matches conceptually with the logic that runs internally in TypeScript when the
|
||||
* `emitDecoratorMetadata` flag is enabled. TypeScript basically surfaces the same problem and
|
||||
* solves it conceptually the same way, but obviously doesn't need to access an `@internal` API.
|
||||
* solves it conceptually the same way, but obviously doesn't need to access an internal API.
|
||||
*
|
||||
* The set that is returned by this function is meant to be filled with import declaration nodes
|
||||
* that have been referenced in a value-position by the transform, such the installed patch can
|
||||
@@ -129,9 +129,8 @@ function isTransformationContextWithEmitResolver(context: ts.TransformationConte
|
||||
*/
|
||||
function throwIncompatibleTransformationContextError(): never {
|
||||
throw Error(
|
||||
'Unable to downlevel Angular decorators due to an incompatible TypeScript ' +
|
||||
'version.\nIf you recently updated TypeScript and this issue surfaces now, consider ' +
|
||||
'downgrading.\n\n' +
|
||||
'Angular compiler is incompatible with this version of the TypeScript compiler.\n\n' +
|
||||
'If you recently updated TypeScript and this issue surfaces now, consider downgrading.\n\n' +
|
||||
'Please report an issue on the Angular repositories when this issue ' +
|
||||
'surfaces and you are using a supposedly compatible TypeScript version.');
|
||||
}
|
||||
@@ -6,6 +6,7 @@ ts_library(
|
||||
name = "downlevel_decorators_transform",
|
||||
srcs = glob(["*.ts"]),
|
||||
deps = [
|
||||
"//packages/compiler-cli/src/ngtsc/imports",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"//packages/compiler-cli/src/ngtsc/ts_compatibility",
|
||||
"@npm//typescript",
|
||||
|
||||
+1
-2
@@ -8,10 +8,9 @@
|
||||
|
||||
import ts from 'typescript';
|
||||
|
||||
import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from '../../ngtsc/imports';
|
||||
import {Decorator, ReflectionHost} from '../../ngtsc/reflection';
|
||||
|
||||
import {isAliasImportDeclaration, loadIsReferencedAliasDeclarationPatch} from './patch_alias_reference_resolution';
|
||||
|
||||
/**
|
||||
* Whether a given decorator should be treated as an Angular decorator.
|
||||
* Either it's used in @angular/core, or it's imported from there.
|
||||
|
||||
@@ -16,7 +16,7 @@ import {getAngularDecorators} from '../../utils/ng_decorators';
|
||||
import {closestNode} from '../../utils/typescript/nodes';
|
||||
|
||||
import {ComponentImportsRemapper, convertNgModuleDeclarationToStandalone, extractDeclarationsFromModule, findTestObjectsToMigrate, migrateTestDeclarations} from './to-standalone';
|
||||
import {ChangeTracker, findClassDeclaration, findLiteralProperty, getNodeLookup, getRelativeImportPath, ImportRemapper, NamedClassDeclaration, NodeLookup, offsetsToNodes, ReferenceResolver, UniqueItemTracker} from './util';
|
||||
import {ChangeTracker, closestOrSelf, findClassDeclaration, findLiteralProperty, getNodeLookup, getRelativeImportPath, ImportRemapper, isClassReferenceInAngularModule, NamedClassDeclaration, NodeLookup, offsetsToNodes, ReferenceResolver, UniqueItemTracker} from './util';
|
||||
|
||||
/** Information extracted from a `bootstrapModule` call necessary to migrate it. */
|
||||
interface BootstrapCallAnalysis {
|
||||
@@ -308,6 +308,22 @@ function migrateImportsForBootstrapCall(
|
||||
tracker.addImport(sourceFile, 'provideNoopAnimations', animationsImport), [], []));
|
||||
continue;
|
||||
}
|
||||
|
||||
// `HttpClientModule` can be replaced with `provideHttpClient()`.
|
||||
const httpClientModule = 'common/http';
|
||||
const httpClientImport = `@angular/${httpClientModule}`;
|
||||
if (isClassReferenceInAngularModule(
|
||||
element, 'HttpClientModule', httpClientModule, typeChecker)) {
|
||||
const callArgs = [
|
||||
// we add `withInterceptorsFromDi()` to the call to ensure that class-based interceptors
|
||||
// still work
|
||||
ts.factory.createCallExpression(
|
||||
tracker.addImport(sourceFile, 'withInterceptorsFromDi', httpClientImport), [], [])
|
||||
];
|
||||
providersInNewCall.push(ts.factory.createCallExpression(
|
||||
tracker.addImport(sourceFile, 'provideHttpClient', httpClientImport), [], callArgs));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const target =
|
||||
@@ -658,16 +674,6 @@ function isExported(node: ts.Node): node is ts.Node {
|
||||
false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the closest node that matches a predicate, including the node that the search started from.
|
||||
* @param node Node from which to start the search.
|
||||
* @param predicate Predicate that the result needs to pass.
|
||||
*/
|
||||
function closestOrSelf<T extends ts.Node>(node: ts.Node, predicate: (n: ts.Node) => n is T): T|
|
||||
null {
|
||||
return predicate(node) ? node : closestNode(node, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a node is an exportable declaration, which means that it can either be exported or
|
||||
* it can be safely copied into another file.
|
||||
@@ -680,29 +686,6 @@ function isExportableDeclaration(node: ts.Node): node is ts.EnumDeclaration|ts.C
|
||||
ts.isTypeAliasDeclaration(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node is referring to a specific class declaration.
|
||||
* @param node Node that is being checked.
|
||||
* @param className Name of the class that the node might be referring to.
|
||||
* @param moduleName Name of the Angular module that should contain the class.
|
||||
* @param typeChecker
|
||||
*/
|
||||
function isClassReferenceInAngularModule(
|
||||
node: ts.Node, className: string, moduleName: string, typeChecker: ts.TypeChecker): boolean {
|
||||
const symbol = typeChecker.getTypeAtLocation(node).getSymbol();
|
||||
const externalName = `@angular/${moduleName}`;
|
||||
const internalName = `angular2/rc/packages/${moduleName}`;
|
||||
|
||||
return !!symbol?.declarations?.some(decl => {
|
||||
const closestClass = closestOrSelf(decl, ts.isClassDeclaration);
|
||||
const closestClassFileName = closestClass?.getSourceFile().fileName;
|
||||
return closestClass && closestClassFileName && closestClass.name &&
|
||||
ts.isIdentifier(closestClass.name) && closestClass.name.text === className &&
|
||||
(closestClassFileName.includes(externalName) ||
|
||||
closestClassFileName.includes(internalName));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index after the last import in a file. Can be used to insert new code into the file.
|
||||
* @param sourceFile File in which to search for imports.
|
||||
|
||||
@@ -15,7 +15,7 @@ import {getImportSpecifier} from '../../utils/typescript/imports';
|
||||
import {closestNode} from '../../utils/typescript/nodes';
|
||||
import {isReferenceToImport} from '../../utils/typescript/symbol';
|
||||
|
||||
import {ChangesByFile, ChangeTracker, findClassDeclaration, findLiteralProperty, ImportRemapper, NamedClassDeclaration} from './util';
|
||||
import {ChangesByFile, ChangeTracker, findClassDeclaration, findLiteralProperty, ImportRemapper, isClassReferenceInAngularModule, NamedClassDeclaration} from './util';
|
||||
|
||||
/**
|
||||
* Function that can be used to prcess the dependencies that
|
||||
@@ -602,8 +602,16 @@ function analyzeTestingModules(
|
||||
|
||||
const importsProp = findLiteralProperty(obj, 'imports');
|
||||
const importElements = importsProp && hasNgModuleMetadataElements(importsProp) ?
|
||||
// Filter out calls since they may be a `ModuleWithProviders`.
|
||||
importsProp.initializer.elements.filter(el => !ts.isCallExpression(el)) :
|
||||
importsProp.initializer.elements.filter(el => {
|
||||
// Filter out calls since they may be a `ModuleWithProviders`.
|
||||
return !ts.isCallExpression(el) &&
|
||||
// Also filter out the animations modules since they throw errors if they're imported
|
||||
// multiple times and it's common for apps to use the `NoopAnimationsModule` to
|
||||
// disable animations in screenshot tests.
|
||||
!isClassReferenceInAngularModule(
|
||||
el, /^BrowserAnimationsModule|NoopAnimationsModule$/,
|
||||
'platform-browser/animations', typeChecker);
|
||||
}) :
|
||||
null;
|
||||
|
||||
for (const decl of declarations) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {dirname, relative} from 'path';
|
||||
import ts from 'typescript';
|
||||
|
||||
import {ImportManager} from '../../utils/import_manager';
|
||||
import {closestNode} from '../../utils/typescript/nodes';
|
||||
|
||||
/** Mapping between a source file and the changes that have to be applied to it. */
|
||||
export type ChangesByFile = ReadonlyMap<ts.SourceFile, PendingChange[]>;
|
||||
@@ -391,3 +392,43 @@ export function knownInternalAliasRemapper(imports: PotentialImport[]) {
|
||||
{...current, symbolName: 'NgFor'} :
|
||||
current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the closest node that matches a predicate, including the node that the search started from.
|
||||
* @param node Node from which to start the search.
|
||||
* @param predicate Predicate that the result needs to pass.
|
||||
*/
|
||||
export function closestOrSelf<T extends ts.Node>(
|
||||
node: ts.Node, predicate: (n: ts.Node) => n is T): T|null {
|
||||
return predicate(node) ? node : closestNode(node, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node is referring to a specific class declaration.
|
||||
* @param node Node that is being checked.
|
||||
* @param className Name of the class that the node might be referring to.
|
||||
* @param moduleName Name of the Angular module that should contain the class.
|
||||
* @param typeChecker
|
||||
*/
|
||||
export function isClassReferenceInAngularModule(
|
||||
node: ts.Node, className: string|RegExp, moduleName: string,
|
||||
typeChecker: ts.TypeChecker): boolean {
|
||||
const symbol = typeChecker.getTypeAtLocation(node).getSymbol();
|
||||
const externalName = `@angular/${moduleName}`;
|
||||
const internalName = `angular2/rc/packages/${moduleName}`;
|
||||
|
||||
return !!symbol?.declarations?.some(decl => {
|
||||
const closestClass = closestOrSelf(decl, ts.isClassDeclaration);
|
||||
const closestClassFileName = closestClass?.getSourceFile().fileName;
|
||||
|
||||
if (!closestClass || !closestClassFileName || !closestClass.name ||
|
||||
!ts.isIdentifier(closestClass.name) ||
|
||||
(!closestClassFileName.includes(externalName) &&
|
||||
!closestClassFileName.includes(internalName))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof className === 'string' ? closestClass.name.text === className :
|
||||
className.test(closestClass.name.text);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -124,6 +124,16 @@ describe('standalone migration', () => {
|
||||
}
|
||||
`);
|
||||
|
||||
writeFile('/node_modules/@angular/common/http/index.d.ts', `
|
||||
import {ModuleWithProviders} from '@angular/core';
|
||||
|
||||
export declare class HttpClientModule {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<HttpClientModule, never>;
|
||||
static ɵmod: i0.ɵɵNgModuleDeclaration<HttpClientModule, never, never, never>;
|
||||
static ɵinj: i0.ɵɵInjectorDeclaration<HttpClientModule>;
|
||||
}
|
||||
`);
|
||||
|
||||
writeFile('/node_modules/@angular/core/testing/index.d.ts', `
|
||||
export declare class TestBed {
|
||||
static configureTestingModule(config: any): any;
|
||||
@@ -1274,6 +1284,82 @@ describe('standalone migration', () => {
|
||||
`));
|
||||
});
|
||||
|
||||
it('should not copy over the NoopAnimationsModule into the imports of a test component',
|
||||
async () => {
|
||||
writeFile('app.spec.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
||||
describe('bootstrapping an app', () => {
|
||||
it('should work', () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatCardModule, NoopAnimationsModule],
|
||||
declarations: [App]
|
||||
});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
expect(fixture.nativeElement.innerHTML).toBe('<hello>Hello</hello>');
|
||||
});
|
||||
});
|
||||
|
||||
@Component({template: 'hello'})
|
||||
class App {}
|
||||
`);
|
||||
|
||||
await runMigration('convert-to-standalone');
|
||||
|
||||
const content = stripWhitespace(tree.readContent('app.spec.ts'));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatCardModule, NoopAnimationsModule, App]
|
||||
});
|
||||
`));
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@Component({template: 'hello', standalone: true, imports: [MatCardModule]})
|
||||
class App {}
|
||||
`));
|
||||
});
|
||||
|
||||
it('should not copy over the BrowserAnimationsModule into the imports of a test component',
|
||||
async () => {
|
||||
writeFile('app.spec.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
||||
describe('bootstrapping an app', () => {
|
||||
it('should work', () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatCardModule, BrowserAnimationsModule],
|
||||
declarations: [App]
|
||||
});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
expect(fixture.nativeElement.innerHTML).toBe('<hello>Hello</hello>');
|
||||
});
|
||||
});
|
||||
|
||||
@Component({template: 'hello'})
|
||||
class App {}
|
||||
`);
|
||||
|
||||
await runMigration('convert-to-standalone');
|
||||
|
||||
const content = stripWhitespace(tree.readContent('app.spec.ts'));
|
||||
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatCardModule, BrowserAnimationsModule, App]
|
||||
});
|
||||
`));
|
||||
expect(content).toContain(stripWhitespace(`
|
||||
@Component({template: 'hello', standalone: true, imports: [MatCardModule]})
|
||||
class App {}
|
||||
`));
|
||||
});
|
||||
|
||||
it('should not move declarations that are not being migrated out of the declarations array',
|
||||
async () => {
|
||||
const appComponentContent = `
|
||||
@@ -1526,6 +1612,51 @@ describe('standalone migration', () => {
|
||||
`));
|
||||
});
|
||||
|
||||
it('should use the generated alias if a conflicting symbol already exists', async () => {
|
||||
writeFile('module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {MyComp} from './comp';
|
||||
import {MyButton} from './button';
|
||||
|
||||
@NgModule({declarations: [MyComp, MyButton], exports: [MyComp]})
|
||||
export class Mod {}
|
||||
`);
|
||||
|
||||
writeFile('comp.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
import {MyButton} from '@external/button';
|
||||
|
||||
MyButton.sayHello();
|
||||
|
||||
@Component({selector: 'my-comp', template: '<my-button>Hello</my-button>'})
|
||||
export class MyComp {}
|
||||
`);
|
||||
|
||||
writeFile('button.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({selector: 'my-button', template: '<ng-content></ng-content>'})
|
||||
export class MyButton {}
|
||||
`);
|
||||
|
||||
await runMigration('convert-to-standalone');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('comp.ts'))).toBe(stripWhitespace(`
|
||||
import {Component} from '@angular/core';
|
||||
import {MyButton} from '@external/button';
|
||||
import {MyButton as MyButton_1} from './button';
|
||||
|
||||
MyButton.sayHello();
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp', template: '<my-button>Hello</my-button>',
|
||||
standalone: true,
|
||||
imports: [MyButton_1]
|
||||
})
|
||||
export class MyComp {}
|
||||
`));
|
||||
});
|
||||
|
||||
it('should remove a module that only has imports and exports', async () => {
|
||||
writeFile('app.module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
@@ -3272,6 +3403,43 @@ describe('standalone migration', () => {
|
||||
`));
|
||||
});
|
||||
|
||||
it('should convert HttpClientModule references to provideHttpClient(withInterceptorsFromDi())',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
import {AppModule} from './app/app.module';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
|
||||
`);
|
||||
|
||||
writeFile('./app/app.module.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
|
||||
@Component({template: 'hello'})
|
||||
export class AppComponent {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [HttpClientModule]
|
||||
})
|
||||
export class AppModule {}
|
||||
`);
|
||||
|
||||
await runMigration('standalone-bootstrap');
|
||||
|
||||
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
|
||||
import {AppComponent} from './app/app.module';
|
||||
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
|
||||
import {withInterceptorsFromDi, provideHttpClient} from '@angular/common/http';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [provideHttpClient(withInterceptorsFromDi())]
|
||||
}).catch(e => console.error(e));
|
||||
`));
|
||||
});
|
||||
|
||||
it('should omit standalone directives from the imports array from the importProvidersFrom call',
|
||||
async () => {
|
||||
writeFile('main.ts', `
|
||||
|
||||
@@ -170,7 +170,7 @@ export class ImportManager {
|
||||
if (symbolName) {
|
||||
const {propertyName, name} = this._getImportParts(sourceFile, symbolName, alias);
|
||||
const importMap = this.newImports.get(sourceFile)!.namedImports;
|
||||
identifier = propertyName || name;
|
||||
identifier = name;
|
||||
|
||||
if (!importMap.has(moduleName)) {
|
||||
importMap.set(moduleName, []);
|
||||
|
||||
@@ -215,13 +215,12 @@ export function getInheritedInjectableDef<T>(type: any): ɵɵInjectableDeclarati
|
||||
|
||||
if (def) {
|
||||
const typeName = getTypeName(type);
|
||||
// TODO(FW-1307): Re-add ngDevMode when closure can handle it
|
||||
// ngDevMode &&
|
||||
console.warn(
|
||||
`DEPRECATED: DI is instantiating a token "${
|
||||
typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` +
|
||||
`This will become an error in a future version of Angular. Please add @Injectable() to the "${
|
||||
typeName}" class.`);
|
||||
ngDevMode &&
|
||||
console.warn(
|
||||
`DEPRECATED: DI is instantiating a token "${
|
||||
typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` +
|
||||
`This will become an error in a future version of Angular. Please add @Injectable() to the "${
|
||||
typeName}" class.`);
|
||||
return def;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -13,9 +13,9 @@ import {getFactoryDef} from '../render3/definition_factory';
|
||||
import {throwCyclicDependencyError, throwInvalidProviderError} from '../render3/errors_di';
|
||||
import {stringifyForError} from '../render3/util/stringify_utils';
|
||||
import {deepForEach} from '../util/array_utils';
|
||||
import {EMPTY_ARRAY} from '../util/empty';
|
||||
import {getClosureSafeProperty} from '../util/property';
|
||||
import {stringify} from '../util/stringify';
|
||||
import {EMPTY_ARRAY} from '../view';
|
||||
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {ENVIRONMENT_INITIALIZER} from './initializer_token';
|
||||
|
||||
@@ -195,7 +195,7 @@ export interface Directive {
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'child-dir',
|
||||
* outputs: [ 'bankNameChange' ]
|
||||
* outputs: [ 'bankNameChange' ],
|
||||
* template: `<input (input)="bankNameChange.emit($event.target.value)" />`
|
||||
* })
|
||||
* class ChildDir {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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 {EMPTY_ARRAY} from '../util/empty';
|
||||
@@ -401,6 +401,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NoopAnimationDriver"
|
||||
},
|
||||
@@ -440,6 +443,9 @@
|
||||
{
|
||||
"name": "REMOVAL_FLAG"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "RefCountOperator"
|
||||
},
|
||||
@@ -1310,9 +1316,6 @@
|
||||
{
|
||||
"name": "removeNodesAfterAnimationDone"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
||||
@@ -296,6 +296,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -320,6 +323,9 @@
|
||||
{
|
||||
"name": "R3Injector"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "RefCountOperator"
|
||||
},
|
||||
@@ -998,9 +1004,6 @@
|
||||
{
|
||||
"name": "removeFromArray"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
||||
@@ -407,6 +407,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -443,6 +446,9 @@
|
||||
{
|
||||
"name": "R3ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "RadioControlRegistryModule"
|
||||
},
|
||||
@@ -1451,9 +1457,6 @@
|
||||
{
|
||||
"name": "removeListItem2"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "removeValidators"
|
||||
},
|
||||
|
||||
@@ -398,6 +398,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -434,6 +437,9 @@
|
||||
{
|
||||
"name": "R3ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "REQUIRED_VALIDATOR"
|
||||
},
|
||||
@@ -1415,9 +1421,6 @@
|
||||
{
|
||||
"name": "removeListItem"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "removeValidators"
|
||||
},
|
||||
|
||||
@@ -476,6 +476,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -533,6 +536,9 @@
|
||||
{
|
||||
"name": "R3ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "ROUTER_CONFIGURATION"
|
||||
},
|
||||
@@ -1763,9 +1769,6 @@
|
||||
{
|
||||
"name": "removeFromArray"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
||||
@@ -269,6 +269,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -290,6 +293,9 @@
|
||||
{
|
||||
"name": "R3Injector"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "RefCountOperator"
|
||||
},
|
||||
@@ -869,9 +875,6 @@
|
||||
{
|
||||
"name": "removeFromArray"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
||||
@@ -311,6 +311,9 @@
|
||||
{
|
||||
"name": "NodeInjectorFactory"
|
||||
},
|
||||
{
|
||||
"name": "NoneEncapsulationDomRenderer"
|
||||
},
|
||||
{
|
||||
"name": "NullInjector"
|
||||
},
|
||||
@@ -347,6 +350,9 @@
|
||||
{
|
||||
"name": "R3ViewContainerRef"
|
||||
},
|
||||
{
|
||||
"name": "REMOVE_STYLES_ON_COMPONENT_DESTROY"
|
||||
},
|
||||
{
|
||||
"name": "RefCountOperator"
|
||||
},
|
||||
@@ -1211,9 +1217,6 @@
|
||||
{
|
||||
"name": "removeFromArray"
|
||||
},
|
||||
{
|
||||
"name": "removeStyle"
|
||||
},
|
||||
{
|
||||
"name": "renderComponent"
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser';
|
||||
import {MockAnimationDriver} from '@angular/animations/browser/testing';
|
||||
import {ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {NgZone, RendererFactory2, RendererType2} from '@angular/core';
|
||||
import {NoopNgZone} from '@angular/core/src/zone/ng_zone';
|
||||
import {EventManager, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser';
|
||||
@@ -39,8 +38,8 @@ export class SimpleDomEventsPlugin extends EventManagerPlugin {
|
||||
export function getRendererFactory2(document: any): RendererFactory2 {
|
||||
const fakeNgZone: NgZone = new NoopNgZone();
|
||||
const eventManager = new EventManager([new SimpleDomEventsPlugin(document)], fakeNgZone);
|
||||
const rendererFactory =
|
||||
new ɵDomRendererFactory2(eventManager, new ɵDomSharedStylesHost(document), 'dummyappid');
|
||||
const rendererFactory = new ɵDomRendererFactory2(
|
||||
eventManager, new ɵDomSharedStylesHost(document), 'dummyappid', true);
|
||||
const origCreateRenderer = rendererFactory.createRenderer;
|
||||
rendererFactory.createRenderer = function(element: any, type: RendererType2|null) {
|
||||
const renderer = origCreateRenderer.call(this, element, type);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
import {SERVER_TRANSITION_PROVIDERS, TRANSITION_ID} from './browser/server-transition';
|
||||
import {BrowserGetTestability} from './browser/testability';
|
||||
import {BrowserXhr} from './browser/xhr';
|
||||
import {DomRendererFactory2} from './dom/dom_renderer';
|
||||
import {DomRendererFactory2, REMOVE_STYLES_ON_COMPONENT_DESTROY} from './dom/dom_renderer';
|
||||
import {DomEventsPlugin} from './dom/events/dom_events';
|
||||
import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
|
||||
import {KeyEventsPlugin} from './dom/events/key_events';
|
||||
@@ -207,7 +207,7 @@ const BROWSER_MODULE_PROVIDERS: Provider[] = [
|
||||
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [DOCUMENT]}, {
|
||||
provide: DomRendererFactory2,
|
||||
useClass: DomRendererFactory2,
|
||||
deps: [EventManager, DomSharedStylesHost, APP_ID]
|
||||
deps: [EventManager, DomSharedStylesHost, APP_ID, REMOVE_STYLES_ON_COMPONENT_DESTROY]
|
||||
},
|
||||
{provide: RendererFactory2, useExisting: DomRendererFactory2},
|
||||
{provide: SharedStylesHost, useExisting: DomSharedStylesHost},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {APP_ID, Inject, Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ViewEncapsulation} from '@angular/core';
|
||||
import {APP_ID, Inject, Injectable, InjectionToken, OnDestroy, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {EventManager} from './events/event_manager';
|
||||
import {DomSharedStylesHost} from './shared_styles_host';
|
||||
@@ -27,6 +27,24 @@ export const COMPONENT_VARIABLE = '%COMP%';
|
||||
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
/**
|
||||
* The default value for the `REMOVE_STYLES_ON_COMPONENT_DESTROY` DI token.
|
||||
*/
|
||||
const REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT = false;
|
||||
|
||||
/**
|
||||
* A [DI token](guide/glossary#di-token "DI token definition") that indicates whether styles
|
||||
* of destroyed components should be removed from DOM.
|
||||
*
|
||||
* By default, the value is set to `false`. This will be changed in the next major version.
|
||||
* @publicApi
|
||||
*/
|
||||
export const REMOVE_STYLES_ON_COMPONENT_DESTROY =
|
||||
new InjectionToken<boolean>('RemoveStylesOnCompDestory', {
|
||||
providedIn: 'root',
|
||||
factory: () => REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT,
|
||||
});
|
||||
|
||||
export function shimContentAttribute(componentShortId: string): string {
|
||||
return CONTENT_ATTR.replace(COMPONENT_REGEX, componentShortId);
|
||||
}
|
||||
@@ -67,13 +85,15 @@ function decoratePreventDefault(eventHandler: Function): Function {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomRendererFactory2 implements RendererFactory2 {
|
||||
private rendererByCompId = new Map<string, Renderer2>();
|
||||
export class DomRendererFactory2 implements RendererFactory2, OnDestroy {
|
||||
private rendererByCompId =
|
||||
new Map<string, EmulatedEncapsulationDomRenderer2|NoneEncapsulationDomRenderer>();
|
||||
private defaultRenderer: Renderer2;
|
||||
|
||||
constructor(
|
||||
private eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost,
|
||||
@Inject(APP_ID) private appId: string) {
|
||||
@Inject(APP_ID) private appId: string,
|
||||
@Inject(REMOVE_STYLES_ON_COMPONENT_DESTROY) private removeStylesOnCompDestory: boolean) {
|
||||
this.defaultRenderer = new DefaultDomRenderer2(eventManager);
|
||||
}
|
||||
|
||||
@@ -81,28 +101,51 @@ export class DomRendererFactory2 implements RendererFactory2 {
|
||||
if (!element || !type) {
|
||||
return this.defaultRenderer;
|
||||
}
|
||||
switch (type.encapsulation) {
|
||||
case ViewEncapsulation.Emulated: {
|
||||
let renderer = this.rendererByCompId.get(type.id);
|
||||
if (!renderer) {
|
||||
renderer = new EmulatedEncapsulationDomRenderer2(
|
||||
this.eventManager, this.sharedStylesHost, type, this.appId);
|
||||
this.rendererByCompId.set(type.id, renderer);
|
||||
}
|
||||
(<EmulatedEncapsulationDomRenderer2>renderer).applyToHost(element);
|
||||
return renderer;
|
||||
}
|
||||
case ViewEncapsulation.ShadowDom:
|
||||
return new ShadowDomRenderer(this.eventManager, this.sharedStylesHost, element, type);
|
||||
default: {
|
||||
if (!this.rendererByCompId.has(type.id)) {
|
||||
const styles = flattenStyles(type.id, type.styles);
|
||||
this.sharedStylesHost.addStyles(styles);
|
||||
this.rendererByCompId.set(type.id, this.defaultRenderer);
|
||||
}
|
||||
return this.defaultRenderer;
|
||||
}
|
||||
|
||||
const renderer = this.getOrCreateRenderer(element, type);
|
||||
|
||||
// Renderers have different logic due to different encapsulation behaviours.
|
||||
// Ex: for emulated, an attribute is added to the element.
|
||||
if (renderer instanceof EmulatedEncapsulationDomRenderer2) {
|
||||
renderer.applyToHost(element);
|
||||
} else if (renderer instanceof NoneEncapsulationDomRenderer) {
|
||||
renderer.applyStyles();
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private getOrCreateRenderer(element: any, type: RendererType2): Renderer2 {
|
||||
const rendererByCompId = this.rendererByCompId;
|
||||
let renderer = rendererByCompId.get(type.id);
|
||||
|
||||
if (!renderer) {
|
||||
const eventManager = this.eventManager;
|
||||
const sharedStylesHost = this.sharedStylesHost;
|
||||
const removeStylesOnCompDestory = this.removeStylesOnCompDestory;
|
||||
|
||||
switch (type.encapsulation) {
|
||||
case ViewEncapsulation.Emulated:
|
||||
renderer = new EmulatedEncapsulationDomRenderer2(
|
||||
eventManager, sharedStylesHost, type, this.appId, removeStylesOnCompDestory);
|
||||
break;
|
||||
case ViewEncapsulation.ShadowDom:
|
||||
return new ShadowDomRenderer(eventManager, sharedStylesHost, element, type);
|
||||
default:
|
||||
renderer = new NoneEncapsulationDomRenderer(
|
||||
eventManager, sharedStylesHost, type, removeStylesOnCompDestory);
|
||||
break;
|
||||
}
|
||||
|
||||
renderer.onDestroy = () => rendererByCompId.delete(type.id);
|
||||
rendererByCompId.set(type.id, renderer);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.rendererByCompId.clear();
|
||||
}
|
||||
|
||||
begin() {}
|
||||
@@ -265,36 +308,11 @@ function checkNoSyntheticProp(name: string, nameKind: string) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isTemplateNode(node: any): node is HTMLTemplateElement {
|
||||
return node.tagName === 'TEMPLATE' && node.content !== undefined;
|
||||
}
|
||||
|
||||
class EmulatedEncapsulationDomRenderer2 extends DefaultDomRenderer2 {
|
||||
private contentAttr: string;
|
||||
private hostAttr: string;
|
||||
|
||||
constructor(
|
||||
eventManager: EventManager, sharedStylesHost: DomSharedStylesHost,
|
||||
private component: RendererType2, appId: string) {
|
||||
super(eventManager);
|
||||
const styles = flattenStyles(appId + '-' + component.id, component.styles);
|
||||
sharedStylesHost.addStyles(styles);
|
||||
|
||||
this.contentAttr = shimContentAttribute(appId + '-' + component.id);
|
||||
this.hostAttr = shimHostAttribute(appId + '-' + component.id);
|
||||
}
|
||||
|
||||
applyToHost(element: any) {
|
||||
super.setAttribute(element, this.hostAttr, '');
|
||||
}
|
||||
|
||||
override createElement(parent: any, name: string): Element {
|
||||
const el = super.createElement(parent, name);
|
||||
super.setAttribute(el, this.contentAttr, '');
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
class ShadowDomRenderer extends DefaultDomRenderer2 {
|
||||
private shadowRoot: any;
|
||||
|
||||
@@ -303,11 +321,13 @@ class ShadowDomRenderer extends DefaultDomRenderer2 {
|
||||
private hostEl: any, component: RendererType2) {
|
||||
super(eventManager);
|
||||
this.shadowRoot = (hostEl as any).attachShadow({mode: 'open'});
|
||||
|
||||
this.sharedStylesHost.addHost(this.shadowRoot);
|
||||
const styles = flattenStyles(component.id, component.styles);
|
||||
for (let i = 0; i < styles.length; i++) {
|
||||
|
||||
for (const style of styles) {
|
||||
const styleEl = document.createElement('style');
|
||||
styleEl.textContent = styles[i];
|
||||
styleEl.textContent = style;
|
||||
this.shadowRoot.appendChild(styleEl);
|
||||
}
|
||||
}
|
||||
@@ -316,10 +336,6 @@ class ShadowDomRenderer extends DefaultDomRenderer2 {
|
||||
return node === this.hostEl ? this.shadowRoot : node;
|
||||
}
|
||||
|
||||
override destroy() {
|
||||
this.sharedStylesHost.removeHost(this.shadowRoot);
|
||||
}
|
||||
|
||||
override appendChild(parent: any, newChild: any): void {
|
||||
return super.appendChild(this.nodeOrShadowRoot(parent), newChild);
|
||||
}
|
||||
@@ -332,4 +348,67 @@ class ShadowDomRenderer extends DefaultDomRenderer2 {
|
||||
override parentNode(node: any): any {
|
||||
return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(node)));
|
||||
}
|
||||
|
||||
override destroy() {
|
||||
this.sharedStylesHost.removeHost(this.shadowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
class NoneEncapsulationDomRenderer extends DefaultDomRenderer2 {
|
||||
private readonly styles: string[];
|
||||
private rendererUsageCount = 0;
|
||||
onDestroy: VoidFunction|undefined;
|
||||
|
||||
constructor(
|
||||
eventManager: EventManager,
|
||||
private readonly sharedStylesHost: DomSharedStylesHost,
|
||||
component: RendererType2,
|
||||
private removeStylesOnCompDestory: boolean,
|
||||
compId = component.id,
|
||||
) {
|
||||
super(eventManager);
|
||||
this.styles = flattenStyles(compId, component.styles);
|
||||
}
|
||||
|
||||
applyStyles(): void {
|
||||
this.sharedStylesHost.addStyles(this.styles);
|
||||
this.rendererUsageCount++;
|
||||
}
|
||||
|
||||
override destroy(): void {
|
||||
if (!this.removeStylesOnCompDestory) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.sharedStylesHost.removeStyles(this.styles);
|
||||
this.rendererUsageCount--;
|
||||
if (this.rendererUsageCount === 0) {
|
||||
this.onDestroy?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmulatedEncapsulationDomRenderer2 extends NoneEncapsulationDomRenderer {
|
||||
private contentAttr: string;
|
||||
private hostAttr: string;
|
||||
|
||||
constructor(
|
||||
eventManager: EventManager, sharedStylesHost: DomSharedStylesHost, component: RendererType2,
|
||||
appId: string, removeStylesOnCompDestory: boolean) {
|
||||
const compId = appId + '-' + component.id;
|
||||
super(eventManager, sharedStylesHost, component, removeStylesOnCompDestory, compId);
|
||||
this.contentAttr = shimContentAttribute(compId);
|
||||
this.hostAttr = shimHostAttribute(compId);
|
||||
}
|
||||
|
||||
applyToHost(element: any): void {
|
||||
this.applyStyles();
|
||||
this.setAttribute(element, this.hostAttr, '');
|
||||
}
|
||||
|
||||
override createElement(parent: any, name: string): Element {
|
||||
const el = super.createElement(parent, name);
|
||||
super.setAttribute(el, this.contentAttr, '');
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,75 +6,123 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {Inject, Injectable, OnDestroy} from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class SharedStylesHost {
|
||||
/** @internal */
|
||||
protected _stylesSet = new Set<string>();
|
||||
export class SharedStylesHost implements OnDestroy {
|
||||
private readonly usageCount = new Map<string /** Style string */, number /** Usage count */>();
|
||||
|
||||
addStyles(styles: string[]): void {
|
||||
const additions = new Set<string>();
|
||||
styles.forEach(style => {
|
||||
if (!this._stylesSet.has(style)) {
|
||||
this._stylesSet.add(style);
|
||||
additions.add(style);
|
||||
for (const style of styles) {
|
||||
const usageCount = this.changeUsageCount(style, 1);
|
||||
|
||||
if (usageCount === 1) {
|
||||
this.onStyleAdded(style);
|
||||
}
|
||||
});
|
||||
this.onStylesAdded(additions);
|
||||
}
|
||||
}
|
||||
|
||||
onStylesAdded(additions: Set<string>): void {}
|
||||
removeStyles(styles: string[]): void {
|
||||
for (const style of styles) {
|
||||
const usageCount = this.changeUsageCount(style, -1);
|
||||
|
||||
getAllStyles(): string[] {
|
||||
return Array.from(this._stylesSet);
|
||||
if (usageCount === 0) {
|
||||
this.onStyleRemoved(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onStyleRemoved(style: string): void {}
|
||||
|
||||
onStyleAdded(style: string): void {}
|
||||
|
||||
getAllStyles(): IterableIterator<string> {
|
||||
return this.usageCount.keys();
|
||||
}
|
||||
|
||||
private changeUsageCount(style: string, delta: number): number {
|
||||
const map = this.usageCount;
|
||||
let usage = map.get(style) ?? 0;
|
||||
usage += delta;
|
||||
|
||||
if (usage > 0) {
|
||||
map.set(style, usage);
|
||||
} else {
|
||||
map.delete(style);
|
||||
}
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
for (const style of this.getAllStyles()) {
|
||||
this.onStyleRemoved(style);
|
||||
}
|
||||
|
||||
this.usageCount.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DomSharedStylesHost extends SharedStylesHost implements OnDestroy {
|
||||
// Maps all registered host nodes to a list of style nodes that have been added to the host node.
|
||||
private _hostNodes = new Map<Node, Node[]>();
|
||||
private readonly styleRef = new Map<string, HTMLStyleElement[]>();
|
||||
private hostNodes = new Set<Node>();
|
||||
|
||||
constructor(@Inject(DOCUMENT) private _doc: any) {
|
||||
constructor(@Inject(DOCUMENT) private readonly doc: any) {
|
||||
super();
|
||||
this._hostNodes.set(_doc.head, []);
|
||||
this.resetHostNodes();
|
||||
}
|
||||
|
||||
private _addStylesToHost(styles: Set<string>, host: Node, styleNodes: Node[]): void {
|
||||
styles.forEach((style: string) => {
|
||||
const styleEl = this._doc.createElement('style');
|
||||
styleEl.textContent = style;
|
||||
styleNodes.push(host.appendChild(styleEl));
|
||||
});
|
||||
override onStyleAdded(style: string): void {
|
||||
for (const host of this.hostNodes) {
|
||||
this.addStyleToHost(host, style);
|
||||
}
|
||||
}
|
||||
|
||||
override onStyleRemoved(style: string): void {
|
||||
const styleRef = this.styleRef;
|
||||
const styleElements = styleRef.get(style);
|
||||
styleElements?.forEach(e => e.remove());
|
||||
styleRef.delete(style);
|
||||
}
|
||||
|
||||
override ngOnDestroy(): void {
|
||||
super.ngOnDestroy();
|
||||
this.styleRef.clear();
|
||||
this.resetHostNodes();
|
||||
}
|
||||
|
||||
addHost(hostNode: Node): void {
|
||||
const styleNodes: Node[] = [];
|
||||
this._addStylesToHost(this._stylesSet, hostNode, styleNodes);
|
||||
this._hostNodes.set(hostNode, styleNodes);
|
||||
this.hostNodes.add(hostNode);
|
||||
|
||||
for (const style of this.getAllStyles()) {
|
||||
this.addStyleToHost(hostNode, style);
|
||||
}
|
||||
}
|
||||
|
||||
removeHost(hostNode: Node): void {
|
||||
const styleNodes = this._hostNodes.get(hostNode);
|
||||
if (styleNodes) {
|
||||
styleNodes.forEach(removeStyle);
|
||||
this.hostNodes.delete(hostNode);
|
||||
}
|
||||
|
||||
private addStyleToHost(host: Node, style: string): void {
|
||||
const styleEl = this.doc.createElement('style');
|
||||
styleEl.textContent = style;
|
||||
host.appendChild(styleEl);
|
||||
|
||||
const styleElRef = this.styleRef.get(style);
|
||||
if (styleElRef) {
|
||||
styleElRef.push(styleEl);
|
||||
} else {
|
||||
this.styleRef.set(style, [styleEl]);
|
||||
}
|
||||
this._hostNodes.delete(hostNode);
|
||||
}
|
||||
|
||||
override onStylesAdded(additions: Set<string>): void {
|
||||
this._hostNodes.forEach((styleNodes, hostNode) => {
|
||||
this._addStylesToHost(additions, hostNode, styleNodes);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._hostNodes.forEach(styleNodes => styleNodes.forEach(removeStyle));
|
||||
private resetHostNodes(): void {
|
||||
const hostNodes = this.hostNodes;
|
||||
hostNodes.clear();
|
||||
// Re-add the head element back since this is the default host.
|
||||
hostNodes.add(this.doc.head);
|
||||
}
|
||||
}
|
||||
|
||||
function removeStyle(styleNode: Node): void {
|
||||
getDOM().remove(styleNode);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export {Title} from './browser/title';
|
||||
export {disableDebugTools, enableDebugTools} from './browser/tools/tools';
|
||||
export {BrowserTransferStateModule, makeStateKey, StateKey, TransferState} from './browser/transfer_state';
|
||||
export {By} from './dom/debug/by';
|
||||
export {REMOVE_STYLES_ON_COMPONENT_DESTROY} from './dom/dom_renderer';
|
||||
export {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
|
||||
export {HAMMER_GESTURE_CONFIG, HAMMER_LOADER, HammerGestureConfig, HammerLoader, HammerModule} from './dom/events/hammer_gestures';
|
||||
export {DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl, SafeValue} from './security/dom_sanitization_service';
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Component, Renderer2, ViewEncapsulation} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {NAMESPACE_URIS} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||
import {NAMESPACE_URIS, REMOVE_STYLES_ON_COMPONENT_DESTROY} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
{
|
||||
@@ -24,9 +24,13 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
TestCmp, SomeApp, CmpEncapsulationEmulated, CmpEncapsulationShadow, CmpEncapsulationNone,
|
||||
CmpEncapsulationShadow
|
||||
]
|
||||
TestCmp,
|
||||
SomeApp,
|
||||
SomeAppForCleanUp,
|
||||
CmpEncapsulationEmulated,
|
||||
CmpEncapsulationNone,
|
||||
CmpEncapsulationShadow,
|
||||
],
|
||||
});
|
||||
renderer = TestBed.createComponent(TestCmp).componentInstance.renderer;
|
||||
});
|
||||
@@ -105,6 +109,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
it('should allow to style components with emulated encapsulation and no encapsulation inside of components with shadow DOM',
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(SomeApp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const cmp = fixture.debugElement.query(By.css('cmp-shadow')).nativeElement;
|
||||
const shadow = cmp.shadowRoot.querySelector('.shadow');
|
||||
|
||||
@@ -136,9 +142,139 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
expect(otherChild.parentNode).toBe(template.content);
|
||||
});
|
||||
|
||||
describe('should not cleanup styles of destroyed components by default', () => {
|
||||
it('works for components without encapsulation emulated', () => {
|
||||
const fixture = TestBed.createComponent(SomeAppForCleanUp);
|
||||
const compInstance = fixture.componentInstance;
|
||||
compInstance.showEmulatedComponents = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
// verify style is in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(1);
|
||||
|
||||
// Remove a single instance of the component.
|
||||
compInstance.componentOneInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(1);
|
||||
|
||||
// Hide all instances of the component
|
||||
compInstance.componentTwoInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(1);
|
||||
});
|
||||
|
||||
it('works for components without encapsulation none', () => {
|
||||
const fixture = TestBed.createComponent(SomeAppForCleanUp);
|
||||
const compInstance = fixture.componentInstance;
|
||||
compInstance.showEmulatedComponents = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
// verify style is in DOM
|
||||
expect(styleCount(fixture, '.none')).toBe(1);
|
||||
|
||||
// Remove a single instance of the component.
|
||||
compInstance.componentOneInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.none')).toBe(1);
|
||||
|
||||
// Hide all instances of the component
|
||||
compInstance.componentTwoInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.none')).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe(
|
||||
'should cleanup styles of destroyed components when `REMOVE_STYLES_ON_COMPONENT_DESTROY` is `true`',
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
SomeAppForCleanUp,
|
||||
CmpEncapsulationEmulated,
|
||||
CmpEncapsulationNone,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: REMOVE_STYLES_ON_COMPONENT_DESTROY,
|
||||
useValue: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('works for components without encapsulation emulated', () => {
|
||||
const fixture = TestBed.createComponent(SomeAppForCleanUp);
|
||||
const compInstance = fixture.componentInstance;
|
||||
compInstance.showEmulatedComponents = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
// verify style is in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(1);
|
||||
|
||||
// Remove a single instance of the component.
|
||||
compInstance.componentOneInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(1);
|
||||
|
||||
// Hide all instances of the component
|
||||
compInstance.componentTwoInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
// Verify style is not in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(0);
|
||||
});
|
||||
|
||||
it('works for components without encapsulation none', () => {
|
||||
const fixture = TestBed.createComponent(SomeAppForCleanUp);
|
||||
const compInstance = fixture.componentInstance;
|
||||
compInstance.showEmulatedComponents = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
// verify style is in DOM
|
||||
expect(styleCount(fixture, '.none')).toBe(1);
|
||||
|
||||
// Remove a single instance of the component.
|
||||
compInstance.componentOneInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
// Verify style is still in DOM
|
||||
expect(styleCount(fixture, '.none')).toBe(1);
|
||||
|
||||
// Hide all instances of the component
|
||||
compInstance.componentTwoInstanceHidden = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
// Verify style is not in DOM
|
||||
expect(styleCount(fixture, '.emulated')).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function styleCount(fixture: ComponentFixture<unknown>, cssContentMatcher: string): number {
|
||||
const html = fixture.debugElement.parent?.parent;
|
||||
const debugElements = html?.queryAll(By.css('style'));
|
||||
|
||||
if (!debugElements) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return debugElements
|
||||
.filter(({nativeElement}) => nativeElement.textContent.includes(cssContentMatcher))
|
||||
.length;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'cmp-emulated',
|
||||
template: `<div class="emulated"></div>`,
|
||||
@@ -169,9 +305,9 @@ class CmpEncapsulationShadow {
|
||||
@Component({
|
||||
selector: 'some-app',
|
||||
template: `
|
||||
<cmp-shadow></cmp-shadow>
|
||||
<cmp-emulated></cmp-emulated>
|
||||
<cmp-none></cmp-none>
|
||||
<cmp-shadow></cmp-shadow>
|
||||
<cmp-emulated></cmp-emulated>
|
||||
<cmp-none></cmp-none>
|
||||
`,
|
||||
})
|
||||
export class SomeApp {
|
||||
@@ -181,3 +317,19 @@ export class SomeApp {
|
||||
class TestCmp {
|
||||
constructor(public renderer: Renderer2) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'some-app',
|
||||
template: `
|
||||
<cmp-emulated *ngIf="!componentOneInstanceHidden && showEmulatedComponents"></cmp-emulated>
|
||||
<cmp-emulated *ngIf="!componentTwoInstanceHidden && showEmulatedComponents"></cmp-emulated>
|
||||
|
||||
<cmp-none *ngIf="!componentOneInstanceHidden && !showEmulatedComponents"></cmp-none>
|
||||
<cmp-none *ngIf="!componentTwoInstanceHidden && !showEmulatedComponents"></cmp-none>
|
||||
`,
|
||||
})
|
||||
export class SomeAppForCleanUp {
|
||||
componentOneInstanceHidden = false;
|
||||
componentTwoInstanceHidden = false;
|
||||
showEmulatedComponents = true;
|
||||
}
|
||||
|
||||
@@ -46,15 +46,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
expect(doc.head).toHaveText('a {};b {};');
|
||||
});
|
||||
|
||||
it('should remove style nodes when the host is removed', () => {
|
||||
ssh.addStyles(['a {};']);
|
||||
ssh.addHost(someHost);
|
||||
expect(someHost.innerHTML).toEqual('<style>a {};</style>');
|
||||
|
||||
ssh.removeHost(someHost);
|
||||
expect(someHost.innerHTML).toEqual('');
|
||||
});
|
||||
|
||||
it('should remove style nodes on destroy', () => {
|
||||
ssh.addStyles(['a {};']);
|
||||
ssh.addHost(someHost);
|
||||
|
||||
@@ -22,8 +22,8 @@ export class ServerStylesHost extends SharedStylesHost {
|
||||
this.head = doc.getElementsByTagName('head')[0];
|
||||
}
|
||||
|
||||
private _addStyle(style: string): void {
|
||||
let adapter = getDOM();
|
||||
override onStyleAdded(style: string): void {
|
||||
const adapter = getDOM();
|
||||
const el = adapter.createElement('style');
|
||||
el.textContent = style;
|
||||
if (!!this.transitionId) {
|
||||
@@ -33,11 +33,9 @@ export class ServerStylesHost extends SharedStylesHost {
|
||||
this._styleNodes.add(el);
|
||||
}
|
||||
|
||||
override onStylesAdded(additions: Set<string>) {
|
||||
additions.forEach(style => this._addStyle(style));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
override ngOnDestroy() {
|
||||
this._styleNodes.forEach(styleNode => styleNode.remove());
|
||||
this._styleNodes.clear();
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,14 +35,11 @@ function serializeTransferStateFactory(doc: Document, appId: string, transferSto
|
||||
script.id = appId + '-state';
|
||||
script.setAttribute('type', 'application/json');
|
||||
script.textContent = escapeHtml(content);
|
||||
const existingScript = doc.body.querySelector('script');
|
||||
if (existingScript) {
|
||||
// Insert the state script before any script so that the the script is available
|
||||
// before Angular is bootstrapped as otherwise this can causes the state not to be present.
|
||||
existingScript.before(script);
|
||||
} else {
|
||||
doc.body.appendChild(script);
|
||||
}
|
||||
|
||||
// It is intentional that we add the script at the very bottom. Angular CLI script tags for
|
||||
// bundles are always `type="module"`. These are deferred by default and cause the transfer
|
||||
// transfer data to be queried only after the browser has finished parsing the DOM.
|
||||
doc.body.appendChild(script);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,6 @@ export function renderApplication<T>(rootComponent: Type<T>, options: {
|
||||
const appProviders = [
|
||||
importProvidersFrom(BrowserModule.withServerTransition({appId})),
|
||||
importProvidersFrom(ServerModule),
|
||||
...TRANSFER_STATE_SERIALIZATION_PROVIDERS,
|
||||
...(options.providers ?? []),
|
||||
];
|
||||
return _render(platform, internalCreateApplication({rootComponent, appProviders}));
|
||||
|
||||
@@ -10,9 +10,9 @@ import {animate, AnimationBuilder, state, style, transition, trigger} from '@ang
|
||||
import {DOCUMENT, isPlatformServer, PlatformLocation, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {ApplicationRef, CompilerFactory, Component, destroyPlatform, getPlatform, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation} from '@angular/core';
|
||||
import {ApplicationRef, CompilerFactory, Component, destroyPlatform, getPlatform, HostListener, Inject, inject as coreInject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation} from '@angular/core';
|
||||
import {inject, TestBed, waitForAsync} from '@angular/core/testing';
|
||||
import {BrowserModule, Title} from '@angular/platform-browser';
|
||||
import {BrowserModule, makeStateKey, Title, TransferState} from '@angular/platform-browser';
|
||||
import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, platformDynamicServer, PlatformState, renderModule, renderModuleFactory, ServerModule} from '@angular/platform-server';
|
||||
import {Observable} from 'rxjs';
|
||||
import {first} from 'rxjs/operators';
|
||||
@@ -298,6 +298,33 @@ const MyStylesAppStandalone = createMyStylesApp(true);
|
||||
class ExampleStylesModule {
|
||||
}
|
||||
|
||||
function createMyTransferStateApp(standalone: boolean) {
|
||||
@Component({
|
||||
standalone,
|
||||
selector: 'app',
|
||||
template: `<div>Works!</div>`,
|
||||
})
|
||||
class MyStylesApp {
|
||||
state = coreInject(TransferState);
|
||||
constructor() {
|
||||
this.state.set(makeStateKey<string>('some-key'), 'some-value');
|
||||
}
|
||||
}
|
||||
return MyStylesApp;
|
||||
}
|
||||
|
||||
const MyTransferStateApp = createMyTransferStateApp(false);
|
||||
const MyTransferStateAppStandalone = createMyTransferStateApp(true);
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyTransferStateApp],
|
||||
imports: [BrowserModule.withServerTransition({appId: 'transfer-state-app'}), ServerModule],
|
||||
bootstrap: [MyTransferStateApp]
|
||||
})
|
||||
class MyTransferStateModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
imports: [MyServerAppModule, ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
@@ -828,6 +855,23 @@ describe('platform-server integration', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it(`using ${isStandalone ? 'renderApplication' : 'renderModule'} ` +
|
||||
`should serialize transfer state only once`,
|
||||
waitForAsync(() => {
|
||||
const options = {document: doc};
|
||||
const bootstrap = isStandalone ?
|
||||
renderApplication(
|
||||
MyTransferStateAppStandalone, {...options, appId: 'transfer-state-app'}) :
|
||||
renderModule(MyTransferStateModule, {...options});
|
||||
bootstrap.then(output => {
|
||||
const expectedOutput =
|
||||
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER" ng-server-context="other"><div>Works!</div></app>' +
|
||||
'<script id="transfer-state-app-state" type="application/json">{&q;some-key&q;:&q;some-value&q;}</script></body></html>';
|
||||
expect(output).toEqual(expectedOutput);
|
||||
called = true;
|
||||
});
|
||||
}));
|
||||
|
||||
it('uses `other` as the `serverContext` value when all symbols are removed after sanitization',
|
||||
waitForAsync(() => {
|
||||
const options = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user