Compare commits

...

68 Commits

Author SHA1 Message Date
b8cbcbcf49 release: cut the v8.0.0-rc.3 release 2019-05-07 14:30:37 -07:00
1ed45bd783 docs(aio): add awade jigsaw into resources (#30204)
PR Close #30204
2019-05-07 13:13:22 -07:00
82fd1920b1 docs: update date pipe example for minutes and seconds (#30281) (#30295)
PR Close #30295
2019-05-07 10:29:31 -07:00
f6d7271ec7 refactor: remove tslint no-use-before-declare rule (#30288)
PR Close #30288
2019-05-07 10:25:36 -07:00
c1f3faf1df test: fix several bazel compiler-cli tests in windows (#30189)
```
//packages/compiler-cli/integrationtest:integrationtest
//packages/compiler-cli/test/compliance:compliance
```

Partially addresses #29785

PR Close #30189
2019-05-07 10:21:37 -07:00
97202278f9 fix(router): ensure history.state is set in eager update mode (#30154)
Without this change, `history.state` isn't being set when updating the browser URL in `eager` update mode.

PR Close #30154
2019-05-06 16:09:59 -07:00
132f01c5ca fix(router): fix a problem with router not responding to back button (#30160)
There was a problem with a combination of the `eager` URL update, browser `back` button, and hybrid applications. Details provided in internal ticket http://b/123667227.

This fix handles the problem by setting `router.browserUrlTree` when all conditions have failed, meaning the browser doesn't do anything with the navigation other than update internal data structures. Without this change, the problem was an old value was stored in `router.broserUrlTree` causing some new navigations to be compared to an old value and breaking future navigations.

PR Close #30160
2019-05-06 16:06:59 -07:00
6a987f1b9c docs(forms): fix import line ending (#30290)
PR Close #30290
2019-05-06 16:05:23 -07:00
548b003ed3 fix(compiler-cli): log ngcc skipping messages as debug instead of info (#30232)
Related to https://github.com/angular/angular-cli/issues/14194, https://github.com/angular/angular-cli/pull/14320

PR Close #30232
2019-05-06 14:11:28 -07:00
48dc41de01 docs: fix naming miss and improve anchor reference in router guide (#30225)
Signed-off-by: Richard Lea <chigix@zoho.com>

PR Close #30225
2019-05-06 11:48:05 -07:00
817c2b49bc docs: add explanation on what lazy loading is (#29667)
PR Close #29667
2019-05-06 10:16:58 -07:00
fed07c735c docs(forms): fix ControlValueAccessor registerOnChange code example (#30276)
PR Close #30276
2019-05-06 09:27:12 -07:00
390cac6874 docs: fix targetEntryPointPath description (#30237)
PR Close #30237
2019-05-06 09:21:23 -07:00
8eb0b8bd40 docs: update Wikipedia link to Polyfill description (#30214)
PR Close #30214
2019-05-06 09:20:14 -07:00
d7283c6085 docs: fix example header in singleton services guide to match styleguide (#30097)
Removes the ".0" from code header, as that doesn't match the
recommended code style for service files names.

fixes #29862

PR Close #30097
2019-05-06 09:19:36 -07:00
3fe3a84a4b fix(core): fix interpolate identifier in AOT (#30243)
This commit fixes a regression introduced in PR 29692 where
the interpolate symbol in View Engine was improperly prefixed
with the ɵɵ that signifies private instructions for Ivy. It
resulted in interpolations of 10+ values not working correctly
in AOT mode. This commit removes the prefix.

PR Close #30243
2019-05-02 10:33:34 -07:00
28e4187bd6 fix(docs-infra): update app code to work with Ivy (#28530)
This commit also enables more tests to be run on CI with Ivy.

PR Close #28530
2019-05-01 16:38:33 -07:00
7cbc69c890 fix(ivy): allow R3TestBedCompiler to work in ngcc-processed apps (#28530)
Previously, `R3TestBedCompiler` was dynamically defining an
`@NgModule`-decorated `CompilerModule` class inside a method call.
Since ngcc only processes top-level classes, this class was not
transformed causing failures in unit tests (see #30121 for details).

This commit fixes it by using `compileNgModuleDefs()` directly (similar
to the fix in #30037).

Fixes #30121

PR Close #28530
2019-05-01 16:38:33 -07:00
1dc134bc6b docs: explicitly state purpose of components (#29879)
Current description is vague. I'd love to see this doc explicitly state that components are for reusable sets of UI features instead.

PR Close #29879
2019-05-01 13:38:13 -07:00
6a61d37f95 ci(docs-infra): re-enable aio_monitoring CircleCI jobs and change time (#30168)
The `aio_monitoring_next` CircleCI job was disabled due to a failure in
[302254](https://circleci.com/gh/angular/angular/302254). It turned out
the failure was caused because the job happened to run after a change
had been merged into master and right before it was deployed to
https://next.angular.io/ causing the tests not to match the deployed
version.

This commit re-enables the job and moves it to a different time, when it
is less likely that PRs will be being merged (and thus reducing the risk
of a similar timming issue).

Fixes #30101

PR Close #30168
2019-04-30 16:17:53 -07:00
8d2e92bcfe build(docs-infra): upgrade material and cdk to 8.0.0-rc.0 (#30202)
I bumped the payload size limits to reflect the current values - the changes have been insignificant.

PR Close #30202
2019-04-29 18:46:54 -07:00
5038f5c909 build(docs-infra): upgrade framework and cli to 8.0.0-rc.2 (#30202)
Brings in small payload size fixes.

PR Close #30202
2019-04-29 18:46:54 -07:00
6eeca70043 refactor(core): static-query migrations fails if options cannot be transformed (#30178)
Currently the `static-query` migrations fails at the final step of
updating a query when the query already specifies options which
cannot be transformed easily. e.g. the options are computed through
a function call: `@ViewChild(..., getQueryOpts());` or `@ViewChild(..., myOptionsVar)`.

In these cases we technically could add additionally logic to update
the query options, but given that this is an edge-case and it's
potentially over-engineering the migration schematic, we just
always add a TODO for the timing and print out the determined
query timing in the console. The developer in that case just needs
to manually update the logic for the query options to contain the
printed query timing.

Potentially related to: https://github.com/angular/angular-cli/issues/14298

PR Close #30178
2019-04-29 13:30:37 -07:00
9f68c35fa9 fix(bazel): Bump ibazel to 0.10.1 for windows fixes (#30196)
PR Close #30196
2019-04-29 13:29:12 -07:00
21418ea109 test: fix ngc-wrapped bazel tests in windows (#30111)
Partially addresses #29785

PR Close #30111
2019-04-29 13:23:45 -07:00
02d8b4ed3c docs: add in-page nav, change title to match left nav better (#30093)
PR Close #30093
2019-04-29 13:23:15 -07:00
6748392edc docs: add architect terms (#26963)
PR Close #26963
2019-04-29 13:22:20 -07:00
d9fd301157 build(docs-infra): update the aio app to framework and cli 8.0.0-rc.1 (#30183)
I updated the payload size limits as well. There still seem to be size regressions in the framework,
but at least the polyfills now uses the evergreen build of zones so we shaved off a few KB there.

PR Close #30183
2019-04-29 12:30:26 -07:00
71c5d80ce7 build: add size-tracking bazel test (#30070)
Introduces a new Bazel test that allows us to inspect
what source-files contribute to a given bundled file
and how much bytes they contribute to the bundle size.

Additionally the size-tracking rule groups the size
data by directories. This allows us to compare size
changes in the scope of directories. e.g. a lot of
files in a directory could increase slightly in size, but
in the directory scope the size change could be significant
and needs to be reported by the test target.

Resolves FW-1278

PR Close #30070
2019-04-29 12:29:25 -07:00
9798229fde release: cut the v8.0.0-rc.2 release 2019-04-29 12:03:39 -07:00
4b2fcfd5dc fix: disable injectable-pipe migration (#30180)
Disables the injectable pipe migration until we can decide whether this is the right solution for Ivy. Rolling it out properly will involve a more detailed plan and more changes like updating the styleguide, scaffolding schematics etc.

Context for the new `test-migrations.json`: since we use the `migrations.json` both for the real migrations and for tests, it doesn't allow us to disable a schematic, but continue running its tests. This change adds the test-specific file so that we can continue running the `injectable-pipe` tests, even though the schematic itself is disabled.

PR Close #30180
2019-04-29 09:40:00 -07:00
b4d291aa7a fix(ivy): injectable pipe schematic generating incorrect import statements (#30170)
Currently the injectable pipe schematic generates invalid imports like `import import { Pipe, PipeTransform, Injectable } from '@angular/core'; from '@angular/core';`. The issue wasn't caught by the unit tests, because the invalid import still contains the valid one.

Fixes #30159.

PR Close #30170
2019-04-29 09:39:11 -07:00
b0ecafdc2f docs: temporary disable aio_monitoring job due to a version skew (#30161)
It looks like `aio_monitoring` CircleCI job is still failing. Disabling it for now to keep master green.

PR Close #30161
2019-04-26 17:38:59 -07:00
6816bb62d7 Revert "refactor: use new Http library in playground (#29355)"
This reverts commit 908d43a5bb.
2019-04-26 16:51:38 -07:00
4b05b8cea0 refactor(ivy): move exports tests to acceptance (#30157)
PR Close #30157
2019-04-26 16:37:34 -07:00
a0728aedf7 docs: update readme. sentence correction. (#30136)
PR Close #30136
2019-04-26 16:36:53 -07:00
ea96f6112a build: unable to accept new symbol-extractor golden on windows (#30127)
Currently when working on Windows, it's not possible to accept a new
golden for a `symbol-extractor` Bazel test. This is because the generated
executable output from the `nodejs_binary` rule (without a macro) misses
a Windows executable wrapper that sets up the proper environment
variables for the runfiles. Causing the following failure on Windows:

```
 >>>> FAIL: RUNFILES environment variable is not set. <<<<
```

PR Close #30127
2019-04-26 16:35:51 -07:00
b706800ea8 fix(language-service): Remove tsserverlibrary from rollup globals (#30123)
This PR removes `tsserverlibrary` from rollup globals since the symbol
should not appear by the time rollup is invoked. `tsserverlibrary` is
used for types only, so the import statement should not be emitted by
tsc.

PR Close #30123
2019-04-26 16:35:05 -07:00
c6f95b1d70 ci: enable language service tests in codefresh (#30113)
PR Close #30113
2019-04-26 16:34:23 -07:00
d896126604 test: fix language service tests in windows (#30113)
This PR parially addresses #29785 and fixes ` //packages/language-service/test:test`

PR Close #30113
2019-04-26 16:34:23 -07:00
a20da5ddcc ci(docs-infra): use the tests from the stable branch in aio_monitoring_stable CircleCI job (#30110)
Previously, the `aio_monitoring_stable` job (which runs tests against
https://angular.io/) was using the tests from the master branch. As a
result, if the master branch included changes in those tests that were
not yet backported to the stable branch (and thus deployed to
https://angular.io/), the tests would fail.

This commit fixes this by using the tests from the stable branch to test
against https://angular.io/.

Fixes #30101

PR Close #30110
2019-04-26 16:33:46 -07:00
18878600ba ci(docs-infra): split the aio_monitoring CircleCI job into two jobs (#30110)
Previously, the `aio_monitoring` job was testing both the stable
(https://angular.io/) and the @next (https://next.angular.io/) versions.

This commit splits the tests into two separate jobs (still run as part
of the same workflow). This speeds up the tests (since the two jobs can
now run in parallel) and makes it easier to isolate failures (e.g.
identify which branch is failing, disable one of the two, etc.).
(Credits to @petebacondarwin 😉)

PR Close #30110
2019-04-26 16:33:46 -07:00
d7e10f3f7e ci(docs-infra): re-use setup CircleCI job in aio_monitoring (#30110)
PR Close #30110
2019-04-26 16:33:46 -07:00
4d044ea5b2 ci(docs-infra): re-enable aio_monitoring CircleCI job (#30110)
The job started failing for https://angular.io/, due to changes in tests
that only affected https://next.angular.io/, and was disabled in #30102.

This commit re-enables the job (since it does not block anything and it
will be fixed in a subsequent commit).

PR Close #30110
2019-04-26 16:33:46 -07:00
e2d1e0cd98 docs(docs-infra): fix typo in workspace-config.md file (#30108)
Fixed typo in 'Project asset configuration' section inside workdspace-config.md

PR Close #30108
2019-04-26 16:33:03 -07:00
4e056580bb test: fix api guardian tests on windows when node_modules are not installed (#30105)
When the workspace node_modules are not installed outside of bazel context the api guardian tests fails because the tree artifacts files are not symlinked in windows.

We need to pass the node module location in the node_path

PR Close #30105
2019-04-26 16:32:23 -07:00
e4c2e6a904 ci: add public_api_guard tests to codefresh config (#30105)
PR Close #30105
2019-04-26 16:32:23 -07:00
a50989832d test: fix ts api guardian and public guard tests on windows (#30105)
This change addresses several issues with ts-api-guardian and public api guards related tests in Windows

The fixes contain 3 main changes:
1) In `stripExportPattern` - replace `^` with `^^^^`  in RegExp due to a double escaping requirment under Windows. Note that under Linux this the extra character has no effect because it's still a valid RegExp in Js.

2. Force `*.patch` files to always be with a LF line sequence instead of CRLF in windows

3. When adding JSDoc comments consider the presence of a carriage return in a line new feed

Partially addresses #29785

PR Close #30105
2019-04-26 16:32:23 -07:00
525307b6a3 fix(ivy): correctly reflect undefined values (#30103)
Fixes Ivy reflecting properties with `undefined` values, rather than omitting them. Also fixes that Ivy doesn't clear the reflected values when property value changes from something that is defined to undefined.

This PR resolves FW-1277.

PR Close #30103
2019-04-26 16:31:50 -07:00
a3ab76b216 docs: update redirect links and fix typo (#30092)
PR Close #30092
2019-04-26 16:31:04 -07:00
f2265d4b46 build: upgrade yargs package to 13.1.0 (#29722)
Update yargs, because the old version was transitively (via os-local) depending on a vulnerable version of the mem package: https://app.snyk.io/vuln/npm:mem:20180117

PR Close #29722
2019-04-26 16:29:29 -07:00
d3ac709b99 fix(ivy): @Component should support entry components from another module (#29566)
PR Close #29566
2019-04-26 16:27:19 -07:00
908d43a5bb refactor: use new Http library in playground (#29355)
PR Close #29355
2019-04-26 16:26:31 -07:00
71cdb0a08e refactor(ivy): move di tests for inject to acceptance (#29299)
PR Close #29299
2019-04-26 16:23:44 -07:00
c7fbbdfa99 refactor(ivy): move di tests for flags to acceptance (#29299)
Including tests for `@Optional`, `@Self`, `@SkipSelf` and `@Host`.

PR Close #29299
2019-04-26 16:23:44 -07:00
10e4ab7712 refactor(ivy): move di tests for directive injection to acceptance (#29299)
PR Close #29299
2019-04-26 16:23:44 -07:00
5114c23c21 refactor(ivy): move di tests for DI tokens to acceptance (#29299)
Move tests for special tokens like `Injector`, `ElementRef`, `TemplateRef`, `ViewContainerRef`, `ChangeDectetorRef` and custom string tokens.

PR Close #29299
2019-04-26 16:23:43 -07:00
c3e585d7eb refactor(ivy): move di tests for Attribute to acceptance (#29299)
PR Close #29299
2019-04-26 16:23:43 -07:00
0776daec88 release: cut the v8.0.0-rc.1 release 2019-04-26 13:28:49 -07:00
615e1a58b2 test(ivy): pin deps on hello world size tests (#30152)
We recently had an unexpected size regression in the hello world
tests because the CLI devkit released an RC that regressed us and
the dependencies were not pinned. This change ensures that we only
update dependencies like devkit deliberately, so we do not have
mysterious breakages caused by other packages.

PR Close #30152
2019-04-26 12:34:10 -07:00
606758357e fix(bazel): update peerDep ranges (#30155)
PR Close #30155
2019-04-26 12:32:35 -07:00
ba2a3595c6 fix(ivy): output should not be subscribe twice when 2 listeners (#30144)
PR Close #30144
2019-04-26 11:11:09 -07:00
a50bfe5054 fix(ivy): property bindings use correct indices (#30129)
- Extracts and documents code that will be common to interpolation instructions
- Ensures that binding indices are updated at the proper time during compilation
- Adds additional tests

Related #30011

PR Close #30129
2019-04-26 11:09:51 -07:00
c8983bc367 docs: remove note about ivy being coupled to dynamic import (#30151)
PR Close #30151
2019-04-26 11:08:51 -07:00
8d6d2c6704 build: bazel ts-api-guardian usage fails on workspaces which don't depend on chalk (#30138)
When using the npm package in a workspace which doesn't depend on chalk, ts-api-guardian fails with an error `Error: Cannot find module 'chalk'`

PR Close #30138
2019-04-26 11:08:05 -07:00
6711f22e62 fix(bazel): Exclude common/upgrade* in metadata.tsconfig.json (#30133)
It has a dependency on @angular/upgrade which is not part of the
dependencies in package.json, so postinstall would fail.

PR Close #30133
2019-04-26 11:07:34 -07:00
8dd9192fe3 refactor(ivy): undeprecate inject (#30132)
PR Close #30132
2019-04-26 11:06:42 -07:00
870e0dab48 fix(ivy): remove debug utilities from ivy production builds (#30130)
Prior to this commit, we were pulling DebugNode and DebugElement
into production builds because BrowserModule automatically pulled
in NgProbe and thus getDebugNode. In Ivy, this is not necessary
because Ivy has its own set of debug utilities. We should use these
existing tools instead of NgProbe.

This commit adds an Ivy switch so we do not pull in NgProbe utilities
when running with Ivy. This saves us ~8KB in prod builds.

PR Close #30130
2019-04-26 11:04:48 -07:00
123 changed files with 4540 additions and 3066 deletions

View File

@ -269,7 +269,7 @@ jobs:
- *attach_workspace
- *init_environment
# Deploy angular.io to production (if necessary)
- run: setPublicVar CI_STABLE_BRANCH "$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')"
- run: setPublicVar_CI_STABLE_BRANCH
- run: yarn --cwd aio deploy-production
test_aio_local:
@ -292,11 +292,21 @@ jobs:
test_aio_local_ivy:
<<: *job_defaults
docker:
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
- image: *browsers_docker_image
steps:
- *attach_workspace
- *init_environment
# Build aio with Ivy (using local Angular packages)
- run: yarn --cwd aio build-with-ivy --progress=false
# Run PWA-score tests
# (Run before unit and e2e tests, which destroy the `dist/` directory.)
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
- run: yarn --cwd aio e2e --configuration=ci
test_aio_tools:
<<: *job_defaults
@ -486,20 +496,43 @@ jobs:
command: 'openssl aes-256-cbc -d -in .circleci/github_token -k "${KEY}" -out ~/.git_credentials'
- run: ./scripts/ci/publish-build-artifacts.sh
aio_monitoring:
aio_monitoring_stable:
<<: *job_defaults
docker:
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
- image: *browsers_docker_image
steps:
- checkout
- *post_checkout
- *restore_cache
- *attach_workspace
- *init_environment
- run: setPublicVar_CI_STABLE_BRANCH
- run:
name: Check out `aio/` from the stable branch
command: |
git fetch origin $CI_STABLE_BRANCH
git checkout --force origin/$CI_STABLE_BRANCH -- aio/
- run:
name: Run tests against https://angular.io/
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
- run:
name: Notify caretaker about failure
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
command: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $SLACK_CARETAKER_WEBHOOK_URL'
when: on_fail
aio_monitoring_next:
<<: *job_defaults
docker:
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
- image: *browsers_docker_image
steps:
- *attach_workspace
- *init_environment
- run:
name: Run tests against the deployed apps
command: ./aio/scripts/test-production.sh $CI_AIO_MIN_PWA_SCORE
name: Run tests against https://next.angular.io/
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
- run:
name: Notify caretaker about failure
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
@ -689,19 +722,23 @@ workflows:
cron: "0 * * * *"
filters: *publish_branches_filter
# This job is currently disabled due to a version skew problem.
# More info is available here: https://github.com/angular/angular/issues/30101
# aio_monitoring:
# jobs:
# - aio_monitoring
# triggers:
# - schedule:
# # Runs AIO monitoring job at 00:00AM every day.
# cron: "0 0 * * *"
# filters:
# branches:
# only:
# - master
aio_monitoring:
jobs:
- setup
- aio_monitoring_stable:
requires:
- setup
- aio_monitoring_next:
requires:
- setup
triggers:
- schedule:
# Runs AIO monitoring jobs at 10:00AM every day.
cron: "0 10 * * *"
filters:
branches:
only:
- master
# TODO:
# - don't build the g3 branch

View File

@ -36,3 +36,38 @@ function setSecretVar() {
# Restore original shell options.
eval "$originalShellOptions";
}
# Create a function to set an environment variable, when called.
#
# Use this function for creating setter for public environment variables that require expensive or
# time-consuming computaions and may not be needed. When needed, you can call this function to set
# the environment variable (which will be available through `$BASH_ENV` from that point onwards).
#
# Arguments:
# - `<name>`: The name of the environment variable. The generated setter function will be
# `setPublicVar_<name>`.
# - `<code>`: The code to run to compute the value for the variable. Since this code should be
# executed lazily, it must be properly escaped. For example:
# ```sh
# # DO NOT do this:
# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly
#
# # DO this isntead:
# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly
# ```
#
# Usage: `createPublicVarSetter <name> <code>`
#
# Example:
# ```sh
# createPublicVarSetter MY_VAR 'echo "FOO"';
# echo $MY_VAR; # Not defined
#
# setPublicVar_MY_VAR;
# source $BASH_ENV;
# echo $MY_VAR; # FOO
# ```
function createPublicVarSetter() {
echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV;
}

View File

@ -34,6 +34,13 @@ setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
####################################################################################################
# Define "lazy" PUBLIC environment variables for CircleCI.
# (I.e. functions to set an environment variable when called.)
####################################################################################################
createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')";
####################################################################################################
# Define SECRET environment variables for CircleCI.
####################################################################################################

View File

@ -20,5 +20,7 @@ steps:
# Add Bazel CI config
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
# Run tests
- yarn bazel test //tools/ts-api-guardian:all
- yarn bazel test //tools/ts-api-guardian:all //packages/language-service/test
- yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test
- yarn bazel test //tools/public_api_guard/...
- yarn bazel test //packages/compiler-cli/integrationtest:integrationtest //packages/compiler-cli/test/compliance:compliance

3
.gitattributes vendored
View File

@ -5,5 +5,8 @@
*.js eol=lf
*.ts eol=lf
# API guardian patch must always use LF for tests to work
*.patch eol=lf
# Must keep Windows line ending to be parsed correctly
scripts/windows/packages.txt eol=crlf

View File

@ -1,3 +1,39 @@
<a name="8.0.0-rc.3"></a>
# [8.0.0-rc.3](https://github.com/angular/angular/compare/8.0.0-rc.2...8.0.0-rc.3) (2019-05-07)
### Bug Fixes
* **bazel:** Bump ibazel to 0.10.1 for windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([9f68c35](https://github.com/angular/angular/commit/9f68c35))
* **compiler-cli:** log ngcc skipping messages as debug instead of info ([#30232](https://github.com/angular/angular/issues/30232)) ([548b003](https://github.com/angular/angular/commit/548b003))
* **core:** fix interpolate identifier in AOT ([#30243](https://github.com/angular/angular/issues/30243)) ([3fe3a84](https://github.com/angular/angular/commit/3fe3a84))
* **router:** ensure `history.state` is set in `eager` update mode ([#30154](https://github.com/angular/angular/issues/30154)) ([9720227](https://github.com/angular/angular/commit/9720227))
* **router:** fix a problem with router not responding to back button ([#30160](https://github.com/angular/angular/issues/30160)) ([132f01c](https://github.com/angular/angular/commit/132f01c))
<a name="8.0.0-rc.2"></a>
# [8.0.0-rc.2](https://github.com/angular/angular/compare/8.0.0-rc.1...8.0.0-rc.2) (2019-04-29)
### Bug Fixes
* **language-service:** Remove tsserverlibrary from rollup globals ([#30123](https://github.com/angular/angular/issues/30123)) ([b706800](https://github.com/angular/angular/commit/b706800))
* disable injectable-pipe migration ([#30180](https://github.com/angular/angular/issues/30180)) ([4b2fcfd](https://github.com/angular/angular/commit/4b2fcfd))
<a name="8.0.0-rc.1"></a>
# [8.0.0-rc.1](https://github.com/angular/angular/compare/8.0.0-rc.0...8.0.0-rc.1) (2019-04-26)
### Bug Fixes
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([6711f22](https://github.com/angular/angular/commit/6711f22))
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([6067583](https://github.com/angular/angular/commit/6067583))
<a name="8.0.0-rc.0"></a>
# [8.0.0-rc.0](https://github.com/angular/angular/compare/8.0.0-beta.14...8.0.0-rc.0) (2019-04-25)

View File

@ -1,4 +1,4 @@
<h1 class="no-toc">CLI Command Reference</h1>
# CLI Overview and Command Reference
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications. You can use the tool directly in a command shell, or indirectly through an interactive UI such as [Angular Console](https://angularconsole.com).

View File

@ -41,7 +41,6 @@
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": false,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -149,8 +149,8 @@ Learn more: See the [Template Syntax guide](guide/template-syntax "Template Synt
{@a components}
## Components
*Components* are the building blocks of Angular apps.
You've already been working with the product list component.
*Components* define areas of responsibility in your UI that let you reuse these sets of UI functionality.
You've already built one with the product list component.
A component is comprised of three things:
* **A component class,** which handles data and functionality. In the previous section, the product data and the `share()` method were defined for you in the component class.

View File

@ -87,6 +87,8 @@ If that form will need additional customization by the user, it might work best
However, if the forms will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form.
In general, the more complex the customization, the more useful the schematic approach.
{@a integrating-with-the-cli}
## Integrating with the CLI
A library can include [schematics](guide/glossary#schematic) that allow it to integrate with the Angular CLI.

View File

@ -223,9 +223,7 @@ const routes: Routes = [{
<div class="alert is-helpful">
**v8 update**: When you update to version 8, the [`ng update`](cli/update) command performs the transformation automatically. Prior to version 7, the `import()` syntax works in JIT mode (with view engine).
**Ivy:** If you are using Ivy, you must update your lazy routes to the new dynamic import syntax. See the [Ivy guide](guide/ivy) for more information.
**v8 update**: When you update to version 8, the [`ng update`](cli/update) command performs the transformation automatically. Prior to version 7, the `import()` syntax only works in JIT mode (with view engine).
</div>

View File

@ -8,9 +8,9 @@ This glossary lists the most prominent terms
and a few less familiar ones with unusual or
unexpected definitions.
[A](guide/glossary#A) [B](guide/glossary#B) [C](guide/glossary#C) [D](guide/glossary#D) [E](guide/glossary#E) [F](guide/glossary#F) [G](guide/glossary#G) [H](guide/glossary#H) [I](guide/glossary#I)
[J](guide/glossary#J) [K](guide/glossary#K) [L](guide/glossary#L) [M](guide/glossary#M) [N](guide/glossary#N) [O](guide/glossary#O) [P](guide/glossary#P) [Q](guide/glossary#Q) [R](guide/glossary#R)
[S](guide/glossary#S) [T](guide/glossary#T) [U](guide/glossary#U) [V](guide/glossary#V) [W](guide/glossary#W) [X](guide/glossary#X) [Y](guide/glossary#Y) [Z](guide/glossary#Z)
[A](#A) [B](#B) [C](#C) [D](#D) [E](#E) [F](#F) [G](#G) [H](#H) [I](#I)
[J](#J) [K](#K) [L](#L) [M](#M) [N](#N) [O](#O) [P](#P) [Q](#Q) [R](#R)
[S](#S) [T](#T) [U](#U) [V](#V) [W](#W) [X](#X) [Y](#Y) [Z](#Z)
{@a A}
@ -22,7 +22,7 @@ unexpected definitions.
The Angular ahead-of-time (AOT) compiler converts Angular HTML and TypeScript code
into efficient JavaScript code during the build phase, before the browser downloads
and runs that code.
This is the best compilation mode for production environments, with decreased load time and increased performance compared to [just-in-time (JIT) compilation](guide/glossary#jit).
This is the best compilation mode for production environments, with decreased load time and increased performance compared to [just-in-time (JIT) compilation](#jit).
By compiling your application using the `ngc` command-line tool, you can bootstrap directly to a module factory, so you don't need to include the Angular compiler in your JavaScript bundle.
@ -30,7 +30,7 @@ By compiling your application using the `ngc` command-line tool, you can bootstr
## Angular element
An Angular [component](guide/glossary#component) packaged as a [custom element](guide/glossary#custom-element).
An Angular [component](#component) packaged as a [custom element](#custom-element).
Learn more in [Angular Elements Overview](guide/elements).
@ -38,7 +38,7 @@ Learn more in [Angular Elements Overview](guide/elements).
## annotation
A structure that provides metadata for a class. See [decorator](guide/glossary#decorator).
A structure that provides metadata for a class. See [decorator](#decorator).
{@a app-shell}
@ -53,6 +53,19 @@ You can use the Angular CLI to [generate](cli/generate#appshell) an app shell.
This can improve the user experience by quickly launching a static rendered page (a skeleton common to all pages) while the browser downloads the full client version and switches to it automatically after the code loads.
See also [Service Worker and PWA](guide/service-worker-intro).
{@a architect}
## Architect
The tool that the CLI uses to perform complex tasks such as compilation and test running, according to a provided configuration.
Architect is a shell that runs a [builder](#builder) (defined in an [npm package](#npm-package)) with a given [target configuration](#target).
In the [workspace configuration file](guide/workspace-config#project-tool-configuration-options), an "architect" section provides configuration options for Architect builders.
For example, a built-in builder for linting is defined in the package `@angular-devkit/build_angular:tslint`, which uses the [TSLint](https://palantir.github.io/tslint/) tool to perform linting, with a configuration specified in a `tslint.json` file.
Use the [CLI command `ng run`](cli/run) to invoke a builder by specifying a [target configuration](#target) associated with that builder.
Integrators can add builders to enable tools and workflows to run through the Angular CLI. For example, a custom builder can replace the third-party tools used by the built-in implementations for CLI commands such as `ng build` or `ng test`.
{@a attribute-directive}
@ -62,7 +75,7 @@ See also [Service Worker and PWA](guide/service-worker-intro).
## attribute directives
A category of [directive](guide/glossary#directive) that can listen to and modify the behavior of
A category of [directive](#directive) that can listen to and modify the behavior of
other HTML elements, attributes, properties, and components. They are usually represented
as HTML attributes, hence the name.
@ -76,11 +89,11 @@ Learn more in [Attribute Directives](guide/attribute-directives).
## binding
Generally, the practice of setting a variable or property to a data value.
Within Angular, typically refers to [data binding](guide/glossary#data-binding),
Within Angular, typically refers to [data binding](#data-binding),
which coordinates DOM object properties with data object properties.
Sometimes refers to a [dependency-injection](guide/glossary#dependency-injection) binding
between a [token](guide/glossary#token) and a dependency [provider](guide/glossary#provider).
Sometimes refers to a [dependency-injection](#dependency-injection) binding
between a [token](#token) and a dependency [provider](#provider).
{@a bootstrap}
@ -88,12 +101,24 @@ between a [token](guide/glossary#token) and a dependency [provider](guide/glossa
A way to initialize and launch an app or system.
In Angular, an app's root NgModule (`AppModule`) has a `bootstrap` property that identifies the app's top-level [components](guide/glossary#component).
In Angular, an app's root NgModule (`AppModule`) has a `bootstrap` property that identifies the app's top-level [components](#component).
During the bootstrap process, Angular creates and inserts these components into the `index.html` host web page.
You can bootstrap multiple apps in the same `index.html`. Each app contains its own components.
Learn more in [Bootstrapping](guide/bootstrapping).
{@a builder}
## builder
A function that uses the [Architect](#architect) API to perform a complex process such as "build" or "test".
The builder code is defined in an [npm package](#npm-package).
For example, [BrowserBuilder](https://github.com/angular/angular-cli/tree/master/packages/angular_devkit/build_angular/src/browser) runs a [webpack](https://webpack.js.org/) build for a browser target and [KarmaBuilder](https://github.com/angular/angular-cli/tree/master/packages/angular_devkit/build_angular/src/karma) starts the Karma server and runs a webpack build for unit tests.
The [CLI command `ng run`](cli/run) invokes a builder with a specific [target configuration](#target).
The [workspace configuration](guide/workspace-config) file, `angular.json`, contains default configurations for built-in builders.
{@a C}
{@a case-conventions}
@ -123,7 +148,7 @@ Upper snake case uses words in all capital letters connected with underscores. F
## class decorator
A [decorator](guide/glossary#decorator) that appears immediately before a class definition, which declares the class to be of the given type, and provides metadata suitable to the type.
A [decorator](#decorator) that appears immediately before a class definition, which declares the class to be of the given type, and provides metadata suitable to the type.
The following decorators can declare Angular class types:
* `@Component()`
@ -137,7 +162,7 @@ The following decorators can declare Angular class types:
## class field decorator
A [decorator](guide/glossary#decorator) statement immediately before a field in a class definition that declares the type of that field. Some examples are `@Input` and `@Output`.
A [decorator](#decorator) statement immediately before a field in a class definition that declares the type of that field. Some examples are `@Input` and `@Output`.
{@a collection}
@ -149,7 +174,7 @@ In Angular, a set of related [schematics](#schematic) collected in an [npm packa
## command-line interface (CLI)
The [Angular CLI](cli) is a command-line tool for managing the Angular development cycle. Use it to create the initial filesystem scaffolding for a [workspace](guide/glossary#workspace) or [project](guide/glossary#project), and to run [schematics](guide/glossary#schematic) that add and modify code for initial generic versions of various elements. The CLI supports all stages of the development cycle, including building, testing, bundling, and deployment.
The [Angular CLI](cli) is a command-line tool for managing the Angular development cycle. Use it to create the initial filesystem scaffolding for a [workspace](#workspace) or [project](#project), and to run [schematics](#schematic) that add and modify code for initial generic versions of various elements. The CLI supports all stages of the development cycle, including building, testing, bundling, and deployment.
* To begin using the CLI for a new project, see [Getting Started](guide/quickstart).
* To learn more about the full capabilities of the CLI, see the [CLI command reference](cli).
@ -160,14 +185,19 @@ See also [Schematics CLI](#schematics-cli).
## component
A class with the `@Component()` [decorator](guide/glossary#decorator) that associates it with a companion [template](guide/glossary#template). Together, the component and template define a [view](guide/glossary#view).
A component is a special type of [directive](guide/glossary#directive).
A class with the `@Component()` [decorator](#decorator) that associates it with a companion [template](#template). Together, the component and template define a [view](#view).
A component is a special type of [directive](#directive).
The `@Component()` decorator extends the `@Directive()` decorator with template-oriented features.
An Angular component class is responsible for exposing data and handling most of the view's display and user-interaction logic through [data binding](guide/glossary#data-binding).
An Angular component class is responsible for exposing data and handling most of the view's display and user-interaction logic through [data binding](#data-binding).
Read more about components, templates, and views in [Architecture Overview](guide/architecture).
## configuration
See [workspace configuration](#cli-config)
{@a custom-element}
## custom element
@ -178,7 +208,9 @@ The custom element feature extends HTML by allowing you to define a tag whose co
You can use the API to transform an Angular component so that it can be registered with the browser and used in any HTML that you add directly to the DOM within an Angular app. The custom element tag inserts the component's view, with change-detection and data-binding functionality, into content that would otherwise be displayed without Angular processing.
See also [dynamic component loading](guide/glossary#dynamic-components).
See [Angular element](#angular-element).
See also [dynamic component loading](#dynamic-components).
{@a D}
@ -210,8 +242,8 @@ Read about the following forms of binding in [Template Syntax](guide/template-sy
## declarable
A class type that you can add to the `declarations` list of an [NgModule](guide/glossary#ngmodule).
You can declare [components](guide/glossary#component), [directives](guide/glossary#directive), and [pipes](guide/glossary#pipe).
A class type that you can add to the `declarations` list of an [NgModule](#ngmodule).
You can declare [components](#component), [directives](#directive), and [pipes](#pipe).
Don't declare the following:
* A class that's already declared in another NgModule
@ -233,7 +265,7 @@ TypeScript adds support for decorators.
Angular defines decorators that attach metadata to classes or properties
so that it knows what those classes or properties mean and how they should work.
See [class decorator](guide/glossary#class-decorator), [class field decorator](guide/glossary#class-field-decorator).
See [class decorator](#class-decorator), [class field decorator](#class-field-decorator).
{@a di}
@ -244,7 +276,7 @@ See [class decorator](guide/glossary#class-decorator), [class field decorator](g
A design pattern and mechanism for creating and delivering some parts of an application (dependencies) to other parts of an application that require them.
In Angular, dependencies are typically services, but they also can be values, such as strings or functions.
An [injector](guide/glossary#injector) for an app (created automatically during bootstrap) instantiates dependencies when needed, using a configured [provider](guide/glossary#provider) of the service or value.
An [injector](#injector) for an app (created automatically during bootstrap) instantiates dependencies when needed, using a configured [provider](#provider) of the service or value.
Learn more in [Dependency Injection in Angular](guide/dependency-injection).
@ -252,7 +284,7 @@ Learn more in [Dependency Injection in Angular](guide/dependency-injection).
## DI token
A lookup token associated with a dependency [provider](guide/glossary#provider), for use with the [dependency injection](guide/glossary#di) system.
A lookup token associated with a dependency [provider](#provider), for use with the [dependency injection](#di) system.
{@a directive}
@ -260,16 +292,16 @@ A lookup token associated with a dependency [provider](guide/glossary#provider),
## directive
A class that can modify the structure of the DOM or modify attributes in the DOM and component data model. A directive class definition is immediately preceded by a `@Directive()` [decorator](guide/glossary#decorator) that supplies metadata.
A class that can modify the structure of the DOM or modify attributes in the DOM and component data model. A directive class definition is immediately preceded by a `@Directive()` [decorator](#decorator) that supplies metadata.
A directive class is usually associated with an HTML element or attribute, and that element or attribute is often referred to as the directive itself. When Angular finds a directive in an HTML [template](guide/glossary#template), it creates the matching directive class instance and gives the instance control over that portion of the browser DOM.
A directive class is usually associated with an HTML element or attribute, and that element or attribute is often referred to as the directive itself. When Angular finds a directive in an HTML [template](#template), it creates the matching directive class instance and gives the instance control over that portion of the browser DOM.
There are three categories of directive:
* [Components](guide/glossary#component) use `@Component()` (an extension of `@Directive()`) to associate a template with a class.
* [Components](#component) use `@Component()` (an extension of `@Directive()`) to associate a template with a class.
* [Attribute directives](guide/glossary#attribute-directive) modify behavior and appearance of page elements.
* [Attribute directives](#attribute-directive) modify behavior and appearance of page elements.
* [Structural directives](guide/glossary#structural-directive) modify the structure of the DOM.
* [Structural directives](#structural-directive) modify the structure of the DOM.
Angular supplies a number of built-in directives that begin with the `ng` prefix.
You can also create new directives to implement your own functionality.
@ -288,7 +320,7 @@ Angular extends TypeScript with domain-specific languages for a number of domain
A technique for adding a component to the DOM at run time. Requires that you exclude the component from compilation and then connect it to Angular's change-detection and event-handling framework when you add it to the DOM.
See also [custom element](guide/glossary#custom-element), which provides an easier path with the same result.
See also [custom element](#custom-element), which provides an easier path with the same result.
{@a E}
@ -299,7 +331,7 @@ See also [custom element](guide/glossary#custom-element), which provides an easi
NgModules or components that are loaded on launch are called eager-loaded, to distinguish them from those
that are loaded at run time (lazy-loaded).
See [lazy loading](guide/glossary#lazy-load).
See [lazy loading](#lazy-load).
{@a ecma}
@ -308,7 +340,7 @@ See [lazy loading](guide/glossary#lazy-load).
The [official JavaScript language specification](https://en.wikipedia.org/wiki/ECMAScript).
Not all browsers support the latest ECMAScript standard, but you can use a [transpiler](guide/glossary#transpile) (like [TypeScript](guide/glossary#typescript)) to write code using the latest features, which will then be transpiled to code that runs on versions that are supported by browsers.
Not all browsers support the latest ECMAScript standard, but you can use a [transpiler](#transpile) (like [TypeScript](#typescript)) to write code using the latest features, which will then be transpiled to code that runs on versions that are supported by browsers.
To learn more, see [Browser Support](guide/browser-support).
@ -324,16 +356,16 @@ without reference to the native element.
The documentation generally refers to *elements* (`ElementRef` instances), as distinct from *DOM elements*
(which can be accessed directly if necessary).
Compare to [custom element](guide/glossary#custom-element).
Compare to [custom element](#custom-element).
{@a entry-point}
## entry point
A JavaScript symbol that makes parts of an [npm package](guide/npm-packages) available for import by other code.
The Angular [scoped packages](guide/glossary#scoped-package) each have an entry point named `index`.
The Angular [scoped packages](#scoped-package) each have an entry point named `index`.
Within Angular, use [NgModules](guide/glossary#ngmodule) to make public parts available for import by other NgModules.
Within Angular, use [NgModules](#ngmodule) to make public parts available for import by other NgModules.
{@a F}
@ -350,7 +382,7 @@ Read more forms in the [Introduction to forms in Angular](guide/forms-overview).
## form model
The "source of truth" for the value and validation status of a form input element at a given point in time. When using [reactive forms](guide/glossary#reactive-forms), the form model is created explicitly in the component class. When using [template-driven forms](guide/glossary#template-driven-forms), the form model is implicitly created by directives.
The "source of truth" for the value and validation status of a form input element at a given point in time. When using [reactive forms](#reactive-forms), the form model is created explicitly in the component class. When using [template-driven forms](#template-driven-forms), the form model is implicitly created by directives.
Learn more about reactive and template-driven forms in the [Introduction to forms in Angular](guide/forms-overview).
@ -375,22 +407,22 @@ To learn more, see [Form Validation](guide/form-validation).
## immutability
The ability to alter the state of a value after its creation. [Reactive forms](guide/glossary#reactive-forms) perform immutable changes in that
each change to the data model produces a new data model rather than modifying the existing one. [Template-driven forms](guide/glossary#template-driven-forms) perform mutable changes with `NgModel` and [two-way data binding](guide/glossary#data-binding) to modify the existing data model in place.
The ability to alter the state of a value after its creation. [Reactive forms](#reactive-forms) perform immutable changes in that
each change to the data model produces a new data model rather than modifying the existing one. [Template-driven forms](#template-driven-forms) perform mutable changes with `NgModel` and [two-way data binding](#data-binding) to modify the existing data model in place.
{@a injectable}
## injectable
An Angular class or other definition that provides a dependency using the [dependency injection](guide/glossary#di) mechanism. An injectable [service](guide/glossary#service) class must be marked by the `@Injectable()` [decorator](guide/glossary#decorator). Other items, such as constant values, can also be injectable.
An Angular class or other definition that provides a dependency using the [dependency injection](#di) mechanism. An injectable [service](#service) class must be marked by the `@Injectable()` [decorator](#decorator). Other items, such as constant values, can also be injectable.
{@a injector}
## injector
An object in the Angular [dependency-injection](guide/glossary#dependency-injection) system
An object in the Angular [dependency-injection](#dependency-injection) system
that can find a named dependency in its cache or create a dependency
using a configured [provider](guide/glossary#provider).
using a configured [provider](#provider).
Injectors are created for NgModules automatically as part of the bootstrap process
and are inherited through the component hierarchy.
@ -406,10 +438,10 @@ Learn more about the injector hierarchy in [Hierarchical Dependency Injectors](g
## input
When defining a [directive](guide/glossary#directive), the `@Input()` decorator on a directive property
When defining a [directive](#directive), the `@Input()` decorator on a directive property
makes that property available as a *target* of a [property binding](guide/template-syntax#property-binding).
Data values flow into an input property from the data source identified
in the [template expression](guide/glossary#template-expression) to the right of the equal sign.
in the [template expression](#template-expression) to the right of the equal sign.
To learn more, see [input and output properties](guide/template-syntax#inputs-outputs).
@ -417,7 +449,7 @@ To learn more, see [input and output properties](guide/template-syntax#inputs-ou
## interpolation
A form of property [data binding](guide/glossary#data-binding) in which a [template expression](guide/glossary#template-expression) between double-curly braces renders as text.
A form of property [data binding](#data-binding) in which a [template expression](#template-expression) between double-curly braces renders as text.
That text can be concatenated with neighboring text before it is assigned to an element property
or displayed between element tags, as in this example.
@ -436,7 +468,7 @@ Read more about [interpolation](guide/template-syntax#interpolation) in [Templat
## JavaScript
See [ECMAScript](guide/glossary#ecma), [TypeScript](guide/glossary#typescript).
See [ECMAScript](#ecma), [TypeScript](#typescript).
{@a jit}
@ -451,7 +483,7 @@ JIT compilation is the default (as opposed to AOT compilation) when you run Angu
JIT mode is strongly discouraged for production use
because it results in large application payloads that hinder the bootstrap performance.
Compare to [ahead-of-time (AOT) compilation](guide/glossary#aot).
Compare to [ahead-of-time (AOT) compilation](#aot).
{@a K}
@ -464,21 +496,22 @@ Compare to [ahead-of-time (AOT) compilation](guide/glossary#aot).
## lazy loading
A process that speeds up application load time by splitting the application into multiple bundles and loading them on demand.
For example, dependencies can be lazy loaded as needed&mdash;as opposed to [eager-loaded](guide/glossary#eager-loading) modules that are required by the root module and are thus loaded on launch.
For example, dependencies can be lazy loaded as needed&mdash;as opposed to [eager-loaded](#eager-loading) modules that are required by the root module and are thus loaded on launch.
The [router](guide/glossary#router) makes use of lazy loading to load child views only when the parent view is activated.
The [router](#router) makes use of lazy loading to load child views only when the parent view is activated.
Similarly, you can build custom elements that can be loaded into an Angular app when needed.
{@a library}
## library
In Angular, a [project](guide/glossary#project) that provides functionality that can be included in other Angular apps.
In Angular, a [project](#project) that provides functionality that can be included in other Angular apps.
A library isn't a complete Angular app and can't run independently.
(To add re-usable Angular functionality to non-Angular web apps, you can use Angular [custom elements](#angular-element).)
* Library developers can use the [Angular CLI](guide/glossary#cli) to `generate` scaffolding for a new library in an existing [workspace](guide/glossary#workspace), and can publish a library as an `npm` package.
* Library developers can use the [Angular CLI](#cli) to `generate` scaffolding for a new library in an existing [workspace](#workspace), and can publish a library as an `npm` package.
* Application developers can use the [Angular CLI](guide/glossary#cli) to `add` a published library for use with an application in the same [workspace](guide/glossary#workspace).
* Application developers can use the [Angular CLI](#cli) to `add` a published library for use with an application in the same [workspace](#workspace).
See also [schematic](#schematic).
@ -486,14 +519,14 @@ See also [schematic](#schematic).
## lifecycle hook
An interface that allows you to tap into the lifecycle of [directives](guide/glossary#directive) and [components](guide/glossary#component) as they are created, updated, and destroyed.
An interface that allows you to tap into the lifecycle of [directives](#directive) and [components](#component) as they are created, updated, and destroyed.
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
For example, the `OnInit` interface has a hook method named `ngOnInit`.
Angular calls these hook methods in the following order:
* `ngOnChanges`: When an [input](guide/glossary#input)/[output](guide/glossary#output) binding value changes.
* `ngOnChanges`: When an [input](#input)/[output](#output) binding value changes.
* `ngOnInit`: After the first `ngOnChanges`.
* `ngDoCheck`: Developer's custom change detection.
* `ngAfterContentInit`: After component content initialized.
@ -517,7 +550,7 @@ In JavaScript (ECMAScript), each file is a module and all objects defined in the
Angular ships as a collection of JavaScript modules (also called libraries). Each Angular library name begins with the `@angular` prefix. Install Angular libraries with the [npm package manager](https://docs.npmjs.com/getting-started/what-is-npm) and import parts of them with JavaScript `import` declarations.
Compare to [NgModule](guide/glossary#ngmodule).
Compare to [NgModule](#ngmodule).
{@a N}
@ -526,12 +559,12 @@ Compare to [NgModule](guide/glossary#ngmodule).
## NgModule
A class definition preceded by the `@NgModule()` [decorator](guide/glossary#decorator), which declares and serves as a manifest for a block of code dedicated to an application domain, a workflow, or a closely related set of capabilities.
A class definition preceded by the `@NgModule()` [decorator](#decorator), which declares and serves as a manifest for a block of code dedicated to an application domain, a workflow, or a closely related set of capabilities.
Like a [JavaScript module](guide/glossary#module), an NgModule can export functionality for use by other NgModules and import public functionality from other NgModules.
The metadata for an NgModule class collects components, directives, and pipes that the application uses along with the list of imports and exports. See also [declarable](guide/glossary#declarable).
Like a [JavaScript module](#module), an NgModule can export functionality for use by other NgModules and import public functionality from other NgModules.
The metadata for an NgModule class collects components, directives, and pipes that the application uses along with the list of imports and exports. See also [declarable](#declarable).
NgModules are typically named after the file in which the exported thing is defined. For example, the Angular [DatePipe](api/common/DatePipe) class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`. You import them from an Angular [scoped package](guide/glossary#scoped-package) such as `@angular/core`.
NgModules are typically named after the file in which the exported thing is defined. For example, the Angular [DatePipe](api/common/DatePipe) class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`. You import them from an Angular [scoped package](#scoped-package) such as `@angular/core`.
Every Angular application has a root module. By convention, the class is called `AppModule` and resides in a file named `app.module.ts`.
@ -551,7 +584,7 @@ Learn more about how Angular uses [Npm Packages](guide/npm-packages).
## observable
A producer of multiple values, which it pushes to [subscribers](guide/glossary#subscriber). Used for asynchronous event handling throughout Angular. You execute an observable by subscribing to it with its `subscribe()` method, passing callbacks for notifications of new values, errors, or completion.
A producer of multiple values, which it pushes to [subscribers](#subscriber). Used for asynchronous event handling throughout Angular. You execute an observable by subscribing to it with its `subscribe()` method, passing callbacks for notifications of new values, errors, or completion.
Observables can deliver single or multiple values of any type to subscribers, either synchronously (as a function delivers a value to its caller) or on a schedule. A subscriber receives notification of new values as they are produced and notification of either normal completion or error completion.
@ -564,16 +597,16 @@ To learn more, see [Observables](guide/observables).
## observer
An object passed to the `subscribe()` method for an [observable](guide/glossary#observable). The object defines the callbacks for the [subscriber](guide/glossary#subscriber).
An object passed to the `subscribe()` method for an [observable](#observable). The object defines the callbacks for the [subscriber](#subscriber).
{@a output}
## output
When defining a [directive](guide/glossary#directive), the `@Output{}` decorator on a directive property
When defining a [directive](#directive), the `@Output{}` decorator on a directive property
makes that property available as a *target* of [event binding](guide/template-syntax#event-binding).
Events stream *out* of this property to the receiver identified
in the [template expression](guide/glossary#template-expression) to the right of the equal sign.
in the [template expression](#template-expression) to the right of the equal sign.
To learn more, see [Input and Output Properties](guide/template-syntax#inputs-outputs).
@ -584,7 +617,7 @@ To learn more, see [Input and Output Properties](guide/template-syntax#inputs-ou
## pipe
A class which is preceded by the `@Pipe{}` decorator and which defines a function that transforms input values to output values for display in a [view](guide/glossary#view). Angular defines various pipes, and you can define new pipes.
A class which is preceded by the `@Pipe{}` decorator and which defines a function that transforms input values to output values for display in a [view](#view). Angular defines various pipes, and you can define new pipes.
To learn more, see [Pipes](guide/pipes).
@ -599,22 +632,26 @@ See [Browser Support](guide/browser-support) for polyfills that support particul
## project
In Angular, a folder within a [workspace](guide/glossary#workspace) that contains an Angular app or [library](guide/glossary#library).
A workspace can contain multiple projects.
All apps in a workspace can use libraries in the same workspace.
In the Angular CLI, a standalone application or [library](#library) that can be created or modified by a CLI command.
A project, as generated by the [`ng new`](cli/new), contains the set of source files, resources, and configuration files that you need to develop and test the application using the CLI. Projects can also be created with the `ng generate application` and `ng generate library` commands.
For more information, see [Project File Structure](guide/file-structure).
The [`angular.json`](guide/workspace-config) file configures all projects in a [workspace](#workspace).
{@a provider}
## provider
An object that implements one of the [`Provider`](api/core/Provider) interfaces. A provider object defines how to obtain an injectable dependency associated with a [DI token](guide/glossary#token).
An [injector](guide/glossary#injector) uses the provider to create a new instance of a dependency
An object that implements one of the [`Provider`](api/core/Provider) interfaces. A provider object defines how to obtain an injectable dependency associated with a [DI token](#token).
An [injector](#injector) uses the provider to create a new instance of a dependency
for a class that requires it.
Angular registers its own providers with every injector, for services that Angular defines.
You can register your own providers for services that your app needs.
See also [service](guide/glossary#service), [dependency injection](guide/glossary#di).
See also [service](#service), [dependency injection](#di).
Learn more in [Dependency Injection](guide/dependency-injection).
@ -628,7 +665,7 @@ Learn more in [Dependency Injection](guide/dependency-injection).
## reactive forms
A framework for building Angular forms through code in a component.
The alternative is a [template-driven form](guide/glossary#template-driven-forms).
The alternative is a [template-driven form](#template-driven-forms).
When using reactive forms:
@ -645,11 +682,11 @@ The alternative is a template-driven form. For an introduction and comparison of
## router
A tool that configures and implements navigation among states and [views](guide/glossary#view) within an Angular app.
A tool that configures and implements navigation among states and [views](#view) within an Angular app.
The `Router` module is an [NgModule](guide/glossary#ngmodule) that provides the necessary service providers and directives for navigating through application views. A [routing component](guide/glossary#routing-component) is one that imports the `Router` module and whose template contains a `RouterOutlet` element where it can display views produced by the router.
The `Router` module is an [NgModule](#ngmodule) that provides the necessary service providers and directives for navigating through application views. A [routing component](#routing-component) is one that imports the `Router` module and whose template contains a `RouterOutlet` element where it can display views produced by the router.
The router defines navigation among views on a single page, as opposed to navigation among pages. It interprets URL-like links to determine which views to create or destroy, and which components to load or unload. It allows you to take advantage of [lazy loading](guide/glossary#lazy-load) in your Angular apps.
The router defines navigation among views on a single page, as opposed to navigation among pages. It interprets URL-like links to determine which views to create or destroy, and which components to load or unload. It allows you to take advantage of [lazy loading](#lazy-load) in your Angular apps.
To learn more, see [Routing and Navigation](guide/router).
@ -657,20 +694,21 @@ To learn more, see [Routing and Navigation](guide/router).
## router outlet
A [directive](guide/glossary#directive) that acts as a placeholder in a routing component's template. Angular dynamically renders the template based on the current router state.
A [directive](#directive) that acts as a placeholder in a routing component's template. Angular dynamically renders the template based on the current router state.
{@a router-component}
## routing component
An Angular [component](guide/glossary#component) with a `RouterOutlet` directive in its template that displays views based on router navigations.
An Angular [component](#component) with a `RouterOutlet` directive in its template that displays views based on router navigations.
For more information, see [Routing and Navigation](guide/router).
{@a rule}
In [schematics](#schematic), a function that operates on a [file tree](#file-tree) to create, delete, or modify files in a specific manner, and returns a new `Tree` object.
## rule
In [schematics](#schematic), a function that operates on a [file tree](#file-tree) to create, delete, or modify files in a specific manner.
{@a S}
@ -680,13 +718,15 @@ In [schematics](#schematic), a function that operates on a [file tree](#file-tre
A scaffolding library that defines how to generate or transform a programming project by creating, modifying, refactoring, or moving files and code.
A schematic defines [rules](#rule) that operate on a virtual file system called a [tree](#file-tree).
The [Angular CLI](guide/glossary#cli) uses schematics to generate and modify [Angular projects](guide/glossary#project) and parts of projects.
The [Angular CLI](#cli) uses schematics to generate and modify [Angular projects](#project) and parts of projects.
* Angular provides a set of schematics for use with the CLI. See the [Angular CLI command reference](cli). The [`ng add`](cli/add) command runs schematics as part of adding a library to your project. The [`ng generate`](cli/generate) command runs schematics to create apps, libraries, and Angular code constructs.
* [Library](#library) developers can use the [Schematics CLI](#schematics-cli) to create schematics that enable the Angular CLI to add and update their published libraries, and to generate artifacts the library defines.
* [Library](#library) developers can create schematics that enable the Angular CLI to add and update their published libraries, and to generate artifacts the library defines.
Add these schematics to the npm package that you use to publish and share your library.
For more information, see [devkit documentation](https://www.npmjs.com/package/@angular-devkit/schematics).
For more information, see [Schematics](guide/schematics) and [Integrating Libraries with the CLI](guide/creating-libraries#integrating-with-the-cli).
{@a schematics-cli}
@ -699,7 +739,7 @@ Using Node 6.9 or above, install the Schematics CLI globally:
npm install -g @angular-devkit/schematics-cli
</code-example>
This installs the `schematics` executable, which you can use to create a new project, add a new schematic to an existing project, or extend an existing schematic.
This installs the `schematics` executable, which you can use to create a new schematics [collection](#collection) with an initial named schematic. The collection folder is a workspace for schematics. You can also use the `schematics` command to add a new schematic to an existing collection, or extend an existing schematic.
{@a scoped-package}
@ -724,19 +764,19 @@ It can also pre-generate pages as HTML files that you serve later.
This technique can improve performance on mobile and low-powered devices and improve the user experience by showing a static first page quickly while the client-side app is loading.
The static version can also make your app more visible to web crawlers.
You can easily prepare an app for server-side rendering by using the [CLI](guide/glossary#cli) to run the [Angular Universal](#universal) tool, using the `@nguniversal/express-engine` [schematic](#schematic).
You can easily prepare an app for server-side rendering by using the [CLI](#cli) to run the [Angular Universal](#universal) tool, using the `@nguniversal/express-engine` [schematic](#schematic).
{@a service}
## service
In Angular, a class with the [@Injectable()](guide/glossary#injectable) decorator that encapsulates non-UI logic and code that can be reused across an application.
In Angular, a class with the [@Injectable()](#injectable) decorator that encapsulates non-UI logic and code that can be reused across an application.
Angular distinguishes components from services to increase modularity and reusability.
The `@Injectable()` metadata allows the service class to be used with the [dependency injection](guide/glossary#di) mechanism.
The injectable class is instantiated by a [provider](guide/glossary#provider).
[Injectors](guide/glossary#injector) maintain lists of providers and use them to provide service instances when they are required by components or other services.
The `@Injectable()` metadata allows the service class to be used with the [dependency injection](#di) mechanism.
The injectable class is instantiated by a [provider](#provider).
[Injectors](#injector) maintain lists of providers and use them to provide service instances when they are required by components or other services.
To learn more, see [Introduction to Services and Dependency Injection](guide/architecture-services).
@ -745,7 +785,7 @@ To learn more, see [Introduction to Services and Dependency Injection](guide/arc
## structural directives
A category of [directive](guide/glossary#directive) that is responsible for shaping HTML layout by modifying the DOM&mdashthat is, adding, removing, or manipulating elements and their children.
A category of [directive](#directive) that is responsible for shaping HTML layout by modifying the DOM&mdashthat is, adding, removing, or manipulating elements and their children.
To learn more, see [Structural Directives](guide/structural-directives).
@ -753,42 +793,57 @@ To learn more, see [Structural Directives](guide/structural-directives).
## subscriber
A function that defines how to obtain or generate values or messages to be published. This function is executed when a consumer calls the `subscribe()` method of an [observable](guide/glossary#observable).
A function that defines how to obtain or generate values or messages to be published. This function is executed when a consumer calls the `subscribe()` method of an [observable](#observable).
The act of subscribing to an observable triggers its execution, associates callbacks with it, and creates a `Subscription` object that lets you unsubscribe.
The `subscribe()` method takes a JavaScript object (called an [observer](guide/glossary#observer)) with up to three callbacks, one for each type of notification that an observable can deliver:
The `subscribe()` method takes a JavaScript object (called an [observer](#observer)) with up to three callbacks, one for each type of notification that an observable can deliver:
* The `next` notification sends a value such as a number, a string, or an object.
* The `error` notification sends a JavaScript Error or exception.
* The `complete` notification doesn't send a value, but the handler is called when the call completes. Scheduled values can continue to be returned after the call completes.
{@a T}
{@a target}
## target
A buildable or runnable subset of a [project](#project), configured as an object in the [workspace configuration file](guide/workspace-config#project-tool-configuration-options), and executed by an [Architect](#architect) [builder](#builder).
In the `angular.json` file, each project has an "architect" section that contains targets which configure builders. Some of these targets correspond to [CLI commands](#cli), such as `build`, `serve`, `test`, and `lint`.
For example, the Architect builder invoked by the `ng build` command to compile a project uses a particular build tool, and has a default configuration whose values can be overridden on the command line. The `build` target also defines an alternate configuration for a "production" build, that can be invoked with the `--prod` flag on the `build` command.
The Architect tool provides a set of builders. The [`ng new` command](cli/new) provides a set of targets for the initial application project. The [`ng generate application`](cli/generate#application) and [`ng generate library`](cli/generate#library) commands provide a set of targets for each new [project](#project). These targets, their options and configurations, can be customized to meet the needs of your project. For example, you may want to add a "staging" or "testing" configuration to a project's "build" target.
You can also define a custom builder, and add a target to the project configuration that uses your custom builder. You can then run the target using the [`ng run`](cli/run) CLI command.
{@a template}
## template
Code associated with a component that defines how to render the component's [view](guide/glossary#view).
Code associated with a component that defines how to render the component's [view](#view).
A template combines straight HTML with Angular [data-binding](guide/glossary#data-binding) syntax, [directives](guide/glossary#directive),
and [template expressions](guide/glossary#template-expression) (logical constructs).
A template combines straight HTML with Angular [data-binding](#data-binding) syntax, [directives](#directive),
and [template expressions](#template-expression) (logical constructs).
The Angular elements insert or calculate values that modify the HTML elements before the page is displayed.
A template is associated with a [component](guide/glossary#component) class through the `@Component()` [decorator](guide/glossary#decorator). The HTML can be provided inline, as the value of the `template` property, or in a separate HTML file linked through the `templateUrl` property.
A template is associated with a [component](#component) class through the `@Component()` [decorator](#decorator). The HTML can be provided inline, as the value of the `template` property, or in a separate HTML file linked through the `templateUrl` property.
Additional templates, represented by `TemplateRef` objects, can define alternative or *embedded* views, which can be referenced from multiple components.
{@a template-drive-forms}
{@a template-driven-forms}
## template-driven forms
A format for building Angular forms using HTML forms and input elements in the view.
The alternative format uses the [reactive forms](guide/glossary#reactive-forms) framework.
The alternative format uses the [reactive forms](#reactive-forms) framework.
When using template-driven forms:
* The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
* [Two-way binding](guide/glossary#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
* [Two-way binding](#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
* Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
* The associated Angular directives are prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
@ -798,7 +853,7 @@ The alternative is a reactive form. For an introduction and comparison of both f
## template expression
A TypeScript-like syntax that Angular evaluates within a [data binding](guide/glossary#data-binding).
A TypeScript-like syntax that Angular evaluates within a [data binding](#data-binding).
Read about how to write template expressions in [Template expressions](guide/template-syntax#template-expressions).
@ -806,7 +861,7 @@ Read about how to write template expressions in [Template expressions](guide/te
## token
An opaque identifier used for efficient table lookup. In Angular, a [DI token](guide/glossary#di-token) is used to find [providers](guide/glossary#provider) of dependencies in the [dependency injection](guide/glossary#di) system.
An opaque identifier used for efficient table lookup. In Angular, a [DI token](#di-token) is used to find [providers](#provider) of dependencies in the [dependency injection](#di) system.
{@a transpile}
@ -853,18 +908,18 @@ To learn more, see [Angular Universal: server-side rendering](guide/universal).
## view
The smallest grouping of display elements that can be created and destroyed together.
Angular renders a view under the control of one or more [directives](guide/glossary#directive),
especially [component](guide/glossary#component) directives and their companion [templates](guide/glossary#template).
Angular renders a view under the control of one or more [directives](#directive),
especially [component](#component) directives and their companion [templates](#template).
A view is specifically represented by a `ViewRef` instance associated with the component.
A view that belongs to a component is called a *host view*.
Views are typically collected into [view hierarchies](guide/glossary#view-tree).
Views are typically collected into [view hierarchies](#view-tree).
Properties of elements in a view can change dynamically, in response to user actions;
the structure (number and order) of elements in a view can't.
You can change the structure of elements by inserting, moving, or removing nested views within their view containers.
View hierarchies can be loaded and unloaded dynamically as the user navigates through the application, typically under the control of a [router](guide/glossary#router).
View hierarchies can be loaded and unloaded dynamically as the user navigates through the application, typically under the control of a [router](#router).
{@a view-tree}
@ -879,16 +934,35 @@ The view hierarchy doesn't imply a component hierarchy. Views that are embedded
## web component
See [custom element](guide/glossary#custom-element).
See [custom element](#custom-element).
{@a workspace}
## workspace
In Angular, a folder that contains [projects](guide/glossary#project) (that is, apps and libraries).
The [CLI](guide/glossary#cli) `ng new` command creates a workspace to contain projects.
A collection of Angular [projects](#project) (that is, applications and libraries) powered by the [Angular CLI] (#cli) that are typically co-located in a single source-control repository (such as [git](https://git-scm.com/)).
The [CLI](#cli) [`ng new` command](cli/new) creates a file system directory (the "workspace root").
In the workspace root, it also creates the workspace [configuration file](#configuration) (`angular.json`) and, by default, an initial application project with the same name.
Commands that create or operate on apps and libraries (such as `add` and `generate`) must be executed from within a workspace folder.
For more information, see [Workspace Configuration](guide/workspace-config).
{@a cli-config}
{@a config}
## workspace configuration
A file named `angular.json` at the root level of an Angular [workspace](#workspace) provides workspace-wide and project-specific configuration defaults for build and development tools that are provided by or integrated with the [Angular CLI](#cli).
For more information, see [Workspace Configuration](guide/workspace-config).
Additional project-specific configuration files are used by tools, such as `package.json` for the [npm package manager](#npm-package), `tsconfig.json` for [TypeScript transpilation](#transpile), and `tslint.json` for [TSLint](https://palantir.github.io/tslint/).
For more information, see [Workspace and Project File Structure](guide/file-structure).
{@a X}
@ -902,7 +976,7 @@ Commands that create or operate on apps and libraries (such as `add` and `genera
An execution context for a set of asynchronous tasks. Useful for debugging, profiling, and testing apps that include asynchronous operations such as event processing, promises, and calls to remote servers.
An Angular app runs in a zone where it can respond to asynchronous events by checking for data changes and updating the information it displays by resolving [data bindings](guide/glossary#data-binding).
An Angular app runs in a zone where it can respond to asynchronous events by checking for data changes and updating the information it displays by resolving [data bindings](#data-binding).
A zone client can take action before and after an async operation completes.

View File

@ -15,6 +15,9 @@ For the final sample app with two lazy loaded modules that this page describes,
## High level view
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading&mdash;a design pattern that loads NgModules as needed. Lazy loading helps keep initial
bundle sizes smaller, which in turn helps decrease load times.
There are three main steps to setting up a lazy loaded feature module:
1. Create the feature module.
@ -67,9 +70,6 @@ ng generate component customers/customer-list
This creates a folder inside of `customers` called `customer-list`
with the four files that make up the component.
<!-- For more information
about components, see [Components](). -->
Just like with the routing module, the CLI imports the
`CustomerListComponent` into the `CustomersModule`.
@ -217,7 +217,7 @@ knows that the route list is only responsible for providing additional routes an
`forRoot()` contains injector configuration which is global; such as configuring the Router. `forChild()` has no injector configuration, only directives such as `RouterOutlet` and `RouterLink`.
For more information, see the [`forRoot()` deep dive](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
For more information, see the [`forRoot()` pattern](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
<hr>

View File

@ -89,7 +89,7 @@ Package name | Description
Many browsers lack native support for some features in the latest HTML standards,
features that Angular requires.
[_Polyfills_](https://en.wikipedia.org/wiki/Polyfill) can emulate the missing features.
[_Polyfills_](https://en.wikipedia.org/wiki/Polyfill_(programming)) can emulate the missing features.
The [Browser Support](guide/browser-support) guide explains which browsers need polyfills and
how you can add them.

View File

@ -2261,7 +2261,7 @@ For the `@routeAnimation` transitions to key off states, you'll need to provide
</code-example>
The `@routeAnimation` property is bound to the `getAnimationData` with the provided `routerOutlet` reference, so you'll need to define that function in the `AppComponent`. The `getAnimationData` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideDownAnimation` defined in `animations.ts`.
The `@routeAnimation` property is bound to the `getAnimationData` with the provided `routerOutlet` reference, so you'll need to define that function in the `AppComponent`. The `getAnimationData` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (router outlet)" region="function-binding">
@ -2677,7 +2677,7 @@ display the `Crisis Center Home` and `Crisis Detail` route components.
The `Crisis Detail` route is a child of the `Crisis List`. The router [reuses components](#reuse)
by default, so the `Crisis Detail` component will be re-used as you select different crises.
In contrast, back in the `Hero Detail` route, the component was recreated each time you selected a different hero.
In contrast, back in the `Hero Detail` route, [the component was recreated](#snapshot-the-no-observable-alternative) each time you selected a different hero from the list of heroes.
At the top level, paths that begin with `/` refer to the root of the application.
But child routes *extend* the path of the parent route.

View File

@ -157,7 +157,7 @@ export class <%= classify(name) %>Service {
</code-example>
* The `classify` and `dasherize` methods are utility functions you schematic will use to transform your source template and filename.
* The `classify` and `dasherize` methods are utility functions that your schematic will use to transform your source template and filename.
* The `name` is provided as a property from your factory function. It is the same `name` you defined in the schema.

View File

@ -25,7 +25,7 @@ There are two ways to make a service a singleton in Angular:
Beginning with Angular 6.0, the preferred way to create a singleton service is to set `providedIn` to `root` on the service's `@Injectable()` decorator. This tells Angular
to provide the service in the application root.
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.0.ts" linenums="false"> </code-example>
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
[Tour of Heroes tutorial](tutorial).

View File

@ -179,7 +179,7 @@ Some additional options (listed below) can only be set through the configuration
## Project asset configuration
Each `build` target configuration can include as `assets` array that lists files or folders you want to copy as-is when building your project.
Each `build` target configuration can include an `assets` array that lists files or folders you want to copy as-is when building your project.
By default, the `src/assets/` folder and `src/favicon.ico` are copied over.
<code-example format="." language="json" linenums="false">

View File

@ -490,6 +490,12 @@
"rev": true,
"title": "Carbon Components Angular",
"url": "https://angular.carbondesignsystem.com/"
},
"jigsaw": {
"desc": "Jigsaw provides a set of web components based on Angular. It is supporting the development of all applications of Big Data Product of ZTE (http://www.zte.com.cn).",
"rev": true,
"title": "Awade Jigsaw (Chinese)",
"url": "http://rdk.zte.com.cn/components"
}
}
}

View File

@ -54,9 +54,9 @@
{"type": 301, "source": "/**/api/common/SelectControlValueAccessor-*", "destination": "/api/forms/SelectControlValueAccessor"},
{"type": 301, "source": "/**/api/common/NgModel", "destination": "/api/forms/NgModel"},
// APIs under `http` package is deprecated and new APIs are available under `common/http` package
{"type": 301, "source": "/api/http/:rest*", "destination": "/guide/deprecation#http"},
{"type": 301, "source": "/api/http", "destination": "/guide/deprecation#http"},
// `@angular/http` package was removed, and new `HttpClient` APIs are available under `@angular/common/http` package
{"type": 301, "source": "/api/http/:rest*", "destination": "/guide/deprecations#http"},
{"type": 301, "source": "/api/http", "destination": "/guide/deprecations#http"},
// Animations moves, renames and removals
{"type": 301, "source": "/api/animate/:rest*", "destination": "/api/animations/:rest*"},

View File

@ -76,28 +76,28 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^8.0.0-beta.14",
"@angular/cdk": "8.0.0-beta.2",
"@angular/common": "^8.0.0-beta.14",
"@angular/core": "^8.0.0-beta.14",
"@angular/elements": "^8.0.0-beta.14",
"@angular/forms": "^8.0.0-beta.14",
"@angular/material": "8.0.0-beta.2",
"@angular/platform-browser": "^8.0.0-beta.14",
"@angular/platform-browser-dynamic": "^8.0.0-beta.14",
"@angular/router": "^8.0.0-beta.14",
"@angular/service-worker": "^8.0.0-beta.14",
"@angular/animations": "^8.0.0-rc.2",
"@angular/cdk": "8.0.0-rc.0",
"@angular/common": "^8.0.0-rc.2",
"@angular/core": "^8.0.0-rc.2",
"@angular/elements": "^8.0.0-rc.2",
"@angular/forms": "^8.0.0-rc.2",
"@angular/material": "8.0.0-rc.0",
"@angular/platform-browser": "^8.0.0-rc.2",
"@angular/platform-browser-dynamic": "^8.0.0-rc.2",
"@angular/router": "^8.0.0-rc.2",
"@angular/service-worker": "^8.0.0-rc.2",
"@types/lunr": "^2.3.2",
"@webcomponents/custom-elements": "^1.2.0",
"rxjs": "^6.5.1",
"zone.js": "^0.9.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.800.0-beta.18",
"@angular/cli": "8.0.0-beta.18",
"@angular/compiler": "^8.0.0-beta.14",
"@angular/compiler-cli": "^8.0.0-beta.14",
"@angular/language-service": "^8.0.0-beta.14",
"@angular-devkit/build-angular": "0.800.0-rc.2",
"@angular/cli": "8.0.0-rc.2",
"@angular/compiler": "^8.0.0-rc.2",
"@angular/compiler-cli": "^8.0.0-rc.2",
"@angular/language-service": "^8.0.0-rc.2",
"@types/jasmine": "^2.5.52",
"@types/jasminewd2": "^2.0.4",
"@types/node": "~6.0.60",

View File

@ -4,10 +4,10 @@
"uncompressed": {
"runtime-es5": 2980,
"runtime-es2015": 2986,
"main-es5": 501356,
"main-es2015": 440336,
"main-es5": 504760,
"main-es2015": 443497,
"polyfills-es5": 128751,
"polyfills-es2015": 59557
"polyfills-es2015": 53147
}
}
}

View File

@ -33,7 +33,7 @@ function _main() {
const oldTsConfigStr = readFileSync(tsConfigPath, 'utf8');
const oldTsConfigObj = parse(oldTsConfigStr);
const newTsConfigObj = extend(true, oldTsConfigObj, NG_COMPILER_OPTS);
const newTsConfigStr = JSON.stringify(newTsConfigObj, null, 2);
const newTsConfigStr = `${JSON.stringify(newTsConfigObj, null, 2)}\n`;
console.log(`\nNew config: ${newTsConfigStr}`);
writeFileSync(tsConfigPath, newTsConfigStr);

View File

@ -6,11 +6,8 @@ set +x -eu -o pipefail
readonly aioDir="$(realpath $thisDir/..)"
readonly protractorConf="$aioDir/tests/deployment/e2e/protractor.conf.js"
readonly minPwaScore="$1"
readonly urls=(
"https://angular.io/"
"https://next.angular.io/"
)
readonly targetUrl="$1"
readonly minPwaScore="$2"
cd "$aioDir"
@ -19,16 +16,14 @@ set +x -eu -o pipefail
yarn install --frozen-lockfile --non-interactive
yarn update-webdriver
# Run checks for all URLs.
for url in "${urls[@]}"; do
echo -e "\nChecking '$url'...\n-----"
# Run checks for target URL.
echo -e "\nChecking '$targetUrl'...\n-----"
# Run basic e2e and deployment config tests.
yarn protractor "$protractorConf" --baseUrl "$url"
# Run basic e2e and deployment config tests.
yarn protractor "$protractorConf" --baseUrl "$targetUrl"
# Run PWA-score tests.
yarn test-pwa-score "$url" "$minPwaScore"
done
# Run PWA-score tests.
yarn test-pwa-score "$targetUrl" "$minPwaScore"
echo -e "\nAll checks passed!"
)

View File

@ -23,10 +23,10 @@ describe('CodeExampleComponent', () => {
});
fixture = TestBed.createComponent(HostComponent);
fixture.detectChanges();
hostComponent = fixture.componentInstance;
codeExampleComponent = hostComponent.codeExampleComponent;
fixture.detectChanges();
});
it('should be able to capture the code snippet provided in content', () => {

View File

@ -23,10 +23,10 @@ describe('CodeTabsComponent', () => {
});
fixture = TestBed.createComponent(HostComponent);
fixture.detectChanges();
hostComponent = fixture.componentInstance;
codeTabsComponent = hostComponent.codeTabsComponent;
fixture.detectChanges();
});
it('should get correct tab info', () => {

View File

@ -63,178 +63,182 @@ describe('TocComponent', () => {
expect(tocComponent.type).toEqual('None');
});
it('should not display anything when no h2 or h3 TocItems', () => {
tocService.tocList.next([tocItem('H1', 'h1')]);
fixture.detectChanges();
expect(tocComponentDe.children.length).toEqual(0);
});
describe('(once the lifecycle hooks have run)', () => {
beforeEach(() => fixture.detectChanges());
it('should update when the TocItems are updated', () => {
tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(3);
});
it('should only display H2 and H3 TocItems', () => {
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]);
fixture.detectChanges();
const tocItems = tocComponentDe.queryAll(By.css('li'));
const textContents = tocItems.map(item => item.nativeNode.textContent.trim());
expect(tocItems.length).toBe(2);
expect(textContents.find(text => text === 'Heading A')).toBeFalsy();
expect(textContents.find(text => text === 'Heading B')).toBeTruthy();
expect(textContents.find(text => text === 'Heading C')).toBeTruthy();
expect(setPage().tocH1Heading).toBeFalsy();
});
it('should stop listening for TocItems once destroyed', () => {
tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocComponent.ngOnDestroy();
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
});
describe('when fewer than `maxPrimary` TocItems', () => {
beforeEach(() => {
tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C'), tocItem('Heading D')]);
it('should not display anything when no h2 or h3 TocItems', () => {
tocService.tocList.next([tocItem('H1', 'h1')]);
fixture.detectChanges();
page = setPage();
expect(tocComponentDe.children.length).toEqual(0);
});
it('should have four displayed items', () => {
expect(page.listItems.length).toEqual(4);
});
it('should not have secondary items', () => {
expect(tocComponent.type).toEqual('EmbeddedSimple');
const aSecond = page.listItems.find(item => item.classes.secondary);
expect(aSecond).toBeFalsy('should not find a secondary');
});
it('should not display expando buttons', () => {
expect(page.tocHeadingButtonEmbedded).toBeFalsy('top expand/collapse button');
expect(page.tocMoreButton).toBeFalsy('bottom more button');
});
});
describe('when many TocItems', () => {
let scrollToTopSpy: jasmine.Spy;
beforeEach(() => {
it('should update when the TocItems are updated', () => {
tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges();
page = setPage();
scrollToTopSpy = TestBed.get(ScrollService).scrollToTop;
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(3);
});
it('should have more than 4 displayed items', () => {
expect(page.listItems.length).toBeGreaterThan(4);
it('should only display H2 and H3 TocItems', () => {
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]);
fixture.detectChanges();
const tocItems = tocComponentDe.queryAll(By.css('li'));
const textContents = tocItems.map(item => item.nativeNode.textContent.trim());
expect(tocItems.length).toBe(2);
expect(textContents.find(text => text === 'Heading A')).toBeFalsy();
expect(textContents.find(text => text === 'Heading B')).toBeTruthy();
expect(textContents.find(text => text === 'Heading C')).toBeTruthy();
expect(setPage().tocH1Heading).toBeFalsy();
});
it('should not display the h1 item', () => {
expect(page.listItems.find(item => item.classes.h1)).toBeFalsy('should not find h1 item');
it('should stop listening for TocItems once destroyed', () => {
tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocComponent.ngOnDestroy();
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges();
expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
});
it('should be in "collapsed" (not expanded) state at the start', () => {
expect(tocComponent.isCollapsed).toBeTruthy();
});
it('should have "collapsed" class at the start', () => {
expect(tocComponentDe.children[0].classes.collapsed).toEqual(true);
});
it('should display expando buttons', () => {
expect(page.tocHeadingButtonEmbedded).toBeTruthy('top expand/collapse button');
expect(page.tocMoreButton).toBeTruthy('bottom more button');
});
it('should have secondary items', () => {
expect(tocComponent.type).toEqual('EmbeddedExpandable');
});
// CSS will hide items with the secondary class when collapsed
it('should have secondary item with a secondary class', () => {
const aSecondary = page.listItems.find(item => item.classes.secondary);
expect(aSecondary).toBeTruthy('should find a secondary');
});
describe('after click tocHeading button', () => {
describe('when fewer than `maxPrimary` TocItems', () => {
beforeEach(() => {
page.tocHeadingButtonEmbedded.nativeElement.click();
tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C'), tocItem('Heading D')]);
fixture.detectChanges();
page = setPage();
});
it('should not be "collapsed"', () => {
expect(tocComponent.isCollapsed).toEqual(false);
it('should have four displayed items', () => {
expect(page.listItems.length).toEqual(4);
});
it('should not have "collapsed" class', () => {
expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy();
it('should not have secondary items', () => {
expect(tocComponent.type).toEqual('EmbeddedSimple');
const aSecond = page.listItems.find(item => item.classes.secondary);
expect(aSecond).toBeFalsy('should not find a secondary');
});
it('should not scroll', () => {
expect(scrollToTopSpy).not.toHaveBeenCalled();
});
it('should be "collapsed" after clicking again', () => {
page.tocHeadingButtonEmbedded.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
});
it('should not scroll after clicking again', () => {
page.tocHeadingButtonEmbedded.nativeElement.click();
fixture.detectChanges();
expect(scrollToTopSpy).not.toHaveBeenCalled();
it('should not display expando buttons', () => {
expect(page.tocHeadingButtonEmbedded).toBeFalsy('top expand/collapse button');
expect(page.tocMoreButton).toBeFalsy('bottom more button');
});
});
describe('after click tocMore button', () => {
describe('when many TocItems', () => {
let scrollToTopSpy: jasmine.Spy;
beforeEach(() => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
page = setPage();
scrollToTopSpy = TestBed.get(ScrollService).scrollToTop;
});
it('should not be "collapsed"', () => {
expect(tocComponent.isCollapsed).toEqual(false);
it('should have more than 4 displayed items', () => {
expect(page.listItems.length).toBeGreaterThan(4);
});
it('should not have "collapsed" class', () => {
expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy();
it('should not display the h1 item', () => {
expect(page.listItems.find(item => item.classes.h1)).toBeFalsy('should not find h1 item');
});
it('should not scroll', () => {
expect(scrollToTopSpy).not.toHaveBeenCalled();
it('should be in "collapsed" (not expanded) state at the start', () => {
expect(tocComponent.isCollapsed).toBeTruthy();
});
it('should be "collapsed" after clicking again', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
it('should have "collapsed" class at the start', () => {
expect(tocComponentDe.children[0].classes.collapsed).toEqual(true);
});
it('should be "collapsed" after clicking tocHeadingButton', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
it('should display expando buttons', () => {
expect(page.tocHeadingButtonEmbedded).toBeTruthy('top expand/collapse button');
expect(page.tocMoreButton).toBeTruthy('bottom more button');
});
it('should scroll after clicking again', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(scrollToTopSpy).toHaveBeenCalled();
it('should have secondary items', () => {
expect(tocComponent.type).toEqual('EmbeddedExpandable');
});
// CSS will hide items with the secondary class when collapsed
it('should have secondary item with a secondary class', () => {
const aSecondary = page.listItems.find(item => item.classes.secondary);
expect(aSecondary).toBeTruthy('should find a secondary');
});
describe('after click tocHeading button', () => {
beforeEach(() => {
page.tocHeadingButtonEmbedded.nativeElement.click();
fixture.detectChanges();
});
it('should not be "collapsed"', () => {
expect(tocComponent.isCollapsed).toEqual(false);
});
it('should not have "collapsed" class', () => {
expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy();
});
it('should not scroll', () => {
expect(scrollToTopSpy).not.toHaveBeenCalled();
});
it('should be "collapsed" after clicking again', () => {
page.tocHeadingButtonEmbedded.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
});
it('should not scroll after clicking again', () => {
page.tocHeadingButtonEmbedded.nativeElement.click();
fixture.detectChanges();
expect(scrollToTopSpy).not.toHaveBeenCalled();
});
});
describe('after click tocMore button', () => {
beforeEach(() => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
});
it('should not be "collapsed"', () => {
expect(tocComponent.isCollapsed).toEqual(false);
});
it('should not have "collapsed" class', () => {
expect(tocComponentDe.children[0].classes.collapsed).toBeFalsy();
});
it('should not scroll', () => {
expect(scrollToTopSpy).not.toHaveBeenCalled();
});
it('should be "collapsed" after clicking again', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
});
it('should be "collapsed" after clicking tocHeadingButton', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(tocComponent.isCollapsed).toEqual(true);
});
it('should scroll after clicking again', () => {
page.tocMoreButton.nativeElement.click();
fixture.detectChanges();
expect(scrollToTopSpy).toHaveBeenCalled();
});
});
});
});

View File

@ -38,7 +38,7 @@ describe('SearchBoxComponent', () => {
it('should get the current search query from the location service',
fakeAsync(inject([LocationService], (location: MockLocationService) => {
location.search.and.returnValue({ search: 'initial search' });
component.ngOnInit();
component.ngAfterViewInit();
expect(location.search).toHaveBeenCalled();
tick(300);
expect(host.searchHandler).toHaveBeenCalledWith('initial search');

View File

@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild, ElementRef, EventEmitter, Output } from '@angular/core';
import { AfterViewInit, Component, ViewChild, ElementRef, EventEmitter, Output } from '@angular/core';
import { LocationService } from 'app/shared/location.service';
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
@ -24,7 +24,7 @@ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
(focus)="doFocus()"
(click)="doSearch()">`
})
export class SearchBoxComponent implements OnInit {
export class SearchBoxComponent implements AfterViewInit {
private searchDebounce = 300;
private searchSubject = new Subject<string>();
@ -40,7 +40,7 @@ export class SearchBoxComponent implements OnInit {
/**
* When we first show this search box we trigger a search if there is a search query in the URL
*/
ngOnInit() {
ngAfterViewInit() {
const query = this.locationService.search()['search'];
if (query) {
this.query = query;

View File

@ -22,11 +22,11 @@
/api/core/testing/index/TestBed-class.html /api/core/testing/TestBed
/api/core/testing/inject-function /api/core/testing/inject
/api/core/testing/inject-function.html /api/core/testing/inject
/api/http/Headers-class /guide/deprecation#http
/api/http/Headers-class.html /guide/deprecation#http
/api/http/HTTP_PROVIDERS-let /guide/deprecation#http
/api/http/testing/index/MockBackend-class /guide/deprecation#http
/api/http/testing/index/MockBackend-class.html /guide/deprecation#http
/api/http/Headers-class /guide/deprecations#http
/api/http/Headers-class.html /guide/deprecations#http
/api/http/HTTP_PROVIDERS-let /guide/deprecations#http
/api/http/testing/index/MockBackend-class /guide/deprecations#http
/api/http/testing/index/MockBackend-class.html /guide/deprecations#http
/api/platform-browser-dynamic/testing/index/platformBrowserDynamicTesting-let.html /api/platform-browser-dynamic/testing/platformBrowserDynamicTesting
/api/platform-browser/AnimationDriver /api/animations/browser/AnimationDriver
/api/router/Route-class /api/router/Route
@ -95,15 +95,15 @@
/docs/js/latest/api/forms/index/FormBuilder-class.html /api/forms/FormBuilder
/docs/js/latest/api/forms/index/NG_VALIDATORS-let /api/forms/NG_VALIDATORS
/docs/js/latest/api/forms/index/Validator-interface.html /api/forms/Validator
/docs/js/latest/api/http/ConnectionBackend-class /guide/deprecation#http
/docs/js/latest/api/http/index/Http-class.html /guide/deprecation#http
/docs/js/latest/api/http/index/Jsonp-class.html /guide/deprecation#http
/docs/js/latest/api/http/index/ResponseOptions-class.html /guide/deprecation#http
/docs/js/latest/api/http/index/URLSearchParams-class /guide/deprecation#http
/docs/js/latest/api/http/index/XHRConnection-class /guide/deprecation#http
/docs/js/latest/api/http/index/XHRConnection-class.html /guide/deprecation#http
/docs/js/latest/api/http/testing/index/MockConnection-class.html /guide/deprecation#http
/docs/js/latest/api/http/testing/MockBackend-class /guide/deprecation#http
/docs/js/latest/api/http/ConnectionBackend-class /guide/deprecations#http
/docs/js/latest/api/http/index/Http-class.html /guide/deprecations#http
/docs/js/latest/api/http/index/Jsonp-class.html /guide/deprecations#http
/docs/js/latest/api/http/index/ResponseOptions-class.html /guide/deprecations#http
/docs/js/latest/api/http/index/URLSearchParams-class /guide/deprecations#http
/docs/js/latest/api/http/index/XHRConnection-class /guide/deprecations#http
/docs/js/latest/api/http/index/XHRConnection-class.html /guide/deprecations#http
/docs/js/latest/api/http/testing/index/MockConnection-class.html /guide/deprecations#http
/docs/js/latest/api/http/testing/MockBackend-class /guide/deprecations#http
/docs/js/latest/api/platform-browser-dynamic/index/platformBrowserDynamic-let.html /api/platform-browser-dynamic/platformBrowserDynamic
/docs/js/latest/api/platform-browser-dynamic/testing/index/BrowserDynamicTestingModule-class.html /api/platform-browser-dynamic/testing/BrowserDynamicTestingModule
/docs/js/latest/api/platform-browser/animations/index/BrowserAnimationsModule-class /api/platform-browser/animations/BrowserAnimationsModule
@ -165,9 +165,9 @@
/docs/ts/latest/api/core/testing/index/fakeAsync-function.html /api/core/testing/fakeAsync
/docs/ts/latest/api/core/testing/index/TestComponentRenderer-class.html /api/core/testing/TestComponentRenderer
/docs/ts/latest/api/core/testing/index/tick-function.html /api/core/testing/tick
/docs/ts/latest/api/http/Connection-class.html /guide/deprecation#http
/docs/ts/latest/api/http/testing/index/MockBackend-class.html /guide/deprecation#http
/docs/ts/latest/api/http/testing/index/MockConnection-class.html /guide/deprecation#http
/docs/ts/latest/api/http/Connection-class.html /guide/deprecations#http
/docs/ts/latest/api/http/testing/index/MockBackend-class.html /guide/deprecations#http
/docs/ts/latest/api/http/testing/index/MockConnection-class.html /guide/deprecations#http
/docs/ts/latest/api/platform-browser-dynamic/index/workerAppDynamicPlatform-let.html /api/platform-browser-dynamic/workerAppDynamicPlatform
/docs/ts/latest/api/testing/fakeAsync-function.html /api/core/testing/fakeAsync
/docs/ts/latest/cookbook/ts-to-js.html https://v2.angular.io/docs/ts/latest/cookbook/ts-to-js.html

View File

@ -239,7 +239,9 @@ describe('site App', function() {
/* tslint:disable:max-line-length */
expect(page.ghLinks.get(0).getAttribute('href'))
.toMatch(/https:\/\/github\.com\/angular\/angular\/edit\/master\/aio\/content\/guide\/http\.md\?message=docs%3A%20describe%20your%20change\.\.\./);
});
// TODO(gkalpak): This test often times out with Ivy (because loading `guide/http` takes a lot of time).
// Remove the timeout once the performance issues have been fixed.
}, 60000);
it('should not be present on top level pages', () => {
page.navigateTo('features');

View File

@ -73,7 +73,6 @@
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -48,7 +48,6 @@
"no-unused-expression": true,
"no-unused-variable": true,
"no-unreachable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -41,7 +41,6 @@
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -2,24 +2,24 @@
# yarn lockfile v1
"@angular-devkit/architect@0.800.0-beta.18":
version "0.800.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.800.0-beta.18.tgz#fdf23d3854d4ca62b2e201efd2b267f800eba8d8"
integrity sha512-No7RpK98O+S1zSC0omO66yBKqUnM2Vt1l4tXDC43BGSMijL5JN/uSHpMSObUpmmMC1qxCeN5OXRU7QhZW+DxqA==
"@angular-devkit/architect@0.800.0-rc.2":
version "0.800.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.800.0-rc.2.tgz#4096016fa6df93acbf02be648554631939aa87c0"
integrity sha512-JU/x3UvUW+uVuF0tNkVTRtAvGfbKDXLFI3lm7i40qmts5z1zeQlPjrz+DRTe7msevoVu7DMQTJ7vlbyHUjZOFw==
dependencies:
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/core" "8.0.0-rc.2"
rxjs "6.4.0"
"@angular-devkit/build-angular@0.800.0-beta.18":
version "0.800.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.800.0-beta.18.tgz#3ce37929cb9462fc43f532b1186c8a0e3005fcb1"
integrity sha512-bEzgBI8kWpdapKU0xMONHYtzfCDMZVwfwetTK9nRCk2c4CEa0k6I9r6UTgPpqGU1pf2wHgrSd+QdpwbOORNufw==
"@angular-devkit/build-angular@0.800.0-rc.2":
version "0.800.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.800.0-rc.2.tgz#df78a27a58813bc442629f8500e1f3d4fd72c519"
integrity sha512-E6P3CO4IUEyCezrBuNwMZ+H/Rg+0R/FH3/TyWiivY5D3bLGvRFJbQE5ZQkVWbd1i1woxvHdqkjgco9hpOko2XQ==
dependencies:
"@angular-devkit/architect" "0.800.0-beta.18"
"@angular-devkit/build-optimizer" "0.800.0-beta.18"
"@angular-devkit/build-webpack" "0.800.0-beta.18"
"@angular-devkit/core" "8.0.0-beta.18"
"@ngtools/webpack" "8.0.0-beta.18"
"@angular-devkit/architect" "0.800.0-rc.2"
"@angular-devkit/build-optimizer" "0.800.0-rc.2"
"@angular-devkit/build-webpack" "0.800.0-rc.2"
"@angular-devkit/core" "8.0.0-rc.2"
"@ngtools/webpack" "8.0.0-rc.2"
ajv "6.10.0"
autoprefixer "9.5.1"
browserslist "4.5.5"
@ -38,7 +38,7 @@
loader-utils "1.2.3"
mini-css-extract-plugin "0.6.0"
minimatch "3.0.4"
open "6.1.0"
open "6.2.0"
parse5 "4.0.0"
postcss "7.0.14"
postcss-import "12.0.1"
@ -65,30 +65,30 @@
webpack-subresource-integrity "1.1.0-rc.6"
worker-plugin "3.1.0"
"@angular-devkit/build-optimizer@0.800.0-beta.18":
version "0.800.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.800.0-beta.18.tgz#4e8e8ae1c2e14146633217605315b51e3f9eeee6"
integrity sha512-x5oh7GUjYLvrOvh4uNM6aDtNFv7hafo9ru11ee7sgXwHIYdcVmp6ew19Sjj3nAr0Sh1rAUf7QoNZVO/txxnBRA==
"@angular-devkit/build-optimizer@0.800.0-rc.2":
version "0.800.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.800.0-rc.2.tgz#d56cdc127699ad25eb817caf97336d239600f5cb"
integrity sha512-YKTFlAfD4JZk1h4lZDA4HVPPIq1JB5Dxg/Icn2GvcuUws6wVcXUflMTIpBgIMF2j07fMIYPqSAcSBd+UsBJBvA==
dependencies:
loader-utils "1.2.3"
source-map "0.5.6"
typescript "3.4.4"
webpack-sources "1.3.0"
"@angular-devkit/build-webpack@0.800.0-beta.18":
version "0.800.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.800.0-beta.18.tgz#f343d759bdd09a907c77fc6a0515416c30291e88"
integrity sha512-pSPyW1D7yEZXxCRb42qXmwEO8vHLeDgbRUXiTtMh0Yf86Zb5Ku7xH+pK7suOhJXEZKv2UWBfQXWWP9SrBX/4rg==
"@angular-devkit/build-webpack@0.800.0-rc.2":
version "0.800.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.800.0-rc.2.tgz#c08218f54381157744a3166c0d94b1db180ba063"
integrity sha512-t+/5WqgcsvgPIFGE48I+UWJPDpkQ6E+dKH6RuXec94VBJEv1pC9FJdoi6s/CqEzzPiZsWxJzrWI4dpPAn1eWuA==
dependencies:
"@angular-devkit/architect" "0.800.0-beta.18"
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/architect" "0.800.0-rc.2"
"@angular-devkit/core" "8.0.0-rc.2"
rxjs "6.4.0"
webpack-merge "4.2.1"
"@angular-devkit/core@8.0.0-beta.18":
version "8.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.0.0-beta.18.tgz#dfaa0786af0a7466467b8a9cd1fe745418690df0"
integrity sha512-+kQd0m6HgGTn7JM9GWzrJgjI/PPOa0K+t+a6YZS4n/MdZSzhId556Df5/UnrgsBghSDjyVwu7+owijvNwQlj2w==
"@angular-devkit/core@8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.0.0-rc.2.tgz#e596a44da215713cdef5405f1477e70626e5d68c"
integrity sha512-hfkQ1QaA0ZIquTNQYJiK0OhdSzdxWY1SJr20JwSBHezAvhN4sJHRBRN9RxGLWdL1d4Z4rUB4KEIvx0cMMk6Ueg==
dependencies:
ajv "6.10.0"
fast-json-stable-stringify "2.0.0"
@ -96,63 +96,63 @@
rxjs "6.4.0"
source-map "0.7.3"
"@angular-devkit/schematics@8.0.0-beta.18":
version "8.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.0.0-beta.18.tgz#97dece6eaddd331516a94da985f33add21b76f5c"
integrity sha512-F2pkiNe/rMOdcnKm/4s/lvM/8guwtrj5nEk0klyesDZLaZt94XEtasq0XrBxHju+7PmM6IwaDA6o5qsQ/6IAKg==
"@angular-devkit/schematics@8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.0.0-rc.2.tgz#f4490b1277cdba9622227b178128c76f54e7eca1"
integrity sha512-VuXCRE/PmQWMHaaBbbOq7f2M6/DeKnYUyqG2xpBJaSP+rX7j08gd6RGzkn6V81C6jxt0Q3WkXnQfj5ZQuFnF5w==
dependencies:
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/core" "8.0.0-rc.2"
rxjs "6.4.0"
"@angular/animations@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-8.0.0-beta.14.tgz#50259f6f3c97101183216e8311b8c0875c4356c3"
integrity sha512-ihYHQXEbjzemjJxJV4p2XAWOD9H4r8HpfNekxxelgj2RyZhrMQStzL+PEKxHHlGn167HuZz5YEoW4MXGflTtsA==
"@angular/animations@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-8.0.0-rc.2.tgz#aef2683aba35498fe26253bf8f461a1658864e49"
integrity sha512-XTT7Eif6Km6MSbLr6qlCmx8vjRgx/Hp5Hv1WnnPtRFt8XeidYGGYIq2si3CxQ2xBjEO3OpNbPqtYvZKH1yuBdA==
dependencies:
tslib "^1.9.0"
"@angular/cdk@8.0.0-beta.2":
version "8.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-8.0.0-beta.2.tgz#27544be4a99b0d56bdbf9f206b2f8e290de82f61"
integrity sha512-/ze014/AGp9nAI6kK7w6TQnz533PRvjJOpEULaBclNRHSNGZElncOGPfnD6V/Zr/CKY5zFbwLHQEqVUP4ObTwg==
"@angular/cdk@8.0.0-rc.0":
version "8.0.0-rc.0"
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-8.0.0-rc.0.tgz#64731574ddcf6912b079e03efab95464ce17acee"
integrity sha512-fa0AFR/v4t4rVFUwqpfRnzRhzyoReYcssjR6fQ4WMMxbhRHNZSJaPxvnykMOZsh7oQy0d6Dy4kENO6nXZptR9g==
dependencies:
tslib "^1.7.1"
optionalDependencies:
parse5 "^5.0.0"
"@angular/cli@8.0.0-beta.18":
version "8.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-8.0.0-beta.18.tgz#454a3d230c280fea7ec02c97eae8bd56a9a17dd9"
integrity sha512-uNuTCN75w9m2uLq3Vk2e+FfiSa19AHeGOrrwtLdPJLr5GU1Jum2wl9Mn9W4eyiSSiZNKN+IWWa6wkLV0/SybQw==
"@angular/cli@8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-8.0.0-rc.2.tgz#fda3c42c3682a53168be67c0624db04144c0aa9c"
integrity sha512-Aj5JJKa6JxNU3U9InvveIhU8gg6ZA1UvvKHHzX87FsUQhHzm7XIfwC5JrYGDE4c9ErIE40J3we7Hcsv2AjzkTw==
dependencies:
"@angular-devkit/architect" "0.800.0-beta.18"
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/schematics" "8.0.0-beta.18"
"@schematics/angular" "8.0.0-beta.18"
"@schematics/update" "0.800.0-beta.18"
"@angular-devkit/architect" "0.800.0-rc.2"
"@angular-devkit/core" "8.0.0-rc.2"
"@angular-devkit/schematics" "8.0.0-rc.2"
"@schematics/angular" "8.0.0-rc.2"
"@schematics/update" "0.800.0-rc.2"
"@yarnpkg/lockfile" "1.1.0"
debug "^4.1.1"
ini "1.3.5"
inquirer "6.3.1"
npm-package-arg "6.1.0"
open "6.1.0"
open "6.2.0"
pacote "9.5.0"
semver "6.0.0"
symbol-observable "1.2.0"
universal-analytics "^0.4.20"
uuid "^3.3.2"
"@angular/common@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-8.0.0-beta.14.tgz#a27efe0b3e38e38f0e481a2a3f8cad026823b39d"
integrity sha512-4mBGXb+VyakX+YqAAfDsYrlKmjDcyD0BjmWi1u7aTJgf5QmEvFD4QLXzHoWEsmBFnTgoFHXMtXogF04GXy3kuA==
"@angular/common@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/common/-/common-8.0.0-rc.2.tgz#77b2da22cb11bf2039799d56ec2fb7c4f51a1374"
integrity sha512-/MUZhebMAkAwV8jkimBONN2eOQ2/5qO54ZvPypyy2tEHk69JRNLFjREaR+VU8yGlS/1MU6jTYxyfRW2c8ggLzA==
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-8.0.0-beta.14.tgz#61aa5947d3427afd0043fdba079a8cc616d29201"
integrity sha512-Q+B9bUDqVOT3OZdUsOFJeQWSmVKUdUoaU5ptq6UQFALCa/0skQrcL/KOFpej3fk3ZRVTkQ+vWQ1nMzv+tm8Byw==
"@angular/compiler-cli@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-8.0.0-rc.2.tgz#a3ef74f3e4ea448ccf1387c18c32fc664b67b2e1"
integrity sha512-lLhedaTQ08/i4aysCPpTYbAP0/8T5/EHE79FByp/7XxqRuQxcXkFJujQT1Q/KReKa0gA9JEYD4I1eBJpNYyMLQ==
dependencies:
canonical-path "1.0.0"
chokidar "^2.1.1"
@ -164,73 +164,73 @@
shelljs "^0.8.1"
source-map "^0.6.1"
tslib "^1.9.0"
yargs "9.0.1"
yargs "13.1.0"
"@angular/compiler@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-8.0.0-beta.14.tgz#dc01066a060ea9fbd9696a015f481279aa23c2b0"
integrity sha512-Copy2bMf8EL9WcHwz1rHeMY6CqWhOZmvGRBSVwI149awbTxc31h3+01TG2I5FlgIm+vc70PUldp7EumqFiCCDA==
"@angular/compiler@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-8.0.0-rc.2.tgz#8395c20d2d7e93c6941a45b039e03ae7a6277797"
integrity sha512-/NR0EN6wjit2mfgOhWirOjKp+s7lO1QSKhzK3TjYCda1L7Pijo29s3BGMWUSPCRno8zNH5o7Q0mWCC55MBGB4w==
dependencies:
tslib "^1.9.0"
"@angular/core@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-8.0.0-beta.14.tgz#d3e75291ac390a1a29bf037bfd292586a90aea8b"
integrity sha512-7gltt13BTUFgGHpOBUmvxr/PUcqY8n6177NpYgEJRBFLZl2FtErG+7BfYBCt23StAi+K6ZafP+IX2m1evOtX8g==
"@angular/core@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/core/-/core-8.0.0-rc.2.tgz#800fd49bbc176f7ee064375b726503c5e6c8387c"
integrity sha512-ImXzoQk8U3IiL75dJWnO/aIGfaExgiqDc6GJYgQ8PvB7ZUkdUOj/a+jd6asMvE52VZJkof5XShhdonTScPcdUA==
dependencies:
tslib "^1.9.0"
"@angular/elements@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-8.0.0-beta.14.tgz#951a9c79c475a26a86bc83c7e21769863b6611f0"
integrity sha512-oY6hv7cN0zOElABrO7lEiz+/xVPF5+97r/RA/x2vIVWyei7J7VIWQxAvHX+osrndIwAgXtBlyx0U0EXfp+BshA==
"@angular/elements@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-8.0.0-rc.2.tgz#84ca2aadff66bfcd3c8042492a0c6b87a6a749fa"
integrity sha512-7KVPDSGXo33WD+ZQHjmvSXyBa6az6MHIQo3I+wRKUB9pBZNqMa2Dcb7FQp7fq3tQTEpOW6fO52ozxAZ/ybozxA==
dependencies:
tslib "^1.9.0"
"@angular/forms@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-8.0.0-beta.14.tgz#db0958d9f1bd8d22aef1605c66704d9247837f31"
integrity sha512-40GN2X+ycaUyq1/VX8jQcQqnuDojp2A7OBtfGguTsz2viKlIFY89GvErqPzGcbUwoKE2e00pNDEK2NWwLqLmpg==
"@angular/forms@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-8.0.0-rc.2.tgz#f4db4653470b74cd55508aa83da761309a1d341d"
integrity sha512-rjjGNVaxwOayuZCpsG8V13RK/h7S6v5ItzG6Ai1agO/em4/riNhEP3+BvxM4Rs1v4AfGOd+LhOeLt5qptj0JKA==
dependencies:
tslib "^1.9.0"
"@angular/language-service@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-8.0.0-beta.14.tgz#cf293951d518889cdaedc433306d0699d078a364"
integrity sha512-FNPYEDEUlgF7dejL6spCtLs/7+nMq7mAAMvtI1hPDldAhLMkfygrGlxtDrREG+6ekYDBJVpHy7Sik/e9Hn/Szw==
"@angular/language-service@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-8.0.0-rc.2.tgz#5b8fd13b7cd9e7cc1c097f92749f05e72b1c7e47"
integrity sha512-PXsntGd053iiM9lJPo7jmjhd5dp6AxugEC4WDTXimZ3vK2Hzv53CnKuVTUGcTWjwazbKAEyG0jiUl4VXptwTAA==
"@angular/material@8.0.0-beta.2":
version "8.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@angular/material/-/material-8.0.0-beta.2.tgz#91abce0ef64b655ecec4ff3895ab02c485906071"
integrity sha512-3816IV/IVg1juj6jlEbXmWepl97JsLwVO0aHRGpTg8FAdilK5w36eUiXCqKecvDnNAHpwDFavAwrAlToxUQBvw==
"@angular/material@8.0.0-rc.0":
version "8.0.0-rc.0"
resolved "https://registry.yarnpkg.com/@angular/material/-/material-8.0.0-rc.0.tgz#c5170f9617abba3176e991a1e197e1efa0bff95a"
integrity sha512-8J+oPHxcoDJnZUE+j5eYDR5FS/zN7RHljK2FtjiG2wsGGRiomnMLQBoFCQL55P1sgqWz3l4vKgi5GuwA65pADQ==
dependencies:
tslib "^1.7.1"
"@angular/platform-browser-dynamic@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.0.0-beta.14.tgz#ef2463033f7383500aaa55627fbd487c5c377033"
integrity sha512-NWcOey/bzstnbVzNvBpryEAamWNAQobUkP4i3mSnKvWAyhs1GO7IPOsTwzOOPVvQBTOq0GFdYJjDoB4DIo3/ZA==
"@angular/platform-browser-dynamic@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.0.0-rc.2.tgz#86e8e227f19aa9c6ef4439623ce6cf7f2b0cd3b1"
integrity sha512-c4QXgAikQx25AOnNiQUOe/yNJunR95rfTJ5yApcNU76eeCMG9KLFNGGQTg+PmcxZ6UMcw9zp2Tck9vjSb2BZYg==
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-8.0.0-beta.14.tgz#9023453d850243fbc7c6452c26e100fd1d505617"
integrity sha512-nOo8wZU1PToNgb1BdKVWVmJqX4l30YWOzMCJ1S41LMImf0k+PXgKYUCQ5OzFBJNTH0x8JvANTvsBiqJyNN+QkQ==
"@angular/platform-browser@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-8.0.0-rc.2.tgz#b40fa05fd32422be499ab3986bc3031374f7c5bc"
integrity sha512-ErzIDckIqic9rmqMFk4scRX+lo7AWifOP+IOCot1KGPdQ7+CP/h/neJ9fiIzYTSDYUm5xk5i+2aW+2SWRJYQdA==
dependencies:
tslib "^1.9.0"
"@angular/router@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-8.0.0-beta.14.tgz#62e4be7b085cce764e00c608eb910886767cd61e"
integrity sha512-QGIvWPd4ngxWzEwzAqEk7M54CGmmYGRxs/+V8Zohau580XL5lK6tuPvN+slO1dWe3+YuFt6U02zzs9mRGlLnYw==
"@angular/router@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/router/-/router-8.0.0-rc.2.tgz#e0e4636f56fc08760863486f45f203bb9b00543e"
integrity sha512-7NnRdEykm0FJf/SHjUWz3cgGDtW5KM2gKm8it8V8azr3vIiMRYyqhDqCKkhutjt7C+yKNotB5M7Da9uYf0n/xQ==
dependencies:
tslib "^1.9.0"
"@angular/service-worker@^8.0.0-beta.14":
version "8.0.0-beta.14"
resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-8.0.0-beta.14.tgz#7fdac405d2f6757cbaeb2abdb35b513f69e0bdf2"
integrity sha512-dQGdpjj97bVp0AvLHI4wpbQCEeWSoPssnKf24oXaJHLM0UuWQgumlBzKU4/gWBpQ+lQoW+txELp4qoY4YH/Yig==
"@angular/service-worker@^8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-8.0.0-rc.2.tgz#e73374fe4ccd8c15790f007832a5e60576ed85d9"
integrity sha512-uudl7+P9Xov9naoW7Ruv0rvvp+jYWe3P3OOpRul8X6tTYarKnorj3F44Y5GoNO5Xxjp0psPSD558rXrrIBt98w==
dependencies:
tslib "^1.9.0"
@ -316,40 +316,32 @@
through2 "^2.0.0"
xdg-basedir "^3.0.0"
"@ngtools/webpack@8.0.0-beta.18":
version "8.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-8.0.0-beta.18.tgz#64ebc3823aad7cf80e1afe0e91e06bc281a4448c"
integrity sha512-9c7ovJqVq6EpxQx4WYiR1OI6AhG3/RC+dyzvNE3uKEL7WGT6fG/4dKCxr7CA/wYjuogz5Q2Z5n2dLunOTTt5PQ==
"@ngtools/webpack@8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-8.0.0-rc.2.tgz#d8321a89b2c14fcb627d3d5e326b4125b283f08c"
integrity sha512-0pXtkvbwp53z+BgQwBllyIHjMM082phkM8hFwlEHCbYeWkSRBqDld7HgzXBEwpBe+4MKjtWF2xXbDp/4BdTjOQ==
dependencies:
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/core" "8.0.0-rc.2"
enhanced-resolve "4.1.0"
rxjs "6.4.0"
tree-kill "1.2.1"
webpack-sources "1.3.0"
"@phenomnomnominal/tsquery@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-3.0.0.tgz#6f2f4dbf6304ff52b12cc7a5b979f20c3794a22a"
integrity sha512-SW8lKitBHWJ9fAYkJ9kJivuctwNYCh3BUxLdH0+XiR1GPBiu+7qiZzh8p8jqlj1LgVC1TbvfNFroaEsmYlL8Iw==
"@schematics/angular@8.0.0-rc.2":
version "8.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-8.0.0-rc.2.tgz#94bc9bb68d75e07142a37f4881c287063d460df2"
integrity sha512-sBIUz2xEBZJxXAiIsJEaTI7G8r1Mc0aI0tNnw0vQLF6sMSaVKJssN2gYg5dmceDXohJtcgdc3hN1xPL6ZpvsdA==
dependencies:
esquery "^1.0.1"
"@angular-devkit/core" "8.0.0-rc.2"
"@angular-devkit/schematics" "8.0.0-rc.2"
"@schematics/angular@8.0.0-beta.18":
version "8.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-8.0.0-beta.18.tgz#5ad1c76d0279a6b0d286a52b27e4e61b62f348c3"
integrity sha512-gmSlDX9ywU3Ce40jl71FpoJDGyYcH3olPnRQGUn8auF7Z/+XVuG2+9EkTrnGwOpuxlmIMLkrlSJjL/1bnJbtpw==
"@schematics/update@0.800.0-rc.2":
version "0.800.0-rc.2"
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.800.0-rc.2.tgz#4ea24343c32f47ee7c2f2404fb67a50331dc8b1a"
integrity sha512-oWnxEoscVHfzz0mEvfqwe7hRw4fzsYOmnZEVVLAb62lZQdPQHitflBITI+ubrPmHSgILHoBAWqOYlkuEKVXVAg==
dependencies:
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/schematics" "8.0.0-beta.18"
"@phenomnomnominal/tsquery" "3.0.0"
"@schematics/update@0.800.0-beta.18":
version "0.800.0-beta.18"
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.800.0-beta.18.tgz#03becf819814b5498ff94a21330a95d0529c3f83"
integrity sha512-U8yUgRapI64ABHbBQ0KIRKMAUd935n2bKcscechoocB9L/64uRgaBqBsNFXsTuepgGmPxPz0ZKq12h4ny1m+nA==
dependencies:
"@angular-devkit/core" "8.0.0-beta.18"
"@angular-devkit/schematics" "8.0.0-beta.18"
"@angular-devkit/core" "8.0.0-rc.2"
"@angular-devkit/schematics" "8.0.0-rc.2"
"@yarnpkg/lockfile" "1.1.0"
ini "1.3.5"
pacote "9.5.0"
@ -3143,6 +3135,11 @@ elliptic@^6.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e"
integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@ -3447,13 +3444,6 @@ esquery@^1.0.0:
dependencies:
estraverse "^4.0.0"
esquery@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
dependencies:
estraverse "^4.0.0"
esrecurse@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
@ -4173,6 +4163,11 @@ get-caller-file@^1.0.1:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-stream@3.0.0, get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@ -7340,10 +7335,10 @@ onetime@^2.0.0:
dependencies:
mimic-fn "^1.0.0"
open@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/open/-/open-6.1.0.tgz#0e7e671b883976a4e5251b5d1ca905ab6f4be78f"
integrity sha512-Vqch7NFb/WsMujhqfq+B3u0xkssRjZlxh+NSsBSphpcgaFD7gfB0SUBfR91E9ygBlyNGNogXR2cUB8rRfoo2kQ==
open@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/open/-/open-6.2.0.tgz#7cf92cb961b5d8498b071e64098bf5e27f57230c"
integrity sha512-Vxf6HJkwrqmvh9UAID3MnMYXntbTxKLOSfOnO7LJdzPf3NE3KQYFNV0/Lcz2VAndbRFil58XVCyh8tiX11fiYw==
dependencies:
is-wsl "^1.1.0"
@ -7436,7 +7431,7 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
os-locale@^3.0.0:
os-locale@^3.0.0, os-locale@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
@ -8678,6 +8673,11 @@ require-main-filename@^1.0.1:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-uncached@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
@ -9654,6 +9654,15 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
dependencies:
emoji-regex "^7.0.1"
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string.prototype.padend@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0"
@ -11197,6 +11206,14 @@ yargs-parser@^11.1.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b"
integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
@ -11245,6 +11262,23 @@ yargs@12.0.5:
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^11.1.1"
yargs@13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.1.0.tgz#b2729ce4bfc0c584939719514099d8a916ad2301"
integrity sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==
dependencies:
cliui "^4.0.0"
find-up "^3.0.0"
get-caller-file "^2.0.1"
os-locale "^3.1.0"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^3.0.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^13.0.0"
yargs@3.32.0, yargs@^3.32.0:
version "3.32.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"
@ -11257,25 +11291,6 @@ yargs@3.32.0, yargs@^3.32.0:
window-size "^0.1.4"
y18n "^3.2.0"
yargs@9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c"
integrity sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=
dependencies:
camelcase "^4.1.0"
cliui "^3.2.0"
decamelize "^1.1.1"
get-caller-file "^1.0.1"
os-locale "^2.0.0"
read-pkg-up "^2.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
y18n "^3.2.1"
yargs-parser "^7.0.0"
yargs@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"

View File

@ -3,7 +3,7 @@
"master": {
"uncompressed": {
"runtime": 1497,
"main": 167065,
"main": 164945,
"polyfills": 43626
}
}
@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 30932,
"main": 14487,
"polyfills": 43567
}
}
@ -21,7 +21,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 157393,
"main": 149205,
"polyfills": 43567
}
}

View File

@ -9,7 +9,7 @@ function installLocalPackages() {
readonly pwd=$(pwd)
readonly packages=(
animations common compiler core forms platform-browser
platform-browser-dynamic router bazel compiler-cli language-service upgrade
platform-browser-dynamic router bazel compiler-cli language-service
)
local local_packages=()
for package in "${packages[@]}"; do

View File

@ -23,6 +23,7 @@
"node_modules/@angular/core/schematics/**",
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**",
"node_modules/@angular/common/upgrade*",
"node_modules/@angular/router/upgrade*"
]
}

View File

@ -10,7 +10,6 @@
"@angular/platform-browser": "packages-dist:platform-browser",
"@angular/platform-browser-dynamic": "packages-dist:platform-browser-dynamic",
"@angular/router": "packages-dist:router",
"@angular/upgrade": "packages-dist:upgrade",
"reflect-metadata": "0.1.12",
"rxjs": "6.4.0",
"tslib": "1.9.3",
@ -31,4 +30,4 @@
"postinstall": "ngc -p ./angular-metadata.tsconfig.json",
"//": "TODO(gregmagolan): figure out how to keep dependencies here up to date with the root package.json"
}
}
}

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@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",

View File

@ -73,7 +73,6 @@
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@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",

View File

@ -73,7 +73,6 @@
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -28,7 +28,7 @@
"zone.js": "file:../../node_modules/zone.js"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.800.0-beta.11",
"@angular-devkit/build-angular": "0.800.0-beta.15",
"@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",

View File

@ -73,7 +73,6 @@
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@ -63,7 +63,7 @@ if [[ $? != 0 ]]; then exit 1; fi
# Can it be safely run again (as a noop)?
# And check that it logged skipping compilation as expected
ivy-ngcc | grep 'Skipping'
ivy-ngcc -l debug | grep 'Skipping'
if [[ $? != 0 ]]; then exit 1; fi
# Check that running it with logging level error outputs nothing

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "8.0.0-rc.0",
"version": "8.0.0-rc.3",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -107,7 +107,7 @@
"tslint": "5.7.0",
"typescript": "~3.4.2",
"xhr2": "0.1.4",
"yargs": "9.0.1",
"yargs": "13.1.0",
"zone.js": "^0.9.0"
},
"optionalDependencies": {

View File

@ -34,11 +34,11 @@
"@types/node": "6.0.84",
"semver": "^5.6.0",
"shelljs": "0.8.2",
"tsickle": "0.34.3"
"tsickle": "^0.35.0"
},
"peerDependencies": {
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
"@bazel/typescript": "0.27.12",
"@bazel/typescript": "0.*",
"typescript": ">=3.4 <3.5"
},
"repository": {

View File

@ -202,7 +202,7 @@ export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost,
throw new Error(`Couldn't find bazel bin in the rootDirs: ${compilerOpts.rootDirs}`);
}
const writtenExpectedOuts = [...expectedOuts];
const writtenExpectedOuts = expectedOuts.map(p => p.replace(/\\/g, '/'));
const originalWriteFile = tsHost.writeFile.bind(tsHost);
tsHost.writeFile =

View File

@ -20,6 +20,7 @@
"node_modules/@angular/core/schematics/**",
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**",
"node_modules/@angular/common/upgrade*",
"node_modules/@angular/router/upgrade*"
]
}

View File

@ -49,7 +49,7 @@ function addDevDependenciesToPackageJson(options: Schema) {
const devDependencies: {[k: string]: string} = {
'@angular/bazel': angularCoreVersion,
'@bazel/bazel': '^0.24.0',
'@bazel/ibazel': '^0.9.0',
'@bazel/ibazel': '^0.10.1',
'@bazel/karma': '0.27.12',
'@bazel/typescript': '0.27.12',
};

View File

@ -8,7 +8,6 @@
import {runOneBuild} from '@angular/bazel';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as ts from 'typescript';
@ -47,9 +46,7 @@ export function setup(
const bazelBinPath = path.resolve(basePath, bazelBin);
fs.mkdirSync(bazelBinPath);
const angularCorePath = path.resolve(runfilesPath, 'angular', 'packages', 'core');
const ngFiles = listFilesRecursive(angularCorePath);
const angularCorePath = path.dirname(require.resolve('angular/packages/core'));
const tsConfigJsonPath = path.resolve(basePath, tsconfig);
return {
@ -107,7 +104,10 @@ export function setup(
const files = [...compilationTargetSrc];
depPaths = depPaths.concat([angularCorePath]);
pathMapping = pathMapping.concat([{moduleName: '@angular/core', path: angularCorePath}]);
pathMapping = pathMapping.concat([
{moduleName: '@angular/core', path: angularCorePath},
{moduleName: 'angular/packages/core', path: angularCorePath}
]);
for (const depPath of depPaths) {
files.push(...listFilesRecursive(depPath).filter(f => f.endsWith('.d.ts')));
@ -116,14 +116,12 @@ export function setup(
const pathMappingObj = {};
for (const mapping of pathMapping) {
pathMappingObj[mapping.moduleName] = [mapping.path];
pathMappingObj[path.join(mapping.moduleName, '*')] = [path.join(mapping.path, '*')];
pathMappingObj[path.posix.join(mapping.moduleName, '*')] =
[path.posix.join(mapping.path, '*')];
}
const emptyTsConfig = ts.readConfigFile(
path.resolve(
runfilesPath, 'angular', 'packages', 'bazel', 'test', 'ngc-wrapped', 'empty',
'empty_tsconfig.json'),
read);
require.resolve('angular/packages/bazel/test/ngc-wrapped/empty/empty_tsconfig.json'), read);
const tsconfig = createTsConfig({
defaultTsConfig: emptyTsConfig.config,

View File

@ -7,7 +7,6 @@
*/
import * as path from 'path';
import * as ts from 'typescript';
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
@ -32,7 +31,6 @@ export function createTsConfig(options: TsConfigOptions) {
const result = options.defaultTsConfig;
return {
'extends': '../angular/packages/bazel/test/ngc-wrapped/empty/tsconfig',
'compilerOptions': {
...result.compilerOptions,
'outDir': options.outDir,
@ -71,7 +69,8 @@ export function createTsConfig(options: TsConfigOptions) {
'tsickleExternsPath': '',
// we don't copy the node_modules into our tmp dir, so we should look in
// the original workspace directory for it
'nodeModulesPrefix': '../npm/node_modules',
'nodeModulesPrefix':
path.join(require.resolve('npm/node_modules/typescript/package.json'), '../../'),
},
'files': options.files,
'angularCompilerOptions': {

View File

@ -125,7 +125,7 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* {{ dateObj | date }} // output is 'Jun 15, 2015'
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
* {{ dateObj | date:'mmss' }} // output is '43:11'
* {{ dateObj | date:'mm:ss' }} // output is '43:11'
* ```
*
* ### Usage example

View File

@ -98,13 +98,8 @@ function symlinkNodeModules() {
Object.keys(requiredNodeModules).forEach(importName => {
const outputPath = path.join(tmpDir, 'node_modules', importName);
const moduleDir = requiredNodeModules[importName];
findFilesWithinDirectory(moduleDir).forEach(filePath => {
const outputFilePath = path.join(outputPath, path.relative(moduleDir, filePath));
shx.mkdir('-p', path.dirname(outputFilePath));
fs.symlinkSync(filePath, outputFilePath);
});
shx.mkdir('-p', path.dirname(outputPath));
fs.symlinkSync(moduleDir, outputPath, 'junction');
});
}
@ -162,5 +157,5 @@ function resolveNpmTreeArtifact(manifestPath, resolveFile = 'package.json') {
/** Finds all files within a specified directory. */
function findFilesWithinDirectory(directoryPath) {
return shx.find(directoryPath).filter(filePath => !fs.statSync(filePath).isDirectory());
return shx.find(directoryPath).filter(filePath => !fs.lstatSync(filePath).isDirectory());
}

View File

@ -33,7 +33,8 @@ export interface NgccOptions {
/** The absolute path to the `node_modules` folder that contains the packages to process. */
basePath: string;
/**
* The path, relative to `basePath` to the primary package to be processed.
* The path to the primary package to be processed. If not absolute then it must be relative to
* `basePath`.
*
* All its dependencies will need to be processed too.
*/
@ -86,7 +87,7 @@ export function mainNgcc({basePath, targetEntryPointPath,
if (absoluteTargetEntryPointPath &&
hasProcessedTargetEntryPoint(
absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
logger.info('The target entry-point has already been processed');
logger.debug('The target entry-point has already been processed');
return;
}
@ -130,7 +131,7 @@ export function mainNgcc({basePath, targetEntryPointPath,
if (hasBeenProcessed(entryPointPackageJson, property)) {
compiledFormats.add(formatPath);
logger.info(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
logger.debug(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
continue;
}
@ -152,7 +153,7 @@ export function mainNgcc({basePath, targetEntryPointPath,
`Skipping ${entryPoint.name} : ${format} (no valid entry point file for this format).`);
}
} else if (!compileAllFormats) {
logger.info(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
logger.debug(`Skipping ${entryPoint.name} : ${property} (already compiled).`);
}
// Either this format was just compiled or its underlying format was compiled because of a

View File

@ -99,7 +99,7 @@ describe('ngcc main()', () => {
basePath: '/node_modules',
targetEntryPointPath: '@angular/common/http/testing', logger,
});
expect(logger.logs.info).toContain(['The target entry-point has already been processed']);
expect(logger.logs.debug).toContain(['The target entry-point has already been processed']);
});
it('should process the target if any `propertyToConsider` is not marked as processed', () => {
@ -110,7 +110,7 @@ describe('ngcc main()', () => {
targetEntryPointPath: '@angular/common/http/testing',
propertiesToConsider: ['fesm2015', 'esm5', 'esm2015'], logger,
});
expect(logger.logs.info).not.toContain([
expect(logger.logs.debug).not.toContain([
'The target entry-point has already been processed'
]);
});
@ -128,7 +128,7 @@ describe('ngcc main()', () => {
compileAllFormats: false, logger,
});
expect(logger.logs.info).not.toContain([
expect(logger.logs.debug).not.toContain([
'The target entry-point has already been processed'
]);
});
@ -145,7 +145,7 @@ describe('ngcc main()', () => {
compileAllFormats: false, logger,
});
expect(logger.logs.info).toContain([
expect(logger.logs.debug).toContain([
'The target entry-point has already been processed'
]);
});

View File

@ -20,7 +20,7 @@
"shelljs": "^0.8.1",
"source-map": "^0.6.1",
"tslib": "^1.9.0",
"yargs": "9.0.1"
"yargs": "13.1.0"
},
"peerDependencies": {
"@angular/compiler": "0.0.0-PLACEHOLDER",

View File

@ -470,7 +470,7 @@ describe('compiler compliance: bindings', () => {
if (rf & 2) {
i0.ɵɵselect(0);
i0.ɵɵpropertyInterpolateV("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j");
i0.ɵɵpropertyInterpolateV("title", ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
i0.ɵɵselect(1);
i0.ɵɵpropertyInterpolate8("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
i0.ɵɵselect(2);

View File

@ -122,20 +122,21 @@ export function setupBazelTo(tmpDirPath: string) {
fs.mkdirSync(nodeModulesPath);
fs.mkdirSync(angularDirectory);
getAngularPackagesFromRunfiles().forEach(
({pkgPath, name}) => { fs.symlinkSync(pkgPath, path.join(angularDirectory, name), 'dir'); });
getAngularPackagesFromRunfiles().forEach(({pkgPath, name}) => {
fs.symlinkSync(pkgPath, path.join(angularDirectory, name), 'junction');
});
// Link typescript
const typeScriptSource = resolveNpmTreeArtifact('npm/node_modules/typescript');
const typescriptDest = path.join(nodeModulesPath, 'typescript');
fs.symlinkSync(typeScriptSource, typescriptDest, 'dir');
fs.symlinkSync(typeScriptSource, typescriptDest, 'junction');
// Link "rxjs" if it has been set up as a runfile. "rxjs" is linked optionally because
// not all compiler-cli tests need "rxjs" set up.
try {
const rxjsSource = resolveNpmTreeArtifact('rxjs', 'index.js');
const rxjsDest = path.join(nodeModulesPath, 'rxjs');
fs.symlinkSync(rxjsSource, rxjsDest, 'dir');
fs.symlinkSync(rxjsSource, rxjsDest, 'junction');
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') throw e;
}

View File

@ -155,16 +155,12 @@ export function convertPropertyBinding(
localResolver = new DefaultLocalResolver();
}
const currValExpr = createCurrValueExpr(bindingId);
const stmts: o.Statement[] = [];
const visitor =
new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
const outputExpr: o.Expression = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
const stmts: o.Statement[] = getStatementsFromVisitor(visitor, bindingId);
if (visitor.temporaryCount) {
for (let i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
} else if (form == BindingForm.TrySimple) {
if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
return new ConvertPropertyBindingResult([], outputExpr);
}
@ -172,6 +168,58 @@ export function convertPropertyBinding(
return new ConvertPropertyBindingResult(stmts, currValExpr);
}
/**
* Given some expression, such as a binding or interpolation expression, and a context expression to
* look values up on, visit each facet of the given expression resolving values from the context
* expression such that a list of arguments can be derived from the found values that can be used as
* arguments to an external update instruction.
*
* @param localResolver The resolver to use to look up expressions by name appropriately
* @param contextVariableExpression The expression representing the context variable used to create
* the final argument expressions
* @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
* be resolved and what arguments list to build.
* @param bindingId A name prefix used to create temporary variable names if they're needed for the
* arguments generated
* @returns An array of expressions that can be passed as arguments to instruction expressions like
* `o.importExpr(R3.propertyInterpolate).callFn(result)`
*/
export function convertUpdateArguments(
localResolver: LocalResolver, contextVariableExpression: o.Expression,
expressionWithArgumentsToExtract: cdAst.AST, bindingId: string) {
const visitor =
new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
const outputExpr: o.InvokeFunctionExpr =
expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
const stmts = getStatementsFromVisitor(visitor, bindingId);
// Removing the first argument, because it was a length for ViewEngine, not Ivy.
let args = outputExpr.args.slice(1);
if (expressionWithArgumentsToExtract instanceof cdAst.Interpolation) {
// If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
// args returned to just the value, because we're going to pass it to a special instruction.
const strings = expressionWithArgumentsToExtract.strings;
if (args.length === 3 && strings[0] === '' && strings[1] === '') {
// Single argument interpolate instructions.
args = [args[1]];
} else if (args.length >= 19) {
// 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
// an array of arguments
args = [o.literalArr(args)];
}
}
return {stmts, args};
}
function getStatementsFromVisitor(visitor: _AstToIrVisitor, bindingId: string) {
const stmts: o.Statement[] = [];
for (let i = 0; i < visitor.temporaryCount; i++) {
stmts.push(temporaryDeclaration(bindingId, i));
}
return stmts;
}
function convertBuiltins(converterFactory: BuiltinConverterFactory, ast: cdAst.AST): cdAst.AST {
const visitor = new _BuiltinAstConverter(converterFactory);
return ast.visit(visitor);

View File

@ -92,7 +92,7 @@ export class Identifiers {
name: 'ɵinlineInterpolate',
moduleName: CORE,
};
static interpolate: o.ExternalReference = {name: ɵinterpolate', moduleName: CORE};
static interpolate: o.ExternalReference = {name: 'ɵinterpolate', moduleName: CORE};
static EMPTY_ARRAY: o.ExternalReference = {name: 'ɵEMPTY_ARRAY', moduleName: CORE};
static EMPTY_MAP: o.ExternalReference = {name: 'ɵEMPTY_MAP', moduleName: CORE};
static Renderer: o.ExternalReference = {name: 'Renderer', moduleName: CORE};

View File

@ -7,7 +7,7 @@
*/
import {flatten, sanitizeIdentifier} from '../../compile_metadata';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding, convertUpdateArguments} from '../../compiler_util/expression_converter';
import {ConstantPool} from '../../constant_pool';
import * as core from '../../core';
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, ParsedEventType, PropertyRead} from '../../expression_parser/ast';
@ -750,23 +750,13 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
if (inputType === BindingType.Property) {
if (value instanceof Interpolation) {
// Interpolated properties
const {currValExpr} = convertPropertyBinding(
this, implicit, value, this.bindingContext(), BindingForm.TrySimple);
let args: o.Expression[] = (currValExpr as any).args;
args.shift(); // ViewEngine required a count, we don't need that.
// For interpolations like attr="{{foo}}", we don't need ["", foo, ""], just [foo].
if (args.length === 3 && isEmptyStringExpression(args[0]) &&
isEmptyStringExpression(args[2])) {
args = [args[1]];
}
this.updateInstruction(
elementIndex, input.sourceSpan, propertyInterpolate(args.length), () => {
return [o.literal(attrName), ...args, ...params];
});
elementIndex, input.sourceSpan, getPropertyInterpolationExpression(value),
() =>
[o.literal(attrName),
...this.getUpdateInstructionArguments(o.variable(CONTEXT_NAME), value),
...params]);
} else {
// Bound, un-interpolated properties
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
@ -1076,6 +1066,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
o.importExpr(R3.bind).callFn([valExpr]);
}
/**
* Gets a list of argument expressions to pass to an update instruction expression. Also updates
* the temp variables state with temp variables that were identified as needing to be created
* while visiting the arguments.
* @param contextExpression The expression for the context variable used to create arguments
* @param value The original expression we will be resolving an arguments list from.
*/
private getUpdateInstructionArguments(contextExpression: o.Expression, value: AST):
o.Expression[] {
const {args, stmts} =
convertUpdateArguments(this, contextExpression, value, this.bindingContext());
this._tempVariables.push(...stmts);
return args;
}
private matchDirectives(tagName: string, elOrTpl: t.Element|t.Template) {
if (this.directiveMatcher) {
const selector = createCssSelector(tagName, getAttrsForDirectiveMatching(elOrTpl));
@ -1646,16 +1651,12 @@ function interpolate(args: o.Expression[]): o.Expression {
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
}
function isEmptyStringExpression(exp: o.Expression) {
return exp instanceof o.LiteralExpr && exp.value === '';
}
function propertyInterpolate(argsLength: number) {
if (argsLength % 2 !== 1) {
error(`Invalid propertyInterpolate argument length ${argsLength}`);
}
switch (argsLength) {
/**
* Gets the instruction to generate for an interpolated property
* @param interpolation An Interpolation AST
*/
function getPropertyInterpolationExpression(interpolation: Interpolation) {
switch (getInterpolationArgsLength(interpolation)) {
case 1:
return R3.propertyInterpolate;
case 3:
@ -1679,6 +1680,22 @@ function propertyInterpolate(argsLength: number) {
}
}
/**
* Gets the number of arguments expected to be passed to a generated instruction in the case of
* interpolation instructions.
* @param interpolation An interpolation ast
*/
function getInterpolationArgsLength(interpolation: Interpolation) {
const {expressions, strings} = interpolation;
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
// `textInterpolate`.
return 1;
} else {
return expressions.length + strings.length;
}
}
/**
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
*/

View File

@ -576,8 +576,8 @@ function readBazelWrittenFilesFrom(
function processDirectory(dir: string, dest: string) {
const entries = fs.readdirSync(dir);
for (const name of entries) {
const fullName = path.join(dir, name);
const destName = path.join(dest, name);
const fullName = path.posix.join(dir, name);
const destName = path.posix.join(dest, name);
const stat = fs.statSync(fullName);
if (!skip(name, fullName)) {
if (stat.isDirectory()) {
@ -590,11 +590,11 @@ function readBazelWrittenFilesFrom(
}
}
try {
processDirectory(bazelPackageRoot, path.join('/node_modules/@angular', packageName));
processDirectory(bazelPackageRoot, path.posix.join('/node_modules/@angular', packageName));
// todo: check why we always need an index.d.ts
if (fs.existsSync(path.join(bazelPackageRoot, `${packageName}.d.ts`))) {
const content = fs.readFileSync(path.join(bazelPackageRoot, `${packageName}.d.ts`), 'utf8');
map.set(path.join('/node_modules/@angular', packageName, 'index.d.ts'), content);
map.set(path.posix.join('/node_modules/@angular', packageName, 'index.d.ts'), content);
}
} catch (e) {
console.error(
@ -632,24 +632,25 @@ export function setup(options: {
if (options.compileAngular) {
// If this fails please add //packages/core:npm_package as a test data dependency.
readBazelWrittenFilesFrom(
path.join(sources, 'angular/packages/core/npm_package'), 'core', angularFiles,
resolveNpmTreeArtifact('angular/packages/core/npm_package'), 'core', angularFiles,
skipDirs);
}
if (options.compileFakeCore) {
readBazelWrittenFilesFrom(
path.join(sources, 'angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package'),
resolveNpmTreeArtifact(
'angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package'),
'core', angularFiles, skipDirs);
}
if (options.compileAnimations) {
// If this fails please add //packages/animations:npm_package as a test data dependency.
readBazelWrittenFilesFrom(
path.join(sources, 'angular/packages/animations/npm_package'), 'animations',
resolveNpmTreeArtifact('angular/packages/animations/npm_package'), 'animations',
angularFiles, skipDirs);
}
if (options.compileCommon) {
// If this fails please add //packages/common:npm_package as a test data dependency.
readBazelWrittenFilesFrom(
path.join(sources, 'angular/packages/common/npm_package'), 'common', angularFiles,
resolveNpmTreeArtifact('angular/packages/common/npm_package'), 'common', angularFiles,
skipDirs);
}
return;
@ -750,6 +751,10 @@ function isSourceOrDts(fileName: string): boolean {
return /\.ts$/.test(fileName);
}
function resolveNpmTreeArtifact(manifestPath: string, resolveFile = 'package.json') {
return path.dirname(require.resolve(path.posix.join(manifestPath, resolveFile)));
}
export function compile(
rootDirs: MockData, options: {
emit?: boolean,

View File

@ -3,6 +3,7 @@ load("//tools:defaults.bzl", "npm_package")
exports_files([
"tsconfig.json",
"migrations.json",
"test-migrations.json",
])
npm_package(

View File

@ -14,11 +14,6 @@
"version": "8-beta",
"description": "Warns developers if values are assigned to template variables",
"factory": "./migrations/template-var-assignment/index"
},
"migration-v8-injectable-pipe": {
"version": "8-beta",
"description": "Migrates all Pipe classes so that they have an Injectable annotation",
"factory": "./migrations/injectable-pipe/index"
}
}
}

View File

@ -10,7 +10,7 @@ import {Replacement, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {InjectablePipeVisitor} from '../angular/injectable_pipe_visitor';
import {INJECTABLE_DECORATOR_NAME, addNamedImport, getNamedImports} from '../util';
import {INJECTABLE_DECORATOR_NAME, addImport, getNamedImports} from '../util';
/**
* TSLint rule that flags `@Pipe` classes that haven't been marked as `@Injectable`.
@ -37,8 +37,7 @@ export class Rule extends Rules.TypedRule {
fixes.push(new Replacement(
namedImports.getStart(), namedImports.getWidth(),
printer.printNode(
ts.EmitHint.Unspecified,
addNamedImport(importDeclarationMissingImport, INJECTABLE_DECORATOR_NAME),
ts.EmitHint.Unspecified, addImport(namedImports, INJECTABLE_DECORATOR_NAME),
sourceFile)));
}
}

View File

@ -14,7 +14,7 @@ import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig';
import {InjectablePipeVisitor} from './angular/injectable_pipe_visitor';
import {INJECTABLE_DECORATOR_NAME, addNamedImport, getNamedImports} from './util';
import {INJECTABLE_DECORATOR_NAME, addImport, getNamedImports} from './util';
/**
* Runs a migration over a TypeScript project that adds an `@Injectable`
@ -78,8 +78,7 @@ function runInjectablePipeMigration(tree: Tree, tsconfigPath: string, basePath:
update.insertRight(
namedImports.getStart(),
printer.printNode(
ts.EmitHint.Unspecified,
addNamedImport(importDeclarationMissingImport, INJECTABLE_DECORATOR_NAME),
ts.EmitHint.Unspecified, addImport(namedImports, INJECTABLE_DECORATOR_NAME),
sourceFile));
}
}

View File

@ -12,28 +12,17 @@ import * as ts from 'typescript';
export const INJECTABLE_DECORATOR_NAME = 'Injectable';
/**
* Adds a named import to an import declaration node.
* Adds an import to a named import node, if the import does not exist already.
* @param node Node to which to add the import.
* @param importName Name of the import that should be added.
*/
export function addNamedImport(node: ts.ImportDeclaration, importName: string) {
const namedImports = getNamedImports(node);
export function addImport(node: ts.NamedImports, importName: string) {
const elements = node.elements;
const isAlreadyImported = elements.some(element => element.name.text === importName);
if (namedImports && ts.isNamedImports(namedImports)) {
const elements = namedImports.elements;
const isAlreadyImported = elements.some(element => element.name.text === importName);
if (!isAlreadyImported) {
// If there are named imports, there will be an import clause as well.
const importClause = node.importClause !;
const newImportClause = ts.createNamedImports(
[...elements, ts.createImportSpecifier(undefined, ts.createIdentifier(importName))]);
return ts.updateImportDeclaration(
node, node.decorators, node.modifiers,
ts.updateImportClause(importClause, importClause.name, newImportClause),
node.moduleSpecifier);
}
if (!isAlreadyImported) {
return ts.updateNamedImports(
node, [...elements, ts.createImportSpecifier(undefined, ts.createIdentifier(importName))]);
}
return node;

View File

@ -64,13 +64,13 @@ export class Rule extends Rules.TypedRule {
queries.forEach(q => {
const queryExpr = q.decorator.node.expression;
const {timing, message} = usageStrategy.detectTiming(q);
const transformedNode = getTransformedQueryCallExpr(q, timing, !!message);
const result = getTransformedQueryCallExpr(q, timing, !!message);
if (!transformedNode) {
if (!result) {
return;
}
const newText = printer.printNode(ts.EmitHint.Unspecified, transformedNode, sourceFile);
const newText = printer.printNode(ts.EmitHint.Unspecified, result.node, sourceFile);
// Replace the existing query decorator call expression with the
// updated call expression node.

View File

@ -192,23 +192,24 @@ async function runStaticQueryMigration(
queries.forEach(q => {
const queryExpr = q.decorator.node.expression;
const {timing, message} = strategy.detectTiming(q);
const transformedNode = getTransformedQueryCallExpr(q, timing, !!message);
const result = getTransformedQueryCallExpr(q, timing, !!message);
if (!transformedNode) {
if (!result) {
return;
}
const newText = printer.printNode(ts.EmitHint.Unspecified, transformedNode, sourceFile);
const newText = printer.printNode(ts.EmitHint.Unspecified, result.node, sourceFile);
// Replace the existing query decorator call expression with the updated
// call expression node.
update.remove(queryExpr.getStart(), queryExpr.getWidth());
update.insertRight(queryExpr.getStart(), newText);
if (message) {
if (result.failureMessage || message) {
const {line, character} =
ts.getLineAndCharacterOfPosition(sourceFile, q.decorator.node.getStart());
failureMessages.push(`${relativePath}@${line + 1}:${character + 1}: ${message}`);
failureMessages.push(
`${relativePath}@${line + 1}:${character + 1}: ${result.failureMessage || message}`);
}
});

View File

@ -10,6 +10,13 @@ import * as ts from 'typescript';
import {getPropertyNameText} from '../../utils/typescript/property_name';
import {NgQueryDefinition, QueryTiming} from './angular/query-definition';
export type TransformedQueryResult = null | {
/** Transformed call expression. */
node: ts.CallExpression;
/** Failure message which is set when the query could not be transformed successfully. */
failureMessage: string|null;
};
const TODO_COMMENT = 'TODO: add static flag';
/**
@ -17,8 +24,8 @@ const TODO_COMMENT = 'TODO: add static flag';
* determined timing. The updated decorator call expression node will be returned.
*/
export function getTransformedQueryCallExpr(
query: NgQueryDefinition, timing: QueryTiming | null, createTodo: boolean): ts.CallExpression|
null {
query: NgQueryDefinition, timing: QueryTiming | null,
createTodo: boolean): TransformedQueryResult {
const queryExpr = query.decorator.node.expression;
const queryArguments = queryExpr.arguments;
const queryPropertyAssignments = timing === null ?
@ -27,29 +34,52 @@ export function getTransformedQueryCallExpr(
'static', timing === QueryTiming.STATIC ? ts.createTrue() : ts.createFalse())];
// If the query decorator is already called with two arguments, we need to
// keep the existing options untouched and just add the new property if needed.
// keep the existing options untouched and just add the new property if possible.
if (queryArguments.length === 2) {
const existingOptions = queryArguments[1] as ts.ObjectLiteralExpression;
const existingOptions = queryArguments[1];
const hasTodoComment = existingOptions.getFullText().includes(TODO_COMMENT);
let newOptionsNode: ts.Expression;
let failureMessage: string|null = null;
// In case the options already contains a property for the "static" flag, we just
// skip this query and leave it untouched.
if (existingOptions.properties.some(
p => !!p.name && getPropertyNameText(p.name) === 'static')) {
return null;
if (ts.isObjectLiteralExpression(existingOptions)) {
// In case the options already contains a property for the "static" flag,
// we just skip this query and leave it untouched.
if (existingOptions.properties.some(
p => !!p.name && getPropertyNameText(p.name) === 'static')) {
return null;
}
newOptionsNode = ts.updateObjectLiteral(
existingOptions, existingOptions.properties.concat(queryPropertyAssignments));
// In case we want to add a todo and the options do not have the todo
// yet, we add the query timing todo as synthetic multi-line comment.
if (createTodo && !hasTodoComment) {
addQueryTimingTodoToNode(newOptionsNode);
}
} else {
// In case the options query parameter is not an object literal expression, and
// we want to set the query timing, we just preserve the existing query parameter.
newOptionsNode = existingOptions;
// We always want to add a TODO in case the query options cannot be updated.
if (!hasTodoComment) {
addQueryTimingTodoToNode(existingOptions);
}
// If there is a new explicit timing that has been determined for the given query,
// we create a transformation failure message that shows developers that they need
// to set the query timing manually to the determined query timing.
if (timing !== null) {
failureMessage = 'Cannot update query declaration to explicit timing. Please manually ' +
`set the query timing to: "{static: ${(timing === QueryTiming.STATIC).toString()}}"`;
}
}
const updatedOptions = ts.updateObjectLiteral(
existingOptions, existingOptions.properties.concat(queryPropertyAssignments));
// In case we want to add a todo and the options do not have the todo
// yet, we add the query timing todo as synthetic multi-line comment.
if (createTodo && !existingOptions.getFullText().includes(TODO_COMMENT)) {
addQueryTimingTodoToNode(updatedOptions);
}
return ts.updateCall(
queryExpr, queryExpr.expression, queryExpr.typeArguments,
[queryArguments[0], updatedOptions]);
return {
failureMessage,
node: ts.updateCall(
queryExpr, queryExpr.expression, queryExpr.typeArguments,
[queryArguments[0], newOptionsNode !])
};
}
const optionsNode = ts.createObjectLiteral(queryPropertyAssignments);
@ -58,8 +88,12 @@ export function getTransformedQueryCallExpr(
addQueryTimingTodoToNode(optionsNode);
}
return ts.updateCall(
queryExpr, queryExpr.expression, queryExpr.typeArguments, [queryArguments[0], optionsNode]);
return {
failureMessage: null,
node: ts.updateCall(
queryExpr, queryExpr.expression, queryExpr.typeArguments,
[queryArguments[0], optionsNode])
};
}
/**

View File

@ -0,0 +1,20 @@
{
"schematics": {
"migration-move-document": {
"description": "Migrates DOCUMENT Injection token from platform-browser imports to common import",
"factory": "./migrations/move-document/index"
},
"migration-static-queries": {
"description": "Migrates ViewChild and ContentChild to explicit query timing",
"factory": "./migrations/static-queries/index"
},
"migration-template-local-variables": {
"description": "Warns developers if values are assigned to template variables",
"factory": "./migrations/template-var-assignment/index"
},
"migration-injectable-pipe": {
"description": "Migrates all Pipe classes so that they have an Injectable annotation",
"factory": "./migrations/injectable-pipe/index"
}
}
}

View File

@ -5,7 +5,7 @@ ts_library(
testonly = True,
srcs = glob(["**/*.ts"]),
data = [
"//packages/core/schematics:migrations.json",
"//packages/core/schematics:test-migrations.json",
],
deps = [
"//packages/core/schematics/migrations/injectable-pipe",

View File

@ -85,7 +85,10 @@ describe('Google3 injectable pipe TSLint rule', () => {
`);
runTSLint();
expect(getFile('/index.ts')).toContain('import { Pipe, Injectable } from \'@angular/core\'');
const content = getFile('/index.ts');
expect(content).toContain('import { Pipe, Injectable } from \'@angular/core\'');
expect((content.match(/import/g) || []).length).toBe(1, 'Expected only one import statement');
});
});

View File

@ -20,7 +20,7 @@ describe('injectable pipe migration', () => {
let previousWorkingDir: string;
beforeEach(() => {
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
runner = new SchematicTestRunner('test', require.resolve('../test-migrations.json'));
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
@ -70,8 +70,10 @@ describe('injectable pipe migration', () => {
`);
runMigration();
expect(tree.readContent('/index.ts'))
.toContain('import { Pipe, Injectable } from \'@angular/core\'');
const content = tree.readContent('/index.ts');
expect(content).toContain('import { Pipe, Injectable } from \'@angular/core\'');
expect((content.match(/import/g) || []).length).toBe(1, 'Expected only one import statement');
});
it('should not add an import for Injectable if it is imported already', () => {
@ -121,5 +123,5 @@ describe('injectable pipe migration', () => {
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
}
function runMigration() { runner.runSchematic('migration-v8-injectable-pipe', {}, tree); }
function runMigration() { runner.runSchematic('migration-injectable-pipe', {}, tree); }
});

View File

@ -20,7 +20,7 @@ describe('move-document migration', () => {
let previousWorkingDir: string;
beforeEach(() => {
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
runner = new SchematicTestRunner('test', require.resolve('../test-migrations.json'));
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
@ -151,5 +151,5 @@ describe('move-document migration', () => {
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
}
function runMigration() { runner.runSchematic('migration-v8-move-document', {}, tree); }
function runMigration() { runner.runSchematic('migration-move-document', {}, tree); }
});

View File

@ -21,7 +21,7 @@ describe('static-queries migration with template strategy', () => {
let warnOutput: string[];
beforeEach(() => {
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
runner = new SchematicTestRunner('test', require.resolve('../test-migrations.json'));
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
@ -97,7 +97,7 @@ describe('static-queries migration with template strategy', () => {
}
async function runMigration() {
await runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise();
await runner.runSchematicAsync('migration-static-queries', {}, tree).toPromise();
}
describe('ViewChild', () => {
@ -105,7 +105,7 @@ describe('static-queries migration with template strategy', () => {
it('should detect queries selecting elements through template reference', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
@Component({template: \`
<ng-template>
<button #myButton>My Button</button>
@ -118,7 +118,7 @@ describe('static-queries migration with template strategy', () => {
private @ViewChild('myButton') query: any;
private @ViewChild('myStaticButton') query2: any;
}
@NgModule({declarations: [MyComp]})
export class MyModule {}
`);
@ -134,7 +134,7 @@ describe('static-queries migration with template strategy', () => {
it('should detect queries selecting ng-template as static', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
@Component({template: \`
<ng-template #myTmpl>
My template
@ -143,7 +143,7 @@ describe('static-queries migration with template strategy', () => {
export class MyComp {
private @ViewChild('myTmpl') query: any;
}
@NgModule({declarations: [MyComp]})
export class MyModule {}
`);
@ -157,7 +157,7 @@ describe('static-queries migration with template strategy', () => {
it('should detect queries selecting component view providers through string token', async() => {
writeFile('/index.ts', `
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
@Directive({
selector: '[myDirective]',
providers: [
@ -165,7 +165,7 @@ describe('static-queries migration with template strategy', () => {
]
})
export class MyDirective {}
@Directive({
selector: '[myDirective2]',
providers: [
@ -173,13 +173,13 @@ describe('static-queries migration with template strategy', () => {
]
})
export class MyDirective2 {}
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {
private @ViewChild('my-token') query: any;
private @ViewChild('my-token-2') query2: any;
}
@NgModule({declarations: [MyComp, MyDirective, MyDirective2]})
export class MyModule {}
`);
@ -202,28 +202,28 @@ describe('static-queries migration with template strategy', () => {
it('should detect queries selecting component view providers using class token', async() => {
writeFile('/index.ts', `
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
export class MyService {}
export class MyService2 {}
@Directive({
selector: '[myDirective]',
providers: [MyService]
})
export class MyDirective {}
@Directive({
selector: '[myDirective2]',
providers: [MyService2]
})
export class MyDirective2 {}
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {
private @ViewChild(MyService) query: any;
private @ViewChild(MyService2) query2: any;
}
@NgModule({declarations: [MyComp, MyDirective, MyDirective2]})
export class MyModule {}
`);
@ -247,7 +247,7 @@ describe('static-queries migration with template strategy', () => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
import {HomeComponent, HomeComponent2} from './home-comp';
@Component({
template: \`
<home-comp></home-comp>
@ -260,20 +260,20 @@ describe('static-queries migration with template strategy', () => {
private @ViewChild(HomeComponent) query: any;
private @ViewChild(HomeComponent2) query2: any;
}
@NgModule({declarations: [MyComp, HomeComponent, HomeComponent2]})
export class MyModule {}
`);
writeFile(`/home-comp.ts`, `
import {Component} from '@angular/core';
@Component({
selector: 'home-comp',
template: '<span>Home</span>'
})
export class HomeComponent {}
@Component({
selector: 'home-comp2',
template: '<span>Home 2</span>'
@ -294,12 +294,12 @@ describe('static-queries migration with template strategy', () => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
import {MyLibComponent} from 'my-lib';
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {
private @ViewChild(MyLibComponent) query: any;
}
@NgModule({declarations: [MyComp, MyLibComponent]})
export class MyModule {}
`);
@ -319,12 +319,12 @@ describe('static-queries migration with template strategy', () => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
import {MyLibComponent} from 'my-lib';
@Component({templateUrl: './my-tmpl.html'})
export class MyComp {
private @ViewChild(MyLibComponent) query: any;
}
@NgModule({declarations: [MyComp, MyLibComponent]})
export class MyModule {}
`);
@ -345,16 +345,16 @@ describe('static-queries migration with template strategy', () => {
it('should detect queries within structural directive', async() => {
writeFile('/index.ts', `
import {Component, Directive, NgModule, ViewChild} from '@angular/core';
@Directive({selector: '[ngIf]'})
export class FakeNgIf {}
@Component({templateUrl: 'my-tmpl.html'})
export class MyComp {
private @ViewChild('myRef') query: any;
private @ViewChild('myRef2') query2: any;
}
@NgModule({declarations: [MyComp, FakeNgIf]})
export class MyModule {}
`);
@ -375,14 +375,14 @@ describe('static-queries migration with template strategy', () => {
it('should detect inherited queries', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
export class BaseClass {
@ViewChild('myRef') query: any;
}
@Component({templateUrl: 'my-tmpl.html'})
export class MyComp extends BaseClass {}
@NgModule({declarations: [MyComp]})
export class MyModule {}
`);
@ -400,7 +400,7 @@ describe('static-queries migration with template strategy', () => {
it('should add a todo if a query is not declared in any component', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild, SomeToken} from '@angular/core';
export class NotAComponent {
@ViewChild('myRef', {read: SomeToken}) query: any;
}
@ -420,17 +420,17 @@ describe('static-queries migration with template strategy', () => {
it('should add a todo if a query is used multiple times with different timing', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
export class BaseClass {
@ViewChild('myRef') query: any;
}
@Component({template: '<ng-template><p #myRef></p></ng-template>'})
export class FirstComp extends BaseClass {}
@Component({template: '<span #myRef></span>'})
export class SecondComp extends BaseClass {}
@NgModule({declarations: [FirstComp, SecondComp]})
export class MyModule {}
`);
@ -448,12 +448,12 @@ describe('static-queries migration with template strategy', () => {
it('should gracefully exit migration if queries could not be analyzed', async() => {
writeFile('/index.ts', `
import {Component, ViewChild} from '@angular/core';
@Component({template: '<ng-template><p #myRef></p></ng-template>'})
export class MyComp {
@ViewChild('myRef') query: any;
}
// **NOTE**: Analysis will fail as there is no "NgModule" that declares the component.
`);
@ -492,6 +492,31 @@ describe('static-queries migration with template strategy', () => {
.toMatch(/^⮑ {3}index.ts@6:11: Content queries cannot be migrated automatically\./);
});
it('should add a todo if query options cannot be migrated inline', async() => {
writeFile('/index.ts', `
import {Component, NgModule, ViewChild} from '@angular/core';
const myOptionsVar = {};
@Component({template: '<p #myRef></p>'})
export class MyComp {
@ViewChild('myRef', myOptionsVar) query: any;
}
@NgModule({declarations: [MyComp]})
export class MyModule {}
`);
await runMigration();
expect(tree.readContent('/index.ts'))
.toContain(`@ViewChild('myRef', /* TODO: add static flag */ myOptionsVar) query: any;`);
expect(warnOutput.length).toBe(1);
expect(warnOutput[0])
.toMatch(/^⮑ {3}index.ts@8:11: Cannot update query declaration to explicit timing./);
expect(warnOutput[0]).toMatch(/Please manually set the query timing to.*static: true/);
});
it('should not normalize stylesheets which are referenced in component', async() => {
writeFile('sub_dir/index.ts', `
import {Component, NgModule, ContentChild} from '@angular/core';
@ -533,7 +558,7 @@ describe('static-queries migration with template strategy', () => {
writeFile('/src/test.ts', `
import {ViewChild} from '@angular/core';
import {AppComponent} from './app.component';
@Component({template: '<span #test>Test</span>'})
class MyTestComponent {
@ViewChild('test') query: any;
@ -542,7 +567,7 @@ describe('static-queries migration with template strategy', () => {
writeFile('/src/app.component.ts', `
import {Component, ViewChild} from '@angular/core';
@Component({template: '<span #test></span>'})
export class AppComponent {
@ViewChild('test') query: any;
@ -552,7 +577,7 @@ describe('static-queries migration with template strategy', () => {
writeFile('/src/app.module.ts', `
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
@NgModule({declarations: [AppComponent]})
export class MyModule {}
`);

View File

@ -26,7 +26,7 @@ describe('static-queries migration with usage strategy', () => {
afterAll(() => process.env['NG_STATIC_QUERY_USAGE_STRATEGY'] = '');
beforeEach(() => {
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
runner = new SchematicTestRunner('test', require.resolve('../test-migrations.json'));
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
@ -58,11 +58,11 @@ describe('static-queries migration with usage strategy', () => {
it('should mark view queries used in "ngAfterContentInit" as static', async() => {
writeFile('/index.ts', `
import {Component, ViewChild} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@ViewChild('test') query: any;
ngAfterContentInit() {
this.query.classList.add('test');
}
@ -78,11 +78,11 @@ describe('static-queries migration with usage strategy', () => {
it('should mark view queries used in "ngAfterContentChecked" as static', async() => {
writeFile('/index.ts', `
import {Component, ViewChild} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@ViewChild('test') query: any;
ngAfterContentChecked() {
this.query.classList.add('test');
}
@ -102,11 +102,11 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark content queries used in "ngAfterContentInit" as static', async() => {
writeFile('/index.ts', `
import {Component, ContentChild} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@ContentChild('test') query: any;
ngAfterContentInit() {
this.query.classList.add('test');
}
@ -122,11 +122,11 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark content queries used in "ngAfterContentChecked" as static', async() => {
writeFile('/index.ts', `
import {Component, ContentChild} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@ContentChild('test') query: any;
ngAfterContentChecked() {
this.query.classList.add('test');
}
@ -145,19 +145,19 @@ describe('static-queries migration with usage strategy', () => {
}
async function runMigration() {
await runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise();
await runner.runSchematicAsync('migration-static-queries', {}, tree).toPromise();
}
function createQueryTests(queryType: 'ViewChild' | 'ContentChild') {
it('should mark queries as dynamic', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') unused: any;
@${queryType}('dynamic') dynamic: any;
onClick() {
this.dynamicQuery.classList.add('test');
}
@ -175,13 +175,13 @@ describe('static-queries migration with usage strategy', () => {
it('should mark queries used in "ngOnChanges" as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngOnChanges() {
this.query.classList.add('test');
this.query.classList.add('test');
}
}
`);
@ -195,13 +195,13 @@ describe('static-queries migration with usage strategy', () => {
it('should mark queries used in "ngOnInit" as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngOnInit() {
this.query.classList.add('test');
this.query.classList.add('test');
}
}
`);
@ -215,13 +215,13 @@ describe('static-queries migration with usage strategy', () => {
it('should mark queries used in "ngDoCheck" as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngDoCheck() {
this.query.classList.add('test');
this.query.classList.add('test');
}
}
`);
@ -235,11 +235,11 @@ describe('static-queries migration with usage strategy', () => {
it('should keep existing query options when updating timing', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test', { /* test */ read: null }) query: any;
ngOnInit() {
this.query.classList.add('test');
}
@ -255,7 +255,7 @@ describe('static-queries migration with usage strategy', () => {
it('should not overwrite existing explicit query timing', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test', {static: /* untouched */ someVal}) query: any;
@ -271,26 +271,26 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries used in deep method chain', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
// We intentionally add this comma for the second parameter in order
// to ensure that the migration does not incorrectly create an invalid
// decorator call with three parameters. e.g. "ViewQuery('test', {...}, )"
@${queryType}('test', ) query: any;
ngOnInit() {
this.a();
}
a() {
this.b();
}
b() {
this.c();
}
c() {
console.log(this.query);
}
@ -306,16 +306,16 @@ describe('static-queries migration with usage strategy', () => {
it('should properly exit if recursive function is analyzed', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngOnInit() {
this.recursive();
}
recursive() {
recursive() {
this.recursive();
}
}
@ -330,27 +330,27 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries used in newly instantiated classes', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
@${queryType}('test') query2: any;
ngOnInit() {
new A(this);
new class Inline {
constructor(private ctx: MyComp) {
this.a();
}
a() {
this.ctx.query2.useStatically();
}
}(this);
}
}
export class A {
constructor(ctx: MyComp) {
ctx.query.test();
@ -369,16 +369,16 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries used in parenthesized new expressions', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
ngOnInit() {
new ((A))(this);
}
}
export class A {
constructor(ctx: MyComp) {
ctx.query.test();
@ -395,11 +395,11 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries in lifecycle hook with string literal name', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
'ngOnInit'() {
this.query.test();
}
@ -415,19 +415,19 @@ describe('static-queries migration with usage strategy', () => {
it('should detect static queries within nested inheritance', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
}
export class A extends MyComp {}
export class B extends A {
ngOnInit() {
this.query.testFn();
}
}
`);
@ -440,11 +440,11 @@ describe('static-queries migration with usage strategy', () => {
it('should detect static queries used within input setters', async() => {
writeFile('/index.ts', `
import {Component, Input, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
@Input()
get myVal() { return null; }
set myVal(newVal: any) {
@ -462,14 +462,14 @@ describe('static-queries migration with usage strategy', () => {
it('should detect inputs defined in metadata', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({
template: '<span #test></span>',
inputs: ["myVal"],
})
export class MyComp {
@${queryType}('test') query: any;
// We don't use the input decorator here as we want to verify
// that it properly detects the input through the component metadata.
get myVal() { return null; }
@ -488,14 +488,14 @@ describe('static-queries migration with usage strategy', () => {
it('should detect aliased inputs declared in metadata', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({
template: '<span #test></span>',
inputs: ['prop: publicName'],
})
export class MyComp {
@${queryType}('test') query: any;
set prop(val: any) {
this.query.test();
}
@ -511,11 +511,11 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark query as static if query is used in non-input setter', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
set myProperty(val: any) {
this.query.test();
}
@ -531,13 +531,13 @@ describe('static-queries migration with usage strategy', () => {
it('should detect input decorator on setter', async() => {
writeFile('/index.ts', `
import {Input, Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
get myProperty() { return null; }
// Usually the decorator is set on the get accessor, but it's also possible
// to declare the input on the setter. This ensures that it is handled properly.
@Input()
@ -556,7 +556,7 @@ describe('static-queries migration with usage strategy', () => {
it('should detect setter inputs in derived classes', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({
template: '<span #test></span>',
inputs: ['childSetter'],
@ -564,7 +564,7 @@ describe('static-queries migration with usage strategy', () => {
export class MyComp {
protected @${queryType}('test') query: any;
}
export class B extends MyComp {
set childSetter(newVal: any) {
this.query.test();
@ -581,7 +581,7 @@ describe('static-queries migration with usage strategy', () => {
it('should properly detect static query in external derived class', async() => {
writeFile('/src/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
@ -590,7 +590,7 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/src/external.ts', `
import {MyComp} from './index';
export class ExternalComp extends MyComp {
ngOnInit() {
this.query.test();
@ -614,30 +614,30 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark queries used in promises as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
private @${queryType}('test') query2: any;
ngOnInit() {
const a = Promise.resolve();
Promise.resolve().then(() => {
this.query.doSomething();
});
Promise.reject().catch(() => {
this.query.doSomething();
});
a.then(() => {}).then(() => {
this.query.doSomething();
});
Promise.resolve().then(this.createPromiseCb());
}
createPromiseCb() {
this.query2.doSomething();
return () => { /* empty callback */}
@ -656,19 +656,19 @@ describe('static-queries migration with usage strategy', () => {
it('should handle function callbacks which statically access queries', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
ngOnInit() {
ngOnInit() {
this.callSync(() => this.query.doSomething());
}
callSync(cb: Function) {
this.callSync2(cb);
}
callSync2(cb: Function) {
cb();
}
@ -686,12 +686,12 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
import {External} from './external';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
ngOnInit() {
ngOnInit() {
new External(() => this.query.doSomething());
}
}
@ -700,7 +700,7 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/external.ts', `
export class External {
constructor(cb: () => void) {
// Add extra parentheses to ensure that expression is unwrapped.
// Add extra parentheses to ensure that expression is unwrapped.
((cb))();
}
}
@ -715,21 +715,21 @@ describe('static-queries migration with usage strategy', () => {
it('should handle nested functions with arguments from parent closure', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
ngOnInit() {
ngOnInit() {
this.callSync(() => this.query.doSomething());
}
callSync(cb: Function) {
function callSyncNested() {
// The "cb" identifier comes from the "callSync" function.
cb();
}
callSyncNested();
}
}
@ -744,22 +744,22 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark queries used in setTimeout as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
private @${queryType}('test') query2: any;
private @${queryType}('test') query3: any;
ngOnInit() {
setTimeout(function() {
this.query.doSomething();
});
setTimeout(createCallback(this));
}
}
function createCallback(instance: MyComp) {
instance.query2.doSomething();
return () => instance.query3.doSomething();
@ -779,13 +779,13 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark queries used in "addEventListener" as static', async() => {
writeFile('/index.ts', `
import {Component, ElementRef, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
constructor(private elementRef: ElementRef) {}
ngOnInit() {
this.elementRef.addEventListener(() => {
this.query.classList.add('test');
@ -803,13 +803,13 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark queries used in "requestAnimationFrame" as static', async() => {
writeFile('/index.ts', `
import {Component, ElementRef, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
constructor(private elementRef: ElementRef) {}
ngOnInit() {
requestAnimationFrame(() => {
this.query.classList.add('test');
@ -827,17 +827,17 @@ describe('static-queries migration with usage strategy', () => {
it('should mark queries used in immediately-invoked function expression as static', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
private @${queryType}('test') query2: any;
ngOnInit() {
(() => {
this.query.usedStatically();
})();
(function(ctx) {
ctx.query2.useStatically();
})(this);
@ -857,11 +857,11 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
import {externalFn} from './external';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
ngOnInit() {
externalFn(this);
}
@ -870,7 +870,7 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/external.ts', `
import {MyComp} from './index';
export function externalFn(ctx: MyComp) {
ctx.query.usedStatically();
}
@ -885,15 +885,15 @@ describe('static-queries migration with usage strategy', () => {
it('should detect static queries used through getter property access', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
get myProp() {
return this.query.myValue;
}
ngOnInit() {
this.myProp.test();
}
@ -910,17 +910,17 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
import {External} from './external';
@Component({template: '<span #test></span>'})
export class MyComp {
@${queryType}('test') query: any;
private external = new External(this);
get myProp() {
return this.query.myValue;
}
ngOnInit() {
console.log(this.external.query);
}
@ -929,10 +929,10 @@ describe('static-queries migration with usage strategy', () => {
writeFile('/external.ts', `
import {MyComp} from './index';
export class External {
constructor(private comp: MyComp) {}
set query() { /** noop */ }
get query() { return this.comp.query; }
}
@ -947,16 +947,16 @@ describe('static-queries migration with usage strategy', () => {
it('should not mark queries as static if a value is assigned to accessor property', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
set myProp(value: any) { /* noop */}
get myProp() {
return this.query.myValue;
}
ngOnInit() {
this.myProp = true;
}
@ -972,16 +972,16 @@ describe('static-queries migration with usage strategy', () => {
it('should mark queries as static if non-input setter uses query', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
get myProp() { return null; }
set myProp(value: any) {
this.query.doSomething();
}
ngOnInit() {
this.myProp = 'newValue';
}
@ -997,17 +997,17 @@ describe('static-queries migration with usage strategy', () => {
it('should check setter and getter when using compound assignment', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
private @${queryType}('test') query2: any;
get myProp() { return this.query2 }
set myProp(value: any) {
this.query.doSomething();
}
ngOnInit() {
this.myProp *= 5;
}
@ -1025,14 +1025,14 @@ describe('static-queries migration with usage strategy', () => {
it('should check getters when using comparison operator in binary expression', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;
get myProp() { return this.query }
set myProp(value: any) { /* noop */ }
ngOnInit() {
if (this.myProp === 3) {
// noop
@ -1050,29 +1050,29 @@ describe('static-queries migration with usage strategy', () => {
it('should check derived abstract class methods', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export abstract class RootBaseClass {
abstract getQuery(): any;
ngOnInit() {
this.getQuery().doSomething();
}
}
export abstract class BaseClass extends RootBaseClass {
abstract getQuery2(): any;
getQuery() {
this.getQuery2();
}
}
@Component({template: '<span #test></span>'})
export class Subclass extends BaseClass {
@${queryType}('test') query: any;
getQuery2(): any {
return this.query;
return this.query;
}
}
`);
@ -1086,25 +1086,25 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries accessed through deep abstract class method', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export abstract class RootBaseClass {
abstract getQuery(): any;
ngOnInit() {
this.getQuery().doSomething();
}
}
export abstract class BaseClass extends RootBaseClass {
/* additional layer of indirection */
}
@Component({template: '<span #test></span>'})
export class Subclass extends BaseClass {
@${queryType}('test') query: any;
getQuery(): any {
return this.query;
return this.query;
}
}
`);
@ -1118,19 +1118,19 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries accessed through abstract property getter', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export abstract class BaseClass {
abstract myQuery: any;
ngOnInit() {
this.myQuery.doSomething();
}
}
@Component({template: '<span #test></span>'})
export class Subclass extends BaseClass {
@${queryType}('test') query: any;
get myQuery() { return this.query; }
}
`);
@ -1144,19 +1144,19 @@ describe('static-queries migration with usage strategy', () => {
it('should detect queries accessed through abstract property setter', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export abstract class BaseClass {
abstract myQuery: any;
ngOnInit() {
this.myQuery = "trigger";
}
}
@Component({template: '<span #test></span>'})
export class Subclass extends BaseClass {
@${queryType}('test') query: any;
set myQuery(val: any) { this.query.doSomething() }
get myQuery() { /* noop */ }
}
@ -1171,27 +1171,27 @@ describe('static-queries migration with usage strategy', () => {
it('should detect query usage in abstract class methods accessing inherited query', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export abstract class RootBaseClass {
abstract getQuery(): any;
ngOnInit() {
this.getQuery().doSomething();
}
}
export abstract class BaseClass extends RootBaseClass {
@${queryType}('test') query: any;
abstract getQuery2(): any;
getQuery() {
this.getQuery2();
}
}
@Component({template: '<span #test></span>'})
export class Subclass extends BaseClass {
getQuery2(): any {
return this.query;
}
@ -1207,7 +1207,7 @@ describe('static-queries migration with usage strategy', () => {
it('should detect query usage within component template', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({templateUrl: 'my-template.html'})
export class MyComponent {
@${queryType}('test') query: any;
@ -1228,7 +1228,7 @@ describe('static-queries migration with usage strategy', () => {
it('should detect query usage with nested property read within component template', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({templateUrl: 'my-template.html'})
export class MyComponent {
@${queryType}('test') query: any;
@ -1250,7 +1250,7 @@ describe('static-queries migration with usage strategy', () => {
async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({templateUrl: 'my-template.html'})
export class MyComponent {
@${queryType}('test') query: any;
@ -1274,7 +1274,7 @@ describe('static-queries migration with usage strategy', () => {
async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({templateUrl: 'my-template.html'})
export class MyComponent {
myObject: {someProp: any};
@ -1299,7 +1299,7 @@ describe('static-queries migration with usage strategy', () => {
it('should ignore queries accessed within <ng-template> element', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({templateUrl: 'my-template.html'})
export class MyComponent {
@${queryType}('test') query: any;
@ -1308,7 +1308,7 @@ describe('static-queries migration with usage strategy', () => {
writeFile(`/my-template.html`, `
<foo #test></foo>
<ng-template>
<my-comp [myInput]="query"></my-comp>
</ng-template>
@ -1323,11 +1323,11 @@ describe('static-queries migration with usage strategy', () => {
it('should detect inherited queries used in templates', async() => {
writeFile('/index.ts', `
import {Component, ${queryType}} from '@angular/core';
export class ParentClass {
@${queryType}('test') query: any;
}
@Component({templateUrl: 'my-template.html'})
export class MyComponent extends ParentClass {}
`);
@ -1346,7 +1346,7 @@ describe('static-queries migration with usage strategy', () => {
it('should properly handle multiple tsconfig files', async() => {
writeFile('/src/index.ts', `
import {Component, ${queryType}} from '@angular/core';
@Component({template: '<span #test></span>'})
export class MyComp {
private @${queryType}('test') query: any;

View File

@ -21,7 +21,7 @@ describe('template variable assignment migration', () => {
let warnOutput: string[];
beforeEach(() => {
runner = new SchematicTestRunner('test', require.resolve('../migrations.json'));
runner = new SchematicTestRunner('test', require.resolve('../test-migrations.json'));
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
@ -58,14 +58,12 @@ describe('template variable assignment migration', () => {
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
}
function runMigration() {
runner.runSchematic('migration-v8-template-local-variables', {}, tree);
}
function runMigration() { runner.runSchematic('migration-template-local-variables', {}, tree); }
it('should warn for two-way data binding variable assignment', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
template: '<cmp *ngFor="let optionName of options" [(opt)]="optionName"></cmp>',
})
@ -81,7 +79,7 @@ describe('template variable assignment migration', () => {
it('should warn for two-way data binding assigning to "as" variable', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './tmpl.html',
})
@ -103,7 +101,7 @@ describe('template variable assignment migration', () => {
it('should warn for bound event assignments to "as" variable', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -127,7 +125,7 @@ describe('template variable assignment migration', () => {
it('should warn for bound event assignments to template "let" variables', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -151,7 +149,7 @@ describe('template variable assignment migration', () => {
it('should not warn for bound event assignments to component property', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -168,7 +166,7 @@ describe('template variable assignment migration', () => {
it('should not warn for bound event assignments to template variable object property', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -188,7 +186,7 @@ describe('template variable assignment migration', () => {
() => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -213,7 +211,7 @@ describe('template variable assignment migration', () => {
it('should not warn for property writes with template variable name but different scope', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -236,7 +234,7 @@ describe('template variable assignment migration', () => {
it('should not throw an error if a detected template fails parsing', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
templateUrl: './sub_dir/tmpl.html',
})
@ -253,12 +251,12 @@ describe('template variable assignment migration', () => {
it('should be able to report multiple templates within the same source file', () => {
writeFile('/index.ts', `
import {Component} from '@angular/core';
@Component({
template: '<ng-template let-one><a (sayHello)="one=true"></a></ng-template>',
})
export class MyComp {}
@Component({
template: '<ng-template let-two><b (greet)="two=true"></b></ng-template>',
})

View File

@ -71,10 +71,23 @@ export function injectInjectorOnly<T>(
/**
* Generated instruction: Injects a token from the currently active injector.
*
* WARNING: This function is meant to be generated by the Ivy compiler, and is not meant for
* developer consumption!
* Must be used in the context of a factory function such as one defined for an
* `InjectionToken`. Throws an error if not called from such a context.
*
* https://github.com/angular/angular/blob/master/packages/core/src/render3/DELTA_INSTRUCTIONS.md
* (Additional documentation moved to `inject`, as it is the public API, and an alias for this instruction)
*
* @see inject
* @codeGenApi
*/
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>): T;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|
null {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* Injects a token from the currently active injector.
*
* Must be used in the context of a factory function such as one defined for an
* `InjectionToken`. Throws an error if not called from such a context.
@ -97,17 +110,6 @@ export function injectInjectorOnly<T>(
*
* @publicApi
*/
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>): T;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function ɵɵinject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|
null {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* @deprecated in v8, delete after v10. This API should be used only be generated code, and that
* code should now use ɵɵinject instead.
* @publicApi
*/
export const inject = ɵɵinject;
/**

View File

@ -110,6 +110,8 @@ function listenerInternal(
ngDevMode && assertNodeOfPossibleTypes(
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
let processOutputs = true;
// add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) {
const native = getNativeByTNode(tNode, lView) as RElement;
@ -149,6 +151,7 @@ function listenerInternal(
// Attach a new listener at the head of the coalesced listeners list.
(<any>listenerFn).__ngNextListenerFn__ = (<any>existingListener).__ngNextListenerFn__;
(<any>existingListener).__ngNextListenerFn__ = listenerFn;
processOutputs = false;
} else {
// The first argument of `listen` function in Procedural Renderer is:
// - either a target name (as a string) in case of global target (window, document, body)
@ -180,7 +183,7 @@ function listenerInternal(
const outputs = tNode.outputs;
let props: PropertyAliasValue|undefined;
if (outputs && (props = outputs[eventName])) {
if (processOutputs && outputs && (props = outputs[eventName])) {
const propsLength = props.length;
if (propsLength) {
const lCleanup = getCleanup(lView);

View File

@ -7,6 +7,7 @@
*/
import {assertEqual, assertLessThan} from '../../util/assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
@ -15,6 +16,7 @@ import {renderStringify} from '../util/misc_utils';
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
/**
* Create interpolation bindings with a variable number of expressions.
*
@ -290,12 +292,6 @@ export function ɵɵinterpolation8(
/// NEW INSTRUCTIONS
/////////////////////////////////////////////////////////////////////
/**
* Shared reference to a string, used in `ɵɵpropertyInterpolate`.
*/
const EMPTY_STRING = '';
/**
*
* Update an interpolated property on an element with a lone bound value
@ -321,11 +317,13 @@ const EMPTY_STRING = '';
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009 {
ɵɵpropertyInterpolate1(propName, EMPTY_STRING, v0, EMPTY_STRING);
export function ɵɵpropertyInterpolate(
propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009 {
ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
return ɵɵpropertyInterpolate;
}
@ -354,13 +352,15 @@ export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate1(
propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009 {
propName: string, prefix: string, v0: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix));
elementPropertyInternal(index, propName, ɵɵinterpolation1(prefix, v0, suffix), sanitizer);
return ɵɵpropertyInterpolate1;
}
@ -390,14 +390,15 @@ export function ɵɵpropertyInterpolate1(
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate2(
propName: string, prefix: string, v0: any, i0: string, v1: any,
suffix: string): TsickleIssue1009 {
propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix));
elementPropertyInternal(index, propName, ɵɵinterpolation2(prefix, v0, i0, v1, suffix), sanitizer);
return ɵɵpropertyInterpolate2;
}
@ -430,14 +431,16 @@ export function ɵɵpropertyInterpolate2(
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate3(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
suffix: string): TsickleIssue1009 {
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix));
elementPropertyInternal(
index, propName, ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix), sanitizer);
return ɵɵpropertyInterpolate3;
}
@ -472,15 +475,16 @@ export function ɵɵpropertyInterpolate3(
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate4(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, suffix: string): TsickleIssue1009 {
v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix));
index, propName, ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix), sanitizer);
return ɵɵpropertyInterpolate4;
}
@ -517,15 +521,17 @@ export function ɵɵpropertyInterpolate4(
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate5(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix));
index, propName, ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix),
sanitizer);
return ɵɵpropertyInterpolate5;
}
@ -564,16 +570,18 @@ export function ɵɵpropertyInterpolate5(
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate6(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix));
index, propName, ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix),
sanitizer);
return ɵɵpropertyInterpolate6;
}
@ -614,17 +622,19 @@ export function ɵɵpropertyInterpolate6(
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate7(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any,
suffix: string): TsickleIssue1009 {
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix));
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix),
sanitizer);
return ɵɵpropertyInterpolate7;
}
@ -667,17 +677,19 @@ export function ɵɵpropertyInterpolate7(
* @param i6 Static value used for concatenation only.
* @param v7 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolate8(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string): TsickleIssue1009 {
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(
index, propName,
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix));
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix),
sanitizer);
return ɵɵpropertyInterpolate8;
}
@ -707,12 +719,14 @@ export function ɵɵpropertyInterpolate8(
* @param values The a collection of values and the strings inbetween those values, beginning with a
* string prefix and ending with a string suffix.
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
* @param sanitizer An optional sanitizer function
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009 {
export function ɵɵpropertyInterpolateV(
propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009 {
const index = getSelectedIndex();
elementPropertyInternal(index, propName, ɵɵinterpolationV(values));
elementPropertyInternal(index, propName, ɵɵinterpolationV(values), sanitizer);
return ɵɵpropertyInterpolateV;
}

View File

@ -905,15 +905,20 @@ export function setNgReflectProperty(
attrName = normalizeDebugBindingName(attrName);
const debugValue = normalizeDebugBindingValue(value);
if (type === TNodeType.Element) {
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
} else if (value !== undefined) {
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue((element as RComment), value);
if (value == null) {
isProceduralRenderer(renderer) ? renderer.removeAttribute((element as RElement), attrName) :
(element as RElement).removeAttribute(attrName);
} else {
(element as RComment).textContent = value;
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
}
} else {
const textContent = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue((element as RComment), textContent);
} else {
(element as RComment).textContent = textContent;
}
}
}

View File

@ -14,6 +14,7 @@ import {Type} from '../../interface/type';
import {registerNgModuleType} from '../../linker/ng_module_factory_loader';
import {Component} from '../../metadata';
import {ModuleWithProviders, NgModule, NgModuleDef, NgModuleTransitiveScopes} from '../../metadata/ng_module';
import {flatten} from '../../util/array_utils';
import {assertDefined} from '../../util/assert';
import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from '../fields';
@ -158,6 +159,7 @@ function verifySemanticsOfNgModuleDef(moduleType: NgModuleType): void {
const errors: string[] = [];
const declarations = maybeUnwrapFn(ngModuleDef.declarations);
const imports = maybeUnwrapFn(ngModuleDef.imports);
flatten(imports, unwrapModuleWithProvidersImports).forEach(verifySemanticsOfNgModuleDef);
const exports = maybeUnwrapFn(ngModuleDef.exports);
declarations.forEach(verifyDeclarationsHaveDefinitions);
const combinedDeclarations: Type<any>[] = [
@ -464,18 +466,6 @@ export function transitiveScopesFor<T>(
return scopes;
}
function flatten<T>(values: any[], mapFn?: (value: T) => any): Type<T>[] {
const out: Type<T>[] = [];
values.forEach(value => {
if (Array.isArray(value)) {
out.push(...flatten<T>(value, mapFn));
} else {
out.push(mapFn ? mapFn(value) : value);
}
});
return out;
}
function expandModuleWithProviders(value: Type<any>| ModuleWithProviders<{}>): Type<any> {
if (isModuleWithProviders(value)) {
return value.ngModule;

View File

@ -21,7 +21,7 @@ export function addAllToArray(items: any[], arr: any[]) {
/**
* Flattens an array in non-recursive way. Input arrays are not modified.
*/
export function flatten(list: any[]): any[] {
export function flatten(list: any[], mapFn?: (value: any) => any): any[] {
const result: any[] = [];
let i = 0;
while (i < list.length) {
@ -34,7 +34,7 @@ export function flatten(list: any[]): any[] {
i++;
}
} else {
result.push(item);
result.push(mapFn ? mapFn(item) : item);
i++;
}
}

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, InjectionToken} from '@angular/core';
import {Component, ComponentFactoryResolver, ComponentRef, InjectionToken, NgModule, Type, ViewChild, ViewContainerRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
describe('component', () => {
@ -49,4 +50,42 @@ describe('component', () => {
expect(destroyCalls).toBe(1, 'Expected `ngOnDestroy` to only be called once.');
});
});
it('should support entry components from another module', () => {
@Component({selector: 'other-component', template: `bar`})
class OtherComponent {
}
@NgModule({
declarations: [OtherComponent],
exports: [OtherComponent],
entryComponents: [OtherComponent]
})
class OtherModule {
}
@Component({
selector: 'test_component',
template: `foo|<ng-template #vc></ng-template>`,
entryComponents: [OtherComponent]
})
class TestComponent {
@ViewChild('vc', {read: ViewContainerRef}) vcref !: ViewContainerRef;
constructor(private _cfr: ComponentFactoryResolver) {}
createComponentView<T>(cmptType: Type<T>): ComponentRef<T> {
const cf = this._cfr.resolveComponentFactory(cmptType);
return this.vcref.createComponent(cf);
}
}
TestBed.configureTestingModule({declarations: [TestComponent], imports: [OtherModule]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
fixture.componentInstance.createComponentView(OtherComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo|bar');
});
});

File diff suppressed because it is too large Load Diff

View File

@ -135,6 +135,86 @@ describe('exports', () => {
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('span').innerHTML).toBe('First');
});
describe('forward refs', () => {
it('should work with basic text bindings', () => {
const fixture = initWithTemplate(AppComp, '{{ myInput.value}} <input value="one" #myInput>');
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('one <input value="one">');
});
it('should work with element properties', () => {
const fixture = initWithTemplate(
AppComp, '<div [title]="myInput.value"></div> <input value="one" #myInput>');
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual('<div title="one"></div><input value="one">');
});
it('should work with element attrs', () => {
const fixture = initWithTemplate(
AppComp, '<div [attr.aria-label]="myInput.value"></div> <input value="one" #myInput>');
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML)
.toEqual('<div aria-label="one"></div><input value="one">');
});
it('should work with element classes', () => {
const fixture = initWithTemplate(
AppComp,
'<div [class.red]="myInput.checked"></div> <input type="checkbox" checked #myInput>');
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toContain('<div class="red"></div>');
});
it('should work with component refs', () => {
const fixture = initWithTemplate(
AppComp, '<div [dirWithInput]="myComp"></div><comp-to-ref #myComp></comp-to-ref>');
fixture.detectChanges();
const dirWithInput = fixture.debugElement.children[0].injector.get(DirWithCompInput);
const myComp = fixture.debugElement.children[1].injector.get(ComponentToReference);
expect(dirWithInput.comp).toEqual(myComp);
});
it('should work with multiple forward refs', () => {
const fixture = initWithTemplate(
AppComp,
'{{ myInput.value }} {{ myComp.name }} <comp-to-ref #myComp></comp-to-ref> <input value="one" #myInput>');
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML)
.toEqual('one Nancy <comp-to-ref></comp-to-ref><input value="one">');
});
it('should support local refs in nested dynamic views', () => {
const fixture = initWithTemplate(AppComp, `
<input value="one" #outerInput>
<div *ngIf="outer">
{{ outerInput.value }}
<input value = "two" #innerInput>
<div *ngIf="inner">
{{ outerInput.value }} - {{ innerInput.value}}
</div>
</div>
`);
fixture.detectChanges();
fixture.componentInstance.outer = true;
fixture.componentInstance.inner = true;
fixture.detectChanges();
// result should be <input value="one"><div>one <input value="two"><div>one - two</div></div>
// but contains bindings comments for ngIf
// so we check the outer div
expect(fixture.nativeElement.innerHTML).toContain('one <input value="two">');
// and the inner div
expect(fixture.nativeElement.innerHTML).toContain('one - two');
});
});
});
function initWithTemplate(compType: Type<any>, template: string) {
@ -149,6 +229,8 @@ class ComponentToReference {
@Component({selector: 'app-comp', template: ``})
class AppComp {
outer = false;
inner = false;
}
@Directive({selector: '[dir]', exportAs: 'dir'})

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, ErrorHandler, HostListener, QueryList, ViewChildren} from '@angular/core';
import {Component, Directive, ErrorHandler, EventEmitter, HostListener, Input, Output, QueryList, ViewChild, ViewChildren} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {onlyInIvy} from '@angular/private/testing';
@ -203,5 +203,40 @@ describe('event listeners', () => {
expect(returnsFalseDir.event.preventDefault).toHaveBeenCalled();
});
it('should not subscribe twice to the output when there are 2 coalesced listeners', () => {
@Directive({selector: '[foo]'})
class FooDirective {
@Input('foo') model: any;
@Output('fooChange') update = new EventEmitter();
updateValue(value: any) { this.update.emit(value); }
}
@Component({
selector: 'test-component',
template: `<div [(foo)]="someValue" (fooChange)="fooChange($event)"></div>`
})
class TestComponent {
count = 0;
someValue = -1;
@ViewChild(FooDirective) fooDirective: FooDirective|null = null;
fooChange() { this.count++; }
triggerUpdate(value: any) { this.fooDirective !.updateValue(value); }
}
TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const componentInstance = fixture.componentInstance;
componentInstance.triggerUpdate(42);
fixture.detectChanges();
expect(componentInstance.count).toEqual(1);
expect(componentInstance.someValue).toEqual(42);
});
});
});

View File

@ -10,8 +10,9 @@ import {Component, Input} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {of } from 'rxjs';
describe('elementProperty', () => {
describe('property instructions', () => {
it('should bind to properties whose names do not correspond to their attribute names', () => {
@Component({template: '<label [for]="forValue"></label>'})
class MyComp {
@ -33,6 +34,25 @@ describe('elementProperty', () => {
expect(labelNode.nativeElement.getAttribute('for')).toBe('some-textarea');
});
it('should not allow unsanitary urls in bound properties', () => {
@Component({
template: `
<img [src]="naughty">
`
})
class App {
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should not map properties whose names do not correspond to their attribute names, ' +
'if they correspond to inputs',
() => {
@ -60,4 +80,154 @@ describe('elementProperty', () => {
expect(myCompNode.nativeElement.getAttribute('for')).toBeFalsy();
expect(myCompNode.componentInstance.for).toBe('hej');
});
it('should handle all flavors of interpolated properties', () => {
@Component({
template: `
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
<div title="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
<div title="a{{one}}b{{two}}c{{three}}d"></div>
<div title="a{{one}}b{{two}}c"></div>
<div title="a{{one}}b"></div>
<div title="{{one}}"></div>
`
})
class App {
one = 1;
two = 2;
three = 3;
four = 4;
five = 5;
six = 6;
seven = 7;
eight = 8;
nine = 9;
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const titles = Array.from(fixture.nativeElement.querySelectorAll('div[title]'))
.map((div: HTMLDivElement) => div.title);
expect(titles).toEqual([
'a1b2c3d4e5f6g7h8i9j',
'a1b2c3d4e5f6g7h8i',
'a1b2c3d4e5f6g7h',
'a1b2c3d4e5f6g',
'a1b2c3d4e5f',
'a1b2c3d4e',
'a1b2c3d',
'a1b2c',
'a1b',
'1',
]);
});
it('should handle pipes in interpolated properties', () => {
@Component({
template: `
<img title="{{(details | async)?.title}}" src="{{(details | async)?.url}}" />
`
})
class App {
details = of ({
title: 'cool image',
url: 'http://somecooldomain:1234/cool_image.png',
});
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
expect(img.title).toBe('cool image');
});
// From https://angular-team.atlassian.net/browse/FW-1287
it('should handle multiple elvis operators', () => {
@Component({
template: `
<img src="{{leadSurgeon?.getCommonInfo()?.getPhotoUrl() }}">
`
})
class App {
/** Clearly this is a doctor of heavy metals. */
leadSurgeon = {
getCommonInfo() {
return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }};
}
};
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src).toBe('http://somecooldomain:1234/cool_image.png');
});
it('should not allow unsanitary urls in interpolated properties', () => {
@Component({
template: `
<img src="{{naughty}}">
`
})
class App {
naughty = 'javascript:alert("haha, I am taking over your computer!!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img: HTMLImageElement = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should not allow unsanitary urls in interpolated properties, even if you are tricky', () => {
@Component({
template: `
<img src="{{ja}}{{va}}script:{{naughty}}">
`
})
class App {
ja = 'ja';
va = 'va';
naughty = 'alert("I am a h4xx0rz1!!");';
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const img = fixture.nativeElement.querySelector('img');
expect(img.src.indexOf('unsafe:')).toBe(0);
});
it('should handle interpolations with 10+ values', () => {
@Component({
selector: 'app-comp',
template: `
<a href="http://g.com/?one={{'1'}}&two={{'2'}}&three={{'3'}}&four={{'4'}}&five={{'5'}}&six={{'6'}}&seven={{'7'}}&eight={{'8'}}&nine={{'9'}}&ten={{'10'}}">link2</a>`
})
class AppComp {
}
TestBed.configureTestingModule({declarations: [AppComp]});
const fixture = TestBed.createComponent(AppComp);
fixture.detectChanges();
const anchor = fixture.debugElement.query(By.css('a')).nativeElement;
expect(anchor.getAttribute('href'))
.toEqual(
`http://g.com/?one=1&two=2&three=3&four=4&five=5&six=6&seven=7&eight=8&nine=9&ten=10`);
});
});

View File

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load("//tools:defaults.bzl", "ng_rollup_bundle", "ts_library")
load("//tools/size-tracking:index.bzl", "js_size_tracking_test")
ts_library(
name = "core_all",
srcs = ["index.ts"],
tags = ["ivy-only"],
deps = [
"//packages/core",
],
)
ng_rollup_bundle(
name = "bundle",
entry_point = "packages/core/test/bundling/core_all/index.js",
tags = [
"ivy-only",
],
deps = [
":core_all",
"//packages/core",
"@npm//rxjs",
],
)
js_size_tracking_test(
name = "size_test",
src = "angular/packages/core/test/bundling/core_all/bundle.min.js",
data = [
":bundle",
":bundle.golden_size_map.json",
],
diffThreshold = 3,
goldenFile = "angular/packages/core/test/bundling/core_all/bundle.golden_size_map.json",
sourceMap = "angular/packages/core/test/bundling/core_all/bundle.min.js.map",
tags = [
"ivy-only",
"manual",
],
)

View File

@ -0,0 +1,362 @@
{
"unmapped": 25,
"files": {
"size": 268455,
"@angular/": {
"size": 248616,
"core/": {
"size": 248616,
"src/": {
"size": 248535,
"application_init.ts": 626,
"application_module.ts": 634,
"application_ref.ts": 7371,
"application_tokens.ts": 307,
"change_detection/": {
"size": 14119,
"change_detection.ts": 46,
"change_detection_util.ts": 822,
"change_detector_ref.ts": 93,
"constants.ts": 411,
"differs/": {
"size": 12747,
"default_iterable_differ.ts": 7623,
"default_keyvalue_differ.ts": 3882,
"iterable_differs.ts": 655,
"keyvalue_differs.ts": 587
}
},
"compiler/": {
"size": 442,
"compiler_facade.ts": 442
},
"console.ts": 217,
"debug/": {
"size": 7621,
"debug_node.ts": 7621
},
"di/": {
"size": 20079,
"forward_ref.ts": 211,
"injectable.ts": 82,
"injection_token.ts": 322,
"injector.ts": 3872,
"injector_compatibility.ts": 1005,
"interface/": {
"size": 484,
"defs.ts": 339,
"injector.ts": 145
},
"jit/": {
"size": 1988,
"environment.ts": 162,
"injectable.ts": 803,
"util.ts": 1023
},
"metadata.ts": 157,
"r3_injector.ts": 4765,
"reflective_errors.ts": 1376,
"reflective_injector.ts": 3062,
"reflective_key.ts": 661,
"reflective_provider.ts": 2000,
"scope.ts": 90,
"util.ts": 4
},
"error_handler.ts": 444,
"errors.ts": 175,
"event_emitter.ts": 952,
"i18n/": {
"size": 178,
"tokens.ts": 178
},
"interface/": {
"size": 222,
"simple_change.ts": 170,
"type.ts": 52
},
"ivy_switch.ts": 936,
"linker/": {
"size": 4923,
"compiler.ts": 825,
"component_factory.ts": 91,
"component_factory_resolver.ts": 1003,
"element_ref.ts": 119,
"ng_module_factory.ts": 78,
"ng_module_factory_loader.ts": 449,
"query_list.ts": 1011,
"system_js_ng_module_factory_loader.ts": 957,
"template_ref.ts": 97,
"view_container_ref.ts": 97,
"view_ref.ts": 196
},
"metadata/": {
"size": 3522,
"di.ts": 547,
"directives.ts": 604,
"ng_module.ts": 95,
"resource_loading.ts": 839,
"schema.ts": 1306,
"view.ts": 131
},
"platform_core_providers.ts": 118,
"profile/": {
"size": 442,
"profile.ts": 170,
"wtf_impl.ts": 272
},
"reflection/": {
"size": 4878,
"reflection.ts": 15,
"reflection_capabilities.ts": 3678,
"reflector.ts": 1185
},
"render/": {
"size": 482,
"api.ts": 482
},
"render3/": {
"size": 103297,
"bindings.ts": 300,
"component.ts": 4000,
"component_ref.ts": 2512,
"context_discovery.ts": 2098,
"definition.ts": 2486,
"di.ts": 3651,
"di_setup.ts": 1584,
"empty.ts": 16,
"errors.ts": 89,
"features/": {
"size": 2677,
"inherit_definition_feature.ts": 1993,
"ng_onchanges_feature.ts": 571,
"providers_feature.ts": 113
},
"fields.ts": 140,
"hooks.ts": 1843,
"i18n.ts": 14527,
"instructions/": {
"size": 20030,
"alloc_host_vars.ts": 290,
"change_detection.ts": 91,
"container.ts": 758,
"di.ts": 129,
"element.ts": 1214,
"element_container.ts": 335,
"embedded_view.ts": 678,
"get_current_view.ts": 26,
"listener.ts": 1401,
"next_context.ts": 44,
"projection.ts": 348,
"property.ts": 193,
"property_interpolation.ts": 2584,
"select.ts": 51,
"shared.ts": 10205,
"storage.ts": 169,
"styling.ts": 1329,
"text.ts": 185
},
"interfaces/": {
"size": 619,
"container.ts": 24,
"context.ts": 19,
"i18n.ts": 48,
"injector.ts": 242,
"renderer.ts": 176,
"view.ts": 110
},
"jit/": {
"size": 9479,
"directive.ts": 3409,
"environment.ts": 2758,
"module.ts": 3047,
"pipe.ts": 265
},
"metadata.ts": 615,
"ng_module_ref.ts": 986,
"node_manipulation.ts": 4571,
"node_selector_matcher.ts": 1780,
"node_util.ts": 335,
"pipe.ts": 958,
"players.ts": 564,
"pure_function.ts": 1273,
"query.ts": 3303,
"state.ts": 1442,
"styling/": {
"size": 11242,
"class_and_style_bindings.ts": 9074,
"core_player_handler.ts": 274,
"host_instructions_queue.ts": 335,
"player_factory.ts": 118,
"shared.ts": 5,
"state.ts": 55,
"util.ts": 1381
},
"tokens.ts": 10,
"util/": {
"size": 4102,
"attrs_utils.ts": 423,
"discovery_utils.ts": 1489,
"global_utils.ts": 374,
"injector_utils.ts": 150,
"misc_utils.ts": 625,
"view_traversal_utils.ts": 221,
"view_utils.ts": 820
},
"view_engine_compatibility.ts": 3815,
"view_engine_compatibility_prebound.ts": 38,
"view_ref.ts": 2212
},
"sanitization/": {
"size": 9766,
"bypass.ts": 669,
"html_sanitizer.ts": 4721,
"inert_body.ts": 2066,
"sanitization.ts": 1057,
"security.ts": 206,
"style_sanitizer.ts": 574,
"url_sanitizer.ts": 473
},
"testability/": {
"size": 3796,
"testability.ts": 3796
},
"util/": {
"size": 4317,
"array_utils.ts": 210,
"assert.ts": 81,
"closure.ts": 37,
"comparison.ts": 90,
"decorators.ts": 1640,
"errors.ts": 164,
"global.ts": 271,
"is_dev_mode.ts": 358,
"lang.ts": 109,
"microtask.ts": 159,
"ng_i18n_closure_mode.ts": 118,
"ng_reflect.ts": 334,
"property.ts": 201,
"stringify.ts": 290,
"symbol.ts": 255
},
"version.ts": 179,
"view/": {
"size": 55747,
"element.ts": 3814,
"entrypoint.ts": 962,
"errors.ts": 642,
"ng_content.ts": 447,
"ng_module.ts": 2448,
"provider.ts": 5363,
"pure_expression.ts": 2279,
"query.ts": 2385,
"refs.ts": 9337,
"services.ts": 11639,
"text.ts": 1551,
"types.ts": 768,
"util.ts": 4728,
"view.ts": 8143,
"view_attach.ts": 1241
},
"zone/": {
"size": 2745,
"ng_zone.ts": 2745
}
},
"test/": {
"size": 81,
"bundling/": {
"size": 81,
"core_all/": {
"size": 81,
"index.ts": 81
}
}
}
}
},
"external/": {
"size": 19814,
"npm/": {
"size": 19814,
"node_modules/": {
"size": 19814,
"rxjs/": {
"size": 18753,
"_esm5/": {
"size": 18753,
"internal/": {
"size": 18753,
"InnerSubscriber.js": 415,
"Notification.js": 15,
"Observable.js": 1420,
"Observer.js": 137,
"OuterSubscriber.js": 298,
"Subject.js": 1910,
"SubjectSubscription.js": 346,
"Subscriber.js": 3254,
"Subscription.js": 1536,
"config.js": 136,
"observable/": {
"size": 3191,
"ConnectableObservable.js": 1435,
"from.js": 245,
"fromArray.js": 186,
"fromIterable.js": 395,
"fromObservable.js": 347,
"fromPromise.js": 287,
"merge.js": 296
},
"operators/": {
"size": 3322,
"map.js": 624,
"mergeAll.js": 69,
"mergeMap.js": 1445,
"multicast.js": 415,
"refCount.js": 683,
"share.js": 82,
"windowToggle.js": 4
},
"symbol/": {
"size": 256,
"iterator.js": 104,
"observable.js": 64,
"rxSubscriber.js": 88
},
"util/": {
"size": 2517,
"EmptyError.js": 6,
"ObjectUnsubscribedError.js": 168,
"UnsubscriptionError.js": 279,
"canReportError.js": 114,
"hostReportError.js": 47,
"identity.js": 24,
"isArray.js": 67,
"isArrayLike.js": 74,
"isFunction.js": 42,
"isInteropObservable.js": 49,
"isIterable.js": 49,
"isObject.js": 51,
"isPromise.js": 84,
"isScheduler.js": 54,
"noop.js": 15,
"pipe.js": 105,
"subscribeTo.js": 434,
"subscribeToArray.js": 114,
"subscribeToIterable.js": 213,
"subscribeToObservable.js": 192,
"subscribeToPromise.js": 146,
"subscribeToResult.js": 74,
"toSubscriber.js": 116
}
}
}
},
"tslib/": {
"size": 1061,
"tslib.es6.js": 1061
}
}
}
}
}
}

View File

@ -0,0 +1,13 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as core from '@angular/core';
// We need to something with the "core" import in order to ensure
// that all symbols from core are preserved in the bundle.
console.error(core);

View File

@ -1772,6 +1772,99 @@ function declareTests(config?: {useJit: boolean}) {
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('[ERROR]');
});
it('should not reflect undefined values', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);
fixture.componentInstance.ctxProp = 'hello';
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('ng-reflect-dir-prop="hello"');
fixture.componentInstance.ctxProp = undefined !;
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).not.toContain('ng-reflect-');
});
it('should not reflect null values', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);
fixture.componentInstance.ctxProp = 'hello';
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('ng-reflect-dir-prop="hello"');
fixture.componentInstance.ctxProp = null !;
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).not.toContain('ng-reflect-');
});
it('should reflect empty strings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyDir2]});
TestBed.overrideComponent(
MyComp, {set: {template: `<div my-dir [elprop]="ctxProp"></div>`}});
const fixture = TestBed.createComponent(MyComp);
fixture.componentInstance.ctxProp = '';
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-dir-prop=""');
});
it('should not reflect in comment nodes when the value changes to undefined', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp, {set: {template: `<ng-template [ngIf]="ctxBoolProp"></ng-template>`}})
.createComponent(MyComp);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
let html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": "true"');
fixture.componentInstance.ctxBoolProp = undefined !;
fixture.detectChanges();
html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).not.toContain('ng-reflect');
});
it('should reflect in comment nodes when the value changes to null', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp, {set: {template: `<ng-template [ngIf]="ctxBoolProp"></ng-template>`}})
.createComponent(MyComp);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
let html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": "true"');
fixture.componentInstance.ctxBoolProp = null !;
fixture.detectChanges();
html = getDOM().getInnerHTML(fixture.nativeElement);
expect(html).toContain('bindings={');
expect(html).toContain('"ng-reflect-ng-if": null');
});
});
describe('property decorators', () => {

File diff suppressed because it is too large Load Diff

View File

@ -6,176 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective} from '../../src/render3/index';
import {ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementAttribute, ɵɵelementClassProp, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation2, ɵɵnextContext, ɵɵreference, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all';
import {ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵreference, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {NgIf} from './common_with_def';
import {ComponentFixture, createComponent, renderToHtml} from './render_util';
import {ComponentFixture, createComponent} from './render_util';
describe('exports', () => {
// For basic use cases, see core/test/acceptance/exports_spec.ts.
describe('forward refs', () => {
it('should work with basic text bindings', () => {
/** {{ myInput.value}} <input value="one" #myInput> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵtext(0);
ɵɵelement(1, 'input', ['value', 'one'], ['myInput', '']);
}
if (rf & RenderFlags.Update) {
const tmp = ɵɵreference(2) as any;
ɵɵtextBinding(0, ɵɵbind(tmp.value));
}
}, 3, 1);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('one<input value="one">');
});
it('should work with element properties', () => {
/** <div [title]="myInput.value"</div> <input value="one" #myInput> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div');
ɵɵelement(1, 'input', ['value', 'one'], ['myInput', '']);
}
if (rf & RenderFlags.Update) {
const tmp = ɵɵreference(2) as any;
ɵɵelementProperty(0, 'title', ɵɵbind(tmp.value));
}
}, 3, 1);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div title="one"></div><input value="one">');
});
it('should work with element attrs', () => {
/** <div [attr.aria-label]="myInput.value"</div> <input value="one" #myInput> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div');
ɵɵelement(1, 'input', ['value', 'one'], ['myInput', '']);
}
if (rf & RenderFlags.Update) {
const tmp = ɵɵreference(2) as any;
ɵɵelementAttribute(0, 'aria-label', ɵɵbind(tmp.value));
}
}, 3, 1);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div aria-label="one"></div><input value="one">');
});
it('should work with element classes', () => {
/** <div [class.red]="myInput.checked"</div> <input type="checkbox" checked #myInput> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'div', [AttributeMarker.Classes, 'red']);
ɵɵelementStyling(['red']);
ɵɵelementEnd();
ɵɵelement(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']);
}
if (rf & RenderFlags.Update) {
const tmp = ɵɵreference(2) as any;
ɵɵelementClassProp(0, 0, tmp.checked);
ɵɵelementStylingApply(0);
}
}, 3);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div class="red"></div><input checked="true" type="checkbox">');
});
it('should work with component refs', () => {
let myComponent: MyComponent;
let myDir: MyDir;
class MyComponent {
constructor() { myComponent = this; }
static ngComponentDef = ɵɵdefineComponent({
type: MyComponent,
selectors: [['comp']],
consts: 0,
vars: 0,
template: function(rf: RenderFlags, ctx: MyComponent) {},
factory: () => new MyComponent
});
}
class MyDir {
// TODO(issue/24571): remove '!'.
myDir !: MyComponent;
constructor() { myDir = this; }
static ngDirectiveDef = ɵɵdefineDirective({
type: MyDir,
selectors: [['', 'myDir', '']],
factory: () => new MyDir,
inputs: {myDir: 'myDir'}
});
}
/** <div [myDir]="myComp"></div><comp #myComp></comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'div', ['myDir', '']);
ɵɵelement(1, 'comp', null, ['myComp', '']);
}
if (rf & RenderFlags.Update) {
const tmp = ɵɵreference(2) as any;
ɵɵelementProperty(0, 'myDir', ɵɵbind(tmp));
}
}, 3, 1, [MyComponent, MyDir]);
const fixture = new ComponentFixture(App);
expect(myDir !.myDir).toEqual(myComponent !);
});
it('should work with multiple forward refs', () => {
let myComponent: MyComponent;
class MyComponent {
name = 'Nancy';
constructor() { myComponent = this; }
static ngComponentDef = ɵɵdefineComponent({
type: MyComponent,
selectors: [['comp']],
consts: 0,
vars: 0,
template: function() {},
factory: () => new MyComponent
});
}
/** {{ myInput.value }} {{ myComp.name }} <comp #myComp></comp> <input value="one" #myInput>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵtext(0);
ɵɵtext(1);
ɵɵelement(2, 'comp', null, ['myComp', '']);
ɵɵelement(4, 'input', ['value', 'one'], ['myInput', '']);
}
if (rf & RenderFlags.Update) {
const tmp1 = ɵɵreference(3) as any;
const tmp2 = ɵɵreference(5) as any;
ɵɵtextBinding(0, ɵɵbind(tmp2.value));
ɵɵtextBinding(1, ɵɵbind(tmp1.name));
}
}, 6, 2, [MyComponent]);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('oneNancy<comp></comp><input value="one">');
});
/**
* This test needs to be moved to acceptance/exports_spec.ts
* when Ivy compiler supports inline views.
*/
it('should work inside a view container', () => {
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
@ -214,74 +58,5 @@ describe('exports', () => {
fixture.update();
expect(fixture.html).toEqual('<div></div>');
});
it('should support local refs in nested dynamic views', () => {
/**
* <input value="one" #outerInput>
* <div *ngIf="outer">
* {{ outerInput.value }}
*
* <input value = "two" #innerInput>
*
* <div *ngIf="inner">
* {{ outerInput.value }} - {{ innerInput.value}}
* </div>
* </div>
*/
const App = createComponent('app', function(rf: RenderFlags, app: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'input', ['value', 'one'], ['outerInput', '']);
ɵɵelementEnd();
ɵɵtemplate(2, outerTemplate, 5, 2, 'div', [AttributeMarker.Template, 'ngIf']);
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(2, 'ngIf', ɵɵbind(app.outer));
}
}, 3, 1, [NgIf]);
function outerTemplate(rf: RenderFlags, outer: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'div');
{
ɵɵtext(1);
ɵɵelementStart(2, 'input', ['value', 'two'], ['innerInput', '']);
ɵɵelementEnd();
ɵɵtemplate(4, innerTemplate, 2, 2, 'div', [AttributeMarker.Template, 'ngIf']);
}
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
const app = ɵɵnextContext();
const outerInput = ɵɵreference(1) as any;
ɵɵtextBinding(1, ɵɵbind(outerInput.value));
ɵɵelementProperty(4, 'ngIf', ɵɵbind(app.inner));
}
}
function innerTemplate(rf: RenderFlags, inner: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'div');
{ ɵɵtext(1); }
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵnextContext();
const innerInput = ɵɵreference(3) as any;
ɵɵnextContext();
const outerInput = ɵɵreference(1) as any;
ɵɵtextBinding(1, ɵɵinterpolation2('', outerInput.value, ' - ', innerInput.value, ''));
}
}
const fixture = new ComponentFixture(App);
fixture.component.outer = true;
fixture.component.inner = true;
fixture.update();
expect(fixture.html)
.toEqual(`<input value="one"><div>one<input value="two"><div>one - two</div></div>`);
});
});
});

View File

@ -548,9 +548,8 @@ export class R3TestBedCompiler {
}
// TODO(ocombe): make this work with an Injector directly instead of creating a module for it
@NgModule({providers})
class CompilerModule {
}
class CompilerModule {}
compileNgModuleDefs(CompilerModule as NgModuleType<any>, {providers});
const CompilerModuleFactory = new R3NgModuleFactory(CompilerModule);
this._injector = CompilerModuleFactory.create(this.platform.injector).injector;

View File

@ -70,7 +70,7 @@ export interface ControlValueAccessor {
*
* ```ts
* host: {
* (change): '_onChange($event.target.value)'
* '(change)': '_onChange($event.target.value)'
* }
* ```
*

View File

@ -113,7 +113,7 @@ export const controlNameBinding: any = {
*
* ```ts
* imports: [
* ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'});
* ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'})
* ]
* ```
*

Some files were not shown because too many files have changed in this diff Show More