vectify 2.1.1 → 2.1.2

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.
@@ -1,2024 +0,0 @@
1
- import {
2
- __require,
3
- ensureDir,
4
- fileExists,
5
- findProjectRoot,
6
- getComponentName,
7
- getSvgFiles,
8
- readFile,
9
- writeFile
10
- } from "./chunk-FS34P27H.mjs";
11
-
12
- // src/parsers/svg-parser.ts
13
- import * as cheerio from "cheerio";
14
- var SUPPORTED_ELEMENTS = [
15
- "circle",
16
- "ellipse",
17
- "line",
18
- "path",
19
- "polygon",
20
- "polyline",
21
- "rect",
22
- "g"
23
- ];
24
- function parseSvg(svgContent) {
25
- const $ = cheerio.load(svgContent, {
26
- xml: true
27
- });
28
- const svgElement = $("svg");
29
- if (svgElement.length === 0) {
30
- throw new Error("Invalid SVG: No <svg> tag found");
31
- }
32
- const viewBox = svgElement.attr("viewBox") || "0 0 24 24";
33
- const iconNodes = [];
34
- svgElement.children().each((_, element) => {
35
- const node = parseElement($, element);
36
- if (node) {
37
- iconNodes.push(node);
38
- }
39
- });
40
- const isMultiColor = detectMultiColor(iconNodes);
41
- return { iconNodes, viewBox, isMultiColor };
42
- }
43
- function detectMultiColor(nodes) {
44
- const colors = /* @__PURE__ */ new Set();
45
- function collectColors(node) {
46
- const [, attrs, children] = node;
47
- if (attrs.fill && attrs.fill !== "none" && attrs.fill !== "transparent") {
48
- colors.add(String(attrs.fill).toLowerCase());
49
- }
50
- if (attrs.stroke && attrs.stroke !== "none" && attrs.stroke !== "transparent") {
51
- colors.add(String(attrs.stroke).toLowerCase());
52
- }
53
- if (children && children.length > 0) {
54
- children.forEach(collectColors);
55
- }
56
- }
57
- nodes.forEach(collectColors);
58
- const realColors = Array.from(colors).filter((color) => {
59
- const c = color.toLowerCase();
60
- return c !== "currentcolor";
61
- });
62
- return realColors.length >= 2;
63
- }
64
- function parseElement($, element) {
65
- if (element.type !== "tag") {
66
- return null;
67
- }
68
- const tagName = element.name;
69
- if (!SUPPORTED_ELEMENTS.includes(tagName)) {
70
- return null;
71
- }
72
- const attrs = parseAttributes(element);
73
- const $element = $(element);
74
- const children = [];
75
- $element.children().each((_, child) => {
76
- const childNode = parseElement($, child);
77
- if (childNode) {
78
- children.push(childNode);
79
- }
80
- });
81
- if (children.length > 0) {
82
- return [tagName, attrs, children];
83
- }
84
- return [tagName, attrs];
85
- }
86
- function parseAttributes(element) {
87
- const attrs = {};
88
- if (element.type !== "tag" || !element.attribs) {
89
- return attrs;
90
- }
91
- const attributes = element.attribs;
92
- for (const [key, value] of Object.entries(attributes)) {
93
- if (key.startsWith("xmlns")) {
94
- continue;
95
- }
96
- if (typeof value !== "string") {
97
- continue;
98
- }
99
- const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
100
- const numValue = Number.parseFloat(value);
101
- attrs[camelKey] = Number.isNaN(numValue) ? value : numValue;
102
- }
103
- return attrs;
104
- }
105
- function formatIconNode(node, indent = 2) {
106
- const [elementType, attrs, children] = node;
107
- const indentStr = " ".repeat(indent);
108
- const attrsStr = formatAttrs(attrs);
109
- if (children && children.length > 0) {
110
- const childrenStr = children.map((child) => formatIconNode(child, indent + 2)).join(",\n");
111
- return `${indentStr}['${elementType}', ${attrsStr}, [
112
- ${childrenStr}
113
- ${indentStr}]]`;
114
- }
115
- return `${indentStr}['${elementType}', ${attrsStr}]`;
116
- }
117
- function formatAttrs(attrs) {
118
- const entries = Object.entries(attrs);
119
- if (entries.length === 0) {
120
- return "{}";
121
- }
122
- const formatted = entries.map(([key, value]) => {
123
- if (typeof value === "number") {
124
- return `${key}: ${value}`;
125
- }
126
- const escaped = value.replace(/'/g, "\\'");
127
- return `${key}: '${escaped}'`;
128
- }).join(", ");
129
- return `{ ${formatted} }`;
130
- }
131
-
132
- // src/generators/templates/template-engine.ts
133
- import fs from "fs";
134
- import path from "path";
135
- import { fileURLToPath } from "url";
136
- import Handlebars from "handlebars";
137
- function getTemplatesDir() {
138
- if (typeof __dirname !== "undefined") {
139
- return path.join(__dirname, "templates");
140
- }
141
- const currentFile = fileURLToPath(import.meta.url);
142
- return path.join(path.dirname(currentFile), "templates");
143
- }
144
- function loadTemplate(templatePath) {
145
- const templatesDir = getTemplatesDir();
146
- const fullPath = path.join(templatesDir, templatePath);
147
- const templateContent = fs.readFileSync(fullPath, "utf-8");
148
- return Handlebars.compile(templateContent, { noEscape: true });
149
- }
150
- function renderTemplate(templatePath, data) {
151
- const template = loadTemplate(templatePath);
152
- return template(data);
153
- }
154
- function getReactTemplatePath(typescript, type) {
155
- const ext = typescript ? "tsx" : "jsx";
156
- return `react/${type}.${ext}.hbs`;
157
- }
158
- function getVueTemplatePath(typescript, type) {
159
- const suffix = typescript ? "ts" : "js";
160
- return `vue/${type}.${suffix}.vue.hbs`;
161
- }
162
- function getVue2TemplatePath(typescript, type) {
163
- const suffix = typescript ? "ts" : "js";
164
- return `vue2/${type}.${suffix}.vue.hbs`;
165
- }
166
- function getSvelteTemplatePath(typescript, type) {
167
- const suffix = typescript ? "ts" : "js";
168
- return `svelte/${type}.${suffix}.svelte.hbs`;
169
- }
170
- function getSolidTemplatePath(typescript, type) {
171
- const ext = typescript ? "tsx" : "jsx";
172
- return `solid/${type}.${ext}.hbs`;
173
- }
174
- function getPreactTemplatePath(typescript, type) {
175
- const ext = typescript ? "tsx" : "jsx";
176
- return `preact/${type}.${ext}.hbs`;
177
- }
178
- function getVanillaTemplatePath(typescript, type) {
179
- const ext = typescript ? "ts" : "js";
180
- return `vanilla/${type}.${ext}.hbs`;
181
- }
182
- function getQwikTemplatePath(typescript, type) {
183
- const ext = typescript ? "tsx" : "jsx";
184
- return `qwik/${type}.${ext}.hbs`;
185
- }
186
-
187
- // src/generators/angular.ts
188
- function getAngularTemplatePath(type) {
189
- return `angular/${type}.ts.hbs`;
190
- }
191
- function generateAngularComponent(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
192
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
193
- const templatePath = getAngularTemplatePath("component");
194
- return renderTemplate(templatePath, {
195
- componentName,
196
- formattedNodes,
197
- typescript,
198
- keepColors,
199
- viewBox
200
- });
201
- }
202
- function generateAngularBaseComponent(typescript) {
203
- const templatePath = getAngularTemplatePath("createIcon");
204
- return {
205
- code: renderTemplate(templatePath, { typescript }),
206
- fileName: "createIcon.ts"
207
- };
208
- }
209
-
210
- // src/generators/astro.ts
211
- function getAstroTemplatePath(type) {
212
- return `astro/${type}.astro.hbs`;
213
- }
214
- function generateAstroComponent(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
215
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
216
- const templatePath = getAstroTemplatePath("component");
217
- return renderTemplate(templatePath, {
218
- componentName,
219
- formattedNodes,
220
- typescript,
221
- keepColors,
222
- viewBox
223
- });
224
- }
225
- function generateAstroBaseComponent(typescript) {
226
- const templatePath = getAstroTemplatePath("createIcon");
227
- return {
228
- code: renderTemplate(templatePath, { typescript }),
229
- fileName: "createIcon.astro"
230
- };
231
- }
232
-
233
- // src/generators/lit.ts
234
- function getLitTemplatePath(typescript, type) {
235
- const ext = typescript ? "ts" : "js";
236
- return `lit/${type}.${ext}.hbs`;
237
- }
238
- function generateLitComponent(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
239
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
240
- const templatePath = getLitTemplatePath(typescript, "component");
241
- return renderTemplate(templatePath, {
242
- componentName,
243
- formattedNodes,
244
- typescript,
245
- keepColors,
246
- viewBox
247
- });
248
- }
249
- function generateLitBaseComponent(typescript) {
250
- const templatePath = getLitTemplatePath(typescript, "createIcon");
251
- return {
252
- code: renderTemplate(templatePath, { typescript }),
253
- fileName: typescript ? "createIcon.ts" : "createIcon.js"
254
- };
255
- }
256
-
257
- // src/generators/react.ts
258
- function generateReactComponent(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
259
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
260
- const templatePath = getReactTemplatePath(typescript, "component");
261
- return renderTemplate(templatePath, {
262
- typescript,
263
- componentName,
264
- formattedNodes,
265
- keepColors,
266
- viewBox
267
- });
268
- }
269
- function generateCreateIcon(typescript) {
270
- const templatePath = getReactTemplatePath(typescript, "createIcon");
271
- return renderTemplate(templatePath, { typescript });
272
- }
273
-
274
- // src/generators/react-like.ts
275
- function generateReactLikeComponent(componentName, iconNode, typescript, keepColors, viewBox, framework) {
276
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
277
- let templatePath;
278
- if (framework === "solid") {
279
- templatePath = getSolidTemplatePath(typescript, "component");
280
- } else if (framework === "preact") {
281
- templatePath = getPreactTemplatePath(typescript, "component");
282
- } else if (framework === "qwik") {
283
- templatePath = getQwikTemplatePath(typescript, "component");
284
- } else {
285
- throw new Error(`Unsupported framework: ${framework}`);
286
- }
287
- return renderTemplate(templatePath, {
288
- componentName,
289
- formattedNodes,
290
- typescript,
291
- keepColors,
292
- viewBox
293
- });
294
- }
295
- function generateReactLikeBaseComponent(typescript, framework) {
296
- let templatePath;
297
- if (framework === "solid") {
298
- templatePath = getSolidTemplatePath(typescript, "createIcon");
299
- } else if (framework === "preact") {
300
- templatePath = getPreactTemplatePath(typescript, "createIcon");
301
- } else if (framework === "qwik") {
302
- templatePath = getQwikTemplatePath(typescript, "createIcon");
303
- } else {
304
- throw new Error(`Unsupported framework: ${framework}`);
305
- }
306
- return {
307
- code: renderTemplate(templatePath, { typescript }),
308
- fileName: typescript ? "createIcon.tsx" : "createIcon.jsx"
309
- };
310
- }
311
-
312
- // src/generators/svelte.ts
313
- function generateSvelteComponent(_componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
314
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 4)).join(",\n");
315
- const templatePath = getSvelteTemplatePath(typescript, "component");
316
- return renderTemplate(templatePath, {
317
- typescript,
318
- formattedNodes,
319
- keepColors,
320
- viewBox
321
- });
322
- }
323
- function generateSvelteIcon(typescript) {
324
- const templatePath = getSvelteTemplatePath(typescript, "icon");
325
- return renderTemplate(templatePath, { typescript });
326
- }
327
-
328
- // src/generators/vanilla.ts
329
- function generateVanillaComponent(componentName, iconNode, typescript, keepColors, viewBox) {
330
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 2)).join(",\n");
331
- const templatePath = getVanillaTemplatePath(typescript, "component");
332
- return renderTemplate(templatePath, {
333
- componentName,
334
- formattedNodes,
335
- typescript,
336
- keepColors,
337
- viewBox
338
- });
339
- }
340
- function generateVanillaBaseComponent(typescript) {
341
- const templatePath = getVanillaTemplatePath(typescript, "createIcon");
342
- return {
343
- code: renderTemplate(templatePath, { typescript }),
344
- fileName: typescript ? "createIcon.ts" : "createIcon.js"
345
- };
346
- }
347
-
348
- // src/generators/vue.ts
349
- function generateVueComponent(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
350
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 4)).join(",\n");
351
- const templatePath = getVueTemplatePath(typescript, "component");
352
- return renderTemplate(templatePath, {
353
- typescript,
354
- componentName,
355
- formattedNodes,
356
- keepColors,
357
- viewBox
358
- });
359
- }
360
- function generateVueIcon(typescript) {
361
- const templatePath = getVueTemplatePath(typescript, "icon");
362
- return renderTemplate(templatePath, { typescript });
363
- }
364
-
365
- // src/generators/vue2.ts
366
- function generateVue2Component(componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") {
367
- const formattedNodes = iconNode.map((node) => formatIconNode(node, 4)).join(",\n");
368
- const templatePath = getVue2TemplatePath(typescript, "component");
369
- return renderTemplate(templatePath, {
370
- typescript,
371
- componentName,
372
- formattedNodes,
373
- keepColors,
374
- viewBox
375
- });
376
- }
377
- function generateVue2Icon(typescript) {
378
- const templatePath = getVue2TemplatePath(typescript, "icon");
379
- return renderTemplate(templatePath, { typescript });
380
- }
381
-
382
- // src/generators/framework-strategy.ts
383
- var ReactStrategy = class {
384
- constructor() {
385
- this.name = "react";
386
- this.getComponentExtension = (typescript) => {
387
- return typescript ? "tsx" : "jsx";
388
- };
389
- this.getIndexExtension = (typescript) => {
390
- return typescript ? "ts" : "js";
391
- };
392
- this.generateComponent = (componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") => {
393
- return generateReactComponent(componentName, iconNode, typescript, keepColors, viewBox);
394
- };
395
- this.generateBaseComponent = (typescript) => {
396
- return {
397
- code: generateCreateIcon(typescript),
398
- fileName: `createIcon.${this.getComponentExtension(typescript)}`
399
- };
400
- };
401
- }
402
- };
403
- var VueStrategy = class {
404
- constructor() {
405
- this.name = "vue";
406
- this.getComponentExtension = (_typescript) => {
407
- return "vue";
408
- };
409
- this.getIndexExtension = (typescript) => {
410
- return typescript ? "ts" : "js";
411
- };
412
- this.generateComponent = (componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") => {
413
- return generateVueComponent(componentName, iconNode, typescript, keepColors, viewBox);
414
- };
415
- this.generateBaseComponent = (typescript) => {
416
- return {
417
- code: generateVueIcon(typescript),
418
- fileName: "Icon.vue"
419
- };
420
- };
421
- }
422
- };
423
- var Vue2Strategy = class {
424
- constructor() {
425
- this.name = "vue2";
426
- this.getComponentExtension = (_typescript) => {
427
- return "vue";
428
- };
429
- this.getIndexExtension = (typescript) => {
430
- return typescript ? "ts" : "js";
431
- };
432
- this.generateComponent = (componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") => {
433
- return generateVue2Component(componentName, iconNode, typescript, keepColors, viewBox);
434
- };
435
- this.generateBaseComponent = (typescript) => {
436
- return {
437
- code: generateVue2Icon(typescript),
438
- fileName: "Icon.vue"
439
- };
440
- };
441
- }
442
- };
443
- var SvelteStrategy = class {
444
- constructor() {
445
- this.name = "svelte";
446
- this.getComponentExtension = (_typescript) => {
447
- return "svelte";
448
- };
449
- this.getIndexExtension = (typescript) => {
450
- return typescript ? "ts" : "js";
451
- };
452
- this.generateComponent = (componentName, iconNode, typescript, keepColors = false, viewBox = "0 0 24 24") => {
453
- return generateSvelteComponent(componentName, iconNode, typescript, keepColors, viewBox);
454
- };
455
- this.generateBaseComponent = (typescript) => {
456
- return {
457
- code: generateSvelteIcon(typescript),
458
- fileName: "Icon.svelte"
459
- };
460
- };
461
- }
462
- };
463
- var SolidStrategy = class {
464
- constructor() {
465
- this.name = "solid";
466
- this.getComponentExtension = (typescript) => {
467
- return typescript ? "tsx" : "jsx";
468
- };
469
- this.getIndexExtension = (typescript) => {
470
- return typescript ? "ts" : "js";
471
- };
472
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
473
- return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24", "solid");
474
- };
475
- this.generateBaseComponent = (typescript) => {
476
- return generateReactLikeBaseComponent(typescript, "solid");
477
- };
478
- }
479
- };
480
- var PreactStrategy = class {
481
- constructor() {
482
- this.name = "preact";
483
- this.getComponentExtension = (typescript) => {
484
- return typescript ? "tsx" : "jsx";
485
- };
486
- this.getIndexExtension = (typescript) => {
487
- return typescript ? "ts" : "js";
488
- };
489
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
490
- return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24", "preact");
491
- };
492
- this.generateBaseComponent = (typescript) => {
493
- return generateReactLikeBaseComponent(typescript, "preact");
494
- };
495
- }
496
- };
497
- var VanillaStrategy = class {
498
- constructor() {
499
- this.name = "vanilla";
500
- this.getComponentExtension = (typescript) => {
501
- return typescript ? "ts" : "js";
502
- };
503
- this.getIndexExtension = (typescript) => {
504
- return typescript ? "ts" : "js";
505
- };
506
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
507
- return generateVanillaComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24");
508
- };
509
- this.generateBaseComponent = (typescript) => {
510
- return generateVanillaBaseComponent(typescript);
511
- };
512
- }
513
- };
514
- var LitStrategy = class {
515
- constructor() {
516
- this.name = "lit";
517
- this.getComponentExtension = (typescript) => {
518
- return typescript ? "ts" : "js";
519
- };
520
- this.getIndexExtension = (typescript) => {
521
- return typescript ? "ts" : "js";
522
- };
523
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
524
- return generateLitComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24");
525
- };
526
- this.generateBaseComponent = (typescript) => {
527
- return generateLitBaseComponent(typescript);
528
- };
529
- }
530
- };
531
- var QwikStrategy = class {
532
- constructor() {
533
- this.name = "qwik";
534
- this.getComponentExtension = (typescript) => {
535
- return typescript ? "tsx" : "jsx";
536
- };
537
- this.getIndexExtension = (typescript) => {
538
- return typescript ? "ts" : "js";
539
- };
540
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
541
- return generateReactLikeComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24", "qwik");
542
- };
543
- this.generateBaseComponent = (typescript) => {
544
- return generateReactLikeBaseComponent(typescript, "qwik");
545
- };
546
- }
547
- };
548
- var AstroStrategy = class {
549
- constructor() {
550
- this.name = "astro";
551
- this.getComponentExtension = (_typescript) => {
552
- return "astro";
553
- };
554
- this.getIndexExtension = (typescript) => {
555
- return typescript ? "ts" : "js";
556
- };
557
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
558
- return generateAstroComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24");
559
- };
560
- this.generateBaseComponent = (typescript) => {
561
- return generateAstroBaseComponent(typescript);
562
- };
563
- }
564
- };
565
- var AngularStrategy = class {
566
- constructor() {
567
- this.name = "angular";
568
- this.getComponentExtension = (_typescript) => {
569
- return "ts";
570
- };
571
- this.getIndexExtension = (_typescript) => {
572
- return "ts";
573
- };
574
- this.generateComponent = (componentName, iconNode, typescript, keepColors, viewBox) => {
575
- return generateAngularComponent(componentName, iconNode, typescript, keepColors ?? false, viewBox ?? "0 0 24 24");
576
- };
577
- this.generateBaseComponent = (typescript) => {
578
- return generateAngularBaseComponent(typescript);
579
- };
580
- }
581
- };
582
- var FrameworkRegistry = class {
583
- constructor() {
584
- this.strategies = /* @__PURE__ */ new Map();
585
- this.register(new ReactStrategy());
586
- this.register(new VueStrategy());
587
- this.register(new Vue2Strategy());
588
- this.register(new SvelteStrategy());
589
- this.register(new SolidStrategy());
590
- this.register(new PreactStrategy());
591
- this.register(new VanillaStrategy());
592
- this.register(new LitStrategy());
593
- this.register(new QwikStrategy());
594
- this.register(new AstroStrategy());
595
- this.register(new AngularStrategy());
596
- }
597
- /**
598
- * Register a framework strategy
599
- */
600
- register(strategy) {
601
- this.strategies.set(strategy.name, strategy);
602
- }
603
- /**
604
- * Get a framework strategy
605
- */
606
- get(framework) {
607
- const strategy = this.strategies.get(framework);
608
- if (!strategy) {
609
- throw new Error(`Unsupported framework: ${framework}`);
610
- }
611
- return strategy;
612
- }
613
- /**
614
- * Check if a framework is supported
615
- */
616
- has(framework) {
617
- return this.strategies.has(framework);
618
- }
619
- /**
620
- * Get all supported frameworks
621
- */
622
- getSupportedFrameworks() {
623
- return Array.from(this.strategies.keys());
624
- }
625
- };
626
- var frameworkRegistry = new FrameworkRegistry();
627
- function getFrameworkStrategy(framework) {
628
- return frameworkRegistry.get(framework);
629
- }
630
-
631
- // src/config/loader.ts
632
- import path2 from "path";
633
- import process2 from "process";
634
- import { createJiti } from "jiti";
635
- var DEFAULT_CONFIG = {
636
- input: "./icons",
637
- output: "./src/icons",
638
- typescript: true,
639
- optimize: true,
640
- prefix: "",
641
- suffix: "",
642
- configDir: ".",
643
- generateOptions: {
644
- index: true,
645
- types: true,
646
- preview: false,
647
- cleanOutput: false
648
- },
649
- watch: {
650
- enabled: false,
651
- ignore: ["**/node_modules/**", "**/.git/**"],
652
- debounce: 300
653
- }
654
- };
655
- async function loadConfig(configPath) {
656
- const absolutePath = path2.resolve(process2.cwd(), configPath);
657
- const configDir = path2.dirname(absolutePath);
658
- const jiti = createJiti(configDir, {
659
- interopDefault: true
660
- });
661
- let config;
662
- try {
663
- config = await jiti.import(absolutePath);
664
- } catch (error) {
665
- throw new Error(`Failed to load config from ${configPath}: ${error.message}`);
666
- }
667
- let loadedConfig = config.default || config;
668
- if (Array.isArray(loadedConfig)) {
669
- loadedConfig = loadedConfig[0];
670
- }
671
- const mergedConfig = {
672
- ...DEFAULT_CONFIG,
673
- ...loadedConfig,
674
- generateOptions: {
675
- ...DEFAULT_CONFIG.generateOptions,
676
- ...loadedConfig.generateOptions
677
- },
678
- watch: {
679
- ...DEFAULT_CONFIG.watch,
680
- ...loadedConfig.watch
681
- }
682
- };
683
- if (mergedConfig.configDir && mergedConfig.configDir !== ".") {
684
- const projectRoot = path2.resolve(configDir, mergedConfig.configDir);
685
- mergedConfig.input = path2.resolve(projectRoot, mergedConfig.input);
686
- mergedConfig.output = path2.resolve(projectRoot, mergedConfig.output);
687
- } else {
688
- mergedConfig.input = path2.resolve(configDir, mergedConfig.input);
689
- mergedConfig.output = path2.resolve(configDir, mergedConfig.output);
690
- }
691
- if (!mergedConfig.framework) {
692
- const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
693
- throw new Error(`Config must specify a framework (${supported})`);
694
- }
695
- if (!frameworkRegistry.has(mergedConfig.framework)) {
696
- const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
697
- throw new Error(`Invalid framework: ${mergedConfig.framework}. Supported: ${supported}`);
698
- }
699
- return mergedConfig;
700
- }
701
- async function findConfig() {
702
- const { fileExists: fileExists2 } = await import("./helpers-ZOR3OD66.mjs");
703
- const configFiles = [
704
- "vectify.config.ts",
705
- "vectify.config.js"
706
- ];
707
- for (const file of configFiles) {
708
- const configPath = path2.resolve(process2.cwd(), file);
709
- if (await fileExists2(configPath)) {
710
- return configPath;
711
- }
712
- }
713
- return null;
714
- }
715
-
716
- // src/generators/index.ts
717
- import path4 from "path";
718
-
719
- // src/cache/hash-utils.ts
720
- import { createHash } from "crypto";
721
- import { readFile as readFile2 } from "fs/promises";
722
- function hashContent(content) {
723
- return createHash("sha256").update(content).digest("hex");
724
- }
725
- async function hashFile(filePath) {
726
- try {
727
- const content = await readFile2(filePath, "utf-8");
728
- return hashContent(content);
729
- } catch (error) {
730
- throw new Error(`Failed to hash file ${filePath}: ${error}`);
731
- }
732
- }
733
- function hashConfig(config) {
734
- const relevantConfig = {
735
- framework: config.framework,
736
- typescript: config.typescript,
737
- keepColors: config.keepColors,
738
- prefix: config.prefix,
739
- suffix: config.suffix,
740
- optimize: config.optimize,
741
- svgoConfig: config.svgoConfig,
742
- componentNameTransform: config.componentNameTransform?.toString()
743
- };
744
- return hashContent(JSON.stringify(relevantConfig));
745
- }
746
- function hashSvgoConfig(svgoConfig) {
747
- return hashContent(JSON.stringify(svgoConfig || {}));
748
- }
749
-
750
- // src/parsers/optimizer.ts
751
- import { optimize } from "svgo";
752
- var DEFAULT_SVGO_CONFIG = {
753
- plugins: [
754
- "preset-default",
755
- "removeViewBox",
756
- "removeDimensions",
757
- "convertShapeToPath",
758
- {
759
- name: "removeAttrs",
760
- params: {
761
- attrs: ["class", "data-name"]
762
- }
763
- }
764
- ]
765
- };
766
- async function optimizeSvg(svgContent, config) {
767
- if (!config.optimize) {
768
- return svgContent;
769
- }
770
- try {
771
- const result = optimize(svgContent, {
772
- ...DEFAULT_SVGO_CONFIG,
773
- ...config.svgoConfig
774
- });
775
- return result.data;
776
- } catch (error) {
777
- console.warn(`SVGO optimization failed: ${error.message}`);
778
- return svgContent;
779
- }
780
- }
781
-
782
- // src/utils/formatter.ts
783
- import { exec } from "child_process";
784
- import path3 from "path";
785
- import process3 from "process";
786
- import { promisify } from "util";
787
- var execAsync = promisify(exec);
788
- var FORMATTER_PATTERNS = {
789
- biome: ["biome.json", "biome.jsonc"],
790
- prettier: [
791
- ".prettierrc",
792
- ".prettierrc.json",
793
- ".prettierrc.yml",
794
- ".prettierrc.yaml",
795
- ".prettierrc.js",
796
- ".prettierrc.cjs",
797
- ".prettierrc.mjs",
798
- "prettier.config.js",
799
- "prettier.config.cjs",
800
- "prettier.config.mjs"
801
- ],
802
- eslint: [
803
- "eslint.config.js",
804
- "eslint.config.mjs",
805
- "eslint.config.cjs",
806
- "eslint.config.ts",
807
- ".eslintrc",
808
- ".eslintrc.js",
809
- ".eslintrc.cjs",
810
- ".eslintrc.json",
811
- ".eslintrc.yml",
812
- ".eslintrc.yaml"
813
- ]
814
- };
815
- var FORMATTER_COMMANDS = {
816
- biome: (outputDir, args) => `npx @biomejs/biome format --write ${args || ""} "${outputDir}"`.trim(),
817
- prettier: (outputDir, args) => `npx prettier --write ${args || ""} "${outputDir}"`.trim(),
818
- eslint: (outputDir, args) => `npx eslint --fix ${args || ""} "${outputDir}"`.trim()
819
- };
820
- function normalizeFormatOption(format) {
821
- if (format === false) {
822
- return null;
823
- }
824
- if (format === true) {
825
- return { tool: "auto" };
826
- }
827
- if (typeof format === "string") {
828
- return { tool: format };
829
- }
830
- return format;
831
- }
832
- async function detectFormatter() {
833
- const cwd = process3.cwd();
834
- const priority = ["biome", "prettier", "eslint"];
835
- for (const tool of priority) {
836
- const patterns = FORMATTER_PATTERNS[tool];
837
- for (const pattern of patterns) {
838
- const configPath = path3.join(cwd, pattern);
839
- if (await fileExists(configPath)) {
840
- return tool;
841
- }
842
- }
843
- }
844
- return null;
845
- }
846
- async function formatOutput(outputDir, format) {
847
- const config = normalizeFormatOption(format);
848
- if (!config) {
849
- return { success: true };
850
- }
851
- let tool = null;
852
- if (config.tool === "auto") {
853
- tool = await detectFormatter();
854
- if (!tool) {
855
- return {
856
- success: true,
857
- error: "No formatter detected. Install prettier, eslint, or biome to enable auto-formatting."
858
- };
859
- }
860
- } else {
861
- tool = config.tool || "prettier";
862
- }
863
- const command = FORMATTER_COMMANDS[tool](outputDir, config.args);
864
- try {
865
- await execAsync(command, { cwd: process3.cwd() });
866
- return { success: true, tool };
867
- } catch (error) {
868
- return {
869
- success: false,
870
- tool,
871
- error: `Format failed with ${tool}: ${error.message}`
872
- };
873
- }
874
- }
875
-
876
- // src/generators/index.ts
877
- async function generateIcons(config, dryRun = false) {
878
- const stats = {
879
- success: 0,
880
- failed: 0,
881
- total: 0,
882
- errors: []
883
- };
884
- try {
885
- if (!dryRun) {
886
- await ensureDir(config.output);
887
- }
888
- const svgFiles = await getSvgFiles(config.input);
889
- stats.total = svgFiles.length;
890
- if (svgFiles.length === 0) {
891
- console.warn(`No SVG files found in ${config.input}`);
892
- return stats;
893
- }
894
- if (config.generateOptions?.cleanOutput && !dryRun) {
895
- await cleanOutputDirectory(svgFiles, config);
896
- }
897
- await generateBaseComponent(config, dryRun);
898
- for (const svgFile of svgFiles) {
899
- try {
900
- await generateIconComponent(svgFile, config, dryRun);
901
- stats.success++;
902
- } catch (error) {
903
- stats.failed++;
904
- stats.errors.push({
905
- file: svgFile,
906
- error: error.message
907
- });
908
- console.error(`Failed to generate ${svgFile}: ${error.message}`);
909
- }
910
- }
911
- if (config.generateOptions?.index) {
912
- await generateIndexFile(svgFiles, config, dryRun);
913
- }
914
- if (config.generateOptions?.preview && !dryRun) {
915
- await generatePreviewHtml(svgFiles, config);
916
- }
917
- if (config.format && !dryRun) {
918
- const formatResult = await formatOutput(config.output, config.format);
919
- if (formatResult.success && formatResult.tool) {
920
- console.log(`Formatted with ${formatResult.tool}`);
921
- } else if (formatResult.error) {
922
- console.warn(formatResult.error);
923
- }
924
- }
925
- if (config.hooks?.onComplete) {
926
- await config.hooks.onComplete(stats);
927
- }
928
- } catch (error) {
929
- throw new Error(`Generation failed: ${error.message}`);
930
- }
931
- return stats;
932
- }
933
- async function generateIconsIncremental(config, cacheManager, dryRun = false) {
934
- const stats = {
935
- success: 0,
936
- failed: 0,
937
- total: 0,
938
- errors: []
939
- };
940
- try {
941
- if (!dryRun) {
942
- await ensureDir(config.output);
943
- }
944
- const svgFiles = await getSvgFiles(config.input);
945
- stats.total = svgFiles.length;
946
- if (svgFiles.length === 0) {
947
- console.warn(`No SVG files found in ${config.input}`);
948
- return stats;
949
- }
950
- if (cacheManager.hasConfigChanged(config)) {
951
- console.log("\u26A0\uFE0F Configuration changed, rebuilding all icons...");
952
- cacheManager.invalidateAll();
953
- }
954
- cacheManager.updateConfigHash(config);
955
- await cacheManager.cleanStaleEntries(svgFiles);
956
- if (config.generateOptions?.cleanOutput && !dryRun) {
957
- await cleanOutputDirectory(svgFiles, config);
958
- }
959
- await generateBaseComponent(config, dryRun);
960
- const strategy = getFrameworkStrategy(config.framework);
961
- const typescript = config.typescript ?? true;
962
- const fileExt = strategy.getComponentExtension(typescript);
963
- const filesToGenerate = [];
964
- const cachedFiles = [];
965
- for (const svgFile of svgFiles) {
966
- const fileName = path4.basename(svgFile);
967
- const componentName = getComponentName(
968
- fileName,
969
- config.prefix,
970
- config.suffix,
971
- config.componentNameTransform
972
- );
973
- const componentPath = path4.join(config.output, `${componentName}.${fileExt}`);
974
- const needsRegen = await cacheManager.needsRegeneration(
975
- svgFile,
976
- componentPath,
977
- config
978
- );
979
- if (needsRegen) {
980
- filesToGenerate.push(svgFile);
981
- } else {
982
- cachedFiles.push(svgFile);
983
- }
984
- }
985
- if (cachedFiles.length > 0) {
986
- console.log(`\u{1F4E6} Cache: ${cachedFiles.length} cached, ${filesToGenerate.length} to generate`);
987
- }
988
- for (const svgFile of filesToGenerate) {
989
- try {
990
- await generateIconComponent(svgFile, config, dryRun, cacheManager);
991
- stats.success++;
992
- } catch (error) {
993
- stats.failed++;
994
- stats.errors.push({
995
- file: svgFile,
996
- error: error.message
997
- });
998
- console.error(`Failed to generate ${svgFile}: ${error.message}`);
999
- }
1000
- }
1001
- stats.success += cachedFiles.length;
1002
- if (config.generateOptions?.index) {
1003
- await generateIndexFile(svgFiles, config, dryRun);
1004
- }
1005
- if (config.generateOptions?.preview && !dryRun) {
1006
- await generatePreviewHtml(svgFiles, config);
1007
- }
1008
- if (!dryRun) {
1009
- await cacheManager.save();
1010
- const cacheStats = cacheManager.getStats();
1011
- if (cacheStats.total > 0) {
1012
- const hitRate = cacheManager.getHitRate();
1013
- const timeSaved = (cacheStats.timeSaved / 1e3).toFixed(1);
1014
- console.log(`\u26A1 Cache saved ~${timeSaved}s (${hitRate.toFixed(1)}% hit rate)`);
1015
- }
1016
- }
1017
- if (config.format && !dryRun) {
1018
- const formatResult = await formatOutput(config.output, config.format);
1019
- if (formatResult.success && formatResult.tool) {
1020
- console.log(`Formatted with ${formatResult.tool}`);
1021
- } else if (formatResult.error) {
1022
- console.warn(formatResult.error);
1023
- }
1024
- }
1025
- if (config.hooks?.onComplete) {
1026
- await config.hooks.onComplete(stats);
1027
- }
1028
- } catch (error) {
1029
- throw new Error(`Generation failed: ${error.message}`);
1030
- }
1031
- return stats;
1032
- }
1033
- async function generateIconComponent(svgFile, config, dryRun = false, cacheManager) {
1034
- let svgContent = await readFile(svgFile);
1035
- const fileName = path4.basename(svgFile);
1036
- if (config.hooks?.beforeParse) {
1037
- svgContent = await config.hooks.beforeParse(svgContent, fileName);
1038
- }
1039
- const { isMultiColor: isMultiColorBeforeOptimization } = parseSvg(svgContent);
1040
- svgContent = await optimizeSvg(svgContent, config);
1041
- const { iconNodes, viewBox } = parseSvg(svgContent);
1042
- const componentName = getComponentName(
1043
- fileName,
1044
- config.prefix,
1045
- config.suffix,
1046
- config.componentNameTransform
1047
- );
1048
- const strategy = getFrameworkStrategy(config.framework);
1049
- const typescript = config.typescript ?? true;
1050
- const keepColors = config.keepColors !== void 0 ? config.keepColors : isMultiColorBeforeOptimization;
1051
- let code = strategy.generateComponent(
1052
- componentName,
1053
- iconNodes,
1054
- typescript,
1055
- keepColors,
1056
- viewBox
1057
- );
1058
- if (config.hooks?.afterGenerate) {
1059
- code = await config.hooks.afterGenerate(code, componentName);
1060
- }
1061
- const fileExt = strategy.getComponentExtension(typescript);
1062
- const outputPath = path4.join(config.output, `${componentName}.${fileExt}`);
1063
- if (dryRun) {
1064
- console.log(` ${componentName}.${fileExt}`);
1065
- } else {
1066
- await writeFile(outputPath, code);
1067
- if (cacheManager) {
1068
- const componentHash = hashContent(code);
1069
- await cacheManager.updateEntry(
1070
- svgFile,
1071
- outputPath,
1072
- componentName,
1073
- componentHash,
1074
- config
1075
- );
1076
- }
1077
- }
1078
- }
1079
- async function generateBaseComponent(config, dryRun = false) {
1080
- const typescript = config.typescript ?? true;
1081
- const strategy = getFrameworkStrategy(config.framework);
1082
- const { code, fileName } = strategy.generateBaseComponent(typescript);
1083
- const outputPath = path4.join(config.output, fileName);
1084
- if (dryRun) {
1085
- console.log(` ${fileName}`);
1086
- } else {
1087
- await writeFile(outputPath, code);
1088
- }
1089
- }
1090
- async function generateIndexFile(svgFiles, config, dryRun = false) {
1091
- const typescript = config.typescript ?? true;
1092
- const strategy = getFrameworkStrategy(config.framework);
1093
- const ext = strategy.getIndexExtension(typescript);
1094
- const componentExt = strategy.getComponentExtension(typescript);
1095
- const usesDefaultExport = ["vue", "vue2", "svelte", "react", "preact"].includes(config.framework);
1096
- const needsExtension = ["vue", "vue2", "svelte"].includes(config.framework);
1097
- const exports = svgFiles.map((svgFile) => {
1098
- const fileName = path4.basename(svgFile);
1099
- const componentName = getComponentName(
1100
- fileName,
1101
- config.prefix,
1102
- config.suffix,
1103
- config.componentNameTransform
1104
- );
1105
- const importPath = needsExtension ? `./${componentName}.${componentExt}` : `./${componentName}`;
1106
- if (usesDefaultExport) {
1107
- return `export { default as ${componentName} } from '${importPath}'`;
1108
- } else {
1109
- return `export { ${componentName} } from '${importPath}'`;
1110
- }
1111
- }).join("\n");
1112
- const indexPath = path4.join(config.output, `index.${ext}`);
1113
- if (dryRun) {
1114
- console.log(` index.${ext}`);
1115
- } else {
1116
- await writeFile(indexPath, `${exports}
1117
- `);
1118
- }
1119
- }
1120
- async function generatePreviewHtml(svgFiles, config) {
1121
- const componentNames = svgFiles.map((svgFile) => {
1122
- const fileName = path4.basename(svgFile);
1123
- return getComponentName(
1124
- fileName,
1125
- config.prefix,
1126
- config.suffix,
1127
- config.componentNameTransform
1128
- );
1129
- });
1130
- const svgContents = await Promise.all(
1131
- svgFiles.map(async (svgFile) => {
1132
- return await readFile(svgFile);
1133
- })
1134
- );
1135
- const html = `<!DOCTYPE html>
1136
- <html lang="en">
1137
- <head>
1138
- <meta charset="UTF-8">
1139
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1140
- <title>Icons - ${componentNames.length} Total</title>
1141
- <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
1142
- </head>
1143
- <body class="bg-white">
1144
- <div class="max-w-[1400px] mx-auto px-6 py-12">
1145
- <header class="mb-12">
1146
- <h1 class="text-3xl font-semibold text-gray-900 mb-2">Icons</h1>
1147
- <p class="text-sm text-gray-600">${componentNames.length} icons \u2022 ${config.framework} \u2022 Click to copy name</p>
1148
- </header>
1149
-
1150
- <div class="flex items-center gap-4 mb-8 pb-6 border-b border-gray-200">
1151
- <div class="flex-1">
1152
- <input
1153
- type="search"
1154
- id="search"
1155
- placeholder="Search icons..."
1156
- class="w-full h-10 px-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:border-gray-900 transition-colors"
1157
- />
1158
- </div>
1159
-
1160
- <div class="flex items-center gap-3">
1161
- <label for="size" class="text-sm text-gray-600">Size</label>
1162
- <input
1163
- type="range"
1164
- id="size"
1165
- min="16"
1166
- max="96"
1167
- value="32"
1168
- step="8"
1169
- class="w-24 h-1 accent-gray-900"
1170
- />
1171
- <span id="sizeValue" class="text-sm text-gray-900 w-10">32px</span>
1172
- </div>
1173
-
1174
- <div class="flex items-center gap-3">
1175
- <label for="color" class="text-sm text-gray-600">Color</label>
1176
- <input
1177
- type="color"
1178
- id="color"
1179
- value="#171717"
1180
- class="w-10 h-10 rounded-lg border border-gray-300 cursor-pointer"
1181
- />
1182
- </div>
1183
- </div>
1184
-
1185
- <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>
1186
- </div>
1187
-
1188
- <script>
1189
- const icons = ${JSON.stringify(componentNames)};
1190
- const svgContents = ${JSON.stringify(svgContents)};
1191
-
1192
- let currentSize = 32;
1193
- let currentColor = '#171717';
1194
- let searchQuery = '';
1195
-
1196
- function renderIcons() {
1197
- const grid = document.getElementById('iconGrid');
1198
- grid.innerHTML = '';
1199
-
1200
- const filteredIcons = icons.filter((name) =>
1201
- name.toLowerCase().includes(searchQuery.toLowerCase())
1202
- );
1203
-
1204
- if (filteredIcons.length === 0) {
1205
- grid.innerHTML = '<div class="col-span-full text-center py-20 text-gray-400 text-sm">No icons found</div>';
1206
- return;
1207
- }
1208
-
1209
- for (let i = 0; i < filteredIcons.length; i++) {
1210
- const iconName = filteredIcons[i];
1211
- const svgIndex = icons.indexOf(iconName);
1212
- const svgContent = svgContents[svgIndex];
1213
-
1214
- const card = document.createElement('div');
1215
- 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';
1216
- card.onclick = () => copyToClipboard(iconName);
1217
- card.title = iconName;
1218
-
1219
- const wrapper = document.createElement('div');
1220
- wrapper.className = 'flex items-center justify-center mb-2';
1221
-
1222
- wrapper.innerHTML = svgContent;
1223
-
1224
- const svg = wrapper.querySelector('svg');
1225
- if (svg) {
1226
- svg.setAttribute('width', currentSize);
1227
- svg.setAttribute('height', currentSize);
1228
- svg.setAttribute('stroke', currentColor);
1229
- svg.setAttribute('fill', 'none');
1230
-
1231
- // Update stroke color for all child elements
1232
- const elements = svg.querySelectorAll('[stroke]');
1233
- elements.forEach(el => {
1234
- el.setAttribute('stroke', currentColor);
1235
- });
1236
- }
1237
-
1238
- const name = document.createElement('div');
1239
- name.className = 'text-[10px] text-gray-500 text-center truncate w-full group-hover:text-gray-900';
1240
- name.textContent = iconName;
1241
-
1242
- card.appendChild(wrapper);
1243
- card.appendChild(name);
1244
- grid.appendChild(card);
1245
- }
1246
- }
1247
-
1248
- function copyToClipboard(text) {
1249
- navigator.clipboard.writeText(text).then(() => {
1250
- showCopiedNotification(text);
1251
- });
1252
- }
1253
-
1254
- function showCopiedNotification(text) {
1255
- const existing = document.querySelector('.toast');
1256
- if (existing) existing.remove();
1257
-
1258
- const notification = document.createElement('div');
1259
- 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';
1260
- notification.innerHTML = \`
1261
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1262
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
1263
- </svg>
1264
- <span>Copied \${text}</span>
1265
- \`;
1266
- document.body.appendChild(notification);
1267
-
1268
- setTimeout(() => {
1269
- notification.style.opacity = '0';
1270
- setTimeout(() => notification.remove(), 200);
1271
- }, 1800);
1272
- }
1273
-
1274
- document.getElementById('size').addEventListener('input', (e) => {
1275
- currentSize = e.target.value;
1276
- document.getElementById('sizeValue').textContent = currentSize + 'px';
1277
- renderIcons();
1278
- });
1279
-
1280
- document.getElementById('color').addEventListener('input', (e) => {
1281
- currentColor = e.target.value;
1282
- renderIcons();
1283
- });
1284
-
1285
- document.getElementById('search').addEventListener('input', (e) => {
1286
- searchQuery = e.target.value;
1287
- renderIcons();
1288
- });
1289
-
1290
- renderIcons();
1291
- </script>
1292
- </body>
1293
- </html>`;
1294
- const previewPath = path4.join(config.output, "preview.html");
1295
- await writeFile(previewPath, html);
1296
- }
1297
- async function cleanOutputDirectory(svgFiles, config) {
1298
- const { readdir, unlink } = await import("fs/promises");
1299
- const strategy = getFrameworkStrategy(config.framework);
1300
- const fileExt = strategy.getComponentExtension(config.typescript ?? true);
1301
- const expectedComponents = new Set(
1302
- svgFiles.map((svgFile) => {
1303
- const fileName = path4.basename(svgFile, ".svg");
1304
- const componentName = getComponentName(
1305
- fileName,
1306
- config.prefix,
1307
- config.suffix,
1308
- config.componentNameTransform
1309
- );
1310
- return `${componentName}.${fileExt}`;
1311
- })
1312
- );
1313
- const protectedFiles = /* @__PURE__ */ new Set([
1314
- `Icon.${fileExt}`,
1315
- `createIcon.${fileExt}`,
1316
- `index.${config.typescript ? "ts" : "js"}`,
1317
- `index.d.ts`,
1318
- "preview.html"
1319
- ]);
1320
- try {
1321
- const files = await readdir(config.output);
1322
- for (const file of files) {
1323
- if (protectedFiles.has(file)) {
1324
- continue;
1325
- }
1326
- if (!file.endsWith(`.${fileExt}`)) {
1327
- continue;
1328
- }
1329
- if (!expectedComponents.has(file)) {
1330
- const filePath = path4.join(config.output, file);
1331
- await unlink(filePath);
1332
- console.log(`Deleted orphaned component: ${file}`);
1333
- }
1334
- }
1335
- } catch (error) {
1336
- console.warn(`Failed to clean output directory: ${error.message}`);
1337
- }
1338
- }
1339
-
1340
- // src/commands/generate.ts
1341
- import chalk from "chalk";
1342
- import ora from "ora";
1343
-
1344
- // src/cache/cache-manager.ts
1345
- import { existsSync } from "fs";
1346
- import { mkdir, readFile as readFile3, stat, writeFile as writeFile2 } from "fs/promises";
1347
- import { dirname, join } from "path";
1348
- var CACHE_VERSION = "1.0.0";
1349
- var CacheManager = class {
1350
- constructor(outputDir, cacheDir = ".vectify") {
1351
- this.cache = {
1352
- version: CACHE_VERSION,
1353
- configHash: "",
1354
- entries: {},
1355
- baseComponentHash: ""
1356
- };
1357
- this.cacheFilePath = join(outputDir, cacheDir, "cache.json");
1358
- this.stats = {
1359
- hits: 0,
1360
- misses: 0,
1361
- total: 0,
1362
- timeSaved: 0
1363
- };
1364
- this.isDirty = false;
1365
- process.on("beforeExit", () => {
1366
- if (this.isDirty) {
1367
- this.saveSync();
1368
- }
1369
- });
1370
- }
1371
- /**
1372
- * Load cache from disk
1373
- */
1374
- async load() {
1375
- try {
1376
- if (!existsSync(this.cacheFilePath)) {
1377
- return;
1378
- }
1379
- const content = await readFile3(this.cacheFilePath, "utf-8");
1380
- const loadedCache = JSON.parse(content);
1381
- if (loadedCache.version !== CACHE_VERSION) {
1382
- console.log("\u26A0\uFE0F Cache version mismatch, rebuilding cache...");
1383
- return;
1384
- }
1385
- this.cache = loadedCache;
1386
- } catch (error) {
1387
- console.warn("\u26A0\uFE0F Failed to load cache, starting fresh:", error);
1388
- this.cache = {
1389
- version: CACHE_VERSION,
1390
- configHash: "",
1391
- entries: {},
1392
- baseComponentHash: ""
1393
- };
1394
- }
1395
- }
1396
- /**
1397
- * Save cache to disk (atomic write)
1398
- */
1399
- async save() {
1400
- try {
1401
- const cacheDir = dirname(this.cacheFilePath);
1402
- await mkdir(cacheDir, { recursive: true });
1403
- const tempPath = `${this.cacheFilePath}.tmp`;
1404
- await writeFile2(tempPath, JSON.stringify(this.cache, null, 2), "utf-8");
1405
- await writeFile2(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
1406
- this.isDirty = false;
1407
- } catch (error) {
1408
- console.warn("\u26A0\uFE0F Failed to save cache:", error);
1409
- }
1410
- }
1411
- /**
1412
- * Synchronous save for process exit
1413
- */
1414
- saveSync() {
1415
- try {
1416
- const fs2 = __require("fs");
1417
- const cacheDir = dirname(this.cacheFilePath);
1418
- if (!fs2.existsSync(cacheDir)) {
1419
- fs2.mkdirSync(cacheDir, { recursive: true });
1420
- }
1421
- fs2.writeFileSync(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
1422
- this.isDirty = false;
1423
- } catch {
1424
- console.warn("\u26A0\uFE0F Failed to save cache on exit");
1425
- }
1426
- }
1427
- /**
1428
- * Check if a file needs regeneration
1429
- */
1430
- async needsRegeneration(svgPath, componentPath, config) {
1431
- this.stats.total++;
1432
- if (!existsSync(componentPath)) {
1433
- this.stats.misses++;
1434
- return true;
1435
- }
1436
- const entry = this.cache.entries[svgPath];
1437
- if (!entry) {
1438
- this.stats.misses++;
1439
- return true;
1440
- }
1441
- try {
1442
- const stats = await stat(svgPath);
1443
- const currentMtime = stats.mtimeMs;
1444
- if (currentMtime === entry.svgMtime) {
1445
- if (!this.isConfigMatching(entry, config)) {
1446
- this.stats.misses++;
1447
- return true;
1448
- }
1449
- this.stats.hits++;
1450
- this.stats.timeSaved += 50;
1451
- return false;
1452
- }
1453
- const currentHash = await hashFile(svgPath);
1454
- if (currentHash === entry.svgHash) {
1455
- entry.svgMtime = currentMtime;
1456
- this.isDirty = true;
1457
- this.stats.hits++;
1458
- this.stats.timeSaved += 50;
1459
- return false;
1460
- }
1461
- this.stats.misses++;
1462
- return true;
1463
- } catch {
1464
- this.stats.misses++;
1465
- return true;
1466
- }
1467
- }
1468
- /**
1469
- * Check if config matches cache entry
1470
- */
1471
- isConfigMatching(entry, config) {
1472
- const snapshot = entry.configSnapshot;
1473
- return snapshot.framework === config.framework && snapshot.typescript === config.typescript && snapshot.keepColors === config.keepColors && snapshot.prefix === (config.prefix || "") && snapshot.suffix === (config.suffix || "") && snapshot.optimize === config.optimize && snapshot.svgoConfigHash === hashSvgoConfig(config.svgoConfig);
1474
- }
1475
- /**
1476
- * Update cache entry after successful generation
1477
- */
1478
- async updateEntry(svgPath, componentPath, componentName, componentHash, config) {
1479
- try {
1480
- const svgHash = await hashFile(svgPath);
1481
- const stats = await stat(svgPath);
1482
- this.cache.entries[svgPath] = {
1483
- svgPath,
1484
- svgHash,
1485
- svgMtime: stats.mtimeMs,
1486
- componentName,
1487
- componentPath,
1488
- componentHash,
1489
- configSnapshot: {
1490
- framework: config.framework,
1491
- typescript: config.typescript ?? true,
1492
- keepColors: config.keepColors ?? false,
1493
- prefix: config.prefix || "",
1494
- suffix: config.suffix || "",
1495
- optimize: config.optimize ?? true,
1496
- svgoConfigHash: hashSvgoConfig(config.svgoConfig)
1497
- },
1498
- generatedAt: Date.now()
1499
- };
1500
- this.isDirty = true;
1501
- } catch (error) {
1502
- console.warn(`\u26A0\uFE0F Failed to update cache entry for ${svgPath}:`, error);
1503
- }
1504
- }
1505
- /**
1506
- * Remove cache entry
1507
- */
1508
- removeEntry(svgPath) {
1509
- if (this.cache.entries[svgPath]) {
1510
- delete this.cache.entries[svgPath];
1511
- this.isDirty = true;
1512
- }
1513
- }
1514
- /**
1515
- * Update config hash
1516
- */
1517
- updateConfigHash(config) {
1518
- const newHash = hashConfig(config);
1519
- if (this.cache.configHash !== newHash) {
1520
- this.cache.configHash = newHash;
1521
- this.isDirty = true;
1522
- }
1523
- }
1524
- /**
1525
- * Update base component hash
1526
- */
1527
- updateBaseComponentHash(hash) {
1528
- if (this.cache.baseComponentHash !== hash) {
1529
- this.cache.baseComponentHash = hash;
1530
- this.isDirty = true;
1531
- }
1532
- }
1533
- /**
1534
- * Check if config has changed (invalidates all cache)
1535
- */
1536
- hasConfigChanged(config) {
1537
- const currentHash = hashConfig(config);
1538
- return this.cache.configHash !== "" && this.cache.configHash !== currentHash;
1539
- }
1540
- /**
1541
- * Invalidate all cache entries
1542
- */
1543
- invalidateAll() {
1544
- this.cache.entries = {};
1545
- this.isDirty = true;
1546
- }
1547
- /**
1548
- * Clean stale entries (files that no longer exist)
1549
- */
1550
- async cleanStaleEntries(existingSvgPaths) {
1551
- const existingSet = new Set(existingSvgPaths);
1552
- let cleaned = 0;
1553
- for (const svgPath of Object.keys(this.cache.entries)) {
1554
- if (!existingSet.has(svgPath)) {
1555
- delete this.cache.entries[svgPath];
1556
- cleaned++;
1557
- this.isDirty = true;
1558
- }
1559
- }
1560
- if (cleaned > 0) {
1561
- console.log(`\u{1F9F9} Cleaned ${cleaned} stale cache entries`);
1562
- }
1563
- }
1564
- /**
1565
- * Get cache statistics
1566
- */
1567
- getStats() {
1568
- return { ...this.stats };
1569
- }
1570
- /**
1571
- * Reset statistics
1572
- */
1573
- resetStats() {
1574
- this.stats = {
1575
- hits: 0,
1576
- misses: 0,
1577
- total: 0,
1578
- timeSaved: 0
1579
- };
1580
- }
1581
- /**
1582
- * Get cache hit rate
1583
- */
1584
- getHitRate() {
1585
- if (this.stats.total === 0)
1586
- return 0;
1587
- return this.stats.hits / this.stats.total * 100;
1588
- }
1589
- };
1590
-
1591
- // src/commands/generate.ts
1592
- async function generate(options = {}) {
1593
- const spinner = ora("Loading configuration...").start();
1594
- try {
1595
- let configPath = options.config;
1596
- if (!configPath) {
1597
- const foundConfig = await findConfig();
1598
- if (!foundConfig) {
1599
- spinner.fail("No config file found");
1600
- console.log(chalk.yellow("\nRun"), chalk.cyan("svg-icon init"), chalk.yellow("to create a config file"));
1601
- return;
1602
- }
1603
- configPath = foundConfig;
1604
- }
1605
- const config = await loadConfig(configPath);
1606
- spinner.succeed(`Config loaded from ${chalk.green(configPath)}`);
1607
- if (options.dryRun) {
1608
- spinner.info(chalk.cyan("Dry run mode - no files will be written"));
1609
- console.log(`
1610
- ${chalk.bold("Files that would be generated:")}
1611
- `);
1612
- }
1613
- const useIncremental = config.incremental?.enabled !== false && !options.force && !options.dryRun;
1614
- let cacheManager = null;
1615
- if (useIncremental) {
1616
- const cacheDir = config.incremental?.cacheDir || ".vectify";
1617
- cacheManager = new CacheManager(config.output, cacheDir);
1618
- await cacheManager.load();
1619
- }
1620
- if (options.force) {
1621
- spinner.info(chalk.yellow("Force mode - ignoring cache, regenerating all icons"));
1622
- }
1623
- const actionText = options.dryRun ? "Analyzing icons..." : "Generating icon components...";
1624
- spinner.start(actionText);
1625
- const stats = cacheManager ? await generateIconsIncremental(config, cacheManager, options.dryRun) : await generateIcons(config, options.dryRun);
1626
- if (stats.failed > 0) {
1627
- spinner.warn(`${options.dryRun ? "Analyzed" : "Generated"} ${chalk.green(stats.success)} icons, ${chalk.red(stats.failed)} failed`);
1628
- if (stats.errors.length > 0) {
1629
- console.log(`
1630
- ${chalk.bold.red("Errors:")}`);
1631
- stats.errors.forEach(({ file, error }) => {
1632
- console.log(` ${chalk.red("\u2717")} ${file}: ${error}`);
1633
- });
1634
- }
1635
- } else {
1636
- const message = options.dryRun ? `Would generate ${chalk.green(stats.success)} icon components` : `Generated ${chalk.green(stats.success)} icon components`;
1637
- spinner.succeed(message);
1638
- }
1639
- if (options.dryRun) {
1640
- console.log(`
1641
- ${chalk.bold("Target directory:")} ${chalk.cyan(config.output)}`);
1642
- console.log(chalk.gray("\nRun without --dry-run to generate files"));
1643
- } else {
1644
- console.log(`
1645
- ${chalk.bold("Output:")} ${chalk.cyan(config.output)}`);
1646
- if (config.generateOptions?.preview) {
1647
- console.log(`${chalk.bold("Preview:")} ${chalk.cyan(`${config.output}/preview.html`)}`);
1648
- }
1649
- }
1650
- } catch (error) {
1651
- spinner.fail("Generation failed");
1652
- console.error(chalk.red(error.message));
1653
- throw error;
1654
- }
1655
- }
1656
-
1657
- // src/commands/init.ts
1658
- import path5 from "path";
1659
- import process4 from "process";
1660
- import chalk2 from "chalk";
1661
- import inquirer from "inquirer";
1662
- import ora2 from "ora";
1663
- async function init(options = {}) {
1664
- try {
1665
- const projectRoot = await findProjectRoot();
1666
- const currentDir = process4.cwd();
1667
- if (currentDir !== projectRoot) {
1668
- console.log(chalk2.yellow(`
1669
- Note: Project root detected at ${chalk2.cyan(projectRoot)}`));
1670
- console.log(chalk2.yellow(`Current directory: ${chalk2.cyan(currentDir)}
1671
- `));
1672
- }
1673
- const pathAnswers = await inquirer.prompt([
1674
- {
1675
- type: "input",
1676
- name: "configPath",
1677
- message: "Where should we create the config file?",
1678
- default: options.config || "./vectify.config.ts",
1679
- validate: (input) => {
1680
- if (!input.endsWith(".ts") && !input.endsWith(".js")) {
1681
- return "Config file must have .ts or .js extension";
1682
- }
1683
- return true;
1684
- }
1685
- }
1686
- ]);
1687
- const configPath = path5.resolve(projectRoot, pathAnswers.configPath);
1688
- const configDir = path5.dirname(configPath);
1689
- if (!options.force && await fileExists(configPath)) {
1690
- const { overwrite } = await inquirer.prompt([
1691
- {
1692
- type: "confirm",
1693
- name: "overwrite",
1694
- message: "Config file already exists. Overwrite?",
1695
- default: false
1696
- }
1697
- ]);
1698
- if (!overwrite) {
1699
- console.log(chalk2.yellow("Cancelled"));
1700
- return;
1701
- }
1702
- }
1703
- const supportedFrameworks = frameworkRegistry.getSupportedFrameworks();
1704
- const frameworkChoices = supportedFrameworks.map((fw) => ({
1705
- name: fw.charAt(0).toUpperCase() + fw.slice(1),
1706
- value: fw
1707
- }));
1708
- const answers = await inquirer.prompt([
1709
- {
1710
- type: "list",
1711
- name: "framework",
1712
- message: "Which framework are you using?",
1713
- choices: frameworkChoices
1714
- },
1715
- {
1716
- type: "list",
1717
- name: "vueVersion",
1718
- message: "Which Vue version?",
1719
- choices: [
1720
- { name: "Vue 3", value: "vue" },
1721
- { name: "Vue 2", value: "vue2" }
1722
- ],
1723
- when: (answers2) => answers2.framework === "vue"
1724
- },
1725
- {
1726
- type: "input",
1727
- name: "input",
1728
- message: "Where are your SVG files located?",
1729
- default: "./icons"
1730
- },
1731
- {
1732
- type: "input",
1733
- name: "output",
1734
- message: "Where should we output the components?",
1735
- default: "./src/icons"
1736
- },
1737
- {
1738
- type: "confirm",
1739
- name: "typescript",
1740
- message: "Use TypeScript?",
1741
- default: true
1742
- },
1743
- {
1744
- type: "confirm",
1745
- name: "optimize",
1746
- message: "Optimize SVG files with SVGO?",
1747
- default: true
1748
- },
1749
- {
1750
- type: "input",
1751
- name: "prefix",
1752
- message: "Component name prefix (optional):",
1753
- default: ""
1754
- },
1755
- {
1756
- type: "input",
1757
- name: "suffix",
1758
- message: "Component name suffix (optional):",
1759
- default: ""
1760
- }
1761
- ]);
1762
- const inputPath = path5.resolve(projectRoot, answers.input);
1763
- const outputPath = path5.resolve(projectRoot, answers.output);
1764
- const spinner = ora2("Setting up directories...").start();
1765
- await ensureDir(inputPath);
1766
- spinner.text = `Created input directory: ${chalk2.cyan(answers.input)}`;
1767
- await ensureDir(outputPath);
1768
- spinner.succeed(`Created output directory: ${chalk2.cyan(answers.output)}`);
1769
- const relativeConfigDir = path5.relative(configDir, projectRoot) || ".";
1770
- const finalFramework = answers.vueVersion || answers.framework;
1771
- const configContent = generateConfigContent({ ...answers, framework: finalFramework }, relativeConfigDir);
1772
- spinner.start("Creating config file...");
1773
- await writeFile(configPath, configContent);
1774
- spinner.succeed(`Config file created at ${chalk2.green(configPath)}`);
1775
- console.log(`
1776
- ${chalk2.bold("Next steps:")}`);
1777
- console.log(` 1. Place your SVG files in ${chalk2.cyan(answers.input)}`);
1778
- console.log(` 2. Run ${chalk2.cyan("svg-icon generate")} to generate components`);
1779
- console.log(` 3. Import and use your icons!
1780
- `);
1781
- } catch (error) {
1782
- console.error(chalk2.red("Initialization failed"));
1783
- throw error;
1784
- }
1785
- }
1786
- function generateConfigContent(answers, configDir) {
1787
- return `import { defineConfig } from 'vectify'
1788
-
1789
- export default defineConfig({
1790
- framework: '${answers.framework}',
1791
- configDir: '${configDir}',
1792
- input: '${answers.input}',
1793
- output: '${answers.output}',
1794
- typescript: ${answers.typescript},
1795
- optimize: ${answers.optimize},
1796
- prefix: '${answers.prefix}',
1797
- suffix: '${answers.suffix}',
1798
- generateOptions: {
1799
- index: true,
1800
- types: true,
1801
- preview: false
1802
- },
1803
- watch: {
1804
- enabled: false,
1805
- ignore: ['**/node_modules/**', '**/.git/**'],
1806
- debounce: 300,
1807
- },
1808
- })
1809
- `;
1810
- }
1811
-
1812
- // src/commands/watch.ts
1813
- import path6 from "path";
1814
- import chalk3 from "chalk";
1815
- import chokidar from "chokidar";
1816
- import ora3 from "ora";
1817
-
1818
- // src/cache/svg-validator.ts
1819
- import { readFile as readFile4, stat as stat2 } from "fs/promises";
1820
- import { load as load2 } from "cheerio";
1821
- var MIN_SVG_SIZE = 20;
1822
- var DRAWABLE_ELEMENTS = [
1823
- "path",
1824
- "circle",
1825
- "rect",
1826
- "ellipse",
1827
- "line",
1828
- "polyline",
1829
- "polygon",
1830
- "text",
1831
- "image",
1832
- "use"
1833
- ];
1834
- async function validateSVGFile(filePath) {
1835
- try {
1836
- const stats = await stat2(filePath);
1837
- if (stats.size < MIN_SVG_SIZE) {
1838
- return {
1839
- isValid: false,
1840
- isEmpty: true,
1841
- reason: "File is too small to be a valid SVG"
1842
- };
1843
- }
1844
- const content = await readFile4(filePath, "utf-8");
1845
- if (!content.trim()) {
1846
- return {
1847
- isValid: false,
1848
- isEmpty: true,
1849
- reason: "File is empty"
1850
- };
1851
- }
1852
- if (!content.includes("<svg")) {
1853
- return {
1854
- isValid: false,
1855
- isEmpty: false,
1856
- reason: "File does not contain <svg> tag"
1857
- };
1858
- }
1859
- if (!hasDrawableContent(content)) {
1860
- return {
1861
- isValid: false,
1862
- isEmpty: true,
1863
- reason: "SVG has no drawable content"
1864
- };
1865
- }
1866
- try {
1867
- const $ = load2(content, { xmlMode: true });
1868
- const svgElement = $("svg");
1869
- if (svgElement.length === 0) {
1870
- return {
1871
- isValid: false,
1872
- isEmpty: false,
1873
- reason: "Failed to parse SVG element"
1874
- };
1875
- }
1876
- return {
1877
- isValid: true,
1878
- isEmpty: false
1879
- };
1880
- } catch (parseError) {
1881
- return {
1882
- isValid: false,
1883
- isEmpty: false,
1884
- reason: `Failed to parse SVG: ${parseError}`
1885
- };
1886
- }
1887
- } catch (error) {
1888
- return {
1889
- isValid: false,
1890
- isEmpty: false,
1891
- reason: `Failed to read file: ${error}`
1892
- };
1893
- }
1894
- }
1895
- function hasDrawableContent(content) {
1896
- return DRAWABLE_ELEMENTS.some((element) => content.includes(`<${element}`));
1897
- }
1898
-
1899
- // src/commands/watch.ts
1900
- async function watch(options = {}) {
1901
- const spinner = ora3("Loading configuration...").start();
1902
- try {
1903
- let configPath = options.config;
1904
- if (!configPath) {
1905
- const foundConfig = await findConfig();
1906
- if (!foundConfig) {
1907
- spinner.fail("No config file found");
1908
- console.log(chalk3.yellow("\nRun"), chalk3.cyan("svg-icon init"), chalk3.yellow("to create a config file"));
1909
- return;
1910
- }
1911
- configPath = foundConfig;
1912
- }
1913
- const config = await loadConfig(configPath);
1914
- spinner.succeed(`Config loaded from ${chalk3.green(configPath)}`);
1915
- const useIncremental = config.incremental?.enabled !== false;
1916
- const cacheDir = config.incremental?.cacheDir || ".vectify";
1917
- const cacheManager = useIncremental ? new CacheManager(config.output, cacheDir) : null;
1918
- if (cacheManager) {
1919
- await cacheManager.load();
1920
- }
1921
- spinner.start("Generating icon components...");
1922
- const initialStats = useIncremental && cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
1923
- spinner.succeed(`Generated ${chalk3.green(initialStats.success)} icon components`);
1924
- const watchPath = path6.join(config.input, "**/*.svg");
1925
- const debounce = config.watch?.debounce ?? 300;
1926
- const ignore = config.watch?.ignore ?? ["**/node_modules/**", "**/.git/**"];
1927
- const emptyFileRetryDelay = config.watch?.emptyFileRetryDelay ?? 2e3;
1928
- console.log(chalk3.bold("\nWatching for changes..."));
1929
- console.log(` ${chalk3.cyan(watchPath)}`);
1930
- console.log(` ${chalk3.gray("Press Ctrl+C to stop")}
1931
- `);
1932
- const pendingChanges = /* @__PURE__ */ new Map();
1933
- const debounceTimer = null;
1934
- const watcher = chokidar.watch(watchPath, {
1935
- ignored: ignore,
1936
- persistent: true,
1937
- ignoreInitial: true
1938
- });
1939
- watcher.on("add", (filePath) => {
1940
- handleChange("added", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
1941
- }).on("change", (filePath) => {
1942
- handleChange("changed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
1943
- }).on("unlink", (filePath) => {
1944
- console.log(chalk3.yellow(`SVG file removed: ${path6.basename(filePath)}`));
1945
- handleChange("removed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
1946
- }).on("error", (error) => {
1947
- console.error(chalk3.red(`Watcher error: ${error.message}`));
1948
- });
1949
- process.on("SIGINT", () => {
1950
- console.log(`
1951
-
1952
- ${chalk3.yellow("Stopping watch mode...")}`);
1953
- watcher.close();
1954
- process.exit(0);
1955
- });
1956
- } catch (error) {
1957
- spinner.fail("Watch mode failed");
1958
- console.error(chalk3.red(error.message));
1959
- throw error;
1960
- }
1961
- }
1962
- function handleChange(event, filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer) {
1963
- pendingChanges.set(filePath, event);
1964
- if (timer) {
1965
- clearTimeout(timer);
1966
- }
1967
- timer = setTimeout(async () => {
1968
- const changes = Array.from(pendingChanges.entries());
1969
- pendingChanges.clear();
1970
- const validChanges = [];
1971
- const invalidFiles = [];
1972
- for (const [file, changeEvent] of changes) {
1973
- if (changeEvent === "removed") {
1974
- validChanges.push([file, changeEvent]);
1975
- continue;
1976
- }
1977
- const validation = await validateSVGFile(file);
1978
- if (!validation.isValid) {
1979
- if (validation.isEmpty) {
1980
- console.log(chalk3.yellow(`\u23F3 Waiting for content: ${path6.basename(file)}`));
1981
- setTimeout(() => {
1982
- handleChange(changeEvent, file, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer);
1983
- }, emptyFileRetryDelay);
1984
- } else {
1985
- console.error(chalk3.red(`\u274C Invalid SVG: ${path6.basename(file)} - ${validation.reason}`));
1986
- invalidFiles.push(file);
1987
- }
1988
- continue;
1989
- }
1990
- validChanges.push([file, changeEvent]);
1991
- }
1992
- if (validChanges.length === 0) {
1993
- return;
1994
- }
1995
- const spinner = ora3(`Processing ${validChanges.length} change(s)...`).start();
1996
- try {
1997
- const stats = cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
1998
- if (stats.failed > 0) {
1999
- spinner.warn(
2000
- `Regenerated ${chalk3.green(stats.success)} icons, ${chalk3.red(stats.failed)} failed`
2001
- );
2002
- } else {
2003
- spinner.succeed(`\u2713 Updated ${chalk3.green(stats.success)} icon components`);
2004
- }
2005
- const triggerFiles = validChanges.map(([file, evt]) => `${path6.basename(file)} (${evt})`).join(", ");
2006
- console.log(chalk3.gray(` Triggered by: ${triggerFiles}
2007
- `));
2008
- } catch (error) {
2009
- spinner.fail("Regeneration failed");
2010
- console.error(chalk3.red(error.message));
2011
- }
2012
- }, debounce);
2013
- }
2014
-
2015
- export {
2016
- frameworkRegistry,
2017
- getFrameworkStrategy,
2018
- loadConfig,
2019
- findConfig,
2020
- generateIcons,
2021
- generate,
2022
- init,
2023
- watch
2024
- };