vectify 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -13
- package/README.zh-CN.md +12 -13
- package/dist/{chunk-CIKTK6HI.mjs → chunk-FS34P27H.mjs} +8 -0
- package/dist/{chunk-5RWOYQAG.mjs → chunk-QYE23M3E.mjs} +538 -18
- package/dist/cli.js +589 -70
- package/dist/cli.mjs +3 -3
- package/dist/{helpers-UPZEBRGK.mjs → helpers-ZOR3OD66.mjs} +1 -1
- package/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +582 -63
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40,10 +40,10 @@ __export(helpers_exports, {
|
|
|
40
40
|
getComponentName: () => getComponentName,
|
|
41
41
|
getSvgFiles: () => getSvgFiles,
|
|
42
42
|
mergeClasses: () => mergeClasses,
|
|
43
|
-
readFile: () =>
|
|
43
|
+
readFile: () => readFile3,
|
|
44
44
|
toKebabCase: () => toKebabCase,
|
|
45
45
|
toPascalCase: () => toPascalCase,
|
|
46
|
-
writeFile: () =>
|
|
46
|
+
writeFile: () => writeFile2
|
|
47
47
|
});
|
|
48
48
|
function toPascalCase(str) {
|
|
49
49
|
return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (char) => char.toUpperCase()).replace(/[^a-z0-9]/gi, "");
|
|
@@ -93,11 +93,11 @@ async function fileExists(filePath) {
|
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
async function
|
|
96
|
+
async function readFile3(filePath) {
|
|
97
97
|
const fs2 = await import("fs/promises");
|
|
98
98
|
return await fs2.readFile(filePath, "utf-8");
|
|
99
99
|
}
|
|
100
|
-
async function
|
|
100
|
+
async function writeFile2(filePath, content) {
|
|
101
101
|
const fs2 = await import("fs/promises");
|
|
102
102
|
await fs2.writeFile(filePath, content, "utf-8");
|
|
103
103
|
}
|
|
@@ -133,8 +133,8 @@ var init_helpers = __esm({
|
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
// src/cli.ts
|
|
136
|
-
var
|
|
137
|
-
var
|
|
136
|
+
var import_node_fs3 = require("fs");
|
|
137
|
+
var import_node_path8 = require("path");
|
|
138
138
|
var import_node_url2 = require("url");
|
|
139
139
|
var import_chalk4 = __toESM(require("chalk"));
|
|
140
140
|
var import_commander = require("commander");
|
|
@@ -143,8 +143,288 @@ var import_commander = require("commander");
|
|
|
143
143
|
var import_chalk = __toESM(require("chalk"));
|
|
144
144
|
var import_ora = __toESM(require("ora"));
|
|
145
145
|
|
|
146
|
+
// src/cache/cache-manager.ts
|
|
147
|
+
var import_node_fs = require("fs");
|
|
148
|
+
var import_promises2 = require("fs/promises");
|
|
149
|
+
var import_node_path = require("path");
|
|
150
|
+
|
|
151
|
+
// src/cache/hash-utils.ts
|
|
152
|
+
var import_node_crypto = require("crypto");
|
|
153
|
+
var import_promises = require("fs/promises");
|
|
154
|
+
function hashContent(content) {
|
|
155
|
+
return (0, import_node_crypto.createHash)("sha256").update(content).digest("hex");
|
|
156
|
+
}
|
|
157
|
+
async function hashFile(filePath) {
|
|
158
|
+
try {
|
|
159
|
+
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
160
|
+
return hashContent(content);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
throw new Error(`Failed to hash file ${filePath}: ${error}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function hashConfig(config) {
|
|
166
|
+
const relevantConfig = {
|
|
167
|
+
framework: config.framework,
|
|
168
|
+
typescript: config.typescript,
|
|
169
|
+
keepColors: config.keepColors,
|
|
170
|
+
prefix: config.prefix,
|
|
171
|
+
suffix: config.suffix,
|
|
172
|
+
optimize: config.optimize,
|
|
173
|
+
svgoConfig: config.svgoConfig,
|
|
174
|
+
componentNameTransform: config.componentNameTransform?.toString()
|
|
175
|
+
};
|
|
176
|
+
return hashContent(JSON.stringify(relevantConfig));
|
|
177
|
+
}
|
|
178
|
+
function hashSvgoConfig(svgoConfig) {
|
|
179
|
+
return hashContent(JSON.stringify(svgoConfig || {}));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/cache/cache-manager.ts
|
|
183
|
+
var CACHE_VERSION = "1.0.0";
|
|
184
|
+
var CacheManager = class {
|
|
185
|
+
constructor(outputDir, cacheDir = ".vectify") {
|
|
186
|
+
this.cache = {
|
|
187
|
+
version: CACHE_VERSION,
|
|
188
|
+
configHash: "",
|
|
189
|
+
entries: {},
|
|
190
|
+
baseComponentHash: ""
|
|
191
|
+
};
|
|
192
|
+
this.cacheFilePath = (0, import_node_path.join)(outputDir, cacheDir, "cache.json");
|
|
193
|
+
this.stats = {
|
|
194
|
+
hits: 0,
|
|
195
|
+
misses: 0,
|
|
196
|
+
total: 0,
|
|
197
|
+
timeSaved: 0
|
|
198
|
+
};
|
|
199
|
+
this.isDirty = false;
|
|
200
|
+
process.on("beforeExit", () => {
|
|
201
|
+
if (this.isDirty) {
|
|
202
|
+
this.saveSync();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Load cache from disk
|
|
208
|
+
*/
|
|
209
|
+
async load() {
|
|
210
|
+
try {
|
|
211
|
+
if (!(0, import_node_fs.existsSync)(this.cacheFilePath)) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const content = await (0, import_promises2.readFile)(this.cacheFilePath, "utf-8");
|
|
215
|
+
const loadedCache = JSON.parse(content);
|
|
216
|
+
if (loadedCache.version !== CACHE_VERSION) {
|
|
217
|
+
console.log("\u26A0\uFE0F Cache version mismatch, rebuilding cache...");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.cache = loadedCache;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.warn("\u26A0\uFE0F Failed to load cache, starting fresh:", error);
|
|
223
|
+
this.cache = {
|
|
224
|
+
version: CACHE_VERSION,
|
|
225
|
+
configHash: "",
|
|
226
|
+
entries: {},
|
|
227
|
+
baseComponentHash: ""
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Save cache to disk (atomic write)
|
|
233
|
+
*/
|
|
234
|
+
async save() {
|
|
235
|
+
try {
|
|
236
|
+
const cacheDir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
237
|
+
await (0, import_promises2.mkdir)(cacheDir, { recursive: true });
|
|
238
|
+
const tempPath = `${this.cacheFilePath}.tmp`;
|
|
239
|
+
await (0, import_promises2.writeFile)(tempPath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
240
|
+
await (0, import_promises2.writeFile)(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
241
|
+
this.isDirty = false;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.warn("\u26A0\uFE0F Failed to save cache:", error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Synchronous save for process exit
|
|
248
|
+
*/
|
|
249
|
+
saveSync() {
|
|
250
|
+
try {
|
|
251
|
+
const fs2 = require("fs");
|
|
252
|
+
const cacheDir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
253
|
+
if (!fs2.existsSync(cacheDir)) {
|
|
254
|
+
fs2.mkdirSync(cacheDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
fs2.writeFileSync(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
257
|
+
this.isDirty = false;
|
|
258
|
+
} catch {
|
|
259
|
+
console.warn("\u26A0\uFE0F Failed to save cache on exit");
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Check if a file needs regeneration
|
|
264
|
+
*/
|
|
265
|
+
async needsRegeneration(svgPath, componentPath, config) {
|
|
266
|
+
this.stats.total++;
|
|
267
|
+
if (!(0, import_node_fs.existsSync)(componentPath)) {
|
|
268
|
+
this.stats.misses++;
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
const entry = this.cache.entries[svgPath];
|
|
272
|
+
if (!entry) {
|
|
273
|
+
this.stats.misses++;
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const stats = await (0, import_promises2.stat)(svgPath);
|
|
278
|
+
const currentMtime = stats.mtimeMs;
|
|
279
|
+
if (currentMtime === entry.svgMtime) {
|
|
280
|
+
if (!this.isConfigMatching(entry, config)) {
|
|
281
|
+
this.stats.misses++;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
this.stats.hits++;
|
|
285
|
+
this.stats.timeSaved += 50;
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const currentHash = await hashFile(svgPath);
|
|
289
|
+
if (currentHash === entry.svgHash) {
|
|
290
|
+
entry.svgMtime = currentMtime;
|
|
291
|
+
this.isDirty = true;
|
|
292
|
+
this.stats.hits++;
|
|
293
|
+
this.stats.timeSaved += 50;
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
this.stats.misses++;
|
|
297
|
+
return true;
|
|
298
|
+
} catch {
|
|
299
|
+
this.stats.misses++;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Check if config matches cache entry
|
|
305
|
+
*/
|
|
306
|
+
isConfigMatching(entry, config) {
|
|
307
|
+
const snapshot = entry.configSnapshot;
|
|
308
|
+
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);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Update cache entry after successful generation
|
|
312
|
+
*/
|
|
313
|
+
async updateEntry(svgPath, componentPath, componentName, componentHash, config) {
|
|
314
|
+
try {
|
|
315
|
+
const svgHash = await hashFile(svgPath);
|
|
316
|
+
const stats = await (0, import_promises2.stat)(svgPath);
|
|
317
|
+
this.cache.entries[svgPath] = {
|
|
318
|
+
svgPath,
|
|
319
|
+
svgHash,
|
|
320
|
+
svgMtime: stats.mtimeMs,
|
|
321
|
+
componentName,
|
|
322
|
+
componentPath,
|
|
323
|
+
componentHash,
|
|
324
|
+
configSnapshot: {
|
|
325
|
+
framework: config.framework,
|
|
326
|
+
typescript: config.typescript ?? true,
|
|
327
|
+
keepColors: config.keepColors ?? false,
|
|
328
|
+
prefix: config.prefix || "",
|
|
329
|
+
suffix: config.suffix || "",
|
|
330
|
+
optimize: config.optimize ?? true,
|
|
331
|
+
svgoConfigHash: hashSvgoConfig(config.svgoConfig)
|
|
332
|
+
},
|
|
333
|
+
generatedAt: Date.now()
|
|
334
|
+
};
|
|
335
|
+
this.isDirty = true;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.warn(`\u26A0\uFE0F Failed to update cache entry for ${svgPath}:`, error);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Remove cache entry
|
|
342
|
+
*/
|
|
343
|
+
removeEntry(svgPath) {
|
|
344
|
+
if (this.cache.entries[svgPath]) {
|
|
345
|
+
delete this.cache.entries[svgPath];
|
|
346
|
+
this.isDirty = true;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Update config hash
|
|
351
|
+
*/
|
|
352
|
+
updateConfigHash(config) {
|
|
353
|
+
const newHash = hashConfig(config);
|
|
354
|
+
if (this.cache.configHash !== newHash) {
|
|
355
|
+
this.cache.configHash = newHash;
|
|
356
|
+
this.isDirty = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Update base component hash
|
|
361
|
+
*/
|
|
362
|
+
updateBaseComponentHash(hash) {
|
|
363
|
+
if (this.cache.baseComponentHash !== hash) {
|
|
364
|
+
this.cache.baseComponentHash = hash;
|
|
365
|
+
this.isDirty = true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Check if config has changed (invalidates all cache)
|
|
370
|
+
*/
|
|
371
|
+
hasConfigChanged(config) {
|
|
372
|
+
const currentHash = hashConfig(config);
|
|
373
|
+
return this.cache.configHash !== "" && this.cache.configHash !== currentHash;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Invalidate all cache entries
|
|
377
|
+
*/
|
|
378
|
+
invalidateAll() {
|
|
379
|
+
this.cache.entries = {};
|
|
380
|
+
this.isDirty = true;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Clean stale entries (files that no longer exist)
|
|
384
|
+
*/
|
|
385
|
+
async cleanStaleEntries(existingSvgPaths) {
|
|
386
|
+
const existingSet = new Set(existingSvgPaths);
|
|
387
|
+
let cleaned = 0;
|
|
388
|
+
for (const svgPath of Object.keys(this.cache.entries)) {
|
|
389
|
+
if (!existingSet.has(svgPath)) {
|
|
390
|
+
delete this.cache.entries[svgPath];
|
|
391
|
+
cleaned++;
|
|
392
|
+
this.isDirty = true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (cleaned > 0) {
|
|
396
|
+
console.log(`\u{1F9F9} Cleaned ${cleaned} stale cache entries`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Get cache statistics
|
|
401
|
+
*/
|
|
402
|
+
getStats() {
|
|
403
|
+
return { ...this.stats };
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Reset statistics
|
|
407
|
+
*/
|
|
408
|
+
resetStats() {
|
|
409
|
+
this.stats = {
|
|
410
|
+
hits: 0,
|
|
411
|
+
misses: 0,
|
|
412
|
+
total: 0,
|
|
413
|
+
timeSaved: 0
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get cache hit rate
|
|
418
|
+
*/
|
|
419
|
+
getHitRate() {
|
|
420
|
+
if (this.stats.total === 0)
|
|
421
|
+
return 0;
|
|
422
|
+
return this.stats.hits / this.stats.total * 100;
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
146
426
|
// src/config/loader.ts
|
|
147
|
-
var
|
|
427
|
+
var import_node_path3 = __toESM(require("path"));
|
|
148
428
|
var import_node_process = __toESM(require("process"));
|
|
149
429
|
var import_jiti = require("jiti");
|
|
150
430
|
|
|
@@ -269,22 +549,22 @@ function formatAttrs(attrs) {
|
|
|
269
549
|
}
|
|
270
550
|
|
|
271
551
|
// src/generators/templates/template-engine.ts
|
|
272
|
-
var
|
|
273
|
-
var
|
|
552
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
553
|
+
var import_node_path2 = __toESM(require("path"));
|
|
274
554
|
var import_node_url = require("url");
|
|
275
555
|
var import_handlebars = __toESM(require("handlebars"));
|
|
276
556
|
var import_meta = {};
|
|
277
557
|
function getTemplatesDir() {
|
|
278
558
|
if (typeof __dirname !== "undefined") {
|
|
279
|
-
return
|
|
559
|
+
return import_node_path2.default.join(__dirname, "templates");
|
|
280
560
|
}
|
|
281
561
|
const currentFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
282
|
-
return
|
|
562
|
+
return import_node_path2.default.join(import_node_path2.default.dirname(currentFile), "templates");
|
|
283
563
|
}
|
|
284
564
|
function loadTemplate(templatePath) {
|
|
285
565
|
const templatesDir = getTemplatesDir();
|
|
286
|
-
const fullPath =
|
|
287
|
-
const templateContent =
|
|
566
|
+
const fullPath = import_node_path2.default.join(templatesDir, templatePath);
|
|
567
|
+
const templateContent = import_node_fs2.default.readFileSync(fullPath, "utf-8");
|
|
288
568
|
return import_handlebars.default.compile(templateContent, { noEscape: true });
|
|
289
569
|
}
|
|
290
570
|
function renderTemplate(templatePath, data) {
|
|
@@ -790,8 +1070,8 @@ var DEFAULT_CONFIG = {
|
|
|
790
1070
|
}
|
|
791
1071
|
};
|
|
792
1072
|
async function loadConfig(configPath) {
|
|
793
|
-
const absolutePath =
|
|
794
|
-
const configDir =
|
|
1073
|
+
const absolutePath = import_node_path3.default.resolve(import_node_process.default.cwd(), configPath);
|
|
1074
|
+
const configDir = import_node_path3.default.dirname(absolutePath);
|
|
795
1075
|
const jiti = (0, import_jiti.createJiti)(configDir, {
|
|
796
1076
|
interopDefault: true
|
|
797
1077
|
});
|
|
@@ -818,12 +1098,12 @@ async function loadConfig(configPath) {
|
|
|
818
1098
|
}
|
|
819
1099
|
};
|
|
820
1100
|
if (mergedConfig.configDir && mergedConfig.configDir !== ".") {
|
|
821
|
-
const projectRoot =
|
|
822
|
-
mergedConfig.input =
|
|
823
|
-
mergedConfig.output =
|
|
1101
|
+
const projectRoot = import_node_path3.default.resolve(configDir, mergedConfig.configDir);
|
|
1102
|
+
mergedConfig.input = import_node_path3.default.resolve(projectRoot, mergedConfig.input);
|
|
1103
|
+
mergedConfig.output = import_node_path3.default.resolve(projectRoot, mergedConfig.output);
|
|
824
1104
|
} else {
|
|
825
|
-
mergedConfig.input =
|
|
826
|
-
mergedConfig.output =
|
|
1105
|
+
mergedConfig.input = import_node_path3.default.resolve(configDir, mergedConfig.input);
|
|
1106
|
+
mergedConfig.output = import_node_path3.default.resolve(configDir, mergedConfig.output);
|
|
827
1107
|
}
|
|
828
1108
|
if (!mergedConfig.framework) {
|
|
829
1109
|
const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
|
|
@@ -842,7 +1122,7 @@ async function findConfig() {
|
|
|
842
1122
|
"vectify.config.js"
|
|
843
1123
|
];
|
|
844
1124
|
for (const file of configFiles) {
|
|
845
|
-
const configPath =
|
|
1125
|
+
const configPath = import_node_path3.default.resolve(import_node_process.default.cwd(), file);
|
|
846
1126
|
if (await fileExists2(configPath)) {
|
|
847
1127
|
return configPath;
|
|
848
1128
|
}
|
|
@@ -851,7 +1131,7 @@ async function findConfig() {
|
|
|
851
1131
|
}
|
|
852
1132
|
|
|
853
1133
|
// src/generators/index.ts
|
|
854
|
-
var
|
|
1134
|
+
var import_node_path5 = __toESM(require("path"));
|
|
855
1135
|
|
|
856
1136
|
// src/parsers/optimizer.ts
|
|
857
1137
|
var import_svgo = require("svgo");
|
|
@@ -887,7 +1167,7 @@ async function optimizeSvg(svgContent, config) {
|
|
|
887
1167
|
|
|
888
1168
|
// src/utils/formatter.ts
|
|
889
1169
|
var import_node_child_process = require("child_process");
|
|
890
|
-
var
|
|
1170
|
+
var import_node_path4 = __toESM(require("path"));
|
|
891
1171
|
var import_node_process2 = __toESM(require("process"));
|
|
892
1172
|
var import_node_util = require("util");
|
|
893
1173
|
init_helpers();
|
|
@@ -942,7 +1222,7 @@ async function detectFormatter() {
|
|
|
942
1222
|
for (const tool of priority) {
|
|
943
1223
|
const patterns = FORMATTER_PATTERNS[tool];
|
|
944
1224
|
for (const pattern of patterns) {
|
|
945
|
-
const configPath =
|
|
1225
|
+
const configPath = import_node_path4.default.join(cwd, pattern);
|
|
946
1226
|
if (await fileExists(configPath)) {
|
|
947
1227
|
return tool;
|
|
948
1228
|
}
|
|
@@ -1038,9 +1318,109 @@ async function generateIcons(config, dryRun = false) {
|
|
|
1038
1318
|
}
|
|
1039
1319
|
return stats;
|
|
1040
1320
|
}
|
|
1041
|
-
async function
|
|
1042
|
-
|
|
1043
|
-
|
|
1321
|
+
async function generateIconsIncremental(config, cacheManager, dryRun = false) {
|
|
1322
|
+
const stats = {
|
|
1323
|
+
success: 0,
|
|
1324
|
+
failed: 0,
|
|
1325
|
+
total: 0,
|
|
1326
|
+
errors: []
|
|
1327
|
+
};
|
|
1328
|
+
try {
|
|
1329
|
+
if (!dryRun) {
|
|
1330
|
+
await ensureDir(config.output);
|
|
1331
|
+
}
|
|
1332
|
+
const svgFiles = await getSvgFiles(config.input);
|
|
1333
|
+
stats.total = svgFiles.length;
|
|
1334
|
+
if (svgFiles.length === 0) {
|
|
1335
|
+
console.warn(`No SVG files found in ${config.input}`);
|
|
1336
|
+
return stats;
|
|
1337
|
+
}
|
|
1338
|
+
if (cacheManager.hasConfigChanged(config)) {
|
|
1339
|
+
console.log("\u26A0\uFE0F Configuration changed, rebuilding all icons...");
|
|
1340
|
+
cacheManager.invalidateAll();
|
|
1341
|
+
}
|
|
1342
|
+
cacheManager.updateConfigHash(config);
|
|
1343
|
+
await cacheManager.cleanStaleEntries(svgFiles);
|
|
1344
|
+
if (config.generateOptions?.cleanOutput && !dryRun) {
|
|
1345
|
+
await cleanOutputDirectory(svgFiles, config);
|
|
1346
|
+
}
|
|
1347
|
+
await generateBaseComponent(config, dryRun);
|
|
1348
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
1349
|
+
const typescript = config.typescript ?? true;
|
|
1350
|
+
const fileExt = strategy.getComponentExtension(typescript);
|
|
1351
|
+
const filesToGenerate = [];
|
|
1352
|
+
const cachedFiles = [];
|
|
1353
|
+
for (const svgFile of svgFiles) {
|
|
1354
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1355
|
+
const componentName = getComponentName(
|
|
1356
|
+
fileName,
|
|
1357
|
+
config.prefix,
|
|
1358
|
+
config.suffix,
|
|
1359
|
+
config.componentNameTransform
|
|
1360
|
+
);
|
|
1361
|
+
const componentPath = import_node_path5.default.join(config.output, `${componentName}.${fileExt}`);
|
|
1362
|
+
const needsRegen = await cacheManager.needsRegeneration(
|
|
1363
|
+
svgFile,
|
|
1364
|
+
componentPath,
|
|
1365
|
+
config
|
|
1366
|
+
);
|
|
1367
|
+
if (needsRegen) {
|
|
1368
|
+
filesToGenerate.push(svgFile);
|
|
1369
|
+
} else {
|
|
1370
|
+
cachedFiles.push(svgFile);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (cachedFiles.length > 0) {
|
|
1374
|
+
console.log(`\u{1F4E6} Cache: ${cachedFiles.length} cached, ${filesToGenerate.length} to generate`);
|
|
1375
|
+
}
|
|
1376
|
+
for (const svgFile of filesToGenerate) {
|
|
1377
|
+
try {
|
|
1378
|
+
await generateIconComponent(svgFile, config, dryRun, cacheManager);
|
|
1379
|
+
stats.success++;
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
stats.failed++;
|
|
1382
|
+
stats.errors.push({
|
|
1383
|
+
file: svgFile,
|
|
1384
|
+
error: error.message
|
|
1385
|
+
});
|
|
1386
|
+
console.error(`Failed to generate ${svgFile}: ${error.message}`);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
stats.success += cachedFiles.length;
|
|
1390
|
+
if (config.generateOptions?.index) {
|
|
1391
|
+
await generateIndexFile(svgFiles, config, dryRun);
|
|
1392
|
+
}
|
|
1393
|
+
if (config.generateOptions?.preview && !dryRun) {
|
|
1394
|
+
await generatePreviewHtml(svgFiles, config);
|
|
1395
|
+
}
|
|
1396
|
+
if (!dryRun) {
|
|
1397
|
+
await cacheManager.save();
|
|
1398
|
+
const cacheStats = cacheManager.getStats();
|
|
1399
|
+
if (cacheStats.total > 0) {
|
|
1400
|
+
const hitRate = cacheManager.getHitRate();
|
|
1401
|
+
const timeSaved = (cacheStats.timeSaved / 1e3).toFixed(1);
|
|
1402
|
+
console.log(`\u26A1 Cache saved ~${timeSaved}s (${hitRate.toFixed(1)}% hit rate)`);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (config.format && !dryRun) {
|
|
1406
|
+
const formatResult = await formatOutput(config.output, config.format);
|
|
1407
|
+
if (formatResult.success && formatResult.tool) {
|
|
1408
|
+
console.log(`Formatted with ${formatResult.tool}`);
|
|
1409
|
+
} else if (formatResult.error) {
|
|
1410
|
+
console.warn(formatResult.error);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
if (config.hooks?.onComplete) {
|
|
1414
|
+
await config.hooks.onComplete(stats);
|
|
1415
|
+
}
|
|
1416
|
+
} catch (error) {
|
|
1417
|
+
throw new Error(`Generation failed: ${error.message}`);
|
|
1418
|
+
}
|
|
1419
|
+
return stats;
|
|
1420
|
+
}
|
|
1421
|
+
async function generateIconComponent(svgFile, config, dryRun = false, cacheManager) {
|
|
1422
|
+
let svgContent = await readFile3(svgFile);
|
|
1423
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1044
1424
|
if (config.hooks?.beforeParse) {
|
|
1045
1425
|
svgContent = await config.hooks.beforeParse(svgContent, fileName);
|
|
1046
1426
|
}
|
|
@@ -1051,7 +1431,7 @@ async function generateIconComponent(svgFile, config, dryRun = false) {
|
|
|
1051
1431
|
fileName,
|
|
1052
1432
|
config.prefix,
|
|
1053
1433
|
config.suffix,
|
|
1054
|
-
config.
|
|
1434
|
+
config.componentNameTransform
|
|
1055
1435
|
);
|
|
1056
1436
|
const strategy = getFrameworkStrategy(config.framework);
|
|
1057
1437
|
const typescript = config.typescript ?? true;
|
|
@@ -1067,22 +1447,32 @@ async function generateIconComponent(svgFile, config, dryRun = false) {
|
|
|
1067
1447
|
code = await config.hooks.afterGenerate(code, componentName);
|
|
1068
1448
|
}
|
|
1069
1449
|
const fileExt = strategy.getComponentExtension(typescript);
|
|
1070
|
-
const outputPath =
|
|
1450
|
+
const outputPath = import_node_path5.default.join(config.output, `${componentName}.${fileExt}`);
|
|
1071
1451
|
if (dryRun) {
|
|
1072
1452
|
console.log(` ${componentName}.${fileExt}`);
|
|
1073
1453
|
} else {
|
|
1074
|
-
await
|
|
1454
|
+
await writeFile2(outputPath, code);
|
|
1455
|
+
if (cacheManager) {
|
|
1456
|
+
const componentHash = hashContent(code);
|
|
1457
|
+
await cacheManager.updateEntry(
|
|
1458
|
+
svgFile,
|
|
1459
|
+
outputPath,
|
|
1460
|
+
componentName,
|
|
1461
|
+
componentHash,
|
|
1462
|
+
config
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1075
1465
|
}
|
|
1076
1466
|
}
|
|
1077
1467
|
async function generateBaseComponent(config, dryRun = false) {
|
|
1078
1468
|
const typescript = config.typescript ?? true;
|
|
1079
1469
|
const strategy = getFrameworkStrategy(config.framework);
|
|
1080
1470
|
const { code, fileName } = strategy.generateBaseComponent(typescript);
|
|
1081
|
-
const outputPath =
|
|
1471
|
+
const outputPath = import_node_path5.default.join(config.output, fileName);
|
|
1082
1472
|
if (dryRun) {
|
|
1083
1473
|
console.log(` ${fileName}`);
|
|
1084
1474
|
} else {
|
|
1085
|
-
await
|
|
1475
|
+
await writeFile2(outputPath, code);
|
|
1086
1476
|
}
|
|
1087
1477
|
}
|
|
1088
1478
|
async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
@@ -1093,12 +1483,12 @@ async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
|
1093
1483
|
const usesDefaultExport = ["vue", "vue2", "svelte", "react", "preact"].includes(config.framework);
|
|
1094
1484
|
const needsExtension = ["vue", "vue2", "svelte"].includes(config.framework);
|
|
1095
1485
|
const exports2 = svgFiles.map((svgFile) => {
|
|
1096
|
-
const fileName =
|
|
1486
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1097
1487
|
const componentName = getComponentName(
|
|
1098
1488
|
fileName,
|
|
1099
1489
|
config.prefix,
|
|
1100
1490
|
config.suffix,
|
|
1101
|
-
config.
|
|
1491
|
+
config.componentNameTransform
|
|
1102
1492
|
);
|
|
1103
1493
|
const importPath = needsExtension ? `./${componentName}.${componentExt}` : `./${componentName}`;
|
|
1104
1494
|
if (usesDefaultExport) {
|
|
@@ -1107,27 +1497,27 @@ async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
|
1107
1497
|
return `export { ${componentName} } from '${importPath}'`;
|
|
1108
1498
|
}
|
|
1109
1499
|
}).join("\n");
|
|
1110
|
-
const indexPath =
|
|
1500
|
+
const indexPath = import_node_path5.default.join(config.output, `index.${ext}`);
|
|
1111
1501
|
if (dryRun) {
|
|
1112
1502
|
console.log(` index.${ext}`);
|
|
1113
1503
|
} else {
|
|
1114
|
-
await
|
|
1504
|
+
await writeFile2(indexPath, `${exports2}
|
|
1115
1505
|
`);
|
|
1116
1506
|
}
|
|
1117
1507
|
}
|
|
1118
1508
|
async function generatePreviewHtml(svgFiles, config) {
|
|
1119
1509
|
const componentNames = svgFiles.map((svgFile) => {
|
|
1120
|
-
const fileName =
|
|
1510
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1121
1511
|
return getComponentName(
|
|
1122
1512
|
fileName,
|
|
1123
1513
|
config.prefix,
|
|
1124
1514
|
config.suffix,
|
|
1125
|
-
config.
|
|
1515
|
+
config.componentNameTransform
|
|
1126
1516
|
);
|
|
1127
1517
|
});
|
|
1128
1518
|
const svgContents = await Promise.all(
|
|
1129
1519
|
svgFiles.map(async (svgFile) => {
|
|
1130
|
-
return await
|
|
1520
|
+
return await readFile3(svgFile);
|
|
1131
1521
|
})
|
|
1132
1522
|
);
|
|
1133
1523
|
const html = `<!DOCTYPE html>
|
|
@@ -1289,8 +1679,8 @@ async function generatePreviewHtml(svgFiles, config) {
|
|
|
1289
1679
|
</script>
|
|
1290
1680
|
</body>
|
|
1291
1681
|
</html>`;
|
|
1292
|
-
const previewPath =
|
|
1293
|
-
await
|
|
1682
|
+
const previewPath = import_node_path5.default.join(config.output, "preview.html");
|
|
1683
|
+
await writeFile2(previewPath, html);
|
|
1294
1684
|
}
|
|
1295
1685
|
async function cleanOutputDirectory(svgFiles, config) {
|
|
1296
1686
|
const { readdir, unlink } = await import("fs/promises");
|
|
@@ -1298,12 +1688,12 @@ async function cleanOutputDirectory(svgFiles, config) {
|
|
|
1298
1688
|
const fileExt = strategy.getComponentExtension(config.typescript ?? true);
|
|
1299
1689
|
const expectedComponents = new Set(
|
|
1300
1690
|
svgFiles.map((svgFile) => {
|
|
1301
|
-
const fileName =
|
|
1691
|
+
const fileName = import_node_path5.default.basename(svgFile, ".svg");
|
|
1302
1692
|
const componentName = getComponentName(
|
|
1303
1693
|
fileName,
|
|
1304
1694
|
config.prefix,
|
|
1305
1695
|
config.suffix,
|
|
1306
|
-
config.
|
|
1696
|
+
config.componentNameTransform
|
|
1307
1697
|
);
|
|
1308
1698
|
return `${componentName}.${fileExt}`;
|
|
1309
1699
|
})
|
|
@@ -1325,7 +1715,7 @@ async function cleanOutputDirectory(svgFiles, config) {
|
|
|
1325
1715
|
continue;
|
|
1326
1716
|
}
|
|
1327
1717
|
if (!expectedComponents.has(file)) {
|
|
1328
|
-
const filePath =
|
|
1718
|
+
const filePath = import_node_path5.default.join(config.output, file);
|
|
1329
1719
|
await unlink(filePath);
|
|
1330
1720
|
console.log(`Deleted orphaned component: ${file}`);
|
|
1331
1721
|
}
|
|
@@ -1357,9 +1747,19 @@ async function generate(options = {}) {
|
|
|
1357
1747
|
${import_chalk.default.bold("Files that would be generated:")}
|
|
1358
1748
|
`);
|
|
1359
1749
|
}
|
|
1750
|
+
const useIncremental = config.incremental?.enabled !== false && !options.force && !options.dryRun;
|
|
1751
|
+
let cacheManager = null;
|
|
1752
|
+
if (useIncremental) {
|
|
1753
|
+
const cacheDir = config.incremental?.cacheDir || ".vectify";
|
|
1754
|
+
cacheManager = new CacheManager(config.output, cacheDir);
|
|
1755
|
+
await cacheManager.load();
|
|
1756
|
+
}
|
|
1757
|
+
if (options.force) {
|
|
1758
|
+
spinner.info(import_chalk.default.yellow("Force mode - ignoring cache, regenerating all icons"));
|
|
1759
|
+
}
|
|
1360
1760
|
const actionText = options.dryRun ? "Analyzing icons..." : "Generating icon components...";
|
|
1361
1761
|
spinner.start(actionText);
|
|
1362
|
-
const stats = await generateIcons(config, options.dryRun);
|
|
1762
|
+
const stats = cacheManager ? await generateIconsIncremental(config, cacheManager, options.dryRun) : await generateIcons(config, options.dryRun);
|
|
1363
1763
|
if (stats.failed > 0) {
|
|
1364
1764
|
spinner.warn(`${options.dryRun ? "Analyzed" : "Generated"} ${import_chalk.default.green(stats.success)} icons, ${import_chalk.default.red(stats.failed)} failed`);
|
|
1365
1765
|
if (stats.errors.length > 0) {
|
|
@@ -1392,7 +1792,7 @@ ${import_chalk.default.bold("Output:")} ${import_chalk.default.cyan(config.outpu
|
|
|
1392
1792
|
}
|
|
1393
1793
|
|
|
1394
1794
|
// src/commands/init.ts
|
|
1395
|
-
var
|
|
1795
|
+
var import_node_path6 = __toESM(require("path"));
|
|
1396
1796
|
var import_node_process3 = __toESM(require("process"));
|
|
1397
1797
|
var import_chalk2 = __toESM(require("chalk"));
|
|
1398
1798
|
var import_inquirer = __toESM(require("inquirer"));
|
|
@@ -1422,8 +1822,8 @@ Note: Project root detected at ${import_chalk2.default.cyan(projectRoot)}`));
|
|
|
1422
1822
|
}
|
|
1423
1823
|
}
|
|
1424
1824
|
]);
|
|
1425
|
-
const configPath =
|
|
1426
|
-
const configDir =
|
|
1825
|
+
const configPath = import_node_path6.default.resolve(projectRoot, pathAnswers.configPath);
|
|
1826
|
+
const configDir = import_node_path6.default.dirname(configPath);
|
|
1427
1827
|
if (!options.force && await fileExists(configPath)) {
|
|
1428
1828
|
const { overwrite } = await import_inquirer.default.prompt([
|
|
1429
1829
|
{
|
|
@@ -1497,18 +1897,18 @@ Note: Project root detected at ${import_chalk2.default.cyan(projectRoot)}`));
|
|
|
1497
1897
|
default: ""
|
|
1498
1898
|
}
|
|
1499
1899
|
]);
|
|
1500
|
-
const inputPath =
|
|
1501
|
-
const outputPath =
|
|
1900
|
+
const inputPath = import_node_path6.default.resolve(projectRoot, answers.input);
|
|
1901
|
+
const outputPath = import_node_path6.default.resolve(projectRoot, answers.output);
|
|
1502
1902
|
const spinner = (0, import_ora2.default)("Setting up directories...").start();
|
|
1503
1903
|
await ensureDir(inputPath);
|
|
1504
1904
|
spinner.text = `Created input directory: ${import_chalk2.default.cyan(answers.input)}`;
|
|
1505
1905
|
await ensureDir(outputPath);
|
|
1506
1906
|
spinner.succeed(`Created output directory: ${import_chalk2.default.cyan(answers.output)}`);
|
|
1507
|
-
const relativeConfigDir =
|
|
1907
|
+
const relativeConfigDir = import_node_path6.default.relative(configDir, projectRoot) || ".";
|
|
1508
1908
|
const finalFramework = answers.vueVersion || answers.framework;
|
|
1509
1909
|
const configContent = generateConfigContent({ ...answers, framework: finalFramework }, relativeConfigDir);
|
|
1510
1910
|
spinner.start("Creating config file...");
|
|
1511
|
-
await
|
|
1911
|
+
await writeFile2(configPath, configContent);
|
|
1512
1912
|
spinner.succeed(`Config file created at ${import_chalk2.default.green(configPath)}`);
|
|
1513
1913
|
console.log(`
|
|
1514
1914
|
${import_chalk2.default.bold("Next steps:")}`);
|
|
@@ -1548,10 +1948,93 @@ export default defineConfig({
|
|
|
1548
1948
|
}
|
|
1549
1949
|
|
|
1550
1950
|
// src/commands/watch.ts
|
|
1551
|
-
var
|
|
1951
|
+
var import_node_path7 = __toESM(require("path"));
|
|
1552
1952
|
var import_chalk3 = __toESM(require("chalk"));
|
|
1553
1953
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1554
1954
|
var import_ora3 = __toESM(require("ora"));
|
|
1955
|
+
|
|
1956
|
+
// src/cache/svg-validator.ts
|
|
1957
|
+
var import_promises3 = require("fs/promises");
|
|
1958
|
+
var import_cheerio = require("cheerio");
|
|
1959
|
+
var MIN_SVG_SIZE = 20;
|
|
1960
|
+
var DRAWABLE_ELEMENTS = [
|
|
1961
|
+
"path",
|
|
1962
|
+
"circle",
|
|
1963
|
+
"rect",
|
|
1964
|
+
"ellipse",
|
|
1965
|
+
"line",
|
|
1966
|
+
"polyline",
|
|
1967
|
+
"polygon",
|
|
1968
|
+
"text",
|
|
1969
|
+
"image",
|
|
1970
|
+
"use"
|
|
1971
|
+
];
|
|
1972
|
+
async function validateSVGFile(filePath) {
|
|
1973
|
+
try {
|
|
1974
|
+
const stats = await (0, import_promises3.stat)(filePath);
|
|
1975
|
+
if (stats.size < MIN_SVG_SIZE) {
|
|
1976
|
+
return {
|
|
1977
|
+
isValid: false,
|
|
1978
|
+
isEmpty: true,
|
|
1979
|
+
reason: "File is too small to be a valid SVG"
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
const content = await (0, import_promises3.readFile)(filePath, "utf-8");
|
|
1983
|
+
if (!content.trim()) {
|
|
1984
|
+
return {
|
|
1985
|
+
isValid: false,
|
|
1986
|
+
isEmpty: true,
|
|
1987
|
+
reason: "File is empty"
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
if (!content.includes("<svg")) {
|
|
1991
|
+
return {
|
|
1992
|
+
isValid: false,
|
|
1993
|
+
isEmpty: false,
|
|
1994
|
+
reason: "File does not contain <svg> tag"
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
if (!hasDrawableContent(content)) {
|
|
1998
|
+
return {
|
|
1999
|
+
isValid: false,
|
|
2000
|
+
isEmpty: true,
|
|
2001
|
+
reason: "SVG has no drawable content"
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
try {
|
|
2005
|
+
const $ = (0, import_cheerio.load)(content, { xmlMode: true });
|
|
2006
|
+
const svgElement = $("svg");
|
|
2007
|
+
if (svgElement.length === 0) {
|
|
2008
|
+
return {
|
|
2009
|
+
isValid: false,
|
|
2010
|
+
isEmpty: false,
|
|
2011
|
+
reason: "Failed to parse SVG element"
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
isValid: true,
|
|
2016
|
+
isEmpty: false
|
|
2017
|
+
};
|
|
2018
|
+
} catch (parseError) {
|
|
2019
|
+
return {
|
|
2020
|
+
isValid: false,
|
|
2021
|
+
isEmpty: false,
|
|
2022
|
+
reason: `Failed to parse SVG: ${parseError}`
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
return {
|
|
2027
|
+
isValid: false,
|
|
2028
|
+
isEmpty: false,
|
|
2029
|
+
reason: `Failed to read file: ${error}`
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function hasDrawableContent(content) {
|
|
2034
|
+
return DRAWABLE_ELEMENTS.some((element) => content.includes(`<${element}`));
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// src/commands/watch.ts
|
|
1555
2038
|
async function watch(options = {}) {
|
|
1556
2039
|
const spinner = (0, import_ora3.default)("Loading configuration...").start();
|
|
1557
2040
|
try {
|
|
@@ -1567,16 +2050,24 @@ async function watch(options = {}) {
|
|
|
1567
2050
|
}
|
|
1568
2051
|
const config = await loadConfig(configPath);
|
|
1569
2052
|
spinner.succeed(`Config loaded from ${import_chalk3.default.green(configPath)}`);
|
|
2053
|
+
const useIncremental = config.incremental?.enabled !== false;
|
|
2054
|
+
const cacheDir = config.incremental?.cacheDir || ".vectify";
|
|
2055
|
+
const cacheManager = useIncremental ? new CacheManager(config.output, cacheDir) : null;
|
|
2056
|
+
if (cacheManager) {
|
|
2057
|
+
await cacheManager.load();
|
|
2058
|
+
}
|
|
1570
2059
|
spinner.start("Generating icon components...");
|
|
1571
|
-
const initialStats = await generateIcons(config);
|
|
2060
|
+
const initialStats = useIncremental && cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
|
|
1572
2061
|
spinner.succeed(`Generated ${import_chalk3.default.green(initialStats.success)} icon components`);
|
|
1573
|
-
const watchPath =
|
|
2062
|
+
const watchPath = import_node_path7.default.join(config.input, "**/*.svg");
|
|
1574
2063
|
const debounce = config.watch?.debounce ?? 300;
|
|
1575
2064
|
const ignore = config.watch?.ignore ?? ["**/node_modules/**", "**/.git/**"];
|
|
2065
|
+
const emptyFileRetryDelay = config.watch?.emptyFileRetryDelay ?? 2e3;
|
|
1576
2066
|
console.log(import_chalk3.default.bold("\nWatching for changes..."));
|
|
1577
2067
|
console.log(` ${import_chalk3.default.cyan(watchPath)}`);
|
|
1578
2068
|
console.log(` ${import_chalk3.default.gray("Press Ctrl+C to stop")}
|
|
1579
2069
|
`);
|
|
2070
|
+
const pendingChanges = /* @__PURE__ */ new Map();
|
|
1580
2071
|
const debounceTimer = null;
|
|
1581
2072
|
const watcher = import_chokidar.default.watch(watchPath, {
|
|
1582
2073
|
ignored: ignore,
|
|
@@ -1584,12 +2075,12 @@ async function watch(options = {}) {
|
|
|
1584
2075
|
ignoreInitial: true
|
|
1585
2076
|
});
|
|
1586
2077
|
watcher.on("add", (filePath) => {
|
|
1587
|
-
handleChange("added", filePath, config, debounce, debounceTimer);
|
|
2078
|
+
handleChange("added", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1588
2079
|
}).on("change", (filePath) => {
|
|
1589
|
-
handleChange("changed", filePath, config, debounce, debounceTimer);
|
|
2080
|
+
handleChange("changed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1590
2081
|
}).on("unlink", (filePath) => {
|
|
1591
|
-
console.log(import_chalk3.default.yellow(`SVG file removed: ${
|
|
1592
|
-
handleChange("removed", filePath, config, debounce, debounceTimer);
|
|
2082
|
+
console.log(import_chalk3.default.yellow(`SVG file removed: ${import_node_path7.default.basename(filePath)}`));
|
|
2083
|
+
handleChange("removed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1593
2084
|
}).on("error", (error) => {
|
|
1594
2085
|
console.error(import_chalk3.default.red(`Watcher error: ${error.message}`));
|
|
1595
2086
|
});
|
|
@@ -1606,23 +2097,51 @@ ${import_chalk3.default.yellow("Stopping watch mode...")}`);
|
|
|
1606
2097
|
throw error;
|
|
1607
2098
|
}
|
|
1608
2099
|
}
|
|
1609
|
-
function handleChange(event, filePath, config, debounce, timer) {
|
|
1610
|
-
|
|
2100
|
+
function handleChange(event, filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer) {
|
|
2101
|
+
pendingChanges.set(filePath, event);
|
|
1611
2102
|
if (timer) {
|
|
1612
2103
|
clearTimeout(timer);
|
|
1613
2104
|
}
|
|
1614
2105
|
timer = setTimeout(async () => {
|
|
1615
|
-
const
|
|
2106
|
+
const changes = Array.from(pendingChanges.entries());
|
|
2107
|
+
pendingChanges.clear();
|
|
2108
|
+
const validChanges = [];
|
|
2109
|
+
const invalidFiles = [];
|
|
2110
|
+
for (const [file, changeEvent] of changes) {
|
|
2111
|
+
if (changeEvent === "removed") {
|
|
2112
|
+
validChanges.push([file, changeEvent]);
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
const validation = await validateSVGFile(file);
|
|
2116
|
+
if (!validation.isValid) {
|
|
2117
|
+
if (validation.isEmpty) {
|
|
2118
|
+
console.log(import_chalk3.default.yellow(`\u23F3 Waiting for content: ${import_node_path7.default.basename(file)}`));
|
|
2119
|
+
setTimeout(() => {
|
|
2120
|
+
handleChange(changeEvent, file, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer);
|
|
2121
|
+
}, emptyFileRetryDelay);
|
|
2122
|
+
} else {
|
|
2123
|
+
console.error(import_chalk3.default.red(`\u274C Invalid SVG: ${import_node_path7.default.basename(file)} - ${validation.reason}`));
|
|
2124
|
+
invalidFiles.push(file);
|
|
2125
|
+
}
|
|
2126
|
+
continue;
|
|
2127
|
+
}
|
|
2128
|
+
validChanges.push([file, changeEvent]);
|
|
2129
|
+
}
|
|
2130
|
+
if (validChanges.length === 0) {
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
const spinner = (0, import_ora3.default)(`Processing ${validChanges.length} change(s)...`).start();
|
|
1616
2134
|
try {
|
|
1617
|
-
const stats = await generateIcons(config);
|
|
2135
|
+
const stats = cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
|
|
1618
2136
|
if (stats.failed > 0) {
|
|
1619
2137
|
spinner.warn(
|
|
1620
2138
|
`Regenerated ${import_chalk3.default.green(stats.success)} icons, ${import_chalk3.default.red(stats.failed)} failed`
|
|
1621
2139
|
);
|
|
1622
2140
|
} else {
|
|
1623
|
-
spinner.succeed(
|
|
2141
|
+
spinner.succeed(`\u2713 Updated ${import_chalk3.default.green(stats.success)} icon components`);
|
|
1624
2142
|
}
|
|
1625
|
-
|
|
2143
|
+
const triggerFiles = validChanges.map(([file, evt]) => `${import_node_path7.default.basename(file)} (${evt})`).join(", ");
|
|
2144
|
+
console.log(import_chalk3.default.gray(` Triggered by: ${triggerFiles}
|
|
1626
2145
|
`));
|
|
1627
2146
|
} catch (error) {
|
|
1628
2147
|
spinner.fail("Regeneration failed");
|
|
@@ -1636,13 +2155,13 @@ var import_meta2 = {};
|
|
|
1636
2155
|
function getPackageJson() {
|
|
1637
2156
|
let pkgPath;
|
|
1638
2157
|
if (typeof __dirname !== "undefined") {
|
|
1639
|
-
pkgPath = (0,
|
|
2158
|
+
pkgPath = (0, import_node_path8.join)(__dirname, "../package.json");
|
|
1640
2159
|
} else {
|
|
1641
2160
|
const __filename = (0, import_node_url2.fileURLToPath)(import_meta2.url);
|
|
1642
|
-
const __dirname2 = (0,
|
|
1643
|
-
pkgPath = (0,
|
|
2161
|
+
const __dirname2 = (0, import_node_path8.dirname)(__filename);
|
|
2162
|
+
pkgPath = (0, import_node_path8.join)(__dirname2, "../package.json");
|
|
1644
2163
|
}
|
|
1645
|
-
return JSON.parse((0,
|
|
2164
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(pkgPath, "utf-8"));
|
|
1646
2165
|
}
|
|
1647
2166
|
var packageJson = getPackageJson();
|
|
1648
2167
|
var program = new import_commander.Command();
|
|
@@ -1655,7 +2174,7 @@ program.command("init").description("Initialize a new configuration file").optio
|
|
|
1655
2174
|
process.exit(1);
|
|
1656
2175
|
}
|
|
1657
2176
|
});
|
|
1658
|
-
program.command("generate").description("Generate icon components from SVG files").option("-c, --config <path>", "Path to config file").option("--dry-run", "Preview what will be generated without writing files").action(async (options) => {
|
|
2177
|
+
program.command("generate").description("Generate icon components from SVG files").option("-c, --config <path>", "Path to config file").option("--dry-run", "Preview what will be generated without writing files").option("--force", "Force full regeneration, ignoring cache").action(async (options) => {
|
|
1659
2178
|
try {
|
|
1660
2179
|
await generate(options);
|
|
1661
2180
|
} catch (error) {
|