Compare commits

..

283 Commits

Author SHA1 Message Date
9b961a410a release: cut the v9.1.4 release 2020-04-29 13:43:03 -07:00
420c73c722 build: use sh instead of exec for release scripts (#36862)
Previously the exec command was used, however the exec command would
exit the original calling script regardless of the whether exit was
called.  This caused the release script to always exit after the
pre-check phase.

PR Close #36862
2020-04-29 13:04:44 -07:00
62930aac7b Revert "refactor(localize): move source_file_utils.ts up (#36834)" (#36861)
This reverts commit d669429bd2.

The Angular CLI relies upon deep imports into `@angular/localize`. In this
case it relies upon the `source_file_utils.ts` file being in its previous
position.

In `master` (i.e. v10) the CLI will update its import to cope but we need
to revert for 9.1.x.

PR Close #36861
2020-04-29 10:51:39 -07:00
d7433316b0 fix(core): attempt to recover from user errors during creation (#36381)
If there's an error during the first creation pass of a `TView`, the data structure may be corrupted which will cause framework assertion failures downstream which can mask the user's error. These changes add a new flag to the `TView` that indicates whether the first creation pass was successful, and if it wasn't we try re-create the `TView`.

Fixes #31221.

PR Close #36381
2020-04-29 08:36:43 -07:00
c440165384 fix(ngcc): recognize enum declarations emitted in JavaScript (#36550)
An enum declaration in TypeScript code will be emitted into JavaScript
as a regular variable declaration, with the enum members being declared
inside an IIFE. For ngcc to support interpreting such variable
declarations as enum declarations with its members, ngcc needs to
recognize the enum declaration emit structure and extract all member
from the statements in the IIFE.

This commit extends the `ConcreteDeclaration` structure in the
`ReflectionHost` abstraction to be able to capture the enum members
on a variable declaration, as a substitute for the original
`ts.EnumDeclaration` as it existed in TypeScript code. The static
interpreter has been extended to handle the extracted enum members
as it would have done for `ts.EnumDeclaration`.

Fixes #35584
Resolves FW-2069

PR Close #36550
2020-04-28 15:59:58 -07:00
0ce96f1d78 refactor(localize): remove unused code (#36834)
This commit removes some code that is not actually used.
Some more text to ensure the commit message is long enough.

PR Close #36834
2020-04-28 09:19:25 -07:00
d3deaf9a99 refactor(localize): consolidate message/translation metadata (#36834)
This commit moves the metadata around between the various interfaces to
simplify and remove duplication.

PR Close #36834
2020-04-28 09:19:25 -07:00
5c88a9fcfe test(localize): tidy up translation parser tests (#36834)
There was a lot of duplication and multiline backtick
strings that made it hard to maintain.

Some more text to ensure the commit message is long enough.

PR Close #36834
2020-04-28 09:19:25 -07:00
0bd7517c73 refactor(localize): tighten up recognition of simple JSON translations (#36834)
Now the `SimpleJsonTranslationParser` will check that the format of the
JSON is correct before agreeing to parse it.

PR Close #36834
2020-04-28 09:19:25 -07:00
d669429bd2 refactor(localize): move source_file_utils.ts up (#36834)
This will allow the utilities in this file to be shared outside
`translate` code.

Some more text to get to the 100 character commit message requirement.

PR Close #36834
2020-04-28 09:19:25 -07:00
6a9b2c9a67 ci: fix bad reference to head property in rebase-pr script (#36825)
Update rebase-pr script to properly reference a property on
the refs object using `target` rather than the previously
named `head`.

PR Close #36825
2020-04-28 09:13:35 -07:00
0f389fa5ae fix(core): handle synthetic props in Directive host bindings correctly (#35568)
Prior to this change, animations-related runtime logic assumed that the @HostBinding and @HostListener with synthetic (animations) props are used for Components only. However having @HostBinding and @HostListener with synthetic props on Directives is also supported by View Engine. This commit updates the logic to select correct renderer to execute instructions (current renderer for Directives and sub-component renderer for Components).

This PR resolves #35501.

PR Close #35568
2020-04-27 14:55:17 -07:00
2a27f69522 docs: fix typo (#36786)
Correct typo in the router docs, changing "as your app growns" to "as your app grows". Previously the wrong spelling was used and this commit rectifies this.

PR Close #36786
2020-04-27 12:50:43 -07:00
180a32894e build: migrate from gulp to ng-dev for running formatting (#36726)
Migrates away from gulp to ng-dev for running our formatter.
Additionally, provides a deprecation warning for any attempted
usage of the previous `gulp format:*` tasks.

PR Close #36726
2020-04-24 12:32:19 -07:00
00724dcdbb feat(dev-infra): create format tool in @angular/dev-infra-private (#36726)
Previously we used gulp to run our formatter, currently clang-format,
across our repository.  This new tool within ng-dev allows us to
migrate away from our gulp based solution as our gulp solution had
issue with memory pressure and would cause OOM errors with too large
of change sets.

PR Close #36726
2020-04-24 12:32:19 -07:00
cc71116ba6 revert: "feat(dev-infra): exposed new rule 'component_benchmark' via dev_infra (#36434)" (#36798)
This reverts commit b7f2a033df.

PR Close #36798
2020-04-24 11:03:39 -07:00
ddfcf082ca revert: "build(dev-infra): update package.json and :npm_package (#36434)" (#36798)
This reverts commit d6f6cd0cb1.

PR Close #36798
2020-04-24 11:03:39 -07:00
c7e4f5eb4d revert: "build(core): use dev-infra's component_benchmark to show PoC (#36434)" (#36798)
This reverts commit e6161ca307.

PR Close #36798
2020-04-24 11:03:38 -07:00
b3fe9017f7 ci: migrate payload size tracking goldens to the golden directory (#36455)
This change is part of a larger effort to migrate all golden type
tracking files to a single location.  Additionally, this makes it
a bit easier to manage file ownership in pullapprove.

PR Close #36455
2020-04-24 09:05:11 -07:00
d3a77ea91a fix(language-service): disable update the @angular/core module (#36783)
After the user edits the file `core.d.ts`, the symbol from the core module will be invalided, which only is created when init the language service. Then the language-service will crash.

PR Close #36783
2020-04-24 09:04:25 -07:00
1b5a931d63 build(docs-infra): upgrade cli command docs sources to 31ac61357 (#36791)
Updating [angular#9.1.x](https://github.com/angular/angular/tree/9.1.x) from [cli-builds#9.1.x](https://github.com/angular/cli-builds/tree/9.1.x).

##
Relevant changes in [commit range](526c3cc37...31ac61357):

**Modified**
- help/analytics.json

PR Close #36791
2020-04-24 09:02:58 -07:00
d840503d35 build(core): use dev-infra's component_benchmark to show PoC (#36434)
This change demonstrates how to use the newly created
rule in one of our performance tests.

Future commits and PRs will migrate the remaining tests to this new bazel rule.

PR Close #36434
2020-04-23 13:31:54 -07:00
b49a734f0d build(dev-infra): update package.json and :npm_package (#36434)
* Set up dev-infra's :npm_package to also contain benchmarking suite
* Add benchmarking deps to dev-infra's package.json
* Add a bazel workspace to dev-infra's package.json. This is so that when a
  project wants to use dev-infra's code and macros, they can just import the
  macros from their node_modules instead of loading it separately

PR Close #36434
2020-04-23 13:31:54 -07:00
695f83529d feat(dev-infra): exposed new rule 'component_benchmark' via dev_infra (#36434)
* Move tools/brotli-cli, tools/browsers, tools/components,
  tools/ng_rollup_bundle, and modules/e2e_util to dev-infra/benchmarking
* Fix imports and references to moved folders and files
* Set up BUILD.bazel files for moved folders so they can be packaged with
  dev-infra's :npm_package

PR Close #36434
2020-04-23 13:31:54 -07:00
f898c9ab57 build: require a commit body in commit messages (#36632)
Enforces a requirement that all PR commit messages contain a body
of at least 100 characters.  This is meant to encourage commits
within the repo to be more descriptive of each change.

PR Close #36632
2020-04-23 12:18:57 -07:00
5337e138e3 fix(dev-infra): properly handle multiline regex of commit body (#36632)
Previously, the commit message body regex only matched the first line
of the body.  This change corrects the regex to match the entire line.

PR Close #36632
2020-04-23 12:18:57 -07:00
e33047454f ci: fix pullapprove incorrectly skipping fw-compiler as owner (#36661)
Currently, if changes are made to `compiler-cli/ngcc` and to other
compiler-related files, then only the `fw-ngcc` group is requested
for review. This is because the `not contains_any_globs` condition
will be false for `fw-compiler` and the group will never become active.

We fix this by removing the incorrect condition and filtering out ngcc
files before checking `contains_any_globs` in the primary fw-compiler
condition.

PR Close #36661
2020-04-23 12:17:11 -07:00
858fa45556 ci: add codeowner group for migrations (#36661)
Adds a new codeowner group that is dedicated for changes
to the migrations stored in `packages/core/schematics`.

PR Close #36661
2020-04-23 12:17:11 -07:00
b97211ee2b feat(dev-infra): pullapprove verify should handle files in conditions (#36661)
Currently, when verifying our pullapprove configuration, we don't
respect modifications to the set of files in a condition.

e.g. It's not possible to do the following:

```
contains_any_globs(files.exclude(...), [
```

This prevents us from having codeowner groups which match a directory,
but want to filter out specific sub directories. For example, `fw-core`
matches all files in the core package. We want to exclude the schematics
from that glob. Usually we do this by another exclude condition.

This has a *significant* downside though. It means that fw-core will not
be requested if a PR changes schematic code, _and_ actual fw-core code.

To support these conditions, the pullapprove verification tool is
refactored, so that it no longer uses Regular expressions for parsing,
but rather evaluates the code through a dynamic function. This is
possible since the conditions are written in simple Python that can
be run in NodeJS too (with small modifications/transformations).

PR Close #36661
2020-04-23 12:17:11 -07:00
25d4238371 docs: correct ngSwitch definition (#35489)
PR Close #35489
2020-04-23 12:13:40 -07:00
7743c43529 test(router): fix router test failing on IE (#36742)
This was originally fixed in #35976, but one of the window.scrollY
assertions was missed. Also updated tests to use toBeGreater/LessThan
to improve failure messages.

PR Close #36742
2020-04-23 12:13:27 -04:00
54883cb477 test(router): add canDeactivate test for forChild route (#36699)
This PR adds test case to cover a failure that was detected after
merging #36302. That commit will be reverted and will need a new PR that
does not cause this test to fail.

PR Close #36699
2020-04-23 12:08:45 -04:00
91cef8cfc7 docs: add Annie Wang to contributors.json (#36612)
PR Close #36612
2020-04-23 12:08:10 -04:00
d40bcce84e build: add AndrewKushnir to the fw-testing group (#36744)
This commit update the PullApprove config to add AndrewKushnir to the `fw-testing` group. The reason for this change is that I was involved into TestBed rewrite (for Ivy) and it belongs to the `fw-testing` group ownership.

PR Close #36744
2020-04-22 17:12:11 -04:00
7a18fb2448 build: move circular deps golden to a subfolder (#36630)
Moves the circular deps golden for packages into a subfolder of goldens,
`/goldens/circular-deps/` to more easily target the files for
ownership.

PR Close #36630
2020-04-22 17:11:20 -04:00
ec39bdcc15 release(benchpress): bump version of benchpress to 0.2.0 (#36457)
Bumping the version of benchpress as a new version needs to be released
as part of the effort to set up more benchmarking accross the
angular/angular and angular/components repos.

PR Close #36457
2020-04-22 17:10:28 -04:00
b7070b0ad6 build: run pre-check before publishing (#36527)
Previously, our process included running the pre-check script before
releasing.  With our new publishing process this was dropped.  This
change adds in automatically executing this check before publish for
both next and latest

PR Close #36527
2020-04-22 17:07:49 -04:00
aa94cd505c fix(localize): include legacy ids when describing messages (#36761)
Previously, we only displayed the new `$localize` id, which is not
currently what most people have in their translation files.
Until people migrate to the new message id system it is confusing
not to display the legacy ids.

PR Close #36761
2020-04-22 16:31:47 -04:00
cc6ccf28a6 build: update to @bazel/bazelisk@^1.4.0 (#36729)
Upgrading @bazel/bazelisk to version 1.4.0 as this introduces the
bazel binary.  This prevents the need to have a `bazel` script defined
in package.json to point to `bazelisk`, instead it is just available
on install.

PR Close #36729
2020-04-22 16:31:17 -04:00
e1071615c6 release: cut the v9.1.3 release 2020-04-22 11:30:42 -07:00
8bd5374cfd revert: fix(common): format day-periods that cross midnight (#36611) (#36751)
This reverts commit 1756cced4a.

The reason for this revert is because of the change being too risky for
a patch release of Angular. Changing date formatting proves to be a
breaking change and can only be apart of the next release.

PR Close #36751
2020-04-22 12:26:51 -04:00
b9b9cc2ba8 build: fix the compare master and patch script output (#36749)
Currently the commit message and corresponding version are flipped, which makes it hard to review the changes. This commit updates the script to properly recognize the order of arguments.

PR Close #36749
2020-04-22 00:23:57 -04:00
a6e10ef869 build: update REQUIRED_BASE_SHA in merge script to commit-message script update commit (#36750)
Updating `REQUIRED_BASE_SHA` for master and patch branches to make sure PRs that we merge are rebased after `commit-message` validation script update (to make sure the `lint` CI job fails in case a PR contains commits with invalid commit messages).

PR Close #36750
2020-04-21 21:58:23 -04:00
9724169bf4 fix(core): properly identify modules affected by overrides in TestBed (#36649)
When module overrides (via `TestBed.overrideModule`) are present, it might affect all modules that import (even transitively) an overridden one. For all affected modules we need to recalculate their scopes for a given test run and restore original scopes at the end. Prior to this change, we were recalculating module scopes only for components that are used in a test, without taking into account module hierarchy. This commit updates Ivy TestBed logic to calculate all potentially affected modules are reset cached scopes information for them (so that scopes are recalculated as needed).

Resolves #36619.

PR Close #36649
2020-04-21 21:57:49 -04:00
c0ed57db76 fix(core): do not use unbound attributes as inputs to structural directives (#36441)
Prior to this commit unbound attributes were treated as possible inputs to structural directives. Since structural directives can only accepts inputs defined using microsyntax expression (e.g. `<div *dir="exp">`), such unbound attributes should not be considered as inputs. This commit aligns Ivy and View Engine behavior and avoids using unbound attributes as inputs to structural directives.

PR Close #36441
2020-04-21 13:30:25 -04:00
0bd50e2e50 fix(core): missing-injectable migration should not migrate @NgModule classes (#36369)
Based on the migration guide, provided classes which don't have
either `@Injectable`, `@Directive`, `@Component` or `@Pipe` need
to be migrated.

This is not correct as provided classes with an `@NgModule` also
have a factory function that can be read by the r3 injector. It's
unclear in which cases the `@NgModule` decorator is used for
provided classes, but this scenario has been reported.

Either we fix this in the migration, or we make sure to report
this as unsupported in the Ivy compiler.

Fixes #35700.

PR Close #36369
2020-04-21 12:54:24 -04:00
0ceb27041f docs: place download section in architecture to the top (#36565)
link is very deep down on architecture page this commit is part of a larger effort to standardise ownload sections on angular.io

This commit partially addresses #35459

PR Close #36565
2020-04-21 12:50:29 -04:00
ec2affe104 refactor(docs-infra): refactors autoLinkCode (#36686)
PR Close #36686
2020-04-21 12:49:56 -04:00
c590e8ca7a fix(dev-infra): extract commit headers before checking commit message validity (#36733)
This commit fixes an issue where adding `fixup` commits was triggering a lint error. The problem was caused by the fact that we used the entire message body while checking whether `fixup` commit has a corresponding "parent" commit in a range. This issue was found after enforcing a check that exits the process if there is an invalid commit message found (4341743b4a).

PR Close #36733
2020-04-21 12:49:27 -04:00
254b9ea44c docs: place download section in accessibility to the top (#36561)
link is very deep down on acessibility page this commit is part of a larger effort to standardise ownload sections on angular.io

This commit partially addresses #35459

PR Close #36561
2020-04-20 16:20:27 -04:00
2a53f47159 fix(dev-infra): exit non-zero if commit message validation failed (#36723)
Currently the `commit-message` validation script does not exit
with a non-zero exit code if the commit message validation failed.

This means that invalid commit messages are currently not
causing CI to be red. See: https://circleci.com/gh/angular/angular/686008

PR Close #36723
2020-04-20 14:28:17 -04:00
722d9397b0 Revert "fix(router): pass correct component to canDeactivate checks when using two or more sibling router-outlets (#36302)" (#36697)
This reverts commit 80e6c07d89.

PR Close #36697
2020-04-20 13:42:45 -04:00
03de31a78e docs: remove version ^7.0.0 from LTS support (#36708)
Version 7.0.0 is under LTS until 18-4-2020 removed it from the table which showed it as LTS  and added to versions that are no longer under support.
PR Close #36708
2020-04-20 13:38:05 -04:00
b22c5a953d docs: fix typo (#36665)
PR Close #36665
2020-04-20 13:36:40 -04:00
24222e0c1f build: list feat commits in patch branch in relase review script (#36651)
PR Close #36651
2020-04-17 18:16:36 -04:00
95f45e8070 refactor(compiler): remove unused CachedFileSystem (#36687)
This was only being used by ngcc but not any longer.

PR Close #36687
2020-04-17 16:33:49 -04:00
18be33a9d1 fix(ngcc): do not use cached file-system (#36687)
The cached file-system was implemented to speed up ngcc
processing, but in reality most files are not accessed many times
and there is no noticeable degradation in speed by removing it.

Benchmarking `ngcc -l debug` for AIO on a local machine
gave a range of 196-236 seconds with the cache and 197-224
seconds without the cache.

Moreover, when running in parallel mode, ngcc has a separate
file cache for each process. This results in excess memory usage.
Notably the master process, which only does analysis of entry-points
holds on to up to 500Mb for AIO when using the cache compared to
only around 30Mb when not using the cache.

Finally, the file-system cache being incorrectly primed with file
contents before being processed has been the cause of a number
of bugs. For example https://github.com/angular/angular-cli/issues/16860#issuecomment-614694269.

PR Close #36687
2020-04-17 16:33:49 -04:00
a22d4f6c98 docs(dev-infra): document limitation in ts-circular-deps tool (#36659)
Adds documentation on discovered limitations in the ts-circular-deps
tool, so that we can reference it when needed.

PR Close #36659
2020-04-17 16:25:00 -04:00
5ae8473c6b fix(core): pipes injecting viewProviders when used on a component host node (#36512)
The flag that determines whether something should be able to inject from `viewProviders` is opt-out and the pipes weren't opted out, resulting in them being able to see the viewProviders if they're placed on a component host node.

Fixes #36146.

PR Close #36512
2020-04-17 16:15:10 -04:00
fd7c39e3cf fixup!: build(docs-infra): Ensures that only member docs are linked (#36316)
PR Close #36316
2020-04-17 12:33:57 -04:00
d85d91df66 fixup!: build(docs-infra): Ensures that only member docs are linked (#36316)
PR Close #36316
2020-04-17 12:33:57 -04:00
15930d21c7 fixup!: build(docs-infra): add _ to ignored ignoreGenericWords (#36316)
PR Close #36316
2020-04-17 12:33:57 -04:00
61a7f98b98 build(docs-infra): add _ to ignored ignoreGenericWords (#36316)
Previously, when a document included `_`, the autoLinker will try to
generate a link, e.g from `core/ɵComponentDef._`. This commit adds it
to the ignored words to prevent that.

PR Close #36316
2020-04-17 12:33:56 -04:00
c3c7bf6509 build(docs-infra): Ensures that only member docs are linked (#36316)
This commit ensures that `member` docs are only linked if the linking
text contains `.`.

PR Close #36316
2020-04-17 12:33:56 -04:00
b2e7ce47ec build(docs-infra): fix autoLinkCode to ignore docs without a path (#36316)
Previously, the auto linker generated links without an `href` when the
API was private. This commit fixes this by making sure that the `path`
of the document is not empty.

Closes #36260

PR Close #36316
2020-04-17 12:33:56 -04:00
94e518e3c7 docs: add tony bove to about page (#36335)
PR Close #36335
2020-04-17 12:33:16 -04:00
0fa5ac8d0d ci: remove reliance on Github API for CI setup (#36500)
Previously our CI during the setup process has made requests
to the Github API to determine the target branch and shas.
With this change, this information is now determined via git
commands using pipeline parameters from CircleCI.

PR Close #36500
2020-04-16 17:14:34 -04:00
f2fca3e243 docs: getting started guide use pipe before introduction (#36584)
In "Getting started" guide pipes are not intoduced anywhere but are used in the guide.
Added refrence to pipes for better consistency in the tutorial.

Fixes #36375

PR Close #36584
2020-04-16 16:09:20 -04:00
5bab49828d fix(language-service): properly evaluate types in comparable expressions (#36529)
This commit fixes how the language service evaluates the compatibility
of types to work with arbitrary union types. As a result, compatibility
checks are now more strict and can catch similarities or differences
more clearly.

```
number|string == string|null  // OK
number|string == number       // OK
number|string == null         // not comparable
number == string              // not comparable
```

Using Ivy as a backend should provide these diagnoses for free, but we
can backfill them for now.

Closes https://github.com/angular/vscode-ng-language-service/issues/723

PR Close #36529
2020-04-16 16:07:47 -04:00
db4e93d0ca docs: remove rob (#36285)
PR Close #36285
2020-04-16 16:07:07 -04:00
479a59be43 refactor(ngcc): moved shared setup into a single function (#36637)
The `main.ts` and `worker.ts` had duplicate logic, which has now been
moved to a single function called `getSharedSetup()`.

PR Close #36637
2020-04-16 16:05:13 -04:00
52aab63dd9 refactor(ngcc): simplify cluster PackageJsonUpdater (#36637)
PR Close #36637
2020-04-16 16:05:13 -04:00
506beeddc1 refactor(ngcc): create new entry-point for cluster workers (#36637)
PR Close #36637
2020-04-16 16:05:13 -04:00
0075078179 refactor(ngcc): move pathMapping processing to utils (#36637)
PR Close #36637
2020-04-16 16:05:13 -04:00
bb7edc52aa refactor(ngcc): move analyze and compile functions into their own files (#36637)
PR Close #36637
2020-04-16 16:05:13 -04:00
ed2b0e945e refactor(ngcc): move command line option parsing to its own file (#36637)
PR Close #36637
2020-04-16 16:05:13 -04:00
da159bde83 fix(ngcc): display unlocker process output in sync mode (#36637)
The change in e041ac6f0d
to support sending unlocker process output to the main ngcc
console output prevents messages require that the main process
relinquishes the event-loop to allow the `stdout.on()` handler to
run.  This results in none of the messages being written when ngcc
is run in `--no-async` mode, and some messages failing to be
written if the main process is killed (e.g. ctrl-C).

It appears that the problem with Windows and detached processes
is known - see https://github.com/nodejs/node/issues/3596#issuecomment-250890218.
But in the meantime, this commit is a workaround, where non-Windows
`inherit` the main process `stdout` while on Windows it reverts
to the async handler approach, which is better than nothing.

PR Close #36637
2020-04-16 16:05:13 -04:00
06a9809e32 Revert "fix(ngcc): do not spawn unlocker processes on cluster workers (#36569)" (#36637)
This reverts commit 66effde9f3.

PR Close #36637
2020-04-16 16:05:13 -04:00
1e4fb74ec8 docs: refactor routing doc (#35566)
This rewrite changes headings to focus on user tasks rather than features,
verifies that content is up-to-date and complete, removes colloquial phrases,
adds prerequisites, and expands on a task-based section in the beginning
(a quick reference).

PR Close #35566
2020-04-16 10:36:01 -07:00
797c306306 docs: add string-union type note (#35859)
PR Close #35859
2020-04-16 09:47:22 -07:00
972fc06135 docs: style edit (#35859)
PR Close #35859
2020-04-16 09:47:22 -07:00
a9117061d0 docs: update http guide (#35859)
PR Close #35859
2020-04-16 09:47:22 -07:00
fe1d9bacc3 fix(core): prevent unknown property check for AOT-compiled components (#36072)
Prior to this commit, the unknown property check was unnecessarily invoked for AOT-compiled components (for these components, the check happens at compile time). This commit updates the code to avoid unknown property verification for AOT-compiled components by checking whether schemas information is present (as a way to detect whether this is JIT or AOT compiled component).

Resolves #35945.

PR Close #36072
2020-04-16 09:45:16 -07:00
08b8b51486 fix(compiler): avoid generating i18n attributes in plain form (#36422)
Prior to this change, there was a problem while matching template attributes, which mistakenly took i18n attributes (that might be present in attrs array after template ones) into account. This commit updates the logic to avoid template attribute matching logic from entering the i18n section and as a result this also allows generating proper i18n attributes sections instead of keeping these attribute in plain form (with their values) in attribute arrays.

PR Close #36422
2020-04-16 09:44:10 -07:00
1d4af3f734 docs: remove unneeded code from universal example/guide (#36483)
In the past, server-side rendered apps needed to convert URLs used in
API requests to absolute when rendering on the server. Originally, this
was handled in the `universal` guide and corresponding example app by
modifying the `HeroService` to use `APP_BASE_HREF` to derive the
absolute URL.

In #28956, the guide was updated to show an improved method: Specifying
an `HttpInterceptor` that took care of converting the URLs to absolute.
That interceptor was only provided when rendering the app on the server.
By mistake, the corresponding example app was not updated along with the
guide.

Since `@nguniversal/*` v7.1.0, it is no longer necessary to convert the
URLs to absolute inside the app. This is handled in the `@nguniversal`
libs (see angular/universal#897).

This commit updates the example app to remove unnecessary code and
modifies the guide to mention the issue with absolute URLs, but explain
that developers only need to worry about it when not using one of the
`@nguniversal/*-engine` packages.

PR Close #36483
2020-04-16 09:43:43 -07:00
609d81c65e docs: minor fixes/improvements in the universal guide (#36483)
PR Close #36483
2020-04-16 09:43:43 -07:00
af30efddc5 test(docs-infra): add tests for universal docs example (#36483)
Previously, there were no tests for the `universal` docs example. This
meant that the project was not tested at all (not even ensuring that it
can be built successfully).

This commit adds e2e tests for the `universal` example (ported from
`toh-pt6` and cleaned up) and also verifies that the project can be
built successfully (including the server).

PR Close #36483
2020-04-16 09:43:42 -07:00
15115f6179 build(docs-infra): update @types/express-serve-static-core to avoid error in universal example (#36483)
Previously, building the `universal` example failed with:
```
node_modules/@types/express/index.d.ts(90,50): error TS2694: Namespace '".../@types/express-serve-static-core/index"' has no exported member 'Params'.
node_modules/@types/express/index.d.ts(90,64): error TS2694: Namespace '".../@types/express-serve-static-core/index"' has no exported member 'ParamsDictionary'.
```

This commit fixes the error by upgrading
`@types/express-serve-static-core` to a newer version.
See DefinitelyTyped/DefinitelyTyped#40905 for more details.

PR Close #36483
2020-04-16 09:43:42 -07:00
eec9b6bbb5 refactor(docs-infra): update universal example to match latest CLI (#36483)
Update the `universal` example (and related files) to match what would
be generated by the latest CLI.

Fixes #35289

PR Close #36483
2020-04-16 09:43:42 -07:00
45fd77ead1 fix(docs-infra): align universal example with toh-pt6 (#36483)
As mentioned in the `universal` guide, the `toh-pt6` examples is the
starting poitn for the `universal` example. However, the two examples
had become out-of-sync, because some fixes/changes were made to the
Tour-of-Heroes examples.

This commit ports these changes to the `universal` example.

PR Close #36483
2020-04-16 09:43:42 -07:00
f16587e9b7 refactor(docs-infra): update main.ts in Tour-of-Heroes examples to match latest CLI (#36483)
Update the `main.ts` files in Tour-of-Heroes examples to match what
would be generated by the latest CLI.

PR Close #36483
2020-04-16 09:43:42 -07:00
4f9991534e style(docs-infra): clean up Tour-of-Heroes examples (#36483)
I noticed these minor styling issues while aligning the `universal`
examples with the `toh-pt6` example (in a subsequent commit).

PR Close #36483
2020-04-16 09:43:42 -07:00
51a0ed2222 build(docs-infra): add missing build npm script for universal docs example ZIP archive (#36483)
Previously, the `package.json` template used in the ZIP archive of the
`universal` example that we offer for download missed the `build` npm
script.

This commit updates the template for the `universal` docs example to
include the `build` npm script.

NOTE:
The `build` npm script is already included in
`aio/tools/examples/shared/boilerplate/universal/package.json`, but it
was removed by the example zipper.

PR Close #36483
2020-04-16 09:43:42 -07:00
a5ea100e7c fix(core): handle empty translations correctly (#36499)
In certain use-cases it's useful to have an ability to use empty strings as translations. Currently Ivy fails at runtime if empty string is used as a translation, since some parts of internal data structures are not created properly. This commit updates runtime i18n logic to handle empty translations and avoid unnecessary extra processing for such cases.

Fixes #36476.

PR Close #36499
2020-04-16 09:42:05 -07:00
0429c7f5e9 docs: add " in architecture page (#36564)
after alert is helpful " is missing added it to the architecture page

PR Close #36564
2020-04-16 09:41:32 -07:00
1756cced4a fix(common): format day-periods that cross midnight (#36611)
When formatting a time with the `b` or `B` format codes, the rendered
string was not correctly handling day periods that spanned midnight.
Instead the logic was falling back to the default case of `AM`.

Now the logic has been updated so that it matches times that are within
a day period that spans midnight, so it will now render the correct
output, such as `at night` in the case of English.

Applications that are using either `formatDate()` or `DatePipe` and any
of the `b` or `B` format codes will be affected by this change.

Fixes #36566

PR Close #36611
2020-04-16 09:40:41 -07:00
793a001d7c release: cut the v9.1.2 release 2020-04-15 15:45:54 -07:00
5c3774cfe6 Revert "fix(common): locales/global/*.js are not ES5 compliant (#36342)" (#36648)
This reverts commit c8f3fa9f3e.

PR Close #36648
2020-04-15 15:33:59 -07:00
12266b2042 fix(ngcc): display output from the unlocker process on Windows (#36569)
On Windows, the output of a detached process (such as the unlocker
process used by `LockFileWithChildProcess`) is not shown in the parent
process' stdout.

This commit addresses this by piping the spawned process' stdin/stdout
and manually writing to the parent process' stdout.

PR Close #36569
2020-04-15 09:25:27 -07:00
e385abc83c fix(ngcc): do not spawn unlocker processes on cluster workers (#36569)
The current ngcc lock-file strategy spawns a new process in order to
capture a potential `SIGINT` and remove the lock-file. For more
information see #35861.

Previously, this unlocker process was spawned as soon as the `LockFile`
was instantiated in order to have it available as soon as possible
(given that spawning a process is an asynchronous operation). Since the
`LockFile` was instantiated and passed to the `Executor`, this meant
that an unlocker process was spawned for each cluster worker, when
running ngcc in parallel mode. These processes were not needed, since
the `LockFile` was not used in cluster workers, but we still had to pay
the overhead of each process' own memory and V8 instance.
(NOTE: This overhead was small compared to the memory consumed by ngcc's
normal operations, but still unnecessary.)

This commit avoids the extra processes by only spawning an unlocker
process when running on the cluster master process and not on worker
processes.

PR Close #36569
2020-04-15 09:25:27 -07:00
933cbfb828 fix(ngcc): force ngcc to exit on error (#36622)
For some reason (possibly related to async/await promises)
the ngcc process is not exiting when spawned from the CLI
when there has been an error (such as when it timesout waiting
for a lockfile to become free).

Calling `process.exit()` directly fixes this.

Fixes #36616

PR Close #36622
2020-04-15 09:24:54 -07:00
c5e725111d build: fix circular deps failure (#36629)
With the large scale refactoring of the repo to the new version of clang-format, some import orders were changed.

Specifically the imports found in this range.

The file previously read:

import {TestBed} from './test_bed';
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
import {R3TestBedCompiler} from './r3_test_bed_compiler';
and now reads:

import {R3TestBedCompiler} from './r3_test_bed_compiler';
import {TestBed} from './test_bed';
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
This change in order cause the circular dependency to be entered earlier and changed the golden file for our circular deps discovery.

PR Close #36629
2020-04-14 13:56:55 -07:00
93993864b1 build: update to latest version of yarn (#36464)
PR Close #36464
2020-04-14 12:47:31 -07:00
26f49151e7 build: reformat repo to new clang@1.4.0 (#36628)
PR Close #36628
2020-04-14 12:07:43 -07:00
4b3f9ac739 refactor(language-service): clean up and exports and consolidate types (#36533)
PR Close #36533
2020-04-14 10:17:43 -07:00
80604d3a76 style: lint (#36580)
PR Close #36580
2020-04-14 10:12:59 -07:00
e615a10371 build: update to rules_nodejs 1.6.0 (#36580)
Lots of bug fixes and stability fixes. Last 1.x release for rules_nodejs.

PR Close #36580
2020-04-14 10:12:59 -07:00
c8f2ca2349 docs: edit to setup-local (#36168)
PR Close #36168
2020-04-13 17:33:35 -07:00
a67afcc932 fix(upgrade): update $locationShim to handle Location changes before initialization (#36498)
Updates the $locationShim to receive the most recent Location change
made, even if it happened before initialize() is called. This is
important when AngularJS bootstrapping is deferred and there is a delay
between when $locationShim is constructed and when it is initialized.
With this change, the $locationShim will correctly reflect any redirects
that occurred between construction and initialization.

Closes #36492

PR Close #36498
2020-04-13 17:33:00 -07:00
fd7698253e docs: move ng-conf 2020 to the already presented section (#36413)
PR Close #36413
2020-04-13 08:20:04 -07:00
32de025dce fix(docs-infra): contribute page not visible correctly on mobile devices (#36573)
on mobile devices screen size < 600px the contribute page is not visible in correct form changed styles to make it visible correctly

PR Close #36573
2020-04-13 08:19:01 -07:00
a4e1768a74 fix(docs-infra): fix About page button text being truncated on small screens (#36576)
On small screens (e.g. on mobile), the text on some of the buttons in
the About page was truncated. Changed the text size, margin and
padding so that the the whole text is visible on such screens (320px to
480px).

PR Close #36576
2020-04-13 08:18:11 -07:00
b336871303 docs: fix typo in Tests guide (#36592)
PR Close #36592
2020-04-13 08:17:27 -07:00
92fa6399f7 docs(zone.js): fix typos in NgZone guide code example (#36597)
Fixes #36594

PR Close #36597
2020-04-13 08:16:59 -07:00
3992341d34 fix(core): undecorated-classes-with-decorated-fields migration should avoid error if base class has no value declaration (#36543)
The undecorated-classes-with-decorated-fields migration relies on
the type checker to resolve base classes of individual classes.

It could happen that resolved base classes have no value declaration.
e.g. if they are declared through an interface in the default types.
Currently the migration will throw in such situations because it assumes
that `ts.Symbol#valueDeclaration` is always present. This is not the
case, but we don't get good type-checking here due to a bug in the
TypeScript types. See:
https://github.com/microsoft/TypeScript/issues/24706.

Fixes #36522.

PR Close #36543
2020-04-10 13:53:15 -07:00
8c559ef104 fix(ngcc): correctly detect external files from nested node_modules/ (#36559)
Previously, when we needed to detect whether a file is external to a
package, we only checked whether the relative path to the file from the
package's root started with `..`. This would detect external imports
when the packages were siblings (e.g. peer dependencies or hoisted to
the top of `node_modules/` by the package manager), but would fail to
detect imports from packages located in nested `node_modules/` as
external. For example, importing `node_modules/foo/node_modules/bar`
from a file in `node_modules/foo/` would be considered internal to the
`foo` package.

This could result in processing/analyzing more files than necessary.
More importantly it could lead to errors due to trying to analyze
non-Angular packages that were direct dependencies of Angular packages.

This commit fixes it by also verifying that the relative path to a file
does not start with `node_modules/`.

Jira issue: [FW-2068](https://angular-team.atlassian.net/browse/FW-2068)

Fixes #36526

PR Close #36559
2020-04-10 09:10:26 -07:00
0bac2b062c build: update REQUIRED_BASE_SHA in merge script to clang 1.4.0 upgrade commit (#36547)
Updating `REQUIRED_BASE_SHA` for master and patch branches to make sure PRs that we merge are rebased after clang 1.4.0 upgrade.

PR Close #36547
2020-04-09 16:00:31 -07:00
58d028178f fix(docs-infra): fix elements example when used with ES5 (#36536)
Previously, the `elements` docs example only worked in browsers that
natively supported Custom Elements and ES2015 modules. Furthermore, it
didn't work on StackBlitz, because StackBlitz ignores the specified
`target` in `tsconfig.json` and uses the UMD bundles (i.e. ES5 code)
even on browsers that do support ES2015.
(NOTE: In the past, this was not a problem, because we explicitly did
not provide a StackBlitz example. This has changed in #36067.)

This commit ensures the example works on all browsers and also on
StackBlitz by providing the necessary Custom Elements polyfills.

Fixes #36532

PR Close #36536
2020-04-09 13:35:28 -07:00
e06512b22f perf(ngcc): only load if it is needed (#36486)
PR Close #36486
2020-04-09 11:33:29 -07:00
603b0944d5 perf(ngcc): reduce the size of the entry-point manifest file (#36486)
The base path for package and entry-points is known so there is
no need to store these in the file. Also this commit avoids storing
empty arrays unnecessarily.

PR Close #36486
2020-04-09 11:33:29 -07:00
918e628f9b perf(ngcc): read dependencies from entry-point manifest (#36486)
Previously, even if an entry-point did not need to be processed,
ngcc would always parse the files of the entry-point to compute
its dependencies. This can take a lot of time for large node_modules.

Now these dependencies are cached in the entry-point manifest,
and read from there rather than computing them every time.

See https://github.com/angular/angular/issues/36414\#issuecomment-608401834
FW-2047

PR Close #36486
2020-04-09 11:33:28 -07:00
468cf69c55 fix(compiler): handle type references to namespaced symbols correctly (#36106)
When the compiler needs to convert a type reference to a value
expression, it may encounter a type that refers to a namespaced symbol.
Such namespaces need to be handled specially as there's various forms
available. Consider a namespace named "ns":

1. One can refer to a namespace by itself: `ns`. A namespace is only
   allowed to be used in a type position if it has been merged with a
   class, but even if this is the case it may not be possible to convert
   that type into a value expression depending on the import form. More
   on this later (case a below)
2. One can refer to a type within the namespace: `ns.Foo`. An import
   needs to be generated to `ns`, from which the `Foo` property can then
   be read.
3. One can refer to a type in a nested namespace within `ns`:
   `ns.Foo.Bar` and possibly even deeper nested. The value
   representation is similar to case 2, but includes additional property
   accesses.

The exact strategy of how to deal with these cases depends on the type
of import used. There's two flavors available:

a. A namespaced import like `import * as ns from 'ns';` that creates
   a local namespace that is irrelevant to the import that needs to be
   generated (as said import would be used instead of the original
   import).

   If the local namespace "ns" itself is referred to in a type position,
   it is invalid to convert it into a value expression. Some JavaScript
   libraries publish a value as default export using `export = MyClass;`
   syntax, however it is illegal to refer to that value using "ns".
   Consequently, such usage in a type position *must* be accompanied by
   an `@Inject` decorator to provide an explicit token.

b. An explicit namespace declaration within a module, that can be
   imported using a named import like `import {ns} from 'ns';` where the
   "ns" module declares a namespace using `declare namespace ns {}`.
   In this case, it's the namespace itself that needs to be imported,
   after which any qualified references into the namespace are converted
   into property accesses.

Before this change, support for namespaces in the type-to-value
conversion was limited and only worked  correctly for a single qualified
name using a namespace import (case 2a). All other cases were either
producing incorrect code or would crash the compiler (case 1a).

Crashing the compiler is not desirable as it does not indicate where
the issue is. Moreover, the result of a type-to-value conversion is
irrelevant when an explicit injection token is provided using `@Inject`,
so referring to a namespace in a type position (case 1) could still be
valid.

This commit introduces logic to the type-to-value conversion to be able
to properly deal with all type references to namespaced symbols.

Fixes #36006
Resolves FW-1995

PR Close #36106
2020-04-09 11:32:22 -07:00
c8f3fa9f3e fix(common): locales/global/*.js are not ES5 compliant (#36342)
Although this code has been part of Angular 9.x I only noticed this
error when upgrading to Angular 9.1.x because historically the source
locale data was not injected when localizing, but as of
angular/angular-cli#16394 (9.1.0) it is now included. This tipped me off
that my other bundles were not being built properly, and this change
allows me to build a valid ES5 bundle (I have also added a verification
step to my build pipeline to alert me if this error appears again in any
of my bundles).

I found the `locales/global/*.js` file paths being referenced by the
`I18nOptions` in
@angular-devkit/build-angular/src/utils/i18n-options.ts,
and following that it looks like it is actually loaded and used in
@angular-devkit/build-angular/src/utils/process-bundle.ts. I saw the
function `terserMangle` does appear that it is likely aware of the build
being ES5, but I'm not sure why this is not producing a valid ES5
bundle.

This change updates `tools/gulp-tasks/cldr/extract.js` to produce ES5
compliant `locales/global/*.js` and that fixes my issue. However, I am
not sure if @angular-devkit/build-angular should be modified to produce
a valid ES5 bundle instead or if the files could be TypeScript rather
than JavaScript files.

A test that a valid ES5 bundle is produced would be helpful, and I hope
this is reproducible and not some issue with my config.

PR Close #36342
2020-04-09 11:30:32 -07:00
78136cc3a7 style: format forms validators to fix lint error (#36546)
PR Close #36546
2020-04-09 11:18:22 -07:00
66724fd159 docs(forms): clarify the description of minLength and maxLength (#36297)
Previously, it was not clear that the `minLength` and `maxLength` validators
can only be used with objects that contain a `length` property. This commit
clarifies this.

PR Close #36297
2020-04-09 10:31:03 -07:00
8e7f9033a3 fix(router): pass correct component to canDeactivate checks when using two or more sibling router-outlets (#36302)
fixes #34614

There's an edge case where if I use two (or more) sibling <router-outlet>s in two (or more) child routes where their parent route doesn't have a component then preactivation will trigger all canDeactivate checks with the same component because it will use wrong OutletContext.

PR Close #36302
2020-04-09 10:09:43 -07:00
c8f9092364 fix(dev-infra): fix commit message validation in git worktrees (#36507)
Previously, the `pre-commit-validate` command (used in the `commit-msg`
git hook) assumed that the commit message was stored in
`.git/COMMIT_EDITMSG` file. This is usually true, but not when using
[git worktrees](https://git-scm.com/docs/git-worktree), where `.git` is
a file containing the path to the actual git directory.

This commit fixes it by taking advantage of the fact that git passes the
actual path of the file holding the commit message to the `commit-msg`
hook and husky exposes the arguments passed by git as
`$HUSKY_GIT_PARAMS`.

NOTE:
We cannot use the environment variable directly in the `commit-msg` hook
command, because environment variables need to be referenced differently
on Windows (`%VAR_NAME%`) vs macOS/Linux (`$VAR_NAME`). Instead, we pass
the name of the environment variable and the validation script reads the
variable's value off of `process.env`.

PR Close #36507
2020-04-09 09:46:19 -07:00
bc995835b9 refactor(compiler): create a new root BindingScope for each template (#36362)
Previously we had a singleton `ROOT_SCOPE` object, from
which all `BindingScope`s derived. But this caused ngcc to
produce non-deterministic output when running multiple workers
in parallel, since each process had its own `ROOT_SCOPE`.

In reality there is no need for `BindingScope` reference names
to be unique across an entire application (or in the case of ngcc
across all the libraries). Instead we just need uniqueness within
a template.

This commit changes the compiler to create a new root `BindingScope`
each time it compiles a component's template.

Resolves #35180

PR Close #36362
2020-04-09 09:44:57 -07:00
1bfa908ab3 style: typescript lint fix (#36531)
PR Close #36531
2020-04-09 00:59:22 +00:00
2479f7d7ef Revert "refactor(bazel): use runfiles helper in ts-api-guardian (#36471)" (#36531)
This reverts commit 92c4f3d508.

PR Close #36531
2020-04-09 00:59:22 +00:00
333b8679ad refactor(bazel): use runfiles helper in ts-api-guardian (#36471)
Pre-fractor for future rules_nodejs release when require.resolve patches are removed.

PR Close #36471
2020-04-08 15:57:52 -07:00
5da621d3dd fix(language-service): remove circular dependency instance (#36463)
PR Close #36463
2020-04-08 15:29:09 -07:00
cbed582a1a style(compiler): reformat of codebase with new clang-format version (#36520)
This commit reformats the packages/compiler tree using the new version of
clang-format.

PR Close #36520
2020-04-08 14:51:08 -07:00
d5aa6b5bd6 style(compiler-cli): reformat of codebase with new clang-format version (#36520)
This commit reformats the packages/compiler-cli tree using the new version
of clang-format.

PR Close #36520
2020-04-08 14:51:08 -07:00
33eee43263 fix(ngcc): do not warn if paths mapping does not exist (#36525)
In cc4b813e75 the `getBasePaths()`
function was changed to log a warning if a `basePath()` computed from
the `paths` mappings did not exist. It turns out this is a common and
accepted scenario, so we should not log warnings in this case.

Fixes #36518

PR Close #36525
2020-04-08 14:29:57 -07:00
72053b0f27 build(bazel): fix runfiles resolve in karma-saucelabs.js after $location => $rootpath cleanup (#36511)
This wasn't caught by CI on the PR as this binary is only run once daily via a monitor job.

PR Close #36511
2020-04-08 12:13:27 -07:00
d20ef47b16 build: ts-circular-deps tool should normalize golden (#36505)
Currently the golden output of the circular-deps tool is purely
based on the order of source files passed to the tool, and on the
amount of imports inside source files.

This is actually resulting in deterministic output as running
the tool multiple times without any changes to source files,
results in the same output.

Though it seems like the tool is too strict and we can avoid
unnecessary golden changes if:

1. A source file that is part of a cycle is imported earlier (in terms
of how the analyzer visits them). This could result in the cycle path
starting with a different source file.

2. Source files which are not part of a cycle are imported earlier
(in terms of how the analyzer visits them). This could result in moved
items in the golden if re-approved (even though the cycles remain the same)

To fix this, we normalize the cycle path array that serves as
serializable data structure for the text-based goldens. Since
the paths represents a cycle, the path can be shifted in a
deterministic way so that cycles don't change unnecessarily
in the golden, and to simplify comparison of cycles.

Additionally, we sort the cycles in a deterministic way so
that the golden doesn't change unnecessarily (as explained above).

PR Close #36505
2020-04-08 12:12:59 -07:00
bdc05aef64 docs: update the Support policy and schedule (#35390)
PR Close #35359

PR Close #35390
2020-04-08 12:12:32 -07:00
798d959bee build: remove fullTemplateTypeCheck from aio tsconfig (#36502)
`fullTemplateTypeCheck` is no longer required since we now use `strictTemplates` which is a superset of the former option.

Follow-up on: 04f61c0c3e (r38354112)

PR Close #36502
2020-04-08 12:11:29 -07:00
3570aaa363 test(language-service): Inline test cases in parsing-cases.ts (#36495)
This commit removes individual components from parsing-cases.ts and
colocate them with the actual tests. This makes the tests more readable.

PR Close #36495
2020-04-08 12:11:04 -07:00
421b6a97d6 fix(zone.js): add issue numbers of @types/jasmine to the test cases (#34625)
Some cases will still need to use `spy as any` cast, because `@types/jasmine` have some issues,
1. The issue jasmine doesn't handle optional method properties, https://github.com/DefinitelyTyped/DefinitelyTyped/issues/43486
2. The issue jasmine doesn't handle overload method correctly, https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42455

PR Close #34625
2020-04-08 12:10:35 -07:00
b28a5f6eef build: update jasmine to 3.5 (#34625)
1. update jasmine to 3.5
2. update @types/jasmine to 3.5
3. update @types/jasminewd2 to 2.0.8

Also fix several cases, the new jasmine 3 will help to create test cases correctly,
such as in the `jasmine 2.x` version, the following case will pass

```
expect(1 == 2);
```

But in jsamine 3, the case will need to be

```
expect(1 == 2).toBeTrue();
```

PR Close #34625
2020-04-08 12:10:34 -07:00
52ab9397a0 build: update matching regex for bazel stamping (#36523)
Previously, the bazel stamping regex only matched on versions
0-9 for major and minor numbers, this update allows for matching
on any number for major, minor or patch.

PR Close #36523
2020-04-08 11:04:08 -07:00
b99e539b0e release: cut the v9.1.1 release 2020-04-07 15:48:51 -07:00
fd2ef74a31 docs: update reactive forms page (#35969)
PR Close #35969
2020-04-07 15:24:18 -07:00
cbc25bbf31 fix(compiler): resolve enum values in binary operations (#36461)
During static evaluation of expressions, the partial evaluator
may come across a binary + operator for which it needs to
evaluate its operands. Any of these operands may be a reference
to an enum member, in which case the enum member's value needs
to be used as literal value, not the enum member reference
itself. This commit fixes the behavior by resolving an
`EnumValue` when used as a literal value.

Fixes #35584
Resolves FW-1951

PR Close #36461
2020-04-07 15:21:52 -07:00
7385b2cf25 style(compiler): reformat partial evaluator source tree (#36461)
PR Close #36461
2020-04-07 15:21:52 -07:00
0daa48800e fix(ngcc): correctly identify relative Windows-style import paths (#36372)
Previously, `isRelativePath()` assumed paths are *nix-style. This caused
Windows-style paths (such as `C:\foo\some-package\some-file.js`) to not
be recognized as "relative" imports.

This commit fixes this by using the OS-agnostic `isRooted()` helper and
also accounting for both styles of path delimiters: `/` and `\`

PR Close #36372
2020-04-07 15:21:27 -07:00
ffa4e11db1 fix(language-service): use the HtmlAst to get the span of HTML tag (#36371)
The HTML tag may include `-` (e.g. `app-root`), so use the `HtmlAst` to get the span.

PR Close #36371
2020-04-07 15:10:34 -07:00
41a41e741d style(docs-infra): removed extra , from _resources.scss file (#35935)
there was a typo in _resourcess.scss file there was an extra comma added some spaces too that were needed for proper styling of the code

PR Close #35935
2020-04-07 15:08:58 -07:00
c5384aee58 fix(docs-infra): fix resources page tabs text which is not visible on small screens (#35935)
on small mobile screens the top tab bar contains text which was not visible on small screens changed text size, margin and padding so that the text could be contained in these screens (320px to 480px)

PR Close #35935
2020-04-07 15:08:58 -07:00
b02c950aac docs(forms): Remove unnecessary repeating periods (#36474)
PR Close #36474
2020-04-07 11:35:44 -07:00
2eaa304aba test(language-service): remove ng-for-cases.ts (#36470)
This commit removes ng-for-cases.ts and moves all test cases to
inline expressions in TEST_TEMPLATE.

PR Close #36470
2020-04-07 11:34:39 -07:00
9faf2e4a96 test(language-service): remove ng-if-cases.ts (#36470)
This commit removes ng-if-cases.ts and moves all test cases to inline
expressions in TEST_TEMPLATE.

PR Close #36470
2020-04-07 11:34:39 -07:00
0e7a89a391 fix(router): state data missing in routerLink (#36462)
fixes #33173 - router state data is missing on routerLink when used
with non-anchor elements.

PR Close #36462
2020-04-07 11:31:26 -07:00
af4269471d fix(core): ngOnDestroy on multi providers called with incorrect context (#35840)
Currently destroy hooks are stored in memory as `[1, hook, 5, hook]` where
the numbers represent the index at which to find the context and `hook` is
the function to be invoked. This breaks down for `multi` providers,
because the value at the index will be an array of providers, resulting in
the hook being invoked with an array of all the multi provider values,
rather than the provider that was destroyed. In ViewEngine `ngOnDestroy`
wasn't being called for `multi` providers at all.

These changes fix the issue by changing the structure of the destroy hooks to `[1, hook, 5, [0, hook, 3, hook]]` where the indexes inside the inner array point to the provider inside of the multi provider array. Note that this is slightly different from the original design which called for the structure to be `[1, hook, 5, [hook, hook]`, because in the process of implementing it, I realized that we wouldn't get passing the correct context if only some of the `multi` providers have `ngOnDestroy` and others don't.

I've run the newly-added `view_destroy_hooks` benchmark against these changes and compared it to master. The difference seems to be insignificant (between 1% and 2% slower).

Fixes #35231.

PR Close #35840
2020-04-07 10:31:41 -07:00
915a6c17b9 test(language-service): delete expression-cases.ts (#36468)
This commit deletes `expression-cases.ts` and moves the test cases to
inline expressions in TEST_TEMPLATE.

PR Close #36468
2020-04-07 10:20:27 -07:00
93b32d3023 fix(ngcc): detect non-emitted, non-imported TypeScript helpers (#36418)
When TypeScript downlevels ES2015+ code to ES5, it uses some helper
functions to emulate some ES2015+ features, such as spread syntax. The
TypeScript compiler can be configured to emit these helpers into the
transpiled code (which is controlled by the `noEmitHelpers` option -
false by default). It can also be configured to import these helpers
from the `tslib` module (which is controlled by the `importHelpers`
option - false by default).

While most of the time the helpers will be either emitted or imported,
it is possible that one configures their app to neither emit nor import
them. In that case, the helpers could, for example, be made available on
the global object. This is what `@nativescript/angular`
v9.0.0-next-2019-11-12-155500-01 does. See, for example, [common.js][1].

Ngcc must be able to detect and statically evaluate these helpers.
Previously, it was only able to detect emitted or imported helpers.

This commit adds support for detecting these helpers if they are neither
emitted nor imported. It does this by checking identifiers for which no
declaration (either concrete or inline) can be found against a list of
known TypeScript helper function names.

[1]: https://unpkg.com/browse/@nativescript/angular@9.0.0-next-2019-11-12-155500-01/common.js

PR Close #36418
2020-04-07 10:19:22 -07:00
bd82b34f79 refactor(language-service): provide service for attribute binding type (#36301)
This commit refactors the process for determining the type of an Angular
attribute to be use a function that takes an attribute name and returns
the Angular attribute kind and name, rather than requiring the user to
query match the attribute name with the regex and query the matching
array.

This refactor prepares for a future change that will improve the
experience of completing attributes in `()`, `[]`, or `[()]` contexts.

PR Close #36301
2020-04-07 10:18:32 -07:00
c626e90ab0 ci: manually set available resources for bazel on windows CI (#36458)
PR Close #36458
2020-04-07 10:12:30 -07:00
c6bbb0d8e5 style(zone.js): fix lint errors after clang update (#36487)
Recent ZoneJS-related commit (416c786774) update the `promise.ts` file, but it looks like original PR was not rebased after clang update. As a result, the `lint` CircleCI job started to fail in master after merging that PR (https://github.com/angular/angular/pull/36311). This commit updates the format of the `promise.ts` script according to the new clang rules.

PR Close #36487
2020-04-07 10:10:34 -07:00
f48a065db0 style(forms): reformat of the forms package after clang update (#36466)
PR Close #36466
2020-04-07 09:47:09 -07:00
aeb6d0d0f7 fix(zone.js): should not try to patch fetch if it is not writable (#36311)
Close #36142

In Firefox extensions, the `window.fetch` is not configurable, that means

```
const desc = Object.getOwnPropertyDescriptor(window, 'fetch');
desc.writable === false;
```

So in this case, we should not try to patch `fetch`, otherwise, it will
throw error ('fetch is ReadOnly`)

PR Close #36311
2020-04-06 15:34:34 -07:00
655c9e386b build: update to latest version of husky (#36459)
PR Close #36459
2020-04-06 15:33:26 -07:00
2ad20b953f test(language-service): Move pipe tests to TEST_TEMPLATE (#36407)
This commit simplifies the completion tests for pipes by moving them to TEST_TEMPLATE.

PR Close #36407
2020-04-06 15:32:33 -07:00
7059eaf2d2 build(docs-infra): renamed e2e property of example-config.json to tests (#36143)
Each docs example has an `example-config.json` configuration file. Among
other things, this file can be used to specify what commands to run in
order to test the example. (If not specified, the `run-example-e2e.js`
script will run a default `yarn e2e` command.)

Previously, the property specifying the test commands was called `e2e`.
This is because in the past only e2e tests were run for docs examples.
Since recently, some examples may specify commands for other types of
tests (such as unit tests). Therefore, calling the property that holds
the list of test commands `e2e` no longer makes sense and can be
misleading to people looking at the configuration files.

This commit renamed the property to the more generic `tests`. In the
future, the `run-example-e2e.js` script (and corresponding npm script)
should be renamed and refactored to also avoid giving the impression
that only e2e tests are run.

Discussed in:
https://github.com/angular/angular/pull/36143#discussion_r395148379

PR Close #36143
2020-04-06 15:31:08 -07:00
939fda410f build(docs-infra): update docs examples package.json templates wrt core-js (#36143)
The `core-js` dependency is no longer included in `package.json` for
`cli`-type examples, but only for the `systemjs` ones. This commit
updates the `package.json` templates to reflect that (and also updates
the `npm-packages` guide accordingly).

PR Close #36143
2020-04-06 15:31:08 -07:00
35ab6acab3 test(docs-infra): fix unit tests and run them for specific docs examples on CI (#36143)
Previously, only e2e tests were run for docs examples on CI. As a
result, unit tests (which are included in the zipped archives we provide
for users to download and play with the examples locally) were often
outdated and broken.

This commit configures specific docs examples that have meaningful unit
tests to run them on CI (via the `run-example-e2e.js` script). Where
necessary, the unit tests are fixed to ensure they pass and reflect the
changes in the corresponding component/service.
This commit also removes some auto-generated unit tests that are not
meaningful (e.g. make trivial assertions, such that a component instance
is truthy) and are often broken anyway (e.g. because the corresponding
component has been changed in ways that make the tests fail).

PR Close #36143
2020-04-06 15:31:08 -07:00
f4ee067cb8 build(docs-infra): switch docs examples to Ivy (#36143)
The docs examples are switched to Ivy. On CI, the tests are run with
both Ivy and ViewEngine.

Partially addresses:
[FW-1609](https://angular-team.atlassian.net/browse/FW-1609)

PR Close #36143
2020-04-06 15:31:08 -07:00
068e28381d docs: fix broken guide link (tutorial/index --> tutorial) (#36453)
Replace broken link (`tutorial/index`) with `tutorial`.

PR Close #36453
2020-04-06 13:56:00 -07:00
dff52ecb11 fix(core): avoid migration error when non-existent symbol is imported (#36367)
In rare cases a project with configured `rootDirs` that has imports to
non-existent identifiers could fail in the migration.

This happens because based on the application code, the migration could
end up trying to resolve the `ts.Symbol` of such non-existent
identifiers. This isn't a problem usually, but due to a upstream bug
in the TypeScript compiler, a runtime error is thrown.

This is because TypeScript is unable to compute a relative path from the
originating source file to the imported source file which _should_
provide the non-existent identifier. An issue for this has been reported
upstream: https://github.com/microsoft/TypeScript/issues/37731. The
issue only surfaces since our migrations don't provide an absolute base
path that is used for resolving the root directories.

To fix this, we ensure that we never use relative paths when parsing
tsconfig files. More details can be found in the TS issue.

Fixes #36346.

PR Close #36367
2020-04-06 13:21:54 -07:00
56af303dc3 fix(ngcc): don't crash on cyclic source-map references (#36452)
The source-map flattening was throwing an error when there
is a cyclic dependency between source files and source-maps.
The error was either a custom one describing the cycle, or a
"Maximum call stack size exceeded" one.

Now this is handled more leniently, resulting in a partially loaded
source file (or source-map) and a warning logged.

Fixes #35727
Fixes #35757
Fixes https://github.com/angular/angular-cli/issues/17106
Fixes https://github.com/angular/angular-cli/issues/17115

PR Close #36452
2020-04-06 13:19:53 -07:00
136596ddb4 fix(ngcc): add process title (#36448)
Add process.title, so it's clearly in the task manager when ngcc is running

See: https://github.com/angular/angular/issues/36414#issuecomment-609644282

PR Close #36448
2020-04-06 13:19:18 -07:00
e515c913d9 ci: exclude ngcc directory from fw-compiler pull-approve rule (#35933)
ngcc has its own pull-approve group

PR Close #35933
2020-04-06 13:16:37 -07:00
31eaf78035 fix(ngcc): support ignoring deep-imports via package config (#36423)
Recently we added support for ignoring specified deep-import
warnings by providing sets of regular expressions within the
`ngcc.config.js` file. But this was only working for the project
level configuration.

This commit fixes ngcc so that it will also read these regular
expressions from package level configuration too.

Fixes #35750

PR Close #36423
2020-04-06 11:32:09 -07:00
b0d680daa0 fix(ngcc): support simple browser property in entry-points (#36396)
The `browser` package.json property is now supported to the same
level as `main` - i.e. it is sniffed for UMD, ESM5 and CommonJS.

The `browser` property can also contain an object with file overrides
but this is not supported by ngcc.

Fixes #36062

PR Close #36396
2020-04-06 11:31:10 -07:00
93cbef26c7 fix(ngcc): sniff main property for ESM5 format (#36396)
Previously, `main` was only checked for `umd` or `commonjs`
formats. Now if there are `import` or `export` statements in the
source file it will be deemed to be in `esm5` format.

Fixes #35788

PR Close #36396
2020-04-06 11:31:10 -07:00
e2b221b8fa refactor(language-service): reformat using clang-format (#36426)
clang-format was recently updated and any PRs that touch files in the
language service will have to reformat all the files.

Instead of changing the formatting in those PRs, this PR formats all
files in language-service package once and for all.

PR Close #36426
2020-04-06 09:29:43 -07:00
7a33c456d3 docs: add missing command in WebWorker guide (#36397)
The command was accidentally removed in #36383

PR Close #36397
2020-04-06 09:29:17 -07:00
61db817eed fix(dev-infra): use commit message validation from @angular/dev-infra-private (#36172)
Prior to this change we manage a local version of commit message validation
in addition to the commit message validation tool contained in the ng-dev
tooling.  By adding the ability to validate a range of commit messages
together, the remaining piece of commit message validation that is in the
local version is replicated.

We use both commands provided by the `ng-dev commit-message` tooling:
- pre-commit-validate: Set to automatically run on an git hook to validate
    commits as they are created locally.
- validate-range: Run by CI for every PR, testing that all of the commits
    added by the PR are valid when considered together.  Ensuring that all
    fixups are matched to another commit in the change.

PR Close #36172
2020-04-06 09:28:52 -07:00
4894220acf fix(compiler-cli): pass real source spans where they are empty (#31805)
Some consumers of functions that take `ParseSourceSpan`s currently pass
empty and incorrect source spans. This fixes those cases.

PR Close #31805
2020-04-06 09:28:27 -07:00
74b7a8eaf5 style(ngcc): reformat of ngcc after clang update (#36447)
PR Close #36447
2020-04-06 09:26:58 -07:00
bfa55162de docs: adding Doguhan Uluca as GDE resource (#34789)
PR Close #34789
2020-04-03 14:03:57 -07:00
776f991c1a build: update pullapprove config to better handle global approvals (#36384)
Beginning with this change, global approvals will now require the approver
to include `Reviewed-for: global-approvers`, and a docs global approval
requires `Reviewed-for: global-docs-approvers`.

Historically, providing a review by any member of the global reviewer
groups would automatically be considered a global review.  This change
enforces that global approval should be an intentional, explicit action.

The global-approvers and global-docs-approvers group will not be
requested as reviews by pullapprove.

PR Close #36384
2020-04-03 11:11:31 -07:00
d9d1a1e682 fix(dev-infra): correct pullapprove global approval regex (#36384)
PR Close #36384
2020-04-03 11:11:31 -07:00
867ff14a4a build: rebuild yarn lock from scratch (#36377)
Rebuild the yarn lock file from scratch to collapse instances where
one package is able to satisfy multiple dependencies.  Currently we
have some situations where we have multiple versions when one would
work.

Example:
```
"@babel/code-frame@^7.0.0":
  version "7.0.0"
  resolved "https://registry.yarnpkg.com/@babel/cod
  integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij
  dependencies:
    "@babel/highlight" "^7.0.0"

"@babel/code-frame@^7.5.5":
  version "7.5.5"
  resolved "https://registry.yarnpkg.com/@babel/cod
  integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQ
  dependencies:
    "@babel/highlight" "^7.0.0"

"@babel/code-frame@^7.8.3":
  version "7.8.3"
  resolved "https://registry.yarnpkg.com/@babel/cod
  integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0j
  dependencies:
    "@babel/highlight" "^7.8.3"
```

becomes

```
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3":
  version "7.8.3"
  resolved "https://registry.yarnpkg.com/@babel/cod
  integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0j
  dependencies:
    "@babel/highlight" "^7.8.3"
```

PR Close #36377
2020-04-03 11:09:18 -07:00
879457ca05 fix(ngcc): correctly detect imported TypeScript helpers (#36284)
The `NgccReflectionHost`s have logic for detecting certain known
declarations (such as `Object.assign()` and TypeScript helpers), which
allows the `PartialEvaluator` to evaluate expressions it would not be
able to statically evaluate otherwise.

In #36089, `DelegatingReflectionHost` was introduced, which delegates to
a TypeScript `ReflectionHost` when reflecting on TypeScript files, which
for ngcc's case means `.d.ts` files of dependencies. As a result, ngcc
lost the ability to detect TypeScript helpers imported from `tslib`,
because `DelegatingReflectionHost` was not able to apply the known
declaration detection logic while reflecting on `tslib`'s `.d.ts` files.

This commit fixes this by ensuring `DelegatingReflectionHost` calls the
`NgccReflectionHost`'s `detectKnownDeclaration()` method as necessary,
even when using the TypeScript `ReflectionHost`.

NOTE:
The previous commit exposed a bug in ngcc that was hidden due to the
tests' being inconsistent with how the `ReflectionHost`s are used in the
actual program. The changes in this commit are verified by ensuring the
failing tests are now passing (hence no new tests are added).

PR Close #36284
2020-04-03 11:08:46 -07:00
efbe4ad28a test(ngcc): use DelegatingReflectionHost for testing NgccReflectionHosts (#36284)
In #36089, `DelegatingReflectionHost` was introduced. Under the hood, it
delegates another `NgccReflectionHost` in order to reflect over the
program's source files, while using a different TypeScript
`ReflectionHost` to reflect over `.d.ts` files (which is how external
dependencies are represented in the program).

Previously, the `NgccReflectionHost`s were used directly in tests. This
does not exercise them in the way they are exercised in the actual
program, because (when used directly) they will also reflect on `.d.ts`
files too (instead of delegating to the TypeScript `ReflectionHost`).
This could hide bugs that would happen on the actual program.

This commit fixes this by using the `DelegatingReflectionHost` in the
various `NgccReflectionHost` tests.

NOTE:
This change will cause some of the existing tests to start failing.
These failures demonstrate pre-existing bugs in ngcc, that were hidden
due to the tests' being inconsistent with how the `ReflectionHost`s are
used in the actual program. They will be fixed in the next commit.

PR Close #36284
2020-04-03 11:08:46 -07:00
64f72c0600 refactor(ngcc): move logic for identifying known declarations to method (#36284)
The `NgccReflectionHost`s have logic for detecting certain known
declarations (such as `Object.assign()` and TypeScript helpers), which
allows the `PartialEvaluator` to evaluate expressions it would not be
able to statically evaluate otherwise.

This commit moves the logic for identifying these known declarations to
dedicated methods. This is in preparation of allowing ngcc's
`DelegatingReflectionHost` (introduced in #36089) to also apply the
known declaration detection logic when reflecting on TypeScript sources.

PR Close #36284
2020-04-03 11:08:46 -07:00
81c05bfa01 fix(benchpress): fix typings in lview_debug.ts (#36236)
* Installing & using benchpress was throwing typescript errors because typings were not defined for the getters in lview_debug.ts

PR Close #36236
2020-04-03 11:06:08 -07:00
56abcf088d docs: place download section in angular forms to the top (#36075)
This commit partially addresses #35459

PR Close #36075
2020-04-03 11:05:32 -07:00
6818432cd0 docs: place download section in angular forms validation to the top of the page (#36074)
This commit partially addresses #35459

PR Close #36074
2020-04-03 11:03:36 -07:00
79bfb037df docs: place download section in angular pipes to the top (#36073)
This commit partially addresses #35459

PR Close #36073
2020-04-03 11:01:08 -07:00
0fce20d79d docs: place download section to the top of the page (#36067)
Previously, the download link to the example for the angular element
guide was in the middle of the page. To make it easier for the user to
find the download link, it has been placed to the top of the page.

This commit partially addresses #35459

PR Close #36067
2020-04-03 11:00:26 -07:00
9f486c31d2 build: enable strictTemplates in AIO (#36391)
PR Close #36391
2020-04-02 10:53:13 -07:00
d36066dd6c build: sort module resolution warnings in ts-circular-deps tool (#36361)
For better overview of modules that cannot be resolved in the
`ts-circular-deps` tool, the warnings are now sorted.

Additionally, an empty line between fixed and new circular dependencies
is now printed. That should slightly help with distinguishing.

PR Close #36361
2020-04-02 10:52:47 -07:00
a631b99c69 feat(core): undecorated-classes migration should handle derived abstract classes (#35339)
In version 10, undecorated base classes that use Angular features need
to be decorated explicitly with `@Directive()`. Additionally, derived
classes of abstract directives need to be decorated.

The migration already handles this for undecorated classes that are
not explicitly decorated, but since in V9, abstract directives can be
used, we also need to handle this for explicitly decorated abstract
directives. e.g.

```
@Directive()
export class Base {...}

// needs to be decorated by migration when updating from v9 to v10
export class Wrapped extends Base {}

@Component(...)
export class Cmp extends Wrapped {}
```

PR Close #35339
2020-04-02 10:51:50 -07:00
78211c42ea test: add integration test for undecorated-classes-with-decorated-fields migration (#35339)
We don't have an integration test for the `undecorated-classes-with-decorated-fields
migration. For consistency and to cover for the latest changes, we add
it to the `ng update` integration test.

PR Close #35339
2020-04-02 10:51:50 -07:00
5ff5a11509 fix(core): undecorated-classes-with-decorated-fields migration does not decorate derived classes (#35339)
The `undecorated-classes-with-decorated-fields` migration has been
introduced with 904a2018e0, but misses
logic for decorating derived classes of undecorated classes which use
Angular features. Example scenario:

```ts
export abstract class MyBaseClass {
  @Input() someInput = true;
}

export abstract class BaseClassTwo extends MyBaseClass {}

@Component(...)
export class MyButton extends BaseClassTwo {}
```

Both abstract classes would need to be migrated. Previously, the migration
only added `@Directive()` to `MyBaseClass`, but with this change, it
also decorates `BaseClassTwo`.

This is necessary because the Angular Compiler requires `BaseClassTwo` to
have a directive definition when it flattens the directive metadata for
`MyButton` in order to perform type checking. Technically, not decorating
`BaseClassTwo` does not break at runtime.

We basically want to enforce consistent use of `@Directive` to simplify the
mental model. [See the migration guide](https://angular.io/guide/migration-undecorated-classes#migrating-classes-that-use-field-decorators).

Fixes #34376.

PR Close #35339
2020-04-02 10:51:49 -07:00
e80047e897 refactor(core): move schematic import manager to shared utils (#35339)
The import manager has been created for both the `missing-injectable`
and `undecorated-classes-with-di` migration. Both initial PRs brought
in the manager class, so the manager is duplicated in the schematics.

In order to reduce this duplication, and to expose the manager to other
schematics/migrations, we move it into the shared schematic utils.

PR Close #35339
2020-04-02 10:51:49 -07:00
eee5eeae76 refactor(core): move schematic base classes logic into shared utils (#35339)
Moves the `findBaseClassDeclarations` method into the shared
schematic utilities. This method will be useful for future
migrations, and for planned changes to the
`undecorated-classes-with-decorated-fields` migration.

PR Close #35339
2020-04-02 10:51:49 -07:00
c3ce1903b6 build: enable service-worker tests on saucelabs (#36129)
Enables the `service-worker` tests on Saucelabs and fixes some issues that were preventing them from running on IE. The issues were:
1. We were serving es2017 code during tests. I've set it to es5.
2. The check which was verifying whether the environment is supported ended up hitting a `require` call in the browser which caused it to fail on browsers that don't support the `URL` API.

PR Close #36129
2020-04-01 15:37:48 -07:00
21da0346c7 refactor(compiler): add @nocollapse annotation using a synthetic comment (#35932)
In Ivy, Angular decorators are compiled into static fields that are
inserted into a class declaration in a TypeScript transform. When
targeting Closure compiler such fields need to be annotated with
`@nocollapse` to prevent them from being lifted from a static field into
a variable, as that would prevent the Ivy runtime from being able to
find the compiled definitions.

Previously, there was a bug in TypeScript where synthetic comments added
in a transform would not be emitted at all, so as a workaround a global
regex-replace was done in the emit's `writeFile` callback that would add
the `@nocollapse` annotation to all static Ivy definition fields. This
approach is no longer possible when ngtsc is running as TypeScript
plugin, as a plugin cannot control emit behavior.

The workaround is no longer necessary, as synthetic comments are now
properly emitted, likely as of
https://github.com/microsoft/TypeScript/pull/22141 which has been
released with TypeScript 2.8.

This change is required for running ngtsc as TypeScript plugin in
Bazel's `ts_library` rule, to move away from the custom `ngc_wrapped`
approach.

Resolves FW-1952

PR Close #35932
2020-04-01 15:37:06 -07:00
af2d74fde9 docs(zone.js): fix typos and align formatting (#35907)
PR Close #35907
2020-04-01 15:35:37 -07:00
8de62a9811 docs: update and edit web-worker page (#36383)
PR Close #36383
2020-04-01 15:25:47 -07:00
26e28453d6 ci: use dev-infra cli from local sources (#36326)
Use dev-infra cli from local sources rather than loading
from @angular/dev-infra-private builds.

PR Close #36326
2020-04-01 15:24:29 -07:00
8ea30b662f feat(dev-infra): standard CLI commands using yargs (#36326)
Creates a standard model for CLI commands provided by ng-dev.
Allows for us to have any of the tools/scripts extend to be
included in the ng-dev command, or be standalone using the same
yargs parser.

PR Close #36326
2020-04-01 15:24:29 -07:00
cb0a2a055d fix(ngcc): allow ngcc configuration to match pre-release versions of packages (#36370)
Ngcc supports providing a project-level configuration to affect how
certain dependencies are processed and also has a built-in fallback
configuration for some unmaintained packages. Each entry in these
configurations could be scoped to specific versions of a package by
providing a version range. If no version range is provided for a
package, it defaults to `*` (with the intention of matching any
version).

Previously, the installed version of a package was tested against the
version range using the [semver][1] package's `satisfies()` function
with the default options. By default, `satisfies()` does not match
pre-releases (see [here][2] for more details on reasoning). While this
makes sense when determining what version of a dependency to install
(trying to avoid unexpected breaking changes), it is not desired in the
case of ngcc.

This commit fixes it by explicitly specifying that pre-release versions
should be matched normally.

[1]: https://www.npmjs.com/package/semver
[2]: https://github.com/npm/node-semver#prerelease-tags

PR Close #36370
2020-04-01 13:32:32 -07:00
14ae3c0a21 fix(platform-server): update xhr2 dependency (#36366)
Previous versions of xhr2 used the depreciated "new Buffer()".

Closes #36358

PR Close #36366
2020-04-01 13:31:39 -07:00
90cae34c05 fix(compiler): avoid undefined expressions in holey array (#36343)
From G3 bug ID: 116443331

The View Engine compiler crashes when it tries to compile a test in JIT mode
that includes the d3-scale-chromatic library [1]. The d3 package initializes
some arrays using the following pattern:

```js
export var scheme = new Array(3).concat(
  "d8b365f5f5f55ab4ac",
  // ... more entries
).map(colors);
```

The stack trace from the crash is as follows:

```
TypeError: Cannot read property 'visitExpression' of undefined
    at ../../../third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts:505:39
    at JitEmitterVisitor.AbstractEmitterVisitor.visitAllObjects third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=526
    at JitEmitterVisitor.AbstractEmitterVisitor.visitAllExpressions third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=505
    at JitEmitterVisitor.AbstractEmitterVisitor.visitLiteralArrayExpr third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=484
    at LiteralArrayExpr.visitExpression third_party/javascript/angular2/rc/packages/compiler/src/output/output_ast.ts?l=791
    at ../../../third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts:492:19
    at JitEmitterVisitor.AbstractEmitterVisitor.visitAllObjects third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=526
    at JitEmitterVisitor.AbstractEmitterVisitor.visitLiteralMapExpr third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=490
    at LiteralMapExpr.visitExpression third_party/javascript/angular2/rc/packages/compiler/src/output/output_ast.ts?l=819
    at ../../../third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts:505:39
    at JitEmitterVisitor.AbstractEmitterVisitor.visitAllObjects third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=526
    at JitEmitterVisitor.AbstractEmitterVisitor.visitAllExpressions third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=505
    at JitEmitterVisitor.AbstractEmitterVisitor.visitInvokeFunctionExpr third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_emitter.ts?l=318
    at JitEmitterVisitor.AbstractJsEmitterVisitor.visitInvokeFunctionExpr third_party/javascript/angular2/rc/packages/compiler/src/output/abstract_js_emitter.ts?l=112
    at InvokeFunctionExpr.visitExpression third_party/javascript/angular2/rc/packages/compiler/src/output/output_ast.ts?l=440
```

This is because the corresponding output AST for the array is of the form

```ts
[
  undefined,
  undefined,
  undefined,
  o.LiteralExpr,
  // ...
]
```

The output AST is clearly malformed and breaks the type representation of
`LiteralArrayExpr` in which every entry is expected to be of type `Expression`.

This commit fixes the bug by using a plain `for` loop to iterate over the
entire length of the holey array and convert undefined elements to
`LiteralExpr`.

[1]: https://github.com/d3/d3-scale-chromatic/blob/master/src/diverging/BrBG.js

PR Close #36343
2020-04-01 13:31:15 -07:00
7bb35889fa fix(ngcc): handle bad path mappings when finding entry-points (#36331)
Previously, a bad baseUrl or path mapping passed to an `EntryPointFinder`
could cause the original `sourceDirectory` to be superceded by a higher
directory. This could result in none of the sourceDirectory entry-points being
processed.

Now missing basePaths computed from path-mappings are discarded with
a warning. Further, if the `baseUrl` is the root directory then a warning is
given as this is most likely an error in the tsconfig.json.

Resolves #36313
Resolves #36283

PR Close #36331
2020-04-01 13:30:46 -07:00
03a3c753bb docs: fix typo in Tests guide (#36330)
Fixing typo in testing.md

PR Close #36330
2020-04-01 13:30:16 -07:00
74b1464dff feat(dev-infra): add support for new global approvers in pullapprove (#36324)
Pullapprove as added a few new features to allow for us to better
execute our expectation for global approvals. We need to allow for
an expectation that our global approver groups are not in the list
of approved groups. Additionally, since approval groups apply to
all files in the repo, the global approval groups also do not have
conditions defined for them, which means pullapprove verification
need to allow for no conditions need to be defined.

PR Close #36324
2020-04-01 13:25:48 -07:00
ff523c9ec9 fix(language-service): infer type of elements of array-like objects (#36312)
Currently the language service only provides support for determining the
type of array-like members when the array-like object is an `Array`.
However, there are other kinds of array-like objects, including
`ReadonlyArray`s and `readonly`-property arrays. This commit adds
support for retrieving the element type of arbitrary array-like objects.

Closes #36191

PR Close #36312
2020-04-01 13:24:53 -07:00
392ef93c2b fix(ngcc): handle entry-points within container folders (#36305)
The previous optimizations in #35756 to the
`DirectoryWalkerEntryPointFinder` were over zealous
with regard to packages that have entry-points stored
in "container" directories in the package, where the
container directory was not an entry-point itself.

Now we will also walk such "container" folders as long
as they do not contain `.js` files, which we regard as an
indicator that the directory will not contain entry-points.

Fixes #36216

PR Close #36305
2020-04-01 13:20:52 -07:00
29dcb5bf68 refactor(ngcc): simplify DirectoryWalkerEntryPointFinder (#36305)
This commit simplifies the `DirectoryWalkerEntryPointFinder` inter-method
calling to make it easier to follow, and also to support controlling
walking of a directory based on its children.

PR Close #36305
2020-04-01 13:20:52 -07:00
de594ca221 refactor(ngcc): rename INVALID_ENTRY_POINT to INCOMPATIBLE_ENTRY_POINT (#36305)
This name better reflects its meaning.

PR Close #36305
2020-04-01 13:20:52 -07:00
3545a49a79 fix(benchpress): update dependencies (#36205)
* updated, added, and removed dependencies in package.json
* added dependencies to BUILD.bazel

PR Close #36205
2020-04-01 13:19:33 -07:00
f4681b6e40 build: update to clang 1.4.0 and only run clang format on changed files (#36203)
Update to clang@1.4.0 to gain support for optional changing and nullish
coalescing.  Because this would trigger a change on >1800 files in the
repository, also changes our format enforcement to only be run against
changed files.  This will allow us to incramentally roll out the value
add of the upgraded clang format.

PR Close #36203
2020-04-01 13:18:09 -07:00
fb16557381 docs: update and edit web-worker page (#36026)
PR Close #36026
2020-04-01 13:17:26 -07:00
61c9da5542 docs: make page titles and toc task-oriented (#36024)
PR Close #36024
2020-04-01 13:16:48 -07:00
dbb1eb0ffe docs: change page title and minor edit (#36021)
PR Close #36021
2020-04-01 13:16:19 -07:00
002b81c0b0 docs: update ng-conf announcement to remove livestream link (#36382)
Since the livestream for ng-conf is not public this year,
(and is only available to ng-conf attendees), we are
removing the link from the angular.io homepage.

Instead, we are now pointing to the ng-conf homepage for
more information.

PR Close #36382
2020-04-01 13:14:42 -07:00
beb3f19dc0 docs: update end date of survey to middle of April (#36339)
PR Close #36339
2020-04-01 11:10:58 -07:00
e8f3c47b03 build(docs-infra): rename duplicate test name (#36348)
When running `docs-test` for the docs generation, a warning is printed for a
duplicate test. This commit fixes this issue.

PR Close #36348
2020-03-31 11:06:16 -07:00
c58e6ba13a build: don't use deprecated $(location) pre-declared variable (#36308)
$(location) is not recommended in the bazel docs as depending on context it will either return the value of $(execpath) or $(rootpath). rules_nodejs now supports $(rootpath) and $(execpath) in templated_args of nodejs_binary.

PR Close #36308
2020-03-31 11:02:57 -07:00
61e5ab4703 fix(zone.js): fix 2 bluebird test cases for each/mapSeries (#36295)
`Bluebird.each` and `Bluebird.mapSeries` will accept a callback with `value` parameter,
the `value` should be the item in the array, not array itself.

For example:
```
const arr = [1, 2];
Bluebird.each(arr, function(value, idx) {
  console.log(`value: ${value}, idx: ${idx}`);
})
```

the output will be
```
value: 1, idx: 0
value: 2, idx: 1
```

This PR fix the test cases for `each` and `mapSeries` APIs.

PR Close #36295
2020-03-31 10:59:56 -07:00
10385c27da build(zone.js): update zone.js version to 0.10.3 (#36214)
PR Close #36214
2020-03-31 10:59:18 -07:00
0ab244f39e docs: fix typo in Schematics guide (#36328)
Fixing typo in schematics-for-libraries.md
PR Close #36328
2020-03-30 15:32:05 -07:00
eafa5260db refactor(core): use more narrow QueryList import to avoid circular deps issue (#36286)
Prior to this commit, the `packages/core/src/render3/interfaces/query.ts` file used to import `QueryList` using `../../linker`, which contains a lot of re-exports and as a result, this one import caused a lot of circular deps cycles reported by the tool that checks such deps. In other places in the code the `QueryList` is imported using more narrow import (`linker/query_list`), so this commit uses the same pattern. This change allowed to reduce the number of known cycles from 343 to 207, the golden file was updated accordingly.

PR Close #36286
2020-03-30 15:31:39 -07:00
15cffa210c build: update to rules_nodejs 1.5.0 (#36307)
### New stuff

* The `ts_project` rule is a simple wrapper around the TypeScript compiler, `tsc`. This is an alternative to `ts_library` but not a replacement. Read more about the trade-offs at https://bazelbuild.github.io/rules_nodejs/TypeScript#alternatives or read the [API docs](https://bazelbuild.github.io/rules_nodejs/TypeScript#ts_project)
* `pkg_npm` can now be used as a dependency within your repo as well as for publishing to npm. It provides a `LinkablePackageInfo` which is our internal API to pass package name/path to downstream compilations, essentially providing the "Lerna" feature.
* There is experimental support for Bazel's "worker mode" in `rollup_bundle`, which essentially puts Rollup in watch mode. Add the `supports_workers = True` attribute to opt-in.
* Better support for [pre-defined label variables](https://docs.bazel.build/versions/master/be/make-variables.html#predefined_label_variables) like `$(rootpath)` and `$(execpath)` - we no longer recommend using `$(location)` at all.

See release notes https://github.com/bazelbuild/rules_nodejs/releases/tag/1.5.0 for more info.

PR Close #36307
2020-03-30 11:25:17 -07:00
df8e45a193 docs: fix typo in Dependency Injection guide (#36304)
Fixing typo in dependency-injection-navtree.md

PR Close #36304
2020-03-30 11:14:55 -07:00
bb8744db94 fix(ngcc): do not write entry-point manifest outside node_modules (#36299)
Fixes #36296

PR Close #36299
2020-03-30 11:03:26 -07:00
43bad87ae1 refactor(ngcc): rename workerCount to maxWorkerCount (#36298)
Now that we spawn workers lazily as needed, this private property is
really the upper limit on how many workers we might spawn.

PR Close #36298
2020-03-30 11:02:53 -07:00
c3b297a318 refactor(dev-infra): fix lint warnings for Pullapprove-related scripts (#36287)
The `dev-infra` scripts were added to the list of sources that should be verified with clang (b07b6edc2a), but the Pullapprove-related scripts that were merged before (83e4a76afa) doesn't pass these checks. This commit updates a couple scripts to have a proper formatting.

PR Close #36287
2020-03-28 23:21:12 -07:00
6ea232eb3c fix(ngcc): do not spawn more processes than intended in parallel mode (#36280)
When running in parallel mode, ngcc spawns multiple worker processed to
process the various entry-points. The number of max allowed processes is
determined by the number of CPU cores available to the OS. There is also
currently an [upper limit of 8][1]. The number of active workers is in
turn inferred by the number of [task assignments][2].

In the past, counting the entries of `ClusterMaster#taskAssignments` was
enough, because worker processes were spawned eagerly at the beginning
and corresponding entries were created in `taskAssignments`.
Since #35719 however, worker processes are spawned lazily on an as
needed basis. Because there is some delay between
[spawning a process][3] and [inserting it][4] into `taskAssignments`,
there is a short period of time when `taskAssignment.size` does not
actually represent the number of spawned processes. This can result in
spawning more than `ClusterMaster#workerCount` processes.

An example of this can be seen in #36278, where the debug logs indicate
9 worker processes had been spawned (`All 9 workers are currently busy`)
despite the hard limit of 8.

This commit fixes this by using `cluster.workers` to compute the number
of spawned worker processes. `cluster.workers` is updated synchronously
with `cluster.fork()` and thus reflects the number of spawned workers
accurately at all times.

[1]: https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429
[2]: https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108
[3]: https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110
[4]: https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199

PR Close #36280
2020-03-27 14:12:28 -07:00
0baab0f3db docs: update release nots with blog post (#36268)
PR Close #36268
2020-03-27 11:18:23 -07:00
e53b686375 fix(ngcc): correctly identify the package path of secondary entry-points (#36249)
Previously we only searched for package paths below the set of `basePaths`
that were computed from the `basePath` provided to ngcc and the set of
`pathMappings`.

In some scenarios, such as hoisted packages, the entry-point is not within
any of the `basePaths` identified above. For example:

```
project
  packages
    app
      node_modules
        app-lib (depends on lib1)
  node_modules
    lib1 (depends on lib2)
      node_modules
        lib2 (depends on lib3/entry-point)
    lib3
      entry-point
```

When CLI is compiling `app-lib` ngcc will be given
`project/packages/app/node_modules` as the `basePath.

If ngcc is asked to target `lib2`, the `targetPath` will be
`project/node_modules/lib1/node_modules/lib2`.

Since `lib2` depends upon `lib3/entry-point`, ngcc will need to compute
the package path for `project/node_modules/lib3/entry-point`.

Since `project/node_modules/lib3/entry-point` is not contained in the `basePath`
`project/packages/app/node_modules`, ngcc failed to compute the `packagePath`
correctly, instead assuming that it was the same as the entry-point path.

Now we also consider the nearest `node_modules` folder to the entry-point
path as an additional `basePath`. If one is found then we use the first
directory directly below that `node_modules` directory as the package path.

In the case of our example this extra `basePath` would be `project/node_modules`
which allows us to compute the `packagePath` of `project/node_modules/lib3`.

Fixes #35747

PR Close #36249
2020-03-27 11:17:45 -07:00
5c28af0e74 fix(core): run APP_INITIALIZERs before accessing LOCALE_ID token in Ivy TestBed (#36237)
Prior to this commit, Ivy TestBed was accessing locale ID before `APP_INITIALIZER` functions were called. This execution order is not consistent with the app bootstrap logic in `application_ref.ts`. This commit updates Ivy TestBed execution order to call initializers first (since they might affect `LOCALE_ID` token value) and accessing and setting locale ID after that.

Fixes #36230.

PR Close #36237
2020-03-27 11:16:57 -07:00
60adc35d30 build: fix bad pullapprove rule (#36232)
The dev-infra pull approve group contained a condition with
the glob having a string concatenation rather than a comma
separated list. This corrects this error, and in another PR
we will correct the verification scripts failure to catch
this.

PR Close #36232
2020-03-27 11:15:39 -07:00
024cc3b84a build: allow custom module resolution for ts-circular-deps tests (#36226)
Currently the `ts-circular-deps` tool uses a hard-coded module resolver
that only works in the `angular/angular` repository.

If the tool is consumed in other repositories through the shared
dev-infra package, the module resolution won't work, and a few
resolvable imports (usually cross-entry-points) are accidentally
skipped. For each test, the resolution might differ, so tests can
now configure their module resolution in a configuration file.

Note that we intentionally don't rely on tsconfig's for module
resolution as parsing their mappings rather complicates the
circular dependency tool. Additionally, not every test has a
corresponding tsconfig file.

Also, hard-coding mappings to `@angular/*` while accepting a
path to the packages folder would work, but it would mean
that the circular deps tool is no longer self-contained. Rather,
and also for better flexibility, a custom resolver should be
specified.

PR Close #36226
2020-03-27 11:14:49 -07:00
ac17142001 build: add dev-infra to clang format sources to format (#36204)
PR Close #36204
2020-03-27 11:13:17 -07:00
8d7066223f feat(dev-infra): handle excluding files via globs in pullapprove (#36162)
Updates the pullapprove verification script to handle
cases of excluding globs from groups.

PR Close #36162
2020-03-27 11:12:49 -07:00
0cb3c04128 docs: correct a misleading sentence (#36155) (#36158)
Fixes #36155

PR Close #36158
2020-03-27 10:49:31 -07:00
2649794e65 docs: clarify observables doc with new titles and tooltips (#36023)
PR Close #36023
2020-03-27 10:49:03 -07:00
bf426c5f0d build(docs-infra): do not include CI-specific config in docs examples ZIP archives (#36018)
In #35381, a new Protractor config file was introduced in docs examples,
`protractor-puppeteer.conf.js`, that was only supposed to be used on CI
and not be shipped with the ZIP archives provided for users to download
and experiment with the docs examples locally.

The logic to ignore the `protractor-puppeteer.conf.js` file was
incorrect, resulting in the file being retained in some examples (e.g.
[universal][1]). The problem was not immediately obvious, because most
examples explicitly specify all `**/*.js` files as ignored, but for
other examples the file was retained in the ZIP archive.

This commit fixes the logic to ensure the file is excluded from all docs
examples ZIP archives.

[1]: https://v9.angular.io/generated/zips/universal/universal.zip

PR Close #36018
2020-03-27 10:48:30 -07:00
aa6b9f0382 build(docs-infra): remove obsolete properties from zipper.json files (#36018)
The `removeSystemJsConfig` and `type` properties (present in some
`zipper.json` files) are now obsolete and are not taken into account by
the example zipper:
- `removeSystemJsConfig` is no longer relevant since most examples have
  been migrated to use the CLI.
- `type` is no longer relevant, because the project type is determined
  based on the `projectType` property in `example-config.json` files.

This commit removes these properties from `zipper.json` files and
updates the `example-zipper` docs to not mention them.

PR Close #36018
2020-03-27 10:48:30 -07:00
822b420a11 docs: update glossary defs for components, templates, and views (#35559)
PR Close #35559
2020-03-27 10:46:49 -07:00
294c6297d7 docs: coalesce release notes for the v9.1.0 release (#36247)
Merge all of the release notes across all v9.1.0 releases into a single one.

PR Close #36247
2020-03-25 14:13:17 -07:00
58fc65d198 build: ensure that refs and shas for PRs only need to be requested once (#36207)
This is done by requesting the refs and shas for the PR when the
env.sh script is run.  Additionally, the env.sh script is now setup
to write all of the environment variables created to a cache file
and subsequent loads of the environment load the values from there.

The get-refs-and-shas-for-target.js script now also first attempts
to load the refs and shas from an environment variable before
falling back to requesting from github via the API.

PR Close #36207
2020-03-25 11:49:42 -07:00
b6bd8d7572 release: cut the v9.1.0 release 2020-03-25 09:23:48 -07:00
b08168bb90 release: cut the v9.1.0-rc.2 release 2020-03-24 15:50:38 -07:00
407fa42679 fix(common): let KeyValuePipe accept type unions with null (#36093)
`KeyValuePipe` currently accepts `null` values as well as `Map`s and a
few others. However, due to the way in which TS overloads work, a type
of `T|null` will not be accepted by `KeyValuePipe`'s signatures, even
though both `T` and `null` individually would be.

To make this work, each signature that accepts some type `T` has been
duplicated with a second one below it that accepts a `T|null` and
includes `null` in its return type.

Fixes #35743

PR Close #36093
2020-03-24 14:41:42 -07:00
aef432384a fix(ngcc): use preserve whitespaces from tsconfig if provided (#36189)
Previously ngcc never preserved whitespaces but this is at odds
with how the ViewEngine compiler works. In ViewEngine, library
templates are recompiled with the current application's tsconfig
settings, which meant that whitespace preservation could be set
in the application tsconfig file.

This commit allows ngcc to use the `preserveWhitespaces` setting
from tsconfig when compiling library templates. One should be aware
that this disallows different projects with different tsconfig settings
to share the same node_modules folder, with regard to whitespace
preservation. But this is already the case in the current ngcc since
this configuration is hard coded right now.

Fixes #35871

PR Close #36189
2020-03-24 14:25:06 -07:00
fb70083339 feat(compiler): add dependency info and ng-content selectors to metadata (#35695)
This commit augments the `FactoryDef` declaration of Angular decorated
classes to contain information about the parameter decorators used in
the constructor. If no constructor is present, or none of the parameters
have any Angular decorators, then this will be represented using the
`null` type. Otherwise, a tuple type is used where the entry at index `i`
corresponds with parameter `i`. Each tuple entry can be one of two types:

1. If the associated parameter does not have any Angular decorators,
   the tuple entry will be the `null` type.
2. Otherwise, a type literal is used that may declare at least one of
   the following properties:
   - "attribute": if `@Attribute` is present. The injected attribute's
   name is used as string literal type, or the `unknown` type if the
   attribute name is not a string literal.
   - "self": if `@Self` is present, always of type `true`.
   - "skipSelf": if `@SkipSelf` is present, always of type `true`.
   - "host": if `@Host` is present, always of type `true`.
   - "optional": if `@Optional` is present, always of type `true`.

   A property is only present if the corresponding decorator is used.

   Note that the `@Inject` decorator is currently not included, as it's
   non-trivial to properly convert the token's value expression to a
   type that is valid in a declaration file.

Additionally, the `ComponentDefWithMeta` declaration that is created for
Angular components has been extended to include all selectors on
`ng-content` elements within the component's template.

This additional metadata is useful for tooling such as the Angular
Language Service, as it provides the ability to offer suggestions for
directives/components defined in libraries. At the moment, such
tooling extracts the necessary information from the _metadata.json_
manifest file as generated by ngc, however this metadata representation
is being replaced by the information emitted into the declaration files.

Resolves FW-1870

PR Close #35695
2020-03-24 14:21:42 -07:00
c9c2408176 docs(elements): correct typo in custom elements image (#36090)
Fixes #36050

PR Close #36090
2020-03-24 10:36:11 -07:00
e066bddfe9 fix(elements): correctly handle setting inputs to undefined (#36140)
Previously, when an input property was initially set to `undefined` it
would not be correctly recognized as a change (and trigger
`ngOnChanges()`).

This commit ensures that explicitly setting an input to `undefined` is
correctly handled the same as setting the property to any other value.
This aligns the behavior of Angular custom elements with that of the
corresponding components when used directly (not as custom elements).

PR Close #36140
2020-03-24 10:29:33 -07:00
447a600477 fix(elements): correctly set SimpleChange#firstChange for pre-existing inputs (#36140)
Previously, when an input property was set on an `NgElement` before
instantiating the underlying component, the `SimpleChange` object passed
to `ngOnChanges()` would have `firstChange` set to false, even if this
was the first change (as far as the component instance was concerned).

This commit fixes this by ensuring `SimpleChange#firstChange` is set to
true on first change, regardless if the property was set before or after
instantiating the component. This alignthe behavior of Angular custom
elements with that of the corresponding components when used directly
(not as custom elements).

Jira issue: [FW-2007](https://angular-team.atlassian.net/browse/FW-2007)

Fixes #36130

PR Close #36140
2020-03-24 10:29:33 -07:00
70f9bfff43 docs: fix typo in testing component with dependencies (#36219)
Fixes #36210

PR Close #36219
2020-03-24 10:19:48 -07:00
57c02b044c build(docs-infra): upgrade cli command docs sources to 526c3cc37 (#36225)
Updating [angular#9.1.x](https://github.com/angular/angular/tree/9.1.x) from [cli-builds#9.1.x](https://github.com/angular/cli-builds/tree/9.1.x).

##
Relevant changes in [commit range](1bc653bac...526c3cc37):

**Modified**
- help/deploy.json
- help/doc.json
- help/e2e.json
- help/generate.json
- help/test.json
- help/update.json

PR Close #36225
2020-03-24 10:18:14 -07:00
6defe962c8 fix(ngcc): use path-mappings from tsconfig in dependency resolution (#36180)
When computing the dependencies between packages which are not in
node_modules, we may need to rely upon path-mappings to find the path
to the imported entry-point.

This commit allows ngcc to use the path-mappings from a tsconfig
file to find dependencies. By default any tsconfig.json file in the directory
above the `basePath` is loaded but it is possible to use a path to a
specific file by providing the `tsConfigPath` property to mainNgcc,
or to turn off loading any tsconfig file by setting `tsConfigPath` to `null`.
At the command line this is controlled via the `--tsconfig` option.

Fixes #36119

PR Close #36180
2020-03-24 10:16:13 -07:00
267bcb3e9c test(common): Add test for NgForOfContext.count (#36046)
`NgForOfContext.count` is the length of the iterable.

PR Close #36046
2020-03-24 10:15:12 -07:00
b0b66881b4 docs(common): Add missing entry for NgForOfContext.count (#36046)
`count` is available in `NgForOfContext` but it's missing in the docs.

PR Close #36046
2020-03-24 10:15:12 -07:00
9ff8d78bcd test(core): re-enable IE 10/11 test on SauceLabs (#35962)
I was not able to reproduce IE 10/11 failrue of the disabled
tests on SauceLabs any more. I did some cleanup of the test
in question but I doubt it was the root cause of the problem.

PR Close #35962
2020-03-24 10:14:48 -07:00
563b707497 fix(dev-infra): use @angular/dev-infra-private package for pullapprove verification (#35996)
Adds devDependency on @angular/dev-infra-private and removes the verify script
from tools, relying instead on the script from ng-dev.

PR Close #35996
2020-03-24 10:14:06 -07:00
5357e643b3 release: cut the v9.1.0-rc.1 release 2020-03-23 13:01:11 -07:00
f71d132f7c fix(core): workaround Terser inlining bug (#36200)
This variable name change works around https://github.com/terser/terser/issues/615, which was causing the JIT production tests to fail in the Angular CLI repository (https://github.com/angular/angular-cli/issues/17264).

PR Close #36200
2020-03-23 12:24:03 -07:00
ba3edda230 fix(docs-infra): change app-list-item to app-item-list (#35601)
The `app-list-item` component sounds like it is used for a single
item, however it renders a list of items. There were also
several changes in the documentation, where it was becoming
confusing if the `app-list-item` is using a single item or multiple
items. This commit fixes this issue. It renames the component and its
respective properties to make sure that the intention is very clear.

Closes #35598

PR Close #35601
2020-03-23 11:40:16 -07:00
0767d37c07 fix(localize): allow ICU expansion case to start with any character except } (#36123)
Previously, an expansion case could only start with an alpha numeric character.
This commit fixes this by allowing an expansion case to start with any character
except `}`.

The [ICU spec](http://userguide.icu-project.org/formatparse/messages) is pretty vague:

> Use a "select" argument to select sub-messages via a fixed set of keywords.

It does not specify what can be a "keyword" but from looking at the surrounding syntax it
appears that it can indeed be any string that does not contain a `}` character.

Closes #31586

PR Close #36123
2020-03-23 11:37:12 -07:00
8ba24578bc fix(dev-infra): change circular deps positional params to camelCase (#36165)
Changes the positional params for the circular deps tooling to
use camelCase as it requires being defined in camelCase while
in strict mode.  Additionally, remove the `version()` call as
the boolean arguement does not exist in current versions and
throws errors on execution.

PR Close #36165
2020-03-23 11:36:28 -07:00
133a97ad67 fix(dev-infra): prep ts-circular-deps to load via node_modules (#36165)
to run ts-circular-deps via installed node_modules, we needed to set
the hashbang of the script to be a node environment, and discover the
project directory based on where the script is run rather than the
scripts file location.

PR Close #36165
2020-03-23 11:36:28 -07:00
4e67a3ab3f docs: Add asterisk info in template syntax guide (#36176)
Add helpful alert for asterisk syntax in the `ngFor` section of template syntax guide

PR Close #36176
2020-03-23 11:36:09 -07:00
377f0010fc docs: Change important alert of ngFor (#36176)
Update the important alert of ngFor so that it has a unique format with that of ngIf

PR Close #36176
2020-03-23 11:36:09 -07:00
6e09129e4c docs(elements): Edge supports Web Components (#36182)
PR Close #36182
2020-03-23 11:35:49 -07:00
d80e51a6b1 build(docs-infra): upgrade cli command docs sources to 1bc653bac (#36160)
Updating [angular#9.1.x](https://github.com/angular/angular/tree/9.1.x) from [cli-builds#9.0.x](https://github.com/angular/cli-builds/tree/9.0.x).

##
Relevant changes in [commit range](6aa3c134c...1bc653bac):

**Modified**
- help/deploy.json
- help/doc.json
- help/generate.json
- help/test.json
- help/update.json

PR Close #36160
2020-03-20 13:58:57 -07:00
feb66b00da docs(zone.js): Typos on zone.md file and fixes on code examples. (#36138)
1. During reading the documentation I found some code examples that were refering to the class properties via methods, but without specifying the context `this`.
2. The 'onInvoke' hook was duplicated
3. A minor typo on `Zones and execution contexts` section
4. A minor typo on `Zones and async lifecycle hooks` section

PR Close #36138
2020-03-20 13:57:01 -07:00
cb19eac105 fix(docs-infra): include correct dependencies in StackBlitz examples (#36071)
Previously, all StackBlitz examples included the default dependencies
for `cli`-type projects. However, different example types may have
different `package.json` files with different dependencies.
For example, the [boilerplate `package.json`][1] for `elements` examples
includes an extra dependency on `@angular/elements`.

This commit changes `StackblitzBuilder` to use the dependencies that
correspond to each example type.
(NOTE: Manually verified the changes.)

Jira issue: [FW-2002][2]

[1]: https://github.com/angular/angular/blob/05d058622/aio/tools/examples/shared/boilerplate/elements/package.json
[2]: https://angular-team.atlassian.net/browse/FW-2002

PR Close #36071
2020-03-20 13:56:26 -07:00
6e0564ade6 refactor(docs-infra): clean up stackblitz-builder/builder.js script (#36071)
- Remove unused dependencies.
- Change `var` to `const/let`.
- Change regular functions as callbacks to arrow functions.
- Remove unnecessary intermediate variables.
- Switch from custom `_existsSync()` implementation to built-in
  `fs.existsSync()`.

PR Close #36071
2020-03-20 13:56:26 -07:00
05eeb7d279 test(compiler): remove whitespace in spans (#36169)
https://github.com/angular/angular/pull/36133 and https://github.com/angular/angular/pull/35986
caused a conflict in test after they both got merged to master.
This PR fixes the failed tests.

PR Close #36169
2020-03-20 12:52:31 -07:00
2ce5fa3cce feat(compiler): Propagate value span of ExpressionBinding to ParsedProperty (#36133)
This commit propagates the correct value span in an ExpressionBinding of
a microsyntax expression to ParsedProperty, which in turn porpagates the
span to the template ASTs (both VE and Ivy).

PR Close #36133
2020-03-20 10:21:11 -07:00
e140cdcb34 fix(docs-infra): fix image name in example (#36127)
Closes #35618

PR Close #36127
2020-03-20 10:20:36 -07:00
14b2db1d43 feat(dev-infra): create commit-message validation script/tooling (#36117)
PR Close #36117
2020-03-20 10:20:13 -07:00
2afc7e982e refactor(benchpress): delete broken code (#35922)
PR Close #35922
2020-03-20 10:19:49 -07:00
785 changed files with 10582 additions and 9827 deletions

View File

@ -236,7 +236,7 @@ jobs:
git config user.name "angular-ci"
git config user.email "angular-ci"
# Rebase PR on top of target branch.
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
node tools/rebase-pr.js
else
echo "This build is not over a PR, nothing to do."
fi
@ -278,7 +278,8 @@ jobs:
- run: 'yarn bazel:lint ||
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
- run: yarn -s lint --branch $CI_GIT_BASE_REVISION
- run: yarn -s tslint
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
- run: yarn -s ts-circular-deps:check
- run: yarn -s ng-dev pullapprove verify
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE

View File

@ -22,6 +22,7 @@ else
####################################################################################################
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
####################################################################################################
setPublicVar CI "$CI"
setPublicVar PROJECT_ROOT "$projectDir";
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
# This is the branch being built; e.g. `pull/12345` for PR builds.
@ -36,9 +37,8 @@ else
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
# Store a PR's refs and shas so they don't need to be requested multiple times.
setPublicVar GITHUB_REFS_AND_SHAS $(node tools/utils/get-refs-and-shas-for-target.js ${CIRCLE_PR_NUMBER:-false} | awk '{ gsub(/"/,"\\\"") } 1');
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME";
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
####################################################################################################
@ -82,7 +82,7 @@ else
setPublicVar COMPONENTS_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
setPublicVar COMPONENTS_REPO_COMMIT "598db096e668aa7e9debd56eedfd127b7a55e371"
# Save the created BASH_ENV into the bash env cache file.
cat "$BASH_ENV" >> $bashEnvCachePath;
fi

View File

@ -1,7 +1,7 @@
{
"commitMessage": {
"maxLength": 120,
"minBodyLength": 0,
"minBodyLength": 100,
"types": [
"build",
"ci",
@ -43,5 +43,27 @@
"ve",
"zone.js"
]
},
"format": {
"matchers": [
"dev-infra/**/*.{js,ts}",
"packages/**/*.{js,ts}",
"!packages/zone.js",
"!packages/common/locales/**/*.{js,ts}",
"!packages/common/src/i18n/available_locales.ts",
"!packages/common/src/i18n/currencies.ts",
"!packages/common/src/i18n/locale_en.ts",
"modules/benchmarks/**/*.{js,ts}",
"modules/playground/**/*.{js,ts}",
"tools/**/*.{js,ts}",
"!tools/gulp-tasks/cldr/extract.js",
"!tools/public_api_guard/**/*.d.ts",
"!tools/ts-api-guardian/test/fixtures/**",
"./*.{js,ts}",
"!**/node_modules/**",
"!**/dist/**",
"!**/built/**",
"!shims_for_IE.js"
]
}
}
}

View File

@ -189,7 +189,7 @@ groups:
- *can-be-global-approved
- *can-be-global-docs-approved
- >
contains_any_globs(files, [
contains_any_globs(files.exclude('packages/compiler-cli/ngcc/**'), [
'packages/compiler/**',
'packages/examples/compiler/**',
'packages/compiler-cli/**',
@ -198,10 +198,6 @@ groups:
'aio/content/guide/aot-metadata-errors.md',
'aio/content/guide/template-typecheck.md '
])
- >
not contains_any_globs(files, [
'packages/compiler-cli/ngcc/**'
])
reviewers:
users:
- alxhub
@ -217,10 +213,7 @@ groups:
conditions:
- *can-be-global-approved
- *can-be-global-docs-approved
- >
contains_any_globs(files, [
'packages/compiler-cli/ngcc/**'
])
- files.include('packages/compiler-cli/ngcc/**')
reviewers:
users:
- alxhub
@ -229,6 +222,22 @@ groups:
- petebacondarwin
# =========================================================
# Framework: Migrations
# =========================================================
fw-migrations:
conditions:
- *can-be-global-approved
- *can-be-global-docs-approved
- files.include("packages/core/schematics/**")
reviewers:
users:
- alxhub
- crisbeto
- devversion
- kara
# =========================================================
# Framework: Core
# =========================================================
@ -237,7 +246,7 @@ groups:
- *can-be-global-approved
- *can-be-global-docs-approved
- >
contains_any_globs(files, [
contains_any_globs(files.exclude("packages/core/schematics/**"), [
'packages/core/**',
'packages/examples/core/**',
'packages/common/**',
@ -566,6 +575,7 @@ groups:
])
reviewers:
users:
- AndrewKushnir
- IgorMinar
- kara
- pkozlowski-opensource
@ -848,7 +858,7 @@ groups:
'aio/content/images/guide/deployment/**',
'aio/content/guide/file-structure.md',
'aio/content/guide/ivy.md',
'aio/content/guide/web-worker.md'
'aio/content/guide/web-worker.md',
'aio/content/guide/workspace-config.md',
])
reviewers:
@ -1027,8 +1037,7 @@ groups:
- *can-be-global-approved
- >
contains_any_globs(files, [
'aio/scripts/_payload-limits.json',
'integration/_payload-limits.json'
'goldens/size-tracking/**'
])
reviewers:
users:
@ -1044,7 +1053,7 @@ groups:
- *can-be-global-approved
- >
contains_any_globs(files, [
'goldens/packages-circular-deps.json'
'goldens/circular-deps/packages.json'
])
reviewers:
users:

View File

@ -1,26 +1,33 @@
<a name="10.0.0-next.2"></a>
# [10.0.0-next.2](https://github.com/angular/angular/compare/10.0.0-next.1...10.0.0-next.2) (2020-04-15)
<a name="9.1.4"></a>
## [9.1.4](https://github.com/angular/angular/compare/9.1.3...9.1.4) (2020-04-29)
### Bug Fixes
* **common:** `locales/global/*.js` are not ES5 compliant ([#36342](https://github.com/angular/angular/issues/36342)) ([078b0be](https://github.com/angular/angular/commit/078b0be)), closes [angular/angular-cli#16394](https://github.com/angular/angular-cli/issues/16394)
* **compiler:** handle type references to namespaced symbols correctly ([#36106](https://github.com/angular/angular/issues/36106)) ([4aa4e6f](https://github.com/angular/angular/commit/4aa4e6f)), closes [#36006](https://github.com/angular/angular/issues/36006)
* **core:** undecorated-classes-with-decorated-fields migration should avoid error if base class has no value declaration ([#36543](https://github.com/angular/angular/issues/36543)) ([ca67748](https://github.com/angular/angular/commit/ca67748)), closes [#36522](https://github.com/angular/angular/issues/36522)
* **ngcc:** correctly detect external files from nested `node_modules/` ([#36559](https://github.com/angular/angular/issues/36559)) ([6ab43d7](https://github.com/angular/angular/commit/6ab43d7)), closes [#36526](https://github.com/angular/angular/issues/36526)
* **ngcc:** display output from the unlocker process on Windows ([#36569](https://github.com/angular/angular/issues/36569)) ([e041ac6](https://github.com/angular/angular/commit/e041ac6))
* **ngcc:** do not spawn unlocker processes on cluster workers ([#36569](https://github.com/angular/angular/issues/36569)) ([66effde](https://github.com/angular/angular/commit/66effde)), closes [#35861](https://github.com/angular/angular/issues/35861)
* **ngcc:** do not warn if `paths` mapping does not exist ([#36525](https://github.com/angular/angular/issues/36525)) ([717df13](https://github.com/angular/angular/commit/717df13)), closes [#36518](https://github.com/angular/angular/issues/36518)
* **ngcc:** force ngcc to exit on error ([#36622](https://github.com/angular/angular/issues/36622)) ([663b768](https://github.com/angular/angular/commit/663b768)), closes [#36616](https://github.com/angular/angular/issues/36616)
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([80e6c07](https://github.com/angular/angular/commit/80e6c07)), closes [#34614](https://github.com/angular/angular/issues/34614)
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([0cc53fb](https://github.com/angular/angular/commit/0cc53fb)), closes [#36492](https://github.com/angular/angular/issues/36492)
* **core:** attempt to recover from user errors during creation ([#36381](https://github.com/angular/angular/issues/36381)) ([d743331](https://github.com/angular/angular/commit/d743331)), closes [#31221](https://github.com/angular/angular/issues/31221)
* **core:** handle synthetic props in Directive host bindings correctly ([#35568](https://github.com/angular/angular/issues/35568)) ([0f389fa](https://github.com/angular/angular/commit/0f389fa)), closes [#35501](https://github.com/angular/angular/issues/35501)
* **language-service:** disable update the `[@angular](https://github.com/angular)/core` module ([#36783](https://github.com/angular/angular/issues/36783)) ([d3a77ea](https://github.com/angular/angular/commit/d3a77ea))
* **localize:** include legacy ids when describing messages ([#36761](https://github.com/angular/angular/issues/36761)) ([aa94cd5](https://github.com/angular/angular/commit/aa94cd5))
* **ngcc:** recognize enum declarations emitted in JavaScript ([#36550](https://github.com/angular/angular/issues/36550)) ([c440165](https://github.com/angular/angular/commit/c440165)), closes [#35584](https://github.com/angular/angular/issues/35584)
### Performance Improvements
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([3bedfda](https://github.com/angular/angular/commit/3bedfda))
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([a185efb](https://github.com/angular/angular/commit/a185efb)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([ec0ce60](https://github.com/angular/angular/commit/ec0ce60))
<a name="9.1.3"></a>
## [9.1.3](https://github.com/angular/angular/compare/9.1.2...9.1.3) (2020-04-22)
### Bug Fixes
* **compiler:** avoid generating i18n attributes in plain form ([#36422](https://github.com/angular/angular/issues/36422)) ([08b8b51](https://github.com/angular/angular/commit/08b8b51))
* **core:** do not use unbound attributes as inputs to structural directives ([#36441](https://github.com/angular/angular/issues/36441)) ([c0ed57d](https://github.com/angular/angular/commit/c0ed57d))
* **core:** handle empty translations correctly ([#36499](https://github.com/angular/angular/issues/36499)) ([a5ea100](https://github.com/angular/angular/commit/a5ea100)), closes [#36476](https://github.com/angular/angular/issues/36476)
* **core:** missing-injectable migration should not migrate `@NgModule` classes ([#36369](https://github.com/angular/angular/issues/36369)) ([0bd50e2](https://github.com/angular/angular/commit/0bd50e2)), closes [#35700](https://github.com/angular/angular/issues/35700)
* **core:** pipes injecting viewProviders when used on a component host node ([#36512](https://github.com/angular/angular/issues/36512)) ([5ae8473](https://github.com/angular/angular/commit/5ae8473)), closes [#36146](https://github.com/angular/angular/issues/36146)
* **core:** prevent unknown property check for AOT-compiled components ([#36072](https://github.com/angular/angular/issues/36072)) ([fe1d9ba](https://github.com/angular/angular/commit/fe1d9ba)), closes [#35945](https://github.com/angular/angular/issues/35945)
* **core:** properly identify modules affected by overrides in TestBed ([#36649](https://github.com/angular/angular/issues/36649)) ([9724169](https://github.com/angular/angular/commit/9724169)), closes [#36619](https://github.com/angular/angular/issues/36619)
* **language-service:** properly evaluate types in comparable expressions ([#36529](https://github.com/angular/angular/issues/36529)) ([5bab498](https://github.com/angular/angular/commit/5bab498))
* **ngcc:** display unlocker process output in sync mode ([#36637](https://github.com/angular/angular/issues/36637)) ([da159bd](https://github.com/angular/angular/commit/da159bd)), closes [/github.com/nodejs/node/issues/3596#issuecomment-250890218](https://github.com//github.com/nodejs/node/issues/3596/issues/issuecomment-250890218)
* **ngcc:** do not use cached file-system ([#36687](https://github.com/angular/angular/issues/36687)) ([18be33a](https://github.com/angular/angular/commit/18be33a)), closes [/github.com/angular/angular-cli/issues/16860#issuecomment-614694269](https://github.com//github.com/angular/angular-cli/issues/16860/issues/issuecomment-614694269)
@ -36,79 +43,18 @@
* **ngcc:** display output from the unlocker process on Windows ([#36569](https://github.com/angular/angular/issues/36569)) ([12266b2](https://github.com/angular/angular/commit/12266b2))
* **ngcc:** do not spawn unlocker processes on cluster workers ([#36569](https://github.com/angular/angular/issues/36569)) ([e385abc](https://github.com/angular/angular/commit/e385abc)), closes [#35861](https://github.com/angular/angular/issues/35861)
* **ngcc:** do not warn if `paths` mapping does not exist ([#36525](https://github.com/angular/angular/issues/36525)) ([33eee43](https://github.com/angular/angular/commit/33eee43)), closes [#36518](https://github.com/angular/angular/issues/36518)
* **ngcc:** force ngcc to exit on error ([#36622](https://github.com/angular/angular/issues/36622)) ([933cbfb](https://github.com/angular/angular/commit/933cbfb)), closes [#36616](https://github.com/angular/angular/issues/36616) * **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614) * **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
* **ngcc:** force ngcc to exit on error ([#36622](https://github.com/angular/angular/issues/36622)) ([933cbfb](https://github.com/angular/angular/commit/933cbfb)), closes [#36616](https://github.com/angular/angular/issues/36616)
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614)
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
### Performance Improvements
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b)) * **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b))
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([603b094](https://github.com/angular/angular/commit/603b094))
<a name="10.0.0-next.1"></a>
# [10.0.0-next.1](https://github.com/angular/angular/compare/10.0.0-next.0...10.0.0-next.1) (2020-04-08)
This release contains various API docs improvements.
<a name="10.0.0-next.0"></a>
# [10.0.0-next.0](https://github.com/angular/angular/compare/9.1.0-rc.0...10.0.0-next.0) (2020-04-08)
### Bug Fixes
* **common:** let `KeyValuePipe` accept type unions with `null` ([#36093](https://github.com/angular/angular/issues/36093)) ([d783519](https://github.com/angular/angular/commit/d783519)), closes [#35743](https://github.com/angular/angular/issues/35743)
* **compiler:** avoid undefined expressions in holey array ([#36343](https://github.com/angular/angular/issues/36343)) ([5516802](https://github.com/angular/angular/commit/5516802))
* **compiler:** record correct end of expression ([#34690](https://github.com/angular/angular/issues/34690)) ([df890d7](https://github.com/angular/angular/commit/df890d7)), closes [#33477](https://github.com/angular/angular/issues/33477)
* **compiler:** resolve enum values in binary operations ([#36461](https://github.com/angular/angular/issues/36461)) ([64022f5](https://github.com/angular/angular/commit/64022f5)), closes [#35584](https://github.com/angular/angular/issues/35584)
* **compiler-cli:** pass real source spans where they are empty ([#31805](https://github.com/angular/angular/issues/31805)) ([e893c5a](https://github.com/angular/angular/commit/e893c5a))
* **core:** avoid migration error when non-existent symbol is imported ([#36367](https://github.com/angular/angular/issues/36367)) ([d43c306](https://github.com/angular/angular/commit/d43c306)), closes [#36346](https://github.com/angular/angular/issues/36346)
* **core:** ngOnDestroy on multi providers called with incorrect context ([#35840](https://github.com/angular/angular/issues/35840)) ([95fc3d4](https://github.com/angular/angular/commit/95fc3d4)), closes [#35231](https://github.com/angular/angular/issues/35231)
* **core:** run `APP_INITIALIZER`s before accessing `LOCALE_ID` token in Ivy TestBed ([#36237](https://github.com/angular/angular/issues/36237)) ([1649743](https://github.com/angular/angular/commit/1649743)), closes [#36230](https://github.com/angular/angular/issues/36230)
* **core:** undecorated-classes-with-decorated-fields migration does not decorate derived classes ([#35339](https://github.com/angular/angular/issues/35339)) ([32eafef](https://github.com/angular/angular/commit/32eafef)), closes [#34376](https://github.com/angular/angular/issues/34376)
* **core:** workaround Terser inlining bug ([#36200](https://github.com/angular/angular/issues/36200)) ([0ce8ad3](https://github.com/angular/angular/commit/0ce8ad3))
* **elements:** correctly handle setting inputs to `undefined` ([#36140](https://github.com/angular/angular/issues/36140)) ([9ba46d9](https://github.com/angular/angular/commit/9ba46d9))
* **elements:** correctly set `SimpleChange#firstChange` for pre-existing inputs ([#36140](https://github.com/angular/angular/issues/36140)) ([b14ac96](https://github.com/angular/angular/commit/b14ac96)), closes [#36130](https://github.com/angular/angular/issues/36130)
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([fe2b692](https://github.com/angular/angular/commit/fe2b692)), closes [#36191](https://github.com/angular/angular/issues/36191)
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([81195a2](https://github.com/angular/angular/commit/81195a2))
* **localize:** allow ICU expansion case to start with any character except `}` ([#36123](https://github.com/angular/angular/issues/36123)) ([fced8ee](https://github.com/angular/angular/commit/fced8ee)), closes [#31586](https://github.com/angular/angular/issues/31586)
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([76a8cd5](https://github.com/angular/angular/commit/76a8cd5)), closes [/github.com/angular/angular/issues/36414#issuecomment-609644282](https://github.com//github.com/angular/angular/issues/36414/issues/issuecomment-609644282)
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([326240e](https://github.com/angular/angular/commit/326240e))
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([ca25c95](https://github.com/angular/angular/commit/ca25c95)), closes [#36089](https://github.com/angular/angular/issues/36089)
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([aecf9de](https://github.com/angular/angular/commit/aecf9de))
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([995cd15](https://github.com/angular/angular/commit/995cd15)), closes [#35747](https://github.com/angular/angular/issues/35747)
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([5fa7b8b](https://github.com/angular/angular/commit/5fa7b8b))
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([5cee709](https://github.com/angular/angular/commit/5cee709)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts/issues/L429) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L108) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L110) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L199)
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([c6dd900](https://github.com/angular/angular/commit/c6dd900)), closes [#36296](https://github.com/angular/angular/issues/36296)
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([ee70a18](https://github.com/angular/angular/commit/ee70a18)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([cc4b813](https://github.com/angular/angular/commit/cc4b813)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
* **ngcc:** handle entry-points within container folders ([#36305](https://github.com/angular/angular/issues/36305)) ([38ad1d9](https://github.com/angular/angular/commit/38ad1d9)), closes [#35756](https://github.com/angular/angular/issues/35756) [#36216](https://github.com/angular/angular/issues/36216)
* **ngcc:** sniff `main` property for ESM5 format ([#36396](https://github.com/angular/angular/issues/36396)) ([2463548](https://github.com/angular/angular/commit/2463548)), closes [#35788](https://github.com/angular/angular/issues/35788)
* **ngcc:** support ignoring deep-imports via package config ([#36423](https://github.com/angular/angular/issues/36423)) ([f9fb833](https://github.com/angular/angular/commit/f9fb833)), closes [#35750](https://github.com/angular/angular/issues/35750)
* **ngcc:** support simple `browser` property in entry-points ([#36396](https://github.com/angular/angular/issues/36396)) ([6b3aa60](https://github.com/angular/angular/commit/6b3aa60)), closes [#36062](https://github.com/angular/angular/issues/36062)
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([380de1e](https://github.com/angular/angular/commit/380de1e)), closes [#36119](https://github.com/angular/angular/issues/36119)
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([b8e9a30](https://github.com/angular/angular/commit/b8e9a30)), closes [#35871](https://github.com/angular/angular/issues/35871)
* **platform-server:** update `xhr2` dependency ([#36366](https://github.com/angular/angular/issues/36366)) ([b59bc0e](https://github.com/angular/angular/commit/b59bc0e)), closes [#36358](https://github.com/angular/angular/issues/36358)
* **router:** allow UrlMatcher to return null ([#36402](https://github.com/angular/angular/issues/36402)) ([568e9df](https://github.com/angular/angular/commit/568e9df)), closes [#29824](https://github.com/angular/angular/issues/29824)
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([e0415db](https://github.com/angular/angular/commit/e0415db)), closes [#33173](https://github.com/angular/angular/issues/33173)
* **service-worker:** by default register the SW after 30s even the app never stabilizes ([#35870](https://github.com/angular/angular/issues/35870)) ([29e8a64](https://github.com/angular/angular/commit/29e8a64)), closes [#34464](https://github.com/angular/angular/issues/34464)
* **service-worker:** prevent SW registration strategies from affecting app stabilization ([#35870](https://github.com/angular/angular/issues/35870)) ([2d7c95f](https://github.com/angular/angular/commit/2d7c95f))
### Features
* **compiler:** add dependency info and ng-content selectors to metadata ([#35695](https://github.com/angular/angular/issues/35695)) ([32ce8b1](https://github.com/angular/angular/commit/32ce8b1))
* **compiler:** Propagate value span of ExpressionBinding to ParsedProperty ([#36133](https://github.com/angular/angular/issues/36133)) ([d714b95](https://github.com/angular/angular/commit/d714b95))
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([c24ad56](https://github.com/angular/angular/commit/c24ad56))
* **service-worker:** support timeout in `registerWhenStable` SW registration strategy ([#35870](https://github.com/angular/angular/issues/35870)) ([00efacf](https://github.com/angular/angular/commit/00efacf)), closes [#34464](https://github.com/angular/angular/issues/34464)
### BREAKING CHANGES
* **router:** UrlMatcher's type now reflects that it could always return
null.
If you implemented your own Router or Recognizer class, please update it to
handle matcher returning null.
<a name="9.1.1"></a>
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
@ -144,6 +90,7 @@ This release contains various API docs improvements.
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
<a name="9.1.0"></a>
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)

View File

@ -82,9 +82,6 @@ upgrade-phonecat-2-hybrid/aot/**/*
# styleguide
!styleguide/src/systemjs.custom.js
# universal
!universal/webpack.server.config.js
# stackblitz
*stackblitz.no-link.html
@ -97,4 +94,4 @@ upgrade-phonecat-3-final/rollup-config.js
!upgrade-phonecat-*/**/karma-test-shim.js
# schematics
!schematics-for-libraries/projects/my-lib/package.json
!schematics-for-libraries/projects/my-lib/package.json

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
const routes: Routes = []; // sets up routes constant where you define your routes
// configures NgModule imports and exports
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,26 @@
// #docplaster
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
// #docregion routes, routes-with-wildcard, redirect
const routes: Routes = [
{ path: 'first-component', component: FirstComponent },
{ path: 'second-component', component: SecondComponent },
// #enddocregion routes
{ path: '', redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component`
{ path: '**', component: FirstComponent },
// #enddocregion redirect
{ path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page
// #docregion routes
// #docregion redirect
];
// #enddocregion routes, routes-with-wildcard, redirect
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,28 @@
// #docplaster
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
// #docregion child-routes
const routes: Routes = [
{ path: 'first-component',
component: FirstComponent, // this is the component with the <router-outlet> in the template
children: [
{
path: 'child-a', // child route path
component: ChildAComponent // child route component that the router renders
},
{
path: 'child-b',
component: ChildBComponent // another child route component that the router renders
}
] },
// #enddocregion child-routes
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,15 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
// #docregion relative-to
goToItems() {
this.router.navigate(['items'], { relativeTo: this.route });
}
// #enddocregion relative-to
}

View File

@ -0,0 +1,10 @@
<h1>Angular Router App</h1>
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in AppRoutingModule) -->
<nav>
<ul>
<li><a routerLink="/first-component" routerLinkActive="active">First Component</a></li>
<li><a routerLink="/second-component" routerLinkActive="active">Second Component</a></li>
</ul>
</nav>
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>

View File

@ -0,0 +1,26 @@
<!-- #docregion child-routes-->
<h2>First Component</h2>
<nav>
<ul>
<li><a routerLink="child-a">Child A</a></li>
<li><a routerLink="child-b">Child B</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
<!-- #enddocregion child-routes-->
<!-- #docregion relative-route-->
<h2>First Component</h2>
<nav>
<ul>
<li><a routerLink="../second-component">Relative Route to second component</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
<!-- #enddocregion relative-route-->

View File

@ -0,0 +1,17 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module'; // CLI imports AppRoutingModule
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule // CLI adds AppRoutingModule to the AppModule's imports array
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -2,7 +2,9 @@
// #docregion
import { switchMap } from 'rxjs/operators';
import { Component, OnInit } from '@angular/core';
// #docregion imports-route-info
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
// #enddocregion imports-route-info
import { Observable } from 'rxjs';
import { HeroService } from '../hero.service';
@ -16,11 +18,16 @@ import { Hero } from '../hero';
export class HeroDetailComponent implements OnInit {
hero$: Observable<Hero>;
// #docregion activated-route
constructor(
private route: ActivatedRoute,
// #enddocregion activated-route
private router: Router,
private service: HeroService
// #docregion activated-route
) {}
// #enddocregion activated-route
ngOnInit() {
this.hero$ = this.route.paramMap.pipe(

View File

@ -8,4 +8,6 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -9,5 +9,6 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
// #enddocregion

View File

@ -9,5 +9,6 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
// #enddocregion

View File

@ -9,5 +9,6 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
// #enddocregion

View File

@ -8,4 +8,5 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -19,7 +19,6 @@ button {
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;

View File

@ -8,4 +8,5 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -1,5 +1,5 @@
// #docplaster
// #docregion, v1
// #docregion , v1
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
@ -59,6 +59,5 @@ import { MessagesComponent } from './messages/messages.component';
// #docregion import-httpclientmodule
})
// #enddocregion import-httpclientmodule
export class AppModule { }
// #enddocregion , v1

View File

@ -18,7 +18,7 @@ button {
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer; cursor: hand;
cursor: pointer;
}
button:hover {
background-color: #cfd8dc;

View File

@ -33,10 +33,10 @@ export class HeroDetailComponent implements OnInit {
this.location.back();
}
// #docregion save
save(): void {
// #docregion save
save(): void {
this.heroService.updateHero(this.hero)
.subscribe(() => this.goBack());
}
// #enddocregion save
// #enddocregion save
}

View File

@ -36,7 +36,7 @@ export class HeroService {
// #docregion getHeroes, getHeroes-1
/** GET heroes from the server */
// #docregion getHeroes-2
getHeroes (): Observable<Hero[]> {
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
// #enddocregion getHeroes-1
.pipe(
@ -98,7 +98,7 @@ export class HeroService {
// #docregion addHero
/** POST: add a new hero to the server */
addHero (hero: Hero): Observable<Hero> {
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
catchError(this.handleError<Hero>('addHero'))
@ -108,7 +108,7 @@ export class HeroService {
// #docregion deleteHero
/** DELETE: delete the hero from the server */
deleteHero (hero: Hero | number): Observable<Hero> {
deleteHero(hero: Hero | number): Observable<Hero> {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;
@ -121,7 +121,7 @@ export class HeroService {
// #docregion updateHero
/** PUT: update the hero on the server */
updateHero (hero: Hero): Observable<any> {
updateHero(hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
@ -136,7 +136,7 @@ export class HeroService {
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T> (operation = 'operation', result?: T) {
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure

View File

@ -30,7 +30,7 @@
}
.heroes a:hover {
color:#607D8B;
color: #607D8B;
}
.heroes .badge {
@ -38,7 +38,7 @@
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color:#405061;
background-color: #405061;
line-height: 1em;
position: relative;
left: -1px;

View File

@ -1,7 +1,7 @@
// #docregion , init
import { Injectable } from '@angular/core';
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',

View File

@ -9,4 +9,5 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -0,0 +1,300 @@
import { browser, by, element, ElementArrayFinder, ElementFinder, logging } from 'protractor';
class Hero {
id: number;
name: string;
// Factory methods
// Hero from string formatted as '<id> <name>'.
static fromString(s: string): Hero {
return {
id: +s.substr(0, s.indexOf(' ')),
name: s.substr(s.indexOf(' ') + 1),
};
}
// Hero from hero list <li> element.
static async fromLi(li: ElementFinder): Promise<Hero> {
const stringsFromA = await li.all(by.css('a')).getText();
const strings = stringsFromA[0].split(' ');
return { id: +strings[0], name: strings[1] };
}
// Hero id and name from the given detail element.
static async fromDetail(detail: ElementFinder): Promise<Hero> {
// Get hero id from the first <div>
const id = await detail.all(by.css('div')).first().getText();
// Get name from the h2
const name = await detail.element(by.css('h2')).getText();
return {
id: +id.substr(id.indexOf(' ') + 1),
name: name.substr(0, name.lastIndexOf(' '))
};
}
}
describe('Universal', () => {
const expectedH1 = 'Tour of Heroes';
const expectedTitle = `${expectedH1}`;
const targetHero = { id: 15, name: 'Magneta' };
const targetHeroDashboardIndex = 3;
const nameSuffix = 'X';
const newHeroName = targetHero.name + nameSuffix;
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const severeLogs = logs.filter(entry => entry.level === logging.Level.SEVERE);
expect(severeLogs).toEqual([]);
});
describe('Initial page', () => {
beforeAll(() => browser.get(''));
it(`has title '${expectedTitle}'`, () => {
expect(browser.getTitle()).toEqual(expectedTitle);
});
it(`has h1 '${expectedH1}'`, () => {
expectHeading(1, expectedH1);
});
const expectedViewNames = ['Dashboard', 'Heroes'];
it(`has views ${expectedViewNames}`, () => {
const viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText());
expect(viewNames).toEqual(expectedViewNames);
});
it('has dashboard as the active view', () => {
const page = getPageElts();
expect(page.appDashboard.isPresent()).toBeTruthy();
});
});
describe('Dashboard tests', () => {
beforeAll(() => browser.get(''));
it('has top heroes', () => {
const page = getPageElts();
expect(page.topHeroes.count()).toEqual(4);
});
it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
it(`cancels and shows ${targetHero.name} in Dashboard`, () => {
element(by.buttonText('go back')).click();
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
expect(targetHeroElt.getText()).toEqual(targetHero.name);
});
it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
it(`saves and shows ${newHeroName} in Dashboard`, () => {
element(by.buttonText('save')).click();
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
expect(targetHeroElt.getText()).toEqual(newHeroName);
});
});
describe('Heroes tests', () => {
beforeAll(() => browser.get(''));
it('can switch to Heroes view', () => {
getPageElts().appHeroesHref.click();
const page = getPageElts();
expect(page.appHeroes.isPresent()).toBeTruthy();
expect(page.allHeroes.count()).toEqual(10, 'number of heroes');
});
it('can route to hero details', async () => {
getHeroLiEltById(targetHero.id).click();
const page = getPageElts();
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
const hero = await Hero.fromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(targetHero.name.toUpperCase());
});
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
it(`shows ${newHeroName} in Heroes list`, () => {
element(by.buttonText('save')).click();
browser.waitForAngular();
const expectedText = `${targetHero.id} ${newHeroName}`;
expect(getHeroAEltById(targetHero.id).getText()).toEqual(expectedText);
});
it(`deletes ${newHeroName} from Heroes list`, async () => {
const heroesBefore = await toHeroArray(getPageElts().allHeroes);
const li = getHeroLiEltById(targetHero.id);
li.element(by.buttonText('x')).click();
const page = getPageElts();
expect(page.appHeroes.isPresent()).toBeTruthy();
expect(page.allHeroes.count()).toEqual(9, 'number of heroes');
const heroesAfter = await toHeroArray(page.allHeroes);
// console.log(await Hero.fromLi(page.allHeroes[0]));
const expectedHeroes = heroesBefore.filter(h => h.name !== newHeroName);
expect(heroesAfter).toEqual(expectedHeroes);
// expect(page.selectedHeroSubview.isPresent()).toBeFalsy();
});
it(`adds back ${targetHero.name}`, async () => {
const updatedHeroName = 'Alice';
const heroesBefore = await toHeroArray(getPageElts().allHeroes);
const numHeroes = heroesBefore.length;
element(by.css('input')).sendKeys(updatedHeroName);
element(by.buttonText('add')).click();
const page = getPageElts();
const heroesAfter = await toHeroArray(page.allHeroes);
expect(heroesAfter.length).toEqual(numHeroes + 1, 'number of heroes');
expect(heroesAfter.slice(0, numHeroes)).toEqual(heroesBefore, 'Old heroes are still there');
const maxId = heroesBefore[heroesBefore.length - 1].id;
expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: updatedHeroName});
});
it('displays correctly styled buttons', async () => {
element.all(by.buttonText('x')).then(buttons => {
for (const button of buttons) {
// Inherited styles from styles.css
expect(button.getCssValue('font-family')).toBe('Arial');
expect(button.getCssValue('border')).toContain('none');
expect(button.getCssValue('padding')).toBe('5px 10px');
expect(button.getCssValue('border-radius')).toBe('4px');
// Styles defined in heroes.component.css
expect(button.getCssValue('left')).toBe('194px');
expect(button.getCssValue('top')).toBe('-32px');
}
});
const addButton = element(by.buttonText('add'));
// Inherited styles from styles.css
expect(addButton.getCssValue('font-family')).toBe('Arial');
expect(addButton.getCssValue('border')).toContain('none');
expect(addButton.getCssValue('padding')).toBe('5px 10px');
expect(addButton.getCssValue('border-radius')).toBe('4px');
});
});
describe('Progressive hero search', () => {
beforeAll(() => browser.get(''));
it(`searches for 'Ma'`, async () => {
getPageElts().searchBox.sendKeys('Ma');
browser.sleep(1000);
expect(getPageElts().searchResults.count()).toBe(4);
});
it(`continues search with 'g'`, async () => {
getPageElts().searchBox.sendKeys('g');
browser.sleep(1000);
expect(getPageElts().searchResults.count()).toBe(2);
});
it(`continues search with 'e' and gets ${targetHero.name}`, async () => {
getPageElts().searchBox.sendKeys('n');
browser.sleep(1000);
const page = getPageElts();
expect(page.searchResults.count()).toBe(1);
const hero = page.searchResults.get(0);
expect(hero.getText()).toEqual(targetHero.name);
});
it(`navigates to ${targetHero.name} details view`, async () => {
const hero = getPageElts().searchResults.get(0);
expect(hero.getText()).toEqual(targetHero.name);
hero.click();
const page = getPageElts();
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
const hero2 = await Hero.fromDetail(page.heroDetail);
expect(hero2.id).toEqual(targetHero.id);
expect(hero2.name).toEqual(targetHero.name.toUpperCase());
});
});
// Helpers
function addToHeroName(text: string): Promise<void> {
return element(by.css('input')).sendKeys(text) as Promise<void>;
}
async function dashboardSelectTargetHero(): Promise<void> {
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
expect(targetHeroElt.getText()).toEqual(targetHero.name);
targetHeroElt.click();
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
const page = getPageElts();
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
const hero = await Hero.fromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(targetHero.name.toUpperCase());
}
function expectHeading(hLevel: number, expectedText: string): void {
const hTag = `h${hLevel}`;
const hText = element(by.css(hTag)).getText();
expect(hText).toEqual(expectedText, hTag);
}
function getHeroAEltById(id: number): ElementFinder {
const spanForId = element(by.cssContainingText('li span.badge', id.toString()));
return spanForId.element(by.xpath('..'));
}
function getHeroLiEltById(id: number): ElementFinder {
const spanForId = element(by.cssContainingText('li span.badge', id.toString()));
return spanForId.element(by.xpath('../..'));
}
function getPageElts() {
const navElts = element.all(by.css('app-root nav a'));
return {
navElts,
appDashboardHref: navElts.get(0),
appDashboard: element(by.css('app-root app-dashboard')),
topHeroes: element.all(by.css('app-root app-dashboard > div h4')),
appHeroesHref: navElts.get(1),
appHeroes: element(by.css('app-root app-heroes')),
allHeroes: element.all(by.css('app-root app-heroes li')),
selectedHeroSubview: element(by.css('app-root app-heroes > div:last-child')),
heroDetail: element(by.css('app-root app-hero-detail > div')),
searchBox: element(by.css('#search-box')),
searchResults: element.all(by.css('.search-result li'))
};
}
async function toHeroArray(allHeroes: ElementArrayFinder): Promise<Hero[]> {
return await allHeroes.map(Hero.fromLi);
}
async function updateHeroNameInDetailView(): Promise<void> {
// Assumes that the current view is the hero details view.
addToHeroName(nameSuffix);
const page = getPageElts();
const hero = await Hero.fromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(newHeroName.toUpperCase());
}
});

View File

@ -1,3 +1,7 @@
{
"projectType": "universal"
"projectType": "universal",
"e2e": [
{"cmd": "yarn", "args": ["e2e", "--prod", "--protractor-config=e2e/protractor-puppeteer.conf.js", "--no-webdriver-update", "--port={PORT}"]},
{"cmd": "yarn", "args": ["run", "build:ssr"]}
]
}

View File

@ -6,24 +6,28 @@ import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const distFolder = join(process.cwd(), 'dist/express-engine-ivy/browser');
const distFolder = join(process.cwd(), 'dist/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// #docregion ngExpressEngine
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
// #enddocregion ngExpressEngine
server.set('view engine', 'html');
server.set('views', distFolder);
// #docregion data-request
// TODO: implement data requests securely
server.get('/api/*', (req, res) => {
res.status(404).send('data requests are not supported');
server.get('/api/**', (req, res) => {
res.status(404).send('data requests are not yet supported');
});
// #enddocregion data-request
@ -37,7 +41,7 @@ export function app() {
// #docregion navigation-request
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
// #enddocregion navigation-request
@ -59,7 +63,8 @@ function run() {
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
if (mainModule && mainModule.filename === __filename) {
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}

View File

@ -1,7 +1,6 @@
/* AppComponent's private CSS styles */
h1 {
font-size: 1.2em;
color: #999;
margin-bottom: 0;
}
h2 {
@ -18,7 +17,7 @@ nav a {
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
color: #334953;
}
nav a:hover {
color: #039be5;

View File

@ -14,8 +14,6 @@ import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroSearchComponent } from './hero-search/hero-search.component';
import { HeroService } from './hero.service';
import { MessageService } from './message.service';
import { MessagesComponent } from './messages/messages.component';
// #docregion platform-detection
@ -32,6 +30,10 @@ import { isPlatformBrowser } from '@angular/common';
FormsModule,
AppRoutingModule,
HttpClientModule,
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
// and returns simulated server responses.
// Remove it when a real server is ready to receive requests.
HttpClientInMemoryWebApiModule.forRoot(
InMemoryDataService, { dataEncapsulation: false }
)
@ -44,7 +46,6 @@ import { isPlatformBrowser } from '@angular/common';
MessagesComponent,
HeroSearchComponent
],
providers: [ HeroService, MessageService ],
bootstrap: [ AppComponent ]
})
export class AppModule {

View File

@ -1,6 +1,5 @@
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
@ -9,11 +8,10 @@ import { AppComponent } from './app.component';
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule
],
providers: [
// Add universal-only providers here
// Add server-only providers here.
],
bootstrap: [ AppComponent ],
bootstrap: [AppComponent],
})
export class AppServerModule {}

View File

@ -34,7 +34,7 @@ h4 {
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
background-color: #3f525c;
border-radius: 2px;
}
.module:hover {

View File

@ -8,4 +8,4 @@
</a>
</div>
<hero-search></hero-search>
<app-hero-search></app-hero-search>

View File

@ -18,6 +18,6 @@ export class DashboardComponent implements OnInit {
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
}
}

View File

@ -19,7 +19,6 @@ button {
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;

View File

@ -1,5 +1,5 @@
<div *ngIf="hero">
<h2>{{ hero.name | uppercase }} Details</h2>
<h2>{{hero.name | uppercase}} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div>
<label>name:

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
@Input() hero: Hero;
constructor(
private route: ActivatedRoute,
@ -33,7 +33,7 @@ export class HeroDetailComponent implements OnInit {
this.location.back();
}
save(): void {
save(): void {
this.heroService.updateHero(this.hero)
.subscribe(() => this.goBack());
}

View File

@ -1,10 +1,10 @@
<div id="search-component">
<h4>Hero Search</h4>
<h4><label for="search-box">Hero Search</label></h4>
<input #searchBox id="search-box" (input)="search(searchBox.value)" />
<ul class="search-result">
<li *ngFor="let hero of heroes | async" >
<li *ngFor="let hero of heroes$ | async" >
<a routerLink="/detail/{{hero.id}}">
{{hero.name}}
</a>

View File

@ -10,12 +10,12 @@ import { Hero } from '../hero';
import { HeroService } from '../hero.service';
@Component({
selector: 'hero-search',
selector: 'app-hero-search',
templateUrl: './hero-search.component.html',
styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {
heroes: Observable<Hero[]>;
heroes$: Observable<Hero[]>;
private searchTerms = new Subject<string>();
constructor(private heroService: HeroService) {}
@ -26,7 +26,7 @@ export class HeroSearchComponent implements OnInit {
}
ngOnInit(): void {
this.heroes = this.searchTerms.pipe(
this.heroes$ = this.searchTerms.pipe(
// wait 300ms after each keystroke before considering the term
debounceTime(300),

View File

@ -1,6 +1,5 @@
import { Injectable, Inject, Optional } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { HttpClient, HttpHeaders }from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
@ -8,30 +7,26 @@ import { catchError, map, tap } from 'rxjs/operators';
import { Hero } from './hero';
import { MessageService } from './message.service';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable()
@Injectable({ providedIn: 'root' })
export class HeroService {
private heroesUrl = 'api/heroes'; // URL to web api
// #docregion ctor
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(
private http: HttpClient,
private messageService: MessageService,
@Optional() @Inject(APP_BASE_HREF) origin?: string) {
this.heroesUrl = `${origin}${this.heroesUrl}`;
}
// #enddocregion ctor
private messageService: MessageService) { }
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
getHeroes(): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(heroes => this.log('fetched heroes')),
catchError(this.handleError('getHeroes', []))
tap(_ => this.log('fetched heroes')),
catchError(this.handleError<Hero[]>('getHeroes', []))
);
}
@ -65,7 +60,9 @@ export class HeroService {
return of([]);
}
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
tap(_ => this.log(`found heroes matching "${term}"`)),
tap(x => x.length ?
this.log(`found heroes matching "${term}"`) :
this.log(`no heroes matching "${term}"`)),
catchError(this.handleError<Hero[]>('searchHeroes', []))
);
}
@ -73,29 +70,27 @@ export class HeroService {
//////// Save methods //////////
/** POST: add a new hero to the server */
addHero (name: string): Observable<Hero> {
const hero = { name };
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
/** DELETE: delete the hero from the server */
deleteHero (hero: Hero | number): Observable<Hero> {
deleteHero(hero: Hero | number): Observable<Hero> {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;
return this.http.delete<Hero>(url, httpOptions).pipe(
return this.http.delete<Hero>(url, this.httpOptions).pipe(
tap(_ => this.log(`deleted hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
}
/** PUT: update the hero on the server */
updateHero (hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
updateHero(hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
);
@ -107,7 +102,7 @@ export class HeroService {
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T> (operation = 'operation', result?: T) {
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure

View File

@ -22,7 +22,7 @@
}
.heroes a {
color: #888;
color: #333;
text-decoration: none;
position: relative;
display: block;
@ -30,7 +30,7 @@
}
.heroes a:hover {
color:#607D8B;
color: #607D8B;
}
.heroes .badge {
@ -38,7 +38,7 @@
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
background-color: #405061;
line-height: 1em;
position: relative;
left: -1px;
@ -50,7 +50,7 @@
border-radius: 4px 0 0 4px;
}
.button {
button {
background-color: #eee;
border: none;
padding: 5px 10px;

View File

@ -16,6 +16,6 @@
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
<button class="delete" title="delete hero"
(click)="delete(hero);$event.stopPropagation()">x</button>
(click)="delete(hero)">x</button>
</li>
</ul>

View File

@ -25,17 +25,15 @@ export class HeroesComponent implements OnInit {
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.addHero(name)
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
delete(hero: Hero): void {
this.heroService.deleteHero(hero)
.subscribe(() => {
this.heroes = this.heroes.filter(h => h !== hero);
});
this.heroes = this.heroes.filter(h => h !== hero);
this.heroService.deleteHero(hero).subscribe();
}
}

View File

@ -1,5 +1,10 @@
import { Injectable } from '@angular/core';
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';
@Injectable({
providedIn: 'root',
})
export class InMemoryDataService implements InMemoryDbService {
createDb() {
const heroes = [
@ -16,4 +21,13 @@ export class InMemoryDataService implements InMemoryDbService {
];
return {heroes};
}
// Overrides the genId method to ensure that a hero always has an id.
// If the heroes array is empty,
// the method below returns the initial number (11).
// if the heroes array is not empty, the method below returns the highest
// hero id + 1.
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
@Injectable()
@Injectable({ providedIn: 'root' })
export class MessageService {
messages: string[] = [];

View File

@ -30,6 +30,6 @@ button:disabled {
cursor: auto;
}
button.clear {
color: #888;
color: #333;
margin-bottom: 12px;
}

View File

@ -8,4 +8,7 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@ -1,32 +0,0 @@
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: { server: './server.ts' },
resolve: { extensions: ['.js', '.ts'] },
target: 'node',
mode: 'none',
// this makes sure we include node_modules and other 3rd party libraries
externals: [/node_modules/],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [{ test: /\.ts$/, loader: 'ts-loader' }]
},
plugins: [
// Temporary Fix for issue: https://github.com/angular/angular/issues/11580
// for 'WARNING Critical dependency: the request of a dependency is an expression'
new webpack.ContextReplacementPlugin(
/(.+)?angular(\\|\/)core(.+)?/,
path.join(__dirname, 'src'), // location of your src
{} // a map of your routes
),
new webpack.ContextReplacementPlugin(
/(.+)?express(\\|\/)(.+)?/,
path.join(__dirname, 'src'),
{}
)
]
};

View File

@ -10,6 +10,12 @@ For an in-depth introduction to issues and techniques for designing accessible a
This page discusses best practices for designing Angular applications that
work well for all users, including those who rely on assistive technologies.
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
</div>
## Accessibility attributes
Building accessible web experience often involves setting [ARIA attributes](https://developers.google.com/web/fundamentals/accessibility/semantics-aria)
@ -92,8 +98,6 @@ The following example shows how to make a simple progress bar accessible by usin
<code-example path="accessibility/src/app/app.component.html" header="src/app/app.component.html" region="template"></code-example>
To see the progress bar in a working example app, refer to the <live-example></live-example>.
## Routing and focus management
Tracking and controlling [focus](https://developers.google.com/web/fundamentals/accessibility/focus/) in a UI is an important consideration in designing for accessibility.

View File

@ -415,8 +415,8 @@ The following are some of the key AngularJS built-in directives and their equiva
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link"></code-example>
For more information on routing, see the [RouterLink binding](guide/router#router-link)
section of the [Routing & Navigation](guide/router) page.
For more information on routing, see [Defining a basic route](guide/router#basic-route)
in the [Routing & Navigation](guide/router) page.
</td>

View File

@ -19,12 +19,17 @@ Both components and services are simply classes, with *decorators* that mark the
An app's components typically define many views, arranged hierarchically. Angular provides the `Router` service to help you define navigation paths among views. The router provides sophisticated in-browser navigational capabilities.
<div class="alert is-helpful>
<div class="alert is-helpful">
See the [Angular Glossary](guide/glossary) for basic definitions of important Angular terms and usage.
</div>
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
</div>
## Modules
Angular *NgModules* differ from and complement JavaScript (ES2015) modules. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
@ -148,10 +153,5 @@ Each of these subjects is introduced in more detail in the following pages.
* [Introduction to services and dependency injection](guide/architecture-services)
<div class="alert is-helpful">
Note that the code referenced on these pages is available as a <live-example></live-example>.
</div>
When you're familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps: tools and techniques](guide/architecture-next-steps).
</div>

View File

@ -303,7 +303,7 @@ Some features of Angular may require additional polyfills.
<td>
[Router](guide/router) when using
[hash-based routing](guide/router#appendix-locationstrategy-and-browser-url-styles)
[hash-based routing](guide/router#location-strategy)
</td>
<td>

View File

@ -311,11 +311,11 @@ To use CSS grid with IE10/11, you must explicitly enable it using the `autoplace
To do this, add the following to the top of the global styles file (or within a specific css selector scope):
```
/* autoprefixer grid: autoplace /
/* autoprefixer grid: autoplace */
```
or
```
/ autoprefixer grid: no-autoplace */
/* autoprefixer grid: no-autoplace */
```
For more information, see [Autoprefixer documentation](https://autoprefixer.github.io/).

View File

@ -321,7 +321,7 @@ absolutely must be present when the app starts.
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
[waiting until the app has launched](guide/router#preloading "Preloading")
or by [_lazy loading_](guide/router#asynchronous-routing "Lazy loading")
or by [_lazy loading_](guide/router#lazy-loading "Lazy loading")
them on demand.
<div class="callout is-helpful">

View File

@ -318,6 +318,7 @@ const routes: Routes = [{
{@a activatedroute-props}
### ActivatedRoute params and queryParams properties
[ActivatedRoute](api/router/ActivatedRoute) contains two [properties](api/router/ActivatedRoute#properties) that are less capable than their replacements and may be deprecated in a future Angular version.
@ -327,7 +328,7 @@ const routes: Routes = [{
| `params` | `paramMap` |
| `queryParams` | `queryParamMap` |
For more information see the [Router guide](guide/router#activated-route).
For more information see the [Getting route information](guide/router#activated-route) section of the [Router guide](guide/router).
{@a reflect-metadata}

View File

@ -99,7 +99,7 @@ Project-specific [TypeScript](https://www.typescriptlang.org/) configuration fil
| APPLICATION-SPECIFIC CONFIG FILES | PURPOSE |
| :--------------------- | :------------------------------------------|
| `.browserslistrc` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
| `browserslist` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
| `karma.conf.js` | Application-specific [Karma](https://karma-runner.github.io/2.0/config/configuration-file.html) configuration. |
| `tsconfig.app.json` | Application-specific [TypeScript](https://www.typescriptlang.org/) configuration, including TypeScript and Angular template compiler options. See [TypeScript Configuration](guide/typescript-configuration) and [Angular Compiler Options](guide/angular-compiler-options). |
| `tsconfig.spec.json` | [TypeScript](https://www.typescriptlang.org/) configuration for the application tests. See [TypeScript Configuration](guide/typescript-configuration). |

File diff suppressed because it is too large Load Diff

View File

@ -103,9 +103,8 @@ Version | Status | Released | Active Ends | LTS Ends
------- | ------ | ------------ | ------------ | ------------
^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
^7.0.0 | LTS | Oct 18, 2018 | Apr 18, 2019 | Apr 18, 2020
Angular versions ^4.0.0, ^5.0.0 and ^6.0.0 are no longer under support.
Angular versions ^4.0.0, ^5.0.0, ^6.0.0 and ^7.0.0 are no longer under support.
{@a deprecation}
## Deprecation practices

File diff suppressed because it is too large Load Diff

View File

@ -1958,7 +1958,7 @@ for the `id` to change during its lifetime.
<div class="alert is-helpful">
The [Router](guide/router#route-parameters) guide covers `ActivatedRoute.paramMap` in more detail.
The [ActivatedRoute in action](guide/router#activated-route-in-action) section of the [Router](guide/router) guide covers `ActivatedRoute.paramMap` in more detail.
</div>

View File

@ -53,7 +53,7 @@ The initial `tsconfig.json` for an Angular app typically looks like the followin
]
},
"angularCompilerOptions": {
"strictTemplates": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
@ -62,6 +62,7 @@ The initial `tsconfig.json` for an Angular app typically looks like the followin
{@a noImplicitAny}
### *noImplicitAny* and *suppressImplicitAnyIndexErrors*
TypeScript developers disagree about whether the `noImplicitAny` flag should be `true` or `false`.
@ -95,7 +96,6 @@ For more information about how the TypeScript configuration affects compilation,
</div>
{@a typings}
## TypeScript typings
@ -146,6 +146,7 @@ For instance, to install typings for `jasmine` you run `npm install @types/jasmi
{@a target}
### *target*
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.

View File

@ -15,7 +15,7 @@ The CLI schematic `@nguniversal/express-engine` performs the required steps, as
<div class="alert is-helpful">
**Note:** [Download the finished sample code](generated/zips/universal/universal.zip),
**Note:** <live-example downloadOnly>Download the finished sample code</live-example>,
which runs in a [Node.js® Express](https://expressjs.com/) server.
</div>
@ -27,7 +27,7 @@ The [Tour of Heroes tutorial](tutorial) is the foundation for this walkthrough.
In this example, the Angular CLI compiles and bundles the Universal version of the app with the
[Ahead-of-Time (AOT) compiler](guide/aot-compiler).
A Node Express web server compiles HTML pages with Universal based on client requests.
A Node.js Express web server compiles HTML pages with Universal based on client requests.
To create the server-side app module, `app.server.module.ts`, run the following CLI command.
@ -62,10 +62,10 @@ The files marked with `*` are new and not in the original tutorial sample.
To start rendering your app with Universal on your local system, use the following command.
<code-example language="bash">
npm run build:ssr && npm run serve:ssr
npm run dev:ssr
</code-example>
Open a browser and navigate to http://localhost:4000/.
Open a browser and navigate to http://localhost:4200/.
You should see the familiar Tour of Heroes dashboard page.
Navigation via `routerLinks` works correctly because they use the native anchor (`<a>`) tags.
@ -158,13 +158,12 @@ The sample web server for this guide is based on the popular [Express](https://e
Universal applications use the Angular `platform-server` package (as opposed to `platform-browser`), which provides
server implementations of the DOM, `XMLHttpRequest`, and other low-level features that don't rely on a browser.
The server ([Node Express](https://expressjs.com/) in this guide's example)
The server ([Node.js Express](https://expressjs.com/) in this guide's example)
passes client requests for application pages to the NgUniversal `ngExpressEngine`. Under the hood, this
calls Universal's `renderModule()` function, while providing caching and other helpful utilities.
The `renderModule()` function takes as inputs a *template* HTML page (usually `index.html`),
an Angular *module* containing components,
and a *route* that determines which components to display.
an Angular *module* containing components, and a *route* that determines which components to display.
The route comes from the client's request to the server.
Each request results in the appropriate view for the requested route.
@ -188,71 +187,6 @@ Similarly, without mouse or keyboard events, a server-side app can't rely on a u
The app must determine what to render based solely on the incoming client request.
This is a good argument for making the app [routable](guide/router).
{@a http-urls}
### Using absolute URLs for server requests
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data.
These services send requests to _relative_ URLs such as `api/heroes`.
In a Universal app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`).
This means you need to change your services to make requests with absolute URLs when running on the server and with relative
URLs when running in the browser.
One solution is to provide the full URL to your application on the server, and write an interceptor that can retrieve this
value and prepend it to the request URL. If you're using the `ngExpressEngine`, as shown in the example in this guide, half
the work is already done. We'll assume this is the case, but it's trivial to provide the same functionality.
Start by creating an [HttpInterceptor](api/common/http/HttpInterceptor).
<code-example language="typescript" header="universal-interceptor.ts">
import {Injectable, Inject, Optional} from '@angular/core';
import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http';
import {Request} from 'express';
import {REQUEST} from '@nguniversal/express-engine/tokens';
@Injectable()
export class UniversalInterceptor implements HttpInterceptor {
constructor(@Optional() @Inject(REQUEST) protected request?: Request) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
let serverReq: HttpRequest<any> = req;
if (this.request) {
let newUrl = `${this.request.protocol}://${this.request.get('host')}`;
if (!req.url.startsWith('/')) {
newUrl += '/';
}
newUrl += req.url;
serverReq = req.clone({url: newUrl});
}
return next.handle(serverReq);
}
}
</code-example>
Next, provide the interceptor in the providers for the server `AppModule`.
<code-example language="typescript" header="app.server.module.ts">
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {UniversalInterceptor} from './universal-interceptor';
@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: UniversalInterceptor,
multi: true
}],
})
export class AppServerModule {}
</code-example>
Now, on every HTTP request made on the server, this interceptor will fire and replace the request URL with the absolute
URL provided in the Express `Request` object.
{@a universal-engine}
### Universal template engine
@ -262,16 +196,10 @@ The important bit in the `server.ts` file is the `ngExpressEngine()` function.
</code-example>
The `ngExpressEngine()` function is a wrapper around Universal's `renderModule()` function which turns a client's
requests into server-rendered HTML pages.
requests into server-rendered HTML pages. It accepts an object with the following properties:
* The first parameter is `AppServerModule`.
It's the bridge between the Universal server-side renderer and the Angular application.
* The second parameter, `extraProviders`, is optional. It lets you specify dependency providers that apply only when
running on this server.
You can do this when your app needs information that can only be determined by the currently running server instance.
One example could be the running server's *origin*, which could be used to [calculate absolute HTTP URLs](#http-urls) if
not using the `Request` token as shown above.
* `bootstrap`: The root `NgModule` or `NgModule` factory to use for bootstraping the app when rendering on the server. For the example app, it is `AppServerModule`. It's the bridge between the Universal server-side renderer and the Angular application.
* `extraProviders`: This is optional and lets you specify dependency providers that apply only when rendering the app on the server. You can do this when your app needs information that can only be determined by the currently running server instance.
The `ngExpressEngine()` function returns a `Promise` callback that resolves to the rendered page.
It's up to the engine to decide what to do with that page.
@ -287,7 +215,7 @@ which then forwards it to the client in the HTTP response.
### Filtering request URLs
NOTE: the basic behavior described below is handled automatically when using the NgUniversal Express schematic, this
NOTE: The basic behavior described below is handled automatically when using the NgUniversal Express schematic. This
is helpful when trying to understand the underlying behavior or replicate it without using the schematic.
The web server must distinguish _app page requests_ from other kinds of requests.
@ -307,8 +235,8 @@ Because we use routing, we can easily recognize the three types of requests and
1. **App navigation**: request URL with no file extension.
1. **Static asset**: all other requests.
A Node Express server is a pipeline of middleware that filters and processes requests one after the other.
You configure the Node Express server pipeline with calls to `app.get()` like this one for data requests.
A Node.js Express server is a pipeline of middleware that filters and processes requests one after the other.
You configure the Node.js Express server pipeline with calls to `server.get()` like this one for data requests.
<code-example path="universal/server.ts" header="server.ts (data URL)" region="data-request"></code-example>
@ -328,13 +256,32 @@ The following code filters for request URLs with no extensions and treats them a
### Serving static files safely
A single `app.use()` treats all other URLs as requests for static assets
A single `server.use()` treats all other URLs as requests for static assets
such as JavaScript, image, and style files.
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in
the `/dist` folder and only honor requests for files from the `/dist` folder.
The following Node Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the
The following Node.js Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the
file isn't found.
<code-example path="universal/server.ts" header="server.ts (static files)" region="static"></code-example>
### Using absolute URLs for HTTP (data) requests on the server
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data.
These services send requests to _relative_ URLs such as `api/heroes`.
In a server-side rendered app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`).
This means that the URLs must be somehow converted to absolute when running on the server and be left relative when running in the browser.
If you are using one of the `@nguniversal/*-engine` packages (such as `@nguniversal/express-engine`), this is taken care for you automatically.
You don't need to do anything to make relative URLs work on the server.
If, for some reason, you are not using an `@nguniversal/*-engine` package, you may need to handle it yourself.
The recommended solution is to pass the full request URL to the `options` argument of [renderModule()](api/platform-server/renderModule) or [renderModuleFactory()](api/platform-server/renderModuleFactory) (depending on what you use to render `AppServerModule` on the server).
This option is the least intrusive as it does not require any changes to the app.
Here, "request URL" refers to the URL of the request as a response to which the app is being rendered on the server.
For example, if the client requested `https://my-server.com/dashboard` and you are rendering the app on the server to respond to that request, `options.url` should be set to `https://my-server.com/dashboard`.
Now, on every HTTP request made as part of rendering the app on the server, Angular can correctly resolve the request URL to an absolute URL, using the provided `options.url`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View File

@ -44,15 +44,6 @@
"groups": ["Angular"],
"lead": "juleskremer"
},
"robwormald": {
"name": "Rob Wormald",
"picture": "rob-wormald.jpg",
"twitter": "robwormald",
"website": "http://github.com/robwormald",
"bio": "Rob is a Developer Advocate on the Angular team at Google. He's the Angular team's resident reactive programming geek and founded the Reactive Extensions for Angular project, ngrx.",
"groups": ["Angular"],
"lead": "stephenfluin"
},
"alexeagle": {
"name": "Alex Eagle",
"picture": "alex-eagle.jpg",
@ -667,6 +658,13 @@
"groups": ["Angular"],
"lead": "dennispbrown"
},
"rockument69": {
"name": "Tony Bove",
"picture": "rockument69.jpg",
"bio": "Tony is a technical writer with Expert Support. His lifelong passions are helping people use technology, writing fiction, and playing music. When he's not working or playing the harmonica with friends in a bluegrass band, he's swimming and snorkeling on a Kauai beach and playing ball with his Irish Wolfhound. He's worked at home for decades before it became a thing.",
"groups": ["Angular"],
"lead": "aikidave"
},
"kapunahelewong": {
"name": "Kapunahele Wong",
"picture": "kapunahele.jpg",
@ -835,5 +833,12 @@
"bio": "Manu heads technical program management for Angular at Google. Manu keeps the big picture in focus and works with cross-functional teams to plan, execute and usher programs through the entire lifecycle.",
"groups": ["Angular"],
"lead": "juleskremer"
},
"anneiyw": {
"name": "Annie Wang",
"picture": "annieyw.jpg",
"bio": "Annie is an engineering resident on the Angular Components team at Google. She is passionate about the intersection between design and technology and enjoys drawing in her free time.",
"groups": ["Angular"],
"lead": "jelbourn"
}
}

View File

@ -101,6 +101,12 @@ This section walks you through using the cart service to add a product to the ca
<code-example header="src/app/product-details/product-details.component.html" path="getting-started/src/app/product-details/product-details.component.html">
</code-example>
<div class="alert is-helpful">
The line, `<h4>{{ product.price | currency }}</h4>` uses the `currency` pipe to transform `product.price` from a number to a currency string. A pipe is a way you can transform data in your HTML template. For more information about Angular pipes, see [Pipes](guide/pipes "Pipes").
</div>
1. To see the new "Buy" button, refresh the application and click on a product's name to display its details.

View File

@ -23,7 +23,7 @@
"build-local-with-viewengine": "yarn ~~build",
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 56c648827",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 31ac61357",
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn update-webdriver",

View File

@ -12,4 +12,4 @@ source ../scripts/ci/payload-size.sh
# Provide node_modules from aio
NODE_MODULES_BIN=$PROJECT_ROOT/aio/node_modules/.bin/
trackPayloadSize "$target" "dist/*.js" true "${thisDir}/_payload-limits.json"
trackPayloadSize "$target" "dist/*.js" true "$PROJECT_ROOT/goldens/size-tracking/aio-payloads.json"

View File

@ -18,7 +18,6 @@
],
"devDependencies": [
"@angular/compiler-cli",
"@angular/platform-server",
"@types/jasmine",
"@types/node",
"jasmine-core",

View File

@ -1,27 +1,29 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" },
{ "name": "build:ssr", "command": "npm run build:client-and-server-bundles && npm run webpack:server" },
{ "name": "serve:ssr", "command": "node dist/server.js" },
{ "name": "build:client-and-server-bundles", "command": "ng build --prod && ng run angular.io-example:server" },
{ "name": "webpack:server", "command": "webpack --config webpack.server.config.js --progress --colors" }
{ "name": "dev:ssr", "command": "ng run angular.io-example:serve-ssr" },
{ "name": "build:ssr", "command": "ng build --prod && ng run angular.io-example:server:production" },
{ "name": "serve:ssr", "command": "node dist/server/main.js" },
{ "name": "prerender", "command": "ng run angular.io-example:prerender" }
],
"dependencies": [
"@angular/platform-server",
"@nguniversal/express-engine",
"@nguniversal/module-map-ngfactory-loader"
"express"
],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@nguniversal/builders",
"@types/express",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-loader",
"ts-node",
"webpack-cli"
"ts-node"
]
}

View File

@ -1,4 +1,4 @@
# How to update the CLI project
# How to update the CLI project
The Angular CLI default setup is updated using `ng update`. Any necessary file changes will be done automatically through migration schematics.
@ -46,5 +46,5 @@ The specific changes to each project type are listed below:
- Includes a `server` target in the `build` architect runners
- package.json
- Includes custom scripts for building the `server`
- Includes additional `dependencies` on `@nguniversal/common`, `@nguniversal/express-engine`, and `@nguniversal/module-map-ngfactory-loader`
- Includes `devDependencies` on `@angular/platform-server`, and `ts-loader`
- Includes additional `dependencies` on `@angular/platform-server`, `@nguniversal/express-engine`, and `express`
- Includes additional `devDependencies` on `@nguniversal/builders` and `@types/express`

View File

@ -122,8 +122,47 @@
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/server",
"main": "src/main.server.ts",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"sourceMap": false,
"optimization": true
}
}
},
"serve-ssr": {
"builder": "@nguniversal/builders:ssr-dev-server",
"options": {
"browserTarget": "angular.io-example:build",
"serverTarget": "angular.io-example:server"
},
"configurations": {
"production": {
"browserTarget": "angular.io-example:build:production",
"serverTarget": "angular.io-example:server:production"
}
}
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"browserTarget": "angular.io-example:build:production",
"serverTarget": "angular.io-example:server:production",
"routes": [
"/"
]
},
"configurations": {
"production": {}
}
}
}

View File

@ -9,10 +9,10 @@
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
"serve:ssr": "node dist/server.js",
"build:client-and-server-bundles": "ng build --prod && ng run angular.io-example:server",
"webpack:server": "webpack --config webpack.server.config.js --progress --colors"
"dev:ssr": "ng run angular.io-example:serve-ssr",
"serve:ssr": "node dist/server/main.js",
"build:ssr": "ng build --prod && ng run angular.io-example:server:production",
"prerender": "ng run angular.io-example:prerender"
},
"private": true,
"dependencies": {
@ -23,12 +23,11 @@
"@angular/forms": "~9.0.6",
"@angular/platform-browser": "~9.0.6",
"@angular/platform-browser-dynamic": "~9.0.6",
"@angular/platform-server": "~9.0.6",
"@angular/router": "~9.0.6",
"@nguniversal/common": "~9.0.1",
"@nguniversal/express-engine": "~9.0.1",
"@nguniversal/module-map-ngfactory-loader": "~9.0.0-next.9",
"angular-in-memory-web-api": "~0.9.0",
"express": "^4.17.1",
"express": "^4.15.2",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
"zone.js": "~0.10.3"
@ -38,8 +37,8 @@
"@angular/cli": "~9.0.6",
"@angular/compiler-cli": "~9.0.6",
"@angular/language-service": "~9.0.6",
"@angular/platform-server": "~9.0.6",
"@types/express": "^4.17.2",
"@nguniversal/builders": "^9.0.2",
"@types/express": "^4.17.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
@ -53,10 +52,8 @@
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"protractor": "~5.4.3",
"ts-loader": "^6.2.1",
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5",
"webpack-cli": "^3.3.10"
"typescript": "~3.7.5"
}
}

View File

@ -28,18 +28,17 @@
"@angular/forms": "~9.0.6",
"@angular/platform-browser": "~9.0.6",
"@angular/platform-browser-dynamic": "~9.0.6",
"@angular/platform-server": "~9.0.6",
"@angular/router": "~9.0.6",
"@angular/service-worker": "~9.0.6",
"@angular/upgrade": "~9.0.6",
"@nguniversal/common": "~9.0.1",
"@nguniversal/express-engine": "~9.0.1",
"@nguniversal/module-map-ngfactory-loader": "~9.0.0-next.9",
"@webcomponents/custom-elements": "^1.4.1",
"angular": "1.7.9",
"angular-in-memory-web-api": "~0.9.0",
"angular-route": "1.7.9",
"core-js": "^2.5.4",
"express": "^4.17.1",
"express": "^4.15.2",
"rxjs": "~6.5.4",
"systemjs": "0.19.39",
"tslib": "^1.10.0",
@ -50,13 +49,13 @@
"@angular/cli": "~9.0.6",
"@angular/compiler-cli": "~9.0.6",
"@angular/language-service": "~9.0.6",
"@angular/platform-server": "~9.0.6",
"@nguniversal/builders": "^9.0.2",
"@types/angular": "1.6.47",
"@types/angular-animate": "1.5.10",
"@types/angular-mocks": "1.6.0",
"@types/angular-resource": "1.5.14",
"@types/angular-route": "1.3.5",
"@types/express": "4.0.35",
"@types/express": "^4.17.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/jquery": "3.3.28",
@ -83,10 +82,8 @@
"rollup-plugin-node-resolve": "^4.0.0",
"rollup-plugin-uglify": "^1.0.1",
"source-map-explorer": "^1.3.2",
"ts-loader": "^6.2.1",
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5",
"webpack-cli": "^3.3.10"
"typescript": "~3.7.5"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -28,56 +28,100 @@ module.exports = function autoLinkCode(getDocFromAlias) {
return autoLinkCodeImpl;
function autoLinkCodeImpl() {
return (ast) => {
return (ast, file) => {
visit(ast, 'element', (node, ancestors) => {
// Only interested in code elements that:
// * do not have `no-auto-link` class
// * do not have an ignored language
// * are not inside links
if (autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)) &&
(!node.properties.className || !node.properties.className.includes('no-auto-link')) &&
!autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language) &&
ancestors.every(ancestor => !is(ancestor, 'a'))) {
visit(node, 'text', (node, ancestors) => {
// Only interested in text nodes that are not inside links
if (ancestors.every(ancestor => !is(ancestor, 'a'))) {
const parent = ancestors[ancestors.length - 1];
const index = parent.children.indexOf(node);
// Can we convert the whole text node into a doc link?
const docs = getDocFromAlias(node.value);
if (foundValidDoc(docs)) {
parent.children.splice(index, 1, createLinkNode(docs[0], node.value));
} else {
// Parse the text for words that we can convert to links
const nodes =
textContent(node)
.split(/([A-Za-z0-9_.-]+)/)
.filter(word => word.length)
.map((word, index, words) => {
// remove docs that fail the custom filter tests
const filteredDocs = autoLinkCodeImpl.customFilters.reduce(
(docs, filter) => filter(docs, words, index), getDocFromAlias(word));
return foundValidDoc(filteredDocs) ?
// Create a link wrapping the text node.
createLinkNode(filteredDocs[0], word) :
// this is just text so push a new text node
{type: 'text', value: word};
});
// Replace the text node with the links and leftover text nodes
Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
}
}
});
if (!isValidCodeElement(node, ancestors)) {
return;
}
visit(node, 'text', (node, ancestors) => {
const isInLink = isInsideLink(ancestors);
if (isInLink) {
return;
}
const parent = ancestors[ancestors.length - 1];
const index = parent.children.indexOf(node);
// Can we convert the whole text node into a doc link?
const docs = getDocFromAlias(node.value);
if (foundValidDoc(docs, node.value, file)) {
parent.children.splice(index, 1, createLinkNode(docs[0], node.value));
} else {
// Parse the text for words that we can convert to links
const nodes = getNodes(node, file);
// Replace the text node with the links and leftover text nodes
Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
}
});
});
};
}
function foundValidDoc(docs) {
return docs.length === 1 && !docs[0].internal &&
autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1;
function isValidCodeElement(node, ancestors) {
// Only interested in code elements that:
// * do not have `no-auto-link` class
// * do not have an ignored language
// * are not inside links
const isCodeElement = autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType));
const hasNoAutoLink = node.properties.className && node.properties.className.includes('no-auto-link');
const isLanguageSupported = !autoLinkCodeImpl.ignoredLanguages.includes(node.properties.language);
const isInLink = isInsideLink(ancestors);
return isCodeElement && !hasNoAutoLink && isLanguageSupported && !isInLink;
}
function isInsideLink(ancestors) {
return ancestors.some(ancestor => is(ancestor, 'a'));
}
function getNodes(node, file) {
return textContent(node)
.split(/([A-Za-z0-9_.-]+)/)
.filter(word => word.length)
.map((word, index, words) => {
// remove docs that fail the custom filter tests
const filteredDocs = autoLinkCodeImpl.customFilters.reduce(
(docs, filter) => filter(docs, words, index), getDocFromAlias(word));
return foundValidDoc(filteredDocs, word, file) ?
// Create a link wrapping the text node.
createLinkNode(filteredDocs[0], word) :
// this is just text so push a new text node
{type: 'text', value: word};
});
}
/**
* Validates the docs to be used to generate the links. The validation ensures
* that the docs are not `internal` and that the `docType` is supported. The `path`
* can be empty when the `API` is not public.
*
* @param {Array<Object>} docs An array of objects containing the doc details
*
* @param {string} keyword The keyword the doc applies to
*/
function foundValidDoc(docs, keyword, file) {
if (docs.length !== 1) {
return false;
}
var doc = docs[0];
const isInvalidDoc = doc.docType === 'member' && !keyword.includes('.');
if (isInvalidDoc) {
return false;
}
if (doc.path === '') {
var message = `
autoLinkCode: Doc path is empty for "${doc.id}" - link will not be generated for "${keyword}".
Please make sure if the doc should be public. If not, it should probably not be referenced in the docs.`;
file.message(message);
return false;
}
return !doc.internal && autoLinkCodeImpl.docTypes.includes(doc.docType);
}
function createLinkNode(doc, text) {

View File

@ -126,6 +126,24 @@ describe('autoLinkCode post-processor', () => {
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
});
it('should ignore code items that match an API doc but have no path set',
() => {
aliasMap.addDoc(
{docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: ''});
const doc = {docType: 'test-doc', renderedContent: '<code>MyClass</code>'};
processor.$process([doc]);
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
});
it('should ignore documents when the `docType` is set to `member` and the keyword doesn\'t include `.`',
() => {
aliasMap.addDoc(
{docType: 'member', id: 'MyEnum', aliases: ['MyEnum'], path: 'a/b/c'});
const doc = {docType: 'test-doc', renderedContent: '<code>MyEnum</code>'};
processor.$process([doc]);
expect(doc.renderedContent).toEqual('<code>MyEnum</code>');
});
it('should insert anchors for individual text nodes within a code block', () => {
aliasMap.addDoc({docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass'});
const doc = {

View File

@ -5,6 +5,6 @@
*/
module.exports = function ignoreGenericWords() {
const ignoredWords = new Set(['a', 'classes', 'create', 'error', 'group', 'request', 'target', 'value']);
const ignoredWords = new Set(['a', 'classes', 'create', 'error', 'group', 'request', 'target', 'value', '_']);
return (docs, words, index) => ignoredWords.has(words[index].toLowerCase()) ? [] : docs;
};

View File

@ -9,9 +9,10 @@ ts_library(
module_name = "@angular/dev-infra-private",
deps = [
"//dev-infra/commit-message",
"//dev-infra/format",
"//dev-infra/pullapprove",
"//dev-infra/ts-circular-dependencies",
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/node",
"@npm//@types/yargs",
"@npm//yargs",

View File

@ -10,6 +10,7 @@ import * as yargs from 'yargs';
import {tsCircularDependenciesBuilder} from './ts-circular-dependencies/index';
import {buildPullapproveParser} from './pullapprove/cli';
import {buildCommitMessageParser} from './commit-message/cli';
import {buildFormatParser} from './format/cli';
yargs.scriptName('ng-dev')
.demandCommand()
@ -17,6 +18,7 @@ yargs.scriptName('ng-dev')
.command('ts-circular-deps <command>', '', tsCircularDependenciesBuilder)
.command('pullapprove <command>', '', buildPullapproveParser)
.command('commit-message <command>', '', buildCommitMessageParser)
.command('format <command>', '', buildFormatParser)
.wrap(120)
.strict()
.parse();

View File

@ -13,7 +13,7 @@ ts_library(
module_name = "@angular/dev-infra-private/commit-message",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/node",
"@npm//@types/shelljs",
"@npm//@types/yargs",
@ -29,7 +29,7 @@ ts_library(
srcs = ["validate.spec.ts"],
deps = [
":commit-message",
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/events",
"@npm//@types/jasmine",
"@npm//@types/node",

View File

@ -11,6 +11,9 @@ import {parseCommitMessage, validateCommitMessage, ValidateCommitMessageOptions}
// Whether the provided commit is a fixup commit.
const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup;
// Extracts commit header (first line of commit message).
const extractCommitHeader = (m: string) => parseCommitMessage(m).header;
/** Validate all commits in a provided git commit range. */
export function validateCommitRange(range: string) {
// A random value is used as a string to allow for a definite split point in the git log result.
@ -35,11 +38,18 @@ export function validateCommitRange(range: string) {
const allCommitsInRangeValid = commits.every((m, i) => {
const options: ValidateCommitMessageOptions = {
disallowSquash: true,
nonFixupCommitHeaders: isNonFixup(m) ? undefined : commits.slice(0, i).filter(isNonFixup)
nonFixupCommitHeaders: isNonFixup(m) ?
undefined :
commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader)
};
return validateCommitMessage(m, options);
});
if (allCommitsInRangeValid) {
console.info('√ All commit messages in range valid.');
} else {
// Exit with a non-zero exit code if invalid commit messages have
// been discovered.
process.exit(1);
}
}

View File

@ -160,21 +160,12 @@ describe('validate-commit-message.js', () => {
});
describe('(squash)', () => {
it('should strip the `squash! ` prefix and validate the rest', () => {
const errorMessage = `The commit message header does not match the expected format.`;
// Valid messages.
expect(validateCommitMessage('squash! feat(core): add feature')).toBe(VALID);
expect(validateCommitMessage('squash! fix: a bug', {disallowSquash: false})).toBe(VALID);
// Invalid messages.
expect(validateCommitMessage('squash! fix a typo', {disallowSquash: false})).toBe(INVALID);
expect(lastError).toContain('squash! fix a typo');
expect(lastError).toContain(errorMessage);
expect(validateCommitMessage('squash! squash! fix: a bug')).toBe(INVALID);
expect(lastError).toContain('squash! squash! fix: a bug');
expect(lastError).toContain(errorMessage);
describe('without `disallowSquash`', () => {
it('should return commits as valid', () => {
expect(validateCommitMessage('squash! feat(core): add feature')).toBe(VALID);
expect(validateCommitMessage('squash! fix: a bug')).toBe(VALID);
expect(validateCommitMessage('squash! fix a typo')).toBe(VALID);
});
});
describe('with `disallowSquash`', () => {
@ -191,21 +182,10 @@ describe('validate-commit-message.js', () => {
describe('(fixup)', () => {
describe('without `nonFixupCommitHeaders`', () => {
it('should strip the `fixup! ` prefix and validate the rest', () => {
const errorMessage = `The commit message header does not match the expected format.`;
// Valid messages.
it('should return commits as valid', () => {
expect(validateCommitMessage('fixup! feat(core): add feature')).toBe(VALID);
expect(validateCommitMessage('fixup! fix: a bug')).toBe(VALID);
// Invalid messages.
expect(validateCommitMessage('fixup! fix a typo')).toBe(INVALID);
expect(lastError).toContain('fixup! fix a typo');
expect(lastError).toContain(errorMessage);
expect(validateCommitMessage('fixup! fixup! fix: a bug')).toBe(INVALID);
expect(lastError).toContain('fixup! fixup! fix: a bug');
expect(lastError).toContain(errorMessage);
expect(validateCommitMessage('fixup! fixup! fix: a bug')).toBe(VALID);
});
});

View File

@ -20,7 +20,7 @@ const SQUASH_PREFIX_RE = /^squash! /i;
const REVERT_PREFIX_RE = /^revert:? /i;
const TYPE_SCOPE_RE = /^(\w+)(?:\(([^)]+)\))?\:\s(.+)$/;
const COMMIT_HEADER_RE = /^(.*)/i;
const COMMIT_BODY_RE = /^.*\n\n(.*)/i;
const COMMIT_BODY_RE = /^.*\n\n([\s\S]*)$/;
/** Parse a full commit message into its composite parts. */
export function parseCommitMessage(commitMsg: string) {
@ -79,20 +79,32 @@ export function validateCommitMessage(
const config = getAngularDevConfig<'commitMessage', CommitMessageConfig>().commitMessage;
const commit = parseCommitMessage(commitMsg);
////////////////////////////////////
// Checking revert, squash, fixup //
////////////////////////////////////
// All revert commits are considered valid.
if (commit.isRevert) {
return true;
}
if (commit.isSquash && options.disallowSquash) {
error('The commit must be manually squashed into the target commit');
return false;
// All squashes are considered valid, as the commit will be squashed into another in
// the git history anyway, unless the options provided to not allow squash commits.
if (commit.isSquash) {
if (options.disallowSquash) {
error('The commit must be manually squashed into the target commit');
return false;
}
return true;
}
// If it is a fixup commit and `nonFixupCommitHeaders` is not empty, we only care to check whether
// there is a corresponding non-fixup commit (i.e. a commit whose header is identical to this
// commit's header after stripping the `fixup! ` prefix).
if (commit.isFixup && options.nonFixupCommitHeaders) {
if (!options.nonFixupCommitHeaders.includes(commit.header)) {
// Fixups commits are considered valid, unless nonFixupCommitHeaders are provided to check
// against. If `nonFixupCommitHeaders` is not empty, we check whether there is a corresponding
// non-fixup commit (i.e. a commit whose header is identical to this commit's header after
// stripping the `fixup! ` prefix), otherwise we assume this verification will happen in another
// check.
if (commit.isFixup) {
if (options.nonFixupCommitHeaders && !options.nonFixupCommitHeaders.includes(commit.header)) {
error(
'Unable to find match for fixup commit among prior commits: ' +
(options.nonFixupCommitHeaders.map(x => `\n ${x}`).join('') || '-'));
@ -102,6 +114,9 @@ export function validateCommitMessage(
return true;
}
////////////////////////////
// Checking commit header //
////////////////////////////
if (commit.header.length > config.maxLineLength) {
error(`The commit message header is longer than ${config.maxLineLength} characters`);
return false;
@ -122,6 +137,10 @@ export function validateCommitMessage(
return false;
}
//////////////////////////
// Checking commit body //
//////////////////////////
if (commit.bodyWithoutLinking.trim().length < config.minBodyLength) {
error(`The commit message body does not meet the minimum length of ${
config.minBodyLength} characters`);

View File

@ -0,0 +1,27 @@
load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library(
name = "format",
srcs = [
"cli.ts",
"config.ts",
"format.ts",
"run-commands-parallel.ts",
],
module_name = "@angular/dev-infra-private/format",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils",
"@npm//@types/cli-progress",
"@npm//@types/inquirer",
"@npm//@types/node",
"@npm//@types/shelljs",
"@npm//@types/yargs",
"@npm//cli-progress",
"@npm//inquirer",
"@npm//multimatch",
"@npm//shelljs",
"@npm//tslib",
"@npm//yargs",
],
)

45
dev-infra/format/cli.ts Normal file
View File

@ -0,0 +1,45 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as yargs from 'yargs';
import {allChangedFilesSince, allFiles} from '../utils/repo-files';
import {checkFiles, formatFiles} from './format';
/** Build the parser for the format commands. */
export function buildFormatParser(localYargs: yargs.Argv) {
return localYargs.help()
.strict()
.demandCommand()
.option('check', {
type: 'boolean',
default: process.env['CI'] ? true : false,
description: 'Run the formatter to check formatting rather than updating code format'
})
.command(
'all', 'Run the formatter on all files in the repository', {},
({check}) => {
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(allFiles());
})
.command(
'changed [shaOrRef]', 'Run the formatter on files changed since the provided sha/ref', {},
({shaOrRef, check}) => {
const sha = shaOrRef || 'master';
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(allChangedFilesSince(sha));
})
.command('files <files..>', 'Run the formatter on provided files', {}, ({check, files}) => {
const executionCmd = check ? checkFiles : formatFiles;
executionCmd(files);
});
}
if (require.main === module) {
buildFormatParser(yargs).parse();
}

View File

@ -0,0 +1,11 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export interface FormatConfig {
matchers: string[];
}

130
dev-infra/format/format.ts Normal file
View File

@ -0,0 +1,130 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {prompt} from 'inquirer';
import * as multimatch from 'multimatch';
import {join} from 'path';
import {getAngularDevConfig, getRepoBaseDir} from '../utils/config';
import {FormatConfig} from './config';
import {runInParallel} from './run-commands-parallel';
/** By default, run the formatter on all javascript and typescript files. */
const DEFAULT_MATCHERS = ['**/*.{t,j}s'];
/**
* Format provided files in place.
*/
export async function formatFiles(unfilteredFiles: string[]) {
// Whether any files failed to format.
let formatFailed = false;
// All files which formatting should be applied to.
const files = filterFilesByMatchers(unfilteredFiles);
console.info(`Formatting ${files.length} file(s)`);
// Run the formatter to format the files in place, split across (number of available
// cpu threads - 1) processess. The task is done in multiple processess to speed up
// the overall time of the task, as running across entire repositories takes a large
// amount of time.
// As a data point for illustration, using 8 process rather than 1 cut the execution
// time from 276 seconds to 39 seconds for the same 2700 files
await runInParallel(files, `${getFormatterBinary()} -i -style=file`, (file, code, _, stderr) => {
if (code !== 0) {
formatFailed = true;
console.error(`Error running clang-format on: ${file}`);
console.error(stderr);
console.error();
}
});
// The process should exit as a failure if any of the files failed to format.
if (formatFailed) {
console.error(`Formatting failed, see errors above for more information.`);
process.exit(1);
}
console.info(`√ Formatting complete.`);
process.exit(0);
}
/**
* Check provided files for formatting correctness.
*/
export async function checkFiles(unfilteredFiles: string[]) {
// All files which formatting should be applied to.
const files = filterFilesByMatchers(unfilteredFiles);
// Files which are currently not formatted correctly.
const failures: string[] = [];
console.info(`Checking format of ${files.length} file(s)`);
// Run the formatter to check the format of files, split across (number of available
// cpu threads - 1) processess. The task is done in multiple processess to speed up
// the overall time of the task, as running across entire repositories takes a large
// amount of time.
// As a data point for illustration, using 8 process rather than 1 cut the execution
// time from 276 seconds to 39 seconds for the same 2700 files.
await runInParallel(files, `${getFormatterBinary()} --Werror -n -style=file`, (file, code) => {
// Add any files failing format checks to the list.
if (code !== 0) {
failures.push(file);
}
});
if (failures.length) {
// Provide output expressing which files are failing formatting.
console.group('\nThe following files are out of format:');
for (const file of failures) {
console.info(` - ${file}`);
}
console.groupEnd();
console.info();
// If the command is run in a non-CI environment, prompt to format the files immediately.
let runFormatter = false;
if (!process.env['CI']) {
runFormatter = (await prompt({
type: 'confirm',
name: 'runFormatter',
message: 'Format the files now?',
})).runFormatter;
}
if (runFormatter) {
// Format the failing files as requested.
await formatFiles(failures);
process.exit(0);
} else {
// Inform user how to format files in the future.
console.info();
console.info(`To format the failing file run the following command:`);
console.info(` yarn ng-dev format files ${failures.join(' ')}`);
process.exit(1);
}
} else {
console.info('√ All files correctly formatted.');
process.exit(0);
}
}
/** Get the full path of the formatter binary to execute. */
function getFormatterBinary() {
return join(getRepoBaseDir(), 'node_modules/.bin/clang-format');
}
/** Filter a list of files to only contain files which are expected to be formatted. */
function filterFilesByMatchers(allFiles: string[]) {
const matchers =
getAngularDevConfig<'format', FormatConfig>().format.matchers || DEFAULT_MATCHERS;
const files = multimatch(allFiles, matchers, {dot: true});
console.info(`Formatting enforced on ${files.length} of ${allFiles.length} file(s)`);
return files;
}

View File

@ -0,0 +1,77 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Bar} from 'cli-progress';
import {cpus} from 'os';
import {exec} from 'shelljs';
const AVAILABLE_THREADS = Math.max(cpus().length - 1, 1);
type CallbackFunction = (file: string, code?: number, stdout?: string, stderr?: string) => void;
/**
* Run the provided commands in parallel for each provided file.
*
* A promise is returned, completed when the command has completed running for each file.
*/
export function runInParallel(providedFiles: string[], cmd: string, callback: CallbackFunction) {
return new Promise<void>((resolve) => {
if (providedFiles.length === 0) {
return resolve();
}
// The progress bar instance to use for progress tracking.
const progressBar =
new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total} files`, clearOnComplete: true});
// A local copy of the files to run the command on.
const files = providedFiles.slice();
// An array to represent the current usage state of each of the threads for parallelization.
const threads = new Array<boolean>(AVAILABLE_THREADS).fill(false);
// Recursively run the command on the next available file from the list using the provided
// thread.
function runCommandInThread(thread: number) {
// Get the next file.
const file = files.pop();
// If no file was pulled from the array, return as there are no more files to run against.
if (!file) {
return;
}
exec(
`${cmd} ${file}`,
{async: true, silent: true},
(code, stdout, stderr) => {
// Run the provided callback function.
callback(file, code, stdout, stderr);
// Note in the progress bar another file being completed.
progressBar.increment(1);
// If more files exist in the list, run again to work on the next file,
// using the same slot.
if (files.length) {
return runCommandInThread(thread);
}
// If not more files are available, mark the thread as unused.
threads[thread] = false;
// If all of the threads are false, as they are unused, mark the progress bar
// completed and resolve the promise.
if (threads.every(active => !active)) {
progressBar.stop();
resolve();
}
},
);
// Mark the thread as in use as the command execution has been started.
threads[thread] = true;
}
// Start the progress bar
progressBar.start(files.length, 0);
// Start running the command on files from the least in each available thread.
threads.forEach((_, idx) => runCommandInThread(idx));
});
}

View File

@ -4,6 +4,7 @@ ts_library(
name = "pullapprove",
srcs = [
"cli.ts",
"condition_evaluator.ts",
"group.ts",
"logging.ts",
"parse-yaml.ts",
@ -12,7 +13,7 @@ ts_library(
module_name = "@angular/dev-infra-private/pullapprove",
visibility = ["//dev-infra:__subpackages__"],
deps = [
"//dev-infra/utils:config",
"//dev-infra/utils",
"@npm//@types/minimatch",
"@npm//@types/node",
"@npm//@types/shelljs",

View File

@ -10,8 +10,11 @@ import {verify} from './verify';
/** Build the parser for the pullapprove commands. */
export function buildPullapproveParser(localYargs: yargs.Argv) {
return localYargs.help().strict().demandCommand().command(
'verify', 'Verify the pullapprove config', {}, () => verify());
return localYargs.help()
.strict()
.option('verbose', {alias: ['v'], description: 'Enable verbose logging'})
.demandCommand()
.command('verify', 'Verify the pullapprove config', {}, ({verbose}) => verify(verbose));
}
if (require.main === module) {

View File

@ -0,0 +1,99 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {IMinimatch, Minimatch} from 'minimatch';
/** Map that holds patterns and their corresponding Minimatch globs. */
const patternCache = new Map<string, IMinimatch>();
/**
* Context that is provided to conditions. Conditions can use various helpers
* that PullApprove provides. We try to mock them here. Consult the official
* docs for more details: https://docs.pullapprove.com/config/conditions.
*/
const conditionContext = {
'len': (value: any[]) => value.length,
'contains_any_globs': (files: PullApproveArray, patterns: string[]) => {
// Note: Do not always create globs for the same pattern again. This method
// could be called for each source file. Creating glob's is expensive.
return files.some(f => patterns.some(pattern => getOrCreateGlob(pattern).match(f)));
}
};
/**
* Converts a given condition to a function that accepts a set of files. The returned
* function can be called to check if the set of files matches the condition.
*/
export function convertConditionToFunction(expr: string): (files: string[]) => boolean {
// Creates a dynamic function with the specified expression. The first parameter will
// be `files` as that corresponds to the supported `files` variable that can be accessed
// in PullApprove condition expressions. The followed parameters correspond to other
// context variables provided by PullApprove for conditions.
const evaluateFn = new Function('files', ...Object.keys(conditionContext), `
return (${transformExpressionToJs(expr)});
`);
// Create a function that calls the dynamically constructed function which mimics
// the condition expression that is usually evaluated with Python in PullApprove.
return files => {
const result = evaluateFn(new PullApproveArray(...files), ...Object.values(conditionContext));
// If an array is returned, we consider the condition as active if the array is not
// empty. This matches PullApprove's condition evaluation that is based on Python.
if (Array.isArray(result)) {
return result.length !== 0;
}
return !!result;
};
}
/**
* Transforms a condition expression from PullApprove that is based on python
* so that it can be run inside JavaScript. Current transformations:
* 1. `not <..>` -> `!<..>`
*/
function transformExpressionToJs(expression: string): string {
return expression.replace(/not\s+/g, '!');
}
/**
* Superset of a native array. The superset provides methods which mimic the
* list data structure used in PullApprove for files in conditions.
*/
class PullApproveArray extends Array<string> {
constructor(...elements: string[]) {
super(...elements);
// Set the prototype explicitly because in ES5, the prototype is accidentally
// lost due to a limitation in down-leveling.
// https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work.
Object.setPrototypeOf(this, PullApproveArray.prototype);
}
/** Returns a new array which only includes files that match the given pattern. */
include(pattern: string): PullApproveArray {
return new PullApproveArray(...this.filter(s => getOrCreateGlob(pattern).match(s)));
}
/** Returns a new array which only includes files that did not match the given pattern. */
exclude(pattern: string): PullApproveArray {
return new PullApproveArray(...this.filter(s => !getOrCreateGlob(pattern).match(s)));
}
}
/**
* Gets a glob for the given pattern. The cached glob will be returned
* if available. Otherwise a new glob will be created and cached.
*/
function getOrCreateGlob(pattern: string) {
if (patternCache.has(pattern)) {
return patternCache.get(pattern)!;
}
const glob = new Minimatch(pattern, {dot: true});
patternCache.set(pattern, glob);
return glob;
}

View File

@ -5,162 +5,101 @@
* 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 {IMinimatch, Minimatch} from 'minimatch';
import {convertConditionToFunction} from './condition_evaluator';
import {PullApproveGroupConfig} from './parse-yaml';
/** A condition for a group. */
interface GroupCondition {
glob: string;
matcher: IMinimatch;
expression: string;
checkFn: (files: string[]) => boolean;
matchedFiles: Set<string>;
}
/** Result of testing files against the group. */
export interface PullApproveGroupResult {
groupName: string;
matchedIncludes: GroupCondition[];
matchedExcludes: GroupCondition[];
matchedConditions: GroupCondition[];
matchedCount: number;
unmatchedIncludes: GroupCondition[];
unmatchedExcludes: GroupCondition[];
unmatchedConditions: GroupCondition[];
unmatchedCount: number;
}
// Regex Matcher for contains_any_globs conditions
const CONTAINS_ANY_GLOBS_REGEX = /^'([^']+)',?$/;
// Regular expression that matches conditions for the global approval.
const GLOBAL_APPROVAL_CONDITION_REGEX = /^"global-(docs-)?approvers" not in groups.approved$/;
const CONDITION_TYPES = {
INCLUDE_GLOBS: /^contains_any_globs/,
EXCLUDE_GLOBS: /^not contains_any_globs/,
ATTR_LENGTH: /^len\(.*\)/,
GLOBAL_APPROVAL: /^"global-(docs-)?approvers" not in groups.approved$/,
};
// Name of the PullApprove group that serves as fallback. This group should never capture
// any conditions as it would always match specified files. This is not desired as we want
// to figure out as part of this tool, whether there actually are unmatched files.
const FALLBACK_GROUP_NAME = 'fallback';
/** A PullApprove group to be able to test files against. */
export class PullApproveGroup {
// Lines which were not able to be parsed as expected.
private misconfiguredLines: string[] = [];
// Conditions for the group for including files.
private includeConditions: GroupCondition[] = [];
// Conditions for the group for excluding files.
private excludeConditions: GroupCondition[] = [];
// Whether the group has file matchers.
public hasMatchers = false;
/** List of conditions for the group. */
conditions: GroupCondition[] = [];
constructor(public groupName: string, group: PullApproveGroupConfig) {
if (group.conditions) {
for (let condition of group.conditions) {
condition = condition.trim();
constructor(public groupName: string, config: PullApproveGroupConfig) {
this._captureConditions(config);
}
if (condition.match(CONDITION_TYPES.INCLUDE_GLOBS)) {
const [conditions, misconfiguredLines] = getLinesForContainsAnyGlobs(condition);
conditions.forEach(globString => this.includeConditions.push({
glob: globString,
matcher: new Minimatch(globString, {dot: true}),
matchedFiles: new Set<string>(),
}));
this.misconfiguredLines.push(...misconfiguredLines);
this.hasMatchers = true;
} else if (condition.match(CONDITION_TYPES.EXCLUDE_GLOBS)) {
const [conditions, misconfiguredLines] = getLinesForContainsAnyGlobs(condition);
conditions.forEach(globString => this.excludeConditions.push({
glob: globString,
matcher: new Minimatch(globString, {dot: true}),
matchedFiles: new Set<string>(),
}));
this.misconfiguredLines.push(...misconfiguredLines);
this.hasMatchers = true;
} else if (condition.match(CONDITION_TYPES.ATTR_LENGTH)) {
// Currently a noop as we do not take any action on this condition type.
} else if (condition.match(CONDITION_TYPES.GLOBAL_APPROVAL)) {
private _captureConditions(config: PullApproveGroupConfig) {
if (config.conditions && this.groupName !== FALLBACK_GROUP_NAME) {
return config.conditions.forEach(condition => {
const expression = condition.trim();
if (expression.match(GLOBAL_APPROVAL_CONDITION_REGEX)) {
// Currently a noop as we don't take any action for global approval conditions.
} else {
const errMessage =
`Unrecognized condition found, unable to parse the following condition: \n\n` +
`From the [${groupName}] group:\n` +
` - ${condition}` +
`\n\n` +
`Known condition regexs:\n` +
`${Object.entries(CONDITION_TYPES).map(([k, v]) => ` ${k} - ${v}`).join('\n')}` +
`\n\n`;
console.error(errMessage);
return;
}
try {
this.conditions.push({
expression,
checkFn: convertConditionToFunction(expression),
matchedFiles: new Set(),
});
} catch (e) {
console.error(`Could not parse condition in group: ${this.groupName}`);
console.error(` - ${expression}`);
console.error(`Error:`, e.message, e.stack);
process.exit(1);
}
}
});
}
}
/** Retrieve all of the lines which were not able to be parsed. */
getBadLines(): string[] {
return this.misconfiguredLines;
}
/** Retrieve the results for the Group, all matched and unmatched conditions. */
getResults(): PullApproveGroupResult {
const matchedIncludes = this.includeConditions.filter(c => !!c.matchedFiles.size);
const matchedExcludes = this.excludeConditions.filter(c => !!c.matchedFiles.size);
const unmatchedIncludes = this.includeConditions.filter(c => !c.matchedFiles.size);
const unmatchedExcludes = this.excludeConditions.filter(c => !c.matchedFiles.size);
const unmatchedCount = unmatchedIncludes.length + unmatchedExcludes.length;
const matchedCount = matchedIncludes.length + matchedExcludes.length;
return {
matchedIncludes,
matchedExcludes,
matchedCount,
unmatchedIncludes,
unmatchedExcludes,
unmatchedCount,
groupName: this.groupName,
};
}
/**
* Tests a provided file path to determine if it would be considered matched by
* the pull approve group's conditions.
*/
testFile(file: string) {
let matched = false;
this.includeConditions.forEach((includeCondition: GroupCondition) => {
if (includeCondition.matcher.match(file)) {
let matchedExclude = false;
this.excludeConditions.forEach((excludeCondition: GroupCondition) => {
if (excludeCondition.matcher.match(file)) {
// Add file as a discovered exclude as it is negating a matched
// include condition.
excludeCondition.matchedFiles.add(file);
matchedExclude = true;
}
});
// An include condition is only considered matched if no exclude
// conditions are found to matched the file.
if (!matchedExclude) {
includeCondition.matchedFiles.add(file);
matched = true;
testFile(filePath: string): boolean {
return this.conditions.every(({matchedFiles, checkFn, expression}) => {
try {
const matchesFile = checkFn([filePath]);
if (matchesFile) {
matchedFiles.add(filePath);
}
return matchesFile;
} catch (e) {
const errMessage = `Condition could not be evaluated: \n\n` +
`From the [${this.groupName}] group:\n` +
` - ${expression}` +
`\n\n${e.message} ${e.stack}\n\n`;
console.error(errMessage);
process.exit(1);
}
});
return matched;
}
/** Retrieve the results for the Group, all matched and unmatched conditions. */
getResults(): PullApproveGroupResult {
const matchedConditions = this.conditions.filter(c => !!c.matchedFiles.size);
const unmatchedConditions = this.conditions.filter(c => !c.matchedFiles.size);
return {
matchedConditions,
matchedCount: matchedConditions.length,
unmatchedConditions,
unmatchedCount: unmatchedConditions.length,
groupName: this.groupName,
};
}
}
/**
* Extract all of the individual globs from a group condition,
* providing both the valid and invalid lines.
*/
function getLinesForContainsAnyGlobs(lines: string) {
const invalidLines: string[] = [];
const validLines = lines.split('\n')
.slice(1, -1)
.map((glob: string) => {
const trimmedGlob = glob.trim();
const match = trimmedGlob.match(CONTAINS_ANY_GLOBS_REGEX);
if (!match) {
invalidLines.push(trimmedGlob);
return '';
}
return match[1];
})
.filter(globString => !!globString);
return [validLines, invalidLines];
}

View File

@ -5,26 +5,20 @@
* 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 {PullApproveGroupResult} from './group';
/** Create logs for each pullapprove group result. */
export function logGroup(group: PullApproveGroupResult, matched = true) {
const includeConditions = matched ? group.matchedIncludes : group.unmatchedIncludes;
const excludeConditions = matched ? group.matchedExcludes : group.unmatchedExcludes;
const conditions = matched ? group.matchedConditions : group.unmatchedConditions;
console.groupCollapsed(`[${group.groupName}]`);
if (includeConditions.length) {
console.group('includes');
includeConditions.forEach(
matcher => console.info(`${matcher.glob} - ${matcher.matchedFiles.size}`));
if (conditions.length) {
conditions.forEach(matcher => {
const count = matcher.matchedFiles.size;
console.info(`${count} ${count === 1 ? 'match' : 'matches'} - ${matcher.expression}`)
});
console.groupEnd();
}
if (excludeConditions.length) {
console.group('excludes');
excludeConditions.forEach(
matcher => console.info(`${matcher.glob} - ${matcher.matchedFiles.size}`));
console.groupEnd();
}
console.groupEnd();
}
/** Logs a header within a text drawn box. */
@ -39,4 +33,4 @@ export function logHeader(...params: string[]) {
console.info(`${fill(fillWidth, '─')}`);
console.info(`${fill(leftSpace, ' ')}${headerText}${fill(rightSpace, ' ')}`);
console.info(`${fill(fillWidth, '─')}`);
}
}

View File

@ -8,7 +8,7 @@
import {parse as parseYaml} from 'yaml';
export interface PullApproveGroupConfig {
conditions?: string;
conditions?: string[];
reviewers: {
users: string[],
teams?: string[],

View File

@ -15,11 +15,9 @@ import {PullApproveGroup} from './group';
import {logGroup, logHeader} from './logging';
import {parsePullApproveYaml} from './parse-yaml';
export function verify() {
export function verify(verbose = false) {
// Exit early on shelljs errors
set('-e');
// Whether to log verbosely
const VERBOSE_MODE = process.argv.includes('-v');
// Full path of the angular project directory
const PROJECT_DIR = getRepoBaseDir();
// Change to the Angular project directory
@ -39,24 +37,11 @@ export function verify() {
const groups = Object.entries(pullApprove.groups).map(([groupName, group]) => {
return new PullApproveGroup(groupName, group);
});
// PullApprove groups without matchers.
const groupsWithoutMatchers = groups.filter(group => !group.hasMatchers);
// PullApprove groups with matchers.
const groupsWithMatchers = groups.filter(group => group.hasMatchers);
// All lines from group conditions which are not parsable.
const groupsWithBadLines = groups.filter(g => !!g.getBadLines().length);
// If any groups contains bad lines, log bad lines and exit failing.
if (groupsWithBadLines.length) {
logHeader('PullApprove config file parsing failure');
console.info(`Discovered errors in ${groupsWithBadLines.length} groups`);
groupsWithBadLines.forEach(group => {
console.info(` - [${group.groupName}]`);
group.getBadLines().forEach(line => console.info(` ${line}`));
});
console.info(
`Correct the invalid conditions, before PullApprove verification can be completed`);
process.exit(1);
}
// PullApprove groups without conditions. These are skipped in the verification
// as those would always be active and cause zero unmatched files.
const groupsSkipped = groups.filter(group => !group.conditions.length);
// PullApprove groups with conditions.
const groupsWithConditions = groups.filter(group => !!group.conditions.length);
// Files which are matched by at least one group.
const matchedFiles: string[] = [];
// Files which are not matched by at least one group.
@ -64,14 +49,14 @@ export function verify() {
// Test each file in the repo against each group for being matched.
REPO_FILES.forEach((file: string) => {
if (groupsWithMatchers.filter(group => group.testFile(file)).length) {
if (groupsWithConditions.filter(group => group.testFile(file)).length) {
matchedFiles.push(file);
} else {
unmatchedFiles.push(file);
}
});
// Results for each group
const resultsByGroup = groupsWithMatchers.map(group => group.getResults());
const resultsByGroup = groupsWithConditions.map(group => group.getResults());
// Whether all group condition lines match at least one file and all files
// are matched by at least one group.
const verificationSucceeded =
@ -94,7 +79,7 @@ export function verify() {
*/
logHeader('PullApprove results by file');
console.groupCollapsed(`Matched Files (${matchedFiles.length} files)`);
VERBOSE_MODE && matchedFiles.forEach(file => console.info(file));
verbose && matchedFiles.forEach(file => console.info(file));
console.groupEnd();
console.groupCollapsed(`Unmatched Files (${unmatchedFiles.length} files)`);
unmatchedFiles.forEach(file => console.info(file));
@ -103,12 +88,12 @@ export function verify() {
* Group by group Summary
*/
logHeader('PullApprove results by group');
console.groupCollapsed(`Groups without matchers (${groupsWithoutMatchers.length} groups)`);
VERBOSE_MODE && groupsWithoutMatchers.forEach(group => console.info(`${group.groupName}`));
console.groupCollapsed(`Groups skipped (${groupsSkipped.length} groups)`);
verbose && groupsSkipped.forEach(group => console.info(`${group.groupName}`));
console.groupEnd();
const matchedGroups = resultsByGroup.filter(group => !group.unmatchedCount);
console.groupCollapsed(`Matched conditions by Group (${matchedGroups.length} groups)`);
VERBOSE_MODE && matchedGroups.forEach(group => logGroup(group));
verbose && matchedGroups.forEach(group => logGroup(group));
console.groupEnd();
const unmatchedGroups = resultsByGroup.filter(group => group.unmatchedCount);
console.groupCollapsed(`Unmatched conditions by Group (${unmatchedGroups.length} groups)`);

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