Refactors the `ɵɵcontrolCreate` and `ɵɵcontrol` instructions to delegate control logic to the forms package via new `ɵngControlCreate` and `ɵngControlUpdate` lifecycle hooks. Previously, the logic for binding form state to native elements and custom controls was hardcoded within `@angular/core`.
**Compiler Changes:**
- Introduces a new compilation phase `specializeControlProperties` (in `control_directives.ts`).
- This phase detects properties named `formField` and specializes them into `ControlCreate` and `Control` IR opcodes.
- These opcodes emit `ɵɵcontrolCreate` and `ɵɵcontrol` instructions, respectively.
**Runtime Changes:**
- `ɵɵcontrolCreate` acts as the creation phase. It locates the control directive and invokes its `ɵngControlCreate` method.
- `ɵɵcontrol` acts as the update phase, and invokes the control directive's `ɵngControlUpdate` method (if present).
- Introduces a `passThroughInput` configuration in `ControlFeature`. This specifies the input name (e.g., `formField`) that triggers the control. If the runtime detects that this input is bound to multiple targets (e.g., the `FormField` directive *and* the host component), the control is flagged as "pass-through". In this state, `ɵngControlCreate` returns a no-op update function, deferring responsibility to the other consumer (e.g., the component managing the field itself).
**Forms Changes:**
- `FormField` directive implements `ɵngControlCreate` and `ɵngControlUpdate`.
- Inside this hook, `FormField` determines the type of control it is attached to (Native, CVA, or Custom Signal Control) and delegates to the appropriate handler (`nativeControlCreate`, `cvaControlCreate`, or `customControlCreate`).
- Consolidates all form binding logic within `@angular/forms/signals`, enabling support for new `FormValueControl` and `FormCheckboxControl` interfaces.
- Reorganizes the codebase by moving `FormField` from `api/` to `directive/` and splitting the binding logic into semantic pieces:
- `control_native.ts`, `control_cva.ts`, and `control_custom.ts` contain the specific handlers for each control type.
- `native.ts` and `select.ts` provide helpers for native element discovery and select-specific synchronization.
- `bindings.ts` manages the tracking and application of property/attribute bindings.
The `fullInheritane` flag from the metadata and the `CopyDefinitionFeature` that it controls appear to no longer be used since `fullInheritance` is always false. The feature appears to have been there to support ngcc which was removed some time ago.
Adds a field to the directive's metadata tracking whether it has directive dependencies. Knowing this will allow the pipeline to decide whether to produce DOM-only or full instructions.
PR Close#62096
Similarly to the previous change to the expression AST, these changes replace the `WriteVarExpr`, `WriteKeyExpr` and `WritePropExpr` from the output AST with a binary expression. This is closer aligned to TypeScript and makes it easier to translate code between the two.
PR Close#61682
This commit migrates the remaining pieces of `compiler-cli` to
`ts_project`. This involves a few more things during migration:
- the `ng_module` ngc_wrapped rule broke as part of this change, so we
switched it to `ts_project` too. This logic is soon gone anyway.
- we needed an extra pnpm "package.json" for the linker babel test. This test is
loading from the real compiler-cli npm package. Babel needs a real
node module for this, so this solution seems reasonable. It may be
worth exploring in the future to move this test into an integration
test though.
- the older integrationtest in compiler-cli is removed as the coverage
is much better with the compliance test suite and this test.
PR Close#61826
This commit removes the direct dependency on TypeScript within the linker, addressing a performance overhead that was adding between 500ms to 1s to compilation times for applications.
The primary cause of this overhead was the linker's direct reliance on TypeScript's which was caused by importing from barrel files. While convenient, barrel files are detrimental to code splitting and code motion. They force the bundling of all exported modules, even if only a subset is actually used.
By removing the usage of this barrel file and restructuring the imports to be more granular, we can avoid unnecessary TypeScript imports.
Furthermore, TypeScript has now been changed to an optional peer dependency as using only the linker does not require TypeScript.
PR Close#61618
Currently to create an `R3TargetBinder`, we have to pass some sort of directive matcher, however we also have a couple of use cases where we use the binder to do analysis that's unrelated to directives (e.g. resolving the `@defer` blocks). In these cases having to create a dummy matcher adds some slight overhead and makes the code harder to reason about since it looks like directive matching may be happening.
These changes update the `R3TargetBinder` to allow for `null` to be passed as the directive matcher.
PR Close#61018
Add support for the `void` operator in templates and host bindings.
This is useful when binding a listener that may return `false` and
unintentionally prevent the default event behavior.
Ex:
```
@Directive({
host: { '(mousedown)': 'void handleMousedown()' }
})
```
BREAKING CHANGE: `void` in an expression now refers to the operator
Previously an expression in the template like `{{void}}` referred to a
property on the component class. After this change it now refers to the
`void` operator, which would make the above example invalid. If you have
existing expressions that need to refer to a property named `void`,
change the expression to use `this.void` instead: `{{this.void}}`.
PR Close#59894
When setting `"useDefineForClassFields": false`, static fields are compiled within a block that relies on the `this` context. This output makes it more difficult for bundlers to treeshake and eliminate unused code.
PR Close#58297
The compiler's AST factories now support generating a dynamic import call
expression with either a string literal or an expression. The later is useful
for cases where the URL is dynamically created at runtime. Also, a leading
comment can now be added to the URL for cases where bundler behavior
needs to be included via special comments.
PR Close#58173
Enables the new `@let` syntax by default.
`@let` declarations are defined as:
1. The `@let` keyword.
2. Followed by one or more whitespaces.
3. Followed by a valid JavaScript name and zero or more whitespaces.
4. Followed by the `=` symbol and zero or more whitespaces.
5. Followed by an Angular expression which can be multi-line.
6. Terminated by the `;` symbol.
Example usage:
```
@let user = user$ | async;
@let greeting = user ? 'Hello, ' + user.name : 'Loading';
<h1>{{greeting}}</h1>
```
Fixes#15280.
PR Close#56715
These changes integrate let declarations into the template pipeline. This involves a few operations:
* Producing a `declareLet` instruction call at creation time to initialize the declaration.
* Producing a `storeLet` instruction call in the place of the let declaration, including the necessary `advance` calls beforehand.
* For let declarations used within their declaration view, moving the `const` to be placed right after the `storeLet` call to ensure the their value has been computed.
* For let declarations that are _only_ used in their declaration view, removing the `storeLet` call and inlining the expression into the constant statement.
PR Close#56299
The linker inserts the constant statements that are needed to support compiled templates
after the import statements of an ESM file, but it failed to account for import statements
that are not at the top of the file. This is typically seen in FESM files where multiple
individual ESMs have been concatenated into a single ESM file, with imports in various places.
The linker would then find the very last import statement to insert the constant statements
after, but this may result in TDZ errors for component templates that have been emitted
earlier in the file.
This commit updates the Babel linker plugin to insert constant statements after the last
import of the first import group, therefore avoiding the TDZ error.
Fixes#56403
PR Close#56431
Builds on top of the previous changes to add support for deferred blocks during partial compilation. To do this, the following changes had to be made:
* The metadata passed into `ɵɵngDeclareComponent` has an additional field called `deferBlockDependencies` which has an array of the dependency loading functions for each defer block in the template. During linking, the dependency functions are loaded by matching their template index to the index in the `deferBlockDependencies` array.
* There's a new `ɵɵngDeclareClassMetadataAsync` function that is created for components that have deferred dependencies. It gets transpiled to `setClassMetadataAsync` and works in the same way by capturing a dependency loading function and setting the metadata after the dependencies are resolved. It also has some extra fields for capturing the version which are standard in linker-generated code.
* Deferred import statements are now stripped in partial compilation mode, similar to full compilation.
PR Close#54908
Updates the type of the resolver function to be any `Expression` since JIT may receive a function reference rather than a `ArrowFunctionExpr`.
PR Close#54908
`ImportGenerator` is the abstraction used by the translator functions to
insert imports for `ExternalExpr` in an AST-agnostic way.
This was built specifically for the linker which does not use any of the
complex import managers- but rather re-uses `ngImport` or uses
`ngImport.Bla`.
This commit also switches the linker AST-agnostic generator to follow
the new signatures. This was rather trivial.
PR Close#54819
Moves the logic that creates the defer resolver function into `@angular/compiler` for consistency with the rest of the compilation APIs. Also renames some of the symbols to make it clearer what they're used for.
PR Close#54759
Currently we have the `deferrableDeclToImportDecl`, `deferBlocks`, `deferrableTypes` and `deferBlockDepsEmitMode` fields on the `R3ComponentMetadata` which is incorrect, because the interface is used both for JIT and AOT mode even though the information for those fields is AOT-specific. It will be problematic for partial compilation since the runtime will have a reference to the dependency loading function, but will not be able to provide any of the other information.
These changes make the following refactors:
1. It changes the defer-related information in `R3ComponentMetadata` to include only references to dependency functions which can be provided both in JIT and AOT.
2. Moves the AOT-specific defer analysis into the `ComponentResolutionData`.
3. Moves the construction the defer dependency function into the compilation phase of the `ComponentDecoratorHandler`.
4. Drops support for defer blocks from the `TemplateDefinitionBuilder`. This allows us to clean up some TDB-specific code and shouldn't have an effect on users since the TDB isn't used anymore.
PR Close#54759