@styleframe/scanner 2.0.0 → 3.1.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/CHANGELOG.md +34 -0
- package/dist/index.cjs +232 -68
- package/dist/index.d.ts +47 -24
- package/dist/index.js +232 -68
- package/package.json +5 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @styleframe/scanner
|
|
2
|
+
|
|
3
|
+
## 3.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#129](https://github.com/styleframe-dev/styleframe/pull/129) [`2610041`](https://github.com/styleframe-dev/styleframe/commit/2610041beb03a8afc8de17af8857b9931f3359b0) Thanks [@alexgrozav](https://github.com/alexgrozav)! - Add custom utility syntax support and separate class name generation from CSS escaping
|
|
8
|
+
- Extract `defaultUtilitySelectorFn` to `@styleframe/core` returning raw class names; add `classNameToCssSelector` for consistent CSS escaping
|
|
9
|
+
- Add `ScannerUtilitiesConfig` with pluggable `pattern`, `parse`, and `selector` functions for custom utility naming conventions
|
|
10
|
+
- Thread custom utilities config through extractor, matcher, scanner, and plugin layers
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [#133](https://github.com/styleframe-dev/styleframe/pull/133) [`ce62d31`](https://github.com/styleframe-dev/styleframe/commit/ce62d318275deed277d828fdd8d2500c1a9d767f) Thanks [@alexgrozav](https://github.com/alexgrozav)! - Add unique id and parent-child traversal to token types, validate @ references, and resolve utility sibling keys
|
|
15
|
+
- Add unique `id` field to Root, Selector, AtRule, Theme, Utility, Modifier, and Variable token types for stable identity tracking
|
|
16
|
+
- Add `parentId` to track parent-child relationships across the token tree
|
|
17
|
+
- Add `root._registry` for efficient id-based lookups and tree traversal
|
|
18
|
+
- Validate `@`-prefixed string references against root-level variables in `parseDeclarationsBlock`, throwing descriptive errors for undefined variables
|
|
19
|
+
- Add null/undefined guard to `ref()` with clear error messages
|
|
20
|
+
- Support `@`-prefixed values in utility entries that resolve to sibling keys (e.g., `{ default: "@solid", solid: "solid" }`)
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`295f04e`](https://github.com/styleframe-dev/styleframe/commit/295f04e6fdd011df6437986cc179e17efd8cd1be), [`71009c2`](https://github.com/styleframe-dev/styleframe/commit/71009c2c0a07a0bfd240e70e61020c8b7e923edb), [`2610041`](https://github.com/styleframe-dev/styleframe/commit/2610041beb03a8afc8de17af8857b9931f3359b0), [`7a61df0`](https://github.com/styleframe-dev/styleframe/commit/7a61df083bc534caa9271a1ef4535f7be979d7c2), [`ce62d31`](https://github.com/styleframe-dev/styleframe/commit/ce62d318275deed277d828fdd8d2500c1a9d767f)]:
|
|
23
|
+
- @styleframe/core@3.1.0
|
|
24
|
+
|
|
25
|
+
## 3.0.0
|
|
26
|
+
|
|
27
|
+
### Major Changes
|
|
28
|
+
|
|
29
|
+
- [#117](https://github.com/styleframe-dev/styleframe/pull/117) [`ffe6764`](https://github.com/styleframe-dev/styleframe/commit/ffe6764a2e6c84d5b3cfdf431bf11f17a3f3f118) Thanks [@alexgrozav](https://github.com/alexgrozav)! - Introduce global Styleframe single-instance architecture. Extension files (`*.styleframe.ts`) now share the same instance created in `styleframe.config.ts` instead of creating independent instances. This is a breaking change that affects how styles are imported and composed across files.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [[`266f961`](https://github.com/styleframe-dev/styleframe/commit/266f96143e9ffb47e0e6326d0e5e7cc9d974ab83), [`ffe6764`](https://github.com/styleframe-dev/styleframe/commit/ffe6764a2e6c84d5b3cfdf431bf11f17a3f3f118)]:
|
|
34
|
+
- @styleframe/core@3.0.0
|
package/dist/index.cjs
CHANGED
|
@@ -138,8 +138,8 @@
|
|
|
138
138
|
}
|
|
139
139
|
return segments;
|
|
140
140
|
}
|
|
141
|
-
function extractUtilityClasses(content) {
|
|
142
|
-
const matches = content.match(UTILITY_CLASS_PATTERN);
|
|
141
|
+
function extractUtilityClasses(content, pattern) {
|
|
142
|
+
const matches = content.match(pattern ?? UTILITY_CLASS_PATTERN);
|
|
143
143
|
return matches ? [...new Set(matches)] : [];
|
|
144
144
|
}
|
|
145
145
|
function generateUtilityClassName(name, value, modifiers = []) {
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
const CLASS_EXPR_PATTERN = /\bclass\s*=\s*\{/g;
|
|
154
154
|
const CLASS_ATTR_PATTERN = /\bclass\s*=\s*["']([^"']+)["']/gi;
|
|
155
155
|
const CLASSNAME_STRING_PATTERN = /\bclassName\s*=\s*["']([^"']+)["']/gi;
|
|
156
|
-
const SVELTE_CLASS_DIRECTIVE_PATTERN = /\bclass:(
|
|
156
|
+
const SVELTE_CLASS_DIRECTIVE_PATTERN = /\bclass:([^\s={>]+)/g;
|
|
157
157
|
const SINGLE_QUOTE_PATTERN = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
158
158
|
const DOUBLE_QUOTE_PATTERN = /"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
159
159
|
const TEMPLATE_LITERAL_PATTERN = /`([^`\\]*(?:\\.[^`\\]*)*)`/g;
|
|
@@ -202,51 +202,51 @@
|
|
|
202
202
|
}
|
|
203
203
|
return results;
|
|
204
204
|
}
|
|
205
|
-
function extractFromHTML(content) {
|
|
205
|
+
function extractFromHTML(content, pattern) {
|
|
206
206
|
const classes = [];
|
|
207
207
|
CLASS_ATTR_PATTERN.lastIndex = 0;
|
|
208
208
|
let match;
|
|
209
209
|
while ((match = CLASS_ATTR_PATTERN.exec(content)) !== null) {
|
|
210
210
|
if (match[1]) {
|
|
211
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
211
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
return classes;
|
|
215
215
|
}
|
|
216
|
-
function extractFromJSX(content) {
|
|
216
|
+
function extractFromJSX(content, pattern) {
|
|
217
217
|
const classes = [];
|
|
218
218
|
CLASSNAME_STRING_PATTERN.lastIndex = 0;
|
|
219
219
|
let match;
|
|
220
220
|
while ((match = CLASSNAME_STRING_PATTERN.exec(content)) !== null) {
|
|
221
221
|
if (match[1]) {
|
|
222
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
222
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
const expressions = extractClassNameExpressions(content);
|
|
226
226
|
for (const expr of expressions) {
|
|
227
|
-
classes.push(...extractFromStringLiterals(expr));
|
|
227
|
+
classes.push(...extractFromStringLiterals(expr, pattern));
|
|
228
228
|
}
|
|
229
229
|
return [...new Set(classes)];
|
|
230
230
|
}
|
|
231
|
-
function extractFromVue(content) {
|
|
231
|
+
function extractFromVue(content, pattern) {
|
|
232
232
|
const classes = [];
|
|
233
233
|
const templateMatch = content.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
234
234
|
if (templateMatch?.[1]) {
|
|
235
|
-
classes.push(...extractFromHTML(templateMatch[1]));
|
|
236
|
-
classes.push(...extractFromStringLiterals(templateMatch[1]));
|
|
235
|
+
classes.push(...extractFromHTML(templateMatch[1], pattern));
|
|
236
|
+
classes.push(...extractFromStringLiterals(templateMatch[1], pattern));
|
|
237
237
|
}
|
|
238
238
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
239
239
|
if (scriptMatch?.[1]) {
|
|
240
|
-
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
240
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1], pattern));
|
|
241
241
|
}
|
|
242
242
|
return [...new Set(classes)];
|
|
243
243
|
}
|
|
244
|
-
function extractFromSvelte(content) {
|
|
244
|
+
function extractFromSvelte(content, pattern) {
|
|
245
245
|
const classes = [];
|
|
246
|
-
classes.push(...extractFromHTML(content));
|
|
246
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
247
247
|
const expressions = extractClassExpressions(content);
|
|
248
248
|
for (const expr of expressions) {
|
|
249
|
-
classes.push(...extractFromStringLiterals(expr));
|
|
249
|
+
classes.push(...extractFromStringLiterals(expr, pattern));
|
|
250
250
|
}
|
|
251
251
|
SVELTE_CLASS_DIRECTIVE_PATTERN.lastIndex = 0;
|
|
252
252
|
let match;
|
|
@@ -257,47 +257,47 @@
|
|
|
257
257
|
}
|
|
258
258
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
259
259
|
if (scriptMatch?.[1]) {
|
|
260
|
-
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
260
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1], pattern));
|
|
261
261
|
}
|
|
262
262
|
return [...new Set(classes)];
|
|
263
263
|
}
|
|
264
|
-
function extractFromAstro(content) {
|
|
264
|
+
function extractFromAstro(content, pattern) {
|
|
265
265
|
const classes = [];
|
|
266
|
-
classes.push(...extractFromHTML(content));
|
|
267
|
-
classes.push(...extractFromJSX(content));
|
|
266
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
267
|
+
classes.push(...extractFromJSX(content, pattern));
|
|
268
268
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
269
269
|
if (frontmatterMatch?.[1]) {
|
|
270
|
-
classes.push(...extractFromStringLiterals(frontmatterMatch[1]));
|
|
270
|
+
classes.push(...extractFromStringLiterals(frontmatterMatch[1], pattern));
|
|
271
271
|
}
|
|
272
272
|
return [...new Set(classes)];
|
|
273
273
|
}
|
|
274
|
-
function extractFromStringLiterals(content) {
|
|
274
|
+
function extractFromStringLiterals(content, pattern) {
|
|
275
275
|
const classes = [];
|
|
276
276
|
let match;
|
|
277
277
|
SINGLE_QUOTE_PATTERN.lastIndex = 0;
|
|
278
278
|
while ((match = SINGLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
279
279
|
if (match[1]) {
|
|
280
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
280
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
DOUBLE_QUOTE_PATTERN.lastIndex = 0;
|
|
284
284
|
while ((match = DOUBLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
285
285
|
if (match[1]) {
|
|
286
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
286
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
TEMPLATE_LITERAL_PATTERN.lastIndex = 0;
|
|
290
290
|
while ((match = TEMPLATE_LITERAL_PATTERN.exec(content)) !== null) {
|
|
291
291
|
if (match[1]) {
|
|
292
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
292
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
return classes;
|
|
296
296
|
}
|
|
297
|
-
function extractFromMDX(content) {
|
|
297
|
+
function extractFromMDX(content, pattern) {
|
|
298
298
|
const classes = [];
|
|
299
|
-
classes.push(...extractFromHTML(content));
|
|
300
|
-
classes.push(...extractFromJSX(content));
|
|
299
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
300
|
+
classes.push(...extractFromJSX(content, pattern));
|
|
301
301
|
return [...new Set(classes)];
|
|
302
302
|
}
|
|
303
303
|
const defaultExtractors = {
|
|
@@ -323,7 +323,7 @@
|
|
|
323
323
|
const parts = filePath.split(".");
|
|
324
324
|
return parts[parts.length - 1]?.toLowerCase() ?? "";
|
|
325
325
|
}
|
|
326
|
-
function extractClasses(content, filePath, customExtractors) {
|
|
326
|
+
function extractClasses(content, filePath, customExtractors, utilityPattern) {
|
|
327
327
|
const classes = [];
|
|
328
328
|
const extension = getExtension(filePath);
|
|
329
329
|
if (customExtractors) {
|
|
@@ -333,12 +333,160 @@
|
|
|
333
333
|
}
|
|
334
334
|
const defaultExtractor = defaultExtractors[extension];
|
|
335
335
|
if (defaultExtractor) {
|
|
336
|
-
classes.push(...defaultExtractor(content));
|
|
336
|
+
classes.push(...defaultExtractor(content, utilityPattern));
|
|
337
337
|
} else {
|
|
338
|
-
classes.push(...extractFromStringLiterals(content));
|
|
338
|
+
classes.push(...extractFromStringLiterals(content, utilityPattern));
|
|
339
339
|
}
|
|
340
340
|
return [...new Set(classes)];
|
|
341
341
|
}
|
|
342
|
+
const Be = ({
|
|
343
|
+
name: e,
|
|
344
|
+
value: t,
|
|
345
|
+
modifiers: r
|
|
346
|
+
}) => `_${[
|
|
347
|
+
...r,
|
|
348
|
+
e,
|
|
349
|
+
...t === "default" ? [] : [t]
|
|
350
|
+
].filter(Boolean).join(":")}`;
|
|
351
|
+
function Ke(e) {
|
|
352
|
+
return `.${e.replace(/[[\].#()%,:/]/g, "\\$&")}`;
|
|
353
|
+
}
|
|
354
|
+
function v(e) {
|
|
355
|
+
if (e instanceof Buffer)
|
|
356
|
+
return Buffer.from(e);
|
|
357
|
+
const t = e.constructor;
|
|
358
|
+
return new t(
|
|
359
|
+
e.buffer.slice(0),
|
|
360
|
+
e.byteOffset,
|
|
361
|
+
e.byteLength / e.BYTES_PER_ELEMENT || 1
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
function ue(e) {
|
|
365
|
+
if (e = e || {}, e.circular)
|
|
366
|
+
return le(e);
|
|
367
|
+
const t = /* @__PURE__ */ new Map();
|
|
368
|
+
if (t.set(Date, (i) => new Date(i)), t.set(
|
|
369
|
+
Map,
|
|
370
|
+
(i, f) => new Map(n(Array.from(i), f))
|
|
371
|
+
), t.set(
|
|
372
|
+
Set,
|
|
373
|
+
(i, f) => new Set(n(Array.from(i), f))
|
|
374
|
+
), e.constructorHandlers)
|
|
375
|
+
for (const i of e.constructorHandlers)
|
|
376
|
+
t.set(i[0], i[1]);
|
|
377
|
+
let r;
|
|
378
|
+
return e.proto ? o : s;
|
|
379
|
+
function n(i, f) {
|
|
380
|
+
const c = Object.keys(i), a = Array.from({ length: c.length });
|
|
381
|
+
for (let l = 0; l < c.length; l++) {
|
|
382
|
+
const u = c[l], d = i[u];
|
|
383
|
+
typeof d != "object" || d === null ? a[u] = d : d.constructor !== Object && (r = t.get(d.constructor)) ? a[u] = r(d, f) : ArrayBuffer.isView(d) ? a[u] = v(d) : a[u] = f(d);
|
|
384
|
+
}
|
|
385
|
+
return a;
|
|
386
|
+
}
|
|
387
|
+
function s(i) {
|
|
388
|
+
if (typeof i != "object" || i === null) return i;
|
|
389
|
+
if (Array.isArray(i)) return n(i, s);
|
|
390
|
+
if (i.constructor !== Object && (r = t.get(i.constructor)))
|
|
391
|
+
return r(i, s);
|
|
392
|
+
const f = {};
|
|
393
|
+
for (const c in i) {
|
|
394
|
+
if (Object.hasOwnProperty.call(i, c) === false) continue;
|
|
395
|
+
const a = i[c];
|
|
396
|
+
typeof a != "object" || a === null ? f[c] = a : a.constructor !== Object && (r = t.get(a.constructor)) ? f[c] = r(a, s) : ArrayBuffer.isView(a) ? f[c] = v(a) : f[c] = s(a);
|
|
397
|
+
}
|
|
398
|
+
return f;
|
|
399
|
+
}
|
|
400
|
+
function o(i) {
|
|
401
|
+
if (typeof i != "object" || i === null) return i;
|
|
402
|
+
if (Array.isArray(i)) return n(i, o);
|
|
403
|
+
if (i.constructor !== Object && (r = t.get(i.constructor)))
|
|
404
|
+
return r(i, o);
|
|
405
|
+
const f = {};
|
|
406
|
+
for (const c in i) {
|
|
407
|
+
const a = i[c];
|
|
408
|
+
typeof a != "object" || a === null ? f[c] = a : a.constructor !== Object && (r = t.get(a.constructor)) ? f[c] = r(a, o) : ArrayBuffer.isView(a) ? f[c] = v(a) : f[c] = o(a);
|
|
409
|
+
}
|
|
410
|
+
return f;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function le(e) {
|
|
414
|
+
const t = [], r = [], n = /* @__PURE__ */ new Map();
|
|
415
|
+
if (n.set(Date, (c) => new Date(c)), n.set(
|
|
416
|
+
Map,
|
|
417
|
+
(c, a) => new Map(o(Array.from(c), a))
|
|
418
|
+
), n.set(
|
|
419
|
+
Set,
|
|
420
|
+
(c, a) => new Set(o(Array.from(c), a))
|
|
421
|
+
), e.constructorHandlers)
|
|
422
|
+
for (const c of e.constructorHandlers)
|
|
423
|
+
n.set(c[0], c[1]);
|
|
424
|
+
let s;
|
|
425
|
+
return e.proto ? f : i;
|
|
426
|
+
function o(c, a) {
|
|
427
|
+
const l = Object.keys(c), u = Array.from({ length: l.length });
|
|
428
|
+
for (let d = 0; d < l.length; d++) {
|
|
429
|
+
const y = l[d], m = c[y];
|
|
430
|
+
if (typeof m != "object" || m === null)
|
|
431
|
+
u[y] = m;
|
|
432
|
+
else if (m.constructor !== Object && (s = n.get(m.constructor)))
|
|
433
|
+
u[y] = s(m, a);
|
|
434
|
+
else if (ArrayBuffer.isView(m))
|
|
435
|
+
u[y] = v(m);
|
|
436
|
+
else {
|
|
437
|
+
const p = t.indexOf(m);
|
|
438
|
+
p !== -1 ? u[y] = r[p] : u[y] = a(m);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return u;
|
|
442
|
+
}
|
|
443
|
+
function i(c) {
|
|
444
|
+
if (typeof c != "object" || c === null) return c;
|
|
445
|
+
if (Array.isArray(c)) return o(c, i);
|
|
446
|
+
if (c.constructor !== Object && (s = n.get(c.constructor)))
|
|
447
|
+
return s(c, i);
|
|
448
|
+
const a = {};
|
|
449
|
+
t.push(c), r.push(a);
|
|
450
|
+
for (const l in c) {
|
|
451
|
+
if (Object.hasOwnProperty.call(c, l) === false) continue;
|
|
452
|
+
const u = c[l];
|
|
453
|
+
if (typeof u != "object" || u === null)
|
|
454
|
+
a[l] = u;
|
|
455
|
+
else if (u.constructor !== Object && (s = n.get(u.constructor)))
|
|
456
|
+
a[l] = s(u, i);
|
|
457
|
+
else if (ArrayBuffer.isView(u))
|
|
458
|
+
a[l] = v(u);
|
|
459
|
+
else {
|
|
460
|
+
const d = t.indexOf(u);
|
|
461
|
+
d !== -1 ? a[l] = r[d] : a[l] = i(u);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return t.pop(), r.pop(), a;
|
|
465
|
+
}
|
|
466
|
+
function f(c) {
|
|
467
|
+
if (typeof c != "object" || c === null) return c;
|
|
468
|
+
if (Array.isArray(c)) return o(c, f);
|
|
469
|
+
if (c.constructor !== Object && (s = n.get(c.constructor)))
|
|
470
|
+
return s(c, f);
|
|
471
|
+
const a = {};
|
|
472
|
+
t.push(c), r.push(a);
|
|
473
|
+
for (const l in c) {
|
|
474
|
+
const u = c[l];
|
|
475
|
+
if (typeof u != "object" || u === null)
|
|
476
|
+
a[l] = u;
|
|
477
|
+
else if (u.constructor !== Object && (s = n.get(u.constructor)))
|
|
478
|
+
a[l] = s(u, f);
|
|
479
|
+
else if (ArrayBuffer.isView(u))
|
|
480
|
+
a[l] = v(u);
|
|
481
|
+
else {
|
|
482
|
+
const d = t.indexOf(u);
|
|
483
|
+
d !== -1 ? a[l] = r[d] : a[l] = f(u);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return t.pop(), r.pop(), a;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
ue();
|
|
342
490
|
function generateValueKey(value, modifiers) {
|
|
343
491
|
if (modifiers.length === 0) {
|
|
344
492
|
return value;
|
|
@@ -353,8 +501,8 @@
|
|
|
353
501
|
for (const utility of root.utilities) {
|
|
354
502
|
utilityMap.set(utility.name, utility);
|
|
355
503
|
const valueSet = /* @__PURE__ */ new Set();
|
|
356
|
-
for (const
|
|
357
|
-
valueSet.add(generateValueKey(
|
|
504
|
+
for (const v2 of utility.values) {
|
|
505
|
+
valueSet.add(generateValueKey(v2.key, v2.modifiers));
|
|
358
506
|
}
|
|
359
507
|
factoryValueSets.set(utility, valueSet);
|
|
360
508
|
}
|
|
@@ -391,36 +539,28 @@
|
|
|
391
539
|
}
|
|
392
540
|
return matches;
|
|
393
541
|
}
|
|
394
|
-
function generateUtilitySelector(options) {
|
|
395
|
-
const
|
|
396
|
-
|
|
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(":")}`;
|
|
542
|
+
function generateUtilitySelector(options, selectorFn) {
|
|
543
|
+
const className = classNameFromUtilityOptions(options, selectorFn);
|
|
544
|
+
return Ke(className);
|
|
411
545
|
}
|
|
412
|
-
function
|
|
546
|
+
function classNameFromUtilityOptions(options, selectorFn) {
|
|
547
|
+
return (selectorFn ?? Be)(options);
|
|
548
|
+
}
|
|
549
|
+
function createUtilityFilter(usedClasses, selectorFn) {
|
|
413
550
|
return (utility) => {
|
|
414
|
-
const className = classNameFromUtilityOptions(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
551
|
+
const className = classNameFromUtilityOptions(
|
|
552
|
+
{
|
|
553
|
+
name: utility.name,
|
|
554
|
+
value: utility.value,
|
|
555
|
+
modifiers: utility.modifiers
|
|
556
|
+
},
|
|
557
|
+
selectorFn
|
|
558
|
+
);
|
|
419
559
|
return usedClasses.has(className);
|
|
420
560
|
};
|
|
421
561
|
}
|
|
422
|
-
function filterUtilities(root, usedClasses) {
|
|
423
|
-
const filter = createUtilityFilter(usedClasses);
|
|
562
|
+
function filterUtilities(root, usedClasses, selectorFn) {
|
|
563
|
+
const filter = createUtilityFilter(usedClasses, selectorFn);
|
|
424
564
|
return root.children.filter((child) => {
|
|
425
565
|
if (child.type !== "utility") {
|
|
426
566
|
return true;
|
|
@@ -526,6 +666,8 @@
|
|
|
526
666
|
const cache = createCache();
|
|
527
667
|
const cwd = config.cwd ?? process.cwd();
|
|
528
668
|
const customExtractors = config.extractors;
|
|
669
|
+
const utilityPattern = config.utilities?.pattern;
|
|
670
|
+
const utilityParse = config.utilities?.parse ?? parseUtilityClass;
|
|
529
671
|
async function getFilePaths() {
|
|
530
672
|
return fg(config.content, {
|
|
531
673
|
cwd,
|
|
@@ -540,8 +682,13 @@
|
|
|
540
682
|
if (cached) {
|
|
541
683
|
return cached;
|
|
542
684
|
}
|
|
543
|
-
const classNames = extractClasses(
|
|
544
|
-
|
|
685
|
+
const classNames = extractClasses(
|
|
686
|
+
content,
|
|
687
|
+
filePath,
|
|
688
|
+
customExtractors,
|
|
689
|
+
utilityPattern
|
|
690
|
+
);
|
|
691
|
+
const parsed = classNames.map(utilityParse).filter((p) => p !== null);
|
|
545
692
|
const result = {
|
|
546
693
|
path: filePath,
|
|
547
694
|
classes: new Set(classNames),
|
|
@@ -552,8 +699,13 @@
|
|
|
552
699
|
return result;
|
|
553
700
|
}
|
|
554
701
|
function scanContent(content, filePath = "inline") {
|
|
555
|
-
const classNames = extractClasses(
|
|
556
|
-
|
|
702
|
+
const classNames = extractClasses(
|
|
703
|
+
content,
|
|
704
|
+
filePath,
|
|
705
|
+
customExtractors,
|
|
706
|
+
utilityPattern
|
|
707
|
+
);
|
|
708
|
+
return classNames.map(utilityParse).filter((p) => p !== null);
|
|
557
709
|
}
|
|
558
710
|
async function scan() {
|
|
559
711
|
const filePaths = await getFilePaths();
|
|
@@ -619,14 +771,26 @@
|
|
|
619
771
|
invalidate
|
|
620
772
|
};
|
|
621
773
|
}
|
|
622
|
-
function quickScan(content, filePath = "inline.html") {
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
774
|
+
function quickScan(content, filePath = "inline.html", utilities) {
|
|
775
|
+
const parseFn = utilities?.parse ?? parseUtilityClass;
|
|
776
|
+
const classNames = extractClasses(
|
|
777
|
+
content,
|
|
778
|
+
filePath,
|
|
779
|
+
void 0,
|
|
780
|
+
utilities?.pattern
|
|
781
|
+
);
|
|
782
|
+
return classNames.map(parseFn).filter((p) => p !== null);
|
|
783
|
+
}
|
|
784
|
+
function createContentScanner(customExtractors, utilities) {
|
|
785
|
+
const parseFn = utilities?.parse ?? parseUtilityClass;
|
|
627
786
|
return (content, filePath = "inline") => {
|
|
628
|
-
const classNames = extractClasses(
|
|
629
|
-
|
|
787
|
+
const classNames = extractClasses(
|
|
788
|
+
content,
|
|
789
|
+
filePath,
|
|
790
|
+
customExtractors,
|
|
791
|
+
utilities?.pattern
|
|
792
|
+
);
|
|
793
|
+
return classNames.map(parseFn).filter((p) => p !== null);
|
|
630
794
|
};
|
|
631
795
|
}
|
|
632
796
|
exports2.ARBITRARY_VALUE_PATTERN = ARBITRARY_VALUE_PATTERN;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { ModifierFactory } from '@styleframe/core';
|
|
|
2
2
|
import { Root } from '@styleframe/core';
|
|
3
3
|
import { Utility } from '@styleframe/core';
|
|
4
4
|
import { UtilityFactory } from '@styleframe/core';
|
|
5
|
+
import { UtilitySelectorFn } from '@styleframe/core';
|
|
6
|
+
import { UtilitySelectorOptions } from '@styleframe/core';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Pattern to extract the value from an arbitrary value bracket notation.
|
|
@@ -24,7 +26,7 @@ export declare interface CacheEntry {
|
|
|
24
26
|
/**
|
|
25
27
|
* Generate the raw utility class name from options (without CSS escaping).
|
|
26
28
|
*/
|
|
27
|
-
export declare function classNameFromUtilityOptions(options: UtilitySelectorOptions): string;
|
|
29
|
+
export declare function classNameFromUtilityOptions(options: UtilitySelectorOptions, selectorFn?: UtilitySelectorFn): string;
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Create a scanner cache instance.
|
|
@@ -55,7 +57,7 @@ export declare function createChangeHandler(callback: (changedFiles: string[]) =
|
|
|
55
57
|
*
|
|
56
58
|
* Useful for testing or one-off scans.
|
|
57
59
|
*/
|
|
58
|
-
export declare function createContentScanner(customExtractors?: Extractor[]): (content: string, filePath?: string) => ParsedUtility[];
|
|
60
|
+
export declare function createContentScanner(customExtractors?: Extractor[], utilities?: ScannerUtilitiesConfig): (content: string, filePath?: string) => ParsedUtility[];
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* Create a scanner instance for detecting utility classes in content files.
|
|
@@ -79,9 +81,10 @@ export declare function createScanner(config: ScannerConfig): Scanner;
|
|
|
79
81
|
* Create a filter function that checks if a utility is in the used set.
|
|
80
82
|
*
|
|
81
83
|
* @param usedClasses Set of used utility class names
|
|
84
|
+
* @param selectorFn Optional custom selector function
|
|
82
85
|
* @returns Filter function for utilities
|
|
83
86
|
*/
|
|
84
|
-
export declare function createUtilityFilter(usedClasses: Set<string
|
|
87
|
+
export declare function createUtilityFilter(usedClasses: Set<string>, selectorFn?: UtilitySelectorFn): (utility: Utility) => boolean;
|
|
85
88
|
|
|
86
89
|
/**
|
|
87
90
|
* Simple debounce utility
|
|
@@ -104,33 +107,34 @@ export declare const DEFAULT_IGNORE_PATTERNS: string[];
|
|
|
104
107
|
* @param content The file content
|
|
105
108
|
* @param filePath The file path (used to determine extractor)
|
|
106
109
|
* @param customExtractors Optional custom extractors to use in addition to defaults
|
|
110
|
+
* @param utilityPattern Optional custom regex pattern for utility class extraction
|
|
107
111
|
* @returns Array of unique utility class names found
|
|
108
112
|
*/
|
|
109
|
-
export declare function extractClasses(content: string, filePath: string, customExtractors?: Extractor[]): string[];
|
|
113
|
+
export declare function extractClasses(content: string, filePath: string, customExtractors?: Extractor[], utilityPattern?: RegExp): string[];
|
|
110
114
|
|
|
111
115
|
/**
|
|
112
116
|
* Extract utility classes from Astro content.
|
|
113
117
|
* Handles both HTML-like and JSX-like patterns.
|
|
114
118
|
*/
|
|
115
|
-
export declare function extractFromAstro(content: string): string[];
|
|
119
|
+
export declare function extractFromAstro(content: string, pattern?: RegExp): string[];
|
|
116
120
|
|
|
117
121
|
/**
|
|
118
122
|
* Extract utility classes from HTML-like content.
|
|
119
123
|
* Handles class="..." attributes.
|
|
120
124
|
*/
|
|
121
|
-
export declare function extractFromHTML(content: string): string[];
|
|
125
|
+
export declare function extractFromHTML(content: string, pattern?: RegExp): string[];
|
|
122
126
|
|
|
123
127
|
/**
|
|
124
128
|
* Extract utility classes from JSX/TSX content.
|
|
125
129
|
* Handles className="..." and className={...} patterns.
|
|
126
130
|
*/
|
|
127
|
-
export declare function extractFromJSX(content: string): string[];
|
|
131
|
+
export declare function extractFromJSX(content: string, pattern?: RegExp): string[];
|
|
128
132
|
|
|
129
133
|
/**
|
|
130
134
|
* Extract utility classes from MDX content.
|
|
131
135
|
* Handles both Markdown and JSX patterns.
|
|
132
136
|
*/
|
|
133
|
-
export declare function extractFromMDX(content: string): string[];
|
|
137
|
+
export declare function extractFromMDX(content: string, pattern?: RegExp): string[];
|
|
134
138
|
|
|
135
139
|
/**
|
|
136
140
|
* Extract utility classes from string literals in JavaScript/TypeScript code.
|
|
@@ -140,19 +144,19 @@ export declare function extractFromMDX(content: string): string[];
|
|
|
140
144
|
* extract the static portions. Dynamic class names cannot be statically analyzed.
|
|
141
145
|
* For full coverage, use explicit string arrays or safelist patterns.
|
|
142
146
|
*/
|
|
143
|
-
export declare function extractFromStringLiterals(content: string): string[];
|
|
147
|
+
export declare function extractFromStringLiterals(content: string, pattern?: RegExp): string[];
|
|
144
148
|
|
|
145
149
|
/**
|
|
146
150
|
* Extract utility classes from Svelte content.
|
|
147
151
|
* Handles class="...", class:directive={condition}, and class={...} patterns.
|
|
148
152
|
*/
|
|
149
|
-
export declare function extractFromSvelte(content: string): string[];
|
|
153
|
+
export declare function extractFromSvelte(content: string, pattern?: RegExp): string[];
|
|
150
154
|
|
|
151
155
|
/**
|
|
152
156
|
* Extract utility classes from Vue SFC content.
|
|
153
157
|
* Handles class="...", :class="...", and :class="{...}" patterns.
|
|
154
158
|
*/
|
|
155
|
-
export declare function extractFromVue(content: string): string[];
|
|
159
|
+
export declare function extractFromVue(content: string, pattern?: RegExp): string[];
|
|
156
160
|
|
|
157
161
|
/**
|
|
158
162
|
* Custom extractor function type
|
|
@@ -163,9 +167,10 @@ export declare type Extractor = (content: string, filePath: string) => string[];
|
|
|
163
167
|
* Extract all utility class names from a content string.
|
|
164
168
|
*
|
|
165
169
|
* @param content The content to search
|
|
170
|
+
* @param pattern Optional custom regex pattern (must use global flag). Defaults to UTILITY_CLASS_PATTERN.
|
|
166
171
|
* @returns Array of unique utility class names found
|
|
167
172
|
*/
|
|
168
|
-
export declare function extractUtilityClasses(content: string): string[];
|
|
173
|
+
export declare function extractUtilityClasses(content: string, pattern?: RegExp): string[];
|
|
169
174
|
|
|
170
175
|
/**
|
|
171
176
|
* Scan result for a single file
|
|
@@ -186,9 +191,10 @@ export declare interface FileScanResult {
|
|
|
186
191
|
*
|
|
187
192
|
* @param root The Styleframe root instance
|
|
188
193
|
* @param usedClasses Set of used utility class names
|
|
194
|
+
* @param selectorFn Optional custom selector function
|
|
189
195
|
* @returns Array of used children (utilities are filtered, other types pass through)
|
|
190
196
|
*/
|
|
191
|
-
export declare function filterUtilities(root: Root, usedClasses: Set<string
|
|
197
|
+
export declare function filterUtilities(root: Root, usedClasses: Set<string>, selectorFn?: UtilitySelectorFn): Root["children"];
|
|
192
198
|
|
|
193
199
|
/**
|
|
194
200
|
* Generate a utility selector string from components.
|
|
@@ -204,9 +210,9 @@ export declare function generateUtilityClassName(name: string, value: string, mo
|
|
|
204
210
|
/**
|
|
205
211
|
* Generate the CSS selector for a utility instance.
|
|
206
212
|
*
|
|
207
|
-
*
|
|
213
|
+
* Derives the selector from the class name by adding a `.` prefix and escaping special characters.
|
|
208
214
|
*/
|
|
209
|
-
export declare function generateUtilitySelector(options: UtilitySelectorOptions): string;
|
|
215
|
+
export declare function generateUtilitySelector(options: UtilitySelectorOptions, selectorFn?: UtilitySelectorFn): string;
|
|
210
216
|
|
|
211
217
|
/**
|
|
212
218
|
* Get matches for arbitrary values.
|
|
@@ -292,9 +298,10 @@ export declare function parseUtilityClass(className: string): ParsedUtility | nu
|
|
|
292
298
|
*
|
|
293
299
|
* @param content Content string to scan
|
|
294
300
|
* @param filePath Optional file path hint for extractor selection
|
|
301
|
+
* @param utilities Optional custom utility syntax configuration
|
|
295
302
|
* @returns Array of parsed utilities
|
|
296
303
|
*/
|
|
297
|
-
export declare function quickScan(content: string, filePath?: string): ParsedUtility[];
|
|
304
|
+
export declare function quickScan(content: string, filePath?: string, utilities?: ScannerUtilitiesConfig): ParsedUtility[];
|
|
298
305
|
|
|
299
306
|
/**
|
|
300
307
|
* Scanner instance interface
|
|
@@ -342,6 +349,21 @@ export declare interface ScannerConfig {
|
|
|
342
349
|
extractors?: Extractor[];
|
|
343
350
|
/** Base directory for glob resolution (defaults to process.cwd()) */
|
|
344
351
|
cwd?: string;
|
|
352
|
+
/** Custom utility class syntax configuration */
|
|
353
|
+
utilities?: ScannerUtilitiesConfig;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Configuration for custom utility class syntax.
|
|
358
|
+
* All fields are optional — defaults match the `_modifier:property:value` format.
|
|
359
|
+
*/
|
|
360
|
+
export declare interface ScannerUtilitiesConfig {
|
|
361
|
+
/** Regex pattern to extract utility class candidates from content strings. Must use the global (`g`) flag. */
|
|
362
|
+
pattern?: RegExp;
|
|
363
|
+
/** Parse a matched class name into its components (name, value, modifiers) */
|
|
364
|
+
parse?: UtilityClassParseFn;
|
|
365
|
+
/** Generate a raw class name from components (inverse of parse) */
|
|
366
|
+
selector?: UtilitySelectorFn;
|
|
345
367
|
}
|
|
346
368
|
|
|
347
369
|
/**
|
|
@@ -375,6 +397,12 @@ export declare interface ScanResult {
|
|
|
375
397
|
*/
|
|
376
398
|
export declare const UTILITY_CLASS_PATTERN: RegExp;
|
|
377
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Function that parses a class name string into its structured components.
|
|
402
|
+
* Returns null if the string is not a valid utility class.
|
|
403
|
+
*/
|
|
404
|
+
export declare type UtilityClassParseFn = (className: string) => ParsedUtility | null;
|
|
405
|
+
|
|
378
406
|
/**
|
|
379
407
|
* Filter function for utilities
|
|
380
408
|
*/
|
|
@@ -394,14 +422,9 @@ export declare interface UtilityMatch {
|
|
|
394
422
|
exists: boolean;
|
|
395
423
|
}
|
|
396
424
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
export declare interface UtilitySelectorOptions {
|
|
401
|
-
name: string;
|
|
402
|
-
value: string;
|
|
403
|
-
modifiers: string[];
|
|
404
|
-
}
|
|
425
|
+
export { UtilitySelectorFn }
|
|
426
|
+
|
|
427
|
+
export { UtilitySelectorOptions }
|
|
405
428
|
|
|
406
429
|
/**
|
|
407
430
|
* Callback type for file change events
|
package/dist/index.js
CHANGED
|
@@ -136,8 +136,8 @@ function splitByColons(str) {
|
|
|
136
136
|
}
|
|
137
137
|
return segments;
|
|
138
138
|
}
|
|
139
|
-
function extractUtilityClasses(content) {
|
|
140
|
-
const matches = content.match(UTILITY_CLASS_PATTERN);
|
|
139
|
+
function extractUtilityClasses(content, pattern) {
|
|
140
|
+
const matches = content.match(pattern ?? UTILITY_CLASS_PATTERN);
|
|
141
141
|
return matches ? [...new Set(matches)] : [];
|
|
142
142
|
}
|
|
143
143
|
function generateUtilityClassName(name, value, modifiers = []) {
|
|
@@ -151,7 +151,7 @@ const CLASSNAME_EXPR_PATTERN = /\bclassName\s*=\s*\{/g;
|
|
|
151
151
|
const CLASS_EXPR_PATTERN = /\bclass\s*=\s*\{/g;
|
|
152
152
|
const CLASS_ATTR_PATTERN = /\bclass\s*=\s*["']([^"']+)["']/gi;
|
|
153
153
|
const CLASSNAME_STRING_PATTERN = /\bclassName\s*=\s*["']([^"']+)["']/gi;
|
|
154
|
-
const SVELTE_CLASS_DIRECTIVE_PATTERN = /\bclass:(
|
|
154
|
+
const SVELTE_CLASS_DIRECTIVE_PATTERN = /\bclass:([^\s={>]+)/g;
|
|
155
155
|
const SINGLE_QUOTE_PATTERN = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
156
156
|
const DOUBLE_QUOTE_PATTERN = /"([^"\\]*(?:\\.[^"\\]*)*)"/g;
|
|
157
157
|
const TEMPLATE_LITERAL_PATTERN = /`([^`\\]*(?:\\.[^`\\]*)*)`/g;
|
|
@@ -200,51 +200,51 @@ function extractClassExpressions(content) {
|
|
|
200
200
|
}
|
|
201
201
|
return results;
|
|
202
202
|
}
|
|
203
|
-
function extractFromHTML(content) {
|
|
203
|
+
function extractFromHTML(content, pattern) {
|
|
204
204
|
const classes = [];
|
|
205
205
|
CLASS_ATTR_PATTERN.lastIndex = 0;
|
|
206
206
|
let match;
|
|
207
207
|
while ((match = CLASS_ATTR_PATTERN.exec(content)) !== null) {
|
|
208
208
|
if (match[1]) {
|
|
209
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
209
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
return classes;
|
|
213
213
|
}
|
|
214
|
-
function extractFromJSX(content) {
|
|
214
|
+
function extractFromJSX(content, pattern) {
|
|
215
215
|
const classes = [];
|
|
216
216
|
CLASSNAME_STRING_PATTERN.lastIndex = 0;
|
|
217
217
|
let match;
|
|
218
218
|
while ((match = CLASSNAME_STRING_PATTERN.exec(content)) !== null) {
|
|
219
219
|
if (match[1]) {
|
|
220
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
220
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
const expressions = extractClassNameExpressions(content);
|
|
224
224
|
for (const expr of expressions) {
|
|
225
|
-
classes.push(...extractFromStringLiterals(expr));
|
|
225
|
+
classes.push(...extractFromStringLiterals(expr, pattern));
|
|
226
226
|
}
|
|
227
227
|
return [...new Set(classes)];
|
|
228
228
|
}
|
|
229
|
-
function extractFromVue(content) {
|
|
229
|
+
function extractFromVue(content, pattern) {
|
|
230
230
|
const classes = [];
|
|
231
231
|
const templateMatch = content.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
|
|
232
232
|
if (templateMatch?.[1]) {
|
|
233
|
-
classes.push(...extractFromHTML(templateMatch[1]));
|
|
234
|
-
classes.push(...extractFromStringLiterals(templateMatch[1]));
|
|
233
|
+
classes.push(...extractFromHTML(templateMatch[1], pattern));
|
|
234
|
+
classes.push(...extractFromStringLiterals(templateMatch[1], pattern));
|
|
235
235
|
}
|
|
236
236
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
237
237
|
if (scriptMatch?.[1]) {
|
|
238
|
-
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
238
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1], pattern));
|
|
239
239
|
}
|
|
240
240
|
return [...new Set(classes)];
|
|
241
241
|
}
|
|
242
|
-
function extractFromSvelte(content) {
|
|
242
|
+
function extractFromSvelte(content, pattern) {
|
|
243
243
|
const classes = [];
|
|
244
|
-
classes.push(...extractFromHTML(content));
|
|
244
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
245
245
|
const expressions = extractClassExpressions(content);
|
|
246
246
|
for (const expr of expressions) {
|
|
247
|
-
classes.push(...extractFromStringLiterals(expr));
|
|
247
|
+
classes.push(...extractFromStringLiterals(expr, pattern));
|
|
248
248
|
}
|
|
249
249
|
SVELTE_CLASS_DIRECTIVE_PATTERN.lastIndex = 0;
|
|
250
250
|
let match;
|
|
@@ -255,47 +255,47 @@ function extractFromSvelte(content) {
|
|
|
255
255
|
}
|
|
256
256
|
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
|
|
257
257
|
if (scriptMatch?.[1]) {
|
|
258
|
-
classes.push(...extractFromStringLiterals(scriptMatch[1]));
|
|
258
|
+
classes.push(...extractFromStringLiterals(scriptMatch[1], pattern));
|
|
259
259
|
}
|
|
260
260
|
return [...new Set(classes)];
|
|
261
261
|
}
|
|
262
|
-
function extractFromAstro(content) {
|
|
262
|
+
function extractFromAstro(content, pattern) {
|
|
263
263
|
const classes = [];
|
|
264
|
-
classes.push(...extractFromHTML(content));
|
|
265
|
-
classes.push(...extractFromJSX(content));
|
|
264
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
265
|
+
classes.push(...extractFromJSX(content, pattern));
|
|
266
266
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
267
267
|
if (frontmatterMatch?.[1]) {
|
|
268
|
-
classes.push(...extractFromStringLiterals(frontmatterMatch[1]));
|
|
268
|
+
classes.push(...extractFromStringLiterals(frontmatterMatch[1], pattern));
|
|
269
269
|
}
|
|
270
270
|
return [...new Set(classes)];
|
|
271
271
|
}
|
|
272
|
-
function extractFromStringLiterals(content) {
|
|
272
|
+
function extractFromStringLiterals(content, pattern) {
|
|
273
273
|
const classes = [];
|
|
274
274
|
let match;
|
|
275
275
|
SINGLE_QUOTE_PATTERN.lastIndex = 0;
|
|
276
276
|
while ((match = SINGLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
277
277
|
if (match[1]) {
|
|
278
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
278
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
DOUBLE_QUOTE_PATTERN.lastIndex = 0;
|
|
282
282
|
while ((match = DOUBLE_QUOTE_PATTERN.exec(content)) !== null) {
|
|
283
283
|
if (match[1]) {
|
|
284
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
284
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
TEMPLATE_LITERAL_PATTERN.lastIndex = 0;
|
|
288
288
|
while ((match = TEMPLATE_LITERAL_PATTERN.exec(content)) !== null) {
|
|
289
289
|
if (match[1]) {
|
|
290
|
-
classes.push(...extractUtilityClasses(match[1]));
|
|
290
|
+
classes.push(...extractUtilityClasses(match[1], pattern));
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
return classes;
|
|
294
294
|
}
|
|
295
|
-
function extractFromMDX(content) {
|
|
295
|
+
function extractFromMDX(content, pattern) {
|
|
296
296
|
const classes = [];
|
|
297
|
-
classes.push(...extractFromHTML(content));
|
|
298
|
-
classes.push(...extractFromJSX(content));
|
|
297
|
+
classes.push(...extractFromHTML(content, pattern));
|
|
298
|
+
classes.push(...extractFromJSX(content, pattern));
|
|
299
299
|
return [...new Set(classes)];
|
|
300
300
|
}
|
|
301
301
|
const defaultExtractors = {
|
|
@@ -321,7 +321,7 @@ function getExtension(filePath) {
|
|
|
321
321
|
const parts = filePath.split(".");
|
|
322
322
|
return parts[parts.length - 1]?.toLowerCase() ?? "";
|
|
323
323
|
}
|
|
324
|
-
function extractClasses(content, filePath, customExtractors) {
|
|
324
|
+
function extractClasses(content, filePath, customExtractors, utilityPattern) {
|
|
325
325
|
const classes = [];
|
|
326
326
|
const extension = getExtension(filePath);
|
|
327
327
|
if (customExtractors) {
|
|
@@ -331,12 +331,160 @@ function extractClasses(content, filePath, customExtractors) {
|
|
|
331
331
|
}
|
|
332
332
|
const defaultExtractor = defaultExtractors[extension];
|
|
333
333
|
if (defaultExtractor) {
|
|
334
|
-
classes.push(...defaultExtractor(content));
|
|
334
|
+
classes.push(...defaultExtractor(content, utilityPattern));
|
|
335
335
|
} else {
|
|
336
|
-
classes.push(...extractFromStringLiterals(content));
|
|
336
|
+
classes.push(...extractFromStringLiterals(content, utilityPattern));
|
|
337
337
|
}
|
|
338
338
|
return [...new Set(classes)];
|
|
339
339
|
}
|
|
340
|
+
const Be = ({
|
|
341
|
+
name: e,
|
|
342
|
+
value: t,
|
|
343
|
+
modifiers: r
|
|
344
|
+
}) => `_${[
|
|
345
|
+
...r,
|
|
346
|
+
e,
|
|
347
|
+
...t === "default" ? [] : [t]
|
|
348
|
+
].filter(Boolean).join(":")}`;
|
|
349
|
+
function Ke(e) {
|
|
350
|
+
return `.${e.replace(/[[\].#()%,:/]/g, "\\$&")}`;
|
|
351
|
+
}
|
|
352
|
+
function v(e) {
|
|
353
|
+
if (e instanceof Buffer)
|
|
354
|
+
return Buffer.from(e);
|
|
355
|
+
const t = e.constructor;
|
|
356
|
+
return new t(
|
|
357
|
+
e.buffer.slice(0),
|
|
358
|
+
e.byteOffset,
|
|
359
|
+
e.byteLength / e.BYTES_PER_ELEMENT || 1
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
function ue(e) {
|
|
363
|
+
if (e = e || {}, e.circular)
|
|
364
|
+
return le(e);
|
|
365
|
+
const t = /* @__PURE__ */ new Map();
|
|
366
|
+
if (t.set(Date, (i) => new Date(i)), t.set(
|
|
367
|
+
Map,
|
|
368
|
+
(i, f) => new Map(n(Array.from(i), f))
|
|
369
|
+
), t.set(
|
|
370
|
+
Set,
|
|
371
|
+
(i, f) => new Set(n(Array.from(i), f))
|
|
372
|
+
), e.constructorHandlers)
|
|
373
|
+
for (const i of e.constructorHandlers)
|
|
374
|
+
t.set(i[0], i[1]);
|
|
375
|
+
let r;
|
|
376
|
+
return e.proto ? o : s;
|
|
377
|
+
function n(i, f) {
|
|
378
|
+
const c = Object.keys(i), a = Array.from({ length: c.length });
|
|
379
|
+
for (let l = 0; l < c.length; l++) {
|
|
380
|
+
const u = c[l], d = i[u];
|
|
381
|
+
typeof d != "object" || d === null ? a[u] = d : d.constructor !== Object && (r = t.get(d.constructor)) ? a[u] = r(d, f) : ArrayBuffer.isView(d) ? a[u] = v(d) : a[u] = f(d);
|
|
382
|
+
}
|
|
383
|
+
return a;
|
|
384
|
+
}
|
|
385
|
+
function s(i) {
|
|
386
|
+
if (typeof i != "object" || i === null) return i;
|
|
387
|
+
if (Array.isArray(i)) return n(i, s);
|
|
388
|
+
if (i.constructor !== Object && (r = t.get(i.constructor)))
|
|
389
|
+
return r(i, s);
|
|
390
|
+
const f = {};
|
|
391
|
+
for (const c in i) {
|
|
392
|
+
if (Object.hasOwnProperty.call(i, c) === false) continue;
|
|
393
|
+
const a = i[c];
|
|
394
|
+
typeof a != "object" || a === null ? f[c] = a : a.constructor !== Object && (r = t.get(a.constructor)) ? f[c] = r(a, s) : ArrayBuffer.isView(a) ? f[c] = v(a) : f[c] = s(a);
|
|
395
|
+
}
|
|
396
|
+
return f;
|
|
397
|
+
}
|
|
398
|
+
function o(i) {
|
|
399
|
+
if (typeof i != "object" || i === null) return i;
|
|
400
|
+
if (Array.isArray(i)) return n(i, o);
|
|
401
|
+
if (i.constructor !== Object && (r = t.get(i.constructor)))
|
|
402
|
+
return r(i, o);
|
|
403
|
+
const f = {};
|
|
404
|
+
for (const c in i) {
|
|
405
|
+
const a = i[c];
|
|
406
|
+
typeof a != "object" || a === null ? f[c] = a : a.constructor !== Object && (r = t.get(a.constructor)) ? f[c] = r(a, o) : ArrayBuffer.isView(a) ? f[c] = v(a) : f[c] = o(a);
|
|
407
|
+
}
|
|
408
|
+
return f;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function le(e) {
|
|
412
|
+
const t = [], r = [], n = /* @__PURE__ */ new Map();
|
|
413
|
+
if (n.set(Date, (c) => new Date(c)), n.set(
|
|
414
|
+
Map,
|
|
415
|
+
(c, a) => new Map(o(Array.from(c), a))
|
|
416
|
+
), n.set(
|
|
417
|
+
Set,
|
|
418
|
+
(c, a) => new Set(o(Array.from(c), a))
|
|
419
|
+
), e.constructorHandlers)
|
|
420
|
+
for (const c of e.constructorHandlers)
|
|
421
|
+
n.set(c[0], c[1]);
|
|
422
|
+
let s;
|
|
423
|
+
return e.proto ? f : i;
|
|
424
|
+
function o(c, a) {
|
|
425
|
+
const l = Object.keys(c), u = Array.from({ length: l.length });
|
|
426
|
+
for (let d = 0; d < l.length; d++) {
|
|
427
|
+
const y = l[d], m = c[y];
|
|
428
|
+
if (typeof m != "object" || m === null)
|
|
429
|
+
u[y] = m;
|
|
430
|
+
else if (m.constructor !== Object && (s = n.get(m.constructor)))
|
|
431
|
+
u[y] = s(m, a);
|
|
432
|
+
else if (ArrayBuffer.isView(m))
|
|
433
|
+
u[y] = v(m);
|
|
434
|
+
else {
|
|
435
|
+
const p = t.indexOf(m);
|
|
436
|
+
p !== -1 ? u[y] = r[p] : u[y] = a(m);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return u;
|
|
440
|
+
}
|
|
441
|
+
function i(c) {
|
|
442
|
+
if (typeof c != "object" || c === null) return c;
|
|
443
|
+
if (Array.isArray(c)) return o(c, i);
|
|
444
|
+
if (c.constructor !== Object && (s = n.get(c.constructor)))
|
|
445
|
+
return s(c, i);
|
|
446
|
+
const a = {};
|
|
447
|
+
t.push(c), r.push(a);
|
|
448
|
+
for (const l in c) {
|
|
449
|
+
if (Object.hasOwnProperty.call(c, l) === false) continue;
|
|
450
|
+
const u = c[l];
|
|
451
|
+
if (typeof u != "object" || u === null)
|
|
452
|
+
a[l] = u;
|
|
453
|
+
else if (u.constructor !== Object && (s = n.get(u.constructor)))
|
|
454
|
+
a[l] = s(u, i);
|
|
455
|
+
else if (ArrayBuffer.isView(u))
|
|
456
|
+
a[l] = v(u);
|
|
457
|
+
else {
|
|
458
|
+
const d = t.indexOf(u);
|
|
459
|
+
d !== -1 ? a[l] = r[d] : a[l] = i(u);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return t.pop(), r.pop(), a;
|
|
463
|
+
}
|
|
464
|
+
function f(c) {
|
|
465
|
+
if (typeof c != "object" || c === null) return c;
|
|
466
|
+
if (Array.isArray(c)) return o(c, f);
|
|
467
|
+
if (c.constructor !== Object && (s = n.get(c.constructor)))
|
|
468
|
+
return s(c, f);
|
|
469
|
+
const a = {};
|
|
470
|
+
t.push(c), r.push(a);
|
|
471
|
+
for (const l in c) {
|
|
472
|
+
const u = c[l];
|
|
473
|
+
if (typeof u != "object" || u === null)
|
|
474
|
+
a[l] = u;
|
|
475
|
+
else if (u.constructor !== Object && (s = n.get(u.constructor)))
|
|
476
|
+
a[l] = s(u, f);
|
|
477
|
+
else if (ArrayBuffer.isView(u))
|
|
478
|
+
a[l] = v(u);
|
|
479
|
+
else {
|
|
480
|
+
const d = t.indexOf(u);
|
|
481
|
+
d !== -1 ? a[l] = r[d] : a[l] = f(u);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return t.pop(), r.pop(), a;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
ue();
|
|
340
488
|
function generateValueKey(value, modifiers) {
|
|
341
489
|
if (modifiers.length === 0) {
|
|
342
490
|
return value;
|
|
@@ -351,8 +499,8 @@ function matchUtilities(parsed, root) {
|
|
|
351
499
|
for (const utility of root.utilities) {
|
|
352
500
|
utilityMap.set(utility.name, utility);
|
|
353
501
|
const valueSet = /* @__PURE__ */ new Set();
|
|
354
|
-
for (const
|
|
355
|
-
valueSet.add(generateValueKey(
|
|
502
|
+
for (const v2 of utility.values) {
|
|
503
|
+
valueSet.add(generateValueKey(v2.key, v2.modifiers));
|
|
356
504
|
}
|
|
357
505
|
factoryValueSets.set(utility, valueSet);
|
|
358
506
|
}
|
|
@@ -389,36 +537,28 @@ function matchUtilities(parsed, root) {
|
|
|
389
537
|
}
|
|
390
538
|
return matches;
|
|
391
539
|
}
|
|
392
|
-
function generateUtilitySelector(options) {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
...modifiers,
|
|
396
|
-
name,
|
|
397
|
-
...value === "default" ? [] : [value]
|
|
398
|
-
].filter(Boolean);
|
|
399
|
-
return `._${parts.join("\\:").replace(/[[\].#()%,]/g, "\\$&")}`;
|
|
400
|
-
}
|
|
401
|
-
function classNameFromUtilityOptions(options) {
|
|
402
|
-
const { name, value, modifiers } = options;
|
|
403
|
-
const parts = [
|
|
404
|
-
...modifiers,
|
|
405
|
-
name,
|
|
406
|
-
...value === "default" ? [] : [value]
|
|
407
|
-
].filter(Boolean);
|
|
408
|
-
return `_${parts.join(":")}`;
|
|
540
|
+
function generateUtilitySelector(options, selectorFn) {
|
|
541
|
+
const className = classNameFromUtilityOptions(options, selectorFn);
|
|
542
|
+
return Ke(className);
|
|
409
543
|
}
|
|
410
|
-
function
|
|
544
|
+
function classNameFromUtilityOptions(options, selectorFn) {
|
|
545
|
+
return (selectorFn ?? Be)(options);
|
|
546
|
+
}
|
|
547
|
+
function createUtilityFilter(usedClasses, selectorFn) {
|
|
411
548
|
return (utility) => {
|
|
412
|
-
const className = classNameFromUtilityOptions(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
549
|
+
const className = classNameFromUtilityOptions(
|
|
550
|
+
{
|
|
551
|
+
name: utility.name,
|
|
552
|
+
value: utility.value,
|
|
553
|
+
modifiers: utility.modifiers
|
|
554
|
+
},
|
|
555
|
+
selectorFn
|
|
556
|
+
);
|
|
417
557
|
return usedClasses.has(className);
|
|
418
558
|
};
|
|
419
559
|
}
|
|
420
|
-
function filterUtilities(root, usedClasses) {
|
|
421
|
-
const filter = createUtilityFilter(usedClasses);
|
|
560
|
+
function filterUtilities(root, usedClasses, selectorFn) {
|
|
561
|
+
const filter = createUtilityFilter(usedClasses, selectorFn);
|
|
422
562
|
return root.children.filter((child) => {
|
|
423
563
|
if (child.type !== "utility") {
|
|
424
564
|
return true;
|
|
@@ -524,6 +664,8 @@ function createScanner(config) {
|
|
|
524
664
|
const cache = createCache();
|
|
525
665
|
const cwd = config.cwd ?? process.cwd();
|
|
526
666
|
const customExtractors = config.extractors;
|
|
667
|
+
const utilityPattern = config.utilities?.pattern;
|
|
668
|
+
const utilityParse = config.utilities?.parse ?? parseUtilityClass;
|
|
527
669
|
async function getFilePaths() {
|
|
528
670
|
return fg(config.content, {
|
|
529
671
|
cwd,
|
|
@@ -538,8 +680,13 @@ function createScanner(config) {
|
|
|
538
680
|
if (cached) {
|
|
539
681
|
return cached;
|
|
540
682
|
}
|
|
541
|
-
const classNames = extractClasses(
|
|
542
|
-
|
|
683
|
+
const classNames = extractClasses(
|
|
684
|
+
content,
|
|
685
|
+
filePath,
|
|
686
|
+
customExtractors,
|
|
687
|
+
utilityPattern
|
|
688
|
+
);
|
|
689
|
+
const parsed = classNames.map(utilityParse).filter((p) => p !== null);
|
|
543
690
|
const result = {
|
|
544
691
|
path: filePath,
|
|
545
692
|
classes: new Set(classNames),
|
|
@@ -550,8 +697,13 @@ function createScanner(config) {
|
|
|
550
697
|
return result;
|
|
551
698
|
}
|
|
552
699
|
function scanContent(content, filePath = "inline") {
|
|
553
|
-
const classNames = extractClasses(
|
|
554
|
-
|
|
700
|
+
const classNames = extractClasses(
|
|
701
|
+
content,
|
|
702
|
+
filePath,
|
|
703
|
+
customExtractors,
|
|
704
|
+
utilityPattern
|
|
705
|
+
);
|
|
706
|
+
return classNames.map(utilityParse).filter((p) => p !== null);
|
|
555
707
|
}
|
|
556
708
|
async function scan() {
|
|
557
709
|
const filePaths = await getFilePaths();
|
|
@@ -617,14 +769,26 @@ function createScanner(config) {
|
|
|
617
769
|
invalidate
|
|
618
770
|
};
|
|
619
771
|
}
|
|
620
|
-
function quickScan(content, filePath = "inline.html") {
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
772
|
+
function quickScan(content, filePath = "inline.html", utilities) {
|
|
773
|
+
const parseFn = utilities?.parse ?? parseUtilityClass;
|
|
774
|
+
const classNames = extractClasses(
|
|
775
|
+
content,
|
|
776
|
+
filePath,
|
|
777
|
+
void 0,
|
|
778
|
+
utilities?.pattern
|
|
779
|
+
);
|
|
780
|
+
return classNames.map(parseFn).filter((p) => p !== null);
|
|
781
|
+
}
|
|
782
|
+
function createContentScanner(customExtractors, utilities) {
|
|
783
|
+
const parseFn = utilities?.parse ?? parseUtilityClass;
|
|
625
784
|
return (content, filePath = "inline") => {
|
|
626
|
-
const classNames = extractClasses(
|
|
627
|
-
|
|
785
|
+
const classNames = extractClasses(
|
|
786
|
+
content,
|
|
787
|
+
filePath,
|
|
788
|
+
customExtractors,
|
|
789
|
+
utilities?.pattern
|
|
790
|
+
);
|
|
791
|
+
return classNames.map(parseFn).filter((p) => p !== null);
|
|
628
792
|
};
|
|
629
793
|
}
|
|
630
794
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@styleframe/scanner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"fast-glob": "^3.3.2"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@styleframe/core": "^
|
|
25
|
+
"@styleframe/core": "^3.1.0",
|
|
26
26
|
"@styleframe/license": "^2.0.2"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@styleframe/config-typescript": "^
|
|
30
|
-
"@styleframe/config-vite": "^
|
|
31
|
-
"@styleframe/core": "^
|
|
29
|
+
"@styleframe/config-typescript": "^3.0.0",
|
|
30
|
+
"@styleframe/config-vite": "^3.0.0",
|
|
31
|
+
"@styleframe/core": "^3.1.0",
|
|
32
32
|
"@styleframe/license": "^2.0.2",
|
|
33
33
|
"@vitest/coverage-v8": "^3.2.4",
|
|
34
34
|
"typescript": "^5.8.3",
|