This commit replaces `fake_core` with the real `@angular/core`
output. See previous commit for reasons.
Overall, this commit:
* Replaces references of `fake_core`
* Fixes tests that were testing Angular compiler detection that _would_
already be flagged by type-checking of TS directly. We keep these
tests for now, and add `@ts-ignore` to verify the Angular checks, in
case type checking is disabled in user applications- but it's worth
considering to remove these tests. Follow-up question/non-priority.
* Adds `@ts-ignore` to the tests for `defer` 1P because the property is
marked as `@internal` and now is (correctly) causing failures in the
compiler test environment.
* Fixes a couple of tests with typos, wrong properties etc that
previously weren't detected! A good sign.
PR Close#54650
As part of improving test safety of the compiler, I've noticed that
we have a special pass for detecting external `ModuleWithProviders`
where we detect the module type from an object literal.
This literal is structured like the following: `{ngModule: T}`. The
detection currently takes `T` directly, but in practice it should be
`typeof T` to satisfy the `ModuleWithProviders` type that is accepted
as part of `Component#imports`.
This commit adds support for this, so that we can fix the unit test
in preparation for using the real Angular core types in ngtsc tests.
PR Close#54650
Fixes that a query like `viewChild('locator', {read: ElementRef<HTMLElement>})` would throw because we didn't account for expressions with type parameters.
I've also included support for parenthesized expressions and `as` expressions since it's pretty easy to support them.
Fixes#54645.
PR Close#54647
Fixes that initializer functions weren't being recognized if they are aliased (e.g. `import {model as alias} from '@angular/core';`).
To do this efficiently, I had to introduce the `ImportedSymbolsTracker` which scans the top-level imports of a file and allows them to be checked quickly, without having to go through the type checker. It will be useful in the future when verifying that that initializer APIs aren't used in unexpected places.
I've also introduced tests specifically for the `tryParseInitializerApiMember` function so that we can test it in isolation instead of going through the various functions that call into it.
PR Close#54609
Fixes that initializer functions weren't being recognized if they are aliased (e.g. `import {model as alias} from '@angular/core';`).
To do this efficiently, I had to introduce the `ImportedSymbolsTracker` which scans the top-level imports of a file and allows them to be checked quickly, without having to go through the type checker. It will be useful in the future when verifying that that initializer APIs aren't used in unexpected places.
I've also introduced tests specifically for the `tryParseInitializerApiMember` function so that we can test it in isolation instead of going through the various functions that call into it.
PR Close#54480
The diagnostic for signals that haven't been invoked wasn't working internally, because the path to `@angular/core` is different. These changes resolve the issue and do some general cleanup.
PR Close#54585
Template pipeline is now the default template compiler.
A pair of source map tests is failing, related to DI in JIT mode; I will fix and re-enable these during the preview period.
PR Close#54571
This is based on an internal issue report.
An earlier change introduced a diagnostic to report cases where a symbol is in the `deferredImports` array, but is used eagerly. The check worked by looking through the deferred blocks in a scope, resolving the scope for each and checking if the element is within the scope. The problem is that resolving the scope won't work across scoped node boundaries. For example, if there's a control flow statement around the block or within the block but around the deferred dependency, it won't be able to resolve the scope since it isn't a direct child, e.g.
```
@if (true) {
@defer {
<deferred-dep/>
}
}
```
To fix this the case where the deferred block is inside a scoped node, I've changed the `R3BoundTarget.deferBlocks` to be a `Map` holding both the deferred block and its corresponding scope. Then to resolve the case where the dependency is within a scoped node inside the deferred block, I've added a depth-first traversal through the scopes within the deferred block.
PR Close#54499
We have a couple of cases now (#53753 and #54414) where we're forced to redefine enums as object literals. These literals aren't rendered in the best way in the docs so these changes introduce a new `object-literal-as-enum` tag that we can use to mark them so they're treated for documentation purposes.
PR Close#54487
This commit addresses a problem with PR #53695 that introduced support for default imports,
where the actual dynamic import used in the defer loading function continued to use the
symbol name, instead of `.default` for the dynamic import. This issue went unnoticed in the
testcase because a proper instance was being generated for the `ɵsetClassMetadataAsync` function,
but not the generated dependency loader function.
Fixes#54491
PR Close#54495
When the linker is running using an unpublished version of angular, locally built, the version will be `0.0.0`.
When encountering this situation, the range that for the linker map support is considered to be `*.*.*` allowing
for the linker to work at build time with packages built with versioned angular.
Most notably this allows for us to properly use the linker in building our documentation site with the locally
built version of angular.
PR Close#54439
The version detection condition for signal two-way bindings used an OR
instead of an AND, resulting in every `.0` patch version being considered
as supporting two-way bindings to signals.
This commit fixes the logic and adds additional parentheses to ensure the
meaning of the condition is more clear. Long term, we should switch to
semver version parsing instead.
PR Close#54443
In order to allow both signals and non-signals in two-way bindings, we have to pass the expression through `ɵunwrapWritableSignal`. The problem is that the language service uses a bundled compiler that is fairly new, but it may be compiling an older version of Angular that doesn't expose `ɵunwrapWritableSignal` (see https://github.com/angular/vscode-ng-language-service/issues/2001).
These changes add a `_angularCoreVersion` flag to the compiler which the language service can use to pass the parsed Angular version to the compiler which can then decide whether to emit the function.
PR Close#54423
Currently we have two fake copies of `@angular/core` in the compiler tests which can be out of sync and cause inconsistent tests. These changes reuse a single copy instead.
PR Close#54344
The new `model()` signal introduces a `ModelSignal` type that needs to be handled by the interpolatedSignalNotInvoked diagnostic to catch issues like:
```
<div>{{ myModel }}</div>
```
PR Close#54338
The import of `module` can conflict with the native global variable called `module` and can break some internal tests. These switch to only importing the function we need.
PR Close#54333
This helps with the Angular CLI currently swallowing fatal diagnostic
errors in ways that are extremely difficult to debug due to workers
executing Angular compiler logic.
The worker logic, via piscina, is currently not forwarding such Angular
errors because those don't extend `Error.`
a7042ea27d/src/worker.ts (L175)
Even with access to these errors by manually forwarding errors, via
patching of the Angular CLI, there is no stack trace due to us not using
`Error` as base class for fatal diagnostic errors. This commit improves
this for future debugging and also better reporting of such errors to
our users- if we would accidentally leak one.
PR Close#54309
An identical addition to: 760b1f3d0b.
This commit expands the `try/catch`-es:
- to properly NOT throw and just convert the diagnostic.
- to be in place for all top-level instances. Notably, this logic cannot
reside in the template type checker directly as otherwise we would
risk multiple duplicate diagnostics.
PR Close#54309
Fixes that `ɵunwrapWritableSignal` inferring getter functions as not matching the interface of `WritableSignal` instead of preserving them.
PR Close#54252
In a previous commit the TCB was changed to cast the assignment to an input in order to widen its type to allow `WritableSignal`. This ended up breaking existing inputs whose setter has a wider type than its getter. These changes switch to unwrapping the value on the binding side.
PR Close#54252
Reworks the TCB for two-way bindings to make them simpler and to avoid regressions for two-way bindings to generic inputs. The new TCB looks as follows:
```
var _t1: Dir;
var _t2 = _t1.input;
(_t1 as typeof _t2 | WritableSignal<typeof _t2>) = expression;
```
PR Close#54252
Currently the error is a generic error "exportAs must be a string ...". This commit makes the error more specific to local compilation and adds some action items.
PR Close#54230
Currently the error is a generic error "selector must be a string ...". This commit makes the error more specific to local compilation and adds some action items.
PR Close#54230
Currently the error is a generic error "selector must be a string ...". This commit makes the error more specific to local compilation and adds some action items.
PR Close#54230
Currently the correct error message is shown only if @Component.styles is an array with some unresolved element. This change supports the new case of string type for the @Component.styles field.
PR Close#54230
A helper `validateLocalCompilationUnresolvedConst` is added to encapsulate a common pattern which leads to the error `LOCAL_COMPILATION_UNRESOLVED_CONST`.
PR Close#54230
The trailing error message comes from tracing the chain of DymaicValue which leads to a mostly useless error that highlights the same symbol as the original message and emits the error message "Unknown reference". This error message is removed in the favour of the original message which suffices.
PR Close#54230
A single error code is created to unify the common error pattern in local compilation mode where an imported const cannot be resolved, but needs to be resolved. This mainly happens for Angular decorator fields such as @Component.template.
The error messages are also upgraded to be more centered around this unifying theme.
PR Close#54230
Currently, when two components are named `TestComponent`, and both would
use e.g. control flow. Templates would be generated by the compiler and
those would conflict at runtime because the names for the template
functions are not ensured to be unique.
This seems like a more general problem that could be tackled in the
future in the template pipeline by always using the `ConstantPool`, but
for now, we should be good already, given us ensuring the `baseName`'s are
always unique.
PR Close#54273
The `read` option for queries can rely on lexical variables inside the
class. These constructs are fine from a technical perspective in
TypeScript, but in practice, when the component/directive definition is
being created, the read value is extracted into the definition,
**outside** of the class. This breaks `this` references.
To fix this, we are restricting the `read` option to literal values.
Similar to `descendants`. Literal references are in practice constructs
like:
- `read: bla.X`
- `read: X`
where `bla` or `X` is never a `ThisKeywoord`- hence fixing the issue
and also simplifying the patterns for easier single file compilation.
PR Close#54257
This commit adds a JIT transform for signal-based queries, so that
queries are working as expected in JIT environments like `ng test` where
decorator metadata is needed as a prerequisite for the component
definition creation.
This is similar to the JIT transforms for signal inputs etc.
PR Close#54257
Extracts common JIT transform helper into the transform API, so that
those helpers can be re-used for output, model, queries and inputs.
PR Close#54257
Similar to `input()`, initializer-based `output()`'s need to be
transformed in JIT to be annotated with an `@Output()` decorator.
This is necessary so that Angular can statically collect all defined
outputs without instantiating the class (which would not be possible
upon directive definition computation).
This commit introduces a transform next to the input transform that
automatically runs with the Angular CLI and `ng test`.
PR Close#54217
Adds type check diagnostic tests for the `output()` API. This
is necessary because we are maintaining a separate emitter
for `output()` as with the current design (still discussed - but this is
a starting foundation).
Note: `OutputEmitter` currently does not publicly expose `.subscribe`,
while the testing infrastructure exposes it for now. That is because we
are still discussing this before making changes in the TCB to account
for the case where `.subscribe` might be `@internal` ultimately.
PR Close#54217
Generalizes the type check table scenario testing infrastructure
so that it can also be used for testing outputs in a table-scheme
without a lot of TS code repetition.
PR Close#54217
Adds an ngtsc diagnostic and compilation output test for `output()`. The
test will verify certain recognition restrictions and ensures that
diagnostics are raised, in addition to proper full compilation output
being generated (aside from the compliance tests verifying output more
closely).
PR Close#54217
Adds compliance output tests for `output()` to verify that
we are emitting proper full compilation output, as well as proper
partial compilation output that can be linked to match the full output.
PR Close#54217
As we are introducing the new `output()` function as an inituive
alternative to `@Output()` that matches with signal-based inputs,
this commit prepares the compiler to detect such initializer-based
outputs.
PR Close#54217
The deps tracker which is responsible to track orphan components does not work for classes mutated by custom decorator. Some work needed to make this happen (tracked in b/320536434). As a result, with option `forbidOrphanComponents` being true the deps tracker will falsely report any component as orphan if it or its NgModule have custom/duplicate decorators. So it is unsafe to use this option in the presence of custom/duplicate decorator and we disable it until it is made compatible. Note that applying custom/duplicate decorators to `@Injectable` classes is ok since these classes never make it into the deps tracker. So we excempt them.
PR Close#54139
Custom/duplicate decorators break the deps tracker in local mode. But deps tracker only deals with non-injectable classes. So applying custom/duplicate decorators to `@Injectable` only classes does not disturb deps tracker and local compilation in general. There are also ~ 100 such cases in g3 which cannot be cleaned up.
PR Close#54139
For cases like this:
```
@Component({...})
@Component({...})
export class SomeComp {
}
```
The `DecoratorHandler.detect` apparantly matches only one of the `@Component` decorator, leaving the other undetected which will be transformed by TS decorator helper and that breaks local compilation runtimes. But the error message only mentioned "custom" decorator, while in this case it is a "duplicate Angular" decorator. The respective error message is updated thus.
PR Close#54139
Instead of maintaining individual transforms for `input`, `output`,
`model` etc. we are grouping them directly and the first one matching,
will execute.
This reduces needed traversal through AST and also makes it a little
more clean to write new initializer API metadata transforms.
Note: The Angular JIT transform is now also moving from `tooling.ts`
directly into `/transformers` for more local placement of transformer
logic.
PR Close#54200
Fixes that `@defer` blocks weren't recognizing default imports and generating the proper code for them. Default symbols need to be accessed through the `default` property in the `import` statement, rather than by their name.
PR Close#53695
In one of the earlier commits, the logic that appends `=$event` before parsing two-way bindings was removed and some validation was added to prevent unassignable expressions from being used. This ended up being problematic, because previously the parser was incorrectly allowing some invalid expressions which users came to depend on. For example, it transformed `[(value)]="a && a.b"` to `a && (a.b = $event)`.
These changes add some special cases for the common breakages that came up during the TGP.
PR Close#54154
Updates the template definition builder to emit the new format for the listener side of two-way bindings.
```js
// Before
listener("ngModelChange", function($event) {
return ctx.name = $event;
});
// After
ɵɵtwoWayListener("ngModelChange", function($event) {
ɵɵtwoWayBindingSet(ctx.name, $event) || (ctx.name = $event);
return $event;
});
```
PR Close#54154
Reworks the compiler so that it generates a `twoWayProperty` instruction, instead of `property`, for the property side of a two-way binding. Currently the new instruction passes through to `property`, but it'll have some two-way-binding-specific logic in subsequent PRs.
PR Close#54154
At the moment the extra import generation in local compilation mode fails if these extra imports produce a cycle. To handle this, the cycle handling strategy is updated for local compilation, and following the behaviour in the full compilation mode, the compiler does not generate extra import if it leads to cycle and instead leave things to the runtime.
PR Close#53543
With option `generateExtraImportsInLocalMode` set, in local mode the compiler generates extra imports for each component local dependencies. Here local dependencies means all component's dependencies within the same compilation unit.
To achieve this, the compiler performs a "local version" of its regular static analysis to find each component's deps, and these deps are used to generate extra side effect imports.
PR Close#53543
In this commit the resolve method for components is run fully when the option `generateExtraImportsInLocalMode` is set. This is because we need local component depedencies in order to generate extra imports causing by them. This requires cutting some resolve phase logics that are unnecessary in local mode, such as diagnostics.
PR Close#53543
When option `generateExtraImportsInLocalMode` is set, we need to compute component local depednecies in order to generate extra imports related to them. At the same time running the register phase in general is harmless in local compilation. So we run it anyway.
PR Close#53543
With option `generateExtraImportsInLocalMode` set in local compilation mode, the compiler generates extra side effect imports using this rule: any external module from which an identifier is imported into an NgModule will be added as side effect import to every file in the compilation unit. To illustrate this better assume the compilation unit has source files `a.ts` and `b.ts`, and:
```
// a.ts
import {SomeExternalStuff} from 'path/to/some_where';
import {SomeExternalStuff2} from 'path/to/some_where2';
...
@NgModule({imports: [SomeExternalStuff]})
```
then the extra import `import "path/to/some_where"` will be added to both `a.js` and `b.js`. Note that this is not the case for `import "path/to/some_where2"` though, since the symbol `SomeExternalStuff2` is not imported into any NgModule.
The math behind this mechanism is, in local compilation mode we cannot resolve component external dependencies fully. For example if a component in `a.ts` uses an external component defined in an external file `some_external_comp.ts` then we can generate the import to this file in `a.js`. Instead, we want to generate an import to a file that "gurantees" that `a.js` is placed after `some_external_comp.js` in the bundle. Now since the component in `some_external_comp.ts` is used in `a.ts`, then there must be a chain of imports starting from the NgModule that declares the component in `a.ts` to the component in `some_external_comp.ts`. This chain means some file in the same compilation unit as `a.ts` should import some external NgModule which includes `some_external_comp.ts` in its transitive closure and import it to some NgModule. So by adding this import to `a.js` we ensure that the bundling will have the right order.
PR Close#53543
As the first step, the import manager's `generateSideEffectImport` method is implemented to enable it to store info for side effect imports. Next, the helper `addImports` is modified to be able to generate correct statement for side effect imports.
These changes will be tested in the subsequent commits when these tools are used to generate an actual extra import for the generated file.
PR Close#53543
This commit includes a skeleton of how the tool `LocalCompilationExtraImportsTracker` is used in the overall compilation workflow end-to-end.
First of all, a new option `generateExtraImportsInLocalMode` is added, whose presence will make `LocalCompilationExtraImportsTracker` part of the compilation process. When this option is set an instance of `LocalCompilationExtraImportsTracker` is created within the NgCompiler. Then it is passed to the Ivy transformer and plumbed all the way down and the extra imports registered in it are added to the `ImportManager` instances before the imports are added from `ImportManager` to the generated file. This required adding a new method `generateSideEffectImport` to the `ImportManager`, which is an empty method and will be implemented in the subsequent commits.
This commit expected to make no change in the compilation behavior as the methods are not implemented yet.
PR Close#53543
The tracker is responsible for registering the extra imports during the analysis and resolve compiler phases, and later to be used by the transformer to get a list of extra imports to be generated for each source file.
This commit only contains the API, and the actual implementation for each method will be done in subsequent commits where an application of that method is available and so tests can be written for the implementation.
PR Close#53543
At the moment local compilation mode does not support custom decorators, and it leads to unhandled errors. In this change a compile time diagnostic is produced in local mode for custom decorators. This is a temporary solution since there are few custom decorators are in use in g3. Custom decorators will be eventually supported in local mode.
PR Close#53983
This adds initial support for extracting and rendering call and construct
signatures of classes, like within the new `InputFunction` for signal
inputs.
For now, signatures are a rare occasion and represented as class member
entries. In the future we might consider exposing this via its own entry
type, and field on the class/interface entry.
PR Close#54053
This fixes the definitions for signal-based inputs in the language
service and type checking symbol builder.
Signal inputs emit a slightly different output. The output works well
for comppletion and was designed to affect language service minimally.
Turns out there is a small adjustment needed for the definition symbols.
PR Close#54053
This commit separates `InputSignal` for input signals with transforms.
The reason being that most of the time, signal inputs are not using
transforms and the generics are rather confusing.
Especially for users with inferred types displayed in their IDEs, the
input signal types are seemingly complex, even if no transform is used.
For this reason, we are introducing a new type called
`InputSignalWithTransform`. This type will be used for inputs with
transforms, while non-transform inputs just use `InputSignal`.
A notable fact is that `InputSignal` extends `InputSignalWithTransform`,
with the "identity transform". i.e. there is no transform. This allows
us to share the code for input signals. In practice, we don't expect
users to pass around `InputSignal`'s anyway.
PR Close#54053
During the template parsing stage two-way bindings are split up into a property and event binding. All the downstream code treats these binding the same as their one-way equivalents. For some future work we'll have to distinguish between the two so these changes update the `BoundElementProperty.type` and `ParsedEvent.type` to include a `TwoWay` type. All existing call-sites have been updated to treat `TwoWay` the same as `Property`/`Regular`, but more specialized logic will be added in the future.
PR Close#54065
Previously, defer deps fns names were only prefixed with the component name, meaning that distinct deps fns in the same component would produce a name collision. Now, we take into account the entire template function name when naming inner deps fns.
PR Close#54060
The Template Pipeline is a brand new backend for the Angular compiler, replacing `TemplateDefinitionBuilder`. It generates the Ivy instructions corresponding to an input template (or host binding). The Template Pipeline has an all-new design based on an intermediate representation compiled over many phases, which will allow us to experiment with compiler changes more easily in the future.
With this commit, the template pipeline can now be enabled in any project via the `useTemplatePipeline` TSConfig option. However, it is still disabled by default.
PR Close#54057
This commit introduces three additional diagnostics for queries:
- If a query (either using decorator or signal-based) is declared on a
static class member, a diagnostic is raised.
- If a signal-based query is mixed with a query decorator, a diagnostic
is raised. Similar to signal inputs.
- If a singal-based query is also declared in the directive/component
class decorator metadata, a diagnostic is raised.
PR Close#54019
Due to some refactorings, we were only checking the function name
and whether it originates from an import. We should also verify the
module. This seems like logic we lost in the refactorings.
PR Close#54019
Collapses multiple sibling query advance statements into single
query advance invocations. This will help reducing generated code
for directives/components with many queries.
PR Close#54019
Previously, if an ICU was inside a nested i18n root, it would use the nested root to calculate whether it should be applied. Now, we use the root i18n block.
PR Close#54026
This commit adds compliance tests to ensure that the generated output of
signal-based queries matches our expectation.
Note: collapsing query advance instructions is not implemented yet.
PR Close#53978
This commit ensures that libraries can use signal-based queries, and the
partial compilation output will capture their metadata.
The linker is updated to support parsing this.
Two notes:
1. Older linker versions are not capable of parsing this, so the minimum
version for signal-based queries is adjusted when such are used.
2. We only emit `isSignal` metadata for queries when signal queries are
used. This enables libraries to continue supporting older linker
versions, if signal-based queries are not used.
PR Close#53978
Adds a compiler integration test for recognizing signal-based queries,
and emitting the expected output. Concrete output will be verified via
the compliance tests.
PR Close#53978
This commit uses the initializer API recognition that we built for
signal-based inputs, and teaches the compiler to recognize class members
that refer to `viewChild`, `viewChildren`, `contentChild` or
`contentChildren`. Those will declare signal-based view or content queries.
PR Close#53978
This commit introduces the compiler output generation for signal-based
queries. Signal-based queries will have new creation-mode instructions
and update instructions to advance the current query indices in the
global shared context.
An output like the following is the expected output for signal-based
queries:
```
i0.ɵɵdefineComponent({
viewQuery: function App_Query(rf, ctx) {
if (rf & 1) {
i0.ɵɵviewQuery(ctx.d, _c0, 5);
i0.ɵɵviewQuerySignal(ctx.ds1, _c0, 5);
i0.ɵɵviewQuerySignal(ctx.ds2, _c0, 5);
}
if (rf & 2) {
let _t;
// only change-detected queries need explicit refresh
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.d = _t.first);
// we bump up current query index by 2 positions since there are 2 signal-based queries
i0.ɵɵqueryAdvance(2);
}
…
},
…
});
```
Note: For now, the collapsing of multiple advance instructions is not
implemented. This will be a follow-up.
Note 2: A couple of query helpers are now in their own file. This makes
it easier to focus on query-specific compiler code. The new function is
called `createQueryCreateCall`, which is a modified variant of the
existing function that previously only generated query parameters.
PR Close#53978
The new `input` API is recognized using class member initializers.
We want to support similar APIs for queries, using e.g. `viewChild`
or `viewChild.required`.
This commit extracts the input recognition API and makes it reusable,
so that the same logic can be used to detect queries on a class member.
Additional changes:
- replacing `coreModule` with the simpler `isCore` parameter. This is
more readable.
- support for detecting a list of API names on a single class member.
This allows us to detect possible query functions on the same class
member without having to check X times. We simply check for the
initializer API pattern and check if one API function name matches.
PR Close#53978
At the moment local compilation breaks for host directives because the current logic relies on global static analysis. This change creates a local version by cutting the diagnostics and copying the directive identifier as it is to the generated code without attempting to statically resolve it.
PR Close#53877
At the moment when unified host is selected (through option `_useHostForImportGeneration`) the compiler always generates alias reexports. Such reexports are mainly generated to satisfy strict dependency condition for generated files. Such condition is no longer the case for G3. At the same time, these alias reexports make it impossible to mix locally compiled targets with globally compiled targets. More precisely, a globally compiled target may not be able to consume a locally compiled target as its dependency since the former may import from the alias reexports which do not exist in the latter due to local compilation mode. So, to make global-local compilation interop possible, it is required to be able to turn off alias reexport generation.
PR Close#53937
This commit adds extra logic to produce a diagnostic in case `@Component.deferredImports` contain types from imports that also bring eager symbols. This would result in retaining a regular import and generating a dynamic import, which would not allow to defer-load dependencies.
PR Close#53899
This commit update the logic to enable `register` and `resolve` phases for local compilation. Those phases will be useful for local compilation in certain cases (will be used in followup PRs).
PR Close#53901
Currently when the extended type check fails due to an import reference
that cannot be generated, the fatal diagnostic is not caught and
not properly exposed as a `ts.Diagnostic` that can be gracefully
handled. This is inconsistent to non-extended type checking diagnostics.
This is problematic because Angular CLI applications currently fail in
obscure ways because:
- the CLI does not expect `getDiagnosticsForFile` to actually throw
runtime errors.
- the CLI does not seem to properly print these errors given the
parallel workers and build excection, and those errors are
especially hard to debug because there is no `stack` for
`FatalDiagnosticError`'s.
Example: `MyDir` is not exported and the type check block cannot reference it.
PR Close#53896
This commit updates the `DeferredSymbolTracker` class to take info account the `onlyExplicitDeferDependencyImports` flag. The `DeferredSymbolTracker` class also exposes a new API to register import declarations as explicitly deferred, which will be used in followup commits.
PR Close#53591
This commit updates typechecker to store full Pipe metadata in internal data strctures, so that this information is available to more places in the code, which will be updated in a followup commit.
PR Close#53591
This commit updates a few places to extract the logic into a separate functions which will be reused in a few places in followup commits.
PR Close#53591
This allows us to ensure signal inputs and a potential JIT transform
remain single file compilation compatible. The consequences are that
options need to be statically analyzable more strictly, compared to
loosened restrictions with static interpretation where e.g. `alias`
can be defined through a shared variable.
PR Close#53872
Adds infrastructure to run signal input tests with JIT (using the
transform) and AOT. Acceptance tests for signal inputs will run with
both variants. In the future we can consider expanding this
infrastructure for all of our acceptance tests, but that's a different
story.
PR Close#53808
Improves the recognition of the `input`/`input.required` functions to
not depend on external module resolution. This is useful for local
compilation and for transforms operating on a single file/ isolated
module.
PR Close#53808
Currently when someone declares a signal or non-signal input on a static
class member, the compiler will not yield any diagnostic. We can detect
these mistakes and report a diagnostic to help our users.
PR Close#53808
This commit addds two diagnostics for two scenarios where signal inputs
are declared incorrectly:
- a signal input is also annotated with `@Input` in the TypeScript
sources.
- a signal input is also declared in the `inputs` option of
`@Directive/`@Component`.
PR Close#53808
This commit adds a transform for supporting input signals in JIT
environments. The transform will be wired up for Angular CLI
applications automatically. An integration test verifies that this fixes
unit testing with signal inputs.
The transform basically will take the signal input metadata and
transform it into `@Input` decorators that can provide static
information to the Angular JIT runtime when the directive/component
definition is compiled.
PR Close#53808
This commit does two things:
- exposes `addImports` so that it can be used by transforms that we are
adding to the compiler. e.g. the signal input to `@Input` transform.
- `updates `addImports` to support/use the transform context factory.
This will allow us to write proper transforms using `addImports`. Also
leverages this in the Ivy JS/DTS transforms.
PR Close#53808
Moves the signal input class member extraction logic into the dedicated
input function file. This makes the code for signal inputs more
self-contained.
This commit then re-exposes the function as part of `ngtsc/annotations`
so that it can be used later for a transform that will take the signal
input metadata and translate it into a `@Input` decorator. This allows
us to remove code duplication and guarantees consistency/correctness
PR Close#53808
We recently landed a commit to introduce support for generic type
checking of signal inputs. For that we had to implement logic that
will generate imports for inline type constructors. This required
changes to the context logic and `TypeCtorOp` file-level op.
This commit ensures that everything is working as expected, specifically
in cases where an inline type ctor is generated and imports would be
needed to unwrap the class members for `InputSignal`.
PR Close#53808
Adds signal input compliance tests, ensuring linking works as expected,
partial output is generated properly, types are inferred properly, and
that the full, or linked output matches our expected runtime structure.
PR Close#53808
As we introduced the new partial output for signal inputs, we also need
to update the linker code to be able to parse this. This commit adds
this functionality.
In the follow-up commit, compliance tests for linking, partial output,
and full compilations are added.
PR Close#53808
As part of testing we did accidentally use `bitwiseAnd` for the input
flags, given we started without an extra flag for `HasTransform`.
This commit teaches the compiler to support emitting bitwise OR
and uses it when combining input flags, fully re-enabling transforms
for signal components after the new flag mechanism was introduced in
previous commits.
PR Close#53808
This commit changes the `HasTransform` flag to be only concerned with
decorator inputs. This allows us to automatically detect signal input
transforms without reliance on the flag, resulting in less complexity in
the compiler (as outlined in the design doc) and various other places,
while it also allows us to simplify JIT support for signal inputs
because there would be no need to capture the "hasTransform" state in
the decorator so that JIT can generate the according input flags.
`isSignal` will still persist as an input flag to allow for monomorphic
and highly efficient distinguishing at runtime, whether an input is
signal based or not. JIT transform will also need to propagate this
information to the runtime somehow.
PR Close#53808
In some cases, the input files for a partial output generation
compliance tests may be invalid and lead to compilation errors.
The golden partial would be silently generated with the remaining
test cases. Instead of hiding errors, we will now print these and
cause the script to fail properly.
Note that the error logging is pretty minimalistic, but it's sufficient.
PR Close#53808
The linker AST is abstracted to be agnostic of the underlying
implementation AST. i.e. TS AST or Babel AST.
This abstraction also intends to provide some type-safety-ness to
parsing of various partial declarations. This commit improves type
safety further by fixing that `AstValue'`s were not checked for
assignability of `T`- potentially hiding issues/unaccounted values.
Additionally, we fix that `getObject()` does not properly narrow
union types to actual object literals. This happend because e.g. arrays
are of type `object`. We can improve type safety here. Using `Record`
did not help as an array would still assign to that.
PR Close#53808
We generate `advance` instructions before most update instructions and the majority of `advance` calls are advancing by one. We can save some bytes for the most common case by omitting the parameter for `advance(1)` altogether.
PR Close#53845
Instead of computing the bit input flags at compile-time and inling
the final bit flag number, we will use the `InputFlags` enum directly.
This is a little more code in the compiler side, but will allow us to
have better debuggable development code, and also prevents problems
where runtime flag bitmasks differ from the compiler flag bitmasks.
This is in practice a noop for optimized applications as the enum values
would be inlined anyway. This matches existing compiler emit for e.g.
change detection strategy, or view encapsulation enums.
PR Close#53571
The linker compliance tests were disabled with a Babel update and
nobody realized for quite a while, via
https://github.com/angular/angular/pull/49914.
As we've came across this lost coverage, which also is quite
impactful as all libraries depend on linked output- I've took initiative
to debug the root cause as there was no follow-up.
https://github.com/angular/angular/issues/51647.
It turned out to be a highly complex issue that is non-trivial to fix,
but at least we should try to resurrect the significant portion of test
coverage by still running the linker tests- avoiding regressions, or
unexpected issues (like with defer being developed). We can work on
re-enabling and fixing source-maps separately.
Tracked via https://github.com/angular/angular/issues/51647.
PR Close#53571
The linker compliance tests did not run for a while. There were a couple
of new tests that were not passing as this wasn't flagged on CI. This commit fixes this.
Fortunately there was no problematic code that did indicate issues with linking.
In the follow-up commit, we fix the compliance test infrastructure to
re-enable linker testing..
One clear issue is still that the defer blocks are not handled properly
in linked output- hence making defer not actually "lazy" for compiled
libraries. This needs to be handled separately by the framework team.
PR Close#53571
This commit adds a final test for input signals, integrating all major
parts:
* type-checking
* compiler detection
* compiler emit
* API signature tests
PR Close#53571
This commit introduces a new enum for capturing additional metadata
about inputs. Called `InputFlags`. These will be built up at compile
time and then propagated into the runtime logic, in a way that does
not require additional lookup dictionaries data structures, or
additional memory allocations for "common inputs" that do not have any flags.
The flags will incorporate information on whether an input is signal
based. This can then be used to avoid megamorphic accesses when such
input is set- as we'd not need to check the input field value. This also
avoids cases where an input signal may be used as initial value for an
input (as we'd not incorrectly detect the input as a signal input then).
The new metadata emit will be useful for incorporating additional
metadata for inputs, such as whether they are required etc (although
required inputs are a build-time only construct right now- but this is a
good illustration of why input flags can be useful). An alternative
could have been to have an additional boolean entry for signal inputs,
but allocating a number with more flexible input flags seems more future
proof and more reasonable andreadable.
More information on the megamorphic access when updating an input
signal
https://docs.google.com/document/d/1FpnFruviKb6BFTQfMAP2AMEqEB0FI7z-3mT_qm7lzX8/edit.
PR Close#53571
This commit adds additional type-check transform tests for signal
inputs. These tests verify some of the problems with covariance,
contravariance and bivariance that we were suspecting to be problematic
if we would assign `InputSignal`'s directly to the type constructors.
PR Close#53571
In #52931, Kristiyan fixed a TemplateDefinitionBuilder bug in which derived alias variables in for loops (`$even`, `$first`, etc) were referring to the wrong level of nested `@for` block. (These variables are unique because they become inlined expressions, and are not "real" context variables.) He fixed this by appending level information to the generated alias name.
Template Pipeline actually suffered from the same bug. We fix it in a very similar way -- in particular, whenever these derived context variables are used, we make them depend on versions of `$index` and `$count` that have been suffixed with the xref of the enclosing repeater.
I have added a few more pipeline goldens, because we are not quite as clever as TDB about only generating the duplicate suffixed index and count variables when inside nested loops. This is fine, since in the long run, we want to refactor it more fundamentally.
I have also added a TODO to fix this more rigorously. In particular, it would be nice if we had proper support for shadowed variables, as well as unlimited levels of variables depending on one another.
PR Close#53662
Template pipeline previously mangled CSS property names like
`--camelCase` when used in host style bindings. Note: It still *does*
mangle these names in static style attrs, both in host bindings and on
elements. This is clearly wrong, but is consistent with what TDB does
today.
PR Close#53665
Currently compiling input transform in local mode breaks, since compiler does static analysis for the transform function, and this cannot be done in local mode if the function is imported from another compilation unit. In this fix the static analysis is ditched in local mode.
PR Close#53645
The diagnostic was catching the following case:
```ts
name = signal('Angular');
```
but not the following ones:
```ts
name = signal('Angular').asReadonly();
name = computed(() => 'Angular');
name!: Signal<string>
```
This was not catched in the tests because the type of `Signal` is different than the one actually used in core.
It turns out the real type forces the diagnostic to check both the `symbol.tsType.symbol` and the `symbol.tsType.aliasSymbol`.
PR Close#53585
It's possible for attributes to have a namespace, we need to handle this
possiblity for both attribute instructions and attributes extracted to
the consts array.
PR Close#53646
The way we were handling ICU placeholders was not compatible with using
interpolations on attributes of elements inside the ICU. This change
refactors the handling of ICU placeholders and unifies the way
expression and tag placeholders work inside ICUs.
The new approach modifies the ingest logic to add the placeholder on to
the TextOp rather than the TextInterpolationOp. This is because, in
ICUs, we may need multiple i18n expressions created from the
interpolation expressions to roll up into the same placeholder. ICUs
essentially do the interpolation at compile time, combining the static
strings with special placeholder strings that represent the expression
values.
PR Close#53643
Consider a case when an explicit `this` read is inside a template with a context that also provides the variable name being read:
```
<ng-template let-a>{{this.a}}</ng-template>
```
Clearly, `this.a` should refer to the class property `a`. However, in today's Angular, `this.a` will refer to `let-a` on the template context.
Amazingly, both TemplateDefinitionBuilder and the Typecheck block have the same bug, and are consistent with each other! This is because `ImplicitReceiver` extends `ThisReceiver` in the parser AST, which is an insane gotcha.
In this commit, I patch the template pipeline to emulate this behavior as well.
To actually fix this nastiness, we have to:
- Update `ingest.ts` in the Template Pipeline (see the corresponding comment)
- Check `type_check_block.ts` in the Typecheck block code (see the corresponding comment)
- Turn off legacy TemplateDefinitionBuilder
- Fix g3, and release in a major version
PR Close#53594
`ng-content` elements, and thus their corresponding projection instructions, can have many attributes on them. Some of these attributes may result in special behavior. For example, `ngProjectAs` and `i18n-foo` both result in special const collection, into the approprate BindingKind slot in the const array. Additionally, `i18n-foo` needs to recieve all the additional i18n attribute processing.
We solve this by subjecting `ng-content` attributes to all the same pipeline logic that applies to attributes on elements, and then allow the element const collection phase to collect them.
PR Close#53594
For regular templates, any listener will have its name const collected into the bindings section of the element consts.
In contrast, host bindings omit listener names from their hostAttrs. This is a strange and inconsistent behavior, so we hide it behind a compatiblity mode flag.
PR Close#53594
It's possible for the user to create a host attrbiute binding with a
name that makes it _look_ like a class binding `{['class.foo']: ''}`, we
were previously treating these as actual class property bindings. This
change fixes the logic so that only true property bindings cam be
converted to class property bindings.
Note: A user who added an attribute like the above almost certainly
intended to create an actual class property binding. It would be nice if
we could add a diagnostic to warn them about this.
PR Close#53626
Further refine the template pipeline's behavior w.r.t. duplicate values
in the consts array to better align its behavior with TDB. In particular
this means allowing duplicate values for classes and styles.
PR Close#53596
Adds a test for handling of duplicate bindings. Fow now we replicate the
TDB behavior in template pipeline, which is: For style and class text
attributes, only keep the last one. For all other text attributes, add
all of the values to the consts array.
PR Close#53596
The for loop tracking function doesn't allow references to local template variables, aside from `$index` and the item which are passed in as parameters. We enforce this by rewriting all variable references to the components scope.
The problem is that the logic that rewrites the references first walks the view tree and then checks if the variable is `$index` or the item. This is problematic in nested for loops, because it'll find the `$index` of the parent.
These changes resolve the issue by checking for `$index` and the item first.
Fixes#53600.
PR Close#53604
Changes template pipeline to be less aggressive in const collecting
attrs, to match the behavior of template definition builder. There is
nothing wrong with the more aggressive const collection, and in fact it
would be good to re-enable it later, but for now this makes it easier to
transition from TDB to template pipeline.
Also adds a test to verify that sensitive iframe attributes are properly
validated.
PR Close#53580
TemplateDefinitionBuilder is apparently more careful about when it attempts to split namespaces in attribute values. However, we are doing this on style attributes, which might start with a single `:`. Rather than refactor our logic to only try to split namespaces in some cases, we can just add an option to make namespace splitting fail gracefully. We only use this option for attributes, not elements.
Note also: the compiled code for this, while "correct" is absolutely insane. Maybe we should consider fixing this, as a matter of principle.
PR Close#53574
Some elements may have multiple bindings with the same name. We should accept and emit them all, as long as they have different kinds.
Co-authored-by: Miles Malerba <mmalerba@users.noreply.github.com>
PR Close#53574
The template pipeline was previously not reserving a variable slot for the result of the `deferWhen` instruction, which caused the `defer when` feature to crash at runtime.
PR Close#53574
When an element is self-closing, it will cause an `element` instruction to be emitted (instead of `elementStart`/`elementEnd`). In that case, we should use map whole source span for the instruction, not just the starting span.
PR Close#53574
The template pipeline was producing slightly different names than TemplateDefinitionBuilder for defer deps functions. I have added a workaround in the name of backwards compatibility, to avoid suffixing the const pool function names.
PR Close#53574
Previously when we found an ICU that was the only translatable content
in its i18n block, we assigned the block's i18n context to the ICU.
However, we neglected to set the contextKind to inidcate that the
context was associated with an ICU. As of this change we now set the
correct contextKind.
This change also refactors the context creation to explicitly separate
creation of contexts for attributes, root i18n blocks, child i18n
blocks, and ICUs. This allows us to more easily ensure that contexts are
shared appropriately between i18n blocks and ICUs.
Finally, this change also refactors the i18n message extraction pahse to
simplify how contexts are converted to i18n messages. This
simplification should make it easier to merge i18n contexts and i18n
messages into a single op in a future refactor.
PR Close#53557
Whenever an input of a directive changes, the semantic symbol should
reflect this change for the type check API. This is important because
signal inputs require special output in the type checking blocks- hence
we need to ensure that such type checking blocks are re-generated
properly.
Test verify that incremental type-checking builds work as expected now.
PR Close#53521
Whenever a signal input is captured in a type check block, we will
insert an import. This will change the import graph so that the full
TypeScript program cannot be structurally re-used.
We can fix this trivially by ensuring the import graph remains stable,
by always generating an import to e.g. `@angular/core`. This fixes the
issue nicely for type-check block files. A test verifies this.
For inline code, such as TCB inline or the type constructors inline,
this fix is not applicable because we would change user-input source files,
adding new edges that would not exist for subsequent builds- causing the
program to be not re-used completely. One idea was to rely on the
existing edge that can be assumed to exist for directive code files.
This is true technically, but in practice TS does not deduplicate
imports- so our new namespace import when referencing our symbols will
invalidate the re-use. We will address this in a follow-up. There are a
couple of options, such as working with the TS team, updating the
existing edge, or inlining our helpers as well.
PR Close#53521
This commit adds the last remaining piece for signal input
type-checking. Bound values to signal inputs are already checked
properly at this point, but inference of generic directive/component
types through their inputs is not implemented.
This commit fixes this. To achieve this, there are a couple of potential
solutions. The generics of a directive are inferred based on input
value expressions using a so-called type constructor. The constructor
looks something like this:
```
const _ctor = <T>(v: Pick<Dir<T>, 'input1', 'input2'>) => Dir<T>;
_ctor({input1: expr1, input2: expr2});
```
This works very well for non-signal inputs where the class member is
directly holding the input values. For signal inputs, this does NOT
work because the class member will actually hold the `InputSignal`
instance. There are a couple of solutions to this:
1. Calling `_ctor` with an `InputSignal<typeof value>`
2. Converting the `_ctor` input signal fields to their write types
(unwrapping the input signals).
We've decided to go with the second option as TypeScript is very
sensitive with assignments and its checks. i.e. co-variance,
contravariance or bivariance. Semantically it makes more sense to unwrap
the input signal "write type" directly and "assign to it". This is safer
and conceptually also easier to follow. A type constructor continues to
only receive the "expresison values". This simplifies code as well.
It's worth noting that the unwrapping as per option 2 also comes at a
cost. We need to be able to generate imports in type constructors. This
was not possible until the previous commit because inline type constructors
did not have an associated type-check block `Environment` and we were
missing access to expression translation and correct import generation.
Overall, solution 2 is now implemented as works as expected. This commit
adds additional unit tests to ensure this.
PR Close#53521
For signal inputs we are looking at generating additional code inside
type constructors. This code is planned to reference an external type
from `@angular/core` to unwrap `InputSignal`'s class fields.
The existing `Environment` class contains helpers for emitting such
references / and translating them from the output AST. We extract
this logic into a superclass for only emitting references. A similar
type already existed to avoid circular dependencies- but now we have
actual use-cases to populate this as a base class.
This allows us to create more-suitable minimal emit environments
when we e.g. generate type constructors inline- which are not
part of any type check block. The existing `Environment` class is scoped
to type check blocks and therefore was not suitable.
PR Close#53521
Signal inputs do not need coercion members for their transforms. That is
because the `InputSignal` type- which is accessible in the class member-
already holds the type of potential "write values". This eliminates the
need for coercion members which were simply used to somehow capture this
write type (especially when libraries are consumed and only `.d.ts` is
available).
We can simplify this, and also significantlky loosen restrictions
of transform functions- given that we can fully rely on TypeScript for
inferring the type. There is no requirement in being able to
"transplant" the type into different places- hence also allowing
supporting transform functions with generics, or overloads.
In a follow-up commit, once more parts are place, there will be some
compliance tests to ensure these new "loosend restrictions".
PR Close#53521
This commit ensures that the type-check diagnostic testing
infrastructure is prepared to validate signal inputs. i.e. providing the
necessary "mocks" in the fake "d.ts" of `@angular/core`.
The commit then sets up a Golang-style table driven testing environment
that allows us to validate/verify signal input type-checking in a
readable way.
With this infrastructure set up, this commit defines an initial set
of unit tests for type checking of input signals.
PR Close#53521
This commit introduces the initial type-checking for signal inputs.
To enable type-checking od signal inputs, there are a couple of tricks
needed. It's not trivial as it would look like at first glance.
Initial attempts could have been to generate additional statements in
type-checking blocks for signal inputs to simply call a method like
`InputSignal#applyNewValue`. This would seem natural, as it would match
what will happen at runtime, but this would break the language-service
auto completion in a highly subtle way. Consider the case where multiple
directives match the same input. Consider the directives have some
overlap in accepted input values, but they also have distinct diverging
values, like:
```ts
class DirA {
value = input<'apple'|'shared'>();
}
class DirB {
value = input<'orange'|'shared'>();
}
```
In such cases, auto completion for the binding expression should suggest
the following values: `apple`, `shared`, `orange` and `undefined`.
The language service achieves this by getting completions in the
type-check block where the user expression would live. This BREAKS if
we'd have multiple places where the expression from the user is used.
Two different places, or more, surface additional problems with
diagnostic collection. Previously diagnostics would surface the union
type of allowed values, but with multiple places, we'd have to work with
potentially 1+ diagnostics. This is non-ideal.
Another important consideration is test coverage. It might sound
problematic to consider the existing test infrastructure as relevant,
but in practice, we have thousands of diagnostic type check block tests
that would greatly benefit if the general emit structure would still
match conceptually. This is another bonus argument on why changing the
way inputs are applied is probably an option we should consider as a
last resort.
Ultimately, there is a good solution where we unwrap directive signal
inputs, based on metadata, and access a brand type field on the
`InputSignal`. This ensures auto-completion continues to work as is, and
also the structure of type check blocks doesn't change conceptually. In
future commits we also need to handle type-inference for generic signal
inputs.
Note: Another alternative considered, in terms of using metadata or not.
We could have type helpers to unwrap signal inputs using type helpers
like: `T extends InputSignal<any, WriteT> ? WriteT : T`. This would
allow us to drop the input signal metadata dependency, but in reality,
this has a few issues:
- users might have `@Input`'s passing around `InputSignal`'s. This is
unlikely, but shows that the solution would not be fully correct.
- we need the metadata regardless, as we plan on accessing it at runtime
as well, to distinguish between signal inputs and normal inputs when
applying new values. This was not clear when this option was
considered initially.
PR Close#53521
This commit captures the metadata on whether an input is signal based or
not, in the `.d.ts` of directives and components. This exposes this
information to consumers of the directives. This is needed because
libraries may use signal inputs, and we need to know whether bound
inputs to this library are signal-based or not- so that we can generate
proper type-checking code (account for `InputSignal` or not).
Additionally, this commit introduces a new structure for the partial
compilation output of directive inputs. With the current emit, inputs
are captured in a data structure that is equivalent to the internal data
structure passed to `defineDirective` (the full compilation output).
This worked fine as we only captured a few strings, but in ends up
being a bad practice because partial compilation output should NOT
capture internal data structures that might be specific to a certian
Angular core version. Instead, we introduce a new "future proof"
structure that:
- can hold additional metadata in backwards-compatible ways, like
`isSignal` or `isRequired`.
- can be parsed trivially using the `AstHost` for the linker, instead of
having to unwrap/parse an array structure.
The new structure is only emitted when we discover that some inputs are
signal based (or ultimately end up configuring input flags). This is
done for backwards compatibility, so that libraries without signal
inputs remain compatible with older linker versions. In the future,
this might be the only emit.
Compliance tests for this follow in future commits, when the linker
portion is also in place. This commit specialices on the code
generation. With the linker, and compliance test infrastructure fixed
(that is broken right now), we can test the full integration.
PR Close#53521
This commit defines the initial metadata for inputs passed around in
the compiler-cli. Inputs will now capture additional metadata on whether
they are signal-based or not. This is stored on a per-input basis as
a Zone component may contain both, signal inputs or `@Input` inputs.
The metadata is later used for type-checking, for partial output
generation, or full compilation output generation.
PR Close#53521
This commit introduces a function for declaring inputs in
components. The function is called `input`. It comes in two flavors:
- `input` for optional inputs with initial values
- `input.required` for required inputs
Inputs are declared as class members, like with `@Input`- except that
the class field will no longer hold the input value directly. Angular
takes control over the input field and exposes the input value as a
signal. The runtime implementation will follow in future commits.
This commit simply introduces:
- initial compiler detection to recognize such inputs in classes
- the initial signature of `input` and `input.required`.
Note: the defer size test is flawed and there is no minification- hence
this commit also needs to incorporate the new dependency graph changes.
PR Close#53521
`o.WrappedNodeExpr` can show up in some cases, when a host binding's value is inside a TS expression.
It's an open question whether we will need to support all of the TS expression types as a result.
PR Close#53478
For some reason, the parser reuses the same field to store the animation phase and the event target. We were incorrectly interpreting the presence of any value on that field as an animation phase, leading us to incorrectly emit synthetic listener instructions for listeners on events with targets. This bug is now fixes.
PR Close#53478
`$any` should be interpreted as a cast, not as a context read of a variable called `$any`. This already worked in template compilations, but the relevant phase was not enabled for host bindings.
PR Close#53478
Fixes that the compiler was throwing an error if an ambient type is used inside of an input `transform` function. The problem was that the reference emitter was trying to write a reference to the ambient type's source file which isn't necessary.
Fixes#51424.
PR Close#51474
The ops for the implicit variables in `@for` loops (e.g. `$index`) are marked as being mandatory which means that they're generated even if they aren't used. These changes make them optional so they're only added when necessary.
PR Close#53515
Adds support for sanitizing host bindings. Since the tag name of the
element the host binding is being set on isn't always known, we have to
consider multiple possible security contexts.
This commit also adds additional tests to help verify correct behavior
of the sanitization logic for different edge cases.
PR Close#53513
Use the DomElementSchemaRegistry to determine the correct security
context for static attributes, and pass it along during ingestion. Then
during the resolve sanitizers phase, use the security context to
determine if a trusted value function is needed
PR Close#53473
Consider the case:
```
<button *ngIf="true" [@anim]="field"></button>
```
Only the inner `button` should recieve a `property` instruction for the animation binding. We were previously emitting one for the implicit `ng-template` as well, and collecting it into the consts for the `ng-template`. Both of these issues are now fixed.
PR Close#53457
The behavior of explicit bindings on `ng-template`s was untested, and we differed from `TemplateDefinitionBuilder` significantly. We now have much more similar behavior, although not 100% identical.
For example, consider this templarte:
```
<ng-template l="l1" [p]="p1" [attr.a]="a1" [class.c]="c1"></ng-template>
```
It's not clear what a class binding on an `ng-template` would actually do. Nonetheless, it's well-defined behavior in TemplateDefinitionBuilder, which emits `property` instructions for all three bindings, and people actually do this in google3.
Note that some of these bindings don't really make much sense, but we have to support them for compatibility purposes.
See comments for an in-depth explanation of all the logic.
Also, add a test to exercise the problematic case.
PR Close#53457
The Template Pipeline has had a number of tricky bugs involving bindings on structural elements.
Consider this template:
```
<div *ngIf="true" [class.bar]="field"></div>
```
We were incorrectly emitting `ɵɵclassProp` on *both* the template's view, and the inner view. The solution is to just emit an extracted attribute on the enclosing template, so it still shows up in the const array, but does not affect the update block.
We will refactor binding ingestion soon, but this commit improves our correctness before any big refactor.
PR Close#53457
These patches are no longer necessary with the current state of the
type packages and the code within the repository. The types are now
included in the already required babel.d.ts file for the relevant
babel packages (currently: `@babel/core` and `@babel/generator`).
PR Close#53441
The `@babel/core` package provides the functionality of multiple other babel packages
without the need to directly depend or import the other babel packages. Since the
`@babel/core` package is already used and imported in the locations that previously
used the other babel packages, an overall reduction in both imports and dependencies
is possible. Six babel related packages were able to be removed from the root `package.json`
and one (also present in the aforementioned six) was removed as a dependency from the
`@angular/localize` package. Unfortunately, the functionality used from the `@babel/generator`
package is not provided by `@babel/core` and is still present. Further refactoring may
allow its removal as well in the future.
The following packages were removed:
* @babel/parser
* @babel/template
* @babel/traverse
* @babel/types
* @types/babel__template
* @types/babel__traverse
PR Close#53441
@for does not use actual TemplateOps, but instead has a similar
RepeaterCreateOp. This commit adds support for this op to the relevant
i18n phases.
PR Close#53440
This test actually passes, template pipeline just orders the translated
messages and consts array differently. Since the order isn't important,
we just fork off an alternate golden file for template pipeline.
PR Close#53459
I discovered this failure while looking at presubmit results. We appear to still have ordering issues, when more update ops are present.
PR Close#53405
While running a g3 presubmit, I discovered two related novel failure modes:
1. Simple case: this new test uses an `ngFor` structural directive, which binds a context variable. That variable is immediately used in an attribute binding. It looks like we generate an extra attribute instruction, which might result in an invalid property read at runtime.
2. Complex case: this is another attribute binding, this time on a structural element, inside of an `ng-template`. Not sure what's going on here.
PR Close#53405
Previously, binding ops only knew whether they applied to a structural template (and even this was actually very misleading!).
Now, binding ops have full information about what kind of template they apply to, if any (e.g. plain template, structural template, etc). Additionally, each binding knows whether it `IsStructuralTemplateAttribute`, which is a property of the binding rather than the template target.
In the future, we should refactor this to unify the various flags that can describe binding types, as well as the flags that describe template targets, into a single and comprehensive field on binding ops.
PR Close#53405
Previously, we created i18n contexts for i18n attributes in ingest. This turned out to be the wrong approach, because we don't always want to produce i18n messages for all i18n attributes! In fact, several kinds of i18n attributes on elements with structural directives should not produce their own messages.
This commit also contains related refactors to fix one such structural directives test.
PR Close#53405
When a binding is present on an element with a structural directive, that binding is parsed onto *both* the synthetic `ng-template`, as well as the inner element. However, we do not want to create different i18n messages for both bindings; we only want to generate a new i18n message for the inner, "real" element.
PR Close#53405
Listener instructions should not be inside the i18n block. In order to avoid this, we ingest bindings on an element before starting the i18n block.
We previously missed this case because almost all bindings result in *update* instructions, which don't need to be ordered relative to i18nStart/i18nEnd create instructions. However, listeners are the only kind of binding that gets ingested into the create block.
PR Close#53405
Previously, our i18n slot moving process was buggy. Specifically, it was not resilient to cases in which a create op consumed a slot, but no update ops depended on that slot.
The new algorithm fixes this issue, and is also easier to understand.
PR Close#53405
These patches are no longer necessary with the current state of the
type packages and the code within the repository. The types are now
included in the already required babel.d.ts file for the relevant
babel packages (currently: `@babel/core` and `@babel/generator`).
PR Close#53374
The `@babel/core` package provides the functionality of multiple other babel packages
without the need to directly depend or import the other babel packages. Since the
`@babel/core` package is already used and imported in the locations that previously
used the other babel packages, an overall reduction in both imports and dependencies
is possible. Six babel related packages were able to be removed from the root `package.json`
and one (also present in the aforementioned six) was removed as a dependency from the
`@angular/localize` package. Unfortunately, the functionality used from the `@babel/generator`
package is not provided by `@babel/core` and is still present. Further refactoring may
allow its removal as well in the future.
The following packages were removed:
* @babel/parser
* @babel/template
* @babel/traverse
* @babel/types
* @types/babel__template
* @types/babel__traverse
PR Close#53374
I18n expressions logically have both a target and an owner:
- For i18n text expressions, the owner is the i18nStart instruction. The target is initially the same, but later moves to be the last slot consumer in the i18n block.
- For i18n attribute expressions, the owner is the I18nAttributes config instruction, whereas the target is the ElementCreate that hosts the attribute.
This refactor makes the code clearer in quite a few plases.
Additionally, we now perform a lot of the i18n processing earlier. For example, re-targeting and re-ordering of i18n expressions happens *before* apply instructions are generated. As a result, the re-ordering logic is a lot simpler.
These changes also have consequences on i18n const collection, along with a couple other minor changes.
PR Close#53376
These changes add an option to the `extendedDiagnostics` field that allows the check from #53190 to be disabled. This is a follow-up based on a recent discussion.
PR Close#53311
Add support for i18n attributes:
- Generate i18n contexts from i18n attributes, and extract the eventual messages into the constant pool.
- Emit I18nAttributes config instructions when needed.
- Use the generated i18n variable in the appropriate places, including extracted attribute instructions, as well as I18nAttributes config arrays.
PR Close#53341
Currently we generate the following TCB for a `@for` loop:
```ts
// @for (item of items; track item) {...}
for (const item of this.items) {
var _t1 = item;
// Do things with `_t1`
}
```
This is problematic if the item name is the same as a global variable (e.g. `document`), because when the TCB has references to that variable (e.g. `document.createElement`), it'll find the loop initializer instead of the global variable.
These changes fix the issue by generating the following instead:
```ts
for (const _t1 of this.items) {
// Do things with `_t1`
}
```
Fixes#53293.
PR Close#53319
Previously we recorded separate param values for a strucural directive
and the element tag it goes on. We then later attempted to combine those
into a single value. However in some cases this merging logic matched
the directive with the wrong tag.
This change implements an alternate approach where we match the
directive to its element tag from the start, while we're traversing the
ops. This should be a more robust solution.
PR Close#53327
We previously failed to populate the attributes property on projection
ops, this commit populates it and later strips out the "select"
attribute.
PR Close#53327
Previously we failed to reset the sub-template index counter when we
exited a root block. This caused following sibling blocks to start
counting at the wrong index.
PR Close#53327
It is possible for ICUs to be nested inside other ICUs. This change
adjusts our ingestion logic to create extra interpolation ops for the
nested ICUs during ingestion.
PR Close#53300
We previously had an assertion that every placeholder in the i18n AST
had a corresponding param in the output. However, there are some cases
such as interpolations nested inside ICUs where this assertion is not
true. This change simply removes the asserion.
PR Close#53300
ICUs may share a placeholder, and in that case they need special
post-processing. This change adds logic to cover this possibility. In
particular, we set the param to a special placeholder value and then
pass an array containing the sub-message variables as a post-processing
param.
PR Close#53300
When we re-assign the slot dependencies for the i18nExprs, we should
move them down below the other ops that target their same slot. This
keeps the behavior consistent with TDB
PR Close#53300
When the AOT compiler creates a delegated host for a provided TypeScript CompilerHost,
it delegates functionality back to the original via a series of internal method delegations.
However, unlike other members of the CompilerHost, `jsDocParsingMode` is not a method
and cannot be delegated in this way. Attempting to call bind on the property will result
in a runtime error. Instead, `jsDocParsingMode` is now delegated via get/set accessors.
Additionally, the override of `getSourceFile` now has an updated type signature to reflect
the additional of the `jsDocParsingMode` option for the method.
This is a followup to #53126 which updates the other DelegatingCompilerHost.
PR Close#53292
This commit fixes an issue where having an expression with nullish coalescing in styling host bindings leads to JS errors due to the fact that a declaration for a temporary variable was not included into the generated code.
Resolves#53295.
PR Close#53305
As part of this fix, I realized that child i18n blocks don't need their
own context. Instead, we can just add their params directly to the
context for their root block, and forgo the step of merging the contexts.
PR Close#53209
ICU sub-messages should be recorded as belonging to the message for the
root i18n block they are part of. This ensures that they still get
emitted even if they are nested in a child template.
PR Close#53209
This is a follow-up to the fix from #52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
PR Close#53190
These changes expose the `ngContentSelectors` and `preserveWhitespaces` metadata to the TCB so they can be used in the next commit to implement a new diagnostic.
PR Close#53190
When the AOT compiler creates a delegated host for a provided TypeScript CompilerHost,
it delegates functionality back to the original via a series of internal method delegations.
However, unlike other members of the CompilerHost, `jsDocParsingMode` is not a method
and cannot be delegated in this way. Attempting to call bind on the property will result
in a runtime error. Instead, `jsDocParsingMode` is now delegated via get/set accessors.
Additionally, the override of `getSourceFile` now has an updated type signature to reflect
the additional of the `jsDocParsingMode` option for the method.
PR Close#53126
Adds support for inheriting host directives from the parent class. This is consistent with how we inherit other features like host bindings.
Fixes#51203.
PR Close#52992
This is a follow-up to the fix from #52414. It adds a diagnostic that will tell users when a control flow is preventing its direct descendants from being projected into a specific component slot.
PR Close#52726
These changes expose the `ngContentSelectors` and `preserveWhitespaces` metadata to the TCB so they can be used in the next commit to implement a new diagnostic.
PR Close#52726
The `$first`, `$last`, `$even` and `$odd` variables in `@for` loops aren't defined on the template context of the loop, but are computed based on `$index` and `$count` (e.g. `$first` is defined as `$index === 0`). We do this calculation by looking up `$index` and `$count` when one of the variables is used.
The problem is that all `@for` loop variables are available implicitly which means that when a nested loop tries to rewrite a reference to an outer loop computed variable, it finds its own `$index` and `$count` first and it doesn't look up the ones on the parent at all. This means that the calculated values will be incorrect at runtime.
These changes work around the issue by defining nested-level-specific variable names that can be used for lookups (e.g. `$index` at level `2` will also be available as `ɵ$index_2`). This isn't the most elegant solution, however the `TemplatDefitinionBuilder` wasn't set up to handle shadowed variables like this and it doesn't make sense to refactor it given the upcoming template pipeline.
Fixes#52917.
PR Close#52931
Reworks the `repeater` instruction to go through `advance`, instead of passing in the index directly. This ensures that lifecycle hooks run at the right time and that we don't throw "changed after checked" errors when we shouldn't be.
Fixes#52885.
PR Close#52935
In some cases ICU expression placeholders may have trailing spaces that
need to be trimmed when matching the placeholder to its corresponding
text binding.
PR Close#52698
We were previously counting the i18n expression index and deciding when
to apply i18n expressions based on the i18n context. These should be
done based on the i18n block instead.
PR Close#52698
The previous commit added support for interpolated text in ICUs, but it
made the assumption that the interpolation would be a single variable
read expression.
To properly support all kinds of interpolation expressions, this commit
refactors how ICUs are ingested to allow us to re-use the same logic we
use for bound text outside of ICUs.
To accomplish this, the `IcuOp` creation op has been removed in favor of
a pair of ops: `IcuStartOp` and `IcuEndOp`, that mark the beginning and
end of the ICU. Now, instead of inserting an `IcuUpdateOp` in the update
IR, we call `ingestBoundText` and use the presence of the surrounding
`IcuStartOp` and `IcuEndOp` to match the interpolation with the ICU.
PR Close#52698
Previously ICUs were assumed to only generate a single i18n expression
per ICU. However, it is possible for ICUs to contain text interpolations
which requires additional expressions. This commit adds support for
multiple expressions per ICU.
PR Close#52698
ICUs that contain element tags need extra parameters for the i18n
message. These are in addition to the element slot params that are
already added to the parent i18n block's params. In this commit we add a
new phase to fill in these placeholders.
PR Close#52698
Previously the template pipeline sorted i18n message params before
adding the sub-message placeholders. Now its sorts after all
placeholders are added.
Both the template pipeline and TemplateDefinitionBuilder previously
failed to sort the post-processing params. They both now sort these as
well. This is safe to change in TemplateDefinitionBuilder, as it does
not change anything about the functionality, it simply ensures that
params map in the output has the keys ordered in a way that can be
easily reproduced in the template pipeline.
PR Close#52698
Updates the repo to support TypeScript 5.3 and resolve any issues. Fixes include:
* Updating usages of TS compiler APIs to match their new signatures.
* In TS 5.3 negative numbers are represented as `PrefixUnaryExpression` instead of `NumericExpression`. These changes update all usages to account for it since passing a negative number into the old APIs results in a runtime error.
PR Close#52572
Fixes that all implicit variables in `@for` loops were inferred to be numbers, even though most are actually boolean.
Note that I also had to work around a weird TypeScript behavior in `tsDeclareVariable` where usually we declare variables in the following format:
```
var _t1: <type> = null!;
```
This works in most cases, but if the type is a `boolean`, TypeScript infers the variable as `never`, instead of `boolean`. I've worked around it by adding an `as boolean` to the initializer.
Fixes#52730.
PR Close#52732
Adds the private `_enableBlockSyntax` flag that can be used by the language service to disable blocks on apps that aren't on Angular v17.
PR Close#52683
Now that two-way bindings work correctly with implicit receivers, we can fix the corresponing source map tests. The main issue was that we were not properly mapping `elementEnd` for elements with no closing tag (self-closing elements).
PR Close#52479
Some two-way bindings tests were not working properly, because we could not ingest the implicit receiver required to write to the `ngModelChanges` property. Now, we properly resolve that implicit receiver to the root component context.
Also, add some tests, both for the simple case, and the case where the listener is inside a nested view.
PR Close#52479
Some `defer` blocks have external dependencies on other components or directives. These dependencies need to be extracted into deps functions, which either return local deps, or use a dynamic import for non-local deps. Template Pipeline can now generate these functions.
PR Close#52479
When an `ng-template` has local refs, such as `<ng-template #foo>`, we must emit a `ɵɵtemplateRefExtractor` argument to the template creation functino. The template pipeline now supports this.
PR Close#52479
`TemplateDefinitionBuilder` is somewhat unreliable about extracting constant attributes (e.g. `[attr.foo]="'one'"`). It never extracts const non-string expressions, and usually, but not always, extracts const string expressions. Template pipeline consistently extracts const strings, and we add new goldens for a couple such cases.
PR Close#52479
Some defer triggers, such as `hover`, expect a local reference as an argument. For example, `@defer (on hover(target))` waits until the user hovers over the target.
However, these defer conditions also have a nullary form, in which the trigger is implicitly the first element in the placeholder block. We now support that case in template pipeline.
PR Close#52479
We already supported `defer on` conditions, which become instructions in the create mode block.
Now, we also support `defer when` conditions, where are very similar, with the notable difference that they go in the update block (because a user-supplied condition must be re-evaluated on each update.)
PR Close#52479
Previously we supported ICUs where the ICU itself represetned the entire
translated message. This change allows ICUs to act as a sub-message
inside other translated messages.
PR Close#52503
Discovered this while validating #52414 against Angular Material. We were projecting `<ng-template>` nodes at the root of `@if` and `@for` with the `ng-template` tag name which enables directive matching and applies the directive to the control flow node.
These changes fix the issue by never passing along the `ng-template` tag name.
PR Close#52515
TypeScript JsDoc parsing, by default, treats occurences of Angular decorators (e.g. `@Component`) in JsDoc comments as JsDoc tags. This commit escapes these decorator strings by copying the raw JS doc onto a dummy symbol in a new SourceFile to make TypeScript re-parse the comment.
PR Close#52481
This adds a target to generate a manifest of all public api symbols. The majority of inputs are generated from the extraction rules, but API entries that don't have a TypeScript source symbol (elements and blocks) are defined in hand-written json collections.
PR Close#52472
With the directive-based control flow users were able to conditionally project content using the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are copied to the template via the template creation instruction. With `@if` and `@for` that is not the case, because the conditional is placed *around* elements, rather than *on* them. The result is that content projection won't work in the same way if a user converts from `*ngIf` to `@if`.
These changes aim to cover the most common case by doing the same copying when a control flow node has *one and only one* root element or template node.
This approach comes with some caveats:
1. As soon as any other node is added to the root, the copying behavior won't work anymore. A diagnostic will be added to flag cases like this and to explain how to work around it.
2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this workaround, because it'll include an additional text node as the first child. We can work around it here, but in a discussion it was decided not to, because the user explicitly opted into preserving the whitespace and we would have to drop it from the generated code. The diagnostic mentioned point #1 will flag such cases to users.
Fixes#52277.
PR Close#52414
Prior to this change, the transform function would be referenced with a potentially
relative import into an external declaration file. Such imports are not portable
and should not be created in this context. This commit addresses the issue by threading
though the originally used module specifier by means of the `Reference` type.
Fixes#52324
PR Close#52437
This commit fixes an issue where using literal types in the arguments of an input coercion
function could result in emitting invalid output, due to an assumption that TypeScript makes
when emitting literal types. Specifically, it takes the literal's text from its containing
source file, but this breaks when the literal type node has been transplanted into a
different source file. This issue has surfaced in the type-check code generator and is
already being addressed there, so this commit moves the relevant `TypeEmitter` class
from the `typecheck` module to the `translator` module, such that it can be reused for
emitting types in the type translator.
Fixes#51672
PR Close#52437
The previous commits provided the scaffolding for `defer on`. In this commit, we build on that work, adding triggers for `immediate`, `timer`, `hover`, and `viewport`.
PR Close#52387
Previously, we supported a `HasConst` trait, allowing an op to be const collected automatically. However, that approach had the shortcoming that each op could only collect a single constant.
Instead, we now provide a `ConstCollectedExpr`, which collects constants at the expression level, allowing ops to have multiple collectible consts.
Then, we use this new abstraction to support the `defer on` conditions.
PR Close#52387
Previously, we had an "empty shell" implementation of defer conditions, and we used separate ops to represent secondary defer blocks.
Now, we have a real scaffolding for supporting the various defer conditions, and the secondary defer block information has been refactored onto the main defer op.
Additionally, to enable this, we refactor the way that using slot indices works. Instead of having a trait that causes users of slot indices to be linked to the allocated slot, we share a single `SlotHandle` object by reference. This allows an op to use slot information for more than one Xref at a time, and eliminates a layer of indirection.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
PR Close#52387
Enables a handful of i18n tests that are currently skipped, but pass if
enabled. Some of them require alternate golden files because of
inconsequential differences in the cost array order.
PR Close#52390
This commit expands docs extraction for classes and interfaces to include inherited members. This relies on the type checker to get the _resolved_ members of the type so that the extractor doesn't need to reason about inheritance rules, which can get tricky (especially with regards to method overloads).
PR Close#52389
This commit adds decorators to the extracted API docs. It makes some
very hard-coded assumptions about the pattern used to declare decorators
that's extremely specific to what the framework does today.
PR Close#52389
Previously, we would emit *two* pipe creation instructions for each pipe in a switch case. This is because we were visiting both the transformed and raw versions of the pipe bindings.
Now, we clear the raw case expressions array after generating the transformed test expression.
Also, we introduce some new goldens, because our pipe creation order is harmlessly different.
PR Close#52289
We roughly attempt to match TemplateDefinitionBuilder's pipe creation order, by placing pipe creation instructions after their target elements. However, we cannot fully emulate the "inside-out" ordering TemplateDefinitionBuilder uses when multiple pipes apply to one element, because TemplateDefinitionBuilder creates the pipes as expressions are visited, from the leaves up. Our order is perfectly adequate though.
We also add a non-compatibility-mode ordering, which just appends them to the end of the create block. This is better because it allows for more chaining opportunities.
PR Close#52289
Singleton property interpolation instructions consume only one variable, but are still emitted as an interpolation instruction (they cannot be collapsed because `propertyInterpolate` implicitly stringifies its argument.)
PR Close#52289
We were incorrectly emiting a extracted constant pool index for the final argument of the projection instruction. It actually takes an array literal.
(N.B.: This means we re-create the array every time! We should probably modify the runtime to use a const index for this.)
Additionally, we alter the projection op to not extend the element op base type.
PR Close#52289
The correct order of attributes and properties is:
1. Interpolated properties
2. Interpolated attributes
3. Non-interpolated properties
4. Non-interpolated attributes
This includes an additional nuance: singleton attribute interpolations, such as `[attr.foo]="{{bar}}"`, will be "collaped" into a simple `attribute` instruction. However, this is *not* the case for singleton property interpolations! The ordering phase must take this nuance into account to match the TemplateDefinitionBuilder order.
After the project lands, it might be nice to also collapse singleton property interpolations.
PR Close#52289
Previously, we ran the ordering phase near the end of the compilation. However, this meant that phases like slot assignment and variable offset assignment would happen first, and then the nice, monotonically-increasing orders would be scrambled by the reordering.
It's much more intelligible to order first, and then perform these assignments. However, to make this happen, some modifications to the ordering phase are required. In particular, we can no longer rely on `advance` instructions to break up orderable groups.
PR Close#52289
Many instructions consume variable slots, which are used to persist data between update runs. For top-level instructions, the offset into the variable data array is implicitly advanced, because those instructions always run.
However, instructions in non-top-level expressions cannot be assumed to run every time, because they might be conditionally executed. Therefore, they cannot implicitly advance the offset into the variable data, and must be given an explicitly assigned variable offset.
TemplateDefinitionBuilder assigned offsets top-to-bottom for all instructions *except* pure functions. Pure functions would be assigned offsets lazily, on a second pass.
Template Pipeline can now imitate this behavior, when in compatibility mode: pure functions are assigned offsets on a second pass.
This also makes the "variadic var offsets" phase unnecessary -- the new approach is more general and correct.
PR Close#52289
Previously, inside an event listener, template pipeline would always save the context from restoring a view, e.g.
```
const restored_ctx = r0.ɵɵrestoreView(s);
```
This is usually correct! However, consider the case of a listener in the template's root view. The appropriate context will already be available via closure capture, and we can just use it (as `ctx`).
Now, the context resolution phase understands that we don't need to use the restored view's saved context if we would have access to it by closure.
Note: we also create a new golden, because the const array is in a harmlessly different order.
PR Close#52289
Previously, the template pipeline did not handle "empty" reads gracefully: it would emit syntactically invalid reads of empty properties. Now we read `$implicit`.
This allows us to enable a test that relies on `$implicit`. However, we also have to create another golden, because our variable inlining is more aggressive.
PR Close#52289
We currently allow elements to be collapsed around pipe creation instructions. TemplateDefinitionBuilder disallows this, but only sometimes. Collapsing in this case is actually less generated code, and it's OK to allow it.
PR Close#52289
The template pipeline can now generate track functions, and extract them into the constant pool (or optimize them if needed). Additionally, context variables such as `$index` can be used inside track functions and for loop bodies.
PR Close#52001
Add support for `repeaterCreate` and `repeater` instructions. Correctly count decls and vars, and support primary and empty blocks.
`track` functions are not yet extracted.
PR Close#52001
In #52110 the compiler was changed to produce `if` statements when type checking `@switch` in order to avoid a bug in the TypeScript compiler. In order to avoid duplicate diagnostics, the main `@switch` expression was ignored in each of the `@case` comparisons. This appears to have caused a regression where comparing incompatible types wasn't being reported anymore.
These changes resolve the issue by wrapping the expression in parentheses which allows the compiler to report comparison diagnostics while ignoring diagnostics in the expression itself.
Fixes#52315.
PR Close#52322
Now the method `getConstructorDependencies` no longer needs to do any post analysis, and can rely on the reflection host's result to generate ctor params. This will automatically include invalid factories which fix the issue.
PR Close#52215
Currently the reflection host's `getConstructorParameters` method is not aware of the compilation mode, and it generates result mainly assuming the compilation mode is full and we have access to global type info. As a result, its result is not very suitable for local compilation usage, particularly for deciding if a symbol is imported as type or not. This change plumbs a flag `isLocalCompilation` into reflection host to make it aware of the compilation mode.
Also changes made to the logic in the method `getConstructorParameters` so that in local compilation mode:
- returns NO_VALUE_DECLARATION type value ref only if the type is a type parameter
- returns local type value ref for any imported symbol, unless the import is type only in which case returns TYPE_ONLY_IMPORT type value ref
PR Close#52215
The commit adds messaging to the control flow template diagnostic to direct developers to the new
built-in control flow syntax in Angular.
PR Close#52268
ICUs can be used outside of an i18n block. In this case the ICU should
be automatically wrapped in a new i18n block. This commit adds a new
phase to handle wrapping these bare ICUs.
PR Close#52250
ICU params in i18n messages are now resolved in the post-processing call
rather than in the initial message creation. This matches the output
generated by TemplateDefinitionBuilder.
PR Close#52250
ICUs are now ingested by adding ops to both the creation and update IR.
Both of these ops are ultimately removed before reification, but they
are needed to coordinate and link data between the creation and update
ops. This is done in a new ICU extraction phase that removes both ICU
ops and adds an i18nExpr op to the update IR.
PR Close#52250
This commit extracts the API reference info for generic parameters for
classes, methods, interfaces, and functions. It includes any constraints
and the default type if present.
PR Close#52204
Placing a structural directive on an element with an `i18n` attribute
was generating too many i18n blocks. This was due to both the element
and the template generating their own i18n block. To fix the issue, we
no longer generate top-level i18n blocks for structural directive
templates.
PR Close#52202
Structural directives on an ng-template (e.g. <ng-template *ngIf>) were
being assigned the wrong tag name ('ng-template' instead of null).
PR Close#52202
Fixes handling of placeholders for self-closing tags. Self-closing tags
set a combined value for the start tag placeholder, rather than separate
values for the start and close placeholders.
This commit also enables a number of now passing tests. For some of
these tests I had create a separate golden file due to the different
ordering of the const array. In the template pipeline, i18n and
attribute const collection happen in different pahses and we therefore
get a different order than TemplateDefinitionBuilder, which collected
everything in one pass. The order should not affect the overall behavior.
PR Close#52195
The way we were propagating params up to parent i18n ops didn't account
for the fact that a parent and child could both have a value for the
same placeholder. In order to properly merge the value for these cases,
we need to propagate the params up *before* serialization. Therefore I
removed the standalone param propagation phase and folded the logic into
the placeholder resolution phase.
PR Close#52195
Currently the compiler allocates a variable slot to the `@for` loop expression which ends up unused since we don't store the result on the `LView`.
PR Close#52158
A new flag added to the component's debug info to determine whether to throw runtime error (in dev mode) if component is being rendered without its NgModule. This flag is only set for non-standalone components.
PR Close#52061
Orphan component is an anti-pattern in Angular where a component is rendered while the NgModule declaring it is not installed. It is not easy to capture this scenario, specially in compile time. But it is possible to capture a special case in runtime where the component is being rendered without its NgModule even loaded into the browser. This change adds a flag in cli compiler option to enable such checking, and throwing a runtime exception if it happens. Note that such check is only done in dev mode.
Currently the check requires some generated code that is behind ngJitMode flag (i.e., call to ɵɵsetNgModuleScope), and the new flag can be set only if JIT mode is enabled (i.e., supportJitMode=true) otherwise an error will be thrown.
The orphan component is a main blocker for rolling out local compilation in g3. This option is needed for identifying and isolating such cases.
PR Close#52061
Based on recent discussions, these changes remove the Windows CI check because it has been too flaky for too long. Furthermore, we've concluded that the simulated file system in the compiler tests already catches the same set of bugs as running the tests on a real Windows system.
PR Close#52140
This commit adds support for extracting type alises. It currently
extracts the raw written type from the source without performing any
resolution, such as for resolving `typeof` queries, as current Angular
public APIs do not rely on this.
PR Close#52118
Fixes that the new block syntax was generating instructions in the wrong order which meant that pipes were being declared too early. This meant that if the block is first in the template, any pipes used in it won't be able to inject things like `ChangeDetectorRef`.
These changes update the compiler and add a bunch of tests to ensure that pipes work as expected.
Fixes#52102.
PR Close#52112
Since expressions in event listener are added inside of a callback, type narrowing won't apply to them anymore. These changes add the logic to create a guard expression that will re-narrow the expression in the callback.
Fixes#52052.
PR Close#52069
Since expressions in event listener are added inside of a callback, type narrowing won't apply to them anymore. These changes add the logic to create a guard expression that will re-narrow the expression in the callback.
Fixes#52052.
PR Close#52069
This commit updates `@defer` logic related to handling `after` and `minimum` parameters tree-shakable.
If `after` or `minimum` was used on a `@loading` or `@placeholder` blocks, compiler generates an extra argument for the `ɵɵdefer` instruction. This extra argument is a reference to a function that brings timer-related code.
PR Close#52042
A new statement will be generated for components which will attach some useful debug info to them to be used in runtime error handling. Currently this only happens in full and local compilation modes.
PR Close#51919
The current implementation assumes a qualified name consists of just two identifier, e.g., Foo.Bar. However it can be more nested, like Foo.Bar.Baz.XX.YY. While such nested patterns are quite uncommon and devs mostly just use two identifier here, the TS compiler seems to throw error if we make such assumption and it broke quite a lot of targets in g3 when compiled in local mode. So here we handle this nested property of qualified names.
PR Close#51947
The custom logic in the generate advance phase for i18n expressions did
not work in all cases. Instead we add a new phase to update the
expression's target op, and then allow the standard advance generation
code to determine the number of advance instructions needed.
Co-authored-by: Dylan Hunn <dylhunn@users.noreply.github.com>
PR Close#51988
This commit adds support for extracting function overloads. Interestingly, this worked in an earlier version when the code was extracting all statements in every source file, but the existing compiler API for extracting all exported declarations from an entry-point only returns the first function declaration in cases when there are overloads.
This also marks abstract classes as abstract, required inputs as required, and filters out Angular-private APIs.
PR Close#52040
We type check `@switch` blocks by generating identical TS `switch` statements in the TCB, however TS currently has a bug where parenthesized `switch` block expressions don't narrow their types. Since we use parenthesized expressions to wrap AST nodes for diagnostics, this will bug will affect all Angular-generated `switch` statements.
These changes work around the issue by generating `if`/`else if`/`else` statements that represent the `switch`.
Some alternatives that were considered:
1. Moving the `switch` expression to a constant - this is fairly simple to implement, but it won't fully resolve the narrowing issue since the same constant will have to be used in expressions inside the different cases.
2. Removing the outer-most parenthesis from the switch expression - this works and allows us to continue using switch statements, but because we use parenthesized expressions to map diagnostics to their template locations, I wasn't sure if it won't lead to worse template dignostics.
Fixes#52077.
PR Close#52110
Fixes that the compiler wasn't picking up pipes used inside defer block triggers as dependencies. We had implemented the `visitDeferredTrigger` visitor method, but it wasn't being called, because we weren't going through the `visitAll` method of the deferred block. We don't use `visitAll`, because child nodes have to be processed differently than the connected blocks and triggers.
Fixes#52068.
PR Close#52071
The `_enabledBlockTypes` config option was removed recently, since we've enabled @-syntax by default. This commit removes `_enabledBlockTypes` references from the `compiler-cli` test cases.
PR Close#52066
Adds an `UnknownBlock` node to the Ivy AST to represent blocks that haven't been recognized by the compiler. This will make it easier to integrate blocks into the language service.
PR Close#52047
This adds API doc extraction for interfaces, largely using the same code paths for classes. The primary difference between classes and interfaces is that classes have member _declarations_ while interfaces have member _signatures_. This largely doesn't matter for the purposes of extraction, but the types are distinct with no common base types, so we have to do a fair amount of type unioning and aliasing.
PR Close#52006
A couple tests were already passing, and just needed to be enabled. This includes tests pertaining to:
* ng-template
* host binding styling slots
* and host animation bindings
* some literal tests (which were missing some $foo$ escaped names)
We add pipeline-specific versions of the following tests, and enable them:
* A local refs test. The consts for the element attributes and the consts for local reference are collected in the reverse order, but the emitted template is functionally the same.
* A safe accesstest. Consider the expression `$any(val)?.foo`. `TemplateDefinitionBuilder` extracts a temporary variable: `($tmp_0_0$ = $ctx$.val) == null ? null : $tmp_0_0$.foo`. It presumably does this because it considers the `$any(...)` to be a function call. However, this is not a real call, so Template Pipeline safely ignores it and declines to generate a temporary.
* Another local refs test. AttributeMarker.Template is emitted at the end of the const array (instead of the middle)
PR Close#51950
Consider an `ng-template` which is generated as a result of a structural directive:
```
<div *ngFor="let inner of items"
(click)="onClick(inner)"
[title]="getTitle()"
>
```
This should logically expand into something like the following:
```
<ng-template [ngForOf]="..." >
<div (click)="..." [title]="..."></div>
</ng-template>
```
Note that the `(click)` handler and the `[title]` property are only present on the inner div, *not* on the enclosing generated `ng-template`.
Previously, Template Pipeline would place these bindings on *both* the tempate and the inner element.
However, we can't just remove them completely, because these bindings should still be matchable on the generated `ng-template` (which is very surprising, but nonetheless true).
We resolve this issue with two improvements:
(1) The ingestion step is now much smarter about determining not only if a binding is on a template element, but whether it actually targets that template element.
(2) We use `ExtractedAttributeOp` directly, rather than going through `BindingOp`, to cause the `ng-template` to still receive these bindings in its `consts` array for matching purposes.
PR Close#51950
For components, the parser already extracts the `important` property (and it is later disregarded). However, because host bindings use a totally separate parsing code path, this was never happing for host bindings.
Here, we add some code to the host style parsing phase to drop the `!important` suffix.
We could solve this category of problems for good by parsing host bindings with the same code as template bindings.
PR Close#51950
Previously, we always generated temporary variable declarations at the beginning of each view's update block. This is wrong, for two reasons:
1. Temporaries can be used in the create block
2. When listeners use temporaries, we should declare them inside the listener.
Now, we always place temporaries at the beginning of the enclosing OpList, and recursively try to generate them when we find a listener.
PR Close#51950
Currently the TCB for aliased `if` blocks looks something like this:
```
// Markup: `@if (expr; as alias) { {{alias}} }
if (block.condition) {
var alias = block.condition;
"" + alias;
}
```
The problem with this approach is that the type of `alias` won't be narrowed. This is something that `NgIf` currently supports.
These changes resolve the issue by emitting the variable outside the `if` block and using the variable reference instead:
```
// Markup: `@if (expr; as alias) { {{alias}} }
var alias = block.condition;
if (alias) {
"" + alias;
}
```
PR Close#51952
Reworks a few more places to output arrow functions instead of function declarations in order to reduce the amount of code we generate. Some of these places include:
* Factories in injectable definitions.
* Forward references.
* `dependencies` function in the component definition.
* `consts` function in the component definition.
PR Close#52010
Updates the TCB for `@for` loop blocks to allow nullable values. The runtime already supports it and this makes it easier to switch from `NgFor`.
Fixes#51993.
PR Close#51997
The template pipeline now supports basic forms of `defer` blocks. This includes the `loading`, `placeholder`, and `error` blocks, as well as the loading and placeholder configuration options.
Lazy dependencies and prefetch are not yet implemented.
PR Close#51942
Enables the new `@` block syntax by default by removing the `enabledBlockTypes` flags. There are still some internal flags that allow special use cases to opt out of the block syntax, like during XML parsing and when compiling older libraries (see #51979).
PR Close#51994
Increases the `minVersion` of component declarations that use bloks to v17 in order to indicate to users that they need to update if the library they're using is on the new syntax, while preserving backwards compatibility for libraries that do not use the syntax.
PR Close#51979
Adds some logic to enable parsing of block syntax in the linker. Note that the syntax is only enabled on code compiled with Angular v17 or later.
PR Close#51979