Skip to content

Commit a7e592b

Browse files
authored
Merge pull request #8 from ionic-team/sp/migrate-standalone-class
fix: remove IonicModule for migrated standalone components Resolves #4
2 parents 6099eab + d68a8b8 commit a7e592b

File tree

5 files changed

+126
-2
lines changed

5 files changed

+126
-2
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,13 @@ describe("migrateComponents", () => {
7474

7575
const component = `
7676
import { Component } from "@angular/core";
77+
import { IonicModule } from "@ionic/angular";
7778
7879
@Component({
7980
selector: 'my-component',
8081
templateUrl: './my-component.component.html',
81-
standalone: true
82+
standalone: true,
83+
imports: [IonicModule]
8284
})
8385
export class MyComponent { }
8486
`;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
getAngularComponentDecorator,
1818
isAngularComponentClass,
1919
isAngularComponentStandalone,
20+
removeImportFromComponentDecorator,
2021
} from "../../utils/angular-utils";
2122
import { IONIC_COMPONENTS } from "../../utils/ionic-utils";
2223
import {
@@ -26,6 +27,7 @@ import {
2627
import {
2728
addImportToClass,
2829
getOrCreateConstructor,
30+
removeImportFromClass,
2931
} from "../../utils/typescript-utils";
3032
import { saveFileChanges } from "../../utils/log-utils";
3133

@@ -115,6 +117,8 @@ async function migrateAngularComponentClass(
115117
if (isAngularComponentStandalone(sourceFile)) {
116118
const componentClassName = kebabCaseToPascalCase(ionicComponent);
117119
addImportToComponentDecorator(sourceFile, componentClassName);
120+
removeImportFromComponentDecorator(sourceFile, 'IonicModule');
121+
removeImportFromClass(sourceFile, 'IonicModule', '@ionic/angular');
118122
addImportToClass(
119123
sourceFile,
120124
componentClassName,

packages/cli/src/angular/utils/angular-utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,29 @@ export function addImportToComponentDecorator(
122122
sourceFile.formatText();
123123
}
124124

125+
/**
126+
* Removes an import from the imports array in the Component decorator.
127+
* @param sourceFile The source file to remove the import from.
128+
* @param importName The name of the import to remove.
129+
*/
130+
export function removeImportFromComponentDecorator(
131+
sourceFile: SourceFile,
132+
importName: string,
133+
) {
134+
if (!isAngularComponentStandalone(sourceFile)) {
135+
console.warn(
136+
"[Ionic Dev] Cannot remove import from component decorator. Component is not standalone.",
137+
);
138+
return;
139+
}
140+
141+
const componentDecorator = getAngularComponentDecorator(sourceFile)!;
142+
143+
deleteFromDecoratorArgArray(componentDecorator, "imports", importName);
144+
145+
sourceFile.formatText();
146+
}
147+
125148
/**
126149
* Adds a new import to the imports array in the NgModule decorator.
127150
* @param sourceFile The source file to add the import to.

packages/cli/src/angular/utils/typescript-utils.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
22
import { Project } from "ts-morph";
33
import { dedent } from "ts-dedent";
44

5-
import { getOrCreateConstructor, addImportToClass } from "./typescript-utils";
5+
import { getOrCreateConstructor, addImportToClass, removeImportFromClass } from "./typescript-utils";
66

77
describe("getOrCreateConstructor", () => {
88
it("should return the existing constructor", () => {
@@ -109,3 +109,60 @@ describe("addImportToClass", () => {
109109
);
110110
});
111111
});
112+
113+
describe("removeImportFromClass", () => {
114+
115+
it("should remove an import from a class", () => {
116+
const sourceFileContent = `
117+
import { Component } from "@angular/core";
118+
export class Foo { }
119+
`;
120+
121+
const project = new Project({ useInMemoryFileSystem: true });
122+
const sourceFile = project.createSourceFile("foo.ts", sourceFileContent);
123+
124+
removeImportFromClass(sourceFile, "Component", "@angular/core");
125+
126+
expect(dedent(sourceFile.getText())).toBe(
127+
dedent(`
128+
export class Foo { }
129+
`),
130+
);
131+
});
132+
133+
it("should remove an import from an existing import declaration", () => {
134+
const sourceFileContent = `
135+
import { Injectable, Component } from "@angular/core";
136+
export class Foo { }
137+
`;
138+
139+
const project = new Project({ useInMemoryFileSystem: true });
140+
const sourceFile = project.createSourceFile("foo.ts", sourceFileContent);
141+
142+
removeImportFromClass(sourceFile, "Component", "@angular/core");
143+
144+
expect(dedent(sourceFile.getText())).toBe(
145+
dedent(`
146+
import { Injectable } from "@angular/core";
147+
export class Foo { }
148+
`),
149+
);
150+
});
151+
152+
it("should do nothing if the source file does not have a class", () => {
153+
const sourceFileContent = `
154+
export function foo() { }
155+
`;
156+
157+
const project = new Project({ useInMemoryFileSystem: true });
158+
const sourceFile = project.createSourceFile("foo.ts", sourceFileContent);
159+
160+
removeImportFromClass(sourceFile, "Component", "@angular/core");
161+
162+
expect(dedent(sourceFile.getText())).toBe(
163+
dedent(`
164+
export function foo() { }
165+
`),
166+
);
167+
});
168+
});

packages/cli/src/angular/utils/typescript-utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,41 @@ export function addImportToClass(
5353
addImport(sourceFile, importName, moduleSpecifier);
5454
}
5555
}
56+
57+
export function removeImportFromClass(
58+
sourceFile: SourceFile,
59+
importName: string | string[],
60+
moduleSpecifier: string,
61+
) {
62+
const removeImport = (
63+
sourceFile: SourceFile,
64+
importName: string,
65+
moduleSpecifier: string,
66+
) => {
67+
const importDeclaration = sourceFile.getImportDeclaration(moduleSpecifier);
68+
69+
if (!importDeclaration) {
70+
return;
71+
}
72+
73+
const importSpecifier = importDeclaration
74+
.getNamedImports()
75+
.find((n) => n.getName() === importName);
76+
77+
if (importSpecifier) {
78+
importSpecifier.remove();
79+
}
80+
81+
if (importDeclaration.getNamedImports().length === 0) {
82+
importDeclaration.remove();
83+
}
84+
};
85+
86+
if (Array.isArray(importName)) {
87+
importName.forEach((name) => {
88+
removeImport(sourceFile, name, moduleSpecifier);
89+
});
90+
} else {
91+
removeImport(sourceFile, importName, moduleSpecifier);
92+
}
93+
}

0 commit comments

Comments
 (0)