Compare commits
91 Commits
7.1.0-beta
...
7.1.0
Author | SHA1 | Date | |
---|---|---|---|
bf71b107b3 | |||
0d9b27ff26 | |||
c8c8648abf | |||
8ce59a583b | |||
573fb783e1 | |||
d666370e16 | |||
a72250bace | |||
026b7e34b3 | |||
4390e10dfd | |||
e56c8bf8d1 | |||
ca40565f9a | |||
975c269752 | |||
34306c356e | |||
2a7210e382 | |||
391767fb8f | |||
bf3beb5959 | |||
01917733a1 | |||
859da3af50 | |||
a43998c089 | |||
20729b3378 | |||
cf1ebdc079 | |||
893c1735dd | |||
81e975ad93 | |||
20ea5b5634 | |||
4222b63639 | |||
b5dbf5154e | |||
92e80af875 | |||
a4934a74b6 | |||
91bffa9c53 | |||
60800da6c1 | |||
b07bd30b70 | |||
4f965ad2a1 | |||
848f4148c0 | |||
65943b458f | |||
6574e61062 | |||
ee12e725c0 | |||
3ec7c5081d | |||
e22a302cad | |||
92b05652b2 | |||
4756324b5c | |||
ce5242462b | |||
1c9e526a83 | |||
8a626288a6 | |||
bf6ac6cef8 | |||
3da82338d1 | |||
f8f1168fa6 | |||
a752971bca | |||
f0c4f31771 | |||
a63fd2d0f5 | |||
d97994b27f | |||
c31e78f670 | |||
bc652a2943 | |||
e6e590479e | |||
123da1a8c2 | |||
7695dbd0bd | |||
9741f5b8cf | |||
1b97971eb7 | |||
0ada23a5fb | |||
6b1780d77e | |||
7865abf667 | |||
efa443bba3 | |||
f5a0ec0d7c | |||
5247594e86 | |||
095b6e8113 | |||
8b9249a670 | |||
f80c6008af | |||
b278ea1f09 | |||
1810cdf2c3 | |||
97ef8ae9e7 | |||
99c5db1fb1 | |||
e5c9f7a507 | |||
9729e8f4b9 | |||
78b6f88cb3 | |||
552836ebf0 | |||
2f36a9591d | |||
83c9bff242 | |||
4efb460127 | |||
ac1988d8e6 | |||
edb50a2f51 | |||
c89045fb77 | |||
1258ec041d | |||
499e303ea3 | |||
e618032d53 | |||
b9eeb1c383 | |||
1529ecd8fa | |||
51f26cb4e0 | |||
0d972d9bbf | |||
19546c234e | |||
791d192e63 | |||
15e671ec5a | |||
3567e81175 |
4
.bazelrc
4
.bazelrc
@ -24,6 +24,10 @@ build --symlink_prefix=/
|
||||
# Performance: avoid stat'ing input files
|
||||
build --watchfs
|
||||
|
||||
# Turn off legacy external runfiles
|
||||
run --nolegacy_external_runfiles
|
||||
test --nolegacy_external_runfiles
|
||||
|
||||
###############################
|
||||
# Release support #
|
||||
###############################
|
||||
|
@ -16,10 +16,6 @@ test --test_tag_filters=-manual
|
||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
||||
build --announce_rc
|
||||
|
||||
# Create dist/bin symlink to $(bazel info bazel-bin)
|
||||
# We use this when uploading artifacts after the build finishes
|
||||
build --symlink_prefix=dist/
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
|
@ -90,25 +90,6 @@ jobs:
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only,local --test_tag_filters=-ivy-only,local
|
||||
|
||||
# CircleCI will allow us to go back and view/download these artifacts from past builds.
|
||||
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
|
||||
# The destination keys need be format {projectName}/{context}/{fileName} so that the github-robot can process them for size calculations
|
||||
# projectName should remain consistant to group files
|
||||
# context and fileName can be almost anything (within usual URI rules)
|
||||
# There should only be exactly 2 forward slashes in the path
|
||||
# This is so they're backwards compatiable with the existing data we have on bundle sizes
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
||||
destination: core/hello_world/bundle
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
||||
destination: core/todo/bundle
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
|
||||
destination: core/hello_world/bundle.br
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
||||
destination: core/todo/bundle.br
|
||||
- save_cache:
|
||||
key: *cache_key
|
||||
paths:
|
||||
@ -129,7 +110,29 @@ jobs:
|
||||
- *yarn_install
|
||||
- *setup_bazel_remote_execution
|
||||
|
||||
- run: yarn test-ivy-aot //...
|
||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
||||
# measurements.
|
||||
- run: yarn test-ivy-aot //... --symlink_prefix=dist/
|
||||
|
||||
# Publish bundle artifacts which will be used to calculate the size change. **Note**: Make
|
||||
# sure that the size plugin from the Angular robot fetches the artifacts from this CircleCI
|
||||
# job (see .github/angular-robot.yml). Additionally any artifacts need to be stored with the
|
||||
# following path format: "{projectName}/{context}/{fileName}". This format is necessary
|
||||
# because otherwise the bot is not able to pick up the artifacts from CircleCI. See:
|
||||
# https://github.com/angular/github-robot/blob/master/functions/src/plugins/size.ts#L392-L394
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
||||
destination: core/hello_world/bundle
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
||||
destination: core/todo/bundle
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
|
||||
destination: core/hello_world/bundle.br
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
||||
destination: core/todo/bundle.br
|
||||
|
||||
test_aio:
|
||||
<<: *job_defaults
|
||||
|
2
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
@ -15,7 +15,7 @@ Existing issues often contain information about workarounds, resolution, or prog
|
||||
|
||||
# 🚀 feature request
|
||||
|
||||
### Releavant Package
|
||||
### Relevant Package
|
||||
<!-- Can you pin-point one or more @angular/* packages the are relevant for this feature request? -->
|
||||
<!-- ✍️edit: --> This feature request is for @angular/....
|
||||
|
||||
|
2
.github/angular-robot.yml
vendored
2
.github/angular-robot.yml
vendored
@ -4,7 +4,7 @@
|
||||
size:
|
||||
disabled: false
|
||||
maxSizeIncrease: 2000
|
||||
circleCiStatusName: "ci/circleci: test"
|
||||
circleCiStatusName: "ci/circleci: test_ivy_aot"
|
||||
|
||||
# options for the merge plugin
|
||||
merge:
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
.DS_STORE
|
||||
|
||||
/dist/
|
||||
bazel-*
|
||||
/bazel-*
|
||||
e2e_test.*
|
||||
node_modules
|
||||
bower_components
|
||||
|
@ -108,8 +108,9 @@ groups:
|
||||
- "*.lock"
|
||||
- "tools/*"
|
||||
exclude:
|
||||
- "tools/public_api_guard/*"
|
||||
- "aio/*"
|
||||
- "packages/core/test/bundling/*"
|
||||
- "tools/public_api_guard/*"
|
||||
users:
|
||||
- IgorMinar #primary
|
||||
- alexeagle
|
||||
|
71
CHANGELOG.md
71
CHANGELOG.md
@ -1,3 +1,74 @@
|
||||
<a name="7.1.0"></a>
|
||||
# [7.1.0](https://github.com/angular/angular/compare/7.1.0-rc.0...7.1.0) (2018-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
||||
* **router:** fix regression where navigateByUrl promise didn't resolve on CanLoad failure ([#26455](https://github.com/angular/angular/issues/26455)) ([1c9b065](https://github.com/angular/angular/commit/1c9b065)), closes [#26284](https://github.com/angular/angular/issues/26284)
|
||||
* **service-worker:** clean up caches from old SW versions ([#26319](https://github.com/angular/angular/issues/26319)) ([2326b9c](https://github.com/angular/angular/commit/2326b9c))
|
||||
* **upgrade:** properly destroy upgraded component elements and descendants ([#26209](https://github.com/angular/angular/issues/26209)) ([071934e](https://github.com/angular/angular/commit/071934e)), closes [#26208](https://github.com/angular/angular/issues/26208)
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
* **bazel:** unknown replay compiler error in windows ([#26711](https://github.com/angular/angular/issues/26711)) ([aed95fd](https://github.com/angular/angular/commit/aed95fd))
|
||||
* **core:** ensure that `ɵdefineNgModule` is available in flat-file formats ([#26403](https://github.com/angular/angular/issues/26403)) ([a64859b](https://github.com/angular/angular/commit/a64859b))
|
||||
* **router:** remove type bludgeoning of context and outlet when running CanDeactivate ([#26496](https://github.com/angular/angular/issues/26496)) ([496372d](https://github.com/angular/angular/commit/496372d)), closes [#18253](https://github.com/angular/angular/issues/18253)
|
||||
* **service-worker:** add typing to public api guard and fix lint errors ([#25860](https://github.com/angular/angular/issues/25860)) ([1061875](https://github.com/angular/angular/commit/1061875))
|
||||
* **upgrade:** improve downgrading-related error messages ([#26217](https://github.com/angular/angular/issues/26217)) ([7dbc103](https://github.com/angular/angular/commit/7dbc103))
|
||||
* **upgrade:** make typings compatible with older AngularJS typings ([#26880](https://github.com/angular/angular/issues/26880)) ([64647af](https://github.com/angular/angular/commit/64647af)), closes [#26420](https://github.com/angular/angular/issues/26420)
|
||||
* **compiler-cli:** add missing tslib dependency ([#27063](https://github.com/angular/angular/issues/27063)) ([c31e78f](https://github.com/angular/angular/commit/c31e78f))
|
||||
* **compiler-cli:** only pass canonical genfile paths to compiler host ([#27062](https://github.com/angular/angular/issues/27062)) ([0ada23a](https://github.com/angular/angular/commit/0ada23a))
|
||||
* **router:** add `relativeLinkResolution` to `recognize` operator ([#26990](https://github.com/angular/angular/issues/26990)) ([a752971](https://github.com/angular/angular/commit/a752971)), closes [#26983](https://github.com/angular/angular/issues/26983)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** Bazel workspace schematics ([#26971](https://github.com/angular/angular/issues/26971)) ([b07bd30](https://github.com/angular/angular/commit/b07bd30))
|
||||
* **router:** add prioritizedGuardValue operator optimization and allowing UrlTree return from guard ([#26478](https://github.com/angular/angular/issues/26478)) ([fdfedce](https://github.com/angular/angular/commit/fdfedce))
|
||||
* **compiler:** ability to mark an InvokeFunctionExpr as pure ([#26860](https://github.com/angular/angular/issues/26860)) ([4dfa71f](https://github.com/angular/angular/commit/4dfa71f))
|
||||
* **forms:** add updateOn option to FormBuilder ([#24599](https://github.com/angular/angular/issues/24599)) ([e9e804f](https://github.com/angular/angular/commit/e9e804f))
|
||||
* **router:** allow guards to return UrlTree as well as boolean ([#26521](https://github.com/angular/angular/issues/26521)) ([081f95c](https://github.com/angular/angular/commit/081f95c))
|
||||
* **router:** allow redirect from guards by returning UrlTree ([#26521](https://github.com/angular/angular/issues/26521)) ([152ca66](https://github.com/angular/angular/commit/152ca66))
|
||||
* **router:** guard returning UrlTree cancels current navigation and redirects ([#26521](https://github.com/angular/angular/issues/26521)) ([4e9f2e5](https://github.com/angular/angular/commit/4e9f2e5)), closes [#24618](https://github.com/angular/angular/issues/24618)
|
||||
* **service-worker:** add typing for messagesClicked in SwPush service ([#25860](https://github.com/angular/angular/issues/25860)) ([c78c221](https://github.com/angular/angular/commit/c78c221))
|
||||
* **service-worker:** close notifications and focus window on click ([#25860](https://github.com/angular/angular/issues/25860)) ([f5d5a3d](https://github.com/angular/angular/commit/f5d5a3d))
|
||||
* **service-worker:** handle 'notificationclick' events ([#25860](https://github.com/angular/angular/issues/25860)) ([cf6ea28](https://github.com/angular/angular/commit/cf6ea28)), closes [#20956](https://github.com/angular/angular/issues/20956) [#22311](https://github.com/angular/angular/issues/22311)
|
||||
* **upgrade:** support downgrading multiple modules ([#26217](https://github.com/angular/angular/issues/26217)) ([93837e9](https://github.com/angular/angular/commit/93837e9)), closes [#26062](https://github.com/angular/angular/issues/26062)
|
||||
* **router:** add pathParamsChange mode for runGuardsAndResolvers ([#26861](https://github.com/angular/angular/issues/26861)) ([bf6ac6c](https://github.com/angular/angular/commit/bf6ac6c)), closes [#18253](https://github.com/angular/angular/issues/18253)
|
||||
|
||||
|
||||
<a name="7.1.0-rc.0"></a>
|
||||
# [7.1.0-rc.0](https://github.com/angular/angular/compare/7.1.0-beta.2...7.1.0-rc.0) (2018-11-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** add missing tslib dependency ([#27063](https://github.com/angular/angular/issues/27063)) ([c31e78f](https://github.com/angular/angular/commit/c31e78f))
|
||||
* **compiler-cli:** only pass canonical genfile paths to compiler host ([#27062](https://github.com/angular/angular/issues/27062)) ([0ada23a](https://github.com/angular/angular/commit/0ada23a))
|
||||
* **router:** add `relativeLinkResolution` to `recognize` operator ([#26990](https://github.com/angular/angular/issues/26990)) ([a752971](https://github.com/angular/angular/commit/a752971)), closes [#26983](https://github.com/angular/angular/issues/26983)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** add pathParamsChange mode for runGuardsAndResolvers ([#26861](https://github.com/angular/angular/issues/26861)) ([bf6ac6c](https://github.com/angular/angular/commit/bf6ac6c)), closes [#18253](https://github.com/angular/angular/issues/18253)
|
||||
|
||||
|
||||
|
||||
<a name="7.0.4"></a>
|
||||
## [7.0.4](https://github.com/angular/angular/compare/7.0.3...7.0.4) (2018-11-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** add missing tslib dependency ([#27063](https://github.com/angular/angular/issues/27063)) ([4348c47](https://github.com/angular/angular/commit/4348c47))
|
||||
* **compiler-cli:** only pass canonical genfile paths to compiler host ([#27062](https://github.com/angular/angular/issues/27062)) ([188e9ce](https://github.com/angular/angular/commit/188e9ce))
|
||||
* **router:** add `relativeLinkResolution` to `recognize` operator ([#26990](https://github.com/angular/angular/issues/26990)) ([d304427](https://github.com/angular/angular/commit/d304427)), closes [#26983](https://github.com/angular/angular/issues/26983)
|
||||
|
||||
|
||||
|
||||
<a name="7.1.0-beta.2"></a>
|
||||
# [7.1.0-beta.2](https://github.com/angular/angular/compare/7.1.0-beta.1...7.1.0-beta.2) (2018-11-07)
|
||||
|
||||
|
@ -53,7 +53,7 @@ node_repositories(
|
||||
node_version = "10.9.0",
|
||||
package_json = ["//:package.json"],
|
||||
preserve_symlinks = True,
|
||||
yarn_version = "1.9.2",
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
|
@ -11,13 +11,13 @@ Install the CLI using the `npm` package manager:
|
||||
npm install -g @angular/cli
|
||||
</code-example>
|
||||
|
||||
For details about changes between versions, and information about updating from previous releases,
|
||||
For details about changes between versions, and information about updating from previous releases,
|
||||
see the Releases tab on GitHub: https://github.com/angular/angular-cli/releases
|
||||
|
||||
## Basic workflow
|
||||
|
||||
Invoke the tool on the command line through the `ng` executable.
|
||||
Online help is available on the command line.
|
||||
Invoke the tool on the command line through the `ng` executable.
|
||||
Online help is available on the command line.
|
||||
Enter the following to list commands or options for a given command (such as [generate](cli/generate)) with a short description.
|
||||
|
||||
<code-example format="." language="bash">
|
||||
@ -33,36 +33,35 @@ cd my-first-project
|
||||
ng serve
|
||||
</code-example>
|
||||
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
||||
|
||||
## Workspaces and project files
|
||||
|
||||
The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new app skeleton.
|
||||
The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new app skeleton.
|
||||
A workspace can contain multiple apps and libraries.
|
||||
The initial app created by the [ng new](cli/new) command is at the top level of the workspace.
|
||||
The initial app created by the [ng new](cli/new) command is at the top level of the workspace.
|
||||
When you generate an additional app or library in a workspace, it goes into a `projects/` subfolder.
|
||||
|
||||
A newly generated app contains the source files for a root module, with a root component and template.
|
||||
A newly generated app contains the source files for a root module, with a root component and template.
|
||||
Each app has a `src` folder that contains the logic, data, and assets.
|
||||
|
||||
You can edit the generated files directly, or add to and modify them using CLI commands.
|
||||
Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on.
|
||||
You can edit the generated files directly, or add to and modify them using CLI commands.
|
||||
Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on.
|
||||
Commands such as [add](cli/add) and [generate](cli/generate), which create or operate on apps and libraries, must be executed from within a workspace or project folder.
|
||||
|
||||
* See more about the [Workspace file structure](guide/file-structure).
|
||||
|
||||
### Workspace and project configuration
|
||||
|
||||
A single workspace configuration file, `angular.json`, is created at the top level of the workspace.
|
||||
A single workspace configuration file, `angular.json`, is created at the top level of the workspace.
|
||||
This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets.
|
||||
|
||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands can use either camelCase or dash-case.
|
||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands can use either camelCase or dash-case.
|
||||
|
||||
* See more about [Workspace Configuration](guide/workspace-config).
|
||||
* See the [complete schema](https://github.com/angular/angular-cli/wiki/angular-workspace) for `angular.json`.
|
||||
<!-- * Learn more about *configuration options for Angular(links to new guide or topics TBD)*. -->
|
||||
|
||||
|
||||
## CLI command-language syntax
|
||||
|
||||
@ -72,20 +71,20 @@ Command syntax is shown as follows:
|
||||
|
||||
* Most commands, and some options, have aliases. Aliases are shown in the syntax statement for each command.
|
||||
|
||||
* Option names are prefixed with a double dash (--).
|
||||
Option aliases are prefixed with a single dash (-).
|
||||
* Option names are prefixed with a double dash (--).
|
||||
Option aliases are prefixed with a single dash (-).
|
||||
Arguments are not prefixed.
|
||||
For example: `ng build my-app -c production`
|
||||
|
||||
* Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option.
|
||||
* Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option.
|
||||
|
||||
* Argument and option names can be given in either
|
||||
[camelCase or dash-case](guide/glossary#case-types).
|
||||
* Argument and option names can be given in either
|
||||
[camelCase or dash-case](guide/glossary#case-types).
|
||||
`--myOptionName` is equivalent to `--my-option-name`.
|
||||
|
||||
### Boolean and enumerated options
|
||||
|
||||
Boolean options have two forms: `--thisOption` sets the flag, `--noThisOption` clears it.
|
||||
Boolean options have two forms: `--thisOption` sets the flag, `--noThisOption` clears it.
|
||||
If neither option is supplied, the flag remains in its default state, as listed in the reference documentation.
|
||||
|
||||
Allowed values are given with each enumerated option description, with the default value in **bold**.
|
||||
@ -96,7 +95,6 @@ Options that specify files can be given as absolute paths, or as paths relative
|
||||
|
||||
### Schematics
|
||||
|
||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
||||
Schematic options are supplied to the command in the same format as immediate command options.
|
||||
|
||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
||||
Schematic options are supplied to the command in the same format as immediate command options.
|
||||
|
@ -72,15 +72,15 @@
|
||||
<h2>You submitted the following:</h2>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Name</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.name }}</div>
|
||||
<div class="col-xs-9">{{ model.name }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Alter Ego</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
|
||||
<div class="col-xs-9">{{ model.alterEgo }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">Power</div>
|
||||
<div class="col-xs-9 pull-left">{{ model.power }}</div>
|
||||
<div class="col-xs-9">{{ model.power }}</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-primary" (click)="submitted=false">Edit</button>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<h4>Hero Search</h4>
|
||||
|
||||
<!-- #docregion input -->
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||
<!-- #enddocregion input -->
|
||||
|
||||
<ul class="search-result">
|
||||
|
@ -1,7 +1,11 @@
|
||||
// #docregion , init
|
||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||
import { Hero } from './hero';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class InMemoryDataService implements InMemoryDbService {
|
||||
createDb() {
|
||||
const heroes = [
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
|
||||
|
||||
<ul class="search-result">
|
||||
<li *ngFor="let hero of heroes | async" >
|
||||
|
@ -56,7 +56,9 @@ A component and its template together define a *view*. A component can contain a
|
||||
When you create a component, it's associated directly with a single view, called the *host view*. The host view can be the root of a view hierarchy, which can contain *embedded views*, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
**Note:** The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
|
||||
|
||||
**Note:** The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
|
||||
|
||||
</div>
|
||||
|
||||
## NgModules and JavaScript modules
|
||||
|
@ -48,7 +48,7 @@ Similarly, use the `@Injectable()` decorator to indicate that a component or oth
|
||||
|
||||
* An injector creates dependencies, and maintains a *container* of dependency instances that it reuses if possible.
|
||||
|
||||
* A *provider* is an object that tell an injector how to obtain or create a dependency.
|
||||
* A *provider* is an object that tells an injector how to obtain or create a dependency.
|
||||
|
||||
For any dependency that you need in your app, you must register a provider with the app's injector,
|
||||
so that the injector can use the provider to create new instances.
|
||||
|
@ -32,11 +32,11 @@ For example:
|
||||
|
||||
The base file `environment.ts`, contains the default environment settings. For example:
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
```
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
</code-example>
|
||||
```
|
||||
|
||||
The `build` command uses this as the build target when no environment is specified.
|
||||
You can add further variables, either as additional properties on the environment object, or as separate objects.
|
||||
@ -144,7 +144,11 @@ You can add additional configurations as required. To add a staging environment,
|
||||
You can add more configuration options to this target environment as well.
|
||||
Any option that your build supports can be overridden in a build target configuration.
|
||||
|
||||
To build using the staging configuration, run `ng build --configuration=staging`.
|
||||
To build using the staging configuration, run the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng build --configuration=staging
|
||||
</code-example>
|
||||
|
||||
You can also configure the `serve` command to use the targeted build configuration if you add it to the "serve:configurations" section of `angular.json`:
|
||||
|
||||
|
@ -41,7 +41,7 @@ When the workspace file structure is in place, you can use the `ng generate` com
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Besides using the CLI on the command line, you can also use an interactive development environment like [Angular Console](https://angular.console.com), or manipulate files directly in the app's source folder and configuration files.
|
||||
Besides using the CLI on the command line, you can also use an interactive development environment like [Angular Console](https://angularconsole.com/), or manipulate files directly in the app's source folder and configuration files.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -81,7 +81,7 @@ The following support packages are included as dependencies in the default `pack
|
||||
|
||||
Package name | Description
|
||||
---------------------------------------- | --------------------------------------------------
|
||||
[**rxjs**](https://github.com/ReactiveX/rxjs) | Many Angular APIs return [_observables_](guide/glossary#observable). RxJS is an implementation of the proposed [Observables specification](https://github.com/zenparsing/es-observable) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee, which determines standards for the JavaScript language.
|
||||
[**rxjs**](https://github.com/ReactiveX/rxjs) | Many Angular APIs return [_observables_](guide/glossary#observable). RxJS is an implementation of the proposed [Observables specification](https://github.com/tc39/proposal-observable) currently before the [TC39](https://www.ecma-international.org/memento/tc39-m.htm) committee, which determines standards for the JavaScript language.
|
||||
[**zone.js**](https://github.com/angular/zone.js) | Angular relies on zone.js to run Angular's change detection processes when native JavaScript operations raise events. Zone.js is an implementation of a [specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ Your app may have to launch faster to engage these users before they decide to d
|
||||
|
||||
With Angular Universal, you can generate landing pages for the app that look like the complete app.
|
||||
The pages are pure HTML, and can display even if JavaScript is disabled.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using `[routerLink](guide/router#router-link)`.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using [`routerLink`](guide/router#router-link).
|
||||
|
||||
In practice, you'll serve a static version of the landing page to hold the user's attention.
|
||||
At the same time, you'll load the full Angular app behind it.
|
||||
|
@ -78,7 +78,7 @@ Each target object specifies the `builder` for that target, which is the npm pac
|
||||
}
|
||||
</code-example>
|
||||
|
||||
* The `architect/build` section configures defaults for options of the `ng build` command. See [Build target]{#build-target} below for more information.
|
||||
* The `architect/build` section configures defaults for options of the `ng build` command. See [Build target](#build-target) below for more information.
|
||||
|
||||
* The `architect/serve` section overrides build defaults and supplies additional serve defaults for the `ng serve` command. In addition to the options available for the `ng build` command, it adds options related to serving the app.
|
||||
|
||||
|
@ -22,6 +22,12 @@
|
||||
"rev": true,
|
||||
"title": "Made with Angular",
|
||||
"url": "https://www.madewithangular.com/"
|
||||
},
|
||||
"angular-subreddit": {
|
||||
"desc": "An Angular-dedicated subreddit.",
|
||||
"rev": true,
|
||||
"title": "Angular Subreddit",
|
||||
"url": "https://www.reddit.com/r/Angular2/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -149,6 +155,13 @@
|
||||
"rev": true,
|
||||
"title": "AngularCommerce",
|
||||
"url": "https://github.com/NodeArt/angular-commerce"
|
||||
},
|
||||
"ngx-api-utils": {
|
||||
"desc": "ngx-api-utils is a lean library of utilities and helpers to quickly integrate any HTTP API (REST, Ajax, and any other) with Angular.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "ngx-api-utils",
|
||||
"url": "https://github.com/ngx-api-utils/ngx-api-utils"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -19,7 +19,7 @@ To set up your development environment, follow these instructions in [Getting St
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:**: You do not need to complete the entire Getting Started. After you complete the above two sections of Getting Started, your environment is set up. Continue below to create the Tour of Heroes workspace and an initial app project.
|
||||
**Note:** You do not need to complete the entire Getting Started. After you complete the above two sections of Getting Started, your environment is set up. Continue below to create the Tour of Heroes workspace and an initial app project.
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -77,8 +77,13 @@ _after importing the `HttpClientModule`_,
|
||||
The `forRoot()` configuration method takes an `InMemoryDataService` class
|
||||
that primes the in-memory database.
|
||||
|
||||
The _Tour of Heroes_ sample creates such a class
|
||||
`src/app/in-memory-data.service.ts` which has the following content:
|
||||
The class `src/app/in-memory-data.service.ts` is generated by the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate service InMemoryData
|
||||
</code-example>
|
||||
|
||||
This class has the following content:
|
||||
|
||||
<code-example path="toh-pt6/src/app/in-memory-data.service.ts" region="init" header="src/app/in-memory-data.service.ts" linenums="false"></code-example>
|
||||
|
||||
@ -461,7 +466,7 @@ Replace the generated `HeroSearchComponent` _template_ with a text box and a lis
|
||||
Add private CSS styles to `hero-search.component.css`
|
||||
as listed in the [final code review](#herosearchcomponent) below.
|
||||
|
||||
As the user types in the search box, a *keyup* event binding calls the component's `search()`
|
||||
As the user types in the search box, an *input* event binding calls the component's `search()`
|
||||
method with the new search box value.
|
||||
|
||||
{@a asyncpipe}
|
||||
@ -511,7 +516,7 @@ You can also push values into that `Observable` by calling its `next(value)` met
|
||||
as the `search()` method does.
|
||||
|
||||
The `search()` method is called via an _event binding_ to the
|
||||
textbox's `keystroke` event.
|
||||
textbox's `input` event.
|
||||
|
||||
<code-example path="toh-pt6/src/app/hero-search/hero-search.component.html" region="input"></code-example>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
"build-for": "yarn ~~build --configuration",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build --configuration=stable",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js b50950b97",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js a176d127a",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@ -71,7 +71,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.9.0 <11.0.0",
|
||||
"yarn": ">=1.9.2 <2.0.0"
|
||||
"yarn": ">=1.10.1 <1.13.0"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
@ -1,5 +1,7 @@
|
||||
.cli-name {
|
||||
font-weight: bold;
|
||||
|
||||
.kwd { color: initial } /* override code format */
|
||||
}
|
||||
|
||||
.cli-option-syntax {
|
||||
|
@ -1,9 +1,5 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
// Reflect.metadata polyfill is only needed in the JIT mode which we use only for unit tests
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
|
@ -12,7 +12,7 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.9.0 <11.0.0",
|
||||
"yarn": ">=1.9.2 <2.0.0"
|
||||
"yarn": ">=1.10.1 <1.13.0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
@ -114,23 +114,22 @@ All the tests are executed on our Continuous Integration infrastructure and a PR
|
||||
|
||||
## <a name="clang-format"></a> Formatting your source code
|
||||
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code.
|
||||
If the source code is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
|
||||
You can automatically format your code by running:
|
||||
- `gulp format`: format all source code
|
||||
- `gulp format:changed`: re-format only edited source code.
|
||||
|
||||
``` shell
|
||||
$ gulp format
|
||||
```
|
||||
|
||||
There is a handy [clang-format extension](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format)
|
||||
for Visual Studio Code. Use the following settings to format your code when you save a file:
|
||||
A better way is to set up your IDE to format the changed file on each file save.
|
||||
|
||||
### VS Code
|
||||
1. Install [Clang-Format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) extension for VS Code.
|
||||
2. Open `settings.json` in your workspace and add these lines:
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format"
|
||||
}
|
||||
"files.autoSave": "onFocusChange",
|
||||
"editor.formatOnSave": true,
|
||||
"clang-format.executable": "PATH_TO_YOUR_WORKSPACE/angular/node_modules/.bin/clang-format",
|
||||
```
|
||||
|
||||
## Linting/verifying your source code
|
||||
@ -167,3 +166,33 @@ $ ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||
|
||||
The script will publish the build snapshot to a branch with the same name as your current branch,
|
||||
and create it if it doesn't exist.
|
||||
|
||||
## Bazel support
|
||||
### VS Code
|
||||
|
||||
1. Install [Bazel](https://marketplace.visualstudio.com/items?itemName=DevonDCarew.bazel-code) extension for VS Code.
|
||||
2. Open `settings.json` in your workspace and add these lines:
|
||||
```json
|
||||
"files.associations": {
|
||||
"*.bazel": "bazel"
|
||||
},
|
||||
```
|
||||
|
||||
## General IDE settings
|
||||
### VS Code
|
||||
|
||||
1. Open `settings.json` in your workspace and add these lines:
|
||||
```json
|
||||
"editor.tabSize": 2,
|
||||
"files.exclude": {
|
||||
"bazel-out": true,
|
||||
".idea": true,
|
||||
".bowerrc": true,
|
||||
".circleci": true,
|
||||
".github": true,
|
||||
"dist/**": true,
|
||||
"node_modules/**": true,
|
||||
".rpt2_cache": true,
|
||||
".vscode": true
|
||||
},
|
||||
```
|
||||
|
@ -3,7 +3,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1497,
|
||||
"main": 181839,
|
||||
"main": 185238,
|
||||
"polyfills": 59608
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_reposi
|
||||
check_bazel_version("0.17.0")
|
||||
node_repositories(
|
||||
node_version = "10.9.0",
|
||||
yarn_version = "1.9.2",
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
|
@ -13,8 +13,8 @@
|
||||
"@angular/bazel": "file:../angular/dist/packages-dist/bazel",
|
||||
"@angular/compiler": "file:../angular/dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../angular/dist/packages-dist/compiler-cli",
|
||||
"@bazel/karma": "0.20.3",
|
||||
"@bazel/typescript": "0.20.3",
|
||||
"@bazel/karma": "0.21.0",
|
||||
"@bazel/typescript": "0.21.0",
|
||||
"@types/jasmine": "2.8.8",
|
||||
"@types/source-map": "0.5.1",
|
||||
"protractor": "5.1.2",
|
||||
|
@ -24,13 +24,19 @@ ivy-ngcc
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it compile @angular/core/ApplicationModule correctly?
|
||||
grep "ApplicationModule.ngModuleDef = ɵdefineNgModule" node_modules/@angular/core/fesm2015/core.js
|
||||
grep "ApplicationModule.ngModuleDef = defineNgModule" node_modules/@angular/core/fesm2015/core.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "ApplicationModule.ngModuleDef = ɵdefineNgModule" node_modules/@angular/core/fesm5/core.js
|
||||
grep "ApplicationModule.ngModuleDef = defineNgModule" node_modules/@angular/core/fesm5/core.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "ApplicationModule.ngModuleDef = ɵngcc0.ɵdefineNgModule" node_modules/@angular/core/esm2015/src/application_module.js
|
||||
grep "ApplicationModule.ngModuleDef = ɵngcc0.defineNgModule" node_modules/@angular/core/esm2015/src/application_module.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "ApplicationModule.ngModuleDef = ɵngcc0.ɵdefineNgModule" node_modules/@angular/core/esm5/src/application_module.js
|
||||
grep "ApplicationModule.ngModuleDef = ɵngcc0.defineNgModule" node_modules/@angular/core/esm5/src/application_module.js
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Did it transform @angular/core typing files correctly?
|
||||
grep "import [*] as ɵngcc0 from './r3_symbols';" node_modules/@angular/core/src/application_module.d.ts
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
grep "static ngInjectorDef: ɵngcc0.InjectorDef<ApplicationModule>;" node_modules/@angular/core/src/application_module.d.ts
|
||||
if [[ $? != 0 ]]; then exit 1; fi
|
||||
|
||||
# Can it be safely run again (as a noop)?
|
||||
|
@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test_suite")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ng_module(
|
||||
@ -50,7 +50,7 @@ ts_devserver(
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
protractor_web_test_suite(
|
||||
name = "perf",
|
||||
configuration = "//:protractor-perf.conf.js",
|
||||
data = [
|
||||
@ -64,8 +64,6 @@ protractor_web_test(
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
"ivy-only",
|
||||
],
|
||||
deps = [
|
||||
|
@ -1,7 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ng_module", "ng_rollup_bundle")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test")
|
||||
load("//packages/bazel:index.bzl", "protractor_web_test_suite")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ng_module(
|
||||
@ -48,7 +48,7 @@ ts_devserver(
|
||||
tags = ["ivy-only"],
|
||||
)
|
||||
|
||||
protractor_web_test(
|
||||
protractor_web_test_suite(
|
||||
name = "perf",
|
||||
configuration = "//:protractor-perf.conf.js",
|
||||
data = [
|
||||
@ -58,8 +58,6 @@ protractor_web_test(
|
||||
on_prepare = ":protractor.on_prepare.js",
|
||||
server = ":devserver",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
"ivy-only",
|
||||
],
|
||||
deps = [
|
||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.1.0-beta.2",
|
||||
"version": "7.1.0",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -9,7 +9,7 @@
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.9.0 <11.0.0",
|
||||
"yarn": ">=1.9.2 <2.0.0"
|
||||
"yarn": ">=1.10.1 <1.13.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -28,13 +28,16 @@
|
||||
"commitmsg": "node ./scripts/git/commit-msg.js",
|
||||
"test-ivy-aot": "bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot",
|
||||
"test-fixme-ivy-aot": "bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot --test_tag_filters=-no-ivy-aot",
|
||||
"list-fixme-ivy-targets": "bazel query --output=label 'attr(\"tags\", \"\\[.*fixme-ivy.*\\]\", //...) except kind(\"sh_binary\", //...) except kind(\"devmode_js_sources\", //...)' | sort",
|
||||
"bazel": "bazel"
|
||||
},
|
||||
"// 1": "dependencies are used locally and by bazel",
|
||||
"dependencies": {
|
||||
"@angular-devkit/schematics": "^0.5.5",
|
||||
"@bazel/typescript": "0.20.3",
|
||||
"@schematics/angular": "^0.5.4",
|
||||
"@angular-devkit/architect": "^0.10.6",
|
||||
"@angular-devkit/core": "^7.0.4",
|
||||
"@angular-devkit/schematics": "^7.0.4",
|
||||
"@bazel/typescript": "0.21.0",
|
||||
"@schematics/angular": "^7.0.4",
|
||||
"@types/chokidar": "1.7.3",
|
||||
"@types/convert-source-map": "^1.5.1",
|
||||
"@types/diff": "^3.2.2",
|
||||
@ -88,7 +91,7 @@
|
||||
"devDependencies": {
|
||||
"@bazel/bazel": "^0.18.1",
|
||||
"@bazel/ibazel": "^0.1.1",
|
||||
"@bazel/karma": "0.20.3",
|
||||
"@bazel/karma": "0.21.0",
|
||||
"@types/angular": "^1.6.47",
|
||||
"@types/base64-js": "1.2.5",
|
||||
"@types/jasminewd2": "^2.0.4",
|
||||
|
@ -7,14 +7,17 @@ npm_package(
|
||||
"package.json",
|
||||
"protractor-utils.js",
|
||||
"//packages/bazel/src:package_assets",
|
||||
"//packages/bazel/src/schematics:package_assets",
|
||||
],
|
||||
packages = [
|
||||
"//packages/bazel/docs",
|
||||
],
|
||||
tags = ["release-with-framework"],
|
||||
deps = [
|
||||
"//packages/bazel/src/builders",
|
||||
"//packages/bazel/src/ng_package:lib",
|
||||
"//packages/bazel/src/ngc-wrapped:ngc_lib",
|
||||
"//packages/bazel/src/protractor/utils",
|
||||
"//packages/bazel/src/schematics/bazel-workspace",
|
||||
],
|
||||
)
|
||||
|
@ -25,15 +25,15 @@ def rules_angular_dependencies():
|
||||
_maybe(
|
||||
http_archive,
|
||||
name = "build_bazel_rules_nodejs",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.15.3.zip",
|
||||
strip_prefix = "rules_nodejs-0.15.3",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.16.2.zip",
|
||||
strip_prefix = "rules_nodejs-0.16.2",
|
||||
)
|
||||
|
||||
_maybe(
|
||||
http_archive,
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/8ea1a55cf5cf8be84ddfeefc0940769b80da792f.zip",
|
||||
strip_prefix = "rules_typescript-8ea1a55cf5cf8be84ddfeefc0940769b80da792f",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.21.0.zip",
|
||||
strip_prefix = "rules_typescript-0.21.0",
|
||||
)
|
||||
|
||||
# Needed for Remote Execution
|
||||
|
@ -12,7 +12,11 @@
|
||||
},
|
||||
"typings": "./src/ngc-wrapped/index.d.ts",
|
||||
"dependencies": {
|
||||
"@bazel/typescript": "^0.20.3",
|
||||
"@angular-devkit/architect": "^0.10.6",
|
||||
"@angular-devkit/core": "^7.0.4",
|
||||
"@angular-devkit/schematics": "^7.0.4",
|
||||
"@bazel/typescript": "^0.21.0",
|
||||
"@schematics/angular": "^7.0.4",
|
||||
"@types/node": "6.0.84",
|
||||
"shelljs": "0.8.2",
|
||||
"tsickle": "0.32.1"
|
||||
@ -25,6 +29,8 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
},
|
||||
"builders": "./src/builders/builders.json",
|
||||
"schematics": "./src/schematics/collection.json",
|
||||
"ng-update": {
|
||||
"packageGroup": "NG_UPDATE_PACKAGE_GROUP"
|
||||
},
|
||||
|
30
packages/bazel/src/builders/BUILD.bazel
Normal file
30
packages/bazel/src/builders/BUILD.bazel
Normal file
@ -0,0 +1,30 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "package_assets",
|
||||
srcs = [
|
||||
"builders.json",
|
||||
],
|
||||
visibility = ["//packages/bazel:__subpackages__"],
|
||||
)
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "builders",
|
||||
srcs = [
|
||||
"bazel.ts",
|
||||
"index.ts",
|
||||
"schema.d.ts",
|
||||
],
|
||||
data = [
|
||||
"schema.json",
|
||||
],
|
||||
deps = [
|
||||
"@ngdeps//@angular-devkit/architect",
|
||||
"@ngdeps//@angular-devkit/core",
|
||||
"@ngdeps//@types/node",
|
||||
"@rxjs",
|
||||
"@rxjs//operators",
|
||||
],
|
||||
)
|
43
packages/bazel/src/builders/bazel.ts
Normal file
43
packages/bazel/src/builders/bazel.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/// <reference types='node'/>
|
||||
import {spawn, spawnSync} from 'child_process';
|
||||
import {Observable, Subject} from 'rxjs';
|
||||
|
||||
export type Executable = 'bazel' | 'ibazel';
|
||||
export type Command = 'build' | 'test' | 'run' | 'coverage' | 'query';
|
||||
|
||||
export function runBazel(
|
||||
projectDir: string, executable: Executable, command: Command, workspaceTarget: string,
|
||||
flags: string[]): Observable<void> {
|
||||
const doneSubject = new Subject<void>();
|
||||
const buildProcess = spawn(executable, [command, workspaceTarget, ...flags], {
|
||||
cwd: projectDir,
|
||||
stdio: 'inherit',
|
||||
shell: false,
|
||||
});
|
||||
|
||||
buildProcess.once('close', (code: number) => {
|
||||
if (code === 0) {
|
||||
doneSubject.next();
|
||||
} else {
|
||||
doneSubject.error(`${executable} failed with code ${code}.`);
|
||||
}
|
||||
});
|
||||
|
||||
return doneSubject.asObservable();
|
||||
}
|
||||
|
||||
export function checkInstallation(executable: Executable, projectDir: string) {
|
||||
const child = spawnSync(executable, ['version'], {
|
||||
cwd: projectDir,
|
||||
shell: false,
|
||||
});
|
||||
return child.status === 0;
|
||||
}
|
9
packages/bazel/src/builders/builders.json
Normal file
9
packages/bazel/src/builders/builders.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"builders": {
|
||||
"build": {
|
||||
"class": "./index#Builder",
|
||||
"schema": "./schema.json",
|
||||
"description": "Executes Bazel on a target."
|
||||
}
|
||||
}
|
||||
}
|
40
packages/bazel/src/builders/index.ts
Normal file
40
packages/bazel/src/builders/index.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @fileoverview Bazel bundle builder
|
||||
*/
|
||||
|
||||
import {BuildEvent, Builder as BuilderInterface, BuilderConfiguration, BuilderContext} from '@angular-devkit/architect';
|
||||
import {getSystemPath, resolve} from '@angular-devkit/core';
|
||||
import {Observable, of } from 'rxjs';
|
||||
import {catchError, map, tap} from 'rxjs/operators';
|
||||
|
||||
import {checkInstallation, runBazel} from './bazel';
|
||||
import {Schema} from './schema';
|
||||
|
||||
export class Builder implements BuilderInterface<Schema> {
|
||||
constructor(private context: BuilderContext) {}
|
||||
|
||||
run(builderConfig: BuilderConfiguration<Partial<Schema>>): Observable<BuildEvent> {
|
||||
const projectRoot = getSystemPath(resolve(this.context.workspace.root, builderConfig.root));
|
||||
const targetLabel = builderConfig.options.targetLabel;
|
||||
|
||||
const executable = builderConfig.options.watch ? 'ibazel' : 'bazel';
|
||||
|
||||
if (!checkInstallation(executable, projectRoot)) {
|
||||
throw new Error(
|
||||
`Could not run ${executable}. Please make sure that the ` +
|
||||
`"${executable}" command is available in the $PATH.`);
|
||||
}
|
||||
|
||||
// TODO: Support passing flags.
|
||||
return runBazel(
|
||||
projectRoot, executable, builderConfig.options.bazelCommand !, targetLabel !,
|
||||
[] /* flags */)
|
||||
.pipe(map(() => ({success: true})), catchError(() => of ({success: false})), );
|
||||
}
|
||||
}
|
24
packages/bazel/src/builders/schema.d.ts
vendored
Normal file
24
packages/bazel/src/builders/schema.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
|
||||
// CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
|
||||
|
||||
// tslint:disable:no-global-tslint-disable
|
||||
// tslint:disable
|
||||
|
||||
/**
|
||||
* Options for Bazel Builder
|
||||
*/
|
||||
export interface Schema {
|
||||
bazelCommand: BazelCommand;
|
||||
/**
|
||||
* Target to be executed under Bazel.
|
||||
*/
|
||||
targetLabel: string;
|
||||
watch?: boolean;
|
||||
}
|
||||
|
||||
export enum BazelCommand {
|
||||
Build = 'build',
|
||||
Run = 'run',
|
||||
Test = 'test',
|
||||
}
|
29
packages/bazel/src/builders/schema.json
Normal file
29
packages/bazel/src/builders/schema.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"title": "Bazel builder schema",
|
||||
"description": "Options for Bazel Builder",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"targetLabel": {
|
||||
"type": "string",
|
||||
"description": "Target to be executed under Bazel."
|
||||
},
|
||||
"bazelCommand": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"run",
|
||||
"build",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
"watch": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"targetLabel",
|
||||
"bazelCommand"
|
||||
]
|
||||
}
|
@ -21,9 +21,6 @@ const NGC_ASSETS = /\.(css|html|ngsummary\.json)$/;
|
||||
|
||||
const BAZEL_BIN = /\b(blaze|bazel)-out\b.*?\bbin\b/;
|
||||
|
||||
// TODO(alexeagle): probably not needed, see
|
||||
// https://github.com/bazelbuild/rules_typescript/issues/28
|
||||
const ALLOW_NON_HERMETIC_READS = true;
|
||||
// Note: We compile the content of node_modules with plain ngc command line.
|
||||
const ALL_DEPS_COMPILED_WITH_BAZEL = false;
|
||||
|
||||
@ -58,7 +55,6 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
|
||||
const compilerOpts = ng.createNgCompilerOptions(basePath, config, tsOptions);
|
||||
const tsHost = ts.createCompilerHost(compilerOpts, true);
|
||||
const {diagnostics} = compile({
|
||||
allowNonHermeticReads: ALLOW_NON_HERMETIC_READS,
|
||||
allDepsCompiledWithBazel: ALL_DEPS_COMPILED_WITH_BAZEL,
|
||||
compilerOpts,
|
||||
tsHost,
|
||||
@ -84,9 +80,8 @@ export function relativeToRootDirs(filePath: string, rootDirs: string[]): string
|
||||
return filePath;
|
||||
}
|
||||
|
||||
export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true, compilerOpts,
|
||||
tsHost, bazelOpts, files, inputs, expectedOuts, gatherDiagnostics}: {
|
||||
allowNonHermeticReads: boolean,
|
||||
export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost, bazelOpts, files,
|
||||
inputs, expectedOuts, gatherDiagnostics}: {
|
||||
allDepsCompiledWithBazel?: boolean,
|
||||
compilerOpts: ng.CompilerOptions,
|
||||
tsHost: ts.CompilerHost, inputs?: {[path: string]: string},
|
||||
@ -104,7 +99,7 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||
}
|
||||
|
||||
if (inputs) {
|
||||
fileLoader = new CachedFileLoader(fileCache, allowNonHermeticReads);
|
||||
fileLoader = new CachedFileLoader(fileCache);
|
||||
// Resolve the inputs to absolute paths to match TypeScript internals
|
||||
const resolvedInputs: {[path: string]: string} = {};
|
||||
const inputKeys = Object.keys(inputs);
|
||||
@ -186,8 +181,7 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||
}
|
||||
|
||||
const bazelHost = new CompilerHost(
|
||||
files, compilerOpts, bazelOpts, tsHost, fileLoader, allowNonHermeticReads,
|
||||
generatedFileModuleResolver);
|
||||
files, compilerOpts, bazelOpts, tsHost, fileLoader, generatedFileModuleResolver);
|
||||
|
||||
// Also need to disable decorator downleveling in the BazelHost in Ivy mode.
|
||||
if (isInIvyMode) {
|
||||
|
@ -5,9 +5,6 @@
|
||||
* 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
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const configPath = 'TMPL_config';
|
||||
@ -32,15 +29,44 @@ function setConf(conf, name, value, msg) {
|
||||
conf[name] = value;
|
||||
}
|
||||
|
||||
function mergeCapabilities(conf, capabilities) {
|
||||
if (conf.capabilities) {
|
||||
if (conf.capabilities.browserName === capabilities.browserName) {
|
||||
// there are capabilities to merge
|
||||
if (capabilities.browserName === 'chrome') {
|
||||
conf.capabilities.chromeOptions = conf.capabilities.chromeOptions || {};
|
||||
conf.capabilities.chromeOptions.binary = capabilities.chromeOptions.binary;
|
||||
conf.capabilities.chromeOptions.args = conf.capabilities.chromeOptions.args || [];
|
||||
conf.capabilities.chromeOptions.args.push(...capabilities.chromeOptions.args);
|
||||
console.warn(
|
||||
`Your protractor configuration specifies capabilities for browser '${conf.capabilities.browserName}'
|
||||
which will be merged with capabilities provided by Bazel resulting in:`,
|
||||
JSON.stringify(conf.capabilities, null, 2));
|
||||
} else {
|
||||
// TODO(gmagolan): implement firefox support for protractor
|
||||
throw new Error(
|
||||
`Unexpected browserName ${capabilities.browserName} for capabilities merging`);
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
`Your protractor configuration specifies capabilities for browser '${conf.capabilities.browserName}' which will be overwritten by Bazel`);
|
||||
conf.capabilities = capabilities;
|
||||
}
|
||||
} else {
|
||||
conf.capabilities = capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
let conf = {};
|
||||
|
||||
// Import the user's base protractor configuration if specified
|
||||
if (configPath) {
|
||||
const baseConf = require(configPath);
|
||||
if (!baseConf.config) {
|
||||
throw new Error('Invalid base protractor configration. Expected config to be exported.');
|
||||
throw new Error('Invalid base protractor configuration. Expected config to be exported.');
|
||||
}
|
||||
conf = baseConf.config;
|
||||
if (DEBUG) console.info(`Base protractor configuration: ${JSON.stringify(conf, null, 2)}`);
|
||||
}
|
||||
|
||||
// Import the user's on prepare function if specified
|
||||
@ -109,8 +135,8 @@ if (process.env['WEB_TEST_METADATA']) {
|
||||
}
|
||||
setConf(conf, 'directConnect', true, 'is set to true for chrome');
|
||||
setConf(conf, 'chromeDriver', chromeDriver, 'is determined by the browsers attribute');
|
||||
setConf(
|
||||
conf, 'capabilities', {
|
||||
mergeCapabilities(
|
||||
conf, {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: chromeBin,
|
||||
@ -131,7 +157,7 @@ if (process.env['WEB_TEST_METADATA']) {
|
||||
// }
|
||||
// setConf(conf, 'seleniumAddress', process.env.WEB_TEST_HTTP_SERVER.trim() + "/wd/hub", 'is
|
||||
// configured by Bazel for firefox browser')
|
||||
// setConf(conf, 'capabilities', {
|
||||
// mergeCapabilities(conf, {
|
||||
// browserName: "firefox",
|
||||
// 'moz:firefoxOptions': {
|
||||
// binary: firefoxBin,
|
||||
|
20
packages/bazel/src/schematics/BUILD.bazel
Normal file
20
packages/bazel/src/schematics/BUILD.bazel
Normal file
@ -0,0 +1,20 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "package_assets",
|
||||
srcs = [
|
||||
"collection.json",
|
||||
],
|
||||
visibility = ["//packages/bazel:__subpackages__"],
|
||||
)
|
||||
|
||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||
deps = [
|
||||
"//packages/bazel/src/schematics/bazel-workspace:test",
|
||||
"//tools/testing:node",
|
||||
],
|
||||
)
|
32
packages/bazel/src/schematics/README.md
Normal file
32
packages/bazel/src/schematics/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Schematics for Bazel
|
||||
|
||||
## Development notes
|
||||
|
||||
To test any local changes, run
|
||||
|
||||
```shell
|
||||
bazel build //packages/bazel:npm_package
|
||||
```
|
||||
|
||||
then `cd` to the npm package in the `dist` folder and run `yarn link`.
|
||||
Next run `yarn link` again in the directory where the `ng` command is invoked.
|
||||
Make sure the `ng` command is local, and not the global installation.
|
||||
|
||||
## Generate .d.ts file from JSON schema
|
||||
|
||||
The script to generate `.d.ts` file is located in the
|
||||
[Angular CLI](https://github.com/angular/angular-cli) repo. Make sure
|
||||
the CLI repository is checked out on your local machine.
|
||||
|
||||
Then, in the CLI repository, run the following command
|
||||
|
||||
```shell
|
||||
bazel run //tools:quicktype_runner -- \
|
||||
~/Documents/GitHub/angular/packages/bazel/src/schematics/ng-new/schema.json \
|
||||
~/Documents/GitHub/angular/packages/bazel/src/schematics/ng-new/schema.d.ts
|
||||
```
|
||||
|
||||
## TODOs
|
||||
|
||||
1. Make the `ts_json_schema` rule re-usable and portable.
|
||||
2. Add comments in BUILD files. See discussion [here](https://github.com/angular/angular/pull/26971#discussion_r231325683).
|
34
packages/bazel/src/schematics/bazel-workspace/BUILD.bazel
Normal file
34
packages/bazel/src/schematics/bazel-workspace/BUILD.bazel
Normal file
@ -0,0 +1,34 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
ts_library(
|
||||
name = "bazel-workspace",
|
||||
srcs = [
|
||||
"index.ts",
|
||||
"schema.d.ts",
|
||||
],
|
||||
data = glob(["files/**/*"]) + [
|
||||
"schema.json",
|
||||
],
|
||||
deps = [
|
||||
"@ngdeps//@angular-devkit/core",
|
||||
"@ngdeps//@angular-devkit/schematics",
|
||||
"@ngdeps//@schematics/angular",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "test",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"index_spec.ts",
|
||||
],
|
||||
data = [
|
||||
"//packages/bazel/src/schematics:package_assets",
|
||||
],
|
||||
deps = [
|
||||
":bazel-workspace",
|
||||
"@ngdeps//@angular-devkit/schematics",
|
||||
],
|
||||
)
|
@ -0,0 +1,7 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# This export allows targets in other packages to reference files that live
|
||||
# in this package.
|
||||
exports_files([
|
||||
"tsconfig.json",
|
||||
])
|
@ -0,0 +1,76 @@
|
||||
# WARNING: This file is generated and it's not meant to be edited.
|
||||
# Before making any changes, please read Bazel documentation.
|
||||
# https://docs.bazel.build/versions/master/be/workspace.html
|
||||
# The WORKSPACE file tells Bazel that this directory is a "workspace", which is like a project root.
|
||||
# The content of this file specifies all the external dependencies Bazel needs to perform a build.
|
||||
|
||||
####################################
|
||||
# ESModule imports (and TypeScript imports) can be absolute starting with the workspace name.
|
||||
# The name of the workspace should match the npm package where we publish, so that these
|
||||
# imports also make sense when referencing the published package.
|
||||
workspace(name = "<%= utils.underscore(name) %>")
|
||||
|
||||
# The @angular repo contains rule for building Angular applications
|
||||
# Provides "build_bazel_rules_typescript"
|
||||
ANGULAR_VERSION = "<%= ANGULAR_VERSION %>"
|
||||
http_archive(
|
||||
name = "angular",
|
||||
url = "https://github.com/angular/angular/archive/%s.zip" % ANGULAR_VERSION,
|
||||
strip_prefix = "angular-%s" % ANGULAR_VERSION,
|
||||
)
|
||||
|
||||
# RxJS
|
||||
RXJS_VERSION = "<%= RXJS_VERSION %>"
|
||||
http_archive(
|
||||
name = "rxjs",
|
||||
url = "https://registry.yarnpkg.com/rxjs/-/rxjs-%s.tgz" % RXJS_VERSION,
|
||||
strip_prefix = "package/src",
|
||||
)
|
||||
|
||||
# Rules for compiling sass
|
||||
RULES_SASS_VERSION = "<%= RULES_SASS_VERSION %>"
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
url = "https://github.com/bazelbuild/rules_sass/archive/%s.zip" % RULES_SASS_VERSION,
|
||||
strip_prefix = "rules_sass-%s" % RULES_SASS_VERSION,
|
||||
)
|
||||
|
||||
####################################
|
||||
# Load and install our dependencies downloaded above.
|
||||
|
||||
load("@angular//packages/bazel:package.bzl", "rules_angular_dependencies")
|
||||
rules_angular_dependencies()
|
||||
|
||||
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
|
||||
rules_typescript_dependencies()
|
||||
# build_bazel_rules_nodejs is loaded transitively through rules_typescript_dependencies.
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||
# 0.18.0 is needed for .bazelignore
|
||||
check_bazel_version("0.18.0")
|
||||
node_repositories()
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
package_json = "//:package.json",
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||
go_rules_dependencies()
|
||||
go_register_toolchains()
|
||||
|
||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
|
||||
web_test_repositories()
|
||||
browser_repositories(
|
||||
chromium = True,
|
||||
firefox = True,
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace", "check_rules_typescript_version")
|
||||
ts_setup_workspace()
|
||||
|
||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||
sass_repositories()
|
||||
|
||||
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||
ng_setup_workspace()
|
@ -0,0 +1,2 @@
|
||||
dist
|
||||
node_modules
|
@ -0,0 +1,19 @@
|
||||
# Make TypeScript and Angular compilation fast, by keeping a few copies of the
|
||||
# compiler running as daemons, and cache SourceFile AST's to reduce parse time.
|
||||
build --strategy=TypeScriptCompile=worker
|
||||
build --strategy=AngularTemplateCompile=worker
|
||||
|
||||
# Don't create bazel-* symlinks in the WORKSPACE directory, except `bazel-out`,
|
||||
# which is mandatory.
|
||||
# These require .gitignore and may scare users.
|
||||
# Also, it's a workaround for https://github.com/bazelbuild/rules_typescript/issues/12
|
||||
# which affects the common case of having `tsconfig.json` in the WORKSPACE directory.
|
||||
#
|
||||
# Instead, the output will appear in `dist/bin`. You'll need to ignore the
|
||||
# `bazel-out` directory that is created in the workspace root.
|
||||
build --symlink_prefix=dist/
|
||||
|
||||
test --test_output=errors
|
||||
|
||||
# Use the Angular 6 compiler
|
||||
build --define=compile=legacy
|
@ -0,0 +1,46 @@
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
||||
load("@angular//:index.bzl", "protractor_web_test_suite")
|
||||
|
||||
ts_library(
|
||||
name = "e2e_lib",
|
||||
testonly = 1,
|
||||
srcs = glob(["src/**/*.ts"]),
|
||||
tsconfig = ":tsconfig.e2e.json",
|
||||
deps = [
|
||||
"@npm//@types/jasmine",
|
||||
"@npm//@types/jasminewd2",
|
||||
"@npm//@types/node",
|
||||
"@npm//jasmine",
|
||||
"@npm//protractor",
|
||||
]
|
||||
)
|
||||
|
||||
protractor_web_test_suite(
|
||||
name = "prodserver_test",
|
||||
data = [
|
||||
"@angular//packages/bazel/src/protractor/utils",
|
||||
"@npm//protractor",
|
||||
],
|
||||
on_prepare = ":protractor.on-prepare.js",
|
||||
server = "//src:prodserver",
|
||||
deps = [":e2e_lib"],
|
||||
)
|
||||
|
||||
protractor_web_test_suite(
|
||||
name = "devserver_test",
|
||||
data = [
|
||||
"@angular//packages/bazel/src/protractor/utils",
|
||||
"@npm//protractor",
|
||||
],
|
||||
on_prepare = ":protractor.on-prepare.js",
|
||||
server = "//src:devserver",
|
||||
deps = [":e2e_lib"],
|
||||
)
|
||||
|
||||
# Default target in this package is to run the e2e tests on the devserver.
|
||||
# This is a faster round-trip but doesn't exercise production optimizations like
|
||||
# code-splitting and lazy loading.
|
||||
alias(
|
||||
name = "e2e",
|
||||
actual = "devserver_test",
|
||||
)
|
@ -0,0 +1,23 @@
|
||||
// The function exported from this file is used by the protractor_web_test_suite.
|
||||
// It is passed to the `onPrepare` configuration setting in protractor and executed
|
||||
// before running tests.
|
||||
//
|
||||
// If the function returns a promise, as it does here, protractor will wait
|
||||
// for the promise to resolve before running tests.
|
||||
|
||||
const protractorUtils = require('@angular/bazel/protractor-utils');
|
||||
const protractor = require('protractor');
|
||||
|
||||
module.exports = function(config) {
|
||||
// In this example, `@angular/bazel/protractor-utils` is used to run
|
||||
// the server. protractorUtils.runServer() runs the server on a randomly
|
||||
// selected port (given a port flag to pass to the server as an argument).
|
||||
// The port used is returned in serverSpec and the protractor serverUrl
|
||||
// is the configured.
|
||||
const portFlag = config.server.endsWith('prodserver') ? '-p' : '-port';
|
||||
return protractorUtils.runServer(config.workspace, config.server, portFlag, [])
|
||||
.then(serverSpec => {
|
||||
const serverUrl = `http://localhost:${serverSpec.port}`;
|
||||
protractor.browser.baseUrl = serverUrl;
|
||||
});
|
||||
};
|
@ -0,0 +1,90 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@angular//:index.bzl", "ng_module")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library", "ts_web_test_suite")
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "rollup_bundle", "history_server")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver")
|
||||
|
||||
ng_module(
|
||||
name = "src",
|
||||
srcs = glob(["**/*.ts"], exclude = ["**/*.spec.ts", "test.ts"]),
|
||||
assets = glob([
|
||||
"**/*.css",
|
||||
"**/*.html",
|
||||
]),
|
||||
deps = [
|
||||
"@angular//packages/core",
|
||||
"@angular//packages/platform-browser",
|
||||
"@npm//@types",
|
||||
],
|
||||
)
|
||||
|
||||
rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = "src/main",
|
||||
deps = ["//src"],
|
||||
)
|
||||
|
||||
# Needed because the prodserver only loads static files that appear under this
|
||||
# package.
|
||||
genrule(
|
||||
name = "zonejs",
|
||||
srcs = ["@npm//node_modules/zone.js:dist/zone.min.js"],
|
||||
outs = ["zone.min.js"],
|
||||
cmd = "cp $< $@",
|
||||
)
|
||||
|
||||
history_server(
|
||||
name = "prodserver",
|
||||
data = [
|
||||
"index.html",
|
||||
":bundle",
|
||||
":zonejs",
|
||||
],
|
||||
)
|
||||
|
||||
ts_devserver(
|
||||
name = "devserver",
|
||||
port = 4200,
|
||||
additional_root_paths = [
|
||||
"npm/node_modules/zone.js/dist",
|
||||
"npm/node_modules/tslib",
|
||||
],
|
||||
entry_module = "<%= name %>/src/main",
|
||||
serving_path = "/bundle.min.js",
|
||||
static_files = [
|
||||
"@npm//node_modules/zone.js:dist/zone.min.js",
|
||||
"@npm//node_modules/tslib:tslib.js",
|
||||
"index.html",
|
||||
],
|
||||
deps = [":src"],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "test_lib",
|
||||
testonly = 1,
|
||||
srcs = glob(["**/*.spec.ts"]),
|
||||
deps = [
|
||||
":src",
|
||||
"@angular//packages/core/testing",
|
||||
"@angular//packages/platform-browser-dynamic/testing",
|
||||
"@npm//@types",
|
||||
],
|
||||
)
|
||||
|
||||
ts_web_test_suite(
|
||||
name = "test",
|
||||
srcs = ["@npm//node_modules/tslib:tslib.js"],
|
||||
# do not sort
|
||||
bootstrap = [
|
||||
"@npm//node_modules/zone.js:dist/zone-testing-bundle.js",
|
||||
"@npm//node_modules/reflect-metadata:Reflect.js",
|
||||
],
|
||||
browsers = [
|
||||
"@io_bazel_rules_webtesting//browsers:chromium-local",
|
||||
],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"@npm//karma-jasmine",
|
||||
],
|
||||
)
|
47
packages/bazel/src/schematics/bazel-workspace/index.ts
Normal file
47
packages/bazel/src/schematics/bazel-workspace/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @fileoverview Schematics for bazel-workspace
|
||||
*/
|
||||
|
||||
import {strings} from '@angular-devkit/core';
|
||||
import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, mergeWith, move, url} from '@angular-devkit/schematics';
|
||||
import {getWorkspace} from '@schematics/angular/utility/config';
|
||||
import {validateProjectName} from '@schematics/angular/utility/validation';
|
||||
|
||||
import {Schema as BazelWorkspaceOptions} from './schema';
|
||||
|
||||
export default function(options: BazelWorkspaceOptions): Rule {
|
||||
return (host: Tree, context: SchematicContext) => {
|
||||
if (!options.name) {
|
||||
throw new SchematicsException(`Invalid options, "name" is required.`);
|
||||
}
|
||||
validateProjectName(options.name);
|
||||
let newProjectRoot = '';
|
||||
try {
|
||||
const workspace = getWorkspace(host);
|
||||
newProjectRoot = workspace.newProjectRoot || '';
|
||||
} catch {
|
||||
}
|
||||
const appDir = `${newProjectRoot}/${options.name}`;
|
||||
|
||||
const workspaceVersions = {
|
||||
'ANGULAR_VERSION': '7.0.2',
|
||||
'RULES_SASS_VERSION': '1.14.1',
|
||||
'RXJS_VERSION': '6.3.3',
|
||||
};
|
||||
|
||||
return mergeWith(apply(url('./files'), [
|
||||
applyTemplates({
|
||||
utils: strings,
|
||||
...options,
|
||||
'dot': '.', ...workspaceVersions,
|
||||
}),
|
||||
move(appDir),
|
||||
]));
|
||||
};
|
||||
}
|
47
packages/bazel/src/schematics/bazel-workspace/index_spec.ts
Normal file
47
packages/bazel/src/schematics/bazel-workspace/index_spec.ts
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @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 {SchematicTestRunner} from '@angular-devkit/schematics/testing';
|
||||
|
||||
describe('Bazel-workspace Schematic', () => {
|
||||
const schematicRunner =
|
||||
new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'), );
|
||||
const defaultOptions = {
|
||||
name: 'demo',
|
||||
};
|
||||
|
||||
it('should generate Bazel workspace files', () => {
|
||||
const options = {...defaultOptions};
|
||||
const host = schematicRunner.runSchematic('bazel-workspace', options);
|
||||
const files = host.files;
|
||||
expect(files).toContain('/demo/.bazelignore');
|
||||
expect(files).toContain('/demo/.bazelrc');
|
||||
expect(files).toContain('/demo/BUILD.bazel');
|
||||
expect(files).toContain('/demo/src/BUILD.bazel');
|
||||
expect(files).toContain('/demo/WORKSPACE');
|
||||
expect(files).toContain('/demo/yarn.lock');
|
||||
});
|
||||
|
||||
describe('WORKSPACE', () => {
|
||||
it('should contain project name', () => {
|
||||
const options = {...defaultOptions};
|
||||
const host = schematicRunner.runSchematic('bazel-workspace', options);
|
||||
expect(host.files).toContain('/demo/WORKSPACE');
|
||||
const content = host.readContent('/demo/WORKSPACE');
|
||||
expect(content).toContain('workspace(name = "demo")');
|
||||
});
|
||||
|
||||
it('should convert dashes in name to underscore', () => {
|
||||
const options = {...defaultOptions, name: 'demo-project'};
|
||||
const host = schematicRunner.runSchematic('bazel-workspace', options);
|
||||
expect(host.files).toContain('/demo-project/WORKSPACE');
|
||||
const content = host.readContent('/demo-project/WORKSPACE');
|
||||
expect(content).toContain('workspace(name = "demo_project"');
|
||||
});
|
||||
});
|
||||
});
|
13
packages/bazel/src/schematics/bazel-workspace/schema.d.ts
vendored
Normal file
13
packages/bazel/src/schematics/bazel-workspace/schema.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE
|
||||
// THE CORRESPONDING JSON SCHEMA FILE. See README.md.
|
||||
|
||||
// tslint:disable:no-global-tslint-disable
|
||||
// tslint:disable
|
||||
|
||||
export interface Schema {
|
||||
/**
|
||||
* The name of the project.
|
||||
*/
|
||||
name: string;
|
||||
}
|
20
packages/bazel/src/schematics/bazel-workspace/schema.json
Normal file
20
packages/bazel/src/schematics/bazel-workspace/schema.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"id": "SchematicsAngularBazelWorkspace",
|
||||
"title": "Angular Bazel Workspace Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the project.",
|
||||
"type": "string",
|
||||
"format": "html-selector",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
12
packages/bazel/src/schematics/collection.json
Normal file
12
packages/bazel/src/schematics/collection.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@angular/bazel",
|
||||
"version": "0.1",
|
||||
"schematics": {
|
||||
"bazel-workspace": {
|
||||
"factory": "./bazel-workspace",
|
||||
"schema": "./bazel-workspace/schema.json",
|
||||
"description": "Setup Bazel workspace",
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ 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': '../angular/external/ngdeps/node_modules',
|
||||
'nodeModulesPrefix': '../ngdeps/node_modules',
|
||||
},
|
||||
'files': options.files,
|
||||
'angularCompilerOptions': {
|
||||
|
@ -19,9 +19,6 @@ ts_library(
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"//tools/testing:node",
|
||||
@ -30,9 +27,6 @@ jasmine_node_test(
|
||||
|
||||
ts_web_test_suite(
|
||||
name = "test_web",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
],
|
||||
deps = [
|
||||
":test_lib",
|
||||
],
|
||||
|
@ -6,13 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpClient} from '@angular/common/http/src/client';
|
||||
import {HttpErrorResponse, HttpEventType, HttpResponse} from '@angular/common/http/src/response';
|
||||
import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend';
|
||||
import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
import {HttpClient} from '../src/client';
|
||||
import {HttpErrorResponse, HttpEventType, HttpResponse} from '../src/response';
|
||||
import {HttpClientTestingBackend} from '../testing/src/backend';
|
||||
|
||||
{
|
||||
describe('HttpClient', () => {
|
||||
let client: HttpClient = null !;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHeaders} from '../src/headers';
|
||||
import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
|
||||
{
|
||||
describe('HttpHeaders', () => {
|
||||
|
@ -6,13 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {JSONP_ERR_NO_CALLBACK, JSONP_ERR_WRONG_METHOD, JSONP_ERR_WRONG_RESPONSE_TYPE, JsonpClientBackend} from '@angular/common/http/src/jsonp';
|
||||
import {HttpRequest} from '@angular/common/http/src/request';
|
||||
import {HttpErrorResponse, HttpEventType} from '@angular/common/http/src/response';
|
||||
import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
import {JSONP_ERR_NO_CALLBACK, JSONP_ERR_WRONG_METHOD, JSONP_ERR_WRONG_RESPONSE_TYPE, JsonpClientBackend} from '../src/jsonp';
|
||||
import {HttpRequest} from '../src/request';
|
||||
import {HttpErrorResponse, HttpEventType} from '../src/response';
|
||||
|
||||
import {MockDocument} from './jsonp_mock';
|
||||
|
||||
function runOnlyCallback(home: any, data: Object) {
|
||||
|
@ -6,20 +6,19 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHandler} from '@angular/common/http/src/backend';
|
||||
import {HttpClient} from '@angular/common/http/src/client';
|
||||
import {HTTP_INTERCEPTORS, HttpInterceptor} from '@angular/common/http/src/interceptor';
|
||||
import {HttpRequest} from '@angular/common/http/src/request';
|
||||
import {HttpEvent, HttpResponse} from '@angular/common/http/src/response';
|
||||
import {HttpTestingController} from '@angular/common/http/testing/src/api';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing/src/module';
|
||||
import {TestRequest} from '@angular/common/http/testing/src/request';
|
||||
import {Injectable, Injector} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
import {HttpHandler} from '../src/backend';
|
||||
import {HttpClient} from '../src/client';
|
||||
import {HTTP_INTERCEPTORS, HttpInterceptor} from '../src/interceptor';
|
||||
import {HttpRequest} from '../src/request';
|
||||
import {HttpEvent, HttpResponse} from '../src/response';
|
||||
import {HttpTestingController} from '../testing/src/api';
|
||||
import {HttpClientTestingModule} from '../testing/src/module';
|
||||
import {TestRequest} from '../testing/src/request';
|
||||
|
||||
class TestInterceptor implements HttpInterceptor {
|
||||
constructor(private value: string) {}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpParams} from '../src/params';
|
||||
import {HttpParams} from '@angular/common/http/src/params';
|
||||
|
||||
{
|
||||
describe('HttpUrlEncodedParams', () => {
|
||||
|
@ -6,12 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
import {HttpParams} from '@angular/common/http/src/params';
|
||||
import {HttpRequest} from '@angular/common/http/src/request';
|
||||
import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {HttpHeaders} from '../src/headers';
|
||||
import {HttpParams} from '../src/params';
|
||||
import {HttpRequest} from '../src/request';
|
||||
|
||||
const TEST_URL = 'http://angular.io';
|
||||
const TEST_STRING = `I'm a body!`;
|
||||
|
||||
@ -161,4 +160,4 @@ const TEST_STRING = `I'm a body!`;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
import {HttpResponse} from '@angular/common/http/src/response';
|
||||
import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {HttpHeaders} from '../src/headers';
|
||||
import {HttpResponse} from '../src/response';
|
||||
|
||||
{
|
||||
describe('HttpResponse', () => {
|
||||
describe('constructor()', () => {
|
||||
@ -79,4 +78,4 @@ import {HttpResponse} from '../src/response';
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHeaders} from '../src/headers';
|
||||
import {XhrFactory} from '../src/xhr';
|
||||
import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
import {XhrFactory} from '@angular/common/http/src/xhr';
|
||||
|
||||
export class MockXhrFactory implements XhrFactory {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ -119,4 +119,4 @@ export class MockXMLHttpRequest {
|
||||
}
|
||||
|
||||
abort() { this.mockAborted = true; }
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpRequest} from '@angular/common/http/src/request';
|
||||
import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpUploadProgressEvent} from '@angular/common/http/src/response';
|
||||
import {HttpXhrBackend} from '@angular/common/http/src/xhr';
|
||||
import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {Observable} from 'rxjs';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
import {HttpRequest} from '../src/request';
|
||||
import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpUploadProgressEvent} from '../src/response';
|
||||
import {HttpXhrBackend} from '../src/xhr';
|
||||
|
||||
import {MockXhrFactory} from './xhr_mock';
|
||||
|
||||
function trackEvents(obs: Observable<HttpEvent<any>>): HttpEvent<any>[] {
|
||||
|
@ -6,11 +6,11 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HttpHeaders} from '../src/headers';
|
||||
import {HttpRequest} from '../src/request';
|
||||
import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor} from '../src/xsrf';
|
||||
import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
import {HttpRequest} from '@angular/common/http/src/request';
|
||||
import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor} from '@angular/common/http/src/xsrf';
|
||||
|
||||
import {HttpClientTestingBackend} from '../testing/src/backend';
|
||||
import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend';
|
||||
|
||||
class SampleTokenExtractor extends HttpXsrfTokenExtractor {
|
||||
constructor(private token: string|null) { super(); }
|
||||
@ -88,4 +88,4 @@ class SampleTokenExtractor extends HttpXsrfTokenExtractor {
|
||||
|
||||
function getParseCount(extractor: HttpXsrfCookieExtractor): number {
|
||||
return (extractor as any).parseCount;
|
||||
}
|
||||
}
|
||||
|
@ -15,15 +15,13 @@ ts_library(
|
||||
"//packages/platform-browser",
|
||||
"//packages/platform-browser-dynamic",
|
||||
"//packages/platform-browser/testing",
|
||||
"//packages/private/testing",
|
||||
],
|
||||
)
|
||||
|
||||
jasmine_node_test(
|
||||
name = "test",
|
||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
],
|
||||
deps = [
|
||||
":test_lib",
|
||||
"//tools/testing:node",
|
||||
@ -32,9 +30,6 @@ jasmine_node_test(
|
||||
|
||||
ts_web_test_suite(
|
||||
name = "test_web",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
],
|
||||
deps = [
|
||||
":test_lib",
|
||||
],
|
||||
|
@ -7,20 +7,17 @@
|
||||
*/
|
||||
|
||||
|
||||
import {DeprecatedI18NPipesModule, Plural} from '@angular/common';
|
||||
import {DEPRECATED_PLURAL_FN, getPluralCase} from '@angular/common/src/i18n/localization';
|
||||
import {TestBed, inject} from '@angular/core/testing';
|
||||
import {DeprecatedI18NPipesModule} from '../src/common_module';
|
||||
import {Plural} from '../src/i18n/locale_data_api';
|
||||
import {DEPRECATED_PLURAL_FN, getPluralCase} from '../src/i18n/localization';
|
||||
|
||||
{
|
||||
describe('DeprecatedI18NPipesModule', () => {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [DeprecatedI18NPipesModule]}); });
|
||||
describe('DeprecatedI18NPipesModule', () => {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [DeprecatedI18NPipesModule]}); });
|
||||
|
||||
it('should define the token DEPRECATED_PLURAL_FN',
|
||||
inject(
|
||||
[DEPRECATED_PLURAL_FN],
|
||||
(injectedGetPluralCase?: (locale: string, value: number | string) => Plural) => {
|
||||
expect(injectedGetPluralCase).toEqual(getPluralCase);
|
||||
}));
|
||||
});
|
||||
}
|
||||
it('should define the token DEPRECATED_PLURAL_FN',
|
||||
inject(
|
||||
[DEPRECATED_PLURAL_FN],
|
||||
(injectedGetPluralCase?: (locale: string, value: number | string) => Plural) => {
|
||||
expect(injectedGetPluralCase).toEqual(getPluralCase);
|
||||
}));
|
||||
});
|
||||
|
@ -8,206 +8,209 @@
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet';
|
||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, StaticProvider, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed, async, fakeAsync} from '@angular/core/testing';
|
||||
import {Compiler, Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, Optional, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
|
||||
{
|
||||
describe('insert/remove', () => {
|
||||
describe('insert/remove', () => {
|
||||
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
|
||||
|
||||
it('should do nothing if component is null', async(() => {
|
||||
const template = `<ng-template *ngComponentOutlet="currentComponent"></ng-template>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should do nothing if component is null', async(() => {
|
||||
const template = `<ng-template *ngComponentOutlet="currentComponent"></ng-template>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.componentInstance.currentComponent = null;
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.currentComponent = null;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
|
||||
it('should insert content specified by a component', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should insert content specified by a component', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
}));
|
||||
|
||||
it('should emit a ComponentRef once a component was created', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should emit a ComponentRef once a component was created', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
expect(fixture.componentInstance.cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(fixture.componentInstance.cmpRef !.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
expect(fixture.componentInstance.cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(fixture.componentInstance.cmpRef !.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
}));
|
||||
|
||||
|
||||
it('should clear view if component becomes null', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should clear view if component becomes null', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentComponent = null;
|
||||
fixture.componentInstance.currentComponent = null;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
|
||||
|
||||
it('should swap content if component changes', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should swap content if component changes', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentComponent = InjectedComponentAgain;
|
||||
fixture.componentInstance.currentComponent = InjectedComponentAgain;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('bar');
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('bar');
|
||||
}));
|
||||
|
||||
it('should use the injector, if one supplied', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
fixmeIvy('FW-642: ASSERTION ERROR: Slot should have been initialized to NO_CHANGE') &&
|
||||
it('should use the injector, if one supplied', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
const uniqueValue = {};
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.injector = Injector.create(
|
||||
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
|
||||
const uniqueValue = {};
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.injector = Injector.create(
|
||||
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
|
||||
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBe(uniqueValue);
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBe(uniqueValue);
|
||||
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should resolve a with injector', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
it('should resolve a with injector', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBeNull();
|
||||
}));
|
||||
fixture.componentInstance.cmpRef = null;
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef !;
|
||||
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
|
||||
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
|
||||
expect(cmpRef.instance.testToken).toBeNull();
|
||||
}));
|
||||
|
||||
it('should render projectable nodes, if supplied', async(() => {
|
||||
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||
it('should render projectable nodes, if supplied', async(() => {
|
||||
const template = `<ng-template>projected foo</ng-template>${TEST_CMP_TEMPLATE}`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||
|
||||
TestBed
|
||||
.overrideComponent(InjectedComponent, {set: {template: `<ng-content></ng-content>`}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||
TestBed.overrideComponent(InjectedComponent, {set: {template: `<ng-content></ng-content>`}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
||||
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.projectables =
|
||||
[fixture.componentInstance.vcRef
|
||||
.createEmbeddedView(fixture.componentInstance.tplRefs.first)
|
||||
.rootNodes];
|
||||
fixture.componentInstance.currentComponent = InjectedComponent;
|
||||
fixture.componentInstance.projectables =
|
||||
[fixture.componentInstance.vcRef
|
||||
.createEmbeddedView(fixture.componentInstance.tplRefs.first)
|
||||
.rootNodes];
|
||||
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('projected foo');
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('projected foo');
|
||||
}));
|
||||
|
||||
it('should resolve components from other modules, if supplied', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should resolve components from other modules, if supplied', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
let fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
}));
|
||||
|
||||
it('should clean up moduleRef, if supplied', async(() => {
|
||||
let destroyed = false;
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should clean up moduleRef, if supplied', async(() => {
|
||||
let destroyed = false;
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'] !;
|
||||
spyOn(moduleRef, 'destroy').and.callThrough();
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'] !;
|
||||
spyOn(moduleRef, 'destroy').and.callThrough();
|
||||
|
||||
expect(moduleRef.destroy).not.toHaveBeenCalled();
|
||||
fixture.destroy();
|
||||
expect(moduleRef.destroy).toHaveBeenCalled();
|
||||
}));
|
||||
expect(moduleRef.destroy).not.toHaveBeenCalled();
|
||||
fixture.destroy();
|
||||
expect(moduleRef.destroy).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'];
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'];
|
||||
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent2;
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent2;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('baz2');
|
||||
expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']);
|
||||
}));
|
||||
expect(fixture.nativeElement).toHaveText('baz2');
|
||||
expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']);
|
||||
}));
|
||||
|
||||
it('should re-create moduleRef when changed', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should re-create moduleRef when changed', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule3);
|
||||
fixture.componentInstance.currentComponent = Module3InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule3);
|
||||
fixture.componentInstance.currentComponent = Module3InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('bat');
|
||||
}));
|
||||
});
|
||||
}
|
||||
expect(fixture.nativeElement).toHaveText('bat');
|
||||
}));
|
||||
});
|
||||
|
||||
const TEST_TOKEN = new InjectionToken('TestToken');
|
||||
@Component({selector: 'injected-component', template: 'foo'})
|
||||
|
@ -11,204 +11,200 @@ import {Component, ContentChildren, Directive, Injectable, NO_ERRORS_SCHEMA, OnD
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
{
|
||||
describe('NgTemplateOutlet', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
describe('NgTemplateOutlet', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
function setTplRef(value: any): void { fixture.componentInstance.currentTplRef = value; }
|
||||
function setTplRef(value: any): void { fixture.componentInstance.currentTplRef = value; }
|
||||
|
||||
function detectChangesAndExpectText(text: string): void {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText(text);
|
||||
}
|
||||
function detectChangesAndExpectText(text: string): void {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText(text);
|
||||
}
|
||||
|
||||
afterEach(() => { fixture = null as any; });
|
||||
afterEach(() => { fixture = null as any; });
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt],
|
||||
imports: [CommonModule],
|
||||
providers: [DestroyedSpyService]
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/angular/angular/issues/14778
|
||||
it('should accept the component as the context', async(() => {
|
||||
const template = `<ng-container *ngTemplateOutlet="tpl; context: this"></ng-container>` +
|
||||
`<ng-template #tpl>{{context.foo}}</ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('bar');
|
||||
}));
|
||||
|
||||
it('should do nothing if templateRef is `null`', async(() => {
|
||||
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef', async(() => {
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes `null`', async(() => {
|
||||
const template = `<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
const refs = fixture.debugElement.children[0].references !['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
|
||||
setTplRef(null);
|
||||
detectChangesAndExpectText('');
|
||||
}));
|
||||
|
||||
it('should swap content if TemplateRef changes', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template><ng-template>bar</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
const refs = fixture.debugElement.children[0].references !['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
|
||||
setTplRef(refs.tplRefs.last);
|
||||
detectChangesAndExpectText('bar');
|
||||
}));
|
||||
|
||||
it('should display template if context is `null`', async(() => {
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should reflect initial context and changes', async(() => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
detectChangesAndExpectText('bar');
|
||||
|
||||
fixture.componentInstance.context.foo = 'alter-bar';
|
||||
detectChangesAndExpectText('alter-bar');
|
||||
}));
|
||||
|
||||
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||
const template = `<ng-template let-ctx #tpl>{{ctx.foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||
detectChangesAndExpectText('bra');
|
||||
}));
|
||||
|
||||
it('should reflect context re-binding', async(() => {
|
||||
const template =
|
||||
`<ng-template let-shawshank="shawshank" #tpl>{{shawshank}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||
detectChangesAndExpectText('brooks');
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||
detectChangesAndExpectText('was here');
|
||||
}));
|
||||
|
||||
it('should update but not destroy embedded view when context values change', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should recreate embedded view when context shape changes', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="context"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.context = {foo: 'baz', other: true};
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should destroy embedded view when context value changes and templateRef becomes undefined',
|
||||
() => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when context changes but template stays the same',
|
||||
() => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when template changes', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl1>{{foo}}</ng-template>` +
|
||||
`<ng-template let-foo="foo" #tpl2>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl1 : tpl2" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update context on undefined view', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? null : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [TestComponent, CaptureTplRefs, DestroyableCmpt],
|
||||
imports: [CommonModule],
|
||||
providers: [DestroyedSpyService]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/angular/angular/issues/14778
|
||||
it('should accept the component as the context', async(() => {
|
||||
const template = `<ng-container *ngTemplateOutlet="tpl; context: this"></ng-container>` +
|
||||
`<ng-template #tpl>{{context.foo}}</ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('bar');
|
||||
}));
|
||||
|
||||
it('should do nothing if templateRef is `null`', async(() => {
|
||||
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef', async(() => {
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes `null`', async(() => {
|
||||
const template = `<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
const refs = fixture.debugElement.children[0].references !['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
|
||||
setTplRef(null);
|
||||
detectChangesAndExpectText('');
|
||||
}));
|
||||
|
||||
it('should swap content if TemplateRef changes', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><ng-template>foo</ng-template><ng-template>bar</ng-template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
const refs = fixture.debugElement.children[0].references !['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
|
||||
setTplRef(refs.tplRefs.last);
|
||||
detectChangesAndExpectText('bar');
|
||||
}));
|
||||
|
||||
it('should display template if context is `null`', async(() => {
|
||||
const template = `<ng-template #tpl>foo</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should reflect initial context and changes', async(() => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
detectChangesAndExpectText('bar');
|
||||
|
||||
fixture.componentInstance.context.foo = 'alter-bar';
|
||||
detectChangesAndExpectText('alter-bar');
|
||||
}));
|
||||
|
||||
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||
const template = `<ng-template let-ctx #tpl>{{ctx.foo}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||
detectChangesAndExpectText('bra');
|
||||
}));
|
||||
|
||||
it('should reflect context re-binding', async(() => {
|
||||
const template = `<ng-template let-shawshank="shawshank" #tpl>{{shawshank}}</ng-template>` +
|
||||
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||
detectChangesAndExpectText('brooks');
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||
detectChangesAndExpectText('was here');
|
||||
}));
|
||||
|
||||
it('should update but not destroy embedded view when context values change', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should recreate embedded view when context shape changes', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="context"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.context = {foo: 'baz', other: true};
|
||||
detectChangesAndExpectText('Content to destroy:baz');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should destroy embedded view when context value changes and templateRef becomes undefined', () => {
|
||||
const template =
|
||||
`<ng-template let-foo="foo" #tpl><destroyable-cmpt></destroyable-cmpt>:{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
const spyService = fixture.debugElement.injector.get(DestroyedSpyService);
|
||||
|
||||
detectChangesAndExpectText('Content to destroy:bar');
|
||||
expect(spyService.destroyed).toBeFalsy();
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
expect(spyService.destroyed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when context changes but template stays the same',
|
||||
() => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="tpl" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update null / undefined context when template changes', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl1>{{foo}}</ng-template>` +
|
||||
`<ng-template let-foo="foo" #tpl2>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? tpl1 : tpl2" [ngTemplateOutletContext]="value === 'bar' ? null : undefined"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
|
||||
it('should not try to update context on undefined view', () => {
|
||||
const template = `<ng-template let-foo="foo" #tpl>{{foo}}</ng-template>` +
|
||||
`<ng-template [ngTemplateOutlet]="value === 'bar' ? null : undefined" [ngTemplateOutletContext]="{foo: value}"></ng-template>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
fixture.componentInstance.value = 'baz';
|
||||
detectChangesAndExpectText('');
|
||||
});
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
class DestroyedSpyService {
|
||||
|
@ -12,8 +12,8 @@ import localeZgh from '@angular/common/locales/zgh';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import {LOCALE_ID} from '@angular/core';
|
||||
import {TestBed, inject} from '@angular/core/testing';
|
||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory, DEPRECATED_PLURAL_FN, getPluralCase} from '../../src/i18n/localization';
|
||||
import {Plural} from '../../src/i18n/locale_data_api';
|
||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory, DEPRECATED_PLURAL_FN, getPluralCase} from '@angular/common/src/i18n/localization';
|
||||
import {Plural} from '@angular/common';
|
||||
import {registerLocaleData} from '../../src/i18n/locale_data';
|
||||
|
||||
{
|
||||
|
@ -6,37 +6,35 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Location} from '../../src/location/location';
|
||||
import {Location} from '@angular/common';
|
||||
|
||||
{
|
||||
const baseUrl = '/base';
|
||||
const baseUrl = '/base';
|
||||
|
||||
describe('Location Class', () => {
|
||||
describe('stripTrailingSlash', () => {
|
||||
it('should strip single character slash', () => {
|
||||
const input = '/';
|
||||
expect(Location.stripTrailingSlash(input)).toBe('');
|
||||
});
|
||||
describe('Location Class', () => {
|
||||
describe('stripTrailingSlash', () => {
|
||||
it('should strip single character slash', () => {
|
||||
const input = '/';
|
||||
expect(Location.stripTrailingSlash(input)).toBe('');
|
||||
});
|
||||
|
||||
it('should normalize strip a trailing slash', () => {
|
||||
const input = baseUrl + '/';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(baseUrl);
|
||||
});
|
||||
it('should normalize strip a trailing slash', () => {
|
||||
const input = baseUrl + '/';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(baseUrl);
|
||||
});
|
||||
|
||||
it('should ignore query params when stripping a slash', () => {
|
||||
const input = baseUrl + '/?param=1';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(baseUrl + '?param=1');
|
||||
});
|
||||
it('should ignore query params when stripping a slash', () => {
|
||||
const input = baseUrl + '/?param=1';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(baseUrl + '?param=1');
|
||||
});
|
||||
|
||||
it('should not remove slashes inside query params', () => {
|
||||
const input = baseUrl + '?test/?=3';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(input);
|
||||
});
|
||||
it('should not remove slashes inside query params', () => {
|
||||
const input = baseUrl + '?test/?=3';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(input);
|
||||
});
|
||||
|
||||
it('should not remove slashes after a pound sign', () => {
|
||||
const input = baseUrl + '#test/?=3';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(input);
|
||||
});
|
||||
it('should not remove slashes after a pound sign', () => {
|
||||
const input = baseUrl + '#test/?=3';
|
||||
expect(Location.stripTrailingSlash(input)).toBe(input);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
@ -7,13 +7,8 @@
|
||||
*/
|
||||
|
||||
import {KeyValuePipe} from '@angular/common';
|
||||
import {EventEmitter, KeyValueDiffers, WrappedValue, ɵdefaultKeyValueDiffers as defaultKeyValueDiffers} from '@angular/core';
|
||||
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
|
||||
|
||||
import {defaultComparator} from '../../src/pipes/keyvalue_pipe';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
import {defaultComparator} from '@angular/common/src/pipes/keyvalue_pipe';
|
||||
import {ɵdefaultKeyValueDiffers as defaultKeyValueDiffers} from '@angular/core';
|
||||
|
||||
describe('KeyValuePipe', () => {
|
||||
it('should return null when given null', () => {
|
||||
|
@ -54,7 +54,8 @@ describe('Ivy NgModule', () => {
|
||||
}
|
||||
|
||||
expect(() => createInjector(AModule))
|
||||
.toThrowError('Circular dependency: type AModule ends up importing itself.');
|
||||
.toThrowError(
|
||||
'Circular dependency in DI detected for type AModule. Dependency path: AModule > BModule > AModule.');
|
||||
});
|
||||
|
||||
it('merges imports and exports', () => {
|
||||
|
@ -19,6 +19,7 @@
|
||||
"magic-string": "^0.25.0",
|
||||
"shelljs": "^0.8.1",
|
||||
"source-map": "^0.6.1",
|
||||
"tslib": "^1.9.0",
|
||||
"yargs": "9.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
31
packages/compiler-cli/src/ngcc/src/packages/bundle.ts
Normal file
31
packages/compiler-cli/src/ngcc/src/packages/bundle.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @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 ts from 'typescript';
|
||||
|
||||
/**
|
||||
* A bundle represents the currently compiled entry point format, containing
|
||||
* information that is necessary for compiling @angular/core with ngcc.
|
||||
*/
|
||||
export interface BundleInfo {
|
||||
isCore: boolean;
|
||||
isFlat: boolean;
|
||||
rewriteCoreImportsTo: ts.SourceFile|null;
|
||||
rewriteCoreDtsImportsTo: ts.SourceFile|null;
|
||||
}
|
||||
|
||||
export function createBundleInfo(
|
||||
isCore: boolean, rewriteCoreImportsTo: ts.SourceFile | null,
|
||||
rewriteCoreDtsImportsTo: ts.SourceFile | null): BundleInfo {
|
||||
return {
|
||||
isCore,
|
||||
isFlat: rewriteCoreImportsTo === null,
|
||||
rewriteCoreImportsTo: rewriteCoreImportsTo,
|
||||
rewriteCoreDtsImportsTo: rewriteCoreDtsImportsTo,
|
||||
};
|
||||
}
|
@ -17,15 +17,15 @@ export class DependencyHost {
|
||||
/**
|
||||
* Get a list of the resolved paths to all the dependencies of this entry point.
|
||||
* @param from An absolute path to the file whose dependencies we want to get.
|
||||
* @param resolved A set that will have the resolved dependencies added to it.
|
||||
* @param resolved A set that will have the absolute paths of resolved entry points added to it.
|
||||
* @param missing A set that will have the dependencies that could not be found added to it.
|
||||
* @param deepImports A set that will have the import paths that exist but cannot be mapped to
|
||||
* entry-points, i.e. deep-imports.
|
||||
* @param internal A set that is used to track internal dependencies to prevent getting stuck in a
|
||||
* circular dependency loop.
|
||||
* @returns an object containing an array of absolute paths to `resolved` depenendencies and an
|
||||
* array of import specifiers for dependencies that were `missing`.
|
||||
*/
|
||||
computeDependencies(
|
||||
from: string, resolved: Set<string>, missing: Set<string>,
|
||||
from: string, resolved: Set<string>, missing: Set<string>, deepImports: Set<string>,
|
||||
internal: Set<string> = new Set()): void {
|
||||
const fromContents = fs.readFileSync(from, 'utf8');
|
||||
if (!this.hasImportOrReeportStatements(fromContents)) {
|
||||
@ -48,14 +48,22 @@ export class DependencyHost {
|
||||
// Avoid circular dependencies
|
||||
if (!internal.has(internalDependency)) {
|
||||
internal.add(internalDependency);
|
||||
this.computeDependencies(internalDependency, resolved, missing, internal);
|
||||
this.computeDependencies(
|
||||
internalDependency, resolved, missing, deepImports, internal);
|
||||
}
|
||||
} else {
|
||||
const externalDependency = this.tryResolveExternal(from, importPath);
|
||||
if (externalDependency !== null) {
|
||||
resolved.add(externalDependency);
|
||||
const resolvedEntryPoint = this.tryResolveEntryPoint(from, importPath);
|
||||
if (resolvedEntryPoint !== null) {
|
||||
resolved.add(resolvedEntryPoint);
|
||||
} else {
|
||||
missing.add(importPath);
|
||||
// If the import could not be resolved as entry point, it either does not exist
|
||||
// at all or is a deep import.
|
||||
const deeplyImportedFile = this.tryResolve(from, importPath);
|
||||
if (deeplyImportedFile !== null) {
|
||||
deepImports.add(importPath);
|
||||
} else {
|
||||
missing.add(importPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -91,9 +99,9 @@ export class DependencyHost {
|
||||
* @returns the resolved path to the entry point directory of the import or null
|
||||
* if it cannot be resolved.
|
||||
*/
|
||||
tryResolveExternal(from: string, to: string): string|null {
|
||||
const externalDependency = this.tryResolve(from, `${to}/package.json`);
|
||||
return externalDependency && path.dirname(externalDependency);
|
||||
tryResolveEntryPoint(from: string, to: string): string|null {
|
||||
const entryPoint = this.tryResolve(from, `${to}/package.json`);
|
||||
return entryPoint && path.dirname(entryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +87,8 @@ export class DependencyResolver {
|
||||
|
||||
const dependencies = new Set<string>();
|
||||
const missing = new Set<string>();
|
||||
this.host.computeDependencies(entryPointPath, dependencies, missing);
|
||||
const deepImports = new Set<string>();
|
||||
this.host.computeDependencies(entryPointPath, dependencies, missing, deepImports);
|
||||
|
||||
if (missing.size > 0) {
|
||||
// This entry point has dependencies that are missing
|
||||
@ -109,6 +110,13 @@ export class DependencyResolver {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (deepImports.size) {
|
||||
const imports = Array.from(deepImports).map(i => `'${i}'`).join(', ');
|
||||
console.warn(
|
||||
`Entry point '${entryPoint.name}' contains deep imports into ${imports}. ` +
|
||||
`This is probably not a problem, but may cause the compilation of entry points to be out of order.`);
|
||||
}
|
||||
});
|
||||
|
||||
// The map now only holds entry-points that ngcc cares about and whose dependencies
|
||||
|
@ -50,6 +50,21 @@ interface EntryPointPackageJson {
|
||||
typings?: string; // TypeScript .d.ts files
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the JSON from a package.json file.
|
||||
* @param packageJsonPath the absolute path to the package.json file.
|
||||
* @returns JSON from the package.json file if it is valid, `null` otherwise.
|
||||
*/
|
||||
function loadEntryPointPackage(packageJsonPath: string): {[key: string]: any}|null {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
} catch (e) {
|
||||
// We may have run into a package.json with unexpected symbols
|
||||
console.warn(`Failed to read entry point info from ${packageJsonPath} with error ${e}.`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get entry point info from the given path.
|
||||
* @param pkgPath the absolute path to the containing npm package
|
||||
@ -62,6 +77,11 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi
|
||||
return null;
|
||||
}
|
||||
|
||||
const entryPointPackageJson = loadEntryPointPackage(packageJsonPath);
|
||||
if (!entryPointPackageJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If there is `esm2015` then `es2015` will be FESM2015, otherwise ESM2015.
|
||||
// If there is `esm5` then `module` will be FESM5, otherwise it will be ESM5.
|
||||
const {
|
||||
@ -75,8 +95,7 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi
|
||||
esm2015,
|
||||
esm5,
|
||||
main
|
||||
}: EntryPointPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
} = entryPointPackageJson;
|
||||
// Minimum requirement is that we have typings and one of esm2015 or fesm2015 formats.
|
||||
if (!typings || !(fesm2015 || esm2015)) {
|
||||
return null;
|
||||
|
@ -20,6 +20,7 @@ import {EsmRenderer} from '../rendering/esm_renderer';
|
||||
import {FileInfo, Renderer} from '../rendering/renderer';
|
||||
|
||||
import {checkMarkerFile, writeMarkerFile} from './build_marker';
|
||||
import {BundleInfo, createBundleInfo} from './bundle';
|
||||
import {EntryPoint, EntryPointFormat} from './entry_point';
|
||||
|
||||
/**
|
||||
@ -70,27 +71,40 @@ export class Transformer {
|
||||
const host = ts.createCompilerHost(options);
|
||||
const rootDirs = this.getRootDirs(host, options);
|
||||
const isCore = entryPoint.name === '@angular/core';
|
||||
const r3SymbolsPath = isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath)) : null;
|
||||
const r3SymbolsPath =
|
||||
isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath), 'r3_symbols.js') : null;
|
||||
const rootPaths = r3SymbolsPath ? [entryPointFilePath, r3SymbolsPath] : [entryPointFilePath];
|
||||
const packageProgram = ts.createProgram(rootPaths, options, host);
|
||||
console.time(entryPoint.name + '(dtsmappper creation)');
|
||||
const dtsFilePath = entryPoint.typings;
|
||||
const dtsProgram = transformDts ? ts.createProgram([entryPoint.typings], options, host) : null;
|
||||
console.timeEnd(entryPoint.name + '(dtsmappper creation)');
|
||||
const reflectionHost = this.getHost(isCore, format, packageProgram, dtsFilePath, dtsProgram);
|
||||
const r3SymbolsFile = r3SymbolsPath && packageProgram.getSourceFile(r3SymbolsPath) || null;
|
||||
|
||||
// Create the program for processing DTS files if enabled for this format.
|
||||
const dtsFilePath = entryPoint.typings;
|
||||
let dtsProgram: ts.Program|null = null;
|
||||
let r3SymbolsDtsFile: ts.SourceFile|null = null;
|
||||
if (transformDts) {
|
||||
console.time(`${entryPoint.name} (dtsMapper creation)`);
|
||||
const r3SymbolsDtsPath =
|
||||
isCore ? this.findR3SymbolsPath(dirname(dtsFilePath), 'r3_symbols.d.ts') : null;
|
||||
const rootDtsPaths = r3SymbolsDtsPath ? [dtsFilePath, r3SymbolsDtsPath] : [dtsFilePath];
|
||||
|
||||
dtsProgram = ts.createProgram(rootDtsPaths, options, host);
|
||||
r3SymbolsDtsFile = r3SymbolsDtsPath && dtsProgram.getSourceFile(r3SymbolsDtsPath) || null;
|
||||
console.timeEnd(`${entryPoint.name} (dtsMapper creation)`);
|
||||
}
|
||||
|
||||
const bundle = createBundleInfo(isCore, r3SymbolsFile, r3SymbolsDtsFile);
|
||||
const reflectionHost = this.getHost(isCore, format, packageProgram, dtsFilePath, dtsProgram);
|
||||
|
||||
// Parse and analyze the files.
|
||||
const {decorationAnalyses, switchMarkerAnalyses} =
|
||||
this.analyzeProgram(packageProgram, reflectionHost, rootDirs, isCore);
|
||||
|
||||
console.time(entryPoint.name + '(rendering)');
|
||||
console.time(`${entryPoint.name} (rendering)`);
|
||||
// Transform the source files and source maps.
|
||||
const renderer = this.getRenderer(
|
||||
format, packageProgram, reflectionHost, isCore, r3SymbolsFile, transformDts);
|
||||
const renderer = this.getRenderer(format, packageProgram, reflectionHost, bundle, transformDts);
|
||||
const renderedFiles =
|
||||
renderer.renderProgram(packageProgram, decorationAnalyses, switchMarkerAnalyses);
|
||||
console.timeEnd(entryPoint.name + '(rendering)');
|
||||
console.timeEnd(`${entryPoint.name} (rendering)`);
|
||||
|
||||
// Write out all the transformed files.
|
||||
renderedFiles.forEach(file => this.writeFile(file));
|
||||
@ -125,17 +139,15 @@ export class Transformer {
|
||||
}
|
||||
|
||||
getRenderer(
|
||||
format: string, program: ts.Program, host: NgccReflectionHost, isCore: boolean,
|
||||
rewriteCoreImportsTo: ts.SourceFile|null, transformDts: boolean): Renderer {
|
||||
format: string, program: ts.Program, host: NgccReflectionHost, bundle: BundleInfo,
|
||||
transformDts: boolean): Renderer {
|
||||
switch (format) {
|
||||
case 'esm2015':
|
||||
case 'fesm2015':
|
||||
return new EsmRenderer(
|
||||
host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath, transformDts);
|
||||
return new EsmRenderer(host, bundle, this.sourcePath, this.targetPath, transformDts);
|
||||
case 'esm5':
|
||||
case 'fesm5':
|
||||
return new Esm5Renderer(
|
||||
host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath, transformDts);
|
||||
return new Esm5Renderer(host, bundle, this.sourcePath, this.targetPath, transformDts);
|
||||
default:
|
||||
throw new Error(`Renderer for "${format}" not yet implemented.`);
|
||||
}
|
||||
@ -162,8 +174,8 @@ export class Transformer {
|
||||
writeFileSync(file.path, file.contents, 'utf8');
|
||||
}
|
||||
|
||||
findR3SymbolsPath(directory: string): string|null {
|
||||
const r3SymbolsFilePath = resolve(directory, 'r3_symbols.js');
|
||||
findR3SymbolsPath(directory: string, fileName: string): string|null {
|
||||
const r3SymbolsFilePath = resolve(directory, fileName);
|
||||
if (existsSync(r3SymbolsFilePath)) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
@ -181,7 +193,7 @@ export class Transformer {
|
||||
});
|
||||
|
||||
for (const subDirectory of subDirectories) {
|
||||
const r3SymbolsFilePath = this.findR3SymbolsPath(resolve(directory, subDirectory));
|
||||
const r3SymbolsFilePath = this.findR3SymbolsPath(resolve(directory, subDirectory), fileName);
|
||||
if (r3SymbolsFilePath) {
|
||||
return r3SymbolsFilePath;
|
||||
}
|
||||
|
@ -9,14 +9,14 @@ import * as ts from 'typescript';
|
||||
import MagicString from 'magic-string';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {BundleInfo} from '../packages/bundle';
|
||||
import {EsmRenderer} from './esm_renderer';
|
||||
|
||||
export class Esm5Renderer extends EsmRenderer {
|
||||
constructor(
|
||||
protected host: NgccReflectionHost, protected isCore: boolean,
|
||||
protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string,
|
||||
protected targetPath: string, transformDts: boolean) {
|
||||
super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath, transformDts);
|
||||
protected host: NgccReflectionHost, protected bundle: BundleInfo,
|
||||
protected sourcePath: string, protected targetPath: string, transformDts: boolean) {
|
||||
super(host, bundle, sourcePath, targetPath, transformDts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,14 +9,14 @@ import * as ts from 'typescript';
|
||||
import MagicString from 'magic-string';
|
||||
import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
import {BundleInfo} from '../packages/bundle';
|
||||
import {Renderer} from './renderer';
|
||||
|
||||
export class EsmRenderer extends Renderer {
|
||||
constructor(
|
||||
protected host: NgccReflectionHost, protected isCore: boolean,
|
||||
protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string,
|
||||
protected targetPath: string, transformDts: boolean) {
|
||||
super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath, transformDts);
|
||||
protected host: NgccReflectionHost, protected bundle: BundleInfo,
|
||||
protected sourcePath: string, protected targetPath: string, transformDts: boolean) {
|
||||
super(host, bundle, sourcePath, targetPath, transformDts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,10 +12,11 @@ import {ImportManager} from '../../../ngtsc/translator';
|
||||
export class NgccImportManager extends ImportManager {
|
||||
constructor(private isFlat: boolean, isCore: boolean, prefix?: string) { super(isCore, prefix); }
|
||||
|
||||
generateNamedImport(moduleName: string, symbol: string): string|null {
|
||||
generateNamedImport(moduleName: string, symbol: string):
|
||||
{moduleImport: string | null, symbol: string} {
|
||||
if (this.isFlat && this.isCore && moduleName === '@angular/core') {
|
||||
return null;
|
||||
return {moduleImport: null, symbol: this.rewriteSymbol(moduleName, symbol)};
|
||||
}
|
||||
return super.generateNamedImport(moduleName, symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import {translateStatement, translateType} from '../../../ngtsc/translator';
|
||||
import {NgccImportManager} from './ngcc_import_manager';
|
||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||
import {BundleInfo} from '../packages/bundle';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
|
||||
@ -55,9 +56,9 @@ interface DtsClassInfo {
|
||||
*/
|
||||
export abstract class Renderer {
|
||||
constructor(
|
||||
protected host: NgccReflectionHost, protected isCore: boolean,
|
||||
protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string,
|
||||
protected targetPath: string, protected transformDts: boolean) {}
|
||||
protected host: NgccReflectionHost, protected bundle: BundleInfo,
|
||||
protected sourcePath: string, protected targetPath: string, protected transformDts: boolean) {
|
||||
}
|
||||
|
||||
renderProgram(
|
||||
program: ts.Program, decorationAnalyses: DecorationAnalyses,
|
||||
@ -101,7 +102,7 @@ export abstract class Renderer {
|
||||
|
||||
if (compiledFile) {
|
||||
const importManager =
|
||||
new NgccImportManager(!this.rewriteCoreImportsTo, this.isCore, IMPORT_PREFIX);
|
||||
new NgccImportManager(this.bundle.isFlat, this.bundle.isCore, IMPORT_PREFIX);
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
|
||||
compiledFile.compiledClasses.forEach(clazz => {
|
||||
@ -116,8 +117,8 @@ export abstract class Renderer {
|
||||
compiledFile.sourceFile);
|
||||
|
||||
this.addImports(
|
||||
outputText,
|
||||
importManager.getAllImports(compiledFile.sourceFile.fileName, this.rewriteCoreImportsTo));
|
||||
outputText, importManager.getAllImports(
|
||||
compiledFile.sourceFile.fileName, this.bundle.rewriteCoreImportsTo));
|
||||
|
||||
// TODO: remove contructor param metadata and property decorators (we need info from the
|
||||
// handlers to do this)
|
||||
@ -130,7 +131,7 @@ export abstract class Renderer {
|
||||
renderDtsFile(dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[]): FileInfo[] {
|
||||
const input = this.extractSourceMap(dtsFile);
|
||||
const outputText = new MagicString(input.source);
|
||||
const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
|
||||
const importManager = new NgccImportManager(false, this.bundle.isCore, IMPORT_PREFIX);
|
||||
|
||||
dtsClasses.forEach(dtsClass => {
|
||||
const endOfClass = dtsClass.dtsDeclaration.getEnd();
|
||||
@ -142,7 +143,8 @@ export abstract class Renderer {
|
||||
});
|
||||
|
||||
this.addImports(
|
||||
outputText, importManager.getAllImports(dtsFile.fileName, this.rewriteCoreImportsTo));
|
||||
outputText,
|
||||
importManager.getAllImports(dtsFile.fileName, this.bundle.rewriteCoreDtsImportsTo));
|
||||
|
||||
return this.renderSourceAndMap(dtsFile, input, outputText);
|
||||
}
|
||||
|
@ -27,61 +27,88 @@ describe('DependencyHost', () => {
|
||||
it('should not generate a TS AST if the source does not contain any imports or re-exports',
|
||||
() => {
|
||||
spyOn(ts, 'createSourceFile');
|
||||
host.computeDependencies('/no/imports/or/re-exports.js', new Set(), new Set());
|
||||
host.computeDependencies('/no/imports/or/re-exports.js', new Set(), new Set(), new Set());
|
||||
expect(ts.createSourceFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should resolve all the external imports of the source file', () => {
|
||||
spyOn(host, 'tryResolveExternal')
|
||||
spyOn(host, 'tryResolveEntryPoint')
|
||||
.and.callFake((from: string, importPath: string) => `RESOLVED/${importPath}`);
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
host.computeDependencies('/external/imports.js', resolved, missing);
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/imports.js', resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x'));
|
||||
expect(resolved.has('RESOLVED/path/to/y'));
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
});
|
||||
|
||||
it('should resolve all the external re-exports of the source file', () => {
|
||||
spyOn(host, 'tryResolveExternal')
|
||||
spyOn(host, 'tryResolveEntryPoint')
|
||||
.and.callFake((from: string, importPath: string) => `RESOLVED/${importPath}`);
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
host.computeDependencies('/external/re-exports.js', resolved, missing);
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/re-exports.js', resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x'));
|
||||
expect(resolved.has('RESOLVED/path/to/y'));
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
});
|
||||
|
||||
it('should capture missing external imports', () => {
|
||||
spyOn(host, 'tryResolveExternal')
|
||||
spyOn(host, 'tryResolveEntryPoint')
|
||||
.and.callFake(
|
||||
(from: string, importPath: string) =>
|
||||
importPath === 'missing' ? null : `RESOLVED/${importPath}`);
|
||||
spyOn(host, 'tryResolve').and.callFake(() => null);
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
host.computeDependencies('/external/imports-missing.js', resolved, missing);
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/imports-missing.js', resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(1);
|
||||
expect(resolved.has('RESOLVED/path/to/x'));
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(missing.size).toBe(1);
|
||||
expect(missing.has('missing'));
|
||||
expect(missing.has('missing')).toBe(true);
|
||||
expect(deepImports.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should not register deep imports as missing', () => {
|
||||
// This scenario verifies the behavior of the dependency analysis when an external import
|
||||
// is found that does not map to an entry-point but still exists on disk, i.e. a deep import.
|
||||
// Such deep imports are captured for diagnostics purposes.
|
||||
const tryResolveEntryPoint = (from: string, importPath: string) =>
|
||||
importPath === 'deep/import' ? null : `RESOLVED/${importPath}`;
|
||||
spyOn(host, 'tryResolveEntryPoint').and.callFake(tryResolveEntryPoint);
|
||||
spyOn(host, 'tryResolve')
|
||||
.and.callFake((from: string, importPath: string) => `RESOLVED/${importPath}`);
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/external/deep-import.js', resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(0);
|
||||
expect(missing.size).toBe(0);
|
||||
expect(deepImports.size).toBe(1);
|
||||
expect(deepImports.has('deep/import')).toBe(true);
|
||||
});
|
||||
|
||||
it('should recurse into internal dependencies', () => {
|
||||
spyOn(host, 'resolveInternal')
|
||||
.and.callFake(
|
||||
(from: string, importPath: string) => path.join('/internal', importPath + '.js'));
|
||||
spyOn(host, 'tryResolveExternal')
|
||||
spyOn(host, 'tryResolveEntryPoint')
|
||||
.and.callFake((from: string, importPath: string) => `RESOLVED/${importPath}`);
|
||||
const getDependenciesSpy = spyOn(host, 'computeDependencies').and.callThrough();
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
host.computeDependencies('/internal/outer.js', resolved, missing);
|
||||
expect(getDependenciesSpy).toHaveBeenCalledWith('/internal/outer.js', resolved, missing);
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/internal/outer.js', resolved, missing, deepImports);
|
||||
expect(getDependenciesSpy)
|
||||
.toHaveBeenCalledWith('/internal/inner.js', resolved, missing, jasmine.any(Set));
|
||||
.toHaveBeenCalledWith('/internal/outer.js', resolved, missing, deepImports);
|
||||
expect(getDependenciesSpy)
|
||||
.toHaveBeenCalledWith(
|
||||
'/internal/inner.js', resolved, missing, deepImports, jasmine.any(Set));
|
||||
expect(resolved.size).toBe(1);
|
||||
expect(resolved.has('RESOLVED/path/to/y'));
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
@ -89,14 +116,15 @@ describe('DependencyHost', () => {
|
||||
spyOn(host, 'resolveInternal')
|
||||
.and.callFake(
|
||||
(from: string, importPath: string) => path.join('/internal', importPath + '.js'));
|
||||
spyOn(host, 'tryResolveExternal')
|
||||
spyOn(host, 'tryResolveEntryPoint')
|
||||
.and.callFake((from: string, importPath: string) => `RESOLVED/${importPath}`);
|
||||
const resolved = new Set();
|
||||
const missing = new Set();
|
||||
host.computeDependencies('/internal/circular-a.js', resolved, missing);
|
||||
const deepImports = new Set();
|
||||
host.computeDependencies('/internal/circular-a.js', resolved, missing, deepImports);
|
||||
expect(resolved.size).toBe(2);
|
||||
expect(resolved.has('RESOLVED/path/to/x'));
|
||||
expect(resolved.has('RESOLVED/path/to/y'));
|
||||
expect(resolved.has('RESOLVED/path/to/x')).toBe(true);
|
||||
expect(resolved.has('RESOLVED/path/to/y')).toBe(true);
|
||||
});
|
||||
|
||||
function createMockFileSystem() {
|
||||
@ -105,6 +133,7 @@ describe('DependencyHost', () => {
|
||||
'/external/imports.js': `import {X} from 'path/to/x';\nimport {Y} from 'path/to/y';`,
|
||||
'/external/re-exports.js': `export {X} from 'path/to/x';\nexport {Y} from 'path/to/y';`,
|
||||
'/external/imports-missing.js': `import {X} from 'path/to/x';\nimport {Y} from 'missing';`,
|
||||
'/external/deep-import.js': `import {Y} from 'deep/import';`,
|
||||
'/internal/outer.js': `import {X} from './inner';`,
|
||||
'/internal/inner.js': `import {Y} from 'path/to/y';`,
|
||||
'/internal/circular-a.js': `import {B} from './circular-b'; import {X} from 'path/to/x';`,
|
||||
@ -131,18 +160,18 @@ describe('DependencyHost', () => {
|
||||
describe('tryResolveExternal', () => {
|
||||
it('should call `tryResolve`, appending `package.json` to the target path', () => {
|
||||
const tryResolveSpy = spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
|
||||
host.tryResolveExternal('SOURCE_PATH', 'TARGET_PATH');
|
||||
host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH');
|
||||
expect(tryResolveSpy).toHaveBeenCalledWith('SOURCE_PATH', 'TARGET_PATH/package.json');
|
||||
});
|
||||
|
||||
it('should return the directory containing the result from `tryResolve', () => {
|
||||
spyOn(host, 'tryResolve').and.returnValue('PATH/TO/RESOLVED');
|
||||
expect(host.tryResolveExternal('SOURCE_PATH', 'TARGET_PATH')).toEqual('PATH/TO');
|
||||
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual('PATH/TO');
|
||||
});
|
||||
|
||||
it('should return null if `tryResolve` returns null', () => {
|
||||
spyOn(host, 'tryResolve').and.returnValue(null);
|
||||
expect(host.tryResolveExternal('SOURCE_PATH', 'TARGET_PATH')).toEqual(null);
|
||||
expect(host.tryResolveEntryPoint('SOURCE_PATH', 'TARGET_PATH')).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -78,6 +78,11 @@ describe('getEntryPointInfo()', () => {
|
||||
umd: `/some_package/material_style/bundles/material_style.umd.js`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null if the package.json is not valid JSON', () => {
|
||||
const entryPoint = getEntryPointInfo('/some_package', '/some_package/unexpected_symbols');
|
||||
expect(entryPoint).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
function createMockFileSystem() {
|
||||
@ -115,8 +120,15 @@ function createMockFileSystem() {
|
||||
"module": "./esm5/material_style.es5.js",
|
||||
"es2015": "./esm2015/material_style.js"
|
||||
}`,
|
||||
'material_style.metadata.json': 'some meta data'
|
||||
}
|
||||
'material_style.metadata.json': 'some meta data',
|
||||
},
|
||||
'unexpected_symbols': {
|
||||
// package.json might not be a valid JSON
|
||||
// for example, @schematics/angular contains a package.json blueprint
|
||||
// with unexpected symbols
|
||||
'package.json':
|
||||
'{"devDependencies": {<% if (!minimal) { %>"@types/jasmine": "~2.8.8" <% } %>}}',
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import MagicString from 'magic-string';
|
||||
import {makeProgram} from '../helpers/utils';
|
||||
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||
import {createBundleInfo} from '../../src/packages/bundle';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {EsmRenderer} from '../../src/rendering/esm_renderer';
|
||||
|
||||
@ -23,7 +24,8 @@ function setup(file: {name: string, contents: string}, transformDts: boolean = f
|
||||
const decorationAnalyses =
|
||||
new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program);
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program);
|
||||
const renderer = new EsmRenderer(host, false, null, dir, dir, false);
|
||||
const bundle = createBundleInfo(false, null, null);
|
||||
const renderer = new EsmRenderer(host, bundle, dir, dir, false);
|
||||
return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses};
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import MagicString from 'magic-string';
|
||||
import {makeProgram, getDeclaration} from '../helpers/utils';
|
||||
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||
import {createBundleInfo} from '../../src/packages/bundle';
|
||||
import {Esm5ReflectionHost} from '../../src/host/esm5_host';
|
||||
import {Esm5Renderer} from '../../src/rendering/esm5_renderer';
|
||||
|
||||
@ -20,7 +21,8 @@ function setup(file: {name: string, contents: string}) {
|
||||
const decorationAnalyses =
|
||||
new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program);
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program);
|
||||
const renderer = new Esm5Renderer(host, false, null, '', '', false);
|
||||
const bundle = createBundleInfo(false, null, null);
|
||||
const renderer = new Esm5Renderer(host, bundle, '', '', false);
|
||||
return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses};
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,14 @@ import {fromObject, generateMapFileComment} from 'convert-source-map';
|
||||
import {makeProgram} from '../helpers/utils';
|
||||
import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
|
||||
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
|
||||
import {BundleInfo, createBundleInfo} from '../../src/packages/bundle';
|
||||
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
|
||||
import {Renderer} from '../../src/rendering/renderer';
|
||||
|
||||
class TestRenderer extends Renderer {
|
||||
constructor(host: Esm2015ReflectionHost) { super(host, false, null, '/src', '/dist', false); }
|
||||
constructor(host: Esm2015ReflectionHost, bundle: BundleInfo) {
|
||||
super(host, bundle, '/src', '/dist', false);
|
||||
}
|
||||
addImports(output: MagicString, imports: {name: string, as: string}[]) {
|
||||
output.prepend('\n// ADD IMPORTS\n');
|
||||
}
|
||||
@ -35,13 +38,19 @@ class TestRenderer extends Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
function createTestRenderer(file: {name: string, contents: string}) {
|
||||
const program = makeProgram(file);
|
||||
const host = new Esm2015ReflectionHost(false, program.getTypeChecker());
|
||||
function createTestRenderer(
|
||||
files: {name: string, contents: string}[],
|
||||
options: {isCore?: boolean, rewriteCoreImportsTo?: string} = {}) {
|
||||
const program = makeProgram(...files);
|
||||
const rewriteCoreImportsTo =
|
||||
options.rewriteCoreImportsTo ? program.getSourceFile(options.rewriteCoreImportsTo) ! : null;
|
||||
const bundle = createBundleInfo(options.isCore || false, rewriteCoreImportsTo, null);
|
||||
const host = new Esm2015ReflectionHost(bundle.isCore, program.getTypeChecker());
|
||||
const decorationAnalyses =
|
||||
new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program);
|
||||
new DecorationAnalyzer(program.getTypeChecker(), host, [''], bundle.isCore)
|
||||
.analyzeProgram(program);
|
||||
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program);
|
||||
const renderer = new TestRenderer(host);
|
||||
const renderer = new TestRenderer(host, bundle);
|
||||
spyOn(renderer, 'addImports').and.callThrough();
|
||||
spyOn(renderer, 'addDefinitions').and.callThrough();
|
||||
spyOn(renderer, 'removeDecorators').and.callThrough();
|
||||
@ -94,7 +103,7 @@ describe('Renderer', () => {
|
||||
it('should render the modified contents; and a new map file, if the original provided no map file.',
|
||||
() => {
|
||||
const {renderer, program, decorationAnalyses, switchMarkerAnalyses} =
|
||||
createTestRenderer(INPUT_PROGRAM);
|
||||
createTestRenderer([INPUT_PROGRAM]);
|
||||
const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
@ -106,7 +115,7 @@ describe('Renderer', () => {
|
||||
it('should call addImports with the source code and info about the core Angular library.',
|
||||
() => {
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} =
|
||||
createTestRenderer(INPUT_PROGRAM);
|
||||
createTestRenderer([INPUT_PROGRAM]);
|
||||
renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||
expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
@ -118,7 +127,7 @@ describe('Renderer', () => {
|
||||
it('should call addDefinitions with the source code, the analyzed class and the renderered definitions.',
|
||||
() => {
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} =
|
||||
createTestRenderer(INPUT_PROGRAM);
|
||||
createTestRenderer([INPUT_PROGRAM]);
|
||||
renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
@ -137,7 +146,7 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',
|
||||
() => {
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} =
|
||||
createTestRenderer(INPUT_PROGRAM);
|
||||
createTestRenderer([INPUT_PROGRAM]);
|
||||
renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy;
|
||||
expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS);
|
||||
@ -157,10 +166,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
|
||||
it('should merge any inline source map from the original file and write the output as an inline source map',
|
||||
() => {
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer({
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer([{
|
||||
...INPUT_PROGRAM,
|
||||
contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment()
|
||||
});
|
||||
}]);
|
||||
const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
@ -172,10 +181,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
() => {
|
||||
// Mock out reading the map file from disk
|
||||
spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON());
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer({
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer([{
|
||||
...INPUT_PROGRAM,
|
||||
contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map'
|
||||
});
|
||||
}]);
|
||||
const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
expect(result[0].path).toEqual('/dist/file.js');
|
||||
expect(result[0].contents)
|
||||
@ -183,5 +192,48 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""
|
||||
expect(result[1].path).toEqual('/dist/file.js.map');
|
||||
expect(result[1].contents).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toJSON());
|
||||
});
|
||||
|
||||
describe('@angular/core support', () => {
|
||||
|
||||
it('should render relative imports in ESM bundles', () => {
|
||||
const R3_SYMBOLS_FILE = {
|
||||
name: '/src/r3_symbols.js',
|
||||
contents: `export const NgModule = () => null;`
|
||||
};
|
||||
const CORE_FILE = {
|
||||
name: '/src/core.js',
|
||||
contents:
|
||||
`import { NgModule } from './ng_module';\nexport class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
|
||||
};
|
||||
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer(
|
||||
[R3_SYMBOLS_FILE, CORE_FILE],
|
||||
{isCore: true, rewriteCoreImportsTo: R3_SYMBOLS_FILE.name});
|
||||
renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`);
|
||||
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||
expect(addImportsSpy.calls.first().args[1]).toEqual([{name: './r3_symbols', as: 'ɵngcc0'}]);
|
||||
});
|
||||
|
||||
it('should render no imports in FESM bundles', () => {
|
||||
const CORE_FILE = {
|
||||
name: '/src/core.js',
|
||||
contents: `export const NgModule = () => null;
|
||||
export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n`
|
||||
};
|
||||
|
||||
const {decorationAnalyses, program, renderer, switchMarkerAnalyses} =
|
||||
createTestRenderer([CORE_FILE], {isCore: true});
|
||||
renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses);
|
||||
const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toContain(`/*@__PURE__*/ setClassMetadata(`);
|
||||
const addImportsSpy = renderer.addImports as jasmine.Spy;
|
||||
expect(addImportsSpy.calls.first().args[1]).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {ClassMemberKind, ReflectionHost} from '../../host';
|
||||
|
||||
const TS_DTS_JS_EXTENSION = /(\.d)?\.ts|\.js$/;
|
||||
const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
|
||||
|
||||
/**
|
||||
* Represents a value which cannot be determined statically.
|
||||
|
@ -42,6 +42,7 @@ function flipIvySwitchInFile(sf: ts.SourceFile): ts.SourceFile {
|
||||
|
||||
// Only update the statements in the SourceFile if any have changed.
|
||||
if (newStatements !== undefined) {
|
||||
sf = ts.getMutableClone(sf);
|
||||
sf.statements = ts.createNodeArray(newStatements);
|
||||
}
|
||||
return sf;
|
||||
@ -119,9 +120,6 @@ function flipIvySwitchesInVariableStatement(
|
||||
/* name */ decl.name,
|
||||
/* type */ decl.type,
|
||||
/* initializer */ newIdentifier);
|
||||
|
||||
// Keeping parent pointers up to date is important for emit.
|
||||
newIdentifier.parent = newDeclarations[i];
|
||||
}
|
||||
|
||||
const newDeclList = ts.updateVariableDeclarationList(
|
||||
@ -133,12 +131,6 @@ function flipIvySwitchesInVariableStatement(
|
||||
/* modifiers */ stmt.modifiers,
|
||||
/* declarationList */ newDeclList);
|
||||
|
||||
// Keeping parent pointers up to date is important for emit.
|
||||
for (const decl of newDeclarations) {
|
||||
decl.parent = newDeclList;
|
||||
}
|
||||
newDeclList.parent = newStmt;
|
||||
newStmt.parent = stmt.parent;
|
||||
return newStmt;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user