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/index.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
|
}
|
|
@@ -151,8 +151,288 @@ module.exports = __toCommonJS(src_exports);
|
|
|
151
151
|
var import_chalk = __toESM(require("chalk"));
|
|
152
152
|
var import_ora = __toESM(require("ora"));
|
|
153
153
|
|
|
154
|
+
// src/cache/cache-manager.ts
|
|
155
|
+
var import_node_fs = require("fs");
|
|
156
|
+
var import_promises2 = require("fs/promises");
|
|
157
|
+
var import_node_path = require("path");
|
|
158
|
+
|
|
159
|
+
// src/cache/hash-utils.ts
|
|
160
|
+
var import_node_crypto = require("crypto");
|
|
161
|
+
var import_promises = require("fs/promises");
|
|
162
|
+
function hashContent(content) {
|
|
163
|
+
return (0, import_node_crypto.createHash)("sha256").update(content).digest("hex");
|
|
164
|
+
}
|
|
165
|
+
async function hashFile(filePath) {
|
|
166
|
+
try {
|
|
167
|
+
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
168
|
+
return hashContent(content);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw new Error(`Failed to hash file ${filePath}: ${error}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function hashConfig(config) {
|
|
174
|
+
const relevantConfig = {
|
|
175
|
+
framework: config.framework,
|
|
176
|
+
typescript: config.typescript,
|
|
177
|
+
keepColors: config.keepColors,
|
|
178
|
+
prefix: config.prefix,
|
|
179
|
+
suffix: config.suffix,
|
|
180
|
+
optimize: config.optimize,
|
|
181
|
+
svgoConfig: config.svgoConfig,
|
|
182
|
+
componentNameTransform: config.componentNameTransform?.toString()
|
|
183
|
+
};
|
|
184
|
+
return hashContent(JSON.stringify(relevantConfig));
|
|
185
|
+
}
|
|
186
|
+
function hashSvgoConfig(svgoConfig) {
|
|
187
|
+
return hashContent(JSON.stringify(svgoConfig || {}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/cache/cache-manager.ts
|
|
191
|
+
var CACHE_VERSION = "1.0.0";
|
|
192
|
+
var CacheManager = class {
|
|
193
|
+
constructor(outputDir, cacheDir = ".vectify") {
|
|
194
|
+
this.cache = {
|
|
195
|
+
version: CACHE_VERSION,
|
|
196
|
+
configHash: "",
|
|
197
|
+
entries: {},
|
|
198
|
+
baseComponentHash: ""
|
|
199
|
+
};
|
|
200
|
+
this.cacheFilePath = (0, import_node_path.join)(outputDir, cacheDir, "cache.json");
|
|
201
|
+
this.stats = {
|
|
202
|
+
hits: 0,
|
|
203
|
+
misses: 0,
|
|
204
|
+
total: 0,
|
|
205
|
+
timeSaved: 0
|
|
206
|
+
};
|
|
207
|
+
this.isDirty = false;
|
|
208
|
+
process.on("beforeExit", () => {
|
|
209
|
+
if (this.isDirty) {
|
|
210
|
+
this.saveSync();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Load cache from disk
|
|
216
|
+
*/
|
|
217
|
+
async load() {
|
|
218
|
+
try {
|
|
219
|
+
if (!(0, import_node_fs.existsSync)(this.cacheFilePath)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const content = await (0, import_promises2.readFile)(this.cacheFilePath, "utf-8");
|
|
223
|
+
const loadedCache = JSON.parse(content);
|
|
224
|
+
if (loadedCache.version !== CACHE_VERSION) {
|
|
225
|
+
console.log("\u26A0\uFE0F Cache version mismatch, rebuilding cache...");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.cache = loadedCache;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.warn("\u26A0\uFE0F Failed to load cache, starting fresh:", error);
|
|
231
|
+
this.cache = {
|
|
232
|
+
version: CACHE_VERSION,
|
|
233
|
+
configHash: "",
|
|
234
|
+
entries: {},
|
|
235
|
+
baseComponentHash: ""
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Save cache to disk (atomic write)
|
|
241
|
+
*/
|
|
242
|
+
async save() {
|
|
243
|
+
try {
|
|
244
|
+
const cacheDir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
245
|
+
await (0, import_promises2.mkdir)(cacheDir, { recursive: true });
|
|
246
|
+
const tempPath = `${this.cacheFilePath}.tmp`;
|
|
247
|
+
await (0, import_promises2.writeFile)(tempPath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
248
|
+
await (0, import_promises2.writeFile)(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
249
|
+
this.isDirty = false;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.warn("\u26A0\uFE0F Failed to save cache:", error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Synchronous save for process exit
|
|
256
|
+
*/
|
|
257
|
+
saveSync() {
|
|
258
|
+
try {
|
|
259
|
+
const fs2 = require("fs");
|
|
260
|
+
const cacheDir = (0, import_node_path.dirname)(this.cacheFilePath);
|
|
261
|
+
if (!fs2.existsSync(cacheDir)) {
|
|
262
|
+
fs2.mkdirSync(cacheDir, { recursive: true });
|
|
263
|
+
}
|
|
264
|
+
fs2.writeFileSync(this.cacheFilePath, JSON.stringify(this.cache, null, 2), "utf-8");
|
|
265
|
+
this.isDirty = false;
|
|
266
|
+
} catch {
|
|
267
|
+
console.warn("\u26A0\uFE0F Failed to save cache on exit");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Check if a file needs regeneration
|
|
272
|
+
*/
|
|
273
|
+
async needsRegeneration(svgPath, componentPath, config) {
|
|
274
|
+
this.stats.total++;
|
|
275
|
+
if (!(0, import_node_fs.existsSync)(componentPath)) {
|
|
276
|
+
this.stats.misses++;
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
const entry = this.cache.entries[svgPath];
|
|
280
|
+
if (!entry) {
|
|
281
|
+
this.stats.misses++;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
const stats = await (0, import_promises2.stat)(svgPath);
|
|
286
|
+
const currentMtime = stats.mtimeMs;
|
|
287
|
+
if (currentMtime === entry.svgMtime) {
|
|
288
|
+
if (!this.isConfigMatching(entry, config)) {
|
|
289
|
+
this.stats.misses++;
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
this.stats.hits++;
|
|
293
|
+
this.stats.timeSaved += 50;
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
const currentHash = await hashFile(svgPath);
|
|
297
|
+
if (currentHash === entry.svgHash) {
|
|
298
|
+
entry.svgMtime = currentMtime;
|
|
299
|
+
this.isDirty = true;
|
|
300
|
+
this.stats.hits++;
|
|
301
|
+
this.stats.timeSaved += 50;
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
this.stats.misses++;
|
|
305
|
+
return true;
|
|
306
|
+
} catch {
|
|
307
|
+
this.stats.misses++;
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Check if config matches cache entry
|
|
313
|
+
*/
|
|
314
|
+
isConfigMatching(entry, config) {
|
|
315
|
+
const snapshot = entry.configSnapshot;
|
|
316
|
+
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);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Update cache entry after successful generation
|
|
320
|
+
*/
|
|
321
|
+
async updateEntry(svgPath, componentPath, componentName, componentHash, config) {
|
|
322
|
+
try {
|
|
323
|
+
const svgHash = await hashFile(svgPath);
|
|
324
|
+
const stats = await (0, import_promises2.stat)(svgPath);
|
|
325
|
+
this.cache.entries[svgPath] = {
|
|
326
|
+
svgPath,
|
|
327
|
+
svgHash,
|
|
328
|
+
svgMtime: stats.mtimeMs,
|
|
329
|
+
componentName,
|
|
330
|
+
componentPath,
|
|
331
|
+
componentHash,
|
|
332
|
+
configSnapshot: {
|
|
333
|
+
framework: config.framework,
|
|
334
|
+
typescript: config.typescript ?? true,
|
|
335
|
+
keepColors: config.keepColors ?? false,
|
|
336
|
+
prefix: config.prefix || "",
|
|
337
|
+
suffix: config.suffix || "",
|
|
338
|
+
optimize: config.optimize ?? true,
|
|
339
|
+
svgoConfigHash: hashSvgoConfig(config.svgoConfig)
|
|
340
|
+
},
|
|
341
|
+
generatedAt: Date.now()
|
|
342
|
+
};
|
|
343
|
+
this.isDirty = true;
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.warn(`\u26A0\uFE0F Failed to update cache entry for ${svgPath}:`, error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Remove cache entry
|
|
350
|
+
*/
|
|
351
|
+
removeEntry(svgPath) {
|
|
352
|
+
if (this.cache.entries[svgPath]) {
|
|
353
|
+
delete this.cache.entries[svgPath];
|
|
354
|
+
this.isDirty = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Update config hash
|
|
359
|
+
*/
|
|
360
|
+
updateConfigHash(config) {
|
|
361
|
+
const newHash = hashConfig(config);
|
|
362
|
+
if (this.cache.configHash !== newHash) {
|
|
363
|
+
this.cache.configHash = newHash;
|
|
364
|
+
this.isDirty = true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Update base component hash
|
|
369
|
+
*/
|
|
370
|
+
updateBaseComponentHash(hash) {
|
|
371
|
+
if (this.cache.baseComponentHash !== hash) {
|
|
372
|
+
this.cache.baseComponentHash = hash;
|
|
373
|
+
this.isDirty = true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Check if config has changed (invalidates all cache)
|
|
378
|
+
*/
|
|
379
|
+
hasConfigChanged(config) {
|
|
380
|
+
const currentHash = hashConfig(config);
|
|
381
|
+
return this.cache.configHash !== "" && this.cache.configHash !== currentHash;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Invalidate all cache entries
|
|
385
|
+
*/
|
|
386
|
+
invalidateAll() {
|
|
387
|
+
this.cache.entries = {};
|
|
388
|
+
this.isDirty = true;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Clean stale entries (files that no longer exist)
|
|
392
|
+
*/
|
|
393
|
+
async cleanStaleEntries(existingSvgPaths) {
|
|
394
|
+
const existingSet = new Set(existingSvgPaths);
|
|
395
|
+
let cleaned = 0;
|
|
396
|
+
for (const svgPath of Object.keys(this.cache.entries)) {
|
|
397
|
+
if (!existingSet.has(svgPath)) {
|
|
398
|
+
delete this.cache.entries[svgPath];
|
|
399
|
+
cleaned++;
|
|
400
|
+
this.isDirty = true;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (cleaned > 0) {
|
|
404
|
+
console.log(`\u{1F9F9} Cleaned ${cleaned} stale cache entries`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get cache statistics
|
|
409
|
+
*/
|
|
410
|
+
getStats() {
|
|
411
|
+
return { ...this.stats };
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Reset statistics
|
|
415
|
+
*/
|
|
416
|
+
resetStats() {
|
|
417
|
+
this.stats = {
|
|
418
|
+
hits: 0,
|
|
419
|
+
misses: 0,
|
|
420
|
+
total: 0,
|
|
421
|
+
timeSaved: 0
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get cache hit rate
|
|
426
|
+
*/
|
|
427
|
+
getHitRate() {
|
|
428
|
+
if (this.stats.total === 0)
|
|
429
|
+
return 0;
|
|
430
|
+
return this.stats.hits / this.stats.total * 100;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
154
434
|
// src/config/loader.ts
|
|
155
|
-
var
|
|
435
|
+
var import_node_path3 = __toESM(require("path"));
|
|
156
436
|
var import_node_process = __toESM(require("process"));
|
|
157
437
|
var import_jiti = require("jiti");
|
|
158
438
|
|
|
@@ -277,22 +557,22 @@ function formatAttrs(attrs) {
|
|
|
277
557
|
}
|
|
278
558
|
|
|
279
559
|
// src/generators/templates/template-engine.ts
|
|
280
|
-
var
|
|
281
|
-
var
|
|
560
|
+
var import_node_fs2 = __toESM(require("fs"));
|
|
561
|
+
var import_node_path2 = __toESM(require("path"));
|
|
282
562
|
var import_node_url = require("url");
|
|
283
563
|
var import_handlebars = __toESM(require("handlebars"));
|
|
284
564
|
var import_meta = {};
|
|
285
565
|
function getTemplatesDir() {
|
|
286
566
|
if (typeof __dirname !== "undefined") {
|
|
287
|
-
return
|
|
567
|
+
return import_node_path2.default.join(__dirname, "templates");
|
|
288
568
|
}
|
|
289
569
|
const currentFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
290
|
-
return
|
|
570
|
+
return import_node_path2.default.join(import_node_path2.default.dirname(currentFile), "templates");
|
|
291
571
|
}
|
|
292
572
|
function loadTemplate(templatePath) {
|
|
293
573
|
const templatesDir = getTemplatesDir();
|
|
294
|
-
const fullPath =
|
|
295
|
-
const templateContent =
|
|
574
|
+
const fullPath = import_node_path2.default.join(templatesDir, templatePath);
|
|
575
|
+
const templateContent = import_node_fs2.default.readFileSync(fullPath, "utf-8");
|
|
296
576
|
return import_handlebars.default.compile(templateContent, { noEscape: true });
|
|
297
577
|
}
|
|
298
578
|
function renderTemplate(templatePath, data) {
|
|
@@ -798,8 +1078,8 @@ var DEFAULT_CONFIG = {
|
|
|
798
1078
|
}
|
|
799
1079
|
};
|
|
800
1080
|
async function loadConfig(configPath) {
|
|
801
|
-
const absolutePath =
|
|
802
|
-
const configDir =
|
|
1081
|
+
const absolutePath = import_node_path3.default.resolve(import_node_process.default.cwd(), configPath);
|
|
1082
|
+
const configDir = import_node_path3.default.dirname(absolutePath);
|
|
803
1083
|
const jiti = (0, import_jiti.createJiti)(configDir, {
|
|
804
1084
|
interopDefault: true
|
|
805
1085
|
});
|
|
@@ -826,12 +1106,12 @@ async function loadConfig(configPath) {
|
|
|
826
1106
|
}
|
|
827
1107
|
};
|
|
828
1108
|
if (mergedConfig.configDir && mergedConfig.configDir !== ".") {
|
|
829
|
-
const projectRoot =
|
|
830
|
-
mergedConfig.input =
|
|
831
|
-
mergedConfig.output =
|
|
1109
|
+
const projectRoot = import_node_path3.default.resolve(configDir, mergedConfig.configDir);
|
|
1110
|
+
mergedConfig.input = import_node_path3.default.resolve(projectRoot, mergedConfig.input);
|
|
1111
|
+
mergedConfig.output = import_node_path3.default.resolve(projectRoot, mergedConfig.output);
|
|
832
1112
|
} else {
|
|
833
|
-
mergedConfig.input =
|
|
834
|
-
mergedConfig.output =
|
|
1113
|
+
mergedConfig.input = import_node_path3.default.resolve(configDir, mergedConfig.input);
|
|
1114
|
+
mergedConfig.output = import_node_path3.default.resolve(configDir, mergedConfig.output);
|
|
835
1115
|
}
|
|
836
1116
|
if (!mergedConfig.framework) {
|
|
837
1117
|
const supported = frameworkRegistry.getSupportedFrameworks().join(", ");
|
|
@@ -850,7 +1130,7 @@ async function findConfig() {
|
|
|
850
1130
|
"vectify.config.js"
|
|
851
1131
|
];
|
|
852
1132
|
for (const file of configFiles) {
|
|
853
|
-
const configPath =
|
|
1133
|
+
const configPath = import_node_path3.default.resolve(import_node_process.default.cwd(), file);
|
|
854
1134
|
if (await fileExists2(configPath)) {
|
|
855
1135
|
return configPath;
|
|
856
1136
|
}
|
|
@@ -859,7 +1139,7 @@ async function findConfig() {
|
|
|
859
1139
|
}
|
|
860
1140
|
|
|
861
1141
|
// src/generators/index.ts
|
|
862
|
-
var
|
|
1142
|
+
var import_node_path5 = __toESM(require("path"));
|
|
863
1143
|
|
|
864
1144
|
// src/parsers/optimizer.ts
|
|
865
1145
|
var import_svgo = require("svgo");
|
|
@@ -895,7 +1175,7 @@ async function optimizeSvg(svgContent, config) {
|
|
|
895
1175
|
|
|
896
1176
|
// src/utils/formatter.ts
|
|
897
1177
|
var import_node_child_process = require("child_process");
|
|
898
|
-
var
|
|
1178
|
+
var import_node_path4 = __toESM(require("path"));
|
|
899
1179
|
var import_node_process2 = __toESM(require("process"));
|
|
900
1180
|
var import_node_util = require("util");
|
|
901
1181
|
init_helpers();
|
|
@@ -950,7 +1230,7 @@ async function detectFormatter() {
|
|
|
950
1230
|
for (const tool of priority) {
|
|
951
1231
|
const patterns = FORMATTER_PATTERNS[tool];
|
|
952
1232
|
for (const pattern of patterns) {
|
|
953
|
-
const configPath =
|
|
1233
|
+
const configPath = import_node_path4.default.join(cwd, pattern);
|
|
954
1234
|
if (await fileExists(configPath)) {
|
|
955
1235
|
return tool;
|
|
956
1236
|
}
|
|
@@ -1046,9 +1326,109 @@ async function generateIcons(config, dryRun = false) {
|
|
|
1046
1326
|
}
|
|
1047
1327
|
return stats;
|
|
1048
1328
|
}
|
|
1049
|
-
async function
|
|
1050
|
-
|
|
1051
|
-
|
|
1329
|
+
async function generateIconsIncremental(config, cacheManager, dryRun = false) {
|
|
1330
|
+
const stats = {
|
|
1331
|
+
success: 0,
|
|
1332
|
+
failed: 0,
|
|
1333
|
+
total: 0,
|
|
1334
|
+
errors: []
|
|
1335
|
+
};
|
|
1336
|
+
try {
|
|
1337
|
+
if (!dryRun) {
|
|
1338
|
+
await ensureDir(config.output);
|
|
1339
|
+
}
|
|
1340
|
+
const svgFiles = await getSvgFiles(config.input);
|
|
1341
|
+
stats.total = svgFiles.length;
|
|
1342
|
+
if (svgFiles.length === 0) {
|
|
1343
|
+
console.warn(`No SVG files found in ${config.input}`);
|
|
1344
|
+
return stats;
|
|
1345
|
+
}
|
|
1346
|
+
if (cacheManager.hasConfigChanged(config)) {
|
|
1347
|
+
console.log("\u26A0\uFE0F Configuration changed, rebuilding all icons...");
|
|
1348
|
+
cacheManager.invalidateAll();
|
|
1349
|
+
}
|
|
1350
|
+
cacheManager.updateConfigHash(config);
|
|
1351
|
+
await cacheManager.cleanStaleEntries(svgFiles);
|
|
1352
|
+
if (config.generateOptions?.cleanOutput && !dryRun) {
|
|
1353
|
+
await cleanOutputDirectory(svgFiles, config);
|
|
1354
|
+
}
|
|
1355
|
+
await generateBaseComponent(config, dryRun);
|
|
1356
|
+
const strategy = getFrameworkStrategy(config.framework);
|
|
1357
|
+
const typescript = config.typescript ?? true;
|
|
1358
|
+
const fileExt = strategy.getComponentExtension(typescript);
|
|
1359
|
+
const filesToGenerate = [];
|
|
1360
|
+
const cachedFiles = [];
|
|
1361
|
+
for (const svgFile of svgFiles) {
|
|
1362
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1363
|
+
const componentName = getComponentName(
|
|
1364
|
+
fileName,
|
|
1365
|
+
config.prefix,
|
|
1366
|
+
config.suffix,
|
|
1367
|
+
config.componentNameTransform
|
|
1368
|
+
);
|
|
1369
|
+
const componentPath = import_node_path5.default.join(config.output, `${componentName}.${fileExt}`);
|
|
1370
|
+
const needsRegen = await cacheManager.needsRegeneration(
|
|
1371
|
+
svgFile,
|
|
1372
|
+
componentPath,
|
|
1373
|
+
config
|
|
1374
|
+
);
|
|
1375
|
+
if (needsRegen) {
|
|
1376
|
+
filesToGenerate.push(svgFile);
|
|
1377
|
+
} else {
|
|
1378
|
+
cachedFiles.push(svgFile);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (cachedFiles.length > 0) {
|
|
1382
|
+
console.log(`\u{1F4E6} Cache: ${cachedFiles.length} cached, ${filesToGenerate.length} to generate`);
|
|
1383
|
+
}
|
|
1384
|
+
for (const svgFile of filesToGenerate) {
|
|
1385
|
+
try {
|
|
1386
|
+
await generateIconComponent(svgFile, config, dryRun, cacheManager);
|
|
1387
|
+
stats.success++;
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
stats.failed++;
|
|
1390
|
+
stats.errors.push({
|
|
1391
|
+
file: svgFile,
|
|
1392
|
+
error: error.message
|
|
1393
|
+
});
|
|
1394
|
+
console.error(`Failed to generate ${svgFile}: ${error.message}`);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
stats.success += cachedFiles.length;
|
|
1398
|
+
if (config.generateOptions?.index) {
|
|
1399
|
+
await generateIndexFile(svgFiles, config, dryRun);
|
|
1400
|
+
}
|
|
1401
|
+
if (config.generateOptions?.preview && !dryRun) {
|
|
1402
|
+
await generatePreviewHtml(svgFiles, config);
|
|
1403
|
+
}
|
|
1404
|
+
if (!dryRun) {
|
|
1405
|
+
await cacheManager.save();
|
|
1406
|
+
const cacheStats = cacheManager.getStats();
|
|
1407
|
+
if (cacheStats.total > 0) {
|
|
1408
|
+
const hitRate = cacheManager.getHitRate();
|
|
1409
|
+
const timeSaved = (cacheStats.timeSaved / 1e3).toFixed(1);
|
|
1410
|
+
console.log(`\u26A1 Cache saved ~${timeSaved}s (${hitRate.toFixed(1)}% hit rate)`);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
if (config.format && !dryRun) {
|
|
1414
|
+
const formatResult = await formatOutput(config.output, config.format);
|
|
1415
|
+
if (formatResult.success && formatResult.tool) {
|
|
1416
|
+
console.log(`Formatted with ${formatResult.tool}`);
|
|
1417
|
+
} else if (formatResult.error) {
|
|
1418
|
+
console.warn(formatResult.error);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (config.hooks?.onComplete) {
|
|
1422
|
+
await config.hooks.onComplete(stats);
|
|
1423
|
+
}
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
throw new Error(`Generation failed: ${error.message}`);
|
|
1426
|
+
}
|
|
1427
|
+
return stats;
|
|
1428
|
+
}
|
|
1429
|
+
async function generateIconComponent(svgFile, config, dryRun = false, cacheManager) {
|
|
1430
|
+
let svgContent = await readFile3(svgFile);
|
|
1431
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1052
1432
|
if (config.hooks?.beforeParse) {
|
|
1053
1433
|
svgContent = await config.hooks.beforeParse(svgContent, fileName);
|
|
1054
1434
|
}
|
|
@@ -1059,7 +1439,7 @@ async function generateIconComponent(svgFile, config, dryRun = false) {
|
|
|
1059
1439
|
fileName,
|
|
1060
1440
|
config.prefix,
|
|
1061
1441
|
config.suffix,
|
|
1062
|
-
config.
|
|
1442
|
+
config.componentNameTransform
|
|
1063
1443
|
);
|
|
1064
1444
|
const strategy = getFrameworkStrategy(config.framework);
|
|
1065
1445
|
const typescript = config.typescript ?? true;
|
|
@@ -1075,22 +1455,32 @@ async function generateIconComponent(svgFile, config, dryRun = false) {
|
|
|
1075
1455
|
code = await config.hooks.afterGenerate(code, componentName);
|
|
1076
1456
|
}
|
|
1077
1457
|
const fileExt = strategy.getComponentExtension(typescript);
|
|
1078
|
-
const outputPath =
|
|
1458
|
+
const outputPath = import_node_path5.default.join(config.output, `${componentName}.${fileExt}`);
|
|
1079
1459
|
if (dryRun) {
|
|
1080
1460
|
console.log(` ${componentName}.${fileExt}`);
|
|
1081
1461
|
} else {
|
|
1082
|
-
await
|
|
1462
|
+
await writeFile2(outputPath, code);
|
|
1463
|
+
if (cacheManager) {
|
|
1464
|
+
const componentHash = hashContent(code);
|
|
1465
|
+
await cacheManager.updateEntry(
|
|
1466
|
+
svgFile,
|
|
1467
|
+
outputPath,
|
|
1468
|
+
componentName,
|
|
1469
|
+
componentHash,
|
|
1470
|
+
config
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1083
1473
|
}
|
|
1084
1474
|
}
|
|
1085
1475
|
async function generateBaseComponent(config, dryRun = false) {
|
|
1086
1476
|
const typescript = config.typescript ?? true;
|
|
1087
1477
|
const strategy = getFrameworkStrategy(config.framework);
|
|
1088
1478
|
const { code, fileName } = strategy.generateBaseComponent(typescript);
|
|
1089
|
-
const outputPath =
|
|
1479
|
+
const outputPath = import_node_path5.default.join(config.output, fileName);
|
|
1090
1480
|
if (dryRun) {
|
|
1091
1481
|
console.log(` ${fileName}`);
|
|
1092
1482
|
} else {
|
|
1093
|
-
await
|
|
1483
|
+
await writeFile2(outputPath, code);
|
|
1094
1484
|
}
|
|
1095
1485
|
}
|
|
1096
1486
|
async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
@@ -1101,12 +1491,12 @@ async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
|
1101
1491
|
const usesDefaultExport = ["vue", "vue2", "svelte", "react", "preact"].includes(config.framework);
|
|
1102
1492
|
const needsExtension = ["vue", "vue2", "svelte"].includes(config.framework);
|
|
1103
1493
|
const exports2 = svgFiles.map((svgFile) => {
|
|
1104
|
-
const fileName =
|
|
1494
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1105
1495
|
const componentName = getComponentName(
|
|
1106
1496
|
fileName,
|
|
1107
1497
|
config.prefix,
|
|
1108
1498
|
config.suffix,
|
|
1109
|
-
config.
|
|
1499
|
+
config.componentNameTransform
|
|
1110
1500
|
);
|
|
1111
1501
|
const importPath = needsExtension ? `./${componentName}.${componentExt}` : `./${componentName}`;
|
|
1112
1502
|
if (usesDefaultExport) {
|
|
@@ -1115,27 +1505,27 @@ async function generateIndexFile(svgFiles, config, dryRun = false) {
|
|
|
1115
1505
|
return `export { ${componentName} } from '${importPath}'`;
|
|
1116
1506
|
}
|
|
1117
1507
|
}).join("\n");
|
|
1118
|
-
const indexPath =
|
|
1508
|
+
const indexPath = import_node_path5.default.join(config.output, `index.${ext}`);
|
|
1119
1509
|
if (dryRun) {
|
|
1120
1510
|
console.log(` index.${ext}`);
|
|
1121
1511
|
} else {
|
|
1122
|
-
await
|
|
1512
|
+
await writeFile2(indexPath, `${exports2}
|
|
1123
1513
|
`);
|
|
1124
1514
|
}
|
|
1125
1515
|
}
|
|
1126
1516
|
async function generatePreviewHtml(svgFiles, config) {
|
|
1127
1517
|
const componentNames = svgFiles.map((svgFile) => {
|
|
1128
|
-
const fileName =
|
|
1518
|
+
const fileName = import_node_path5.default.basename(svgFile);
|
|
1129
1519
|
return getComponentName(
|
|
1130
1520
|
fileName,
|
|
1131
1521
|
config.prefix,
|
|
1132
1522
|
config.suffix,
|
|
1133
|
-
config.
|
|
1523
|
+
config.componentNameTransform
|
|
1134
1524
|
);
|
|
1135
1525
|
});
|
|
1136
1526
|
const svgContents = await Promise.all(
|
|
1137
1527
|
svgFiles.map(async (svgFile) => {
|
|
1138
|
-
return await
|
|
1528
|
+
return await readFile3(svgFile);
|
|
1139
1529
|
})
|
|
1140
1530
|
);
|
|
1141
1531
|
const html = `<!DOCTYPE html>
|
|
@@ -1297,8 +1687,8 @@ async function generatePreviewHtml(svgFiles, config) {
|
|
|
1297
1687
|
</script>
|
|
1298
1688
|
</body>
|
|
1299
1689
|
</html>`;
|
|
1300
|
-
const previewPath =
|
|
1301
|
-
await
|
|
1690
|
+
const previewPath = import_node_path5.default.join(config.output, "preview.html");
|
|
1691
|
+
await writeFile2(previewPath, html);
|
|
1302
1692
|
}
|
|
1303
1693
|
async function cleanOutputDirectory(svgFiles, config) {
|
|
1304
1694
|
const { readdir, unlink } = await import("fs/promises");
|
|
@@ -1306,12 +1696,12 @@ async function cleanOutputDirectory(svgFiles, config) {
|
|
|
1306
1696
|
const fileExt = strategy.getComponentExtension(config.typescript ?? true);
|
|
1307
1697
|
const expectedComponents = new Set(
|
|
1308
1698
|
svgFiles.map((svgFile) => {
|
|
1309
|
-
const fileName =
|
|
1699
|
+
const fileName = import_node_path5.default.basename(svgFile, ".svg");
|
|
1310
1700
|
const componentName = getComponentName(
|
|
1311
1701
|
fileName,
|
|
1312
1702
|
config.prefix,
|
|
1313
1703
|
config.suffix,
|
|
1314
|
-
config.
|
|
1704
|
+
config.componentNameTransform
|
|
1315
1705
|
);
|
|
1316
1706
|
return `${componentName}.${fileExt}`;
|
|
1317
1707
|
})
|
|
@@ -1333,7 +1723,7 @@ async function cleanOutputDirectory(svgFiles, config) {
|
|
|
1333
1723
|
continue;
|
|
1334
1724
|
}
|
|
1335
1725
|
if (!expectedComponents.has(file)) {
|
|
1336
|
-
const filePath =
|
|
1726
|
+
const filePath = import_node_path5.default.join(config.output, file);
|
|
1337
1727
|
await unlink(filePath);
|
|
1338
1728
|
console.log(`Deleted orphaned component: ${file}`);
|
|
1339
1729
|
}
|
|
@@ -1365,9 +1755,19 @@ async function generate(options = {}) {
|
|
|
1365
1755
|
${import_chalk.default.bold("Files that would be generated:")}
|
|
1366
1756
|
`);
|
|
1367
1757
|
}
|
|
1758
|
+
const useIncremental = config.incremental?.enabled !== false && !options.force && !options.dryRun;
|
|
1759
|
+
let cacheManager = null;
|
|
1760
|
+
if (useIncremental) {
|
|
1761
|
+
const cacheDir = config.incremental?.cacheDir || ".vectify";
|
|
1762
|
+
cacheManager = new CacheManager(config.output, cacheDir);
|
|
1763
|
+
await cacheManager.load();
|
|
1764
|
+
}
|
|
1765
|
+
if (options.force) {
|
|
1766
|
+
spinner.info(import_chalk.default.yellow("Force mode - ignoring cache, regenerating all icons"));
|
|
1767
|
+
}
|
|
1368
1768
|
const actionText = options.dryRun ? "Analyzing icons..." : "Generating icon components...";
|
|
1369
1769
|
spinner.start(actionText);
|
|
1370
|
-
const stats = await generateIcons(config, options.dryRun);
|
|
1770
|
+
const stats = cacheManager ? await generateIconsIncremental(config, cacheManager, options.dryRun) : await generateIcons(config, options.dryRun);
|
|
1371
1771
|
if (stats.failed > 0) {
|
|
1372
1772
|
spinner.warn(`${options.dryRun ? "Analyzed" : "Generated"} ${import_chalk.default.green(stats.success)} icons, ${import_chalk.default.red(stats.failed)} failed`);
|
|
1373
1773
|
if (stats.errors.length > 0) {
|
|
@@ -1400,7 +1800,7 @@ ${import_chalk.default.bold("Output:")} ${import_chalk.default.cyan(config.outpu
|
|
|
1400
1800
|
}
|
|
1401
1801
|
|
|
1402
1802
|
// src/commands/init.ts
|
|
1403
|
-
var
|
|
1803
|
+
var import_node_path6 = __toESM(require("path"));
|
|
1404
1804
|
var import_node_process3 = __toESM(require("process"));
|
|
1405
1805
|
var import_chalk2 = __toESM(require("chalk"));
|
|
1406
1806
|
var import_inquirer = __toESM(require("inquirer"));
|
|
@@ -1430,8 +1830,8 @@ Note: Project root detected at ${import_chalk2.default.cyan(projectRoot)}`));
|
|
|
1430
1830
|
}
|
|
1431
1831
|
}
|
|
1432
1832
|
]);
|
|
1433
|
-
const configPath =
|
|
1434
|
-
const configDir =
|
|
1833
|
+
const configPath = import_node_path6.default.resolve(projectRoot, pathAnswers.configPath);
|
|
1834
|
+
const configDir = import_node_path6.default.dirname(configPath);
|
|
1435
1835
|
if (!options.force && await fileExists(configPath)) {
|
|
1436
1836
|
const { overwrite } = await import_inquirer.default.prompt([
|
|
1437
1837
|
{
|
|
@@ -1505,18 +1905,18 @@ Note: Project root detected at ${import_chalk2.default.cyan(projectRoot)}`));
|
|
|
1505
1905
|
default: ""
|
|
1506
1906
|
}
|
|
1507
1907
|
]);
|
|
1508
|
-
const inputPath =
|
|
1509
|
-
const outputPath =
|
|
1908
|
+
const inputPath = import_node_path6.default.resolve(projectRoot, answers.input);
|
|
1909
|
+
const outputPath = import_node_path6.default.resolve(projectRoot, answers.output);
|
|
1510
1910
|
const spinner = (0, import_ora2.default)("Setting up directories...").start();
|
|
1511
1911
|
await ensureDir(inputPath);
|
|
1512
1912
|
spinner.text = `Created input directory: ${import_chalk2.default.cyan(answers.input)}`;
|
|
1513
1913
|
await ensureDir(outputPath);
|
|
1514
1914
|
spinner.succeed(`Created output directory: ${import_chalk2.default.cyan(answers.output)}`);
|
|
1515
|
-
const relativeConfigDir =
|
|
1915
|
+
const relativeConfigDir = import_node_path6.default.relative(configDir, projectRoot) || ".";
|
|
1516
1916
|
const finalFramework = answers.vueVersion || answers.framework;
|
|
1517
1917
|
const configContent = generateConfigContent({ ...answers, framework: finalFramework }, relativeConfigDir);
|
|
1518
1918
|
spinner.start("Creating config file...");
|
|
1519
|
-
await
|
|
1919
|
+
await writeFile2(configPath, configContent);
|
|
1520
1920
|
spinner.succeed(`Config file created at ${import_chalk2.default.green(configPath)}`);
|
|
1521
1921
|
console.log(`
|
|
1522
1922
|
${import_chalk2.default.bold("Next steps:")}`);
|
|
@@ -1556,10 +1956,93 @@ export default defineConfig({
|
|
|
1556
1956
|
}
|
|
1557
1957
|
|
|
1558
1958
|
// src/commands/watch.ts
|
|
1559
|
-
var
|
|
1959
|
+
var import_node_path7 = __toESM(require("path"));
|
|
1560
1960
|
var import_chalk3 = __toESM(require("chalk"));
|
|
1561
1961
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1562
1962
|
var import_ora3 = __toESM(require("ora"));
|
|
1963
|
+
|
|
1964
|
+
// src/cache/svg-validator.ts
|
|
1965
|
+
var import_promises3 = require("fs/promises");
|
|
1966
|
+
var import_cheerio = require("cheerio");
|
|
1967
|
+
var MIN_SVG_SIZE = 20;
|
|
1968
|
+
var DRAWABLE_ELEMENTS = [
|
|
1969
|
+
"path",
|
|
1970
|
+
"circle",
|
|
1971
|
+
"rect",
|
|
1972
|
+
"ellipse",
|
|
1973
|
+
"line",
|
|
1974
|
+
"polyline",
|
|
1975
|
+
"polygon",
|
|
1976
|
+
"text",
|
|
1977
|
+
"image",
|
|
1978
|
+
"use"
|
|
1979
|
+
];
|
|
1980
|
+
async function validateSVGFile(filePath) {
|
|
1981
|
+
try {
|
|
1982
|
+
const stats = await (0, import_promises3.stat)(filePath);
|
|
1983
|
+
if (stats.size < MIN_SVG_SIZE) {
|
|
1984
|
+
return {
|
|
1985
|
+
isValid: false,
|
|
1986
|
+
isEmpty: true,
|
|
1987
|
+
reason: "File is too small to be a valid SVG"
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
const content = await (0, import_promises3.readFile)(filePath, "utf-8");
|
|
1991
|
+
if (!content.trim()) {
|
|
1992
|
+
return {
|
|
1993
|
+
isValid: false,
|
|
1994
|
+
isEmpty: true,
|
|
1995
|
+
reason: "File is empty"
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
if (!content.includes("<svg")) {
|
|
1999
|
+
return {
|
|
2000
|
+
isValid: false,
|
|
2001
|
+
isEmpty: false,
|
|
2002
|
+
reason: "File does not contain <svg> tag"
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
if (!hasDrawableContent(content)) {
|
|
2006
|
+
return {
|
|
2007
|
+
isValid: false,
|
|
2008
|
+
isEmpty: true,
|
|
2009
|
+
reason: "SVG has no drawable content"
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
try {
|
|
2013
|
+
const $ = (0, import_cheerio.load)(content, { xmlMode: true });
|
|
2014
|
+
const svgElement = $("svg");
|
|
2015
|
+
if (svgElement.length === 0) {
|
|
2016
|
+
return {
|
|
2017
|
+
isValid: false,
|
|
2018
|
+
isEmpty: false,
|
|
2019
|
+
reason: "Failed to parse SVG element"
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
return {
|
|
2023
|
+
isValid: true,
|
|
2024
|
+
isEmpty: false
|
|
2025
|
+
};
|
|
2026
|
+
} catch (parseError) {
|
|
2027
|
+
return {
|
|
2028
|
+
isValid: false,
|
|
2029
|
+
isEmpty: false,
|
|
2030
|
+
reason: `Failed to parse SVG: ${parseError}`
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
} catch (error) {
|
|
2034
|
+
return {
|
|
2035
|
+
isValid: false,
|
|
2036
|
+
isEmpty: false,
|
|
2037
|
+
reason: `Failed to read file: ${error}`
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
function hasDrawableContent(content) {
|
|
2042
|
+
return DRAWABLE_ELEMENTS.some((element) => content.includes(`<${element}`));
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/commands/watch.ts
|
|
1563
2046
|
async function watch(options = {}) {
|
|
1564
2047
|
const spinner = (0, import_ora3.default)("Loading configuration...").start();
|
|
1565
2048
|
try {
|
|
@@ -1575,16 +2058,24 @@ async function watch(options = {}) {
|
|
|
1575
2058
|
}
|
|
1576
2059
|
const config = await loadConfig(configPath);
|
|
1577
2060
|
spinner.succeed(`Config loaded from ${import_chalk3.default.green(configPath)}`);
|
|
2061
|
+
const useIncremental = config.incremental?.enabled !== false;
|
|
2062
|
+
const cacheDir = config.incremental?.cacheDir || ".vectify";
|
|
2063
|
+
const cacheManager = useIncremental ? new CacheManager(config.output, cacheDir) : null;
|
|
2064
|
+
if (cacheManager) {
|
|
2065
|
+
await cacheManager.load();
|
|
2066
|
+
}
|
|
1578
2067
|
spinner.start("Generating icon components...");
|
|
1579
|
-
const initialStats = await generateIcons(config);
|
|
2068
|
+
const initialStats = useIncremental && cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
|
|
1580
2069
|
spinner.succeed(`Generated ${import_chalk3.default.green(initialStats.success)} icon components`);
|
|
1581
|
-
const watchPath =
|
|
2070
|
+
const watchPath = import_node_path7.default.join(config.input, "**/*.svg");
|
|
1582
2071
|
const debounce = config.watch?.debounce ?? 300;
|
|
1583
2072
|
const ignore = config.watch?.ignore ?? ["**/node_modules/**", "**/.git/**"];
|
|
2073
|
+
const emptyFileRetryDelay = config.watch?.emptyFileRetryDelay ?? 2e3;
|
|
1584
2074
|
console.log(import_chalk3.default.bold("\nWatching for changes..."));
|
|
1585
2075
|
console.log(` ${import_chalk3.default.cyan(watchPath)}`);
|
|
1586
2076
|
console.log(` ${import_chalk3.default.gray("Press Ctrl+C to stop")}
|
|
1587
2077
|
`);
|
|
2078
|
+
const pendingChanges = /* @__PURE__ */ new Map();
|
|
1588
2079
|
const debounceTimer = null;
|
|
1589
2080
|
const watcher = import_chokidar.default.watch(watchPath, {
|
|
1590
2081
|
ignored: ignore,
|
|
@@ -1592,12 +2083,12 @@ async function watch(options = {}) {
|
|
|
1592
2083
|
ignoreInitial: true
|
|
1593
2084
|
});
|
|
1594
2085
|
watcher.on("add", (filePath) => {
|
|
1595
|
-
handleChange("added", filePath, config, debounce, debounceTimer);
|
|
2086
|
+
handleChange("added", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1596
2087
|
}).on("change", (filePath) => {
|
|
1597
|
-
handleChange("changed", filePath, config, debounce, debounceTimer);
|
|
2088
|
+
handleChange("changed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1598
2089
|
}).on("unlink", (filePath) => {
|
|
1599
|
-
console.log(import_chalk3.default.yellow(`SVG file removed: ${
|
|
1600
|
-
handleChange("removed", filePath, config, debounce, debounceTimer);
|
|
2090
|
+
console.log(import_chalk3.default.yellow(`SVG file removed: ${import_node_path7.default.basename(filePath)}`));
|
|
2091
|
+
handleChange("removed", filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, debounceTimer);
|
|
1601
2092
|
}).on("error", (error) => {
|
|
1602
2093
|
console.error(import_chalk3.default.red(`Watcher error: ${error.message}`));
|
|
1603
2094
|
});
|
|
@@ -1614,23 +2105,51 @@ ${import_chalk3.default.yellow("Stopping watch mode...")}`);
|
|
|
1614
2105
|
throw error;
|
|
1615
2106
|
}
|
|
1616
2107
|
}
|
|
1617
|
-
function handleChange(event, filePath, config, debounce, timer) {
|
|
1618
|
-
|
|
2108
|
+
function handleChange(event, filePath, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer) {
|
|
2109
|
+
pendingChanges.set(filePath, event);
|
|
1619
2110
|
if (timer) {
|
|
1620
2111
|
clearTimeout(timer);
|
|
1621
2112
|
}
|
|
1622
2113
|
timer = setTimeout(async () => {
|
|
1623
|
-
const
|
|
2114
|
+
const changes = Array.from(pendingChanges.entries());
|
|
2115
|
+
pendingChanges.clear();
|
|
2116
|
+
const validChanges = [];
|
|
2117
|
+
const invalidFiles = [];
|
|
2118
|
+
for (const [file, changeEvent] of changes) {
|
|
2119
|
+
if (changeEvent === "removed") {
|
|
2120
|
+
validChanges.push([file, changeEvent]);
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const validation = await validateSVGFile(file);
|
|
2124
|
+
if (!validation.isValid) {
|
|
2125
|
+
if (validation.isEmpty) {
|
|
2126
|
+
console.log(import_chalk3.default.yellow(`\u23F3 Waiting for content: ${import_node_path7.default.basename(file)}`));
|
|
2127
|
+
setTimeout(() => {
|
|
2128
|
+
handleChange(changeEvent, file, config, cacheManager, debounce, emptyFileRetryDelay, pendingChanges, timer);
|
|
2129
|
+
}, emptyFileRetryDelay);
|
|
2130
|
+
} else {
|
|
2131
|
+
console.error(import_chalk3.default.red(`\u274C Invalid SVG: ${import_node_path7.default.basename(file)} - ${validation.reason}`));
|
|
2132
|
+
invalidFiles.push(file);
|
|
2133
|
+
}
|
|
2134
|
+
continue;
|
|
2135
|
+
}
|
|
2136
|
+
validChanges.push([file, changeEvent]);
|
|
2137
|
+
}
|
|
2138
|
+
if (validChanges.length === 0) {
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
const spinner = (0, import_ora3.default)(`Processing ${validChanges.length} change(s)...`).start();
|
|
1624
2142
|
try {
|
|
1625
|
-
const stats = await generateIcons(config);
|
|
2143
|
+
const stats = cacheManager ? await generateIconsIncremental(config, cacheManager) : await generateIcons(config);
|
|
1626
2144
|
if (stats.failed > 0) {
|
|
1627
2145
|
spinner.warn(
|
|
1628
2146
|
`Regenerated ${import_chalk3.default.green(stats.success)} icons, ${import_chalk3.default.red(stats.failed)} failed`
|
|
1629
2147
|
);
|
|
1630
2148
|
} else {
|
|
1631
|
-
spinner.succeed(
|
|
2149
|
+
spinner.succeed(`\u2713 Updated ${import_chalk3.default.green(stats.success)} icon components`);
|
|
1632
2150
|
}
|
|
1633
|
-
|
|
2151
|
+
const triggerFiles = validChanges.map(([file, evt]) => `${import_node_path7.default.basename(file)} (${evt})`).join(", ");
|
|
2152
|
+
console.log(import_chalk3.default.gray(` Triggered by: ${triggerFiles}
|
|
1634
2153
|
`));
|
|
1635
2154
|
} catch (error) {
|
|
1636
2155
|
spinner.fail("Regeneration failed");
|