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