diff --git a/packages/cli/package.json b/packages/cli/package.json index 63651c3..ea35c1c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -2,7 +2,7 @@ "name": "@ionic/angular-standalone-codemods", "version": "0.0.7", "dependencies": { - "@angular-eslint/template-parser": "^16.1.2", + "@angular-eslint/template-parser": "^17.1.0", "@clack/core": "^0.3.3", "@clack/prompts": "^0.7.0", "@ionic/utils-terminal": "^2.3.4", diff --git a/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.test.ts b/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.test.ts index e1b5ba4..68c19d8 100644 --- a/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.test.ts +++ b/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.test.ts @@ -579,4 +579,103 @@ describe("migrateComponents", () => { ); }); }); + + it("should migrate components using inline templates with control flow", async () => { + const project = new Project({ useInMemoryFileSystem: true }); + + const component = ` + import { Component } from "@angular/core"; + + @Component({ + selector: 'my-component', + template: \` + + + My Component + + + + @defer { + + @for (item of [0, 1, 2]; track item) { + + @if (1 === 1){ } + @switch (flag) { + @case(0) { + My Item + } + @default { + Your Item + } + } + + } + + } @loading (after 100ms; minimum 1s) { + + } + + \`, + standalone: true + }) + export class MyComponent { flag = 1 } + `; + + const componentSourceFile = project.createSourceFile( + "foo.component.ts", + dedent(component), + ); + + await migrateComponents(project, { dryRun: false }); + + expect(dedent(componentSourceFile.getText())).toBe( + dedent(` + import { Component } from "@angular/core"; + import { addIcons } from "ionicons"; + import { logoIonic, reloadOutline } from "ionicons/icons"; + import { IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonIcon, IonLabel } from "@ionic/angular/standalone"; + + @Component({ + selector: 'my-component', + template: \` + + + My Component + + + + @defer { + + @for (item of [0, 1, 2]; track item) { + + @if (1 === 1){ } + @switch (flag) { + @case(0) { + My Item + } + @default { + Your Item + } + } + + } + + } @loading (after 100ms; minimum 1s) { + + } + + \`, + standalone: true, + imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonIcon, IonLabel] + }) + export class MyComponent { + flag = 1 + + constructor() { + addIcons({ logoIonic, reloadOutline }); + } + } + `), + ); + }); }); diff --git a/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.ts b/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.ts index 6109b56..10a8c72 100644 --- a/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.ts +++ b/packages/cli/src/angular/migrations/standalone/0002-import-standalone-component.ts @@ -325,6 +325,38 @@ function detectIonicComponentsAndIcons(htmlAsString: string, filePath: string) { recursivelyFindIonicComponents(childNode); } } + } else if (node.type === "IfBlock") { + for (const branch of node.branches) { + for (const childNode of branch.children) { + recursivelyFindIonicComponents(childNode); + } + } + } else if (node.type === "ForLoopBlock") { + for (const childNode of node.children) { + recursivelyFindIonicComponents(childNode); + } + } else if (node.type === "SwitchBlock") { + for (const c of node.cases) { + for (const childNode of c.children) { + recursivelyFindIonicComponents(childNode); + } + } + } else if (node.type === "DeferredBlock") { + if (node.children) { + for (const childNode of node.children) { + recursivelyFindIonicComponents(childNode); + } + } + + for (const childKey of Object.keys(node)) { + if (node[childKey]?.children) { + for (const childNode of node[childKey].children) { + recursivelyFindIonicComponents(Object.assign(childNode, { + type: childNode.constructor.name + })); + } + } + } } }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbac0e6..1ffeb6a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -245,8 +245,8 @@ importers: packages/cli: dependencies: '@angular-eslint/template-parser': - specifier: ^16.1.2 - version: 16.1.2(eslint@8.48.0)(typescript@4.9.5) + specifier: ^17.1.0 + version: 17.1.1(eslint@8.48.0)(typescript@4.9.5) '@clack/core': specifier: ^0.3.3 version: 0.3.3 @@ -663,6 +663,11 @@ packages: /@angular-eslint/bundled-angular-compiler@16.1.2: resolution: {integrity: sha512-wDiHPFsKTijMcQUPNcoHOJ5kezIPCCbmDK6LHH7hAdAC/eDY9NHL5e4zQ2Xkf3/r1PFuwVLGTwwreEHlmeENDw==} + dev: true + + /@angular-eslint/bundled-angular-compiler@17.1.1: + resolution: {integrity: sha512-xRlSh9qjdUdUKAy/0UQsxX7wf1tHApAsHsfismebPriqfmVAPyEg4HBrM8ImWaZxiqaTGC1AyHsUBQD5FK8o6w==} + dev: false /@angular-eslint/eslint-plugin-template@16.0.0(eslint@7.26.0)(typescript@5.0.2): resolution: {integrity: sha512-2m2NsB+WHO61eR1qvRvAidL5NBY89U/7bSPivA0o0lYuYZMuAczkDfsOBn4ejlaNdk+/vzXsmchza0B1ujrecA==} @@ -730,13 +735,13 @@ packages: typescript: 5.0.2 dev: true - /@angular-eslint/template-parser@16.1.2(eslint@8.48.0)(typescript@4.9.5): - resolution: {integrity: sha512-vIkPOShVJLBEHYY3jISCVvJF3lXL//Y70J8T9lY2CBowgqp6AzzJ6cZU7JxrORN6b64rBUVvUtCGo8L36GvfuA==} + /@angular-eslint/template-parser@17.1.1(eslint@8.48.0)(typescript@4.9.5): + resolution: {integrity: sha512-ofL46rNhRVeSxrSQF0vwhKMco+vJuo+ZGjSOzFmT9N3KAMB0j+WXTbpyGGMy0gQSBc4W6p+j+zxGa2CR2xb6wA==} peerDependencies: eslint: ^7.20.0 || ^8.0.0 typescript: '*' dependencies: - '@angular-eslint/bundled-angular-compiler': 16.1.2 + '@angular-eslint/bundled-angular-compiler': 17.1.1 eslint: 8.48.0 eslint-scope: 7.2.2 typescript: 4.9.5