This commit adds end-to-end support for pipes in the template pipeline. This
support works across multiple steps:
1. Pipes are first ingested as `ir.PipeBindingExpr`s during the ingest step.
2. A "pipe creation" phase inserts operations to instantiate each required
pipe, based on the presence of those `ir.PipeBindingExpr`s.
3. A "variadic pipe" phase transforms pipes with more than 4 arguments into
variadic pipe bindings, which use a literal array argument. This literal
array will be later memoized into a pure function invocation.
4. A special phase (`phaseAlignPipeVariadicVarOffset`) reconciles a
difference in variable slot assignment logic between the template pipeline
and the `TemplateDefinitionBuilder`, to ensure that the pipeline output can
pass the existing tests. This phase should not affect runtime semantics and
can be dropped once matching output is no longer necessary.
5. Reification emits pipe instructions based on the argument count.
PR Close#50118
The logic for `insertBefore` in template pipeline operation lists has a bug
when inserting at the end of a list. This commit fixes the safety assertions
to be more accurate.
PR Close#50118
This commit transforms literal arrays and maps within expressions in the
template pipeline into `ir.PureFunctionExpr` expressions, in order to
memoize the allocation of objects and arrays inside the update pass of
change detection.
PR Close#50118
Previously the helper operations for transforming expressions in the
template pipeline would only operate against `ir.Expression`s. This commit
changes them to process `o.Expression`s instead, paving the way to use them
for transformations of native expressions in addition to IR expressions.
PR Close#50118
This commit adds support for generating pure functions in the output
`ConstantPool` based on `ir.PureFunctionExpr`s. Note that nothing yet
generates these pure function forms - in the future they will be used both
in the implementation of the `pipeBindV` instruction as well as literal
arrays and maps in expressions.
PR Close#50118
This commit adds the `ConstantPool` to `ComponentCompilation`, making it
available to all phases of the template pipeline. Constant extraction is a
common operation in pipeline phases.
PR Close#50118
This commit adds a "shared constant" concept to the `ConstantPool`. This
is a generalization of the `LiteralFactory` concept the pool previously
supported. For stability's sake, the existing concept isn't modified, but
could be unified in the future.
PR Close#50118
This commit introduces a new trait `UsesVarOffset` for expressions which
consume variable slots and thus need an offset into the variable slot space
to locate their slots.
PR Close#50118
The template pipeline implements variadic instruction generation for text
node interpolation using an `InterpolationConfig` concept. This commit
refactors that code to generalize it to work not just with interpolations,
but with all instruction generation for variadic instructions.
PR Close#50118
This commit adds support to the template pipeline to ingest and process
literal array and map expressions. A future phase may process these literal
expressions and memoize them into pure functions where required.
PR Close#50118
Prior to this commit, comments in CSS were being removed. This caused inline sourcemaps to break to the shift in lines.
This caused sourcemaps to break in the ESBuild based builder as this always adds comments at the top of the file with the filename.
Example
```css
/* src/app/app.component.scss */
* {
color: red;
background: transparent;
}
/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8uL3NyYy9hcHAvYXBwLmNvbXBvbmVudC5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBOzs7Ozs7Ozs7Q0FBQTtBQVdBO0VBQ0UsVUFBQTtFQUNBLHVCQUFBO0FBREYiLCJzb3VyY2VzQ29udGVudCI6WyIvL01FRElBIFFVRVJZIE1BTkFHRVJcbi8qXG4gIDAgLSA2MDA6IFBob25lXG4gIDYwMCAtIDkwMDogVGFibGV0IHBvcnRyYWl0XG4gIDkwMCAtIDEyMDA6IFRhYmxldCBsYW5kc2NhcGVcbiAgMTIwMCAtIDE4MDA6IE5vcm1hbCBzdHlsZXNcbiAgMTgwMCsgOiBCaWcgRGVza3RvcFxuICAxZW0gPSAxNnB4XG4gIFRoZSBzbWFsbGVyIGRldmljZSBydWxlcyBhbHdheXMgc2hvdWxkIHdyaXRlIGJlbG93IHRoZSBiaWdnZXIgZGV2aWNlIHJ1bGVzXG4gIEZpeGluZyBPcmRlciA9PiBCYXNlICsgVHlwb2dyYXBoeSA+PiBHZW5lcmFsIExheW91dCArIEdyaWQgPj4gUGFnZSBMYXlvdXQgKyBDb21wb25lbnRcbiovXG5cbioge1xuICBjb2xvcjogcmVkO1xuICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= */
```
Closes#50308
PR Close#50346
According to the HTML specification most attributes are defined as strings, however some can be interpreted as different types like booleans or numbers. [In the HTML standard](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes), boolean attributes are considered `true` if they are present on a DOM node and `false` if they are omitted. Common examples of boolean attributes are `disabled` on interactive elements like `<button>` or `checked` on `<input type="checkbox">`. Another example of an attribute that is defined as a string, but interpreted as a different type is the `value` attribute of `<input type="number">` which logs a warning and ignores the value if it can't be parsed as a number.
Historically, authoring Angular inputs that match the native behavior in a type-safe way has been difficult for developers, because Angular interprets all static attributes as strings. While some recent TypeScript versions made this easier by allowing setters and getters to have different types, supporting this pattern still requires a lot of boilerplate and additional properties to be declared. For example, currently developers have to write something like this to have a `disabled` input that behaves like the native one:
```typescript
import {Directive, Input} from '@angular/core';
@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
@Input()
get disabled() {
return this._disabled;
}
set disabled(value: any) {
this._disabled = typeof value === 'boolean' ? value : (value != null && value !== 'false');
}
private _disabled = false;
}
```
This feature aims to address the issue by introducing a `transform` property on inputs. If an input has a `transform` function, any values set through the template will be passed through the function before being assigned to the directive instance. The example from above can be rewritten to the following:
```typescript
import {Directive, Input, booleanAttribute} from '@angular/core';
@Directive({selector: 'mat-checkbox'})
export class MatCheckbox {
@Input({transform: booleanAttribute}) disabled: boolean = false;
}
```
These changes also add the `booleanAttribute` and `numberAttribute` utilities to `@angular/core` since they're common enough to be useful for most projects.
Fixes#8968.
Fixes#14761.
PR Close#50420
Adds the necessary compiler changes to support input transform functions. The compiler output has changed in the following ways:
### Directive handler
The directive handler now extracts a reference to the input transform function and it resolves the type of its first parameter. It also asserts that the type can be referenced in the compiled output and that it doesn't clash with any pre-existing `ngAcceptInputType_` members.
### .d.ts
In the generated declaration files the compiler now inserts an `ngAcceptInputType_` member for each input with a `transform` function. The member's type corresponds to the type of the first parameter of the function, e.g.
```typescript
// foo.directive.ts
@Directive()
export class Foo {
@Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}
// foo.directive.d.ts
export class Foo {
value: number;
static ngAcceptInputType_value: string;
}
```
### Type check block
If an input has `transform` function, the TCB will use the type of its first parameter for the setter type. This uses the same infrastructure as the `ngAcceptInputType_` members.
### Directive declaration
The generated runtime directive declaration call now includes the `transform` function in the `inputs` map, if the input is being transformed. The function will be picked up by the runtime in the next commit to do the actual transformation.
```typescript
// foo.directive.ts
@Directive()
export class Foo {
@Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}
// foo.directive.js
export class Foo {
ɵdir = ɵɵdefineDirective({
inputs: {
value: ['value', 'value', incomingValue => parseInt(incomingValue)]
}
});
}
```
PR Close#50225
Adds a new AST for a `TransplantedType` in the compiler which will be used for some upcoming work. A transplanted type is a type node that is defined in one place in the app, but needs to be copied to a different one (e.g. the generated .d.ts). These changes also include updates to the type translator that will rewrite any type references within the type to point to the new context file.
PR Close#50104
Saving and restoring the view is significant enough that it makes sense to handle it independently. This makes for easier reasoning about how view save/restore works.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
PR Close#50008
When ingesting an `ng-template`, inputs might be on the `inputs` or the `templateAttrs` field. More investigation is required to pinpoint the specifics of `templateAttrs`.
For now, we can process them both and generate the appropriate update-mode property instructions.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
PR Close#50008
It's possible to have chains of statements, exclusively in event listeners. A listener with a chain looks like the following:
```
(click)="onClick($event); 1 == 1"
```
We handle this by generating multiple statements, one for each expression in the chain, and only making the final statement the return statement. We place this logic in code specific to listeners, since they are the only place this construct can appear.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
PR Close#50008
We should be able to ingest binary operators. This involves parsing the left and right ASTs, and converting the operator string to a logical `BinaryOperator`.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
PR Close#50008
ElementContainer instructions refer to `ng-container` element tags, which don't produce corresponding DOM nodes. Much like element instructions, container instructions can also have their start and end tags combined.
Co-authored-by: Alex Rickabaugh <alxhub@users.noreply.github.com>
Co-authored-by: Andrew Scott <atscott@users.noreply.github.com>
PR Close#50008
This commit adds the `signals: boolean` property to the internal
directive/component metadata. This does not add it to the public API
yet, as the feature has no internal support other than compiler
detection.
PR Close#49981
This commit adds a phase to the template pipeline to merge `nextContext()`
instructions that follow each other without context reads in between. That
is, the sequence:
```typescript
nextContext();
var v1 = nextContext();
```
becomes:
```typescript
var v1 = nextContext(2);
```
PR Close#49797
This commit modifies the `ListenerOp` operation to capture the context
needed to generate the "correct" (per `TemplateDefinitionBuilder`) function
name for the handler function for the listener.
PR Close#49797
The `resetView` instruction in the template pipeline had a copy-paste error
where it was emitting a call to `reference` instead. This commit fixes the
issue.
PR Close#49797
This commit adds a chaining phase which post-processes reified template
pipeline operations, and collapses chainable instructions into chained
calls. Performing chaining as a post-processing step after reification
allows the specifically selected instruction variants to be known when
considering chaining two operations.
PR Close#49797
This commit adds a variable optimization pass to the template pipeline. The
pipeline generates all variables which might be referenced within a given
view's template function, regardless of whether other operations will read
those values.
The variable optimizer post-processes the IR and performs several variable-
related optimizations:
* It transforms variable declarations to side effectful expressions when the
variable is not used, but its initializer has global effects which other
operations rely upon.
* It removes variable declarations if those variables are not referenced and
either they do not have global effects, or nothing relies on them.
* It inlines variable declarations when those variables are only used once
and the inlining is semantically safe.
PR Close#49797
This commit teaches the template pipeline how to generate `textInterpolate`
when there's a single expression with no surrounding static text.
PR Close#49797
This commit reverses the generate template functions when adding them to the
constant pool in the template pipeline. This seems to better match the
ordering in which `TemplateDefinitionBuilder` generates template functions.
Further study is needed to determine if this is exactly accurate.
PR Close#49797
`advance()` was not emitted correctly by the template pipeline. There were
two problems:
* it was not handled in `transformExpressionsInOp()`.
* it was not added to the list correctly in `phaseGenerateAdvance()`.
This commit addresses both problems.
PR Close#49797
This commit fixes a broken assertion in the template pipeline concerning the
ownership of nodes in `insertBefore`, as well as adjusts a few other
assertions.
PR Close#49797
The template pipeline previously included an error when a static attribute
binding was found on an `<ng-template>`, under the assumption that this case
didn't happen in reality. It turns out that it does, so this commit removes
the error in favor of a comment to investigate the case further.
PR Close#49797
The template pipeline previously reified the parameters to `template()`
incorrectly. This commit adjusts the output to correctly reference the
attributes of the `template()` call.
PR Close#49797
This commit introduces a flag which is tracked while visiting expression
nodes in the template pipeline. This flag can be used to differentiate when
in an immediate evaluation context vs. a closure, which is useful for
certain operations.
PR Close#49797
The `TemplateDefinitionBuilder` has a specific pattern it uses for template
function names for embedded view template functions. This commit changes the
template pipeline to use the same format, allowing the generated code to
match between them.
PR Close#49797
The `TemplateDefinitionBuilder` uses the same name for the same semantic
variables across different views declared in a component. This commit
refactors the template pipeline's concept of `ir.SemanticVariable` to share
instances across all `ViewCompilation`s. This allows the `name` of the
variable to be stored on the `ir.SemanticVariable` instance instead of on
the `ir.VariableOp` (which makes sense as variables are often named based on
the `ir.SemanticVariable` anyway).
PR Close#49797
The template pipeline was previously reading the context of the wrong view,
resulting in incorrect generated code. Previously only `ctx` was being used,
since the context read was always that of the current view being compiled,
even for variables which exist on the contexts of parent views.
PR Close#49797
The template pipeline was emitting fields on the component definition in a
different order than the `TemplateDefinitionBuilder`, which causes test
failures. Additionally, the `consts` field was being emitted even if it was
empty.
PR Close#49797
Fixes that the compiler was matching directives based on `attr` bindings which doesn't correspond to the runtime behavior. This wasn't a problem until now because the matched directives would basically be a noop, but they can cause issues with required inputs.
PR Close#49713
This commit adds a Bazel flag which controls a constant in the compiler code.
When this flag (`--//packages/compiler:use_template_pipeline`) is specified, the
prototype template pipeline code is enabled.
This is not used in any production workflows and only works in the local Angular
repository. It will be used to develop the template pipeline against the
existing compliance tests.
PR Close#48580
This commit adds `transformTemplate` and `emitTemplateFn()` to the template
pipeline. These operations respectively apply all template compilation phases
in the right order, and then generate a final template function AST from the
template IR.
PR Close#48580
This commit introduces `phaseReify()` which performs "reification" of IR
operations. This converts previously semantic operations (e.g. `ir.Element`)
into generated instruction calls (`element(...)`). At the end of reification,
all operations have been converted into `ir.StatementOp`s, and the IR AST should
be suitable for emitting as generated code.
Note that after reification, more transformations may still be applied. For
example, instruction chaining will be performed post-reification, since it
depends on the exact selected instructions for each operation.
PR Close#48580