@csszyx/unplugin 0.6.2 → 0.7.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/{chunk-JGJOUK2R.js → chunk-GXGGTRUA.js} +351 -33
- package/dist/index.cjs +357 -31
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +17 -1
- package/dist/vite.cjs +341 -31
- package/dist/vite.js +1 -1
- package/dist/webpack.cjs +341 -31
- package/dist/webpack.js +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,101 @@ import 'rollup';
|
|
|
7
7
|
import 'unplugin';
|
|
8
8
|
import 'vite';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Direct RSC boundary violation found in a transformed module.
|
|
12
|
+
*/
|
|
13
|
+
interface RSCBoundaryViolation {
|
|
14
|
+
/** Forbidden runtime helper that crossed into an RSC server module. */
|
|
15
|
+
symbol: string;
|
|
16
|
+
/** Server module path where the import was found. */
|
|
17
|
+
path: string;
|
|
18
|
+
/** Import chain used in the fatal build error. */
|
|
19
|
+
importChain: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* RSC module metadata collected during the transform phase.
|
|
23
|
+
*/
|
|
24
|
+
interface RSCModuleRecord {
|
|
25
|
+
/** Normalized absolute module ID. */
|
|
26
|
+
id: string;
|
|
27
|
+
/** True when this module is an RSC server module entry or has `'use server'`. */
|
|
28
|
+
isServer: boolean;
|
|
29
|
+
/** True when this module declares the client boundary. */
|
|
30
|
+
isClient: boolean;
|
|
31
|
+
/** Local modules imported by this file after path resolution. */
|
|
32
|
+
imports: string[];
|
|
33
|
+
/** Forbidden runtime imports found directly in this module. */
|
|
34
|
+
runtimeImports: Array<{
|
|
35
|
+
source: string;
|
|
36
|
+
symbols: string[];
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns true when a module starts with the top-level `'use server'`
|
|
41
|
+
* directive. Comments and blank lines before the directive are allowed, but
|
|
42
|
+
* detection stops at the first real statement.
|
|
43
|
+
*
|
|
44
|
+
* @param code module source
|
|
45
|
+
* @returns true when the module has a top-level `'use server'` directive
|
|
46
|
+
*/
|
|
47
|
+
declare function hasUseServerDirective(code: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Returns true when a module starts with the top-level `'use client'`
|
|
50
|
+
* directive.
|
|
51
|
+
*
|
|
52
|
+
* @param code module source
|
|
53
|
+
* @returns true when the module has a top-level `'use client'` directive
|
|
54
|
+
*/
|
|
55
|
+
declare function hasUseClientDirective(code: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Detects modules that should be treated as RSC server modules by csszyx.
|
|
58
|
+
*
|
|
59
|
+
* @param code module source
|
|
60
|
+
* @param id module ID/path
|
|
61
|
+
* @returns true when the module is server-side for RSC boundary purposes
|
|
62
|
+
*/
|
|
63
|
+
declare function isRSCServerModule(code: string, id: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Finds the first direct forbidden runtime helper import in an RSC server
|
|
66
|
+
* module.
|
|
67
|
+
*
|
|
68
|
+
* @param code module source
|
|
69
|
+
* @param id module ID/path
|
|
70
|
+
* @returns violation details, or null when the module is allowed
|
|
71
|
+
*/
|
|
72
|
+
declare function findRSCBoundaryViolation(code: string, id: string): RSCBoundaryViolation | null;
|
|
73
|
+
/**
|
|
74
|
+
* Builds module metadata for the RSC graph walker.
|
|
75
|
+
*
|
|
76
|
+
* @param code module source
|
|
77
|
+
* @param id module ID/path
|
|
78
|
+
* @returns graph metadata for the module
|
|
79
|
+
*/
|
|
80
|
+
declare function createRSCModuleRecord(code: string, id: string): RSCModuleRecord;
|
|
81
|
+
/**
|
|
82
|
+
* Finds forbidden runtime helper imports reachable from an RSC server module.
|
|
83
|
+
* Traversal stops at `'use client'` modules because they define a separate
|
|
84
|
+
* client module graph.
|
|
85
|
+
*
|
|
86
|
+
* @param records module graph records keyed by normalized module ID
|
|
87
|
+
* @returns first graph violation, or null when the graph is allowed
|
|
88
|
+
*/
|
|
89
|
+
declare function findRSCGraphViolation(records: Map<string, RSCModuleRecord>): RSCBoundaryViolation | null;
|
|
90
|
+
/**
|
|
91
|
+
* Throws the spec-format fatal RSC boundary error for graph-level violations.
|
|
92
|
+
*
|
|
93
|
+
* @param records module graph records keyed by normalized module ID
|
|
94
|
+
*/
|
|
95
|
+
declare function assertNoRSCGraphViolation(records: Map<string, RSCModuleRecord>): void;
|
|
96
|
+
/**
|
|
97
|
+
* Throws the spec-format fatal RSC boundary error when a server module imports
|
|
98
|
+
* a forbidden csszyx runtime helper.
|
|
99
|
+
*
|
|
100
|
+
* @param code module source
|
|
101
|
+
* @param id module ID/path
|
|
102
|
+
*/
|
|
103
|
+
declare function assertNoRSCBoundaryViolation(code: string, id: string): void;
|
|
104
|
+
|
|
10
105
|
/**
|
|
11
106
|
* Theme Scanner — parses Tailwind v4 @theme blocks from CSS files.
|
|
12
107
|
*
|
|
@@ -55,4 +150,4 @@ declare function mergeThemes(themes: ParsedTheme[]): ParsedTheme;
|
|
|
55
150
|
*/
|
|
56
151
|
declare function hasTokens(theme: ParsedTheme): boolean;
|
|
57
152
|
|
|
58
|
-
export { type ParsedTheme, hasTokens, mergeThemes, parseThemeBlocks };
|
|
153
|
+
export { type ParsedTheme, type RSCBoundaryViolation, type RSCModuleRecord, assertNoRSCBoundaryViolation, assertNoRSCGraphViolation, createRSCModuleRecord, findRSCBoundaryViolation, findRSCGraphViolation, hasTokens, hasUseClientDirective, hasUseServerDirective, isRSCServerModule, mergeThemes, parseThemeBlocks };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
assertNoRSCBoundaryViolation,
|
|
3
|
+
assertNoRSCGraphViolation,
|
|
4
|
+
createRSCModuleRecord,
|
|
2
5
|
esbuildPlugin,
|
|
6
|
+
findRSCBoundaryViolation,
|
|
7
|
+
findRSCGraphViolation,
|
|
3
8
|
hasTokens,
|
|
9
|
+
hasUseClientDirective,
|
|
10
|
+
hasUseServerDirective,
|
|
11
|
+
isRSCServerModule,
|
|
4
12
|
mangleCodeClassesSync,
|
|
5
13
|
mergeThemes,
|
|
6
14
|
parseThemeBlocks,
|
|
@@ -8,7 +16,7 @@ import {
|
|
|
8
16
|
unplugin,
|
|
9
17
|
vitePlugin,
|
|
10
18
|
webpackPlugin
|
|
11
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-GXGGTRUA.js";
|
|
12
20
|
import {
|
|
13
21
|
createPostCSSPlugin,
|
|
14
22
|
escapeCSSClassName,
|
|
@@ -17,11 +25,19 @@ import {
|
|
|
17
25
|
unescapeTailwindClass
|
|
18
26
|
} from "./chunk-4M7CPGP7.js";
|
|
19
27
|
export {
|
|
28
|
+
assertNoRSCBoundaryViolation,
|
|
29
|
+
assertNoRSCGraphViolation,
|
|
20
30
|
createPostCSSPlugin,
|
|
31
|
+
createRSCModuleRecord,
|
|
21
32
|
unplugin as default,
|
|
22
33
|
esbuildPlugin,
|
|
23
34
|
escapeCSSClassName,
|
|
35
|
+
findRSCBoundaryViolation,
|
|
36
|
+
findRSCGraphViolation,
|
|
24
37
|
hasTokens,
|
|
38
|
+
hasUseClientDirective,
|
|
39
|
+
hasUseServerDirective,
|
|
40
|
+
isRSCServerModule,
|
|
25
41
|
mangleCSS,
|
|
26
42
|
mangleCSSSync,
|
|
27
43
|
mangleCodeClassesSync,
|
package/dist/vite.cjs
CHANGED
|
@@ -35,8 +35,8 @@ __export(vite_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(vite_exports);
|
|
36
36
|
|
|
37
37
|
// src/unplugin.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
38
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
39
|
+
var path2 = __toESM(require("path"), 1);
|
|
40
40
|
var import_compiler = require("@csszyx/compiler");
|
|
41
41
|
var import_core = require("@csszyx/core");
|
|
42
42
|
var import_svelte_adapter = require("@csszyx/svelte-adapter");
|
|
@@ -200,7 +200,7 @@ function injectHydrationData(html, mangleMap, checksum, options = {}) {
|
|
|
200
200
|
function transformIndexHtml(html, mangleMap, checksum, options = {}) {
|
|
201
201
|
return injectHydrationData(html, mangleMap, checksum, options);
|
|
202
202
|
}
|
|
203
|
-
function buildRecoveryManifest(tokens, options
|
|
203
|
+
function buildRecoveryManifest(tokens, options) {
|
|
204
204
|
const stripped = options.production === true;
|
|
205
205
|
const strippedDevOnlyPaths = [];
|
|
206
206
|
const sorted = {};
|
|
@@ -221,7 +221,7 @@ function buildRecoveryManifest(tokens, options = {}) {
|
|
|
221
221
|
const checksum = fullChecksum.substring(0, 16);
|
|
222
222
|
const buildId = `${Date.now().toString(36)}-${fullChecksum.substring(0, 6)}`;
|
|
223
223
|
return {
|
|
224
|
-
manifest: { buildId, checksum, tokens: sorted },
|
|
224
|
+
manifest: { buildId, checksum, mangleChecksum: options.mangleChecksum, tokens: sorted },
|
|
225
225
|
strippedDevOnlyPaths
|
|
226
226
|
};
|
|
227
227
|
}
|
|
@@ -241,6 +241,303 @@ function injectRecoveryManifest(html, manifest) {
|
|
|
241
241
|
return html + scriptTag;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
// src/rsc-boundary.ts
|
|
245
|
+
var fs = __toESM(require("fs"), 1);
|
|
246
|
+
var path = __toESM(require("path"), 1);
|
|
247
|
+
var SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
|
|
248
|
+
var CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
|
|
249
|
+
var RUNTIME_MODULES = /* @__PURE__ */ new Set([
|
|
250
|
+
"@csszyx/runtime",
|
|
251
|
+
"@csszyx/runtime/lite",
|
|
252
|
+
"csszyx",
|
|
253
|
+
"csszyx/lite"
|
|
254
|
+
]);
|
|
255
|
+
var FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
256
|
+
"_sz",
|
|
257
|
+
"_sz2",
|
|
258
|
+
"_sz3",
|
|
259
|
+
"_szIf",
|
|
260
|
+
"_szMerge",
|
|
261
|
+
"_szSwitch",
|
|
262
|
+
"__csszyx_runtime__"
|
|
263
|
+
]);
|
|
264
|
+
function hasUseServerDirective(code) {
|
|
265
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
266
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
function hasUseClientDirective(code) {
|
|
276
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
277
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function isRSCServerModule(code, id) {
|
|
287
|
+
if (hasUseServerDirective(code)) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
if (hasUseClientDirective(code)) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
return isNextAppRouterEntry(id);
|
|
294
|
+
}
|
|
295
|
+
function findRSCBoundaryViolation(code, id) {
|
|
296
|
+
if (!isRSCServerModule(code, id)) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
for (const imported of findRuntimeImports(code)) {
|
|
300
|
+
for (const symbol of imported.symbols) {
|
|
301
|
+
if (FORBIDDEN_SYMBOLS.has(symbol)) {
|
|
302
|
+
return {
|
|
303
|
+
symbol,
|
|
304
|
+
path: id,
|
|
305
|
+
importChain: [id, imported.source]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
function createRSCModuleRecord(code, id) {
|
|
313
|
+
const normalized = normalizeModuleId(id);
|
|
314
|
+
return {
|
|
315
|
+
id: normalized,
|
|
316
|
+
isServer: isRSCServerModule(code, normalized),
|
|
317
|
+
isClient: hasUseClientDirective(code),
|
|
318
|
+
imports: findLocalImportSources(code).map((source) => resolveLocalModule(normalized, source)).filter((resolved) => resolved !== null),
|
|
319
|
+
runtimeImports: findRuntimeImports(code).filter((imported) => imported.symbols.some((symbol) => FORBIDDEN_SYMBOLS.has(symbol)))
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function findRSCGraphViolation(records) {
|
|
323
|
+
for (const root of records.values()) {
|
|
324
|
+
if (!root.isServer) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const violation = walkRSCGraph(root, records, [root.id], /* @__PURE__ */ new Set([root.id]));
|
|
328
|
+
if (violation) {
|
|
329
|
+
return {
|
|
330
|
+
symbol: violation.symbol,
|
|
331
|
+
path: root.id,
|
|
332
|
+
importChain: violation.importChain
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
function assertNoRSCGraphViolation(records) {
|
|
339
|
+
const violation = findRSCGraphViolation(records);
|
|
340
|
+
if (!violation) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
throw new Error(formatRSCViolation(violation));
|
|
344
|
+
}
|
|
345
|
+
function isNextAppRouterEntry(id) {
|
|
346
|
+
const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
|
|
347
|
+
return /(^|\/)app\/.*\/?(?:page|layout|template|loading|error|not-found|global-error|default|route)\.[cm]?[tj]sx?$/.test(clean);
|
|
348
|
+
}
|
|
349
|
+
function assertNoRSCBoundaryViolation(code, id) {
|
|
350
|
+
const violation = findRSCBoundaryViolation(code, id);
|
|
351
|
+
if (!violation) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
throw new Error(formatRSCViolation(violation));
|
|
355
|
+
}
|
|
356
|
+
function formatRSCViolation(violation) {
|
|
357
|
+
return `csszyxRSCViolation: ${violation.symbol} imported in Server Component ${violation.path}
|
|
358
|
+
Import chain: ${violation.importChain.join(" -> ")}`;
|
|
359
|
+
}
|
|
360
|
+
function walkRSCGraph(current, records, chain, seen) {
|
|
361
|
+
if (current.isClient) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const runtime = current.runtimeImports[0];
|
|
365
|
+
const symbol = runtime?.symbols.find((s) => FORBIDDEN_SYMBOLS.has(s));
|
|
366
|
+
if (runtime && symbol) {
|
|
367
|
+
return {
|
|
368
|
+
symbol,
|
|
369
|
+
path: chain[0] ?? current.id,
|
|
370
|
+
importChain: [...chain, runtime.source]
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
for (const importedId of current.imports) {
|
|
374
|
+
if (seen.has(importedId)) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const next = records.get(importedId);
|
|
378
|
+
if (!next) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
seen.add(importedId);
|
|
382
|
+
const violation = walkRSCGraph(next, records, [...chain, importedId], seen);
|
|
383
|
+
if (violation) {
|
|
384
|
+
return violation;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
function readDirectivePrologue(code) {
|
|
390
|
+
const out = [];
|
|
391
|
+
let i = code.charCodeAt(0) === 65279 ? 1 : 0;
|
|
392
|
+
while (i < code.length) {
|
|
393
|
+
i = skipWhitespaceAndComments(code, i);
|
|
394
|
+
const quote = code[i];
|
|
395
|
+
if (quote !== '"' && quote !== "'") {
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
let j = i + 1;
|
|
399
|
+
let escaped = false;
|
|
400
|
+
while (j < code.length) {
|
|
401
|
+
const ch = code[j];
|
|
402
|
+
if (escaped) {
|
|
403
|
+
escaped = false;
|
|
404
|
+
} else if (ch === "\\") {
|
|
405
|
+
escaped = true;
|
|
406
|
+
} else if (ch === quote) {
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
j++;
|
|
410
|
+
}
|
|
411
|
+
if (j >= code.length) {
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
let end = j + 1;
|
|
415
|
+
while (end < code.length && /[ \t\r\n]/.test(code[end])) {
|
|
416
|
+
end++;
|
|
417
|
+
}
|
|
418
|
+
if (code[end] === ";") {
|
|
419
|
+
end++;
|
|
420
|
+
}
|
|
421
|
+
out.push(code.slice(i, end).trim());
|
|
422
|
+
i = end;
|
|
423
|
+
}
|
|
424
|
+
return out;
|
|
425
|
+
}
|
|
426
|
+
function skipWhitespaceAndComments(code, start) {
|
|
427
|
+
let i = start;
|
|
428
|
+
while (i < code.length) {
|
|
429
|
+
while (i < code.length && /\s/.test(code[i])) {
|
|
430
|
+
i++;
|
|
431
|
+
}
|
|
432
|
+
if (code.startsWith("//", i)) {
|
|
433
|
+
const next = code.indexOf("\n", i + 2);
|
|
434
|
+
i = next === -1 ? code.length : next + 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (code.startsWith("/*", i)) {
|
|
438
|
+
const next = code.indexOf("*/", i + 2);
|
|
439
|
+
i = next === -1 ? code.length : next + 2;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
return i;
|
|
445
|
+
}
|
|
446
|
+
function findRuntimeImports(code) {
|
|
447
|
+
const imports = [];
|
|
448
|
+
const staticImportRe = /import\s+(?!type\b)([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
449
|
+
const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
|
|
450
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
451
|
+
let match;
|
|
452
|
+
while ((match = staticImportRe.exec(code)) !== null) {
|
|
453
|
+
const clause = match[1];
|
|
454
|
+
const source = match[2];
|
|
455
|
+
if (!RUNTIME_MODULES.has(source)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
imports.push({ source, symbols: readImportedSymbols(clause) });
|
|
459
|
+
}
|
|
460
|
+
while ((match = sideEffectImportRe.exec(code)) !== null) {
|
|
461
|
+
const source = match[1];
|
|
462
|
+
if (RUNTIME_MODULES.has(source)) {
|
|
463
|
+
imports.push({ source, symbols: [] });
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
while ((match = dynamicImportRe.exec(code)) !== null) {
|
|
467
|
+
const source = match[1];
|
|
468
|
+
if (RUNTIME_MODULES.has(source)) {
|
|
469
|
+
imports.push({ source, symbols: Array.from(FORBIDDEN_SYMBOLS) });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return imports;
|
|
473
|
+
}
|
|
474
|
+
function findLocalImportSources(code) {
|
|
475
|
+
const out = [];
|
|
476
|
+
const staticImportRe = /import\s+(?!type\b)(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
477
|
+
const exportFromRe = /export\s+(?!type\b)(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
478
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
479
|
+
let match;
|
|
480
|
+
for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
|
|
481
|
+
while ((match = re.exec(code)) !== null) {
|
|
482
|
+
const source = match[1];
|
|
483
|
+
if (source.startsWith(".") || source.startsWith("/")) {
|
|
484
|
+
out.push(source);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return out;
|
|
489
|
+
}
|
|
490
|
+
function normalizeModuleId(id) {
|
|
491
|
+
const clean = id.split("?")[0] ?? id;
|
|
492
|
+
try {
|
|
493
|
+
return fs.realpathSync.native(clean).replace(/\\/g, "/");
|
|
494
|
+
} catch {
|
|
495
|
+
return path.resolve(clean).replace(/\\/g, "/");
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function resolveLocalModule(importer, source) {
|
|
499
|
+
const base = source.startsWith("/") ? source : path.resolve(path.dirname(importer), source);
|
|
500
|
+
const candidates = [
|
|
501
|
+
base,
|
|
502
|
+
`${base}.tsx`,
|
|
503
|
+
`${base}.ts`,
|
|
504
|
+
`${base}.jsx`,
|
|
505
|
+
`${base}.js`,
|
|
506
|
+
`${base}.mjs`,
|
|
507
|
+
`${base}.cjs`,
|
|
508
|
+
path.join(base, "index.tsx"),
|
|
509
|
+
path.join(base, "index.ts"),
|
|
510
|
+
path.join(base, "index.jsx"),
|
|
511
|
+
path.join(base, "index.js")
|
|
512
|
+
];
|
|
513
|
+
for (const candidate of candidates) {
|
|
514
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
515
|
+
return normalizeModuleId(candidate);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
function readImportedSymbols(clause) {
|
|
521
|
+
const symbols = [];
|
|
522
|
+
const named = clause.match(/\{([\s\S]*?)\}/);
|
|
523
|
+
if (named) {
|
|
524
|
+
for (const part of named[1].split(",")) {
|
|
525
|
+
const trimmed = part.trim();
|
|
526
|
+
if (!trimmed || trimmed.startsWith("type ")) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const sourceName = trimmed.replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
|
|
530
|
+
if (sourceName) {
|
|
531
|
+
symbols.push(sourceName);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (/\*\s+as\s+\w+/.test(clause)) {
|
|
536
|
+
symbols.push(...FORBIDDEN_SYMBOLS);
|
|
537
|
+
}
|
|
538
|
+
return symbols;
|
|
539
|
+
}
|
|
540
|
+
|
|
244
541
|
// src/theme-scanner.ts
|
|
245
542
|
var EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
|
|
246
543
|
function stripLayerWrappers(css) {
|
|
@@ -489,8 +786,8 @@ function runThemeScan(rootDir, scanCss) {
|
|
|
489
786
|
const patterns = Array.isArray(scanCss) ? scanCss : [scanCss];
|
|
490
787
|
const sourceFiles = [];
|
|
491
788
|
for (const pattern of patterns) {
|
|
492
|
-
const resolved =
|
|
493
|
-
if (
|
|
789
|
+
const resolved = path2.isAbsolute(pattern) ? pattern : path2.join(rootDir, pattern);
|
|
790
|
+
if (fs2.existsSync(resolved)) {
|
|
494
791
|
sourceFiles.push(resolved);
|
|
495
792
|
}
|
|
496
793
|
}
|
|
@@ -499,20 +796,20 @@ function runThemeScan(rootDir, scanCss) {
|
|
|
499
796
|
}
|
|
500
797
|
const themes = sourceFiles.map((f) => {
|
|
501
798
|
try {
|
|
502
|
-
return parseThemeBlocks(
|
|
799
|
+
return parseThemeBlocks(fs2.readFileSync(f, "utf-8"));
|
|
503
800
|
} catch {
|
|
504
801
|
return null;
|
|
505
802
|
}
|
|
506
803
|
}).filter((t) => t !== null);
|
|
507
804
|
const merged = mergeThemes(themes);
|
|
508
|
-
const outputPath =
|
|
805
|
+
const outputPath = path2.join(rootDir, ".csszyx", "theme.d.ts");
|
|
509
806
|
writeThemeDts({ outputPath, theme: merged, sourceFiles });
|
|
510
807
|
if (!_hasWarnedTsConfig) {
|
|
511
808
|
_hasWarnedTsConfig = true;
|
|
512
809
|
try {
|
|
513
810
|
const checkFile = (cfgPath) => {
|
|
514
|
-
if (
|
|
515
|
-
const content =
|
|
811
|
+
if (fs2.existsSync(cfgPath)) {
|
|
812
|
+
const content = fs2.readFileSync(cfgPath, "utf-8");
|
|
516
813
|
if (!content.includes(".csszyx")) {
|
|
517
814
|
console.warn(`
|
|
518
815
|
\x1B[33m\u26A0\uFE0F CSSzyx: Theme Auto-Scan enabled, but TypeScript isn't configured. Run "npx @csszyx/cli init" to fix.\x1B[0m
|
|
@@ -522,8 +819,8 @@ function runThemeScan(rootDir, scanCss) {
|
|
|
522
819
|
}
|
|
523
820
|
return false;
|
|
524
821
|
};
|
|
525
|
-
if (!checkFile(
|
|
526
|
-
checkFile(
|
|
822
|
+
if (!checkFile(path2.join(rootDir, "tsconfig.json"))) {
|
|
823
|
+
checkFile(path2.join(rootDir, "tsconfig.app.json"));
|
|
527
824
|
}
|
|
528
825
|
} catch {
|
|
529
826
|
}
|
|
@@ -710,7 +1007,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
710
1007
|
checksum: "",
|
|
711
1008
|
finalized: false,
|
|
712
1009
|
rootDir: process.cwd(),
|
|
713
|
-
recoveryTokens: /* @__PURE__ */ new Map()
|
|
1010
|
+
recoveryTokens: /* @__PURE__ */ new Map(),
|
|
1011
|
+
rscModules: /* @__PURE__ */ new Map()
|
|
714
1012
|
};
|
|
715
1013
|
const SAFELIST_FILENAME = "csszyx-classes.html";
|
|
716
1014
|
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
|
|
@@ -719,16 +1017,16 @@ function createCsszyxPlugins(options = {}) {
|
|
|
719
1017
|
if (classes.size === 0) {
|
|
720
1018
|
return;
|
|
721
1019
|
}
|
|
722
|
-
const safelistPath =
|
|
1020
|
+
const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME);
|
|
723
1021
|
const classList = Array.from(classes).join(" ");
|
|
724
1022
|
const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
|
|
725
1023
|
<!-- Tailwind CSS scans this file for class name detection -->
|
|
726
1024
|
<div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
|
|
727
1025
|
`;
|
|
728
1026
|
try {
|
|
729
|
-
const existing =
|
|
1027
|
+
const existing = fs2.existsSync(safelistPath) ? fs2.readFileSync(safelistPath, "utf-8") : "";
|
|
730
1028
|
if (existing !== content) {
|
|
731
|
-
|
|
1029
|
+
fs2.writeFileSync(safelistPath, content);
|
|
732
1030
|
}
|
|
733
1031
|
} catch {
|
|
734
1032
|
}
|
|
@@ -739,19 +1037,19 @@ function createCsszyxPlugins(options = {}) {
|
|
|
739
1037
|
function scanDir(dir) {
|
|
740
1038
|
let entries;
|
|
741
1039
|
try {
|
|
742
|
-
entries =
|
|
1040
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
743
1041
|
} catch {
|
|
744
1042
|
return;
|
|
745
1043
|
}
|
|
746
1044
|
for (const entry of entries) {
|
|
747
1045
|
if (entry.isDirectory()) {
|
|
748
1046
|
if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
749
|
-
scanDir(
|
|
1047
|
+
scanDir(path2.join(dir, entry.name));
|
|
750
1048
|
}
|
|
751
|
-
} else if (SOURCE_EXTENSIONS.has(
|
|
752
|
-
const filePath =
|
|
1049
|
+
} else if (SOURCE_EXTENSIONS.has(path2.extname(entry.name))) {
|
|
1050
|
+
const filePath = path2.join(dir, entry.name);
|
|
753
1051
|
try {
|
|
754
|
-
const content =
|
|
1052
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
755
1053
|
if (!content.includes("sz=") && !content.includes("sz:")) {
|
|
756
1054
|
continue;
|
|
757
1055
|
}
|
|
@@ -943,14 +1241,17 @@ function createCsszyxPlugins(options = {}) {
|
|
|
943
1241
|
* @returns transformed code with source map, or null if no changes were made
|
|
944
1242
|
*/
|
|
945
1243
|
transform(code, id) {
|
|
1244
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1245
|
+
assertNoRSCBoundaryViolation(code, id);
|
|
1246
|
+
}
|
|
946
1247
|
if (/\.css(\?.*)?$/.test(id)) {
|
|
947
1248
|
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
948
1249
|
if (hasTailwindImport && state.classes.size > 0) {
|
|
949
1250
|
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
950
1251
|
if (candidates) {
|
|
951
|
-
const safelistPath =
|
|
952
|
-
const cssDir =
|
|
953
|
-
let relPath =
|
|
1252
|
+
const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
|
|
1253
|
+
const cssDir = path2.dirname(id).replace(/\\/g, "/");
|
|
1254
|
+
let relPath = path2.posix.relative(cssDir, safelistPath);
|
|
954
1255
|
if (!relPath.startsWith(".")) {
|
|
955
1256
|
relPath = "./" + relPath;
|
|
956
1257
|
}
|
|
@@ -1054,6 +1355,11 @@ ${sourceDirective}`
|
|
|
1054
1355
|
transformed = true;
|
|
1055
1356
|
}
|
|
1056
1357
|
}
|
|
1358
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1359
|
+
assertNoRSCBoundaryViolation(transformedCode, id);
|
|
1360
|
+
const record = createRSCModuleRecord(transformedCode, id);
|
|
1361
|
+
state.rscModules.set(record.id, record);
|
|
1362
|
+
}
|
|
1057
1363
|
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
1058
1364
|
if (szClasses !== void 0) {
|
|
1059
1365
|
for (const cls of szClasses) {
|
|
@@ -1069,6 +1375,7 @@ ${sourceDirective}`
|
|
|
1069
1375
|
/** Finalizes the mangle map after all source modules have been processed. */
|
|
1070
1376
|
buildEnd() {
|
|
1071
1377
|
finalizeMangleMap();
|
|
1378
|
+
assertNoRSCGraphViolation(state.rscModules);
|
|
1072
1379
|
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
1073
1380
|
globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
|
|
1074
1381
|
}
|
|
@@ -1091,8 +1398,8 @@ ${sourceDirective}`
|
|
|
1091
1398
|
compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
|
|
1092
1399
|
const root = compiler.context || process.cwd();
|
|
1093
1400
|
for (const pattern of patterns) {
|
|
1094
|
-
const resolved =
|
|
1095
|
-
if (
|
|
1401
|
+
const resolved = path2.isAbsolute(pattern) ? pattern : path2.join(root, pattern);
|
|
1402
|
+
if (fs2.existsSync(resolved)) {
|
|
1096
1403
|
compilation.fileDependencies.add(resolved);
|
|
1097
1404
|
}
|
|
1098
1405
|
}
|
|
@@ -1122,14 +1429,14 @@ ${sourceDirective}`
|
|
|
1122
1429
|
const patterns = Array.isArray(scanCss) ? scanCss : [scanCss];
|
|
1123
1430
|
const root = ctx.server.config.root || process.cwd();
|
|
1124
1431
|
const isWatched = patterns.some((p) => {
|
|
1125
|
-
const resolved =
|
|
1432
|
+
const resolved = path2.isAbsolute(p) ? p : path2.join(root, p);
|
|
1126
1433
|
return ctx.file === resolved;
|
|
1127
1434
|
});
|
|
1128
1435
|
if (isWatched) {
|
|
1129
1436
|
runThemeScan(root, scanCss);
|
|
1130
1437
|
}
|
|
1131
1438
|
}
|
|
1132
|
-
if (!SOURCE_EXTENSIONS.has(
|
|
1439
|
+
if (!SOURCE_EXTENSIONS.has(path2.extname(ctx.file))) {
|
|
1133
1440
|
return;
|
|
1134
1441
|
}
|
|
1135
1442
|
if (ctx.file.includes("node_modules")) {
|
|
@@ -1137,7 +1444,7 @@ ${sourceDirective}`
|
|
|
1137
1444
|
}
|
|
1138
1445
|
let fileContent, result;
|
|
1139
1446
|
try {
|
|
1140
|
-
fileContent =
|
|
1447
|
+
fileContent = fs2.readFileSync(ctx.file, "utf-8");
|
|
1141
1448
|
} catch {
|
|
1142
1449
|
return;
|
|
1143
1450
|
}
|
|
@@ -1161,7 +1468,7 @@ ${sourceDirective}`
|
|
|
1161
1468
|
}
|
|
1162
1469
|
if (state.classes.size > sizeBefore) {
|
|
1163
1470
|
writeSafelistFile(state.classes);
|
|
1164
|
-
const safelistPath =
|
|
1471
|
+
const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME);
|
|
1165
1472
|
ctx.server.watcher.emit("change", safelistPath);
|
|
1166
1473
|
}
|
|
1167
1474
|
},
|
|
@@ -1183,7 +1490,10 @@ ${sourceDirective}`
|
|
|
1183
1490
|
const isProduction = process.env.NODE_ENV === "production";
|
|
1184
1491
|
const { manifest, strippedDevOnlyPaths } = buildRecoveryManifest(
|
|
1185
1492
|
state.recoveryTokens,
|
|
1186
|
-
{
|
|
1493
|
+
{
|
|
1494
|
+
production: isProduction,
|
|
1495
|
+
mangleChecksum: state.checksum
|
|
1496
|
+
}
|
|
1187
1497
|
);
|
|
1188
1498
|
if (strippedDevOnlyPaths.length > 0) {
|
|
1189
1499
|
console.warn(
|