Skip to content

Commit 9ab663e

Browse files
JeanMechedevversion
authored andcommitted
refactor(migrations): Port standalone migration to standalone by default (#58169)
With this commit, the standalone migration will only migrate code with `standalone: false` to standalone by default (without the standalone attribute) PR Close #58169
1 parent 7ed5665 commit 9ab663e

File tree

3 files changed

+159
-157
lines changed

3 files changed

+159
-157
lines changed

packages/core/schematics/ng-generate/standalone-migration/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ listed in:
4040

4141
### Convert declarations to standalone
4242
In this mode, the migration will find all of the components, directives and pipes, and convert them
43-
to standalone by setting `standalone: true` and adding any dependencies to the `imports` array.
43+
to standalone by removing `standalone: false` and adding any dependencies to the `imports` array.
4444

4545
**Note:** NgModules which bootstrap a component are explicitly ignored in this step, because they
4646
are likely to be root modules and they would have to be bootstrapped using `bootstrapApplication`
@@ -62,6 +62,7 @@ export class AppModule {}
6262
@Component({
6363
selector: 'my-comp',
6464
template: '<div my-dir *ngIf="showGreeting">{{ "Hello" | myPipe }}</div>',
65+
standalone: false,
6566
})
6667
export class MyComp {
6768
public showGreeting = true;
@@ -70,13 +71,13 @@ export class MyComp {
7071

7172
```typescript
7273
// my-dir.ts
73-
@Directive({selector: '[my-dir]'})
74+
@Directive({selector: '[my-dir]', standalone: false})
7475
export class MyDir {}
7576
```
7677

7778
```typescript
7879
// my-pipe.ts
79-
@Pipe({name: 'myPipe', pure: true})
80+
@Pipe({name: 'myPipe', pure: true, standalone: false})
8081
export class MyPipe {}
8182
```
8283

@@ -94,7 +95,6 @@ export class AppModule {}
9495
@Component({
9596
selector: 'my-comp',
9697
template: '<div my-dir *ngIf="showGreeting">{{ "Hello" | myPipe }}</div>',
97-
standalone: true,
9898
imports: [NgIf, MyDir, MyPipe]
9999
})
100100
export class MyComp {
@@ -104,13 +104,13 @@ export class MyComp {
104104

105105
```typescript
106106
// my-dir.ts
107-
@Directive({selector: '[my-dir]', standalone: true})
107+
@Directive({selector: '[my-dir]'})
108108
export class MyDir {}
109109
```
110110

111111
```typescript
112112
// my-pipe.ts
113-
@Pipe({name: 'myPipe', pure: true, standalone: true})
113+
@Pipe({name: 'myPipe', pure: true})
114114
export class MyPipe {}
115115
```
116116

@@ -244,7 +244,7 @@ export class AppModule {}
244244

245245
```typescript
246246
// ./app/app.component.ts
247-
@Component({selector: 'app', template: 'hello'})
247+
@Component({selector: 'app', template: 'hello', standalone: false})
248248
export class AppComponent {}
249249
```
250250

@@ -279,7 +279,7 @@ export class ExportedConfigClass {}
279279

280280
```typescript
281281
// ./app/app.component.ts
282-
@Component({selector: 'app', template: 'hello', standalone: true})
282+
@Component({selector: 'app', template: 'hello'})
283283
export class AppComponent {}
284284
```
285285

packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export function convertNgModuleDeclarationToStandalone(
125125
const directiveMeta = typeChecker.getDirectiveMetadata(decl);
126126

127127
if (directiveMeta && directiveMeta.decorator && !directiveMeta.isStandalone) {
128-
let decorator = addStandaloneToDecorator(directiveMeta.decorator);
128+
let decorator = removeStandaloneFalseFromDecorator(directiveMeta.decorator);
129129

130130
if (directiveMeta.isComponent) {
131131
const importsToAdd = getComponentImportExpressions(
@@ -157,7 +157,10 @@ export function convertNgModuleDeclarationToStandalone(
157157
const pipeMeta = typeChecker.getPipeMetadata(decl);
158158

159159
if (pipeMeta && pipeMeta.decorator && !pipeMeta.isStandalone) {
160-
tracker.replaceNode(pipeMeta.decorator, addStandaloneToDecorator(pipeMeta.decorator));
160+
tracker.replaceNode(
161+
pipeMeta.decorator,
162+
removeStandaloneFalseFromDecorator(pipeMeta.decorator),
163+
);
161164
}
162165
}
163166
}
@@ -427,11 +430,31 @@ function moveDeclarationsToImports(
427430
}
428431

429432
/** Adds `standalone: true` to a decorator node. */
430-
function addStandaloneToDecorator(node: ts.Decorator): ts.Decorator {
431-
return setPropertyOnAngularDecorator(
432-
node,
433-
'standalone',
434-
ts.factory.createToken(ts.SyntaxKind.TrueKeyword),
433+
function removeStandaloneFalseFromDecorator(node: ts.Decorator): ts.Decorator {
434+
// Invalid decorator.
435+
if (!ts.isCallExpression(node.expression) || node.expression.arguments.length !== 1) {
436+
return node;
437+
}
438+
439+
if (!ts.isObjectLiteralExpression(node.expression.arguments[0])) {
440+
// Unsupported case (e.g. `@Component(SOME_CONST)`). Return the original node.
441+
return node;
442+
}
443+
444+
const hasTrailingComma = node.expression.arguments[0].properties.hasTrailingComma;
445+
446+
const properties = node.expression.arguments[0].properties;
447+
const literalProperties = properties.filter((element) => !isStandaloneProperty(element));
448+
449+
// Use `createDecorator` instead of `updateDecorator`, because
450+
// the latter ends up duplicating the node's leading comment.
451+
return ts.factory.createDecorator(
452+
ts.factory.createCallExpression(node.expression.expression, node.expression.typeArguments, [
453+
ts.factory.createObjectLiteralExpression(
454+
ts.factory.createNodeArray(literalProperties, hasTrailingComma),
455+
literalProperties.length > 1,
456+
),
457+
]),
435458
);
436459
}
437460

@@ -489,6 +512,11 @@ function setPropertyOnAngularDecorator(
489512
);
490513
}
491514

515+
function isStandaloneProperty(prop: ts.Node): prop is ts.PropertyAssignment {
516+
return (
517+
ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'standalone'
518+
);
519+
}
492520
/** Checks if a node is a `PropertyAssignment` with a name. */
493521
function isNamedPropertyAssignment(
494522
node: ts.Node,
@@ -746,13 +774,13 @@ export function migrateTestDeclarations(
746774
const closestClass = closestNode(decorator.node, ts.isClassDeclaration);
747775

748776
if (decorator.name === 'Pipe' || decorator.name === 'Directive') {
749-
tracker.replaceNode(decorator.node, addStandaloneToDecorator(decorator.node));
777+
tracker.replaceNode(decorator.node, removeStandaloneFalseFromDecorator(decorator.node));
750778

751779
if (closestClass) {
752780
allDeclarations.add(closestClass);
753781
}
754782
} else if (decorator.name === 'Component') {
755-
const newDecorator = addStandaloneToDecorator(decorator.node);
783+
const newDecorator = removeStandaloneFalseFromDecorator(decorator.node);
756784
const importsToAdd = componentImports.get(decorator.node);
757785

758786
if (closestClass) {

0 commit comments

Comments
 (0)