Compare commits
133 Commits
Author | SHA1 | Date | |
---|---|---|---|
13d176302b | |||
e3b801053a | |||
6626739798 | |||
396033da80 | |||
02ee9d2938 | |||
a4c7f183d7 | |||
d690eec88f | |||
10e4dfae27 | |||
f8d948b46b | |||
9cf78d5701 | |||
45471dbbd6 | |||
387e8386d1 | |||
eec6e4be7a | |||
b711f25892 | |||
788f0453f7 | |||
45b1775a53 | |||
53e4ff72d2 | |||
7813a7d129 | |||
c0ced6dc2d | |||
8157ee87b0 | |||
ab051aba27 | |||
5a61ef0c49 | |||
c451dbda9f | |||
0d38288078 | |||
2e9b953e9d | |||
a94383f168 | |||
75c40ddd61 | |||
86a75a0670 | |||
c776825fdd | |||
1cc9383d91 | |||
8ed1e53e99 | |||
7833c88ac4 | |||
12f177399f | |||
5be32366be | |||
5b7d2eeabf | |||
6cd10a1b10 | |||
822652aa0d | |||
cf47ace493 | |||
0595f11950 | |||
35df312ea4 | |||
489eb8519e | |||
b76a2dc2cb | |||
f2f5f7fc6e | |||
8ee23ba67b | |||
ecb422b360 | |||
60389d5441 | |||
b186db70db | |||
324b6f1b1a | |||
cdba1d37a4 | |||
dc42c97ee4 | |||
bc00e8d312 | |||
720b71d01f | |||
1132b07c53 | |||
9230194794 | |||
d724896f04 | |||
29866dfb91 | |||
a249622159 | |||
9f2393fb80 | |||
d5f8040d0a | |||
e0b8ea136b | |||
879b2273c1 | |||
f24972b1b1 | |||
d2886b3bb4 | |||
f296fea112 | |||
2605fc46e7 | |||
9d54b3a14b | |||
d09a6283ed | |||
1c168c3a44 | |||
0f74479c47 | |||
790bb949f6 | |||
2adcad6dd2 | |||
242ef1ace1 | |||
842b6a1247 | |||
98335529eb | |||
ca7ee794bf | |||
f9f2ba6faf | |||
aea1d211d4 | |||
57a518a36d | |||
29b83189b0 | |||
1d3df7885d | |||
fd06ffa2af | |||
36a1622dd1 | |||
7a91b23cb5 | |||
4b90b6a226 | |||
b13daa4cdf | |||
0c6f026828 | |||
a2520bd267 | |||
b928a209a4 | |||
89e16ed6a5 | |||
1a1f99af37 | |||
df2cd37ed2 | |||
12a71bc6bc | |||
7d270c235a | |||
b0b7248504 | |||
78460c1848 | |||
75b119eafc | |||
64b0ae93f7 | |||
7c0b25f5a6 | |||
07b5df3a19 | |||
e7023726f4 | |||
a9ccd9254c | |||
335f3271d2 | |||
7f93f7ef47 | |||
cf46a87fcd | |||
ad6680f602 | |||
5e287f67af | |||
ecfe6e0609 | |||
df9790dd11 | |||
67cfc4c9bc | |||
a68e623c80 | |||
9e3915ba48 | |||
ba2de61748 | |||
a9a4edebe2 | |||
64f2ffa166 | |||
13020b9cc2 | |||
96b96fba0f | |||
2cbe53a9ba | |||
48755114e5 | |||
a5d5f67be7 | |||
dfb58c44a2 | |||
69948ce919 | |||
3190ccf3b2 | |||
a8ea8173aa | |||
e13a49d1f0 | |||
2f0b8f675a | |||
c2aed033ba | |||
0f8a780b0d | |||
c5bc2e77c8 | |||
079310dc7c | |||
0d2cdf6165 | |||
436dde271f | |||
96891a076f | |||
9ce0067bdf |
@ -32,7 +32,7 @@ var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelver
|
||||
|
||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||
var_5: &components_repo_unit_tests_cache_key v7-angular-components-189d98e8b01b33974328255f085de04251d61567
|
||||
var_5: &components_repo_unit_tests_cache_key v7-angular-components-f428c00465dfcf8a020237f22532480eedbd2cb6
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||
|
@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "189d98e8b01b33974328255f085de04251d61567"
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "f428c00465dfcf8a020237f22532480eedbd2cb6"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
145
.gitmessage
Normal file
145
.gitmessage
Normal file
@ -0,0 +1,145 @@
|
||||
<type>(<scope>): <summary>
|
||||
|
||||
<Describe the motivation behind this change - explain WHY you are making this change. Wrap all lines
|
||||
at 100 characters.>
|
||||
|
||||
Fixes #<issue number>
|
||||
|
||||
# ────────────────────────────────────────── 100 chars ────────────────────────────────────────────┤
|
||||
|
||||
|
||||
# Example Commit Messages
|
||||
# =======================
|
||||
|
||||
|
||||
# ─── Example: Simple refactor ────────────────────────────────────────────────────────────────────┤
|
||||
# refactor(core): rename refreshDynamicEmbeddedViews to refreshEmbeddedViews
|
||||
#
|
||||
# Improve code readability. The original name no longer matches how the function is used.
|
||||
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
||||
|
||||
# ─── Example: Simple docs change ─────────────────────────────────────────────────────────────────┤
|
||||
# docs: clarify the service limitation in providers.md guide
|
||||
#
|
||||
# Fixes #36332
|
||||
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
||||
|
||||
# ─── Example: A bug fix ──────────────────────────────────────────────────────────────────────────┤
|
||||
# fix(ngcc): ensure lockfile is removed when `analyzeFn` fails
|
||||
#
|
||||
# Previously an error thrown in the `analyzeFn` would cause the ngcc process to exit immediately
|
||||
# without removing the lockfile, and potentially before the unlocker process had been successfully
|
||||
# spawned resulting in the lockfile being orphaned and left behind.
|
||||
#
|
||||
# Now we catch these errors and remove the lockfile as needed.
|
||||
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
||||
|
||||
# ─── Example: Breaking change ────────────────────────────────────────────────────────────────────┤
|
||||
# feat(bazel): simplify ng_package by dropping esm5 and fesm5
|
||||
#
|
||||
# esm5 and fesm5 distributions are no longer needed and have been deprecated in the past.
|
||||
#
|
||||
# https://v9.angular.io/guide/deprecations#esm5-and-fesm5-code-formats-in-angular-npm-packages
|
||||
#
|
||||
# This commit modifies ng_package to no longer distribute these two formats in npm packages built by
|
||||
# ng_package (e.g. @angular/core).
|
||||
#
|
||||
# This commit intentionally doesn't fully clean up the ng_package rule to remove all traces of esm5
|
||||
# and fems5 build artifacts as that is a bigger cleanup and currently we are narrowing down the
|
||||
# scope of this change to the MVP needed for v10, which in this case is 'do not put esm5 and fesm5'
|
||||
# into the npm packages.
|
||||
#
|
||||
# More cleanup to follow: https://angular-team.atlassian.net/browse/FW-2143
|
||||
#
|
||||
# BREAKING CHANGE: esm5 and fesm5 format is no longer distributed in Angular's npm packages e.g.
|
||||
# @angular/core
|
||||
#
|
||||
# Angular CLI will automatically downlevel the code to es5 if differential loading is enabled in the
|
||||
# Angular project, so no action is required from Angular CLI users.
|
||||
#
|
||||
# If you are not using Angular CLI to build your application or library, and you need to be able to
|
||||
# build es5 artifacts, then you will need to downlevel the distributed Angular code to es5 on your
|
||||
# own.
|
||||
#
|
||||
#
|
||||
# Fixes #1234
|
||||
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
|
||||
|
||||
|
||||
# Angular Commit Message Format
|
||||
# =============================
|
||||
#
|
||||
# The full specification of the Angular Commit Message Format can be found at
|
||||
# https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
|
||||
#
|
||||
# The following is an excerpt of the specification with the most commonly needed info.
|
||||
#
|
||||
# Each commit message consists of a *header*, a *body*, and a *footer*.
|
||||
#
|
||||
# <header>
|
||||
# <BLANK LINE>
|
||||
# <body>
|
||||
# <BLANK LINE>
|
||||
# <footer>
|
||||
#
|
||||
# The header is mandatory.
|
||||
#
|
||||
# The body is mandatory for all commits except for those of scope "docs". When the body is required
|
||||
# it must be at least 20 characters long.
|
||||
#
|
||||
# The footer is optional.
|
||||
#
|
||||
# Any line of the commit message cannot be longer than 100 characters.
|
||||
#
|
||||
#
|
||||
# Commit Message Header
|
||||
# ---------------------
|
||||
#
|
||||
# <type>(<scope>): <short summary>
|
||||
# │ │ │
|
||||
# │ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
||||
# │ │
|
||||
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||
# │ elements|forms|http|language-service|localize|platform-browser|
|
||||
# │ platform-browser-dynamic|platform-server|platform-webworker|
|
||||
# │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
||||
# │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
||||
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
|
||||
# │
|
||||
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||
# https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type
|
||||
#
|
||||
#
|
||||
# Commit Message Body
|
||||
# ---------------------
|
||||
#
|
||||
# Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
|
||||
#
|
||||
# Explain the motivation for the change in the commit message body. This commit message should
|
||||
# explain WHY you are making the change. You can include a comparison of the previous behavior with
|
||||
# the new behavior in order to illustrate the impact of the change.
|
||||
#
|
||||
#
|
||||
# Commit Message Footer
|
||||
# ---------------------
|
||||
#
|
||||
# The footer can contain information about breaking changes and is also the place to reference
|
||||
# GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
|
||||
#
|
||||
# ```
|
||||
# BREAKING CHANGE: <breaking change summary>
|
||||
# <BLANK LINE>
|
||||
# <breaking change description + migration instructions>
|
||||
# <BLANK LINE>
|
||||
# <BLANK LINE>
|
||||
# Fixes #<issue number>
|
||||
# ```
|
||||
#
|
||||
# Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of
|
||||
# the breaking change, a blank line, and a detailed description of the breaking change that also
|
||||
# includes migration instructions.
|
||||
#
|
52
.ng-dev/commit-message.ts
Normal file
52
.ng-dev/commit-message.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {CommitMessageConfig} from '../dev-infra/commit-message/config';
|
||||
|
||||
/**
|
||||
* The configuration for `ng-dev commit-message` commands.
|
||||
*/
|
||||
export const commitMessage: CommitMessageConfig = {
|
||||
maxLineLength: 120,
|
||||
minBodyLength: 20,
|
||||
minBodyLengthTypeExcludes: ['docs'],
|
||||
types: [
|
||||
'build',
|
||||
'ci',
|
||||
'docs',
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'release',
|
||||
'style',
|
||||
'test',
|
||||
],
|
||||
scopes: [
|
||||
'animations',
|
||||
'bazel',
|
||||
'benchpress',
|
||||
'changelog',
|
||||
'common',
|
||||
'compiler',
|
||||
'compiler-cli',
|
||||
'core',
|
||||
'dev-infra',
|
||||
'docs-infra',
|
||||
'elements',
|
||||
'forms',
|
||||
'http',
|
||||
'language-service',
|
||||
'localize',
|
||||
'migrations',
|
||||
'ngcc',
|
||||
'packaging',
|
||||
'platform-browser',
|
||||
'platform-browser-dynamic',
|
||||
'platform-server',
|
||||
'platform-webworker',
|
||||
'platform-webworker-dynamic',
|
||||
'router',
|
||||
'service-worker',
|
||||
'upgrade',
|
||||
've',
|
||||
'zone.js',
|
||||
]
|
||||
};
|
@ -1,116 +1,8 @@
|
||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
||||
import {commitMessage} from './commit-message';
|
||||
import {format} from './format';
|
||||
import {github} from './github';
|
||||
import {merge} from './merge';
|
||||
|
||||
// The configuration for `ng-dev commit-message` commands.
|
||||
const commitMessage = {
|
||||
'maxLength': 120,
|
||||
'minBodyLength': 100,
|
||||
'types': [
|
||||
'build',
|
||||
'ci',
|
||||
'docs',
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'release',
|
||||
'style',
|
||||
'test',
|
||||
],
|
||||
'scopes': [
|
||||
'animations',
|
||||
'bazel',
|
||||
'benchpress',
|
||||
'changelog',
|
||||
'common',
|
||||
'compiler',
|
||||
'compiler-cli',
|
||||
'core',
|
||||
'dev-infra',
|
||||
'docs-infra',
|
||||
'elements',
|
||||
'forms',
|
||||
'http',
|
||||
'language-service',
|
||||
'localize',
|
||||
'migrations',
|
||||
'ngcc',
|
||||
'packaging',
|
||||
'platform-browser',
|
||||
'platform-browser-dynamic',
|
||||
'platform-server',
|
||||
'platform-webworker',
|
||||
'platform-webworker-dynamic',
|
||||
'router',
|
||||
'service-worker',
|
||||
'upgrade',
|
||||
've',
|
||||
'zone.js',
|
||||
]
|
||||
};
|
||||
|
||||
// The configuration for `ng-dev format` commands.
|
||||
const format = {
|
||||
'clang-format': {
|
||||
'matchers': [
|
||||
'**/*.{js,ts}',
|
||||
// TODO: burn down format failures and remove aio and integration exceptions.
|
||||
'!aio/**',
|
||||
'!integration/**',
|
||||
// TODO: remove this exclusion as part of IE deprecation.
|
||||
'!shims_for_IE.js',
|
||||
// Both third_party and .yarn are directories containing copied code which should
|
||||
// not be modified.
|
||||
'!third_party/**',
|
||||
'!.yarn/**',
|
||||
// Do not format d.ts files as they are generated
|
||||
'!**/*.d.ts',
|
||||
]
|
||||
},
|
||||
'buildifier': true
|
||||
};
|
||||
|
||||
/** Github metadata information for `ng-dev` commands. */
|
||||
const github = {
|
||||
owner: 'angular',
|
||||
name: 'angular',
|
||||
};
|
||||
|
||||
// Configuration for the `ng-dev pr merge` command. The command can be used
|
||||
// for merging upstream pull requests into branches based on a PR target label.
|
||||
const merge = () => {
|
||||
// TODO: resume dynamically determining patch branch
|
||||
const patch = '10.0.x';
|
||||
const config: MergeConfig = {
|
||||
githubApiMerge: false,
|
||||
claSignedLabel: 'cla: yes',
|
||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
||||
commitMessageFixupLabel: 'commit message fixup',
|
||||
labels: [
|
||||
{
|
||||
pattern: 'PR target: master-only',
|
||||
branches: ['master'],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: patch-only',
|
||||
branches: [patch],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: master & patch',
|
||||
branches: ['master', patch],
|
||||
},
|
||||
],
|
||||
requiredBaseCommits: {
|
||||
// PRs that target either `master` or the patch branch, need to be rebased
|
||||
// on top of the latest commit message validation fix.
|
||||
// These SHAs are the commits that update the required license text in the header.
|
||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
||||
},
|
||||
};
|
||||
return config;
|
||||
};
|
||||
|
||||
// Export function to build ng-dev configuration object.
|
||||
module.exports = {
|
||||
commitMessage,
|
||||
format,
|
||||
|
22
.ng-dev/format.ts
Normal file
22
.ng-dev/format.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {FormatConfig} from '../dev-infra/format/config';
|
||||
|
||||
/**
|
||||
* Configuration for the `ng-dev format` command.
|
||||
*/
|
||||
export const format: FormatConfig = {
|
||||
'clang-format': {
|
||||
'matchers': [
|
||||
'**/*.{js,ts}',
|
||||
// TODO: burn down format failures and remove aio and integration exceptions.
|
||||
'!aio/**',
|
||||
'!integration/**',
|
||||
// Both third_party and .yarn are directories containing copied code which should
|
||||
// not be modified.
|
||||
'!third_party/**',
|
||||
'!.yarn/**',
|
||||
// Do not format d.ts files as they are generated
|
||||
'!**/*.d.ts',
|
||||
]
|
||||
},
|
||||
'buildifier': true
|
||||
};
|
15
.ng-dev/gitconfig
Normal file
15
.ng-dev/gitconfig
Normal file
@ -0,0 +1,15 @@
|
||||
# The file is inert unless it's explicitly included into the local git config via:
|
||||
#
|
||||
# ```
|
||||
# git config --add include.path '../.ng-dev/gitconfig'
|
||||
# ```
|
||||
#
|
||||
# Calling that command will append the following into `.git/config` of the current git workspace
|
||||
# (i.e. $GIT_DIR, typically `angular/.git/config`):
|
||||
#
|
||||
# ```
|
||||
# [include]
|
||||
# path = ../.ng-dev/gitconfig
|
||||
# ```
|
||||
[commit]
|
||||
template = .gitmessage
|
11
.ng-dev/github.ts
Normal file
11
.ng-dev/github.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {GithubConfig} from '../dev-infra/utils/config';
|
||||
|
||||
/**
|
||||
* Github configuration for the `ng-dev` command. This repository is used as
|
||||
* remote for the merge script and other utilities like `ng-dev pr rebase`.
|
||||
*/
|
||||
|
||||
export const github: GithubConfig = {
|
||||
owner: 'angular',
|
||||
name: 'angular'
|
||||
};
|
38
.ng-dev/merge.ts
Normal file
38
.ng-dev/merge.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
||||
|
||||
/**
|
||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||
* are respected by the merge script (e.g. the target labels).
|
||||
*/
|
||||
export const merge = (): MergeConfig => {
|
||||
// TODO: resume dynamically determining patch branch
|
||||
const patch = '10.0.x';
|
||||
return {
|
||||
githubApiMerge: false,
|
||||
claSignedLabel: 'cla: yes',
|
||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
||||
caretakerNoteLabel: 'PR action: merge-assistance',
|
||||
commitMessageFixupLabel: 'commit message fixup',
|
||||
labels: [
|
||||
{
|
||||
pattern: 'PR target: master-only',
|
||||
branches: ['master'],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: patch-only',
|
||||
branches: [patch],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: master & patch',
|
||||
branches: ['master', patch],
|
||||
},
|
||||
],
|
||||
requiredBaseCommits: {
|
||||
// PRs that target either `master` or the patch branch, need to be rebased
|
||||
// on top of the latest commit message validation fix.
|
||||
// These SHAs are the commits that update the required license text in the header.
|
||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
||||
},
|
||||
};
|
||||
};
|
@ -362,6 +362,7 @@ groups:
|
||||
'aio/content/images/guide/pipes/**',
|
||||
'aio/content/guide/providers.md',
|
||||
'aio/content/examples/providers/**',
|
||||
'aio/content/images/guide/providers/**',
|
||||
'aio/content/guide/singleton-services.md',
|
||||
'aio/content/guide/set-document-title.md',
|
||||
'aio/content/examples/set-document-title/**',
|
||||
@ -528,6 +529,7 @@ groups:
|
||||
'packages/examples/router/**',
|
||||
'aio/content/guide/router.md',
|
||||
'aio/content/guide/router-tutorial.md',
|
||||
'aio/content/guide/router-tutorial-toh.md',
|
||||
'aio/content/examples/router-tutorial/**',
|
||||
'aio/content/examples/router/**',
|
||||
'aio/content/images/guide/router/**'
|
||||
@ -608,6 +610,14 @@ groups:
|
||||
contains_any_globs(files.exclude('packages/compiler-cli/**'), [
|
||||
'**/testing/**',
|
||||
'aio/content/guide/testing.md',
|
||||
'aio/content/guide/test-debugging.md',
|
||||
'aio/content/guide/testing-attribute-directives.md',
|
||||
'aio/content/guide/testing-code-coverage.md',
|
||||
'aio/content/guide/testing-components-basics.md',
|
||||
'aio/content/guide/testing-components-scenarios.md',
|
||||
'aio/content/guide/testing-pipes.md',
|
||||
'aio/content/guide/testing-services.md',
|
||||
'aio/content/guide/testing-utility-apis.md',
|
||||
'aio/content/examples/testing/**',
|
||||
'aio/content/images/guide/testing/**'
|
||||
])
|
||||
@ -1036,7 +1046,7 @@ groups:
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
contains_any_globs(files.exclude("CHANGELOG.md"), [
|
||||
contains_any_globs(files.exclude("CHANGELOG.md").exclude("packages/compiler-cli/**/BUILD.bazel"), [
|
||||
'*',
|
||||
'.circleci/**',
|
||||
'.devcontainer/**',
|
||||
|
@ -24,7 +24,7 @@ filegroup(
|
||||
"//packages/zone.js/dist:zone-testing.js",
|
||||
"//packages/zone.js/dist:task-tracking.js",
|
||||
"//:test-events.js",
|
||||
"//:shims_for_IE.js",
|
||||
"//:third_party/shims_for_IE.js",
|
||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
"@npm//:node_modules/systemjs/dist/system.src.js",
|
||||
"@npm//:node_modules/reflect-metadata/Reflect.js",
|
||||
|
927
CHANGELOG.md
927
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
297
CONTRIBUTING.md
297
CONTRIBUTING.md
@ -1,7 +1,7 @@
|
||||
# Contributing to Angular
|
||||
|
||||
We would love for you to contribute to Angular and help make it even better than it is
|
||||
today! As a contributor, here are the guidelines we would like you to follow:
|
||||
We would love for you to contribute to Angular and help make it even better than it is today!
|
||||
As a contributor, here are the guidelines we would like you to follow:
|
||||
|
||||
- [Code of Conduct](#coc)
|
||||
- [Question or Problem?](#question)
|
||||
@ -12,12 +12,17 @@ today! As a contributor, here are the guidelines we would like you to follow:
|
||||
- [Commit Message Guidelines](#commit)
|
||||
- [Signing the CLA](#cla)
|
||||
|
||||
|
||||
## <a name="coc"></a> Code of Conduct
|
||||
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
|
||||
|
||||
Help us keep Angular open and inclusive.
|
||||
Please read and follow our [Code of Conduct][coc].
|
||||
|
||||
|
||||
## <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
|
||||
Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
|
||||
|
||||
Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
@ -29,35 +34,41 @@ To save your and our time, we will systematically close all issues that are requ
|
||||
|
||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||
|
||||
|
||||
## <a name="issue"></a> Found a Bug?
|
||||
If you find a bug in the source code, you can help us by
|
||||
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||
[submit a Pull Request](#submit-pr) with a fix.
|
||||
|
||||
If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github].
|
||||
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
|
||||
|
||||
|
||||
## <a name="feature"></a> Missing a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
|
||||
Repository. If you would like to *implement* a new feature, please submit an issue with
|
||||
a proposal for your work first, to be sure that we can use it.
|
||||
Please consider what kind of change it is:
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
|
||||
If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed:
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
|
||||
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
|
||||
|
||||
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||
and help you to craft the change so that it is successfully accepted into the project. **Note**:
|
||||
Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major
|
||||
feature.
|
||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
|
||||
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we will systematically ask you to provide a minimal reproduction. Having a minimal reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions.
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
|
||||
In order to reproduce bugs, we require that you provide a minimal reproduction.
|
||||
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
|
||||
|
||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
||||
|
||||
We will be insisting on a minimal reproduction scenario in order to save maintainers' time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
||||
We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs.
|
||||
Often, developers find coding problems themselves while preparing a minimal reproduction.
|
||||
We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
||||
|
||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
||||
|
||||
@ -65,56 +76,66 @@ You can file new issues by selecting from our [new issue templates](https://gith
|
||||
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
||||
Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
|
||||
1. Fork the angular/angular repo.
|
||||
1. Make your changes in a new git branch:
|
||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
|
||||
You don't want to duplicate existing efforts.
|
||||
|
||||
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
||||
Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||
|
||||
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without a signed CLA.
|
||||
Make sure you author all contributed Git commits with email address associated with your CLA signature.
|
||||
|
||||
4. Fork the angular/angular repo.
|
||||
|
||||
5. Make your changes in a new git branch:
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
1. Create your patch, **including appropriate test cases**.
|
||||
1. Follow our [Coding Rules](#rules).
|
||||
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||
and ensure that all tests pass.
|
||||
1. Commit your changes using a descriptive commit message that follows our
|
||||
[commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||
6. Create your patch, **including appropriate test cases**.
|
||||
|
||||
7. Follow our [Coding Rules](#rules).
|
||||
|
||||
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
|
||||
|
||||
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
|
||||
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
|
||||
1. Push your branch to GitHub:
|
||||
10. Push your branch to GitHub:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
1. In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
11. In GitHub, send a pull request to `angular:master`.
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
If we ask for changes via code reviews then:
|
||||
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
|
||||
#### After your pull request is merged
|
||||
|
||||
After your pull request is merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||
|
||||
@ -140,55 +161,66 @@ from the main (upstream) repository:
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
|
||||
## <a name="rules"></a> Coding Rules
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||
* All public API methods **must be documented**. (Details TBC).
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available, see
|
||||
[DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||
* All public API methods **must be documented**.
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
|
||||
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||
|
||||
We have very precise rules over how our git commit messages can be formatted. This leads to **more
|
||||
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||
we use the git commit messages to **generate the Angular change log**.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
## <a name="commit"></a> Commit Message Format
|
||||
|
||||
*This specification is inspired and supersedes the [AngularJS commit message format][commit-message-format].*
|
||||
|
||||
We have very precise rules over how our Git commit messages must be formatted.
|
||||
This format leads to **easier to read commit history**.
|
||||
|
||||
Each commit message consists of a **header**, a **body**, and a **footer**.
|
||||
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<header>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
|
||||
|
||||
Any line of the commit message cannot be longer than 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
The `body` is mandatory for all commits except for those of scope "docs".
|
||||
When the body is required it must be at least 20 characters long.
|
||||
|
||||
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
The `footer` is optional.
|
||||
|
||||
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||
Any line of the commit message cannot be longer than 100 characters.
|
||||
|
||||
|
||||
#### <a href="commit-header"></a>Commit Message Header
|
||||
|
||||
```
|
||||
docs(changelog): update changelog to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on the latest rxjs and zone.js
|
||||
|
||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||
<type>(<scope>): <short summary>
|
||||
│ │ │
|
||||
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
||||
│ │
|
||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||
│ elements|forms|http|language-service|localize|platform-browser|
|
||||
│ platform-browser-dynamic|platform-server|platform-webworker|
|
||||
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
||||
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
||||
│
|
||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||
```
|
||||
|
||||
### Revert
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
||||
|
||||
|
||||
##### Type
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
@ -201,67 +233,95 @@ Must be one of the following:
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
|
||||
##### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
* **animations**
|
||||
* **bazel**
|
||||
* **benchpress**
|
||||
* **common**
|
||||
* **compiler**
|
||||
* **compiler-cli**
|
||||
* **core**
|
||||
* **elements**
|
||||
* **forms**
|
||||
* **http**
|
||||
* **language-service**
|
||||
* **localize**
|
||||
* **platform-browser**
|
||||
* **platform-browser-dynamic**
|
||||
* **platform-server**
|
||||
* **platform-webworker**
|
||||
* **platform-webworker-dynamic**
|
||||
* **router**
|
||||
* **service-worker**
|
||||
* **upgrade**
|
||||
* **zone.js**
|
||||
* `animations`
|
||||
* `bazel`
|
||||
* `benchpress`
|
||||
* `common`
|
||||
* `compiler`
|
||||
* `compiler-cli`
|
||||
* `core`
|
||||
* `elements`
|
||||
* `forms`
|
||||
* `http`
|
||||
* `language-service`
|
||||
* `localize`
|
||||
* `platform-browser`
|
||||
* `platform-browser-dynamic`
|
||||
* `platform-server`
|
||||
* `platform-webworker`
|
||||
* `platform-webworker-dynamic`
|
||||
* `router`
|
||||
* `service-worker`
|
||||
* `upgrade`
|
||||
* `zone.js`
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g.
|
||||
public path changes, package.json changes done to all packages, d.ts file/format changes, changes
|
||||
to bundles, etc.
|
||||
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
||||
repo
|
||||
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||
* **migrations**: used for changes to the `ng update` migrations.
|
||||
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
||||
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
|
||||
specific package (e.g. `docs: fix typo in tutorial`).
|
||||
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||
|
||||
### Subject
|
||||
The subject contains a succinct description of the change:
|
||||
* `changelog`: used for updating the release notes in CHANGELOG.md
|
||||
|
||||
* `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||
|
||||
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||
|
||||
* `migrations`: used for changes to the `ng update` migrations.
|
||||
|
||||
* `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||
|
||||
* `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
|
||||
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
|
||||
|
||||
|
||||
##### Summary
|
||||
|
||||
Use the summary field to provide a succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize the first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
### Body
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
|
||||
The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Footer
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
#### Commit Message Body
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
|
||||
|
||||
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
|
||||
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
|
||||
|
||||
|
||||
#### Commit Message Footer
|
||||
|
||||
The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
|
||||
|
||||
```
|
||||
BREAKING CHANGE: <breaking change summary>
|
||||
<BLANK LINE>
|
||||
<breaking change description + migration instructions>
|
||||
<BLANK LINE>
|
||||
<BLANK LINE>
|
||||
Fixes #<issue number>
|
||||
```
|
||||
|
||||
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
||||
|
||||
|
||||
### Revert commits
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
|
||||
|
||||
The content of the commit message body should contain:
|
||||
|
||||
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
||||
- a clear description of the reason for reverting the commit message.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
|
||||
@ -272,18 +332,17 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
* For corporations, we'll need you to
|
||||
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||
|
||||
<hr>
|
||||
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
|
||||
|
||||
If you have more than one Git identity, you must make sure that you sign the CLA using the primary email address associated with the ID that has been granted access to the Angular repository. Git identities can be associated with more than one email address, and only one is primary. Here are some links to help you sort out multiple Git identities and email addresses:
|
||||
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
|
||||
|
||||
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
|
||||
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
|
||||
* https://help.github.com/articles/about-commit-email-addresses/
|
||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||
|
||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA before you commit changes. If not, your PR will fail the CLA check.
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
[angular-group]: https://groups.google.com/forum/#!forum/angular
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
|
@ -58,7 +58,7 @@
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"src/styles/main.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"budgets": [
|
||||
@ -158,7 +158,7 @@
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"src/styles/main.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
@ -193,4 +193,4 @@
|
||||
}
|
||||
},
|
||||
"defaultProject": "site"
|
||||
}
|
||||
}
|
||||
|
@ -32,15 +32,15 @@ export const slideInAnimation =
|
||||
// #enddocregion style-view
|
||||
// #docregion query
|
||||
query(':enter', [
|
||||
style({ left: '-100%'})
|
||||
style({ left: '-100%' })
|
||||
]),
|
||||
query(':leave', animateChild()),
|
||||
group([
|
||||
query(':leave', [
|
||||
animate('300ms ease-out', style({ left: '100%'}))
|
||||
animate('300ms ease-out', style({ left: '100%' }))
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%'}))
|
||||
animate('300ms ease-out', style({ left: '0%' }))
|
||||
])
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
@ -56,15 +56,15 @@ export const slideInAnimation =
|
||||
})
|
||||
]),
|
||||
query(':enter', [
|
||||
style({ left: '-100%'})
|
||||
style({ left: '-100%' })
|
||||
]),
|
||||
query(':leave', animateChild()),
|
||||
group([
|
||||
query(':leave', [
|
||||
animate('200ms ease-out', style({ left: '100%'}))
|
||||
animate('200ms ease-out', style({ left: '100%' }))
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%'}))
|
||||
animate('300ms ease-out', style({ left: '0%' }))
|
||||
])
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
|
@ -17,7 +17,7 @@ Toggle All Animations <input type="checkbox" [checked]="!animationsDisabled" (cl
|
||||
</nav>
|
||||
|
||||
<!-- #docregion route-animations-outlet -->
|
||||
<div [@routeAnimations]="prepareRoute(outlet)" >
|
||||
<div [@routeAnimations]="prepareRoute(outlet)">
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
</div>
|
||||
<!-- #enddocregion route-animations-outlet -->
|
||||
|
@ -76,15 +76,15 @@ export class ConfigService {
|
||||
console.error('An error occurred:', error.error.message);
|
||||
} else {
|
||||
// The backend returned an unsuccessful response code.
|
||||
// The response body may contain clues as to what went wrong,
|
||||
// The response body may contain clues as to what went wrong.
|
||||
console.error(
|
||||
`Backend returned code ${error.status}, ` +
|
||||
`body was: ${error.error}`);
|
||||
}
|
||||
// return an observable with a user-facing error message
|
||||
// Return an observable with a user-facing error message.
|
||||
return throwError(
|
||||
'Something bad happened; please try again later.');
|
||||
};
|
||||
}
|
||||
// #enddocregion handleError
|
||||
|
||||
makeIntentionalError() {
|
||||
|
@ -3,7 +3,9 @@
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0-9].*"
|
||||
"!**/*.[0-9].*",
|
||||
"!doc-files/**/*",
|
||||
"**/*.xlf"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["Angular", "i18n", "internationalization"]
|
||||
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
"!dist/",
|
||||
"!**/*.d.ts",
|
||||
"!src/**/*.js",
|
||||
"!doc-files/**/*",
|
||||
"**/*.xlf"
|
||||
]
|
||||
}
|
@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
||||
templateUrl: './app.component.html'
|
||||
})
|
||||
export class AppComponent {
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
||||
}
|
||||
|
@ -8,5 +8,5 @@ import { Component } from '@angular/core';
|
||||
// #enddocregion hero-birthday-template
|
||||
})
|
||||
export class HeroBirthdayComponent {
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
// #docregion class
|
||||
export class HeroBirthday2Component {
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
||||
toggle = true; // start with true == shortDate
|
||||
|
||||
get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
|
||||
|
7
aio/content/examples/providers/src/app/user.service.2.ts
Normal file
7
aio/content/examples/providers/src/app/user.service.2.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'any',
|
||||
})
|
||||
export class UserService {
|
||||
}
|
@ -6,13 +6,11 @@ import { Routes, RouterModule } from '@angular/router'; // CLI imports router
|
||||
const routes: Routes = [
|
||||
{ path: 'first-component', component: FirstComponent },
|
||||
{ path: 'second-component', component: SecondComponent },
|
||||
// #enddocregion routes
|
||||
// #enddocregion routes, routes-with-wildcard
|
||||
{ path: '', redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component`
|
||||
{ path: '**', component: FirstComponent },
|
||||
// #enddocregion redirect
|
||||
// #docregion routes-with-wildcard
|
||||
{ path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page
|
||||
// #docregion routes
|
||||
// #docregion redirect
|
||||
];
|
||||
// #enddocregion routes, routes-with-wildcard, redirect
|
||||
|
||||
|
@ -33,7 +33,7 @@ export class HeroesComponent implements OnInit {
|
||||
|
||||
onSelect(hero: Hero): void {
|
||||
this.selectedHero = hero;
|
||||
this.messageService.add(`HeroService: Selected hero id=${hero.id}`);
|
||||
this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`);
|
||||
}
|
||||
|
||||
// #docregion getHeroes
|
||||
|
@ -197,11 +197,11 @@ Like `EvenBetterLogger`, `HeroService` needs to know if the user is authorized
|
||||
That authorization can change during the course of a single application session,
|
||||
as when you log in a different user.
|
||||
|
||||
Let's say you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information.
|
||||
Imagine that you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information.
|
||||
`HeroService` won't have direct access to the user information to decide
|
||||
who is authorized and who isn't.
|
||||
|
||||
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes.
|
||||
To resolve this, give the `HeroService` constructor a boolean flag to control display of secret heroes.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>
|
||||
|
||||
|
@ -320,7 +320,7 @@ You can dramatically reduce launch time by only loading the application modules
|
||||
absolutely must be present when the app starts.
|
||||
|
||||
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
|
||||
[waiting until the app has launched](guide/router#preloading "Preloading")
|
||||
[waiting until the app has launched](guide/router-tutorial-toh#preloading "Preloading")
|
||||
or by [_lazy loading_](guide/router#lazy-loading "Lazy loading")
|
||||
them on demand.
|
||||
|
||||
|
@ -119,7 +119,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
|
||||
|
||||
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
|
||||
|
||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --name=*your_project_name*`.
|
||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --project=*your_project_name*`.
|
||||
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
||||
|
||||
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
|
||||
@ -186,7 +186,7 @@ aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
|
||||
aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
|
||||
```
|
||||
|
||||
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for you custom element. But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
|
||||
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for your custom element. But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
|
||||
|
||||
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLElementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
|
||||
|
||||
|
@ -117,7 +117,7 @@ As users change values and make selections through the view, the new values must
|
||||
Similarly, when the program logic changes values in the data model, those values must be reflected in the view.
|
||||
|
||||
Reactive and template-driven forms differ in how they handle data flowing from the user or from programmatic changes.
|
||||
The following diagrams illustrate both kinds of data flow for each type of form, using the a favorite-color input field defined above.
|
||||
The following diagrams illustrate both kinds of data flow for each type of form, using the favorite-color input field defined above.
|
||||
|
||||
### Data flow in reactive forms
|
||||
|
||||
|
@ -93,7 +93,7 @@ In the course of this tutorial, you bind a sample form to data and handle user i
|
||||
* Add custom CSS to provide visual feedback on the status.
|
||||
* Show and hide validation-error messages.
|
||||
4. Respond to a native HTML button-click event by adding to the model data.
|
||||
5. Handle form submission using the [`ngSubmit`(api/forms/NgForm#properties)] output property of the form.
|
||||
5. Handle form submission using the [`ngSubmit`](api/forms/NgForm#properties) output property of the form.
|
||||
* Disable the **Submit** button until the form is valid.
|
||||
* After submit, swap out the finished form for different content on the page.
|
||||
|
||||
@ -467,7 +467,7 @@ You will bind the form property that indicates its overall validity to the **Sub
|
||||
3. Run the application now. Notice that the button is enabled—although
|
||||
it doesn't do anything useful yet.
|
||||
|
||||
4. Delete the **Name** value. This violates the "required" rule, so it displays the error message&emdash;and notice that it also disables the **Submit** button.
|
||||
4. Delete the **Name** value. This violates the "required" rule, so it displays the error message—and notice that it also disables the **Submit** button.
|
||||
|
||||
|
||||
You didn't have to explicitly wire the button's enabled state to the form's validity.
|
||||
|
@ -732,7 +732,7 @@ The alternative is a [template-driven form](#template-driven-forms).
|
||||
When using reactive forms:
|
||||
|
||||
* The "source of truth", the form model, is defined in the component class.
|
||||
* Validation is set up through validation functions rather than valdation directives.
|
||||
* Validation is set up through validation functions rather than validation directives.
|
||||
* Each control is explicitly created in the component class by creating a `FormControl` instance manually or with `FormBuilder`.
|
||||
* The template input elements do *not* use `ngModel`.
|
||||
* The associated Angular directives are prefixed with `form`, such as `formControl`, `formGroup`, and `formControlName`.
|
||||
|
@ -277,7 +277,7 @@ searchHeroes(term: string): Observable {
|
||||
return this.http.jsonp(heroesUrl, 'callback').pipe(
|
||||
catchError(this.handleError('searchHeroes', [])) // then handle the error
|
||||
);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
This request passes the `heroesURL` as the first parameter and the callback function name as the second parameter.
|
||||
|
@ -834,7 +834,7 @@ Global variants of the locale data are available in [`@angular/common/locales/gl
|
||||
The following example imports the global variants for French (`fr`):
|
||||
|
||||
<code-example language="javascript" header="app.module.ts">
|
||||
import '@angular/common/locales/global/fr;
|
||||
import '@angular/common/locales/global/fr';
|
||||
</code-example>
|
||||
|
||||
{@a custom-id}
|
||||
|
@ -311,7 +311,7 @@ ngOnInit() {
|
||||
}
|
||||
</code-example>
|
||||
|
||||
For more information with a working example, see the [routing tutorial section on preloading](guide/router#preloading-background-loading-of-feature-areas).
|
||||
For more information with a working example, see the [routing tutorial section on preloading](guide/router-tutorial-toh#preloading-background-loading-of-feature-areas).
|
||||
|
||||
|
||||
<hr>
|
||||
|
@ -495,7 +495,7 @@ for one turn of the browser's JavaScript cycle, which triggers a new change-dete
|
||||
|
||||
#### Write lean hook methods to avoid performance problems
|
||||
|
||||
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`$emdash;often when there are no changes of interest.
|
||||
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`-often when there are no changes of interest.
|
||||
Be very careful about how much logic or computation you put into one of these methods.
|
||||
|
||||
<div class="lightbox">
|
||||
|
@ -5,6 +5,7 @@
|
||||
This migration adds support to existing projects for TypeScript's new ["solution-style" tsconfig feature](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig).
|
||||
|
||||
Support is added by making two changes:
|
||||
|
||||
1. Renaming the workspace-level `tsconfig.json` to `tsconfig.base.json`.
|
||||
All project [TypeScript configuration files](guide/typescript-configuration) will extend from this base which contains the common options used throughout the workspace.
|
||||
|
||||
|
@ -112,7 +112,7 @@ Because observables produce values asynchronously, try/catch will not effectivel
|
||||
<code-example>
|
||||
myObservable.subscribe({
|
||||
next(num) { console.log('Next num: ' + num)},
|
||||
error(err) { console.log('Received an errror: ' + err)}
|
||||
error(err) { console.log('Received an error: ' + err)}
|
||||
});
|
||||
</code-example>
|
||||
|
||||
|
@ -52,6 +52,14 @@ Any component created within a lazy loaded module’s context, such as by router
|
||||
|
||||
Though you can provide services by lazy loading modules, not all services can be lazy loaded. For instance, some modules only work in the root module, such as the Router. The Router works with the global location object in the browser.
|
||||
|
||||
As of Angular version 9, you can provide a new instance of a service with each lazy loaded module. The following code adds this functionality to `UserService`.
|
||||
|
||||
<code-example path="providers/src/app/user.service.2.ts" header="src/app/user.service.ts"></code-example>
|
||||
|
||||
With `providedIn: 'any'`, all eagerly loaded modules share a singleton instance; however, lazy loaded modules each get their own unique instance, as shown in the following diagram.
|
||||
|
||||
<img src="generated/images/guide/providers/any-provider.svg" alt="any-provider-scope" class="left">
|
||||
|
||||
|
||||
## Limiting provider scope with components
|
||||
|
||||
|
@ -101,6 +101,7 @@ The following table provides the status for Angular versions under support.
|
||||
|
||||
Version | Status | Released | Active Ends | LTS Ends
|
||||
------- | ------ | ------------ | ------------ | ------------
|
||||
^10.0.0 | Active | Jun 24, 2020 | Dec 24, 2020 | Dec 24, 2021
|
||||
^9.0.0 | Active | Feb 06, 2020 | Aug 06, 2020 | Aug 06, 2021
|
||||
^8.0.0 | LTS | May 28, 2019 | Nov 28, 2019 | Nov 28, 2020
|
||||
|
||||
|
@ -111,7 +111,7 @@ Let's assume that we are routing from the *Home => About*.
|
||||
|
||||
The animation code does the following after styling the views:
|
||||
|
||||
* `query(':enter style({ left: '-100%'})` matches the view that is added and hides the newly added view by positioning it to the far left.
|
||||
* `query(':enter', style({ left: '-100%' }))` matches the view that is added and hides the newly added view by positioning it to the far left.
|
||||
* Calls `animateChild()` on the view that is leaving, to run its child animations.
|
||||
* Uses `group()` function to make the inner animations run in parallel.
|
||||
* Within the `group()` function:
|
||||
|
2893
aio/content/guide/router-tutorial-toh.md
Normal file
2893
aio/content/guide/router-tutorial-toh.md
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
# Schematics for libraries
|
||||
|
||||
When you create an Angular library, you can provide and package it with schematics that integrate it with the Angular CLI.
|
||||
With your schematics, your users can use `ng add` to install an initial version of your library,
|
||||
With your schematics, your users can use `ng add` to install an initial version of your library,
|
||||
`ng generate` to create artifacts defined in your library, and `ng update` to adjust their project for a new version of your library that introduces breaking changes.
|
||||
|
||||
All three types of schematics can be part of a collection that you package with your library.
|
||||
@ -115,10 +115,10 @@ When you add a schematic to the collection, you have to point to it in the colle
|
||||
<code-example header="projects/my-lib/schematics/my-service/schema.json (Schematic JSON Schema)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.json">
|
||||
</code-example>
|
||||
|
||||
* *id* : A unique id for the schema in the collection.
|
||||
* *title* : A human-readable description of the schema.
|
||||
* *type* : A descriptor for the type provided by the properties.
|
||||
* *properties* : An object that defines the available options for the schematic.
|
||||
* *id*: A unique id for the schema in the collection.
|
||||
* *title*: A human-readable description of the schema.
|
||||
* *type*: A descriptor for the type provided by the properties.
|
||||
* *properties*: An object that defines the available options for the schematic.
|
||||
|
||||
Each option associates key with a type, description, and optional alias.
|
||||
The type defines the shape of the value you expect, and the description is displayed when the user requests usage help for your schematic.
|
||||
@ -130,9 +130,9 @@ When you add a schematic to the collection, you have to point to it in the colle
|
||||
<code-example header="projects/my-lib/schematics/my-service/schema.ts (Schematic Interface)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.ts">
|
||||
</code-example>
|
||||
|
||||
* *name* : The name you want to provide for the created service.
|
||||
* *path* : Overrides the path provided to the schematic. The default path value is based on the current working directory.
|
||||
* *project* : Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user.
|
||||
* *name*: The name you want to provide for the created service.
|
||||
* *path*: Overrides the path provided to the schematic. The default path value is based on the current working directory.
|
||||
* *project*: Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user.
|
||||
|
||||
### Add template files
|
||||
|
||||
@ -169,10 +169,9 @@ The Schematics framework provides a file templating system, which supports both
|
||||
The system operates on placeholders defined inside files or paths that loaded in the input `Tree`.
|
||||
It fills these in using values passed into the `Rule`.
|
||||
|
||||
For details of these data structure and syntax, see the [Schematics README](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/README.md).
|
||||
For details of these data structures and syntax, see the [Schematics README](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/README.md).
|
||||
|
||||
|
||||
1. Create the main file, `index.ts` and add the source code for your schematic factory function.
|
||||
1. Create the main file `index.ts` and add the source code for your schematic factory function.
|
||||
|
||||
1. First, import the schematics definitions you will need. The Schematics framework offers many utility functions to create and use rules when running a schematic.
|
||||
|
||||
@ -271,7 +270,6 @@ For more information about rules and utility methods, see [Provided Rules](https
|
||||
|
||||
After you build your library and schematics, you can install the schematics collection to run against your project. The steps below show you how to generate a service using the schematic you created above.
|
||||
|
||||
|
||||
### Build your library and schematics
|
||||
|
||||
From the root of your workspace, run the `ng build` command for your library.
|
||||
|
28
aio/content/guide/test-debugging.md
Normal file
28
aio/content/guide/test-debugging.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Debugging tests
|
||||
|
||||
If your tests aren't working as you expect them to, you can inspect and debug them in the browser.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Debug specs in the browser in the same way that you debug an application.
|
||||
|
||||
1. Reveal the Karma browser window. See [Set up testing](guide/testing#set-up-testing) if you need help with this step.
|
||||
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
|
||||
1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS).
|
||||
1. Pick the "sources" section.
|
||||
1. Open the `1st.spec.ts` test file (Control/Command-P, then start typing the name of the file).
|
||||
1. Set a breakpoint in the test.
|
||||
1. Refresh the browser, and it stops at the breakpoint.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
|
||||
</div>
|
||||
|
||||
<hr>
|
78
aio/content/guide/testing-attribute-directives.md
Normal file
78
aio/content/guide/testing-attribute-directives.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
{@a attribute-directive}
|
||||
|
||||
# Testing Attribute Directives
|
||||
|
||||
An _attribute directive_ modifies the behavior of an element, component or another directive.
|
||||
Its name reflects the way the directive is applied: as an attribute on a host element.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
## Testing the `HighlightDirective`
|
||||
|
||||
The sample application's `HighlightDirective` sets the background color of an element
|
||||
based on either a data bound color or a default color (lightgray).
|
||||
It also sets a custom property of the element (`customProperty`) to `true`
|
||||
for no reason other than to show that it can.
|
||||
|
||||
<code-example path="testing/src/app/shared/highlight.directive.ts" header="app/shared/highlight.directive.ts"></code-example>
|
||||
|
||||
It's used throughout the application, perhaps most simply in the `AboutComponent`:
|
||||
|
||||
<code-example path="testing/src/app/about/about.component.ts" header="app/about/about.component.ts"></code-example>
|
||||
|
||||
Testing the specific use of the `HighlightDirective` within the `AboutComponent` requires only the techniques explored in the ["Nested component tests"](guide/testing-components-scenarios#nested-component-tests) section of [Component testing scenarios](guide/testing-components-scenarios).
|
||||
|
||||
<code-example path="testing/src/app/about/about.component.spec.ts" region="tests" header="app/about/about.component.spec.ts"></code-example>
|
||||
|
||||
However, testing a single use case is unlikely to explore the full range of a directive's capabilities.
|
||||
Finding and testing all components that use the directive is tedious, brittle, and almost as unlikely to afford full coverage.
|
||||
|
||||
_Class-only tests_ might be helpful,
|
||||
but attribute directives like this one tend to manipulate the DOM.
|
||||
Isolated unit tests don't touch the DOM and, therefore,
|
||||
do not inspire confidence in the directive's efficacy.
|
||||
|
||||
A better solution is to create an artificial test component that demonstrates all ways to apply the directive.
|
||||
|
||||
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="test-component" header="app/shared/highlight.directive.spec.ts (TestComponent)"></code-example>
|
||||
|
||||
<div class="lightbox">
|
||||
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The `<input>` case binds the `HighlightDirective` to the name of a color value in the input box.
|
||||
The initial value is the word "cyan" which should be the background color of the input box.
|
||||
|
||||
</div>
|
||||
|
||||
Here are some tests of this component:
|
||||
|
||||
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="selected-tests" header="app/shared/highlight.directive.spec.ts (selected tests)"></code-example>
|
||||
|
||||
A few techniques are noteworthy:
|
||||
|
||||
- The `By.directive` predicate is a great way to get the elements that have this directive _when their element types are unknown_.
|
||||
|
||||
- The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:not">`:not` pseudo-class</a>
|
||||
in `By.css('h2:not([highlight])')` helps find `<h2>` elements that _do not_ have the directive.
|
||||
`By.css('*:not([highlight])')` finds _any_ element that does not have the directive.
|
||||
|
||||
- `DebugElement.styles` affords access to element styles even in the absence of a real browser, thanks to the `DebugElement` abstraction.
|
||||
But feel free to exploit the `nativeElement` when that seems easier or more clear than the abstraction.
|
||||
|
||||
- Angular adds a directive to the injector of the element to which it is applied.
|
||||
The test for the default color uses the injector of the second `<h2>` to get its `HighlightDirective` instance
|
||||
and its `defaultColor`.
|
||||
|
||||
- `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
|
||||
|
||||
<hr>
|
57
aio/content/guide/testing-code-coverage.md
Normal file
57
aio/content/guide/testing-code-coverage.md
Normal file
@ -0,0 +1,57 @@
|
||||
{@a code-coverage}
|
||||
|
||||
# Find out how much code you're testing
|
||||
|
||||
The CLI can run unit tests and create code coverage reports.
|
||||
Code coverage reports show you any parts of your code base that may not be properly tested by your unit tests.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
To generate a coverage report run the following command in the root of your project.
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng test --no-watch --code-coverage
|
||||
</code-example>
|
||||
|
||||
When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values.
|
||||
|
||||
If you want to create code-coverage reports every time you test, you can set the following option in the CLI configuration file, `angular.json`:
|
||||
|
||||
```
|
||||
"test": {
|
||||
"options": {
|
||||
"codeCoverage": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Code coverage enforcement
|
||||
|
||||
The code coverage percentages let you estimate how much of your code is tested.
|
||||
If your team decides on a set minimum amount to be unit tested, you can enforce this minimum with the Angular CLI.
|
||||
|
||||
For example, suppose you want the code base to have a minimum of 80% code coverage.
|
||||
To enable this, open the [Karma](https://karma-runner.github.io) test platform configuration file, `karma.conf.js`, and add the following in the `coverageIstanbulReporter:` key.
|
||||
|
||||
```
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true,
|
||||
thresholds: {
|
||||
statements: 80,
|
||||
lines: 80,
|
||||
branches: 80,
|
||||
functions: 80
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `thresholds` property causes the tool to enforce a minimum of 80% code coverage when the unit tests are run in the project.
|
||||
|
380
aio/content/guide/testing-components-basics.md
Normal file
380
aio/content/guide/testing-components-basics.md
Normal file
@ -0,0 +1,380 @@
|
||||
# Basics of testing components
|
||||
|
||||
A component, unlike all other parts of an Angular application,
|
||||
combines an HTML template and a TypeScript class.
|
||||
The component truly is the template and the class _working together_. To adequately test a component, you should test that they work together
|
||||
as intended.
|
||||
|
||||
Such tests require creating the component's host element in the browser DOM,
|
||||
as Angular does, and investigating the component class's interaction with
|
||||
the DOM as described by its template.
|
||||
|
||||
The Angular `TestBed` facilitates this kind of testing as you'll see in the sections below.
|
||||
But in many cases, _testing the component class alone_, without DOM involvement,
|
||||
can validate much of the component's behavior in an easier, more obvious way.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{@a component-class-testing}
|
||||
|
||||
## Component class testing
|
||||
|
||||
Test a component class on its own as you would test a service class.
|
||||
|
||||
Component class testing should be kept very clean and simple.
|
||||
It should test only a single unit.
|
||||
At first glance, you should be able to understand
|
||||
what the test is testing.
|
||||
|
||||
Consider this `LightswitchComponent` which toggles a light on and off
|
||||
(represented by an on-screen message) when the user clicks the button.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/demo.ts"
|
||||
region="LightswitchComp"
|
||||
header="app/demo/demo.ts (LightswitchComp)"></code-example>
|
||||
|
||||
You might decide only to test that the `clicked()` method
|
||||
toggles the light's _on/off_ state and sets the message appropriately.
|
||||
|
||||
This component class has no dependencies. To test these types of classes, follow the same steps as you would for a service that has no dependencies:
|
||||
|
||||
1. Create a component using the new keyword.
|
||||
2. Poke at its API.
|
||||
3. Assert expectations on its public state.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/demo.spec.ts"
|
||||
region="Lightswitch"
|
||||
header="app/demo/demo.spec.ts (Lightswitch tests)"></code-example>
|
||||
|
||||
Here is the `DashboardHeroComponent` from the _Tour of Heroes_ tutorial.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/dashboard/dashboard-hero.component.ts"
|
||||
region="class"
|
||||
header="app/dashboard/dashboard-hero.component.ts (component)"></code-example>
|
||||
|
||||
It appears within the template of a parent component,
|
||||
which binds a _hero_ to the `@Input` property and
|
||||
listens for an event raised through the _selected_ `@Output` property.
|
||||
|
||||
You can test that the class code works without creating the `DashboardHeroComponent`
|
||||
or its parent component.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/dashboard/dashboard-hero.component.spec.ts"
|
||||
region="class-only"
|
||||
header="app/dashboard/dashboard-hero.component.spec.ts (class tests)"></code-example>
|
||||
|
||||
When a component has dependencies, you may wish to use the `TestBed` to both
|
||||
create the component and its dependencies.
|
||||
|
||||
The following `WelcomeComponent` depends on the `UserService` to know the name of the user to greet.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/welcome/welcome.component.ts"
|
||||
region="class"
|
||||
header="app/welcome/welcome.component.ts"></code-example>
|
||||
|
||||
You might start by creating a mock of the `UserService` that meets the minimum needs of this component.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/welcome/welcome.component.spec.ts"
|
||||
region="mock-user-service"
|
||||
header="app/welcome/welcome.component.spec.ts (MockUserService)"></code-example>
|
||||
|
||||
Then provide and inject _both the_ **component** _and the service_ in the `TestBed` configuration.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/welcome/welcome.component.spec.ts"
|
||||
region="class-only-before-each"
|
||||
header="app/welcome/welcome.component.spec.ts (class-only setup)"></code-example>
|
||||
|
||||
Then exercise the component class, remembering to call the [lifecycle hook methods](guide/lifecycle-hooks) as Angular does when running the app.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/welcome/welcome.component.spec.ts"
|
||||
region="class-only-tests"
|
||||
header="app/welcome/welcome.component.spec.ts (class-only tests)"></code-example>
|
||||
|
||||
## Component DOM testing
|
||||
|
||||
Testing the component _class_ is as easy as [testing a service](guide/testing-services).
|
||||
|
||||
But a component is more than just its class.
|
||||
A component interacts with the DOM and with other components.
|
||||
The _class-only_ tests can tell you about class behavior.
|
||||
They cannot tell you if the component is going to render properly,
|
||||
respond to user input and gestures, or integrate with its parent and child components.
|
||||
|
||||
None of the _class-only_ tests above can answer key questions about how the
|
||||
components actually behave on screen.
|
||||
|
||||
- Is `Lightswitch.clicked()` bound to anything such that the user can invoke it?
|
||||
- Is the `Lightswitch.message` displayed?
|
||||
- Can the user actually select the hero displayed by `DashboardHeroComponent`?
|
||||
- Is the hero name displayed as expected (i.e, in uppercase)?
|
||||
- Is the welcome message displayed by the template of `WelcomeComponent`?
|
||||
|
||||
These may not be troubling questions for the simple components illustrated above.
|
||||
But many components have complex interactions with the DOM elements
|
||||
described in their templates, causing HTML to appear and disappear as
|
||||
the component state changes.
|
||||
|
||||
To answer these kinds of questions, you have to create the DOM elements associated
|
||||
with the components, you must examine the DOM to confirm that component state
|
||||
displays properly at the appropriate times, and you must simulate user interaction
|
||||
with the screen to determine whether those interactions cause the component to
|
||||
behave as expected.
|
||||
|
||||
To write these kinds of test, you'll use additional features of the `TestBed`
|
||||
as well as other testing helpers.
|
||||
|
||||
### CLI-generated tests
|
||||
|
||||
The CLI creates an initial test file for you by default when you ask it to
|
||||
generate a new component.
|
||||
|
||||
For example, the following CLI command generates a `BannerComponent` in the `app/banner` folder (with inline template and styles):
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate component banner --inline-template --inline-style --module app
|
||||
</code-example>
|
||||
|
||||
It also generates an initial test file for the component, `banner-external.component.spec.ts`, that looks like this:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v1"
|
||||
header="app/banner/banner-external.component.spec.ts (initial)"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Because `compileComponents` is asynchronous, it uses
|
||||
the [`async`](api/core/testing/async) utility
|
||||
function imported from `@angular/core/testing`.
|
||||
|
||||
Please refer to the [async](guide/testing-components-scenarios#async) section for more details.
|
||||
|
||||
</div>
|
||||
|
||||
### Reduce the setup
|
||||
|
||||
Only the last three lines of this file actually test the component
|
||||
and all they do is assert that Angular can create the component.
|
||||
|
||||
The rest of the file is boilerplate setup code anticipating more advanced tests that _might_ become necessary if the component evolves into something substantial.
|
||||
|
||||
You'll learn about these advanced test features below.
|
||||
For now, you can radically reduce this test file to a more manageable size:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v2"
|
||||
header="app/banner/banner-initial.component.spec.ts (minimal)"></code-example>
|
||||
|
||||
In this example, the metadata object passed to `TestBed.configureTestingModule`
|
||||
simply declares `BannerComponent`, the component to test.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="configureTestingModule">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
There's no need to declare or import anything else.
|
||||
The default test module is pre-configured with
|
||||
something like the `BrowserModule` from `@angular/platform-browser`.
|
||||
|
||||
Later you'll call `TestBed.configureTestingModule()` with
|
||||
imports, providers, and more declarations to suit your testing needs.
|
||||
Optional `override` methods can further fine-tune aspects of the configuration.
|
||||
|
||||
</div>
|
||||
|
||||
{@a create-component}
|
||||
|
||||
### _createComponent()_
|
||||
|
||||
After configuring `TestBed`, you call its `createComponent()` method.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="createComponent">
|
||||
</code-example>
|
||||
|
||||
`TestBed.createComponent()` creates an instance of the `BannerComponent`,
|
||||
adds a corresponding element to the test-runner DOM,
|
||||
and returns a [`ComponentFixture`](#component-fixture).
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
Do not re-configure `TestBed` after calling `createComponent`.
|
||||
|
||||
The `createComponent` method freezes the current `TestBed` definition,
|
||||
closing it to further configuration.
|
||||
|
||||
You cannot call any more `TestBed` configuration methods, not `configureTestingModule()`,
|
||||
nor `get()`, nor any of the `override...` methods.
|
||||
If you try, `TestBed` throws an error.
|
||||
|
||||
</div>
|
||||
|
||||
{@a component-fixture}
|
||||
|
||||
### _ComponentFixture_
|
||||
|
||||
The [ComponentFixture](api/core/testing/ComponentFixture) is a test harness for interacting with the created component and its corresponding element.
|
||||
|
||||
Access the component instance through the fixture and confirm it exists with a Jasmine expectation:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="componentInstance">
|
||||
</code-example>
|
||||
|
||||
### _beforeEach()_
|
||||
|
||||
You will add more tests as this component evolves.
|
||||
Rather than duplicate the `TestBed` configuration for each test,
|
||||
you refactor to pull the setup into a Jasmine `beforeEach()` and some supporting variables:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v3"
|
||||
></code-example>
|
||||
|
||||
Now add a test that gets the component's element from `fixture.nativeElement` and
|
||||
looks for the expected text.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v4-test-2">
|
||||
</code-example>
|
||||
|
||||
{@a native-element}
|
||||
|
||||
### _nativeElement_
|
||||
|
||||
The value of `ComponentFixture.nativeElement` has the `any` type.
|
||||
Later you'll encounter the `DebugElement.nativeElement` and it too has the `any` type.
|
||||
|
||||
Angular can't know at compile time what kind of HTML element the `nativeElement` is or
|
||||
if it even is an HTML element.
|
||||
The app might be running on a _non-browser platform_, such as the server or a
|
||||
[Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API),
|
||||
where the element may have a diminished API or not exist at all.
|
||||
|
||||
The tests in this guide are designed to run in a browser so a
|
||||
`nativeElement` value will always be an `HTMLElement` or
|
||||
one of its derived classes.
|
||||
|
||||
Knowing that it is an `HTMLElement` of some sort, you can use
|
||||
the standard HTML `querySelector` to dive deeper into the element tree.
|
||||
|
||||
Here's another test that calls `HTMLElement.querySelector` to get the paragraph element and look for the banner text:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v4-test-3">
|
||||
</code-example>
|
||||
|
||||
{@a debug-element}
|
||||
|
||||
### _DebugElement_
|
||||
|
||||
The Angular _fixture_ provides the component's element directly through the `fixture.nativeElement`.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="nativeElement">
|
||||
</code-example>
|
||||
|
||||
This is actually a convenience method, implemented as `fixture.debugElement.nativeElement`.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="debugElement-nativeElement">
|
||||
</code-example>
|
||||
|
||||
There's a good reason for this circuitous path to the element.
|
||||
|
||||
The properties of the `nativeElement` depend upon the runtime environment.
|
||||
You could be running these tests on a _non-browser_ platform that doesn't have a DOM or
|
||||
whose DOM-emulation doesn't support the full `HTMLElement` API.
|
||||
|
||||
Angular relies on the `DebugElement` abstraction to work safely across _all supported platforms_.
|
||||
Instead of creating an HTML element tree, Angular creates a `DebugElement` tree that wraps the _native elements_ for the runtime platform.
|
||||
The `nativeElement` property unwraps the `DebugElement` and returns the platform-specific element object.
|
||||
|
||||
Because the sample tests for this guide are designed to run only in a browser,
|
||||
a `nativeElement` in these tests is always an `HTMLElement`
|
||||
whose familiar methods and properties you can explore within a test.
|
||||
|
||||
Here's the previous test, re-implemented with `fixture.debugElement.nativeElement`:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v4-test-4">
|
||||
</code-example>
|
||||
|
||||
The `DebugElement` has other methods and properties that
|
||||
are useful in tests, as you'll see elsewhere in this guide.
|
||||
|
||||
You import the `DebugElement` symbol from the Angular core library.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="import-debug-element">
|
||||
</code-example>
|
||||
|
||||
{@a by-css}
|
||||
### _By.css()_
|
||||
|
||||
Although the tests in this guide all run in the browser,
|
||||
some apps might run on a different platform at least some of the time.
|
||||
|
||||
For example, the component might render first on the server as part of a strategy to make the application launch faster on poorly connected devices. The server-side renderer might not support the full HTML element API.
|
||||
If it doesn't support `querySelector`, the previous test could fail.
|
||||
|
||||
The `DebugElement` offers query methods that work for all supported platforms.
|
||||
These query methods take a _predicate_ function that returns `true` when a node in the `DebugElement` tree matches the selection criteria.
|
||||
|
||||
You create a _predicate_ with the help of a `By` class imported from a
|
||||
library for the runtime platform. Here's the `By` import for the browser platform:
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="import-by">
|
||||
</code-example>
|
||||
|
||||
The following example re-implements the previous test with
|
||||
`DebugElement.query()` and the browser's `By.css` method.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/banner/banner-initial.component.spec.ts"
|
||||
region="v4-test-5">
|
||||
</code-example>
|
||||
|
||||
Some noteworthy observations:
|
||||
|
||||
- The `By.css()` static method selects `DebugElement` nodes
|
||||
with a [standard CSS selector](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_started/Selectors 'CSS selectors').
|
||||
- The query returns a `DebugElement` for the paragraph.
|
||||
- You must unwrap that result to get the paragraph element.
|
||||
|
||||
When you're filtering by CSS selector and only testing properties of a browser's _native element_, the `By.css` approach may be overkill.
|
||||
|
||||
It's often easier and more clear to filter with a standard `HTMLElement` method
|
||||
such as `querySelector()` or `querySelectorAll()`,
|
||||
as you'll see in the next set of tests.
|
||||
|
1826
aio/content/guide/testing-components-scenarios.md
Normal file
1826
aio/content/guide/testing-components-scenarios.md
Normal file
File diff suppressed because it is too large
Load Diff
41
aio/content/guide/testing-pipes.md
Normal file
41
aio/content/guide/testing-pipes.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Testing Pipes
|
||||
|
||||
You can test [pipes](guide/pipes) without the Angular testing utilities.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
## Testing the `TitleCasePipe`
|
||||
|
||||
A pipe class has one method, `transform`, that manipulates the input
|
||||
value into a transformed output value.
|
||||
The `transform` implementation rarely interacts with the DOM.
|
||||
Most pipes have no dependence on Angular other than the `@Pipe`
|
||||
metadata and an interface.
|
||||
|
||||
Consider a `TitleCasePipe` that capitalizes the first letter of each word.
|
||||
Here's an implementation with a regular expression.
|
||||
|
||||
<code-example path="testing/src/app/shared/title-case.pipe.ts" header="app/shared/title-case.pipe.ts"></code-example>
|
||||
|
||||
Anything that uses a regular expression is worth testing thoroughly.
|
||||
Use simple Jasmine to explore the expected cases and the edge cases.
|
||||
|
||||
<code-example path="testing/src/app/shared/title-case.pipe.spec.ts" region="excerpt" header="app/shared/title-case.pipe.spec.ts"></code-example>
|
||||
|
||||
{@a write-tests}
|
||||
|
||||
## Writing DOM tests to support a pipe test
|
||||
|
||||
These are tests of the pipe _in isolation_.
|
||||
They can't tell if the `TitleCasePipe` is working properly as applied in the application components.
|
||||
|
||||
Consider adding component tests such as this one:
|
||||
|
||||
<code-example path="testing/src/app/hero/hero-detail.component.spec.ts" region="title-case-pipe" header="app/hero/hero-detail.component.spec.ts (pipe test)"></code-example>
|
||||
|
199
aio/content/guide/testing-services.md
Normal file
199
aio/content/guide/testing-services.md
Normal file
@ -0,0 +1,199 @@
|
||||
# Testing services
|
||||
|
||||
|
||||
To check that your services are working as you intend, you can write tests specifically for them.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample app that the testing guides describe, see the <live-example name="testing" embedded-style noDownload>sample app</live-example>.
|
||||
|
||||
For the tests features in the testing guides, see <live-example name="testing" stackblitz="specs" noDownload>tests</live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Services are often the easiest files to unit test.
|
||||
Here are some synchronous and asynchronous unit tests of the `ValueService`
|
||||
written without assistance from Angular testing utilities.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.spec.ts" region="ValueService" header="app/demo/demo.spec.ts"></code-example>
|
||||
|
||||
{@a services-with-dependencies}
|
||||
|
||||
## Services with dependencies
|
||||
|
||||
Services often depend on other services that Angular injects into the constructor.
|
||||
In many cases, it's easy to create and _inject_ these dependencies by hand while
|
||||
calling the service's constructor.
|
||||
|
||||
The `MasterService` is a simple example:
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.ts" region="MasterService" header="app/demo/demo.ts"></code-example>
|
||||
|
||||
`MasterService` delegates its only method, `getValue`, to the injected `ValueService`.
|
||||
|
||||
Here are several ways to test it.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.spec.ts" region="MasterService" header="app/demo/demo.spec.ts"></code-example>
|
||||
|
||||
The first test creates a `ValueService` with `new` and passes it to the `MasterService` constructor.
|
||||
|
||||
However, injecting the real service rarely works well as most dependent services are difficult to create and control.
|
||||
|
||||
Instead you can mock the dependency, use a dummy value, or create a
|
||||
[spy](https://jasmine.github.io/2.0/introduction.html#section-Spies)
|
||||
on the pertinent service method.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Prefer spies as they are usually the easiest way to mock services.
|
||||
|
||||
</div>
|
||||
|
||||
These standard testing techniques are great for unit testing services in isolation.
|
||||
|
||||
However, you almost always inject services into application classes using Angular
|
||||
dependency injection and you should have tests that reflect that usage pattern.
|
||||
Angular testing utilities make it easy to investigate how injected services behave.
|
||||
|
||||
## Testing services with the _TestBed_
|
||||
|
||||
Your app relies on Angular [dependency injection (DI)](guide/dependency-injection)
|
||||
to create services.
|
||||
When a service has a dependent service, DI finds or creates that dependent service.
|
||||
And if that dependent service has its own dependencies, DI finds-or-creates them as well.
|
||||
|
||||
As service _consumer_, you don't worry about any of this.
|
||||
You don't worry about the order of constructor arguments or how they're created.
|
||||
|
||||
As a service _tester_, you must at least think about the first level of service dependencies
|
||||
but you _can_ let Angular DI do the service creation and deal with constructor argument order
|
||||
when you use the `TestBed` testing utility to provide and create services.
|
||||
|
||||
{@a testbed}
|
||||
|
||||
## Angular _TestBed_
|
||||
|
||||
The `TestBed` is the most important of the Angular testing utilities.
|
||||
The `TestBed` creates a dynamically-constructed Angular _test_ module that emulates
|
||||
an Angular [@NgModule](guide/ngmodules).
|
||||
|
||||
The `TestBed.configureTestingModule()` method takes a metadata object that can have most of the properties of an [@NgModule](guide/ngmodules).
|
||||
|
||||
To test a service, you set the `providers` metadata property with an
|
||||
array of the services that you'll test or mock.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="value-service-before-each" header="app/demo/demo.testbed.spec.ts (provide ValueService in beforeEach)"></code-example>
|
||||
|
||||
Then inject it inside a test by calling `TestBed.inject()` with the service class as the argument.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** `TestBed.get()` was deprecated as of Angular version 9.
|
||||
To help minimize breaking changes, Angular introduces a new function called `TestBed.inject()`, which you should use instead.
|
||||
For information on the removal of `TestBed.get()`,
|
||||
see its entry in the [Deprecations index](guide/deprecations#index).
|
||||
|
||||
</div>
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="value-service-inject-it"></code-example>
|
||||
|
||||
Or inside the `beforeEach()` if you prefer to inject the service as part of your setup.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="value-service-inject-before-each"> </code-example>
|
||||
|
||||
When testing a service with a dependency, provide the mock in the `providers` array.
|
||||
|
||||
In the following example, the mock is a spy object.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="master-service-before-each"></code-example>
|
||||
|
||||
The test consumes that spy in the same way it did earlier.
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="master-service-it">
|
||||
</code-example>
|
||||
|
||||
{@a no-before-each}
|
||||
|
||||
## Testing without _beforeEach()_
|
||||
|
||||
Most test suites in this guide call `beforeEach()` to set the preconditions for each `it()` test
|
||||
and rely on the `TestBed` to create classes and inject services.
|
||||
|
||||
There's another school of testing that never calls `beforeEach()` and prefers to create classes explicitly rather than use the `TestBed`.
|
||||
|
||||
Here's how you might rewrite one of the `MasterService` tests in that style.
|
||||
|
||||
Begin by putting re-usable, preparatory code in a _setup_ function instead of `beforeEach()`.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/demo.spec.ts"
|
||||
region="no-before-each-setup"
|
||||
header="app/demo/demo.spec.ts (setup)"></code-example>
|
||||
|
||||
The `setup()` function returns an object literal
|
||||
with the variables, such as `masterService`, that a test might reference.
|
||||
You don't define _semi-global_ variables (e.g., `let masterService: MasterService`)
|
||||
in the body of the `describe()`.
|
||||
|
||||
Then each test invokes `setup()` in its first line, before continuing
|
||||
with steps that manipulate the test subject and assert expectations.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/demo.spec.ts"
|
||||
region="no-before-each-test"></code-example>
|
||||
|
||||
Notice how the test uses
|
||||
[_destructuring assignment_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
|
||||
to extract the setup variables that it needs.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/demo.spec.ts"
|
||||
region="no-before-each-setup-call">
|
||||
</code-example>
|
||||
|
||||
Many developers feel this approach is cleaner and more explicit than the
|
||||
traditional `beforeEach()` style.
|
||||
|
||||
Although this testing guide follows the traditional style and
|
||||
the default [CLI schematics](https://github.com/angular/angular-cli)
|
||||
generate test files with `beforeEach()` and `TestBed`,
|
||||
feel free to adopt _this alternative approach_ in your own projects.
|
||||
|
||||
## Testing HTTP services
|
||||
|
||||
Data services that make HTTP calls to remote servers typically inject and delegate
|
||||
to the Angular [`HttpClient`](guide/http) service for XHR calls.
|
||||
|
||||
You can test a data service with an injected `HttpClient` spy as you would
|
||||
test any service with a dependency.
|
||||
<code-example
|
||||
path="testing/src/app/model/hero.service.spec.ts"
|
||||
region="test-with-spies"
|
||||
header="app/model/hero.service.spec.ts (tests with spies)">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
The `HeroService` methods return `Observables`. You must
|
||||
_subscribe_ to an observable to (a) cause it to execute and (b)
|
||||
assert that the method succeeds or fails.
|
||||
|
||||
The `subscribe()` method takes a success (`next`) and fail (`error`) callback.
|
||||
Make sure you provide _both_ callbacks so that you capture errors.
|
||||
Neglecting to do so produces an asynchronous uncaught observable error that
|
||||
the test runner will likely attribute to a completely different test.
|
||||
|
||||
</div>
|
||||
|
||||
## _HttpClientTestingModule_
|
||||
|
||||
Extended interactions between a data service and the `HttpClient` can be complex
|
||||
and difficult to mock with spies.
|
||||
|
||||
The `HttpClientTestingModule` can make these testing scenarios more manageable.
|
||||
|
||||
While the _code sample_ accompanying this guide demonstrates `HttpClientTestingModule`,
|
||||
this page defers to the [Http guide](guide/http#testing-http-requests),
|
||||
which covers testing with the `HttpClientTestingModule` in detail.
|
||||
|
794
aio/content/guide/testing-utility-apis.md
Normal file
794
aio/content/guide/testing-utility-apis.md
Normal file
@ -0,0 +1,794 @@
|
||||
# Testing Utility APIs
|
||||
|
||||
This page describes the most useful Angular testing features.
|
||||
|
||||
The Angular testing utilities include the `TestBed`, the `ComponentFixture`, and a handful of functions that control the test environment.
|
||||
The [_TestBed_](#testbed-api-summary) and [_ComponentFixture_](#component-fixture-api-summary) classes are covered separately.
|
||||
|
||||
Here's a summary of the stand-alone functions, in order of likely utility:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Function
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>async</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Runs the body of a test (`it`) or setup (`beforeEach`) function within a special _async test zone_.
|
||||
See [discussion above](guide/testing-components-scenarios#async).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>fakeAsync</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Runs the body of a test (`it`) within a special _fakeAsync test zone_, enabling
|
||||
a linear control flow coding style. See [discussion above](guide/testing-components-scenarios#fake-async).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>tick</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Simulates the passage of time and the completion of pending asynchronous activities
|
||||
by flushing both _timer_ and _micro-task_ queues within the _fakeAsync test zone_.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The curious, dedicated reader might enjoy this lengthy blog post,
|
||||
["_Tasks, microtasks, queues and schedules_"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/).
|
||||
|
||||
</div>
|
||||
|
||||
Accepts an optional argument that moves the virtual clock forward
|
||||
by the specified number of milliseconds,
|
||||
clearing asynchronous activities scheduled within that timeframe.
|
||||
See [discussion above](guide/testing-components-scenarios#tick).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>inject</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Injects one or more services from the current `TestBed` injector into a test function.
|
||||
It cannot inject a service provided by the component itself.
|
||||
See discussion of the [debugElement.injector](guide/testing-components-scenarios#get-injected-services).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>discardPeriodicTasks</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
When a `fakeAsync()` test ends with pending timer event _tasks_ (queued `setTimeOut` and `setInterval` callbacks),
|
||||
the test fails with a clear error message.
|
||||
|
||||
In general, a test should end with no queued tasks.
|
||||
When pending timer tasks are expected, call `discardPeriodicTasks` to flush the _task_ queue
|
||||
and avoid the error.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>flushMicrotasks</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
When a `fakeAsync()` test ends with pending _micro-tasks_ such as unresolved promises,
|
||||
the test fails with a clear error message.
|
||||
|
||||
In general, a test should wait for micro-tasks to finish.
|
||||
When pending microtasks are expected, call `flushMicrotasks` to flush the _micro-task_ queue
|
||||
and avoid the error.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>ComponentFixtureAutoDetect</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
A provider token for a service that turns on [automatic change detection](guide/testing-components-scenarios#automatic-change-detection).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>getTestBed</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Gets the current instance of the `TestBed`.
|
||||
Usually unnecessary because the static class methods of the `TestBed` class are typically sufficient.
|
||||
The `TestBed` instance exposes a few rarely used members that are not available as
|
||||
static methods.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
{@a testbed-class-summary}
|
||||
|
||||
## _TestBed_ class summary
|
||||
|
||||
The `TestBed` class is one of the principal Angular testing utilities.
|
||||
Its API is quite large and can be overwhelming until you've explored it,
|
||||
a little at a time. Read the early part of this guide first
|
||||
to get the basics before trying to absorb the full API.
|
||||
|
||||
The module definition passed to `configureTestingModule`
|
||||
is a subset of the `@NgModule` metadata properties.
|
||||
|
||||
<code-example language="javascript">
|
||||
type TestModuleMetadata = {
|
||||
providers?: any[];
|
||||
declarations?: any[];
|
||||
imports?: any[];
|
||||
schemas?: Array<SchemaMetadata | any[]>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
{@a metadata-override-object}
|
||||
|
||||
Each override method takes a `MetadataOverride<T>` where `T` is the kind of metadata
|
||||
appropriate to the method, that is, the parameter of an `@NgModule`,
|
||||
`@Component`, `@Directive`, or `@Pipe`.
|
||||
|
||||
<code-example language="javascript">
|
||||
type MetadataOverride<T> = {
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
{@a testbed-methods}
|
||||
{@a testbed-api-summary}
|
||||
|
||||
The `TestBed` API consists of static class methods that either update or reference a _global_ instance of the `TestBed`.
|
||||
|
||||
Internally, all static methods cover methods of the current runtime `TestBed` instance,
|
||||
which is also returned by the `getTestBed()` function.
|
||||
|
||||
Call `TestBed` methods _within_ a `beforeEach()` to ensure a fresh start before each individual test.
|
||||
|
||||
Here are the most important static methods, in order of likely utility.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Methods
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>configureTestingModule</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The testing shims (`karma-test-shim`, `browser-test-shim`)
|
||||
establish the [initial test environment](guide/testing) and a default testing module.
|
||||
The default testing module is configured with basic declaratives and some Angular service substitutes that every tester needs.
|
||||
|
||||
Call `configureTestingModule` to refine the testing module configuration for a particular set of tests
|
||||
by adding and removing imports, declarations (of components, directives, and pipes), and providers.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>compileComponents</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Compile the testing module asynchronously after you've finished configuring it.
|
||||
You **must** call this method if _any_ of the testing module components have a `templateUrl`
|
||||
or `styleUrls` because fetching component template and style files is necessarily asynchronous.
|
||||
See [above](guide/testing-components-scenarios#compile-components).
|
||||
|
||||
After calling `compileComponents`, the `TestBed` configuration is frozen for the duration of the current spec.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>createComponent<T></code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Create an instance of a component of type `T` based on the current `TestBed` configuration.
|
||||
After calling `compileComponent`, the `TestBed` configuration is frozen for the duration of the current spec.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>overrideModule</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Replace metadata for the given `NgModule`. Recall that modules can import other modules.
|
||||
The `overrideModule` method can reach deeply into the current testing module to
|
||||
modify one of these inner modules.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>overrideComponent</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Replace metadata for the given component class, which could be nested deeply
|
||||
within an inner module.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>overrideDirective</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Replace metadata for the given directive class, which could be nested deeply
|
||||
within an inner module.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>overridePipe</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Replace metadata for the given pipe class, which could be nested deeply
|
||||
within an inner module.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
{@a testbed-inject}
|
||||
<code>inject</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Retrieve a service from the current `TestBed` injector.
|
||||
|
||||
The `inject` function is often adequate for this purpose.
|
||||
But `inject` throws an error if it can't provide the service.
|
||||
|
||||
What if the service is optional?
|
||||
|
||||
The `TestBed.inject()` method takes an optional second parameter,
|
||||
the object to return if Angular can't find the provider
|
||||
(`null` in this example):
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="testbed-get-w-null" header="app/demo/demo.testbed.spec.ts"></code-example>
|
||||
|
||||
After calling `TestBed.inject`, the `TestBed` configuration is frozen for the duration of the current spec.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
{@a testbed-initTestEnvironment}
|
||||
<code>initTestEnvironment</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Initialize the testing environment for the entire test run.
|
||||
|
||||
The testing shims (`karma-test-shim`, `browser-test-shim`) call it for you
|
||||
so there is rarely a reason for you to call it yourself.
|
||||
|
||||
You may call this method _exactly once_. If you must change
|
||||
this default in the middle of your test run, call `resetTestEnvironment` first.
|
||||
|
||||
Specify the Angular compiler factory, a `PlatformRef`, and a default Angular testing module.
|
||||
Alternatives for non-browser platforms are available in the general form
|
||||
`@angular/platform-<platform_name>/testing/<platform_name>`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>resetTestEnvironment</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Reset the initial test environment, including the default testing module.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
A few of the `TestBed` instance methods are not covered by static `TestBed` _class_ methods.
|
||||
These are rarely needed.
|
||||
|
||||
{@a component-fixture-api-summary}
|
||||
|
||||
## The _ComponentFixture_
|
||||
|
||||
The `TestBed.createComponent<T>`
|
||||
creates an instance of the component `T`
|
||||
and returns a strongly typed `ComponentFixture` for that component.
|
||||
|
||||
The `ComponentFixture` properties and methods provide access to the component,
|
||||
its DOM representation, and aspects of its Angular environment.
|
||||
|
||||
{@a component-fixture-properties}
|
||||
|
||||
### _ComponentFixture_ properties
|
||||
|
||||
Here are the most important properties for testers, in order of likely utility.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Properties
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>componentInstance</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The instance of the component class created by `TestBed.createComponent`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>debugElement</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The `DebugElement` associated with the root element of the component.
|
||||
|
||||
The `debugElement` provides insight into the component and its DOM element during test and debugging.
|
||||
It's a critical property for testers. The most interesting members are covered [below](#debug-element-details).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>nativeElement</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The native DOM element at the root of the component.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>changeDetectorRef</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The `ChangeDetectorRef` for the component.
|
||||
|
||||
The `ChangeDetectorRef` is most valuable when testing a
|
||||
component that has the `ChangeDetectionStrategy.OnPush` method
|
||||
or the component's change detection is under your programmatic control.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{@a component-fixture-methods}
|
||||
|
||||
### _ComponentFixture_ methods
|
||||
|
||||
The _fixture_ methods cause Angular to perform certain tasks on the component tree.
|
||||
Call these method to trigger Angular behavior in response to simulated user action.
|
||||
|
||||
Here are the most useful methods for testers.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Methods
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>detectChanges</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Trigger a change detection cycle for the component.
|
||||
|
||||
Call it to initialize the component (it calls `ngOnInit`) and after your
|
||||
test code, change the component's data bound property values.
|
||||
Angular can't see that you've changed `personComponent.name` and won't update the `name`
|
||||
binding until you call `detectChanges`.
|
||||
|
||||
Runs `checkNoChanges` afterwards to confirm that there are no circular updates unless
|
||||
called as `detectChanges(false)`;
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>autoDetectChanges</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Set this to `true` when you want the fixture to detect changes automatically.
|
||||
|
||||
When autodetect is `true`, the test fixture calls `detectChanges` immediately
|
||||
after creating the component. Then it listens for pertinent zone events
|
||||
and calls `detectChanges` accordingly.
|
||||
When your test code modifies component property values directly,
|
||||
you probably still have to call `fixture.detectChanges` to trigger data binding updates.
|
||||
|
||||
The default is `false`. Testers who prefer fine control over test behavior
|
||||
tend to keep it `false`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>checkNoChanges</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Do a change detection run to make sure there are no pending changes.
|
||||
Throws an exceptions if there are.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>isStable</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
If the fixture is currently _stable_, returns `true`.
|
||||
If there are async tasks that have not completed, returns `false`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>whenStable</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Returns a promise that resolves when the fixture is stable.
|
||||
|
||||
To resume testing after completion of asynchronous activity or
|
||||
asynchronous change detection, hook that promise.
|
||||
See [above](guide/testing-components-scenarios#when-stable).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>destroy</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Trigger component destruction.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{@a debug-element-details}
|
||||
|
||||
#### _DebugElement_
|
||||
|
||||
The `DebugElement` provides crucial insights into the component's DOM representation.
|
||||
|
||||
From the test root component's `DebugElement` returned by `fixture.debugElement`,
|
||||
you can walk (and query) the fixture's entire element and component subtrees.
|
||||
|
||||
Here are the most useful `DebugElement` members for testers, in approximate order of utility:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Member
|
||||
</th>
|
||||
<th>
|
||||
Description
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>nativeElement</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The corresponding DOM element in the browser (null for WebWorkers).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>query</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Calling `query(predicate: Predicate<DebugElement>)` returns the first `DebugElement`
|
||||
that matches the [predicate](#query-predicate) at any depth in the subtree.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>queryAll</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Calling `queryAll(predicate: Predicate<DebugElement>)` returns all `DebugElements`
|
||||
that matches the [predicate](#query-predicate) at any depth in subtree.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>injector</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The host dependency injector.
|
||||
For example, the root element's component instance injector.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>componentInstance</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The element's own component instance, if it has one.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>context</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
An object that provides parent context for this element.
|
||||
Often an ancestor component instance that governs this element.
|
||||
|
||||
When an element is repeated within `*ngFor`, the context is an `NgForRow` whose `$implicit`
|
||||
property is the value of the row instance value.
|
||||
For example, the `hero` in `*ngFor="let hero of heroes"`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>children</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The immediate `DebugElement` children. Walk the tree by descending through `children`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
`DebugElement` also has `childNodes`, a list of `DebugNode` objects.
|
||||
`DebugElement` derives from `DebugNode` objects and there are often
|
||||
more nodes than elements. Testers can usually ignore plain nodes.
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>parent</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
The `DebugElement` parent. Null if this is the root element.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>name</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The element tag name, if it is an element.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>triggerEventHandler</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Triggers the event by its name if there is a corresponding listener
|
||||
in the element's `listeners` collection.
|
||||
The second parameter is the _event object_ expected by the handler.
|
||||
See [above](guide/testing-components-scenarios#trigger-event-handler).
|
||||
|
||||
If the event lacks a listener or there's some other problem,
|
||||
consider calling `nativeElement.dispatchEvent(eventObject)`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>listeners</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
The callbacks attached to the component's `@Output` properties and/or the element's event properties.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>providerTokens</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
This component's injector lookup tokens.
|
||||
Includes the component itself plus the tokens that the component lists in its `providers` metadata.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>source</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Where to find this element in the source component template.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="vertical-align: top">
|
||||
<code>references</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Dictionary of objects associated with template local variables (e.g. `#foo`),
|
||||
keyed by the local variable name.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{@a query-predicate}
|
||||
|
||||
The `DebugElement.query(predicate)` and `DebugElement.queryAll(predicate)` methods take a
|
||||
predicate that filters the source element's subtree for matching `DebugElement`.
|
||||
|
||||
The predicate is any method that takes a `DebugElement` and returns a _truthy_ value.
|
||||
The following example finds all `DebugElements` with a reference to a template local variable named "content":
|
||||
|
||||
<code-example path="testing/src/app/demo/demo.testbed.spec.ts" region="custom-predicate" header="app/demo/demo.testbed.spec.ts"></code-example>
|
||||
|
||||
The Angular `By` class has three static methods for common predicates:
|
||||
|
||||
- `By.all` - return all elements.
|
||||
- `By.css(selector)` - return elements with matching CSS selectors.
|
||||
- `By.directive(directive)` - return elements that Angular matched to an instance of the directive class.
|
||||
|
||||
<code-example path="testing/src/app/hero/hero-list.component.spec.ts" region="by" header="app/hero/hero-list.component.spec.ts"></code-example>
|
||||
|
||||
<hr>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@ If you're curious about the specific migrations being run by the CLI, see the [a
|
||||
* The `minLength` and `maxLength` validators only validate values that have a numeric `length` property. See [PR 36157](https://github.com/angular/angular/pull/36157).
|
||||
* Templates with unknown property bindings or unknown element names now log errors instead of warnings. See [PR 36399](https://github.com/angular/angular/pull/36399).
|
||||
* `UrlMatcher` can now return `null` values. See [PR 36402](https://github.com/angular/angular/pull/36402).
|
||||
* Transplanted views now refresh at insertion point only. See PR 35968](https://github.com/angular/angular/pull/35968).
|
||||
* Transplanted views now refresh at insertion point only. See [PR 35968](https://github.com/angular/angular/pull/35968).
|
||||
* Formatting times with the `b` or `B` format codes now supports time periods that cross midnight. See [PR 36611](https://github.com/angular/angular/pull/36611).
|
||||
* Navigation is canceled for routes with at least one empty resolver. See [PR 24621](https://github.com/angular/angular/pull/24621).
|
||||
|
||||
|
@ -308,7 +308,7 @@ So when IE is refreshed (manually or automatically by `ng serve`), sometimes the
|
||||
|
||||
## Appendix: Test using `fakeAsync()/async()`
|
||||
|
||||
If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read the [Testing guide](guide/testing#async-test-with-fakeasync)), you need to import `zone.js/dist/zone-testing` in your test setup file.
|
||||
If you use the `fakeAsync()/async()` helper function to run unit tests (for details, read the [Testing guide](guide/testing-components-scenarios#fake-async)), you need to import `zone.js/dist/zone-testing` in your test setup file.
|
||||
|
||||
<div class="alert is-important">
|
||||
If you create project with `Angular/CLI`, it is already imported in `src/test.ts`.
|
||||
|
BIN
aio/content/images/bios/ahasall.jpg
Normal file
BIN
aio/content/images/bios/ahasall.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
BIN
aio/content/images/bios/sonukapoor.jpg
Normal file
BIN
aio/content/images/bios/sonukapoor.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
3
aio/content/images/guide/providers/any-provider.svg
Normal file
3
aio/content/images/guide/providers/any-provider.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.6 KiB |
@ -596,6 +596,13 @@
|
||||
"twitter": "devjoost",
|
||||
"bio": "Joost is a Software Engineer from the Netherlands with an interest in open source software who likes to learn something new every day. He works at Blueriq during the day and contributes to Angular in his spare time, by working on the Angular compiler and runtime. He may review your PR even if you never asked for it ;)"
|
||||
},
|
||||
"sonukapoor": {
|
||||
"name": "Sonu Kapoor",
|
||||
"groups": ["Collaborators"],
|
||||
"picture": "sonukapoor.jpg",
|
||||
"website": "https://www.linkedin.com/in/sonu-kapoor/",
|
||||
"bio": "Sonu is a Software Engineer from Toronto, with a high interest in front-end technologies and algorithms."
|
||||
},
|
||||
"jschwarty": {
|
||||
"name": "Justin Schwartzenberger",
|
||||
"picture": "justinschwartzenberger.jpg",
|
||||
@ -655,12 +662,6 @@
|
||||
"picture": "bonnie.jpg",
|
||||
"bio": "Bonnie has been specializing in Angular since 2013. She is the founder of ngHouston Angular Meetup and a regular panelist on Angular Air. She is also the very proud parent component of @thelittlestdev!"
|
||||
},
|
||||
"globegitter": {
|
||||
"name": "Markus Padourek",
|
||||
"groups": ["Collaborators"],
|
||||
"mentor": "gregmagolan",
|
||||
"picture": "globegitter.jpg"
|
||||
},
|
||||
"ahsanayaz": {
|
||||
"name": "Muhammad Ahsan Ayaz",
|
||||
"picture": "ahsanayaz.jpg",
|
||||
@ -815,5 +816,13 @@
|
||||
"website": "https://wellwind.idv.tw/blog/",
|
||||
"bio": "Mike is a full-stack developer, consultant, blogger, instructor, and conference speaker. He has over 10 years of web development experience and passion to share his knowledge.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"ahasall": {
|
||||
"name": "Amadou Sall",
|
||||
"picture": "ahasall.jpg",
|
||||
"groups": ["GDE"],
|
||||
"twitter": "ahasall",
|
||||
"website": "https://www.amadousall.com",
|
||||
"bio": "Amadou is a Frontend Software Engineer from Senegal based in France. He currently works at Air France where he helps developers build better Angular applications. Passionate about web technologies, Amadou is an international speaker, a technical writer, and a Google Developer Expert in Angular."
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,11 @@
|
||||
"Data Libraries": {
|
||||
"order": 3,
|
||||
"resources": {
|
||||
"formly": {
|
||||
"desc": "Formly is a dynamic (JSON powered) form library, built on top of Angular Reactive Forms.",
|
||||
"title": "Formly",
|
||||
"url": "https://formly.dev"
|
||||
},
|
||||
"rx-web": {
|
||||
"desc": "RxWeb Reactive Form Validators provides all types of complex, conditional, cross field, and dynamic validation on validator-based reactive forms, model-based reactive forms, and template driven forms.",
|
||||
"title": "RxWeb Reactive Form Validators",
|
||||
@ -236,6 +241,12 @@
|
||||
"logo": "",
|
||||
"title": "Protractor",
|
||||
"url": "https://protractor.angular.io/"
|
||||
},
|
||||
"scully": {
|
||||
"desc": "Scully (Jamstack Toolchain for Angular) makes building, testing, and deploying Jamstack apps extremely simple.",
|
||||
"title": "Scully",
|
||||
"logo": "https://raw.githubusercontent.com/scullyio/scully/main/assets/logos/PNG/Green/scullyio-logo-green.png",
|
||||
"url": "https://scully.io"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -506,80 +506,6 @@
|
||||
"url": "guide/universal",
|
||||
"title": "Server-side Rendering",
|
||||
"tooltip": "Render HTML server-side with Angular Universal."
|
||||
},
|
||||
{
|
||||
"title": "Upgrading from AngularJS",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/upgrade-setup",
|
||||
"title": "Setup for Upgrading from AngularJS",
|
||||
"tooltip": "Use code from the Angular QuickStart seed as part of upgrading from AngularJS.",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade",
|
||||
"title": "Upgrading Instructions",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade-performance",
|
||||
"title": "Upgrading for Performance",
|
||||
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
||||
},
|
||||
{
|
||||
"url": "guide/ajs-quick-reference",
|
||||
"title": "AngularJS-Angular Concepts",
|
||||
"tooltip": "Learn how AngularJS concepts and techniques map to Angular."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Angular Libraries",
|
||||
"tooltip": "Extending Angular with shared libraries.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/libraries",
|
||||
"title": "Libraries Overview",
|
||||
"tooltip": "Understand how and when to use or create libraries."
|
||||
},
|
||||
{
|
||||
"url": "guide/using-libraries",
|
||||
"title": "Using Published Libraries",
|
||||
"tooltip": "Integrate published libraries into an app."
|
||||
},
|
||||
{
|
||||
"url": "guide/creating-libraries",
|
||||
"title": "Creating Libraries",
|
||||
"tooltip": "Extend Angular by creating, publishing, and using your own libraries."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Schematics",
|
||||
"tooltip": "Using CLI schematics for code generation.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/schematics",
|
||||
"title": "Schematics Overview",
|
||||
"tooltip": "How the CLI uses schematics to generate code."
|
||||
},
|
||||
{
|
||||
"url": "guide/schematics-authoring",
|
||||
"title": "Authoring Schematics",
|
||||
"tooltip": "Understand the structure of a schematic."
|
||||
},
|
||||
{
|
||||
"url": "guide/schematics-for-libraries",
|
||||
"title": "Schematics for Libraries",
|
||||
"tooltip": "Use schematics to integrate your library with the Angular CLI."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/cli-builder",
|
||||
"title": "CLI Builders",
|
||||
"tooltip": "Using builders to customize Angular CLI."
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -600,28 +526,74 @@
|
||||
"url": "guide/angular-compiler-options",
|
||||
"title": "Angular Compiler Options",
|
||||
"tooltip": "Configuring AOT compilation."
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"url": "guide/aot-metadata-errors",
|
||||
"title": "AOT Metadata Errors",
|
||||
"tooltip": "Troubleshooting AOT compilation."
|
||||
},
|
||||
{
|
||||
"url": "guide/template-typecheck",
|
||||
"title": "Template Type-checking",
|
||||
"tooltip": "Template type-checking in Angular."
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"url": "guide/template-typecheck",
|
||||
"title": "Template Type-checking",
|
||||
"tooltip": "Template type-checking in Angular."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/build",
|
||||
"title": "Building & Serving",
|
||||
"tooltip": "Building and serving Angular apps."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing",
|
||||
"title": "Testing",
|
||||
"tooltip": "Techniques and practices for testing an Angular app."
|
||||
"tooltip": "Testing your Angular apps.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/testing",
|
||||
"title": "Intro to Testing",
|
||||
"tooltip": "Introduction to testing an Angular app."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-code-coverage",
|
||||
"title": "Code Coverage",
|
||||
"tooltip": "Determine how much of your code is tested."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-services",
|
||||
"title": "Testing Services",
|
||||
"tooltip": "How to test services."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-components-basics",
|
||||
"title": "Basics of Testing Components",
|
||||
"tooltip": "The fundamentals of how to test components."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-components-scenarios",
|
||||
"title": "Component Testing Scenarios",
|
||||
"tooltip": "Use cases for testing components."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-attribute-directives",
|
||||
"title": "Testing Attribute Directives",
|
||||
"tooltip": "How to test attribute directives."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-pipes",
|
||||
"title": "Testing Pipes",
|
||||
"tooltip": "Writing tests for pipes."
|
||||
},
|
||||
{
|
||||
"url": "guide/test-debugging",
|
||||
"title": "Debugging Tests",
|
||||
"tooltip": "How to debug tests."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-utility-apis",
|
||||
"title": "Testing Utility APIs",
|
||||
"tooltip": "Features of the Angular testing utilities."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/deployment",
|
||||
@ -741,9 +713,20 @@
|
||||
"tooltip": "End-to-end tutorials for learning Angular concepts and patterns.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/router-tutorial",
|
||||
"title": "Using Angular Routes in a Single-page Application",
|
||||
"tooltip": "A tutorial that covers many patterns associated with Angular routing."
|
||||
"title": "Routing",
|
||||
"tooltip": "End-to-end tutorials for learning about Angular's router.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/router-tutorial",
|
||||
"title": "Using Angular Routes in a Single-page Application",
|
||||
"tooltip": "A tutorial that covers many patterns associated with Angular routing."
|
||||
},
|
||||
{
|
||||
"url": "guide/router-tutorial-toh",
|
||||
"title": "Router tutorial: tour of heroes",
|
||||
"tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/forms",
|
||||
@ -1008,6 +991,10 @@
|
||||
}
|
||||
],
|
||||
"docVersions": [
|
||||
{
|
||||
"title": "v9",
|
||||
"url": "https://v9.angular.io/"
|
||||
},
|
||||
{
|
||||
"title": "v8",
|
||||
"url": "https://v8.angular.io/"
|
||||
|
@ -6,7 +6,7 @@
|
||||
In this tutorial, you build your own app from the ground up, providing experience with the typical development process, as well as an introduction to basic app-design concepts, tools, and terminology.
|
||||
|
||||
If you're completely new to Angular, you might want to try the [**Try it now**](start) quick-start app first.
|
||||
It is based on a ready-made partially-completed project, which you can examine and modify in the StacBlitz interactive development environment, where you can see the results in real time.
|
||||
It is based on a ready-made partially-completed project, which you can examine and modify in the StackBlitz interactive development environment, where you can see the results in real time.
|
||||
|
||||
The "Try it" tutorial covers the same major topics—components, template syntax, routing, services, and accessing data via HTTP—in a condensed format, following the most current best practices.
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"hosting": {
|
||||
"target": "aio",
|
||||
"public": "dist",
|
||||
"cleanUrls": true,
|
||||
"redirects": [
|
||||
@ -127,7 +128,7 @@
|
||||
// The below paths are referenced in users projects generated by the CLI
|
||||
{"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"},
|
||||
{"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"},
|
||||
{"type": 301, "source": "/config/app-package-json", "destination": "https://webpack.js.org/configuration/optimization/#optimizationsideeffects"}
|
||||
{"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
|
@ -87,28 +87,28 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "10.0.0-rc.2",
|
||||
"@angular/cdk": "^9.2.2",
|
||||
"@angular/common": "10.0.0-rc.2",
|
||||
"@angular/compiler": "10.0.0-rc.2",
|
||||
"@angular/core": "10.0.0-rc.2",
|
||||
"@angular/elements": "10.0.0-rc.2",
|
||||
"@angular/forms": "10.0.0-rc.2",
|
||||
"@angular/material": "^9.2.2",
|
||||
"@angular/platform-browser": "10.0.0-rc.2",
|
||||
"@angular/platform-browser-dynamic": "10.0.0-rc.2",
|
||||
"@angular/router": "10.0.0-rc.2",
|
||||
"@angular/service-worker": "10.0.0-rc.2",
|
||||
"@angular/animations": "10.0.2",
|
||||
"@angular/cdk": "10.0.1",
|
||||
"@angular/common": "10.0.2",
|
||||
"@angular/compiler": "10.0.2",
|
||||
"@angular/core": "10.0.2",
|
||||
"@angular/elements": "10.0.2",
|
||||
"@angular/forms": "10.0.2",
|
||||
"@angular/material": "10.0.1",
|
||||
"@angular/platform-browser": "10.0.2",
|
||||
"@angular/platform-browser-dynamic": "10.0.2",
|
||||
"@angular/router": "10.0.2",
|
||||
"@angular/service-worker": "10.0.2",
|
||||
"@webcomponents/custom-elements": "1.2.1",
|
||||
"rxjs": "^6.5.3",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.1000.0-rc.2",
|
||||
"@angular/cli": "10.0.0-rc.2",
|
||||
"@angular/compiler-cli": "10.0.0-rc.2",
|
||||
"@angular/language-service": "10.0.0-rc.2",
|
||||
"@angular-devkit/build-angular": "0.1000.1",
|
||||
"@angular/cli": "10.0.1",
|
||||
"@angular/compiler-cli": "10.0.2",
|
||||
"@angular/language-service": "10.0.2",
|
||||
"@types/jasmine": "^3.4.2",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/lunr": "^2.3.2",
|
||||
@ -123,7 +123,7 @@
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.11",
|
||||
"dgeni-packages": "^0.28.3",
|
||||
"dgeni-packages": "^0.28.4",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
@ -175,4 +175,4 @@
|
||||
"xregexp": "^4.0.0",
|
||||
"yargs": "^7.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ else
|
||||
readonly majorVersionStable=${CI_STABLE_BRANCH%%.*}
|
||||
|
||||
# Do not deploy if the major version is not less than the stable branch major version
|
||||
if [[ !( "$majorVersion" -lt "$majorVersionStable" ) ]]; then
|
||||
if (( $majorVersion >= $majorVersionStable )); then
|
||||
echo "Skipping deploy of branch \"$CI_BRANCH\" to firebase."
|
||||
echo "We only deploy archive branches with the major version less than the stable branch: \"$CI_STABLE_BRANCH\""
|
||||
exit 0
|
||||
@ -64,16 +64,27 @@ fi
|
||||
case $deployEnv in
|
||||
next)
|
||||
readonly projectId=aio-staging
|
||||
readonly siteId=$projectId
|
||||
readonly deployedUrl=https://next.angular.io/
|
||||
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
|
||||
;;
|
||||
stable)
|
||||
readonly projectId=angular-io
|
||||
readonly siteId=$projectId
|
||||
readonly deployedUrl=https://angular.io/
|
||||
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
|
||||
;;
|
||||
archive)
|
||||
readonly projectId=v${majorVersion}-angular-io
|
||||
# Special case v9-angular-io because its piloting the firebase hosting "multisites" setup
|
||||
# See https://angular-team.atlassian.net/browse/DEV-125 for more info.
|
||||
if [[ "$majorVersion" == "9" ]]; then
|
||||
readonly projectId=aio-staging
|
||||
readonly siteId=v9-angular-io
|
||||
else
|
||||
readonly projectId=v${majorVersion}-angular-io
|
||||
readonly siteId=$projectId
|
||||
fi
|
||||
|
||||
readonly deployedUrl=https://v${majorVersion}.angular.io/
|
||||
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
|
||||
;;
|
||||
@ -82,6 +93,7 @@ esac
|
||||
echo "Git branch : $CI_BRANCH"
|
||||
echo "Build/deploy mode : $deployEnv"
|
||||
echo "Firebase project : $projectId"
|
||||
echo "Firebase site : $siteId"
|
||||
echo "Deployment URL : $deployedUrl"
|
||||
|
||||
if [[ ${1:-} == "--dry-run" ]]; then
|
||||
@ -92,23 +104,29 @@ fi
|
||||
(
|
||||
cd "`dirname $0`/.."
|
||||
|
||||
# Build the app
|
||||
echo "\n\n\n==== Build the aio app ====\n"
|
||||
yarn build --configuration=$deployEnv --progress=false
|
||||
|
||||
# Include any mode-specific files
|
||||
|
||||
echo "\n\n\n==== Add any mode-specific files into the aio distribution ====\n"
|
||||
cp -rf src/extra-files/$deployEnv/. dist/
|
||||
|
||||
# Set deployedUrl as parameter in the opensearch description
|
||||
|
||||
echo "\n\n\n==== Update opensearch descriptor for aio with the deployedUrl ====\n"
|
||||
# deployedUrl must end with /
|
||||
yarn set-opensearch-url $deployedUrl
|
||||
|
||||
# Check payload size
|
||||
echo "\n\n\n==== Check payload size and upload the numbers to firebase db ====\n"
|
||||
yarn payload-size
|
||||
|
||||
# Deploy to Firebase
|
||||
yarn firebase use "$projectId" --token "$firebaseToken"
|
||||
yarn firebase deploy --message "Commit: $CI_COMMIT" --non-interactive --token "$firebaseToken"
|
||||
|
||||
# Run PWA-score tests
|
||||
echo "\n\n\n==== Deploy aio to firebase hosting ====\n"
|
||||
|
||||
yarn firebase use "${projectId}" --token "$firebaseToken"
|
||||
yarn firebase target:apply hosting aio $siteId --token "$firebaseToken"
|
||||
yarn firebase deploy --only hosting:aio --message "Commit: $CI_COMMIT" --non-interactive --token "$firebaseToken"
|
||||
|
||||
|
||||
echo "\n\n\n==== Run PWA-score tests ====\n"
|
||||
yarn test-pwa-score "$deployedUrl" "$CI_AIO_MIN_PWA_SCORE"
|
||||
)
|
||||
|
@ -68,6 +68,7 @@ function check {
|
||||
expected="Git branch : master
|
||||
Build/deploy mode : next
|
||||
Firebase project : aio-staging
|
||||
Firebase site : aio-staging
|
||||
Deployment URL : https://next.angular.io/"
|
||||
check "$actual" "$expected"
|
||||
)
|
||||
@ -103,6 +104,7 @@ Deployment URL : https://next.angular.io/"
|
||||
expected="Git branch : 4.3.x
|
||||
Build/deploy mode : stable
|
||||
Firebase project : angular-io
|
||||
Firebase site : angular-io
|
||||
Deployment URL : https://angular.io/"
|
||||
check "$actual" "$expected"
|
||||
)
|
||||
@ -139,10 +141,37 @@ Deployment URL : https://angular.io/"
|
||||
expected="Git branch : 2.4.x
|
||||
Build/deploy mode : archive
|
||||
Firebase project : v2-angular-io
|
||||
Firebase site : v2-angular-io
|
||||
Deployment URL : https://v2.angular.io/"
|
||||
check "$actual" "$expected"
|
||||
)
|
||||
|
||||
(
|
||||
echo ===== archive - v9-angular-io multisite special case - deploy success
|
||||
actual=$(
|
||||
export BASH_ENV=/dev/null
|
||||
export CI_REPO_OWNER=angular
|
||||
export CI_REPO_NAME=angular
|
||||
export CI_PULL_REQUEST=false
|
||||
export CI_BRANCH=9.1.x
|
||||
export CI_STABLE_BRANCH=10.0.x
|
||||
export CI_COMMIT=$(git ls-remote origin 9.1.x | cut -c1-40)
|
||||
export CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN=XXXXX
|
||||
$deployToFirebaseDryRun
|
||||
)
|
||||
expected="Git branch : 9.1.x
|
||||
Build/deploy mode : archive
|
||||
Firebase project : aio-staging
|
||||
Firebase site : v9-angular-io
|
||||
Deployment URL : https://v9.angular.io/"
|
||||
# TODO: This test incorrectly expects the Firebase project to be v9-angular-io.
|
||||
# v9-angular-io is a "multisites" project currently within the aio-staging project
|
||||
# This setup is temporary and was created in order to deploy v9.angular.io without
|
||||
# disruptions.
|
||||
# See https://angular-team.atlassian.net/browse/DEV-125 for more info.
|
||||
check "$actual" "$expected"
|
||||
)
|
||||
|
||||
(
|
||||
echo ===== archive - skip deploy - commit not HEAD
|
||||
actual=$(
|
||||
|
@ -14,11 +14,11 @@
|
||||
<button mat-button class="hamburger" [class.starting]="isStarting" (click)="sidenav.toggle()" title="Docs menu">
|
||||
<mat-icon svgIcon="menu"></mat-icon>
|
||||
</button>
|
||||
<a class="nav-link home" href="/" [ngSwitch]="isSideBySide">
|
||||
<a class="nav-link home" href="/" [ngSwitch]="showTopMenu">
|
||||
<img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home">
|
||||
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
|
||||
</a>
|
||||
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes" [currentNode]="currentNodes?.TopBar"></aio-top-menu>
|
||||
<aio-top-menu *ngIf="showTopMenu" [nodes]="topMenuNodes" [currentNode]="currentNodes?.TopBar"></aio-top-menu>
|
||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||
<div class="toolbar-external-icons-container">
|
||||
<a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter">
|
||||
@ -35,9 +35,9 @@
|
||||
|
||||
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
|
||||
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide}" #sidenav class="sidenav" [mode]="mode" [opened]="isOpened" (openedChange)="updateHostClasses()">
|
||||
<aio-nav-menu *ngIf="!isSideBySide" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
|
||||
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="isSideBySide"></aio-nav-menu>
|
||||
<mat-sidenav [ngClass]="{'collapsed': !dockSideNav}" #sidenav class="sidenav" [mode]="mode" [opened]="isOpened" (openedChange)="updateHostClasses()">
|
||||
<aio-nav-menu *ngIf="!showTopMenu" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
|
||||
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="dockSideNav"></aio-nav-menu>
|
||||
|
||||
<div class="doc-version">
|
||||
<aio-select (change)="onDocVersionChange($event.index)" [options]="docVersions" [selected]="currentDocVersion"></aio-select>
|
||||
|
@ -25,11 +25,9 @@ import { first, mapTo } from 'rxjs/operators';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
import { MockLogger } from 'testing/logger.service';
|
||||
import { MockSearchService } from 'testing/search.service';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppComponent, dockSideNavWidth, showFloatingTocWidth, showTopMenuWidth } from './app.component';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
const sideBySideBreakPoint = 992;
|
||||
const hideToCBreakPoint = 800;
|
||||
const startedDelay = 100;
|
||||
|
||||
describe('AppComponent', () => {
|
||||
@ -58,7 +56,7 @@ describe('AppComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
component.onResize(sideBySideBreakPoint + 1); // wide by default
|
||||
component.onResize(showTopMenuWidth + 1); // wide by default
|
||||
|
||||
const de = fixture.debugElement;
|
||||
const docViewerDe = de.query(By.css('aio-doc-viewer'));
|
||||
@ -99,7 +97,7 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should be false on narrow screens', () => {
|
||||
component.onResize(hideToCBreakPoint - 1);
|
||||
component.onResize(showFloatingTocWidth - 1);
|
||||
|
||||
tocService.tocList.next([{}, {}, {}] as TocItem[]);
|
||||
expect(component.hasFloatingToc).toBe(false);
|
||||
@ -112,7 +110,7 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should be true on wide screens unless the toc is empty', () => {
|
||||
component.onResize(hideToCBreakPoint + 1);
|
||||
component.onResize(showFloatingTocWidth + 1);
|
||||
|
||||
tocService.tocList.next([{}, {}, {}] as TocItem[]);
|
||||
expect(component.hasFloatingToc).toBe(true);
|
||||
@ -127,37 +125,47 @@ describe('AppComponent', () => {
|
||||
it('should be false when toc is empty', () => {
|
||||
tocService.tocList.next([]);
|
||||
|
||||
component.onResize(hideToCBreakPoint + 1);
|
||||
component.onResize(showFloatingTocWidth + 1);
|
||||
expect(component.hasFloatingToc).toBe(false);
|
||||
|
||||
component.onResize(hideToCBreakPoint - 1);
|
||||
component.onResize(showFloatingTocWidth - 1);
|
||||
expect(component.hasFloatingToc).toBe(false);
|
||||
|
||||
component.onResize(hideToCBreakPoint + 1);
|
||||
component.onResize(showFloatingTocWidth + 1);
|
||||
expect(component.hasFloatingToc).toBe(false);
|
||||
});
|
||||
|
||||
it('should be true when toc is not empty unless the screen is narrow', () => {
|
||||
tocService.tocList.next([{}, {}, {}] as TocItem[]);
|
||||
|
||||
component.onResize(hideToCBreakPoint + 1);
|
||||
component.onResize(showFloatingTocWidth + 1);
|
||||
expect(component.hasFloatingToc).toBe(true);
|
||||
|
||||
component.onResize(hideToCBreakPoint - 1);
|
||||
component.onResize(showFloatingTocWidth - 1);
|
||||
expect(component.hasFloatingToc).toBe(false);
|
||||
|
||||
component.onResize(hideToCBreakPoint + 1);
|
||||
component.onResize(showFloatingTocWidth + 1);
|
||||
expect(component.hasFloatingToc).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSideBySide', () => {
|
||||
describe('showTopMenu', () => {
|
||||
it('should be updated on resize', () => {
|
||||
component.onResize(sideBySideBreakPoint - 1);
|
||||
expect(component.isSideBySide).toBe(false);
|
||||
component.onResize(showTopMenuWidth - 1);
|
||||
expect(component.showTopMenu).toBe(false);
|
||||
|
||||
component.onResize(sideBySideBreakPoint + 1);
|
||||
expect(component.isSideBySide).toBe(true);
|
||||
component.onResize(showTopMenuWidth + 1);
|
||||
expect(component.showTopMenu).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dockSideNav', () => {
|
||||
it('should be updated on resize', () => {
|
||||
component.onResize(dockSideNavWidth - 1);
|
||||
expect(component.dockSideNav).toBe(false);
|
||||
|
||||
component.onResize(dockSideNavWidth + 1);
|
||||
expect(component.dockSideNav).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -185,8 +193,8 @@ describe('AppComponent', () => {
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
describe('when side-by-side (wide)', () => {
|
||||
beforeEach(() => resizeTo(sideBySideBreakPoint + 1)); // side-by-side
|
||||
describe('when view is wide', () => {
|
||||
beforeEach(() => resizeTo(dockSideNavWidth + 1)); // wide view
|
||||
|
||||
it('should open when navigating to a guide page (guide/pipes)', () => {
|
||||
navigateTo('guide/pipes');
|
||||
@ -232,8 +240,8 @@ describe('AppComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when NOT side-by-side (narrow)', () => {
|
||||
beforeEach(() => resizeTo(sideBySideBreakPoint - 1)); // NOT side-by-side
|
||||
describe('when view is narrow', () => {
|
||||
beforeEach(() => resizeTo(dockSideNavWidth - 1)); // narrow view
|
||||
|
||||
it('should be closed when navigating to a guide page (guide/pipes)', () => {
|
||||
navigateTo('guide/pipes');
|
||||
@ -286,30 +294,30 @@ describe('AppComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changing side-by-side (narrow --> wide)', () => {
|
||||
describe('when changing from narrow to wide view', () => {
|
||||
const sidenavDocs = ['api/a/b/c/d', 'guide/pipes'];
|
||||
const nonSidenavDocs = ['features', 'about'];
|
||||
|
||||
sidenavDocs.forEach(doc => {
|
||||
it(`should open when on a sidenav doc (${doc})`, () => {
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
|
||||
navigateTo(doc);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
|
||||
resizeTo(sideBySideBreakPoint + 1);
|
||||
resizeTo(dockSideNavWidth + 1);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
nonSidenavDocs.forEach(doc => {
|
||||
it(`should remain closed when on a non-sidenav doc (${doc})`, () => {
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
|
||||
navigateTo(doc);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
|
||||
resizeTo(sideBySideBreakPoint + 1);
|
||||
resizeTo(dockSideNavWidth + 1);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -317,33 +325,33 @@ describe('AppComponent', () => {
|
||||
describe('when manually opened', () => {
|
||||
sidenavDocs.forEach(doc => {
|
||||
it(`should remain opened when on a sidenav doc (${doc})`, () => {
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
|
||||
navigateTo(doc);
|
||||
toggleSidenav();
|
||||
expect(sidenav.opened).toBe(true);
|
||||
|
||||
resizeTo(sideBySideBreakPoint + 1);
|
||||
resizeTo(dockSideNavWidth + 1);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
nonSidenavDocs.forEach(doc => {
|
||||
it(`should close when on a non-sidenav doc (${doc})`, () => {
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
|
||||
navigateTo(doc);
|
||||
toggleSidenav();
|
||||
expect(sidenav.opened).toBe(true);
|
||||
|
||||
resizeTo(sideBySideBreakPoint + 1);
|
||||
resizeTo(showTopMenuWidth + 1);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when changing side-by-side (wide --> narrow)', () => {
|
||||
describe('when changing from wide to narrow view', () => {
|
||||
const sidenavDocs = ['api/a/b/c/d', 'guide/pipes'];
|
||||
const nonSidenavDocs = ['features', 'about'];
|
||||
|
||||
@ -352,7 +360,7 @@ describe('AppComponent', () => {
|
||||
navigateTo(doc);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -362,7 +370,7 @@ describe('AppComponent', () => {
|
||||
navigateTo(doc);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
|
||||
resizeTo(sideBySideBreakPoint - 1);
|
||||
resizeTo(dockSideNavWidth - 1);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -376,7 +384,7 @@ describe('AppComponent', () => {
|
||||
async function setupSelectorForTesting(mode?: string) {
|
||||
createTestingModule('a/b', mode);
|
||||
await initializeTest();
|
||||
component.onResize(sideBySideBreakPoint + 1); // side-by-side
|
||||
component.onResize(dockSideNavWidth + 1); // wide view
|
||||
selectElement = fixture.debugElement.query(By.directive(SelectComponent));
|
||||
selectComponent = selectElement.componentInstance;
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
|
||||
const sideNavView = 'SideNav';
|
||||
export const showTopMenuWidth = 1048;
|
||||
export const dockSideNavWidth = 992;
|
||||
export const showFloatingTocWidth = 800;
|
||||
|
||||
@Component({
|
||||
selector: 'aio-shell',
|
||||
@ -57,18 +60,17 @@ export class AppComponent implements OnInit {
|
||||
isStarting = true;
|
||||
isTransitioning = true;
|
||||
isFetching = false;
|
||||
isSideBySide = false;
|
||||
showTopMenu = false;
|
||||
dockSideNav = false;
|
||||
private isFetchingTimeout: any;
|
||||
private isSideNavDoc = false;
|
||||
|
||||
private sideBySideWidth = 992;
|
||||
sideNavNodes: NavigationNode[];
|
||||
topMenuNodes: NavigationNode[];
|
||||
topMenuNarrowNodes: NavigationNode[];
|
||||
|
||||
hasFloatingToc = false;
|
||||
private showFloatingToc = new BehaviorSubject(false);
|
||||
private showFloatingTocWidth = 800;
|
||||
tocMaxHeight: string;
|
||||
private tocMaxHeightOffset = 0;
|
||||
|
||||
@ -76,8 +78,8 @@ export class AppComponent implements OnInit {
|
||||
|
||||
private currentUrl: string;
|
||||
|
||||
get isOpened() { return this.isSideBySide && this.isSideNavDoc; }
|
||||
get mode() { return this.isSideBySide ? 'side' : 'over'; }
|
||||
get isOpened() { return this.dockSideNav && this.isSideNavDoc; }
|
||||
get mode() { return this.dockSideNav && (this.isSideNavDoc || this.showTopMenu) ? 'side' : 'over'; }
|
||||
|
||||
// Search related properties
|
||||
showSearchResults = false;
|
||||
@ -239,13 +241,14 @@ export class AppComponent implements OnInit {
|
||||
|
||||
@HostListener('window:resize', ['$event.target.innerWidth'])
|
||||
onResize(width: number) {
|
||||
this.isSideBySide = width >= this.sideBySideWidth;
|
||||
this.showFloatingToc.next(width > this.showFloatingTocWidth);
|
||||
this.showTopMenu = width >= showTopMenuWidth;
|
||||
this.dockSideNav = width >= dockSideNavWidth;
|
||||
this.showFloatingToc.next(width > showFloatingTocWidth);
|
||||
|
||||
if (this.isSideBySide && !this.isSideNavDoc) {
|
||||
if (this.showTopMenu && !this.isSideNavDoc) {
|
||||
// If this is a non-sidenav doc and the screen is wide enough so that we can display menu
|
||||
// items in the top-bar, ensure the sidenav is closed.
|
||||
// (This condition can only be met when the resize event changes the value of `isSideBySide`
|
||||
// (This condition can only be met when the resize event changes the value of `showTopMenu`
|
||||
// from `false` to `true` while on a non-sidenav doc.)
|
||||
this.sidenav.toggle(false);
|
||||
}
|
||||
@ -338,7 +341,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
// May be open or closed when wide; always closed when narrow.
|
||||
this.sidenav.toggle(this.isSideBySide && openSideNav);
|
||||
this.sidenav.toggle(this.dockSideNav && openSideNav);
|
||||
}
|
||||
|
||||
// Dynamically change height of table of contents container
|
||||
|
@ -401,10 +401,7 @@ describe('DocViewerComponent', () => {
|
||||
expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]);
|
||||
});
|
||||
|
||||
// This test sometimes incorrectly fails on CI.
|
||||
// Reported in https://github.com/angular/angular/issues/37629.
|
||||
// Investigated in https://github.com/angular/angular/pull/37637.
|
||||
xit('should unsubscribe from the previous "embed" observable when unsubscribed from', () => {
|
||||
it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => {
|
||||
const obs = new ObservableWithSubscriptionSpies();
|
||||
loadElementsSpy.and.returnValue(obs);
|
||||
|
||||
@ -439,10 +436,7 @@ describe('DocViewerComponent', () => {
|
||||
expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy);
|
||||
});
|
||||
|
||||
// This test sometimes incorrectly fails on CI.
|
||||
// Reported in https://github.com/angular/angular/issues/37629.
|
||||
// Investigated in https://github.com/angular/angular/pull/37637.
|
||||
xit('should unsubscribe from the previous "swap" observable when unsubscribed from', () => {
|
||||
it('should unsubscribe from the previous "swap" observable when unsubscribed from', () => {
|
||||
const obs = new ObservableWithSubscriptionSpies();
|
||||
swapViewsSpy.and.returnValue(obs);
|
||||
|
||||
|
@ -1,27 +1,31 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { ErrorHandler } from '@angular/core';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { CustomIconRegistry, SvgIconInfo } from './custom-icon-registry';
|
||||
|
||||
describe('CustomIconRegistry', () => {
|
||||
const fakeHttpClient: HttpClient = {} as any;
|
||||
const fakeDomSanitizer: DomSanitizer = {} as any;
|
||||
const fakeDocument: Document = {} as any;
|
||||
const fakeErrorHandler: ErrorHandler = {handleError: err => console.error(err)};
|
||||
|
||||
it('should get the SVG element for a preloaded icon from the cache', () => {
|
||||
const mockHttp: any = {};
|
||||
const mockSanitizer: any = {};
|
||||
const mockDocument: any = {};
|
||||
const svgSrc = '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
|
||||
'viewBox="0 0 24 24"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></svg>';
|
||||
const svgIcons: SvgIconInfo[] = [
|
||||
{ name: 'test_icon', svgSource: svgSrc }
|
||||
];
|
||||
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
|
||||
|
||||
const registry = new CustomIconRegistry(
|
||||
fakeHttpClient, fakeDomSanitizer, fakeDocument, fakeErrorHandler, svgIcons);
|
||||
let svgElement: SVGElement|undefined;
|
||||
registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el);
|
||||
|
||||
expect(svgElement).toEqual(createSvg(svgSrc));
|
||||
});
|
||||
|
||||
it('should support caching icons with a namespace', () => {
|
||||
const mockHttp: any = {};
|
||||
const mockSanitizer: any = {};
|
||||
const mockDocument: any = {};
|
||||
|
||||
const svgSrc1 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h100" /></svg>';
|
||||
const svgSrc2 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h200" /></svg>';
|
||||
const svgSrc3 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h300" /></svg>';
|
||||
@ -31,7 +35,8 @@ describe('CustomIconRegistry', () => {
|
||||
{ namespace: 'bar', name: 'test_icon', svgSource: svgSrc3 },
|
||||
];
|
||||
|
||||
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
|
||||
const registry = new CustomIconRegistry(
|
||||
fakeHttpClient, fakeDomSanitizer, fakeDocument, fakeErrorHandler, svgIcons);
|
||||
let svgElement: SVGElement|undefined;
|
||||
registry.getNamedSvgIcon('test_icon', 'foo').subscribe(el => svgElement = el);
|
||||
|
||||
@ -39,9 +44,6 @@ describe('CustomIconRegistry', () => {
|
||||
});
|
||||
|
||||
it('should call through to the MdIconRegistry if the icon name is not in the preloaded cache', () => {
|
||||
const mockHttp: any = {};
|
||||
const mockSanitizer: any = {};
|
||||
const mockDocument: any = {};
|
||||
const svgSrc = '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
|
||||
'viewBox="0 0 24 24"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></svg>';
|
||||
const svgIcons: SvgIconInfo[] = [
|
||||
@ -49,7 +51,8 @@ describe('CustomIconRegistry', () => {
|
||||
];
|
||||
spyOn(MatIconRegistry.prototype, 'getNamedSvgIcon');
|
||||
|
||||
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
|
||||
const registry = new CustomIconRegistry(
|
||||
fakeHttpClient, fakeDomSanitizer, fakeDocument, fakeErrorHandler, svgIcons);
|
||||
|
||||
registry.getNamedSvgIcon('other_icon');
|
||||
expect(MatIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', undefined);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { InjectionToken, Inject, Injectable, Optional } from '@angular/core';
|
||||
import { ErrorHandler, InjectionToken, Inject, Injectable, Optional } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { of } from 'rxjs';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
@ -42,8 +42,8 @@ export class CustomIconRegistry extends MatIconRegistry {
|
||||
private preloadedSvgElements: SvgIconMap = {[DEFAULT_NS]: {}};
|
||||
|
||||
constructor(http: HttpClient, sanitizer: DomSanitizer, @Optional() @Inject(DOCUMENT) document: Document,
|
||||
@Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
|
||||
super(http, sanitizer, document);
|
||||
errorHandler: ErrorHandler, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
|
||||
super(http, sanitizer, document, errorHandler);
|
||||
this.loadSvgElements(svgIcons);
|
||||
}
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import './styles/ng-io-theme';
|
||||
@import './styles/main.scss';
|
@ -1,4 +1,6 @@
|
||||
// VARIABLES
|
||||
$showTopMenuWidth: 1048px;
|
||||
$hideTopMenuWidth: $showTopMenuWidth - 1;
|
||||
$hamburgerShownMargin: 0 8px 0 0;
|
||||
$hamburgerHiddenMargin: 0 16px 0 -64px;
|
||||
|
||||
@ -59,9 +61,9 @@ aio-shell.folder-docs mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-guide mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-start mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
@media (min-width: 992px) {
|
||||
@media (min-width: $showTopMenuWidth) {
|
||||
.hamburger.mat-button {
|
||||
// Hamburger shown on non-marketing pages on large screens.
|
||||
// Hamburger shown on non-marketing pages even on large screens.
|
||||
margin: $hamburgerShownMargin;
|
||||
}
|
||||
}
|
||||
@ -73,7 +75,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
margin: $hamburgerShownMargin;
|
||||
padding: 0;
|
||||
|
||||
@media (min-width: 992px) {
|
||||
@media (min-width: $showTopMenuWidth) {
|
||||
// Hamburger hidden by default on large screens.
|
||||
// (Will be shown per doc.)
|
||||
margin: $hamburgerHiddenMargin;
|
||||
@ -111,7 +113,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
@media screen and (max-width: $hideTopMenuWidth) {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
@ -125,7 +127,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
top: 12px;
|
||||
height: 40px;
|
||||
|
||||
@media (max-width: 991px) {
|
||||
@media (max-width: $hideTopMenuWidth) {
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
@ -237,6 +239,7 @@ aio-search-box.search-container {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
width: 150px;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
// import global themes
|
||||
@import '~@angular/material/theming';
|
||||
@import './ng-io-theme';
|
||||
|
||||
// import global variables
|
||||
|
52
aio/tests/e2e/src/api-list.e2e-spec.ts
Normal file
52
aio/tests/e2e/src/api-list.e2e-spec.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { by, element } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('api-list', () => {
|
||||
const apiSearchInput = element(by.css('aio-api-list .form-search input'));
|
||||
const apiStatusDropdown = element(by.css('aio-api-list aio-select[label="Status:"]'));
|
||||
const apiTypeDropdown = element(by.css('aio-api-list aio-select[label="Type:"]'));
|
||||
let page: SitePage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new SitePage();
|
||||
page.navigateTo('api');
|
||||
});
|
||||
|
||||
it('should find AnimationSequenceMetadata when searching by partial word anima', () => {
|
||||
expect(page.getApiSearchResults()).toContain('HttpEventType');
|
||||
|
||||
apiSearchInput.clear();
|
||||
apiSearchInput.sendKeys('anima');
|
||||
|
||||
expect(page.getApiSearchResults()).not.toContain('HttpEventType');
|
||||
expect(page.getApiSearchResults()).toContain('AnimationSequenceMetadata');
|
||||
});
|
||||
|
||||
it('should find getLocaleDateTimeFormat when searching by partial word date', () => {
|
||||
expect(page.getApiSearchResults()).toContain('formatCurrency');
|
||||
|
||||
apiSearchInput.clear();
|
||||
apiSearchInput.sendKeys('date');
|
||||
|
||||
expect(page.getApiSearchResults()).not.toContain('formatCurrency');
|
||||
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
|
||||
});
|
||||
|
||||
it('should find LowerCasePipe when searching for type pipe', () => {
|
||||
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
|
||||
|
||||
page.clickDropdownItem(apiTypeDropdown, 'Pipe');
|
||||
|
||||
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
|
||||
expect(page.getApiSearchResults()).toContain('LowerCasePipe');
|
||||
});
|
||||
|
||||
it('should find ElementRef when searching for status Security Risk', () => {
|
||||
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
|
||||
|
||||
page.clickDropdownItem(apiStatusDropdown, 'Security Risk');
|
||||
|
||||
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
|
||||
expect(page.getApiSearchResults()).toContain('ElementRef');
|
||||
});
|
||||
});
|
@ -83,4 +83,16 @@ export class SitePage {
|
||||
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
|
||||
return results.map(link => link && link.getText());
|
||||
}
|
||||
|
||||
getApiSearchResults() {
|
||||
const results = element.all(by.css('aio-api-list .api-item'));
|
||||
browser.wait(ExpectedConditions.presenceOf(results.first()), 2000);
|
||||
return results.map(elem => elem && elem.getText());
|
||||
}
|
||||
|
||||
clickDropdownItem(dropdown: ElementFinder, itemName: string){
|
||||
dropdown.element(by.css('.form-select-button')).click();
|
||||
const menuItem = dropdown.element(by.cssContainingText('.form-select-dropdown li', itemName));
|
||||
menuItem.click();
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
|
@ -1,9 +1,14 @@
|
||||
const fs = require('fs');
|
||||
const sh = require('shelljs');
|
||||
|
||||
const PATCH_LOCK = 'node_modules/@angular/cli/.patched';
|
||||
|
||||
if (!fs.existsSync(PATCH_LOCK)) {
|
||||
sh.set('-e');
|
||||
sh.cd(`${__dirname}/../../`);
|
||||
|
||||
if (!sh.test('-f', PATCH_LOCK)) {
|
||||
sh.ls('-l', __dirname)
|
||||
.filter(stat => stat.isFile() && /\.patch$/i.test(stat.name))
|
||||
.forEach(stat => sh.exec(`patch -p0 -i "${__dirname}/${stat.name}"`));
|
||||
|
||||
sh.touch(PATCH_LOCK);
|
||||
}
|
||||
|
||||
|
449
aio/yarn.lock
449
aio/yarn.lock
@ -2,23 +2,23 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular-devkit/architect@0.1000.0-rc.2":
|
||||
version "0.1000.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.0-rc.2.tgz#a6aaef2ceed03c28817b23d0e393bf67b383a393"
|
||||
integrity sha512-4Nhrr56cVEXAykIwAVcpqKNNeMXIpfoxeWF/PLWr5VTV8XR2GO1h7wGz0f1/RRrxkOy5/6EGD7GoPpNVoPQ1/A==
|
||||
"@angular-devkit/architect@0.1000.1":
|
||||
version "0.1000.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1000.1.tgz#6ca8529d888b3f5fc1f28863bb744855b67b811e"
|
||||
integrity sha512-GpoJ+p38feerxwfpJgrjwv/2c47qIX+TMdfKVqbswxLnzK21hXjd0zn6UfovAFwLeL1hRu2O00NDsmQn01gdoA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
rxjs "6.5.5"
|
||||
|
||||
"@angular-devkit/build-angular@0.1000.0-rc.2":
|
||||
version "0.1000.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.0-rc.2.tgz#162992f6a6b59b7a7ac79bada23512c469b44976"
|
||||
integrity sha512-ZDxgrGaVqwUbOf4y4NDdAlJ2GEaPSMQ39l24jtTAyIOQ7IIICaEI7VWMBIeioY7MS/CY83EnbygiqjB6ks+LPA==
|
||||
"@angular-devkit/build-angular@0.1000.1":
|
||||
version "0.1000.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1000.1.tgz#8073c8826763b6e0d2e5f2dcb21d55be11ce6937"
|
||||
integrity sha512-rRYtHzy27BM3XjPVqlpQOs+RXAgtiT1Qr6EiLiMcb8Twg4aWHzKF+IITLQL9CSYkpOnY40vHjWZuEVu0qIhEPQ==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1000.0-rc.2"
|
||||
"@angular-devkit/build-optimizer" "0.1000.0-rc.2"
|
||||
"@angular-devkit/build-webpack" "0.1000.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/architect" "0.1000.1"
|
||||
"@angular-devkit/build-optimizer" "0.1000.1"
|
||||
"@angular-devkit/build-webpack" "0.1000.1"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
"@babel/core" "7.9.6"
|
||||
"@babel/generator" "7.9.6"
|
||||
"@babel/plugin-transform-runtime" "7.9.6"
|
||||
@ -26,7 +26,7 @@
|
||||
"@babel/runtime" "7.9.6"
|
||||
"@babel/template" "7.8.6"
|
||||
"@jsdevtools/coverage-istanbul-loader" "3.0.3"
|
||||
"@ngtools/webpack" "10.0.0-rc.2"
|
||||
"@ngtools/webpack" "10.0.1"
|
||||
ajv "6.12.2"
|
||||
autoprefixer "9.8.0"
|
||||
babel-loader "8.1.0"
|
||||
@ -34,7 +34,7 @@
|
||||
cacache "15.0.3"
|
||||
caniuse-lite "^1.0.30001032"
|
||||
circular-dependency-plugin "5.2.0"
|
||||
copy-webpack-plugin "5.1.1"
|
||||
copy-webpack-plugin "6.0.3"
|
||||
core-js "3.6.4"
|
||||
css-loader "3.5.3"
|
||||
cssnano "4.1.10"
|
||||
@ -81,29 +81,29 @@
|
||||
webpack-subresource-integrity "1.4.1"
|
||||
worker-plugin "4.0.3"
|
||||
|
||||
"@angular-devkit/build-optimizer@0.1000.0-rc.2":
|
||||
version "0.1000.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.0-rc.2.tgz#963043cbcc50869a3b8f6c9152388dcb2240c42a"
|
||||
integrity sha512-z9lhoS9/mwsQ5zltoiWkzz3NDhqtAu1jr8WObha+nV2Lh087Un1PbgmZDGfZUKoOacve8vm39472D9+ypT5U+w==
|
||||
"@angular-devkit/build-optimizer@0.1000.1":
|
||||
version "0.1000.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.1.tgz#2cb46c7b8fc4361d06244d50545b23e91e866c15"
|
||||
integrity sha512-Q60lxyetBcMKUiNMc1vGUExuJMashHB9CF8HVenX80R9ihaA/GHAUrD2PbKBpLVoC4T291uUhfcA8MB1fJNRfw==
|
||||
dependencies:
|
||||
loader-utils "2.0.0"
|
||||
source-map "0.7.3"
|
||||
tslib "2.0.0"
|
||||
webpack-sources "1.4.3"
|
||||
|
||||
"@angular-devkit/build-webpack@0.1000.0-rc.2":
|
||||
version "0.1000.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.0-rc.2.tgz#2d4fb96614d95aca6d825aeae71194335a79e3d0"
|
||||
integrity sha512-2UCyDiGC9ymo0vNkDyc9c6u//+Z5VlU9hO7CI1fodH0/snXpjnPuP/BI74NMQFyYuK0MWQZe3AiUgybisqhnEA==
|
||||
"@angular-devkit/build-webpack@0.1000.1":
|
||||
version "0.1000.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1000.1.tgz#c0e8208dfa52ca1743955c6ae96fb1d7685103e1"
|
||||
integrity sha512-m+abxD38LYdHw+w53Rmc4MbuwDP7rfevAZ/1QR2WFYj0BI5QsTYGlpmieI2TXiwiwvTILn9UzYZAA16nKvmUwA==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1000.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/architect" "0.1000.1"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
rxjs "6.5.5"
|
||||
|
||||
"@angular-devkit/core@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.0-rc.2.tgz#6bc0bea5dec4b86960ff778e2d2b0ab5384648c3"
|
||||
integrity sha512-Yggx8uKCLJ31u1NpSb6USZsHcbejUHgJlBAmC8WiJeSDROO/kiDWsfPqa5q94NmKXcv3gdVwN44c8h5HA4hcEQ==
|
||||
"@angular-devkit/core@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.0.1.tgz#acc1c6a088507e7340964c8cbf85e91781e86686"
|
||||
integrity sha512-AXsxN00zbixi/9HyzzsDGm6rtMferxKfhG8WPJfp/0TLeJrmiLs5wdNjk8LhfTZABSTYx/QxRgOI6OnBoXePgg==
|
||||
dependencies:
|
||||
ajv "6.12.2"
|
||||
fast-json-stable-stringify "2.1.0"
|
||||
@ -111,39 +111,41 @@
|
||||
rxjs "6.5.5"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/schematics@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.0-rc.2.tgz#9930a5f19f5b2b52adb428a86a807b0a85cbd6b6"
|
||||
integrity sha512-vDDzpPgwmWaE3PXXZu+7RpoLssHcH1yY3bKwNCJZbKD9BFRwelTuIpNVdRZ4aXe2/zeEgY+8uJdliqdUYN8eDQ==
|
||||
"@angular-devkit/schematics@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.0.1.tgz#2631043e7459c5d1228ffeba18eb63aa1e45f40e"
|
||||
integrity sha512-lG70f4KsZews/z1npzJC7ccJgz3RXyLetyg+wa5uGWV+Silpr7XX+3U65DjPwG/+921woifeqRMbOhK+zCCaVA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
ora "4.0.4"
|
||||
rxjs "6.5.5"
|
||||
|
||||
"@angular/animations@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.0-rc.2.tgz#d349c7088435c28b793a12a20c99a0d92fb28eae"
|
||||
integrity sha512-dL06sIsVaxrIbiRHmUQdnjVoqSehD2Pa9BEMsmMmcWUYBUIeXpwWvE/AG8HFMba903UKsBq0U9VQ2UDnBBFlxg==
|
||||
"@angular/animations@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.0.2.tgz#4af6983800c7e6fbb402f7183fc547ae480cb534"
|
||||
integrity sha512-3fMR574KnaeAon7ZlwYCB2qkUoZ255Y2KwU6Z/ki37U66QZc8XMqf/d4oUiTZgLsJG4a5Whse0IX8Jz5/HAvQQ==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/cdk@^9.2.2":
|
||||
version "9.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-9.2.2.tgz#776be85aa55146f00dcd4909b81c67da47286c75"
|
||||
integrity sha512-VNd+KuMN6cBcy4/8OyMxqYaxdjPP6IyCqIVijB2JREkc5Sg4VWmPgx2L3rHt/DzjsVBVRgx35uqOMymDezG3jQ==
|
||||
"@angular/cdk@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-10.0.1.tgz#8e10317cd6fee4d9f8a3794998629496fb60e68b"
|
||||
integrity sha512-tEgaTDQplptbOf4cHHdVScH0h5QNvkWDhabAWpWaT4/dVXEsp+p2E9Pzkemesi/gNmUIetVjGaicX5VqsijZSQ==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/cli@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.0-rc.2.tgz#48bb8e2d1ac5aa69945e491834c4da26b33565c7"
|
||||
integrity sha512-95f7raKAoxRMyVbss4Wh37ixD99IwCd/3xvjMMqDt7u6ECW8B0weqmaZWiannrDQXkhncnfGqeC2+TufJ+ZHWw==
|
||||
"@angular/cli@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-10.0.1.tgz#23fd9e6495a89d7fe1bc756fdfb5af9c71bcaf26"
|
||||
integrity sha512-6Ht3022UcaWTBDnQKgcvTHbOP4ITjzn1DcZWKN0+zKk4PNqOwWtOEF2CBokOG79gTQVdrp2p5YQo5uX6UG7KMQ==
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.1000.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/schematics" "10.0.0-rc.2"
|
||||
"@schematics/angular" "10.0.0-rc.2"
|
||||
"@schematics/update" "0.1000.0-rc.2"
|
||||
"@angular-devkit/architect" "0.1000.1"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
"@angular-devkit/schematics" "10.0.1"
|
||||
"@schematics/angular" "10.0.1"
|
||||
"@schematics/update" "0.1000.1"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ansi-colors "4.1.1"
|
||||
debug "4.1.1"
|
||||
@ -160,17 +162,17 @@
|
||||
universal-analytics "0.4.20"
|
||||
uuid "8.1.0"
|
||||
|
||||
"@angular/common@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.0-rc.2.tgz#40f4f4ae4f99e3bdcc6332105d8f18b14d3814b7"
|
||||
integrity sha512-ooCElBY1T4xpEJBDUiB34rOsy5/hV4mBvoY5HMlHq09Agc+X8QVOGe4RPP6v9aMLklkV1Lj3Z8YZWx8+fblJsA==
|
||||
"@angular/common@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-10.0.2.tgz#8d5422b3a3e1bc405606476b90b0caffc9c9155a"
|
||||
integrity sha512-zpNB2XD6jinXZjfihoO5Q1Yg7urfpZTt/fitdmwFHCcQ/1qZ9T2BVh8+VqVRkh6Pjxmtvu0uPnJ1a/aZ5f9r9A==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/compiler-cli@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.0.0-rc.2.tgz#31d2c388774a46857167e1e97c9ba513a1468c0b"
|
||||
integrity sha512-9uYsFo/C/EpKUNIC5CHYST16xBRfEZ0+90kp6oS4vX/PqZ8hHMtMd/KOpO+aJKxDLKrkkZUOBWsRYXFnxX+H9g==
|
||||
"@angular/compiler-cli@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-10.0.2.tgz#8f178a03989e0018890c52807df9e3e03faaf47a"
|
||||
integrity sha512-LGX86k1hOyaw5aPCjFfoLuPhVLKMENOdCBeNBzZB+H2CUGHfv8OWFB1EzjZO1N07VGR7JoMx9ZWSP7ornhuS4Q==
|
||||
dependencies:
|
||||
canonical-path "1.0.0"
|
||||
chokidar "^3.0.0"
|
||||
@ -186,10 +188,10 @@
|
||||
tslib "^2.0.0"
|
||||
yargs "15.3.0"
|
||||
|
||||
"@angular/compiler@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.0.0-rc.2.tgz#0a8674938b7eed7f1f9a05cb7e51cfb61deacd14"
|
||||
integrity sha512-7WdgSw1KvEqoIxNIJ3v+TkiriybCHmRnYIzHeuDzPwZ04MZogDirwhR4jtQ6lqbiI+3LgvmWdaXZEE2XGtQfMQ==
|
||||
"@angular/compiler@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-10.0.2.tgz#1056af84ad76763f03228aa3a60b63a5cd775944"
|
||||
integrity sha512-TNE5ESDlRCVneb/K62HOEgLpxuZZTlw8RcmOy5vt3SngEVc576OE/aYsEA6MCwTlqj16GLSS3jGm9HBJcBKUVw==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
@ -200,10 +202,10 @@
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/core@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.0-rc.2.tgz#4504478e1e95b9856c998798133cf4c92c7234c3"
|
||||
integrity sha512-k0tXyQO3Yg+GqNxPF4ZQUlGS0PeODnQurox80VIDyfXYY7ds1c4LlXrFu/KBtlbG2i98tl5H8wq3u2+RNs9zPQ==
|
||||
"@angular/core@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-10.0.2.tgz#d2d2c2dd4a9e80dcccc63c274f13ab7397ee5a3b"
|
||||
integrity sha512-r4M1D2NOdkmmFyvYLHRYSIBKTGNXQarZHDZcm5oEq2eTsRVe2u9MYIeOpHKeVQCQK7XKQVB13IZQP3XpUvljFg==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
@ -214,55 +216,57 @@
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/elements@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-10.0.0-rc.2.tgz#27c1c693ad4e5e2b37476a0ac597d493cfa703ab"
|
||||
integrity sha512-0R0Rr6d2hZb/2HB8WMkyWkW7LE/oDsTXu8Qj9LPv62o2VLQ6jU6HllIBKbX9WHuowUZp4rVeMcjU24aIFLNOHg==
|
||||
"@angular/elements@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/elements/-/elements-10.0.2.tgz#42464da37306ada1a5c9c8db00e0395b5a6939f0"
|
||||
integrity sha512-ZYOuDojWJCCiRoZYXPtGhVVPT1y75V/wktzaMKLcNY/MxnJYW984i/Ynv7gdeqC8kvf0v1s0FkF7t1xndUwbCA==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/forms@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.0-rc.2.tgz#e7312a8aa3c496ca28b96bcdd2d081974f5d6c08"
|
||||
integrity sha512-Jao3QDwhXE1EDro5ScLXsEPFLsmXymOnv8oddtDmg7MDJAen/O99p7iKo+jvuAJuH7UDVzz/o2Uz0nyb9HzAXw==
|
||||
"@angular/forms@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-10.0.2.tgz#fa443f73156640664a9e018aaeb42f85d6d20fcb"
|
||||
integrity sha512-qnfApb5Q58SFdl8za8i6ijvP0UfVGxxTtIVnf8czVU5Jz5/KYDbPeChVw/aPl3hfXq8jt0Q6Yl99aAm/BNyhpQ==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/language-service@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.0-rc.2.tgz#b1ea4f4c73a1b39e6513ba54d23ef8dbc233f0f5"
|
||||
integrity sha512-jQAUdN1vRAjWhjriOhcDrbgqLYC0GXtzl3RQONJnMWm00z6he4Gw1+JRvhsc82wm1jo7hsre5Q19tS18BcnCyQ==
|
||||
"@angular/language-service@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-10.0.2.tgz#31f22020be96ab2cf77283e0627f8a6fffedfe36"
|
||||
integrity sha512-kEMJ3DkprpoGJvEjvH/PZrBKtgCbLtFYtrXHRDy2+92vgCT0Xz6TwzZ2qlwlQSijffOgsV5LwdjI4oxpgVKReQ==
|
||||
|
||||
"@angular/material@^9.2.2":
|
||||
version "9.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-9.2.2.tgz#53b2d8a62fe48b79127cdf43b3b8b082e4c3df27"
|
||||
integrity sha512-gdQiMJ6PtW/5fd+0mglHFyzxULDCBGjn9RTET3sUq2rkc9+jBXr4OvnsUyBWSnqqv97XqotVDIx5JgE4/YX/Rw==
|
||||
|
||||
"@angular/platform-browser-dynamic@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.0-rc.2.tgz#5b4809bc65e268fd43c8f1d65fb52a96975fb1c7"
|
||||
integrity sha512-zkHkMHkQgG31SkLnIcfrI9srDtrPQ3qGVl2YqPrd77PyZAZ3TN9F3cS2nsLuyS+oyYMSFZixg/UwgsdyeK81Jg==
|
||||
"@angular/material@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/material/-/material-10.0.1.tgz#f59988499d20ee4ab93292d7e9a9d16ce24839f0"
|
||||
integrity sha512-4xGIupOiPbyYG/tTbVhgjATRZSRf+Xj2FGkX3csSlIOvhrFtN1B9gTlcbOjzWHPpWTFChZALzMXA/841KA9QqA==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/platform-browser@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.0-rc.2.tgz#b2fbf923a1b9a718f36e5a03cb2357e484f0da36"
|
||||
integrity sha512-CEAXG+lCHNEYSkfcTFlSXblXjbJxZfeyAysr38iCf9lBmLx/+6eHVIrQv1sTIEABpIiqJuXVkWL2Vn6iiuvHVQ==
|
||||
"@angular/platform-browser-dynamic@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.2.tgz#b752e2110e5a4316087ce7922227b453e423c144"
|
||||
integrity sha512-R1rt1/Ynm0DHgzMBcduCPoDg2y3MrYZVgT+N0DLobr2nCaVD74vGsJBVCRv7/m1sdCxyhPlOvq8Bm5sRauw2fw==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/router@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.0-rc.2.tgz#f36f71409720c5fb1e609a2d4403900a1c5fccd3"
|
||||
integrity sha512-0moAS4p2VYxiLVhv7Axp333sbVR4iMACdsxDGJLEpmISMOUIpli7aiK/VEHBdoqESUPokrE6glgxaoXxkCq4Cg==
|
||||
"@angular/platform-browser@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-10.0.2.tgz#518db3bdff9692cc4ec0c871f3305218da66aebd"
|
||||
integrity sha512-FLS3fYSuWvrDc7PiVCvs4joWZZxDe9+alJi90Sub1oBS/EnxmZpkS0Gr7zQv4MjAvVhNCVoTzL3CaV/SIoZqaw==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/service-worker@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.0.0-rc.2.tgz#33650fe682df335cfcbd35d35122f592a364a45c"
|
||||
integrity sha512-48Jk5qdklKa9EEl09NFtsPCg8jiGXWi78911QDYuGBGBxA5jRj6dONSD7Mfbuo+ay9enY2oNshnmkO8Z38mMZg==
|
||||
"@angular/router@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-10.0.2.tgz#6514540f4f3179d8be3fd23b1f4a0dd02801a314"
|
||||
integrity sha512-IcA3W+r5T0NC8TM4J9F0VOxG0wr7nJOSef6Qek6utZd6ByLlXxOhBrPIC5Ou+QNZeg9OuI2FBt6coea9jgmNOA==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@angular/service-worker@10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.0.2.tgz#1414f4b7721a45c97c9a3efc6b7516453643d55d"
|
||||
integrity sha512-PXbh5k7yba+X18o2nNqST92Mcsq+5CXDaYIwUcUkYhx1omAMHhYF9W+FFJSY/EhW2yx5x4aRoh9ZmOqldsQB9A==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
@ -1415,16 +1419,37 @@
|
||||
merge-source-map "^1.1.0"
|
||||
schema-utils "^2.6.4"
|
||||
|
||||
"@ngtools/webpack@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.0-rc.2.tgz#3abecf33a7710550e8ab7a6cb39ded291f495a70"
|
||||
integrity sha512-ALtsFeLmfxpJnc5XvItJRJt7zDI+ggOWF8dMeuYdNIHny8w+EXtZ57h3iB6s9AE9ig9GY/n12Ax4L9OYS4VK5A==
|
||||
"@ngtools/webpack@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.0.1.tgz#f3d5143f268cc1c3c8d94de0e048b970c95e6a27"
|
||||
integrity sha512-/uUTczGcH2WJoVovCLHXdPdTzN0yQZontcf5MkoDjlA0selUdRFntV9cnUWoray9SVxzgoHqjRxBANCTsiiuuw==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
enhanced-resolve "4.1.1"
|
||||
rxjs "6.5.5"
|
||||
webpack-sources "1.4.3"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
|
||||
integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "2.0.3"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
|
||||
integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
|
||||
|
||||
"@nodelib/fs.walk@^1.2.3":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
|
||||
integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
|
||||
dependencies:
|
||||
"@nodelib/fs.scandir" "2.1.3"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@npmcli/move-file@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
|
||||
@ -1485,21 +1510,21 @@
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||
integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=
|
||||
|
||||
"@schematics/angular@10.0.0-rc.2":
|
||||
version "10.0.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.0-rc.2.tgz#bb6bc22a05904adb0b3fa6b7bc2901fe53584528"
|
||||
integrity sha512-CPtujhlumJyouVVx0VWghidtzU4cEHx9TIBJqxpNxUUpQxxCNQrG6PTz8zilP2RuqJ7gZ8Y3VF3lMT6RW8kueQ==
|
||||
"@schematics/angular@10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.0.1.tgz#0660d5eb99169a67ecfc7a4c2946de1ea32320c0"
|
||||
integrity sha512-SrWr2FzenwuofRpcaLhzJYNFVJmJwKxlKu32dWAVnclpteMO0Hnp/jVI/e70HIc6zoWzgJ4yArmwBTA+Q26yaA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/schematics" "10.0.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
"@angular-devkit/schematics" "10.0.1"
|
||||
|
||||
"@schematics/update@0.1000.0-rc.2":
|
||||
version "0.1000.0-rc.2"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.0-rc.2.tgz#d475b295bb90a0bc2713917b35c1850747f7ea85"
|
||||
integrity sha512-3hVwEmIdMYlVwffI6dmCR8s2k2sZIuMKASXjBzl4zAxYEy6CvPVcAnt235sy5c9eSJBmjo+KkRTlwISAQUM26A==
|
||||
"@schematics/update@0.1000.1":
|
||||
version "0.1000.1"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1000.1.tgz#bcccf6822eb2b145a0ac3d1b352ada2925a558bf"
|
||||
integrity sha512-AiU3RiKMfzxquzxANgDMO88iTkk9pVKQ5fZV4UwiyZO5q/fUCW4FFx9h4Nc7e9adchM+VaCz/uEAWqrVdsPv9Q==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "10.0.0-rc.2"
|
||||
"@angular-devkit/schematics" "10.0.0-rc.2"
|
||||
"@angular-devkit/core" "10.0.1"
|
||||
"@angular-devkit/schematics" "10.0.1"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ini "1.3.5"
|
||||
npm-package-arg "^8.0.0"
|
||||
@ -1553,6 +1578,11 @@
|
||||
dependencies:
|
||||
"@types/jasmine" "*"
|
||||
|
||||
"@types/json-schema@^7.0.4":
|
||||
version "7.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
|
||||
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
|
||||
|
||||
"@types/long@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
@ -1957,7 +1987,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
|
||||
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
|
||||
|
||||
ajv@6.12.2:
|
||||
ajv@6.12.2, ajv@^6.12.2:
|
||||
version "6.12.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
|
||||
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
|
||||
@ -2256,6 +2286,11 @@ array-union@^1.0.1:
|
||||
dependencies:
|
||||
array-uniq "^1.0.1"
|
||||
|
||||
array-union@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
||||
|
||||
array-uniq@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
|
||||
@ -2680,7 +2715,7 @@ braces@^2.3.1, braces@^2.3.2:
|
||||
split-string "^3.0.2"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
braces@^3.0.2, braces@~3.0.2:
|
||||
braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
@ -2934,7 +2969,7 @@ cacache@^12.0.0:
|
||||
unique-filename "^1.1.1"
|
||||
y18n "^4.0.0"
|
||||
|
||||
cacache@^12.0.2, cacache@^12.0.3:
|
||||
cacache@^12.0.2:
|
||||
version "12.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390"
|
||||
integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==
|
||||
@ -2955,7 +2990,7 @@ cacache@^12.0.2, cacache@^12.0.3:
|
||||
unique-filename "^1.1.1"
|
||||
y18n "^4.0.0"
|
||||
|
||||
cacache@^15.0.3:
|
||||
cacache@^15.0.3, cacache@^15.0.4:
|
||||
version "15.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1"
|
||||
integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw==
|
||||
@ -3818,23 +3853,22 @@ copy-descriptor@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
copy-webpack-plugin@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88"
|
||||
integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==
|
||||
copy-webpack-plugin@6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b"
|
||||
integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==
|
||||
dependencies:
|
||||
cacache "^12.0.3"
|
||||
find-cache-dir "^2.1.0"
|
||||
glob-parent "^3.1.0"
|
||||
globby "^7.1.1"
|
||||
is-glob "^4.0.1"
|
||||
loader-utils "^1.2.3"
|
||||
minimatch "^3.0.4"
|
||||
cacache "^15.0.4"
|
||||
fast-glob "^3.2.4"
|
||||
find-cache-dir "^3.3.1"
|
||||
glob-parent "^5.1.1"
|
||||
globby "^11.0.1"
|
||||
loader-utils "^2.0.0"
|
||||
normalize-path "^3.0.0"
|
||||
p-limit "^2.2.1"
|
||||
schema-utils "^1.0.0"
|
||||
serialize-javascript "^2.1.2"
|
||||
webpack-log "^2.0.0"
|
||||
p-limit "^3.0.1"
|
||||
schema-utils "^2.7.0"
|
||||
serialize-javascript "^4.0.0"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
core-js-compat@^3.6.2:
|
||||
version "3.6.4"
|
||||
@ -4467,10 +4501,10 @@ dezalgo@^1.0.0:
|
||||
asap "^2.0.0"
|
||||
wrappy "1"
|
||||
|
||||
dgeni-packages@^0.28.3:
|
||||
version "0.28.3"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.28.3.tgz#2e1e55f341c389b67ebb28933ce1e7e9ad05c49b"
|
||||
integrity sha512-WyVzY3Q4ylfnc2677le5G7a7WqkF88rBSjU9IrAofqro71yzZeWLoEdr/gJY+lJZ0PrDyuRW05pFvIbvX8N0PQ==
|
||||
dgeni-packages@^0.28.4:
|
||||
version "0.28.4"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.28.4.tgz#53a3e6700b8d8f6be168cadcc9fdb36e1d7011d3"
|
||||
integrity sha512-7AUG3pKpWtn69c3v2Mzgh+i5gd+L0AxFfYGWGzBdlJqMlQfaQPQjaS54iYCvnOlK9rXBn9j39yO6EU70gDZuFw==
|
||||
dependencies:
|
||||
canonical-path "^1.0.0"
|
||||
catharsis "^0.8.1"
|
||||
@ -4533,12 +4567,12 @@ diffie-hellman@^5.0.0:
|
||||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
dir-glob@^2.0.0:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4"
|
||||
integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
|
||||
dependencies:
|
||||
path-type "^3.0.0"
|
||||
path-type "^4.0.0"
|
||||
|
||||
dns-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
@ -5330,6 +5364,18 @@ fast-deep-equal@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
|
||||
fast-glob@^3.1.1, fast-glob@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
|
||||
integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.0"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.2"
|
||||
picomatch "^2.2.1"
|
||||
|
||||
fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
@ -5357,6 +5403,13 @@ fastparse@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
|
||||
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
|
||||
integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
faye-websocket@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
|
||||
@ -5890,6 +5943,13 @@ glob-parent@^3.1.0:
|
||||
is-glob "^3.1.0"
|
||||
path-dirname "^1.0.0"
|
||||
|
||||
glob-parent@^5.1.0, glob-parent@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@~5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
|
||||
@ -5940,6 +6000,18 @@ globals@^9.14.0:
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
|
||||
|
||||
globby@^11.0.1:
|
||||
version "11.0.1"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
|
||||
integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
|
||||
dependencies:
|
||||
array-union "^2.1.0"
|
||||
dir-glob "^3.0.1"
|
||||
fast-glob "^3.1.1"
|
||||
ignore "^5.1.4"
|
||||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
|
||||
@ -5963,18 +6035,6 @@ globby@^6.1.0:
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
globby@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
|
||||
integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA=
|
||||
dependencies:
|
||||
array-union "^1.0.1"
|
||||
dir-glob "^2.0.0"
|
||||
glob "^7.1.2"
|
||||
ignore "^3.3.5"
|
||||
pify "^3.0.0"
|
||||
slash "^1.0.0"
|
||||
|
||||
globule@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.0.tgz#41d0e9fb44afd4b80d93a23263714f90b3dec904"
|
||||
@ -6601,11 +6661,16 @@ ignore-walk@^3.0.1:
|
||||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
|
||||
ignore@^3.2.0, ignore@^3.3.3, ignore@^3.3.5:
|
||||
ignore@^3.2.0, ignore@^3.3.3:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
|
||||
|
||||
ignore@^5.1.4:
|
||||
version "5.1.8"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
|
||||
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
|
||||
|
||||
ignorefs@^1.0.0, ignorefs@^1.1.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ignorefs/-/ignorefs-1.4.1.tgz#fac9be8777e1999d5179eb1546d36ac5c8099503"
|
||||
@ -8608,6 +8673,11 @@ merge-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
metaviewport-parser@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/metaviewport-parser/-/metaviewport-parser-0.2.0.tgz#535c3ce1ccf6223a5025fddc6a1c36505f7e7db1"
|
||||
@ -8637,6 +8707,14 @@ micromatch@^3.1.10, micromatch@^3.1.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.2"
|
||||
|
||||
micromatch@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
|
||||
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
|
||||
dependencies:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.0.5"
|
||||
|
||||
miller-rabin@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
|
||||
@ -9509,7 +9587,7 @@ p-limit@^1.1.0:
|
||||
dependencies:
|
||||
p-try "^1.0.0"
|
||||
|
||||
p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1:
|
||||
p-limit@^2.0.0, p-limit@^2.2.0:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
|
||||
integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
|
||||
@ -9523,6 +9601,13 @@ p-limit@^2.3.0:
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-limit@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.1.tgz#584784ac0722d1aed09f19f90ed2999af6ce2839"
|
||||
integrity sha512-mw/p92EyOzl2MhauKodw54Rx5ZK4624rNfgNaBguFZkHzyUG9WsDzFF5/yQVEJinbJDdP4jEfMN+uBquiGnaLg==
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-locate@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||
@ -9813,6 +9898,11 @@ path-type@^3.0.0:
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||
@ -9839,6 +9929,11 @@ picomatch@^2.0.4, picomatch@^2.0.7:
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
|
||||
integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==
|
||||
|
||||
picomatch@^2.0.5, picomatch@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
|
||||
pidtree@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b"
|
||||
@ -10609,7 +10704,7 @@ querystringify@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
|
||||
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
@ -11259,6 +11354,11 @@ retry@^0.12.0:
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||
|
||||
reusify@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
rework-visit@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a"
|
||||
@ -11367,6 +11467,11 @@ run-async@^2.4.0:
|
||||
dependencies:
|
||||
is-promise "^2.1.0"
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
||||
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
|
||||
|
||||
run-queue@^1.0.0, run-queue@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
|
||||
@ -11525,6 +11630,15 @@ schema-utils@^2.6.6:
|
||||
ajv "^6.12.0"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
schema-utils@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
|
||||
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.4"
|
||||
ajv "^6.12.2"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
select-hose@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
@ -11630,6 +11744,13 @@ serialize-javascript@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.0.0.tgz#492e489a2d77b7b804ad391a5f5d97870952548e"
|
||||
integrity sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==
|
||||
|
||||
serialize-javascript@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||
integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
serve-index@^1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
|
||||
@ -11746,10 +11867,10 @@ simple-swizzle@^0.2.2:
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
slash@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||
integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
|
||||
slash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
slice-ansi@0.0.4:
|
||||
version "0.0.4"
|
||||
|
@ -38,7 +38,8 @@ genrule(
|
||||
pkg_npm(
|
||||
name = "npm_package",
|
||||
srcs = [
|
||||
"BUILD.bazel",
|
||||
"index.bzl",
|
||||
"//dev-infra/bazel:files",
|
||||
"//dev-infra/benchmark:files",
|
||||
],
|
||||
substitutions = {
|
||||
|
8
dev-infra/bazel/BUILD.bazel
Normal file
8
dev-infra/bazel/BUILD.bazel
Normal file
@ -0,0 +1,8 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
filegroup(
|
||||
name = "files",
|
||||
srcs = [
|
||||
"expand_template.bzl",
|
||||
],
|
||||
)
|
45
dev-infra/bazel/expand_template.bzl
Normal file
45
dev-infra/bazel/expand_template.bzl
Normal file
@ -0,0 +1,45 @@
|
||||
"""Implementation of the expand_template rule """
|
||||
|
||||
def expand_template_impl(ctx):
|
||||
substitutions = dict()
|
||||
|
||||
for k in ctx.attr.configuration_env_vars:
|
||||
if k in ctx.var.keys():
|
||||
substitutions["TMPL_%s" % k] = ctx.var[k]
|
||||
|
||||
for k in ctx.attr.substitutions:
|
||||
substitutions[k] = ctx.expand_location(ctx.attr.substitutions[k], targets = ctx.attr.data)
|
||||
|
||||
ctx.actions.expand_template(
|
||||
template = ctx.file.template,
|
||||
output = ctx.outputs.output_name,
|
||||
substitutions = substitutions,
|
||||
)
|
||||
|
||||
"""Rule that can be used to substitute variables in a given template file."""
|
||||
expand_template = rule(
|
||||
implementation = expand_template_impl,
|
||||
attrs = {
|
||||
"configuration_env_vars": attr.string_list(
|
||||
default = [],
|
||||
doc = "Bazel configuration variables which should be exposed to the template.",
|
||||
),
|
||||
"output_name": attr.output(
|
||||
mandatory = True,
|
||||
doc = "File where the substituted template is written to.",
|
||||
),
|
||||
"substitutions": attr.string_dict(
|
||||
mandatory = True,
|
||||
doc = "Dictionary of substitutions that should be available to the template. Dictionary key represents the placeholder in the template.",
|
||||
),
|
||||
"data": attr.label_list(
|
||||
doc = """Data dependencies for location expansion.""",
|
||||
allow_files = True,
|
||||
),
|
||||
"template": attr.label(
|
||||
mandatory = True,
|
||||
allow_single_file = True,
|
||||
doc = "File used as template.",
|
||||
),
|
||||
},
|
||||
)
|
@ -25,6 +25,7 @@ def component_benchmark(
|
||||
driver_deps,
|
||||
ng_srcs,
|
||||
ng_deps,
|
||||
ng_assets = [],
|
||||
assets = None,
|
||||
styles = None,
|
||||
entry_point = None,
|
||||
@ -65,6 +66,7 @@ def component_benchmark(
|
||||
driver_deps: Driver's dependencies
|
||||
ng_srcs: All of the ts srcs for the angular app
|
||||
ng_deps: Dependencies for the angular app
|
||||
ng_assets: The static assets for the angular app
|
||||
assets: Static files
|
||||
styles: Stylesheets
|
||||
entry_point: Main entry point for the angular app
|
||||
@ -104,6 +106,7 @@ def component_benchmark(
|
||||
ng_module(
|
||||
name = app_lib,
|
||||
srcs = ng_srcs,
|
||||
assets = ng_assets,
|
||||
# Creates ngFactory and ngSummary to be imported by the app's entry point.
|
||||
generate_ve_shims = True,
|
||||
deps = ng_deps,
|
||||
@ -132,7 +135,7 @@ def component_benchmark(
|
||||
bootstrap = ["//packages/zone.js/dist:zone.js"],
|
||||
port = 4200,
|
||||
static_files = assets + styles,
|
||||
deps = [":" + app_main + ".min_debug.es2015.js"],
|
||||
deps = [":" + app_main + ".min_debug.js"],
|
||||
additional_root_paths = ["//dev-infra/benchmark/component_benchmark/defaults"],
|
||||
serving_path = "/app_bundle.js",
|
||||
)
|
||||
|
@ -9,12 +9,12 @@ ts_library(
|
||||
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
|
||||
deps = [
|
||||
"//packages/benchpress",
|
||||
"@npm//@types/fs-extra",
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/selenium-webdriver",
|
||||
"@npm//fs-extra",
|
||||
"@npm//@types/shelljs",
|
||||
"@npm//node-uuid",
|
||||
"@npm//protractor",
|
||||
"@npm//selenium-webdriver",
|
||||
"@npm//shelljs",
|
||||
],
|
||||
)
|
||||
|
@ -5,10 +5,11 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {mkdir} from 'shelljs';
|
||||
|
||||
export {verifyNoBrowserErrors} from './e2e_util';
|
||||
|
||||
const nodeUuid = require('node-uuid');
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, StaticProvider} from '@angular/benchpress';
|
||||
import {openBrowser} from './e2e_util';
|
||||
@ -53,7 +54,7 @@ function createBenchpressRunner(): Runner {
|
||||
runId = process.env.GIT_SHA + ' ' + runId;
|
||||
}
|
||||
const resultsFolder = './dist/benchmark_results';
|
||||
fs.ensureDirSync(resultsFolder);
|
||||
mkdir('-p', resultsFolder);
|
||||
const providers: StaticProvider[] = [
|
||||
SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||
{provide: Options.FORCE_GC, useValue: globalOptions.forceGc},
|
||||
|
@ -3,7 +3,7 @@ package(default_visibility = ["//visibility:public"])
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
|
||||
|
||||
exports_files([
|
||||
"rollup.config.js",
|
||||
"rollup.config-tmpl.js",
|
||||
"terser_config.json",
|
||||
])
|
||||
|
||||
|
@ -3,414 +3,90 @@
|
||||
# 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
|
||||
|
||||
"""Rollup with Build Optimizer
|
||||
|
||||
This provides a variant of the [rollup_bundle] rule that works better for Angular apps.
|
||||
|
||||
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
|
||||
better optimization. It also uses ESM5 format inputs, as this is what
|
||||
build-optimizer is hard-coded to look for and transform.
|
||||
|
||||
[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html
|
||||
"""
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
|
||||
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NpmPackageInfo", "node_modules_aspect")
|
||||
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
|
||||
load("@npm_bazel_terser//:index.bzl", "terser_minified")
|
||||
load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
|
||||
load("//dev-infra/bazel:expand_template.bzl", "expand_template")
|
||||
|
||||
_NG_ROLLUP_BUNDLE_OUTPUTS = {
|
||||
"bundle": "%{name}.js",
|
||||
"sourcemap": "%{name}.js.map",
|
||||
}
|
||||
def ng_rollup_bundle(
|
||||
name,
|
||||
entry_point,
|
||||
deps = [],
|
||||
license_banner = None,
|
||||
build_optimizer = True,
|
||||
visibility = None,
|
||||
format = "iife",
|
||||
globals = {},
|
||||
**kwargs):
|
||||
"""Rollup with Build Optimizer on target prodmode output (ESM2015).
|
||||
|
||||
_NG_ROLLUP_MODULE_MAPPINGS_ATTR = "ng_rollup_module_mappings"
|
||||
|
||||
def _ng_rollup_module_mappings_aspect_impl(target, ctx):
|
||||
mappings = dict()
|
||||
for dep in ctx.rule.attr.deps:
|
||||
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
|
||||
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
|
||||
if k in mappings and mappings[k] != v:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(target.label, k, mappings[k], v)), "deps")
|
||||
mappings[k] = v
|
||||
if ((hasattr(ctx.rule.attr, "module_name") and ctx.rule.attr.module_name) or
|
||||
(hasattr(ctx.rule.attr, "module_root") and ctx.rule.attr.module_root)):
|
||||
mn = ctx.rule.attr.module_name
|
||||
if not mn:
|
||||
mn = target.label.name
|
||||
mr = target.label.package
|
||||
if target.label.workspace_root:
|
||||
mr = "%s/%s" % (target.label.workspace_root, mr)
|
||||
if ctx.rule.attr.module_root and ctx.rule.attr.module_root != ".":
|
||||
if ctx.rule.attr.module_root.endswith(".ts"):
|
||||
# This is the type-checking module mapping. Strip the trailing .d.ts
|
||||
# as it doesn't belong in TypeScript's path mapping.
|
||||
mr = "%s/%s" % (mr, ctx.rule.attr.module_root.replace(".d.ts", ""))
|
||||
else:
|
||||
mr = "%s/%s" % (mr, ctx.rule.attr.module_root)
|
||||
if mn in mappings and mappings[mn] != mr:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(target.label, mn, mappings[mn], mr)), "deps")
|
||||
mappings[mn] = mr
|
||||
return struct(ng_rollup_module_mappings = mappings)
|
||||
|
||||
ng_rollup_module_mappings_aspect = aspect(
|
||||
_ng_rollup_module_mappings_aspect_impl,
|
||||
attr_aspects = ["deps"],
|
||||
)
|
||||
|
||||
_NG_ROLLUP_BUNDLE_DEPS_ASPECTS = [esm5_outputs_aspect, ng_rollup_module_mappings_aspect, node_modules_aspect]
|
||||
|
||||
_NG_ROLLUP_BUNDLE_ATTRS = {
|
||||
"build_optimizer": attr.bool(
|
||||
doc = """Use build optimizer plugin
|
||||
|
||||
Only used if sources are esm5 which depends on value of esm5_sources.""",
|
||||
default = True,
|
||||
),
|
||||
"esm5_sources": attr.bool(
|
||||
doc = """Use esm5 input sources""",
|
||||
default = True,
|
||||
),
|
||||
"srcs": attr.label_list(
|
||||
doc = """JavaScript source files from the workspace.
|
||||
These can use ES2015 syntax and ES Modules (import/export)""",
|
||||
allow_files = True,
|
||||
),
|
||||
"entry_point": attr.label(
|
||||
doc = """The starting point of the application, passed as the `--input` flag to rollup.
|
||||
|
||||
If the entry JavaScript file belongs to the same package (as the BUILD file),
|
||||
you can simply reference it by its relative name to the package directory:
|
||||
|
||||
```
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":main.js",
|
||||
)
|
||||
```
|
||||
|
||||
You can specify the entry point as a typescript file so long as you also include
|
||||
the ts_library target in deps:
|
||||
|
||||
```
|
||||
ts_library(
|
||||
name = "main",
|
||||
srcs = ["main.ts"],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
deps = [":main"]
|
||||
entry_point = ":main.ts",
|
||||
)
|
||||
```
|
||||
|
||||
The rule will use the corresponding `.js` output of the ts_library rule as the entry point.
|
||||
|
||||
If the entry point target is a rule, it should produce a single JavaScript entry file that will be passed to the nodejs_binary rule.
|
||||
For example:
|
||||
|
||||
```
|
||||
filegroup(
|
||||
name = "entry_file",
|
||||
srcs = ["main.js"],
|
||||
)
|
||||
|
||||
ng_rollup_bundle(
|
||||
name = "bundle",
|
||||
entry_point = ":entry_file",
|
||||
)
|
||||
```
|
||||
""",
|
||||
mandatory = True,
|
||||
allow_single_file = True,
|
||||
),
|
||||
"deps": attr.label_list(
|
||||
doc = """Other targets that provide JavaScript files.
|
||||
Typically this will be `ts_library` or `ng_module` targets.""",
|
||||
aspects = _NG_ROLLUP_BUNDLE_DEPS_ASPECTS,
|
||||
),
|
||||
"format": attr.string(
|
||||
doc = """"Specifies the format of the generated bundle. One of the following:
|
||||
|
||||
- `amd`: Asynchronous Module Definition, used with module loaders like RequireJS
|
||||
- `cjs`: CommonJS, suitable for Node and other bundlers
|
||||
- `esm`: Keep the bundle as an ES module file, suitable for other bundlers and inclusion as a `<script type=module>` tag in modern browsers
|
||||
- `iife`: A self-executing function, suitable for inclusion as a `<script>` tag. (If you want to create a bundle for your application, you probably want to use this.)
|
||||
- `umd`: Universal Module Definition, works as amd, cjs and iife all in one
|
||||
- `system`: Native format of the SystemJS loader
|
||||
""",
|
||||
values = ["amd", "cjs", "esm", "iife", "umd", "system"],
|
||||
default = "esm",
|
||||
),
|
||||
"global_name": attr.string(
|
||||
doc = """A name given to this package when referenced as a global variable.
|
||||
This name appears in the bundle module incantation at the beginning of the file,
|
||||
and governs the global symbol added to the global context (e.g. `window`) as a side-
|
||||
effect of loading the UMD/IIFE JS bundle.
|
||||
|
||||
Rollup doc: "The variable name, representing your iife/umd bundle, by which other scripts on the same page can access it."
|
||||
|
||||
This is passed to the `output.name` setting in Rollup.""",
|
||||
),
|
||||
"globals": attr.string_dict(
|
||||
doc = """A dict of symbols that reference external scripts.
|
||||
The keys are variable names that appear in the program,
|
||||
and the values are the symbol to reference at runtime in a global context (UMD bundles).
|
||||
For example, a program referencing @angular/core should use ng.core
|
||||
as the global reference, so Angular users should include the mapping
|
||||
`"@angular/core":"ng.core"` in the globals.""",
|
||||
default = {},
|
||||
),
|
||||
"license_banner": attr.label(
|
||||
doc = """A .txt file passed to the `banner` config option of rollup.
|
||||
The contents of the file will be copied to the top of the resulting bundles.
|
||||
Note that you can replace a version placeholder in the license file, by using
|
||||
the special version `0.0.0-PLACEHOLDER`. See the section on stamping in the README.""",
|
||||
allow_single_file = [".txt"],
|
||||
),
|
||||
"_rollup": attr.label(
|
||||
executable = True,
|
||||
cfg = "host",
|
||||
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup_with_build_optimizer"),
|
||||
),
|
||||
"_rollup_config_tmpl": attr.label(
|
||||
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup.config.js"),
|
||||
allow_single_file = True,
|
||||
),
|
||||
}
|
||||
|
||||
def _compute_node_modules_root(ctx):
|
||||
"""Computes the node_modules root from the node_modules and deps attributes.
|
||||
|
||||
Args:
|
||||
ctx: the skylark execution context
|
||||
|
||||
Returns:
|
||||
The node_modules root as a string
|
||||
"""
|
||||
node_modules_root = None
|
||||
for d in ctx.attr.deps:
|
||||
if NpmPackageInfo in d:
|
||||
possible_root = "/".join(["external", d[NpmPackageInfo].workspace, "node_modules"])
|
||||
if not node_modules_root:
|
||||
node_modules_root = possible_root
|
||||
elif node_modules_root != possible_root:
|
||||
fail("All npm dependencies need to come from a single workspace. Found '%s' and '%s'." % (node_modules_root, possible_root))
|
||||
if not node_modules_root:
|
||||
# there are no fine grained deps but we still need a node_modules_root even if its empty
|
||||
node_modules_root = "external/npm/node_modules"
|
||||
return node_modules_root
|
||||
|
||||
# Avoid using non-normalized paths (workspace/../other_workspace/path)
|
||||
def _to_manifest_path(ctx, file):
|
||||
if file.short_path.startswith("../"):
|
||||
return file.short_path[3:]
|
||||
else:
|
||||
return ctx.workspace_name + "/" + file.short_path
|
||||
|
||||
# Expand entry_point into runfiles and strip the file extension
|
||||
def _esm5_entry_point_path(ctx):
|
||||
return _to_manifest_path(ctx, ctx.file.entry_point)[:-(len(ctx.file.entry_point.extension) + 1)]
|
||||
|
||||
def _no_ext(f):
|
||||
return f.short_path[:-len(f.extension) - 1]
|
||||
|
||||
def _resolve_js_input(f, inputs):
|
||||
if f.extension == "js" or f.extension == "mjs":
|
||||
return f
|
||||
|
||||
# look for corresponding js file in inputs
|
||||
no_ext = _no_ext(f)
|
||||
for i in inputs:
|
||||
if i.extension == "js" or i.extension == "mjs":
|
||||
if _no_ext(i) == no_ext:
|
||||
return i
|
||||
fail("Could not find corresponding javascript entry point for %s. Add the %s.js to your deps." % (f.path, no_ext))
|
||||
|
||||
def _write_rollup_config(ctx, root_dir, build_optimizer, filename = "_%s.rollup.conf.js"):
|
||||
"""Generate a rollup config file.
|
||||
|
||||
Args:
|
||||
ctx: Bazel rule execution context
|
||||
root_dir: root directory for module resolution
|
||||
build_optimizer: whether to enable Build Optimizer plugin
|
||||
filename: output filename pattern (defaults to `_%s.rollup.conf.js`)
|
||||
|
||||
Returns:
|
||||
The rollup config file. See https://rollupjs.org/guide/en#configuration-files
|
||||
"""
|
||||
config = ctx.actions.declare_file(filename % ctx.label.name)
|
||||
|
||||
mappings = dict()
|
||||
all_deps = ctx.attr.deps + ctx.attr.srcs
|
||||
for dep in all_deps:
|
||||
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
|
||||
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
|
||||
if k in mappings and mappings[k] != v:
|
||||
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
|
||||
(dep.label, k, mappings[k], v)), "deps")
|
||||
mappings[k] = v
|
||||
|
||||
globals = {}
|
||||
external = []
|
||||
if ctx.attr.globals:
|
||||
globals = ctx.attr.globals.items()
|
||||
external = ctx.attr.globals.keys()
|
||||
|
||||
ctx.actions.expand_template(
|
||||
output = config,
|
||||
template = ctx.file._rollup_config_tmpl,
|
||||
substitutions = {
|
||||
"TMPL_banner_file": "\"%s\"" % ctx.file.license_banner.path if ctx.file.license_banner else "undefined",
|
||||
"TMPL_build_optimizer": "true" if build_optimizer else "false",
|
||||
"TMPL_module_mappings": str(mappings),
|
||||
"TMPL_node_modules_root": _compute_node_modules_root(ctx),
|
||||
"TMPL_root_dir": root_dir,
|
||||
"TMPL_stamp_data": "\"%s\"" % ctx.version_file.path if ctx.version_file else "undefined",
|
||||
"TMPL_workspace_name": ctx.workspace_name,
|
||||
"TMPL_external": ", ".join(["'%s'" % e for e in external]),
|
||||
"TMPL_globals": ", ".join(["'%s': '%s'" % g for g in globals]),
|
||||
"TMPL_ivy_enabled": "true" if ctx.var.get("angular_ivy_enabled", None) == "True" else "false",
|
||||
},
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
def _filter_js_inputs(all_inputs):
|
||||
all_inputs_list = all_inputs.to_list() if type(all_inputs) == type(depset()) else all_inputs
|
||||
return [
|
||||
f
|
||||
for f in all_inputs_list
|
||||
if f.path.endswith(".js") or f.path.endswith(".mjs") or f.path.endswith(".json")
|
||||
]
|
||||
|
||||
def _run_rollup(ctx, entry_point_path, sources, config):
|
||||
args = ctx.actions.args()
|
||||
args.add("--config", config.path)
|
||||
args.add("--input", entry_point_path)
|
||||
args.add("--output.file", ctx.outputs.bundle)
|
||||
args.add("--output.name", ctx.attr.global_name if ctx.attr.global_name else ctx.label.name)
|
||||
args.add("--output.format", ctx.attr.format)
|
||||
args.add("--output.sourcemap")
|
||||
args.add("--output.sourcemapFile", ctx.outputs.sourcemap)
|
||||
|
||||
# We will produce errors as needed. Anything else is spammy: a well-behaved
|
||||
# bazel rule prints nothing on success.
|
||||
args.add("--silent")
|
||||
|
||||
args.add("--preserveSymlinks")
|
||||
|
||||
direct_inputs = [config]
|
||||
|
||||
# Also include files from npm fine grained deps as inputs.
|
||||
# These deps are identified by the NpmPackageInfo provider.
|
||||
for d in ctx.attr.deps:
|
||||
if NpmPackageInfo in d:
|
||||
# Note: we can't avoid calling .to_list() on sources
|
||||
direct_inputs.extend(_filter_js_inputs(d[NpmPackageInfo].sources.to_list()))
|
||||
|
||||
if ctx.file.license_banner:
|
||||
direct_inputs.append(ctx.file.license_banner)
|
||||
if ctx.version_file:
|
||||
direct_inputs.append(ctx.version_file)
|
||||
|
||||
ctx.actions.run(
|
||||
progress_message = "Bundling JavaScript %s [rollup]" % ctx.outputs.bundle.short_path,
|
||||
executable = ctx.executable._rollup,
|
||||
inputs = depset(direct_inputs, transitive = [sources]),
|
||||
outputs = [ctx.outputs.bundle, ctx.outputs.sourcemap],
|
||||
arguments = [args],
|
||||
)
|
||||
|
||||
def _ng_rollup_bundle_impl(ctx):
|
||||
if ctx.attr.esm5_sources:
|
||||
# Use esm5 sources and build optimzier if ctx.attr.build_optimizer is set
|
||||
rollup_config = _write_rollup_config(ctx, build_optimizer = ctx.attr.build_optimizer, root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
|
||||
_run_rollup(ctx, _esm5_entry_point_path(ctx), flatten_esm5(ctx), rollup_config)
|
||||
else:
|
||||
# Use esm2015 sources and no build optimzier
|
||||
rollup_config = _write_rollup_config(ctx, build_optimizer = False, root_dir = ctx.bin_dir.path)
|
||||
esm2015_files_depsets = []
|
||||
for dep in ctx.attr.deps:
|
||||
if JSEcmaScriptModuleInfo in dep:
|
||||
esm2015_files_depsets.append(dep[JSEcmaScriptModuleInfo].sources)
|
||||
esm2015_files = depset(transitive = esm2015_files_depsets)
|
||||
entry_point_path = _to_manifest_path(ctx, _resolve_js_input(ctx.file.entry_point, esm2015_files.to_list()))
|
||||
_run_rollup(ctx, entry_point_path, esm2015_files, rollup_config)
|
||||
|
||||
return DefaultInfo(files = depset([ctx.outputs.bundle, ctx.outputs.sourcemap]))
|
||||
|
||||
_ng_rollup_bundle = rule(
|
||||
implementation = _ng_rollup_bundle_impl,
|
||||
attrs = _NG_ROLLUP_BUNDLE_ATTRS,
|
||||
outputs = _NG_ROLLUP_BUNDLE_OUTPUTS,
|
||||
)
|
||||
"""
|
||||
Run [Rollup] with the [Build Optimizer] plugin and use esm5 inputs.
|
||||
|
||||
[Rollup]: https://rollupjs.org/
|
||||
[Build Optimizer]: https://www.npmjs.com/package/@angular-devkit/build-optimizer
|
||||
"""
|
||||
|
||||
def ng_rollup_bundle(name, **kwargs):
|
||||
"""Rollup with Build Optimizer on esm5 inputs.
|
||||
|
||||
This provides a variant of the [legacy rollup_bundle] rule that works better for Angular apps.
|
||||
This provides an extension of the [rollup_bundle] rule that works better for Angular apps.
|
||||
|
||||
Runs [rollup], [terser_minified] and [brotli] to produce a number of output bundles.
|
||||
|
||||
es5 : "%{name}.js"
|
||||
es5 minified : "%{name}.min.js"
|
||||
es5 minified (compressed) : "%{name}.min.js.br",
|
||||
es5 minified (debug) : "%{name}.min_debug.js"
|
||||
es2015 : "%{name}.es2015.js"
|
||||
es2015 minified : "%{name}.min.es2015.js"
|
||||
es2015 minified (compressed) : "%{name}.min.js.es2015.br",
|
||||
es2015 minified (debug) : "%{name}.min_debug.es2015.js"
|
||||
es2015 : "%{name}.js"
|
||||
es2015 minified : "%{name}.min.js"
|
||||
es2015 minified (compressed) : "%{name}.min.js.br",
|
||||
es2015 minified (debug) : "%{name}.min_debug.js"
|
||||
|
||||
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
|
||||
better optimization. It also uses ESM5 format inputs, as this is what
|
||||
build-optimizer is hard-coded to look for and transform.
|
||||
It registers `@angular-devkit/build-optimizer` as a rollup plugin by default. This helps
|
||||
with further optimization. See https://github.com/angular/angular-cli/tree/master/packages/angular_devkit/build_optimizer.
|
||||
|
||||
[legacy rollup_bundle]: https://github.com/bazelbuild/rules_nodejs/blob/0.38.3/internal/rollup/rollup_bundle.bzl
|
||||
[rollup_bundle]: https://github.com/bazelbuild/rules_nodejs/blob/1.x/packages/rollup/src/rollup_bundle.bzl
|
||||
[rollup]: https://rollupjs.org/guide/en/
|
||||
[terser_minified]: https://bazelbuild.github.io/rules_nodejs/Terser.html
|
||||
[brotli]: https://brotli.org/
|
||||
"""
|
||||
format = kwargs.pop("format", "iife")
|
||||
build_optimizer = kwargs.pop("build_optimizer", True)
|
||||
visibility = kwargs.pop("visibility", None)
|
||||
|
||||
# Common arguments for all terser_minified targets
|
||||
common_terser_args = {
|
||||
# As of terser 4.3.4 license comments are preserved by default. See
|
||||
# https://github.com/terser/terser/blob/master/CHANGELOG.md. We want to
|
||||
# maintain the comments off behavior. We pass the --comments flag with
|
||||
# a regex that always evaluates to false to do this.
|
||||
"args": ["--comments", "/bogus_string_to_suppress_all_comments^/"],
|
||||
config_data = [license_banner] if license_banner else []
|
||||
|
||||
expand_template(
|
||||
name = "%s_rollup_config" % name,
|
||||
template = "//dev-infra/benchmark/ng_rollup_bundle:rollup.config-tmpl.js",
|
||||
output_name = "%s_rollup_config.js" % name,
|
||||
configuration_env_vars = ["angular_ivy_enabled"],
|
||||
data = config_data,
|
||||
substitutions = {
|
||||
"TMPL_build_optimizer": "true" if build_optimizer else "false",
|
||||
"TMPL_banner_file": "\"$(execpath %s)\"" % license_banner if license_banner else "undefined",
|
||||
"TMPL_external": ", ".join(["'%s'" % e for e in globals.keys()]),
|
||||
"TMPL_globals": ", ".join(["'%s': '%s'" % (g, g) for g in globals]),
|
||||
},
|
||||
visibility = visibility,
|
||||
)
|
||||
|
||||
rollup_bundle(
|
||||
name = name,
|
||||
config_file = "%s_rollup_config" % name,
|
||||
entry_points = {
|
||||
(entry_point): name,
|
||||
},
|
||||
visibility = visibility,
|
||||
deps = config_data + deps + [
|
||||
"@npm//rollup-plugin-node-resolve",
|
||||
"@npm//rollup-plugin-sourcemaps",
|
||||
"@npm//rollup-plugin-commonjs",
|
||||
"@npm//@angular-devkit/build-optimizer",
|
||||
],
|
||||
silent = True,
|
||||
format = format,
|
||||
sourcemap = "true",
|
||||
**kwargs
|
||||
)
|
||||
|
||||
common_terser_options = {
|
||||
"visibility": visibility,
|
||||
"config_file": "//dev-infra/benchmark/ng_rollup_bundle:terser_config.json",
|
||||
# TODO: Enable source maps for better debugging when `@bazel/terser` pre-declares
|
||||
# JS and map outputs. Tracked with: DEV-120
|
||||
"sourcemap": False,
|
||||
}
|
||||
|
||||
# TODO(gregmagolan): reduce this macro to just use the new @bazel/rollup rollup_bundle
|
||||
# once esm5 inputs are no longer needed. _ng_rollup_bundle is just here for esm5 support
|
||||
# and once that requirement is removed for Angular 10 then there is nothing that rule is doing
|
||||
# that the new @bazel/rollup rollup_bundle rule can't do.
|
||||
_ng_rollup_bundle(
|
||||
name = name,
|
||||
build_optimizer = build_optimizer,
|
||||
format = format,
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
terser_minified(name = name + ".min", src = name, visibility = visibility, **common_terser_args)
|
||||
terser_minified(name = name + ".min", src = name + ".js", **common_terser_options)
|
||||
native.filegroup(name = name + ".min.js", srcs = [name + ".min"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug", src = name, debug = True, visibility = visibility, **common_terser_args)
|
||||
terser_minified(name = name + ".min_debug", src = name + ".js", debug = True, **common_terser_options)
|
||||
native.filegroup(name = name + ".min_debug.js", srcs = [name + ".min_debug"], visibility = visibility)
|
||||
|
||||
npm_package_bin(
|
||||
name = "_%s_brotli" % name,
|
||||
tool = "//dev-infra/benchmark/brotli-cli",
|
||||
@ -422,50 +98,3 @@ def ng_rollup_bundle(name, **kwargs):
|
||||
],
|
||||
visibility = visibility,
|
||||
)
|
||||
|
||||
_ng_rollup_bundle(
|
||||
name = name + ".es2015",
|
||||
esm5_sources = False,
|
||||
format = format,
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
terser_minified(name = name + ".min.es2015", src = name + ".es2015", visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min.es2015.js", srcs = [name + ".min.es2015"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug.es2015", src = name + ".es2015", debug = True, visibility = visibility, **common_terser_args)
|
||||
native.filegroup(name = name + ".min_debug.es2015.js", srcs = [name + ".min_debug.es2015"], visibility = visibility)
|
||||
npm_package_bin(
|
||||
name = "_%s_es2015_brotli" % name,
|
||||
tool = "//dev-infra/benchmark/brotli-cli",
|
||||
data = [name + ".min.es2015.js"],
|
||||
outs = [name + ".min.es2015.js.br"],
|
||||
args = [
|
||||
"--output=$(execpath %s.min.es2015.js.br)" % name,
|
||||
"$(execpath %s.min.es2015.js)" % name,
|
||||
],
|
||||
visibility = visibility,
|
||||
)
|
||||
|
||||
def ls_rollup_bundle(name, **kwargs):
|
||||
"""A variant of ng_rollup_bundle for the language-service bundle
|
||||
|
||||
ls_rollup_bundle uses esm5 inputs, outputs AMD and does not use the build optimizer.
|
||||
"""
|
||||
visibility = kwargs.pop("visibility", None)
|
||||
|
||||
# Note: the output file is called "umd.js" because of historical reasons.
|
||||
# The format is actually AMD and not UMD, but we are afraid to rename
|
||||
# the file because that would likely break the IDE and other integrations that
|
||||
# have the path hardcoded in them.
|
||||
ng_rollup_bundle(
|
||||
name = name + ".umd",
|
||||
build_optimizer = False,
|
||||
format = "amd",
|
||||
visibility = visibility,
|
||||
**kwargs
|
||||
)
|
||||
native.alias(
|
||||
name = name,
|
||||
actual = name + ".umd",
|
||||
visibility = visibility,
|
||||
)
|
||||
|
89
dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js
Normal file
89
dev-infra/benchmark/ng_rollup_bundle/rollup.config-tmpl.js
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// Rollup configuration
|
||||
// GENERATED BY Bazel
|
||||
|
||||
const buildOptimizer =
|
||||
require('@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js');
|
||||
const nodeResolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function log_verbose(...m) {
|
||||
// This is a template file so we use __filename to output the actual filename
|
||||
if (!!process.env['VERBOSE_LOGS']) console.error(`[${path.basename(__filename)}]`, ...m);
|
||||
}
|
||||
|
||||
// Substitutions from the `ng_rollup_bundle` macro. We want to conditionally toggle
|
||||
// build optimizer, support optional banner files, and generally respect the current
|
||||
// compilation mode (i.e. Ivy or View Engine) as that affects module resolution.
|
||||
const useBuildOptimizer = TMPL_build_optimizer;
|
||||
const bannerFile = TMPL_banner_file;
|
||||
const ivyEnabled = 'TMPL_angular_ivy_enabled' === 'True';
|
||||
// `bazel_stamp_file` is a substitution that is applied by `@bazel/rollup`.
|
||||
const stampDataFile = bazel_stamp_file;
|
||||
|
||||
log_verbose(`running with
|
||||
cwd: ${process.cwd()}
|
||||
useBuildOptimizer: ${useBuildOptimizer}
|
||||
bannerFile: ${bannerFile}
|
||||
stampDataFile: ${stampDataFile}
|
||||
ivyEnabled: ${ivyEnabled}
|
||||
`);
|
||||
|
||||
const plugins = [
|
||||
nodeResolve({
|
||||
// If Ivy is enabled, we need to make sure that the module resolution prioritizes ngcc
|
||||
// processed entry-point fields. Ngcc adds special fields to `package.json` files of
|
||||
// modules that have been processed. Prioritizing these fields matches the Angular CLIs
|
||||
// behavior for supporting Ivy. We need to support ngcc because `ng_rollup_bundle` rule is
|
||||
// shared with other repositories that consume Angular from NPM (w/ ngcc).
|
||||
// https://github.com/angular/angular-cli/blob/1a1ceb609b9a87c4021cce3a6f0fc6d167cd09d2/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L918-L920
|
||||
mainFields: ivyEnabled ? ['module_ivy_ngcc', 'main_ivy_ngcc', 'module', 'main'] :
|
||||
['module', 'main'],
|
||||
}),
|
||||
commonjs({ignoreGlobal: true}),
|
||||
sourcemaps(),
|
||||
];
|
||||
|
||||
if (useBuildOptimizer) {
|
||||
plugins.unshift(buildOptimizer.default({
|
||||
sideEffectFreeModules: [],
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
plugins,
|
||||
external: [TMPL_external],
|
||||
output: {
|
||||
globals: {TMPL_globals},
|
||||
banner: extractBannerIfConfigured(),
|
||||
}
|
||||
};
|
||||
|
||||
/** Extracts the top-level bundle banner if specified. */
|
||||
function extractBannerIfConfigured() {
|
||||
if (!bannerFile) {
|
||||
return undefined;
|
||||
}
|
||||
let banner = fs.readFileSync(bannerFile, 'utf8');
|
||||
if (stampDataFile) {
|
||||
const versionTag = fs.readFileSync(stampDataFile, 'utf8')
|
||||
.split('\n')
|
||||
.find(s => s.startsWith('BUILD_SCM_VERSION'));
|
||||
// Don't assume BUILD_SCM_VERSION exists
|
||||
if (versionTag) {
|
||||
const version = versionTag.split(' ')[1].trim();
|
||||
banner = banner.replace(/0.0.0-PLACEHOLDER/, version);
|
||||
}
|
||||
}
|
||||
return banner;
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// Rollup configuration
|
||||
// GENERATED BY Bazel
|
||||
|
||||
const buildOptimizer = require(
|
||||
'npm/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js');
|
||||
const nodeResolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function log_verbose(...m) {
|
||||
// This is a template file so we use __filename to output the actual filename
|
||||
if (!!process.env['VERBOSE_LOGS']) console.error(`[${path.basename(__filename)}]`, ...m);
|
||||
}
|
||||
|
||||
const workspaceName = 'TMPL_workspace_name';
|
||||
const useBuildOptimzier = TMPL_build_optimizer;
|
||||
const rootDir = 'TMPL_root_dir';
|
||||
const bannerFile = TMPL_banner_file;
|
||||
const stampData = TMPL_stamp_data;
|
||||
const moduleMappings = TMPL_module_mappings;
|
||||
const nodeModulesRoot = 'TMPL_node_modules_root';
|
||||
const ivyEnabled = TMPL_ivy_enabled;
|
||||
|
||||
log_verbose(`running with
|
||||
cwd: ${process.cwd()}
|
||||
workspaceName: ${workspaceName}
|
||||
useBuildOptimzier: ${useBuildOptimzier}
|
||||
rootDir: ${rootDir}
|
||||
bannerFile: ${bannerFile}
|
||||
stampData: ${stampData}
|
||||
moduleMappings: ${JSON.stringify(moduleMappings)}
|
||||
nodeModulesRoot: ${nodeModulesRoot}
|
||||
ivyEnabled: ${ivyEnabled}
|
||||
`);
|
||||
|
||||
function fileExists(filePath) {
|
||||
try {
|
||||
return fs.statSync(filePath).isFile();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This resolver mimics the TypeScript Path Mapping feature, which lets us resolve
|
||||
// modules based on a mapping of short names to paths.
|
||||
function resolveBazel(
|
||||
importee, importer, baseDir = process.cwd(), resolve = require.resolve, root = rootDir) {
|
||||
log_verbose(`resolving '${importee}' from ${importer}`);
|
||||
|
||||
function resolveInRootDir(importee) {
|
||||
function tryImportee(importee) {
|
||||
var candidate = path.join(baseDir, root, importee);
|
||||
log_verbose(`try to resolve '${importee}' at '${candidate}'`);
|
||||
try {
|
||||
var result = resolve(candidate);
|
||||
return result;
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
// Attempt in the following order {importee}.mjs, {importee}/index.mjs,
|
||||
// {importee}, {importee}.js, {importee}/index.js. If an .mjs file is
|
||||
// available it should be resolved as rollup cannot handle the .js files
|
||||
// generated by ts_library as they are not esm. When rolling up esm5 files
|
||||
// these are re-rooted so it is not an issue.
|
||||
// TODO(gregmagolan): clean this up in the future as the .mjs es2015 outputs
|
||||
// along side the .js es5 outputs from ts_library creates this unusual situation for
|
||||
// which we can't rely on standard node module resolution to do the right thing.
|
||||
// In the future ts_library (or equivalent) should only produce a single flavor of
|
||||
// output and ng_rollup_bundle should also just use the use the vanilla rollup_bundle
|
||||
// rule without the need for a custom bazel resolver.
|
||||
return tryImportee(`${importee}.mjs`) || tryImportee(`${importee}/index.mjs`) ||
|
||||
tryImportee(importee) || tryImportee(`${importee}.js`) ||
|
||||
tryImportee(`${importee}/index.js`);
|
||||
}
|
||||
|
||||
// Since mappings are always in POSIX paths, when comparing the importee to mappings
|
||||
// we should normalize the importee.
|
||||
// Having it normalized is also useful to determine relative paths.
|
||||
const normalizedImportee = importee.replace(/\\/g, '/');
|
||||
|
||||
// If import is fully qualified then resolve it directly
|
||||
if (fileExists(importee)) {
|
||||
log_verbose(`resolved fully qualified '${importee}'`);
|
||||
return importee;
|
||||
}
|
||||
|
||||
var resolved;
|
||||
if (normalizedImportee.startsWith('./') || normalizedImportee.startsWith('../')) {
|
||||
// relative import
|
||||
if (importer) {
|
||||
let importerRootRelative = path.dirname(importer);
|
||||
const relative = path.relative(path.join(baseDir, root), importerRootRelative);
|
||||
if (!relative.startsWith('.')) {
|
||||
importerRootRelative = relative;
|
||||
}
|
||||
resolved = path.join(importerRootRelative, importee);
|
||||
} else {
|
||||
throw new Error('cannot resolve relative paths without an importer');
|
||||
}
|
||||
if (resolved) resolved = resolveInRootDir(resolved);
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
// possible workspace import or external import if importee matches a module
|
||||
// mapping
|
||||
for (const k in moduleMappings) {
|
||||
if (normalizedImportee == k || normalizedImportee.startsWith(k + '/')) {
|
||||
// replace the root module name on a mappings match
|
||||
// note that the module_root attribute is intended to be used for type-checking
|
||||
// so it uses eg. "index.d.ts". At runtime, we have only index.js, so we strip the
|
||||
// .d.ts suffix and let node require.resolve do its thing.
|
||||
var v = moduleMappings[k].replace(/\.d\.ts$/, '');
|
||||
const mappedImportee = path.join(v, normalizedImportee.slice(k.length + 1));
|
||||
log_verbose(`module mapped '${importee}' to '${mappedImportee}'`);
|
||||
resolved = resolveInRootDir(mappedImportee);
|
||||
if (resolved) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
// workspace import
|
||||
const userWorkspacePath = path.relative(workspaceName, importee);
|
||||
resolved = resolveInRootDir(userWorkspacePath.startsWith('..') ? importee : userWorkspacePath);
|
||||
}
|
||||
|
||||
if (resolved) {
|
||||
log_verbose(`resolved to ${resolved}`);
|
||||
} else {
|
||||
log_verbose(`allowing rollup to resolve '${importee}' with node module resolution`);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// We make mainFields match what was the default for plugin-node-resolve <4.2.0
|
||||
// when mainFields was introduced. Resolving to 'browser' or 'es2015' first breaks
|
||||
// some of the benchmarks such as `//modules/benchmarks/src/expanding_rows:perf_chromium-local`.
|
||||
// See https://app.circleci.com/jobs/github/angular/angular/507444 &&
|
||||
// https://app.circleci.com/jobs/github/angular/angular/507442 for affected tests.
|
||||
const mainFields = ['module', 'main'];
|
||||
const ngccMainFields = mainFields.map(f => `${f}_ivy_ngcc`);
|
||||
|
||||
let plugins = [
|
||||
{
|
||||
name: 'resolveBazel',
|
||||
resolveId: resolveBazel,
|
||||
},
|
||||
nodeResolve({
|
||||
// If Ivy is enabled, we need to make sure that the module resolution prioritizes ngcc
|
||||
// processed entry-point fields. Ngcc adds special fields to `package.json` files of
|
||||
// modules that have been processed. Prioritizing these fields matches the Angular CLIs
|
||||
// behavior for supporting Ivy. We need to support ngcc because `ng_rollup_bundle` rule is
|
||||
// shared with other repositories that consume Angular from NPM (w/ ngcc).
|
||||
// https://github.com/angular/angular-cli/blob/1a1ceb609b9a87c4021cce3a6f0fc6d167cd09d2/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L918-L920
|
||||
mainFields: ivyEnabled ? [...ngccMainFields, ...mainFields] : mainFields,
|
||||
jail: process.cwd(),
|
||||
customResolveOptions: {moduleDirectory: nodeModulesRoot}
|
||||
}),
|
||||
commonjs({ignoreGlobal: true}),
|
||||
sourcemaps(),
|
||||
];
|
||||
|
||||
if (useBuildOptimzier) {
|
||||
plugins = [
|
||||
buildOptimizer.default({
|
||||
sideEffectFreeModules: [
|
||||
'.esm5/packages/core/src',
|
||||
'.esm5/packages/common/src',
|
||||
'.esm5/packages/compiler/src',
|
||||
'.esm5/packages/platform-browser/src',
|
||||
]
|
||||
}),
|
||||
].concat(plugins);
|
||||
}
|
||||
|
||||
let banner = '';
|
||||
if (bannerFile) {
|
||||
banner = fs.readFileSync(bannerFile, {encoding: 'utf-8'});
|
||||
if (stampData) {
|
||||
const versionTag = fs.readFileSync(stampData, {encoding: 'utf-8'})
|
||||
.split('\n')
|
||||
.find(s => s.startsWith('BUILD_SCM_VERSION'));
|
||||
// Don't assume BUILD_SCM_VERSION exists
|
||||
if (versionTag) {
|
||||
const version = versionTag.split(' ')[1].trim();
|
||||
banner = banner.replace(/0.0.0-PLACEHOLDER/, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
plugins,
|
||||
external: [TMPL_external],
|
||||
output: {
|
||||
globals: {TMPL_globals},
|
||||
banner,
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
@ -1,12 +1,18 @@
|
||||
{
|
||||
"compress": {
|
||||
"global_defs": {"ngDevMode": false, "ngI18nClosureMode": false, "ngJitMode": false},
|
||||
"keep_fnames": "bazel_no_debug",
|
||||
"passes": 3,
|
||||
"pure_getters": true,
|
||||
"reduce_funcs": "bazel_no_debug",
|
||||
"reduce_vars": "bazel_no_debug",
|
||||
"sequences": "bazel_no_debug"
|
||||
"output": {
|
||||
"ecma": "es2015",
|
||||
"comments": false,
|
||||
"beautify": "bazel_debug"
|
||||
},
|
||||
"compress": {
|
||||
"global_defs": {
|
||||
"ngDevMode": false,
|
||||
"ngI18nClosureMode": false,
|
||||
"ngJitMode": false
|
||||
},
|
||||
"passes": 3,
|
||||
"pure_getters": true
|
||||
},
|
||||
"toplevel": true,
|
||||
"mangle": "bazel_no_debug"
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {assertNoErrors, getConfig, NgDevConfig} from '../utils/config';
|
||||
export interface CommitMessageConfig {
|
||||
maxLineLength: number;
|
||||
minBodyLength: number;
|
||||
minBodyLengthTypeExcludes?: string[];
|
||||
types: string[];
|
||||
scopes: string[];
|
||||
}
|
||||
@ -19,7 +20,7 @@ export interface CommitMessageConfig {
|
||||
export function getCommitMessageConfig() {
|
||||
// List of errors encountered validating the config.
|
||||
const errors: string[] = [];
|
||||
// The unvalidated config object.
|
||||
// The non-validated config object.
|
||||
const config: Partial<NgDevConfig<{commitMessage: CommitMessageConfig}>> = getConfig();
|
||||
|
||||
if (config.commitMessage === undefined) {
|
||||
|
@ -10,19 +10,22 @@
|
||||
import * as validateConfig from './config';
|
||||
import {validateCommitMessage} from './validate';
|
||||
|
||||
type CommitMessageConfig = validateConfig.CommitMessageConfig;
|
||||
|
||||
|
||||
// Constants
|
||||
const config = {
|
||||
'commitMessage': {
|
||||
'maxLineLength': 120,
|
||||
'minBodyLength': 0,
|
||||
'types': [
|
||||
const config: {commitMessage: CommitMessageConfig} = {
|
||||
commitMessage: {
|
||||
maxLineLength: 120,
|
||||
minBodyLength: 0,
|
||||
types: [
|
||||
'feat',
|
||||
'fix',
|
||||
'refactor',
|
||||
'release',
|
||||
'style',
|
||||
],
|
||||
'scopes': [
|
||||
scopes: [
|
||||
'common',
|
||||
'compiler',
|
||||
'core',
|
||||
@ -74,6 +77,15 @@ describe('validate-commit-message.js', () => {
|
||||
config.commitMessage.maxLineLength} characters`);
|
||||
});
|
||||
|
||||
it('should skip max length limit for URLs', () => {
|
||||
const msg = 'fix(compiler): this is just an usual commit message tile\n\n' +
|
||||
'This is a normal commit message body which does not exceed the max length\n' +
|
||||
'limit. For more details see the following super long URL:\n\n' +
|
||||
'https://github.com/angular/components/commit/e2ace018ddfad10608e0e32932c43dcfef4095d7#diff-9879d6db96fd29134fc802214163b95a';
|
||||
|
||||
expect(validateCommitMessage(msg)).toBe(VALID);
|
||||
});
|
||||
|
||||
it('should validate "<type>(<scope>): <subject>" format', () => {
|
||||
const msg = 'not correct format';
|
||||
|
||||
@ -224,5 +236,42 @@ describe('validate-commit-message.js', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('minBodyLength', () => {
|
||||
const minBodyLengthConfig: {commitMessage: CommitMessageConfig} = {
|
||||
commitMessage: {
|
||||
maxLineLength: 120,
|
||||
minBodyLength: 30,
|
||||
minBodyLengthTypeExcludes: ['docs'],
|
||||
types: ['fix', 'docs'],
|
||||
scopes: ['core']
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
(validateConfig.getCommitMessageConfig as jasmine.Spy).and.returnValue(minBodyLengthConfig);
|
||||
});
|
||||
|
||||
it('should fail validation if the body is shorter than `minBodyLength`', () => {
|
||||
expect(validateCommitMessage(
|
||||
'fix(core): something\n\n Explanation of the motivation behind this change'))
|
||||
.toBe(VALID);
|
||||
expect(validateCommitMessage('fix(core): something\n\n too short')).toBe(INVALID);
|
||||
expect(lastError).toContain(
|
||||
'The commit message body does not meet the minimum length of 30 characters');
|
||||
expect(validateCommitMessage('fix(core): something')).toBe(INVALID);
|
||||
expect(lastError).toContain(
|
||||
'The commit message body does not meet the minimum length of 30 characters');
|
||||
});
|
||||
|
||||
it('should pass validation if the body is shorter than `minBodyLength` but the commit type is in the `minBodyLengthTypeExclusions` list',
|
||||
() => {
|
||||
expect(validateCommitMessage('docs: just fixing a typo')).toBe(VALID);
|
||||
expect(validateCommitMessage('docs(core): just fixing a typo')).toBe(VALID);
|
||||
expect(validateCommitMessage(
|
||||
'docs(core): just fixing a typo\n\nThis was just a silly typo.'))
|
||||
.toBe(VALID);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ const REVERT_PREFIX_RE = /^revert:? /i;
|
||||
const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/;
|
||||
const COMMIT_HEADER_RE = /^(.*)/i;
|
||||
const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/;
|
||||
const COMMIT_BODY_URL_LINE_RE = /^https?:\/\/.*$/;
|
||||
|
||||
/** Parse a full commit message into its composite parts. */
|
||||
export function parseCommitMessage(commitMsg: string) {
|
||||
@ -148,16 +149,23 @@ export function validateCommitMessage(
|
||||
// Checking commit body //
|
||||
//////////////////////////
|
||||
|
||||
if (commit.bodyWithoutLinking.trim().length < config.minBodyLength) {
|
||||
if (!config.minBodyLengthTypeExcludes?.includes(commit.type) &&
|
||||
commit.bodyWithoutLinking.trim().length < config.minBodyLength) {
|
||||
printError(`The commit message body does not meet the minimum length of ${
|
||||
config.minBodyLength} characters`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const bodyByLine = commit.body.split('\n');
|
||||
if (bodyByLine.some(line => line.length > config.maxLineLength)) {
|
||||
const lineExceedsMaxLength = bodyByLine.some(line => {
|
||||
// Check if any line exceeds the max line length limit. The limit is ignored for
|
||||
// lines that just contain an URL (as these usually cannot be wrapped or shortened).
|
||||
return line.length > config.maxLineLength && !COMMIT_BODY_URL_LINE_RE.test(line);
|
||||
});
|
||||
|
||||
if (lineExceedsMaxLength) {
|
||||
printError(
|
||||
`The commit messsage body contains lines greater than ${config.maxLineLength} characters`);
|
||||
`The commit message body contains lines greater than ${config.maxLineLength} characters`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
9
dev-infra/index.bzl
Normal file
9
dev-infra/index.bzl
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright Google LLC All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by an MIT-style license that can be
|
||||
# found in the LICENSE file at https://angular.io/license
|
||||
|
||||
# File is currently empty but serves as indicator for `rules_nodejs` and instructs it to
|
||||
# preserve the content output in the NPM install workspace. This allows consumers to use
|
||||
# rules and targets from within Bazel. e.g. by using `@npm//@angular/dev-infra-private/<..>`.
|
||||
# See: https://github.com/bazelbuild/rules_nodejs/commit/4f508b1a0be1f5444e9c13b0439e649449792fef.
|
@ -63,8 +63,8 @@ export async function discoverNewConflictsForPr(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/** The active github branch when the run began. */
|
||||
const originalBranch = git.getCurrentBranch();
|
||||
/** The active github branch or revision before we performed any Git commands. */
|
||||
const previousBranchOrRevision = git.getCurrentBranchOrRevision();
|
||||
/* Progress bar to indicate progress. */
|
||||
const progressBar = new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total}`});
|
||||
/* PRs which were found to be conflicting. */
|
||||
@ -103,7 +103,7 @@ export async function discoverNewConflictsForPr(
|
||||
const result = exec(`git rebase FETCH_HEAD`);
|
||||
if (result.code) {
|
||||
error('The requested PR currently has conflicts');
|
||||
cleanUpGitState(originalBranch);
|
||||
cleanUpGitState(previousBranchOrRevision);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ export async function discoverNewConflictsForPr(
|
||||
info();
|
||||
info(`Result:`);
|
||||
|
||||
cleanUpGitState(originalBranch);
|
||||
cleanUpGitState(previousBranchOrRevision);
|
||||
|
||||
// If no conflicts are found, exit successfully.
|
||||
if (conflicts.length === 0) {
|
||||
@ -147,14 +147,14 @@ export async function discoverNewConflictsForPr(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
/** Reset git back to the provided branch. */
|
||||
export function cleanUpGitState(branch: string) {
|
||||
/** Reset git back to the provided branch or revision. */
|
||||
export function cleanUpGitState(previousBranchOrRevision: string) {
|
||||
// Ensure that any outstanding rebases are aborted.
|
||||
exec(`git rebase --abort`);
|
||||
// Ensure that any changes in the current repo state are cleared.
|
||||
exec(`git reset --hard`);
|
||||
// Checkout the original branch from before the run began.
|
||||
exec(`git checkout ${branch}`);
|
||||
exec(`git checkout ${previousBranchOrRevision}`);
|
||||
// Delete the generated branch.
|
||||
exec(`git branch -D ${tempWorkingBranch}`);
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ export interface MergeConfig {
|
||||
claSignedLabel: string|RegExp;
|
||||
/** Pattern that matches labels which imply a merge ready pull request. */
|
||||
mergeReadyLabel: string|RegExp;
|
||||
/** Label that is applied when special attention from the caretaker is required. */
|
||||
caretakerNoteLabel?: string|RegExp;
|
||||
/** Label which can be applied to fixup commit messages in the merge script. */
|
||||
commitMessageFixupLabel: string|RegExp;
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ export async function mergePullRequest(
|
||||
/**
|
||||
* Handles the merge result by printing console messages, exiting the process
|
||||
* based on the result, or by restarting the merge if force mode has been enabled.
|
||||
* @returns Whether the merge was successful or not.
|
||||
* @returns Whether the merge completed without errors or not.
|
||||
*/
|
||||
async function handleMergeResult(result: MergeResult, disableForceMergePrompt = false) {
|
||||
const {failure, status} = result;
|
||||
@ -98,7 +98,7 @@ export async function mergePullRequest(
|
||||
|
||||
switch (status) {
|
||||
case MergeStatus.SUCCESS:
|
||||
info(green(`Successfully merged the pull request: ${prNumber}`));
|
||||
info(green(`Successfully merged the pull request: #${prNumber}`));
|
||||
return true;
|
||||
case MergeStatus.DIRTY_WORKING_DIR:
|
||||
error(
|
||||
@ -114,6 +114,9 @@ export async function mergePullRequest(
|
||||
error(red('An error related to interacting with Github has been discovered.'));
|
||||
error(failure!.message);
|
||||
return false;
|
||||
case MergeStatus.USER_ABORTED:
|
||||
info(`Merge of pull request has been aborted manually: #${prNumber}`);
|
||||
return true;
|
||||
case MergeStatus.FAILED:
|
||||
error(yellow(`Could not merge the specified pull request.`));
|
||||
error(red(failure!.message));
|
||||
|
15
dev-infra/pr/merge/messages.ts
Normal file
15
dev-infra/pr/merge/messages.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {red} from '../../utils/console';
|
||||
|
||||
import {PullRequest} from './pull-request';
|
||||
|
||||
export function getCaretakerNotePromptMessage(pullRequest: PullRequest): string {
|
||||
return red('Pull request has a caretaker note applied. Please make sure you read it.') +
|
||||
`\nQuick link to PR: ${pullRequest.url}`;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user