The partial compiler will add a version number to the objects that are
generated so that the linker can select the appropriate partial linker
class to process the metadata.
Previously this version matching was a simple number check. Now
the partial compilation writes the current Angular compiler version
into the generated metadata, and semantic version ranges are used
to select the appropriate partial linker.
PR Close#39847
The newly built compliance test runner was not using the shared source
file cache that was added in b627f7f02e,
which offers a significant performance boost to the compliance test
targets.
PR Close#39956
Previously, if a component had an external template with a hard error, the
compiler would "forget" the link between that component and its NgModule.
Additionally, the NgModule would be marked as being in error, because the
template issue would prevent the compiler from registering the component
class as a component, so from the NgModule it would look like a declaration
of a non-directive/pipe class. As a combined result, the next incremental
step could fix the template error, but would not refresh diagnostics for the
NgModule, leading to an incrementality issue.
The various facets of this problem were fixed in prior commits. This commit
adds a test verifying the above case works now as expected.
PR Close#39923
Previously, if a trait's analysis step resulted in diagnostics, the trait
would be considered "errored" and no further operations, including register,
would be performed. Effectively, this meant that the compiler would pretend
the class in question was actually undecorated.
However, this behavior is problematic for several reasons:
1. It leads to inaccurate diagnostics being reported downstream.
For example, if a component is put into the error state, for example due to
a template error, the NgModule which declares the component would produce a
diagnostic claiming that the declaration is neither a directive nor a pipe.
This happened because the compiler wouldn't register() the component trait,
so the component would not be recorded as actually being a directive.
2. It can cause incorrect behavior on incremental builds.
This bug is more complex, but the general issue is that if the compiler
fails to associate a component and its module, then incremental builds will
not correctly re-analyze the module when the component's template changes.
Failing to register the component as such is one link in the larger chain of
issues that result in these kinds of issues.
3. It lumps together diagnostics produced during analysis and resolve steps.
This is not causing issues currently as the dependency graph ensures the
right classes are re-analyzed when needed, instead of showing stale
diagnostics. However, the dependency graph was not intended to serve this
role, and could potentially be optimized in ways that would break this
functionality.
This commit removes the concept of an "errored" trait entirely from the
trait system. Instead, analyzed and resolved traits have corresponding (and
separate) diagnostics, in addition to potentially `null` analysis results.
Analysis (but not resolution) diagnostics are carried forward during
incremental build operations. Compilation (emit) is only performed when
a trait reaches the resolved state with no diagnostics.
This change is functionally different than before as the `register` step is
now performed even in the presence of analysis errors, as long as analysis
results are also produced. This fixes problem 1 above, and is part of the
larger solution to problem 2.
PR Close#39923
If the testcase has not specified that errors were expected, then any
errors that have occurred should be reported. These errors may have
prevented an output file from being generated, which resulted in hard
to debug test failures due to missing files.
PR Close#39862
Previously this would have just printed that `false` was not equal to
`true`, which, although true, is not very helpful. This commit adds
details about which special check failed together with the generated
code, for easier debugging.
PR Close#39863
This commit provides the machinery for the new file-based compliance test
approach for i18n tests, and migrates the i18n tests to this new format.
PR Close#39661
This commit implements partial compilation of components, together with
linking the partial declaration into its full AOT output.
This commit does not yet enable accurate source maps into external
templates. This requires additional work to account for escape sequences
which is non-trivial. Inline templates that were represented using a
string or template literal are transplated into the partial declaration
output, so their source maps should be accurate. Note, however, that
the accuracy of source maps is not currently verified in tests; this is
also left as future work.
The golden files of partial compilation output have been updated to
reflect the generated code for components. Please note that the current
output should not yet be considered stable.
PR Close#39707
In production mode this flag defaults to `true`, but the compliance
tests override this to `false` unless it is provided. As such, the
linker should also adhere to this default as otherwise the compilation
output would not align with the output of the full tests.
There are still tests that exercise the value of this flag, together
with it being `undefined` to verify the behavior of the actual default
value.
PR Close#39707
The linker does not currently support outputting ES5 syntax, so any
compliance tests that request ES5 output cannot be run in partial
compilation mode. This commit marks these tests as pending.
PR Close#39707
The JSON schema reference was off-by-one, preventing IDEs from finding
the file and offering suggestions and documentation. Additionally the
name of the golden file was slightly off.
PR Close#39707
If a template declares a reference to a missing target then referring to
that reference from elsewhere in the template would crash the template
type checker, due to a regression introduced in #38618. This commit
fixes the crash by ensuring that the invalid reference will resolve to
a variable of type any.
Fixes#39744
PR Close#39805
When the `preserveWhitespaces` is not true, the template parser will
process the parsed AST nodes to remove excess whitespace. Since the
generated `goog.getMsg()` statements rely upon the AST nodes after
this whitespace is removed, the i18n extraction must make a second pass.
Previously this resulted in innacurrate source-spans for the i18n text and
placeholder nodes that were extracted in the second pass.
This commit fixes this by reusing the source-spans from the first pass
when extracting the nodes in the second pass.
Fixes#39671
PR Close#39717
ngtsc has a robust suite of testing utilities, designed for in-memory
testing of a TypeScript compiler. Previously these utilities lived in the
`test` directory for the compiler-cli package.
This commit moves those utilities to an `ngtsc/testing` package, enabling
them to be depended on separately and opening the door for using them from
the upcoming language server testing infrastructure.
As part of this refactoring, the `fake_core` package (a lightweight API
replacement for @angular/core) is expanded to include functionality needed
for Language Service test use cases.
PR Close#39594
Currently when we encounter an implicit method call (e.g. `{{ foo(1) }}`) and we manage to resolve
its receiver to something within the template, we assume that the method is on the receiver itself
so we generate a type checking code to reflect it. This assumption is true in most cases, but it
breaks down if the call is on an implicit receiver and the receiver itself is being invoked. E.g.
```
<div *ngFor="let fn of functions">{{ fn(1) }}</div>
```
These changes resolve the issue by generating a regular function call if the method call's receiver
is pointing to `$implicit`.
Fixes#39634.
PR Close#39686
In order to more accurately map from a node in the TCB to a template position,
we need to provide more span information in the TCB. These changes are necessary
for the Language Service to map from a TCB node back to a specific
locations in the template for actions like "find references" and
"refactor/rename". After the TS "find references" returns results,
including those in the TCB, we need to map specifically to the matching
key/value spans in the template rather than the entire source span.
This also has the benefit of producing diagnostics which align more
closely with what TypeScript produces.
The following example shows TS code and the diagnostic produced by an invalid assignment to a property:
```
let a: {age: number} = {} as any;
a.age = 'laksjdf';
^^^^^ <-- Type 'string' is not assignable to type 'number'.
```
A corollary to this in a template file would be [age]="'someString'". The diagnostic we currently produce for this is:
```
Type 'number' is not assignable to type 'string'.
1 <app-hello [greeting]="1"></app-hello>
~~~~~~~~~~~~~~
```
Notice that the underlined text includes the entire span.
If we included the keySpan for the assignment to the property,
this diagnostic underline would be more similar to the one produced by TypeScript;
that is, it would only underline “greeting”.
[design/discussion doc]
(https://docs.google.com/document/d/1FtaHdVL805wKe4E6FxVTnVHl38lICoHIjS2nThtRJ6I/edit?usp=sharing)
PR Close#39665
ngtsc will avoid emitting generated imports that would create an import
cycle in the user's program. The main way such imports can arise is when
a component would ordinarily reference its dependencies in its component
definition `directiveDefs` and `pipeDefs`. This requires adding imports,
which run the risk of creating a cycle.
When ngtsc detects that adding such an import would cause this to occur, it
instead falls back on a strategy called "remote scoping", where a side-
effectful call to `setComponentScope` in the component's NgModule file is
used to patch `directiveDefs` and `pipeDefs` onto the component. Since the
NgModule file already imports all of the component's dependencies (to
declare them in the NgModule), this approach does not risk adding a cycle.
It has several large downsides, however:
1. it breaks under `sideEffects: false` logic in bundlers including the CLI
2. it breaks tree-shaking for the given component and its dependencies
See this doc for further details: https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view
In particular, the impact on tree-shaking was exacerbated by the naive logic
ngtsc used to employ here. When this feature was implemented, at the time of
generating the side-effectful `setComponentScope` call, the compiler did not
know which of the component's declared dependencies were actually used in
its template. This meant that unlike the generation of `directiveDefs` in
the component definition itself, `setComponentScope` calls had to list the
_entire_ compilation scope of the component's NgModule, including directives
and pipes which were not actually used in the template. This made the tree-
shaking impact much worse, since if the component's NgModule made use of any
shared NgModules (e.g. `CommonModule`), every declaration therein would
become un-treeshakable.
Today, ngtsc does have the information on which directives/pipes are
actually used in the template, but this was not being used during the remote
scoping operation. This commit modifies remote scoping to take advantage of
the extra context and only list used dependencies in `setComponentScope`
calls, which should ameliorate the tree-shaking impact somewhat.
PR Close#39662