vectify 2.0.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/README.md +679 -0
- package/README.zh-CN.md +683 -0
- package/dist/chunk-4BWKFV7W.mjs +1311 -0
- package/dist/chunk-CIKTK6HI.mjs +96 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1483 -0
- package/dist/cli.mjs +56 -0
- package/dist/helpers-UPZEBRGK.mjs +26 -0
- package/dist/index.d.mts +281 -0
- package/dist/index.d.ts +281 -0
- package/dist/index.js +1463 -0
- package/dist/index.mjs +27 -0
- package/dist/templates/angular/component.ts.hbs +121 -0
- package/dist/templates/angular/createIcon.ts.hbs +116 -0
- package/dist/templates/astro/component.astro.hbs +110 -0
- package/dist/templates/astro/createIcon.astro.hbs +111 -0
- package/dist/templates/lit/component.js.hbs +12 -0
- package/dist/templates/lit/component.ts.hbs +19 -0
- package/dist/templates/lit/createIcon.js.hbs +98 -0
- package/dist/templates/lit/createIcon.ts.hbs +99 -0
- package/dist/templates/preact/component.jsx.hbs +8 -0
- package/dist/templates/preact/component.tsx.hbs +11 -0
- package/dist/templates/preact/createIcon.jsx.hbs +101 -0
- package/dist/templates/preact/createIcon.tsx.hbs +121 -0
- package/dist/templates/qwik/component.jsx.hbs +7 -0
- package/dist/templates/qwik/component.tsx.hbs +8 -0
- package/dist/templates/qwik/createIcon.jsx.hbs +100 -0
- package/dist/templates/qwik/createIcon.tsx.hbs +111 -0
- package/dist/templates/react/component.jsx.hbs +8 -0
- package/dist/templates/react/component.tsx.hbs +11 -0
- package/dist/templates/react/createIcon.jsx.hbs +100 -0
- package/dist/templates/react/createIcon.tsx.hbs +117 -0
- package/dist/templates/solid/component.tsx.hbs +10 -0
- package/dist/templates/solid/createIcon.jsx.hbs +127 -0
- package/dist/templates/solid/createIcon.tsx.hbs +139 -0
- package/dist/templates/svelte/component.js.svelte.hbs +9 -0
- package/dist/templates/svelte/component.ts.svelte.hbs +10 -0
- package/dist/templates/svelte/icon.js.svelte.hbs +123 -0
- package/dist/templates/svelte/icon.ts.svelte.hbs +124 -0
- package/dist/templates/template-engine.ts +107 -0
- package/dist/templates/vanilla/component.ts.hbs +8 -0
- package/dist/templates/vanilla/createIcon.js.hbs +111 -0
- package/dist/templates/vanilla/createIcon.ts.hbs +124 -0
- package/dist/templates/vue/component.js.vue.hbs +21 -0
- package/dist/templates/vue/component.ts.vue.hbs +22 -0
- package/dist/templates/vue/icon.js.vue.hbs +155 -0
- package/dist/templates/vue/icon.ts.vue.hbs +157 -0
- package/package.json +78 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1463 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/utils/helpers.ts
|
|
34
|
+
var helpers_exports = {};
|
|
35
|
+
__export(helpers_exports, {
|
|
36
|
+
ensureDir: () => ensureDir,
|
|
37
|
+
fileExists: () => fileExists,
|
|
38
|
+
findProjectRoot: () => findProjectRoot,
|
|
39
|
+
formatAttributes: () => formatAttributes,
|
|
40
|
+
getComponentName: () => getComponentName,
|
|
41
|
+
getSvgFiles: () => getSvgFiles,
|
|
42
|
+
mergeClasses: () => mergeClasses,
|
|
43
|
+
readFile: () => readFile,
|
|
44
|
+
toKebabCase: () => toKebabCase,
|
|
45
|
+
toPascalCase: () => toPascalCase,
|
|
46
|
+
writeFile: () => writeFile
|
|
47
|
+
});
|
|
48
|
+
function toPascalCase(str) {
|
|
49
|
+
return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (char) => char.toUpperCase()).replace(/[^a-z0-9]/gi, "");
|
|
50
|
+
}
|
|
51
|
+
function toKebabCase(str) {
|
|
52
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
function mergeClasses(...classes) {
|
|
55
|
+
return classes.filter(Boolean).join(" ");
|
|
56
|
+
}
|
|
57
|
+
function getComponentName(fileName, prefix = "", suffix = "", transform) {
|
|
58
|
+
const baseName = fileName.replace(/\.svg$/, "");
|
|
59
|
+
let componentName = toPascalCase(baseName);
|
|
60
|
+
if (transform) {
|
|
61
|
+
componentName = transform(componentName);
|
|
62
|
+
}
|
|
63
|
+
return `${prefix}${componentName}${suffix}`;
|
|
64
|
+
}
|
|
65
|
+
function formatAttributes(attrs) {
|
|
66
|
+
const entries = Object.entries(attrs);
|
|
67
|
+
if (entries.length === 0)
|
|
68
|
+
return "{}";
|
|
69
|
+
const formatted = entries.map(([key, value]) => {
|
|
70
|
+
if (typeof value === "number") {
|
|
71
|
+
return `${key}: ${value}`;
|
|
72
|
+
}
|
|
73
|
+
return `${key}: '${value}'`;
|
|
74
|
+
}).join(", ");
|
|
75
|
+
return `{ ${formatted} }`;
|
|
76
|
+
}
|
|
77
|
+
async function ensureDir(dirPath) {
|
|
78
|
+
const fs2 = await import("fs/promises");
|
|
79
|
+
try {
|
|
80
|
+
await fs2.mkdir(dirPath, { recursive: true });
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error.code !== "EEXIST") {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function fileExists(filePath) {
|
|
88
|
+
const fs2 = await import("fs/promises");
|
|
89
|
+
try {
|
|
90
|
+
await fs2.access(filePath);
|
|
91
|
+
return true;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function readFile(filePath) {
|
|
97
|
+
const fs2 = await import("fs/promises");
|
|
98
|
+
return await fs2.readFile(filePath, "utf-8");
|
|
99
|
+
}
|
|
100
|
+
async function writeFile(filePath, content) {
|
|
101
|
+
const fs2 = await import("fs/promises");
|
|
102
|
+
await fs2.writeFile(filePath, content, "utf-8");
|
|
103
|
+
}
|
|
104
|
+
async function getSvgFiles(dirPath) {
|
|
105
|
+
const fs2 = await import("fs/promises");
|
|
106
|
+
const path6 = await import("path");
|
|
107
|
+
try {
|
|
108
|
+
const files = await fs2.readdir(dirPath);
|
|
109
|
+
return files.filter((file) => file.endsWith(".svg")).map((file) => path6.join(dirPath, file));
|
|
110
|
+
} catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function findProjectRoot(startDir = process.cwd()) {
|
|
115
|
+
const path6 = await import("path");
|
|
116
|
+
let currentDir = startDir;
|
|
117
|
+
while (true) {
|
|
118
|
+
const packageJsonPath = path6.join(currentDir, "package.json");
|
|
119
|
+
if (await fileExists(packageJsonPath)) {
|
|
120
|
+
return currentDir;
|
|
121
|
+
}
|
|
122
|
+
const parentDir = path6.dirname(currentDir);
|
|
123
|
+
if (parentDir === currentDir) {
|
|
124
|
+
return startDir;
|
|
125
|
+
}
|
|
126
|
+
currentDir = parentDir;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
var init_helpers = __esm({
|
|
130
|
+
"src/utils/helpers.ts"() {
|
|
131
|
+
"use strict";
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// src/index.ts
|
|
136
|
+
var src_exports = {};
|
|
137
|
+
__export(src_exports, {
|
|
138
|
+
defineConfig: () => defineConfig,
|
|
139
|
+
findConfig: () => findConfig,
|
|
140
|
+
frameworkRegistry: () => frameworkRegistry,
|
|
141
|
+
generate: () => generate,
|
|
142
|
+
generateIcons: () => generateIcons,
|
|
143
|
+
getFrameworkStrategy: () => getFrameworkStrategy,
|
|
144
|
+
init: () => init,
|
|
145
|
+
loadConfig: () => loadConfig,
|
|
146
|
+
watch: () => watch
|
|
147
|
+
});
|
|
148
|
+
module.exports = __toCommonJS(src_exports);
|
|
149
|
+
|
|
150
|
+
// src/commands/generate.ts
|
|
151
|
+
var import_chalk = __toESM(require("chalk"));
|
|
152
|
+
var import_ora = __toESM(require("ora"));
|
|
153
|
+
|
|
154
|
+
// src/config/loader.ts
|
|
155
|
+
var import_node_path2 = __toESM(require("path"));
|
|
156
|
+
var import_node_process = __toESM(require("process"));
|
|
157
|
+
var import_jiti = require("jiti");
|
|
158
|
+
|
|
159
|
+
// src/parsers/svg-parser.ts
|
|
160
|
+
var cheerio = __toESM(require("cheerio"));
|
|
161
|
+
var SUPPORTED_ELEMENTS = [
|
|
162
|
+
"circle",
|
|
163
|
+
"ellipse",
|
|
164
|
+
"line",
|
|
165
|
+
"path",
|
|
166
|
+
"polygon",
|
|
167
|
+
"polyline",
|
|
168
|
+
"rect",
|
|
169
|
+
"g"
|
|
170
|
+
];
|
|
171
|
+
function parseSvg(svgContent) {
|
|
172
|
+
const $ = cheerio.load(svgContent, {
|
|
173
|
+
xml: true
|
|
174
|
+
});
|
|
175
|
+
const svgElement = $("svg");
|
|
176
|
+
if (svgElement.length === 0) {
|
|
177
|
+
throw new Error("Invalid SVG: No <svg> tag found");
|
|
178
|
+
}
|
|
179
|
+
const iconNodes = [];
|
|
180
|
+
svgElement.children().each((_, element) => {
|
|
181
|
+
const node = parseElement($, element);
|
|
182
|
+
if (node) {
|
|
183
|
+
iconNodes.push(node);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return iconNodes;
|
|
187
|
+
}
|
|
188
|
+
function parseElement($, element) {
|
|
189
|
+
if (element.type !== "tag") {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const tagName = element.name;
|
|
193
|
+
if (!SUPPORTED_ELEMENTS.includes(tagName)) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const attrs = parseAttributes(element);
|
|
197
|
+
const $element = $(element);
|
|
198
|
+
const children = [];
|
|
199
|
+
$element.children().each((_, child) => {
|
|
200
|
+
const childNode = parseElement($, child);
|
|
201
|
+
if (childNode) {
|
|
202
|
+
children.push(childNode);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
if (children.length > 0) {
|
|
206
|
+
return [tagName, attrs, children];
|
|
207
|
+
}
|
|
208
|
+
return [tagName, attrs];
|
|
209
|
+
}
|
|
210
|
+
function parseAttributes(element) {
|
|
211
|
+
const attrs = {};
|
|
212
|
+
if (element.type !== "tag" || !element.attribs) {
|
|
213
|
+
return attrs;
|
|
214
|
+
}
|
|
215
|
+
const attributes = element.attribs;
|
|
216
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
217
|
+
if (key.startsWith("xmlns")) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (typeof value !== "string") {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
224
|
+
const numValue = Number.parseFloat(value);
|
|
225
|
+
attrs[camelKey] = Number.isNaN(numValue) ? value : numValue;
|
|
226
|
+
}
|
|
227
|
+
return attrs;
|
|
228
|
+
}
|
|
229
|
+
function formatIconNode(node, indent = 2) {
|
|
230
|
+
const [elementType, attrs, children] = node;
|
|
231
|
+
const indentStr = " ".repeat(indent);
|
|
232
|
+
const attrsStr = formatAttrs(attrs);
|
|
233
|
+
if (children && children.length > 0) {
|
|
234
|
+
const childrenStr = children.map((child) => formatIconNode(child, indent + 2)).join(",\n");
|
|
235
|
+
return `${indentStr}['${elementType}', ${attrsStr}, [
|
|
236
|
+
${childrenStr}
|
|
237
|
+
${indentStr}]]`;
|
|
238
|
+
}
|
|
239
|
+
return `${indentStr}['${elementType}', ${attrsStr}]`;
|
|
240
|
+
}
|
|
241
|
+
function formatAttrs(attrs) {
|
|
242
|
+
const entries = Object.entries(attrs);
|
|
243
|
+
if (entries.length === 0) {
|
|
244
|
+
return "{}";
|
|
245
|
+
}
|
|
246
|
+
const formatted = entries.map(([key, value]) => {
|
|
247
|
+
if (typeof value === "number") {
|
|
248
|
+
return `${key}: ${value}`;
|
|
249
|
+
}
|
|
250
|
+
const escaped = value.replace(/'/g, "\\'");
|
|
251
|
+
return `${key}: '${escaped}'`;
|
|
252
|
+
}).join(", ");
|
|
253
|
+
return `{ ${formatted} }`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/generators/templates/template-engine.ts
|
|
257
|
+
var import_node_fs = __toESM(require("fs"));
|
|
258
|
+
var import_node_path = __toESM(require("path"));
|
|
259
|
+
var import_node_url = require("url");
|
|
260
|
+
var import_handlebars = __toESM(require("handlebars"));
|
|
261
|
+
var import_meta = {};
|
|
262
|
+
function getTemplatesDir() {
|
|
263
|
+
if (typeof __dirname !== "undefined") {
|
|
264
|
+
return import_node_path.default.join(__dirname, "templates");
|
|
265
|
+
}
|
|
266
|
+
const currentFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
267
|
+
return import_node_path.default.join(import_node_path.default.dirname(currentFile), "templates");
|
|
268
|
+
}
|
|
269
|
+
function loadTemplate(templatePath) {
|
|
270
|
+
const templatesDir = getTemplatesDir();
|
|
271
|
+
const fullPath = import_node_path.default.join(templatesDir, templatePath);
|
|
272
|
+
const templateContent = import_node_fs.default.readFileSync(fullPath, "utf-8");
|
|
273
|
+
return import_handlebars.default.compile(templateContent, { noEscape: true });
|
|
274
|
+
}
|
|
275
|
+
function renderTemplate(templatePath, data) {
|
|
276
|
+
const template = loadTemplate(templatePath);
|
|
277
|
+
return template(data);
|
|
278
|
+
}
|
|
279
|
+
function getReactTemplatePath(typescript, type) {
|
|
280
|
+
const ext = typescript ? "tsx" : "jsx";
|
|
281
|
+
return `react/${type}.${ext}.hbs`;
|
|
282
|
+
}
|
|
283
|
+
function getVueTemplatePath(typescript, type) {
|
|
284
|
+
const suffix = typescript ? "ts" : "js";
|
|
285
|
+
return `vue/${type}.${suffix}.vue.hbs`;
|
|
286
|
+
}
|
|
287
|
+
function getSvelteTemplatePath(typescript, type) {
|
|
288
|
+
const suffix = typescript ? "ts" : "js";
|
|
289
|
+
return `svelte/${type}.${suffix}.svelte.hbs`;
|
|
290
|
+
}
|
|
291
|
+
function getSolidTemplatePath(typescript, type) {
|
|
292
|
+
const ext = typescript ? "tsx" : "jsx";
|
|
293
|
+
return `solid/${type}.${ext}.hbs`;
|
|
294
|
+
}
|
|
295
|
+
function getPreactTemplatePath(typescript, type) {
|
|
296
|
+
const ext = typescript ? "tsx" : "jsx";
|
|
297
|
+
return `preact/${type}.${ext}.hbs`;
|
|
298
|
+
}
|
|
299
|
+
function getVanillaTemplatePath(typescript, type) {
|
|
300
|
+
const ext = typescript ? "ts" : "js";
|
|
301
|
+
return `vanilla/${type}.${ext}.hbs`;
|
|
302
|
+
}
|
|
303
|
+
function getQwikTemplatePath(typescript, type) {
|
|
304
|
+
const ext = typescript ? "tsx" : "jsx";
|
|
305
|
+
return `qwik/${type}.${ext}.hbs`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/generators/angular.ts
|
|
309
|
+
function getAngularTemplatePath(type) {
|
|
310
|
+
return `angular/${type}.ts.hbs`;
|
|
311
|
+
}
|
|
312
|
+
function generateAngularComponent(componentName, iconNode, typescript, keepColors = false) {
|
|
313
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
314
|
+
const templatePath = getAngularTemplatePath("component");
|
|
315
|
+
return renderTemplate(templatePath, {
|
|
316
|
+
componentName,
|
|
317
|
+
formattedNodes,
|
|
318
|
+
typescript,
|
|
319
|
+
keepColors
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function generateAngularBaseComponent(typescript) {
|
|
323
|
+
const templatePath = getAngularTemplatePath("createIcon");
|
|
324
|
+
return {
|
|
325
|
+
code: renderTemplate(templatePath, { typescript }),
|
|
326
|
+
fileName: "createIcon.ts"
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/generators/astro.ts
|
|
331
|
+
function getAstroTemplatePath(type) {
|
|
332
|
+
return `astro/${type}.astro.hbs`;
|
|
333
|
+
}
|
|
334
|
+
function generateAstroComponent(componentName, iconNode, typescript, keepColors = false) {
|
|
335
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
336
|
+
const templatePath = getAstroTemplatePath("component");
|
|
337
|
+
return renderTemplate(templatePath, {
|
|
338
|
+
componentName,
|
|
339
|
+
formattedNodes,
|
|
340
|
+
typescript,
|
|
341
|
+
keepColors
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function generateAstroBaseComponent(typescript) {
|
|
345
|
+
const templatePath = getAstroTemplatePath("createIcon");
|
|
346
|
+
return {
|
|
347
|
+
code: renderTemplate(templatePath, { typescript }),
|
|
348
|
+
fileName: "createIcon.astro"
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/generators/lit.ts
|
|
353
|
+
function getLitTemplatePath(typescript, type) {
|
|
354
|
+
const ext = typescript ? "ts" : "js";
|
|
355
|
+
return `lit/${type}.${ext}.hbs`;
|
|
356
|
+
}
|
|
357
|
+
function generateLitComponent(componentName, iconNode, typescript, keepColors = false) {
|
|
358
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
359
|
+
const templatePath = getLitTemplatePath(typescript, "component");
|
|
360
|
+
return renderTemplate(templatePath, {
|
|
361
|
+
componentName,
|
|
362
|
+
formattedNodes,
|
|
363
|
+
typescript,
|
|
364
|
+
keepColors
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function generateLitBaseComponent(typescript) {
|
|
368
|
+
const templatePath = getLitTemplatePath(typescript, "createIcon");
|
|
369
|
+
return {
|
|
370
|
+
code: renderTemplate(templatePath, { typescript }),
|
|
371
|
+
fileName: typescript ? "createIcon.ts" : "createIcon.js"
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/generators/react.ts
|
|
376
|
+
function generateReactComponent(componentName, iconNode, typescript, keepColors = false) {
|
|
377
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
378
|
+
const templatePath = getReactTemplatePath(typescript, "component");
|
|
379
|
+
return renderTemplate(templatePath, {
|
|
380
|
+
typescript,
|
|
381
|
+
componentName,
|
|
382
|
+
formattedNodes,
|
|
383
|
+
keepColors
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function generateCreateIcon(typescript) {
|
|
387
|
+
const templatePath = getReactTemplatePath(typescript, "createIcon");
|
|
388
|
+
return renderTemplate(templatePath, { typescript });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/generators/react-like.ts
|
|
392
|
+
function generateReactLikeComponent(componentName, iconNode, typescript, keepColors, framework) {
|
|
393
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
394
|
+
let templatePath;
|
|
395
|
+
if (framework === "solid") {
|
|
396
|
+
templatePath = getSolidTemplatePath(typescript, "component");
|
|
397
|
+
} else if (framework === "preact") {
|
|
398
|
+
templatePath = getPreactTemplatePath(typescript, "component");
|
|
399
|
+
} else if (framework === "qwik") {
|
|
400
|
+
templatePath = getQwikTemplatePath(typescript, "component");
|
|
401
|
+
} else {
|
|
402
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
403
|
+
}
|
|
404
|
+
return renderTemplate(templatePath, {
|
|
405
|
+
componentName,
|
|
406
|
+
formattedNodes,
|
|
407
|
+
typescript,
|
|
408
|
+
keepColors
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
function generateReactLikeBaseComponent(typescript, framework) {
|
|
412
|
+
let templatePath;
|
|
413
|
+
if (framework === "solid") {
|
|
414
|
+
templatePath = getSolidTemplatePath(typescript, "createIcon");
|
|
415
|
+
} else if (framework === "preact") {
|
|
416
|
+
templatePath = getPreactTemplatePath(typescript, "createIcon");
|
|
417
|
+
} else if (framework === "qwik") {
|
|
418
|
+
templatePath = getQwikTemplatePath(typescript, "createIcon");
|
|
419
|
+
} else {
|
|
420
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
code: renderTemplate(templatePath, { typescript }),
|
|
424
|
+
fileName: typescript ? "createIcon.tsx" : "createIcon.jsx"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/generators/svelte.ts
|
|
429
|
+
function generateSvelteComponent(_componentName, iconNode, typescript) {
|
|
430
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 4)).join(",\n");
|
|
431
|
+
const templatePath = getSvelteTemplatePath(typescript, "component");
|
|
432
|
+
return renderTemplate(templatePath, {
|
|
433
|
+
typescript,
|
|
434
|
+
formattedNodes
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
function generateSvelteIcon(typescript) {
|
|
438
|
+
const templatePath = getSvelteTemplatePath(typescript, "icon");
|
|
439
|
+
return renderTemplate(templatePath, { typescript });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/generators/vanilla.ts
|
|
443
|
+
function generateVanillaComponent(componentName, iconNode, typescript, keepColors) {
|
|
444
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
|
|
445
|
+
const templatePath = getVanillaTemplatePath(typescript, "component");
|
|
446
|
+
return renderTemplate(templatePath, {
|
|
447
|
+
componentName,
|
|
448
|
+
formattedNodes,
|
|
449
|
+
typescript,
|
|
450
|
+
keepColors
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
function generateVanillaBaseComponent(typescript) {
|
|
454
|
+
const templatePath = getVanillaTemplatePath(typescript, "createIcon");
|
|
455
|
+
return {
|
|
456
|
+
code: renderTemplate(templatePath, { typescript }),
|
|
457
|
+
fileName: typescript ? "createIcon.ts" : "createIcon.js"
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/generators/vue.ts
|
|
462
|
+
function generateVueComponent(componentName, iconNode, typescript) {
|
|
463
|
+
const formattedNodes = iconNode.map((node) => formatIconNode(node, 4)).join(",\n");
|
|
464
|
+
const templatePath = getVueTemplatePath(typescript, "component");
|
|
465
|
+
return renderTemplate(templatePath, {
|
|
466
|
+
typescript,
|
|
467
|
+
componentName,
|
|
468
|
+
formattedNodes
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
function generateVueIcon(typescript) {
|
|
472
|
+
const templatePath = getVueTemplatePath(typescript, "icon");
|
|
473
|
+
return renderTemplate(templatePath, { typescript });
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/generators/framework-strategy.ts
|
|
477
|
+
var ReactStrategy = class {
|
|
478
|
+
constructor() {
|
|
479
|
+
this.name = "react";
|
|
480
|
+
this.getComponentExtension = (typescript) => {
|
|
481
|
+
return typescript ? "tsx" : "jsx";
|
|
482
|
+
};
|
|
483
|
+
this.getIndexExtension = (typescript) => {
|
|
484
|
+
return typescript ? "ts" : "js";
|
|
485
|
+
};
|
|
486
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors = false) => {
|
|
487
|
+
return generateReactComponent(componentName, iconNode, typescript, keepColors);
|
|
488
|
+
};
|
|
489
|
+
this.generateBaseComponent = (typescript) => {
|
|
490
|
+
return {
|
|
491
|
+
code: generateCreateIcon(typescript),
|
|
492
|
+
fileName: `createIcon.${this.getComponentExtension(typescript)}`
|
|
493
|
+
};
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
var VueStrategy = class {
|
|
498
|
+
constructor() {
|
|
499
|
+
this.name = "vue";
|
|
500
|
+
this.getComponentExtension = (_typescript) => {
|
|
501
|
+
return "vue";
|
|
502
|
+
};
|
|
503
|
+
this.getIndexExtension = (typescript) => {
|
|
504
|
+
return typescript ? "ts" : "js";
|
|
505
|
+
};
|
|
506
|
+
this.generateComponent = (componentName, iconNode, typescript) => {
|
|
507
|
+
return generateVueComponent(componentName, iconNode, typescript);
|
|
508
|
+
};
|
|
509
|
+
this.generateBaseComponent = (typescript) => {
|
|
510
|
+
return {
|
|
511
|
+
code: generateVueIcon(typescript),
|
|
512
|
+
fileName: "Icon.vue"
|
|
513
|
+
};
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var SvelteStrategy = class {
|
|
518
|
+
constructor() {
|
|
519
|
+
this.name = "svelte";
|
|
520
|
+
this.getComponentExtension = (_typescript) => {
|
|
521
|
+
return "svelte";
|
|
522
|
+
};
|
|
523
|
+
this.getIndexExtension = (typescript) => {
|
|
524
|
+
return typescript ? "ts" : "js";
|
|
525
|
+
};
|
|
526
|
+
this.generateComponent = (componentName, iconNode, typescript) => {
|
|
527
|
+
return generateSvelteComponent(componentName, iconNode, typescript);
|
|
528
|
+
};
|
|
529
|
+
this.generateBaseComponent = (typescript) => {
|
|
530
|
+
return {
|
|
531
|
+
code: generateSvelteIcon(typescript),
|
|
532
|
+
fileName: "Icon.svelte"
|
|
533
|
+
};
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
var SolidStrategy = class {
|
|
538
|
+
constructor() {
|
|
539
|
+
this.name = "solid";
|
|
540
|
+
this.getComponentExtension = (typescript) => {
|
|
541
|
+
return typescript ? "tsx" : "jsx";
|
|
542
|
+
};
|
|
543
|
+
this.getIndexExtension = (typescript) => {
|
|
544
|
+
return typescript ? "ts" : "js";
|
|
545
|
+
};
|
|
546
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
547
|
+
return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, "solid");
|
|
548
|
+
};
|
|
549
|
+
this.generateBaseComponent = (typescript) => {
|
|
550
|
+
return generateReactLikeBaseComponent(typescript, "solid");
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
var PreactStrategy = class {
|
|
555
|
+
constructor() {
|
|
556
|
+
this.name = "preact";
|
|
557
|
+
this.getComponentExtension = (typescript) => {
|
|
558
|
+
return typescript ? "tsx" : "jsx";
|
|
559
|
+
};
|
|
560
|
+
this.getIndexExtension = (typescript) => {
|
|
561
|
+
return typescript ? "ts" : "js";
|
|
562
|
+
};
|
|
563
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
564
|
+
return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, "preact");
|
|
565
|
+
};
|
|
566
|
+
this.generateBaseComponent = (typescript) => {
|
|
567
|
+
return generateReactLikeBaseComponent(typescript, "preact");
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
var VanillaStrategy = class {
|
|
572
|
+
constructor() {
|
|
573
|
+
this.name = "vanilla";
|
|
574
|
+
this.getComponentExtension = (typescript) => {
|
|
575
|
+
return typescript ? "ts" : "js";
|
|
576
|
+
};
|
|
577
|
+
this.getIndexExtension = (typescript) => {
|
|
578
|
+
return typescript ? "ts" : "js";
|
|
579
|
+
};
|
|
580
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
581
|
+
return generateVanillaComponent(componentName, iconNode, typescript, keepColors ?? false);
|
|
582
|
+
};
|
|
583
|
+
this.generateBaseComponent = (typescript) => {
|
|
584
|
+
return generateVanillaBaseComponent(typescript);
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
var LitStrategy = class {
|
|
589
|
+
constructor() {
|
|
590
|
+
this.name = "lit";
|
|
591
|
+
this.getComponentExtension = (typescript) => {
|
|
592
|
+
return typescript ? "ts" : "js";
|
|
593
|
+
};
|
|
594
|
+
this.getIndexExtension = (typescript) => {
|
|
595
|
+
return typescript ? "ts" : "js";
|
|
596
|
+
};
|
|
597
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
598
|
+
return generateLitComponent(componentName, iconNode, typescript, keepColors ?? false);
|
|
599
|
+
};
|
|
600
|
+
this.generateBaseComponent = (typescript) => {
|
|
601
|
+
return generateLitBaseComponent(typescript);
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
var QwikStrategy = class {
|
|
606
|
+
constructor() {
|
|
607
|
+
this.name = "qwik";
|
|
608
|
+
this.getComponentExtension = (typescript) => {
|
|
609
|
+
return typescript ? "tsx" : "jsx";
|
|
610
|
+
};
|
|
611
|
+
this.getIndexExtension = (typescript) => {
|
|
612
|
+
return typescript ? "ts" : "js";
|
|
613
|
+
};
|
|
614
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
615
|
+
return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, "qwik");
|
|
616
|
+
};
|
|
617
|
+
this.generateBaseComponent = (typescript) => {
|
|
618
|
+
return generateReactLikeBaseComponent(typescript, "qwik");
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var AstroStrategy = class {
|
|
623
|
+
constructor() {
|
|
624
|
+
this.name = "astro";
|
|
625
|
+
this.getComponentExtension = (_typescript) => {
|
|
626
|
+
return "astro";
|
|
627
|
+
};
|
|
628
|
+
this.getIndexExtension = (typescript) => {
|
|
629
|
+
return typescript ? "ts" : "js";
|
|
630
|
+
};
|
|
631
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
632
|
+
return generateAstroComponent(componentName, iconNode, typescript, keepColors ?? false);
|
|
633
|
+
};
|
|
634
|
+
this.generateBaseComponent = (typescript) => {
|
|
635
|
+
return generateAstroBaseComponent(typescript);
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
var AngularStrategy = class {
|
|
640
|
+
constructor() {
|
|
641
|
+
this.name = "angular";
|
|
642
|
+
this.getComponentExtension = (_typescript) => {
|
|
643
|
+
return "ts";
|
|
644
|
+
};
|
|
645
|
+
this.getIndexExtension = (_typescript) => {
|
|
646
|
+
return "ts";
|
|
647
|
+
};
|
|
648
|
+
this.generateComponent = (componentName, iconNode, typescript, keepColors) => {
|
|
649
|
+
return generateAngularComponent(componentName, iconNode, typescript, keepColors ?? false);
|
|
650
|
+
};
|
|
651
|
+
this.generateBaseComponent = (typescript) => {
|
|
652
|
+
return generateAngularBaseComponent(typescript);
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
var FrameworkRegistry = class {
|
|
657
|
+
constructor() {
|
|
658
|
+
this.strategies = /* @__PURE__ */ new Map();
|
|
659
|
+
this.register(new ReactStrategy());
|
|
660
|
+
this.register(new VueStrategy());
|
|
661
|
+
this.register(new SvelteStrategy());
|
|
662
|
+
this.register(new SolidStrategy());
|
|
663
|
+
this.register(new PreactStrategy());
|
|
664
|
+
this.register(new VanillaStrategy());
|
|
665
|
+
this.register(new LitStrategy());
|
|
666
|
+
this.register(new QwikStrategy());
|
|
667
|
+
this.register(new AstroStrategy());
|
|
668
|
+
this.register(new AngularStrategy());
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Register a framework strategy
|
|
672
|
+
*/
|
|
673
|
+
register(strategy) {
|
|
674
|
+
this.strategies.set(strategy.name, strategy);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Get a framework strategy
|
|
678
|
+
*/
|
|
679
|
+
get(framework) {
|
|
680
|
+
const strategy = this.strategies.get(framework);
|
|
681
|
+
if (!strategy) {
|
|
682
|
+
throw new Error(`Unsupported framework: ${framework}`);
|
|
683
|
+
}
|
|
684
|
+
return strategy;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Check if a framework is supported
|
|
688
|
+
*/
|
|
689
|
+
has(framework) {
|
|
690
|
+
return this.strategies.has(framework);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get all supported frameworks
|
|
694
|
+
*/
|
|
695
|
+
getSupportedFrameworks() {
|
|
696
|
+
return Array.from(this.strategies.keys());
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
var frameworkRegistry = new FrameworkRegistry();
|
|
700
|
+
function getFrameworkStrategy(framework) {
|
|
701
|
+
return frameworkRegistry.get(framework);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/config/loader.ts
|
|
705
|
+
var DEFAULT_CONFIG = {
|
|
706
|
+
input: "./icons",
|
|
707
|
+
output: "./src/icons",
|
|
708
|
+
typescript: true,
|
|
709
|
+
optimize: true,
|
|
710
|
+
keepColors: false,
|
|
711
|
+
prefix: "",
|
|
712
|
+
suffix: "",
|
|
713
|
+
configDir: ".",
|
|
714
|
+
generateOptions: {
|
|
715
|
+
index: true,
|
|
716
|
+
types: true,
|
|
717
|
+
preview: false,
|
|
718
|
+
cleanOutput: false
|
|
719
|
+
},
|
|
720
|
+
watch: {
|
|
721
|
+
enabled: false,
|
|
722
|
+
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
723
|
+
debounce: 300
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
async function loadConfig(configPath) {
|
|
727
|
+
const absolutePath = import_node_path2.default.resolve(import_node_process.default.cwd(), configPath);
|
|
728
|
+
const configDir = import_node_path2.default.dirname(absolutePath);
|
|
729
|
+
const jiti = (0, import_jiti.createJiti)(configDir, {
|
|
730
|
+
interopDefault: true
|
|
731
|
+
});
|
|
732
|
+
let config;
|
|
733
|
+
try {
|
|
734
|
+
config = await jiti.import(absolutePath);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
|
|
737
|
+
}
|
|
738
|
+
let loadedConfig = config.default || config;
|
|
739
|
+
if (Array.isArray(loadedConfig)) {
|
|
740
|
+
loadedConfig = loadedConfig[0];
|
|
741
|
+
}
|
|
742
|
+
const mergedConfig = {
|
|
743
|
+
...DEFAULT_CONFIG,
|
|
744
|
+
...loadedConfig,
|
|
745
|
+
generateOptions: {
|
|
746
|
+
...DEFAULT_CONFIG.generateOptions,
|
|
747
|
+
...loadedConfig.generateOptions
|
|
748
|
+
},
|
|
749
|
+
watch: {
|
|
750
|
+
...DEFAULT_CONFIG.watch,
|
|
751
|
+
...loadedConfig.watch
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
if (mergedConfig.configDir && mergedConfig.configDir !== ".") {
|
|
755
|
+
const projectRoot = import_node_path2.default.resolve(configDir, mergedConfig.configDir);
|
|
756
|
+
mergedConfig.input = import_node_path2.default.resolve(projectRoot, mergedConfig.input);
|
|
757
|
+
mergedConfig.output = import_node_path2.default.resolve(projectRoot, mergedConfig.output);
|
|
758
|
+
} else {
|
|
759
|
+
mergedConfig.input = import_node_path2.default.resolve(configDir, mergedConfig.input);
|
|
760
|
+
mergedConfig.output = import_node_path2.default.resolve(configDir, mergedConfig.output);
|
|
761
|
+
}
|
|
762
|
+
if (!mergedConfig.framework) {
|
|
763
|
+
const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
|
|
764
|
+
throw new Error(`Config must specify a framework (${supported})`);
|
|
765
|
+
}
|
|
766
|
+
if (!frameworkRegistry.has(mergedConfig.framework)) {
|
|
767
|
+
const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
|
|
768
|
+
throw new Error(`Invalid framework: ${mergedConfig.framework}. Supported: ${supported}`);
|
|
769
|
+
}
|
|
770
|
+
return mergedConfig;
|
|
771
|
+
}
|
|
772
|
+
async function findConfig() {
|
|
773
|
+
const { fileExists: fileExists2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
774
|
+
const configFiles = [
|
|
775
|
+
"vectify.config.ts",
|
|
776
|
+
"vectify.config.js"
|
|
777
|
+
];
|
|
778
|
+
for (const file of configFiles) {
|
|
779
|
+
const configPath = import_node_path2.default.resolve(import_node_process.default.cwd(), file);
|
|
780
|
+
if (await fileExists2(configPath)) {
|
|
781
|
+
return configPath;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/generators/index.ts
|
|
788
|
+
var import_node_path3 = __toESM(require("path"));
|
|
789
|
+
|
|
790
|
+
// src/parsers/optimizer.ts
|
|
791
|
+
var import_svgo = require("svgo");
|
|
792
|
+
var DEFAULT_SVGO_CONFIG = {
|
|
793
|
+
plugins: [
|
|
794
|
+
"preset-default",
|
|
795
|
+
"removeViewBox",
|
|
796
|
+
"removeDimensions",
|
|
797
|
+
"convertShapeToPath",
|
|
798
|
+
{
|
|
799
|
+
name: "removeAttrs",
|
|
800
|
+
params: {
|
|
801
|
+
attrs: ["class", "data-name"]
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
]
|
|
805
|
+
};
|
|
806
|
+
async function optimizeSvg(svgContent, config) {
|
|
807
|
+
if (!config.optimize) {
|
|
808
|
+
return svgContent;
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
const result = (0, import_svgo.optimize)(svgContent, {
|
|
812
|
+
...DEFAULT_SVGO_CONFIG,
|
|
813
|
+
...config.svgoConfig
|
|
814
|
+
});
|
|
815
|
+
return result.data;
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.warn(`SVGO optimization failed: ${error.message}`);
|
|
818
|
+
return svgContent;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// src/generators/index.ts
|
|
823
|
+
init_helpers();
|
|
824
|
+
async function generateIcons(config, dryRun = false) {
|
|
825
|
+
const stats = {
|
|
826
|
+
success: 0,
|
|
827
|
+
failed: 0,
|
|
828
|
+
total: 0,
|
|
829
|
+
errors: []
|
|
830
|
+
};
|
|
831
|
+
try {
|
|
832
|
+
if (!dryRun) {
|
|
833
|
+
await ensureDir(config.output);
|
|
834
|
+
}
|
|
835
|
+
const svgFiles = await getSvgFiles(config.input);
|
|
836
|
+
stats.total = svgFiles.length;
|
|
837
|
+
if (svgFiles.length === 0) {
|
|
838
|
+
console.warn(`No SVG files found in ${config.input}`);
|
|
839
|
+
return stats;
|
|
840
|
+
}
|
|
841
|
+
if (config.generateOptions?.cleanOutput && !dryRun) {
|
|
842
|
+
await cleanOutputDirectory(svgFiles, config);
|
|
843
|
+
}
|
|
844
|
+
await generateBaseComponent(config, dryRun);
|
|
845
|
+
for (const svgFile of svgFiles) {
|
|
846
|
+
try {
|
|
847
|
+
await generateIconComponent(svgFile, config, dryRun);
|
|
848
|
+
stats.success++;
|
|
849
|
+
} catch (error) {
|
|
850
|
+
stats.failed++;
|
|
851
|
+
stats.errors.push({
|
|
852
|
+
file: svgFile,
|
|
853
|
+
error: error.message
|
|
854
|
+
});
|
|
855
|
+
console.error(`Failed to generate ${svgFile}: ${error.message}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (config.generateOptions?.index) {
|
|
859
|
+
await generateIndexFile(svgFiles, config, dryRun);
|
|
860
|
+
}
|
|
861
|
+
if (config.generateOptions?.preview && !dryRun) {
|
|
862
|
+
await generatePreviewHtml(svgFiles, config);
|
|
863
|
+
}
|
|
864
|
+
if (config.hooks?.onComplete) {
|
|
865
|
+
await config.hooks.onComplete(stats);
|
|
866
|
+
}
|
|
867
|
+
} catch (error) {
|
|
868
|
+
throw new Error(`Generation failed: ${error.message}`);
|
|
869
|
+
}
|
|
870
|
+
return stats;
|
|
871
|
+
}
|
|
872
|
+
async function generateIconComponent(svgFile, config, dryRun = false) {
|
|
873
|
+
let svgContent = await readFile(svgFile);
|
|
874
|
+
const fileName = import_node_path3.default.basename(svgFile);
|
|
875
|
+
if (config.hooks?.beforeParse) {
|
|
876
|
+
svgContent = await config.hooks.beforeParse(svgContent, fileName);
|
|
877
|
+
}
|
|
878
|
+
svgContent = await optimizeSvg(svgContent, config);
|
|
879
|
+
const iconNode = parseSvg(svgContent);
|
|
880
|
+
const componentName = getComponentName(
|
|
881
|
+
fileName,
|
|
882
|
+
config.prefix,
|
|
883
|
+
config.suffix,
|
|
884
|
+
config.transform
|
|
885
|
+
);
|
|
886
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
887
|
+
const typescript = config.typescript ?? true;
|
|
888
|
+
let code = strategy.generateComponent(
|
|
889
|
+
componentName,
|
|
890
|
+
iconNode,
|
|
891
|
+
typescript,
|
|
892
|
+
config.keepColors ?? false
|
|
893
|
+
);
|
|
894
|
+
if (config.hooks?.afterGenerate) {
|
|
895
|
+
code = await config.hooks.afterGenerate(code, componentName);
|
|
896
|
+
}
|
|
897
|
+
const fileExt = strategy.getComponentExtension(typescript);
|
|
898
|
+
const outputPath = import_node_path3.default.join(config.output, `${componentName}.${fileExt}`);
|
|
899
|
+
if (dryRun) {
|
|
900
|
+
console.log(` ${componentName}.${fileExt}`);
|
|
901
|
+
} else {
|
|
902
|
+
await writeFile(outputPath, code);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
async function generateBaseComponent(config, dryRun = false) {
|
|
906
|
+
const typescript = config.typescript ?? true;
|
|
907
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
908
|
+
const { code, fileName } = strategy.generateBaseComponent(typescript);
|
|
909
|
+
const outputPath = import_node_path3.default.join(config.output, fileName);
|
|
910
|
+
if (dryRun) {
|
|
911
|
+
console.log(` ${fileName}`);
|
|
912
|
+
} else {
|
|
913
|
+
await writeFile(outputPath, code);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
917
|
+
const typescript = config.typescript ?? true;
|
|
918
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
919
|
+
const ext = strategy.getIndexExtension(typescript);
|
|
920
|
+
const usesDefaultExport = config.framework === "vue" || config.framework === "svelte" || config.framework === "preact";
|
|
921
|
+
const exports2 = svgFiles.map((svgFile) => {
|
|
922
|
+
const fileName = import_node_path3.default.basename(svgFile);
|
|
923
|
+
const componentName = getComponentName(
|
|
924
|
+
fileName,
|
|
925
|
+
config.prefix,
|
|
926
|
+
config.suffix,
|
|
927
|
+
config.transform
|
|
928
|
+
);
|
|
929
|
+
if (usesDefaultExport) {
|
|
930
|
+
return `export { default as ${componentName} } from './${componentName}'`;
|
|
931
|
+
} else {
|
|
932
|
+
return `export { ${componentName} } from './${componentName}'`;
|
|
933
|
+
}
|
|
934
|
+
}).join("\n");
|
|
935
|
+
const indexPath = import_node_path3.default.join(config.output, `index.${ext}`);
|
|
936
|
+
if (dryRun) {
|
|
937
|
+
console.log(` index.${ext}`);
|
|
938
|
+
} else {
|
|
939
|
+
await writeFile(indexPath, `${exports2}
|
|
940
|
+
`);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
async function generatePreviewHtml(svgFiles, config) {
|
|
944
|
+
const componentNames = svgFiles.map((svgFile) => {
|
|
945
|
+
const fileName = import_node_path3.default.basename(svgFile);
|
|
946
|
+
return getComponentName(
|
|
947
|
+
fileName,
|
|
948
|
+
config.prefix,
|
|
949
|
+
config.suffix,
|
|
950
|
+
config.transform
|
|
951
|
+
);
|
|
952
|
+
});
|
|
953
|
+
const svgContents = await Promise.all(
|
|
954
|
+
svgFiles.map(async (svgFile) => {
|
|
955
|
+
return await readFile(svgFile);
|
|
956
|
+
})
|
|
957
|
+
);
|
|
958
|
+
const html = `<!DOCTYPE html>
|
|
959
|
+
<html lang="en">
|
|
960
|
+
<head>
|
|
961
|
+
<meta charset="UTF-8">
|
|
962
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
963
|
+
<title>Icons - ${componentNames.length} Total</title>
|
|
964
|
+
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
965
|
+
</head>
|
|
966
|
+
<body class="bg-white">
|
|
967
|
+
<div class="max-w-[1400px] mx-auto px-6 py-12">
|
|
968
|
+
<header class="mb-12">
|
|
969
|
+
<h1 class="text-3xl font-semibold text-gray-900 mb-2">Icons</h1>
|
|
970
|
+
<p class="text-sm text-gray-600">${componentNames.length} icons \u2022 ${config.framework} \u2022 Click to copy name</p>
|
|
971
|
+
</header>
|
|
972
|
+
|
|
973
|
+
<div class="flex items-center gap-4 mb-8 pb-6 border-b border-gray-200">
|
|
974
|
+
<div class="flex-1">
|
|
975
|
+
<input
|
|
976
|
+
type="search"
|
|
977
|
+
id="search"
|
|
978
|
+
placeholder="Search icons..."
|
|
979
|
+
class="w-full h-10 px-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:border-gray-900 transition-colors"
|
|
980
|
+
/>
|
|
981
|
+
</div>
|
|
982
|
+
|
|
983
|
+
<div class="flex items-center gap-3">
|
|
984
|
+
<label for="size" class="text-sm text-gray-600">Size</label>
|
|
985
|
+
<input
|
|
986
|
+
type="range"
|
|
987
|
+
id="size"
|
|
988
|
+
min="16"
|
|
989
|
+
max="96"
|
|
990
|
+
value="32"
|
|
991
|
+
step="8"
|
|
992
|
+
class="w-24 h-1 accent-gray-900"
|
|
993
|
+
/>
|
|
994
|
+
<span id="sizeValue" class="text-sm text-gray-900 w-10">32px</span>
|
|
995
|
+
</div>
|
|
996
|
+
|
|
997
|
+
<div class="flex items-center gap-3">
|
|
998
|
+
<label for="color" class="text-sm text-gray-600">Color</label>
|
|
999
|
+
<input
|
|
1000
|
+
type="color"
|
|
1001
|
+
id="color"
|
|
1002
|
+
value="#171717"
|
|
1003
|
+
class="w-10 h-10 rounded-lg border border-gray-300 cursor-pointer"
|
|
1004
|
+
/>
|
|
1005
|
+
</div>
|
|
1006
|
+
</div>
|
|
1007
|
+
|
|
1008
|
+
<div id="iconGrid" class="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:grid-cols-12 gap-1"></div>
|
|
1009
|
+
</div>
|
|
1010
|
+
|
|
1011
|
+
<script>
|
|
1012
|
+
const icons = ${JSON.stringify(componentNames)};
|
|
1013
|
+
const svgContents = ${JSON.stringify(svgContents)};
|
|
1014
|
+
|
|
1015
|
+
let currentSize = 32;
|
|
1016
|
+
let currentColor = '#171717';
|
|
1017
|
+
let searchQuery = '';
|
|
1018
|
+
|
|
1019
|
+
function renderIcons() {
|
|
1020
|
+
const grid = document.getElementById('iconGrid');
|
|
1021
|
+
grid.innerHTML = '';
|
|
1022
|
+
|
|
1023
|
+
const filteredIcons = icons.filter((name) =>
|
|
1024
|
+
name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
1025
|
+
);
|
|
1026
|
+
|
|
1027
|
+
if (filteredIcons.length === 0) {
|
|
1028
|
+
grid.innerHTML = '<div class="col-span-full text-center py-20 text-gray-400 text-sm">No icons found</div>';
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
for (let i = 0; i < filteredIcons.length; i++) {
|
|
1033
|
+
const iconName = filteredIcons[i];
|
|
1034
|
+
const svgIndex = icons.indexOf(iconName);
|
|
1035
|
+
const svgContent = svgContents[svgIndex];
|
|
1036
|
+
|
|
1037
|
+
const card = document.createElement('div');
|
|
1038
|
+
card.className = 'group relative aspect-square flex flex-col items-center justify-center p-4 rounded-lg border border-transparent hover:border-gray-200 hover:bg-gray-50 transition-all cursor-pointer';
|
|
1039
|
+
card.onclick = () => copyToClipboard(iconName);
|
|
1040
|
+
card.title = iconName;
|
|
1041
|
+
|
|
1042
|
+
const wrapper = document.createElement('div');
|
|
1043
|
+
wrapper.className = 'flex items-center justify-center mb-2';
|
|
1044
|
+
|
|
1045
|
+
wrapper.innerHTML = svgContent;
|
|
1046
|
+
|
|
1047
|
+
const svg = wrapper.querySelector('svg');
|
|
1048
|
+
if (svg) {
|
|
1049
|
+
svg.setAttribute('width', currentSize);
|
|
1050
|
+
svg.setAttribute('height', currentSize);
|
|
1051
|
+
svg.setAttribute('stroke', currentColor);
|
|
1052
|
+
svg.setAttribute('fill', 'none');
|
|
1053
|
+
|
|
1054
|
+
// Update stroke color for all child elements
|
|
1055
|
+
const elements = svg.querySelectorAll('[stroke]');
|
|
1056
|
+
elements.forEach(el => {
|
|
1057
|
+
el.setAttribute('stroke', currentColor);
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const name = document.createElement('div');
|
|
1062
|
+
name.className = 'text-[10px] text-gray-500 text-center truncate w-full group-hover:text-gray-900';
|
|
1063
|
+
name.textContent = iconName;
|
|
1064
|
+
|
|
1065
|
+
card.appendChild(wrapper);
|
|
1066
|
+
card.appendChild(name);
|
|
1067
|
+
grid.appendChild(card);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
function copyToClipboard(text) {
|
|
1072
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
1073
|
+
showCopiedNotification(text);
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
function showCopiedNotification(text) {
|
|
1078
|
+
const existing = document.querySelector('.toast');
|
|
1079
|
+
if (existing) existing.remove();
|
|
1080
|
+
|
|
1081
|
+
const notification = document.createElement('div');
|
|
1082
|
+
notification.className = 'toast fixed bottom-6 right-6 bg-gray-900 text-white text-sm px-4 py-2.5 rounded-lg shadow-lg flex items-center gap-2 transition-opacity';
|
|
1083
|
+
notification.innerHTML = \`
|
|
1084
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1085
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
1086
|
+
</svg>
|
|
1087
|
+
<span>Copied \${text}</span>
|
|
1088
|
+
\`;
|
|
1089
|
+
document.body.appendChild(notification);
|
|
1090
|
+
|
|
1091
|
+
setTimeout(() => {
|
|
1092
|
+
notification.style.opacity = '0';
|
|
1093
|
+
setTimeout(() => notification.remove(), 200);
|
|
1094
|
+
}, 1800);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
document.getElementById('size').addEventListener('input', (e) => {
|
|
1098
|
+
currentSize = e.target.value;
|
|
1099
|
+
document.getElementById('sizeValue').textContent = currentSize + 'px';
|
|
1100
|
+
renderIcons();
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
document.getElementById('color').addEventListener('input', (e) => {
|
|
1104
|
+
currentColor = e.target.value;
|
|
1105
|
+
renderIcons();
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
document.getElementById('search').addEventListener('input', (e) => {
|
|
1109
|
+
searchQuery = e.target.value;
|
|
1110
|
+
renderIcons();
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
renderIcons();
|
|
1114
|
+
</script>
|
|
1115
|
+
</body>
|
|
1116
|
+
</html>`;
|
|
1117
|
+
const previewPath = import_node_path3.default.join(config.output, "preview.html");
|
|
1118
|
+
await writeFile(previewPath, html);
|
|
1119
|
+
}
|
|
1120
|
+
async function cleanOutputDirectory(svgFiles, config) {
|
|
1121
|
+
const { readdir, unlink } = await import("fs/promises");
|
|
1122
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
1123
|
+
const fileExt = strategy.getComponentExtension(config.typescript ?? true);
|
|
1124
|
+
const expectedComponents = new Set(
|
|
1125
|
+
svgFiles.map((svgFile) => {
|
|
1126
|
+
const fileName = import_node_path3.default.basename(svgFile, ".svg");
|
|
1127
|
+
const componentName = getComponentName(
|
|
1128
|
+
fileName,
|
|
1129
|
+
config.prefix,
|
|
1130
|
+
config.suffix,
|
|
1131
|
+
config.transform
|
|
1132
|
+
);
|
|
1133
|
+
return `${componentName}.${fileExt}`;
|
|
1134
|
+
})
|
|
1135
|
+
);
|
|
1136
|
+
const protectedFiles = /* @__PURE__ */ new Set([
|
|
1137
|
+
`Icon.${fileExt}`,
|
|
1138
|
+
`createIcon.${fileExt}`,
|
|
1139
|
+
`index.${config.typescript ? "ts" : "js"}`,
|
|
1140
|
+
`index.d.ts`,
|
|
1141
|
+
"preview.html"
|
|
1142
|
+
]);
|
|
1143
|
+
try {
|
|
1144
|
+
const files = await readdir(config.output);
|
|
1145
|
+
for (const file of files) {
|
|
1146
|
+
if (protectedFiles.has(file)) {
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
if (!file.endsWith(`.${fileExt}`)) {
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (!expectedComponents.has(file)) {
|
|
1153
|
+
const filePath = import_node_path3.default.join(config.output, file);
|
|
1154
|
+
await unlink(filePath);
|
|
1155
|
+
console.log(`Deleted orphaned component: ${file}`);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
console.warn(`Failed to clean output directory: ${error.message}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/commands/generate.ts
|
|
1164
|
+
async function generate(options = {}) {
|
|
1165
|
+
const spinner = (0, import_ora.default)("Loading configuration...").start();
|
|
1166
|
+
try {
|
|
1167
|
+
let configPath = options.config;
|
|
1168
|
+
if (!configPath) {
|
|
1169
|
+
const foundConfig = await findConfig();
|
|
1170
|
+
if (!foundConfig) {
|
|
1171
|
+
spinner.fail("No config file found");
|
|
1172
|
+
console.log(import_chalk.default.yellow("\nRun"), import_chalk.default.cyan("svg-icon init"), import_chalk.default.yellow("to create a config file"));
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
configPath = foundConfig;
|
|
1176
|
+
}
|
|
1177
|
+
const config = await loadConfig(configPath);
|
|
1178
|
+
spinner.succeed(`Config loaded from ${import_chalk.default.green(configPath)}`);
|
|
1179
|
+
if (options.dryRun) {
|
|
1180
|
+
spinner.info(import_chalk.default.cyan("Dry run mode - no files will be written"));
|
|
1181
|
+
console.log(`
|
|
1182
|
+
${import_chalk.default.bold("Files that would be generated:")}
|
|
1183
|
+
`);
|
|
1184
|
+
}
|
|
1185
|
+
const actionText = options.dryRun ? "Analyzing icons..." : "Generating icon components...";
|
|
1186
|
+
spinner.start(actionText);
|
|
1187
|
+
const stats = await generateIcons(config, options.dryRun);
|
|
1188
|
+
if (stats.failed > 0) {
|
|
1189
|
+
spinner.warn(`${options.dryRun ? "Analyzed" : "Generated"} ${import_chalk.default.green(stats.success)} icons, ${import_chalk.default.red(stats.failed)} failed`);
|
|
1190
|
+
if (stats.errors.length > 0) {
|
|
1191
|
+
console.log(`
|
|
1192
|
+
${import_chalk.default.bold.red("Errors:")}`);
|
|
1193
|
+
stats.errors.forEach(({ file, error }) => {
|
|
1194
|
+
console.log(` ${import_chalk.default.red("\u2717")} ${file}: ${error}`);
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
} else {
|
|
1198
|
+
const message = options.dryRun ? `Would generate ${import_chalk.default.green(stats.success)} icon components` : `Generated ${import_chalk.default.green(stats.success)} icon components`;
|
|
1199
|
+
spinner.succeed(message);
|
|
1200
|
+
}
|
|
1201
|
+
if (options.dryRun) {
|
|
1202
|
+
console.log(`
|
|
1203
|
+
${import_chalk.default.bold("Target directory:")} ${import_chalk.default.cyan(config.output)}`);
|
|
1204
|
+
console.log(import_chalk.default.gray("\nRun without --dry-run to generate files"));
|
|
1205
|
+
} else {
|
|
1206
|
+
console.log(`
|
|
1207
|
+
${import_chalk.default.bold("Output:")} ${import_chalk.default.cyan(config.output)}`);
|
|
1208
|
+
if (config.generateOptions?.preview) {
|
|
1209
|
+
console.log(`${import_chalk.default.bold("Preview:")} ${import_chalk.default.cyan(`${config.output}/preview.html`)}`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
spinner.fail("Generation failed");
|
|
1214
|
+
console.error(import_chalk.default.red(error.message));
|
|
1215
|
+
throw error;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// src/commands/init.ts
|
|
1220
|
+
var import_node_path4 = __toESM(require("path"));
|
|
1221
|
+
var import_node_process2 = __toESM(require("process"));
|
|
1222
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
1223
|
+
var import_inquirer = __toESM(require("inquirer"));
|
|
1224
|
+
var import_ora2 = __toESM(require("ora"));
|
|
1225
|
+
init_helpers();
|
|
1226
|
+
async function init(options = {}) {
|
|
1227
|
+
try {
|
|
1228
|
+
const projectRoot = await findProjectRoot();
|
|
1229
|
+
const currentDir = import_node_process2.default.cwd();
|
|
1230
|
+
if (currentDir !== projectRoot) {
|
|
1231
|
+
console.log(import_chalk2.default.yellow(`
|
|
1232
|
+
Note: Project root detected at ${import_chalk2.default.cyan(projectRoot)}`));
|
|
1233
|
+
console.log(import_chalk2.default.yellow(`Current directory: ${import_chalk2.default.cyan(currentDir)}
|
|
1234
|
+
`));
|
|
1235
|
+
}
|
|
1236
|
+
const pathAnswers = await import_inquirer.default.prompt([
|
|
1237
|
+
{
|
|
1238
|
+
type: "input",
|
|
1239
|
+
name: "configPath",
|
|
1240
|
+
message: "Where should we create the config file?",
|
|
1241
|
+
default: options.config || "./vectify.config.ts",
|
|
1242
|
+
validate: (input) => {
|
|
1243
|
+
if (!input.endsWith(".ts") && !input.endsWith(".js")) {
|
|
1244
|
+
return "Config file must have .ts or .js extension";
|
|
1245
|
+
}
|
|
1246
|
+
return true;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
]);
|
|
1250
|
+
const configPath = import_node_path4.default.resolve(projectRoot, pathAnswers.configPath);
|
|
1251
|
+
const configDir = import_node_path4.default.dirname(configPath);
|
|
1252
|
+
if (!options.force && await fileExists(configPath)) {
|
|
1253
|
+
const { overwrite } = await import_inquirer.default.prompt([
|
|
1254
|
+
{
|
|
1255
|
+
type: "confirm",
|
|
1256
|
+
name: "overwrite",
|
|
1257
|
+
message: "Config file already exists. Overwrite?",
|
|
1258
|
+
default: false
|
|
1259
|
+
}
|
|
1260
|
+
]);
|
|
1261
|
+
if (!overwrite) {
|
|
1262
|
+
console.log(import_chalk2.default.yellow("Cancelled"));
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
const supportedFrameworks = frameworkRegistry.getSupportedFrameworks();
|
|
1267
|
+
const frameworkChoices = supportedFrameworks.map((fw) => ({
|
|
1268
|
+
name: fw.charAt(0).toUpperCase() + fw.slice(1),
|
|
1269
|
+
value: fw
|
|
1270
|
+
}));
|
|
1271
|
+
const answers = await import_inquirer.default.prompt([
|
|
1272
|
+
{
|
|
1273
|
+
type: "list",
|
|
1274
|
+
name: "framework",
|
|
1275
|
+
message: "Which framework are you using?",
|
|
1276
|
+
choices: frameworkChoices
|
|
1277
|
+
},
|
|
1278
|
+
{
|
|
1279
|
+
type: "input",
|
|
1280
|
+
name: "input",
|
|
1281
|
+
message: "Where are your SVG files located?",
|
|
1282
|
+
default: "./icons"
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
type: "input",
|
|
1286
|
+
name: "output",
|
|
1287
|
+
message: "Where should we output the components?",
|
|
1288
|
+
default: "./src/icons"
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
type: "confirm",
|
|
1292
|
+
name: "typescript",
|
|
1293
|
+
message: "Use TypeScript?",
|
|
1294
|
+
default: true
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
type: "confirm",
|
|
1298
|
+
name: "optimize",
|
|
1299
|
+
message: "Optimize SVG files with SVGO?",
|
|
1300
|
+
default: true
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
type: "input",
|
|
1304
|
+
name: "prefix",
|
|
1305
|
+
message: "Component name prefix (optional):",
|
|
1306
|
+
default: ""
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
type: "input",
|
|
1310
|
+
name: "suffix",
|
|
1311
|
+
message: "Component name suffix (optional):",
|
|
1312
|
+
default: ""
|
|
1313
|
+
}
|
|
1314
|
+
]);
|
|
1315
|
+
const inputPath = import_node_path4.default.resolve(projectRoot, answers.input);
|
|
1316
|
+
const outputPath = import_node_path4.default.resolve(projectRoot, answers.output);
|
|
1317
|
+
const spinner = (0, import_ora2.default)("Setting up directories...").start();
|
|
1318
|
+
await ensureDir(inputPath);
|
|
1319
|
+
spinner.text = `Created input directory: ${import_chalk2.default.cyan(answers.input)}`;
|
|
1320
|
+
await ensureDir(outputPath);
|
|
1321
|
+
spinner.succeed(`Created output directory: ${import_chalk2.default.cyan(answers.output)}`);
|
|
1322
|
+
const relativeConfigDir = import_node_path4.default.relative(configDir, projectRoot) || ".";
|
|
1323
|
+
const configContent = generateConfigContent(answers, relativeConfigDir);
|
|
1324
|
+
spinner.start("Creating config file...");
|
|
1325
|
+
await writeFile(configPath, configContent);
|
|
1326
|
+
spinner.succeed(`Config file created at ${import_chalk2.default.green(configPath)}`);
|
|
1327
|
+
console.log(`
|
|
1328
|
+
${import_chalk2.default.bold("Next steps:")}`);
|
|
1329
|
+
console.log(` 1. Place your SVG files in ${import_chalk2.default.cyan(answers.input)}`);
|
|
1330
|
+
console.log(` 2. Run ${import_chalk2.default.cyan("svg-icon generate")} to generate components`);
|
|
1331
|
+
console.log(` 3. Import and use your icons!
|
|
1332
|
+
`);
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
console.error(import_chalk2.default.red("Initialization failed"));
|
|
1335
|
+
throw error;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function generateConfigContent(answers, configDir) {
|
|
1339
|
+
return `import { defineConfig } from 'vectify'
|
|
1340
|
+
|
|
1341
|
+
export default defineConfig({
|
|
1342
|
+
framework: '${answers.framework}',
|
|
1343
|
+
configDir: '${configDir}',
|
|
1344
|
+
input: '${answers.input}',
|
|
1345
|
+
output: '${answers.output}',
|
|
1346
|
+
typescript: ${answers.typescript},
|
|
1347
|
+
optimize: ${answers.optimize},
|
|
1348
|
+
prefix: '${answers.prefix}',
|
|
1349
|
+
suffix: '${answers.suffix}',
|
|
1350
|
+
generateOptions: {
|
|
1351
|
+
index: true,
|
|
1352
|
+
types: true,
|
|
1353
|
+
preview: false
|
|
1354
|
+
},
|
|
1355
|
+
watch: {
|
|
1356
|
+
enabled: false,
|
|
1357
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
1358
|
+
debounce: 300,
|
|
1359
|
+
},
|
|
1360
|
+
})
|
|
1361
|
+
`;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// src/commands/watch.ts
|
|
1365
|
+
var import_node_path5 = __toESM(require("path"));
|
|
1366
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
1367
|
+
var import_chokidar = __toESM(require("chokidar"));
|
|
1368
|
+
var import_ora3 = __toESM(require("ora"));
|
|
1369
|
+
async function watch(options = {}) {
|
|
1370
|
+
const spinner = (0, import_ora3.default)("Loading configuration...").start();
|
|
1371
|
+
try {
|
|
1372
|
+
let configPath = options.config;
|
|
1373
|
+
if (!configPath) {
|
|
1374
|
+
const foundConfig = await findConfig();
|
|
1375
|
+
if (!foundConfig) {
|
|
1376
|
+
spinner.fail("No config file found");
|
|
1377
|
+
console.log(import_chalk3.default.yellow("\nRun"), import_chalk3.default.cyan("svg-icon init"), import_chalk3.default.yellow("to create a config file"));
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
configPath = foundConfig;
|
|
1381
|
+
}
|
|
1382
|
+
const config = await loadConfig(configPath);
|
|
1383
|
+
spinner.succeed(`Config loaded from ${import_chalk3.default.green(configPath)}`);
|
|
1384
|
+
spinner.start("Generating icon components...");
|
|
1385
|
+
const initialStats = await generateIcons(config);
|
|
1386
|
+
spinner.succeed(`Generated ${import_chalk3.default.green(initialStats.success)} icon components`);
|
|
1387
|
+
const watchPath = import_node_path5.default.join(config.input, "**/*.svg");
|
|
1388
|
+
const debounce = config.watch?.debounce ?? 300;
|
|
1389
|
+
const ignore = config.watch?.ignore ?? ["**/node_modules/**", "**/.git/**"];
|
|
1390
|
+
console.log(import_chalk3.default.bold("\nWatching for changes..."));
|
|
1391
|
+
console.log(` ${import_chalk3.default.cyan(watchPath)}`);
|
|
1392
|
+
console.log(` ${import_chalk3.default.gray("Press Ctrl+C to stop")}
|
|
1393
|
+
`);
|
|
1394
|
+
const debounceTimer = null;
|
|
1395
|
+
const watcher = import_chokidar.default.watch(watchPath, {
|
|
1396
|
+
ignored: ignore,
|
|
1397
|
+
persistent: true,
|
|
1398
|
+
ignoreInitial: true
|
|
1399
|
+
});
|
|
1400
|
+
watcher.on("add", (filePath) => {
|
|
1401
|
+
handleChange("added", filePath, config, debounce, debounceTimer);
|
|
1402
|
+
}).on("change", (filePath) => {
|
|
1403
|
+
handleChange("changed", filePath, config, debounce, debounceTimer);
|
|
1404
|
+
}).on("unlink", (filePath) => {
|
|
1405
|
+
console.log(import_chalk3.default.yellow(`SVG file removed: ${import_node_path5.default.basename(filePath)}`));
|
|
1406
|
+
handleChange("removed", filePath, config, debounce, debounceTimer);
|
|
1407
|
+
}).on("error", (error) => {
|
|
1408
|
+
console.error(import_chalk3.default.red(`Watcher error: ${error.message}`));
|
|
1409
|
+
});
|
|
1410
|
+
process.on("SIGINT", () => {
|
|
1411
|
+
console.log(`
|
|
1412
|
+
|
|
1413
|
+
${import_chalk3.default.yellow("Stopping watch mode...")}`);
|
|
1414
|
+
watcher.close();
|
|
1415
|
+
process.exit(0);
|
|
1416
|
+
});
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
spinner.fail("Watch mode failed");
|
|
1419
|
+
console.error(import_chalk3.default.red(error.message));
|
|
1420
|
+
throw error;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
function handleChange(event, filePath, config, debounce, timer) {
|
|
1424
|
+
const fileName = import_node_path5.default.basename(filePath);
|
|
1425
|
+
if (timer) {
|
|
1426
|
+
clearTimeout(timer);
|
|
1427
|
+
}
|
|
1428
|
+
timer = setTimeout(async () => {
|
|
1429
|
+
const spinner = (0, import_ora3.default)(`Regenerating icons...`).start();
|
|
1430
|
+
try {
|
|
1431
|
+
const stats = await generateIcons(config);
|
|
1432
|
+
if (stats.failed > 0) {
|
|
1433
|
+
spinner.warn(
|
|
1434
|
+
`Regenerated ${import_chalk3.default.green(stats.success)} icons, ${import_chalk3.default.red(stats.failed)} failed`
|
|
1435
|
+
);
|
|
1436
|
+
} else {
|
|
1437
|
+
spinner.succeed(`Regenerated ${import_chalk3.default.green(stats.success)} icon components`);
|
|
1438
|
+
}
|
|
1439
|
+
console.log(import_chalk3.default.gray(` Triggered by: ${fileName} (${event})
|
|
1440
|
+
`));
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
spinner.fail("Regeneration failed");
|
|
1443
|
+
console.error(import_chalk3.default.red(error.message));
|
|
1444
|
+
}
|
|
1445
|
+
}, debounce);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// src/types.ts
|
|
1449
|
+
function defineConfig(config) {
|
|
1450
|
+
return config;
|
|
1451
|
+
}
|
|
1452
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1453
|
+
0 && (module.exports = {
|
|
1454
|
+
defineConfig,
|
|
1455
|
+
findConfig,
|
|
1456
|
+
frameworkRegistry,
|
|
1457
|
+
generate,
|
|
1458
|
+
generateIcons,
|
|
1459
|
+
getFrameworkStrategy,
|
|
1460
|
+
init,
|
|
1461
|
+
loadConfig,
|
|
1462
|
+
watch
|
|
1463
|
+
});
|