Skip to content

Commit c33b19d

Browse files
authored
Merge pull request #1 from rdlabo-team/feat/1.0.0
1.0.0
2 parents 9961479 + d0a8c56 commit c33b19d

16 files changed

Lines changed: 609 additions & 34 deletions

README.md

Lines changed: 117 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
## What is this?
44

5-
This library is used to uniquely group the ionIcons in a project and export them from `use-icons.ts`. In small projects, it is difficult to manage `addIcons()` of ionIcons each time, so we automated it.
5+
This library is used to uniquely group the ionIcons in a project, and generate for export ionIcons file. In small projects, it is difficult to manage `addIcons()` of ionIcons each time, so we automated it.
66

7-
- development: Stress-free development by `addIcons()` for all icons.
7+
- development: Stress-free development by add all icons at `addIcons`.
88
- Production: Automatically collect and update the ionIcon used in the template prior to build.
99

1010
Of course, to maximize bundle size reduction, it is important to load a minimum number of icons at each Component lazy loading. This is a compromise to speed up development.
@@ -14,17 +14,32 @@ This project is based [ionic-team/ionic-angular-standalone-codemods](https://git
1414
> [!WARNING]
1515
> This project is experimental. Review all changes before committing them to your project.
1616
17-
## Usage
1817

19-
1. Run the CLI
18+
## Initialize
19+
20+
__This is beta version.__
21+
22+
```bash
23+
npm install @rdlabo/ionic-angular-collect-icons@beta --save-dev
24+
```
25+
26+
### 🤖 Automatic Configuration
27+
28+
```bash
29+
npx @rdlabo/ionic-angular-collect-icons --initialize true
30+
```
31+
32+
### 📝 Manual Configuration
33+
34+
#### 1. Run the CLI
2035

2136
```bash
2237
npx @rdlabo/ionic-angular-collect-icons
2338
```
2439

25-
This will overwrite `use-icons.ts` if it exists, or automatically generate `src/use-icons.ts` if not. If you wish to place this file in an arbitrary location, move it.
40+
This will generate `src/use-icons.ts`.
2641

27-
2. Import the generated file in your `main.ts` ( or `app.config.ts` ) file:
42+
#### 2. Import the generated file in your `main.ts` ( or `app.config.ts` ) file:
2843

2944
```diff
3045
+ import { addIcons } from 'ionicons';
@@ -38,22 +53,111 @@ This will overwrite `use-icons.ts` if it exists, or automatically generate `src/
3853
+ addIcons(environment.production ? useIcons : allIcons);
3954
```
4055

41-
- Can run addIcons in main.ts?
56+
#### 3. Remove other `addIcons` calls in class constructor
4257

43-
Yes. Please check this issue: https://github.com/ionic-team/ionic-framework/issues/28445#issuecomment-1789028722
58+
```diff
59+
@Component(/* ... */)
60+
export class ExampleComponent {
61+
constructor() {
62+
- addIcons(useIcons);
63+
}
64+
}
65+
```
4466

45-
> You're more than welcome to register them in main.ts or app.component.ts. You can then use them anywhere in your application. However, the initial bundle size may increase because the icons need to be loaded up front.
67+
## Usage
4668

47-
3. Add npm script for generate `use-icons.ts` file at every build:
69+
```bash
70+
npx @rdlabo/ionic-angular-collect-icons
71+
```
72+
73+
### Let's automate run
74+
75+
It is inefficient to run commands each time before running a production build, so put them in an npm script to automate the process. Example:
4876

4977
```diff
5078
"scripts": {
5179
"ng": "ng",
52-
"start": "ng run serve",
53-
"build": "ng build --localize",
54-
+ "prebuild": "npx @rdlabo/ionic-angular-collect-icons --non-interactive false",
80+
"start": "ng serve",
81+
"build": "ng build",
82+
+ "prebuild": "npx @rdlabo/ionic-angular-collect-icons",
83+
```
84+
85+
> [!WARNING]
86+
> This method cannot be used for production builds without using the npm script.
87+
88+
## Optional
89+
90+
### --interactive [boolean]
91+
92+
If you want to set all CLI option using the prompts, set `true`. This can be used to check only the results in a Dry run.
93+
The default is `false`.
94+
95+
```bash
96+
npx @rdlabo/ionic-angular-collect-icons --interactive true
5597
```
5698

99+
### --initialize [boolean]
100+
101+
If you want to initialize `addIcons` automatically, you can use the `--initialize` flag. The default is `false`. The CLI add lines:
102+
103+
```diff
104+
+ import { addIcons } from 'ionicons';
105+
+ import * as allIcons from 'ionicons/icons';
106+
+ import * as useIcons from '../use-icons';
107+
108+
if (environment.production) {
109+
enableProdMode();
110+
}
111+
112+
+ addIcons(environment.production ? useIcons : allIcons);
113+
```
114+
115+
the CLI will add lines at the file that has `enableProdMode()`. Of course, it can also be set manually.
116+
117+
And remove other `addIcons` calls in class constructor.
118+
119+
```diff
120+
@Component(/* ... */)
121+
export class ExampleComponent {
122+
constructor() {
123+
- addIcons(useIcons);
124+
}
125+
}
126+
```
127+
128+
```bash
129+
npx @rdlabo/ionic-angular-collect-icons --initialize true
130+
```
131+
132+
### --project-path [string]
133+
134+
If you want to specify the path to the project, you can use the `--project-path` flag. The default is the current directory. 
135+
136+
```bash
137+
npx @rdlabo/ionic-angular-collect-icons --project-path /path/to/project
138+
```
139+
140+
Target files are under the `src` directory from the specified path.
141+
- path/to/project + `src/**/*.ts`
142+
- path/to/project + `src/**/*.html`
143+
- path/to/project + `src/**/*.scss`
144+
145+
### --icon-path [string]
146+
147+
Default create file is (path/to/project +) `src/use-icons.ts`. If you want to specify the file name, you can use the `--icon-path` flag.
148+
149+
```bash
150+
npx @rdlabo/ionic-angular-collect-icons --icon-path src/other-use-icons.ts
151+
```
152+
153+
## FAQ
154+
155+
- Can run addIcons in main.ts?
156+
157+
Yes. Please check this issue: https://github.com/ionic-team/ionic-framework/issues/28445#issuecomment-1789028722
158+
159+
> You're more than welcome to register them in main.ts or app.component.ts. You can then use them anywhere in your application. However, the initial bundle size may increase because the icons need to be loaded up front.
160+
57161
## Developing
58162

59163
1. Clone this repository.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rdlabo/ionic-angular-collect-icons",
3-
"version": "0.1.1",
3+
"version": "1.0.0-beta.2",
44
"repository": {
55
"type": "git",
66
"url": "[email protected]:rdlabo-team/ionic-angular-collect-icons.git"

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rdlabo/ionic-angular-collect-icons",
3-
"version": "0.1.1",
3+
"version": "1.0.0-beta.2",
44
"dependencies": {
55
"@angular-eslint/template-parser": "^16.1.2",
66
"@clack/core": "^0.3.3",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, it, expect } from "vitest";
2+
import { Project } from "ts-morph";
3+
import { dedent } from "ts-dedent";
4+
5+
import { initializeAddIcons } from "./0000-initialize-add-icons";
6+
import {cwd} from 'node:process';
7+
8+
describe("initializeAddIcons", () => {
9+
it("initialize add icons to file", async () => {
10+
const project = new Project({ useInMemoryFileSystem: true });
11+
12+
const appModuleSourceFile = project.createSourceFile(
13+
cwd() + "/src/main.ts",
14+
dedent(`
15+
import { environment } from './environments/environment';
16+
17+
if (environment.production) {
18+
enableProdMode();
19+
}
20+
21+
bootstrapApplication(AppComponent, {
22+
providers: [
23+
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
24+
provideIonicAngular(),
25+
provideRouter(routes),
26+
],
27+
});
28+
`),
29+
);
30+
31+
await initializeAddIcons(project, { dryRun: false, iconPath: "src/use-icons.ts", projectPath: cwd(), interactive: false, initialize: false });
32+
33+
expect(dedent(appModuleSourceFile.getText())).toBe(
34+
dedent(`
35+
import { environment } from './environments/environment';
36+
import { addIcons } from "ionicons";
37+
import * as allIcons from "ionicons/icons";
38+
import * as useIcons from "use-icons";
39+
40+
if (environment.production) {
41+
enableProdMode();
42+
}
43+
addIcons(environment.production ? useIcons : allIcons);
44+
45+
bootstrapApplication(AppComponent, {
46+
providers: [
47+
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
48+
provideIonicAngular(),
49+
provideRouter(routes),
50+
],
51+
});
52+
`));
53+
});
54+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Project, SyntaxKind, ts } from "ts-morph";
2+
import { CliOptions } from "../../../types/cli-options";
3+
4+
import { saveFileChanges } from "../../utils/log-utils";
5+
import {addExportToFile, addImportToClass, addImportToFile} from '../../utils/typescript-utils';
6+
import {getRelativePath} from '../../utils/cli-utils';
7+
8+
export const initializeAddIcons = async (
9+
project: Project,
10+
cliOptions: CliOptions,
11+
) => {
12+
const prodModeSource = project.getSourceFile("app.config.ts") || project.getSourceFile("main.ts");
13+
14+
if (prodModeSource === undefined) {
15+
// If the project does not base angular standalone structured, do nothing.
16+
return;
17+
}
18+
19+
const enableProdMode = prodModeSource.getStatements().find((source) => source.getFullText().includes('enableProdMode()'));
20+
if (!enableProdMode) {
21+
// If the project does not base angular standalone structured, do nothing.
22+
return;
23+
}
24+
25+
const importIonIcons = prodModeSource.getImportDeclaration("ionicons");
26+
if (importIonIcons) {
27+
const namedIconsImports = importIonIcons.getNamedImports();
28+
const importIconSpecifier = namedIconsImports.find(
29+
(n) => n.getName() === "addIcons",
30+
);
31+
if (importIconSpecifier) {
32+
// Remove the addIcons import specifier.
33+
const addIcons = prodModeSource.getStatements().find((l) => {
34+
return l.getFullText().includes("addIcons");
35+
});
36+
if (addIcons) {
37+
// already initialize
38+
return;
39+
} else {
40+
// remove for initialize
41+
importIconSpecifier.remove();
42+
}
43+
}
44+
}
45+
46+
addImportToFile(prodModeSource, "addIcons", "ionicons");
47+
prodModeSource.addImportDeclaration({
48+
defaultImport: '* as allIcons',
49+
moduleSpecifier: 'ionicons/icons'
50+
});
51+
52+
const path = getRelativePath(prodModeSource.getFilePath(), [cliOptions.projectPath, cliOptions.iconPath].join('/'));
53+
prodModeSource.addImportDeclaration({
54+
defaultImport: '* as useIcons',
55+
moduleSpecifier: path.replace('.ts', ''),
56+
});
57+
58+
prodModeSource.insertStatements(enableProdMode.getChildIndex() + 1, `addIcons(environment.production ? useIcons : allIcons);`);
59+
60+
return await saveFileChanges(prodModeSource, cliOptions);
61+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { describe, it, expect } from "vitest";
2+
import { Project } from "ts-morph";
3+
import { dedent } from "ts-dedent";
4+
5+
import { removeAddIcons } from "./0001-remove-add-icons";
6+
import {cwd} from 'node:process';
7+
8+
describe("migrateAppModule", () => {
9+
it("should remove addIcons method", async () => {
10+
const project = new Project({ useInMemoryFileSystem: true });
11+
12+
const component = `
13+
import { Component } from "@angular/core";
14+
import { addIcons } from "ionicons";
15+
import { readerOutline } from "ionicons/icons";
16+
17+
@Component({
18+
selector: 'my-component',
19+
template: '',
20+
standalone: true
21+
})
22+
export class MyComponent {
23+
constructor() {
24+
addIcons([readerOutline]);
25+
}
26+
}
27+
`;
28+
29+
const componentSourceFile = project.createSourceFile(
30+
"foo.component.ts",
31+
dedent(component),
32+
);
33+
34+
await removeAddIcons(project, { dryRun: false, iconPath: "src/use-icons.ts", projectPath: cwd(), interactive: false, initialize: false });
35+
36+
expect(dedent(componentSourceFile.getText())).toBe(
37+
dedent(`
38+
import { Component } from "@angular/core";
39+
40+
@Component({
41+
selector: 'my-component',
42+
template: '',
43+
standalone: true
44+
})
45+
export class MyComponent {
46+
constructor() {
47+
}
48+
}
49+
`),
50+
);
51+
});
52+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Project, ts } from "ts-morph";
2+
import { CliOptions } from "../../../types/cli-options";
3+
4+
import { saveFileChanges } from "../../utils/log-utils";
5+
6+
export const removeAddIcons = async (
7+
project: Project,
8+
cliOptions: CliOptions,
9+
) => {
10+
for (const sourceFile of project.getSourceFiles()) {
11+
const importAddIcons = sourceFile.getImportDeclaration("ionicons");
12+
if (!importAddIcons) {
13+
// If the ionicons import does not exist, then this file do not use ion-icon
14+
continue;
15+
}
16+
importAddIcons.remove();
17+
18+
const importIcons = sourceFile.getImportDeclaration("ionicons/icons");
19+
if (importIcons) {
20+
importIcons.remove();
21+
}
22+
23+
const constructor = sourceFile.getClasses()[0]?.getConstructors()[0];
24+
if (!constructor) {
25+
// Create the constructor if it does not exist
26+
continue;
27+
}
28+
29+
const addIcons = constructor.getStatements().find((l) => {
30+
return l.getFullText().includes("addIcons");
31+
});
32+
33+
if (addIcons) {
34+
addIcons.remove();
35+
}
36+
await saveFileChanges(sourceFile, cliOptions);
37+
}
38+
};

0 commit comments

Comments
 (0)