@csszyx/unplugin 0.1.3 → 0.3.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/README.md +1 -1
- package/dist/{chunk-JKM7IRGZ.js → chunk-3J4XUYF5.js} +56 -37
- package/dist/index.cjs +56 -37
- package/dist/index.js +1 -1
- package/dist/vite.cjs +56 -37
- package/dist/vite.js +1 -1
- package/dist/webpack.cjs +56 -37
- package/dist/webpack.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @csszyx/unplugin
|
|
2
2
|
|
|
3
|
-
> Vite, Webpack, and esbuild integration for
|
|
3
|
+
> Vite, Webpack, and esbuild integration for CSSzyx.
|
|
4
4
|
|
|
5
5
|
Build-time plugin that transforms `sz` props into Tailwind classes, generates static CSS, mangles class names in production, and injects hydration scripts for SSR.
|
|
6
6
|
|
|
@@ -160,34 +160,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
160
160
|
if (!result.transformed) {
|
|
161
161
|
continue;
|
|
162
162
|
}
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
while ((match = classPattern.exec(result.code)) !== null) {
|
|
166
|
-
for (const cls of match[1].split(/\s+/).filter(Boolean)) {
|
|
167
|
-
discoveredClasses.add(cls);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const exprPattern = /className=\{/g;
|
|
171
|
-
while ((match = exprPattern.exec(result.code)) !== null) {
|
|
172
|
-
let depth = 1;
|
|
173
|
-
let i = match.index + match[0].length;
|
|
174
|
-
while (i < result.code.length && depth > 0) {
|
|
175
|
-
if (result.code[i] === "{") {
|
|
176
|
-
depth++;
|
|
177
|
-
} else if (result.code[i] === "}") {
|
|
178
|
-
depth--;
|
|
179
|
-
}
|
|
180
|
-
i++;
|
|
181
|
-
}
|
|
182
|
-
const expr = result.code.slice(match.index + match[0].length, i - 1);
|
|
183
|
-
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
184
|
-
let strMatch;
|
|
185
|
-
while ((strMatch = strPattern.exec(expr)) !== null) {
|
|
186
|
-
const str = strMatch[1] || strMatch[2];
|
|
187
|
-
for (const cls of str.split(/\s+/).filter(Boolean)) {
|
|
188
|
-
discoveredClasses.add(cls);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
163
|
+
for (const cls of result.classes) {
|
|
164
|
+
discoveredClasses.add(cls);
|
|
191
165
|
}
|
|
192
166
|
if (result.usesRuntime) {
|
|
193
167
|
const szCallRe = /_sz\(\s*\{/g;
|
|
@@ -260,12 +234,15 @@ function createCsszyxPlugins(options = {}) {
|
|
|
260
234
|
}
|
|
261
235
|
}
|
|
262
236
|
function extractClasses(code) {
|
|
263
|
-
const
|
|
237
|
+
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
238
|
+
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
264
239
|
let match;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
240
|
+
for (const classPattern of [dqPattern, sqPattern]) {
|
|
241
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
242
|
+
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
243
|
+
for (const cls of classes) {
|
|
244
|
+
state.classes.add(cls);
|
|
245
|
+
}
|
|
269
246
|
}
|
|
270
247
|
}
|
|
271
248
|
const exprStart = /className=\{/g;
|
|
@@ -306,14 +283,24 @@ function createCsszyxPlugins(options = {}) {
|
|
|
306
283
|
return classString.split(/\s+/).map((cls) => state.mangleMap[cls] || cls).join(" ");
|
|
307
284
|
}
|
|
308
285
|
function mangleCodeClasses(code) {
|
|
309
|
-
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*
|
|
286
|
+
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g, (match, classes) => {
|
|
287
|
+
const mangled = mangleClassString(classes);
|
|
288
|
+
if (mangled === classes) {
|
|
289
|
+
return match;
|
|
290
|
+
}
|
|
291
|
+
return match.replace(classes, mangled);
|
|
292
|
+
}).replace(/(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g, (match, classes) => {
|
|
310
293
|
const mangled = mangleClassString(classes);
|
|
311
294
|
if (mangled === classes) {
|
|
312
295
|
return match;
|
|
313
296
|
}
|
|
314
297
|
return match.replace(classes, mangled);
|
|
315
298
|
});
|
|
316
|
-
result = result.replace(/className:([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
299
|
+
result = result.replace(/className:(?!["'])([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
300
|
+
const qIdx = expr.indexOf("?");
|
|
301
|
+
if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
|
|
302
|
+
return fullMatch;
|
|
303
|
+
}
|
|
317
304
|
let changed = false;
|
|
318
305
|
const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
319
306
|
const parts = inner.split(/\s+/).filter(Boolean);
|
|
@@ -375,7 +362,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
375
362
|
return null;
|
|
376
363
|
},
|
|
377
364
|
/**
|
|
378
|
-
* Filters files for the pre-transform phase —
|
|
365
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
366
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
379
367
|
* @param id - the file path to check for inclusion
|
|
380
368
|
* @returns true if the file should be transformed, false otherwise
|
|
381
369
|
*/
|
|
@@ -383,19 +371,43 @@ function createCsszyxPlugins(options = {}) {
|
|
|
383
371
|
if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
|
|
384
372
|
return false;
|
|
385
373
|
}
|
|
374
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
386
377
|
return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
|
|
387
378
|
},
|
|
388
379
|
/**
|
|
389
380
|
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
381
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
390
382
|
* @param code - the source code to transform
|
|
391
383
|
* @param id - the file path of the module being transformed
|
|
392
384
|
* @returns transformed code with source map, or null if no changes were made
|
|
393
385
|
*/
|
|
394
386
|
transform(code, id) {
|
|
387
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
388
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
389
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
390
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
391
|
+
if (candidates) {
|
|
392
|
+
const inlineDirective = `@source inline("${candidates}");
|
|
393
|
+
`;
|
|
394
|
+
const transformed2 = code.replace(
|
|
395
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
396
|
+
`$1
|
|
397
|
+
${inlineDirective}`
|
|
398
|
+
);
|
|
399
|
+
if (transformed2 !== code) {
|
|
400
|
+
return { code: transformed2, map: null };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
395
406
|
let transformedCode = code;
|
|
396
407
|
let usesRuntime = false;
|
|
397
408
|
let usesColorVar = false;
|
|
398
409
|
let transformed = false;
|
|
410
|
+
let szClasses;
|
|
399
411
|
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
400
412
|
if (hasSzProp) {
|
|
401
413
|
if (id.endsWith(".vue")) {
|
|
@@ -416,6 +428,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
416
428
|
usesRuntime = result.usesRuntime;
|
|
417
429
|
usesColorVar = result.usesColorVar;
|
|
418
430
|
transformed = result.transformed;
|
|
431
|
+
szClasses = result.classes;
|
|
419
432
|
}
|
|
420
433
|
}
|
|
421
434
|
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
@@ -452,7 +465,13 @@ function createCsszyxPlugins(options = {}) {
|
|
|
452
465
|
}
|
|
453
466
|
}
|
|
454
467
|
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
455
|
-
|
|
468
|
+
if (szClasses !== void 0) {
|
|
469
|
+
for (const cls of szClasses) {
|
|
470
|
+
state.classes.add(cls);
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
extractClasses(transformedCode);
|
|
474
|
+
}
|
|
456
475
|
return { code: transformedCode, map: null };
|
|
457
476
|
}
|
|
458
477
|
return null;
|
package/dist/index.cjs
CHANGED
|
@@ -397,34 +397,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
397
397
|
if (!result.transformed) {
|
|
398
398
|
continue;
|
|
399
399
|
}
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
while ((match = classPattern.exec(result.code)) !== null) {
|
|
403
|
-
for (const cls of match[1].split(/\s+/).filter(Boolean)) {
|
|
404
|
-
discoveredClasses.add(cls);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
const exprPattern = /className=\{/g;
|
|
408
|
-
while ((match = exprPattern.exec(result.code)) !== null) {
|
|
409
|
-
let depth = 1;
|
|
410
|
-
let i = match.index + match[0].length;
|
|
411
|
-
while (i < result.code.length && depth > 0) {
|
|
412
|
-
if (result.code[i] === "{") {
|
|
413
|
-
depth++;
|
|
414
|
-
} else if (result.code[i] === "}") {
|
|
415
|
-
depth--;
|
|
416
|
-
}
|
|
417
|
-
i++;
|
|
418
|
-
}
|
|
419
|
-
const expr = result.code.slice(match.index + match[0].length, i - 1);
|
|
420
|
-
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
421
|
-
let strMatch;
|
|
422
|
-
while ((strMatch = strPattern.exec(expr)) !== null) {
|
|
423
|
-
const str = strMatch[1] || strMatch[2];
|
|
424
|
-
for (const cls of str.split(/\s+/).filter(Boolean)) {
|
|
425
|
-
discoveredClasses.add(cls);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
400
|
+
for (const cls of result.classes) {
|
|
401
|
+
discoveredClasses.add(cls);
|
|
428
402
|
}
|
|
429
403
|
if (result.usesRuntime) {
|
|
430
404
|
const szCallRe = /_sz\(\s*\{/g;
|
|
@@ -497,12 +471,15 @@ function createCsszyxPlugins(options = {}) {
|
|
|
497
471
|
}
|
|
498
472
|
}
|
|
499
473
|
function extractClasses(code) {
|
|
500
|
-
const
|
|
474
|
+
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
475
|
+
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
501
476
|
let match;
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
477
|
+
for (const classPattern of [dqPattern, sqPattern]) {
|
|
478
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
479
|
+
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
480
|
+
for (const cls of classes) {
|
|
481
|
+
state.classes.add(cls);
|
|
482
|
+
}
|
|
506
483
|
}
|
|
507
484
|
}
|
|
508
485
|
const exprStart = /className=\{/g;
|
|
@@ -543,14 +520,24 @@ function createCsszyxPlugins(options = {}) {
|
|
|
543
520
|
return classString.split(/\s+/).map((cls) => state.mangleMap[cls] || cls).join(" ");
|
|
544
521
|
}
|
|
545
522
|
function mangleCodeClasses(code) {
|
|
546
|
-
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*
|
|
523
|
+
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g, (match, classes) => {
|
|
524
|
+
const mangled = mangleClassString(classes);
|
|
525
|
+
if (mangled === classes) {
|
|
526
|
+
return match;
|
|
527
|
+
}
|
|
528
|
+
return match.replace(classes, mangled);
|
|
529
|
+
}).replace(/(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g, (match, classes) => {
|
|
547
530
|
const mangled = mangleClassString(classes);
|
|
548
531
|
if (mangled === classes) {
|
|
549
532
|
return match;
|
|
550
533
|
}
|
|
551
534
|
return match.replace(classes, mangled);
|
|
552
535
|
});
|
|
553
|
-
result = result.replace(/className:([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
536
|
+
result = result.replace(/className:(?!["'])([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
537
|
+
const qIdx = expr.indexOf("?");
|
|
538
|
+
if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
|
|
539
|
+
return fullMatch;
|
|
540
|
+
}
|
|
554
541
|
let changed = false;
|
|
555
542
|
const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
556
543
|
const parts = inner.split(/\s+/).filter(Boolean);
|
|
@@ -612,7 +599,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
612
599
|
return null;
|
|
613
600
|
},
|
|
614
601
|
/**
|
|
615
|
-
* Filters files for the pre-transform phase —
|
|
602
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
603
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
616
604
|
* @param id - the file path to check for inclusion
|
|
617
605
|
* @returns true if the file should be transformed, false otherwise
|
|
618
606
|
*/
|
|
@@ -620,19 +608,43 @@ function createCsszyxPlugins(options = {}) {
|
|
|
620
608
|
if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
|
|
621
609
|
return false;
|
|
622
610
|
}
|
|
611
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
623
614
|
return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
|
|
624
615
|
},
|
|
625
616
|
/**
|
|
626
617
|
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
618
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
627
619
|
* @param code - the source code to transform
|
|
628
620
|
* @param id - the file path of the module being transformed
|
|
629
621
|
* @returns transformed code with source map, or null if no changes were made
|
|
630
622
|
*/
|
|
631
623
|
transform(code, id) {
|
|
624
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
625
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
626
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
627
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
628
|
+
if (candidates) {
|
|
629
|
+
const inlineDirective = `@source inline("${candidates}");
|
|
630
|
+
`;
|
|
631
|
+
const transformed2 = code.replace(
|
|
632
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
633
|
+
`$1
|
|
634
|
+
${inlineDirective}`
|
|
635
|
+
);
|
|
636
|
+
if (transformed2 !== code) {
|
|
637
|
+
return { code: transformed2, map: null };
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
632
643
|
let transformedCode = code;
|
|
633
644
|
let usesRuntime = false;
|
|
634
645
|
let usesColorVar = false;
|
|
635
646
|
let transformed = false;
|
|
647
|
+
let szClasses;
|
|
636
648
|
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
637
649
|
if (hasSzProp) {
|
|
638
650
|
if (id.endsWith(".vue")) {
|
|
@@ -653,6 +665,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
653
665
|
usesRuntime = result.usesRuntime;
|
|
654
666
|
usesColorVar = result.usesColorVar;
|
|
655
667
|
transformed = result.transformed;
|
|
668
|
+
szClasses = result.classes;
|
|
656
669
|
}
|
|
657
670
|
}
|
|
658
671
|
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
@@ -689,7 +702,13 @@ function createCsszyxPlugins(options = {}) {
|
|
|
689
702
|
}
|
|
690
703
|
}
|
|
691
704
|
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
692
|
-
|
|
705
|
+
if (szClasses !== void 0) {
|
|
706
|
+
for (const cls of szClasses) {
|
|
707
|
+
state.classes.add(cls);
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
extractClasses(transformedCode);
|
|
711
|
+
}
|
|
693
712
|
return { code: transformedCode, map: null };
|
|
694
713
|
}
|
|
695
714
|
return null;
|
package/dist/index.js
CHANGED
package/dist/vite.cjs
CHANGED
|
@@ -288,34 +288,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
288
288
|
if (!result.transformed) {
|
|
289
289
|
continue;
|
|
290
290
|
}
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
while ((match = classPattern.exec(result.code)) !== null) {
|
|
294
|
-
for (const cls of match[1].split(/\s+/).filter(Boolean)) {
|
|
295
|
-
discoveredClasses.add(cls);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
const exprPattern = /className=\{/g;
|
|
299
|
-
while ((match = exprPattern.exec(result.code)) !== null) {
|
|
300
|
-
let depth = 1;
|
|
301
|
-
let i = match.index + match[0].length;
|
|
302
|
-
while (i < result.code.length && depth > 0) {
|
|
303
|
-
if (result.code[i] === "{") {
|
|
304
|
-
depth++;
|
|
305
|
-
} else if (result.code[i] === "}") {
|
|
306
|
-
depth--;
|
|
307
|
-
}
|
|
308
|
-
i++;
|
|
309
|
-
}
|
|
310
|
-
const expr = result.code.slice(match.index + match[0].length, i - 1);
|
|
311
|
-
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
312
|
-
let strMatch;
|
|
313
|
-
while ((strMatch = strPattern.exec(expr)) !== null) {
|
|
314
|
-
const str = strMatch[1] || strMatch[2];
|
|
315
|
-
for (const cls of str.split(/\s+/).filter(Boolean)) {
|
|
316
|
-
discoveredClasses.add(cls);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
291
|
+
for (const cls of result.classes) {
|
|
292
|
+
discoveredClasses.add(cls);
|
|
319
293
|
}
|
|
320
294
|
if (result.usesRuntime) {
|
|
321
295
|
const szCallRe = /_sz\(\s*\{/g;
|
|
@@ -388,12 +362,15 @@ function createCsszyxPlugins(options = {}) {
|
|
|
388
362
|
}
|
|
389
363
|
}
|
|
390
364
|
function extractClasses(code) {
|
|
391
|
-
const
|
|
365
|
+
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
366
|
+
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
392
367
|
let match;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
368
|
+
for (const classPattern of [dqPattern, sqPattern]) {
|
|
369
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
370
|
+
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
371
|
+
for (const cls of classes) {
|
|
372
|
+
state.classes.add(cls);
|
|
373
|
+
}
|
|
397
374
|
}
|
|
398
375
|
}
|
|
399
376
|
const exprStart = /className=\{/g;
|
|
@@ -434,14 +411,24 @@ function createCsszyxPlugins(options = {}) {
|
|
|
434
411
|
return classString.split(/\s+/).map((cls) => state.mangleMap[cls] || cls).join(" ");
|
|
435
412
|
}
|
|
436
413
|
function mangleCodeClasses(code) {
|
|
437
|
-
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*
|
|
414
|
+
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g, (match, classes) => {
|
|
415
|
+
const mangled = mangleClassString(classes);
|
|
416
|
+
if (mangled === classes) {
|
|
417
|
+
return match;
|
|
418
|
+
}
|
|
419
|
+
return match.replace(classes, mangled);
|
|
420
|
+
}).replace(/(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g, (match, classes) => {
|
|
438
421
|
const mangled = mangleClassString(classes);
|
|
439
422
|
if (mangled === classes) {
|
|
440
423
|
return match;
|
|
441
424
|
}
|
|
442
425
|
return match.replace(classes, mangled);
|
|
443
426
|
});
|
|
444
|
-
result = result.replace(/className:([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
427
|
+
result = result.replace(/className:(?!["'])([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
428
|
+
const qIdx = expr.indexOf("?");
|
|
429
|
+
if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
|
|
430
|
+
return fullMatch;
|
|
431
|
+
}
|
|
445
432
|
let changed = false;
|
|
446
433
|
const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
447
434
|
const parts = inner.split(/\s+/).filter(Boolean);
|
|
@@ -503,7 +490,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
503
490
|
return null;
|
|
504
491
|
},
|
|
505
492
|
/**
|
|
506
|
-
* Filters files for the pre-transform phase —
|
|
493
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
494
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
507
495
|
* @param id - the file path to check for inclusion
|
|
508
496
|
* @returns true if the file should be transformed, false otherwise
|
|
509
497
|
*/
|
|
@@ -511,19 +499,43 @@ function createCsszyxPlugins(options = {}) {
|
|
|
511
499
|
if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
|
|
512
500
|
return false;
|
|
513
501
|
}
|
|
502
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
514
505
|
return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
|
|
515
506
|
},
|
|
516
507
|
/**
|
|
517
508
|
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
509
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
518
510
|
* @param code - the source code to transform
|
|
519
511
|
* @param id - the file path of the module being transformed
|
|
520
512
|
* @returns transformed code with source map, or null if no changes were made
|
|
521
513
|
*/
|
|
522
514
|
transform(code, id) {
|
|
515
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
516
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
517
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
518
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
519
|
+
if (candidates) {
|
|
520
|
+
const inlineDirective = `@source inline("${candidates}");
|
|
521
|
+
`;
|
|
522
|
+
const transformed2 = code.replace(
|
|
523
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
524
|
+
`$1
|
|
525
|
+
${inlineDirective}`
|
|
526
|
+
);
|
|
527
|
+
if (transformed2 !== code) {
|
|
528
|
+
return { code: transformed2, map: null };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
523
534
|
let transformedCode = code;
|
|
524
535
|
let usesRuntime = false;
|
|
525
536
|
let usesColorVar = false;
|
|
526
537
|
let transformed = false;
|
|
538
|
+
let szClasses;
|
|
527
539
|
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
528
540
|
if (hasSzProp) {
|
|
529
541
|
if (id.endsWith(".vue")) {
|
|
@@ -544,6 +556,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
544
556
|
usesRuntime = result.usesRuntime;
|
|
545
557
|
usesColorVar = result.usesColorVar;
|
|
546
558
|
transformed = result.transformed;
|
|
559
|
+
szClasses = result.classes;
|
|
547
560
|
}
|
|
548
561
|
}
|
|
549
562
|
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
@@ -580,7 +593,13 @@ function createCsszyxPlugins(options = {}) {
|
|
|
580
593
|
}
|
|
581
594
|
}
|
|
582
595
|
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
583
|
-
|
|
596
|
+
if (szClasses !== void 0) {
|
|
597
|
+
for (const cls of szClasses) {
|
|
598
|
+
state.classes.add(cls);
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
extractClasses(transformedCode);
|
|
602
|
+
}
|
|
584
603
|
return { code: transformedCode, map: null };
|
|
585
604
|
}
|
|
586
605
|
return null;
|
package/dist/vite.js
CHANGED
package/dist/webpack.cjs
CHANGED
|
@@ -288,34 +288,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
288
288
|
if (!result.transformed) {
|
|
289
289
|
continue;
|
|
290
290
|
}
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
while ((match = classPattern.exec(result.code)) !== null) {
|
|
294
|
-
for (const cls of match[1].split(/\s+/).filter(Boolean)) {
|
|
295
|
-
discoveredClasses.add(cls);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
const exprPattern = /className=\{/g;
|
|
299
|
-
while ((match = exprPattern.exec(result.code)) !== null) {
|
|
300
|
-
let depth = 1;
|
|
301
|
-
let i = match.index + match[0].length;
|
|
302
|
-
while (i < result.code.length && depth > 0) {
|
|
303
|
-
if (result.code[i] === "{") {
|
|
304
|
-
depth++;
|
|
305
|
-
} else if (result.code[i] === "}") {
|
|
306
|
-
depth--;
|
|
307
|
-
}
|
|
308
|
-
i++;
|
|
309
|
-
}
|
|
310
|
-
const expr = result.code.slice(match.index + match[0].length, i - 1);
|
|
311
|
-
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
312
|
-
let strMatch;
|
|
313
|
-
while ((strMatch = strPattern.exec(expr)) !== null) {
|
|
314
|
-
const str = strMatch[1] || strMatch[2];
|
|
315
|
-
for (const cls of str.split(/\s+/).filter(Boolean)) {
|
|
316
|
-
discoveredClasses.add(cls);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
291
|
+
for (const cls of result.classes) {
|
|
292
|
+
discoveredClasses.add(cls);
|
|
319
293
|
}
|
|
320
294
|
if (result.usesRuntime) {
|
|
321
295
|
const szCallRe = /_sz\(\s*\{/g;
|
|
@@ -388,12 +362,15 @@ function createCsszyxPlugins(options = {}) {
|
|
|
388
362
|
}
|
|
389
363
|
}
|
|
390
364
|
function extractClasses(code) {
|
|
391
|
-
const
|
|
365
|
+
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
366
|
+
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
392
367
|
let match;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
368
|
+
for (const classPattern of [dqPattern, sqPattern]) {
|
|
369
|
+
while ((match = classPattern.exec(code)) !== null) {
|
|
370
|
+
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
371
|
+
for (const cls of classes) {
|
|
372
|
+
state.classes.add(cls);
|
|
373
|
+
}
|
|
397
374
|
}
|
|
398
375
|
}
|
|
399
376
|
const exprStart = /className=\{/g;
|
|
@@ -434,14 +411,24 @@ function createCsszyxPlugins(options = {}) {
|
|
|
434
411
|
return classString.split(/\s+/).map((cls) => state.mangleMap[cls] || cls).join(" ");
|
|
435
412
|
}
|
|
436
413
|
function mangleCodeClasses(code) {
|
|
437
|
-
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*
|
|
414
|
+
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g, (match, classes) => {
|
|
415
|
+
const mangled = mangleClassString(classes);
|
|
416
|
+
if (mangled === classes) {
|
|
417
|
+
return match;
|
|
418
|
+
}
|
|
419
|
+
return match.replace(classes, mangled);
|
|
420
|
+
}).replace(/(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g, (match, classes) => {
|
|
438
421
|
const mangled = mangleClassString(classes);
|
|
439
422
|
if (mangled === classes) {
|
|
440
423
|
return match;
|
|
441
424
|
}
|
|
442
425
|
return match.replace(classes, mangled);
|
|
443
426
|
});
|
|
444
|
-
result = result.replace(/className:([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
427
|
+
result = result.replace(/className:(?!["'])([^,;}\])\n]+)/g, (fullMatch, expr) => {
|
|
428
|
+
const qIdx = expr.indexOf("?");
|
|
429
|
+
if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
|
|
430
|
+
return fullMatch;
|
|
431
|
+
}
|
|
445
432
|
let changed = false;
|
|
446
433
|
const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
447
434
|
const parts = inner.split(/\s+/).filter(Boolean);
|
|
@@ -503,7 +490,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
503
490
|
return null;
|
|
504
491
|
},
|
|
505
492
|
/**
|
|
506
|
-
* Filters files for the pre-transform phase —
|
|
493
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
494
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
507
495
|
* @param id - the file path to check for inclusion
|
|
508
496
|
* @returns true if the file should be transformed, false otherwise
|
|
509
497
|
*/
|
|
@@ -511,19 +499,43 @@ function createCsszyxPlugins(options = {}) {
|
|
|
511
499
|
if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
|
|
512
500
|
return false;
|
|
513
501
|
}
|
|
502
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
514
505
|
return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
|
|
515
506
|
},
|
|
516
507
|
/**
|
|
517
508
|
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
509
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
518
510
|
* @param code - the source code to transform
|
|
519
511
|
* @param id - the file path of the module being transformed
|
|
520
512
|
* @returns transformed code with source map, or null if no changes were made
|
|
521
513
|
*/
|
|
522
514
|
transform(code, id) {
|
|
515
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
516
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
517
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
518
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
519
|
+
if (candidates) {
|
|
520
|
+
const inlineDirective = `@source inline("${candidates}");
|
|
521
|
+
`;
|
|
522
|
+
const transformed2 = code.replace(
|
|
523
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
524
|
+
`$1
|
|
525
|
+
${inlineDirective}`
|
|
526
|
+
);
|
|
527
|
+
if (transformed2 !== code) {
|
|
528
|
+
return { code: transformed2, map: null };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
523
534
|
let transformedCode = code;
|
|
524
535
|
let usesRuntime = false;
|
|
525
536
|
let usesColorVar = false;
|
|
526
537
|
let transformed = false;
|
|
538
|
+
let szClasses;
|
|
527
539
|
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
528
540
|
if (hasSzProp) {
|
|
529
541
|
if (id.endsWith(".vue")) {
|
|
@@ -544,6 +556,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
544
556
|
usesRuntime = result.usesRuntime;
|
|
545
557
|
usesColorVar = result.usesColorVar;
|
|
546
558
|
transformed = result.transformed;
|
|
559
|
+
szClasses = result.classes;
|
|
547
560
|
}
|
|
548
561
|
}
|
|
549
562
|
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
@@ -580,7 +593,13 @@ function createCsszyxPlugins(options = {}) {
|
|
|
580
593
|
}
|
|
581
594
|
}
|
|
582
595
|
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
583
|
-
|
|
596
|
+
if (szClasses !== void 0) {
|
|
597
|
+
for (const cls of szClasses) {
|
|
598
|
+
state.classes.add(cls);
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
extractClasses(transformedCode);
|
|
602
|
+
}
|
|
584
603
|
return { code: transformedCode, map: null };
|
|
585
604
|
}
|
|
586
605
|
return null;
|
package/dist/webpack.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csszyx/unplugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Vite and Webpack integration for csszyx",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"csszyx",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
"postcss": "^8.4.35",
|
|
56
56
|
"postcss-selector-parser": "^6.0.15",
|
|
57
57
|
"unplugin": "^1.10.1",
|
|
58
|
-
"@csszyx/compiler": "0.
|
|
59
|
-
"@csszyx/core": "0.
|
|
60
|
-
"@csszyx/svelte-adapter": "0.0.
|
|
61
|
-
"@csszyx/types": "0.
|
|
62
|
-
"@csszyx/vue-adapter": "0.0.
|
|
58
|
+
"@csszyx/compiler": "0.3.0",
|
|
59
|
+
"@csszyx/core": "0.3.0",
|
|
60
|
+
"@csszyx/svelte-adapter": "0.0.1",
|
|
61
|
+
"@csszyx/types": "0.3.0",
|
|
62
|
+
"@csszyx/vue-adapter": "0.0.1"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/node": "^20.11.0",
|