Compare commits
83 Commits
10.0.0-rc.
...
10.0.2
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
345940bbc1 | |||
c49507b289 | |||
c730142508 | |||
27aa00b15f | |||
36a00a255b | |||
0e3249c89b | |||
920019ab70 | |||
82c8b44db7 | |||
bb3a307d5a | |||
dcb0ddaf5e | |||
4c1edd52c5 | |||
d512e27979 | |||
0619d82e0b | |||
a4038d5b94 | |||
e3d5e1fab7 | |||
035036308a | |||
0d29259d9b | |||
26b0f3dc96 | |||
5c9306b0fe | |||
3befb0e4b9 | |||
97bb88f10b | |||
6c7467a58b | |||
c579a85c12 | |||
400fdd08fd |
@ -4,6 +4,7 @@ import {MergeConfig} from '../dev-infra/pr/merge/config';
|
||||
const commitMessage = {
|
||||
'maxLength': 120,
|
||||
'minBodyLength': 100,
|
||||
'minBodyLengthExcludes': ['docs'],
|
||||
'types': [
|
||||
'build',
|
||||
'ci',
|
||||
@ -56,8 +57,6 @@ const format = {
|
||||
// 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/**',
|
||||
|
142
.pullapprove.yml
142
.pullapprove.yml
@ -34,41 +34,8 @@
|
||||
####################################################################################
|
||||
# GitHub usernames
|
||||
####################################################################################
|
||||
# aikidave - Dave Shevitz
|
||||
# alan-agius4 - Alan Agius
|
||||
# alxhub - Alex Rickabaugh
|
||||
# AndrewKushnir - Andrew Kushnir
|
||||
# andrewseguin - Andrew Seguin
|
||||
# atscott - Andrew Scott
|
||||
# ayazhafiz - Ayaz Hafiz
|
||||
# clydin - Charles Lyding
|
||||
# crisbeto - Kristiyan Kostadinov
|
||||
# dennispbrown - Denny Brown
|
||||
# devversion - Paul Gschwendtner
|
||||
# dgp1130 - Doug Parker
|
||||
# filipesilva - Filipe Silva
|
||||
# gkalpak - Georgios Kalpakas
|
||||
# gregmagolan - Greg Magolan
|
||||
# IgorMinar - Igor Minar
|
||||
# jbogarthyde - Judy Bogart
|
||||
# jelbourn - Jeremy Elbourn
|
||||
# JiaLiPassion - Jia Li
|
||||
# JoostK - Joost Koehoorn
|
||||
# josephperrott - Joey Perrott
|
||||
# juleskremer - Jules Kremer
|
||||
# kapunahelewong - Kapunahele Wong
|
||||
# kara - Kara Erickson
|
||||
# kyliau - Keen Yee Liau
|
||||
# manughub - Manu Murthy
|
||||
# matsko - Matias Niemela
|
||||
# mgechev - Minko Gechev
|
||||
# mhevery - Miško Hevery
|
||||
# michaelprentice - Michael Prentice
|
||||
# mmalerba - Miles Malerba
|
||||
# petebacondarwin - Pete Bacon Darwin
|
||||
# pkozlowski-opensource - Pawel Kozlowski
|
||||
# robwormald - Rob Wormald
|
||||
# StephenFluin - Stephen Fluin
|
||||
# See reviewer list under `required-minimum-review` group. Team member names and
|
||||
# usernames are managed there.
|
||||
|
||||
|
||||
####################################################################################
|
||||
@ -100,8 +67,16 @@ version: 3
|
||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||
# used throughout the config.
|
||||
meta:
|
||||
1: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
||||
2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
||||
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
||||
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
||||
defaults: &defaults
|
||||
reviews:
|
||||
# Authors provide their approval implicitly, this approval allows for a reviewer
|
||||
# from a group not to need a review specifically for an area of the repository
|
||||
# they own. This is coupled with the `required-minimum-review` group which requires
|
||||
# that all PRs are reviewed by at least one team member who is not the author of
|
||||
# the PR.
|
||||
author_value: 1
|
||||
|
||||
# turn on 'draft' support
|
||||
# https://docs.pullapprove.com/config/github-api-version/
|
||||
@ -121,6 +96,55 @@ pullapprove_conditions:
|
||||
|
||||
|
||||
groups:
|
||||
# =========================================================
|
||||
# Require review on all PRs
|
||||
#
|
||||
# All PRs require at least one review. This rule will not
|
||||
# request any reviewers, however will require that at least
|
||||
# one review is provided before the group is satisfied.
|
||||
# =========================================================
|
||||
required-minimum-review:
|
||||
reviews:
|
||||
request: 0 # Do not request any reviews from the reviewer group
|
||||
required: 1 # Require that all PRs have approval from at least one of the users in the group
|
||||
author_value: 0 # The author of the PR cannot provide an approval for themself
|
||||
reviewers:
|
||||
users:
|
||||
- aikidave # Dave Shevitz
|
||||
- alan-agius4 # Alan Agius
|
||||
- alxhub # Alex Rickabaugh
|
||||
- AndrewKushnir # Andrew Kushnir
|
||||
- andrewseguin # Andrew Seguin
|
||||
- atscott # Andrew Scott
|
||||
- ayazhafiz # Ayaz Hafiz
|
||||
- clydin # Charles Lyding
|
||||
- crisbeto # Kristiyan Kostadinov
|
||||
- dennispbrown # Denny Brown
|
||||
- devversion # Paul Gschwendtner
|
||||
- dgp1130 # Doug Parker
|
||||
- filipesilva # Filipe Silva
|
||||
- gkalpak # Georgios Kalpakas
|
||||
- gregmagolan # Greg Magolan
|
||||
- IgorMinar # Igor Minar
|
||||
- jbogarthyde # Judy Bogart
|
||||
- jelbourn # Jeremy Elbourn
|
||||
- JiaLiPassion # Jia Li
|
||||
- JoostK # Joost Koehoorn
|
||||
- josephperrott # Joey Perrott
|
||||
- juleskremer # Jules Kremer
|
||||
- kapunahelewong # Kapunahele Wong
|
||||
- kara # Kara Erickson
|
||||
- kyliau # Keen Yee Liau
|
||||
- manughub # Manu Murthy
|
||||
- matsko # Matias Niemela
|
||||
- mgechev # Minko Gechev
|
||||
- mhevery # Miško Hevery
|
||||
- michaelprentice # Michael Prentice
|
||||
- mmalerba # Miles Malerba
|
||||
- petebacondarwin # Pete Bacon Darwin
|
||||
- pkozlowski-opensource # Pawel Kozlowski
|
||||
- StephenFluin # Stephen Fluin
|
||||
|
||||
# =========================================================
|
||||
# Global Approvers
|
||||
#
|
||||
@ -161,6 +185,7 @@ groups:
|
||||
# Framework: Animations
|
||||
# =========================================================
|
||||
fw-animations:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -185,6 +210,7 @@ groups:
|
||||
# Framework: Compiler
|
||||
# =========================================================
|
||||
fw-compiler:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -209,6 +235,7 @@ groups:
|
||||
# Framework: Compiler / ngcc
|
||||
# =========================================================
|
||||
fw-ngcc:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -225,6 +252,7 @@ groups:
|
||||
# Framework: Migrations
|
||||
# =========================================================
|
||||
fw-migrations:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -240,6 +268,7 @@ groups:
|
||||
# Framework: Core
|
||||
# =========================================================
|
||||
fw-core:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -359,6 +388,7 @@ groups:
|
||||
# Framework: Http
|
||||
# =========================================================
|
||||
fw-http:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -380,6 +410,7 @@ groups:
|
||||
# Framework: Elements
|
||||
# =========================================================
|
||||
fw-elements:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -400,6 +431,7 @@ groups:
|
||||
# Framework: Forms
|
||||
# =========================================================
|
||||
fw-forms:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -432,6 +464,7 @@ groups:
|
||||
# Framework: i18n
|
||||
# =========================================================
|
||||
fw-i18n:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -465,6 +498,7 @@ groups:
|
||||
# Framework: Platform Server
|
||||
# =========================================================
|
||||
fw-platform-server:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -484,6 +518,7 @@ groups:
|
||||
# Framework: Router
|
||||
# =========================================================
|
||||
fw-router:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -506,6 +541,7 @@ groups:
|
||||
# Framework: Service Worker
|
||||
# =========================================================
|
||||
fw-service-worker:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -533,6 +569,7 @@ groups:
|
||||
# Framework: Upgrade
|
||||
# =========================================================
|
||||
fw-upgrade:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -563,6 +600,7 @@ groups:
|
||||
# Framework: Testing
|
||||
# =========================================================
|
||||
fw-testing:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -584,6 +622,7 @@ groups:
|
||||
# Framework: Benchmarks
|
||||
# =========================================================
|
||||
fw-benchmarks:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -600,6 +639,7 @@ groups:
|
||||
# Framework: Playground
|
||||
# =========================================================
|
||||
fw-playground:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -617,6 +657,7 @@ groups:
|
||||
# Framework: Security
|
||||
# =========================================================
|
||||
fw-security:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -646,6 +687,7 @@ groups:
|
||||
# Bazel
|
||||
# =========================================================
|
||||
bazel:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -664,6 +706,7 @@ groups:
|
||||
# Language Service
|
||||
# =========================================================
|
||||
language-service:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -683,6 +726,7 @@ groups:
|
||||
# zone.js
|
||||
# =========================================================
|
||||
zone-js:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -701,6 +745,7 @@ groups:
|
||||
# Benchpress
|
||||
# =========================================================
|
||||
benchpress:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -718,6 +763,7 @@ groups:
|
||||
# Integration Tests
|
||||
# =========================================================
|
||||
integration-tests:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -735,6 +781,7 @@ groups:
|
||||
# Docs: Gettings Started & Tutorial
|
||||
# =========================================================
|
||||
docs-getting-started-and-tutorial:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -767,6 +814,7 @@ groups:
|
||||
# Docs: Marketing
|
||||
# =========================================================
|
||||
docs-marketing:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -789,6 +837,7 @@ groups:
|
||||
# Docs: Observables
|
||||
# =========================================================
|
||||
docs-observables:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -814,6 +863,7 @@ groups:
|
||||
# Docs: Packaging, Tooling, Releasing
|
||||
# =========================================================
|
||||
docs-packaging-and-releasing:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -833,7 +883,7 @@ groups:
|
||||
'aio/content/guide/migration-localize.md',
|
||||
'aio/content/guide/migration-module-with-providers.md',
|
||||
'aio/content/guide/static-query-migration.md',
|
||||
'aio/content/guide/updating-to-version-9.md',
|
||||
'aio/content/guide/updating-to-version-10.md',
|
||||
'aio/content/guide/ivy-compatibility.md',
|
||||
'aio/content/guide/ivy-compatibility-examples.md'
|
||||
])
|
||||
@ -873,6 +923,7 @@ groups:
|
||||
# Docs: CLI
|
||||
# =========================================================
|
||||
docs-cli:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -889,8 +940,12 @@ groups:
|
||||
'aio/content/images/guide/deployment/**',
|
||||
'aio/content/guide/file-structure.md',
|
||||
'aio/content/guide/ivy.md',
|
||||
'aio/content/guide/strict-mode.md',
|
||||
'aio/content/guide/web-worker.md',
|
||||
'aio/content/guide/workspace-config.md',
|
||||
'aio/content/guide/migration-solution-style-tsconfig.md',
|
||||
'aio/content/guide/migration-update-module-and-target-compiler-options.md',
|
||||
'aio/content/guide/migration-update-libraries-tslib.md',
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
@ -903,6 +958,7 @@ groups:
|
||||
# Docs: CLI Libraries
|
||||
# =========================================================
|
||||
docs-libraries:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -923,6 +979,7 @@ groups:
|
||||
# Docs: Schematics
|
||||
# =========================================================
|
||||
docs-schematics:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -945,6 +1002,7 @@ groups:
|
||||
# Docs-infra
|
||||
# =========================================================
|
||||
docs-infra:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
@ -974,10 +1032,11 @@ groups:
|
||||
# Dev-infra
|
||||
# =========================================================
|
||||
dev-infra:
|
||||
<<: *defaults
|
||||
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/**',
|
||||
@ -1038,6 +1097,7 @@ groups:
|
||||
# Public API
|
||||
# =========================================================
|
||||
public-api:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -1066,6 +1126,7 @@ groups:
|
||||
# Size tracking
|
||||
# ================================================
|
||||
size-tracking:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -1088,6 +1149,7 @@ groups:
|
||||
# Circular dependencies
|
||||
# ================================================
|
||||
circular-dependencies:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -1110,6 +1172,7 @@ groups:
|
||||
# Code Ownership
|
||||
# =========================================================
|
||||
code-ownership:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- *can-be-global-approved
|
||||
- >
|
||||
@ -1125,6 +1188,7 @@ groups:
|
||||
# Catch all for if no groups match the code change
|
||||
# ====================================================
|
||||
fallback:
|
||||
<<: *defaults
|
||||
# A group is considered to be `active` for a PR if at least one of group's
|
||||
# conditions matches the PR.
|
||||
#
|
||||
|
@ -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",
|
||||
|
829
CHANGELOG.md
829
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@ Do not open issues for general support questions as we want to keep GitHub issue
|
||||
Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
- there are thousands of people willing to help on Stack Overflow
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- questions and answers stay available for public viewing so your question/answer might help someone else
|
||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||
@ -57,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
|
||||
|
||||
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 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.
|
||||
|
||||
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.
|
||||
|
||||
@ -70,7 +70,7 @@ 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 up front helps to ensure that we're ready to accept your work.
|
||||
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.
|
||||
@ -85,8 +85,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
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.
|
||||
[commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
@ -181,13 +180,13 @@ Samples: (even more [samples](https://github.com/angular/angular/commits/master)
|
||||
docs(changelog): update changelog to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
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.
|
||||
```
|
||||
|
||||
### 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.
|
||||
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.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
@ -282,7 +281,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
* 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.
|
||||
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>
|
||||
|
||||
|
10
aio/content/examples/i18n/stackblitz.json
Normal file
10
aio/content/examples/i18n/stackblitz.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"description": "i18n",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0-9].*"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["Angular", "i18n", "internationalization"]
|
||||
}
|
@ -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'; }
|
||||
|
@ -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>
|
||||
|
||||
|
@ -490,6 +490,56 @@ If you rely on the behavior that the same object instance should cause change de
|
||||
- Clone the resulting value so that it has a new identity.
|
||||
- Explicitly call [`ChangeDetectorRef.detectChanges()`](api/core/ChangeDetectorRef#detectchanges) to force the update.
|
||||
|
||||
{@a deprecated-cli-flags}
|
||||
## Deprecated CLI APIs and Options
|
||||
|
||||
This section contains a complete list all of the currently deprecated CLI flags.
|
||||
|
||||
### @angular-devkit/build-angular
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
|
||||
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
|
||||
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
|
||||
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
|
||||
| `rebaseRootRelativeCssUrls` | <!--v8--> v11 | Intended only to assist with specific migration issues. |
|
||||
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
|
||||
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
|
||||
| `i18nFormat` | <!--v9--> v11 | Renamed to `format` to simplify the user experience. |
|
||||
| `i18nLocale` | <!--v9--> v11 | Redundant with project’s source locale. |
|
||||
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
|
||||
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
|
||||
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
|
||||
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
|
||||
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
|
||||
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
|
||||
|
||||
### @angular-devkit/core
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||
| `ModuleNotFoundException` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||
| `resolve` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||
| `setResolveHook` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||
| `ResolveOptions` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||
| `terminal` | <!--v8--> v10 | Unused implementation of terminal codes (color). |
|
||||
| `isObservable` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Use `isObservable` function from the `rxjs` package.|
|
||||
|
||||
### @ngtools/webpack
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||
| `discoverLazyRoutes` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||
| `additionalLazyModules` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||
| `additionalLazyModuleResources` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||
|
||||
### @schematics/angular
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||
| `entryComponent` | <!--v9--> TBD | No longer needed with Ivy. |
|
||||
|
||||
{@a removed}
|
||||
## Removed APIs
|
||||
|
||||
|
@ -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).
|
||||
|
@ -78,6 +78,12 @@ Files at the top level of `src/` support testing and running your application. S
|
||||
| `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. |
|
||||
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If you create an application using Angular's strict mode, you will also have an additional `package.json` file in the `src/app` directory. For more information, see [Strict mode](/guide/strict-mode).
|
||||
|
||||
</div>
|
||||
|
||||
{@a app-src}
|
||||
|
||||
Inside the `src/` folder, the `app/` folder contains your project's logic and data.
|
||||
@ -90,6 +96,7 @@ Angular components, templates, and styles go here.
|
||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
||||
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
||||
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
|
||||
| `app/package.json` | This file is generated only in applications created using `--strict` mode. This file is not used by package managers. It is used to tell the tools and bundlers whether the code under this directory is free of non-local [side-effects](guide/strict-mode#side-effect). |
|
||||
|
||||
### Application configuration files
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,18 +2,18 @@
|
||||
|
||||
The Angular team has worked hard to ensure Ivy is as backwards-compatible with the previous rendering engine ("View Engine") as possible.
|
||||
However, in rare cases, minor changes were necessary to ensure that the Angular's behavior was predictable and consistent, correcting issues in the View Engine implementation.
|
||||
In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-9#migrations) wherever possible so your application and library code is migrated automatically by the CLI.
|
||||
In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-10#migrations) wherever possible so your application and library code is migrated automatically by the CLI.
|
||||
That said, some applications will likely need to apply some manual updates.
|
||||
|
||||
{@a debugging}
|
||||
## How to debug errors with Ivy
|
||||
|
||||
In version 9, [a few deprecated APIs have been removed](guide/updating-to-version-9#removals) and there are a [few breaking changes](guide/updating-to-version-9#breaking-changes) unrelated to Ivy.
|
||||
In version 10, [a few deprecated APIs have been removed](guide/updating-to-version-10#removals) and there are a [few breaking changes](guide/updating-to-version-10#breaking-changes) unrelated to Ivy.
|
||||
If you're seeing errors after updating to version 9, you'll first want to rule those changes out.
|
||||
|
||||
To do so, temporarily [turn off Ivy](guide/ivy#opting-out-of-angular-ivy) in your `tsconfig.base.json` and re-start your app.
|
||||
|
||||
If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 9 guide](guide/updating-to-version-9). If you've opted into any of the stricter type-checking settings that are new with v9, you may also want to check out the [template type-checking guide](guide/template-typecheck).
|
||||
If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 10 guide](guide/updating-to-version-10). If you've opted into any of the new, stricter type-checking settings, you may also want to check out the [template type-checking guide](guide/template-typecheck).
|
||||
|
||||
If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.base.json` and review the list of expected changes below.
|
||||
|
||||
|
@ -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">
|
||||
|
54
aio/content/guide/migration-solution-style-tsconfig.md
Normal file
54
aio/content/guide/migration-solution-style-tsconfig.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Solution-style `tsconfig.json` migration
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
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.
|
||||
|
||||
2. Adding the solution `tsconfig.json` file at the root of the workspace.
|
||||
This `tsconfig.json` file will only contain references to project-level TypeScript configuration files and is only used by editors/IDEs.
|
||||
|
||||
As an example, the solution `tsconfig.json` for a new project is as follows:
|
||||
```json
|
||||
// This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
|
||||
// It is not intended to be used to perform a compilation.
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"path": "./e2e/tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
Solution-style `tsconfig.json` files provide an improved editing experience and fix several long-standing defects when editing files in an IDE.
|
||||
IDEs that leverage the TypeScript language service (for example, [Visual Studio Code](https://code.visualstudio.com)), will only use TypeScript configuration files that are named `tsconfig.json`.
|
||||
In complex projects, there may be more than one compilation unit and each of these units may have different settings and options.
|
||||
|
||||
With the Angular CLI, a project will have application code that will target a browser.
|
||||
It will also have unit tests that should not be included within the built application and that also need additional type information present (`jasmine` in this case).
|
||||
Both parts of the project also share some but not all of the code within the project.
|
||||
As a result, two separate TypeScript configuration files (`tsconfig.app.json` and `tsconfig.spec.json`) are needed to ensure that each part of the application is configured properly and that the right types are used for each part.
|
||||
Also if web workers are used within a project, an additional tsconfig (`tsconfig.worker.json`) is needed.
|
||||
Web workers use similar but incompatible types to the main browser application.
|
||||
This requires the additional configuration file to ensure that the web worker files use the appropriate types and will build successfully.
|
||||
|
||||
While the Angular build system knows about all of these TypeScript configuration files, an IDE using TypeScript's language service does not.
|
||||
Because of this, an IDE will not be able to properly analyze the code from each part of the project and may generate false errors or make suggestions that are incorrect for certain files.
|
||||
By leveraging the new solution-style tsconfig, the IDE can now be aware of the configuration of each part of a project.
|
||||
This allows each file to be treated appropriately based on its tsconfig.
|
||||
IDE features such as error/warning reporting and auto-suggestion will operate more effectively as well.
|
||||
|
||||
The TypeScript 3.9 release [blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig) also contains some additional information regarding this new feature.
|
52
aio/content/guide/migration-update-libraries-tslib.md
Normal file
52
aio/content/guide/migration-update-libraries-tslib.md
Normal file
@ -0,0 +1,52 @@
|
||||
# `tslib` direct dependency migration
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
If you have any libraries within your workspace, this migration will convert `tslib` peer dependencies to direct dependencies for the libraries.
|
||||
TypeScript uses the `tslib` package to provide common helper functions used in compiled TypeScript code.
|
||||
The `tslib` version is also updated to `2.0.0` to support TypeScript 3.9.
|
||||
|
||||
Before:
|
||||
```json
|
||||
{
|
||||
"name": "my-lib",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^9.0.0",
|
||||
"@angular/core": "^9.0.0",
|
||||
"tslib": "^1.12.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
```json
|
||||
{
|
||||
"name": "my-lib",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^9.0.0",
|
||||
"@angular/core": "^9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
The [`tslib`](https://github.com/Microsoft/tslib) is a runtime library for Typescript.
|
||||
The version of this library is bound to the version of the TypeScript compiler used to compile a library.
|
||||
Peer dependencies do not accurately represent this relationship between the runtime and the compiler.
|
||||
If `tslib` remained declared as a library peer dependency, it would be possible for some Angular workspaces to get into a state where the workspace could not satisfy `tslib` peer dependency requirements for multiple libraries, resulting in build-time or run-time errors.
|
||||
|
||||
As of TypeScript 3.9 (used by Angular v10), `tslib` version of 2.x is required to build new applications.
|
||||
However, older libraries built with previous version of TypeScript and already published to npm might need `tslib` 1.x.
|
||||
This migration makes it possible for code depending on incompatible versions of the `tslib` runtime library to remain interoperable.
|
||||
|
||||
|
||||
## Do I still need `tslib` as a dependency in my workspace `package.json`?
|
||||
|
||||
Yes.
|
||||
The `tslib` dependency declared in the `package.json` file of the workspace is used to build applications within this workspace, as well as run unit tests for workspace libraries, and is required.
|
@ -0,0 +1,33 @@
|
||||
# Update `module` and `target` compiler options migration
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
This migration adjusts the [`target`](https://www.typescriptlang.org/v2/en/tsconfig#target) and [`module`](https://www.typescriptlang.org/v2/en/tsconfig#module) settings within the [TypeScript configuration files](guide/typescript-configuration) for the workspace.
|
||||
The changes to each option vary based on the builder or command that uses the TypeScript configuration file.
|
||||
Unless otherwise noted, changes are only made if the existing value was not changed since the project was created.
|
||||
This process helps ensure that intentional changes to the options are kept in place.
|
||||
|
||||
TypeScript Configuration File(s) | Changed Property | Existing Value | New Value
|
||||
------------- | ------------- | ------------- | ------------- | -------------
|
||||
`<workspace base>/tsconfig.base.json` | `"module"` | `"esnext"` | `"es2020"`
|
||||
Used in `browser` builder options (`ng build` for applications) | `"module"` | `"esnext"` | `"es2020"`
|
||||
Used in `ng-packgr` builder options (`ng build` for libraries) | `"module"` | `"esnext"` | `"es2020"`
|
||||
Used in `karma` builder options (`ng test` for applications) | `"module"` | `"esnext"` | `"es2020"`
|
||||
Used in `server` builder options (universal) | `"module"` | `"commonjs"` | _removed_
|
||||
Used in `server` builder options (universal) | `"target"` | _any_ | `"es2016"`
|
||||
Used in `protractor` builder options (`ng e2e` for applications) | `"target"` | `"es5"` | `"es2018"`
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
This migration provides improvements to the long-term supportability of projects by updating the projects to use recommended best practice compilation options.
|
||||
|
||||
For the functionality that executes on Node.js, such as Universal and Protractor, the new settings provide performance and troubleshooting benefits as well.
|
||||
The minimum Node.js version for the Angular CLI (v10.13) supports features in ES2018 and earlier.
|
||||
By targeting later ES versions, the compiler transforms less code and can use newer features directly.
|
||||
Since zone.js does not support native `async` and `await`, the universal builds still target ES2016.
|
||||
|
||||
## Why `"es2020"` instead of `"esnext"`?
|
||||
|
||||
In TypeScript 3.9, the behavior of the TypeScript compiler controlled by `module` is the same with both `"esnext"` and `"es2020"` values.
|
||||
This behavior can change in the future, because the `"esnext"` option could evolve in a backwards incompatible ways, resulting in build-time or run-time errors during a TypeScript update.
|
||||
As a result, code can become unstable. Using the `"es2020"` option mitigates this risk.
|
@ -118,7 +118,6 @@ Package name | Description
|
||||
[**@angular‑devkit/<br />build‑angular**](https://github.com/angular/angular-cli/) | The Angular build tools.
|
||||
[**@angular/cli**](https://github.com/angular/angular-cli/) | The Angular CLI tools.
|
||||
**@angular/<br />compiler‑cli** | The Angular compiler, which is invoked by the Angular CLI's `ng build` and `ng serve` commands.
|
||||
**@angular/<br />language‑service** | The [Angular language service](guide/language-service) analyzes component templates and provides type and error information that TypeScript-aware editors can use to improve the developer's experience. For example, see the [Angular language service extension for VS Code](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template).
|
||||
**@types/... ** | TypeScript definition files for 3rd party libraries such as Jasmine and Node.js.
|
||||
[**codelyzer**](https://www.npmjs.com/package/codelyzer) | A linter for Angular apps whose rules conform to the Angular [style guide](guide/styleguide).
|
||||
**jasmine/... ** | Packages to support the [Jasmine](https://jasmine.github.io/) test library.
|
||||
@ -135,3 +134,4 @@ Package name | Description
|
||||
|
||||
* [Building and serving](guide/build) describes how packages come together to create a development build.
|
||||
* [Deployment](guide/deployment) describes how packages come together to create a production build.
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -25,49 +25,48 @@ To use the Angular framework, you should be familiar with the following:
|
||||
|
||||
Knowledge of [TypeScript](https://www.typescriptlang.org/) is helpful, but not required.
|
||||
|
||||
To install Angular on your local system, you need the following:
|
||||
|
||||
{@a nodejs}
|
||||
### Node.js
|
||||
|
||||
Make sure your development environment includes `Node.js®` and an npm package manager.
|
||||
* **Node.js**
|
||||
|
||||
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases) version of Node.js.
|
||||
|
||||
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases/) version of `Node.js`. See the `engines` key for the specific version requirements in our [package.json](https://unpkg.com/@angular/cli/package.json).
|
||||
<div class="alert is-helpful">
|
||||
|
||||
* To check your version, run `node -v` in a terminal/console window.
|
||||
For information about specific version requirements, see the `engines` key in the [package.json](https://unpkg.com/@angular/cli/package.json) file.
|
||||
|
||||
* To get `Node.js`, go to [nodejs.org](https://nodejs.org "Nodejs.org").
|
||||
</div>
|
||||
|
||||
For more information on installing Node.js, see [nodejs.org](http://nodejs.org "Nodejs.org").
|
||||
If you are unsure what version of Node.js runs on your system, run `node -v` in a terminal window.
|
||||
|
||||
{@a npm}
|
||||
### npm package manager
|
||||
|
||||
Angular, the Angular CLI, and Angular apps depend on features and functionality provided by libraries that are available as [npm packages](https://docs.npmjs.com/getting-started/what-is-npm). To download and install npm packages, you must have an npm package manager.
|
||||
* **npm package manager**
|
||||
|
||||
This setup guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default.
|
||||
|
||||
To check that you have the npm client installed, run `npm -v` in a terminal/console window.
|
||||
Angular, the Angular CLI, and Angular applications depend on [npm packages](https://docs.npmjs.com/getting-started/what-is-npm) for many features and functions.
|
||||
To download and install npm packages, you need an npm package manager.
|
||||
This guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default.
|
||||
To check that you have the npm client installed, run `npm -v` in a terminal window.
|
||||
|
||||
|
||||
{@a install-cli}
|
||||
|
||||
## Step 1: Install the Angular CLI
|
||||
## Install the Angular CLI
|
||||
|
||||
You use the Angular CLI
|
||||
to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||
|
||||
Install the Angular CLI globally.
|
||||
|
||||
To install the CLI using `npm`, open a terminal/console window and enter the following command:
|
||||
You use the Angular CLI to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||
|
||||
To install the Angular CLI, open a terminal window and run the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm install -g @angular/cli
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a create-proj}
|
||||
|
||||
## Step 2: Create a workspace and initial application
|
||||
## Create a workspace and initial application
|
||||
|
||||
You develop apps in the context of an Angular [**workspace**](guide/glossary#workspace).
|
||||
|
||||
@ -86,16 +85,22 @@ The Angular CLI installs the necessary Angular npm packages and other dependenci
|
||||
|
||||
The CLI creates a new workspace and a simple Welcome app, ready to run.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You also have the option to use Angular's strict mode, which can help you write better, more maintainable code.
|
||||
For more information, see [Strict mode](/guide/strict-mode).
|
||||
|
||||
</div>
|
||||
|
||||
{@a serve}
|
||||
|
||||
## Step 3: Run the application
|
||||
## Run the application
|
||||
|
||||
The Angular CLI includes a server, so that you can easily build and serve your app locally.
|
||||
The Angular CLI includes a server, so that you can build and serve your app locally.
|
||||
|
||||
1. Go to the workspace folder (`my-app`).
|
||||
1. Navigate to the workspace folder, such as `my-app`.
|
||||
|
||||
1. Launch the server by using the CLI command `ng serve`, with the `--open` option.
|
||||
1. Run the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
cd my-app
|
||||
@ -108,7 +113,7 @@ and rebuilds the app as you make changes to those files.
|
||||
The `--open` (or just `-o`) option automatically opens your browser
|
||||
to `http://localhost:4200/`.
|
||||
|
||||
You will see:
|
||||
If your installation and setup was successful, you should see a page similar to the following.
|
||||
|
||||
|
||||
<div class="lightbox">
|
||||
|
46
aio/content/guide/strict-mode.md
Normal file
46
aio/content/guide/strict-mode.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Strict mode
|
||||
|
||||
When you create a new workspace or a project you have an option to create them in a strict mode using the `--strict` flag.
|
||||
|
||||
Enabling this flag initializes your new workspace or project with a few new settings that improve maintainability, help you catch bugs ahead of time, and allow the CLI to perform advanced optimizations on your application.
|
||||
Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular.
|
||||
|
||||
Specifically, the `strict` flag does the following:
|
||||
|
||||
* Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`.
|
||||
* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates) and [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters)
|
||||
* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%
|
||||
* Turns on [`no-any` tslint rule](https://palantir.github.io/tslint/rules/no-any/) to prevent declarations of type `any`
|
||||
* [Marks your application as side-effect free](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) to enable more advanced tree-shaking
|
||||
|
||||
You can apply these settings at the workspace and project level.
|
||||
|
||||
To create a new workspace and application using the strict mode, run the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
|
||||
ng new [project-name] --strict
|
||||
|
||||
</code-example>
|
||||
|
||||
To create a new application in the strict mode within an existing non-strict workspace, run the following command:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
|
||||
ng generate application [project-name] --strict
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a side-effect}
|
||||
|
||||
### Non-local side effects in applications
|
||||
|
||||
When you create projects and workspaces using the `strict` mode, you'll notice an additional `package.json` file, located in `src/app/` directory.
|
||||
This file informs tools and bundlers that the code under this directory is free of non-local side effects. Non-local side effects in the application code are not common and using them is not considered a good coding pattern.
|
||||
More importantly, code with these types of side effects cannot be optimized, resulting in increased bundle sizes and applications that load more slowly.
|
||||
|
||||
If you need more information, the following links may be helpful.
|
||||
|
||||
* [Tree-shaking](https://webpack.js.org/guides/tree-shaking/)
|
||||
* [Dealing with side effects and pure functions in JavaScript](https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg)
|
||||
* [How to deal with dirty side effects in your pure function JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/)
|
@ -79,6 +79,10 @@ The initial `tsconfig.base.json` for an Angular workspace typically looks like t
|
||||
}
|
||||
</code-example>
|
||||
|
||||
### Strict mode
|
||||
|
||||
When you create new workspaces and projects, you have the option to use Angular's strict mode, which can help you write better, more maintainable code.
|
||||
For more information, see [Strict mode](/guide/strict-mode).
|
||||
|
||||
{@a noImplicitAny}
|
||||
|
||||
|
82
aio/content/guide/updating-to-version-10.md
Normal file
82
aio/content/guide/updating-to-version-10.md
Normal file
@ -0,0 +1,82 @@
|
||||
# Updating to Angular version 10
|
||||
|
||||
This guide contains information related to updating to version 10 of Angular.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For information on upgrading to Angular version 9, see [Updating to Angular version 9](https://v9.angular.io/guide/updating-to-version-9).
|
||||
|
||||
</div>
|
||||
|
||||
## Updating CLI Apps
|
||||
|
||||
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
|
||||
|
||||
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
|
||||
|
||||
## Changes and Deprecations in Version 10
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
|
||||
|
||||
</div>
|
||||
|
||||
{@a breaking-changes}
|
||||
### New Breaking Changes
|
||||
|
||||
* Typescript 3.6, 3.7, and 3.8 are no longer supported. Please update to Typescript 3.9.
|
||||
* Input fields of type `number` fire the `valueChanges` event only once per value change (as opposed to twice in some cases). See [PR 36087](https://github.com/angular/angular/pull/36087).
|
||||
* 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).
|
||||
* 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).
|
||||
|
||||
{@a deprecations}
|
||||
### New Deprecations
|
||||
|
||||
| Area | API or Feature | May be removed in |
|
||||
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
||||
| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | <!--v10--> v12 |
|
||||
| browser support | [`IE 9, 10, and IE Mobile`](guide/deprecations#ie-9-10-and-ie-mobile-support) | <!--v10--> v11 |
|
||||
|
||||
|
||||
{@a removals}
|
||||
### New Removals of Deprecated APIs
|
||||
|
||||
The following APIs have been removed starting with version 10.0.0*:
|
||||
|
||||
| Package | API | Replacement | Notes |
|
||||
| ---------------- | -------------- | ----------- | ----- |
|
||||
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
||||
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
||||
| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](/guide/deprecations#style-sanitization) for more info
|
||||
| `@angular/bazel` | [`Bazel builder and schematics`](guide/deprecations#bazelbuilder) | `bazelbuild/rules_nodejs` | [More info](https://github.com/angular/angular/tree/10.0.x/packages/bazel/src/schematics) |
|
||||
|
||||
|
||||
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
||||
|
||||
{@a ivy}
|
||||
|
||||
## Ivy features and compatibility
|
||||
|
||||
Since version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
|
||||
|
||||
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
|
||||
|
||||
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
|
||||
|
||||
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
|
||||
|
||||
{@a migrations}
|
||||
## Automated Migrations for Version 10
|
||||
|
||||
Read about the migrations the CLI handles for you automatically:
|
||||
|
||||
* [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
|
||||
* [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)
|
||||
* [Solution-style `tsconfig.json` migration](guide/migration-solution-style-tsconfig)
|
||||
* [`tslib` direct dependency migration](guide/migration-update-libraries-tslib)
|
||||
* [Update `module` and `target` compiler options migration](guide/migration-update-module-and-target-compiler-options)
|
@ -1,90 +0,0 @@
|
||||
# Updating to Angular version 9
|
||||
|
||||
This guide contains information related to updating to version 9 of Angular.
|
||||
|
||||
## Updating CLI Apps
|
||||
|
||||
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
|
||||
|
||||
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
|
||||
|
||||
## Changes and Deprecations in Version 9
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
|
||||
|
||||
</div>
|
||||
|
||||
{@a breaking-changes}
|
||||
### New Breaking Changes
|
||||
|
||||
- Angular now compiles with Ivy by default. See the [Ivy compatibility section](#ivy).
|
||||
|
||||
- CLI apps compile in [AOT mode](/guide/aot-compiler) by default (which includes template type-checking).
|
||||
Users who only built with JIT before may see new type errors.
|
||||
See our [template type-checking guide](guide/template-typecheck) for more information and debugging tips.
|
||||
|
||||
- Typescript 3.4 and 3.5 are no longer supported. Please update to Typescript 3.7.
|
||||
|
||||
- `tslib` is now listed as a peer dependency rather than a direct dependency. If you are not using the CLI, you must manually install `tslib`, using `yarn add tslib` or `npm install tslib --save`.
|
||||
|
||||
{@a deprecations}
|
||||
### New Deprecations
|
||||
|
||||
| API | Replacement | Notes |
|
||||
| ------------------------------------------------------------------------| ------------------------------------ | ----- |
|
||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | See [`entryComponents`](guide/deprecations#entryComponents) |
|
||||
| [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation)| `{provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}` | From v11 the default code will be extracted from the locale data given by `LOCAL_ID`, rather than `USD`. |
|
||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](guide/deprecations#entryComponents) |
|
||||
| `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | |
|
||||
| Undecorated base classes that use Angular features | Base classes with `@Directive()` decorator that use Angular features | |
|
||||
| `esm5` and `fesm5` distribution in `@angular/*` npm packages | `esm2015` and `fesm2015` entrypoints | See [`esm5` and `fesm5`](guide/deprecations#esm5-fesm5) |
|
||||
| [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | Same behavior, but type safe. |
|
||||
|
||||
|
||||
{@a removals}
|
||||
### New Removals of Deprecated APIs
|
||||
|
||||
| Package | API | Replacement | Notes |
|
||||
| ------- | -------------- | ----------- | ----- |
|
||||
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](api/core/Renderer2) | [Migration guide.](guide/migration-renderer) |
|
||||
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](api/core/RendererFactory2) | none |
|
||||
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](api/core/RendererType2) | none |
|
||||
| `@angular/core` | [`WtfScopeFn`](https://v8.angular.io/api/core/WtfScopeFn) | none | v8 | See [Web Tracing Framework](#wtf) |
|
||||
| `@angular/core` | [`wtfCreateScope`](https://v8.angular.io/api/core/wtfCreateScope) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
||||
| `@angular/core` | [`wtfStartTimeRange`](https://v8.angular.io/api/core/wtfStartTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
||||
| `@angular/core` | [`wtfEndTimeRange`](https://v8.angular.io/api/core/wtfEndTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
||||
| `@angular/core` | [`wtfLeave`](https://v8.angular.io/api/core/wtfLeave) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
||||
| `@angular/common` | `DeprecatedI18NPipesModule` | [`CommonModule`](api/common/CommonModule#pipes) | none |
|
||||
| `@angular/common` | `DeprecatedCurrencyPipe` | [`CurrencyPipe`](api/common/CurrencyPipe) | none |
|
||||
| `@angular/common` | `DeprecatedDatePipe` | [`DatePipe`](api/common/DatePipe) | none |
|
||||
| `@angular/common` | `DeprecatedDecimalPipe` | [`DecimalPipe`](api/common/DecimalPipe) | none |
|
||||
| `@angular/common` | `DeprecatedPercentPipe` | [`PercentPipe`](api/common/PercentPipe) | none |
|
||||
| `@angular/forms` | [`NgFormSelectorWarning`](https://v8.angular.io/api/forms/NgFormSelectorWarning) | none |
|
||||
| `@angular/forms` | `ngForm` element selector | `ng-form` element selector | none |
|
||||
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
|
||||
|
||||
{@a ivy}
|
||||
|
||||
## Ivy features and compatibility
|
||||
|
||||
In Version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
|
||||
|
||||
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
|
||||
|
||||
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
|
||||
|
||||
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
|
||||
|
||||
{@a migrations}
|
||||
## Automated Migrations for Version 9
|
||||
|
||||
Read about the migrations the CLI handles for you automatically:
|
||||
|
||||
- [Migrating from `Renderer` to `Renderer2`](guide/migration-renderer)
|
||||
- [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
|
||||
- [Migrating missing `@Injectable()` decorators and incomplete provider definitions](guide/migration-injectable)
|
||||
- [Migrating dynamic queries](guide/migration-dynamic-flag)
|
||||
- [Migrating to the new `$localize` i18n support](guide/migration-localize)
|
||||
- [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)
|
@ -41,6 +41,11 @@ When you create a library project with `ng generate library`, the library projec
|
||||
|
||||
</div>
|
||||
|
||||
## Strict mode
|
||||
|
||||
When you create new workspaces and projects, you have the option to use Angular's strict mode, which can help you write better, more maintainable code.
|
||||
For more information, see [Strict mode](/guide/strict-mode).
|
||||
|
||||
## Project configuration options
|
||||
|
||||
The following top-level configuration properties are available for each project, under `projects:<project_name>`.
|
||||
|
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 |
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 |
@ -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",
|
||||
@ -815,5 +822,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."
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,6 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
||||
<td>Oslo, Norway</td>
|
||||
<td>May 25-26 conference, 27 workshops, 2020</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -31,6 +26,12 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ng-vikings 2020 -->
|
||||
<tr>
|
||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
||||
<td>Oslo, Norway</td>
|
||||
<td>May 25-26 conference, 27 workshops, 2020</td>
|
||||
</tr>
|
||||
<!-- ng-conf 2020 -->
|
||||
<tr>
|
||||
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
||||
|
@ -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."
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -675,6 +601,11 @@
|
||||
"url": "guide/browser-support",
|
||||
"title": "Browser Support",
|
||||
"tooltip": "Browser support and polyfills guide."
|
||||
},
|
||||
{
|
||||
"url": "guide/strict-mode",
|
||||
"title": "Strict mode",
|
||||
"tooltip": "Reference documentation for Angular's strict mode."
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -762,13 +693,13 @@
|
||||
"tooltip": "Angular versioning, release, support, and deprecation policies and practices."
|
||||
},
|
||||
{
|
||||
"title": "Updating to Version 9",
|
||||
"tooltip": "Support for updating your application from version 8 to 9.",
|
||||
"title": "Updating to Version 10",
|
||||
"tooltip": "Support for updating your application from version 9 to 10.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/updating-to-version-9",
|
||||
"url": "guide/updating-to-version-10",
|
||||
"title": "Overview",
|
||||
"tooltip": "Everything you need to know for updating your application from version 8 to 9."
|
||||
"tooltip": "Everything you need to know for updating your application from version 9 to 10."
|
||||
},
|
||||
{
|
||||
"url": "guide/ivy-compatibility",
|
||||
@ -776,29 +707,9 @@
|
||||
"tooltip": "Details to help you make sure your application is compatible with Ivy."
|
||||
},
|
||||
{
|
||||
"title": "Optional Migrations",
|
||||
"tooltip": "Optional migration details regarding updating to version 9.",
|
||||
"title": "Migrations",
|
||||
"tooltip": "Migration details regarding updating to version 10.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/migration-renderer",
|
||||
"title": "Renderer to Renderer2",
|
||||
"tooltip": "Migration from the deprecated Renderer API to the newer Renderer2 API."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-dynamic-flag",
|
||||
"title": "Dynamic Queries Flag",
|
||||
"tooltip": "Migration to remove unnecessary `static: false` flag from @ViewChild and @ContentChild queries."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-injectable",
|
||||
"title": "Missing @Injectable() Decorators",
|
||||
"tooltip": "Migration to add missing @Injectable() decorators and incomplete provider definitions."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-localize",
|
||||
"title": "$localize Global Import",
|
||||
"tooltip": "Migration to add an import statement for @angular/localize to polyfills.ts."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-module-with-providers",
|
||||
"title": "Missing ModuleWithProviders Generic",
|
||||
@ -808,6 +719,26 @@
|
||||
"url": "guide/migration-undecorated-classes",
|
||||
"title": "Missing @Directive() Decorators",
|
||||
"tooltip": "Migration to add missing @Directive()/@Component() decorators."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-injectable",
|
||||
"title": "Missing @Injectable() Decorators",
|
||||
"tooltip": "Migration to add missing @Injectable() decorators and incomplete provider definitions."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-solution-style-tsconfig",
|
||||
"title": "Solution-style `tsconfig.json`",
|
||||
"tooltip": "Migration to create a solution-style `tsconfig.json`."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-update-libraries-tslib",
|
||||
"title": "`tslib` direct dependency",
|
||||
"tooltip": "Migration to a direct dependency on the `tslib` npm package."
|
||||
},
|
||||
{
|
||||
"url": "guide/migration-update-module-and-target-compiler-options",
|
||||
"title": "`module` and `target` compiler options",
|
||||
"tooltip": "Migration to update `module` and `target` compiler options."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1003,6 +934,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": [
|
||||
{
|
||||
|
@ -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=$(
|
||||
|
@ -401,7 +401,10 @@ describe('DocViewerComponent', () => {
|
||||
expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]);
|
||||
});
|
||||
|
||||
it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => {
|
||||
// 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', () => {
|
||||
const obs = new ObservableWithSubscriptionSpies();
|
||||
loadElementsSpy.and.returnValue(obs);
|
||||
|
||||
@ -436,7 +439,10 @@ describe('DocViewerComponent', () => {
|
||||
expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy);
|
||||
});
|
||||
|
||||
it('should unsubscribe from the previous "swap" observable when unsubscribed from', () => {
|
||||
// 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', () => {
|
||||
const obs = new ObservableWithSubscriptionSpies();
|
||||
swapViewsSpy.and.returnValue(obs);
|
||||
|
||||
|
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();
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,7 @@ const DEFAULT_CLI_EXAMPLE_PORT = 4200;
|
||||
const DEFAULT_CLI_SPECS_CONCURRENCY = 1;
|
||||
const IGNORED_EXAMPLES = [];
|
||||
|
||||
const fixmeIvyExamples = [
|
||||
// fixmeIvy('unknown') app fails at runtime due to missing external service (goog is undefined)
|
||||
'i18n',
|
||||
];
|
||||
const fixmeIvyExamples = [];
|
||||
|
||||
if (!argv.viewengine) {
|
||||
IGNORED_EXAMPLES.push(...fixmeIvyExamples);
|
||||
@ -72,8 +69,10 @@ function runE2e() {
|
||||
const outputFile = path.join(AIO_PATH, './protractor-results.txt');
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => findAndRunE2eTests(argv.filter, outputFile, argv.shard,
|
||||
argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY, argv.retry || 1))
|
||||
.then(
|
||||
() => findAndRunE2eTests(
|
||||
argv.filter, outputFile, argv.shard,
|
||||
argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY, argv.retry || 1))
|
||||
.then((status) => {
|
||||
reportStatus(status, outputFile);
|
||||
if (status.failed.length > 0) {
|
||||
@ -226,8 +225,12 @@ function runProtractorSystemJS(prepPromise, appDir, appRunSpawnInfo, outputFile)
|
||||
});
|
||||
})
|
||||
.then(
|
||||
function() { return finish(appRunSpawnInfo.proc.pid, true); },
|
||||
function() { return finish(appRunSpawnInfo.proc.pid, false); });
|
||||
function() {
|
||||
return finish(appRunSpawnInfo.proc.pid, true);
|
||||
},
|
||||
function() {
|
||||
return finish(appRunSpawnInfo.proc.pid, false);
|
||||
});
|
||||
}
|
||||
|
||||
function finish(spawnProcId, ok) {
|
||||
@ -263,15 +266,15 @@ function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) {
|
||||
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
|
||||
const config = loadExampleConfig(appDir);
|
||||
const testCommands = config.tests || [{
|
||||
cmd: 'yarn',
|
||||
args: [
|
||||
'e2e',
|
||||
'--prod',
|
||||
'--protractor-config=e2e/protractor-puppeteer.conf.js',
|
||||
'--no-webdriver-update',
|
||||
'--port={PORT}',
|
||||
],
|
||||
}];
|
||||
cmd: 'yarn',
|
||||
args: [
|
||||
'e2e',
|
||||
'--prod',
|
||||
'--protractor-config=e2e/protractor-puppeteer.conf.js',
|
||||
'--no-webdriver-update',
|
||||
'--port={PORT}',
|
||||
],
|
||||
}];
|
||||
let bufferedOutput = `\n\n============== AIO example output for: ${appDir}\n\n`;
|
||||
|
||||
const e2eSpawnPromise = testCommands.reduce((prevSpawnPromise, {cmd, args}) => {
|
||||
@ -281,26 +284,30 @@ function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) {
|
||||
args = args.map(a => a.replace('{PORT}', port || DEFAULT_CLI_EXAMPLE_PORT));
|
||||
|
||||
return prevSpawnPromise.then(() => {
|
||||
const currSpawn = spawnExt(cmd, args, {cwd: appDir}, false,
|
||||
bufferOutput ? msg => bufferedOutput += msg : undefined);
|
||||
const currSpawn = spawnExt(
|
||||
cmd, args, {cwd: appDir}, false, bufferOutput ? msg => bufferedOutput += msg : undefined);
|
||||
return currSpawn.promise.then(
|
||||
() => Promise.resolve(finish(currSpawn.proc.pid, true)),
|
||||
() => Promise.reject(finish(currSpawn.proc.pid, false)));
|
||||
});
|
||||
}, Promise.resolve());
|
||||
|
||||
return e2eSpawnPromise.then(() => {
|
||||
fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`);
|
||||
return true;
|
||||
}, () => {
|
||||
fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`);
|
||||
return false;
|
||||
}).then(passed => {
|
||||
if (bufferOutput) {
|
||||
process.stdout.write(bufferedOutput);
|
||||
}
|
||||
return passed;
|
||||
});
|
||||
return e2eSpawnPromise
|
||||
.then(
|
||||
() => {
|
||||
fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`);
|
||||
return true;
|
||||
},
|
||||
() => {
|
||||
fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`);
|
||||
return false;
|
||||
})
|
||||
.then(passed => {
|
||||
if (bufferOutput) {
|
||||
process.stdout.write(bufferedOutput);
|
||||
}
|
||||
return passed;
|
||||
});
|
||||
}
|
||||
|
||||
// Report final status.
|
||||
@ -309,23 +316,31 @@ function reportStatus(status, outputFile) {
|
||||
|
||||
log.push('Suites ignored due to legacy guides:');
|
||||
IGNORED_EXAMPLES.filter(example => !fixmeIvyExamples.find(ex => ex.startsWith(example)))
|
||||
.forEach(function(val) { log.push(' ' + val); });
|
||||
.forEach(function(val) {
|
||||
log.push(' ' + val);
|
||||
});
|
||||
|
||||
if (!argv.viewengine) {
|
||||
log.push('');
|
||||
log.push('Suites ignored due to breakage with Ivy:');
|
||||
fixmeIvyExamples.forEach(function(val) { log.push(' ' + val); });
|
||||
fixmeIvyExamples.forEach(function(val) {
|
||||
log.push(' ' + val);
|
||||
});
|
||||
}
|
||||
|
||||
log.push('');
|
||||
log.push('Suites passed:');
|
||||
status.passed.forEach(function(val) { log.push(' ' + val); });
|
||||
status.passed.forEach(function(val) {
|
||||
log.push(' ' + val);
|
||||
});
|
||||
|
||||
if (status.failed.length == 0) {
|
||||
log.push('All tests passed');
|
||||
} else {
|
||||
log.push('Suites failed:');
|
||||
status.failed.forEach(function(val) { log.push(' ' + val); });
|
||||
status.failed.forEach(function(val) {
|
||||
log.push(' ' + val);
|
||||
});
|
||||
}
|
||||
log.push('\nElapsed time: ' + status.elapsedTime + ' seconds');
|
||||
log = log.join('\n');
|
||||
@ -334,8 +349,8 @@ function reportStatus(status, outputFile) {
|
||||
}
|
||||
|
||||
// Returns both a promise and the spawned process so that it can be killed if needed.
|
||||
function spawnExt(command, args, options, ignoreClose = false,
|
||||
printMessage = msg => process.stdout.write(msg)) {
|
||||
function spawnExt(
|
||||
command, args, options, ignoreClose = false, printMessage = msg => process.stdout.write(msg)) {
|
||||
let proc;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
let descr = command + ' ' + args.join(' ');
|
||||
@ -370,13 +385,19 @@ function getE2eSpecs(basePath, filter) {
|
||||
let specs = {};
|
||||
|
||||
return getE2eSpecsFor(basePath, SJS_SPEC_FILENAME, filter)
|
||||
.then(sjsPaths => { specs.systemjs = sjsPaths; })
|
||||
.then(sjsPaths => {
|
||||
specs.systemjs = sjsPaths;
|
||||
})
|
||||
.then(() => {
|
||||
return getE2eSpecsFor(basePath, CLI_SPEC_FILENAME, filter).then(cliPaths => {
|
||||
return cliPaths.map(p => { return p.replace(`${CLI_SPEC_FILENAME}`, ''); });
|
||||
return cliPaths.map(p => {
|
||||
return p.replace(`${CLI_SPEC_FILENAME}`, '');
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(cliPaths => { specs.cli = cliPaths; })
|
||||
.then(cliPaths => {
|
||||
specs.cli = cliPaths;
|
||||
})
|
||||
.then(() => specs);
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,18 @@
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"i18n": {
|
||||
"sourceLocale": "en-US",
|
||||
"locales": {
|
||||
"fr": "src/locale/messages.fr.xlf"
|
||||
}
|
||||
},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"localize": true,
|
||||
"aot": true,
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
@ -57,35 +65,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"production-fr": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"outputPath": "dist/my-project-fr/",
|
||||
"i18nFile": "src/locale/messages.fr.xlf",
|
||||
"i18nFormat": "xlf",
|
||||
"i18nLocale": "fr",
|
||||
"i18nMissingTranslation": "error"
|
||||
},
|
||||
"fr": {
|
||||
"aot": true,
|
||||
"outputPath": "dist/my-project-fr/",
|
||||
"i18nFile": "src/locale/messages.fr.xlf",
|
||||
"i18nFormat": "xlf",
|
||||
"i18nLocale": "fr",
|
||||
"i18nMissingTranslation": "error"
|
||||
"localize": [
|
||||
"fr"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,6 +20,7 @@
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/localize": "^9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
|
68
aio/tools/examples/shared/boilerplate/i18n/polyfills.ts
Normal file
68
aio/tools/examples/shared/boilerplate/i18n/polyfills.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/***************************************************************************************************
|
||||
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
|
||||
*/
|
||||
import '@angular/localize/init';
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch
|
||||
* requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch
|
||||
* specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
@ -26,6 +26,7 @@
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/elements": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/localize": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/platform-server": "~9.1.4",
|
||||
|
@ -215,6 +215,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.4.tgz#2fa2c444e5a5a6036d5ca43d2887826df17d0553"
|
||||
integrity sha512-eyVxxiegdb4ESdFGfkuDN+YfUbOVHRQLjIl6ACFJQDNHzVXzbmuqpyr5hIJANIVady103/7+dqRxxJo1DdIdTQ==
|
||||
|
||||
"@angular/localize@~9.1.4":
|
||||
version "9.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.11.tgz#25921d794836fb7a07d284c1ac0ed06c10e77d50"
|
||||
integrity sha512-CrR7RniwJIK3+QKH8nHl35KDAHZn1mp1QAd5vujTWKw6YRLfio7SjM9qIfzw5y4WZuUitTsqKlQT/m/NK146Ag==
|
||||
dependencies:
|
||||
"@babel/core" "7.8.3"
|
||||
glob "7.1.2"
|
||||
yargs "15.3.0"
|
||||
|
||||
"@angular/platform-browser-dynamic@~9.1.4":
|
||||
version "9.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.4.tgz#bf1cde9156bd29eeeef932b683b0c993614f75d5"
|
||||
@ -254,6 +263,13 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.0.0"
|
||||
|
||||
"@babel/code-frame@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff"
|
||||
integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.1"
|
||||
|
||||
"@babel/code-frame@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
|
||||
@ -277,6 +293,27 @@
|
||||
invariant "^2.2.4"
|
||||
semver "^5.5.0"
|
||||
|
||||
"@babel/core@7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941"
|
||||
integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
"@babel/generator" "^7.8.3"
|
||||
"@babel/helpers" "^7.8.3"
|
||||
"@babel/parser" "^7.8.3"
|
||||
"@babel/template" "^7.8.3"
|
||||
"@babel/traverse" "^7.8.3"
|
||||
"@babel/types" "^7.8.3"
|
||||
convert-source-map "^1.7.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.1"
|
||||
json5 "^2.1.0"
|
||||
lodash "^4.17.13"
|
||||
resolve "^1.3.2"
|
||||
semver "^5.4.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/core@7.9.0":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
|
||||
@ -330,6 +367,16 @@
|
||||
lodash "^4.17.13"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.10.1", "@babel/generator@^7.8.3":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9"
|
||||
integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.10.2"
|
||||
jsesc "^2.5.1"
|
||||
lodash "^4.17.13"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.4.0", "@babel/generator@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369"
|
||||
@ -420,6 +467,15 @@
|
||||
"@babel/traverse" "^7.8.3"
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-function-name@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4"
|
||||
integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==
|
||||
dependencies:
|
||||
"@babel/helper-get-function-arity" "^7.10.1"
|
||||
"@babel/template" "^7.10.1"
|
||||
"@babel/types" "^7.10.1"
|
||||
|
||||
"@babel/helper-function-name@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e"
|
||||
@ -447,6 +503,13 @@
|
||||
"@babel/template" "^7.8.3"
|
||||
"@babel/types" "^7.9.5"
|
||||
|
||||
"@babel/helper-get-function-arity@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d"
|
||||
integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.10.1"
|
||||
|
||||
"@babel/helper-get-function-arity@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0"
|
||||
@ -558,6 +621,13 @@
|
||||
"@babel/template" "^7.8.3"
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
|
||||
integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.10.1"
|
||||
|
||||
"@babel/helper-split-export-declaration@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8"
|
||||
@ -572,6 +642,11 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-validator-identifier@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5"
|
||||
integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.9.5":
|
||||
version "7.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
|
||||
@ -587,6 +662,15 @@
|
||||
"@babel/traverse" "^7.8.3"
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helpers@^7.8.3":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973"
|
||||
integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==
|
||||
dependencies:
|
||||
"@babel/template" "^7.10.1"
|
||||
"@babel/traverse" "^7.10.1"
|
||||
"@babel/types" "^7.10.1"
|
||||
|
||||
"@babel/helpers@^7.8.4":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
|
||||
@ -613,6 +697,15 @@
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0"
|
||||
integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.10.1"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.8.3":
|
||||
version "7.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
|
||||
@ -622,6 +715,11 @@
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.10.1":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0"
|
||||
integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==
|
||||
|
||||
"@babel/parser@^7.4.3", "@babel/parser@^7.7.4":
|
||||
version "7.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71"
|
||||
@ -1126,6 +1224,15 @@
|
||||
"@babel/parser" "^7.8.6"
|
||||
"@babel/types" "^7.8.6"
|
||||
|
||||
"@babel/template@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
|
||||
integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.1"
|
||||
"@babel/parser" "^7.10.1"
|
||||
"@babel/types" "^7.10.1"
|
||||
|
||||
"@babel/template@^7.4.0", "@babel/template@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
|
||||
@ -1144,6 +1251,21 @@
|
||||
"@babel/parser" "^7.8.3"
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/traverse@^7.10.1":
|
||||
version "7.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27"
|
||||
integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.1"
|
||||
"@babel/generator" "^7.10.1"
|
||||
"@babel/helper-function-name" "^7.10.1"
|
||||
"@babel/helper-split-export-declaration" "^7.10.1"
|
||||
"@babel/parser" "^7.10.1"
|
||||
"@babel/types" "^7.10.1"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558"
|
||||
@ -1189,6 +1311,15 @@
|
||||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/types@^7.10.1", "@babel/types@^7.10.2":
|
||||
version "7.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d"
|
||||
integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.10.1"
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.4.0", "@babel/types@^7.7.4":
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193"
|
||||
@ -5265,10 +5396,10 @@ glob-parent@~5.1.0:
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@7.1.6, glob@^7.1.4:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
@ -5277,9 +5408,10 @@ glob@7.1.6, glob@^7.1.4:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
glob@7.1.6, glob@^7.1.4:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
|
@ -4467,10 +4467,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"
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
@ -224,5 +227,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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -148,7 +148,8 @@ 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;
|
||||
@ -157,7 +158,7 @@ export function validateCommitMessage(
|
||||
const bodyByLine = commit.body.split('\n');
|
||||
if (bodyByLine.some(line => line.length > config.maxLineLength)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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}`);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ export class AutosquashMergeStrategy extends MergeStrategy {
|
||||
// is desired, we set the `GIT_SEQUENCE_EDITOR` environment variable to `true` so that
|
||||
// the rebase seems interactive to Git, while it's not interactive to the user.
|
||||
// See: https://github.com/git/git/commit/891d4a0313edc03f7e2ecb96edec5d30dc182294.
|
||||
const branchBeforeRebase = this.git.getCurrentBranch();
|
||||
const branchOrRevisionBeforeRebase = this.git.getCurrentBranchOrRevision();
|
||||
const rebaseEnv =
|
||||
needsCommitMessageFixup ? undefined : {...process.env, GIT_SEQUENCE_EDITOR: 'true'};
|
||||
this.git.run(
|
||||
@ -69,9 +69,9 @@ export class AutosquashMergeStrategy extends MergeStrategy {
|
||||
// Update pull requests commits to reference the pull request. This matches what
|
||||
// Github does when pull requests are merged through the Web UI. The motivation is
|
||||
// that it should be easy to determine which pull request contained a given commit.
|
||||
// **Note**: The filter-branch command relies on the working tree, so we want to make
|
||||
// sure that we are on the initial branch where the merge script has been run.
|
||||
this.git.run(['checkout', '-f', branchBeforeRebase]);
|
||||
// Note: The filter-branch command relies on the working tree, so we want to make sure
|
||||
// that we are on the initial branch or revision where the merge script has been invoked.
|
||||
this.git.run(['checkout', '-f', branchOrRevisionBeforeRebase]);
|
||||
this.git.run(
|
||||
['filter-branch', '-f', '--msg-filter', `${MSG_FILTER_SCRIPT} ${prNumber}`, revisionRange]);
|
||||
|
||||
|
@ -76,14 +76,14 @@ export class PullRequestMergeTask {
|
||||
new GithubApiMergeStrategy(this.git, this.config.githubApiMerge) :
|
||||
new AutosquashMergeStrategy(this.git);
|
||||
|
||||
// Branch that is currently checked out so that we can switch back to it once
|
||||
// the pull request has been merged.
|
||||
let previousBranch: null|string = null;
|
||||
// Branch or revision that is currently checked out so that we can switch back to
|
||||
// it once the pull request has been merged.
|
||||
let previousBranchOrRevision: null|string = null;
|
||||
|
||||
// The following block runs Git commands as child processes. These Git commands can fail.
|
||||
// We want to capture these command errors and return an appropriate merge request status.
|
||||
try {
|
||||
previousBranch = this.git.getCurrentBranch();
|
||||
previousBranchOrRevision = this.git.getCurrentBranchOrRevision();
|
||||
|
||||
// Run preparations for the merge (e.g. fetching branches).
|
||||
await strategy.prepare(pullRequest);
|
||||
@ -96,7 +96,7 @@ export class PullRequestMergeTask {
|
||||
|
||||
// Switch back to the previous branch. We need to do this before deleting the temporary
|
||||
// branches because we cannot delete branches which are currently checked out.
|
||||
this.git.run(['checkout', '-f', previousBranch]);
|
||||
this.git.run(['checkout', '-f', previousBranchOrRevision]);
|
||||
|
||||
await strategy.cleanup(pullRequest);
|
||||
|
||||
@ -112,8 +112,8 @@ export class PullRequestMergeTask {
|
||||
} finally {
|
||||
// Always try to restore the branch if possible. We don't want to leave
|
||||
// the repository in a different state than before.
|
||||
if (previousBranch !== null) {
|
||||
this.git.runGraceful(['checkout', '-f', previousBranch]);
|
||||
if (previousBranchOrRevision !== null) {
|
||||
this.git.runGraceful(['checkout', '-f', previousBranchOrRevision]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +50,10 @@ export async function rebasePr(
|
||||
}
|
||||
|
||||
/**
|
||||
* The branch originally checked out before this method performs any Git
|
||||
* operations that may change the working branch.
|
||||
* The branch or revision originally checked out before this method performed
|
||||
* any Git operations that may change the working branch.
|
||||
*/
|
||||
const originalBranch = git.getCurrentBranch();
|
||||
const previousBranchOrRevision = git.getCurrentBranchOrRevision();
|
||||
/* Get the PR information from Github. */
|
||||
const pr = await getPr(PR_SCHEMA, prNumber, config.github);
|
||||
|
||||
@ -121,7 +121,7 @@ export async function rebasePr(
|
||||
info();
|
||||
info(`To abort the rebase and return to the state of the repository before this command`);
|
||||
info(`run the following command:`);
|
||||
info(` $ git rebase --abort && git reset --hard && git checkout ${originalBranch}`);
|
||||
info(` $ git rebase --abort && git reset --hard && git checkout ${previousBranchOrRevision}`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
info(`Cleaning up git state, and restoring previous state.`);
|
||||
@ -137,7 +137,7 @@ export async function rebasePr(
|
||||
// Ensure that any changes in the current repo state are cleared.
|
||||
git.runGraceful(['reset', '--hard'], {stdio: 'ignore'});
|
||||
// Checkout the original branch from before the run began.
|
||||
git.runGraceful(['checkout', originalBranch], {stdio: 'ignore'});
|
||||
git.runGraceful(['checkout', previousBranchOrRevision], {stdio: 'ignore'});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,5 +31,5 @@ export interface PullApproveConfig {
|
||||
}
|
||||
|
||||
export function parsePullApproveYaml(rawYaml: string): PullApproveConfig {
|
||||
return parseYaml(rawYaml) as PullApproveConfig;
|
||||
return parseYaml(rawYaml, {merge: true}) as PullApproveConfig;
|
||||
}
|
||||
|
@ -130,9 +130,16 @@ export class GitClient {
|
||||
return this.run(['branch', branchName, '--contains', sha]).stdout !== '';
|
||||
}
|
||||
|
||||
/** Gets the currently checked out branch. */
|
||||
getCurrentBranch(): string {
|
||||
return this.run(['rev-parse', '--abbrev-ref', 'HEAD']).stdout.trim();
|
||||
/** Gets the currently checked out branch or revision. */
|
||||
getCurrentBranchOrRevision(): string {
|
||||
const branchName = this.run(['rev-parse', '--abbrev-ref', 'HEAD']).stdout.trim();
|
||||
// If no branch name could be resolved. i.e. `HEAD` has been returned, then Git
|
||||
// is currently in a detached state. In those cases, we just want to return the
|
||||
// currently checked out revision/SHA.
|
||||
if (branchName === 'HEAD') {
|
||||
return this.run(['rev-parse', 'HEAD']).stdout.trim();
|
||||
}
|
||||
return branchName;
|
||||
}
|
||||
|
||||
/** Gets whether the current Git repository has uncommitted changes. */
|
||||
|
@ -76,7 +76,7 @@ node ./scripts/build/build-packages-dist.js
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
Bazel is used as the primary tool for building and testing Angular. Building and testing is
|
||||
Bazel is used as the primary tool for building and testing Angular. Building and testing are
|
||||
incremental with Bazel, and it's possible to only run tests for an individual package instead
|
||||
of for all packages. Read more about this in the [BAZEL.md](./BAZEL.md) document.
|
||||
|
||||
@ -190,7 +190,7 @@ a. Any transitive dependencies of the copied packages will not be automatically
|
||||
b. The packages need to be copied over every time `npm/yarn install` is run.
|
||||
c. Some package managers (such as `pnpm` or `yarn pnp`) might not work correctly.
|
||||
|
||||
### Publishing to GitHub repos
|
||||
### Publishing to GitHub Repos
|
||||
You can also manually publish `*-builds` snapshots just like our CircleCI build does for upstream
|
||||
builds. Before being able to publish the packages, you need to build them locally by running the
|
||||
`./scripts/build/build-packages-dist.js` script.
|
||||
|
@ -53,40 +53,45 @@ If you modify any part of a public API in one of the supported public packages,
|
||||
The public API guard provides a Bazel target that updates the current status of a given package. If you add to or modify the public API in any way, you must use [yarn](https://yarnpkg.com/) to execute the Bazel target in your terminal shell of choice (a recent version of `bash` is recommended).
|
||||
|
||||
```shell
|
||||
yarn bazel run //tools/public_api_guard:<modified_package>_api.accept
|
||||
yarn bazel run //packages/<modified_package>:<modified_package>_api.accept
|
||||
```
|
||||
|
||||
Using yarn ensures that you are running the correct version of Bazel.
|
||||
(Read more about building Angular with Bazel [here](./BAZEL.md).)
|
||||
|
||||
Here is an example of a Circle CI test failure that resulted from adding a new allowed type to a public property in `forms.d.ts`. Error messages from the API guard use [`git-diff` formatting](https://git-scm.com/docs/git-diff#_combined_diff_format).
|
||||
Here is an example of a Circle CI test failure that resulted from adding a new allowed type to a public property in `core.d.ts`. Error messages from the API guard use [`git-diff` formatting](https://git-scm.com/docs/git-diff#_combined_diff_format).
|
||||
|
||||
```
|
||||
FAIL: //tools/public_api_guard:forms_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test_attempts/attempt_1.log)
|
||||
FAIL: //tools/public_api_guard:forms_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test.log)
|
||||
FAIL: //packages/core:core_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test_attempts/attempt_1.log)
|
||||
INFO: From Action packages/compiler-cli/ngcc/test/fesm5_angular_core.js:
|
||||
[BABEL] Note: The code generator has deoptimised the styling of /b/f/w/bazel-out/k8-fastbuild/bin/packages/core/npm_package/fesm2015/core.js as it exceeds the max of 500KB.
|
||||
FAIL: //packages/core:core_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test.log)
|
||||
|
||||
FAILED: //packages/core:core_api (Summary)
|
||||
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test.log
|
||||
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test_attempts/attempt_1.log
|
||||
INFO: From Testing //packages/core:core_api:
|
||||
==================== Test output for //packages/core:core_api:
|
||||
/b/f/w/bazel-out/k8-fastbuild/bin/packages/core/core_api.sh.runfiles/angular/packages/core/npm_package/core.d.ts(7,1): error: No export declaration found for symbol "ComponentFactory"
|
||||
--- goldens/public-api/core/core.d.ts Golden file
|
||||
+++ goldens/public-api/core/core.d.ts Generated API
|
||||
@@ -563,9 +563,9 @@
|
||||
ngModule: Type<T>;
|
||||
providers?: Provider[];
|
||||
}
|
||||
|
||||
-export declare type NgIterable<T> = Array<T> | Iterable<T>;
|
||||
+export declare type NgIterable<T> = Iterable<T>;
|
||||
|
||||
export declare interface NgModule {
|
||||
bootstrap?: Array<Type<any> | any[]>;
|
||||
declarations?: Array<Type<any> | any[]>;
|
||||
|
||||
FAILED: //tools/public_api_guard:forms_api (Summary)
|
||||
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test.log
|
||||
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test_attempts/attempt_1.log
|
||||
INFO: From Testing //tools/public_api_guard:forms_api:
|
||||
==================== Test output for //tools/public_api_guard:forms_api:
|
||||
--- tools/public_api_guard/forms/forms.d.ts Golden file
|
||||
+++ tools/public_api_guard/forms/forms.d.ts Generated API
|
||||
@@ -4,9 +4,9 @@
|
||||
readonly disabled: boolean;
|
||||
readonly enabled: boolean;
|
||||
readonly errors: ValidationErrors | null;
|
||||
readonly invalid: boolean;
|
||||
- readonly parent: FormGroup | FormArray;
|
||||
+ readonly parent: FormGroup | FormArray | undefined;
|
||||
readonly pending: boolean;
|
||||
readonly pristine: boolean;
|
||||
readonly root: AbstractControl;
|
||||
readonly status: string;
|
||||
|
||||
If you modify a public API, you must accept the new golden file.
|
||||
|
||||
|
||||
To do so, execute the following Bazel target:
|
||||
yarn bazel run //tools/public_api_guard:forms_api.accept
|
||||
yarn bazel run //packages/core:core_api.accept
|
||||
|
||||
```
|
||||
|
2
goldens/public-api/router/router.d.ts
vendored
2
goldens/public-api/router/router.d.ts
vendored
@ -398,7 +398,7 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont
|
||||
routerLinkActiveOptions: {
|
||||
exact: boolean;
|
||||
};
|
||||
constructor(router: Router, element: ElementRef, renderer: Renderer2, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined);
|
||||
constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined);
|
||||
ngAfterContentInit(): void;
|
||||
ngOnChanges(changes: SimpleChanges): void;
|
||||
ngOnDestroy(): void;
|
||||
|
@ -12,7 +12,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2987,
|
||||
"main-es2015": 451406,
|
||||
"main-es2015": 450883,
|
||||
"polyfills-es2015": 52630
|
||||
}
|
||||
}
|
||||
@ -21,8 +21,8 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3097,
|
||||
"main-es2015": 428886,
|
||||
"polyfills-es2015": 52195
|
||||
"main-es2015": 428031,
|
||||
"polyfills-es2015": 52261
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 1485,
|
||||
"main-es2015": 136302,
|
||||
"polyfills-es2015": 37246
|
||||
"main-es2015": 135533,
|
||||
"polyfills-es2015": 37248
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -39,7 +39,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2289,
|
||||
"main-es2015": 246085,
|
||||
"main-es2015": 245488,
|
||||
"polyfills-es2015": 36938,
|
||||
"5-es2015": 751
|
||||
}
|
||||
@ -62,7 +62,7 @@
|
||||
"bundle": "TODO(i): we should define ngDevMode to false in Closure, but --define only works in the global scope.",
|
||||
"bundle": "TODO(i): (FW-2164) TS 3.9 new class shape seems to have broken Closure in big ways. The size went from 169991 to 252338",
|
||||
"bundle": "TODO(i): after removal of tsickle from ngc-wrapped / ng_package, we had to switch to SIMPLE optimizations which increased the size from 252338 to 1198917, see PR#37221 and PR#37317 for more info",
|
||||
"bundle": 1209688
|
||||
"bundle": 1209651
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import {
|
||||
ElementRef,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Injectable,
|
||||
Input,
|
||||
NgModule
|
||||
NgModule,
|
||||
Pipe
|
||||
} from '@angular/core';
|
||||
|
||||
export class NonAngularBaseClass {
|
||||
@ -76,3 +78,17 @@ export class UndecoratedPipeBase {
|
||||
export class WithDirectiveLifecycleHook {
|
||||
ngOnInit() {}
|
||||
}
|
||||
|
||||
// This class is already decorated and should not be migrated. i.e. no TODO
|
||||
// or Angular decorator should be added. `@Injectable` is sufficient.
|
||||
@Injectable()
|
||||
export class MyService {
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
|
||||
// This class is already decorated and should not be migrated. i.e. no TODO
|
||||
// or Angular decorator should be added. `@Injectable` is sufficient.
|
||||
@Pipe({name: 'my-pipe'})
|
||||
export class MyPipe {
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import {
|
||||
ElementRef,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Injectable,
|
||||
Input,
|
||||
NgModule
|
||||
NgModule,
|
||||
Pipe
|
||||
} from '@angular/core';
|
||||
|
||||
export class NonAngularBaseClass {
|
||||
@ -87,3 +89,17 @@ export class UndecoratedPipeBase {
|
||||
export class WithDirectiveLifecycleHook {
|
||||
ngOnInit() {}
|
||||
}
|
||||
|
||||
// This class is already decorated and should not be migrated. i.e. no TODO
|
||||
// or Angular decorator should be added. `@Injectable` is sufficient.
|
||||
@Injectable()
|
||||
export class MyService {
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
|
||||
// This class is already decorated and should not be migrated. i.e. no TODO
|
||||
// or Angular decorator should be added. `@Injectable` is sufficient.
|
||||
@Pipe({name: 'my-pipe'})
|
||||
export class MyPipe {
|
||||
ngOnDestroy() {}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ module.exports = function(config) {
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'test-events.js',
|
||||
'shims_for_IE.js',
|
||||
'third_party/shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
|
||||
// Serve polyfills necessary for testing the `elements` package.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "10.0.0-rc.6",
|
||||
"version": "10.0.2",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
|
@ -27,5 +27,8 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular/issues"
|
||||
},
|
||||
"homepage": "https://github.com/angular/angular/tree/master/packages/compiler-cli"
|
||||
"homepage": "https://github.com/angular/angular/tree/master/packages/compiler-cli",
|
||||
"publishConfig": {
|
||||
"registry": "https://wombat-dressing-room.appspot.com"
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +339,9 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
}
|
||||
|
||||
// Finally, abort the in-flight request.
|
||||
xhr.abort();
|
||||
if (xhr.readyState !== xhr.DONE) {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -147,6 +147,17 @@ const XSSI_PREFIX = ')]}\'\n';
|
||||
});
|
||||
factory.mock.mockErrorEvent(new Error('blah'));
|
||||
});
|
||||
it('avoids abort a request when fetch operation is completed', done => {
|
||||
const abort = jasmine.createSpy('abort');
|
||||
|
||||
backend.handle(TEST_POST).toPromise().then(() => {
|
||||
expect(abort).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
factory.mock.abort = abort;
|
||||
factory.mock.mockFlush(200, 'OK', 'Done');
|
||||
});
|
||||
describe('progress events', () => {
|
||||
it('are emitted for download progress', done => {
|
||||
backend.handle(TEST_POST.clone({reportProgress: true}))
|
||||
|
@ -31,6 +31,7 @@ ts_library(
|
||||
"//packages/compiler-cli/src/ngtsc/indexer",
|
||||
"//packages/compiler-cli/src/ngtsc/perf",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"//packages/compiler-cli/src/ngtsc/shims",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck",
|
||||
"@npm//@bazel/typescript",
|
||||
"@npm//@types/node",
|
||||
|
@ -12,7 +12,7 @@ import {ParsedConfiguration} from '../../..';
|
||||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
|
||||
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
|
||||
import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics';
|
||||
import {absoluteFrom, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system';
|
||||
import {absoluteFrom, absoluteFromSourceFile, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system';
|
||||
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
|
||||
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
@ -148,7 +148,8 @@ export class DecorationAnalyzer {
|
||||
*/
|
||||
analyzeProgram(): DecorationAnalyses {
|
||||
for (const sourceFile of this.program.getSourceFiles()) {
|
||||
if (!sourceFile.isDeclarationFile && isWithinPackage(this.packagePath, sourceFile)) {
|
||||
if (!sourceFile.isDeclarationFile &&
|
||||
isWithinPackage(this.packagePath, absoluteFromSourceFile(sourceFile))) {
|
||||
this.compiler.analyzeFile(sourceFile);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {MetadataReader} from '../../../src/ngtsc/metadata';
|
||||
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
|
||||
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
|
||||
@ -44,7 +44,7 @@ export class DefaultMigrationHost implements MigrationHost {
|
||||
}
|
||||
|
||||
isInScope(clazz: ClassDeclaration): boolean {
|
||||
return isWithinPackage(this.entryPointPath, clazz.getSourceFile());
|
||||
return isWithinPackage(this.entryPointPath, absoluteFromSourceFile(clazz.getSourceFile()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
|
||||
import {isWithinPackage} from './util';
|
||||
|
||||
@ -35,7 +35,7 @@ export class SwitchMarkerAnalyzer {
|
||||
analyzeProgram(program: ts.Program): SwitchMarkerAnalyses {
|
||||
const analyzedFiles = new SwitchMarkerAnalyses();
|
||||
program.getSourceFiles()
|
||||
.filter(sourceFile => isWithinPackage(this.packagePath, sourceFile))
|
||||
.filter(sourceFile => isWithinPackage(this.packagePath, absoluteFromSourceFile(sourceFile)))
|
||||
.forEach(sourceFile => {
|
||||
const declarations = this.host.getSwitchableDeclarations(sourceFile);
|
||||
if (declarations.length) {
|
||||
|
@ -5,13 +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 * as ts from 'typescript';
|
||||
|
||||
import {absoluteFromSourceFile, AbsoluteFsPath, relative} from '../../../src/ngtsc/file_system';
|
||||
import {AbsoluteFsPath, relative} from '../../../src/ngtsc/file_system';
|
||||
import {DependencyTracker} from '../../../src/ngtsc/incremental/api';
|
||||
|
||||
export function isWithinPackage(packagePath: AbsoluteFsPath, sourceFile: ts.SourceFile): boolean {
|
||||
const relativePath = relative(packagePath, absoluteFromSourceFile(sourceFile));
|
||||
export function isWithinPackage(packagePath: AbsoluteFsPath, filePath: AbsoluteFsPath): boolean {
|
||||
const relativePath = relative(packagePath, filePath);
|
||||
return !relativePath.startsWith('..') && !relativePath.startsWith('node_modules/');
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,13 @@ export class ClusterExecutor implements Executor {
|
||||
|
||||
async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, _createCompileFn: CreateCompileFn):
|
||||
Promise<void> {
|
||||
return this.lockFile.lock(() => {
|
||||
return this.lockFile.lock(async () => {
|
||||
this.logger.debug(
|
||||
`Running ngcc on ${this.constructor.name} (using ${this.workerCount} worker processes).`);
|
||||
const master = new ClusterMaster(
|
||||
this.workerCount, this.fileSystem, this.logger, this.fileWriter, this.pkgJsonUpdater,
|
||||
analyzeEntryPoints, this.createTaskCompletedCallback);
|
||||
return master.run();
|
||||
return await master.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
|
||||
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference} from '../../../src/ngtsc/reflection';
|
||||
import {isWithinPackage} from '../analysis/util';
|
||||
@ -2525,7 +2526,7 @@ function getRootFileOrFail(bundle: BundleProgram): ts.SourceFile {
|
||||
function getNonRootPackageFiles(bundle: BundleProgram): ts.SourceFile[] {
|
||||
const rootFile = bundle.program.getSourceFile(bundle.path);
|
||||
return bundle.program.getSourceFiles().filter(
|
||||
f => (f !== rootFile) && isWithinPackage(bundle.package, f));
|
||||
f => (f !== rootFile) && isWithinPackage(bundle.package, absoluteFromSourceFile(f)));
|
||||
}
|
||||
|
||||
function isTopLevel(node: ts.Node): boolean {
|
||||
|
@ -37,7 +37,11 @@ export class AsyncLocker {
|
||||
*/
|
||||
async lock<T>(fn: () => Promise<T>): Promise<T> {
|
||||
await this.create();
|
||||
return fn().finally(() => this.lockFile.remove());
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
this.lockFile.remove();
|
||||
}
|
||||
}
|
||||
|
||||
protected async create() {
|
||||
|
@ -50,7 +50,7 @@ export function makeEntryPointBundle(
|
||||
const rootDir = entryPoint.packagePath;
|
||||
const options: ts
|
||||
.CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings};
|
||||
const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.path);
|
||||
const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.packagePath);
|
||||
const dtsHost = new NgtscCompilerHost(fs, options);
|
||||
|
||||
// Create the bundle programs, as necessary.
|
||||
@ -63,7 +63,7 @@ export function makeEntryPointBundle(
|
||||
[];
|
||||
const dts = transformDts ? makeBundleProgram(
|
||||
fs, isCore, entryPoint.packagePath, typingsPath, 'r3_symbols.d.ts',
|
||||
options, dtsHost, additionalDtsFiles) :
|
||||
{...options, allowJs: false}, dtsHost, additionalDtsFiles) :
|
||||
null;
|
||||
const isFlatCore = isCore && src.r3SymbolsFile === null;
|
||||
|
||||
|
@ -7,7 +7,8 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system';
|
||||
import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system';
|
||||
import {isWithinPackage} from '../analysis/util';
|
||||
import {isRelativePath} from '../utils';
|
||||
|
||||
/**
|
||||
@ -20,7 +21,7 @@ export class NgccSourcesCompilerHost extends NgtscCompilerHost {
|
||||
private cache = ts.createModuleResolutionCache(
|
||||
this.getCurrentDirectory(), file => this.getCanonicalFileName(file));
|
||||
|
||||
constructor(fs: FileSystem, options: ts.CompilerOptions, protected entryPointPath: string) {
|
||||
constructor(fs: FileSystem, options: ts.CompilerOptions, protected packagePath: AbsoluteFsPath) {
|
||||
super(fs, options);
|
||||
}
|
||||
|
||||
@ -36,13 +37,24 @@ export class NgccSourcesCompilerHost extends NgtscCompilerHost {
|
||||
// file was in the same directory. This is undesirable, as we need to have the actual
|
||||
// JavaScript being present in the program. This logic recognizes this scenario and rewrites
|
||||
// the resolved .d.ts declaration file to its .js counterpart, if it exists.
|
||||
if (resolvedModule !== undefined && resolvedModule.extension === ts.Extension.Dts &&
|
||||
containingFile.endsWith('.js') && isRelativePath(moduleName)) {
|
||||
if (resolvedModule?.extension === ts.Extension.Dts && containingFile.endsWith('.js') &&
|
||||
isRelativePath(moduleName)) {
|
||||
const jsFile = resolvedModule.resolvedFileName.replace(/\.d\.ts$/, '.js');
|
||||
if (this.fileExists(jsFile)) {
|
||||
return {...resolvedModule, resolvedFileName: jsFile, extension: ts.Extension.Js};
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent loading JavaScript source files outside of the package root, which would happen for
|
||||
// packages that don't have .d.ts files. As ngcc should only operate on the .js files
|
||||
// contained within the package, any files outside the package are simply discarded. This does
|
||||
// result in a partial program with error diagnostics, however ngcc won't gather diagnostics
|
||||
// for the program it creates so these diagnostics won't be reported.
|
||||
if (resolvedModule?.extension === ts.Extension.Js &&
|
||||
!isWithinPackage(this.packagePath, this.fs.resolve(resolvedModule.resolvedFileName))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolvedModule;
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {absoluteFrom} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {isWithinPackage} from '../../src/analysis/util';
|
||||
@ -18,15 +17,13 @@ runInEachFileSystem(() => {
|
||||
|
||||
it('should return true if the source-file is contained in the package', () => {
|
||||
const packagePath = _('/node_modules/test');
|
||||
const file =
|
||||
ts.createSourceFile(_('/node_modules/test/src/index.js'), '', ts.ScriptTarget.ES2015);
|
||||
const file = _('/node_modules/test/src/index.js');
|
||||
expect(isWithinPackage(packagePath, file)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the source-file is not contained in the package', () => {
|
||||
const packagePath = _('/node_modules/test');
|
||||
const file =
|
||||
ts.createSourceFile(_('/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015);
|
||||
const file = _('/node_modules/other/src/index.js');
|
||||
expect(isWithinPackage(packagePath, file)).toBe(false);
|
||||
});
|
||||
|
||||
@ -34,13 +31,11 @@ runInEachFileSystem(() => {
|
||||
const packagePath = _('/node_modules/test');
|
||||
|
||||
// An external file inside the package's `node_modules/`.
|
||||
const file1 = ts.createSourceFile(
|
||||
_('/node_modules/test/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015);
|
||||
const file1 = _('/node_modules/test/node_modules/other/src/index.js');
|
||||
expect(isWithinPackage(packagePath, file1)).toBe(false);
|
||||
|
||||
// An internal file starting with `node_modules`.
|
||||
const file2 = ts.createSourceFile(
|
||||
_('/node_modules/test/node_modules_optimizer.js'), '', ts.ScriptTarget.ES2015);
|
||||
const file2 = _('/node_modules/test/node_modules_optimizer.js');
|
||||
expect(isWithinPackage(packagePath, file2)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -34,7 +34,7 @@ runInEachFileSystem(() => {
|
||||
|
||||
beforeEach(() => {
|
||||
masterRunSpy = spyOn(ClusterMaster.prototype, 'run')
|
||||
.and.returnValue(Promise.resolve('CusterMaster#run()' as any));
|
||||
.and.returnValue(Promise.resolve('ClusterMaster#run()' as any));
|
||||
createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback');
|
||||
|
||||
mockLogger = new MockLogger();
|
||||
@ -63,7 +63,7 @@ runInEachFileSystem(() => {
|
||||
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
|
||||
|
||||
expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy))
|
||||
.toBe('CusterMaster#run()' as any);
|
||||
.toBe('ClusterMaster#run()' as any);
|
||||
|
||||
expect(masterRunSpy).toHaveBeenCalledWith();
|
||||
|
||||
@ -78,6 +78,22 @@ runInEachFileSystem(() => {
|
||||
expect(lockFileLog).toEqual(['write()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should call LockFile.write() and LockFile.remove() if analyzeFn fails', async () => {
|
||||
const analyzeEntryPointsSpy =
|
||||
jasmine.createSpy('analyzeEntryPoints').and.throwError('analyze error');
|
||||
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
|
||||
let error = '';
|
||||
try {
|
||||
await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
expect(analyzeEntryPointsSpy).toHaveBeenCalledWith();
|
||||
expect(createCompilerFnSpy).not.toHaveBeenCalled();
|
||||
expect(error).toEqual('analyze error');
|
||||
expect(lockFileLog).toEqual(['write()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should call LockFile.write() and LockFile.remove() if master runner fails', async () => {
|
||||
const anyFn: () => any = () => undefined;
|
||||
masterRunSpy.and.returnValue(Promise.reject(new Error('master runner error')));
|
||||
|
@ -68,7 +68,7 @@ export function makeTestBundleProgram(
|
||||
const rootDir = fs.dirname(entryPointPath);
|
||||
const options: ts.CompilerOptions =
|
||||
{allowJs: true, maxNodeModuleJsDepth: Infinity, checkJs: false, rootDir, rootDirs: [rootDir]};
|
||||
const host = new NgccSourcesCompilerHost(fs, options, entryPointPath);
|
||||
const host = new NgccSourcesCompilerHost(fs, options, rootDir);
|
||||
return makeBundleProgram(
|
||||
fs, isCore, rootDir, path, 'r3_symbols.js', options, host, additionalFiles);
|
||||
}
|
||||
|
@ -401,6 +401,121 @@ runInEachFileSystem(() => {
|
||||
expect(es5Contents).toContain('ɵngcc0.ɵɵtext(0, "a - b - 3 - 4")');
|
||||
});
|
||||
|
||||
it('should not crash when scanning for ModuleWithProviders needs to evaluate code from an external package',
|
||||
() => {
|
||||
// Regression test for https://github.com/angular/angular/issues/37508
|
||||
// During `ModuleWithProviders` analysis, return statements in methods are evaluated using
|
||||
// the partial evaluator to identify whether they correspond with a `ModuleWithProviders`
|
||||
// function. If an arbitrary method has a return statement that calls into an external
|
||||
// module which doesn't have declaration files, ngcc would attempt to reflect on said
|
||||
// module using the reflection host of the entry-point. This would crash in the case where
|
||||
// e.g. the entry-point is UMD and the external module would be CommonJS, as the UMD
|
||||
// reflection host would throw because it is unable to deal with CommonJS.
|
||||
|
||||
// Setup a non-TS package with CommonJS module format
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _(`/node_modules/identity/package.json`),
|
||||
contents: `{"name": "identity", "main": "./index.js"}`,
|
||||
},
|
||||
{
|
||||
name: _(`/node_modules/identity/index.js`),
|
||||
contents: `
|
||||
function identity(x) { return x; };
|
||||
exports.identity = identity;
|
||||
module.exports = identity;
|
||||
`,
|
||||
},
|
||||
]);
|
||||
|
||||
// Setup an Angular entry-point with UMD module format that references an export of the
|
||||
// CommonJS package.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/test-package/package.json'),
|
||||
contents: '{"name": "test-package", "main": "./index.js", "typings": "./index.d.ts"}'
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/index.js'),
|
||||
contents: `
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('identity')) :
|
||||
typeof define === 'function' && define.amd ? define('test', ['exports', 'identity'], factory) :
|
||||
(factory(global.test, global.identity));
|
||||
}(this, (function (exports, identity) { 'use strict';
|
||||
function Foo(x) {
|
||||
// The below statement is analyzed for 'ModuleWithProviders', so is evaluated
|
||||
// by ngcc. The reference into the non-TS CommonJS package used to crash ngcc.
|
||||
return identity.identity(x);
|
||||
}
|
||||
exports.Foo = Foo;
|
||||
})));
|
||||
`
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test-package/index.d.ts'),
|
||||
contents: 'export declare class Foo { static doSomething(x: any): any; }'
|
||||
},
|
||||
{name: _('/node_modules/test-package/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
]);
|
||||
|
||||
expect(() => mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'test-package',
|
||||
propertiesToConsider: ['main'],
|
||||
}))
|
||||
.not.toThrow();
|
||||
});
|
||||
|
||||
it('should not be able to evaluate code in external packages when no .d.ts files are present',
|
||||
() => {
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _(`/node_modules/external/package.json`),
|
||||
contents: `{"name": "external", "main": "./index.js"}`,
|
||||
},
|
||||
{
|
||||
name: _(`/node_modules/external/index.js`),
|
||||
contents: `
|
||||
export const selector = 'my-selector';
|
||||
`,
|
||||
},
|
||||
]);
|
||||
|
||||
compileIntoApf('test-package', {
|
||||
'/index.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {selector} from 'external';
|
||||
|
||||
@Component({
|
||||
selector,
|
||||
template: ''
|
||||
})
|
||||
export class FooComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [FooComponent],
|
||||
})
|
||||
export class FooModule {}
|
||||
`,
|
||||
});
|
||||
|
||||
try {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'test-package',
|
||||
propertiesToConsider: ['esm2015', 'esm5'],
|
||||
});
|
||||
fail('should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
'Failed to compile entry-point test-package (esm2015 as esm2015) due to compilation errors:');
|
||||
expect(e.message).toContain('NG1010');
|
||||
expect(e.message).toContain('selector must be a string');
|
||||
}
|
||||
});
|
||||
|
||||
it('should add ɵfac but not duplicate ɵprov properties on injectables', () => {
|
||||
compileIntoFlatEs2015Package('test-package', {
|
||||
'/index.ts': `
|
||||
|
@ -13,8 +13,12 @@ import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('entry point bundle', () => {
|
||||
let _: typeof absoluteFrom;
|
||||
beforeEach(() => {
|
||||
_ = absoluteFrom;
|
||||
});
|
||||
|
||||
function setupMockFileSystem(): void {
|
||||
const _ = absoluteFrom;
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/test/package.json'),
|
||||
@ -210,6 +214,103 @@ runInEachFileSystem(() => {
|
||||
].map(p => absoluteFrom(p).toString())));
|
||||
});
|
||||
|
||||
it('does not include .js files outside of the package when no .d.ts file is available', () => {
|
||||
// Declare main "test" package with "entry" entry-point that imports all sorts of
|
||||
// internal and external modules.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/test/entry/package.json'),
|
||||
contents: `{"name": "test", "main": "./index.js", "typings": "./index.d.ts"}`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/entry/index.d.ts'),
|
||||
contents: `
|
||||
import 'external-js';
|
||||
import 'external-ts';
|
||||
import 'nested-js';
|
||||
import './local';
|
||||
import '../package';
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/test/entry/index.js'),
|
||||
contents: `
|
||||
import 'external-js';
|
||||
import 'external-ts';
|
||||
import 'nested-js';
|
||||
import './local';
|
||||
import '../package';
|
||||
`,
|
||||
},
|
||||
{name: _('/node_modules/test/entry/local.d.ts'), contents: `export {};`},
|
||||
{name: _('/node_modules/test/entry/local.js'), contents: `export {};`},
|
||||
{name: _('/node_modules/test/package.d.ts'), contents: `export {};`},
|
||||
{name: _('/node_modules/test/package.js'), contents: `export {};`},
|
||||
]);
|
||||
|
||||
// Declare "external-js" package outside of the "test" package without .d.ts files, should
|
||||
// not be included in the program.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/external-js/package.json'),
|
||||
contents: `{"name": "external-js", "main": "./index.js"}`,
|
||||
},
|
||||
{name: _('/node_modules/external-js/index.js'), contents: 'export {};'},
|
||||
]);
|
||||
|
||||
// Same as "external-js" but located in a nested node_modules directory, which should also
|
||||
// not be included in the program.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/test/node_modules/nested-js/package.json'),
|
||||
contents: `{"name": "nested-js", "main": "./index.js"}`,
|
||||
},
|
||||
{name: _('/node_modules/test/node_modules/nested-js/index.js'), contents: 'export {}'},
|
||||
]);
|
||||
|
||||
// Declare "external-ts" which does have .d.ts files, so the .d.ts should be
|
||||
// loaded into the program.
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/external-ts/package.json'),
|
||||
contents: `{"name": "external-ts", "main": "./index.js", "typings": "./index.d.ts"}`,
|
||||
},
|
||||
{name: _('/node_modules/external-ts/index.d.ts'), contents: 'export {};'},
|
||||
{name: _('/node_modules/external-ts/index.js'), contents: 'export {};'},
|
||||
]);
|
||||
|
||||
const fs = getFileSystem();
|
||||
const entryPoint: EntryPoint = {
|
||||
name: 'test/entry',
|
||||
path: absoluteFrom('/node_modules/test/entry'),
|
||||
packageName: 'test',
|
||||
packagePath: absoluteFrom('/node_modules/test'),
|
||||
packageJson: {name: 'test/entry'},
|
||||
typings: absoluteFrom('/node_modules/test/entry/index.d.ts'),
|
||||
compiledByAngular: true,
|
||||
ignoreMissingDependencies: false,
|
||||
generateDeepReexports: false,
|
||||
};
|
||||
const esm5bundle = makeEntryPointBundle(
|
||||
fs, entryPoint, './index.js', false, 'esm5', /* transformDts */ true,
|
||||
/* pathMappings */ undefined, /* mirrorDtsFromSrc */ true);
|
||||
|
||||
expect(esm5bundle.src.program.getSourceFiles().map(sf => _(sf.fileName)))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
_('/node_modules/test/entry/index.js'),
|
||||
_('/node_modules/test/entry/local.js'),
|
||||
_('/node_modules/test/package.js'),
|
||||
_('/node_modules/external-ts/index.d.ts'),
|
||||
]));
|
||||
expect(esm5bundle.dts!.program.getSourceFiles().map(sf => _(sf.fileName)))
|
||||
.toEqual(jasmine.arrayWithExactContents([
|
||||
_('/node_modules/test/entry/index.d.ts'),
|
||||
_('/node_modules/test/entry/local.d.ts'),
|
||||
_('/node_modules/test/package.d.ts'),
|
||||
_('/node_modules/external-ts/index.d.ts'),
|
||||
]));
|
||||
});
|
||||
|
||||
describe(
|
||||
'including equivalently named, internally imported, src files in the typings program',
|
||||
() => {
|
||||
|
@ -116,7 +116,13 @@ export class NgCompiler {
|
||||
|
||||
const moduleResolutionCache = ts.createModuleResolutionCache(
|
||||
this.adapter.getCurrentDirectory(),
|
||||
fileName => this.adapter.getCanonicalFileName(fileName));
|
||||
// Note: this used to be an arrow-function closure. However, JS engines like v8 have some
|
||||
// strange behaviors with retaining the lexical scope of the closure. Even if this function
|
||||
// doesn't retain a reference to `this`, if other closures in the constructor here reference
|
||||
// `this` internally then a closure created here would retain them. This can cause major
|
||||
// memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
|
||||
// way into all kinds of places inside TS internal objects.
|
||||
this.adapter.getCanonicalFileName.bind(this.adapter));
|
||||
this.moduleResolver =
|
||||
new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
|
||||
this.resourceManager = new AdapterResourceLoader(adapter, this.options);
|
||||
|
@ -42,20 +42,22 @@ export class NoopIncrementalBuildStrategy implements IncrementalBuildStrategy {
|
||||
* Tracks an `IncrementalDriver` within the strategy itself.
|
||||
*/
|
||||
export class TrackedIncrementalBuildStrategy implements IncrementalBuildStrategy {
|
||||
private previous: IncrementalDriver|null = null;
|
||||
private next: IncrementalDriver|null = null;
|
||||
private driver: IncrementalDriver|null = null;
|
||||
private isSet: boolean = false;
|
||||
|
||||
getIncrementalDriver(): IncrementalDriver|null {
|
||||
return this.next !== null ? this.next : this.previous;
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
setIncrementalDriver(driver: IncrementalDriver): void {
|
||||
this.next = driver;
|
||||
this.driver = driver;
|
||||
this.isSet = true;
|
||||
}
|
||||
|
||||
toNextBuildStrategy(): TrackedIncrementalBuildStrategy {
|
||||
const strategy = new TrackedIncrementalBuildStrategy();
|
||||
strategy.previous = this.next;
|
||||
// Only reuse a driver that was explicitly set via `setIncrementalDriver`.
|
||||
strategy.driver = this.isSet ? this.driver : null;
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import {NgCompilerOptions} from './core/api';
|
||||
import {TrackedIncrementalBuildStrategy} from './incremental';
|
||||
import {IndexedComponent} from './indexer';
|
||||
import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf';
|
||||
import {retagAllTsFiles, untagAllTsFiles} from './shims';
|
||||
import {ReusedProgramStrategy} from './typecheck';
|
||||
|
||||
|
||||
@ -68,14 +69,26 @@ export class NgtscProgram implements api.Program {
|
||||
}
|
||||
this.closureCompilerEnabled = !!options.annotateForClosureCompiler;
|
||||
|
||||
const reuseProgram = oldProgram && oldProgram.reuseTsProgram;
|
||||
const reuseProgram = oldProgram?.reuseTsProgram;
|
||||
this.host = NgCompilerHost.wrap(delegateHost, rootNames, options, reuseProgram ?? null);
|
||||
|
||||
if (reuseProgram !== undefined) {
|
||||
// Prior to reusing the old program, restore shim tagging for all its `ts.SourceFile`s.
|
||||
// TypeScript checks the `referencedFiles` of `ts.SourceFile`s for changes when evaluating
|
||||
// incremental reuse of data from the old program, so it's important that these match in order
|
||||
// to get the most benefit out of reuse.
|
||||
retagAllTsFiles(reuseProgram);
|
||||
}
|
||||
|
||||
this.tsProgram = ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram);
|
||||
this.reuseTsProgram = this.tsProgram;
|
||||
|
||||
this.host.postProgramCreationCleanup();
|
||||
|
||||
// Shim tagging has served its purpose, and tags can now be removed from all `ts.SourceFile`s in
|
||||
// the program.
|
||||
untagAllTsFiles(this.tsProgram);
|
||||
|
||||
const reusedProgramStrategy = new ReusedProgramStrategy(
|
||||
this.tsProgram, this.host, this.options, this.host.shimExtensionPrefixes);
|
||||
|
||||
@ -93,6 +106,10 @@ export class NgtscProgram implements api.Program {
|
||||
return this.tsProgram;
|
||||
}
|
||||
|
||||
getReuseTsProgram(): ts.Program {
|
||||
return this.reuseTsProgram;
|
||||
}
|
||||
|
||||
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
|
||||
undefined): readonly ts.Diagnostic[] {
|
||||
return this.tsProgram.getOptionsDiagnostics(cancellationToken);
|
||||
@ -248,6 +265,7 @@ export class NgtscProgram implements api.Program {
|
||||
}));
|
||||
this.perfRecorder.stop(fileEmitSpan);
|
||||
}
|
||||
|
||||
this.perfRecorder.stop(emitSpan);
|
||||
|
||||
if (this.perfTracker !== null && this.options.tracePerformance !== undefined) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
/// <reference types="node" />
|
||||
|
||||
export {ShimAdapter} from './src/adapter';
|
||||
export {copyFileShimData, isShim} from './src/expando';
|
||||
export {copyFileShimData, isShim, retagAllTsFiles, retagTsFile, sfExtensionData, untagAllTsFiles, untagTsFile} from './src/expando';
|
||||
export {FactoryGenerator, generatedFactoryTransform} from './src/factory_generator';
|
||||
export {ShimReferenceTagger} from './src/reference_tagger';
|
||||
export {SummaryGenerator} from './src/summary_generator';
|
||||
|
@ -12,7 +12,7 @@ import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '../../file_s
|
||||
import {isDtsPath} from '../../util/src/typescript';
|
||||
import {PerFileShimGenerator, TopLevelShimGenerator} from '../api';
|
||||
|
||||
import {isFileShimSourceFile, isShim, NgExtension, sfExtensionData} from './expando';
|
||||
import {isFileShimSourceFile, isShim, sfExtensionData} from './expando';
|
||||
import {makeShimFileName} from './util';
|
||||
|
||||
interface ShimGeneratorData {
|
||||
|
@ -21,7 +21,16 @@ export const NgExtension = Symbol('NgExtension');
|
||||
export interface NgExtensionData {
|
||||
isTopLevelShim: boolean;
|
||||
fileShim: NgFileShimData|null;
|
||||
|
||||
/**
|
||||
* The contents of the `referencedFiles` array, before modification by a `ShimReferenceTagger`.
|
||||
*/
|
||||
originalReferencedFiles: ReadonlyArray<ts.FileReference>|null;
|
||||
|
||||
/**
|
||||
* The contents of the `referencedFiles` array, after modification by a `ShimReferenceTagger`.
|
||||
*/
|
||||
taggedReferenceFiles: ReadonlyArray<ts.FileReference>|null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,6 +74,7 @@ export function sfExtensionData(sf: ts.SourceFile): NgExtensionData {
|
||||
isTopLevelShim: false,
|
||||
fileShim: null,
|
||||
originalReferencedFiles: null,
|
||||
taggedReferenceFiles: null,
|
||||
};
|
||||
extSf[NgExtension] = extension;
|
||||
return extension;
|
||||
@ -110,3 +120,53 @@ export function copyFileShimData(from: ts.SourceFile, to: ts.SourceFile): void {
|
||||
}
|
||||
sfExtensionData(to).fileShim = sfExtensionData(from).fileShim;
|
||||
}
|
||||
|
||||
/**
|
||||
* For those `ts.SourceFile`s in the `program` which have previously been tagged by a
|
||||
* `ShimReferenceTagger`, restore the original `referencedFiles` array that does not have shim tags.
|
||||
*/
|
||||
export function untagAllTsFiles(program: ts.Program): void {
|
||||
for (const sf of program.getSourceFiles()) {
|
||||
untagTsFile(sf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For those `ts.SourceFile`s in the `program` which have previously been tagged by a
|
||||
* `ShimReferenceTagger`, re-apply the effects of tagging by updating the `referencedFiles` array to
|
||||
* the tagged version produced previously.
|
||||
*/
|
||||
export function retagAllTsFiles(program: ts.Program): void {
|
||||
for (const sf of program.getSourceFiles()) {
|
||||
retagTsFile(sf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original `referencedFiles` for the given `ts.SourceFile`.
|
||||
*/
|
||||
export function untagTsFile(sf: ts.SourceFile): void {
|
||||
if (sf.isDeclarationFile || !isExtended(sf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ext = sfExtensionData(sf);
|
||||
if (ext.originalReferencedFiles !== null) {
|
||||
sf.referencedFiles = ext.originalReferencedFiles as Array<ts.FileReference>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the previously tagged `referencedFiles` to the given `ts.SourceFile`, if it was previously
|
||||
* tagged.
|
||||
*/
|
||||
export function retagTsFile(sf: ts.SourceFile): void {
|
||||
if (sf.isDeclarationFile || !isExtended(sf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ext = sfExtensionData(sf);
|
||||
if (ext.taggedReferenceFiles !== null) {
|
||||
sf.referencedFiles = ext.taggedReferenceFiles as Array<ts.FileReference>;
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {absoluteFrom, absoluteFromSourceFile} from '../../file_system';
|
||||
import {absoluteFromSourceFile} from '../../file_system';
|
||||
import {isNonDeclarationTsPath} from '../../util/src/typescript';
|
||||
|
||||
import {isExtended as isExtendedSf, isShim, NgExtension, sfExtensionData} from './expando';
|
||||
import {isShim, sfExtensionData} from './expando';
|
||||
import {makeShimFileName} from './util';
|
||||
|
||||
/**
|
||||
@ -48,8 +48,16 @@ export class ShimReferenceTagger {
|
||||
return;
|
||||
}
|
||||
|
||||
sfExtensionData(sf).originalReferencedFiles = sf.referencedFiles;
|
||||
const referencedFiles = [...sf.referencedFiles];
|
||||
const ext = sfExtensionData(sf);
|
||||
|
||||
// If this file has never been tagged before, capture its `referencedFiles` in the extension
|
||||
// data.
|
||||
if (ext.originalReferencedFiles === null) {
|
||||
ext.originalReferencedFiles = sf.referencedFiles;
|
||||
}
|
||||
|
||||
const referencedFiles = [...ext.originalReferencedFiles];
|
||||
|
||||
|
||||
const sfPath = absoluteFromSourceFile(sf);
|
||||
for (const suffix of this.suffixes) {
|
||||
@ -60,26 +68,16 @@ export class ShimReferenceTagger {
|
||||
});
|
||||
}
|
||||
|
||||
ext.taggedReferenceFiles = referencedFiles;
|
||||
sf.referencedFiles = referencedFiles;
|
||||
this.tagged.add(sf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original `referencedFiles` values of all tagged `ts.SourceFile`s and disable the
|
||||
* `ShimReferenceTagger`.
|
||||
* Disable the `ShimReferenceTagger` and free memory associated with tracking tagged files.
|
||||
*/
|
||||
finalize(): void {
|
||||
this.enabled = false;
|
||||
for (const sf of this.tagged) {
|
||||
if (!isExtendedSf(sf)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const extensionData = sfExtensionData(sf);
|
||||
if (extensionData.originalReferencedFiles !== null) {
|
||||
sf.referencedFiles = extensionData.originalReferencedFiles! as ts.FileReference[];
|
||||
}
|
||||
}
|
||||
this.tagged.clear();
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import {absoluteFrom as _, AbsoluteFsPath, getSourceFileOrError} from '../../fil
|
||||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {makeProgram} from '../../testing';
|
||||
import {ShimAdapter} from '../src/adapter';
|
||||
import {retagTsFile, untagTsFile} from '../src/expando';
|
||||
import {ShimReferenceTagger} from '../src/reference_tagger';
|
||||
|
||||
import {TestShimGenerator} from './util';
|
||||
@ -67,40 +68,6 @@ runInEachFileSystem(() => {
|
||||
expect(shimSf.referencedFiles).toEqual([]);
|
||||
});
|
||||
|
||||
it('should remove tags during finalization', () => {
|
||||
const tagger = new ShimReferenceTagger(['test1', 'test2']);
|
||||
|
||||
const fileName = _('/file.ts');
|
||||
const sf = makeArbitrarySf(fileName);
|
||||
|
||||
expectReferencedFiles(sf, []);
|
||||
|
||||
tagger.tag(sf);
|
||||
expectReferencedFiles(sf, ['/file.test1.ts', '/file.test2.ts']);
|
||||
|
||||
tagger.finalize();
|
||||
expectReferencedFiles(sf, []);
|
||||
});
|
||||
|
||||
it('should not remove references it did not add during finalization', () => {
|
||||
const tagger = new ShimReferenceTagger(['test1', 'test2']);
|
||||
const fileName = _('/file.ts');
|
||||
const libFileName = _('/lib.d.ts');
|
||||
|
||||
const sf = makeSf(fileName, `
|
||||
/// <reference path="/lib.d.ts" />
|
||||
export const UNIMPORTANT = true;
|
||||
`);
|
||||
|
||||
expectReferencedFiles(sf, [libFileName]);
|
||||
|
||||
tagger.tag(sf);
|
||||
expectReferencedFiles(sf, ['/file.test1.ts', '/file.test2.ts', libFileName]);
|
||||
|
||||
tagger.finalize();
|
||||
expectReferencedFiles(sf, [libFileName]);
|
||||
});
|
||||
|
||||
it('should not tag shims after finalization', () => {
|
||||
const tagger = new ShimReferenceTagger(['test1', 'test2']);
|
||||
tagger.finalize();
|
||||
@ -111,6 +78,56 @@ runInEachFileSystem(() => {
|
||||
tagger.tag(sf);
|
||||
expectReferencedFiles(sf, []);
|
||||
});
|
||||
|
||||
it('should not overwrite original referencedFiles', () => {
|
||||
const tagger = new ShimReferenceTagger(['test']);
|
||||
|
||||
const fileName = _('/file.ts');
|
||||
const sf = makeArbitrarySf(fileName);
|
||||
sf.referencedFiles = [{
|
||||
fileName: _('/other.ts'),
|
||||
pos: 0,
|
||||
end: 0,
|
||||
}];
|
||||
|
||||
tagger.tag(sf);
|
||||
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
|
||||
});
|
||||
|
||||
it('should always tag against the original referencedFiles', () => {
|
||||
const tagger1 = new ShimReferenceTagger(['test1']);
|
||||
const tagger2 = new ShimReferenceTagger(['test2']);
|
||||
|
||||
const fileName = _('/file.ts');
|
||||
const sf = makeArbitrarySf(fileName);
|
||||
|
||||
tagger1.tag(sf);
|
||||
tagger2.tag(sf);
|
||||
expectReferencedFiles(sf, ['/file.test2.ts']);
|
||||
});
|
||||
|
||||
describe('tagging and untagging', () => {
|
||||
it('should be able to untag references and retag them later', () => {
|
||||
const tagger = new ShimReferenceTagger(['test']);
|
||||
|
||||
const fileName = _('/file.ts');
|
||||
const sf = makeArbitrarySf(fileName);
|
||||
sf.referencedFiles = [{
|
||||
fileName: _('/other.ts'),
|
||||
pos: 0,
|
||||
end: 0,
|
||||
}];
|
||||
|
||||
tagger.tag(sf);
|
||||
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
|
||||
|
||||
untagTsFile(sf);
|
||||
expectReferencedFiles(sf, ['/other.ts']);
|
||||
|
||||
retagTsFile(sf);
|
||||
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -5,4 +5,4 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export {getDeclaration, makeProgram} from './src/utils';
|
||||
export {expectCompleteReuse, getDeclaration, makeProgram} from './src/utils';
|
||||
|
@ -97,6 +97,23 @@ export function walkForDeclaration(name: string, rootNode: ts.Node): ts.Declarat
|
||||
return chosenDecl;
|
||||
}
|
||||
|
||||
const COMPLETE_REUSE_FAILURE_MESSAGE =
|
||||
'The original program was not reused completely, even though no changes should have been made to its structure';
|
||||
|
||||
/**
|
||||
* Extracted from TypeScript's internal enum `StructureIsReused`.
|
||||
*/
|
||||
enum TsStructureIsReused {
|
||||
Not = 0,
|
||||
SafeModules = 1,
|
||||
Completely = 2,
|
||||
}
|
||||
|
||||
export function expectCompleteReuse(oldProgram: ts.Program): void {
|
||||
// Assert complete reuse using TypeScript's private API.
|
||||
expect((oldProgram as any).structureIsReused)
|
||||
.toBe(TsStructureIsReused.Completely, COMPLETE_REUSE_FAILURE_MESSAGE);
|
||||
}
|
||||
|
||||
function bindingNameEquals(node: ts.BindingName, name: string): boolean {
|
||||
if (ts.isIdentifier(node)) {
|
||||
|
@ -13,6 +13,7 @@ import {NgCompilerOptions, UnifiedModulesHost} from './core/api';
|
||||
import {NodeJSFileSystem, setFileSystem} from './file_system';
|
||||
import {PatchedProgramIncrementalBuildStrategy} from './incremental';
|
||||
import {NOOP_PERF_RECORDER} from './perf';
|
||||
import {untagAllTsFiles} from './shims';
|
||||
import {ReusedProgramStrategy} from './typecheck/src/augmented_program';
|
||||
|
||||
// The following is needed to fix a the chicken-and-egg issue where the sync (into g3) script will
|
||||
@ -80,6 +81,9 @@ export class NgTscPlugin implements TscPlugin {
|
||||
wrapHost(
|
||||
host: ts.CompilerHost&UnifiedModulesHost, inputFiles: readonly string[],
|
||||
options: ts.CompilerOptions): PluginCompilerHost {
|
||||
// TODO(alxhub): Eventually the `wrapHost()` API will accept the old `ts.Program` (if one is
|
||||
// available). When it does, its `ts.SourceFile`s need to be re-tagged to enable proper
|
||||
// incremental compilation.
|
||||
this.options = {...this.ngOptions, ...options} as NgCompilerOptions;
|
||||
this.host = NgCompilerHost.wrap(host, inputFiles, this.options, /* oldProgram */ null);
|
||||
return this.host;
|
||||
@ -92,6 +96,8 @@ export class NgTscPlugin implements TscPlugin {
|
||||
if (this.host === null || this.options === null) {
|
||||
throw new Error('Lifecycle error: setupCompilation() before wrapHost().');
|
||||
}
|
||||
this.host.postProgramCreationCleanup();
|
||||
untagAllTsFiles(program);
|
||||
const typeCheckStrategy = new ReusedProgramStrategy(
|
||||
program, this.host, this.options, this.host.shimExtensionPrefixes);
|
||||
this._compiler = new NgCompiler(
|
||||
|
@ -9,6 +9,7 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../file_system';
|
||||
import {retagAllTsFiles, untagAllTsFiles} from '../../shims';
|
||||
|
||||
import {TypeCheckingProgramStrategy, UpdateMode} from './api';
|
||||
import {TypeCheckProgramHost} from './host';
|
||||
@ -26,8 +27,10 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
|
||||
*/
|
||||
private sfMap = new Map<string, ts.SourceFile>();
|
||||
|
||||
private program: ts.Program = this.originalProgram;
|
||||
|
||||
constructor(
|
||||
private program: ts.Program, private originalHost: ts.CompilerHost,
|
||||
private originalProgram: ts.Program, private originalHost: ts.CompilerHost,
|
||||
private options: ts.CompilerOptions, private shimExtensionPrefixes: string[]) {}
|
||||
|
||||
getProgram(): ts.Program {
|
||||
@ -35,6 +38,17 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
|
||||
}
|
||||
|
||||
updateFiles(contents: Map<AbsoluteFsPath, string>, updateMode: UpdateMode): void {
|
||||
if (contents.size === 0) {
|
||||
// No changes have been requested. Is it safe to skip updating entirely?
|
||||
// If UpdateMode is Incremental, then yes. If UpdateMode is Complete, then it's safe to skip
|
||||
// only if there are no active changes already (that would be cleared by the update).
|
||||
|
||||
if (updateMode !== UpdateMode.Complete || this.sfMap.size === 0) {
|
||||
// No changes would be made to the `ts.Program` anyway, so it's safe to do nothing here.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateMode === UpdateMode.Complete) {
|
||||
this.sfMap.clear();
|
||||
}
|
||||
@ -43,14 +57,25 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
|
||||
this.sfMap.set(filePath, ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true));
|
||||
}
|
||||
|
||||
const host =
|
||||
new TypeCheckProgramHost(this.sfMap, this.originalHost, this.shimExtensionPrefixes);
|
||||
const host = new TypeCheckProgramHost(
|
||||
this.sfMap, this.originalProgram, this.originalHost, this.shimExtensionPrefixes);
|
||||
const oldProgram = this.program;
|
||||
|
||||
// Retag the old program's `ts.SourceFile`s with shim tags, to allow TypeScript to reuse the
|
||||
// most data.
|
||||
retagAllTsFiles(oldProgram);
|
||||
|
||||
this.program = ts.createProgram({
|
||||
host,
|
||||
rootNames: this.program.getRootFileNames(),
|
||||
options: this.options,
|
||||
oldProgram: this.program,
|
||||
oldProgram,
|
||||
});
|
||||
host.postProgramCreationCleanup();
|
||||
|
||||
// And untag them afterwards. We explicitly untag both programs here, because the oldProgram
|
||||
// may still be used for emit and needs to not contain tags.
|
||||
untagAllTsFiles(this.program);
|
||||
untagAllTsFiles(oldProgram);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user