Narrows down the versions of TypeScript we need to support.
BREAKING CHANGE:
* TypeScript versions less than 5.8 are no longer supported.
PR Close#60197
This commit fixes a bug where nodes are reused across incremental
compilations. The source file's next template ID is lost because a new
source file is created but nodes may still be retained.
PR Close#60152
Instead of using a property on BinaryOperatorExpr / UnaryOperatorExpr,
introduce a ParenthesizedExpr which can be used to parenthesize any
expression.
PR Close#60127
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
The CLI uses the `ts.transform` API to apply the Angular compiler's transformers
on the source files when `isolatedModules` is true (and various other constraints)
instead of going through a full `ts.Program.emit` operation. This results in the
transformers to operate in an environment where no emit resolver is available, which
was not previously accounted for. This commit reflects the possibility for the emit
resolver to be missing and handles this scenario accordingly.
Fixes#59837
PR Close#59869
Currently when we generate the tracking expression for a `@for` block, we process its expression in the context of the creation block. This is incorrect, because the expression may require ops of its own for cases like nullish coalescing or safe reads. The result is that while we do generate the correct variable, they're added to the creation block rather than the tracking function which causes an error at runtime.
These changes address the issue by keeping track of a separate set of ops for the `track` expression that are prepended to the generated function, similarly to how we handle event listeners.
Fixes#56256.
PR Close#58520
When the compiler analyzes the defer blocks in a component, it generates two sets of dependencies: ones specific for each block and others from all the deferred blocks within the component. The logic that combines all the defer block dependencies wasn't de-duplicating them which resulted in us producing `setClassMetadataAsync` calls where the callback can have multiple parameters with the same name. This was a problem both in full and partial compilation, but the latter was more visible, because Babel throws an error in such cases.
These changes add some logic to de-duplicate the dependencies so that we produce valid code.
Fixes#59922.
PR Close#59926
Adjusts the code we generate for HMR so that it passes in the HMR ID and `import.meta` to the `replaceMetadata` call. This is necessary so we can do better logging of errors.
PR Close#59854
When we generate an HMR replacement function, we determine which locals from the file are used and we pass them by reference. This works fine in most cases, but breaks down for const enums which don't have a runtime representation.
These changes work around the issue by passing in all the values as an object literal.
Fixes#59800.
PR Close#59815
Fixes that the partial evaluator was interpreting initializer-less enum members as undefined. In this case the value is the same as the index.
PR Close#59815
Makes the following cleanups in the output AST:
* The `TemplateLiteral` and `TemplateLiteralElement` nodes have been renamed to `TemplateLiteralExpr` and `TemplateLiteralElementExpr` respectively for consistency and to avoid overlaps with the expression AST nodes.
* The `TemplateLiteralExpr` and `TemplateLiteralElementExpr` have been refactored to be `Expression`s for correctness. This involves updating some existing code.
* The `TaggedTemplateExpr` has been renamed to `TaggedTemplateLiteralExpr` for consistency.
PR Close#59230
Updates the HMR dependencies extraction logic to handle conditional expressions.
For example, `providers: [condition ? providersA : providersB]`.
PR Close#59637
Fixes that we were filtering out the component itself from the set of dependencies when HMR is enabled, breaking self-referencing components.
Fixes#59632.
PR Close#59644
When HMR is enabled, we need to capture the dependencies used in a template and forward them to the HMR replacement function. One half of this process is static, meaning that we can't change it after the initial compilation. Tree shaking becomes a problem in such a case, because the user can change the template in a way that changes the set of dependencies which will start matching with the static part of the HMR code.
These changes disable the tree shaking when HMR is enabled to ensure that the dependencies stay stable.
Fixes#59581.
PR Close#59595
Adds a new diagnostic that ensures that a standalone component using custom structural directives in a template has the necessary imports for those directives.
Fixes#37322
PR Close#59443
The URL that is dynamically imported to fetch a potential component update
for HMR is now based on the value of `import.meta.url`. This ensures that
the request is sent to the same location that was used to retrieve the
application code. For some development server setups the HTML base HREF
may not be the location of the Angular development server. By using the
application code location which was generated by the development server,
HMR requests can continue to work as expected in these scenarios. In
most common cases, this change will not have any effect as the HTML base
HREF aligns with the location of the application code files.
PR Close#59459
Fixes that the HMR dependency extraction logic wasn't handling some node types. Most of these are a bit edge-case-ey in component definitions, but variable initializers and arrow functions can realistically happen in factories.
PR Close#59445
in order for the docs to process function entry, this commit refactor function extraction by keeping the implementation as a the default entry and adds all the overloads into a separate array of entries.
fixes#56144
PR Close#56489
Fixes that the compiler wasn't capturing defer block dependencies correctly when `supportTestBed` is disabled. We had tests for this, but we didn't notice the issue because the dependencies ended up being captured because of the `setClassMetadata` calls. Once they're disabled, the dependencies stopped being recorded.
Fixes#59310.
PR Close#59313
During the HMR dependency analysis we need to check if an identifier is top-level or not. We do this by looking at each identifier and its parent, however we didn't account for some cases. These changes expand our logic to cover more of the common node types.
Related to https://github.com/angular/angular/issues/59310#issuecomment-2563963501.
PR Close#59323
Some time ago we narrowed down the expressions we support in two-way bindings, because in most cases any apart from property reads doesn't make sense. This ended up preventing users from using `$any` in the binding since it's considered a function call.
These changes update the validation logic to allow `$any`.
Fixes#51165.
PR Close#59362
We recently introduced a custom error to allow us to catch certain types
of errors. Unfortunately it doesn't work as expected in G3 because the
Node execution seems to run with ES5.
PR Close#59219
Fixes a null pointer error in the unused standalone imports diagnostic. It was caused by an inconsistency in TypeScript's built-in types.
Fixes#58872.
PR Close#59064
In the past two-way bindings used to be interpreted as `foo = $event` at the parser level. In #54065 it was changed to preserve the actual expression, because it was problematic for supporting two-way binding to signals. This unintentionally ended up causing the TCB to two-way bindings to look something like `someOutput.subscribe($event => expr);` which does nothing. It largely hasn't been a problem, because the input side of two-way bindings was still being checked, except for the case where the input side of the two-way binding has a wider type than the output side.
These changes re-add type checking for the output side by generating the following TCB instead:
```
someOutput.subscribe($event => {
var _t1 = unwrapSignalValue(this.someField);
_t1 = $event;
});
```
PR Close#59002
The `isSplitTwoWayBinding` function was a bit misleading, because it has side effects. Renames the function make it a bit more explicit.
PR Close#59002
The current HMR compiler assumes that there will only be one namespace import in the generated code (`@angular/core`). This is incorrect, because the compiler may need to generate additional imports in some cases (e.g. importing directives through a module). These changes adjust the compiler to capture all the namespaces in an array and pass them along.
Fixes#58915.
PR Close#58924
Currently host bindings are in a bit of a weird state, because their source spans all point to the root object literal, rather than the individual expression. This is tricky to handle at the moment, because the object is being passed around as a `Record<string, string>` since the compiler needs to support both JIT and non-JIT environments, and because the AOT compiler evaluates the entire literal rather than doing it expression-by-expression. As a result, when we report errors in one of the host bindings, we end up highlighting the entire expression which can be very noisy in an IDE.
These changes aim to report a more accurate error for the most common case where the `host` object is initialized to a `string -> string` object literal by matching the failing expression to one of the property initializers. Note that this isn't 100% reliable, because we can't map cases like `host: SOME_CONST`, but it's still better than the current setup.
PR Close#58870
This fixes an issue where the lazy-routes migration would crash for component classes a
decorator without arguments in front of the `@Component` decorator (in particular, it needed
to be the first decorator).
Fixes#58793
PR Close#58796
This commit is only useful to Google. It fixes that some code relies on
`readConfiguration`, but doesn't properly parse Angular compiler options
as those are part of `bazelOptions.angularCompilerOptions` if the
1P-generated tsconfig's are used.
PR Close#58637
Currently the `getPotentialImportsFor` only accepts a `ClassDeclaration` as the context for generating an import, but that's not necessary because it doesn't require any class-specific information. These changes expand it to any `Node` so that it can be used when generating imports in testing declarations.
PR Close#58627
Fixes an edge case where a control flow node that has non-projectable nodes followed by an element node at the end would cause the entire control flow node to be project. For example if we have a projection target of `Main: <ng-content/> Slot: <ng-content select="[foo]"/>`, inserting a node of `@if (true) {Hello <span foo>world</span>}` would project the entire `Hello world` into the `[foo]` slot.
In the process of working on the issue, I also found that `@let` declarations at the root of the control flow node would prevent content projection as well.
PR Close#58607
Initially the unused imports check was implemented so that it reports one diagnostic per component with the individual unused imports being highlighted through the `relatedInformation`. This works fine when reporting errors to the command line, but vscode appears to only show `relatedInformation` when the user hovers over a diagnostic which is a sub-par experience.
These changes switch to reporting a diagnostic for each unused import instead.
PR Close#58589
Previously we always ran Tsurge migrations with an Angular program, even
if it's a plain `ts_library` target. This has changed now, so we also
need to properly handle the case where a `ts_library` is analyzed, but
no Angular program is available.
PR Close#58541
Previously, `filterMethodOverloads` excluded all members without a body, causing issues with the extraction of functions and members in TypeScript types.
PR Close#58445
We were not properly passing around the TCB full program optimization,
so TCB generation was done per individual file. This significantly
slowed down reference resolution.
PR Close#58525
Currently when application source code references e.g. an NgModule that
points to references that aren't available, the compiler will break at
runtime without any actionable/reasonable error.
This could happen for example when a library is referenced in code, but
the library is simply not available in the `node_modules`. Clearly,
TypeScript would issue import diagnostics here, but the Angular compiler
shouldn't break fatally. This is useful for migrations which may run
against projects which aren't fully compilable. The compiler handles
this fine in all cases, except when processing `.d.ts` currently... and
the runtime exception invalides all other information of the program
etc.
This commit fixes this by degrading such unexpected cases for `.d.ts`
metadata reading to be handled gracefully. This matches existing logic
where the `.d.ts` doesn't necessarily match the "expecation"/"expected
format".
The worst case is that the Angular compiler will not have type
information for some directives of e.g. a library that just isn't
installed in local `node_modules`; compared to magical errors and
unexpected runtime behavior.
PR Close#58515
Adjusts the HMR initialization to avoid the edge case where a developer makes change to a non-rendered component that exists in a lazy loaded chunk that has not been loaded yet. The changes include:
* Moving the `import` statement out into a separate function.
* Adding a null check for `d.default` before calling `replaceMEtadata`.
* Triggering the `import` callback eagerly on initialization.
Example of the new generated code:
```js
(() => {
function Cmp_HmrLoad(t) {
import(
/* @vite-ignore */ "/@ng/component?c=test.ts%40Cmp&t=" + encodeURIComponent(t)
).then((m) => m.default && i0.ɵɵreplaceMetadata(Cmp, m.default, [/* Dependencies go here */]));
}
(typeof ngDevMode === "undefined" || ngDevMode) && Cmp_HmrLoad(Date.now());
(typeof ngDevMode === "undefined" || ngDevMode) &&
import.meta.hot &&
import.meta.hot.on("angular:component-update", (d) => {
if (d.id === "test.ts%40Cmp") {
Cmp_HmrLoad(d.timestamp);
}
});
})();
```
PR Close#58465
The unused imports diagnostic reports once on the entire initializer and then again once per unused imports. This ends up being a bit hard to follow, because in a lot of cases the code snippet looks identical.
These changes switch to highlighting the `imports:` part of the property declaration and only highlighting the unused imports without a message.
PR Close#58468
Disables the standalone by default behavior in the compiler when running against and older version of Angular. This is necessary, because the language service may be using the latest version of the compiler against and older version of core in a particular workspace.
PR Close#58405
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
By removing the standalone feature, we reduce the amount of code generated for components but at the cost of including the `StandaloneService` in the main bundle even if no standalone components are included in it.
PR Close#58288
Reports a diagnostic if an NgModule refers to itself in its `imports` or `exports`. Previously the compiler would go into an infinite loop.
Fixes#58224.
PR Close#58231
Prior to this commit, each abstract method that was overloaded was extracted. With this commit it will be extracted only once. Every overload was and still will be supported by the signatures.
fixes#57693
PR Close#57707
Adds the ability to generate the function that replaces the component's metadata during HMR. The HMR update module is a function that is loaded dynamically and as such it has some special considerations:
* It isn't bundled, because doing so will result in multiple version of core.
* Since it isn't bundled, all dependencies have to be passed in as parameters. These changes include some special logic to determine and output those dependencies.
* While HMR is enabled, we have to disable the functionality that generates dynamic imports and drop the dependencies inside `@defer` blocks, because we need to retain the ability to refer to them in case they're needed inside the HMR update function.
* The function is returned by the `NgCompiler` as a string for the CLI's sake.
PR Close#58205
Currently only the prefix of namespace imports is configurable, but for HMR we need to know ahead of time what the name of `@angular/core` will be. These changes allow us to rewrite it.
PR Close#58205
While effective, `preservePlaceholders` unfortunately is not viable in google3 at the moment due to some complexities with how TC extracts messages. Therefore this feature is being removed in favor of whitespace trimming of expressions, which is viable for TC and provides most of the same benefit.
This is a partial revert of dab722f9c8.
PR Close#58176
With this commit directives, components & pipes are standalone by default.
To be declared in an `NgModule`, those require now `standalone: false`.
PR Close#58169
This commit is part of the migration to standalone by default and sets up 2 files with a default value for standalone. They are still `false` in this case to land the change into G3 first. The switch to `true` will be executed in a follow-up PR.
PR Close#58175
For the HMR initializer block to support being used in a Vite setup with
import analysis, the import call expression needs to be a runtime generated
value and include the `@vite-ignore` special comment. Without the first,
Vite will error prior to loading the application. Without the second, a
warning will be shown for each import which is effectively each component
within the application when HMR is enabled.
PR Close#58173
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
We're using `path.relative` to compute a relative path between a `SourceFile` and the one of the `rootDirs`. The problem is that the `rootDirs` get passed through `getCanonicalFileName` which lowercases the path in some platforms, while `SourceFile.fileName` is always case-insensitive. This yields a path outside of the project which we were ignoring.
This change passes the `SourceFile.fileName` before passing it through `path.relative` to ensure that we get a valid result.
PR Close#58150
Consider a template with a context variable `a`:
```
<ng-template let-a>{{this.a}}</ng-template>
```
t push -fAn interpolation inside that template to `this.a` should intuitively read the class variable `a`. However, today, it refers to the context variable `a`, both in the TCB and the generated code.
In this commit, the above interpolation now refers to the class field `a`.
BREAKING CHANGE: `this.foo` property reads no longer refer to template context variables. If you intended to read the template variable, do not use `this.`.
Fixes#55115
PR Close#55183
Whenever information is requested from the template checker right now,
the shim is only ensured to be generated for the single file/component.
This is slow in migrations where we don't want to collect diagnostics,
but rather request information from the component state.
This commit supports `OptimizeFor` in `checker#getTemplate`.
PR Close#58106
Currently we don't defer any symbols that have references outside of the `import` statement and the `imports` array. This is a bit too aggressive, because it's possible that the symbol is only used for types (e.g. `viewChild<SomeCmp>('ref')`) which will be stripped when emitting to JS.
These changes expand the logic so that references inside type nodes aren't considered.
**Note:** one special case is when the symbol used in constructor-based DI (e.g. `constructor(someCmp: SomeCmp)`, because these constructors will be compiled to `directiveInject` calls. We don't need to worry about them, because the compiler introduces an addition `import * as i1 from './some-cmp';` import that it uses to refer to the symbol.
Fixes#55991.
PR Close#58104
To ensure that the external runtime style component feature is correctly
emitted by the Angular compiler, compliance tests have been added for
file-based component styles. Additionally, the partial golden generator
has been updated to work with file-based component styles.
PR Close#57613
To provide support for HMR of inline component styles (`styles` decorator field), the AOT
compiler will now use the resource host transformation API with the Angular CLI to provide
external runtime stylesheet URLs when the `externalRuntimeStyles` compiler option is enabled.
This allows both a component's file-based and inline styles to be available for HMR when used
with a compatible development server such as with the Angular CLI. No behavioral change is
present if the `externalRuntimeStyles` option is not enabled or the resource host transformation
API is not used.
An `order` numeric field is also added to the transformation API which allows consumers such as
the Angular CLI to create identifiers for each inline style in a specific containing file.
PR Close#57613
The AOT compiler now has the capability to handle component stylesheet files as
external runtime files. External runtime files are stylesheets that are not embedded
within the component code at build time. Instead a URL path is emitted within a component's
metadata. When combined with separate updates to the shared style host and DOM renderer,
this will allow these stylesheet files to be fetched and processed by a development
server on-demand. This behavior is controlled by an internal compiler option `externalRuntimeStyles`.
The Angular CLI development server will also be updated to provide the serving functionality
once this capability is enabled. This capability enables upcoming features such as automatic
component style hot module replacement (HMR) and development server deferred stylesheet processing.
The current implementation does not affect the behavior of inline styles. Only the
behavior of stylesheet files referenced via component properties `styleUrl`/`styleUrls`
and relative template `link` elements are changed by enabling the internal option.
PR Close#57613
In order to investigate the performances of SSR, this commit introduces a benchmark suite which will measure several step of the rendering.
PR Close#57647
Some apps follow a pattern where they have an array of common declarations which is imported in most standalone components, but only some of the declarations are used. Such cases will currently raise the unused imports diagnostic but can be hard to fix, because it would require either removing declarations from the common array which can break other components, or copying only the necessary declarations from the array. Since neither of these solutions is great, this commit tweaks the logic for the diagnostic so that unused imports coming from _exported_ arrays are not reported (either from the same file or another one).
PR Close#57940
Add the `strictStandalone` flag to `angularCompilerOptions`. When set to
true, the compiler will require that all declarations of components,
directive, and pipes be standalone. When `standalone: false` is provided,
an error is raised.
Note that until the default value of the standalone flag is flipped, this
does not catch the case where a declaration does not specify a value for
`standalone`.
The default value of the `strictStandalone` flag is `false`.
PR Close#57935
The compiler and its file system implementation expects `fs.exists` to
return `true` even for directories. This caused issues with the TSConfig
resolution as `/` was looked up.
We fix this by making use of `stat` which is equally expensive to
`tree.exists`. The devkit tree's don't expose directory existance checks
out of the box.
Fixes#57887.
PR Close#57897
The visitor that all extended diagnostics are based on hadn't implemented the `visitIcu` method which meant that it wasn't detecting any code inside of them.
Fixes#57838.
PR Close#57845
Finalizes compiler implementation of the new `hydrate` triggers by:
* Reworking the logic that was depending on the `hydrateSpan` to distinguish hydrate triggers from non-hydrate triggers.
* Fixing that the `hydrate when` trigger didn't have a `hydrateSpan`.
* Adding an error if a parameter is passed into a `hydrate` trigger.
* Add an error if other `hydrate` triggers are used with `hydrate never`.
* Replacing the `prefetch` and `hydrate` flags in the template pipeline with a `modifiers` field.
* Fixing an error that was being thrown when reifying `hydrate` triggers in the pipeline.
* Adding quick info support for the `hydrate` keyword in the language service.
* Adding some tests for the new logic.
PR Close#57831
Whenever the `ngc` binary is used directly to parse configurations, we
should try to respect the configured file system like we do in all other
places. Right now one spot where we escape the FS is for reading
directories to e.g. support tsconfig#includes.
This commit fixes this, implementing TypeScript's read directory method
leveraging the configured FS. The approach taken here was used for a
couple of months/years for Angular Material migrations and no issues
were found.
PR Close#57805
Adds a new diagnostic that will report cases where a declaration is in the `imports` array, but isn't being used anywhere. The diagnostic is reported as a warning by default and can be controlled using the following option in the tsconfig:
```
{
"angularCompilerOptions": {
"extendedDiagnostics": {
"checks": {
"unusedStandaloneImports": "suppress"
}
}
}
}
```
**Note:** I'll look into a codefix for the language service in a follow-up.
Fixes#46766.
PR Close#57605
This commit changes the structure of the API extraction files to include all symbols used inside a package.
The structure is a `Map`, Symbol => package
eg: 'ApplicationRef' => '@angular/core'
PR Close#57346
in order for the docs to process function entry, this commit refactor function extraction by keeping the implementation as a the default entry and adds all the overloads into a separate array of entries.
PR Close#56489
This configures whether or not to preserve whitespace content when extracting messages from Angular templates in the legacy (View Engine) extraction pipeline.
This includes several bug fixes which unfortunately cannot be landed without changing message IDs in a breaking fashion and are necessary to properly trim whitespace. Instead these bug fixes are included only when the new flag is disabled.
PR Close#56507
Previously, the component handler was processing and resolving stylesheets
referenced via `styleUrl`/`styleUrls` multiple times when generating the
compiler metadata for components. The style resource information collection
for such styles has been further consolidated to avoid repeat resource loader
resolve calls which potentially could be expensive. Further optimization is
possible for the inline style case. However, inline styles here only require
AST traversal and no potentially expensive external resolve calls.
PR Close#57502
Previously, style elements within a template were used directly and not provided
to the optional transformation step that may be present on the resource host interface.
This causes such styles to not be processed by the Angular CLI's stylesheet pipeline
and could cause the styles to not work properly on all browsers. The style elements
are now processed in the same manner as inline styles within a component's metadata.
Link elements within a stylesheet were already being processed as `styleUrls`
equivalent and there is no behavior change in that regard.
PR Close#57429
Currently in some scenarios the compiler generates code like `null as any ? foo : bar` which will be invalid with [an upcoming TypeScript change](https://devblogs.microsoft.com/typescript/announcing-typescript-5-6-beta/#disallowed-nullish-and-truthy-checks). These changes switch to generating `0 as any` which is exempt from the change.
**Note:** I'm not starting the work to fully get us on TS 5.6 until the 18.2 release comes out, but this change is necessary to unblock an internal team.
PR Close#57303
Currently we use some short variable names like `t` and `r` in the generated factory functions. They can conflict with local symbols with the same names, if they're used for DI.
These changes rename the parameters to reduce the change for conflicts.
Fixes#57168.
PR Close#57181
This commit addresses a performance bottleneck in the `interpolatedSignalNotInvoked` extended
diagnostic by querying directive metadata instead of consulting the type-checker to determine if
a property binding corresponds with an input.
Fixes#57287
PR Close#57291
Similar to a previous fix that intended to make the JIT transforms
compatible with pre-transforms like e.g. Tsickle, we need to solve
an additional issue where the class properties are synthetic and result
in an `getSourceFile() => undefined` invocation that breaks the import
insertion, causing errors like:
```
TypeError: Cannot read properties of undefined (reading 'fileName')
```
PR Close#57262
This commit is similar to 98ed5b609e, and
makes use of the preparation work implemented there.
Similar to directives and components marked via `jit: true`, we also
need to do the same for JIT marked `@NgModule` classes. This is mostly
important for downleveling of decorators to support dependency injection
of such classes.
Inside Google3, migrating from `ts_library` to `ng_module` turns of
decorator downleveling, so the `jit: true` for NgModule's is implicitly
requesting/reliant on this transform— as expected.
PR Close#57212
Currently we use some short variable names like `t` and `r` in the generated factory functions. They can conflict with local symbols with the same names, if they're used for DI.
These changes add a `ɵ` to the generated variables to reduce the chance of conflicts.
Fixes#57168.
PR Close#57181
Follow-up to #56961 which doesn't appear to have caught all the cases. This change moves the pre-emit untagging to `NgCompiler.prepareEmit` which seems to cover a bit more comared to `NgtscProgram.emit`.
Fixes#57135.
PR Close#57138
Updates the import manager to allow for a specific alias to be passed in. This is a prerequisite for switching schematics to the new import manager.
Note that passing in an alias disables identifier conflict resolution in order to avoid rewriting the alias that was passed in explicitly. For now this is fine since we have a very narrow use case for it, but we may want to revisit it in the future.
PR Close#57096
This allows use of poisoned data for migrations. Right now, migrations
often enable this flag by creating some deeper structures of the
Angular compiler, but with this change it's easier to enable as a
private compiler option.
This is helpful for migrations, specifically the signal input migration
as it allows us to generate as much TCB code as possible, for reference
resolution.
PR Close#57082
This commit exposes metadata about inputs that are defined inside
the `inputs` field of `@Directive` or `@Component` class decorators
This is useful and necessary information for migrations, like the
signal inputs migration.
PR Close#57082
Adds a new extended diagnostic that will flag `@let` declarations that aren't used within the template. The diagnostic can be turned off through the `extendedDiagnostics` compiler option.
PR Close#57033
Some Angular template instructions that follow each other may be chained
together in a single expressions statement, containing a deeply nested
AST of call expressions. The number of chained instructions wasn't previously
limited, so this could result in very deep ASTs that cause stack overflow
errors during TypeScript emit.
This commit introduces a limit to the number of chained instructions to
avoid these problems.
Closes#57066
PR Close#57069
Adds a new extended diagnostic that will flag `@let` declarations that aren't used within the template. The diagnostic can be turned off through the `extendedDiagnostics` compiler option.
PR Close#57033
In addition to quick fixes, this commit adds the ability to write
code refactoring actions that can be applied by users.
For example, we may implement a migration as a code refactoring action.
Notably the quick fix support, existing already, is insufficient as it
only allows for edits to be applied based on diagnostics shwon in e.g.
VSCode.
PR Close#56895
In #56358 we removed most of the places that untag the references to typecheck files, because it was causing the compiler to throw error when it produces diagnostics. This appears to have caused a regression in TS 5.4 which now emits the synthetic references.
These changes add tagging right before the program emits.
Fixes#56945.
PR Close#56961
This commit moves the JIT transforms into the ngtsc folder. They existed
outside of ngtsc mostly as an historic artifact— and now with compiler
relying on them even more deeply, it makes sense to move them into
`ngtsc/transform`.
PR Close#56892
Currently when compiling code with the Angular compiler, all classes
with Angular decorators are compiled with AOT. This includes type
checking, scope collection etc.
This may not be desirable for all components, e.g. dynamic components,
or test components w/ `TestBed.configureTestingModule` (if compiled with ngtsc).
Those components can opt out of AOT on a per component-basis via `jit:
true`. This is helpful as it allows incremental migrations/refactorings
to AOT. Whether we want to keep this capability long-term is something
to be discussed separately.
For now though, we should fix that components compiled with `jit: true`
actually work as expected. Currently this **not the case** as soon as
the new initializer APIs are used— as those do no longer declare class
metadata with decorators.
This commit runs the JIT transform on JIT-opted classes.
Related: https://docs.google.com/document/d/1ox4atCJldWWDXlaYgwM-hU8BNsTpKNW7gx8OfZ0HtRY/edit?resourcekey=0-G1haTNYtD-dN0vNRkQ8_OQ&tab=t.0
PR Close#56892
When we process `@if` and `@for` blocks, we create a scope around their expressions in order to encapsulate the aliases to them. The problem is that this doesn't represent the actual structure since the expression is part of the outer scope. This surfaces by not raising the "used before declared" diagnostic for `@let` declarations.
These changes resolve the issue by processing the expression as a part of the parent scope.
Fixes#56842.
PR Close#56843
Currently the logic that maps a name to a variable looks at the variables in their definition order. This means that `@let` declarations from parent views will always come before local ones, because the local ones are declared inline whereas the parent ones are hoisted to the top of the function.
These changes resolve the issue by giving precedence to the local variables.
Fixes#56737.
PR Close#56752
When running the JIT transforms in 1P w/ tsickle, tsickle will
transform source files before our custom transforms can run. This is
also impacting the Ivy transform and hence we use `ts.getOriginalNode`
in various places to inspect the source AST for detecting Angular.
For the JIT transform we need to do a similar change so that the
transform could run in 1P.
PR Close#56520
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
The import manager ensures generation of unique identifiers when
inserting imports. This is done by inspecting existing identifiers
within the original source file, while also checking if a similar
identifier was generated at an earlier time. This is correct behavior.
We can improve the detection so that the same identifier can be
generated in different files. This is beneficial for schematic/migration
use-cases where we wouldn't want to generate import aliases in multiple
files just because we generated an import to e.g. `input` previously.
E.g. it's fine to generate
```ts
// a.ts
import {input} from '@angular/core';
// b.ts
import {input} from `@angular/core';
// instead of `input as input_1`.
```
PR Close#56406
This PR allows the language service to suggest imports for all directives returned from the
compiler, and generate the TypeScript module import and the decorator import when the component
is selected by the user.
PR Close#55595
Updates the check that prevent writes to template variables in two-way bindings to account for let declarations.
Also fixes some old tests that weren't properly setting up two-way bindings.
PR Close#56199
Integrates let declarations into the template type checker by producing corresponding constants in the TCB.
This also includes a couple of custom diagnostics to flag usages of let before they're declared and illegal writes to let declarations. We can't rely on TS for these checks, because it includes the variable name in the diagnostic.
PR Close#56199
Currently the template syntax errors are extracted in the template type check phase. But in local compilation mode we skip the type check phase. As a result template syntax errors are not displayed. With this change we show the template syntax diagnostics in local mode.
PR Close#55855
Introduces a new `LetDeclaration` into the Render3 AST, simiarly to the HTML AST, and adds an initial integration into the various visitors.
PR Close#55848
Currently we optimize methods that pass both `$index` and the item into a method. We can take this a step further by also optimizing calls that only pass `$index` into the first parameter.
PR Close#55872
The compiler now checks if a signal is properly called on dom property bindings.
The ideal solution would be for the compiler to check if dom property bindings in general are properly typed,
but this is currently not the case, and it is a bigger task to land this change.
In the meantime, the signal diagnostic is augmented to catch cases like the following:
```
<div [id]="mySignal"></div>
```
PR Close#54324
When an `@if` expression has an alias, only the type of the alias is
currently narrowed. So for example, suppose `value` is `string|undefined`:
```
@if (value; as alias) {
{{ value.length }} <!-- error, value may be undefined -->
{{ alias.length }} <!-- no error, alias is narrowed -->
}
```
This is especially noticeable when the expression contains guards which are
preconditions for the aliased expression:
```
@if (a && b; as alias) {...}
```
In this case, `a` would not be narrowed within the body, even though the
`@if` condition forces it to be truthy. This is a bug.
The reason is that aliased expressions were previously type-checked as:
```
var alias = a && b;
if (alias) {
// nothing other than alias is narrowed
...
}
```
One option considered was to emit `const alias` instead of `var alias`.
TypeScript _does_ trace `const` expressions and narrow their individual
components when the overall expression is guarded:
```
const alias = a && b;
if (alias) {
// a, b are also narrowed
}
```
However, this narrowing has different semantics than if `a && b` appeared
directly in the guard expression. For example, object properties aren't
narrowed with this approach, so component properties (which are referenced
as e.g. `this.a`) would not be narrowed.
Instead, we amend the guard expression to include both the expression _and_ the
alias variable, enforcing that both are narrowed.
```
var alias = a && b;
if ((a && b) && alias) {
// a, b, and alias all narrowed correctly.
}
```
This form ensures all conditions within the guard expression get narrowed
while also narrowing the alias variable type.
Fixes#52855
PR Close#55835
Currently when attempting to retrieve a TCB symbol for an input binding
that refers to a signal input with e.g. `protected`, while the
`honorAccessModifiersForInputBindings` flag is `false`, Angular will
throw a runtime exception because the symbol retrieval code always
expects a proper field access in the TCB.
This is not the case with `honorAccessModifiersForInputBindings =
false`, as TCB will allocate a temporary variable when ignoring the
field access. This will then trigger the runtime exception (which we
added to flag such "unexpected" cases). This commit handles it
gracefully, as it's valid TCB, but we simply cannot generate a proper
TCB symbol (yet). This is similar to `@Input` decorator inputs.
In the future we may implement logic to build up TCB symbols for
non-property access bindings, for both signal inputs or `@Input`
inputs. This commit just avoids a build exception.
Related to: #54324.
PR Close#55774
Since we aren't using clang anymore, we can remove the comments and the workarounds that were in place to prevent it from doing the wrong thing.
PR Close#55750
Fixes that we didn't have the MathML elements in the schema. Note that we can't discover which tag names are available by looking at globally-available classes, because all MathML elements are `MathMLElement` rather than something like `SVGCircleElement`. As such, I ended up having to hardcode the currently-available tags.
Fixes#55608.
PR Close#55631
Currently we add global extra imports to all the files in the compilation unit. However not all the files need extra imports. For example non-Angular files definitely do not need such extra imports, and in some cases these extra imports causes problems as the file is meant to be run the Node but it has Angular dependencies which are not compatible with Node. This change tries to limit extra import generation to a subset of files. Wit hthis change we create extra imports only for the files that contain at least one component whose NgModule is in a different file. This is because all other files do not need extra imports since they are either not Angular files or they already have all the imports that the components need.
PR Close#55548
Currently whenever a compliance test case TS file is modified, all
compliance tests in repository are rebuilt in partial compilation mode.
This is inefficient and also slows down local development where one may
use a wildcard to run all test targets inside `/test/compliance/...`.
This commit fixes this.
PR Close#55594
Angular only checks the contents of template nodes in full type checking mode. After v17, the new control flow always had its body checked, even in basic mode, which started revealing compilation errors for apps that were using the schematic to automatically switch to the new syntax.
These changes mimic the old behavior by not checking the bodies of `if`, `switch` and `for` blocks in basic mode. Note that the expressions of the blocks are still going to be checked.
Fixes#52969.
PR Close#55360
Two-way bindings are meant to represent a property binding to an input and an event binding to an output, e.g. `[(ngModel)]="foo"` represents `[ngModel]="foo" (ngModelChange)="foo = $event"`. Previously due to a quirk in the template parser, we accidentally supported unassignable expressions in two-way bindings.
In #54154 the quirk was fixed, but we kept support or some common expression because of internal usages. Now the internal usages have been cleaned up so the backwards-compatibility code can be deleted.
Externally a migration was added in #54630 that will automatically fix any places that depended on the old behavior.
BREAKING CHANGE:
Angular only supports writable expressions inside of two-way bindings.
PR Close#55342
Previously the input flags were being generated as a reference to an enum member for better readability and under the assumption that minifiers would inline the values. That doesn't appear to be the case so these changes switch to using the literal values instead.
PR Close#55215
In #52110 we had to use `if` statements to represent `switch` blocks, because TypeScript had a bug when narrowing the type of parenthesized `switch` statements. Now that it has been fixed by TypeScript and we don't support any version that has the broken behavior, we can go back to generating `switch` statements in the TCB which are simpler and better represent the user's code.
PR Close#55168
Allows for `SourceFileValidatorRule.checkNode` to produce a single diagnostic. The most common case should be one diagnostic per node so this allows us to save some array allocations.
PR Close#54993
Adds the new `SourceFileValidator` that will be used to check for file-level issues that may prevent Angular from working, like invoking the `input()` function outside of an initializer. Currently only one check is planned, but this setup will allow us to easily add more in the future.
PR Close#54993
For `FatalDiagnosticError` we are currently hiding the `message` string
field in favor of the actual TS `diagnosticMessage`.
This works as expected, but makes these errors hard to debug in certain
environments (e.g. Jasmine). That is because `null` is the value of
`message` at runtime. We fix this by just overriding the type, like we
originally intended to do.
In addition, we properly render message chains in the `Error#message`
field— so that these errors, when uncaught, are somewhat reasonable and
can be useful.
PR Close#54981
This commit ensures that the new APIs like `input`, `model`, `output`,
or signal-based queries are not accidentally used on fields that have a
problematic visibility/access level that won't work.
For example, queries defined using a private identifier (e.g. `#bla`)
will not be accessible by the Angular runtime and therefore _dont_ work.
This commit ensures:
- `input` is only declared via public and protected fields.
- `output` is only declared via public and protected fields.
- `model` is only declared via public and protected fields.
- signal queries are only declared via public, protected and TS private
fields (`private` works, while `#bla` does not).
Fixes#54863.
PR Close#54981
An initializer API like `input`, `output`, or signal queries may not be
compatible with certain access levels. E.g. queries cannot work with ES
private class fields.
This commit introduces a check for access levels into the initializer
API recognition— enforcing that every initializer API *clearly*
specifies what type of access is allowed.
PR Close#54981
This commit changes the TypeScript reflection host to:
* inspect / process ES private fields. e.g. `#someField` — those are
ignored right now and we would want to check them to issue
diagnostics.
* determine an access level of a class member. E.g. a member may be
public, may be private, may be ES private, or public readonly. This
can then be used in various checks later.
PR Close#54981
Updates the function that parses initializer APIs to check any `Expression`, instead of expecting a class member. This will be useful for the upcoming changes.
PR Close#54981
Adds logic to ingest the content of an `ng-content` element in the template type checker. We treat `ng-content` as a `ScopedNode`, because its content is inserted conditionally.
PR Close#54854
This commit adds support for ignoring specific doc entries when
extracting doc entries. This allows us to drop e.g. `InputFunction` from
the API docs, given that the `input` API entry holds all the relevant
information.
`InputFunction` only exists for type purposes in the `.d.ts`.
PR Close#54925
This commit improves the API documentation for `input` after
we added support for initializer APIs in angular.dev docs generation.
Changes:
- Rename `ReadT` to `T`. This conceptually makes it easy to talk about
inputs of type `T` if there is no transform involved. The common case.
- Rename `WriteT` to `TransformT`. This makes it clear that this is the
type that the "transform" needs to handle.
- Improves the "overall" description of the input function so that it
can be shown as a general overview for the API site.
- Improves usage notes to be a little more helpful, yielding more useful
content in the API docs usage notes section.
- Add short JSDoc description for each individual overload.
PR Close#54925
This commit adds support for extracting initializer API functions.
Initialixer API functions are functions conceptually that can are
intended to be used as class member initializers.
Angular started introducing a few of these for the new signal
APIs, like `input`, `model` or signal-based queries.
These APIs are currently confusingly represented in the API docs because
the API extraction:
- does not properly account for call signatures of interfaces
- does not expose information about sub-property objects and call
signatures (e.g. `input.required`)
- the docs rendering syntax highlighting is too bloated and confusing
with all types being included.
This commit adds support for initializer API functions, namely two
variants:
- interface-based initializer APIs. e.g. `export const input:
InputFunction`- which is a pattern for `input` and `input.required`.
- function-based simpler initializer APIs with overloads. e.g.
`contentChildren` has many signatures but doesn't need to be an
interface as there are no sub-property call signatures.
PR Close#54925
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
Updates the logic that detects if a node should be checked for control flow content projection to exit as soon as it detects a second root node, instead of counting the total and then checking if it's more than one.
PR Close#54921
Previously only the first branch of an `if` block was captured for content projection. This was done because of some planned refactors in the future. Since we've decided not to apply those refactors to conditionals, these changes update the compiler to capture each branch individually for content projection purposes.
PR Close#54921
Currently when aliasing a `for` loop variable with `let`, we replace the variable's old name with the new one. Since users have found this to be confusing, these changes switch to a model where the variable is available both under the original name and the new one.
Fixes#52528.
PR Close#54942
Previously we assumed that if a `for` loop tracking function is in the form of `someMethod($index, $item)`, it will be pure so we didn't pass the parameter to bind the context to it. This appears to be risky, because we don't know if the method is trying to access `this`.
These changes play it safe by always binding method-based tracking functions.
Fixes#53628.
PR Close#54960
We are already testing the JIT transforms via integration tests, but
this commit adds dedicated unit tests for the transform behavior for
proper test coverage (planned follow-up).
PR Close#54841