@styleframe/scanner 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +665 -0
- package/dist/index.d.ts +419 -0
- package/dist/index.js +663 -0
- package/package.json +59 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
(function(global, factory) {
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("node:fs/promises"), require("fast-glob")) : typeof define === "function" && define.amd ? define(["exports", "node:fs/promises", "fast-glob"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.index = {}, global.promises, global.fg));
|
|
3
|
+
})(this, (function(exports2, promises, fg) {
|
|
4
|
+
"use strict";
|
|
5
|
+
function hashContent(content) {
|
|
6
|
+
let hash = 0;
|
|
7
|
+
for (let i = 0; i < content.length; i++) {
|
|
8
|
+
const char = content.charCodeAt(i);
|
|
9
|
+
hash = (hash << 5) - hash + char;
|
|
10
|
+
hash = hash & hash;
|
|
11
|
+
}
|
|
12
|
+
return `${hash.toString(16)}-${content.length}`;
|
|
13
|
+
}
|
|
14
|
+
function createCache() {
|
|
15
|
+
const entries = /* @__PURE__ */ new Map();
|
|
16
|
+
return {
|
|
17
|
+
get(filePath) {
|
|
18
|
+
const entry = entries.get(filePath);
|
|
19
|
+
return entry?.result ?? null;
|
|
20
|
+
},
|
|
21
|
+
set(filePath, result, contentHash) {
|
|
22
|
+
entries.set(filePath, {
|
|
23
|
+
hash: contentHash,
|
|
24
|
+
result,
|
|
25
|
+
timestamp: Date.now()
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
isValid(filePath, contentHash) {
|
|
29
|
+
const entry = entries.get(filePath);
|
|
30
|
+
return entry !== void 0 && entry.hash === contentHash;
|
|
31
|
+
},
|
|
32
|
+
getIfValid(filePath, contentHash) {
|
|
33
|
+
const entry = entries.get(filePath);
|
|
34
|
+
if (entry && entry.hash === contentHash) {
|
|
35
|
+
return entry.result;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
},
|
|
39
|
+
invalidate(filePath) {
|
|
40
|
+
entries.delete(filePath);
|
|
41
|
+
},
|
|
42
|
+
clear() {
|
|
43
|
+
entries.clear();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const UTILITY_CLASS_PATTERN = /_[a-zA-Z][a-zA-Z0-9-]*(?::[a-zA-Z0-9._-]+|:\[[^\]]+\])*/g;
|
|
48
|
+
const ARBITRARY_VALUE_PATTERN = /^\[(.+)\]$/;
|
|
49
|
+
const DEFAULT_EXTENSIONS = [
|
|
50
|
+
"html",
|
|
51
|
+
"htm",
|
|
52
|
+
"vue",
|
|
53
|
+
"svelte",
|
|
54
|
+
"jsx",
|
|
55
|
+
"tsx",
|
|
56
|
+
"js",
|
|
57
|
+
"ts",
|
|
58
|
+
"astro",
|
|
59
|
+
"mdx",
|
|
60
|
+
"php",
|
|
61
|
+
"erb",
|
|
62
|
+
"twig",
|
|
63
|
+
"blade.php"
|
|
64
|
+
];
|
|
65
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
66
|
+
"**/node_modules/**",
|
|
67
|
+
"**/.git/**",
|
|
68
|
+
"**/dist/**",
|
|
69
|
+
"**/build/**",
|
|
70
|
+
"**/.next/**",
|
|
71
|
+
"**/.nuxt/**",
|
|
72
|
+
"**/coverage/**"
|
|
73
|
+
];
|
|
74
|
+
function parseUtilityClass(className) {
|
|
75
|
+
if (!className.startsWith("_")) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const withoutPrefix = className.slice(1);
|
|
79
|
+
if (!withoutPrefix) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const segments = splitByColons(withoutPrefix);
|
|
83
|
+
if (segments.length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
let name;
|
|
87
|
+
let value;
|
|
88
|
+
let modifiers = [];
|
|
89
|
+
if (segments.length === 1) {
|
|
90
|
+
name = segments[0];
|
|
91
|
+
value = "default";
|
|
92
|
+
} else {
|
|
93
|
+
value = segments[segments.length - 1];
|
|
94
|
+
name = segments[segments.length - 2];
|
|
95
|
+
modifiers = segments.slice(0, -2);
|
|
96
|
+
}
|
|
97
|
+
let isArbitrary = false;
|
|
98
|
+
let arbitraryValue;
|
|
99
|
+
const arbitraryMatch = value.match(ARBITRARY_VALUE_PATTERN);
|
|
100
|
+
if (arbitraryMatch) {
|
|
101
|
+
isArbitrary = true;
|
|
102
|
+
arbitraryValue = arbitraryMatch[1];
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
raw: className,
|
|
106
|
+
name,
|
|
107
|
+
value,
|
|
108
|
+
modifiers,
|
|
109
|
+
isArbitrary,
|
|
110
|
+
arbitraryValue
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function splitByColons(str) {
|
|
114
|
+
const segments = [];
|
|
115
|
+
let current = "";
|
|
116
|
+
let bracketDepth = 0;
|
|
117
|
+
for (const char of str) {
|
|
118
|
+
if (char === "[") {
|
|
119
|
+
bracketDepth++;
|
|
120
|
+
current += char;
|
|
121
|
+
} else if (char === "]") {
|
|
122
|
+
bracketDepth--;
|
|
123
|
+
current += char;
|
|
124
|
+
} else if (char === ":" && bracketDepth === 0) {
|
|
125
|
+
if (current) {
|
|
126
|
+
segments.push(current);
|
|
127
|
+
}
|
|
128
|
+
current = "";
|
|
129
|
+
} else {
|
|
130
|
+
current += char;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (bracketDepth !== 0) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
if (current) {
|
|
137
|
+
segments.push(current);
|
|
138
|
+
}
|
|
139
|
+
return segments;
|
|
140
|
+
}
|
|
141
|
+
function extractUtilityClasses(content) {
|
|
142
|
+
const matches = content.match(UTILITY_CLASS_PATTERN);
|
|
143
|
+
return matches ? [...new Set(matches)] : [];
|
|
144
|
+
}
|
|
145
|
+
function generateUtilityClassName(name, value, modifiers = []) {
|
|
146
|
+
const parts = [...modifiers, name];
|
|
147
|
+
if (value !== "default") {
|
|
148
|
+
parts.push(value);
|
|
149
|
+
}
|
|
150
|
+
return `_${parts.join(":")}`;
|
|
151
|
+
}
|
|
152
|
+
const CLASSNAME_EXPR_PATTERN = /\bclassName\s*=\s*\{/g;
|
|
153
|
+
const CLASS_EXPR_PATTERN = /\bclass\s*=\s*\{/g;
|
|
154
|
+
const CLASS_ATTR_PATTERN = /\bclass\s*=\s*["']([^"']+)["']/gi;
|
|
155
|
+
const CLASSNAME_STRING_PATTERN = /\bclassName\s*=\s*["']([^"']+)["']/gi;
|
|
156
|
+
const SVELTE_CLASS_DIRECTIVE_PATTERN = /\bclass:(_[a-zA-Z][a-zA-Z0-9-:[\]._]*)/g;
|
|
157
|
+
const SINGLE_QUOTE_PATTERN = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
158
|
+
const DOUBLE_QUOTE_PATTERN = /"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
159
|
+
const TEMPLATE_LITERAL_PATTERN = /`([^`\\]*(?:\\.[^`\\]*)*)`/g;
|
|
160
|
+
function extractBracedContent(str) {
|
|
161
|
+
let depth = 1;
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < str.length && depth > 0) {
|
|
164
|
+
const char = str[i];
|
|
165
|
+
if (char === "{") {
|
|
166
|
+
depth++;
|
|
167
|
+
} else if (char === "}") {
|
|
168
|
+
depth--;
|
|
169
|
+
}
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
if (depth !== 0) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return str.slice(0, i - 1);
|
|
176
|
+
}
|
|
177
|
+
function extractClassNameExpressions(content) {
|
|
178
|
+
const results = [];
|
|
179
|
+
CLASSNAME_EXPR_PATTERN.lastIndex = 0;
|
|
180
|
+
let match;
|
|
181
|
+
while ((match = CLASSNAME_EXPR_PATTERN.exec(content)) !== null) {
|
|
182
|
+
const startIndex = match.index + match[0].length;
|
|
183
|
+
const remaining = content.slice(startIndex);
|
|
184
|
+
const bracedContent = extractBracedContent(remaining);
|
|
185
|
+
if (bracedContent !== null) {
|
|
186
|
+
results.push(bracedContent);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
function extractClassExpressions(content) {
|
|
192
|
+
const results = [];
|
|
193
|
+
CLASS_EXPR_PATTERN.lastIndex = 0;
|
|
194
|
+
let match;
|
|
195
|
+
while ((match = CLASS_EXPR_PATTERN.exec(content)) !== null) {
|
|
196
|
+
const startIndex = match.index + match[0].length;
|
|
197
|
+
const remaining = content.slice(startIndex);
|
|
198
|
+
const bracedContent = extractBracedContent(remaining);
|
|
199
|
+
if (bracedContent !== null) {
|
|
200
|
+
results.push(bracedContent);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
205
|
+
function extractFromHTML(content) {
|
|
206
|
+
const classes = [];
|
|
207
|
+
CLASS_ATTR_PATTERN.lastIndex = 0;
|
|
208
|
+
let match;
|
|
209
|
+
while ((match = CLASS_ATTR_PATTERN.exec(content)) !== null) {
|
|
210
|
+
if (match[1]) {
|
|
211
|
+
classes.push(...extractUtilityClasses(match[1]));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return classes;
|
|
215
|
+
}
|
|
216
|
+
function extractFromJSX(content) {
|
|
217
|
+
const classes = [];
|
|
218
|
+
CLASSNAME_STRING_PATTERN.lastIndex = 0;
|
|
219
|
+
let match;
|
|
220
|
+
while ((match = CLASSNAME_STRING_PATTERN.exec(content)) !== null) {
|
|
221
|
+
if (match[1]) {
|
|
222
|
+
classes.push(...extractUtilityClasses(match[1]));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const expressions = extractClassNameExpressions(content);
|
|
226
|
+
for (const expr of expressions) {
|
|
227
|
+
classes.push(...extractFromStringLiterals(expr));
|
|
228
|
+
}
|
|
229
|
+
return [...new Set(classes)];
|
|
230
|
+
}
|
|
231
|
+
function extractFromVue(content) {
|
|
232
|
+
const classes = [];
|
|
233
|
+
const templateMatch = content.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
234
|
+
if (templateMatch?.[1]) {
|
|
235
|
+
classes.push(...extractFromHTML(templateMatch[1]));
|
|
236
|
+
classes.push(...extractFromStringLiterals(templateMatch[1]));
|
|
237
|
+
}
|
|
238
|
+
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
239
|
+
if (scriptMatch?.[1]) {
|
|
240
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
241
|
+
}
|
|
242
|
+
return [...new Set(classes)];
|
|
243
|
+
}
|
|
244
|
+
function extractFromSvelte(content) {
|
|
245
|
+
const classes = [];
|
|
246
|
+
classes.push(...extractFromHTML(content));
|
|
247
|
+
const expressions = extractClassExpressions(content);
|
|
248
|
+
for (const expr of expressions) {
|
|
249
|
+
classes.push(...extractFromStringLiterals(expr));
|
|
250
|
+
}
|
|
251
|
+
SVELTE_CLASS_DIRECTIVE_PATTERN.lastIndex = 0;
|
|
252
|
+
let match;
|
|
253
|
+
while ((match = SVELTE_CLASS_DIRECTIVE_PATTERN.exec(content)) !== null) {
|
|
254
|
+
if (match[1]) {
|
|
255
|
+
classes.push(match[1]);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
259
|
+
if (scriptMatch?.[1]) {
|
|
260
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
261
|
+
}
|
|
262
|
+
return [...new Set(classes)];
|
|
263
|
+
}
|
|
264
|
+
function extractFromAstro(content) {
|
|
265
|
+
const classes = [];
|
|
266
|
+
classes.push(...extractFromHTML(content));
|
|
267
|
+
classes.push(...extractFromJSX(content));
|
|
268
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
269
|
+
if (frontmatterMatch?.[1]) {
|
|
270
|
+
classes.push(...extractFromStringLiterals(frontmatterMatch[1]));
|
|
271
|
+
}
|
|
272
|
+
return [...new Set(classes)];
|
|
273
|
+
}
|
|
274
|
+
function extractFromStringLiterals(content) {
|
|
275
|
+
const classes = [];
|
|
276
|
+
let match;
|
|
277
|
+
SINGLE_QUOTE_PATTERN.lastIndex = 0;
|
|
278
|
+
while ((match = SINGLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
279
|
+
if (match[1]) {
|
|
280
|
+
classes.push(...extractUtilityClasses(match[1]));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
DOUBLE_QUOTE_PATTERN.lastIndex = 0;
|
|
284
|
+
while ((match = DOUBLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
285
|
+
if (match[1]) {
|
|
286
|
+
classes.push(...extractUtilityClasses(match[1]));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
TEMPLATE_LITERAL_PATTERN.lastIndex = 0;
|
|
290
|
+
while ((match = TEMPLATE_LITERAL_PATTERN.exec(content)) !== null) {
|
|
291
|
+
if (match[1]) {
|
|
292
|
+
classes.push(...extractUtilityClasses(match[1]));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return classes;
|
|
296
|
+
}
|
|
297
|
+
function extractFromMDX(content) {
|
|
298
|
+
const classes = [];
|
|
299
|
+
classes.push(...extractFromHTML(content));
|
|
300
|
+
classes.push(...extractFromJSX(content));
|
|
301
|
+
return [...new Set(classes)];
|
|
302
|
+
}
|
|
303
|
+
const defaultExtractors = {
|
|
304
|
+
html: extractFromHTML,
|
|
305
|
+
htm: extractFromHTML,
|
|
306
|
+
vue: extractFromVue,
|
|
307
|
+
svelte: extractFromSvelte,
|
|
308
|
+
jsx: extractFromJSX,
|
|
309
|
+
tsx: extractFromJSX,
|
|
310
|
+
js: extractFromStringLiterals,
|
|
311
|
+
ts: extractFromStringLiterals,
|
|
312
|
+
astro: extractFromAstro,
|
|
313
|
+
mdx: extractFromMDX,
|
|
314
|
+
php: extractFromHTML,
|
|
315
|
+
erb: extractFromHTML,
|
|
316
|
+
twig: extractFromHTML,
|
|
317
|
+
"blade.php": extractFromHTML
|
|
318
|
+
};
|
|
319
|
+
function getExtension(filePath) {
|
|
320
|
+
if (filePath.endsWith(".blade.php")) {
|
|
321
|
+
return "blade.php";
|
|
322
|
+
}
|
|
323
|
+
const parts = filePath.split(".");
|
|
324
|
+
return parts[parts.length - 1]?.toLowerCase() ?? "";
|
|
325
|
+
}
|
|
326
|
+
function extractClasses(content, filePath, customExtractors) {
|
|
327
|
+
const classes = [];
|
|
328
|
+
const extension = getExtension(filePath);
|
|
329
|
+
if (customExtractors) {
|
|
330
|
+
for (const extractor of customExtractors) {
|
|
331
|
+
classes.push(...extractor(content, filePath));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const defaultExtractor = defaultExtractors[extension];
|
|
335
|
+
if (defaultExtractor) {
|
|
336
|
+
classes.push(...defaultExtractor(content));
|
|
337
|
+
} else {
|
|
338
|
+
classes.push(...extractFromStringLiterals(content));
|
|
339
|
+
}
|
|
340
|
+
return [...new Set(classes)];
|
|
341
|
+
}
|
|
342
|
+
function generateValueKey(value, modifiers) {
|
|
343
|
+
if (modifiers.length === 0) {
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
const sortedModifiers = [...modifiers].sort().join(",");
|
|
347
|
+
return `${value}|${sortedModifiers}`;
|
|
348
|
+
}
|
|
349
|
+
function matchUtilities(parsed, root) {
|
|
350
|
+
const utilityMap = /* @__PURE__ */ new Map();
|
|
351
|
+
const modifierMap = /* @__PURE__ */ new Map();
|
|
352
|
+
const factoryValueSets = /* @__PURE__ */ new Map();
|
|
353
|
+
for (const utility of root.utilities) {
|
|
354
|
+
utilityMap.set(utility.name, utility);
|
|
355
|
+
const valueSet = /* @__PURE__ */ new Set();
|
|
356
|
+
for (const v of utility.values) {
|
|
357
|
+
valueSet.add(generateValueKey(v.key, v.modifiers));
|
|
358
|
+
}
|
|
359
|
+
factoryValueSets.set(utility, valueSet);
|
|
360
|
+
}
|
|
361
|
+
for (const modifier of root.modifiers) {
|
|
362
|
+
for (const key of modifier.key) {
|
|
363
|
+
modifierMap.set(key, modifier);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const matches = [];
|
|
367
|
+
for (const parsedClass of parsed) {
|
|
368
|
+
const factory = utilityMap.get(parsedClass.name) ?? null;
|
|
369
|
+
const matchedModifiers = [];
|
|
370
|
+
for (const modKey of parsedClass.modifiers) {
|
|
371
|
+
const modifier = modifierMap.get(modKey);
|
|
372
|
+
if (modifier) {
|
|
373
|
+
matchedModifiers.push(modifier);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
let exists = false;
|
|
377
|
+
if (factory) {
|
|
378
|
+
const lookupKey = generateValueKey(
|
|
379
|
+
parsedClass.value,
|
|
380
|
+
parsedClass.modifiers
|
|
381
|
+
);
|
|
382
|
+
const valueSet = factoryValueSets.get(factory);
|
|
383
|
+
exists = valueSet?.has(lookupKey) ?? false;
|
|
384
|
+
}
|
|
385
|
+
matches.push({
|
|
386
|
+
parsed: parsedClass,
|
|
387
|
+
factory,
|
|
388
|
+
modifierFactories: matchedModifiers,
|
|
389
|
+
exists
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return matches;
|
|
393
|
+
}
|
|
394
|
+
function generateUtilitySelector(options) {
|
|
395
|
+
const { name, value, modifiers } = options;
|
|
396
|
+
const parts = [
|
|
397
|
+
...modifiers,
|
|
398
|
+
name,
|
|
399
|
+
...value === "default" ? [] : [value]
|
|
400
|
+
].filter(Boolean);
|
|
401
|
+
return `._${parts.join("\\:").replace(/[[\].#()%,]/g, "\\$&")}`;
|
|
402
|
+
}
|
|
403
|
+
function classNameFromUtilityOptions(options) {
|
|
404
|
+
const { name, value, modifiers } = options;
|
|
405
|
+
const parts = [
|
|
406
|
+
...modifiers,
|
|
407
|
+
name,
|
|
408
|
+
...value === "default" ? [] : [value]
|
|
409
|
+
].filter(Boolean);
|
|
410
|
+
return `_${parts.join(":")}`;
|
|
411
|
+
}
|
|
412
|
+
function createUtilityFilter(usedClasses) {
|
|
413
|
+
return (utility) => {
|
|
414
|
+
const className = classNameFromUtilityOptions({
|
|
415
|
+
name: utility.name,
|
|
416
|
+
value: utility.value,
|
|
417
|
+
modifiers: utility.modifiers
|
|
418
|
+
});
|
|
419
|
+
return usedClasses.has(className);
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function filterUtilities(root, usedClasses) {
|
|
423
|
+
const filter = createUtilityFilter(usedClasses);
|
|
424
|
+
return root.children.filter((child) => {
|
|
425
|
+
if (child.type !== "utility") {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
return filter(child);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
function getUsedClassNames(matches) {
|
|
432
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
433
|
+
for (const match of matches) {
|
|
434
|
+
classNames.add(match.parsed.raw);
|
|
435
|
+
}
|
|
436
|
+
return classNames;
|
|
437
|
+
}
|
|
438
|
+
function getValidMatches(matches) {
|
|
439
|
+
return matches.filter((m) => m.factory !== null);
|
|
440
|
+
}
|
|
441
|
+
function getExistingMatches(matches) {
|
|
442
|
+
return matches.filter((m) => m.exists);
|
|
443
|
+
}
|
|
444
|
+
function getArbitraryMatches(matches) {
|
|
445
|
+
return matches.filter((m) => m.parsed.isArbitrary);
|
|
446
|
+
}
|
|
447
|
+
const compiledPatternCache = /* @__PURE__ */ new Map();
|
|
448
|
+
function getCompiledPattern(pattern) {
|
|
449
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
450
|
+
if (compiledPatternCache.has(normalized)) {
|
|
451
|
+
return compiledPatternCache.get(normalized);
|
|
452
|
+
}
|
|
453
|
+
if (!normalized.includes("**") && !normalized.includes("*") && !normalized.includes("?")) {
|
|
454
|
+
compiledPatternCache.set(normalized, null);
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
const regexStr = globToRegex(normalized);
|
|
459
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
460
|
+
compiledPatternCache.set(normalized, regex);
|
|
461
|
+
return regex;
|
|
462
|
+
} catch {
|
|
463
|
+
compiledPatternCache.set(normalized, null);
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function debounce(fn, delay) {
|
|
468
|
+
let timeoutId = null;
|
|
469
|
+
return (...args) => {
|
|
470
|
+
if (timeoutId) {
|
|
471
|
+
clearTimeout(timeoutId);
|
|
472
|
+
}
|
|
473
|
+
timeoutId = setTimeout(() => {
|
|
474
|
+
fn(...args);
|
|
475
|
+
timeoutId = null;
|
|
476
|
+
}, delay);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function createChangeHandler(callback, options = {}) {
|
|
480
|
+
const { debounce: debounceMs = 100 } = options;
|
|
481
|
+
const changedFiles = /* @__PURE__ */ new Set();
|
|
482
|
+
let timeoutId = null;
|
|
483
|
+
const flush = () => {
|
|
484
|
+
if (changedFiles.size > 0) {
|
|
485
|
+
const files = [...changedFiles];
|
|
486
|
+
changedFiles.clear();
|
|
487
|
+
callback(files);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const onChange = (filePath) => {
|
|
491
|
+
changedFiles.add(filePath);
|
|
492
|
+
if (timeoutId) {
|
|
493
|
+
clearTimeout(timeoutId);
|
|
494
|
+
}
|
|
495
|
+
timeoutId = setTimeout(() => {
|
|
496
|
+
flush();
|
|
497
|
+
timeoutId = null;
|
|
498
|
+
}, debounceMs);
|
|
499
|
+
};
|
|
500
|
+
return { onChange, flush };
|
|
501
|
+
}
|
|
502
|
+
function escapeRegexChars(str) {
|
|
503
|
+
return str.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
504
|
+
}
|
|
505
|
+
function globToRegex(pattern) {
|
|
506
|
+
return pattern.replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "<<<STAR>>>").replace(/\?/g, "<<<QUESTION>>>").split("<<<GLOBSTAR>>>").map(escapeRegexChars).join(".*").replace(/<<<STAR>>>/g, "[^/]*").replace(/<<<QUESTION>>>/g, ".");
|
|
507
|
+
}
|
|
508
|
+
function matchesPatterns(filePath, patterns) {
|
|
509
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
510
|
+
for (const pattern of patterns) {
|
|
511
|
+
const compiledRegex = getCompiledPattern(pattern);
|
|
512
|
+
if (compiledRegex !== null) {
|
|
513
|
+
if (compiledRegex.test(normalizedPath)) {
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
518
|
+
if (normalizedPath === normalizedPattern) {
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
function createScanner(config) {
|
|
526
|
+
const cache = createCache();
|
|
527
|
+
const cwd = config.cwd ?? process.cwd();
|
|
528
|
+
const customExtractors = config.extractors;
|
|
529
|
+
async function getFilePaths() {
|
|
530
|
+
return fg(config.content, {
|
|
531
|
+
cwd,
|
|
532
|
+
absolute: true,
|
|
533
|
+
ignore: DEFAULT_IGNORE_PATTERNS
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
async function scanFile(filePath) {
|
|
537
|
+
const content = await promises.readFile(filePath, "utf-8");
|
|
538
|
+
const contentHash = hashContent(content);
|
|
539
|
+
const cached = cache.getIfValid(filePath, contentHash);
|
|
540
|
+
if (cached) {
|
|
541
|
+
return cached;
|
|
542
|
+
}
|
|
543
|
+
const classNames = extractClasses(content, filePath, customExtractors);
|
|
544
|
+
const parsed = classNames.map(parseUtilityClass).filter((p) => p !== null);
|
|
545
|
+
const result = {
|
|
546
|
+
path: filePath,
|
|
547
|
+
classes: new Set(classNames),
|
|
548
|
+
parsed,
|
|
549
|
+
lastScanned: Date.now()
|
|
550
|
+
};
|
|
551
|
+
cache.set(filePath, result, contentHash);
|
|
552
|
+
return result;
|
|
553
|
+
}
|
|
554
|
+
function scanContent(content, filePath = "inline") {
|
|
555
|
+
const classNames = extractClasses(content, filePath, customExtractors);
|
|
556
|
+
return classNames.map(parseUtilityClass).filter((p) => p !== null);
|
|
557
|
+
}
|
|
558
|
+
async function scan() {
|
|
559
|
+
const filePaths = await getFilePaths();
|
|
560
|
+
const files = /* @__PURE__ */ new Map();
|
|
561
|
+
const allClasses = /* @__PURE__ */ new Set();
|
|
562
|
+
const allParsed = [];
|
|
563
|
+
const results = await Promise.all(
|
|
564
|
+
filePaths.map(async (filePath) => {
|
|
565
|
+
try {
|
|
566
|
+
return await scanFile(filePath);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.warn(
|
|
569
|
+
`[styleframe/scanner] Failed to scan ${filePath}:`,
|
|
570
|
+
error
|
|
571
|
+
);
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
})
|
|
575
|
+
);
|
|
576
|
+
for (const result of results) {
|
|
577
|
+
if (result) {
|
|
578
|
+
files.set(result.path, result);
|
|
579
|
+
for (const className of result.classes) {
|
|
580
|
+
allClasses.add(className);
|
|
581
|
+
}
|
|
582
|
+
allParsed.push(...result.parsed);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
files,
|
|
587
|
+
allClasses,
|
|
588
|
+
allParsed
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
function match(parsed, root) {
|
|
592
|
+
return matchUtilities(parsed, root);
|
|
593
|
+
}
|
|
594
|
+
function watch(callback) {
|
|
595
|
+
const { onChange, flush } = createChangeHandler(async (changedFiles) => {
|
|
596
|
+
for (const filePath of changedFiles) {
|
|
597
|
+
cache.invalidate(filePath);
|
|
598
|
+
}
|
|
599
|
+
const result = await scan();
|
|
600
|
+
callback(result);
|
|
601
|
+
});
|
|
602
|
+
return () => {
|
|
603
|
+
flush();
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function invalidate(filePath) {
|
|
607
|
+
if (filePath) {
|
|
608
|
+
cache.invalidate(filePath);
|
|
609
|
+
} else {
|
|
610
|
+
cache.clear();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
scan,
|
|
615
|
+
scanFile,
|
|
616
|
+
scanContent,
|
|
617
|
+
match,
|
|
618
|
+
watch,
|
|
619
|
+
invalidate
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function quickScan(content, filePath = "inline.html") {
|
|
623
|
+
const classNames = extractClasses(content, filePath);
|
|
624
|
+
return classNames.map(parseUtilityClass).filter((p) => p !== null);
|
|
625
|
+
}
|
|
626
|
+
function createContentScanner(customExtractors) {
|
|
627
|
+
return (content, filePath = "inline") => {
|
|
628
|
+
const classNames = extractClasses(content, filePath, customExtractors);
|
|
629
|
+
return classNames.map(parseUtilityClass).filter((p) => p !== null);
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
exports2.ARBITRARY_VALUE_PATTERN = ARBITRARY_VALUE_PATTERN;
|
|
633
|
+
exports2.DEFAULT_EXTENSIONS = DEFAULT_EXTENSIONS;
|
|
634
|
+
exports2.DEFAULT_IGNORE_PATTERNS = DEFAULT_IGNORE_PATTERNS;
|
|
635
|
+
exports2.UTILITY_CLASS_PATTERN = UTILITY_CLASS_PATTERN;
|
|
636
|
+
exports2.classNameFromUtilityOptions = classNameFromUtilityOptions;
|
|
637
|
+
exports2.createCache = createCache;
|
|
638
|
+
exports2.createChangeHandler = createChangeHandler;
|
|
639
|
+
exports2.createContentScanner = createContentScanner;
|
|
640
|
+
exports2.createScanner = createScanner;
|
|
641
|
+
exports2.createUtilityFilter = createUtilityFilter;
|
|
642
|
+
exports2.debounce = debounce;
|
|
643
|
+
exports2.extractClasses = extractClasses;
|
|
644
|
+
exports2.extractFromAstro = extractFromAstro;
|
|
645
|
+
exports2.extractFromHTML = extractFromHTML;
|
|
646
|
+
exports2.extractFromJSX = extractFromJSX;
|
|
647
|
+
exports2.extractFromMDX = extractFromMDX;
|
|
648
|
+
exports2.extractFromStringLiterals = extractFromStringLiterals;
|
|
649
|
+
exports2.extractFromSvelte = extractFromSvelte;
|
|
650
|
+
exports2.extractFromVue = extractFromVue;
|
|
651
|
+
exports2.extractUtilityClasses = extractUtilityClasses;
|
|
652
|
+
exports2.filterUtilities = filterUtilities;
|
|
653
|
+
exports2.generateUtilityClassName = generateUtilityClassName;
|
|
654
|
+
exports2.generateUtilitySelector = generateUtilitySelector;
|
|
655
|
+
exports2.getArbitraryMatches = getArbitraryMatches;
|
|
656
|
+
exports2.getExistingMatches = getExistingMatches;
|
|
657
|
+
exports2.getUsedClassNames = getUsedClassNames;
|
|
658
|
+
exports2.getValidMatches = getValidMatches;
|
|
659
|
+
exports2.hashContent = hashContent;
|
|
660
|
+
exports2.matchUtilities = matchUtilities;
|
|
661
|
+
exports2.matchesPatterns = matchesPatterns;
|
|
662
|
+
exports2.parseUtilityClass = parseUtilityClass;
|
|
663
|
+
exports2.quickScan = quickScan;
|
|
664
|
+
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
665
|
+
}));
|