Compare commits

..

83 Commits

Author SHA1 Message Date
ca7ee794bf release: cut the v10.0.2 release 2020-06-30 11:40:40 -07:00
f9f2ba6faf docs: add Sonu Kapoor to the collaborator list (#37777)
After 6 months of continuous contributions, Sonu Kapoor did finally make
it into the collaborator list.

PR Close #37777
2020-06-30 10:47:55 -07:00
aea1d211d4 docs: update /config/app-package-json redirect (#37774)
With this change we change the redirect for `/config/app-package-json` from `https://webpack.js.org/configuration/optimization/#optimizationsideeffects` to `https://angular.io/guide/strict-mode#non-local-side-effects-in-applications`

The latter page has more details.

PR Close #37774
2020-06-30 10:45:53 -07:00
57a518a36d perf(compiler-cli): fix memory leak in retained incremental state (#37835)
Incremental compilation allows for the output state of one compilation to be
reused as input to the next compilation. This involves retaining references
to instances from prior compilations, which must be done carefully to avoid
memory leaks.

This commit fixes such a leak with a complicated retention chain:

* `TrackedIncrementalBuildStrategy` unnecessarily hangs on to the previous
  `IncrementalDriver` (state of the previous compilation) once the current
  compilation completes.

  In general this is unnecessary, but should be safe as long as the chain
  only goes back one level - if the `IncrementalDriver` doesn't retain any
  previous `TrackedIncrementalBuildStrategy` instances. However, this does
  happen:

* `NgCompiler` indirectly causes retention of previous `NgCompiler`
  instances (and thus previous `TrackedIncrementalBuildStrategy` instances)
  through accidental capture of the `this` context in a closure created in
  its constructor. This closure is wrapped in a `ts.ModuleResolutionCache`
  used to create a `ModuleResolver` class, which is passed to the program's
  `TraitCompiler` on construction.

* The `IncrementalDriver` retains a reference to the `TraitCompiler` of the
  previous compilation, completing the reference chain.

The final retention chain thus looks like:

* `TrackedIncrementalBuildStrategy` of current program
* `.previous`: `IncrementalDriver` of previous program
* `.lastGood.traitCompiler`: `TraitCompiler`
* `.handlers[..].moduleResolver.moduleResolutionCache`: cache
* (via `getCanonicalFileName` closure): `NgCompiler`
* `.incrementalStrategy`: `TrackedIncrementalBuildStrategy` of previous
  program.

The closure link is the "real" leak here. `NgCompiler` is creating a closure
for `getCanonicalFileName`, delegating to its
`this.adapter.getCanonicalFileName`, for the purposes of creating a
`ts.ModuleResolutionCache`. The fact that the closure references
`NgCompiler` thus eventually causes previous `NgCompiler` iterations to be
retained. This is also potentially problematic due to the shared nature of
`ts.ModuleResolutionCache`, which is potentially retained across multiple
compilations intentionally.

This commit fixes the first two links in the retention chain: the build
strategy is patched to not retain a `previous` pointer, and the `NgCompiler`
is patched to not create a closure in the first place, but instead pass a
bound function. This ensures that the `NgCompiler` does not retain previous
instances of itself in the first place, even if the build strategy does
end up retaining the previous incremental state unnecessarily.

The third link (`IncrementalDriver` unnecessarily retaining the whole
`TraitCompiler`) is not addressed in this commit as it's a more
architectural problem that will require some refactoring. However, the leak
potential of this retention is eliminated thanks to fixing the first two
issues.

PR Close #37835
2020-06-29 16:34:52 -07:00
29b83189b0 build(docs-infra): update to latest dgeni-packages (#37793)
This update of dgeni-packages to 0.28.4 fixes the
rendering of type initializers for classes and interfaces.

Closes #37694

PR Close #37793
2020-06-29 15:01:16 -07:00
1d3df7885d docs: correct the spelling mistake in observables error handling code (#36437)
This commit fixes a spelling error in the word error in the
observables.md guide. It is currently
spelled errror  and the mistake is not intentional.

PR Close #36437
2020-06-29 15:00:39 -07:00
fd06ffa2af docs: change definition of providedIn any (#35292)
change in the definition of providedIn:any any instance creates a singleton instance
for each lazy loaded module and one instance for eager loaded module

PR Close #35292
2020-06-29 15:00:01 -07:00
36a1622dd1 docs: correct left nav to remove duplicated page links (#37833)
The major sections Angular Libraries, Schematics, and CLI Builders appear twice, in their old location under Techniques, and in the new correct location under Extending Angular.

PR Close #37833
2020-06-29 14:57:37 -07:00
7a91b23cb5 fix(core): fake_async_fallback should have the same logic with fake-async (#37680)
PR https://github.com/angular/angular/pull/37523 failed when trying to use `rxjs delay` operator
inside `fakeAsync`, and the reasons are:

1. we need to import `rxjs-fake-async` patch to make the integration work.
2. since in `angular` repo, the bazel target `/tools/testing:node` not using `zone-testing` bundle,
instead it load `zone-spec` packages seperately, so it causes one issue which is the `zone.js/testing/fake-async`
package is not loaded, we do have a fallback logic under `packages/core/testing` calles `fake_async_fallback`,
but the logic is out of date with `fake-async` under `zone.js` package.

So this PR, I updated the content of `fake_async_fallback` to make it consistent with
`fake-async`. And I will make another PR to try to remove the `fallback` logic.

PR Close #37680
2020-06-29 12:22:52 -07:00
4b90b6a226 fix(ngcc): prevent including JavaScript sources outside of the package (#37596)
When ngcc creates an entry-point program, the `allowJs` option is enabled
in order to operate on the JavaScript source files of the entry-point.
A side-effect of this approach is that external modules that don't ship
declaration files will also have their JavaScript source files loaded
into the program, as the `allowJs` flag allows for them to be imported.
This may pose an issue in certain edge cases, where ngcc would inadvertently
operate on these external modules. This can introduce all sorts of undesirable
behavior and incompatibilities, e.g. the reflection host that is selected for
the entry-point's format could be incompatible with that of the external
module's JavaScript bundles.

To avoid these kinds of issues, module resolution that would resolve to
a JavaScript file located outside of the package will instead be rejected,
as if the file would not exist. This would have been the behavior when
`allowJs` is set to false, which is the case in typical Angular compilations.

Fixes #37508

PR Close #37596
2020-06-29 12:21:23 -07:00
b13daa4cdf refactor(ngcc): let isWithinPackage operate on paths instead of source files (#37596)
Changes `isWithinPackage` to take an `AbsoluteFsPath` instead of `ts.SourceFile`,
to allow for an upcoming change to use it when no `ts.SourceFile` is available,
but just a path.

PR Close #37596
2020-06-29 12:21:23 -07:00
0c6f026828 docs: Changing typo Stacblitz into Stackblitz in the Tour of Hereos tutorial docs page (#37794)
Changing the typo of Stacblitz into Stackblitz in the tour of hereos tutorial docs page since that is the actual name of the service

PR Close #37794
2020-06-29 12:17:41 -07:00
a2520bd267 docs: remove first person from 2 sentences (#37768)
This commit removes two instances of the first person in the
Dependency injection providers documentation.

PR Close #37768
2020-06-29 12:17:04 -07:00
b928a209a4 docs: add Amadou Sall to GDE page (#36509)
This commit adds Amadou Sall to the Angular GDE page along with a
biography, his role at Air France, and a photograph.

PR Close #36509
2020-06-29 12:16:23 -07:00
89e16ed6a5 fix(elements): fire custom element output events during component initialization (#37570)
Previously, event listeners for component output events attached on an
Angular custom element before inserting it into the DOM (i.e. before
instantiating the underlying component) didn't fire for events emitted
during initialization lifecycle hooks, such as `ngAfterContentInit`,
`ngAfterViewInit`, `ngOnChanges` (initial call) and `ngOnInit`.
The reason was that `NgElementImpl` [subscribed to events][1] _after_
calling [ngElementStrategy#connect()][2], which is where the
[initial change detection][3] takes place (running the initialization
lifecycle hooks).

This commit fixes this by:
1. Ensuring `ComponentNgElementStrategy#events` is defined and available
   for subscribing to, even before instantiating the component.
2. Changing `NgElementImpl` to subscribe to `NgElementStrategy#events`
   (if available) before calling `NgElementStrategy#connect()` (which
   initializes the component instance) if available.
3. Falling back to the old behavior (subscribing to `events` after
   calling `connect()` for strategies that do not initialize `events`
   before their `connect()` is run).

NOTE:
By falling back to the old behavior when `NgElementStrategy#events` is
not initialized before calling `NgElementStrategy#connect()`, we avoid
breaking existing custom `NgElementStrategy` implementations (with
@remackgeek's [ElementZoneStrategy][4] being a commonly used example).

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

[1]: c0143cb2ab/packages/elements/src/create-custom-element.ts (L167-L170)
[2]: c0143cb2ab/packages/elements/src/create-custom-element.ts (L164)
[3]: c0143cb2ab/packages/elements/src/component-factory-strategy.ts (L158)
[4]: f1b6699495/projects/elements-zone-strategy/src/lib/element-zone-strategy.ts

Fixes #36141

PR Close #37570
2020-06-29 10:33:40 -07:00
1a1f99af37 fix(ngcc): ensure lockfile is removed when analyzeFn fails (#37739)
Previously an error thrown in the `analyzeFn` would cause
the ngcc process to exit immediately without removing the
lockfile, and potentially before the unlocker process had been
successfully spawned resulting in the lockfile being orphaned
and left behind.

Now we catch these errors and remove the lockfile as needed.

PR Close #37739
2020-06-29 10:29:12 -07:00
df2cd37ed2 fix(core): error when invoking callbacks registered via ViewRef.onDestroy (#37543) (#37783)
Invoking a callback registered through `ViewRef.onDestroy` throws an error, because we weren't registering it correctly in the internal data structure. These changes also remove the `storeCleanupFn` function, because it was mostly identical to `storeCleanupWithContext` and was only used in one place.

Fixes #36213.

PR Close #37543

PR Close #37783
2020-06-29 10:27:39 -07:00
12a71bc6bc fix(core): determine required DOMParser feature availability (#36578) (#37783)
Verify that HTML parsing is supported in addition to DOMParser existence.
This maybe wasn't as important before when DOMParser was used just as a
fallback on Firefox, but now that DOMParser is the default choice, we need
to be more accurate.

PR Close #37783
2020-06-29 10:27:39 -07:00
7d270c235a refactor(core): split inert strategies to separate classes (#36578) (#37783)
The `inertDocument` member is only needed when using the InertDocument
strategy. By separating the DOMParser and InertDocument strategies into
separate classes, we can easily avoid creating the inert document
unnecessarily when using DOMParser.

PR Close #37783
2020-06-29 10:27:39 -07:00
b0b7248504 fix(core): do not trigger CSP alert/report in Firefox and Chrome (#36578) (#37783)
If [innerHTML] is used in a component and a Content-Security-Policy is set
that does not allow inline styles then Firefox and Chrome show the following
message:

> Content Security Policy: The page’s settings observed the loading of a
resource at self (“default-src”). A CSP report is being sent.

This message is caused because Angular is creating an inline style tag to
test for a browser bug that we use to decide what sanitization strategy to
use, which causes CSP violation errors if inline CSS is prohibited.

This test is no longer necessary, since the `DOMParser` is now safe to use
and the `style` based check is redundant.

In this fix, we default to using `DOMParser` if it is available and fall back
to `createHTMLDocument()` if needed. This is the approach used by DOMPurify
too.

The related unit tests in `html_sanitizer_spec.ts`, "should not allow
JavaScript execution when creating inert document" and "should not allow
JavaScript hidden in badly formed HTML to get through sanitization (Firefox
bug)", are left untouched to assert that the behavior hasn't changed in
those scenarios.

Fixes #25214.

PR Close #37783
2020-06-29 10:27:38 -07:00
78460c1848 test(core): update symbols used in the test app (#37785)
This commit updates the golden file that contains the set of symbols used in the test TODO app. The `storeCleanupFn` function was replaced by `storeCleanupWithContext` in 75b119eafc and this commit updates the golden file to reflect that.

PR Close #37785
2020-06-26 16:44:00 -07:00
75b119eafc fix(core): error when invoking callbacks registered via ViewRef.onDestroy (#37543)
Invoking a callback registered through `ViewRef.onDestroy` throws an error, because we weren't registering it correctly in the internal data structure. These changes also remove the `storeCleanupFn` function, because it was mostly identical to `storeCleanupWithContext` and was only used in one place.

Fixes #36213.

PR Close #37543
2020-06-26 15:02:43 -07:00
64b0ae93f7 fix(core): don't consider inherited NG_ELEMENT_ID during DI (#37574)
Special DI tokens like `ChangeDetectorRef` and `ElementRef` can provide a factory via `NG_ELEMENT_ID`. The problem is that we were reading it off the token as `token[NG_ELEMENT_ID]` which will go up the prototype chain if it couldn't be found on the current token, resulting in the private `ViewRef` API being exposed, because it extends `ChangeDetectorRef`.

These changes fix the issue by guarding the property access with `hasOwnProperty`.

Fixes #36235.

PR Close #37574
2020-06-26 15:01:21 -07:00
7c0b25f5a6 fix(language-service): incorrect autocomplete results on unknown symbol (#37518)
This commit fixes a bug whereby the language service would incorrectly
return HTML elements if autocomplete is requested for an unknown symbol.
This is because we walk through every possible scenario, and fallback to
element autocomplete if none of the scenarios match.

The fix here is to return results from interpolation if we know for sure
we are in a bound text. This means we will now return an empty results if
there is no suggestions.

This commit also refactors the code a little to make it easier to
understand.

PR Close #37518
2020-06-26 14:51:33 -07:00
07b5df3a19 release: cut the v10.0.1 release 2020-06-26 13:17:36 -07:00
e7023726f4 ci: exclude "docs" commit type from minBodyLength commit message validation (#37764)
docs commits are sometimes trivial (e.g. an obvious typo fix) and in such cases its very
akward to to write up 100 chars worth of text about why this typo fix is the best thing in the
world and why it is so important and crucial that we must know why we are fixing the typo
at all. After all most typos are not just typos. Or are they? We'll shall see...

PR Close #37764
2020-06-26 11:13:10 -07:00
a9ccd9254c feat(dev-infra): add support for minBodyLengthTypeExcludes to commit-message validation (#37764)
This feature will allow us to exclude certain commits from the 100 chars minBodyLength requirement for commit
messages which is hard to satisfy for commits that make trivial changes (e.g. fixing typos in docs or comments).

PR Close #37764
2020-06-26 11:13:10 -07:00
335f3271d2 refactor(core): throw more descriptive error message in case of invalid host element (#35916)
This commit replaces an assert with more descriptive error message that is thrown in case `<ng-template>` or `<ng-container>` is used as host element for a Component.

Resolves #35240.

PR Close #35916
2020-06-26 11:10:15 -07:00
7f93f7ef47 build: move shims_for_IE to third_party directory (#37624)
The shims_for_IE.js file contains vendor code that predates the third_party
directory. This file is currently used for internal karma testing setup. This
change corrects this by moving the shims_for_IE file to //third_part/

PR Close #37624
2020-06-26 11:09:02 -07:00
cf46a87fcd refactor(compiler-cli): Remove any cast for CompilerHost (#37079)
This commit removes the FIXME for casting CompilerHost to any since
google3 is now already on TS 3.8.

PR Close #37079
2020-06-26 11:08:18 -07:00
ad6680f602 fix(language-service): reinstate getExternalFiles() (#37750)
`getExternalFiles()` is an API that could optionally be provided by a tsserver plugin
to notify the server of any additional files that should belong to a particular project.

This API was removed in https://github.com/angular/angular/pull/34260 mainly
due to performance reasons.

However, with the introduction of "solution-style" tsconfig in typescript 3.9,
the Angular extension could no longer reliably detect the owning Project solely
based on the ancestor tsconfig.json. In order to support this use case, we have
to reinstate `getExternalFiles()`.

Fixes https://github.com/angular/vscode-ng-language-service/issues/824

PR Close #37750
2020-06-26 09:57:08 -07:00
5e287f67af docs: correct outdated dev instructions for public api golds (#37026)
This change updates the dev instructions to reflect the location and generation of public API golds, which changed in #35768.

PR Close #37026
2020-06-26 09:56:33 -07:00
ecfe6e0609 docs: add note about the month being zero-based in the Date constructor (#37770)
Because the month is zero based, it may confuse some users that '3'
is in fact 'April'. This comment should clear that up.

PR Close #37770
2020-06-26 09:55:19 -07:00
df9790dd11 fix(dev-infra): support running scripts from within a detached head (#37737)
Scripts provided in the `ng-dev` command might use local `git`
commands. For such scripts, we keep track of the branch that
has been checked out before the command has been invoked.

We do this so that we can later (upon command completion)
restore back to the original branch. We do not want to
leave the Git repository in a dirty state.

It looks like this logic currently only deals with branches
but does not work properly when a command is invoked from
a detached head. We can make it work by just checking out
the previous revision (if no branch is checked out).

PR Close #37737
2020-06-26 09:51:10 -07:00
67cfc4c9bc build: add wombot proxy for publish config for @angular/benchpress (#37752)
Adds the publishConfig registry value to the package.json of the
@angular/benchpress package to publish it via wombat rather than
through npm directly.

PR Close #37752
2020-06-25 17:08:19 -07:00
a68e623c80 docs(elements): fixed command that adds the package @angular/elements (#37681)
I was using schematics with the `--name` parameter instead of the `--project`, I did both ways before sending and my suspicion about outdated documentation was confirmed

PR Close #37681
2020-06-25 17:07:30 -07:00
9e3915ba48 docs: typo fixes for schematics-for-libraries.md (#37753)
Addresses small typos such as extra whitespaces.

This change was extracted from #29505.
This change was extracted from #29505.
This change was extracted from #29505.

PR Close #37753
2020-06-25 17:06:38 -07:00
ba2de61748 fix(docs-infra): fix deploy-to-firebase.sh for master and v10.0.x branches (#37762)
The deployment to aio is currently failing because #37721 introduced
"project" entry into the firebase.json which means that we now need to
select the deployment target before deploying to firebase.

This change fixes the issue and refactors the file to be easier to read.

I also added extra echo statements so that the CI logs are easier to
read in case we need to troubleshoot future issues.

PR Close #37762
2020-06-25 17:03:25 -07:00
a9a4edebe2 fix(docs-infra): fix typo in the deploy-to-firebase.sh script (#37754)
This typo caused the script to fail on Linux (interestingly it works fine on Mac).

This is a painful reminder that we should not write any more Bash scripts EVER. shelljs FTW! :-)

PR Close #37754
2020-06-25 15:21:25 -07:00
64f2ffa166 fix(core): cleanup DOM elements when root view is removed (#37600)
Currently when bootstrapped component is being removed using `ComponentRef.destroy` or `NgModuleRef.destroy` methods, DOM nodes may be retained in the DOM tree. This commit fixes that problem by always attaching host element of the internal root view to the component's host view node, so the cleanup can happen correctly.

Resolves #36449.

PR Close #37600
2020-06-25 14:34:36 -07:00
13020b9cc2 fix(migrations): do not incorrectly add todo for @Injectable or @Pipe (#37732)
As of v10, the `undecorated-classes-with-decorated-fields` migration
generally deals with undecorated classes using Angular features. We
intended to run this migation as part of v10 again as undecorated
classes with Angular features are no longer supported in planned v11.

The migration currently behaves incorrectly in some cases where an
`@Injectable` or `@Pipe` decorated classes uses the `ngOnDestroy`
lifecycle hook. We incorrectly add a TODO for those classes. This
commit fixes that.

Additionally, this change makes the migration more robust to
not migrate a class if it inherits from a component, pipe
injectable or non-abstract directive. We previously did not
need this as the undecorated-classes-with-di migration ran
before, but this is no longer the case.

Last, this commit fixes an issue where multiple TODO's could be
added. This happens when multiple Angular CLI build targets have
an overlap in source files. Multiple programs then capture the
same source file, causing the migration to detect an undecorated
class multiple times (i.e. adding a TODO twice).

Fixes #37726.

PR Close #37732
2020-06-25 14:22:09 -07:00
96b96fba0f perf(compiler-cli): fix regressions in incremental program reuse (#37690)
Commit 4213e8d5 introduced shim reference tagging into the compiler, and
changed how the `TypeCheckProgramHost` worked under the hood during the
creation of a template type-checking program. This work enabled a more
incremental flow for template type-checking, but unintentionally introduced
several regressions in performance, caused by poor incrementality during
`ts.Program` creation.

1. The `TypeCheckProgramHost` was made to rely on the `ts.CompilerHost` to
   retrieve instances of `ts.SourceFile`s from the original program. If the
   host does not return the original instance of such files, but instead
   creates new instances, this has two negative effects: it incurs
   additional parsing time, and it interferes with TypeScript's ability to
   reuse information about such files.

2. During the incremental creation of a `ts.Program`, TypeScript compares
   the `referencedFiles` of `ts.SourceFile` instances from the old program
   with those in the new program. If these arrays differ, TypeScript cannot
   fully reuse the old program. The implementation of reference tagging
   introduced in 4213e8d5 restores the original `referencedFiles` array
   after a `ts.Program` is created, which means that future incremental
   operations involving that program will always fail this comparison,
   effectively limiting the incrementality TypeScript can achieve.

Problem 1 exacerbates problem 2: if a new `ts.SourceFile` is created by the
host after shim generation has been disabled, it will have an untagged
`referencedFiles` array even if the original file's `referencedFiles` was
not restored, triggering problem 2 when creating the template type-checking
program.

To fix these issues, `referencedFiles` arrays are now restored on the old
`ts.Program` prior to the creation of a new incremental program. This allows
TypeScript to get the most out of reusing the old program's data.

Additionally, the `TypeCheckProgramHost` now uses the original `ts.Program`
to retrieve original instances of `ts.SourceFile`s where possible,
preventing issues when a host would otherwise return fresh instances.

Together, these fixes ensure that program reuse is as incremental as
possible, and tests have been added to verify this for certain scenarios.

An optimization was further added to prevent the creation of a type-checking
`ts.Program` in the first place if no type-checking is necessary.

PR Close #37690
2020-06-25 14:13:34 -07:00
2cbe53a9ba docs: Uses correct component in the MessageService (#37666)
This commit uses the correct component (`HeroesComponent`) in the.
`MessageService`. Previously, the `MessageService` was using
`HeroeService`.

Closes #37654

PR Close #37666
2020-06-25 13:49:00 -07:00
48755114e5 feat(docs-infra): update deploy-to-firebase.sh script to support v9 multisite setup (#37721)
v9.angular.io was used to pilot the firebase hosting multisites setup for angular.io.

The deployments so far have been done manually to control the deployment process.

This change, automates the deployment for v9.angular.io so that future deployments can be made from
the CI.

See https://angular-team.atlassian.net/browse/DEV-125 for more info.

In the process of updating the scripts I rediscovered a bug in the deploy-to-firebase.sh script that
incorrect compared two numbers as strings. This previously worked correctly because we were comparing
single digit numbers. With the release of v10, we now compare 9 > 10 which behaves differently for
strings and numbers. The bug was fixed by switching to an arithmetic comparison of the two variables.

This bug has been fixed on the master branch but not on the 9.1.x branch. I realized this during the
rebase, but found my version to be a bit cleaner, so I kept it.

PR Close #37721
2020-06-25 13:44:53 -07:00
a5d5f67be7 fix(http): avoid abort a request when fetch operation is completed (#37367)
`abort` method is calling, even if fetch operation is completed

Fixes https://github.com/angular/angular/issues/36537

PR Close #37367
2020-06-25 12:09:40 -07:00
dfb58c44a2 fix(forms): correct usage of selectedOptions (#37620)
Previously, `registerOnChange` used `hasOwnProperty` to identify if the
property is supported. However, this does not work as the `selectedOptions`
property is an inherited property. This commit fixes this by verifying
the property on the prototype instead.

Closes #37433

PR Close #37620
2020-06-25 12:08:01 -07:00
69948ce919 fix(router): add null support for RouterLink directive (#32616)
Value of "undefined" passed as segment in routerLink is stringified to string "undefined".
This change introduces the same behavior for value of "null".

PR Close #32616
2020-06-25 11:58:01 -07:00
3190ccf3b2 fix(router): fix error when calling ParamMap.get function (#31599)
fix this.params.hasOwnProperty is not a function in case of creating an object using Object.create()

PR Close #31599
2020-06-25 11:57:25 -07:00
a8ea8173aa fix(router): RouterLinkActive should run CD when setting isActive (#21411)
When using the routerLinkActive directive inside a component that is using ChangeDetectionStrategy.OnPush and lazy loaded module routes the routerLinkActive directive does not update after clicking a link to a lazy loaded route that has not already been loaded.

Also the OnPush nav component does not set routerLinkActive correctly when the default route loads, the non-OnPush nav component works fine.

regression caused by #15943
closes #19934

PR Close #21411
2020-06-25 11:56:26 -07:00
e13a49d1f0 feat(dev-infra): add a way to pass assets down to a benchmark application (#37695)
* add a param called ng_assets to the component_benchmark macro to allow static assets to be provided to the base angular app, not just through the ts_devserver

PR Close #37695
2020-06-25 11:51:52 -07:00
2f0b8f675a docs: Add support schedule for v10 (#37745)
This commit adds the support schedule for v10.
v10.0.0 was released on June 24, 2020.
Active support ends six months later, on Dec 24, 2020.
Long term support ends a year after that, on Dec 24, 2021.

PR Close #37745
2020-06-25 11:49:18 -07:00
c2aed033ba ci(compiler-cli): exempt compiler-cli .bazel files from dev-infra approval (#37558)
Previously, dev-infra approval (via PullApprove) was required for all
.bazel files in the monorepo, including those in packages/compiler-cli.

The compiler-cli package is a little special in this sense:
 * it's not shipped to NPM in the APF
 * it uses lots of internal subpackages to organize and test its code

As a result:
 * changes to compiler-cli BUILD.bazel files are not user visible and
   don't have larger implications for the packages published to NPM,
   unlike changes to other BUILD.bazel files in the repo
 * the requirement for dev-infra approval for BUILD.bazel changes is
   overly burdensome, because compiler-cli build files change more
   rapidly than those of other packages.

This commit exempts the compiler-cli's build files from the requirement
for dev-infra approval. It will be sufficient for such files to be
approved by the normal compiler reviewers.

PR Close #37558
2020-06-25 11:47:51 -07:00
0f8a780b0d docs: Replace $emdash; with an actual em dash (#37723)
fix documentation in the lifecycle hooks guide where $emdash; was not being replaced by an actual em dash (-)

PR Close #37723
2020-06-25 11:41:54 -07:00
c5bc2e77c8 fix(forms): change error message (#37643)
Error message mention that ngModel and ngModelChange will be removed in Angular v7 but right not now sure when it will be removed so changed it to a future version

PR Close #37643
2020-06-25 11:37:00 -07:00
079310dc7c test(docs-infra): add end to end tests for api reference (#37612)
Api search functionality only had unit tests @gkalpak suggested we should have some e2e tests too. Added some end to end tests.

Fixes #35170

PR Close #37612
2020-06-25 11:36:03 -07:00
0d2cdf6165 docs: add v9.angular.io to the angular.io version picker (#37719)
Now that v10 is out, it's time to add the v9.angular.io link to the version picker, so here we go...

PR Close #37719
2020-06-24 19:53:25 -07:00
436dde271f docs(changelog): fix v10 announcement url (#37722)
PR Close #37722
2020-06-24 19:50:45 -07:00
96891a076f release: sort the v10.0.0 release in CHANGELOG.md 2020-06-24 15:58:06 -07:00
9ce0067bdf release: consolidate the v10.0.0 release CHANGELOG.md 2020-06-24 13:55:21 -07:00
345940bbc1 release: cut the v10.0.0 release 2020-06-24 11:46:57 -07:00
c49507b289 docs: updating guides and docs related to the v10 release (#37705)
Mostly just adding links to the migrations that were missing, adding the migrations into the navbar,
as well as correcting the @angular/bazel removal in the update guide.

I also added a commented out preamble for the release notes.

PR Close #37705
2020-06-24 09:37:35 -07:00
c730142508 Revert "docs: add lightweight token page to library section of docs (#36144)"
This reverts commit 27aa00b15f.
2020-06-24 09:29:01 -07:00
27aa00b15f docs: add lightweight token page to library section of docs (#36144)
adds new DI technique recommendation for libraries to ensure tree-shaking for unused services
includes reasons for packaging schematics with libraries, clarify schematic usage recommendation

PR Close #36144
2020-06-23 16:34:51 -07:00
36a00a255b ci(docs-infra): disable flaky tests (#37673)
I could not figure out the root cause of the flakes, so disabling the
flaky tests for now. See
https://github.com/angular/angular/pull/37637#issuecomment-647608149 for
more info.

Fixes #37629

PR Close #37673
2020-06-23 13:13:53 -07:00
0e3249c89b docs: add tslib update migration docs (#37402)
This adds documentation for the v10.0 tooling migration `update-libraries-tslib` contained within the `@schematics/angular` package.

PR Close #37402
2020-06-23 13:07:40 -07:00
920019ab70 docs: add module/target compiler option migration docs (#37429)
This adds documentation for the v10.0 tooling migration `update-module-and-target-compiler-options` contained within the `@schematics/angular` package.

PR Close #37429
2020-06-23 12:46:23 -07:00
82c8b44db7 docs: Initial commit of update guide for v10 release. (#37152)
This update includes modifications to the navigation.json file to
remove unneeded migration guides.

TODO: Redirects from v9 topics to v10; links to removed migration
guides need to point to v9.angular.io.

PR Close #37152
2020-06-23 11:56:50 -07:00
bb3a307d5a docs: add solution-style tsconfig migration docs (#37512)
This adds documentation for the v10.0 tooling migration `solution-style-tsconfig` contained within the `@schematics/angular` package.

PR Close #37512
2020-06-23 11:55:02 -07:00
dcb0ddaf5e docs: add strict-mode.md file to pullapprove (#37679)
When I created the strict-mode.md file, I didn't add it to the
pullapprove list. Now it's there.

The issue was introduced in #37486.

PR Close #37679
2020-06-23 10:05:51 -07:00
4c1edd52c5 docs: Add content for new strict mode for Angular CLI (#37486)
In v10, the Angular CLI supports a strict mode, which turns
on additional flags for the TypeScript and Angular compilers.

PR Close #37486
2020-06-22 16:29:16 -07:00
d512e27979 docs: fix invalid anchor for CLI flags in deprecation guide (#37662)
7521834296 added content for CLI
deprecations to the `angular.io` deprecations guide. It looks
like the anchor for the CLI deprecations is incorrect and
ends up showing up as code in the guide.

This commit fixes the anchor so that it doesn't show
up as code in the guide.

PR Close #37662
2020-06-22 10:57:16 -07:00
0619d82e0b docs: Refactor-i18n (#36924)
Rewrite headings to focus on tasks and separate reference info and best practices from tasks. Add missing steps or procedures, and links to important information. Make the example open in StackBlitz. See i18n Documentation Plan at https://docs.google.com/document/d/1aV2TKsIqry7JnNiHEfhmheu5rNAbNl1IRYVhYErc7Ks/edit?usp=sharing

PR Close #36924
2020-06-22 10:53:00 -07:00
a4038d5b94 Revert "fix(router): fix navigation ignoring logic to compare to the browser url (#37408)" (#37650)
This reverts commit d3a817549b.

The reason for the revert is the problem reported in g3 which requires additional investigation.

PR Close #37650
2020-06-22 10:47:47 -07:00
e3d5e1fab7 docs: fix grammatical errors in developer docs (#37633)
The CONTRIBUTOR and DEVELOPER markdown docs contained a few typos
and grammatical errors, which are fixed in this commit.

PR Close #37633
2020-06-18 16:04:59 -07:00
035036308a docs(core): correct type for opts.read (#37626)
The ContentChildren decorator has a metadata property named "read" which
can be used to read a different token from the queried elements. The
documentation incorrectly says "True to read..." when it should say
"Used to read...".

PR Close #37626
2020-06-18 16:04:10 -07:00
0d29259d9b docs: move ng-vikings 2020 to the already presented section (#37466)
This commit moves the ng-vikings 2020 event from the currently presenting
section into the already presented section.

PR Close #37466
2020-06-17 11:18:48 -07:00
26b0f3dc96 docs: add side effect package.json in app structure (#37521)
With this change we add the special `package.json` which is used to mark the application free of non-local side-effects in the application source files section

PR Close #37521
2020-06-16 11:57:40 -07:00
5c9306b0fe docs: @angular/language‑service is no longer a dev-dependencies (#37521)
`@angular/
language‑service` is no longer needed as a dev-dependencies, infact we no longer generate project with this dependency as it can conflict with the https://marketplace.visualstudio.com/items?itemName=Angular.ng-template

PR Close #37521
2020-06-16 11:57:40 -07:00
3befb0e4b9 docs: add new section for CLI deprecations (#37332)
Current documentation does not list CLI flag deprecations. This
change adds it for v10.

PR Close #37332
2020-06-16 11:57:05 -07:00
97bb88f10b docs: wrong example in routerLink (#37590)
In routerLink if a fragment is added than fragment example shows that it is added before the params '/user/bob#education?debug=true' but actually they are added after that '/user/bob?debug=true#education' changed documentation to show correct example

Fixes #18630

PR Close #37590
2020-06-16 09:45:23 -07:00
6c7467a58b perf(forms): optimize internal method _anyControls in FormGroup (#32534)
The method was previously looping through all controls, even after finding at least one that
satisfies the provided condition. This can be a bottleneck with large forms. The new version
of the method returns as soon as a single control which conforms to the condition is found.

PR Close #32534
2020-06-15 14:31:48 -07:00
c579a85c12 ci: include PR author as an approver of all PRs (#36915)
This change adds an implicit approval for any change by the
PR author.  This allows for a PR author to provide the required
owner approval for an area of the code base.

This change helps to align the review methodology with how Google's
internal system works. Where anyone is able to provide the LGTM
for a change if thats all that is needed.

PR Close #36915
2020-06-15 14:29:33 -07:00
400fdd08fd fix(dev-infra): allow for deep merging of pullapprove config aliases (#36915)
Set the yaml parser to support deep merges of yaml aliases,
to support having a default value for all rules to build upon.

PR Close #36915
2020-06-15 14:29:33 -07:00
160 changed files with 3944 additions and 2176 deletions

View File

@ -4,6 +4,7 @@ import {MergeConfig} from '../dev-infra/pr/merge/config';
const commitMessage = { const commitMessage = {
'maxLength': 120, 'maxLength': 120,
'minBodyLength': 100, 'minBodyLength': 100,
'minBodyLengthExcludes': ['docs'],
'types': [ 'types': [
'build', 'build',
'ci', 'ci',
@ -56,8 +57,6 @@ const format = {
// TODO: burn down format failures and remove aio and integration exceptions. // TODO: burn down format failures and remove aio and integration exceptions.
'!aio/**', '!aio/**',
'!integration/**', '!integration/**',
// TODO: remove this exclusion as part of IE deprecation.
'!shims_for_IE.js',
// Both third_party and .yarn are directories containing copied code which should // Both third_party and .yarn are directories containing copied code which should
// not be modified. // not be modified.
'!third_party/**', '!third_party/**',

View File

@ -34,41 +34,8 @@
#################################################################################### ####################################################################################
# GitHub usernames # GitHub usernames
#################################################################################### ####################################################################################
# aikidave - Dave Shevitz # See reviewer list under `required-minimum-review` group. Team member names and
# alan-agius4 - Alan Agius # usernames are managed there.
# alxhub - Alex Rickabaugh
# AndrewKushnir - Andrew Kushnir
# andrewseguin - Andrew Seguin
# atscott - Andrew Scott
# ayazhafiz - Ayaz Hafiz
# clydin - Charles Lyding
# crisbeto - Kristiyan Kostadinov
# dennispbrown - Denny Brown
# devversion - Paul Gschwendtner
# dgp1130 - Doug Parker
# filipesilva - Filipe Silva
# gkalpak - Georgios Kalpakas
# gregmagolan - Greg Magolan
# IgorMinar - Igor Minar
# jbogarthyde - Judy Bogart
# jelbourn - Jeremy Elbourn
# JiaLiPassion - Jia Li
# JoostK - Joost Koehoorn
# josephperrott - Joey Perrott
# juleskremer - Jules Kremer
# kapunahelewong - Kapunahele Wong
# kara - Kara Erickson
# kyliau - Keen Yee Liau
# manughub - Manu Murthy
# matsko - Matias Niemela
# mgechev - Minko Gechev
# mhevery - Miško Hevery
# michaelprentice - Michael Prentice
# mmalerba - Miles Malerba
# petebacondarwin - Pete Bacon Darwin
# pkozlowski-opensource - Pawel Kozlowski
# robwormald - Rob Wormald
# StephenFluin - Stephen Fluin
#################################################################################### ####################################################################################
@ -100,8 +67,16 @@ version: 3
# Meta field that goes unused by PullApprove to allow for defining aliases to be # Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config. # used throughout the config.
meta: meta:
1: &can-be-global-approved "\"global-approvers\" not in groups.approved" can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved" can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
defaults: &defaults
reviews:
# Authors provide their approval implicitly, this approval allows for a reviewer
# from a group not to need a review specifically for an area of the repository
# they own. This is coupled with the `required-minimum-review` group which requires
# that all PRs are reviewed by at least one team member who is not the author of
# the PR.
author_value: 1
# turn on 'draft' support # turn on 'draft' support
# https://docs.pullapprove.com/config/github-api-version/ # https://docs.pullapprove.com/config/github-api-version/
@ -121,6 +96,55 @@ pullapprove_conditions:
groups: groups:
# =========================================================
# Require review on all PRs
#
# All PRs require at least one review. This rule will not
# request any reviewers, however will require that at least
# one review is provided before the group is satisfied.
# =========================================================
required-minimum-review:
reviews:
request: 0 # Do not request any reviews from the reviewer group
required: 1 # Require that all PRs have approval from at least one of the users in the group
author_value: 0 # The author of the PR cannot provide an approval for themself
reviewers:
users:
- aikidave # Dave Shevitz
- alan-agius4 # Alan Agius
- alxhub # Alex Rickabaugh
- AndrewKushnir # Andrew Kushnir
- andrewseguin # Andrew Seguin
- atscott # Andrew Scott
- ayazhafiz # Ayaz Hafiz
- clydin # Charles Lyding
- crisbeto # Kristiyan Kostadinov
- dennispbrown # Denny Brown
- devversion # Paul Gschwendtner
- dgp1130 # Doug Parker
- filipesilva # Filipe Silva
- gkalpak # Georgios Kalpakas
- gregmagolan # Greg Magolan
- IgorMinar # Igor Minar
- jbogarthyde # Judy Bogart
- jelbourn # Jeremy Elbourn
- JiaLiPassion # Jia Li
- JoostK # Joost Koehoorn
- josephperrott # Joey Perrott
- juleskremer # Jules Kremer
- kapunahelewong # Kapunahele Wong
- kara # Kara Erickson
- kyliau # Keen Yee Liau
- manughub # Manu Murthy
- matsko # Matias Niemela
- mgechev # Minko Gechev
- mhevery # Miško Hevery
- michaelprentice # Michael Prentice
- mmalerba # Miles Malerba
- petebacondarwin # Pete Bacon Darwin
- pkozlowski-opensource # Pawel Kozlowski
- StephenFluin # Stephen Fluin
# ========================================================= # =========================================================
# Global Approvers # Global Approvers
# #
@ -161,6 +185,7 @@ groups:
# Framework: Animations # Framework: Animations
# ========================================================= # =========================================================
fw-animations: fw-animations:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -185,6 +210,7 @@ groups:
# Framework: Compiler # Framework: Compiler
# ========================================================= # =========================================================
fw-compiler: fw-compiler:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -209,6 +235,7 @@ groups:
# Framework: Compiler / ngcc # Framework: Compiler / ngcc
# ========================================================= # =========================================================
fw-ngcc: fw-ngcc:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -225,6 +252,7 @@ groups:
# Framework: Migrations # Framework: Migrations
# ========================================================= # =========================================================
fw-migrations: fw-migrations:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -240,6 +268,7 @@ groups:
# Framework: Core # Framework: Core
# ========================================================= # =========================================================
fw-core: fw-core:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -359,6 +388,7 @@ groups:
# Framework: Http # Framework: Http
# ========================================================= # =========================================================
fw-http: fw-http:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -380,6 +410,7 @@ groups:
# Framework: Elements # Framework: Elements
# ========================================================= # =========================================================
fw-elements: fw-elements:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -400,6 +431,7 @@ groups:
# Framework: Forms # Framework: Forms
# ========================================================= # =========================================================
fw-forms: fw-forms:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -432,6 +464,7 @@ groups:
# Framework: i18n # Framework: i18n
# ========================================================= # =========================================================
fw-i18n: fw-i18n:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -465,6 +498,7 @@ groups:
# Framework: Platform Server # Framework: Platform Server
# ========================================================= # =========================================================
fw-platform-server: fw-platform-server:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -484,6 +518,7 @@ groups:
# Framework: Router # Framework: Router
# ========================================================= # =========================================================
fw-router: fw-router:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -506,6 +541,7 @@ groups:
# Framework: Service Worker # Framework: Service Worker
# ========================================================= # =========================================================
fw-service-worker: fw-service-worker:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -533,6 +569,7 @@ groups:
# Framework: Upgrade # Framework: Upgrade
# ========================================================= # =========================================================
fw-upgrade: fw-upgrade:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -563,6 +600,7 @@ groups:
# Framework: Testing # Framework: Testing
# ========================================================= # =========================================================
fw-testing: fw-testing:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -584,6 +622,7 @@ groups:
# Framework: Benchmarks # Framework: Benchmarks
# ========================================================= # =========================================================
fw-benchmarks: fw-benchmarks:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -600,6 +639,7 @@ groups:
# Framework: Playground # Framework: Playground
# ========================================================= # =========================================================
fw-playground: fw-playground:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -617,6 +657,7 @@ groups:
# Framework: Security # Framework: Security
# ========================================================= # =========================================================
fw-security: fw-security:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -646,6 +687,7 @@ groups:
# Bazel # Bazel
# ========================================================= # =========================================================
bazel: bazel:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -664,6 +706,7 @@ groups:
# Language Service # Language Service
# ========================================================= # =========================================================
language-service: language-service:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -683,6 +726,7 @@ groups:
# zone.js # zone.js
# ========================================================= # =========================================================
zone-js: zone-js:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -701,6 +745,7 @@ groups:
# Benchpress # Benchpress
# ========================================================= # =========================================================
benchpress: benchpress:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -718,6 +763,7 @@ groups:
# Integration Tests # Integration Tests
# ========================================================= # =========================================================
integration-tests: integration-tests:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -735,6 +781,7 @@ groups:
# Docs: Gettings Started & Tutorial # Docs: Gettings Started & Tutorial
# ========================================================= # =========================================================
docs-getting-started-and-tutorial: docs-getting-started-and-tutorial:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -767,6 +814,7 @@ groups:
# Docs: Marketing # Docs: Marketing
# ========================================================= # =========================================================
docs-marketing: docs-marketing:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -789,6 +837,7 @@ groups:
# Docs: Observables # Docs: Observables
# ========================================================= # =========================================================
docs-observables: docs-observables:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -814,6 +863,7 @@ groups:
# Docs: Packaging, Tooling, Releasing # Docs: Packaging, Tooling, Releasing
# ========================================================= # =========================================================
docs-packaging-and-releasing: docs-packaging-and-releasing:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -833,7 +883,7 @@ groups:
'aio/content/guide/migration-localize.md', 'aio/content/guide/migration-localize.md',
'aio/content/guide/migration-module-with-providers.md', 'aio/content/guide/migration-module-with-providers.md',
'aio/content/guide/static-query-migration.md', 'aio/content/guide/static-query-migration.md',
'aio/content/guide/updating-to-version-9.md', 'aio/content/guide/updating-to-version-10.md',
'aio/content/guide/ivy-compatibility.md', 'aio/content/guide/ivy-compatibility.md',
'aio/content/guide/ivy-compatibility-examples.md' 'aio/content/guide/ivy-compatibility-examples.md'
]) ])
@ -873,6 +923,7 @@ groups:
# Docs: CLI # Docs: CLI
# ========================================================= # =========================================================
docs-cli: docs-cli:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -889,8 +940,12 @@ groups:
'aio/content/images/guide/deployment/**', 'aio/content/images/guide/deployment/**',
'aio/content/guide/file-structure.md', 'aio/content/guide/file-structure.md',
'aio/content/guide/ivy.md', 'aio/content/guide/ivy.md',
'aio/content/guide/strict-mode.md',
'aio/content/guide/web-worker.md', 'aio/content/guide/web-worker.md',
'aio/content/guide/workspace-config.md', 'aio/content/guide/workspace-config.md',
'aio/content/guide/migration-solution-style-tsconfig.md',
'aio/content/guide/migration-update-module-and-target-compiler-options.md',
'aio/content/guide/migration-update-libraries-tslib.md',
]) ])
reviewers: reviewers:
users: users:
@ -903,6 +958,7 @@ groups:
# Docs: CLI Libraries # Docs: CLI Libraries
# ========================================================= # =========================================================
docs-libraries: docs-libraries:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -923,6 +979,7 @@ groups:
# Docs: Schematics # Docs: Schematics
# ========================================================= # =========================================================
docs-schematics: docs-schematics:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -945,6 +1002,7 @@ groups:
# Docs-infra # Docs-infra
# ========================================================= # =========================================================
docs-infra: docs-infra:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- *can-be-global-docs-approved - *can-be-global-docs-approved
@ -974,10 +1032,11 @@ groups:
# Dev-infra # Dev-infra
# ========================================================= # =========================================================
dev-infra: dev-infra:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
contains_any_globs(files.exclude("CHANGELOG.md"), [ contains_any_globs(files.exclude("CHANGELOG.md").exclude("packages/compiler-cli/**/BUILD.bazel"), [
'*', '*',
'.circleci/**', '.circleci/**',
'.devcontainer/**', '.devcontainer/**',
@ -1038,6 +1097,7 @@ groups:
# Public API # Public API
# ========================================================= # =========================================================
public-api: public-api:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -1066,6 +1126,7 @@ groups:
# Size tracking # Size tracking
# ================================================ # ================================================
size-tracking: size-tracking:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -1088,6 +1149,7 @@ groups:
# Circular dependencies # Circular dependencies
# ================================================ # ================================================
circular-dependencies: circular-dependencies:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -1110,6 +1172,7 @@ groups:
# Code Ownership # Code Ownership
# ========================================================= # =========================================================
code-ownership: code-ownership:
<<: *defaults
conditions: conditions:
- *can-be-global-approved - *can-be-global-approved
- > - >
@ -1125,6 +1188,7 @@ groups:
# Catch all for if no groups match the code change # Catch all for if no groups match the code change
# ==================================================== # ====================================================
fallback: fallback:
<<: *defaults
# A group is considered to be `active` for a PR if at least one of group's # A group is considered to be `active` for a PR if at least one of group's
# conditions matches the PR. # conditions matches the PR.
# #

View File

@ -24,7 +24,7 @@ filegroup(
"//packages/zone.js/dist:zone-testing.js", "//packages/zone.js/dist:zone-testing.js",
"//packages/zone.js/dist:task-tracking.js", "//packages/zone.js/dist:task-tracking.js",
"//:test-events.js", "//:test-events.js",
"//:shims_for_IE.js", "//:third_party/shims_for_IE.js",
# Including systemjs because it defines `__eval`, which produces correct stack traces. # Including systemjs because it defines `__eval`, which produces correct stack traces.
"@npm//:node_modules/systemjs/dist/system.src.js", "@npm//:node_modules/systemjs/dist/system.src.js",
"@npm//:node_modules/reflect-metadata/Reflect.js", "@npm//:node_modules/reflect-metadata/Reflect.js",

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ Do not open issues for general support questions as we want to keep GitHub issue
Stack Overflow is a much better place to ask questions since: Stack Overflow is a much better place to ask questions since:
- there are thousands of people willing to help on Stack Overflow - there are thousands of people willing to help on Stack Overflow
- questions and answers stay available for public viewing so your question / answer might help someone else - questions and answers stay available for public viewing so your question/answer might help someone else
- Stack Overflow's voting system assures that the best answers are prominently visible. - Stack Overflow's voting system assures that the best answers are prominently visible.
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow. To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
@ -57,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem. A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it. We will be insisting on a minimal reproduction scenario in order to save maintainers' time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced. Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
@ -70,7 +70,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR 1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort. that relates to your submission. You don't want to duplicate effort.
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. 1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discussing the design up front helps to ensure that we're ready to accept your work. Discussing the design upfront helps to ensure that we're ready to accept your work.
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs. 1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository. We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
1. Fork the angular/angular repo. 1. Fork the angular/angular repo.
@ -85,8 +85,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc], 1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass. and ensure that all tests pass.
1. Commit your changes using a descriptive commit message that follows our 1. Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit). Adherence to these conventions [commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
is necessary because release notes are automatically generated from these messages.
```shell ```shell
git commit -a git commit -a
@ -181,13 +180,13 @@ Samples: (even more [samples](https://github.com/angular/angular/commits/master)
docs(changelog): update changelog to beta.5 docs(changelog): update changelog to beta.5
``` ```
``` ```
fix(release): need to depend on latest rxjs and zone.js fix(release): need to depend on the latest rxjs and zone.js
The version in our package.json gets copied to the one we publish, and users need the latest of these. The version in our package.json gets copied to the one we publish, and users need the latest of these.
``` ```
### Revert ### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted. If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type ### Type
Must be one of the following: Must be one of the following:
@ -282,7 +281,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
* https://help.github.com/articles/about-commit-email-addresses/ * https://help.github.com/articles/about-commit-email-addresses/
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/ * https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check. Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA before you commit changes. If not, your PR will fail the CLA check.
<hr> <hr>

View File

@ -0,0 +1,10 @@
{
"description": "i18n",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[0-9].*"
],
"file": "src/app/app.component.ts",
"tags": ["Angular", "i18n", "internationalization"]
}

View File

@ -6,5 +6,5 @@ import { Component } from '@angular/core';
templateUrl: './app.component.html' templateUrl: './app.component.html'
}) })
export class AppComponent { export class AppComponent {
birthday = new Date(1988, 3, 15); // April 15, 1988 birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
} }

View File

@ -8,5 +8,5 @@ import { Component } from '@angular/core';
// #enddocregion hero-birthday-template // #enddocregion hero-birthday-template
}) })
export class HeroBirthdayComponent { export class HeroBirthdayComponent {
birthday = new Date(1988, 3, 15); // April 15, 1988 birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
} }

View File

@ -12,7 +12,7 @@ import { Component } from '@angular/core';
}) })
// #docregion class // #docregion class
export class HeroBirthday2Component { export class HeroBirthday2Component {
birthday = new Date(1988, 3, 15); // April 15, 1988 birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
toggle = true; // start with true == shortDate toggle = true; // start with true == shortDate
get format() { return this.toggle ? 'shortDate' : 'fullDate'; } get format() { return this.toggle ? 'shortDate' : 'fullDate'; }

View File

@ -33,7 +33,7 @@ export class HeroesComponent implements OnInit {
onSelect(hero: Hero): void { onSelect(hero: Hero): void {
this.selectedHero = hero; this.selectedHero = hero;
this.messageService.add(`HeroService: Selected hero id=${hero.id}`); this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`);
} }
// #docregion getHeroes // #docregion getHeroes

View File

@ -197,11 +197,11 @@ Like `EvenBetterLogger`, `HeroService` needs to know if the user is authorized
That authorization can change during the course of a single application session, That authorization can change during the course of a single application session,
as when you log in a different user. as when you log in a different user.
Let's say you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information. Imagine that you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information.
`HeroService` won't have direct access to the user information to decide `HeroService` won't have direct access to the user information to decide
who is authorized and who isn't. who is authorized and who isn't.
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes. To resolve this, give the `HeroService` constructor a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example> <code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>

View File

@ -490,6 +490,56 @@ If you rely on the behavior that the same object instance should cause change de
- Clone the resulting value so that it has a new identity. - Clone the resulting value so that it has a new identity.
- Explicitly call [`ChangeDetectorRef.detectChanges()`](api/core/ChangeDetectorRef#detectchanges) to force the update. - Explicitly call [`ChangeDetectorRef.detectChanges()`](api/core/ChangeDetectorRef#detectchanges) to force the update.
{@a deprecated-cli-flags}
## Deprecated CLI APIs and Options
This section contains a complete list all of the currently deprecated CLI flags.
### @angular-devkit/build-angular
| API/Option | May be removed in | Notes |
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
| `rebaseRootRelativeCssUrls` | <!--v8--> v11 | Intended only to assist with specific migration issues. |
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
| `i18nFormat` | <!--v9--> v11 | Renamed to `format` to simplify the user experience. |
| `i18nLocale` | <!--v9--> v11 | Redundant with projects source locale. |
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
### @angular-devkit/core
| API/Option | May be removed in | Notes |
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
| `ModuleNotFoundException` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
| `resolve` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
| `setResolveHook` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
| `ResolveOptions` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
| `terminal` | <!--v8--> v10 | Unused implementation of terminal codes (color). |
| `isObservable` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Use `isObservable` function from the `rxjs` package.|
### @ngtools/webpack
| API/Option | May be removed in | Notes |
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
| `discoverLazyRoutes` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
| `additionalLazyModules` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
| `additionalLazyModuleResources` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
### @schematics/angular
| API/Option | May be removed in | Notes |
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
| `entryComponent` | <!--v9--> TBD | No longer needed with Ivy. |
{@a removed} {@a removed}
## Removed APIs ## Removed APIs

View File

@ -119,7 +119,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code. In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --name=*your_project_name*`. Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --project=*your_project_name*`.
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills). - For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
- For more information about Angular browser support, see [Browser Support](guide/browser-support). - For more information about Angular browser support, see [Browser Support](guide/browser-support).

View File

@ -78,6 +78,12 @@ Files at the top level of `src/` support testing and running your application. S
| `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. | | `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. |
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. | | `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
<div class="alert is-helpful">
If you create an application using Angular's strict mode, you will also have an additional `package.json` file in the `src/app` directory. For more information, see [Strict mode](/guide/strict-mode).
</div>
{@a app-src} {@a app-src}
Inside the `src/` folder, the `app/` folder contains your project's logic and data. Inside the `src/` folder, the `app/` folder contains your project's logic and data.
@ -90,6 +96,7 @@ Angular components, templates, and styles go here.
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. | | `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. | | `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. | | `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
| `app/package.json` | This file is generated only in applications created using `--strict` mode. This file is not used by package managers. It is used to tell the tools and bundlers whether the code under this directory is free of non-local [side-effects](guide/strict-mode#side-effect). |
### Application configuration files ### Application configuration files

File diff suppressed because it is too large Load Diff

View File

@ -2,18 +2,18 @@
The Angular team has worked hard to ensure Ivy is as backwards-compatible with the previous rendering engine ("View Engine") as possible. The Angular team has worked hard to ensure Ivy is as backwards-compatible with the previous rendering engine ("View Engine") as possible.
However, in rare cases, minor changes were necessary to ensure that the Angular's behavior was predictable and consistent, correcting issues in the View Engine implementation. However, in rare cases, minor changes were necessary to ensure that the Angular's behavior was predictable and consistent, correcting issues in the View Engine implementation.
In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-9#migrations) wherever possible so your application and library code is migrated automatically by the CLI. In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-10#migrations) wherever possible so your application and library code is migrated automatically by the CLI.
That said, some applications will likely need to apply some manual updates. That said, some applications will likely need to apply some manual updates.
{@a debugging} {@a debugging}
## How to debug errors with Ivy ## How to debug errors with Ivy
In version 9, [a few deprecated APIs have been removed](guide/updating-to-version-9#removals) and there are a [few breaking changes](guide/updating-to-version-9#breaking-changes) unrelated to Ivy. In version 10, [a few deprecated APIs have been removed](guide/updating-to-version-10#removals) and there are a [few breaking changes](guide/updating-to-version-10#breaking-changes) unrelated to Ivy.
If you're seeing errors after updating to version 9, you'll first want to rule those changes out. If you're seeing errors after updating to version 9, you'll first want to rule those changes out.
To do so, temporarily [turn off Ivy](guide/ivy#opting-out-of-angular-ivy) in your `tsconfig.base.json` and re-start your app. To do so, temporarily [turn off Ivy](guide/ivy#opting-out-of-angular-ivy) in your `tsconfig.base.json` and re-start your app.
If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 9 guide](guide/updating-to-version-9). If you've opted into any of the stricter type-checking settings that are new with v9, you may also want to check out the [template type-checking guide](guide/template-typecheck). If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 10 guide](guide/updating-to-version-10). If you've opted into any of the new, stricter type-checking settings, you may also want to check out the [template type-checking guide](guide/template-typecheck).
If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.base.json` and review the list of expected changes below. If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.base.json` and review the list of expected changes below.

View File

@ -495,7 +495,7 @@ for one turn of the browser's JavaScript cycle, which triggers a new change-dete
#### Write lean hook methods to avoid performance problems #### Write lean hook methods to avoid performance problems
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`$emdash;often when there are no changes of interest. When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`-often when there are no changes of interest.
Be very careful about how much logic or computation you put into one of these methods. Be very careful about how much logic or computation you put into one of these methods.
<div class="lightbox"> <div class="lightbox">

View File

@ -0,0 +1,54 @@
# Solution-style `tsconfig.json` migration
## What does this migration do?
This migration adds support to existing projects for TypeScript's new ["solution-style" tsconfig feature](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig).
Support is added by making two changes:
1. Renaming the workspace-level `tsconfig.json` to `tsconfig.base.json`.
All project [TypeScript configuration files](guide/typescript-configuration) will extend from this base which contains the common options used throughout the workspace.
2. Adding the solution `tsconfig.json` file at the root of the workspace.
This `tsconfig.json` file will only contain references to project-level TypeScript configuration files and is only used by editors/IDEs.
As an example, the solution `tsconfig.json` for a new project is as follows:
```json
// This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScripts language server to improve development experience.
// It is not intended to be used to perform a compilation.
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./e2e/tsconfig.json"
}
]
}
```
## Why is this migration necessary?
Solution-style `tsconfig.json` files provide an improved editing experience and fix several long-standing defects when editing files in an IDE.
IDEs that leverage the TypeScript language service (for example, [Visual Studio Code](https://code.visualstudio.com)), will only use TypeScript configuration files that are named `tsconfig.json`.
In complex projects, there may be more than one compilation unit and each of these units may have different settings and options.
With the Angular CLI, a project will have application code that will target a browser.
It will also have unit tests that should not be included within the built application and that also need additional type information present (`jasmine` in this case).
Both parts of the project also share some but not all of the code within the project.
As a result, two separate TypeScript configuration files (`tsconfig.app.json` and `tsconfig.spec.json`) are needed to ensure that each part of the application is configured properly and that the right types are used for each part.
Also if web workers are used within a project, an additional tsconfig (`tsconfig.worker.json`) is needed.
Web workers use similar but incompatible types to the main browser application.
This requires the additional configuration file to ensure that the web worker files use the appropriate types and will build successfully.
While the Angular build system knows about all of these TypeScript configuration files, an IDE using TypeScript's language service does not.
Because of this, an IDE will not be able to properly analyze the code from each part of the project and may generate false errors or make suggestions that are incorrect for certain files.
By leveraging the new solution-style tsconfig, the IDE can now be aware of the configuration of each part of a project.
This allows each file to be treated appropriately based on its tsconfig.
IDE features such as error/warning reporting and auto-suggestion will operate more effectively as well.
The TypeScript 3.9 release [blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig) also contains some additional information regarding this new feature.

View File

@ -0,0 +1,52 @@
# `tslib` direct dependency migration
## What does this migration do?
If you have any libraries within your workspace, this migration will convert `tslib` peer dependencies to direct dependencies for the libraries.
TypeScript uses the `tslib` package to provide common helper functions used in compiled TypeScript code.
The `tslib` version is also updated to `2.0.0` to support TypeScript 3.9.
Before:
```json
{
"name": "my-lib",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^9.0.0",
"@angular/core": "^9.0.0",
"tslib": "^1.12.0"
}
}
```
After:
```json
{
"name": "my-lib",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^9.0.0",
"@angular/core": "^9.0.0"
},
"dependencies": {
"tslib": "^2.0.0"
}
}
```
## Why is this migration necessary?
The [`tslib`](https://github.com/Microsoft/tslib) is a runtime library for Typescript.
The version of this library is bound to the version of the TypeScript compiler used to compile a library.
Peer dependencies do not accurately represent this relationship between the runtime and the compiler.
If `tslib` remained declared as a library peer dependency, it would be possible for some Angular workspaces to get into a state where the workspace could not satisfy `tslib` peer dependency requirements for multiple libraries, resulting in build-time or run-time errors.
As of TypeScript 3.9 (used by Angular v10), `tslib` version of 2.x is required to build new applications.
However, older libraries built with previous version of TypeScript and already published to npm might need `tslib` 1.x.
This migration makes it possible for code depending on incompatible versions of the `tslib` runtime library to remain interoperable.
## Do I still need `tslib` as a dependency in my workspace `package.json`?
Yes.
The `tslib` dependency declared in the `package.json` file of the workspace is used to build applications within this workspace, as well as run unit tests for workspace libraries, and is required.

View File

@ -0,0 +1,33 @@
# Update `module` and `target` compiler options migration
## What does this migration do?
This migration adjusts the [`target`](https://www.typescriptlang.org/v2/en/tsconfig#target) and [`module`](https://www.typescriptlang.org/v2/en/tsconfig#module) settings within the [TypeScript configuration files](guide/typescript-configuration) for the workspace.
The changes to each option vary based on the builder or command that uses the TypeScript configuration file.
Unless otherwise noted, changes are only made if the existing value was not changed since the project was created.
This process helps ensure that intentional changes to the options are kept in place.
TypeScript Configuration File(s) | Changed Property | Existing Value | New Value
------------- | ------------- | ------------- | ------------- | -------------
`<workspace base>/tsconfig.base.json` | `"module"` | `"esnext"` | `"es2020"`
Used in `browser` builder options (`ng build` for applications) | `"module"` | `"esnext"` | `"es2020"`
Used in `ng-packgr` builder options (`ng build` for libraries) | `"module"` | `"esnext"` | `"es2020"`
Used in `karma` builder options (`ng test` for applications) | `"module"` | `"esnext"` | `"es2020"`
Used in `server` builder options (universal) | `"module"` | `"commonjs"` | _removed_
Used in `server` builder options (universal) | `"target"` | _any_ | `"es2016"`
Used in `protractor` builder options (`ng e2e` for applications) | `"target"` | `"es5"` | `"es2018"`
## Why is this migration necessary?
This migration provides improvements to the long-term supportability of projects by updating the projects to use recommended best practice compilation options.
For the functionality that executes on Node.js, such as Universal and Protractor, the new settings provide performance and troubleshooting benefits as well.
The minimum Node.js version for the Angular CLI (v10.13) supports features in ES2018 and earlier.
By targeting later ES versions, the compiler transforms less code and can use newer features directly.
Since zone.js does not support native `async` and `await`, the universal builds still target ES2016.
## Why `"es2020"` instead of `"esnext"`?
In TypeScript 3.9, the behavior of the TypeScript compiler controlled by `module` is the same with both `"esnext"` and `"es2020"` values.
This behavior can change in the future, because the `"esnext"` option could evolve in a backwards incompatible ways, resulting in build-time or run-time errors during a TypeScript update.
As a result, code can become unstable. Using the `"es2020"` option mitigates this risk.

View File

@ -118,7 +118,6 @@ Package name | Description
[**@angular&#8209;devkit/<br />build&#8209;angular**](https://github.com/angular/angular-cli/) | The Angular build tools. [**@angular&#8209;devkit/<br />build&#8209;angular**](https://github.com/angular/angular-cli/) | The Angular build tools.
[**@angular/cli**](https://github.com/angular/angular-cli/) | The Angular CLI tools. [**@angular/cli**](https://github.com/angular/angular-cli/) | The Angular CLI tools.
**@angular/<br />compiler&#8209;cli** | The Angular compiler, which is invoked by the Angular CLI's `ng build` and `ng serve` commands. **@angular/<br />compiler&#8209;cli** | The Angular compiler, which is invoked by the Angular CLI's `ng build` and `ng serve` commands.
**@angular/<br />language&#8209;service** | The [Angular language service](guide/language-service) analyzes component templates and provides type and error information that TypeScript-aware editors can use to improve the developer's experience. For example, see the [Angular language service extension for VS Code](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template).
**@types/... ** | TypeScript definition files for 3rd party libraries such as Jasmine and Node.js. **@types/... ** | TypeScript definition files for 3rd party libraries such as Jasmine and Node.js.
[**codelyzer**](https://www.npmjs.com/package/codelyzer) | A linter for Angular apps whose rules conform to the Angular [style guide](guide/styleguide). [**codelyzer**](https://www.npmjs.com/package/codelyzer) | A linter for Angular apps whose rules conform to the Angular [style guide](guide/styleguide).
**jasmine/... ** | Packages to support the [Jasmine](https://jasmine.github.io/) test library. **jasmine/... ** | Packages to support the [Jasmine](https://jasmine.github.io/) test library.
@ -135,3 +134,4 @@ Package name | Description
* [Building and serving](guide/build) describes how packages come together to create a development build. * [Building and serving](guide/build) describes how packages come together to create a development build.
* [Deployment](guide/deployment) describes how packages come together to create a production build. * [Deployment](guide/deployment) describes how packages come together to create a production build.

View File

@ -112,7 +112,7 @@ Because observables produce values asynchronously, try/catch will not effectivel
<code-example> <code-example>
myObservable.subscribe({ myObservable.subscribe({
next(num) { console.log('Next num: ' + num)}, next(num) { console.log('Next num: ' + num)},
error(err) { console.log('Received an errror: ' + err)} error(err) { console.log('Received an error: ' + err)}
}); });
</code-example> </code-example>

View File

@ -101,6 +101,7 @@ The following table provides the status for Angular versions under support.
Version | Status | Released | Active Ends | LTS Ends Version | Status | Released | Active Ends | LTS Ends
------- | ------ | ------------ | ------------ | ------------ ------- | ------ | ------------ | ------------ | ------------
^10.0.0 | Active | Jun 24, 2020 | Dec 24, 2020 | Dec 24, 2021
^9.0.0 | Active | Feb 06, 2020 | Aug 06, 2020 | Aug 06, 2021 ^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 ^8.0.0 | LTS | May 28, 2019 | Nov 28, 2019 | Nov 28, 2020

View File

@ -1,7 +1,7 @@
# Schematics for libraries # Schematics for libraries
When you create an Angular library, you can provide and package it with schematics that integrate it with the Angular CLI. When you create an Angular library, you can provide and package it with schematics that integrate it with the Angular CLI.
With your schematics, your users can use `ng add` to install an initial version of your library, With your schematics, your users can use `ng add` to install an initial version of your library,
`ng generate` to create artifacts defined in your library, and `ng update` to adjust their project for a new version of your library that introduces breaking changes. `ng generate` to create artifacts defined in your library, and `ng update` to adjust their project for a new version of your library that introduces breaking changes.
All three types of schematics can be part of a collection that you package with your library. All three types of schematics can be part of a collection that you package with your library.
@ -115,10 +115,10 @@ When you add a schematic to the collection, you have to point to it in the colle
<code-example header="projects/my-lib/schematics/my-service/schema.json (Schematic JSON Schema)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.json"> <code-example header="projects/my-lib/schematics/my-service/schema.json (Schematic JSON Schema)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.json">
</code-example> </code-example>
* *id* : A unique id for the schema in the collection. * *id*: A unique id for the schema in the collection.
* *title* : A human-readable description of the schema. * *title*: A human-readable description of the schema.
* *type* : A descriptor for the type provided by the properties. * *type*: A descriptor for the type provided by the properties.
* *properties* : An object that defines the available options for the schematic. * *properties*: An object that defines the available options for the schematic.
Each option associates key with a type, description, and optional alias. Each option associates key with a type, description, and optional alias.
The type defines the shape of the value you expect, and the description is displayed when the user requests usage help for your schematic. The type defines the shape of the value you expect, and the description is displayed when the user requests usage help for your schematic.
@ -130,9 +130,9 @@ When you add a schematic to the collection, you have to point to it in the colle
<code-example header="projects/my-lib/schematics/my-service/schema.ts (Schematic Interface)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.ts"> <code-example header="projects/my-lib/schematics/my-service/schema.ts (Schematic Interface)" path="schematics-for-libraries/projects/my-lib/schematics/my-service/schema.ts">
</code-example> </code-example>
* *name* : The name you want to provide for the created service. * *name*: The name you want to provide for the created service.
* *path* : Overrides the path provided to the schematic. The default path value is based on the current working directory. * *path*: Overrides the path provided to the schematic. The default path value is based on the current working directory.
* *project* : Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user. * *project*: Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user.
### Add template files ### Add template files
@ -169,10 +169,9 @@ The Schematics framework provides a file templating system, which supports both
The system operates on placeholders defined inside files or paths that loaded in the input `Tree`. The system operates on placeholders defined inside files or paths that loaded in the input `Tree`.
It fills these in using values passed into the `Rule`. It fills these in using values passed into the `Rule`.
For details of these data structure and syntax, see the [Schematics README](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/README.md). For details of these data structures and syntax, see the [Schematics README](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/README.md).
1. Create the main file `index.ts` and add the source code for your schematic factory function.
1. Create the main file, `index.ts` and add the source code for your schematic factory function.
1. First, import the schematics definitions you will need. The Schematics framework offers many utility functions to create and use rules when running a schematic. 1. First, import the schematics definitions you will need. The Schematics framework offers many utility functions to create and use rules when running a schematic.
@ -271,7 +270,6 @@ For more information about rules and utility methods, see [Provided Rules](https
After you build your library and schematics, you can install the schematics collection to run against your project. The steps below show you how to generate a service using the schematic you created above. After you build your library and schematics, you can install the schematics collection to run against your project. The steps below show you how to generate a service using the schematic you created above.
### Build your library and schematics ### Build your library and schematics
From the root of your workspace, run the `ng build` command for your library. From the root of your workspace, run the `ng build` command for your library.

View File

@ -25,49 +25,48 @@ To use the Angular framework, you should be familiar with the following:
Knowledge of [TypeScript](https://www.typescriptlang.org/) is helpful, but not required. Knowledge of [TypeScript](https://www.typescriptlang.org/) is helpful, but not required.
To install Angular on your local system, you need the following:
{@a nodejs} {@a nodejs}
### Node.js
Make sure your development environment includes `Node.js®` and an npm package manager. * **Node.js**
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases) version of Node.js.
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases/) version of `Node.js`. See the `engines` key for the specific version requirements in our [package.json](https://unpkg.com/@angular/cli/package.json). <div class="alert is-helpful">
* To check your version, run `node -v` in a terminal/console window. For information about specific version requirements, see the `engines` key in the [package.json](https://unpkg.com/@angular/cli/package.json) file.
* To get `Node.js`, go to [nodejs.org](https://nodejs.org "Nodejs.org"). </div>
For more information on installing Node.js, see [nodejs.org](http://nodejs.org "Nodejs.org").
If you are unsure what version of Node.js runs on your system, run `node -v` in a terminal window.
{@a npm} {@a npm}
### npm package manager
Angular, the Angular CLI, and Angular apps depend on features and functionality provided by libraries that are available as [npm packages](https://docs.npmjs.com/getting-started/what-is-npm). To download and install npm packages, you must have an npm package manager. * **npm package manager**
This setup guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default. Angular, the Angular CLI, and Angular applications depend on [npm packages](https://docs.npmjs.com/getting-started/what-is-npm) for many features and functions.
To download and install npm packages, you need an npm package manager.
To check that you have the npm client installed, run `npm -v` in a terminal/console window. This guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default.
To check that you have the npm client installed, run `npm -v` in a terminal window.
{@a install-cli} {@a install-cli}
## Step 1: Install the Angular CLI ## Install the Angular CLI
You use the Angular CLI You use the Angular CLI to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
Install the Angular CLI globally.
To install the CLI using `npm`, open a terminal/console window and enter the following command:
To install the Angular CLI, open a terminal window and run the following command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm install -g @angular/cli npm install -g @angular/cli
</code-example> </code-example>
{@a create-proj} {@a create-proj}
## Step 2: Create a workspace and initial application ## Create a workspace and initial application
You develop apps in the context of an Angular [**workspace**](guide/glossary#workspace). You develop apps in the context of an Angular [**workspace**](guide/glossary#workspace).
@ -86,16 +85,22 @@ The Angular CLI installs the necessary Angular npm packages and other dependenci
The CLI creates a new workspace and a simple Welcome app, ready to run. The CLI creates a new workspace and a simple Welcome app, ready to run.
<div class="alert is-helpful">
You also have the option to use Angular's strict mode, which can help you write better, more maintainable code.
For more information, see [Strict mode](/guide/strict-mode).
</div>
{@a serve} {@a serve}
## Step 3: Run the application ## Run the application
The Angular CLI includes a server, so that you can easily build and serve your app locally. The Angular CLI includes a server, so that you can build and serve your app locally.
1. Go to the workspace folder (`my-app`). 1. Navigate to the workspace folder, such as `my-app`.
1. Launch the server by using the CLI command `ng serve`, with the `--open` option. 1. Run the following command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
cd my-app cd my-app
@ -108,7 +113,7 @@ and rebuilds the app as you make changes to those files.
The `--open` (or just `-o`) option automatically opens your browser The `--open` (or just `-o`) option automatically opens your browser
to `http://localhost:4200/`. to `http://localhost:4200/`.
You will see: If your installation and setup was successful, you should see a page similar to the following.
<div class="lightbox"> <div class="lightbox">

View File

@ -0,0 +1,46 @@
# Strict mode
When you create a new workspace or a project you have an option to create them in a strict mode using the `--strict` flag.
Enabling this flag initializes your new workspace or project with a few new settings that improve maintainability, help you catch bugs ahead of time, and allow the CLI to perform advanced optimizations on your application.
Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular.
Specifically, the `strict` flag does the following:
* Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`.
* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates) and [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters)
* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%
* Turns on [`no-any` tslint rule](https://palantir.github.io/tslint/rules/no-any/) to prevent declarations of type `any`
* [Marks your application as side-effect free](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) to enable more advanced tree-shaking
You can apply these settings at the workspace and project level.
To create a new workspace and application using the strict mode, run the following command:
<code-example language="sh" class="code-shell">
ng new [project-name] --strict
</code-example>
To create a new application in the strict mode within an existing non-strict workspace, run the following command:
<code-example language="sh" class="code-shell">
ng generate application [project-name] --strict
</code-example>
{@a side-effect}
### Non-local side effects in applications
When you create projects and workspaces using the `strict` mode, you'll notice an additional `package.json` file, located in `src/app/` directory.
This file informs tools and bundlers that the code under this directory is free of non-local side effects. Non-local side effects in the application code are not common and using them is not considered a good coding pattern.
More importantly, code with these types of side effects cannot be optimized, resulting in increased bundle sizes and applications that load more slowly.
If you need more information, the following links may be helpful.
* [Tree-shaking](https://webpack.js.org/guides/tree-shaking/)
* [Dealing with side effects and pure functions in JavaScript](https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg)
* [How to deal with dirty side effects in your pure function JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/)

View File

@ -79,6 +79,10 @@ The initial `tsconfig.base.json` for an Angular workspace typically looks like t
} }
</code-example> </code-example>
### Strict mode
When you create new workspaces and projects, you have the option to use Angular's strict mode, which can help you write better, more maintainable code.
For more information, see [Strict mode](/guide/strict-mode).
{@a noImplicitAny} {@a noImplicitAny}

View File

@ -0,0 +1,82 @@
# Updating to Angular version 10
This guide contains information related to updating to version 10 of Angular.
<div class="alert is-helpful">
For information on upgrading to Angular version 9, see [Updating to Angular version 9](https://v9.angular.io/guide/updating-to-version-9).
</div>
## Updating CLI Apps
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
## Changes and Deprecations in Version 10
<div class="alert is-helpful">
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
</div>
{@a breaking-changes}
### New Breaking Changes
* Typescript 3.6, 3.7, and 3.8 are no longer supported. Please update to Typescript 3.9.
* Input fields of type `number` fire the `valueChanges` event only once per value change (as opposed to twice in some cases). See [PR 36087](https://github.com/angular/angular/pull/36087).
* The `minLength` and `maxLength` validators only validate values that have a numeric `length` property. See [PR 36157](https://github.com/angular/angular/pull/36157).
* Templates with unknown property bindings or unknown element names now log errors instead of warnings. See [PR 36399](https://github.com/angular/angular/pull/36399).
* `UrlMatcher` can now return `null` values. See [PR 36402](https://github.com/angular/angular/pull/36402).
* Transplanted views now refresh at insertion point only. See PR 35968](https://github.com/angular/angular/pull/35968).
* Formatting times with the `b` or `B` format codes now supports time periods that cross midnight. See [PR 36611](https://github.com/angular/angular/pull/36611).
* Navigation is canceled for routes with at least one empty resolver. See [PR 24621](https://github.com/angular/angular/pull/24621).
{@a deprecations}
### New Deprecations
| Area | API or Feature | May be removed in |
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | <!--v10--> v12 |
| browser support | [`IE 9, 10, and IE Mobile`](guide/deprecations#ie-9-10-and-ie-mobile-support) | <!--v10--> v11 |
{@a removals}
### New Removals of Deprecated APIs
The following APIs have been removed starting with version 10.0.0*:
| Package | API | Replacement | Notes |
| ---------------- | -------------- | ----------- | ----- |
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](/guide/deprecations#style-sanitization) for more info
| `@angular/bazel` | [`Bazel builder and schematics`](guide/deprecations#bazelbuilder) | `bazelbuild/rules_nodejs` | [More info](https://github.com/angular/angular/tree/10.0.x/packages/bazel/src/schematics) |
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
{@a ivy}
## Ivy features and compatibility
Since version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
{@a migrations}
## Automated Migrations for Version 10
Read about the migrations the CLI handles for you automatically:
* [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
* [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)
* [Solution-style `tsconfig.json` migration](guide/migration-solution-style-tsconfig)
* [`tslib` direct dependency migration](guide/migration-update-libraries-tslib)
* [Update `module` and `target` compiler options migration](guide/migration-update-module-and-target-compiler-options)

View File

@ -1,90 +0,0 @@
# Updating to Angular version 9
This guide contains information related to updating to version 9 of Angular.
## Updating CLI Apps
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
## Changes and Deprecations in Version 9
<div class="alert is-helpful">
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
</div>
{@a breaking-changes}
### New Breaking Changes
- Angular now compiles with Ivy by default. See the [Ivy compatibility section](#ivy).
- CLI apps compile in [AOT mode](/guide/aot-compiler) by default (which includes template type-checking).
Users who only built with JIT before may see new type errors.
See our [template type-checking guide](guide/template-typecheck) for more information and debugging tips.
- Typescript 3.4 and 3.5 are no longer supported. Please update to Typescript 3.7.
- `tslib` is now listed as a peer dependency rather than a direct dependency. If you are not using the CLI, you must manually install `tslib`, using `yarn add tslib` or `npm install tslib --save`.
{@a deprecations}
### New Deprecations
| API | Replacement | Notes |
| ------------------------------------------------------------------------| ------------------------------------ | ----- |
| [`entryComponents`](api/core/NgModule#entryComponents) | none | See [`entryComponents`](guide/deprecations#entryComponents) |
| [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation)| `{provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}` | From v11 the default code will be extracted from the locale data given by `LOCAL_ID`, rather than `USD`. |
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](guide/deprecations#entryComponents) |
| `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | |
| Undecorated base classes that use Angular features | Base classes with `@Directive()` decorator that use Angular features | |
| `esm5` and `fesm5` distribution in `@angular/*` npm packages | `esm2015` and `fesm2015` entrypoints | See [`esm5` and `fesm5`](guide/deprecations#esm5-fesm5) |
| [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | Same behavior, but type safe. |
{@a removals}
### New Removals of Deprecated APIs
| Package | API | Replacement | Notes |
| ------- | -------------- | ----------- | ----- |
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](api/core/Renderer2) | [Migration guide.](guide/migration-renderer) |
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](api/core/RendererFactory2) | none |
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](api/core/RendererType2) | none |
| `@angular/core` | [`WtfScopeFn`](https://v8.angular.io/api/core/WtfScopeFn) | none | v8 | See [Web Tracing Framework](#wtf) |
| `@angular/core` | [`wtfCreateScope`](https://v8.angular.io/api/core/wtfCreateScope) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
| `@angular/core` | [`wtfStartTimeRange`](https://v8.angular.io/api/core/wtfStartTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
| `@angular/core` | [`wtfEndTimeRange`](https://v8.angular.io/api/core/wtfEndTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
| `@angular/core` | [`wtfLeave`](https://v8.angular.io/api/core/wtfLeave) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
| `@angular/common` | `DeprecatedI18NPipesModule` | [`CommonModule`](api/common/CommonModule#pipes) | none |
| `@angular/common` | `DeprecatedCurrencyPipe` | [`CurrencyPipe`](api/common/CurrencyPipe) | none |
| `@angular/common` | `DeprecatedDatePipe` | [`DatePipe`](api/common/DatePipe) | none |
| `@angular/common` | `DeprecatedDecimalPipe` | [`DecimalPipe`](api/common/DecimalPipe) | none |
| `@angular/common` | `DeprecatedPercentPipe` | [`PercentPipe`](api/common/PercentPipe) | none |
| `@angular/forms` | [`NgFormSelectorWarning`](https://v8.angular.io/api/forms/NgFormSelectorWarning) | none |
| `@angular/forms` | `ngForm` element selector | `ng-form` element selector | none |
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
{@a ivy}
## Ivy features and compatibility
In Version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
{@a migrations}
## Automated Migrations for Version 9
Read about the migrations the CLI handles for you automatically:
- [Migrating from `Renderer` to `Renderer2`](guide/migration-renderer)
- [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
- [Migrating missing `@Injectable()` decorators and incomplete provider definitions](guide/migration-injectable)
- [Migrating dynamic queries](guide/migration-dynamic-flag)
- [Migrating to the new `$localize` i18n support](guide/migration-localize)
- [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)

View File

@ -41,6 +41,11 @@ When you create a library project with `ng generate library`, the library projec
</div> </div>
## Strict mode
When you create new workspaces and projects, you have the option to use Angular's strict mode, which can help you write better, more maintainable code.
For more information, see [Strict mode](/guide/strict-mode).
## Project configuration options ## Project configuration options
The following top-level configuration properties are available for each project, under `projects:<project_name>`. The following top-level configuration properties are available for each project, under `projects:<project_name>`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -596,6 +596,13 @@
"twitter": "devjoost", "twitter": "devjoost",
"bio": "Joost is a Software Engineer from the Netherlands with an interest in open source software who likes to learn something new every day. He works at Blueriq during the day and contributes to Angular in his spare time, by working on the Angular compiler and runtime. He may review your PR even if you never asked for it ;)" "bio": "Joost is a Software Engineer from the Netherlands with an interest in open source software who likes to learn something new every day. He works at Blueriq during the day and contributes to Angular in his spare time, by working on the Angular compiler and runtime. He may review your PR even if you never asked for it ;)"
}, },
"sonukapoor": {
"name": "Sonu Kapoor",
"groups": ["Collaborators"],
"picture": "sonukapoor.jpg",
"website": "https://www.linkedin.com/in/sonu-kapoor/",
"bio": "Sonu is a Software Engineer from Toronto, with a high interest in front-end technologies and algorithms."
},
"jschwarty": { "jschwarty": {
"name": "Justin Schwartzenberger", "name": "Justin Schwartzenberger",
"picture": "justinschwartzenberger.jpg", "picture": "justinschwartzenberger.jpg",
@ -815,5 +822,13 @@
"website": "https://wellwind.idv.tw/blog/", "website": "https://wellwind.idv.tw/blog/",
"bio": "Mike is a full-stack developer, consultant, blogger, instructor, and conference speaker. He has over 10 years of web development experience and passion to share his knowledge.", "bio": "Mike is a full-stack developer, consultant, blogger, instructor, and conference speaker. He has over 10 years of web development experience and passion to share his knowledge.",
"groups": ["GDE"] "groups": ["GDE"]
},
"ahasall": {
"name": "Amadou Sall",
"picture": "ahasall.jpg",
"groups": ["GDE"],
"twitter": "ahasall",
"website": "https://www.amadousall.com",
"bio": "Amadou is a Frontend Software Engineer from Senegal based in France. He currently works at Air France where he helps developers build better Angular applications. Passionate about web technologies, Amadou is an international speaker, a technical writer, and a Google Developer Expert in Angular."
} }
} }

View File

@ -13,11 +13,6 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr>
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
<td>Oslo, Norway</td>
<td>May 25-26 conference, 27 workshops, 2020</td>
</tr>
</tbody> </tbody>
</table> </table>
@ -31,6 +26,12 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- ng-vikings 2020 -->
<tr>
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
<td>Oslo, Norway</td>
<td>May 25-26 conference, 27 workshops, 2020</td>
</tr>
<!-- ng-conf 2020 --> <!-- ng-conf 2020 -->
<tr> <tr>
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th> <th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>

View File

@ -506,80 +506,6 @@
"url": "guide/universal", "url": "guide/universal",
"title": "Server-side Rendering", "title": "Server-side Rendering",
"tooltip": "Render HTML server-side with Angular Universal." "tooltip": "Render HTML server-side with Angular Universal."
},
{
"title": "Upgrading from AngularJS",
"tooltip": "Incrementally upgrade an AngularJS application to Angular.",
"children": [
{
"url": "guide/upgrade-setup",
"title": "Setup for Upgrading from AngularJS",
"tooltip": "Use code from the Angular QuickStart seed as part of upgrading from AngularJS.",
"hidden": true
},
{
"url": "guide/upgrade",
"title": "Upgrading Instructions",
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
},
{
"url": "guide/upgrade-performance",
"title": "Upgrading for Performance",
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
},
{
"url": "guide/ajs-quick-reference",
"title": "AngularJS-Angular Concepts",
"tooltip": "Learn how AngularJS concepts and techniques map to Angular."
}
]
},
{
"title": "Angular Libraries",
"tooltip": "Extending Angular with shared libraries.",
"children": [
{
"url": "guide/libraries",
"title": "Libraries Overview",
"tooltip": "Understand how and when to use or create libraries."
},
{
"url": "guide/using-libraries",
"title": "Using Published Libraries",
"tooltip": "Integrate published libraries into an app."
},
{
"url": "guide/creating-libraries",
"title": "Creating Libraries",
"tooltip": "Extend Angular by creating, publishing, and using your own libraries."
}
]
},
{
"title": "Schematics",
"tooltip": "Using CLI schematics for code generation.",
"children": [
{
"url": "guide/schematics",
"title": "Schematics Overview",
"tooltip": "How the CLI uses schematics to generate code."
},
{
"url": "guide/schematics-authoring",
"title": "Authoring Schematics",
"tooltip": "Understand the structure of a schematic."
},
{
"url": "guide/schematics-for-libraries",
"title": "Schematics for Libraries",
"tooltip": "Use schematics to integrate your library with the Angular CLI."
}
]
},
{
"url": "guide/cli-builder",
"title": "CLI Builders",
"tooltip": "Using builders to customize Angular CLI."
} }
] ]
}, },
@ -675,6 +601,11 @@
"url": "guide/browser-support", "url": "guide/browser-support",
"title": "Browser Support", "title": "Browser Support",
"tooltip": "Browser support and polyfills guide." "tooltip": "Browser support and polyfills guide."
},
{
"url": "guide/strict-mode",
"title": "Strict mode",
"tooltip": "Reference documentation for Angular's strict mode."
} }
] ]
}, },
@ -762,13 +693,13 @@
"tooltip": "Angular versioning, release, support, and deprecation policies and practices." "tooltip": "Angular versioning, release, support, and deprecation policies and practices."
}, },
{ {
"title": "Updating to Version 9", "title": "Updating to Version 10",
"tooltip": "Support for updating your application from version 8 to 9.", "tooltip": "Support for updating your application from version 9 to 10.",
"children": [ "children": [
{ {
"url": "guide/updating-to-version-9", "url": "guide/updating-to-version-10",
"title": "Overview", "title": "Overview",
"tooltip": "Everything you need to know for updating your application from version 8 to 9." "tooltip": "Everything you need to know for updating your application from version 9 to 10."
}, },
{ {
"url": "guide/ivy-compatibility", "url": "guide/ivy-compatibility",
@ -776,29 +707,9 @@
"tooltip": "Details to help you make sure your application is compatible with Ivy." "tooltip": "Details to help you make sure your application is compatible with Ivy."
}, },
{ {
"title": "Optional Migrations", "title": "Migrations",
"tooltip": "Optional migration details regarding updating to version 9.", "tooltip": "Migration details regarding updating to version 10.",
"children": [ "children": [
{
"url": "guide/migration-renderer",
"title": "Renderer to Renderer2",
"tooltip": "Migration from the deprecated Renderer API to the newer Renderer2 API."
},
{
"url": "guide/migration-dynamic-flag",
"title": "Dynamic Queries Flag",
"tooltip": "Migration to remove unnecessary `static: false` flag from @ViewChild and @ContentChild queries."
},
{
"url": "guide/migration-injectable",
"title": "Missing @Injectable() Decorators",
"tooltip": "Migration to add missing @Injectable() decorators and incomplete provider definitions."
},
{
"url": "guide/migration-localize",
"title": "$localize Global Import",
"tooltip": "Migration to add an import statement for @angular/localize to polyfills.ts."
},
{ {
"url": "guide/migration-module-with-providers", "url": "guide/migration-module-with-providers",
"title": "Missing ModuleWithProviders Generic", "title": "Missing ModuleWithProviders Generic",
@ -808,6 +719,26 @@
"url": "guide/migration-undecorated-classes", "url": "guide/migration-undecorated-classes",
"title": "Missing @Directive() Decorators", "title": "Missing @Directive() Decorators",
"tooltip": "Migration to add missing @Directive()/@Component() decorators." "tooltip": "Migration to add missing @Directive()/@Component() decorators."
},
{
"url": "guide/migration-injectable",
"title": "Missing @Injectable() Decorators",
"tooltip": "Migration to add missing @Injectable() decorators and incomplete provider definitions."
},
{
"url": "guide/migration-solution-style-tsconfig",
"title": "Solution-style `tsconfig.json`",
"tooltip": "Migration to create a solution-style `tsconfig.json`."
},
{
"url": "guide/migration-update-libraries-tslib",
"title": "`tslib` direct dependency",
"tooltip": "Migration to a direct dependency on the `tslib` npm package."
},
{
"url": "guide/migration-update-module-and-target-compiler-options",
"title": "`module` and `target` compiler options",
"tooltip": "Migration to update `module` and `target` compiler options."
} }
] ]
} }
@ -1003,6 +934,10 @@
} }
], ],
"docVersions": [ "docVersions": [
{
"title": "v9",
"url": "https://v9.angular.io/"
},
{ {
"title": "v8", "title": "v8",
"url": "https://v8.angular.io/" "url": "https://v8.angular.io/"

View File

@ -6,7 +6,7 @@
In this tutorial, you build your own app from the ground up, providing experience with the typical development process, as well as an introduction to basic app-design concepts, tools, and terminology. In this tutorial, you build your own app from the ground up, providing experience with the typical development process, as well as an introduction to basic app-design concepts, tools, and terminology.
If you're completely new to Angular, you might want to try the [**Try it now**](start) quick-start app first. If you're completely new to Angular, you might want to try the [**Try it now**](start) quick-start app first.
It is based on a ready-made partially-completed project, which you can examine and modify in the StacBlitz interactive development environment, where you can see the results in real time. It is based on a ready-made partially-completed project, which you can examine and modify in the StackBlitz interactive development environment, where you can see the results in real time.
The "Try it" tutorial covers the same major topics&mdash;components, template syntax, routing, services, and accessing data via HTTP&mdash;in a condensed format, following the most current best practices. The "Try it" tutorial covers the same major topics&mdash;components, template syntax, routing, services, and accessing data via HTTP&mdash;in a condensed format, following the most current best practices.

View File

@ -1,5 +1,6 @@
{ {
"hosting": { "hosting": {
"target": "aio",
"public": "dist", "public": "dist",
"cleanUrls": true, "cleanUrls": true,
"redirects": [ "redirects": [
@ -127,7 +128,7 @@
// The below paths are referenced in users projects generated by the CLI // The below paths are referenced in users projects generated by the CLI
{"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"}, {"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"},
{"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"}, {"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"},
{"type": 301, "source": "/config/app-package-json", "destination": "https://webpack.js.org/configuration/optimization/#optimizationsideeffects"} {"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"}
], ],
"rewrites": [ "rewrites": [
{ {

View File

@ -123,7 +123,7 @@
"cross-spawn": "^5.1.0", "cross-spawn": "^5.1.0",
"css-selector-parser": "^1.3.0", "css-selector-parser": "^1.3.0",
"dgeni": "^0.4.11", "dgeni": "^0.4.11",
"dgeni-packages": "^0.28.3", "dgeni-packages": "^0.28.4",
"entities": "^1.1.1", "entities": "^1.1.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0", "eslint-plugin-jasmine": "^2.2.0",
@ -175,4 +175,4 @@
"xregexp": "^4.0.0", "xregexp": "^4.0.0",
"yargs": "^7.0.2" "yargs": "^7.0.2"
} }
} }

View File

@ -33,7 +33,7 @@ else
readonly majorVersionStable=${CI_STABLE_BRANCH%%.*} readonly majorVersionStable=${CI_STABLE_BRANCH%%.*}
# Do not deploy if the major version is not less than the stable branch major version # Do not deploy if the major version is not less than the stable branch major version
if [[ !( "$majorVersion" -lt "$majorVersionStable" ) ]]; then if (( $majorVersion >= $majorVersionStable )); then
echo "Skipping deploy of branch \"$CI_BRANCH\" to firebase." echo "Skipping deploy of branch \"$CI_BRANCH\" to firebase."
echo "We only deploy archive branches with the major version less than the stable branch: \"$CI_STABLE_BRANCH\"" echo "We only deploy archive branches with the major version less than the stable branch: \"$CI_STABLE_BRANCH\""
exit 0 exit 0
@ -64,16 +64,27 @@ fi
case $deployEnv in case $deployEnv in
next) next)
readonly projectId=aio-staging readonly projectId=aio-staging
readonly siteId=$projectId
readonly deployedUrl=https://next.angular.io/ readonly deployedUrl=https://next.angular.io/
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
;; ;;
stable) stable)
readonly projectId=angular-io readonly projectId=angular-io
readonly siteId=$projectId
readonly deployedUrl=https://angular.io/ readonly deployedUrl=https://angular.io/
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
;; ;;
archive) archive)
readonly projectId=v${majorVersion}-angular-io # Special case v9-angular-io because its piloting the firebase hosting "multisites" setup
# See https://angular-team.atlassian.net/browse/DEV-125 for more info.
if [[ "$majorVersion" == "9" ]]; then
readonly projectId=aio-staging
readonly siteId=v9-angular-io
else
readonly projectId=v${majorVersion}-angular-io
readonly siteId=$projectId
fi
readonly deployedUrl=https://v${majorVersion}.angular.io/ readonly deployedUrl=https://v${majorVersion}.angular.io/
readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN readonly firebaseToken=$CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN
;; ;;
@ -82,6 +93,7 @@ esac
echo "Git branch : $CI_BRANCH" echo "Git branch : $CI_BRANCH"
echo "Build/deploy mode : $deployEnv" echo "Build/deploy mode : $deployEnv"
echo "Firebase project : $projectId" echo "Firebase project : $projectId"
echo "Firebase site : $siteId"
echo "Deployment URL : $deployedUrl" echo "Deployment URL : $deployedUrl"
if [[ ${1:-} == "--dry-run" ]]; then if [[ ${1:-} == "--dry-run" ]]; then
@ -92,23 +104,29 @@ fi
( (
cd "`dirname $0`/.." cd "`dirname $0`/.."
# Build the app echo "\n\n\n==== Build the aio app ====\n"
yarn build --configuration=$deployEnv --progress=false yarn build --configuration=$deployEnv --progress=false
# Include any mode-specific files
echo "\n\n\n==== Add any mode-specific files into the aio distribution ====\n"
cp -rf src/extra-files/$deployEnv/. dist/ cp -rf src/extra-files/$deployEnv/. dist/
# Set deployedUrl as parameter in the opensearch description
echo "\n\n\n==== Update opensearch descriptor for aio with the deployedUrl ====\n"
# deployedUrl must end with / # deployedUrl must end with /
yarn set-opensearch-url $deployedUrl yarn set-opensearch-url $deployedUrl
# Check payload size echo "\n\n\n==== Check payload size and upload the numbers to firebase db ====\n"
yarn payload-size yarn payload-size
# Deploy to Firebase
yarn firebase use "$projectId" --token "$firebaseToken"
yarn firebase deploy --message "Commit: $CI_COMMIT" --non-interactive --token "$firebaseToken"
# Run PWA-score tests echo "\n\n\n==== Deploy aio to firebase hosting ====\n"
yarn firebase use "${projectId}" --token "$firebaseToken"
yarn firebase target:apply hosting aio $siteId --token "$firebaseToken"
yarn firebase deploy --only hosting:aio --message "Commit: $CI_COMMIT" --non-interactive --token "$firebaseToken"
echo "\n\n\n==== Run PWA-score tests ====\n"
yarn test-pwa-score "$deployedUrl" "$CI_AIO_MIN_PWA_SCORE" yarn test-pwa-score "$deployedUrl" "$CI_AIO_MIN_PWA_SCORE"
) )

View File

@ -68,6 +68,7 @@ function check {
expected="Git branch : master expected="Git branch : master
Build/deploy mode : next Build/deploy mode : next
Firebase project : aio-staging Firebase project : aio-staging
Firebase site : aio-staging
Deployment URL : https://next.angular.io/" Deployment URL : https://next.angular.io/"
check "$actual" "$expected" check "$actual" "$expected"
) )
@ -103,6 +104,7 @@ Deployment URL : https://next.angular.io/"
expected="Git branch : 4.3.x expected="Git branch : 4.3.x
Build/deploy mode : stable Build/deploy mode : stable
Firebase project : angular-io Firebase project : angular-io
Firebase site : angular-io
Deployment URL : https://angular.io/" Deployment URL : https://angular.io/"
check "$actual" "$expected" check "$actual" "$expected"
) )
@ -139,10 +141,37 @@ Deployment URL : https://angular.io/"
expected="Git branch : 2.4.x expected="Git branch : 2.4.x
Build/deploy mode : archive Build/deploy mode : archive
Firebase project : v2-angular-io Firebase project : v2-angular-io
Firebase site : v2-angular-io
Deployment URL : https://v2.angular.io/" Deployment URL : https://v2.angular.io/"
check "$actual" "$expected" check "$actual" "$expected"
) )
(
echo ===== archive - v9-angular-io multisite special case - deploy success
actual=$(
export BASH_ENV=/dev/null
export CI_REPO_OWNER=angular
export CI_REPO_NAME=angular
export CI_PULL_REQUEST=false
export CI_BRANCH=9.1.x
export CI_STABLE_BRANCH=10.0.x
export CI_COMMIT=$(git ls-remote origin 9.1.x | cut -c1-40)
export CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN=XXXXX
$deployToFirebaseDryRun
)
expected="Git branch : 9.1.x
Build/deploy mode : archive
Firebase project : aio-staging
Firebase site : v9-angular-io
Deployment URL : https://v9.angular.io/"
# TODO: This test incorrectly expects the Firebase project to be v9-angular-io.
# v9-angular-io is a "multisites" project currently within the aio-staging project
# This setup is temporary and was created in order to deploy v9.angular.io without
# disruptions.
# See https://angular-team.atlassian.net/browse/DEV-125 for more info.
check "$actual" "$expected"
)
( (
echo ===== archive - skip deploy - commit not HEAD echo ===== archive - skip deploy - commit not HEAD
actual=$( actual=$(

View File

@ -401,7 +401,10 @@ describe('DocViewerComponent', () => {
expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]); expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]);
}); });
it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => { // This test sometimes incorrectly fails on CI.
// Reported in https://github.com/angular/angular/issues/37629.
// Investigated in https://github.com/angular/angular/pull/37637.
xit('should unsubscribe from the previous "embed" observable when unsubscribed from', () => {
const obs = new ObservableWithSubscriptionSpies(); const obs = new ObservableWithSubscriptionSpies();
loadElementsSpy.and.returnValue(obs); loadElementsSpy.and.returnValue(obs);
@ -436,7 +439,10 @@ describe('DocViewerComponent', () => {
expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy); expect(swapViewsSpy).toHaveBeenCalledWith(addTitleAndTocSpy);
}); });
it('should unsubscribe from the previous "swap" observable when unsubscribed from', () => { // This test sometimes incorrectly fails on CI.
// Reported in https://github.com/angular/angular/issues/37629.
// Investigated in https://github.com/angular/angular/pull/37637.
xit('should unsubscribe from the previous "swap" observable when unsubscribed from', () => {
const obs = new ObservableWithSubscriptionSpies(); const obs = new ObservableWithSubscriptionSpies();
swapViewsSpy.and.returnValue(obs); swapViewsSpy.and.returnValue(obs);

View File

@ -0,0 +1,52 @@
import { by, element } from 'protractor';
import { SitePage } from './app.po';
describe('api-list', () => {
const apiSearchInput = element(by.css('aio-api-list .form-search input'));
const apiStatusDropdown = element(by.css('aio-api-list aio-select[label="Status:"]'));
const apiTypeDropdown = element(by.css('aio-api-list aio-select[label="Type:"]'));
let page: SitePage;
beforeEach(() => {
page = new SitePage();
page.navigateTo('api');
});
it('should find AnimationSequenceMetadata when searching by partial word anima', () => {
expect(page.getApiSearchResults()).toContain('HttpEventType');
apiSearchInput.clear();
apiSearchInput.sendKeys('anima');
expect(page.getApiSearchResults()).not.toContain('HttpEventType');
expect(page.getApiSearchResults()).toContain('AnimationSequenceMetadata');
});
it('should find getLocaleDateTimeFormat when searching by partial word date', () => {
expect(page.getApiSearchResults()).toContain('formatCurrency');
apiSearchInput.clear();
apiSearchInput.sendKeys('date');
expect(page.getApiSearchResults()).not.toContain('formatCurrency');
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
});
it('should find LowerCasePipe when searching for type pipe', () => {
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
page.clickDropdownItem(apiTypeDropdown, 'Pipe');
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
expect(page.getApiSearchResults()).toContain('LowerCasePipe');
});
it('should find ElementRef when searching for status Security Risk', () => {
expect(page.getApiSearchResults()).toContain('getLocaleDateTimeFormat');
page.clickDropdownItem(apiStatusDropdown, 'Security Risk');
expect(page.getApiSearchResults()).not.toContain('getLocaleDateTimeFormat');
expect(page.getApiSearchResults()).toContain('ElementRef');
});
});

View File

@ -83,4 +83,16 @@ export class SitePage {
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000); browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results.map(link => link && link.getText()); return results.map(link => link && link.getText());
} }
getApiSearchResults() {
const results = element.all(by.css('aio-api-list .api-item'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 2000);
return results.map(elem => elem && elem.getText());
}
clickDropdownItem(dropdown: ElementFinder, itemName: string){
dropdown.element(by.css('.form-select-button')).click();
const menuItem = dropdown.element(by.cssContainingText('.form-select-dropdown li', itemName));
menuItem.click();
}
} }

View File

@ -23,10 +23,7 @@ const DEFAULT_CLI_EXAMPLE_PORT = 4200;
const DEFAULT_CLI_SPECS_CONCURRENCY = 1; const DEFAULT_CLI_SPECS_CONCURRENCY = 1;
const IGNORED_EXAMPLES = []; const IGNORED_EXAMPLES = [];
const fixmeIvyExamples = [ const fixmeIvyExamples = [];
// fixmeIvy('unknown') app fails at runtime due to missing external service (goog is undefined)
'i18n',
];
if (!argv.viewengine) { if (!argv.viewengine) {
IGNORED_EXAMPLES.push(...fixmeIvyExamples); IGNORED_EXAMPLES.push(...fixmeIvyExamples);
@ -72,8 +69,10 @@ function runE2e() {
const outputFile = path.join(AIO_PATH, './protractor-results.txt'); const outputFile = path.join(AIO_PATH, './protractor-results.txt');
return Promise.resolve() return Promise.resolve()
.then(() => findAndRunE2eTests(argv.filter, outputFile, argv.shard, .then(
argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY, argv.retry || 1)) () => findAndRunE2eTests(
argv.filter, outputFile, argv.shard,
argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY, argv.retry || 1))
.then((status) => { .then((status) => {
reportStatus(status, outputFile); reportStatus(status, outputFile);
if (status.failed.length > 0) { if (status.failed.length > 0) {
@ -226,8 +225,12 @@ function runProtractorSystemJS(prepPromise, appDir, appRunSpawnInfo, outputFile)
}); });
}) })
.then( .then(
function() { return finish(appRunSpawnInfo.proc.pid, true); }, function() {
function() { return finish(appRunSpawnInfo.proc.pid, false); }); return finish(appRunSpawnInfo.proc.pid, true);
},
function() {
return finish(appRunSpawnInfo.proc.pid, false);
});
} }
function finish(spawnProcId, ok) { function finish(spawnProcId, ok) {
@ -263,15 +266,15 @@ function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) {
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed. // `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
const config = loadExampleConfig(appDir); const config = loadExampleConfig(appDir);
const testCommands = config.tests || [{ const testCommands = config.tests || [{
cmd: 'yarn', cmd: 'yarn',
args: [ args: [
'e2e', 'e2e',
'--prod', '--prod',
'--protractor-config=e2e/protractor-puppeteer.conf.js', '--protractor-config=e2e/protractor-puppeteer.conf.js',
'--no-webdriver-update', '--no-webdriver-update',
'--port={PORT}', '--port={PORT}',
], ],
}]; }];
let bufferedOutput = `\n\n============== AIO example output for: ${appDir}\n\n`; let bufferedOutput = `\n\n============== AIO example output for: ${appDir}\n\n`;
const e2eSpawnPromise = testCommands.reduce((prevSpawnPromise, {cmd, args}) => { const e2eSpawnPromise = testCommands.reduce((prevSpawnPromise, {cmd, args}) => {
@ -281,26 +284,30 @@ function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) {
args = args.map(a => a.replace('{PORT}', port || DEFAULT_CLI_EXAMPLE_PORT)); args = args.map(a => a.replace('{PORT}', port || DEFAULT_CLI_EXAMPLE_PORT));
return prevSpawnPromise.then(() => { return prevSpawnPromise.then(() => {
const currSpawn = spawnExt(cmd, args, {cwd: appDir}, false, const currSpawn = spawnExt(
bufferOutput ? msg => bufferedOutput += msg : undefined); cmd, args, {cwd: appDir}, false, bufferOutput ? msg => bufferedOutput += msg : undefined);
return currSpawn.promise.then( return currSpawn.promise.then(
() => Promise.resolve(finish(currSpawn.proc.pid, true)), () => Promise.resolve(finish(currSpawn.proc.pid, true)),
() => Promise.reject(finish(currSpawn.proc.pid, false))); () => Promise.reject(finish(currSpawn.proc.pid, false)));
}); });
}, Promise.resolve()); }, Promise.resolve());
return e2eSpawnPromise.then(() => { return e2eSpawnPromise
fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`); .then(
return true; () => {
}, () => { fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`);
fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`); return true;
return false; },
}).then(passed => { () => {
if (bufferOutput) { fs.appendFileSync(outputFile, `Failed: ${appDir}\n\n`);
process.stdout.write(bufferedOutput); return false;
} })
return passed; .then(passed => {
}); if (bufferOutput) {
process.stdout.write(bufferedOutput);
}
return passed;
});
} }
// Report final status. // Report final status.
@ -309,23 +316,31 @@ function reportStatus(status, outputFile) {
log.push('Suites ignored due to legacy guides:'); log.push('Suites ignored due to legacy guides:');
IGNORED_EXAMPLES.filter(example => !fixmeIvyExamples.find(ex => ex.startsWith(example))) IGNORED_EXAMPLES.filter(example => !fixmeIvyExamples.find(ex => ex.startsWith(example)))
.forEach(function(val) { log.push(' ' + val); }); .forEach(function(val) {
log.push(' ' + val);
});
if (!argv.viewengine) { if (!argv.viewengine) {
log.push(''); log.push('');
log.push('Suites ignored due to breakage with Ivy:'); log.push('Suites ignored due to breakage with Ivy:');
fixmeIvyExamples.forEach(function(val) { log.push(' ' + val); }); fixmeIvyExamples.forEach(function(val) {
log.push(' ' + val);
});
} }
log.push(''); log.push('');
log.push('Suites passed:'); log.push('Suites passed:');
status.passed.forEach(function(val) { log.push(' ' + val); }); status.passed.forEach(function(val) {
log.push(' ' + val);
});
if (status.failed.length == 0) { if (status.failed.length == 0) {
log.push('All tests passed'); log.push('All tests passed');
} else { } else {
log.push('Suites failed:'); log.push('Suites failed:');
status.failed.forEach(function(val) { log.push(' ' + val); }); status.failed.forEach(function(val) {
log.push(' ' + val);
});
} }
log.push('\nElapsed time: ' + status.elapsedTime + ' seconds'); log.push('\nElapsed time: ' + status.elapsedTime + ' seconds');
log = log.join('\n'); log = log.join('\n');
@ -334,8 +349,8 @@ function reportStatus(status, outputFile) {
} }
// Returns both a promise and the spawned process so that it can be killed if needed. // Returns both a promise and the spawned process so that it can be killed if needed.
function spawnExt(command, args, options, ignoreClose = false, function spawnExt(
printMessage = msg => process.stdout.write(msg)) { command, args, options, ignoreClose = false, printMessage = msg => process.stdout.write(msg)) {
let proc; let proc;
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
let descr = command + ' ' + args.join(' '); let descr = command + ' ' + args.join(' ');
@ -370,13 +385,19 @@ function getE2eSpecs(basePath, filter) {
let specs = {}; let specs = {};
return getE2eSpecsFor(basePath, SJS_SPEC_FILENAME, filter) return getE2eSpecsFor(basePath, SJS_SPEC_FILENAME, filter)
.then(sjsPaths => { specs.systemjs = sjsPaths; }) .then(sjsPaths => {
specs.systemjs = sjsPaths;
})
.then(() => { .then(() => {
return getE2eSpecsFor(basePath, CLI_SPEC_FILENAME, filter).then(cliPaths => { return getE2eSpecsFor(basePath, CLI_SPEC_FILENAME, filter).then(cliPaths => {
return cliPaths.map(p => { return p.replace(`${CLI_SPEC_FILENAME}`, ''); }); return cliPaths.map(p => {
return p.replace(`${CLI_SPEC_FILENAME}`, '');
});
}); });
}) })
.then(cliPaths => { specs.cli = cliPaths; }) .then(cliPaths => {
specs.cli = cliPaths;
})
.then(() => specs); .then(() => specs);
} }

View File

@ -9,10 +9,18 @@
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"prefix": "app", "prefix": "app",
"i18n": {
"sourceLocale": "en-US",
"locales": {
"fr": "src/locale/messages.fr.xlf"
}
},
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"localize": true,
"aot": true,
"outputPath": "dist", "outputPath": "dist",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
@ -57,35 +65,10 @@
} }
] ]
}, },
"production-fr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"outputPath": "dist/my-project-fr/",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nFormat": "xlf",
"i18nLocale": "fr",
"i18nMissingTranslation": "error"
},
"fr": { "fr": {
"aot": true, "localize": [
"outputPath": "dist/my-project-fr/", "fr"
"i18nFile": "src/locale/messages.fr.xlf", ]
"i18nFormat": "xlf",
"i18nLocale": "fr",
"i18nMissingTranslation": "error"
} }
} }
}, },

View File

@ -20,6 +20,7 @@
"@angular/compiler": "~9.1.4", "@angular/compiler": "~9.1.4",
"@angular/core": "~9.1.4", "@angular/core": "~9.1.4",
"@angular/forms": "~9.1.4", "@angular/forms": "~9.1.4",
"@angular/localize": "^9.1.4",
"@angular/platform-browser": "~9.1.4", "@angular/platform-browser": "~9.1.4",
"@angular/platform-browser-dynamic": "~9.1.4", "@angular/platform-browser-dynamic": "~9.1.4",
"@angular/router": "~9.1.4", "@angular/router": "~9.1.4",

View File

@ -0,0 +1,68 @@
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch
* requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch
* specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@ -26,6 +26,7 @@
"@angular/core": "~9.1.4", "@angular/core": "~9.1.4",
"@angular/elements": "~9.1.4", "@angular/elements": "~9.1.4",
"@angular/forms": "~9.1.4", "@angular/forms": "~9.1.4",
"@angular/localize": "~9.1.4",
"@angular/platform-browser": "~9.1.4", "@angular/platform-browser": "~9.1.4",
"@angular/platform-browser-dynamic": "~9.1.4", "@angular/platform-browser-dynamic": "~9.1.4",
"@angular/platform-server": "~9.1.4", "@angular/platform-server": "~9.1.4",

View File

@ -215,6 +215,15 @@
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.4.tgz#2fa2c444e5a5a6036d5ca43d2887826df17d0553" resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.4.tgz#2fa2c444e5a5a6036d5ca43d2887826df17d0553"
integrity sha512-eyVxxiegdb4ESdFGfkuDN+YfUbOVHRQLjIl6ACFJQDNHzVXzbmuqpyr5hIJANIVady103/7+dqRxxJo1DdIdTQ== integrity sha512-eyVxxiegdb4ESdFGfkuDN+YfUbOVHRQLjIl6ACFJQDNHzVXzbmuqpyr5hIJANIVady103/7+dqRxxJo1DdIdTQ==
"@angular/localize@~9.1.4":
version "9.1.11"
resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.11.tgz#25921d794836fb7a07d284c1ac0ed06c10e77d50"
integrity sha512-CrR7RniwJIK3+QKH8nHl35KDAHZn1mp1QAd5vujTWKw6YRLfio7SjM9qIfzw5y4WZuUitTsqKlQT/m/NK146Ag==
dependencies:
"@babel/core" "7.8.3"
glob "7.1.2"
yargs "15.3.0"
"@angular/platform-browser-dynamic@~9.1.4": "@angular/platform-browser-dynamic@~9.1.4":
version "9.1.4" version "9.1.4"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.4.tgz#bf1cde9156bd29eeeef932b683b0c993614f75d5" resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.4.tgz#bf1cde9156bd29eeeef932b683b0c993614f75d5"
@ -254,6 +263,13 @@
dependencies: dependencies:
"@babel/highlight" "^7.0.0" "@babel/highlight" "^7.0.0"
"@babel/code-frame@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff"
integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==
dependencies:
"@babel/highlight" "^7.10.1"
"@babel/code-frame@^7.5.5": "@babel/code-frame@^7.5.5":
version "7.5.5" version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
@ -277,6 +293,27 @@
invariant "^2.2.4" invariant "^2.2.4"
semver "^5.5.0" semver "^5.5.0"
"@babel/core@7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941"
integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.3"
"@babel/helpers" "^7.8.3"
"@babel/parser" "^7.8.3"
"@babel/template" "^7.8.3"
"@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.1"
json5 "^2.1.0"
lodash "^4.17.13"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
"@babel/core@7.9.0": "@babel/core@7.9.0":
version "7.9.0" version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
@ -330,6 +367,16 @@
lodash "^4.17.13" lodash "^4.17.13"
source-map "^0.5.0" source-map "^0.5.0"
"@babel/generator@^7.10.1", "@babel/generator@^7.8.3":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.2.tgz#0fa5b5b2389db8bfdfcc3492b551ee20f5dd69a9"
integrity sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==
dependencies:
"@babel/types" "^7.10.2"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/generator@^7.4.0", "@babel/generator@^7.7.4": "@babel/generator@^7.4.0", "@babel/generator@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.7.4.tgz#db651e2840ca9aa66f327dcec1dc5f5fa9611369"
@ -420,6 +467,15 @@
"@babel/traverse" "^7.8.3" "@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/helper-function-name@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz#92bd63829bfc9215aca9d9defa85f56b539454f4"
integrity sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==
dependencies:
"@babel/helper-get-function-arity" "^7.10.1"
"@babel/template" "^7.10.1"
"@babel/types" "^7.10.1"
"@babel/helper-function-name@^7.7.4": "@babel/helper-function-name@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e"
@ -447,6 +503,13 @@
"@babel/template" "^7.8.3" "@babel/template" "^7.8.3"
"@babel/types" "^7.9.5" "@babel/types" "^7.9.5"
"@babel/helper-get-function-arity@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz#7303390a81ba7cb59613895a192b93850e373f7d"
integrity sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==
dependencies:
"@babel/types" "^7.10.1"
"@babel/helper-get-function-arity@^7.7.4": "@babel/helper-get-function-arity@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0"
@ -558,6 +621,13 @@
"@babel/template" "^7.8.3" "@babel/template" "^7.8.3"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/helper-split-export-declaration@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f"
integrity sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==
dependencies:
"@babel/types" "^7.10.1"
"@babel/helper-split-export-declaration@^7.7.4": "@babel/helper-split-export-declaration@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz#57292af60443c4a3622cf74040ddc28e68336fd8"
@ -572,6 +642,11 @@
dependencies: dependencies:
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/helper-validator-identifier@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5"
integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==
"@babel/helper-validator-identifier@^7.9.5": "@babel/helper-validator-identifier@^7.9.5":
version "7.9.5" version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
@ -587,6 +662,15 @@
"@babel/traverse" "^7.8.3" "@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/helpers@^7.8.3":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973"
integrity sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==
dependencies:
"@babel/template" "^7.10.1"
"@babel/traverse" "^7.10.1"
"@babel/types" "^7.10.1"
"@babel/helpers@^7.8.4": "@babel/helpers@^7.8.4":
version "7.8.4" version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
@ -613,6 +697,15 @@
esutils "^2.0.2" esutils "^2.0.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/highlight@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0"
integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==
dependencies:
"@babel/helper-validator-identifier" "^7.10.1"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/highlight@^7.8.3": "@babel/highlight@^7.8.3":
version "7.8.3" version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
@ -622,6 +715,11 @@
esutils "^2.0.2" esutils "^2.0.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.10.1":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0"
integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==
"@babel/parser@^7.4.3", "@babel/parser@^7.7.4": "@babel/parser@^7.4.3", "@babel/parser@^7.7.4":
version "7.7.5" version "7.7.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.7.5.tgz#cbf45321619ac12d83363fcf9c94bb67fa646d71"
@ -1126,6 +1224,15 @@
"@babel/parser" "^7.8.6" "@babel/parser" "^7.8.6"
"@babel/types" "^7.8.6" "@babel/types" "^7.8.6"
"@babel/template@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==
dependencies:
"@babel/code-frame" "^7.10.1"
"@babel/parser" "^7.10.1"
"@babel/types" "^7.10.1"
"@babel/template@^7.4.0", "@babel/template@^7.7.4": "@babel/template@^7.4.0", "@babel/template@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
@ -1144,6 +1251,21 @@
"@babel/parser" "^7.8.3" "@babel/parser" "^7.8.3"
"@babel/types" "^7.8.3" "@babel/types" "^7.8.3"
"@babel/traverse@^7.10.1":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27"
integrity sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==
dependencies:
"@babel/code-frame" "^7.10.1"
"@babel/generator" "^7.10.1"
"@babel/helper-function-name" "^7.10.1"
"@babel/helper-split-export-declaration" "^7.10.1"
"@babel/parser" "^7.10.1"
"@babel/types" "^7.10.1"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4": "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558"
@ -1189,6 +1311,15 @@
globals "^11.1.0" globals "^11.1.0"
lodash "^4.17.13" lodash "^4.17.13"
"@babel/types@^7.10.1", "@babel/types@^7.10.2":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d"
integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==
dependencies:
"@babel/helper-validator-identifier" "^7.10.1"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.4.0", "@babel/types@^7.7.4": "@babel/types@^7.4.0", "@babel/types@^7.7.4":
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193"
@ -5265,10 +5396,10 @@ glob-parent@~5.1.0:
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
glob@7.1.6, glob@^7.1.4: glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2:
version "7.1.6" version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
@ -5277,9 +5408,10 @@ glob@7.1.6, glob@^7.1.4:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: glob@7.1.6, glob@^7.1.4:
version "7.1.2" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"

View File

@ -4467,10 +4467,10 @@ dezalgo@^1.0.0:
asap "^2.0.0" asap "^2.0.0"
wrappy "1" wrappy "1"
dgeni-packages@^0.28.3: dgeni-packages@^0.28.4:
version "0.28.3" version "0.28.4"
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.28.3.tgz#2e1e55f341c389b67ebb28933ce1e7e9ad05c49b" resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.28.4.tgz#53a3e6700b8d8f6be168cadcc9fdb36e1d7011d3"
integrity sha512-WyVzY3Q4ylfnc2677le5G7a7WqkF88rBSjU9IrAofqro71yzZeWLoEdr/gJY+lJZ0PrDyuRW05pFvIbvX8N0PQ== integrity sha512-7AUG3pKpWtn69c3v2Mzgh+i5gd+L0AxFfYGWGzBdlJqMlQfaQPQjaS54iYCvnOlK9rXBn9j39yO6EU70gDZuFw==
dependencies: dependencies:
canonical-path "^1.0.0" canonical-path "^1.0.0"
catharsis "^0.8.1" catharsis "^0.8.1"

View File

@ -25,6 +25,7 @@ def component_benchmark(
driver_deps, driver_deps,
ng_srcs, ng_srcs,
ng_deps, ng_deps,
ng_assets = [],
assets = None, assets = None,
styles = None, styles = None,
entry_point = None, entry_point = None,
@ -65,6 +66,7 @@ def component_benchmark(
driver_deps: Driver's dependencies driver_deps: Driver's dependencies
ng_srcs: All of the ts srcs for the angular app ng_srcs: All of the ts srcs for the angular app
ng_deps: Dependencies for the angular app ng_deps: Dependencies for the angular app
ng_assets: The static assets for the angular app
assets: Static files assets: Static files
styles: Stylesheets styles: Stylesheets
entry_point: Main entry point for the angular app entry_point: Main entry point for the angular app
@ -104,6 +106,7 @@ def component_benchmark(
ng_module( ng_module(
name = app_lib, name = app_lib,
srcs = ng_srcs, srcs = ng_srcs,
assets = ng_assets,
# Creates ngFactory and ngSummary to be imported by the app's entry point. # Creates ngFactory and ngSummary to be imported by the app's entry point.
generate_ve_shims = True, generate_ve_shims = True,
deps = ng_deps, deps = ng_deps,

View File

@ -11,6 +11,7 @@ import {assertNoErrors, getConfig, NgDevConfig} from '../utils/config';
export interface CommitMessageConfig { export interface CommitMessageConfig {
maxLineLength: number; maxLineLength: number;
minBodyLength: number; minBodyLength: number;
minBodyLengthTypeExcludes?: string[];
types: string[]; types: string[];
scopes: string[]; scopes: string[];
} }
@ -19,7 +20,7 @@ export interface CommitMessageConfig {
export function getCommitMessageConfig() { export function getCommitMessageConfig() {
// List of errors encountered validating the config. // List of errors encountered validating the config.
const errors: string[] = []; const errors: string[] = [];
// The unvalidated config object. // The non-validated config object.
const config: Partial<NgDevConfig<{commitMessage: CommitMessageConfig}>> = getConfig(); const config: Partial<NgDevConfig<{commitMessage: CommitMessageConfig}>> = getConfig();
if (config.commitMessage === undefined) { if (config.commitMessage === undefined) {

View File

@ -10,19 +10,22 @@
import * as validateConfig from './config'; import * as validateConfig from './config';
import {validateCommitMessage} from './validate'; import {validateCommitMessage} from './validate';
type CommitMessageConfig = validateConfig.CommitMessageConfig;
// Constants // Constants
const config = { const config: {commitMessage: CommitMessageConfig} = {
'commitMessage': { commitMessage: {
'maxLineLength': 120, maxLineLength: 120,
'minBodyLength': 0, minBodyLength: 0,
'types': [ types: [
'feat', 'feat',
'fix', 'fix',
'refactor', 'refactor',
'release', 'release',
'style', 'style',
], ],
'scopes': [ scopes: [
'common', 'common',
'compiler', 'compiler',
'core', 'core',
@ -224,5 +227,42 @@ describe('validate-commit-message.js', () => {
}); });
}); });
}); });
describe('minBodyLength', () => {
const minBodyLengthConfig: {commitMessage: CommitMessageConfig} = {
commitMessage: {
maxLineLength: 120,
minBodyLength: 30,
minBodyLengthTypeExcludes: ['docs'],
types: ['fix', 'docs'],
scopes: ['core']
}
};
beforeEach(() => {
(validateConfig.getCommitMessageConfig as jasmine.Spy).and.returnValue(minBodyLengthConfig);
});
it('should fail validation if the body is shorter than `minBodyLength`', () => {
expect(validateCommitMessage(
'fix(core): something\n\n Explanation of the motivation behind this change'))
.toBe(VALID);
expect(validateCommitMessage('fix(core): something\n\n too short')).toBe(INVALID);
expect(lastError).toContain(
'The commit message body does not meet the minimum length of 30 characters');
expect(validateCommitMessage('fix(core): something')).toBe(INVALID);
expect(lastError).toContain(
'The commit message body does not meet the minimum length of 30 characters');
});
it('should pass validation if the body is shorter than `minBodyLength` but the commit type is in the `minBodyLengthTypeExclusions` list',
() => {
expect(validateCommitMessage('docs: just fixing a typo')).toBe(VALID);
expect(validateCommitMessage('docs(core): just fixing a typo')).toBe(VALID);
expect(validateCommitMessage(
'docs(core): just fixing a typo\n\nThis was just a silly typo.'))
.toBe(VALID);
});
});
}); });
}); });

View File

@ -148,7 +148,8 @@ export function validateCommitMessage(
// Checking commit body // // Checking commit body //
////////////////////////// //////////////////////////
if (commit.bodyWithoutLinking.trim().length < config.minBodyLength) { if (!config.minBodyLengthTypeExcludes?.includes(commit.type) &&
commit.bodyWithoutLinking.trim().length < config.minBodyLength) {
printError(`The commit message body does not meet the minimum length of ${ printError(`The commit message body does not meet the minimum length of ${
config.minBodyLength} characters`); config.minBodyLength} characters`);
return false; return false;
@ -157,7 +158,7 @@ export function validateCommitMessage(
const bodyByLine = commit.body.split('\n'); const bodyByLine = commit.body.split('\n');
if (bodyByLine.some(line => line.length > config.maxLineLength)) { if (bodyByLine.some(line => line.length > config.maxLineLength)) {
printError( printError(
`The commit messsage body contains lines greater than ${config.maxLineLength} characters`); `The commit message body contains lines greater than ${config.maxLineLength} characters`);
return false; return false;
} }

View File

@ -63,8 +63,8 @@ export async function discoverNewConflictsForPr(
process.exit(1); process.exit(1);
} }
/** The active github branch when the run began. */ /** The active github branch or revision before we performed any Git commands. */
const originalBranch = git.getCurrentBranch(); const previousBranchOrRevision = git.getCurrentBranchOrRevision();
/* Progress bar to indicate progress. */ /* Progress bar to indicate progress. */
const progressBar = new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total}`}); const progressBar = new Bar({format: `[{bar}] ETA: {eta}s | {value}/{total}`});
/* PRs which were found to be conflicting. */ /* PRs which were found to be conflicting. */
@ -103,7 +103,7 @@ export async function discoverNewConflictsForPr(
const result = exec(`git rebase FETCH_HEAD`); const result = exec(`git rebase FETCH_HEAD`);
if (result.code) { if (result.code) {
error('The requested PR currently has conflicts'); error('The requested PR currently has conflicts');
cleanUpGitState(originalBranch); cleanUpGitState(previousBranchOrRevision);
process.exit(1); process.exit(1);
} }
@ -130,7 +130,7 @@ export async function discoverNewConflictsForPr(
info(); info();
info(`Result:`); info(`Result:`);
cleanUpGitState(originalBranch); cleanUpGitState(previousBranchOrRevision);
// If no conflicts are found, exit successfully. // If no conflicts are found, exit successfully.
if (conflicts.length === 0) { if (conflicts.length === 0) {
@ -147,14 +147,14 @@ export async function discoverNewConflictsForPr(
process.exit(1); process.exit(1);
} }
/** Reset git back to the provided branch. */ /** Reset git back to the provided branch or revision. */
export function cleanUpGitState(branch: string) { export function cleanUpGitState(previousBranchOrRevision: string) {
// Ensure that any outstanding rebases are aborted. // Ensure that any outstanding rebases are aborted.
exec(`git rebase --abort`); exec(`git rebase --abort`);
// Ensure that any changes in the current repo state are cleared. // Ensure that any changes in the current repo state are cleared.
exec(`git reset --hard`); exec(`git reset --hard`);
// Checkout the original branch from before the run began. // Checkout the original branch from before the run began.
exec(`git checkout ${branch}`); exec(`git checkout ${previousBranchOrRevision}`);
// Delete the generated branch. // Delete the generated branch.
exec(`git branch -D ${tempWorkingBranch}`); exec(`git branch -D ${tempWorkingBranch}`);
} }

View File

@ -59,7 +59,7 @@ export class AutosquashMergeStrategy extends MergeStrategy {
// is desired, we set the `GIT_SEQUENCE_EDITOR` environment variable to `true` so that // is desired, we set the `GIT_SEQUENCE_EDITOR` environment variable to `true` so that
// the rebase seems interactive to Git, while it's not interactive to the user. // the rebase seems interactive to Git, while it's not interactive to the user.
// See: https://github.com/git/git/commit/891d4a0313edc03f7e2ecb96edec5d30dc182294. // See: https://github.com/git/git/commit/891d4a0313edc03f7e2ecb96edec5d30dc182294.
const branchBeforeRebase = this.git.getCurrentBranch(); const branchOrRevisionBeforeRebase = this.git.getCurrentBranchOrRevision();
const rebaseEnv = const rebaseEnv =
needsCommitMessageFixup ? undefined : {...process.env, GIT_SEQUENCE_EDITOR: 'true'}; needsCommitMessageFixup ? undefined : {...process.env, GIT_SEQUENCE_EDITOR: 'true'};
this.git.run( this.git.run(
@ -69,9 +69,9 @@ export class AutosquashMergeStrategy extends MergeStrategy {
// Update pull requests commits to reference the pull request. This matches what // Update pull requests commits to reference the pull request. This matches what
// Github does when pull requests are merged through the Web UI. The motivation is // Github does when pull requests are merged through the Web UI. The motivation is
// that it should be easy to determine which pull request contained a given commit. // that it should be easy to determine which pull request contained a given commit.
// **Note**: The filter-branch command relies on the working tree, so we want to make // Note: The filter-branch command relies on the working tree, so we want to make sure
// sure that we are on the initial branch where the merge script has been run. // that we are on the initial branch or revision where the merge script has been invoked.
this.git.run(['checkout', '-f', branchBeforeRebase]); this.git.run(['checkout', '-f', branchOrRevisionBeforeRebase]);
this.git.run( this.git.run(
['filter-branch', '-f', '--msg-filter', `${MSG_FILTER_SCRIPT} ${prNumber}`, revisionRange]); ['filter-branch', '-f', '--msg-filter', `${MSG_FILTER_SCRIPT} ${prNumber}`, revisionRange]);

View File

@ -76,14 +76,14 @@ export class PullRequestMergeTask {
new GithubApiMergeStrategy(this.git, this.config.githubApiMerge) : new GithubApiMergeStrategy(this.git, this.config.githubApiMerge) :
new AutosquashMergeStrategy(this.git); new AutosquashMergeStrategy(this.git);
// Branch that is currently checked out so that we can switch back to it once // Branch or revision that is currently checked out so that we can switch back to
// the pull request has been merged. // it once the pull request has been merged.
let previousBranch: null|string = null; let previousBranchOrRevision: null|string = null;
// The following block runs Git commands as child processes. These Git commands can fail. // The following block runs Git commands as child processes. These Git commands can fail.
// We want to capture these command errors and return an appropriate merge request status. // We want to capture these command errors and return an appropriate merge request status.
try { try {
previousBranch = this.git.getCurrentBranch(); previousBranchOrRevision = this.git.getCurrentBranchOrRevision();
// Run preparations for the merge (e.g. fetching branches). // Run preparations for the merge (e.g. fetching branches).
await strategy.prepare(pullRequest); await strategy.prepare(pullRequest);
@ -96,7 +96,7 @@ export class PullRequestMergeTask {
// Switch back to the previous branch. We need to do this before deleting the temporary // Switch back to the previous branch. We need to do this before deleting the temporary
// branches because we cannot delete branches which are currently checked out. // branches because we cannot delete branches which are currently checked out.
this.git.run(['checkout', '-f', previousBranch]); this.git.run(['checkout', '-f', previousBranchOrRevision]);
await strategy.cleanup(pullRequest); await strategy.cleanup(pullRequest);
@ -112,8 +112,8 @@ export class PullRequestMergeTask {
} finally { } finally {
// Always try to restore the branch if possible. We don't want to leave // Always try to restore the branch if possible. We don't want to leave
// the repository in a different state than before. // the repository in a different state than before.
if (previousBranch !== null) { if (previousBranchOrRevision !== null) {
this.git.runGraceful(['checkout', '-f', previousBranch]); this.git.runGraceful(['checkout', '-f', previousBranchOrRevision]);
} }
} }
} }

View File

@ -50,10 +50,10 @@ export async function rebasePr(
} }
/** /**
* The branch originally checked out before this method performs any Git * The branch or revision originally checked out before this method performed
* operations that may change the working branch. * any Git operations that may change the working branch.
*/ */
const originalBranch = git.getCurrentBranch(); const previousBranchOrRevision = git.getCurrentBranchOrRevision();
/* Get the PR information from Github. */ /* Get the PR information from Github. */
const pr = await getPr(PR_SCHEMA, prNumber, config.github); const pr = await getPr(PR_SCHEMA, prNumber, config.github);
@ -121,7 +121,7 @@ export async function rebasePr(
info(); info();
info(`To abort the rebase and return to the state of the repository before this command`); info(`To abort the rebase and return to the state of the repository before this command`);
info(`run the following command:`); info(`run the following command:`);
info(` $ git rebase --abort && git reset --hard && git checkout ${originalBranch}`); info(` $ git rebase --abort && git reset --hard && git checkout ${previousBranchOrRevision}`);
process.exit(1); process.exit(1);
} else { } else {
info(`Cleaning up git state, and restoring previous state.`); info(`Cleaning up git state, and restoring previous state.`);
@ -137,7 +137,7 @@ export async function rebasePr(
// Ensure that any changes in the current repo state are cleared. // Ensure that any changes in the current repo state are cleared.
git.runGraceful(['reset', '--hard'], {stdio: 'ignore'}); git.runGraceful(['reset', '--hard'], {stdio: 'ignore'});
// Checkout the original branch from before the run began. // Checkout the original branch from before the run began.
git.runGraceful(['checkout', originalBranch], {stdio: 'ignore'}); git.runGraceful(['checkout', previousBranchOrRevision], {stdio: 'ignore'});
} }
} }

View File

@ -31,5 +31,5 @@ export interface PullApproveConfig {
} }
export function parsePullApproveYaml(rawYaml: string): PullApproveConfig { export function parsePullApproveYaml(rawYaml: string): PullApproveConfig {
return parseYaml(rawYaml) as PullApproveConfig; return parseYaml(rawYaml, {merge: true}) as PullApproveConfig;
} }

View File

@ -130,9 +130,16 @@ export class GitClient {
return this.run(['branch', branchName, '--contains', sha]).stdout !== ''; return this.run(['branch', branchName, '--contains', sha]).stdout !== '';
} }
/** Gets the currently checked out branch. */ /** Gets the currently checked out branch or revision. */
getCurrentBranch(): string { getCurrentBranchOrRevision(): string {
return this.run(['rev-parse', '--abbrev-ref', 'HEAD']).stdout.trim(); const branchName = this.run(['rev-parse', '--abbrev-ref', 'HEAD']).stdout.trim();
// If no branch name could be resolved. i.e. `HEAD` has been returned, then Git
// is currently in a detached state. In those cases, we just want to return the
// currently checked out revision/SHA.
if (branchName === 'HEAD') {
return this.run(['rev-parse', 'HEAD']).stdout.trim();
}
return branchName;
} }
/** Gets whether the current Git repository has uncommitted changes. */ /** Gets whether the current Git repository has uncommitted changes. */

View File

@ -76,7 +76,7 @@ node ./scripts/build/build-packages-dist.js
## Running Tests Locally ## Running Tests Locally
Bazel is used as the primary tool for building and testing Angular. Building and testing is Bazel is used as the primary tool for building and testing Angular. Building and testing are
incremental with Bazel, and it's possible to only run tests for an individual package instead incremental with Bazel, and it's possible to only run tests for an individual package instead
of for all packages. Read more about this in the [BAZEL.md](./BAZEL.md) document. of for all packages. Read more about this in the [BAZEL.md](./BAZEL.md) document.
@ -190,7 +190,7 @@ a. Any transitive dependencies of the copied packages will not be automatically
b. The packages need to be copied over every time `npm/yarn install` is run. b. The packages need to be copied over every time `npm/yarn install` is run.
c. Some package managers (such as `pnpm` or `yarn pnp`) might not work correctly. c. Some package managers (such as `pnpm` or `yarn pnp`) might not work correctly.
### Publishing to GitHub repos ### Publishing to GitHub Repos
You can also manually publish `*-builds` snapshots just like our CircleCI build does for upstream You can also manually publish `*-builds` snapshots just like our CircleCI build does for upstream
builds. Before being able to publish the packages, you need to build them locally by running the builds. Before being able to publish the packages, you need to build them locally by running the
`./scripts/build/build-packages-dist.js` script. `./scripts/build/build-packages-dist.js` script.

View File

@ -53,40 +53,45 @@ If you modify any part of a public API in one of the supported public packages,
The public API guard provides a Bazel target that updates the current status of a given package. If you add to or modify the public API in any way, you must use [yarn](https://yarnpkg.com/) to execute the Bazel target in your terminal shell of choice (a recent version of `bash` is recommended). The public API guard provides a Bazel target that updates the current status of a given package. If you add to or modify the public API in any way, you must use [yarn](https://yarnpkg.com/) to execute the Bazel target in your terminal shell of choice (a recent version of `bash` is recommended).
```shell ```shell
yarn bazel run //tools/public_api_guard:<modified_package>_api.accept yarn bazel run //packages/<modified_package>:<modified_package>_api.accept
``` ```
Using yarn ensures that you are running the correct version of Bazel. Using yarn ensures that you are running the correct version of Bazel.
(Read more about building Angular with Bazel [here](./BAZEL.md).) (Read more about building Angular with Bazel [here](./BAZEL.md).)
Here is an example of a Circle CI test failure that resulted from adding a new allowed type to a public property in `forms.d.ts`. Error messages from the API guard use [`git-diff` formatting](https://git-scm.com/docs/git-diff#_combined_diff_format). Here is an example of a Circle CI test failure that resulted from adding a new allowed type to a public property in `core.d.ts`. Error messages from the API guard use [`git-diff` formatting](https://git-scm.com/docs/git-diff#_combined_diff_format).
``` ```
FAIL: //tools/public_api_guard:forms_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test_attempts/attempt_1.log) FAIL: //packages/core:core_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test_attempts/attempt_1.log)
FAIL: //tools/public_api_guard:forms_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test.log) INFO: From Action packages/compiler-cli/ngcc/test/fesm5_angular_core.js:
[BABEL] Note: The code generator has deoptimised the styling of /b/f/w/bazel-out/k8-fastbuild/bin/packages/core/npm_package/fesm2015/core.js as it exceeds the max of 500KB.
FAIL: //packages/core:core_api (see /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test.log)
FAILED: //packages/core:core_api (Summary)
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test.log
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/packages/core/core_api/test_attempts/attempt_1.log
INFO: From Testing //packages/core:core_api:
==================== Test output for //packages/core:core_api:
/b/f/w/bazel-out/k8-fastbuild/bin/packages/core/core_api.sh.runfiles/angular/packages/core/npm_package/core.d.ts(7,1): error: No export declaration found for symbol "ComponentFactory"
--- goldens/public-api/core/core.d.ts Golden file
+++ goldens/public-api/core/core.d.ts Generated API
@@ -563,9 +563,9 @@
ngModule: Type<T>;
providers?: Provider[];
}
-export declare type NgIterable<T> = Array<T> | Iterable<T>;
+export declare type NgIterable<T> = Iterable<T>;
export declare interface NgModule {
bootstrap?: Array<Type<any> | any[]>;
declarations?: Array<Type<any> | any[]>;
FAILED: //tools/public_api_guard:forms_api (Summary)
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test.log
/home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/bazel-out/k8-fastbuild/testlogs/tools/public_api_guard/forms_api/test_attempts/attempt_1.log
INFO: From Testing //tools/public_api_guard:forms_api:
==================== Test output for //tools/public_api_guard:forms_api:
--- tools/public_api_guard/forms/forms.d.ts Golden file
+++ tools/public_api_guard/forms/forms.d.ts Generated API
@@ -4,9 +4,9 @@
readonly disabled: boolean;
readonly enabled: boolean;
readonly errors: ValidationErrors | null;
readonly invalid: boolean;
- readonly parent: FormGroup | FormArray;
+ readonly parent: FormGroup | FormArray | undefined;
readonly pending: boolean;
readonly pristine: boolean;
readonly root: AbstractControl;
readonly status: string;
If you modify a public API, you must accept the new golden file. If you modify a public API, you must accept the new golden file.
To do so, execute the following Bazel target: To do so, execute the following Bazel target:
yarn bazel run //tools/public_api_guard:forms_api.accept yarn bazel run //packages/core:core_api.accept
``` ```

View File

@ -398,7 +398,7 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont
routerLinkActiveOptions: { routerLinkActiveOptions: {
exact: boolean; exact: boolean;
}; };
constructor(router: Router, element: ElementRef, renderer: Renderer2, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined); constructor(router: Router, element: ElementRef, renderer: Renderer2, cdr: ChangeDetectorRef, link?: RouterLink | undefined, linkWithHref?: RouterLinkWithHref | undefined);
ngAfterContentInit(): void; ngAfterContentInit(): void;
ngOnChanges(changes: SimpleChanges): void; ngOnChanges(changes: SimpleChanges): void;
ngOnDestroy(): void; ngOnDestroy(): void;

View File

@ -12,7 +12,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 2987, "runtime-es2015": 2987,
"main-es2015": 451406, "main-es2015": 450883,
"polyfills-es2015": 52630 "polyfills-es2015": 52630
} }
} }
@ -21,8 +21,8 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 3097, "runtime-es2015": 3097,
"main-es2015": 428886, "main-es2015": 428031,
"polyfills-es2015": 52195 "polyfills-es2015": 52261
} }
} }
} }

View File

@ -30,8 +30,8 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 1485, "runtime-es2015": 1485,
"main-es2015": 136302, "main-es2015": 135533,
"polyfills-es2015": 37246 "polyfills-es2015": 37248
} }
} }
}, },
@ -39,7 +39,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 2289, "runtime-es2015": 2289,
"main-es2015": 246085, "main-es2015": 245488,
"polyfills-es2015": 36938, "polyfills-es2015": 36938,
"5-es2015": 751 "5-es2015": 751
} }
@ -62,7 +62,7 @@
"bundle": "TODO(i): we should define ngDevMode to false in Closure, but --define only works in the global scope.", "bundle": "TODO(i): we should define ngDevMode to false in Closure, but --define only works in the global scope.",
"bundle": "TODO(i): (FW-2164) TS 3.9 new class shape seems to have broken Closure in big ways. The size went from 169991 to 252338", "bundle": "TODO(i): (FW-2164) TS 3.9 new class shape seems to have broken Closure in big ways. The size went from 169991 to 252338",
"bundle": "TODO(i): after removal of tsickle from ngc-wrapped / ng_package, we had to switch to SIMPLE optimizations which increased the size from 252338 to 1198917, see PR#37221 and PR#37317 for more info", "bundle": "TODO(i): after removal of tsickle from ngc-wrapped / ng_package, we had to switch to SIMPLE optimizations which increased the size from 252338 to 1198917, see PR#37221 and PR#37317 for more info",
"bundle": 1209688 "bundle": 1209651
} }
} }
} }

View File

@ -4,8 +4,10 @@ import {
ElementRef, ElementRef,
HostBinding, HostBinding,
HostListener, HostListener,
Injectable,
Input, Input,
NgModule NgModule,
Pipe
} from '@angular/core'; } from '@angular/core';
export class NonAngularBaseClass { export class NonAngularBaseClass {
@ -76,3 +78,17 @@ export class UndecoratedPipeBase {
export class WithDirectiveLifecycleHook { export class WithDirectiveLifecycleHook {
ngOnInit() {} ngOnInit() {}
} }
// This class is already decorated and should not be migrated. i.e. no TODO
// or Angular decorator should be added. `@Injectable` is sufficient.
@Injectable()
export class MyService {
ngOnDestroy() {}
}
// This class is already decorated and should not be migrated. i.e. no TODO
// or Angular decorator should be added. `@Injectable` is sufficient.
@Pipe({name: 'my-pipe'})
export class MyPipe {
ngOnDestroy() {}
}

View File

@ -4,8 +4,10 @@ import {
ElementRef, ElementRef,
HostBinding, HostBinding,
HostListener, HostListener,
Injectable,
Input, Input,
NgModule NgModule,
Pipe
} from '@angular/core'; } from '@angular/core';
export class NonAngularBaseClass { export class NonAngularBaseClass {
@ -87,3 +89,17 @@ export class UndecoratedPipeBase {
export class WithDirectiveLifecycleHook { export class WithDirectiveLifecycleHook {
ngOnInit() {} ngOnInit() {}
} }
// This class is already decorated and should not be migrated. i.e. no TODO
// or Angular decorator should be added. `@Injectable` is sufficient.
@Injectable()
export class MyService {
ngOnDestroy() {}
}
// This class is already decorated and should not be migrated. i.e. no TODO
// or Angular decorator should be added. `@Injectable` is sufficient.
@Pipe({name: 'my-pipe'})
export class MyPipe {
ngOnDestroy() {}
}

View File

@ -42,7 +42,7 @@ module.exports = function(config) {
// Including systemjs because it defines `__eval`, which produces correct stack traces. // Including systemjs because it defines `__eval`, which produces correct stack traces.
'test-events.js', 'test-events.js',
'shims_for_IE.js', 'third_party/shims_for_IE.js',
'node_modules/systemjs/dist/system.src.js', 'node_modules/systemjs/dist/system.src.js',
// Serve polyfills necessary for testing the `elements` package. // Serve polyfills necessary for testing the `elements` package.

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-srcs", "name": "angular-srcs",
"version": "10.0.0-rc.6", "version": "10.0.2",
"private": true, "private": true,
"description": "Angular - a web framework for modern web apps", "description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular", "homepage": "https://github.com/angular/angular",

View File

@ -27,5 +27,8 @@
"bugs": { "bugs": {
"url": "https://github.com/angular/angular/issues" "url": "https://github.com/angular/angular/issues"
}, },
"homepage": "https://github.com/angular/angular/tree/master/packages/compiler-cli" "homepage": "https://github.com/angular/angular/tree/master/packages/compiler-cli",
"publishConfig": {
"registry": "https://wombat-dressing-room.appspot.com"
}
} }

View File

@ -339,7 +339,9 @@ export class HttpXhrBackend implements HttpBackend {
} }
// Finally, abort the in-flight request. // Finally, abort the in-flight request.
xhr.abort(); if (xhr.readyState !== xhr.DONE) {
xhr.abort();
}
}; };
}); });
} }

View File

@ -147,6 +147,17 @@ const XSSI_PREFIX = ')]}\'\n';
}); });
factory.mock.mockErrorEvent(new Error('blah')); factory.mock.mockErrorEvent(new Error('blah'));
}); });
it('avoids abort a request when fetch operation is completed', done => {
const abort = jasmine.createSpy('abort');
backend.handle(TEST_POST).toPromise().then(() => {
expect(abort).not.toHaveBeenCalled();
done();
});
factory.mock.abort = abort;
factory.mock.mockFlush(200, 'OK', 'Done');
});
describe('progress events', () => { describe('progress events', () => {
it('are emitted for download progress', done => { it('are emitted for download progress', done => {
backend.handle(TEST_POST.clone({reportProgress: true})) backend.handle(TEST_POST.clone({reportProgress: true}))

View File

@ -31,6 +31,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/indexer", "//packages/compiler-cli/src/ngtsc/indexer",
"//packages/compiler-cli/src/ngtsc/perf", "//packages/compiler-cli/src/ngtsc/perf",
"//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/reflection",
"//packages/compiler-cli/src/ngtsc/shims",
"//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/typecheck",
"@npm//@bazel/typescript", "@npm//@bazel/typescript",
"@npm//@types/node", "@npm//@types/node",

View File

@ -12,7 +12,7 @@ import {ParsedConfiguration} from '../../..';
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations';
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles'; import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics';
import {absoluteFrom, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system'; import {absoluteFrom, absoluteFromSourceFile, dirname, FileSystem, LogicalFileSystem, resolve} from '../../../src/ngtsc/file_system';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../../src/ngtsc/metadata'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
@ -148,7 +148,8 @@ export class DecorationAnalyzer {
*/ */
analyzeProgram(): DecorationAnalyses { analyzeProgram(): DecorationAnalyses {
for (const sourceFile of this.program.getSourceFiles()) { for (const sourceFile of this.program.getSourceFiles()) {
if (!sourceFile.isDeclarationFile && isWithinPackage(this.packagePath, sourceFile)) { if (!sourceFile.isDeclarationFile &&
isWithinPackage(this.packagePath, absoluteFromSourceFile(sourceFile))) {
this.compiler.analyzeFile(sourceFile); this.compiler.analyzeFile(sourceFile);
} }
} }

View File

@ -7,7 +7,7 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system'; import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system';
import {MetadataReader} from '../../../src/ngtsc/metadata'; import {MetadataReader} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection'; import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
@ -44,7 +44,7 @@ export class DefaultMigrationHost implements MigrationHost {
} }
isInScope(clazz: ClassDeclaration): boolean { isInScope(clazz: ClassDeclaration): boolean {
return isWithinPackage(this.entryPointPath, clazz.getSourceFile()); return isWithinPackage(this.entryPointPath, absoluteFromSourceFile(clazz.getSourceFile()));
} }
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system'; import {absoluteFromSourceFile, AbsoluteFsPath} from '../../../src/ngtsc/file_system';
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host'; import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
import {isWithinPackage} from './util'; import {isWithinPackage} from './util';
@ -35,7 +35,7 @@ export class SwitchMarkerAnalyzer {
analyzeProgram(program: ts.Program): SwitchMarkerAnalyses { analyzeProgram(program: ts.Program): SwitchMarkerAnalyses {
const analyzedFiles = new SwitchMarkerAnalyses(); const analyzedFiles = new SwitchMarkerAnalyses();
program.getSourceFiles() program.getSourceFiles()
.filter(sourceFile => isWithinPackage(this.packagePath, sourceFile)) .filter(sourceFile => isWithinPackage(this.packagePath, absoluteFromSourceFile(sourceFile)))
.forEach(sourceFile => { .forEach(sourceFile => {
const declarations = this.host.getSwitchableDeclarations(sourceFile); const declarations = this.host.getSwitchableDeclarations(sourceFile);
if (declarations.length) { if (declarations.length) {

View File

@ -5,13 +5,11 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import {AbsoluteFsPath, relative} from '../../../src/ngtsc/file_system';
import {absoluteFromSourceFile, AbsoluteFsPath, relative} from '../../../src/ngtsc/file_system';
import {DependencyTracker} from '../../../src/ngtsc/incremental/api'; import {DependencyTracker} from '../../../src/ngtsc/incremental/api';
export function isWithinPackage(packagePath: AbsoluteFsPath, sourceFile: ts.SourceFile): boolean { export function isWithinPackage(packagePath: AbsoluteFsPath, filePath: AbsoluteFsPath): boolean {
const relativePath = relative(packagePath, absoluteFromSourceFile(sourceFile)); const relativePath = relative(packagePath, filePath);
return !relativePath.startsWith('..') && !relativePath.startsWith('node_modules/'); return !relativePath.startsWith('..') && !relativePath.startsWith('node_modules/');
} }

View File

@ -28,13 +28,13 @@ export class ClusterExecutor implements Executor {
async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, _createCompileFn: CreateCompileFn): async execute(analyzeEntryPoints: AnalyzeEntryPointsFn, _createCompileFn: CreateCompileFn):
Promise<void> { Promise<void> {
return this.lockFile.lock(() => { return this.lockFile.lock(async () => {
this.logger.debug( this.logger.debug(
`Running ngcc on ${this.constructor.name} (using ${this.workerCount} worker processes).`); `Running ngcc on ${this.constructor.name} (using ${this.workerCount} worker processes).`);
const master = new ClusterMaster( const master = new ClusterMaster(
this.workerCount, this.fileSystem, this.logger, this.fileWriter, this.pkgJsonUpdater, this.workerCount, this.fileSystem, this.logger, this.fileWriter, this.pkgJsonUpdater,
analyzeEntryPoints, this.createTaskCompletedCallback); analyzeEntryPoints, this.createTaskCompletedCallback);
return master.run(); return await master.run();
}); });
} }
} }

View File

@ -7,6 +7,7 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference} from '../../../src/ngtsc/reflection'; import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference} from '../../../src/ngtsc/reflection';
import {isWithinPackage} from '../analysis/util'; import {isWithinPackage} from '../analysis/util';
@ -2525,7 +2526,7 @@ function getRootFileOrFail(bundle: BundleProgram): ts.SourceFile {
function getNonRootPackageFiles(bundle: BundleProgram): ts.SourceFile[] { function getNonRootPackageFiles(bundle: BundleProgram): ts.SourceFile[] {
const rootFile = bundle.program.getSourceFile(bundle.path); const rootFile = bundle.program.getSourceFile(bundle.path);
return bundle.program.getSourceFiles().filter( return bundle.program.getSourceFiles().filter(
f => (f !== rootFile) && isWithinPackage(bundle.package, f)); f => (f !== rootFile) && isWithinPackage(bundle.package, absoluteFromSourceFile(f)));
} }
function isTopLevel(node: ts.Node): boolean { function isTopLevel(node: ts.Node): boolean {

View File

@ -37,7 +37,11 @@ export class AsyncLocker {
*/ */
async lock<T>(fn: () => Promise<T>): Promise<T> { async lock<T>(fn: () => Promise<T>): Promise<T> {
await this.create(); await this.create();
return fn().finally(() => this.lockFile.remove()); try {
return await fn();
} finally {
this.lockFile.remove();
}
} }
protected async create() { protected async create() {

View File

@ -50,7 +50,7 @@ export function makeEntryPointBundle(
const rootDir = entryPoint.packagePath; const rootDir = entryPoint.packagePath;
const options: ts const options: ts
.CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings}; .CompilerOptions = {allowJs: true, maxNodeModuleJsDepth: Infinity, rootDir, ...pathMappings};
const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.path); const srcHost = new NgccSourcesCompilerHost(fs, options, entryPoint.packagePath);
const dtsHost = new NgtscCompilerHost(fs, options); const dtsHost = new NgtscCompilerHost(fs, options);
// Create the bundle programs, as necessary. // Create the bundle programs, as necessary.
@ -63,7 +63,7 @@ export function makeEntryPointBundle(
[]; [];
const dts = transformDts ? makeBundleProgram( const dts = transformDts ? makeBundleProgram(
fs, isCore, entryPoint.packagePath, typingsPath, 'r3_symbols.d.ts', fs, isCore, entryPoint.packagePath, typingsPath, 'r3_symbols.d.ts',
options, dtsHost, additionalDtsFiles) : {...options, allowJs: false}, dtsHost, additionalDtsFiles) :
null; null;
const isFlatCore = isCore && src.r3SymbolsFile === null; const isFlatCore = isCore && src.r3SymbolsFile === null;

View File

@ -7,7 +7,8 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system';
import {isWithinPackage} from '../analysis/util';
import {isRelativePath} from '../utils'; import {isRelativePath} from '../utils';
/** /**
@ -20,7 +21,7 @@ export class NgccSourcesCompilerHost extends NgtscCompilerHost {
private cache = ts.createModuleResolutionCache( private cache = ts.createModuleResolutionCache(
this.getCurrentDirectory(), file => this.getCanonicalFileName(file)); this.getCurrentDirectory(), file => this.getCanonicalFileName(file));
constructor(fs: FileSystem, options: ts.CompilerOptions, protected entryPointPath: string) { constructor(fs: FileSystem, options: ts.CompilerOptions, protected packagePath: AbsoluteFsPath) {
super(fs, options); super(fs, options);
} }
@ -36,13 +37,24 @@ export class NgccSourcesCompilerHost extends NgtscCompilerHost {
// file was in the same directory. This is undesirable, as we need to have the actual // file was in the same directory. This is undesirable, as we need to have the actual
// JavaScript being present in the program. This logic recognizes this scenario and rewrites // JavaScript being present in the program. This logic recognizes this scenario and rewrites
// the resolved .d.ts declaration file to its .js counterpart, if it exists. // the resolved .d.ts declaration file to its .js counterpart, if it exists.
if (resolvedModule !== undefined && resolvedModule.extension === ts.Extension.Dts && if (resolvedModule?.extension === ts.Extension.Dts && containingFile.endsWith('.js') &&
containingFile.endsWith('.js') && isRelativePath(moduleName)) { isRelativePath(moduleName)) {
const jsFile = resolvedModule.resolvedFileName.replace(/\.d\.ts$/, '.js'); const jsFile = resolvedModule.resolvedFileName.replace(/\.d\.ts$/, '.js');
if (this.fileExists(jsFile)) { if (this.fileExists(jsFile)) {
return {...resolvedModule, resolvedFileName: jsFile, extension: ts.Extension.Js}; return {...resolvedModule, resolvedFileName: jsFile, extension: ts.Extension.Js};
} }
} }
// Prevent loading JavaScript source files outside of the package root, which would happen for
// packages that don't have .d.ts files. As ngcc should only operate on the .js files
// contained within the package, any files outside the package are simply discarded. This does
// result in a partial program with error diagnostics, however ngcc won't gather diagnostics
// for the program it creates so these diagnostics won't be reported.
if (resolvedModule?.extension === ts.Extension.Js &&
!isWithinPackage(this.packagePath, this.fs.resolve(resolvedModule.resolvedFileName))) {
return undefined;
}
return resolvedModule; return resolvedModule;
}); });
} }

View File

@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript';
import {absoluteFrom} from '../../../src/ngtsc/file_system'; import {absoluteFrom} from '../../../src/ngtsc/file_system';
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
import {isWithinPackage} from '../../src/analysis/util'; import {isWithinPackage} from '../../src/analysis/util';
@ -18,15 +17,13 @@ runInEachFileSystem(() => {
it('should return true if the source-file is contained in the package', () => { it('should return true if the source-file is contained in the package', () => {
const packagePath = _('/node_modules/test'); const packagePath = _('/node_modules/test');
const file = const file = _('/node_modules/test/src/index.js');
ts.createSourceFile(_('/node_modules/test/src/index.js'), '', ts.ScriptTarget.ES2015);
expect(isWithinPackage(packagePath, file)).toBe(true); expect(isWithinPackage(packagePath, file)).toBe(true);
}); });
it('should return false if the source-file is not contained in the package', () => { it('should return false if the source-file is not contained in the package', () => {
const packagePath = _('/node_modules/test'); const packagePath = _('/node_modules/test');
const file = const file = _('/node_modules/other/src/index.js');
ts.createSourceFile(_('/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015);
expect(isWithinPackage(packagePath, file)).toBe(false); expect(isWithinPackage(packagePath, file)).toBe(false);
}); });
@ -34,13 +31,11 @@ runInEachFileSystem(() => {
const packagePath = _('/node_modules/test'); const packagePath = _('/node_modules/test');
// An external file inside the package's `node_modules/`. // An external file inside the package's `node_modules/`.
const file1 = ts.createSourceFile( const file1 = _('/node_modules/test/node_modules/other/src/index.js');
_('/node_modules/test/node_modules/other/src/index.js'), '', ts.ScriptTarget.ES2015);
expect(isWithinPackage(packagePath, file1)).toBe(false); expect(isWithinPackage(packagePath, file1)).toBe(false);
// An internal file starting with `node_modules`. // An internal file starting with `node_modules`.
const file2 = ts.createSourceFile( const file2 = _('/node_modules/test/node_modules_optimizer.js');
_('/node_modules/test/node_modules_optimizer.js'), '', ts.ScriptTarget.ES2015);
expect(isWithinPackage(packagePath, file2)).toBe(true); expect(isWithinPackage(packagePath, file2)).toBe(true);
}); });
}); });

View File

@ -34,7 +34,7 @@ runInEachFileSystem(() => {
beforeEach(() => { beforeEach(() => {
masterRunSpy = spyOn(ClusterMaster.prototype, 'run') masterRunSpy = spyOn(ClusterMaster.prototype, 'run')
.and.returnValue(Promise.resolve('CusterMaster#run()' as any)); .and.returnValue(Promise.resolve('ClusterMaster#run()' as any));
createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback'); createTaskCompletedCallback = jasmine.createSpy('createTaskCompletedCallback');
mockLogger = new MockLogger(); mockLogger = new MockLogger();
@ -63,7 +63,7 @@ runInEachFileSystem(() => {
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn'); const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy)) expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy))
.toBe('CusterMaster#run()' as any); .toBe('ClusterMaster#run()' as any);
expect(masterRunSpy).toHaveBeenCalledWith(); expect(masterRunSpy).toHaveBeenCalledWith();
@ -78,6 +78,22 @@ runInEachFileSystem(() => {
expect(lockFileLog).toEqual(['write()', 'remove()']); expect(lockFileLog).toEqual(['write()', 'remove()']);
}); });
it('should call LockFile.write() and LockFile.remove() if analyzeFn fails', async () => {
const analyzeEntryPointsSpy =
jasmine.createSpy('analyzeEntryPoints').and.throwError('analyze error');
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
let error = '';
try {
await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy);
} catch (e) {
error = e.message;
}
expect(analyzeEntryPointsSpy).toHaveBeenCalledWith();
expect(createCompilerFnSpy).not.toHaveBeenCalled();
expect(error).toEqual('analyze error');
expect(lockFileLog).toEqual(['write()', 'remove()']);
});
it('should call LockFile.write() and LockFile.remove() if master runner fails', async () => { it('should call LockFile.write() and LockFile.remove() if master runner fails', async () => {
const anyFn: () => any = () => undefined; const anyFn: () => any = () => undefined;
masterRunSpy.and.returnValue(Promise.reject(new Error('master runner error'))); masterRunSpy.and.returnValue(Promise.reject(new Error('master runner error')));

View File

@ -68,7 +68,7 @@ export function makeTestBundleProgram(
const rootDir = fs.dirname(entryPointPath); const rootDir = fs.dirname(entryPointPath);
const options: ts.CompilerOptions = const options: ts.CompilerOptions =
{allowJs: true, maxNodeModuleJsDepth: Infinity, checkJs: false, rootDir, rootDirs: [rootDir]}; {allowJs: true, maxNodeModuleJsDepth: Infinity, checkJs: false, rootDir, rootDirs: [rootDir]};
const host = new NgccSourcesCompilerHost(fs, options, entryPointPath); const host = new NgccSourcesCompilerHost(fs, options, rootDir);
return makeBundleProgram( return makeBundleProgram(
fs, isCore, rootDir, path, 'r3_symbols.js', options, host, additionalFiles); fs, isCore, rootDir, path, 'r3_symbols.js', options, host, additionalFiles);
} }

View File

@ -401,6 +401,121 @@ runInEachFileSystem(() => {
expect(es5Contents).toContain('ɵngcc0.ɵɵtext(0, "a - b - 3 - 4")'); expect(es5Contents).toContain('ɵngcc0.ɵɵtext(0, "a - b - 3 - 4")');
}); });
it('should not crash when scanning for ModuleWithProviders needs to evaluate code from an external package',
() => {
// Regression test for https://github.com/angular/angular/issues/37508
// During `ModuleWithProviders` analysis, return statements in methods are evaluated using
// the partial evaluator to identify whether they correspond with a `ModuleWithProviders`
// function. If an arbitrary method has a return statement that calls into an external
// module which doesn't have declaration files, ngcc would attempt to reflect on said
// module using the reflection host of the entry-point. This would crash in the case where
// e.g. the entry-point is UMD and the external module would be CommonJS, as the UMD
// reflection host would throw because it is unable to deal with CommonJS.
// Setup a non-TS package with CommonJS module format
loadTestFiles([
{
name: _(`/node_modules/identity/package.json`),
contents: `{"name": "identity", "main": "./index.js"}`,
},
{
name: _(`/node_modules/identity/index.js`),
contents: `
function identity(x) { return x; };
exports.identity = identity;
module.exports = identity;
`,
},
]);
// Setup an Angular entry-point with UMD module format that references an export of the
// CommonJS package.
loadTestFiles([
{
name: _('/node_modules/test-package/package.json'),
contents: '{"name": "test-package", "main": "./index.js", "typings": "./index.d.ts"}'
},
{
name: _('/node_modules/test-package/index.js'),
contents: `
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('identity')) :
typeof define === 'function' && define.amd ? define('test', ['exports', 'identity'], factory) :
(factory(global.test, global.identity));
}(this, (function (exports, identity) { 'use strict';
function Foo(x) {
// The below statement is analyzed for 'ModuleWithProviders', so is evaluated
// by ngcc. The reference into the non-TS CommonJS package used to crash ngcc.
return identity.identity(x);
}
exports.Foo = Foo;
})));
`
},
{
name: _('/node_modules/test-package/index.d.ts'),
contents: 'export declare class Foo { static doSomething(x: any): any; }'
},
{name: _('/node_modules/test-package/index.metadata.json'), contents: 'DUMMY DATA'},
]);
expect(() => mainNgcc({
basePath: '/node_modules',
targetEntryPointPath: 'test-package',
propertiesToConsider: ['main'],
}))
.not.toThrow();
});
it('should not be able to evaluate code in external packages when no .d.ts files are present',
() => {
loadTestFiles([
{
name: _(`/node_modules/external/package.json`),
contents: `{"name": "external", "main": "./index.js"}`,
},
{
name: _(`/node_modules/external/index.js`),
contents: `
export const selector = 'my-selector';
`,
},
]);
compileIntoApf('test-package', {
'/index.ts': `
import {NgModule, Component} from '@angular/core';
import {selector} from 'external';
@Component({
selector,
template: ''
})
export class FooComponent {
}
@NgModule({
declarations: [FooComponent],
})
export class FooModule {}
`,
});
try {
mainNgcc({
basePath: '/node_modules',
targetEntryPointPath: 'test-package',
propertiesToConsider: ['esm2015', 'esm5'],
});
fail('should have thrown');
} catch (e) {
expect(e.message).toContain(
'Failed to compile entry-point test-package (esm2015 as esm2015) due to compilation errors:');
expect(e.message).toContain('NG1010');
expect(e.message).toContain('selector must be a string');
}
});
it('should add ɵfac but not duplicate ɵprov properties on injectables', () => { it('should add ɵfac but not duplicate ɵprov properties on injectables', () => {
compileIntoFlatEs2015Package('test-package', { compileIntoFlatEs2015Package('test-package', {
'/index.ts': ` '/index.ts': `

View File

@ -13,8 +13,12 @@ import {makeEntryPointBundle} from '../../src/packages/entry_point_bundle';
runInEachFileSystem(() => { runInEachFileSystem(() => {
describe('entry point bundle', () => { describe('entry point bundle', () => {
let _: typeof absoluteFrom;
beforeEach(() => {
_ = absoluteFrom;
});
function setupMockFileSystem(): void { function setupMockFileSystem(): void {
const _ = absoluteFrom;
loadTestFiles([ loadTestFiles([
{ {
name: _('/node_modules/test/package.json'), name: _('/node_modules/test/package.json'),
@ -210,6 +214,103 @@ runInEachFileSystem(() => {
].map(p => absoluteFrom(p).toString()))); ].map(p => absoluteFrom(p).toString())));
}); });
it('does not include .js files outside of the package when no .d.ts file is available', () => {
// Declare main "test" package with "entry" entry-point that imports all sorts of
// internal and external modules.
loadTestFiles([
{
name: _('/node_modules/test/entry/package.json'),
contents: `{"name": "test", "main": "./index.js", "typings": "./index.d.ts"}`,
},
{
name: _('/node_modules/test/entry/index.d.ts'),
contents: `
import 'external-js';
import 'external-ts';
import 'nested-js';
import './local';
import '../package';
`,
},
{
name: _('/node_modules/test/entry/index.js'),
contents: `
import 'external-js';
import 'external-ts';
import 'nested-js';
import './local';
import '../package';
`,
},
{name: _('/node_modules/test/entry/local.d.ts'), contents: `export {};`},
{name: _('/node_modules/test/entry/local.js'), contents: `export {};`},
{name: _('/node_modules/test/package.d.ts'), contents: `export {};`},
{name: _('/node_modules/test/package.js'), contents: `export {};`},
]);
// Declare "external-js" package outside of the "test" package without .d.ts files, should
// not be included in the program.
loadTestFiles([
{
name: _('/node_modules/external-js/package.json'),
contents: `{"name": "external-js", "main": "./index.js"}`,
},
{name: _('/node_modules/external-js/index.js'), contents: 'export {};'},
]);
// Same as "external-js" but located in a nested node_modules directory, which should also
// not be included in the program.
loadTestFiles([
{
name: _('/node_modules/test/node_modules/nested-js/package.json'),
contents: `{"name": "nested-js", "main": "./index.js"}`,
},
{name: _('/node_modules/test/node_modules/nested-js/index.js'), contents: 'export {}'},
]);
// Declare "external-ts" which does have .d.ts files, so the .d.ts should be
// loaded into the program.
loadTestFiles([
{
name: _('/node_modules/external-ts/package.json'),
contents: `{"name": "external-ts", "main": "./index.js", "typings": "./index.d.ts"}`,
},
{name: _('/node_modules/external-ts/index.d.ts'), contents: 'export {};'},
{name: _('/node_modules/external-ts/index.js'), contents: 'export {};'},
]);
const fs = getFileSystem();
const entryPoint: EntryPoint = {
name: 'test/entry',
path: absoluteFrom('/node_modules/test/entry'),
packageName: 'test',
packagePath: absoluteFrom('/node_modules/test'),
packageJson: {name: 'test/entry'},
typings: absoluteFrom('/node_modules/test/entry/index.d.ts'),
compiledByAngular: true,
ignoreMissingDependencies: false,
generateDeepReexports: false,
};
const esm5bundle = makeEntryPointBundle(
fs, entryPoint, './index.js', false, 'esm5', /* transformDts */ true,
/* pathMappings */ undefined, /* mirrorDtsFromSrc */ true);
expect(esm5bundle.src.program.getSourceFiles().map(sf => _(sf.fileName)))
.toEqual(jasmine.arrayWithExactContents([
_('/node_modules/test/entry/index.js'),
_('/node_modules/test/entry/local.js'),
_('/node_modules/test/package.js'),
_('/node_modules/external-ts/index.d.ts'),
]));
expect(esm5bundle.dts!.program.getSourceFiles().map(sf => _(sf.fileName)))
.toEqual(jasmine.arrayWithExactContents([
_('/node_modules/test/entry/index.d.ts'),
_('/node_modules/test/entry/local.d.ts'),
_('/node_modules/test/package.d.ts'),
_('/node_modules/external-ts/index.d.ts'),
]));
});
describe( describe(
'including equivalently named, internally imported, src files in the typings program', 'including equivalently named, internally imported, src files in the typings program',
() => { () => {

View File

@ -116,7 +116,13 @@ export class NgCompiler {
const moduleResolutionCache = ts.createModuleResolutionCache( const moduleResolutionCache = ts.createModuleResolutionCache(
this.adapter.getCurrentDirectory(), this.adapter.getCurrentDirectory(),
fileName => this.adapter.getCanonicalFileName(fileName)); // Note: this used to be an arrow-function closure. However, JS engines like v8 have some
// strange behaviors with retaining the lexical scope of the closure. Even if this function
// doesn't retain a reference to `this`, if other closures in the constructor here reference
// `this` internally then a closure created here would retain them. This can cause major
// memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
// way into all kinds of places inside TS internal objects.
this.adapter.getCanonicalFileName.bind(this.adapter));
this.moduleResolver = this.moduleResolver =
new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache); new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
this.resourceManager = new AdapterResourceLoader(adapter, this.options); this.resourceManager = new AdapterResourceLoader(adapter, this.options);

View File

@ -42,20 +42,22 @@ export class NoopIncrementalBuildStrategy implements IncrementalBuildStrategy {
* Tracks an `IncrementalDriver` within the strategy itself. * Tracks an `IncrementalDriver` within the strategy itself.
*/ */
export class TrackedIncrementalBuildStrategy implements IncrementalBuildStrategy { export class TrackedIncrementalBuildStrategy implements IncrementalBuildStrategy {
private previous: IncrementalDriver|null = null; private driver: IncrementalDriver|null = null;
private next: IncrementalDriver|null = null; private isSet: boolean = false;
getIncrementalDriver(): IncrementalDriver|null { getIncrementalDriver(): IncrementalDriver|null {
return this.next !== null ? this.next : this.previous; return this.driver;
} }
setIncrementalDriver(driver: IncrementalDriver): void { setIncrementalDriver(driver: IncrementalDriver): void {
this.next = driver; this.driver = driver;
this.isSet = true;
} }
toNextBuildStrategy(): TrackedIncrementalBuildStrategy { toNextBuildStrategy(): TrackedIncrementalBuildStrategy {
const strategy = new TrackedIncrementalBuildStrategy(); const strategy = new TrackedIncrementalBuildStrategy();
strategy.previous = this.next; // Only reuse a driver that was explicitly set via `setIncrementalDriver`.
strategy.driver = this.isSet ? this.driver : null;
return strategy; return strategy;
} }
} }

View File

@ -17,6 +17,7 @@ import {NgCompilerOptions} from './core/api';
import {TrackedIncrementalBuildStrategy} from './incremental'; import {TrackedIncrementalBuildStrategy} from './incremental';
import {IndexedComponent} from './indexer'; import {IndexedComponent} from './indexer';
import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf'; import {NOOP_PERF_RECORDER, PerfRecorder, PerfTracker} from './perf';
import {retagAllTsFiles, untagAllTsFiles} from './shims';
import {ReusedProgramStrategy} from './typecheck'; import {ReusedProgramStrategy} from './typecheck';
@ -68,14 +69,26 @@ export class NgtscProgram implements api.Program {
} }
this.closureCompilerEnabled = !!options.annotateForClosureCompiler; this.closureCompilerEnabled = !!options.annotateForClosureCompiler;
const reuseProgram = oldProgram && oldProgram.reuseTsProgram; const reuseProgram = oldProgram?.reuseTsProgram;
this.host = NgCompilerHost.wrap(delegateHost, rootNames, options, reuseProgram ?? null); this.host = NgCompilerHost.wrap(delegateHost, rootNames, options, reuseProgram ?? null);
if (reuseProgram !== undefined) {
// Prior to reusing the old program, restore shim tagging for all its `ts.SourceFile`s.
// TypeScript checks the `referencedFiles` of `ts.SourceFile`s for changes when evaluating
// incremental reuse of data from the old program, so it's important that these match in order
// to get the most benefit out of reuse.
retagAllTsFiles(reuseProgram);
}
this.tsProgram = ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram); this.tsProgram = ts.createProgram(this.host.inputFiles, options, this.host, reuseProgram);
this.reuseTsProgram = this.tsProgram; this.reuseTsProgram = this.tsProgram;
this.host.postProgramCreationCleanup(); this.host.postProgramCreationCleanup();
// Shim tagging has served its purpose, and tags can now be removed from all `ts.SourceFile`s in
// the program.
untagAllTsFiles(this.tsProgram);
const reusedProgramStrategy = new ReusedProgramStrategy( const reusedProgramStrategy = new ReusedProgramStrategy(
this.tsProgram, this.host, this.options, this.host.shimExtensionPrefixes); this.tsProgram, this.host, this.options, this.host.shimExtensionPrefixes);
@ -93,6 +106,10 @@ export class NgtscProgram implements api.Program {
return this.tsProgram; return this.tsProgram;
} }
getReuseTsProgram(): ts.Program {
return this.reuseTsProgram;
}
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
undefined): readonly ts.Diagnostic[] { undefined): readonly ts.Diagnostic[] {
return this.tsProgram.getOptionsDiagnostics(cancellationToken); return this.tsProgram.getOptionsDiagnostics(cancellationToken);
@ -248,6 +265,7 @@ export class NgtscProgram implements api.Program {
})); }));
this.perfRecorder.stop(fileEmitSpan); this.perfRecorder.stop(fileEmitSpan);
} }
this.perfRecorder.stop(emitSpan); this.perfRecorder.stop(emitSpan);
if (this.perfTracker !== null && this.options.tracePerformance !== undefined) { if (this.perfTracker !== null && this.options.tracePerformance !== undefined) {

View File

@ -9,7 +9,7 @@
/// <reference types="node" /> /// <reference types="node" />
export {ShimAdapter} from './src/adapter'; export {ShimAdapter} from './src/adapter';
export {copyFileShimData, isShim} from './src/expando'; export {copyFileShimData, isShim, retagAllTsFiles, retagTsFile, sfExtensionData, untagAllTsFiles, untagTsFile} from './src/expando';
export {FactoryGenerator, generatedFactoryTransform} from './src/factory_generator'; export {FactoryGenerator, generatedFactoryTransform} from './src/factory_generator';
export {ShimReferenceTagger} from './src/reference_tagger'; export {ShimReferenceTagger} from './src/reference_tagger';
export {SummaryGenerator} from './src/summary_generator'; export {SummaryGenerator} from './src/summary_generator';

View File

@ -12,7 +12,7 @@ import {absoluteFrom, absoluteFromSourceFile, AbsoluteFsPath} from '../../file_s
import {isDtsPath} from '../../util/src/typescript'; import {isDtsPath} from '../../util/src/typescript';
import {PerFileShimGenerator, TopLevelShimGenerator} from '../api'; import {PerFileShimGenerator, TopLevelShimGenerator} from '../api';
import {isFileShimSourceFile, isShim, NgExtension, sfExtensionData} from './expando'; import {isFileShimSourceFile, isShim, sfExtensionData} from './expando';
import {makeShimFileName} from './util'; import {makeShimFileName} from './util';
interface ShimGeneratorData { interface ShimGeneratorData {

View File

@ -21,7 +21,16 @@ export const NgExtension = Symbol('NgExtension');
export interface NgExtensionData { export interface NgExtensionData {
isTopLevelShim: boolean; isTopLevelShim: boolean;
fileShim: NgFileShimData|null; fileShim: NgFileShimData|null;
/**
* The contents of the `referencedFiles` array, before modification by a `ShimReferenceTagger`.
*/
originalReferencedFiles: ReadonlyArray<ts.FileReference>|null; originalReferencedFiles: ReadonlyArray<ts.FileReference>|null;
/**
* The contents of the `referencedFiles` array, after modification by a `ShimReferenceTagger`.
*/
taggedReferenceFiles: ReadonlyArray<ts.FileReference>|null;
} }
/** /**
@ -65,6 +74,7 @@ export function sfExtensionData(sf: ts.SourceFile): NgExtensionData {
isTopLevelShim: false, isTopLevelShim: false,
fileShim: null, fileShim: null,
originalReferencedFiles: null, originalReferencedFiles: null,
taggedReferenceFiles: null,
}; };
extSf[NgExtension] = extension; extSf[NgExtension] = extension;
return extension; return extension;
@ -110,3 +120,53 @@ export function copyFileShimData(from: ts.SourceFile, to: ts.SourceFile): void {
} }
sfExtensionData(to).fileShim = sfExtensionData(from).fileShim; sfExtensionData(to).fileShim = sfExtensionData(from).fileShim;
} }
/**
* For those `ts.SourceFile`s in the `program` which have previously been tagged by a
* `ShimReferenceTagger`, restore the original `referencedFiles` array that does not have shim tags.
*/
export function untagAllTsFiles(program: ts.Program): void {
for (const sf of program.getSourceFiles()) {
untagTsFile(sf);
}
}
/**
* For those `ts.SourceFile`s in the `program` which have previously been tagged by a
* `ShimReferenceTagger`, re-apply the effects of tagging by updating the `referencedFiles` array to
* the tagged version produced previously.
*/
export function retagAllTsFiles(program: ts.Program): void {
for (const sf of program.getSourceFiles()) {
retagTsFile(sf);
}
}
/**
* Restore the original `referencedFiles` for the given `ts.SourceFile`.
*/
export function untagTsFile(sf: ts.SourceFile): void {
if (sf.isDeclarationFile || !isExtended(sf)) {
return;
}
const ext = sfExtensionData(sf);
if (ext.originalReferencedFiles !== null) {
sf.referencedFiles = ext.originalReferencedFiles as Array<ts.FileReference>;
}
}
/**
* Apply the previously tagged `referencedFiles` to the given `ts.SourceFile`, if it was previously
* tagged.
*/
export function retagTsFile(sf: ts.SourceFile): void {
if (sf.isDeclarationFile || !isExtended(sf)) {
return;
}
const ext = sfExtensionData(sf);
if (ext.taggedReferenceFiles !== null) {
sf.referencedFiles = ext.taggedReferenceFiles as Array<ts.FileReference>;
}
}

View File

@ -8,10 +8,10 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {absoluteFrom, absoluteFromSourceFile} from '../../file_system'; import {absoluteFromSourceFile} from '../../file_system';
import {isNonDeclarationTsPath} from '../../util/src/typescript'; import {isNonDeclarationTsPath} from '../../util/src/typescript';
import {isExtended as isExtendedSf, isShim, NgExtension, sfExtensionData} from './expando'; import {isShim, sfExtensionData} from './expando';
import {makeShimFileName} from './util'; import {makeShimFileName} from './util';
/** /**
@ -48,8 +48,16 @@ export class ShimReferenceTagger {
return; return;
} }
sfExtensionData(sf).originalReferencedFiles = sf.referencedFiles; const ext = sfExtensionData(sf);
const referencedFiles = [...sf.referencedFiles];
// If this file has never been tagged before, capture its `referencedFiles` in the extension
// data.
if (ext.originalReferencedFiles === null) {
ext.originalReferencedFiles = sf.referencedFiles;
}
const referencedFiles = [...ext.originalReferencedFiles];
const sfPath = absoluteFromSourceFile(sf); const sfPath = absoluteFromSourceFile(sf);
for (const suffix of this.suffixes) { for (const suffix of this.suffixes) {
@ -60,26 +68,16 @@ export class ShimReferenceTagger {
}); });
} }
ext.taggedReferenceFiles = referencedFiles;
sf.referencedFiles = referencedFiles; sf.referencedFiles = referencedFiles;
this.tagged.add(sf); this.tagged.add(sf);
} }
/** /**
* Restore the original `referencedFiles` values of all tagged `ts.SourceFile`s and disable the * Disable the `ShimReferenceTagger` and free memory associated with tracking tagged files.
* `ShimReferenceTagger`.
*/ */
finalize(): void { finalize(): void {
this.enabled = false; this.enabled = false;
for (const sf of this.tagged) {
if (!isExtendedSf(sf)) {
continue;
}
const extensionData = sfExtensionData(sf);
if (extensionData.originalReferencedFiles !== null) {
sf.referencedFiles = extensionData.originalReferencedFiles! as ts.FileReference[];
}
}
this.tagged.clear(); this.tagged.clear();
} }
} }

View File

@ -12,6 +12,7 @@ import {absoluteFrom as _, AbsoluteFsPath, getSourceFileOrError} from '../../fil
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {makeProgram} from '../../testing'; import {makeProgram} from '../../testing';
import {ShimAdapter} from '../src/adapter'; import {ShimAdapter} from '../src/adapter';
import {retagTsFile, untagTsFile} from '../src/expando';
import {ShimReferenceTagger} from '../src/reference_tagger'; import {ShimReferenceTagger} from '../src/reference_tagger';
import {TestShimGenerator} from './util'; import {TestShimGenerator} from './util';
@ -67,40 +68,6 @@ runInEachFileSystem(() => {
expect(shimSf.referencedFiles).toEqual([]); expect(shimSf.referencedFiles).toEqual([]);
}); });
it('should remove tags during finalization', () => {
const tagger = new ShimReferenceTagger(['test1', 'test2']);
const fileName = _('/file.ts');
const sf = makeArbitrarySf(fileName);
expectReferencedFiles(sf, []);
tagger.tag(sf);
expectReferencedFiles(sf, ['/file.test1.ts', '/file.test2.ts']);
tagger.finalize();
expectReferencedFiles(sf, []);
});
it('should not remove references it did not add during finalization', () => {
const tagger = new ShimReferenceTagger(['test1', 'test2']);
const fileName = _('/file.ts');
const libFileName = _('/lib.d.ts');
const sf = makeSf(fileName, `
/// <reference path="/lib.d.ts" />
export const UNIMPORTANT = true;
`);
expectReferencedFiles(sf, [libFileName]);
tagger.tag(sf);
expectReferencedFiles(sf, ['/file.test1.ts', '/file.test2.ts', libFileName]);
tagger.finalize();
expectReferencedFiles(sf, [libFileName]);
});
it('should not tag shims after finalization', () => { it('should not tag shims after finalization', () => {
const tagger = new ShimReferenceTagger(['test1', 'test2']); const tagger = new ShimReferenceTagger(['test1', 'test2']);
tagger.finalize(); tagger.finalize();
@ -111,6 +78,56 @@ runInEachFileSystem(() => {
tagger.tag(sf); tagger.tag(sf);
expectReferencedFiles(sf, []); expectReferencedFiles(sf, []);
}); });
it('should not overwrite original referencedFiles', () => {
const tagger = new ShimReferenceTagger(['test']);
const fileName = _('/file.ts');
const sf = makeArbitrarySf(fileName);
sf.referencedFiles = [{
fileName: _('/other.ts'),
pos: 0,
end: 0,
}];
tagger.tag(sf);
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
});
it('should always tag against the original referencedFiles', () => {
const tagger1 = new ShimReferenceTagger(['test1']);
const tagger2 = new ShimReferenceTagger(['test2']);
const fileName = _('/file.ts');
const sf = makeArbitrarySf(fileName);
tagger1.tag(sf);
tagger2.tag(sf);
expectReferencedFiles(sf, ['/file.test2.ts']);
});
describe('tagging and untagging', () => {
it('should be able to untag references and retag them later', () => {
const tagger = new ShimReferenceTagger(['test']);
const fileName = _('/file.ts');
const sf = makeArbitrarySf(fileName);
sf.referencedFiles = [{
fileName: _('/other.ts'),
pos: 0,
end: 0,
}];
tagger.tag(sf);
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
untagTsFile(sf);
expectReferencedFiles(sf, ['/other.ts']);
retagTsFile(sf);
expectReferencedFiles(sf, ['/other.ts', '/file.test.ts']);
});
});
}); });
}); });

View File

@ -5,4 +5,4 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
export {getDeclaration, makeProgram} from './src/utils'; export {expectCompleteReuse, getDeclaration, makeProgram} from './src/utils';

View File

@ -97,6 +97,23 @@ export function walkForDeclaration(name: string, rootNode: ts.Node): ts.Declarat
return chosenDecl; return chosenDecl;
} }
const COMPLETE_REUSE_FAILURE_MESSAGE =
'The original program was not reused completely, even though no changes should have been made to its structure';
/**
* Extracted from TypeScript's internal enum `StructureIsReused`.
*/
enum TsStructureIsReused {
Not = 0,
SafeModules = 1,
Completely = 2,
}
export function expectCompleteReuse(oldProgram: ts.Program): void {
// Assert complete reuse using TypeScript's private API.
expect((oldProgram as any).structureIsReused)
.toBe(TsStructureIsReused.Completely, COMPLETE_REUSE_FAILURE_MESSAGE);
}
function bindingNameEquals(node: ts.BindingName, name: string): boolean { function bindingNameEquals(node: ts.BindingName, name: string): boolean {
if (ts.isIdentifier(node)) { if (ts.isIdentifier(node)) {

View File

@ -13,6 +13,7 @@ import {NgCompilerOptions, UnifiedModulesHost} from './core/api';
import {NodeJSFileSystem, setFileSystem} from './file_system'; import {NodeJSFileSystem, setFileSystem} from './file_system';
import {PatchedProgramIncrementalBuildStrategy} from './incremental'; import {PatchedProgramIncrementalBuildStrategy} from './incremental';
import {NOOP_PERF_RECORDER} from './perf'; import {NOOP_PERF_RECORDER} from './perf';
import {untagAllTsFiles} from './shims';
import {ReusedProgramStrategy} from './typecheck/src/augmented_program'; import {ReusedProgramStrategy} from './typecheck/src/augmented_program';
// The following is needed to fix a the chicken-and-egg issue where the sync (into g3) script will // The following is needed to fix a the chicken-and-egg issue where the sync (into g3) script will
@ -80,6 +81,9 @@ export class NgTscPlugin implements TscPlugin {
wrapHost( wrapHost(
host: ts.CompilerHost&UnifiedModulesHost, inputFiles: readonly string[], host: ts.CompilerHost&UnifiedModulesHost, inputFiles: readonly string[],
options: ts.CompilerOptions): PluginCompilerHost { options: ts.CompilerOptions): PluginCompilerHost {
// TODO(alxhub): Eventually the `wrapHost()` API will accept the old `ts.Program` (if one is
// available). When it does, its `ts.SourceFile`s need to be re-tagged to enable proper
// incremental compilation.
this.options = {...this.ngOptions, ...options} as NgCompilerOptions; this.options = {...this.ngOptions, ...options} as NgCompilerOptions;
this.host = NgCompilerHost.wrap(host, inputFiles, this.options, /* oldProgram */ null); this.host = NgCompilerHost.wrap(host, inputFiles, this.options, /* oldProgram */ null);
return this.host; return this.host;
@ -92,6 +96,8 @@ export class NgTscPlugin implements TscPlugin {
if (this.host === null || this.options === null) { if (this.host === null || this.options === null) {
throw new Error('Lifecycle error: setupCompilation() before wrapHost().'); throw new Error('Lifecycle error: setupCompilation() before wrapHost().');
} }
this.host.postProgramCreationCleanup();
untagAllTsFiles(program);
const typeCheckStrategy = new ReusedProgramStrategy( const typeCheckStrategy = new ReusedProgramStrategy(
program, this.host, this.options, this.host.shimExtensionPrefixes); program, this.host, this.options, this.host.shimExtensionPrefixes);
this._compiler = new NgCompiler( this._compiler = new NgCompiler(

View File

@ -9,6 +9,7 @@
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../file_system'; import {AbsoluteFsPath} from '../../file_system';
import {retagAllTsFiles, untagAllTsFiles} from '../../shims';
import {TypeCheckingProgramStrategy, UpdateMode} from './api'; import {TypeCheckingProgramStrategy, UpdateMode} from './api';
import {TypeCheckProgramHost} from './host'; import {TypeCheckProgramHost} from './host';
@ -26,8 +27,10 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
*/ */
private sfMap = new Map<string, ts.SourceFile>(); private sfMap = new Map<string, ts.SourceFile>();
private program: ts.Program = this.originalProgram;
constructor( constructor(
private program: ts.Program, private originalHost: ts.CompilerHost, private originalProgram: ts.Program, private originalHost: ts.CompilerHost,
private options: ts.CompilerOptions, private shimExtensionPrefixes: string[]) {} private options: ts.CompilerOptions, private shimExtensionPrefixes: string[]) {}
getProgram(): ts.Program { getProgram(): ts.Program {
@ -35,6 +38,17 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
} }
updateFiles(contents: Map<AbsoluteFsPath, string>, updateMode: UpdateMode): void { updateFiles(contents: Map<AbsoluteFsPath, string>, updateMode: UpdateMode): void {
if (contents.size === 0) {
// No changes have been requested. Is it safe to skip updating entirely?
// If UpdateMode is Incremental, then yes. If UpdateMode is Complete, then it's safe to skip
// only if there are no active changes already (that would be cleared by the update).
if (updateMode !== UpdateMode.Complete || this.sfMap.size === 0) {
// No changes would be made to the `ts.Program` anyway, so it's safe to do nothing here.
return;
}
}
if (updateMode === UpdateMode.Complete) { if (updateMode === UpdateMode.Complete) {
this.sfMap.clear(); this.sfMap.clear();
} }
@ -43,14 +57,25 @@ export class ReusedProgramStrategy implements TypeCheckingProgramStrategy {
this.sfMap.set(filePath, ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true)); this.sfMap.set(filePath, ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true));
} }
const host = const host = new TypeCheckProgramHost(
new TypeCheckProgramHost(this.sfMap, this.originalHost, this.shimExtensionPrefixes); this.sfMap, this.originalProgram, this.originalHost, this.shimExtensionPrefixes);
const oldProgram = this.program;
// Retag the old program's `ts.SourceFile`s with shim tags, to allow TypeScript to reuse the
// most data.
retagAllTsFiles(oldProgram);
this.program = ts.createProgram({ this.program = ts.createProgram({
host, host,
rootNames: this.program.getRootFileNames(), rootNames: this.program.getRootFileNames(),
options: this.options, options: this.options,
oldProgram: this.program, oldProgram,
}); });
host.postProgramCreationCleanup(); host.postProgramCreationCleanup();
// And untag them afterwards. We explicitly untag both programs here, because the oldProgram
// may still be used for emit and needs to not contain tags.
untagAllTsFiles(this.program);
untagAllTsFiles(oldProgram);
} }
} }

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