The new `EntryPointManifest` class can read and write a
manifest file that contains all the paths to the entry-points
that have been found in a node_modules folder.
This can be used to speed up finding entry-points in
subsequent runs.
The manifest file stores the ngcc version and hashes of
the package lock-file and project config, since if these
change the manifest will need to be recomputed.
PR Close#35931
Currently, when running the ngcc binary directly and provide an invalid option ngcc will not error out and the user might have a hard time telling why ngcc is behaving not as expected.
With this change we now output an actionable error:
```
yarn ngcc --unknown-option
Options:
--version Show version number [boolean]
-s, --source A path (relative to the working directory)
of the `node_modules` folder to process.
[default: "./node_modules"]
-p, --properties An array of names of properties in
package.json to compile (e.g. `module` or
`es2015`)
Each of these properties should hold the
path to a bundle-format.
If provided, only the specified properties
are considered for processing.
If not provided, all the supported format
properties (e.g. fesm2015, fesm5, es2015,
esm2015, esm5, main, module) in the
package.json are considered. [array]
-t, --target A relative path (from the `source` path) to
a single entry-point to process (plus its
dependencies).
--first-only If specified then only the first matching
package.json property will be compiled.
[boolean]
--create-ivy-entry-points If specified then new `*_ivy_ngcc`
entry-points will be added to package.json
rather than modifying the ones in-place.
For this to work you need to have custom
resolution set up (e.g. in webpack) to look
for these new entry-points.
The Angular CLI does this already, so it is
safe to use this option if the project is
being built via the CLI. [boolean]
--legacy-message-ids Render `$localize` messages with legacy
format ids.
The default value is `true`. Only set this
to `false` if you do not want legacy
message ids to
be rendered. For example, if you are not
using legacy message ids in your
translation files
AND are not doing compile-time inlining of
translations, in which case the extra
message ids
would add unwanted size to the final source
bundle.
It is safe to leave this set to true if you
are doing compile-time inlining because the
extra
legacy message ids will all be stripped
during translation.
[boolean] [default: true]
--async Whether to compile asynchronously. This is
enabled by default as it allows
compilations to be parallelized.
Disabling asynchronous compilation may be
useful for debugging.
[boolean] [default: true]
-l, --loglevel The lowest severity logging message that
should be output.
[choices: "debug", "info", "warn", "error"]
--invalidate-entry-point-manifest If this is set then ngcc will not read an
entry-point manifest file from disk.
Instead it will walking the directory tree
as normal looking for entry-points, and
then write a new manifest file.
[boolean] [default: false]
--help Show help [boolean]
Unknown arguments: unknown-option, unknownOption
```
PR Close#36010
Moves the public api .d.ts files from tools/public_api_guard to
goldens/public-api.
Additionally, provides a README in the goldens directory and a script
assist in testing the current state of the repo against the goldens as
well as a command for accepting all changes to the goldens in a single
command.
PR Close#35768
This commit adds support in the Angular monorepo and in the Angular
compiler(s) for TypeScript 3.8. All packages can now compile with
TS 3.8.
For most of the repo, only a handful few typings adjustments were needed:
* TS 3.8 has a new `CustomElementConstructor` DOM type, which enforces a
zero-argument constructor. The `NgElementConstructor` type previously
declared a required `injector` argument despite the fact that its
implementation allowed `injector` to be optional. The interface type was
updated to reflect the optionality of the argument.
* Certain error messages were changed, and expectations in tests were
updated as a result.
* tsserver (part of language server) now returns performance information in
responses, so test expectations were changed to only assert on the actual
body content of responses.
For compiler-cli and schematics (which use the TypeScript AST) a major
breaking change was the introduction of the export form:
```typescript
export * as foo from 'bar';
```
This is a `ts.NamespaceExport`, and the `exportClause` of a
`ts.ExportDeclaration` can now take this type as well as `ts.NamedExports`.
This broke a lot of places where `exportClause` was assumed to be
`ts.NamedExports`.
For the most part these breakages were in cases where it is not necessary
to handle the new `ts.NamedExports` anyway. ngtsc's design uses the
`ts.TypeChecker` APIs to understand syntax and so automatically supports the
new form of exports.
The View Engine compiler on the other hand extracts TS structures into
metadata.json files, and that format was not designed for namespaced
exports. As a result it will take a nontrivial amount of work if we want to
support such exports in View Engine. For now, these new exports are not
accounted for in metadata.json, and so using them in "folded" Angular
expressions will result in errors (probably claiming that the referenced
exported namespace doesn't exist).
Care was taken to only use TS APIs which are present in 3.7/3.6, as Angular
needs to remain compatible with these for the time being.
This commit does not update angular.io.
PR Close#35864
Prior to this commit, while calculating the scope for a module, Ivy compiler processed `declarations` field first and `imports` after that. That results in a couple issues:
* for Pipes with the same `name` and present in `declarations` and in an imported module, Pipe from imported module was selected. In View Engine the logic is opposite: Pipes from `declarations` field receive higher priority.
* for Directives with the same selector and present in `declarations` and in an imported module, we first invoked the logic of a Directive from `declarations` field and after that - imported Directive logic. In View Engine, it was the opposite and the logic of a Directive from the `declarations` field was invoked last.
In order to align Ivy and View Engine behavior, this commit updates the logic in which we populate module scope: we first process all imports and after that handle `declarations` field. As a result, in Ivy both use-cases listed above work similar to View Engine.
Resolves#35502.
PR Close#35850
This commit splits the ngtsc `core` package's api entrypoint, which
previously was a single `api.ts` file, into an api/ directory with multiple
files. This is done to isolate the parts of the API definitions pertaining
to the public-facing `angularCompilerOptions` field in tsconfig.json into a
single file, which will enable a public API guard test to be added in a
future commit.
PR Close#35885
Currently, when Angular code is built with Bazel and with Ivy, generated
factory shims (.ngfactory files) are not processed via the majority of
tsickle's transforms. This is a subtle effect of the build infrastructure,
but it boils down to a TsickleHost method `shouldSkipTsickleProcessing`.
For ngc_wrapped builds (Bazel + Angular), this method is defined in the
`@bazel/typescript` (aka bazel rules_typescript) implementation of
`CompilerHost`. The default behavior is to skip tsickle processing for files
which are not present in the original `srcs[]` of the build rule. In
Angular's case, this includes all generated shim files.
For View Engine factories this is probably desirable as they're quite
complex and they've never been tested with tsickle. Ivy factories however
are smaller and very straightforward, and it makes sense to treat them like
any other output.
This commit adjusts two independent implementations of
`shouldSkipTsickleProcessing` to enable transformation of Ivy shims:
* in `@angular/bazel` aka ngc_wrapped, the upstream `@bazel/typescript`
`CompilerHost` is patched to treat .ngfactory files the same as their
original source file, with respect to tsickle processing.
It is currently not possible to test this change as we don't have any test
that inspects tsickle output with bazel. It will be extensively tested in
g3.
* in `ngc`, Angular's own implementation is adjusted to allow for the
processing of shims when compiling with Ivy. This enables a unit test to
be written to validate the correct behavior of tsickle when given a host
that's appropriately configured to process factory shims.
For ngtsc-as-a-plugin, a similar fix will need to be submitted upstream in
tsc_wrapped.
PR Close#35848
This version of `LockFile` creates an "unlocker" child-process that monitors
the main ngcc process and deletes the lock file if it exits unexpectedly.
This resolves the issue where the main process could not be killed by pressing
Ctrl-C at the terminal.
Fixes#35761
PR Close#35861
The previous implementation mixed up the management
of locking a piece of code (both sync and async) with the
management of writing and removing the lockFile that is
used as the flag for which process has locked the code.
This change splits these two concepts up. Apart from
avoiding the awkward base class it allows the `LockFile`
implementation to be replaced cleanly.
PR Close#35861
This reduces the time that `findEntryPoints` takes from 9701.143ms to 4177.278ms, by reducing the file operations done.
Reference: #35717
PR Close#35756
It's an error to declare a variable twice on a specific template:
```html
<div *ngFor="let i of items; let i = index">
</div>
```
This commit introduces a template type-checking error which helps to detect
and diagnose this problem.
Fixes#35186
PR Close#35674
`ɵɵNgOnChangesFeature()` would set `ngInherit`, which is a side effect and also not necessary. This was pulled out to module scope so the function itself can be pure. Since it only curries another function, the call is entirely unnecessary. Updated the compiler to only generate a reference to this function, rather than a call to it, and removed the extra curry indirection.
PR Close#35769
Prior to this commit, i18n attributes defined on `<ng-template>` tags were not processed by the compiler. This commit adds the necessary logic to handle i18n attributes in the same way how these attrs are processed for regular elements.
PR Close#35681
With this change we spawn workers lazily based on the amount of work that needs to be done.
Before this change we spawned the maximum of workers possible. However, in some cases there are less tasks than the max number of workers which resulted in created unnecessary workers
Reference: #35717
PR Close#35719
When the `NgIf` directive is used in a template, its context variables
can be used to capture the bound value. This is typically used together
with a pipe or function call, where the resulting value is captured in a
context variable. There's two syntax forms available:
1. Binding to `NgIfContext.ngIf` using the `as` syntax:
```html
<span *ngIf="(user$ | async) as user">{{user.name}}</span>
```
2. Binding to `NgIfContext.$implicit` using the `let` syntax:
```html
<span *ngIf="user$ | async; let user">{{user.name}}</span>
```
Because of the semantics of `ngIf`, it is known that the captured
context variable is non-nullable, however the template type checker
would not consider them as such and still report errors when
`strictNullTypes` is enabled.
This commit updates `NgIf`'s context guard to make the types of the
context variables non-nullable, avoiding the issue.
Fixes#34572
PR Close#35125
For view and content queries, the Ivy compiler attempts to statically
evaluate the predicate token so that string predicates containing
comma-separated reference names can be split into an array of strings
during compilation. When the predicate is a dynamic value that cannot be
statically interpreted at compile time, the compiler would previously
produce an error. This behavior breaks a use-case where an `InjectionToken`
is being used as query predicate, as the usage of the `new` keyword
prevents such predicates from being statically evaluated.
This commit changes the behavior to no longer produce an error for
dynamic values. Instead, the expression is emitted as is into the
generated code, postponing the evaluation to happen at runtime.
Fixes#34267
Resolves FW-1828
PR Close#35307
Source-maps in the wild could be badly formatted,
causing the source-map flattening processing to fail
unexpectedly. Rather than causing the whole of ngcc
to crash, we gracefully fallback to just returning the
generated source-map instead.
PR Close#35718
Previously when rendering flattened source-maps, it was assumed that no
mapping would come from a line that is outside the lines of the actual
source content. It turns out this is not a valid assumption.
Now the code that renders flattened source-maps will handle such
mappings, with the additional benefit that the rendered source-map
will only contain mapping lines up to the last mapping, rather than a
mapping line for every content line.
Fixes#35709
PR Close#35718
If a package has a source-map but it does not provide
the actual content of the sources, then the source-map
flattening was crashing.
Now we ignore such mappings that have no source
since we are not able to compute the merged
mapping if there is no source file.
Fixes#35709
PR Close#35718
This commit adds a new ngcc configuration, `ignorableDeepImportMatchers`
for packages. This is a list of regular expressions matching deep imports
that can be safely ignored from that package. Deep imports that are not
ignored cause a warning to be logged.
// FW-1892
Fixes#35615
PR Close#35683
It's possible to pass a directive as an input to itself. Consider:
```html
<some-cmp #ref [value]="ref">
```
Since the template type-checker attempts to infer a type for `<some-cmp>`
using the values of its inputs, this creates a circular reference where the
type of the `value` input is used in its own inference:
```typescript
var _t0 = SomeCmp.ngTypeCtor({value: _t0});
```
Obviously, this doesn't work. To resolve this, the template type-checker
used to generate a `null!` expression when a reference would otherwise be
circular:
```typescript
var _t0 = SomeCmp.ngTypeCtor({value: null!});
```
This effectively asks TypeScript to infer a value for this context, and
works well to resolve this simple cycle. However, if the template
instead tries to use the circular value in a larger expression:
```html
<some-cmp #ref [value]="ref.prop">
```
The checker would generate:
```typescript
var _t0 = SomeCmp.ngTypeCtor({value: (null!).prop});
```
In this case, TypeScript can't figure out any way `null!` could have a
`prop` key, and so it infers `never` as the type. `(never).prop` is thus a
type error.
This commit implements a better fallback pattern for circular references to
directive types like this. Instead of generating a `null!` in place for the
reference, a type is inferred by calling the type constructor again with
`null!` as its input. This infers the widest possible type for the directive
which is then used to break the cycle:
```typescript
var _t0 = SomeCmp.ngTypeCtor(null!);
var _t1 = SomeCmp.ngTypeCtor({value: _t0.prop});
```
This has the desired effect of validating that `.prop` is legal for the
directive type (the type of `#ref`) while also avoiding a cycle.
Fixes#35372Fixes#35603Fixes#35522
PR Close#35622
NG6002/NG6003 are errors produced when an NgModule being compiled has an
imported or exported type which does not have the proper metadata (that is,
it doesn't appear to be an @NgModule, or @Directive, etc. depending on
context).
Previously this error message was a bit sparse. However, Github issues show
that this is the most common error users receive when for whatever reason
ngcc wasn't able to handle one of their libraries, or they just didn't run
it. So this commit changes the error message to offer a bit more useful
context, instructing users differently depending on whether the class in
question is from their own project, from NPM, or from a monorepo-style local
dependency.
PR Close#35620
The library used by ngcc to update the source files (MagicString) is able
to generate a source-map but it is not able to account for any previous
source-map that the input text is already associated with.
There have been various attempts to fix this but none have been very
successful, since it is not a trivial problem to solve.
This commit contains a novel approach that is able to load up a tree of
source-files connected by source-maps and flatten them down into a single
source-map that maps directly from the final generated file to the original
sources referenced by the intermediate source-maps.
PR Close#35132
Previously if there were two path-mapped libraries that are in
different directories but the path of one started with same string
as the path of the other, we would incorrectly return the shorter
path - e.g. `dist/my-lib` and `dist/my-lib-second`. This was because
the list of `basePaths` was searched in ascending alphabetic order and
we were using `startsWith()` to match the path.
Now the `basePaths` are searched in reverse alphabetic order so the
longer path will be matched correctly.
// FW-1873
Fixes#35536
PR Close#35592
* it's tricky to get out of the runfiles tree with `bazel test` as `BUILD_WORKSPACE_DIRECTORY` is not set but I employed a trick to read the `DO_NOT_BUILD_HERE` file that is one level up from `execroot` and that contains the workspace directory. This is experimental and if `bazel test //:test.debug` fails than `bazel run` is still guaranteed to work as `BUILD_WORKSPACE_DIRECTORY` will be set in that context
* test //integration:bazel_test and //integration:bazel-schematics_test exclusively
* run "exclusive" and "manual" bazel-in-bazel integration tests in their own CI job as they take 8m+ to execute
```
//integration:bazel-schematics_test PASSED in 317.2s
//integration:bazel_test PASSED in 167.8s
```
* Skip all integration tests that are now handled by angular_integration_test except the tests that are tracked for payload size; these are:
- cli-hello-world*
- hello_world__closure
* add & pin @babel deps as newer versions of babel break //packages/localize/src/tools/test:test
@babel/core dep had to be pinned to 7.6.4 or else //packages/localize/src/tools/test:test failed. Also //packages/localize uses @babel/generator, @babel/template, @babel/traverse & @babel/types so these deps were added to package.json as they were not being hoisted anymore from @babel/core transitive.
NB: integration/hello_world__systemjs_umd test must run with systemjs 0.20.0
NB: systemjs must be at 0.18.10 for legacy saucelabs job to pass
NB: With Bazel 2.0, the glob for the files to test `"integration/bazel/**"` is empty if integation/bazel is in .bazelignore. This glob worked under these conditions with 1.1.0. I did not bother testing with 1.2.x as not having integration/bazel in .bazelignore is correct.
PR Close#33927
Under View Engine's default (non-fullTemplateTypeCheck) checking, object and
array literals which appear in templates are treated as having type `any`.
This allows a number of patterns which would not otherwise compile, such as
indexing an object literal by a string:
```html
{{ {'a': 1, 'b': 2}[value] }}
```
(where `value` is `string`)
Ivy, meanwhile, has always inferred strong types for object literals, even
in its compatibility mode. This commit fixes the bug, and adds the
`strictLiteralTypes` flag to specifically control this inference. When the
flag is `false` (in compatibility mode), object and array literals receive
the `any` type.
PR Close#35462
In its default compatibility mode, the Ivy template type-checker attempts to
emulate the View Engine default mode as accurately as is possible. This
commit addresses a gap in this compatibility that stems from a View Engine
type-checking bug.
Consider two template expressions:
```html
{{ obj?.field }}
{{ fn()?.field }}
```
and suppose that the type of `obj` and `fn()` are the same - both return
either `null` or an object with a `field` property.
Under View Engine, these type-check differently. The `obj` case will catch
if the object type (when not null) does not have a `field` property, while
the `fn()` case will not. This is due to how View Engine represents safe
navigations:
```typescript
// for the 'obj' case
(obj == null ? null as any : obj.field)
// for the 'fn()' case
let tmp: any;
((tmp = fn()) == null ? null as any : tmp.field)
```
Because View Engine uses the same code generation backend as it does to
produce the runtime code for this expression, it uses a ternary for safe
navigation, with a temporary variable to avoid invoking 'fn()' twice. The
type of this temporary variable is 'any', however, which causes the
`tmp.field` check to be meaningless.
Previously, the Ivy template type-checker in compatibility mode assumed that
`fn()?.field` would always check for the presence of 'field' on the non-null
result of `fn()`. This commit emulates the View Engine bug in Ivy's
compatibility mode, so an 'any' type will be inferred under the same
conditions.
As part of this fix, a new format for safe navigation operations in template
type-checking code is introduced. This is based on the realization that
ternary based narrowing is unnecessary.
For the `fn()` case in strict mode, Ivy now generates:
```typescript
(null as any ? fn()!.field : undefined)
```
This effectively uses the ternary operator as a type "or" operation. The
resulting type will be a union of the type of `fn()!.field` with
`undefined`.
For the `fn()` case in compatibility mode, Ivy now emulates the bug with:
```typescript
(fn() as any).field
```
The cast expression includes the call to `fn()` and allows it to be checked
while still returning a type of `any` from the expression.
For the `obj` case in compatibility mode, Ivy now generates:
```typescript
(obj!.field as any)
```
This cast expression still returns `any` for its type, but will check for
the existence of `field` on the type of `obj!`.
PR Close#35462
In ES5 code, TypeScript requires certain helpers (such as
`__spreadArrays()`) to be able to support ES2015+ features. These
helpers can be either imported from `tslib` (by setting the
`importHelpers` TS compiler option to `true`) or emitted inline (by
setting the `importHelpers` and `noEmitHelpers` TS compiler options to
`false`, which is the default value for both).
Ngtsc's `StaticInterpreter` (which is also used during ngcc processing)
is able to statically evaluate some of these helpers (currently
`__assign()`, `__spread()` and `__spreadArrays()`), as long as
`ReflectionHost#getDefinitionOfFunction()` correctly detects the
declaration of the helper. For this to happen, the left-hand side of the
corresponding call expression (i.e. `__spread(...)` or
`tslib.__spread(...)`) must be evaluated as a function declaration for
`getDefinitionOfFunction()` to be called with.
In the case of imported helpers, the `tslib.__someHelper` expression was
resolved to a function declaration of the form
`export declare function __someHelper(...args: any[][]): any[];`, which
allows `getDefinitionOfFunction()` to correctly map it to a TS helper.
In contrast, in the case of emitted helpers (and regardless of the
module format: `CommonJS`, `ESNext`, `UMD`, etc.)), the `__someHelper`
identifier was resolved to a variable declaration of the form
`var __someHelper = (this && this.__someHelper) || function () { ... }`,
which upon further evaluation was categorized as a `DynamicValue`
(prohibiting further evaluation by the `getDefinitionOfFunction()`).
As a result of the above, emitted TypeScript helpers were not evaluated
in ES5 code.
---
This commit changes the detection of TS helpers to leverage the existing
`KnownFn` feature (previously only used for built-in functions).
`Esm5ReflectionHost` is changed to always return `KnownDeclaration`s for
TS helpers, both imported (`getExportsOfModule()`) as well as emitted
(`getDeclarationOfIdentifier()`).
Similar changes are made to `CommonJsReflectionHost` and
`UmdReflectionHost`.
The `KnownDeclaration`s are then mapped to `KnownFn`s in
`StaticInterpreter`, allowing it to statically evaluate call expressions
involving any kind of TS helpers.
Jira issue: https://angular-team.atlassian.net/browse/FW-1689
PR Close#35191
This is in preparation of using the `KnownFn` type for known TypeScript
helpers (in addition to built-in functions/methods). This will in turn
allow simplifying the detection of both imported and emitted TypeScript
helpers.
PR Close#35191
When statically evaluating CommonJS code it is possible to find that we
are looking for the declaration of an identifier that actually came from
a typings file (rather than a CommonJS file).
Previously, the CommonJS reflection host would always try to use a
CommonJS specific algorithm for finding identifier declarations, but
when the id is actually in a typings file this resulted in the returned
declaration being the containing file of the declaration rather than the
declaration itself.
Now the CommonJS reflection host will check to see if the file
containing the identifier is a typings file and use the appropriate
stategy.
(Note: This is the equivalent of #34356 but for CommonJS.)
PR Close#35191
In #33705 we made it so that we generate pure functions for object/array literals in order to avoid having them be shared across elements/views. The problem this introduced is that further down the line the `ContantPool` uses the generated literal in order to figure out whether to share an existing factory or to create a new one. `ConstantPool` determines whether to share a factory by creating a key from the AST node and using it to look it up in the factory cache, however the key generation function didn't handle function invocations and replaced them with `null`. This means that the key for `{foo: pureFunction0(...)}` and `{foo: null}` are the same.
These changes rework the logic so that instead of generating a `null` key
for function invocations, we generate a variable called `<unknown>` which
shouldn't be able to collide with anything.
Fixes#35298.
PR Close#35481
Currently Ivy always generates the `$event` function argument, even if it isn't being used by the listener expressions. This can lead to unnecessary bytes being generated, because optimizers won't remove unused arguments by default. These changes add some logic to avoid adding the argument when it isn't required.
PR Close#35097
In ES5 and ES2015, class identifiers may have aliases. Previously, the
`NgccReflectionHost`s recognized the following formats:
- ES5:
```js
var MyClass = (function () {
function InnerClass() {}
InnerClass_1 = InnerClass;
...
}());
```
- ES2015:
```js
let MyClass = MyClass_1 = class MyClass { ... };
```
In addition to the above, this commit adds support for recognizing an
alias outside the IIFE in ES5 classes (which was previously not
supported):
```js
var MyClass = MyClass_1 = (function () { ... }());
```
Jira issue: [FW-1869](https://angular-team.atlassian.net/browse/FW-1869)
Partially addresses #35399.
PR Close#35527
`Esm5ReflectionHost#getInnerFunctionDeclarationFromClassDeclaration()`
was already called with `ts.Declaration`, not `ts.Node`, so we can
tighten its parameter type and get rid of a redundant check.
`getIifeBody()` (called inside
`getInnerFunctionDeclarationFromClassDeclaration()`) will check whether
the given `ts.Declaration` is a `ts.VariableDeclaration`.
PR Close#35527
Prior to this commit, decorator handling logic in Ngtsc used `Error` to throw errors. This commit replaces most of these instances with `FatalDiagnosticError` class, which provider a better diagnostics error (including location of the problematic code).
PR Close#35244
We recently updated chokidar to `3.0.0`. The latest version of
chokidar provides TypeScript types on its own and makes the extra
dependency on the `@types` unnecessary.
This seems to have caused the `build-packages-dist` script to fail with
an error like:
```
[strictDeps] transitive dependency on external/npm/node_modules/chokidar/types/index.d.ts
not allowed. Please add the BUILD target to your rule's deps.
```
It's unclear why that happens, but a reasonable theory would be that
the TS compilation accidentally picked up the types from `chokidar`
instead of `@types/chokidar`, and the strict deps `@bazel/typescript`
check reported this as issue because it's not an explicit target dependency.
PR Close#35371
ngcc uses a lockfile to prevent two ngcc instances from executing at the
same time. Previously, if a lockfile was found the current process would
error and exit.
Now, when in async mode, the current process is able to wait for the previous
process to release the lockfile before continuing itself.
PR Close#35131
Support for re-exports in UMD were added in e9fb5fdb8. This commit adds
some tests for re-exports (similar to the ones used for
`CommonJsReflectionHost`).
PR Close#35312
In Ivy's template type checker, event bindings are checked in a closure
to allow for accurate type inference of the `$event` parameter. Because
of the closure, any narrowing effects of template guards will no longer
be in effect when checking the event binding, as TypeScript assumes that
the guard outside of the closure may no longer be true once the closure
is invoked. For more information on TypeScript's Control Flow Analysis,
please refer to https://github.com/microsoft/TypeScript/issues/9998.
In Angular templates, it is known that an event binding can only be
executed when the view it occurs in is currently rendered, hence the
corresponding template guard is known to hold during the invocation of
an event handler closure. As such, it is desirable that any narrowing
effects from template guards are still in effect within the event
handler closure.
This commit tweaks the generated Type-Check Block (TCB) to repeat all
template guards within an event handler closure. This achieves the
narrowing effect of the guards even within the closure.
Fixes#35073
PR Close#35193
The `TargetedEntryPointFinder` must work out what the
containing package is for each entry-point that it finds.
The logic for doing this was flawed in the case that the
package was in a path-mapped directory and not in a
node_modules folder. This meant that secondary entry-points
were incorrectly setting their own path as the package
path, rather than the primary entry-point path.
Fixes#35188
PR Close#35227
This commit implements an experimental integration with tsc_wrapped, where
it can load the Angular compiler as a plugin and perform Angular
transpilation at a user's request.
This is an alternative to the current ngc_wrapped mechanism, which is a fork
of tsc_wrapped from several years ago. tsc_wrapped has improved
significantly since then, and this feature will allow Angular to benefit
from those improvements.
Currently the plugin API between tsc_wrapped and the Angular compiler is a
work in progress, so NgTscPlugin does not yet implement any interfaces from
@bazel/typescript (the home of tsc_wrapped). Instead, an interface is
defined locally to guide this standardization.
PR Close#34792
This commit moves the calculation of `ignoreFiles` - the set of files to be
ignored by a consumer of the `NgCompiler` API - from its `prepareEmit`
operation to its initialization. It's now available as a field on
`NgCompiler`.
This will allow a consumer to skip gathering diagnostics for `ignoreFiles`
as well as skip emit.
PR Close#34792
A bug previously caused the template type-checking diagnostics produced by
TypeScript for template expressions to use -99-prefixed error codes. These
codes are converted to "NG" errors instead of "TS" errors during diagnostic
printing. This commit fixes the issue.
PR Close#35146
In #34021 the ngtsc compiler gained the ability to emit type parameter
constraints, which would generate imports for any type reference that
is used within the constraint. However, the `AbsoluteModuleStrategy`
reference emitter strategy did not consider interface declarations as a
valid declaration it can generate an import for, throwing an error
instead.
This commit fixes the issue by including interface declarations in the
logic that determines whether something is a declaration.
Fixes#34837
PR Close#34849
In #33551, a bug in `ngc --watch` mode was fixed so that a component is
recompiled when its template file is changed. Due to insufficient
normalization of files paths, this fix did not have the desired effect
on Windows.
Fixes#32869
PR Close#34015
To support parallel CLI builds we instruct developers to pre-process
their node_modules via ngcc at the command line.
Despite doing this ngcc was still trying to set a lock when it was being
triggered by the CLI for packages that are not going to be processed,
since they are not compiled by Angular for instance.
This commit checks whether a target package needs to be compiled
at all before attempting to set the lock.
Fixes#35000
PR Close#35057
If ngcc gets updated to a new version then the artifacts
left in packages that were processed by the previous
version are possibly invalid.
Previously we just errored if we found packages that
had already been processed by an outdated version.
Now we automatically clean the packages that have
outdated artifacts so that they can be reprocessed
correctly with the current ngcc version.
Fixes#35082
PR Close#35079
Now `hasBeenProcessed()` will no longer throw if there
is an entry-point that has been built with an outdated
version of ngcc.
Instead it just returns `false`, which will include it in this
processing run.
This is a precursor to adding functionality that will
automatically revert outdate build artifacts.
PR Close#35079
Update from chokidar 2.x to 3.x in ngc/ngtsc, to eliminate any possibility
of a security issue with a downstream dependency of the package.
FW-1809 #resolve
PR Close#35047
The message now gives concrete advice to developers who
experience the error due to running multiple simultaneous builds
via webpack.
Fixes#35000
PR Close#35001
We had some logic for generating and passing in the `elIndex` parameter into the `hostBindings` function, but it wasn't actually being used for anything. The only place left that had a reference to it was the `StylingBuilder` and it only stored it without referencing it again.
PR Close#34969
Component's decorator handler exposes `preanalyze` method to preload async resources (templates, stylesheets). The logic in preanalysis phase may throw `FatalDiagnosticError` errors that contain useful information regarding the origin of the problem. However these errors from preanalysis phase were not intercepted in TraitCompiler, resulting in just error message text be displayed. This commit updates the logic to handle FatalDiagnosticError and transform it before throwing, so that the result diagnostic errors contain the necessary info.
PR Close#34801
NOTE: This change must be reverted with previous deletes so that it code remains in build-able state.
This change deletes old styling code and replaces it with a simplified styling algorithm.
The mental model for the new algorithm is:
- Create a linked list of styling bindings in the order of priority. All styling bindings ere executed in compiled order and than a linked list of bindings is created in priority order.
- Flush the style bindings at the end of `advance()` instruction. This implies that there are two flush events. One at the end of template `advance` instruction in the template. Second one at the end of `hostBindings` `advance` instruction when processing host bindings (if any).
- Each binding instructions effectively updates the string to represent the string at that location. Because most of the bindings are additive, this is a cheap strategy in most cases. In rare cases the strategy requires removing tokens from the styling up to this point. (We expect that to be rare case)S Because, the bindings are presorted in the order of priority, it is safe to resume the processing of the concatenated string from the last change binding.
PR Close#34616
This change reverts https://github.com/angular/angular/pull/28711
NOTE: This change deletes code and creates a BROKEN SHA. If reverting this SHA needs to be reverted with the next SHA to get back into a valid state.
The change removes the fact that `NgStyle`/`NgClass` is special and colaborates with the `[style]`/`[class]` to merge its styles. By reverting to old behavior we have better backwards compatiblity since it is no longer treated special and simply overwrites the styles (same as VE)
PR Close#34616
Compiler keeps track of number of slots (`vars`) which are needed for binding instructions. Normally each binding instructions allocates a single slot in the `LView` but styling instructions need to allocate two slots.
PR Close#34616
This change moves information from instructions to declarative position:
- `ɵɵallocHostVars(vars)` => `DirectiveDef.hostVars`
- `ɵɵelementHostAttrs(attrs)` => `DirectiveDef.hostAttrs`
When merging directives it is necessary to know about `hostVars` and `hostAttrs`. Before this change the information was stored in the `hostBindings` function. This was problematic, because in order to get to the information the `hostBindings` would have to be executed. In order for `hostBindings` to be executed the directives would have to be instantiated. This means that the directive instantiation would happen before we had knowledge about the `hostAttrs` and as a result the directive could observe in the constructor that not all of the `hostAttrs` have been applied. This further complicates the runtime as we have to apply `hostAttrs` in parts over many invocations.
`ɵɵallocHostVars` was unnecessarily complicated because it would have to update the `LView` (and Blueprint) while existing directives are already executing. By moving it out of `hostBindings` function we can access it statically and we can create correct `LView` (and Blueprint) in a single pass.
This change only changes how the instructions are generated, but does not change the runtime much. (We cheat by emulating the old behavior by calling `ɵɵallocHostVars` and `ɵɵelementHostAttrs`) Subsequent change will refactor the runtime to take advantage of the static information.
PR Close#34683
Previously, NgtscProgram lived in the main @angular/compiler-cli package
alongside the legacy View Engine compiler. As a result, the main package
depended on all of the ngtsc internal packages, and a significant portion of
ngtsc logic lived in NgtscProgram.
This commit refactors NgtscProgram and moves the main logic of compilation
into a new 'core' package. The new package defines a new API which enables
implementers of TypeScript compilers (compilers built using the TS API) to
support Angular transpilation as well. It involves a new NgCompiler type
which takes a ts.Program and performs Angular analysis and transformations,
as well as an NgCompilerHost which wraps an input ts.CompilerHost and adds
any extra Angular files.
Together, these two classes are used to implement a new NgtscProgram which
adapts the legacy api.Program interface used by the View Engine compiler
onto operations on the new types. The new NgtscProgram implementation is
significantly smaller and easier to reason about.
The new NgCompilerHost replaces the previous GeneratedShimsHostWrapper which
lived in the 'shims' package.
A new 'resource' package is added to support the HostResourceLoader which
previously lived in the outer compiler package.
As a result of the refactoring, the dependencies of the outer
@angular/compiler-cli package on ngtsc internal packages are significantly
trimmed.
This refactoring was driven by the desire to build a plugin interface to the
compiler so that tsc_wrapped (another consumer of the TS compiler APIs) can
perform Angular transpilation on user request.
PR Close#34887
In #34288, ngtsc was refactored to separate the result of the analysis
and resolve phase for more granular incremental rebuilds. In this model,
any errors in one phase transition the trait into an error state, which
prevents it from being ran through subsequent phases. The ngcc compiler
on the other hand did not adopt this strict error model, which would
cause incomplete metadata—due to errors in earlier phases—to be offered
for compilation that could result in a hard crash.
This commit updates ngcc to take advantage of ngtsc's `TraitCompiler`,
that internally manages all Ivy classes that are part of the
compilation. This effectively replaces ngcc's own `AnalyzedFile` and
`AnalyzedClass` types, together with all of the logic to drive the
`DecoratorHandler`s. All of this is now handled in the `TraitCompiler`,
benefiting from its explicit state transitions of `Trait`s so that the
ngcc crash is a thing of the past.
Fixes#34500
Resolves FW-1788
PR Close#34889
This syntax is invalid in these source files and does result in
compilation errors as the constructor parameters could not be resolved.
This hasn't been an issue until now as those errors were ignored in the
tests, but future work to introduce the Trait system of ngtsc into
ngcc will cause these errors to prevent compilation, resulting in broken
tests.
PR Close#34889
Previously, while trying to build an `NgccReflectionHost`'s
`privateDtsDeclarationMap`, `computePrivateDtsDeclarationMap()` would
try to collect exported declarations from all source files of the
program (i.e. without checking whether they were within the target
package, as happens for declarations in `.d.ts` files).
Most of the time, that would not be a problem, because external packages
would be represented as `.d.ts` files in the program. But when an
external package had no typings, the JS files would be used instead. As
a result, the `ReflectionHost` would try to (unnecessarilly) parse the
file in order to extract exported declarations, which in turn would be
harmless in most cases.
There are certain cases, though, where the `ReflectionHost` would throw
an error, because it cannot parse the external package's JS file. This
could happen, for example, in `UmdReflectionHost`, which expects the
file to contain exactly one statement. See #34544 for more details on a
real-world failure.
This commit fixes the issue by ensuring that
`computePrivateDtsDeclarationMap()` will only collect exported
declarations from files within the target package.
Jira issue: [FW-1794](https://angular-team.atlassian.net/browse/FW-1794)
Fixes#34544
PR Close#34811
This commit fixes a bug in the incremental rebuild engine of ngtsc, where if
a component was removed from its NgModule, it would not be properly
re-emitted.
The bug stemmed from the fact that whether to emit a file was a decision
based purely on the updated dependency graph, which captures the dependency
structure of the rebuild program. This graph has no edge from the component
to its former module (as it was removed, of course), so the compiler
erroneously decides not to emit the component.
The bug here is that the compiler does know, from the previous dependency
graph, that the component file has logically changed, since its previous
dependency (the module file) has changed. This information was not carried
forward into the set of files which need to be emitted, because it was
assumed that the updated dependency graph was a more accurate source of that
information.
With this commit, the set of files which need emit is pre-populated with the
set of logically changed files, to cover edge cases like this.
Fixes#34813
PR Close#34912
Previously, the template type-checker would always construct a generic
template context type with correct bounds, even when strictTemplates was
disabled. This meant that type-checking of expressions involving that type
was stricter than View Engine.
This commit introduces a 'strictContextGenerics' flag which behaves
similarly to other 'strictTemplates' flags, and switches the inference of
generic type parameters on the component context based on the value of this
flag.
PR Close#34649
FileToModuleHost aliasing supports compilation within environments that have
two properties:
1. A `FileToModuleHost` exists which defines canonical module names for any
given TS file.
2. Dependency restrictions exist which prevent the import of arbitrary files
even if such files are within the .d.ts transitive closure of a
compilation ("strictdeps").
In such an environment, generated imports can only go through import paths
which are already present in the user program. The aliasing system supports
the generation and consumption of such imports at runtime.
`FileToModuleHost` aliasing does not emit re-exports in .d.ts files. This
means that it's safe to rely on alias re-exports in generated .js code (they
are guaranteed to exist at runtime) but not in template type-checking code
(since TS will not be able to follow such imports). Therefore, non-aliased
imports should be used in template type-checking code.
This commit adds a `NoAliasing` flag to `ImportFlags` and sets it when
generating imports in template type-checking code. The testing environment
is also patched to support resolution of FileToModuleHost canonical paths
within the template type-checking program, enabling testing of this change.
PR Close#34649
Previously, `ReferenceEmitter.emit()` took an `ImportMode` enum value, where
one value of the enum allowed forcing new imports to be generated when
emitting a reference to some value or type.
This commit refactors `ImportMode` to be an `ImportFlags` value instead.
Using a bit field of flags will allow future customization of reference
emitting.
PR Close#34649
Previously, when generating template type-checking code, casts to 'any' were
produced as `expr as any`, regardless of the expression. However, for
certain expression types, this led to precedence issues with the cast. For
example, `a !== b` is a `ts.BinaryExpression`, and wrapping it directly in
the cast yields `a !== b as any`, which is semantically equivalent to
`a !== (b as any)`. This is obviously not what is intended.
Instead, this commit adds a list of expression types for which a "bare"
wrapping is permitted. For other expressions, parentheses are added to
ensure correct precedence: `(a !== b) as any`
PR Close#34649
Currently, the template type-checker gives an error if there are multiple
bindings to the same input. This commit aligns the behavior of the template
type-checker with the View Engine runtime: only the first binding to a field
has any effect. The rest are ignored.
PR Close#34649
It's possible to declare multiple inputs for a directive/component which all
map to the same property name. This is usually done in error, as only one of
any bindings to the property will "win".
In the template type-checker, an error was previously being raised as a
result of this ambiguity. Specifically, a type constructor was produced
which required a binding for each field, but only one of the fields had
a value via the binding. TypeScript would (rightfully) error on missing
values for the remaining fields. This ultimately was happening when the
code which generated the default values for "unset" inputs belonging to
directives or pipes used the final mapping from properties to fields as
a source for field names.
Instead, this commit uses the original list of fields to generate unset
input values, which correctly provides values for fields which shared a
property name but didn't receive the final binding.
PR Close#34649
Consider a library that uses a shared constant for host bindings. e.g.
```ts
export const BASE_BINDINGS= {
'[class.mat-themed]': '_isThemed',
}
----
@Directive({
host: {...BASE_BINDINGS, '(click)': '...'}
})
export class Dir1 {}
@Directive({
host: {...BASE_BINDINGS, '(click)': '...'}
})
export class Dir2 {}
```
Previously when these components were shipped as part of the
library to NPM, consumers were able to consume `Dir1` and `Dir2`.
No errors showed up.
Now with Ivy, when ngcc tries to process the library, an error
will be thrown. The error is stating that the host bindings should
be an object (which they obviously are). This happens because
TypeScript transforms the object spread to individual
`Object.assign` calls (for compatibility).
The partial evaluator used by the `@Directive` annotation handler
is unable to process this expression because there is no
integrated support for `Object.assign`. In View Engine, this was
not a problem because the `metadata.json` files from the library
were used to compute the host bindings.
Fixes#34659
PR Close#34661
Ngcc adds properties to the `package.json` files of the entry-points it
processes to mark them as processed for a format and point to the
created Ivy entry-points (in case of `--create-ivy-entry-points`). When
running ngcc in parallel mode (which is the default for the standalone
ngcc command), multiple formats can be processed simultaneously for the
same entry-point and the order of completion is not deterministic.
Previously, ngcc would append new properties at the end of the target
object in `package.json` as soon as the format processing was completed.
As a result, the order of properties in the resulting `package.json`
(when processing multiple formats for an entry-point in parallel) was
not deterministic. For tools that use file hashes for caching purposes
(such as Bazel), this lead to a high probability of cache misses.
This commit fixes the problem by ensuring that the position of
properties added to `package.json` files is deterministic and
independent of the order in which each format is processed.
Jira issue: [FW-1801](https://angular-team.atlassian.net/browse/FW-1801)
Fixes#34635
PR Close#34870
The Angular CLI will continue to call ngcc on all possible packages, even if they
have already been processed by ngcc in a postinstall script.
In a parallel build environment, this was causing ngcc to complain that it was
being run in more than one process at the same time.
This commit moves the check for whether the targeted package has been
processed outside the locked code section, since there is no issue with
multiple ngcc processes from doing this check.
PR Close#34722
Previously, it was possible for multiple instance of ngcc to be running
at the same time, but this is not supported and can cause confusing and
flakey errors at build time.
Now, only one instance of ngcc can run at a time. If a second instance
tries to execute it fails with an appropriate error message.
See https://github.com/angular/angular/issues/32431#issuecomment-571825781
PR Close#34722
This commit adds an `exclusive` parameter to the
`FileSystem.writeFile()` method. When this parameter is
true, the method will fail with an `EEXIST` error if the
file already exists on disk.
PR Close#34722