@vuonweb/icon-builder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.js +332 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 icon-builder contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/build.ts
|
|
4
|
+
import { mkdir, readdir, rm, writeFile } from "fs/promises";
|
|
5
|
+
import path3 from "path";
|
|
6
|
+
import { buildIcons } from "@icon-builder/core";
|
|
7
|
+
|
|
8
|
+
// ../core/src/naming.ts
|
|
9
|
+
function toKebabCase(input) {
|
|
10
|
+
return input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ../../templates/shared.ts
|
|
14
|
+
var GENERATED_MARKER = "@generated";
|
|
15
|
+
function generatedHeader() {
|
|
16
|
+
return `/**
|
|
17
|
+
* File n\xE0y \u0111\u01B0\u1EE3c sinh t\u1EF1 \u0111\u1ED9ng b\u1EDFi icon-builder. Kh\xF4ng s\u1EEDa tay.
|
|
18
|
+
* ${GENERATED_MARKER}
|
|
19
|
+
*/
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
function generatedHtmlComment() {
|
|
23
|
+
return `<!--
|
|
24
|
+
File n\xE0y \u0111\u01B0\u1EE3c sinh t\u1EF1 \u0111\u1ED9ng b\u1EDFi icon-builder. Kh\xF4ng s\u1EEDa tay.
|
|
25
|
+
${GENERATED_MARKER}
|
|
26
|
+
-->
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
function renderTypesFile(icons) {
|
|
30
|
+
const entries = icons.map((icon) => ` | "${toKebabCase(icon.name)}"`).join("\n");
|
|
31
|
+
return `${generatedHeader()}
|
|
32
|
+
export type IconName =
|
|
33
|
+
${entries};
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
function makeIndexFileRenderer(extension, style) {
|
|
37
|
+
return (icons) => {
|
|
38
|
+
const line = (name) => style === "named" ? `export * from "./${name}${extension}";` : `export { default as ${name} } from "./${name}${extension}";`;
|
|
39
|
+
const iconLines = icons.map((icon) => line(icon.name)).join("\n");
|
|
40
|
+
return `${generatedHeader()}
|
|
41
|
+
${line("Icon")}
|
|
42
|
+
${iconLines}
|
|
43
|
+
`;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function makeIconMapFileRenderer(extension, style) {
|
|
47
|
+
return (icons) => {
|
|
48
|
+
const importLine = (name) => style === "named" ? `import { ${name} } from "./${name}${extension}";` : `import ${name} from "./${name}${extension}";`;
|
|
49
|
+
const imports = icons.map((icon) => importLine(icon.name)).join("\n");
|
|
50
|
+
const entries = icons.map((icon) => ` ${icon.name},`).join("\n");
|
|
51
|
+
return `${generatedHeader()}
|
|
52
|
+
${imports}
|
|
53
|
+
|
|
54
|
+
export const iconMap = {
|
|
55
|
+
${entries}
|
|
56
|
+
} as const;
|
|
57
|
+
`;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/config.ts
|
|
62
|
+
import path from "path";
|
|
63
|
+
var REPO_ROOT = process.cwd();
|
|
64
|
+
var ICONS_DIR = path.resolve(REPO_ROOT, "icons");
|
|
65
|
+
|
|
66
|
+
// src/fs-utils.ts
|
|
67
|
+
import { readFile } from "fs/promises";
|
|
68
|
+
import prettier from "prettier";
|
|
69
|
+
import * as prettierPluginSvelte from "prettier-plugin-svelte";
|
|
70
|
+
async function readFileSafe(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return await readFile(filePath, "utf8");
|
|
73
|
+
} catch {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function formatCode(source, filePath) {
|
|
78
|
+
const config = await prettier.resolveConfig(filePath) ?? {};
|
|
79
|
+
const plugins = filePath.endsWith(".svelte") ? [prettierPluginSvelte] : void 0;
|
|
80
|
+
return prettier.format(source, { ...config, filepath: filePath, plugins });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/targets.ts
|
|
84
|
+
import path2 from "path";
|
|
85
|
+
|
|
86
|
+
// ../../templates/react/component.ts
|
|
87
|
+
function staticColorProp(icon) {
|
|
88
|
+
return icon.style === "outline" || icon.style === "rounded" ? 'fill="none"' : 'stroke="none"';
|
|
89
|
+
}
|
|
90
|
+
function renderReactComponent(icon) {
|
|
91
|
+
return `${generatedHeader()}
|
|
92
|
+
import * as React from "react";
|
|
93
|
+
import { BaseIcon, type IconProps } from "./Icon.js";
|
|
94
|
+
|
|
95
|
+
export const ${icon.name} = React.forwardRef<SVGSVGElement, IconProps>((props, ref) => {
|
|
96
|
+
return (
|
|
97
|
+
<BaseIcon ref={ref} viewBox="${icon.viewBox}" ${staticColorProp(icon)} {...props}>
|
|
98
|
+
${icon.body}
|
|
99
|
+
</BaseIcon>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
${icon.name}.displayName = "${icon.name}";
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ../../templates/react-native/component.ts
|
|
108
|
+
function capitalizeSvgTags(body) {
|
|
109
|
+
return body.replace(/<\/?([a-zA-Z][\w-]*)/g, (match, tag) => {
|
|
110
|
+
const capitalized = tag.charAt(0).toUpperCase() + tag.slice(1);
|
|
111
|
+
return match.replace(tag, capitalized);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function extractUsedTags(body) {
|
|
115
|
+
const tags = /* @__PURE__ */ new Set();
|
|
116
|
+
const regex = /<([A-Z][\w]*)/g;
|
|
117
|
+
let match;
|
|
118
|
+
while (match = regex.exec(body)) {
|
|
119
|
+
if (match[1]) tags.add(match[1]);
|
|
120
|
+
}
|
|
121
|
+
return [...tags].sort();
|
|
122
|
+
}
|
|
123
|
+
function staticColorProp2(icon) {
|
|
124
|
+
return icon.style === "outline" || icon.style === "rounded" ? 'fill="none"' : 'stroke="none"';
|
|
125
|
+
}
|
|
126
|
+
function renderReactNativeComponent(icon) {
|
|
127
|
+
const body = capitalizeSvgTags(icon.body);
|
|
128
|
+
const tags = extractUsedTags(body);
|
|
129
|
+
return `${generatedHeader()}
|
|
130
|
+
import * as React from "react";
|
|
131
|
+
import { ${tags.join(", ")} } from "react-native-svg";
|
|
132
|
+
import { BaseIcon, type IconProps, type IconRef } from "./Icon.js";
|
|
133
|
+
|
|
134
|
+
export const ${icon.name} = React.forwardRef<IconRef, IconProps>((props, ref) => {
|
|
135
|
+
return (
|
|
136
|
+
<BaseIcon ref={ref} viewBox="${icon.viewBox}" ${staticColorProp2(icon)} {...props}>
|
|
137
|
+
${body}
|
|
138
|
+
</BaseIcon>
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
${icon.name}.displayName = "${icon.name}";
|
|
143
|
+
`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ../../templates/svelte/component.ts
|
|
147
|
+
function staticColorProp3(icon) {
|
|
148
|
+
return icon.style === "outline" || icon.style === "rounded" ? 'fill="none"' : 'stroke="none"';
|
|
149
|
+
}
|
|
150
|
+
function renderSvelteComponent(icon) {
|
|
151
|
+
return `${generatedHtmlComment()}
|
|
152
|
+
<script lang="ts">
|
|
153
|
+
import Icon from "./Icon.svelte";
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<Icon viewBox="${icon.viewBox}" ${staticColorProp3(icon)} {...$$restProps}>
|
|
157
|
+
${icon.body}
|
|
158
|
+
</Icon>
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ../../templates/vue/component.ts
|
|
163
|
+
function staticColorProp4(icon) {
|
|
164
|
+
return icon.style === "outline" || icon.style === "rounded" ? 'fill="none"' : 'stroke="none"';
|
|
165
|
+
}
|
|
166
|
+
function renderVueComponent(icon) {
|
|
167
|
+
return `${generatedHtmlComment()}
|
|
168
|
+
<script setup lang="ts">
|
|
169
|
+
import BaseIcon from "./Icon.vue";
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<template>
|
|
173
|
+
<BaseIcon viewBox="${icon.viewBox}" ${staticColorProp4(icon)}>
|
|
174
|
+
${icon.body}
|
|
175
|
+
</BaseIcon>
|
|
176
|
+
</template>
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/targets.ts
|
|
181
|
+
var namedJs = { renderIndex: makeIndexFileRenderer(".js", "named"), renderIconMap: makeIconMapFileRenderer(".js", "named") };
|
|
182
|
+
var targets = [
|
|
183
|
+
{
|
|
184
|
+
name: "react",
|
|
185
|
+
outDir: path2.resolve(REPO_ROOT, "packages/react/src"),
|
|
186
|
+
componentExtension: ".tsx",
|
|
187
|
+
renderComponent: renderReactComponent,
|
|
188
|
+
renderTypes: renderTypesFile,
|
|
189
|
+
...namedJs
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "react-native",
|
|
193
|
+
outDir: path2.resolve(REPO_ROOT, "packages/react-native/src"),
|
|
194
|
+
componentExtension: ".tsx",
|
|
195
|
+
renderComponent: renderReactNativeComponent,
|
|
196
|
+
renderTypes: renderTypesFile,
|
|
197
|
+
...namedJs
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "vue",
|
|
201
|
+
outDir: path2.resolve(REPO_ROOT, "packages/vue/src"),
|
|
202
|
+
componentExtension: ".vue",
|
|
203
|
+
renderComponent: renderVueComponent,
|
|
204
|
+
renderTypes: renderTypesFile,
|
|
205
|
+
renderIndex: makeIndexFileRenderer(".vue", "default"),
|
|
206
|
+
renderIconMap: makeIconMapFileRenderer(".vue", "default")
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "svelte",
|
|
210
|
+
outDir: path2.resolve(REPO_ROOT, "packages/svelte/src"),
|
|
211
|
+
componentExtension: ".svelte",
|
|
212
|
+
renderComponent: renderSvelteComponent,
|
|
213
|
+
renderTypes: renderTypesFile,
|
|
214
|
+
renderIndex: makeIndexFileRenderer(".svelte", "default"),
|
|
215
|
+
renderIconMap: makeIconMapFileRenderer(".svelte", "default")
|
|
216
|
+
}
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
// src/commands/build.ts
|
|
220
|
+
async function runBuild() {
|
|
221
|
+
console.log("[icon-builder] \u0110ang \u0111\u1ECDc v\xE0 x\u1EED l\xFD SVG...");
|
|
222
|
+
const icons = await buildIcons({
|
|
223
|
+
iconsDir: ICONS_DIR,
|
|
224
|
+
onWarning: (filePath, warnings) => {
|
|
225
|
+
console.warn(` \u26A0 ${path3.relative(REPO_ROOT, filePath)}`);
|
|
226
|
+
for (const warning of warnings) console.warn(` - ${warning}`);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
console.log(`[icon-builder] \u0110\xE3 x\u1EED l\xFD ${icons.length} icon.`);
|
|
230
|
+
for (const target of targets) {
|
|
231
|
+
await mkdir(target.outDir, { recursive: true });
|
|
232
|
+
await removeGeneratedFiles(target.outDir);
|
|
233
|
+
for (const icon of icons) {
|
|
234
|
+
await writeGenerated(
|
|
235
|
+
path3.join(target.outDir, `${icon.name}${target.componentExtension}`),
|
|
236
|
+
target.renderComponent(icon)
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
await writeGenerated(path3.join(target.outDir, "index.ts"), target.renderIndex(icons));
|
|
240
|
+
await writeGenerated(path3.join(target.outDir, "types.ts"), target.renderTypes(icons));
|
|
241
|
+
await writeGenerated(path3.join(target.outDir, "icon-map.ts"), target.renderIconMap(icons));
|
|
242
|
+
console.log(`[icon-builder] ${target.name}: generate xong t\u1EA1i ${path3.relative(REPO_ROOT, target.outDir)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function writeGenerated(filePath, source) {
|
|
246
|
+
const formatted = await formatCode(source, filePath);
|
|
247
|
+
await writeFile(filePath, formatted, "utf8");
|
|
248
|
+
}
|
|
249
|
+
async function removeGeneratedFiles(outDir) {
|
|
250
|
+
const entries = await readdir(outDir).catch(() => []);
|
|
251
|
+
for (const entry of entries) {
|
|
252
|
+
const filePath = path3.join(outDir, entry);
|
|
253
|
+
const content = await readFileSafe(filePath);
|
|
254
|
+
if (content?.includes(GENERATED_MARKER)) {
|
|
255
|
+
await rm(filePath);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/commands/clean.ts
|
|
261
|
+
import { readdir as readdir2, rm as rm2 } from "fs/promises";
|
|
262
|
+
import path4 from "path";
|
|
263
|
+
async function runClean() {
|
|
264
|
+
let removed = 0;
|
|
265
|
+
for (const target of targets) {
|
|
266
|
+
const entries = await readdir2(target.outDir).catch(() => []);
|
|
267
|
+
for (const entry of entries) {
|
|
268
|
+
const filePath = path4.join(target.outDir, entry);
|
|
269
|
+
const content = await readFileSafe(filePath);
|
|
270
|
+
if (content?.includes(GENERATED_MARKER)) {
|
|
271
|
+
await rm2(filePath);
|
|
272
|
+
removed++;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
console.log(`[icon-builder] \u0110\xE3 xo\xE1 ${removed} file generated.`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/commands/watch.ts
|
|
280
|
+
import chokidar from "chokidar";
|
|
281
|
+
async function runWatch() {
|
|
282
|
+
console.log(`[icon-builder] \u0110ang theo d\xF5i th\u01B0 m\u1EE5c icons/ ...`);
|
|
283
|
+
await runBuild().catch((error) => logError(error));
|
|
284
|
+
let timer;
|
|
285
|
+
const debouncedBuild = () => {
|
|
286
|
+
clearTimeout(timer);
|
|
287
|
+
timer = setTimeout(() => {
|
|
288
|
+
console.log("[icon-builder] Ph\xE1t hi\u1EC7n thay \u0111\u1ED5i, \u0111ang rebuild...");
|
|
289
|
+
runBuild().catch((error) => logError(error));
|
|
290
|
+
}, 200);
|
|
291
|
+
};
|
|
292
|
+
chokidar.watch("**/*.svg", { cwd: ICONS_DIR, ignoreInitial: true }).on("add", debouncedBuild).on("change", debouncedBuild).on("unlink", debouncedBuild);
|
|
293
|
+
}
|
|
294
|
+
function logError(error) {
|
|
295
|
+
console.error(error instanceof Error ? error.message : error);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/index.ts
|
|
299
|
+
var [, , command] = process.argv;
|
|
300
|
+
async function main() {
|
|
301
|
+
switch (command) {
|
|
302
|
+
case "build":
|
|
303
|
+
await runBuild();
|
|
304
|
+
break;
|
|
305
|
+
case "watch":
|
|
306
|
+
await runWatch();
|
|
307
|
+
break;
|
|
308
|
+
case "clean":
|
|
309
|
+
await runClean();
|
|
310
|
+
break;
|
|
311
|
+
default:
|
|
312
|
+
printHelp();
|
|
313
|
+
process.exitCode = command ? 1 : 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function printHelp() {
|
|
317
|
+
console.log(`icon-builder <command>
|
|
318
|
+
|
|
319
|
+
Commands:
|
|
320
|
+
build \u0110\u1ECDc SVG, validate, optimize v\xE0 generate React package
|
|
321
|
+
watch Theo d\xF5i th\u01B0 m\u1EE5c icons/ v\xE0 t\u1EF1 \u0111\u1ED9ng rebuild khi c\xF3 thay \u0111\u1ED5i
|
|
322
|
+
clean Xo\xE1 to\xE0n b\u1ED9 file generated
|
|
323
|
+
|
|
324
|
+
V\xED d\u1EE5:
|
|
325
|
+
icon-builder build
|
|
326
|
+
`);
|
|
327
|
+
}
|
|
328
|
+
main().catch((error) => {
|
|
329
|
+
console.error(error instanceof Error ? error.message : error);
|
|
330
|
+
process.exitCode = 1;
|
|
331
|
+
});
|
|
332
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/build.ts","../../core/src/naming.ts","../../../templates/shared.ts","../src/config.ts","../src/fs-utils.ts","../src/targets.ts","../../../templates/react/component.ts","../../../templates/react-native/component.ts","../../../templates/svelte/component.ts","../../../templates/vue/component.ts","../src/commands/clean.ts","../src/commands/watch.ts","../src/index.ts"],"sourcesContent":["import { mkdir, readdir, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { buildIcons } from \"@icon-builder/core\";\nimport { GENERATED_MARKER } from \"../../../../templates/shared.js\";\nimport { ICONS_DIR, REPO_ROOT } from \"../config.js\";\nimport { formatCode, readFileSafe } from \"../fs-utils.js\";\nimport { targets } from \"../targets.js\";\n\nexport async function runBuild(): Promise<void> {\n console.log(\"[icon-builder] Đang đọc và xử lý SVG...\");\n\n const icons = await buildIcons({\n iconsDir: ICONS_DIR,\n onWarning: (filePath, warnings) => {\n console.warn(` ⚠ ${path.relative(REPO_ROOT, filePath)}`);\n for (const warning of warnings) console.warn(` - ${warning}`);\n },\n });\n\n console.log(`[icon-builder] Đã xử lý ${icons.length} icon.`);\n\n for (const target of targets) {\n await mkdir(target.outDir, { recursive: true });\n await removeGeneratedFiles(target.outDir);\n\n for (const icon of icons) {\n await writeGenerated(\n path.join(target.outDir, `${icon.name}${target.componentExtension}`),\n target.renderComponent(icon),\n );\n }\n\n await writeGenerated(path.join(target.outDir, \"index.ts\"), target.renderIndex(icons));\n await writeGenerated(path.join(target.outDir, \"types.ts\"), target.renderTypes(icons));\n await writeGenerated(path.join(target.outDir, \"icon-map.ts\"), target.renderIconMap(icons));\n\n console.log(`[icon-builder] ${target.name}: generate xong tại ${path.relative(REPO_ROOT, target.outDir)}`);\n }\n}\n\nasync function writeGenerated(filePath: string, source: string): Promise<void> {\n const formatted = await formatCode(source, filePath);\n await writeFile(filePath, formatted, \"utf8\");\n}\n\nasync function removeGeneratedFiles(outDir: string): Promise<void> {\n const entries = await readdir(outDir).catch(() => [] as string[]);\n\n for (const entry of entries) {\n const filePath = path.join(outDir, entry);\n const content = await readFileSafe(filePath);\n if (content?.includes(GENERATED_MARKER)) {\n await rm(filePath);\n }\n }\n}\n","import type { IconStyle } from \"./types.js\";\n\nconst STYLE_SUFFIX: Record<IconStyle, string> = {\n outline: \"\",\n solid: \"Solid\",\n filled: \"Filled\",\n rounded: \"Rounded\",\n};\n\nexport function isKnownStyle(value: string): value is IconStyle {\n return value in STYLE_SUFFIX;\n}\n\nexport function toPascalCase(input: string): string {\n return input\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n}\n\nexport function toIconName(originalName: string, style: IconStyle): string {\n return `${toPascalCase(originalName)}${STYLE_SUFFIX[style]}`;\n}\n\nexport function toKebabCase(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n","import { toKebabCase } from \"../packages/core/src/naming.js\";\nimport type { IconDefinition } from \"../packages/core/src/types.js\";\n\nexport const GENERATED_MARKER = \"@generated\";\n\n/** Dùng cho file .ts thuần (index.ts, types.ts, icon-map.ts, component React/RN). */\nexport function generatedHeader(): string {\n return `/**\\n * File này được sinh tự động bởi icon-builder. Không sửa tay.\\n * ${GENERATED_MARKER}\\n */\\n`;\n}\n\n/** Dùng cho định dạng SFC (.vue, .svelte) — comment JS thường không hợp lệ ở ngoài <script>. */\nexport function generatedHtmlComment(): string {\n return `<!--\\n File này được sinh tự động bởi icon-builder. Không sửa tay.\\n ${GENERATED_MARKER}\\n-->\\n`;\n}\n\n// types.ts giống hệt nhau ở mọi target, chỉ phụ thuộc IconDefinition.name.\nexport function renderTypesFile(icons: IconDefinition[]): string {\n const entries = icons.map((icon) => ` | \"${toKebabCase(icon.name)}\"`).join(\"\\n\");\n return `${generatedHeader()}\\nexport type IconName =\\n${entries};\\n`;\n}\n\nexport type ExportStyle = \"named\" | \"default\";\n\n/**\n * React/React Native export named bindings (`export const Home = ...`), nên dùng\n * `export * from`. Vue/Svelte export default component từ mỗi SFC, nên phải rename\n * qua `export { default as Home }`.\n */\nexport function makeIndexFileRenderer(extension: string, style: ExportStyle) {\n return (icons: IconDefinition[]): string => {\n const line = (name: string): string =>\n style === \"named\"\n ? `export * from \"./${name}${extension}\";`\n : `export { default as ${name} } from \"./${name}${extension}\";`;\n\n const iconLines = icons.map((icon) => line(icon.name)).join(\"\\n\");\n return `${generatedHeader()}\\n${line(\"Icon\")}\\n${iconLines}\\n`;\n };\n}\n\nexport function makeIconMapFileRenderer(extension: string, style: ExportStyle) {\n return (icons: IconDefinition[]): string => {\n const importLine = (name: string): string =>\n style === \"named\"\n ? `import { ${name} } from \"./${name}${extension}\";`\n : `import ${name} from \"./${name}${extension}\";`;\n\n const imports = icons.map((icon) => importLine(icon.name)).join(\"\\n\");\n const entries = icons.map((icon) => ` ${icon.name},`).join(\"\\n\");\n\n return `${generatedHeader()}\\n${imports}\\n\\nexport const iconMap = {\\n${entries}\\n} as const;\\n`;\n };\n}\n","import path from \"node:path\";\n\nexport const REPO_ROOT = process.cwd();\nexport const ICONS_DIR = path.resolve(REPO_ROOT, \"icons\");\n","import { readFile } from \"node:fs/promises\";\nimport prettier from \"prettier\";\nimport * as prettierPluginSvelte from \"prettier-plugin-svelte\";\n\nexport async function readFileSafe(filePath: string): Promise<string | undefined> {\n try {\n return await readFile(filePath, \"utf8\");\n } catch {\n return undefined;\n }\n}\n\nexport async function formatCode(source: string, filePath: string): Promise<string> {\n const config = (await prettier.resolveConfig(filePath)) ?? {};\n // .vue dùng parser \"vue\" built-in sẵn trong prettier; .svelte cần plugin riêng.\n const plugins = filePath.endsWith(\".svelte\") ? [prettierPluginSvelte] : undefined;\n return prettier.format(source, { ...config, filepath: filePath, plugins });\n}\n","import path from \"node:path\";\nimport type { IconDefinition } from \"@icon-builder/core\";\nimport { renderReactComponent } from \"../../../templates/react/component.js\";\nimport { renderReactNativeComponent } from \"../../../templates/react-native/component.js\";\nimport {\n makeIconMapFileRenderer,\n makeIndexFileRenderer,\n renderTypesFile,\n} from \"../../../templates/shared.js\";\nimport { renderSvelteComponent } from \"../../../templates/svelte/component.js\";\nimport { renderVueComponent } from \"../../../templates/vue/component.js\";\nimport { REPO_ROOT } from \"./config.js\";\n\nexport type GenerateTarget = {\n name: string;\n outDir: string;\n componentExtension: string;\n renderComponent: (icon: IconDefinition) => string;\n renderIndex: (icons: IconDefinition[]) => string;\n renderTypes: (icons: IconDefinition[]) => string;\n renderIconMap: (icons: IconDefinition[]) => string;\n};\n\nconst namedJs = { renderIndex: makeIndexFileRenderer(\".js\", \"named\"), renderIconMap: makeIconMapFileRenderer(\".js\", \"named\") };\n\nexport const targets: GenerateTarget[] = [\n {\n name: \"react\",\n outDir: path.resolve(REPO_ROOT, \"packages/react/src\"),\n componentExtension: \".tsx\",\n renderComponent: renderReactComponent,\n renderTypes: renderTypesFile,\n ...namedJs,\n },\n {\n name: \"react-native\",\n outDir: path.resolve(REPO_ROOT, \"packages/react-native/src\"),\n componentExtension: \".tsx\",\n renderComponent: renderReactNativeComponent,\n renderTypes: renderTypesFile,\n ...namedJs,\n },\n {\n name: \"vue\",\n outDir: path.resolve(REPO_ROOT, \"packages/vue/src\"),\n componentExtension: \".vue\",\n renderComponent: renderVueComponent,\n renderTypes: renderTypesFile,\n renderIndex: makeIndexFileRenderer(\".vue\", \"default\"),\n renderIconMap: makeIconMapFileRenderer(\".vue\", \"default\"),\n },\n {\n name: \"svelte\",\n outDir: path.resolve(REPO_ROOT, \"packages/svelte/src\"),\n componentExtension: \".svelte\",\n renderComponent: renderSvelteComponent,\n renderTypes: renderTypesFile,\n renderIndex: makeIndexFileRenderer(\".svelte\", \"default\"),\n renderIconMap: makeIconMapFileRenderer(\".svelte\", \"default\"),\n },\n];\n","import type { IconDefinition } from \"../../packages/core/src/types.js\";\nimport { generatedHeader } from \"../shared.js\";\n\n/**\n * outline/rounded: fill=\"none\" tĩnh để prop `color` chỉ điều khiển stroke.\n * solid/filled: stroke=\"none\" tĩnh để prop `color` chỉ điều khiển fill.\n * Cả hai đều được đặt trước {...props} nên người dùng vẫn override được.\n */\nfunction staticColorProp(icon: IconDefinition): string {\n return icon.style === \"outline\" || icon.style === \"rounded\" ? 'fill=\"none\"' : 'stroke=\"none\"';\n}\n\nexport function renderReactComponent(icon: IconDefinition): string {\n return `${generatedHeader()}\nimport * as React from \"react\";\nimport { BaseIcon, type IconProps } from \"./Icon.js\";\n\nexport const ${icon.name} = React.forwardRef<SVGSVGElement, IconProps>((props, ref) => {\n return (\n <BaseIcon ref={ref} viewBox=\"${icon.viewBox}\" ${staticColorProp(icon)} {...props}>\n ${icon.body}\n </BaseIcon>\n );\n});\n\n${icon.name}.displayName = \"${icon.name}\";\n`;\n}\n","import type { IconDefinition } from \"../../packages/core/src/types.js\";\nimport { generatedHeader } from \"../shared.js\";\n\n/**\n * react-native-svg dùng component PascalCase (Path, Circle, G, ...) thay vì thẻ\n * SVG thường (path, circle, g, ...) như trên web, nhưng giữ nguyên tên thuộc tính\n * camelCase — nên chỉ cần viết hoa ký tự đầu của tên thẻ, không cần đổi props.\n */\nfunction capitalizeSvgTags(body: string): string {\n return body.replace(/<\\/?([a-zA-Z][\\w-]*)/g, (match, tag: string) => {\n const capitalized = tag.charAt(0).toUpperCase() + tag.slice(1);\n return match.replace(tag, capitalized);\n });\n}\n\nfunction extractUsedTags(body: string): string[] {\n const tags = new Set<string>();\n const regex = /<([A-Z][\\w]*)/g;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(body))) {\n if (match[1]) tags.add(match[1]);\n }\n return [...tags].sort();\n}\n\nfunction staticColorProp(icon: IconDefinition): string {\n return icon.style === \"outline\" || icon.style === \"rounded\" ? 'fill=\"none\"' : 'stroke=\"none\"';\n}\n\nexport function renderReactNativeComponent(icon: IconDefinition): string {\n const body = capitalizeSvgTags(icon.body);\n const tags = extractUsedTags(body);\n\n return `${generatedHeader()}\nimport * as React from \"react\";\nimport { ${tags.join(\", \")} } from \"react-native-svg\";\nimport { BaseIcon, type IconProps, type IconRef } from \"./Icon.js\";\n\nexport const ${icon.name} = React.forwardRef<IconRef, IconProps>((props, ref) => {\n return (\n <BaseIcon ref={ref} viewBox=\"${icon.viewBox}\" ${staticColorProp(icon)} {...props}>\n ${body}\n </BaseIcon>\n );\n});\n\n${icon.name}.displayName = \"${icon.name}\";\n`;\n}\n","import type { IconDefinition } from \"../../packages/core/src/types.js\";\nimport { generatedHtmlComment } from \"../shared.js\";\n\n/**\n * Home.svelte không khai báo export let nào, nên mọi prop consumer truyền vào\n * <Home color=\"red\" /> đều rơi vào $$restProps và được spread tiếp xuống\n * <Icon>. Icon.svelte khai báo size/color/strokeWidth, phần còn lại (bao gồm\n * fill=\"none\" tĩnh ở đây, hoặc override từ consumer) rơi vào $$restProps của\n * chính nó và spread xuống <svg>.\n */\nfunction staticColorProp(icon: IconDefinition): string {\n return icon.style === \"outline\" || icon.style === \"rounded\" ? 'fill=\"none\"' : 'stroke=\"none\"';\n}\n\nexport function renderSvelteComponent(icon: IconDefinition): string {\n return `${generatedHtmlComment()}\n<script lang=\"ts\">\n import Icon from \"./Icon.svelte\";\n</script>\n\n<Icon viewBox=\"${icon.viewBox}\" ${staticColorProp(icon)} {...$$restProps}>\n ${icon.body}\n</Icon>\n`;\n}\n","import type { IconDefinition } from \"../../packages/core/src/types.js\";\nimport { generatedHtmlComment } from \"../shared.js\";\n\n/**\n * Vue tự động \"fallthrough\" các attr không khai báo là prop xuống phần tử gốc,\n * kể cả khi phần tử gốc là 1 component khác (BaseIcon) — nên fill/viewBox\n * truyền cho <BaseIcon> ở đây tiếp tục rơi xuống <svg> bên trong Icon.vue mà\n * không cần v-bind=\"$attrs\" thủ công. Attr do consumer truyền vào <Home> luôn\n * override attr tĩnh khai báo ở template này.\n */\nfunction staticColorProp(icon: IconDefinition): string {\n return icon.style === \"outline\" || icon.style === \"rounded\" ? 'fill=\"none\"' : 'stroke=\"none\"';\n}\n\nexport function renderVueComponent(icon: IconDefinition): string {\n return `${generatedHtmlComment()}\n<script setup lang=\"ts\">\nimport BaseIcon from \"./Icon.vue\";\n</script>\n\n<template>\n <BaseIcon viewBox=\"${icon.viewBox}\" ${staticColorProp(icon)}>\n ${icon.body}\n </BaseIcon>\n</template>\n`;\n}\n","import { readdir, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { GENERATED_MARKER } from \"../../../../templates/shared.js\";\nimport { readFileSafe } from \"../fs-utils.js\";\nimport { targets } from \"../targets.js\";\n\nexport async function runClean(): Promise<void> {\n let removed = 0;\n\n for (const target of targets) {\n const entries = await readdir(target.outDir).catch(() => [] as string[]);\n for (const entry of entries) {\n const filePath = path.join(target.outDir, entry);\n const content = await readFileSafe(filePath);\n if (content?.includes(GENERATED_MARKER)) {\n await rm(filePath);\n removed++;\n }\n }\n }\n\n console.log(`[icon-builder] Đã xoá ${removed} file generated.`);\n}\n","import chokidar from \"chokidar\";\nimport { ICONS_DIR } from \"../config.js\";\nimport { runBuild } from \"./build.js\";\n\nexport async function runWatch(): Promise<void> {\n console.log(`[icon-builder] Đang theo dõi thư mục icons/ ...`);\n\n await runBuild().catch((error: unknown) => logError(error));\n\n let timer: NodeJS.Timeout | undefined;\n const debouncedBuild = () => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n console.log(\"[icon-builder] Phát hiện thay đổi, đang rebuild...\");\n runBuild().catch((error: unknown) => logError(error));\n }, 200);\n };\n\n chokidar\n .watch(\"**/*.svg\", { cwd: ICONS_DIR, ignoreInitial: true })\n .on(\"add\", debouncedBuild)\n .on(\"change\", debouncedBuild)\n .on(\"unlink\", debouncedBuild);\n}\n\nfunction logError(error: unknown): void {\n console.error(error instanceof Error ? error.message : error);\n}\n","import { runBuild } from \"./commands/build.js\";\nimport { runClean } from \"./commands/clean.js\";\nimport { runWatch } from \"./commands/watch.js\";\n\nconst [, , command] = process.argv;\n\nasync function main(): Promise<void> {\n switch (command) {\n case \"build\":\n await runBuild();\n break;\n case \"watch\":\n await runWatch();\n break;\n case \"clean\":\n await runClean();\n break;\n default:\n printHelp();\n process.exitCode = command ? 1 : 0;\n }\n}\n\nfunction printHelp(): void {\n console.log(`icon-builder <command>\n\nCommands:\n build Đọc SVG, validate, optimize và generate React package\n watch Theo dõi thư mục icons/ và tự động rebuild khi có thay đổi\n clean Xoá toàn bộ file generated\n\nVí dụ:\n icon-builder build\n`);\n}\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":["path","path","staticColorProp","staticColorProp","staticColorProp","path","path","readdir","rm","path","readdir","path","rm"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vuonweb/icon-builder",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI sinh icon component (React/React Native/Vue/Svelte) từ file SVG nguồn.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"icon-builder": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"chokidar": "^3.6.0",
|
|
19
|
+
"prettier": "^3.3.3",
|
|
20
|
+
"prettier-plugin-svelte": "^3.2.7",
|
|
21
|
+
"@icon-builder/core": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.7.4",
|
|
25
|
+
"tsup": "^8.3.0",
|
|
26
|
+
"tsx": "^4.19.1",
|
|
27
|
+
"typescript": "^5.6.2"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsx src/index.ts",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
}
|
|
34
|
+
}
|