The control flow migration determines whether an `*ngIf` uses a `then`
and/or `else` clause by regex matching the raw microsyntax string for
the literal keywords `then`/`else`. The regexes only checked that the
keyword was preceded by a non-word character, but not that it was
followed by one.
As a result, a template reference name that merely starts with `then`
(e.g. `else thenBlock`) or `else` was misidentified as the `then`/`else`
keyword itself. This caused the migration to take the wrong code path
(e.g. then+else instead of else-only), which in turn made
`getTemplateName()` compute a `slice(start, end)` with `start > end`,
producing an empty template name. That empty placeholder was never
resolved and was silently emitted as an invalid
`<ng-template [ngTemplateOutlet]=""></ng-template>`, dropping the
original template content without any warning.
Add a negative lookahead `(?![\w\d])` to both regexes so `then`/`else`
are only matched as whole keywords, not as a prefix of a longer
template reference name.
Fixes#69914
(cherry picked from commit 5ad8231397)
Preserve NgClass import on partial migration and increment
skippedNgClassCount when an unmigrable mixed binding is encountered.
(cherry picked from commit 49672c437b)
When only some NgClass usages are migrated (partial migration),
the NgClass import should be preserved in the module/component
imports if it is still used elsewhere.
(cherry picked from commit 223e40279f)
Currently, signal migration schematics in best effort mode doesn't do a very good job migrating input writes when there is a nested property access in templates.
In event handlers, no attempt is made to migrate a nested access in the left-hand-side of assignments or anything in their right-hand-side. E.g., nothing will happen here:
`(ngModelChange)="inputD.prop = $event + inputF"`.
Additionally, when a migration attempt is made, parentheses are often incorrectly placed on the parent, both in event handlers and two-way bindings:
`(ngModelChange)="inputC = $event"` is migrated to `(ngModelChange)="inputC = $event()"`.
`[(ngModel)]="inputB.prop.prop"` is migrated to `[(ngModel)]="inputB.prop().prop"`.
(cherry picked from commit 74638cab84)
When `rootDir` was set in a project's tsconfig (e.g. `rootDir: "src"`),
tsurge-based migrations would fail because `projectRoot` was derived from
`rootDir`, causing `rootRelativePath` to be computed relative to `src/`
instead of the workspace root. This produced paths like `app/app.ts`
instead of `src/app/app.ts`, which the DevKit tree could not resolve.
Fix by overriding `info.projectRoot` to `absoluteFrom(info.program.getCurrentDirectory())`
immediately after program creation, ensuring workspace-relative paths are
used for all tree updates.
(cherry picked from commit 26b0c719ef)
Our unit tests were missleading, the migration wasn't idempotent and `$safeNavigationMigration` were added multiple times on consecutive runs.
(cherry picked from commit 6038b9ede7)
The following:
`@Output() someChange = new EventEmitter<void>();`
is correctly migrated to:
`readonly someChange = output<void>();`
However, a TODO is incorrectly inserted for subsequent emissions from
`someChange`, stating that an argument is expected.
(cherry picked from commit 16fe27bfef)
Currently if a `@for` loop doesn't have a `track` expression we don't produce an AST for it at all which means no type checking and language service support for it.
These changes make it so we produce the AST anyways since it gives the user more tools to resolve the issue (e.g. autocompletion when writing the `track` expression).
(cherry picked from commit 06f6dec7aa)
Removes the @angular/compiler import from the safe optional chaining migration. This import is not needed as the compiler package import is side-effectful and has no functional use here.
(cherry picked from commit f1738b5032)
This migration ensure that existing code is wrapped by the `$safeNavigationMigration` magic function when necessary to maintain the pre-exisiting behavior of exisiting optional chaining expressions.
becomes input + linkedSignal
When a component has both a model() property and a conflicting output property (e.g., foo model + fooChange output), this migration converts the model() to an input() + linkedSignal() pattern to avoid naming conflicts.
Fixes#67340
This fixes an issue where when removing NgClass from the imports array of a component, an extra trailing comma would be left behind if it was the last element in that component`.
This fixes an issue where when removing NgClass from the imports array of a component, an extra trailing comma would be left behind if it was the last element in that component`.
This commit updates provideClientHydration to automatically enable incremental hydration by default. It also introduces a new withNoIncrementalHydration feature for opting out, adds conflict safety checks, and includes a schematic migration.
Decouple `SymbolBuilder` from the full `BoundTarget` interface by introducing a purpose-built `SymbolBoundTarget` interface containing only the 4 methods required for symbol resolution. This eliminates the need for the large, pass-through `BoundTargetAdapter` and further isolates `SymbolBuilder` from compiler-internal implementation details.
Also minimize `TypeCheckableDirectiveMetaAdapter` by redefining `SymbolDirectiveMeta` to not extend `DirectiveMeta`, exposing only the properties actually used by `SymbolBuilder`.
Removed dead code `getDirectiveMeta` in `template_symbol_builder.ts` which was unused.
These changes improve maintainability and ensure a cleaner architecture by strictly defining the boundaries of what `SymbolBuilder` needs from the rest of the system.
By limiting the required inputs to only what's necessary for the implementation, we make it easier to re-use
the implementation between different compiler architectures
This updates the SymbolBuilder to no longer use ts.TypeChecker internally to
build symbols for the language service. These lookups are deferred/done later
using the newly expanded template type checker API.
Avoid substring matching on importClause.getText() which caused suffix collisions (e.g., BarComponent vs FooBarComponent). Use AST-based matching for default and named (including aliased) imports to reliably resolve the correct import path when generating loadComponent.
Fix inject migration in multi-project workspace. The inject migration doesn't work when targeting one of the projects due to either not finding any files in other projects or considering them external thus it throws a SchematicsException
Fixes: #66074
This fixes an issue where when removing NgStyle from the imports array of a component, an extra trailing comma would be left behind if it was the last element in that component`.
Currently the migration that add `ChangeDetectionStrategy.Eager` to components tries to add the properties as last in the object literal. This can be tricky, because TS doesn't reflect the commas in the AST so we need have to do brittle string lookups to know where to insert it.
These changes switch to inserting the property before the last pre-existing property which should be a bit more robust.
Exisiting applications will be migrated to keep using the XHR backend to prevent any breaking changes. `withXhr()` is to the `provideHttpClient` provider function.
`provideRoutes` was deprecated in v15.
BREAKING CHANGE: `provideRoutes()` has been removed. Use `provideRouter()` or `ROUTES` as multi token if necessary.
In v22, `OnPush` becomes the default strategy. To maintain the ChangeDetection behavior of exisiting apps, components without an explicit change detectino strategy will get `Eager` assigned.
The common-to-standalone migration did not check for existing imports
when adding needed imports after removing CommonModule. This change adds deduplication
logic to filter out imports that already exist in the imports array
before adding them, ensuring each import appears only once.
Back in #39323, I added a new `ThisReceiver` node to represent accesses done through `this` and I ended up making it inherit from `ImplicitReceiver`. The logic was that in most cases accessing through `this` was the same as the implicit access.
Over the years this has proven to not be a great idea, because no other AST nodes do this and one has to keep it in mind whenever dealing with `ImplicitReceiver`.
These changes remove the inheritance and update all of the usage sites accordingly.
the common-to-standalone migration only matched [ngTemplateOutlet] and
[ngComponentOutlet] bindings and missed their structural forms
(*ngTemplateOutlet and *ngComponentOutlet). This caused missing imports
when removing CommonModule. This change adds structural directive
patterns so the migration correctly identifies needed imports.