Compare commits
58 Commits
8.0.0-rc.1
...
8.0.0-rc.3
Author | SHA1 | Date | |
---|---|---|---|
b8cbcbcf49 | |||
1ed45bd783 | |||
82fd1920b1 | |||
f6d7271ec7 | |||
c1f3faf1df | |||
97202278f9 | |||
132f01c5ca | |||
6a987f1b9c | |||
548b003ed3 | |||
48dc41de01 | |||
817c2b49bc | |||
fed07c735c | |||
390cac6874 | |||
8eb0b8bd40 | |||
d7283c6085 | |||
3fe3a84a4b | |||
28e4187bd6 | |||
7cbc69c890 | |||
1dc134bc6b | |||
6a61d37f95 | |||
8d2e92bcfe | |||
5038f5c909 | |||
6eeca70043 | |||
9f68c35fa9 | |||
21418ea109 | |||
02d8b4ed3c | |||
6748392edc | |||
d9fd301157 | |||
71c5d80ce7 | |||
9798229fde | |||
4b2fcfd5dc | |||
b4d291aa7a | |||
b0ecafdc2f | |||
6816bb62d7 | |||
4b05b8cea0 | |||
a0728aedf7 | |||
ea96f6112a | |||
b706800ea8 | |||
c6f95b1d70 | |||
d896126604 | |||
a20da5ddcc | |||
18878600ba | |||
d7e10f3f7e | |||
4d044ea5b2 | |||
e2d1e0cd98 | |||
4e056580bb | |||
e4c2e6a904 | |||
a50989832d | |||
525307b6a3 | |||
a3ab76b216 | |||
f2265d4b46 | |||
d3ac709b99 | |||
908d43a5bb | |||
71cdb0a08e | |||
c7fbbdfa99 | |||
10e4ab7712 | |||
5114c23c21 | |||
c3e585d7eb |
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
####################################################################################################
|
||||
|
@ -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
3
.gitattributes
vendored
@ -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
|
||||
|
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,3 +1,28 @@
|
||||
<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)
|
||||
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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": [
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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—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—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.
|
||||
|
||||
|
@ -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—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>
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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">
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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*"},
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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!"
|
||||
)
|
||||
|
@ -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', () => {
|
||||
|
@ -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', () => {
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
|
@ -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": [
|
||||
|
@ -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": [
|
||||
|
@ -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": [
|
||||
|
329
aio/yarn.lock
329
aio/yarn.lock
@ -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"
|
||||
|
@ -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": [
|
||||
|
@ -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": [
|
||||
|
@ -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": [
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "8.0.0-rc.1",
|
||||
"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": {
|
||||
|
@ -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 =
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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': {
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
]);
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
|
@ -3,6 +3,7 @@ load("//tools:defaults.bzl", "npm_package")
|
||||
exports_files([
|
||||
"tsconfig.json",
|
||||
"migrations.json",
|
||||
"test-migrations.json",
|
||||
])
|
||||
|
||||
npm_package(
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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])
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
20
packages/core/schematics/test-migrations.json
Normal file
20
packages/core/schematics/test-migrations.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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); }
|
||||
});
|
||||
|
@ -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); }
|
||||
});
|
||||
|
@ -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 {}
|
||||
`);
|
||||
|
@ -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;
|
||||
|
@ -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>',
|
||||
})
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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'})
|
||||
|
@ -212,4 +212,22 @@ describe('property instructions', () => {
|
||||
|
||||
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`);
|
||||
});
|
||||
});
|
||||
|
42
packages/core/test/bundling/core_all/BUILD.bazel
Normal file
42
packages/core/test/bundling/core_all/BUILD.bazel
Normal 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",
|
||||
],
|
||||
)
|
362
packages/core/test/bundling/core_all/bundle.golden_size_map.json
Normal file
362
packages/core/test/bundling/core_all/bundle.golden_size_map.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
packages/core/test/bundling/core_all/index.ts
Normal file
13
packages/core/test/bundling/core_all/index.ts
Normal 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);
|
@ -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
@ -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>`);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -70,7 +70,7 @@ export interface ControlValueAccessor {
|
||||
*
|
||||
* ```ts
|
||||
* host: {
|
||||
* (change): '_onChange($event.target.value)'
|
||||
* '(change)': '_onChange($event.target.value)'
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
|
@ -113,7 +113,7 @@ export const controlNameBinding: any = {
|
||||
*
|
||||
* ```ts
|
||||
* imports: [
|
||||
* ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'});
|
||||
* ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'})
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
|
@ -7,7 +7,6 @@ ls_rollup_bundle(
|
||||
"fs": "fs",
|
||||
"path": "path",
|
||||
"typescript": "ts",
|
||||
"typescript/lib/tsserverlibrary": "tsserverlibrary",
|
||||
},
|
||||
license_banner = "banner.js.txt",
|
||||
visibility = ["//packages/language-service:__pkg__"],
|
||||
|
@ -6,15 +6,16 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript/lib/tsserverlibrary';
|
||||
import * as ts from 'typescript'; // used as value, passed in by tsserver at runtime
|
||||
import * as tss from 'typescript/lib/tsserverlibrary'; // used as type only
|
||||
|
||||
import {createLanguageService} from './language_service';
|
||||
import {Completion, Diagnostic, DiagnosticMessageChain} from './types';
|
||||
import {TypeScriptServiceHost} from './typescript_host';
|
||||
|
||||
const projectHostMap = new WeakMap<ts.server.Project, TypeScriptServiceHost>();
|
||||
const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>();
|
||||
|
||||
export function getExternalFiles(project: ts.server.Project): string[]|undefined {
|
||||
export function getExternalFiles(project: tss.server.Project): string[]|undefined {
|
||||
const host = projectHostMap.get(project);
|
||||
if (host) {
|
||||
const externalFiles = host.getTemplateReferences();
|
||||
@ -63,7 +64,7 @@ function diagnosticToDiagnostic(d: Diagnostic, file: ts.SourceFile): ts.Diagnost
|
||||
return result;
|
||||
}
|
||||
|
||||
export function create(info: ts.server.PluginCreateInfo): ts.LanguageService {
|
||||
export function create(info: tss.server.PluginCreateInfo): ts.LanguageService {
|
||||
const oldLS: ts.LanguageService = info.languageService;
|
||||
const proxy: ts.LanguageService = Object.assign({}, oldLS);
|
||||
const logger = info.project.projectService.logger;
|
||||
|
@ -7,12 +7,8 @@
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {createLanguageService} from '../src/language_service';
|
||||
import {ReflectorHost} from '../src/reflector_host';
|
||||
import {Completions, LanguageService} from '../src/types';
|
||||
import {TypeScriptServiceHost} from '../src/typescript_host';
|
||||
|
||||
import {toh} from './test_data';
|
||||
import {MockTypescriptHost} from './test_utils';
|
||||
@ -25,12 +21,14 @@ describe('reflector_host_spec', () => {
|
||||
let mockHost = new MockTypescriptHost(
|
||||
['/app/main.ts', '/app/parsing-cases.ts'], toh, 'app/node_modules',
|
||||
{...path, join: (...args: string[]) => originalJoin.apply(path, args)});
|
||||
let service = ts.createLanguageService(mockHost);
|
||||
let ngHost = new TypeScriptServiceHost(mockHost, service);
|
||||
let ngService = createLanguageService(ngHost);
|
||||
const reflectorHost = new ReflectorHost(() => undefined as any, mockHost, {basePath: '\\app'});
|
||||
|
||||
spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); });
|
||||
if (process.platform !== 'win32') {
|
||||
// If we call this in Windows it will cause a 'Maximum call stack size exceeded error'
|
||||
// Because we are spying on the same function that we are call faking
|
||||
spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); });
|
||||
}
|
||||
|
||||
const result = reflectorHost.moduleNameToFileName('@angular/core');
|
||||
expect(result).not.toBeNull('could not find @angular/core using path.win32');
|
||||
});
|
||||
|
@ -516,7 +516,8 @@ export class Router {
|
||||
tap(t => {
|
||||
if (this.urlUpdateStrategy === 'eager') {
|
||||
if (!t.extras.skipLocationChange) {
|
||||
this.setBrowserUrl(t.urlAfterRedirects, !!t.extras.replaceUrl, t.id);
|
||||
this.setBrowserUrl(
|
||||
t.urlAfterRedirects, !!t.extras.replaceUrl, t.id, t.extras.state);
|
||||
}
|
||||
this.browserUrlTree = t.urlAfterRedirects;
|
||||
}
|
||||
@ -555,6 +556,7 @@ export class Router {
|
||||
* way the next navigation will be coming from the current URL in the browser.
|
||||
*/
|
||||
this.rawUrlTree = t.rawUrl;
|
||||
this.browserUrlTree = t.urlAfterRedirects;
|
||||
t.resolve(null);
|
||||
return EMPTY;
|
||||
}
|
||||
|
@ -631,6 +631,7 @@ describe('Integration', () => {
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
|
||||
})));
|
||||
|
||||
it('should eagerly update the URL with urlUpdateStrategy="eagar"',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = TestBed.createComponent(RootCmp);
|
||||
@ -661,6 +662,33 @@ describe('Integration', () => {
|
||||
expect(location.path()).toEqual('/login');
|
||||
})));
|
||||
|
||||
it('should set browserUrlTree with urlUpdateStrategy="eagar" and false `shouldProcessUrl`',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = TestBed.createComponent(RootCmp);
|
||||
advance(fixture);
|
||||
|
||||
router.urlUpdateStrategy = 'eager';
|
||||
|
||||
router.resetConfig([
|
||||
{path: 'team/:id', component: SimpleCmp},
|
||||
{path: 'login', component: AbsoluteSimpleLinkCmp}
|
||||
]);
|
||||
|
||||
router.navigateByUrl('/team/22');
|
||||
advance(fixture, 1);
|
||||
|
||||
expect((router as any).browserUrlTree.toString()).toBe('/team/22');
|
||||
|
||||
// Force to not process URL changes
|
||||
router.urlHandlingStrategy.shouldProcessUrl = (url: UrlTree) => false;
|
||||
|
||||
router.navigateByUrl('/login');
|
||||
advance(fixture, 1);
|
||||
|
||||
// Because we now can't process any URL, we will end up back at the root.
|
||||
expect((router as any).browserUrlTree.toString()).toBe('/');
|
||||
})));
|
||||
|
||||
it('should eagerly update URL after redirects are applied with urlUpdateStrategy="eagar"',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = TestBed.createComponent(RootCmp);
|
||||
@ -695,6 +723,33 @@ describe('Integration', () => {
|
||||
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
|
||||
})));
|
||||
|
||||
it('should should set `state` with urlUpdateStrategy="eagar"',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: SpyLocation) => {
|
||||
|
||||
router.urlUpdateStrategy = 'eager';
|
||||
router.resetConfig([
|
||||
{path: '', component: SimpleCmp},
|
||||
{path: 'simple', component: SimpleCmp},
|
||||
]);
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
let navigation: Navigation = null !;
|
||||
router.events.subscribe(e => {
|
||||
if (e instanceof NavigationStart) {
|
||||
navigation = router.getCurrentNavigation() !;
|
||||
}
|
||||
});
|
||||
|
||||
router.navigateByUrl('/simple', {state: {foo: 'bar'}});
|
||||
tick();
|
||||
|
||||
const history = (location as any)._history;
|
||||
expect(history[history.length - 1].state.foo).toBe('bar');
|
||||
expect(history[history.length - 1].state)
|
||||
.toEqual({foo: 'bar', navigationId: history.length});
|
||||
expect(navigation.extras.state).toBeDefined();
|
||||
expect(navigation.extras.state).toEqual({foo: 'bar'});
|
||||
})));
|
||||
});
|
||||
|
||||
it('should navigate back and forward',
|
||||
|
35
tools/size-tracking/BUILD.bazel
Normal file
35
tools/size-tracking/BUILD.bazel
Normal file
@ -0,0 +1,35 @@
|
||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ts_library(
|
||||
name = "size-tracking",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
exclude = ["**/*_spec.ts"],
|
||||
),
|
||||
tsconfig = "//tools:tsconfig.json",
|
||||
deps = [
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/source-map",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "test_lib",
|
||||
testonly = True,
|
||||
srcs = glob(["**/*_spec.ts"]),
|
||||
deps = [
|
||||
":size-tracking",
|
||||
"@npm//@types/source-map",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
data = [],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"@npm//source-map",
|
||||
],
|
||||
)
|
96
tools/size-tracking/file_size_compare.ts
Normal file
96
tools/size-tracking/file_size_compare.ts
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @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 {DirectorySizeEntry, FileSizeData, getChildEntryNames} from './file_size_data';
|
||||
|
||||
export interface SizeDifference {
|
||||
filePath?: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** Compares two file size data objects and returns an array of size differences. */
|
||||
export function compareFileSizeData(
|
||||
actual: FileSizeData, expected: FileSizeData, threshold: number) {
|
||||
const diffs: SizeDifference[] = compareSizeEntry(actual.files, expected.files, '/', threshold);
|
||||
const unmappedBytesDiff = getDifferencePercentage(actual.unmapped, expected.unmapped);
|
||||
if (unmappedBytesDiff > threshold) {
|
||||
diffs.push({
|
||||
message: `Unmapped bytes differ by ${unmappedBytesDiff.toFixed(2)}% from ` +
|
||||
`the expected size (actual = ${actual.unmapped}, expected = ${expected.unmapped})`
|
||||
});
|
||||
}
|
||||
return diffs;
|
||||
}
|
||||
|
||||
/** Compares two file size entries and returns an array of size differences. */
|
||||
function compareSizeEntry(
|
||||
actual: DirectorySizeEntry | number, expected: DirectorySizeEntry | number, filePath: string,
|
||||
threshold: number) {
|
||||
if (typeof actual !== 'number' && typeof expected !== 'number') {
|
||||
return compareDirectorySizeEntry(
|
||||
<DirectorySizeEntry>actual, <DirectorySizeEntry>expected, filePath, threshold);
|
||||
} else {
|
||||
return compareActualSizeToExpected(<number>actual, <number>expected, filePath, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two size numbers and returns a size difference when the percentage difference
|
||||
* exceeds the specified threshold.
|
||||
*/
|
||||
function compareActualSizeToExpected(
|
||||
actualSize: number, expectedSize: number, filePath: string,
|
||||
threshold: number): SizeDifference[] {
|
||||
const diffPercentage = getDifferencePercentage(actualSize, expectedSize);
|
||||
if (diffPercentage > threshold) {
|
||||
return [{
|
||||
filePath: filePath,
|
||||
message: `Differs by ${diffPercentage.toFixed(2)}% from the expected size ` +
|
||||
`(actual = ${actualSize}, expected = ${expectedSize})`
|
||||
}];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two size directory size entries and returns an array of found size
|
||||
* differences within that directory.
|
||||
*/
|
||||
function compareDirectorySizeEntry(
|
||||
actual: DirectorySizeEntry, expected: DirectorySizeEntry, filePath: string,
|
||||
threshold: number): SizeDifference[] {
|
||||
const diffs: SizeDifference[] =
|
||||
[...compareActualSizeToExpected(actual.size, expected.size, filePath, threshold)];
|
||||
|
||||
getChildEntryNames(expected).forEach(childName => {
|
||||
if (actual[childName] === undefined) {
|
||||
diffs.push(
|
||||
{filePath: filePath + childName, message: 'Expected file/directory is not included.'});
|
||||
return;
|
||||
}
|
||||
|
||||
diffs.push(...compareSizeEntry(
|
||||
actual[childName], expected[childName], filePath + childName, threshold));
|
||||
});
|
||||
|
||||
getChildEntryNames(actual).forEach(childName => {
|
||||
if (expected[childName] === undefined) {
|
||||
diffs.push({
|
||||
filePath: filePath + childName,
|
||||
message: 'Unexpected file/directory included (not part of golden).'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return diffs;
|
||||
}
|
||||
|
||||
/** Gets the difference of the two size values in percentage. */
|
||||
function getDifferencePercentage(actualSize: number, expectedSize: number) {
|
||||
return (Math.abs(actualSize - expectedSize) / ((expectedSize + actualSize) / 2)) * 100;
|
||||
}
|
92
tools/size-tracking/file_size_compare_spec.ts
Normal file
92
tools/size-tracking/file_size_compare_spec.ts
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @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 {compareFileSizeData} from './file_size_compare';
|
||||
|
||||
describe('file size compare', () => {
|
||||
|
||||
it('should report if size entry differ by more than the specified threshold', () => {
|
||||
const diffs = compareFileSizeData(
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 50,
|
||||
'a.ts': 50,
|
||||
}
|
||||
},
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 75,
|
||||
'a.ts': 75,
|
||||
}
|
||||
},
|
||||
0);
|
||||
|
||||
expect(diffs.length).toBe(2);
|
||||
expect(diffs[0].filePath).toBe('/');
|
||||
expect(diffs[0].message).toMatch(/40.00% from the expected size/);
|
||||
expect(diffs[1].filePath).toBe('/a.ts');
|
||||
expect(diffs[1].message).toMatch(/40.00% from the expected size/);
|
||||
});
|
||||
|
||||
it('should not report if size percentage difference does not exceed threshold', () => {
|
||||
const diffs = compareFileSizeData(
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 50,
|
||||
'a.ts': 50,
|
||||
}
|
||||
},
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 75,
|
||||
'a.ts': 75,
|
||||
}
|
||||
},
|
||||
40);
|
||||
|
||||
expect(diffs.length).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('should report if expected file size data misses a file size entry', () => {
|
||||
const diffs = compareFileSizeData(
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 101,
|
||||
'a.ts': 100,
|
||||
'b.ts': 1,
|
||||
}
|
||||
},
|
||||
{unmapped: 0, files: {size: 100, 'a.ts': 100}}, 1);
|
||||
|
||||
expect(diffs.length).toBe(1);
|
||||
expect(diffs[0].filePath).toBe('/b.ts');
|
||||
expect(diffs[0].message).toMatch(/Unexpected file.*not part of golden./);
|
||||
});
|
||||
|
||||
it('should report if actual file size data misses an expected file size entry', () => {
|
||||
const diffs = compareFileSizeData(
|
||||
{
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 100,
|
||||
'a.ts': 100,
|
||||
}
|
||||
},
|
||||
{unmapped: 0, files: {size: 101, 'a.ts': 100, 'b.ts': 1}}, 1);
|
||||
|
||||
expect(diffs.length).toBe(1);
|
||||
expect(diffs[0].filePath).toBe('/b.ts');
|
||||
expect(diffs[0].message).toMatch(/Expected file.*not included./);
|
||||
});
|
||||
});
|
75
tools/size-tracking/file_size_data.ts
Normal file
75
tools/size-tracking/file_size_data.ts
Normal file
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export interface DirectorySizeEntry {
|
||||
size: number;
|
||||
[filePath: string]: DirectorySizeEntry|number;
|
||||
}
|
||||
|
||||
export interface FileSizeData {
|
||||
unmapped: number;
|
||||
files: DirectorySizeEntry;
|
||||
}
|
||||
|
||||
/** Returns a new file size data sorted by keys in ascending alphabetical order. */
|
||||
export function sortFileSizeData({unmapped, files}: FileSizeData): FileSizeData {
|
||||
return {unmapped, files: _sortDirectorySizeEntryObject(files)};
|
||||
}
|
||||
|
||||
/** Gets the name of all child size entries of the specified one. */
|
||||
export function getChildEntryNames(entry: DirectorySizeEntry): string[] {
|
||||
// The "size" property is reserved for the stored size value.
|
||||
return Object.keys(entry).filter(key => key !== 'size');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first size-entry that has multiple children. This is also known as
|
||||
* the omitting of the common path prefix.
|
||||
* */
|
||||
export function omitCommonPathPrefix(entry: DirectorySizeEntry): DirectorySizeEntry {
|
||||
let current: DirectorySizeEntry = entry;
|
||||
while (getChildEntryNames(current).length === 1) {
|
||||
const newChild = current[getChildEntryNames(current)[0]];
|
||||
// Only omit the current node if it is a size entry. In case the new
|
||||
// child is a holding a number, then this is a file and we don'twant
|
||||
// to incorrectly omit the leaf file entries.
|
||||
if (typeof newChild === 'number') {
|
||||
break;
|
||||
}
|
||||
current = newChild;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function _sortDirectorySizeEntryObject(oldObject: DirectorySizeEntry): DirectorySizeEntry {
|
||||
return Object.keys(oldObject)
|
||||
.sort(_sortSizeEntryKeys)
|
||||
.reduce(
|
||||
(result, key) => {
|
||||
if (typeof oldObject[key] === 'number') {
|
||||
result[key] = oldObject[key];
|
||||
} else {
|
||||
result[key] = _sortDirectorySizeEntryObject(oldObject[key] as DirectorySizeEntry);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
{} as DirectorySizeEntry);
|
||||
}
|
||||
|
||||
function _sortSizeEntryKeys(a: string, b: string) {
|
||||
// The "size" property should always be the first item in the size entry.
|
||||
// This makes it easier to inspect the size of an entry in the golden.
|
||||
if (a === 'size') {
|
||||
return -1;
|
||||
} else if (a < b) {
|
||||
return -1;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
64
tools/size-tracking/file_size_data_spec.ts
Normal file
64
tools/size-tracking/file_size_data_spec.ts
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @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 {FileSizeData, omitCommonPathPrefix, sortFileSizeData} from './file_size_data';
|
||||
|
||||
describe('file size data', () => {
|
||||
it('should be able to properly omit the common path prefix', () => {
|
||||
const data: FileSizeData = {
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 3,
|
||||
'parent/': {
|
||||
size: 3,
|
||||
'parent2/': {
|
||||
size: 3,
|
||||
'a/': {
|
||||
size: 3,
|
||||
'file.ts': 3,
|
||||
},
|
||||
'b/': {
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(omitCommonPathPrefix(data.files)).toEqual({
|
||||
size: 3,
|
||||
'a/': {
|
||||
size: 3,
|
||||
'file.ts': 3,
|
||||
},
|
||||
'b/': {
|
||||
size: 0,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to properly sort file size data in alphabetical order', () => {
|
||||
const data: FileSizeData = {
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 7,
|
||||
'b/': {'c.ts': 3, 'a.ts': 3, size: 6},
|
||||
'a/': {'nested/': {size: 1, 'a.ts': 1}, size: 1},
|
||||
}
|
||||
};
|
||||
|
||||
expect(sortFileSizeData(data)).toEqual({
|
||||
unmapped: 0,
|
||||
files: {
|
||||
size: 7,
|
||||
'a/': {size: 1, 'nested/': {size: 1, 'a.ts': 1}},
|
||||
'b/': {size: 6, 'a.ts': 3, 'c.ts': 3},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
39
tools/size-tracking/index.bzl
Normal file
39
tools/size-tracking/index.bzl
Normal file
@ -0,0 +1,39 @@
|
||||
# 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
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "nodejs_test")
|
||||
|
||||
"""
|
||||
Macro that can be used to track the size of a given input file by inspecting
|
||||
the corresponding source map. A golden file is used to compare the current
|
||||
file size data against previously approved file size data
|
||||
"""
|
||||
|
||||
def js_size_tracking_test(name, src, sourceMap, goldenFile, diffThreshold, data = [], **kwargs):
|
||||
all_data = data + [
|
||||
"//tools/size-tracking",
|
||||
"@npm//source-map",
|
||||
"@npm//chalk",
|
||||
]
|
||||
entry_point = "angular/tools/size-tracking/index.js"
|
||||
|
||||
nodejs_test(
|
||||
name = name,
|
||||
data = all_data,
|
||||
entry_point = entry_point,
|
||||
configuration_env_vars = ["compile"],
|
||||
templated_args = [src, sourceMap, goldenFile, "%d" % diffThreshold, "false"],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
nodejs_binary(
|
||||
name = "%s.accept" % name,
|
||||
testonly = True,
|
||||
data = all_data,
|
||||
entry_point = entry_point,
|
||||
configuration_env_vars = ["compile"],
|
||||
templated_args = [src, sourceMap, goldenFile, "%d" % diffThreshold, "true"],
|
||||
**kwargs
|
||||
)
|
56
tools/size-tracking/index.ts
Normal file
56
tools/size-tracking/index.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @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 {readFileSync, writeFileSync} from 'fs';
|
||||
import {SizeTracker} from './size_tracker';
|
||||
import chalk from 'chalk';
|
||||
import {compareFileSizeData} from './file_size_compare';
|
||||
import {FileSizeData} from './file_size_data';
|
||||
|
||||
if (require.main === module) {
|
||||
const [filePath, sourceMapPath, goldenPath, thresholdArg, writeGoldenArg] = process.argv.slice(2);
|
||||
const status = main(
|
||||
require.resolve(filePath), require.resolve(sourceMapPath), require.resolve(goldenPath),
|
||||
writeGoldenArg === 'true', parseInt(thresholdArg));
|
||||
|
||||
process.exit(status ? 0 : 1);
|
||||
}
|
||||
|
||||
export function main(
|
||||
filePath: string, sourceMapPath: string, goldenSizeMapPath: string, writeGolden: boolean,
|
||||
diffThreshold: number): boolean {
|
||||
const {sizeResult} = new SizeTracker(filePath, sourceMapPath);
|
||||
|
||||
if (writeGolden) {
|
||||
writeFileSync(goldenSizeMapPath, JSON.stringify(sizeResult, null, 2));
|
||||
console.error(chalk.green(`Updated golden size data in ${goldenSizeMapPath}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const expectedSizeData = <FileSizeData>JSON.parse(readFileSync(goldenSizeMapPath, 'utf8'));
|
||||
const differences = compareFileSizeData(sizeResult, expectedSizeData, diffThreshold);
|
||||
|
||||
if (!differences.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`Computed file size data does not match golden size data. ` +
|
||||
`The following differences were found:\n`);
|
||||
differences.forEach(({filePath, message}) => {
|
||||
const failurePrefix = filePath ? `"${filePath}": ` : '';
|
||||
console.error(chalk.red(` ${failurePrefix}${message}`));
|
||||
});
|
||||
|
||||
const compile = process.env['compile'];
|
||||
const defineFlag = (compile !== 'legacy') ? `--define=compile=${compile} ` : '';
|
||||
const bazelTargetName = process.env['TEST_TARGET'];
|
||||
|
||||
console.error(`\nThe golden file can be updated with the following command:`);
|
||||
console.error(` yarn bazel run ${defineFlag}${bazelTargetName}.accept`);
|
||||
}
|
104
tools/size-tracking/size_tracker.ts
Normal file
104
tools/size-tracking/size_tracker.ts
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @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 {readFileSync} from 'fs';
|
||||
import {SourceMapConsumer} from 'source-map';
|
||||
|
||||
import {DirectorySizeEntry, FileSizeData, omitCommonPathPrefix, sortFileSizeData} from './file_size_data';
|
||||
|
||||
export class SizeTracker {
|
||||
private fileContent: string;
|
||||
private consumer: SourceMapConsumer;
|
||||
|
||||
/**
|
||||
* Retraced size result that can be used to inspect where bytes in the input file
|
||||
* originated from and how much each file contributes to the input file.
|
||||
*/
|
||||
readonly sizeResult: FileSizeData;
|
||||
|
||||
constructor(private filePath: string, private sourceMapPath: string) {
|
||||
this.fileContent = readFileSync(filePath, 'utf8');
|
||||
this.consumer = new SourceMapConsumer(JSON.parse(readFileSync(sourceMapPath, 'utf8')));
|
||||
this.sizeResult = this._computeSizeResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the file size data by analyzing the input file through the specified
|
||||
* source-map.
|
||||
*/
|
||||
private _computeSizeResult(): FileSizeData {
|
||||
const lines = this.fileContent.split(/(\r?\n)/);
|
||||
const result: FileSizeData = {
|
||||
unmapped: 0,
|
||||
files: {size: 0},
|
||||
};
|
||||
|
||||
// Walk through the columns for each line in the input file and find the
|
||||
// origin source-file of the given character. This allows us to inspect
|
||||
// how the given input file is composed and how much each individual file
|
||||
// contributes to the overall bundle file.
|
||||
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
||||
const lineText = lines[lineIdx];
|
||||
for (let colIdx = 0; colIdx < lineText.length; colIdx++) {
|
||||
// Note that the "originalPositionFor" line number is one-based.
|
||||
let {source} = this.consumer.originalPositionFor({line: lineIdx + 1, column: colIdx});
|
||||
|
||||
// Increase the amount of total bytes.
|
||||
result.files.size += 1;
|
||||
|
||||
if (!source) {
|
||||
result.unmapped += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const pathSegments = this._resolveMappedPath(source).split('/');
|
||||
let currentEntry = result.files;
|
||||
|
||||
// Walk through each path segment and update the size entries with
|
||||
// new size. This makes it possibly to create na hierarchical tree
|
||||
// that matches the actual file system.
|
||||
pathSegments.forEach((segmentName, index) => {
|
||||
// The last segment always refers to a file and we therefore can
|
||||
// store the size verbatim as property value.
|
||||
if (index === pathSegments.length - 1) {
|
||||
currentEntry[segmentName] = (<number>currentEntry[segmentName] || 0) + 1;
|
||||
} else {
|
||||
// Append a trailing slash to the segment so that it
|
||||
// is clear that this size entry represents a folder.
|
||||
segmentName = `${segmentName}/`;
|
||||
const newEntry = <DirectorySizeEntry>currentEntry[segmentName] || {size: 0};
|
||||
newEntry.size += 1;
|
||||
currentEntry = currentEntry[segmentName] = newEntry;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Omit size entries which are not needed and just bloat up the file
|
||||
// size data. e.g. if all paths start with "../../", we want to omit
|
||||
// this prefix to make the size data less confusing.
|
||||
result.files = omitCommonPathPrefix(result.files);
|
||||
|
||||
return sortFileSizeData(result);
|
||||
}
|
||||
|
||||
private _resolveMappedPath(filePath: string): string {
|
||||
// We only want to store POSIX-like paths in order to avoid path
|
||||
// separator failures when running the golden tests on Windows.
|
||||
filePath = filePath.replace(/\\/g, '/');
|
||||
|
||||
// Workaround for https://github.com/angular/angular/issues/30060
|
||||
if (process.env['BAZEL_TARGET'].includes('test/bundling/core_all:size_test')) {
|
||||
return filePath.replace(/^(\.\.\/)+external/, 'external')
|
||||
.replace(/^(\.\.\/)+packages\/core\//, '@angular/core/')
|
||||
.replace(/^(\.\.\/){3}/, '@angular/core/');
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
111
tools/size-tracking/size_tracking_spec.ts
Normal file
111
tools/size-tracking/size_tracking_spec.ts
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {writeFileSync} from 'fs';
|
||||
import {join} from 'path';
|
||||
import {SourceMapGenerator} from 'source-map';
|
||||
|
||||
import {SizeTracker} from './size_tracker';
|
||||
|
||||
const testTempDir = process.env['TEST_TMPDIR'] !;
|
||||
|
||||
describe('size tracking', () => {
|
||||
let generator: SourceMapGenerator;
|
||||
|
||||
beforeEach(() => { generator = new SourceMapGenerator(); });
|
||||
|
||||
function writeFile(filePath: string, content: string): string {
|
||||
const tmpFilePath = join(testTempDir, filePath);
|
||||
writeFileSync(tmpFilePath, content);
|
||||
return tmpFilePath;
|
||||
}
|
||||
|
||||
it('should keep track of unmapped bytes in the file', () => {
|
||||
generator.addMapping({
|
||||
generated: {line: 1, column: 1},
|
||||
original: {line: 1, column: 1},
|
||||
source: './origin-a.ts',
|
||||
});
|
||||
|
||||
// A => origin-a (2 bytes), U => unmapped (1 byte)
|
||||
const mapPath = writeFile('/test.map', generator.toString());
|
||||
const inputPath = writeFile('/test.js', `UAA`);
|
||||
|
||||
const {sizeResult} = new SizeTracker(inputPath, mapPath);
|
||||
|
||||
expect(sizeResult.unmapped).toBe(1);
|
||||
expect(sizeResult.files).toEqual({
|
||||
size: 3,
|
||||
'origin-a.ts': 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly combine mapped characters from same source', () => {
|
||||
generator.addMapping(
|
||||
{generated: {line: 1, column: 0}, original: {line: 1, column: 0}, source: './origin-a.ts'});
|
||||
|
||||
generator.addMapping(
|
||||
{generated: {line: 1, column: 1}, original: {line: 1, column: 0}, source: './origin-b.ts'});
|
||||
|
||||
generator.addMapping({
|
||||
generated: {line: 1, column: 2},
|
||||
original: {line: 10, column: 0},
|
||||
source: './origin-a.ts'
|
||||
});
|
||||
|
||||
// A => origin-a (1 byte), B => origin-b (two bytes)
|
||||
const mapPath = writeFile('/test.map', generator.toString());
|
||||
const inputPath = writeFile('/test.js', `ABB`);
|
||||
|
||||
const {sizeResult} = new SizeTracker(inputPath, mapPath);
|
||||
|
||||
expect(sizeResult.unmapped).toBe(0);
|
||||
expect(sizeResult.files).toEqual({
|
||||
size: 3,
|
||||
'origin-a.ts': 2,
|
||||
'origin-b.ts': 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep track of summed-up byte sizes for directories', () => {
|
||||
generator.addMapping({
|
||||
generated: {line: 1, column: 0},
|
||||
original: {line: 1, column: 0},
|
||||
source: '@angular/core/render3/a.ts'
|
||||
});
|
||||
|
||||
generator.addMapping({
|
||||
generated: {line: 1, column: 2},
|
||||
original: {line: 1, column: 0},
|
||||
source: '@angular/core/render3/b.ts'
|
||||
});
|
||||
|
||||
generator.addMapping({
|
||||
generated: {line: 1, column: 3},
|
||||
original: {line: 1, column: 0},
|
||||
source: '@angular/core/c.ts'
|
||||
});
|
||||
|
||||
// A => render3/a.ts (2 bytes), B => render3/b.ts (1 byte), C => c.ts (1 byte)
|
||||
const mapPath = writeFile('/test.map', generator.toString());
|
||||
const inputPath = writeFile('/test.js', `AABC`);
|
||||
|
||||
const {sizeResult} = new SizeTracker(inputPath, mapPath);
|
||||
|
||||
expect(sizeResult.unmapped).toBe(0);
|
||||
expect(sizeResult.files).toEqual({
|
||||
size: 4,
|
||||
'render3/': {
|
||||
size: 3,
|
||||
'a.ts': 2,
|
||||
'b.ts': 1,
|
||||
},
|
||||
'c.ts': 1,
|
||||
});
|
||||
});
|
||||
});
|
@ -43,7 +43,7 @@ function main(argv: [string, string, string] | [string, string]): boolean {
|
||||
const defineFlag = (compile !== 'legacy') ? `--define=compile=${compile} ` : '';
|
||||
console.error(`TEST FAILED!`);
|
||||
console.error(` To update the golden file run: `);
|
||||
console.error(` yarn bazel run ${defineFlag}${process.env['BAZEL_TARGET']}.accept`);
|
||||
console.error(` yarn bazel run ${defineFlag}${process.env['TEST_TARGET']}.accept`);
|
||||
}
|
||||
}
|
||||
return passed;
|
||||
|
@ -3,12 +3,11 @@
|
||||
# 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
|
||||
|
||||
"""This test verifies that a set of top level symbols from a javascript file match a gold file.
|
||||
"""
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "nodejs_test")
|
||||
|
||||
# This does a deep import under //internal because of not wanting the wrapper macro
|
||||
# because it introduces an extra target_bin target.
|
||||
load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary", "nodejs_test")
|
||||
"""
|
||||
This test verifies that a set of top level symbols from a javascript file match a gold file.
|
||||
"""
|
||||
|
||||
def js_expected_symbol_test(name, src, golden, data = [], **kwargs):
|
||||
"""This test verifies that a set of top level symbols from a javascript file match a gold file.
|
||||
@ -17,7 +16,6 @@ def js_expected_symbol_test(name, src, golden, data = [], **kwargs):
|
||||
src,
|
||||
golden,
|
||||
Label("//tools/symbol-extractor:lib"),
|
||||
Label("@bazel_tools//tools/bash/runfiles"),
|
||||
Label("@npm//typescript"),
|
||||
]
|
||||
entry_point = "angular/tools/symbol-extractor/cli.js"
|
||||
|
@ -51,7 +51,8 @@ def ts_api_guardian_test(
|
||||
]
|
||||
|
||||
for i in strip_export_pattern:
|
||||
args += ["--stripExportPattern", i]
|
||||
# The below replacement is needed because under Windows '^' needs to be escaped twice
|
||||
args += ["--stripExportPattern", i.replace("^", "^^^^")]
|
||||
|
||||
for i in allow_module_identifiers:
|
||||
args += ["--allowModuleIdentifiers", i]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user