From 001b156be9784575e060d00f73a961d7299c01f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Tue, 2 Jan 2024 11:33:07 +0900 Subject: [PATCH 1/3] feat(cli): support new control flow --- packages/cli/package.json | 2 +- .../0002-import-standalone-component.test.ts | 43 +++++++++++++++++++ .../0002-import-standalone-component.ts | 6 +++ pnpm-lock.yaml | 17 +++++--- 4 files changed, 61 insertions(+), 7 deletions(-) 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..62494a1 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 @@ -167,6 +167,49 @@ describe("migrateComponents", () => { ); }); + it("should detect and import icons used in the template with new control flow", async () => { + const project = new Project({ useInMemoryFileSystem: true }); + + const component = ` + import { Component } from "@angular/core"; + + @Component({ + selector: 'my-component', + template: '@if(1 === 1){ }', + standalone: true + }) + export class MyComponent { } + `; + + 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 } from "ionicons/icons"; + import { IonIcon } from "@ionic/angular/standalone"; + + @Component({ + selector: 'my-component', + template: '@if(1 === 1){ }', + standalone: true, + imports: [IonIcon] + }) + export class MyComponent { + constructor() { + addIcons({ logoIonic }); + } + } + `), + ); + }); + it("should remove duplicate imports from existing declarations", async () => { const project = new Project({ useInMemoryFileSystem: true }); 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..5f8a3f3 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,12 @@ 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); + } + } } }; 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 From 7101f6d5215091dc0a22ff68dbce46a88535c29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Thu, 4 Jan 2024 17:03:34 +0900 Subject: [PATCH 2/3] feat(cli): support control flow of forLoop, switchBlock, and DeferredBlock --- .../0002-import-standalone-component.test.ts | 142 ++++++++++++------ .../0002-import-standalone-component.ts | 29 ++++ 2 files changed, 128 insertions(+), 43 deletions(-) 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 62494a1..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 @@ -167,49 +167,6 @@ describe("migrateComponents", () => { ); }); - it("should detect and import icons used in the template with new control flow", async () => { - const project = new Project({ useInMemoryFileSystem: true }); - - const component = ` - import { Component } from "@angular/core"; - - @Component({ - selector: 'my-component', - template: '@if(1 === 1){ }', - standalone: true - }) - export class MyComponent { } - `; - - 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 } from "ionicons/icons"; - import { IonIcon } from "@ionic/angular/standalone"; - - @Component({ - selector: 'my-component', - template: '@if(1 === 1){ }', - standalone: true, - imports: [IonIcon] - }) - export class MyComponent { - constructor() { - addIcons({ logoIonic }); - } - } - `), - ); - }); - it("should remove duplicate imports from existing declarations", async () => { const project = new Project({ useInMemoryFileSystem: true }); @@ -622,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 5f8a3f3..81e7875 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 @@ -331,6 +331,35 @@ function detectIonicComponentsAndIcons(htmlAsString: string, filePath: string) { recursivelyFindIonicComponents(childNode); } } + } else if (node.type === "ForLoopBlock") { + console.log(node) + for (const childNode of node.children) { + recursivelyFindIonicComponents(childNode); + } + } else if (node.type === "SwitchBlock") { + console.log(node); + 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) { + console.log(childNode); + 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 + })); + } + } + } } }; From 3e4dfba3b4686d0de969268810f5037a0ee2cf3c Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Thu, 11 Jan 2024 22:06:20 -0500 Subject: [PATCH 3/3] chore: remove console logs --- .../migrations/standalone/0002-import-standalone-component.ts | 3 --- 1 file changed, 3 deletions(-) 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 81e7875..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 @@ -332,12 +332,10 @@ function detectIonicComponentsAndIcons(htmlAsString: string, filePath: string) { } } } else if (node.type === "ForLoopBlock") { - console.log(node) for (const childNode of node.children) { recursivelyFindIonicComponents(childNode); } } else if (node.type === "SwitchBlock") { - console.log(node); for (const c of node.cases) { for (const childNode of c.children) { recursivelyFindIonicComponents(childNode); @@ -346,7 +344,6 @@ function detectIonicComponentsAndIcons(htmlAsString: string, filePath: string) { } else if (node.type === "DeferredBlock") { if (node.children) { for (const childNode of node.children) { - console.log(childNode); recursivelyFindIonicComponents(childNode); } }