Skip to content

Commit 2001f32

Browse files
authored
feat(cli): support new control flow (#35)
* feat(cli): support new control flow * feat(cli): support control flow of forLoop, switchBlock, and DeferredBlock ---------
1 parent 9448028 commit 2001f32

File tree

4 files changed

+143
-7
lines changed

4 files changed

+143
-7
lines changed

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@ionic/angular-standalone-codemods",
33
"version": "0.0.7",
44
"dependencies": {
5-
"@angular-eslint/template-parser": "^16.1.2",
5+
"@angular-eslint/template-parser": "^17.1.0",
66
"@clack/core": "^0.3.3",
77
"@clack/prompts": "^0.7.0",
88
"@ionic/utils-terminal": "^2.3.4",

packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,103 @@ describe("migrateComponents", () => {
579579
);
580580
});
581581
});
582+
583+
it("should migrate components using inline templates with control flow", async () => {
584+
const project = new Project({ useInMemoryFileSystem: true });
585+
586+
const component = `
587+
import { Component } from "@angular/core";
588+
589+
@Component({
590+
selector: 'my-component',
591+
template: \`
592+
<ion-header>
593+
<ion-toolbar>
594+
<ion-title>My Component</ion-title>
595+
</ion-toolbar>
596+
</ion-header>
597+
<ion-content>
598+
@defer {
599+
<ion-list>
600+
@for (item of [0, 1, 2]; track item) {
601+
<ion-item>
602+
@if (1 === 1){ <ion-icon name="logo-ionic" slot="start"></ion-icon> }
603+
@switch (flag) {
604+
@case(0) {
605+
<ion-label>My Item</ion-label>
606+
}
607+
@default {
608+
<ion-label>Your Item</ion-label>
609+
}
610+
}
611+
</ion-item>
612+
}
613+
</ion-list>
614+
} @loading (after 100ms; minimum 1s) {
615+
<ion-icon name="reload-outline" slot="start"></ion-icon>
616+
}
617+
</ion-content>
618+
\`,
619+
standalone: true
620+
})
621+
export class MyComponent { flag = 1 }
622+
`;
623+
624+
const componentSourceFile = project.createSourceFile(
625+
"foo.component.ts",
626+
dedent(component),
627+
);
628+
629+
await migrateComponents(project, { dryRun: false });
630+
631+
expect(dedent(componentSourceFile.getText())).toBe(
632+
dedent(`
633+
import { Component } from "@angular/core";
634+
import { addIcons } from "ionicons";
635+
import { logoIonic, reloadOutline } from "ionicons/icons";
636+
import { IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonIcon, IonLabel } from "@ionic/angular/standalone";
637+
638+
@Component({
639+
selector: 'my-component',
640+
template: \`
641+
<ion-header>
642+
<ion-toolbar>
643+
<ion-title>My Component</ion-title>
644+
</ion-toolbar>
645+
</ion-header>
646+
<ion-content>
647+
@defer {
648+
<ion-list>
649+
@for (item of [0, 1, 2]; track item) {
650+
<ion-item>
651+
@if (1 === 1){ <ion-icon name="logo-ionic" slot="start"></ion-icon> }
652+
@switch (flag) {
653+
@case(0) {
654+
<ion-label>My Item</ion-label>
655+
}
656+
@default {
657+
<ion-label>Your Item</ion-label>
658+
}
659+
}
660+
</ion-item>
661+
}
662+
</ion-list>
663+
} @loading (after 100ms; minimum 1s) {
664+
<ion-icon name="reload-outline" slot="start"></ion-icon>
665+
}
666+
</ion-content>
667+
\`,
668+
standalone: true,
669+
imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonIcon, IonLabel]
670+
})
671+
export class MyComponent {
672+
flag = 1
673+
674+
constructor() {
675+
addIcons({ logoIonic, reloadOutline });
676+
}
677+
}
678+
`),
679+
);
680+
});
582681
});

packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,38 @@ function detectIonicComponentsAndIcons(htmlAsString: string, filePath: string) {
325325
recursivelyFindIonicComponents(childNode);
326326
}
327327
}
328+
} else if (node.type === "IfBlock") {
329+
for (const branch of node.branches) {
330+
for (const childNode of branch.children) {
331+
recursivelyFindIonicComponents(childNode);
332+
}
333+
}
334+
} else if (node.type === "ForLoopBlock") {
335+
for (const childNode of node.children) {
336+
recursivelyFindIonicComponents(childNode);
337+
}
338+
} else if (node.type === "SwitchBlock") {
339+
for (const c of node.cases) {
340+
for (const childNode of c.children) {
341+
recursivelyFindIonicComponents(childNode);
342+
}
343+
}
344+
} else if (node.type === "DeferredBlock") {
345+
if (node.children) {
346+
for (const childNode of node.children) {
347+
recursivelyFindIonicComponents(childNode);
348+
}
349+
}
350+
351+
for (const childKey of Object.keys(node)) {
352+
if (node[childKey]?.children) {
353+
for (const childNode of node[childKey].children) {
354+
recursivelyFindIonicComponents(Object.assign(childNode, {
355+
type: childNode.constructor.name
356+
}));
357+
}
358+
}
359+
}
328360
}
329361
};
330362

pnpm-lock.yaml

Lines changed: 11 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)