@csszyx/unplugin 0.8.0 → 0.9.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 +16 -7
- package/dist/css-mangler.cjs +4 -4
- package/dist/css-mangler.mjs +4 -4
- package/dist/index.cjs +6 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.mts +10 -1
- package/dist/index.mjs +5 -1
- package/dist/shared/{unplugin.DCv0RtVZ.mjs → unplugin.BEOG6ePC.mjs} +486 -49
- package/dist/shared/{unplugin.BNsv2szs.cjs → unplugin.CL0F6RZa.cjs} +486 -47
- package/dist/vite.cjs +5 -1
- package/dist/vite.mjs +5 -1
- package/dist/webpack.cjs +5 -1
- package/dist/webpack.mjs +5 -1
- package/package.json +6 -6
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import * as path from 'node:path';
|
|
4
5
|
import { dirname } from 'node:path';
|
|
5
|
-
import {
|
|
6
|
+
import { performance } from 'node:perf_hooks';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transform } from '@csszyx/compiler';
|
|
6
9
|
import { encode, compute_mangle_checksum } from '@csszyx/core';
|
|
7
10
|
import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
|
|
11
|
+
import { DEFAULT_BUILD_CONFIG } from '@csszyx/types';
|
|
8
12
|
import { preprocess } from '@csszyx/vue-adapter';
|
|
9
13
|
import { createUnplugin } from 'unplugin';
|
|
10
14
|
import { mangleCSSSync } from '../css-mangler.mjs';
|
|
@@ -12,12 +16,16 @@ import { createHash } from 'node:crypto';
|
|
|
12
16
|
|
|
13
17
|
const SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
|
|
14
18
|
const CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
|
|
15
|
-
const
|
|
19
|
+
const RUNTIME_HELPER_MODULES = /* @__PURE__ */ new Set([
|
|
16
20
|
"@csszyx/runtime",
|
|
17
21
|
"@csszyx/runtime/lite",
|
|
18
22
|
"csszyx",
|
|
19
23
|
"csszyx/lite"
|
|
20
24
|
]);
|
|
25
|
+
const CLIENT_RUNTIME_MODULES = /* @__PURE__ */ new Set(["csszyx/browser"]);
|
|
26
|
+
const CLIENT_RUNTIME_MODULE_ROOTS = ["@csszyx/dynamic", "csszyx/dynamic"];
|
|
27
|
+
const normalizedModuleIdCache = /* @__PURE__ */ new Map();
|
|
28
|
+
const resolvedLocalModuleCache = /* @__PURE__ */ new Map();
|
|
21
29
|
const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
22
30
|
"_sz",
|
|
23
31
|
"_sz2",
|
|
@@ -87,6 +95,20 @@ function createRSCModuleRecord(code, id) {
|
|
|
87
95
|
)
|
|
88
96
|
};
|
|
89
97
|
}
|
|
98
|
+
function deleteRSCModuleRecord(records, id) {
|
|
99
|
+
const normalized = normalizeModuleId(id);
|
|
100
|
+
const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
|
|
101
|
+
const resolved = path.resolve(clean).replace(/\\/g, "/");
|
|
102
|
+
pruneRSCModulePathCaches(/* @__PURE__ */ new Set([normalized, resolved, clean]));
|
|
103
|
+
let deleted = records.delete(normalized);
|
|
104
|
+
if (resolved !== normalized) {
|
|
105
|
+
deleted = records.delete(resolved) || deleted;
|
|
106
|
+
}
|
|
107
|
+
if (clean !== normalized && clean !== resolved) {
|
|
108
|
+
deleted = records.delete(clean) || deleted;
|
|
109
|
+
}
|
|
110
|
+
return deleted;
|
|
111
|
+
}
|
|
90
112
|
function findRSCGraphViolation(records) {
|
|
91
113
|
for (const root of records.values()) {
|
|
92
114
|
if (!root.isServer) {
|
|
@@ -110,11 +132,26 @@ function assertNoRSCGraphViolation(records) {
|
|
|
110
132
|
}
|
|
111
133
|
throw new Error(formatRSCViolation(violation));
|
|
112
134
|
}
|
|
135
|
+
const APP_ROUTER_ENTRIES = /* @__PURE__ */ new Set([
|
|
136
|
+
"page",
|
|
137
|
+
"layout",
|
|
138
|
+
"template",
|
|
139
|
+
"loading",
|
|
140
|
+
"error",
|
|
141
|
+
"not-found",
|
|
142
|
+
"global-error",
|
|
143
|
+
"default",
|
|
144
|
+
"route"
|
|
145
|
+
]);
|
|
113
146
|
function isNextAppRouterEntry(id) {
|
|
114
147
|
const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
);
|
|
148
|
+
if (!clean.includes("/app/") && !clean.startsWith("app/")) return false;
|
|
149
|
+
const basename = clean.split("/").pop() ?? "";
|
|
150
|
+
const dotIdx = basename.indexOf(".");
|
|
151
|
+
if (dotIdx === -1) return false;
|
|
152
|
+
const stem = basename.slice(0, dotIdx);
|
|
153
|
+
const ext = basename.slice(dotIdx + 1);
|
|
154
|
+
return APP_ROUTER_ENTRIES.has(stem) && /^[cm]?[tj]sx?$/.test(ext);
|
|
118
155
|
}
|
|
119
156
|
function assertNoRSCBoundaryViolation(code, id) {
|
|
120
157
|
const violation = findRSCBoundaryViolation(code, id);
|
|
@@ -215,35 +252,51 @@ function skipWhitespaceAndComments(code, start) {
|
|
|
215
252
|
}
|
|
216
253
|
function findRuntimeImports(code) {
|
|
217
254
|
const imports = [];
|
|
218
|
-
const
|
|
255
|
+
const scanCode = stripCommentsForImportScan(code);
|
|
256
|
+
const staticImportRe = /import\s+(?!type\b)(\S(?:.*\S)?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
219
257
|
const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
|
|
220
258
|
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
221
|
-
for (const match of
|
|
259
|
+
for (const match of scanCode.matchAll(staticImportRe)) {
|
|
222
260
|
const clause = match[1];
|
|
223
261
|
const source = match[2];
|
|
224
|
-
if (!
|
|
262
|
+
if (!isRuntimeImportSource(source)) {
|
|
225
263
|
continue;
|
|
226
264
|
}
|
|
227
|
-
imports.push({ source, symbols:
|
|
265
|
+
imports.push({ source, symbols: readRuntimeImportSymbols(source, clause) });
|
|
228
266
|
}
|
|
229
|
-
for (const match of
|
|
267
|
+
for (const match of scanCode.matchAll(sideEffectImportRe)) {
|
|
230
268
|
const source = match[1];
|
|
231
|
-
if (
|
|
232
|
-
imports.push({
|
|
269
|
+
if (isRuntimeImportSource(source)) {
|
|
270
|
+
imports.push({
|
|
271
|
+
source,
|
|
272
|
+
symbols: isWholeRuntimeModuleForbidden(source) ? Array.from(FORBIDDEN_SYMBOLS) : []
|
|
273
|
+
});
|
|
233
274
|
}
|
|
234
275
|
}
|
|
235
|
-
for (const match of
|
|
276
|
+
for (const match of scanCode.matchAll(dynamicImportRe)) {
|
|
236
277
|
const source = match[1];
|
|
237
|
-
if (
|
|
278
|
+
if (isRuntimeImportSource(source)) {
|
|
238
279
|
imports.push({ source, symbols: Array.from(FORBIDDEN_SYMBOLS) });
|
|
239
280
|
}
|
|
240
281
|
}
|
|
241
282
|
return imports;
|
|
242
283
|
}
|
|
284
|
+
function isRuntimeImportSource(source) {
|
|
285
|
+
return RUNTIME_HELPER_MODULES.has(source) || source.startsWith("@csszyx/runtime/") || CLIENT_RUNTIME_MODULES.has(source) || CLIENT_RUNTIME_MODULE_ROOTS.some((root) => source === root || source.startsWith(`${root}/`));
|
|
286
|
+
}
|
|
287
|
+
function isWholeRuntimeModuleForbidden(source) {
|
|
288
|
+
return source.startsWith("@csszyx/runtime/") || CLIENT_RUNTIME_MODULES.has(source) || CLIENT_RUNTIME_MODULE_ROOTS.some((root) => source === root || source.startsWith(`${root}/`));
|
|
289
|
+
}
|
|
290
|
+
function readRuntimeImportSymbols(source, clause) {
|
|
291
|
+
if (isWholeRuntimeModuleForbidden(source)) {
|
|
292
|
+
return Array.from(FORBIDDEN_SYMBOLS);
|
|
293
|
+
}
|
|
294
|
+
return readImportedSymbols(clause);
|
|
295
|
+
}
|
|
243
296
|
function findLocalImportSources(code) {
|
|
244
297
|
const out = [];
|
|
245
|
-
const staticImportRe = /import\s+(?!type\b)(
|
|
246
|
-
const exportFromRe = /export\s+(?!type\b)
|
|
298
|
+
const staticImportRe = /import\s+(?!type\b)(?:\S(?:.*\S)?\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
299
|
+
const exportFromRe = /export\s+(?!type\b)\S(?:.*\S)?\s+from\s+['"]([^'"]+)['"]/g;
|
|
247
300
|
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
248
301
|
for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
|
|
249
302
|
for (const match of code.matchAll(re)) {
|
|
@@ -257,13 +310,25 @@ function findLocalImportSources(code) {
|
|
|
257
310
|
}
|
|
258
311
|
function normalizeModuleId(id) {
|
|
259
312
|
const clean = id.split("?")[0] ?? id;
|
|
313
|
+
const cached = normalizedModuleIdCache.get(clean);
|
|
314
|
+
if (cached) {
|
|
315
|
+
return cached;
|
|
316
|
+
}
|
|
317
|
+
let normalized;
|
|
260
318
|
try {
|
|
261
|
-
|
|
319
|
+
normalized = fs.realpathSync.native(clean).replace(/\\/g, "/");
|
|
262
320
|
} catch {
|
|
263
|
-
|
|
321
|
+
normalized = path.resolve(clean).replace(/\\/g, "/");
|
|
264
322
|
}
|
|
323
|
+
normalizedModuleIdCache.set(clean, normalized);
|
|
324
|
+
return normalized;
|
|
265
325
|
}
|
|
266
326
|
function resolveLocalModule(importer, source) {
|
|
327
|
+
const cacheKey = `${importer}\0${source}`;
|
|
328
|
+
const cached = resolvedLocalModuleCache.get(cacheKey);
|
|
329
|
+
if (cached) {
|
|
330
|
+
return cached;
|
|
331
|
+
}
|
|
267
332
|
const base = source.startsWith("/") ? source : path.resolve(path.dirname(importer), source);
|
|
268
333
|
const candidates = [
|
|
269
334
|
base,
|
|
@@ -280,11 +345,26 @@ function resolveLocalModule(importer, source) {
|
|
|
280
345
|
];
|
|
281
346
|
for (const candidate of candidates) {
|
|
282
347
|
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
283
|
-
|
|
348
|
+
const resolved = normalizeModuleId(candidate);
|
|
349
|
+
resolvedLocalModuleCache.set(cacheKey, resolved);
|
|
350
|
+
return resolved;
|
|
284
351
|
}
|
|
285
352
|
}
|
|
286
353
|
return null;
|
|
287
354
|
}
|
|
355
|
+
function pruneRSCModulePathCaches(moduleIds) {
|
|
356
|
+
for (const [key, value] of normalizedModuleIdCache) {
|
|
357
|
+
const normalizedKey = key.replace(/\\/g, "/");
|
|
358
|
+
if (moduleIds.has(value) || moduleIds.has(normalizedKey)) {
|
|
359
|
+
normalizedModuleIdCache.delete(key);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for (const [key, value] of resolvedLocalModuleCache) {
|
|
363
|
+
if (moduleIds.has(value)) {
|
|
364
|
+
resolvedLocalModuleCache.delete(key);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
288
368
|
function readImportedSymbols(clause) {
|
|
289
369
|
const symbols = [];
|
|
290
370
|
const named = clause.match(/\{([\s\S]*?)\}/);
|
|
@@ -303,8 +383,72 @@ function readImportedSymbols(clause) {
|
|
|
303
383
|
if (/\*\s+as\s+\w+/.test(clause)) {
|
|
304
384
|
symbols.push(...FORBIDDEN_SYMBOLS);
|
|
305
385
|
}
|
|
386
|
+
const braceStart = clause.indexOf("{");
|
|
387
|
+
const braceEnd = clause.indexOf("}", braceStart);
|
|
388
|
+
const stripped = braceStart !== -1 && braceEnd !== -1 ? clause.slice(0, braceStart) + clause.slice(braceEnd + 1) : clause;
|
|
389
|
+
const defaultImport = stripped.match(/^\s*([A-Z_$][\w$]*)\s*(?:,|$)/i);
|
|
390
|
+
const defaultSymbol = defaultImport?.[1];
|
|
391
|
+
if (defaultSymbol && FORBIDDEN_SYMBOLS.has(defaultSymbol)) {
|
|
392
|
+
symbols.push(defaultSymbol);
|
|
393
|
+
}
|
|
306
394
|
return symbols;
|
|
307
395
|
}
|
|
396
|
+
function stripCommentsForImportScan(code) {
|
|
397
|
+
let out = "";
|
|
398
|
+
let i = 0;
|
|
399
|
+
let quote = null;
|
|
400
|
+
let escaped = false;
|
|
401
|
+
while (i < code.length) {
|
|
402
|
+
const ch = code[i];
|
|
403
|
+
const next = code[i + 1];
|
|
404
|
+
if (quote) {
|
|
405
|
+
out += ch;
|
|
406
|
+
if (escaped) {
|
|
407
|
+
escaped = false;
|
|
408
|
+
} else if (ch === "\\") {
|
|
409
|
+
escaped = true;
|
|
410
|
+
} else if (ch === quote) {
|
|
411
|
+
quote = null;
|
|
412
|
+
}
|
|
413
|
+
i++;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
417
|
+
quote = ch;
|
|
418
|
+
out += ch;
|
|
419
|
+
i++;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (ch === "/" && next === "/") {
|
|
423
|
+
out += " ";
|
|
424
|
+
i += 2;
|
|
425
|
+
while (i < code.length && code[i] !== "\n") {
|
|
426
|
+
out += " ";
|
|
427
|
+
i++;
|
|
428
|
+
}
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (ch === "/" && next === "*") {
|
|
432
|
+
out += " ";
|
|
433
|
+
i += 2;
|
|
434
|
+
while (i < code.length) {
|
|
435
|
+
const blockCh = code[i];
|
|
436
|
+
const blockNext = code[i + 1];
|
|
437
|
+
if (blockCh === "*" && blockNext === "/") {
|
|
438
|
+
out += " ";
|
|
439
|
+
i += 2;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
out += blockCh === "\n" ? "\n" : " ";
|
|
443
|
+
i++;
|
|
444
|
+
}
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
out += ch;
|
|
448
|
+
i++;
|
|
449
|
+
}
|
|
450
|
+
return out;
|
|
451
|
+
}
|
|
308
452
|
|
|
309
453
|
const EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
|
|
310
454
|
function stripLayerWrappers(css) {
|
|
@@ -703,6 +847,149 @@ function writeThemeDts(opts) {
|
|
|
703
847
|
writeFileSync(opts.outputPath, content, "utf-8");
|
|
704
848
|
}
|
|
705
849
|
|
|
850
|
+
const CACHE_SCHEMA_VERSION = 2;
|
|
851
|
+
function resolveTransformCacheDir(rootDir, cacheDir) {
|
|
852
|
+
return path.resolve(rootDir, cacheDir ?? ".csszyx/cache", "transform");
|
|
853
|
+
}
|
|
854
|
+
function createTransformCacheKey(input) {
|
|
855
|
+
const inputSha256 = createHash("sha256").update(input.source).digest("hex");
|
|
856
|
+
const keyMaterial = [
|
|
857
|
+
`schema=${CACHE_SCHEMA_VERSION}`,
|
|
858
|
+
`plugin=${input.pluginVersion}`,
|
|
859
|
+
`compiler=${input.compilerVersion}`,
|
|
860
|
+
`parser=${input.parserMode}`,
|
|
861
|
+
`producer=${input.producer}`,
|
|
862
|
+
`astBudget=${input.astBudget ?? "default"}`,
|
|
863
|
+
`filename=${input.filename}`,
|
|
864
|
+
`source=${inputSha256}`
|
|
865
|
+
].join("\n");
|
|
866
|
+
return {
|
|
867
|
+
key: createHash("sha256").update(keyMaterial).digest("hex").slice(0, 16),
|
|
868
|
+
inputSha256
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
function readTransformCache(cacheRoot, input, precomputedKey) {
|
|
872
|
+
const { key, inputSha256 } = precomputedKey ?? createTransformCacheKey(input);
|
|
873
|
+
const file = cacheEntryPath(cacheRoot, key);
|
|
874
|
+
let entry;
|
|
875
|
+
try {
|
|
876
|
+
entry = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
877
|
+
} catch {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
if (entry.version !== CACHE_SCHEMA_VERSION || entry.pluginVersion !== input.pluginVersion || entry.compilerVersion !== input.compilerVersion || entry.parserMode !== input.parserMode || entry.producer !== input.producer || entry.astBudget !== (input.astBudget ?? null) || entry.filename !== input.filename || entry.inputSha256 !== inputSha256) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
return deserializeResult(entry.result);
|
|
884
|
+
}
|
|
885
|
+
function writeTransformCache(cacheRoot, input, result, precomputedKey) {
|
|
886
|
+
const { key, inputSha256 } = precomputedKey ?? createTransformCacheKey(input);
|
|
887
|
+
const file = cacheEntryPath(cacheRoot, key);
|
|
888
|
+
const dir = path.dirname(file);
|
|
889
|
+
const tmp = path.join(
|
|
890
|
+
dir,
|
|
891
|
+
`.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
|
|
892
|
+
);
|
|
893
|
+
const entry = {
|
|
894
|
+
version: CACHE_SCHEMA_VERSION,
|
|
895
|
+
pluginVersion: input.pluginVersion,
|
|
896
|
+
compilerVersion: input.compilerVersion,
|
|
897
|
+
parserMode: input.parserMode,
|
|
898
|
+
producer: input.producer,
|
|
899
|
+
astBudget: input.astBudget ?? null,
|
|
900
|
+
filename: input.filename,
|
|
901
|
+
inputSha256,
|
|
902
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
903
|
+
result: serializeResult(result)
|
|
904
|
+
};
|
|
905
|
+
try {
|
|
906
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
907
|
+
fs.writeFileSync(tmp, JSON.stringify(entry), "utf8");
|
|
908
|
+
fs.renameSync(tmp, file);
|
|
909
|
+
} catch {
|
|
910
|
+
try {
|
|
911
|
+
fs.rmSync(tmp, { force: true });
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function evictOldTransformCacheEntries(cacheRoot, options) {
|
|
917
|
+
let deleted = 0;
|
|
918
|
+
const now = options.now ?? Date.now();
|
|
919
|
+
const survivors = [];
|
|
920
|
+
for (const file of listJsonFiles(cacheRoot)) {
|
|
921
|
+
try {
|
|
922
|
+
const entry = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
923
|
+
const timestamp = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : 0;
|
|
924
|
+
if (!Number.isFinite(timestamp) || now - timestamp > options.maxAgeMs) {
|
|
925
|
+
fs.rmSync(file, { force: true });
|
|
926
|
+
deleted++;
|
|
927
|
+
} else {
|
|
928
|
+
survivors.push({ file, timestamp });
|
|
929
|
+
}
|
|
930
|
+
} catch {
|
|
931
|
+
fs.rmSync(file, { force: true });
|
|
932
|
+
deleted++;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
const overflow = survivors.length - options.maxEntries ;
|
|
936
|
+
if (overflow > 0) {
|
|
937
|
+
survivors.sort((a, b) => a.timestamp - b.timestamp);
|
|
938
|
+
for (const survivor of survivors.slice(0, overflow)) {
|
|
939
|
+
fs.rmSync(survivor.file, { force: true });
|
|
940
|
+
deleted++;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return deleted;
|
|
944
|
+
}
|
|
945
|
+
function cacheEntryPath(cacheRoot, key) {
|
|
946
|
+
return path.join(cacheRoot, key.slice(0, 2), `${key.slice(2)}.json`);
|
|
947
|
+
}
|
|
948
|
+
function serializeResult(result) {
|
|
949
|
+
return {
|
|
950
|
+
code: result.code,
|
|
951
|
+
transformed: result.transformed,
|
|
952
|
+
usesRuntime: result.usesRuntime,
|
|
953
|
+
usesMerge: result.usesMerge,
|
|
954
|
+
usesColorVar: result.usesColorVar,
|
|
955
|
+
classes: [...result.classes],
|
|
956
|
+
rawClassNames: [...result.rawClassNames],
|
|
957
|
+
diagnostics: [...result.diagnostics],
|
|
958
|
+
recoveryTokens: [...result.recoveryTokens]
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function deserializeResult(result) {
|
|
962
|
+
return {
|
|
963
|
+
code: result.code,
|
|
964
|
+
transformed: result.transformed,
|
|
965
|
+
usesRuntime: result.usesRuntime,
|
|
966
|
+
usesMerge: result.usesMerge,
|
|
967
|
+
usesColorVar: result.usesColorVar,
|
|
968
|
+
classes: new Set(result.classes),
|
|
969
|
+
rawClassNames: new Set(result.rawClassNames),
|
|
970
|
+
diagnostics: [...result.diagnostics],
|
|
971
|
+
recoveryTokens: new Map(result.recoveryTokens)
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
function listJsonFiles(dir) {
|
|
975
|
+
let entries;
|
|
976
|
+
try {
|
|
977
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
978
|
+
} catch {
|
|
979
|
+
return [];
|
|
980
|
+
}
|
|
981
|
+
const files = [];
|
|
982
|
+
for (const entry of entries) {
|
|
983
|
+
const fullPath = path.join(dir, entry.name);
|
|
984
|
+
if (entry.isDirectory()) {
|
|
985
|
+
files.push(...listJsonFiles(fullPath));
|
|
986
|
+
} else if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
987
|
+
files.push(fullPath);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return files;
|
|
991
|
+
}
|
|
992
|
+
|
|
706
993
|
const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
|
|
707
994
|
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
708
995
|
const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
|
|
@@ -753,7 +1040,37 @@ function resolveVirtualModule(id) {
|
|
|
753
1040
|
|
|
754
1041
|
const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
|
|
755
1042
|
const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
|
|
1043
|
+
const UNKNOWN_PACKAGE_VERSION = "0.0.0";
|
|
1044
|
+
const TRANSFORM_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1045
|
+
const TRANSFORM_CACHE_MAX_ENTRIES = 1e4;
|
|
1046
|
+
const TRANSFORM_MEMORY_CACHE_MAX_ENTRIES = 1e3;
|
|
1047
|
+
const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
|
|
1048
|
+
const RUNTIME_HELPER_IMPORT_RE = {
|
|
1049
|
+
_sz: /\{[^}]*\b_sz\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
|
|
1050
|
+
_szMerge: /\{[^}]*\b_szMerge\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
|
|
1051
|
+
__szColorVar: /\{[^}]*\b__szColorVar\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/
|
|
1052
|
+
};
|
|
756
1053
|
let _hasWarnedTsConfig = false;
|
|
1054
|
+
let _hasWarnedTransformCacheVersion = false;
|
|
1055
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
1056
|
+
const PLUGIN_VERSION = findPackageVersionFromFile(
|
|
1057
|
+
fileURLToPath(import.meta.url),
|
|
1058
|
+
UNKNOWN_PACKAGE_VERSION
|
|
1059
|
+
);
|
|
1060
|
+
const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
|
|
1061
|
+
const BENCH_TRACE_ENABLED = process.env.CSSZYX_BENCH_TRACE === "1";
|
|
1062
|
+
const BENCH_TRACE_FILE = process.env.CSSZYX_BENCH_TRACE_FILE;
|
|
1063
|
+
function traceBenchTiming(label, filename, elapsedMs) {
|
|
1064
|
+
if (!BENCH_TRACE_ENABLED) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (BENCH_TRACE_FILE && !filename.includes(BENCH_TRACE_FILE)) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
console.log(
|
|
1071
|
+
`[csszyx:bench] ${label} ${elapsedMs.toFixed(3)}ms ${normalizeSourceFilename(filename)}`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
757
1074
|
function runThemeScan(rootDir, scanCss) {
|
|
758
1075
|
if (!scanCss) {
|
|
759
1076
|
return;
|
|
@@ -796,6 +1113,42 @@ function runThemeScan(rootDir, scanCss) {
|
|
|
796
1113
|
}
|
|
797
1114
|
}
|
|
798
1115
|
}
|
|
1116
|
+
function findPackageVersionFromModule(specifier, fallback) {
|
|
1117
|
+
try {
|
|
1118
|
+
return findPackageVersionFromFile(requireFromHere.resolve(specifier), fallback);
|
|
1119
|
+
} catch {
|
|
1120
|
+
return fallback;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function findPackageVersionFromFile(file, fallback) {
|
|
1124
|
+
let dir = path.dirname(file);
|
|
1125
|
+
while (true) {
|
|
1126
|
+
const packageJson = path.join(dir, "package.json");
|
|
1127
|
+
try {
|
|
1128
|
+
const parsed = JSON.parse(fs.readFileSync(packageJson, "utf8"));
|
|
1129
|
+
if (typeof parsed.version === "string") {
|
|
1130
|
+
return parsed.version;
|
|
1131
|
+
}
|
|
1132
|
+
return fallback;
|
|
1133
|
+
} catch {
|
|
1134
|
+
}
|
|
1135
|
+
const parent = path.dirname(dir);
|
|
1136
|
+
if (parent === dir) {
|
|
1137
|
+
return fallback;
|
|
1138
|
+
}
|
|
1139
|
+
dir = parent;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function normalizeSourceFilename(filename) {
|
|
1143
|
+
return filename.replace(/\\/g, "/");
|
|
1144
|
+
}
|
|
1145
|
+
function insertRuntimeImport(code, importStmt) {
|
|
1146
|
+
const directiveMatch = code.match(DIRECTIVE_PROLOGUE_PREFIX_RE);
|
|
1147
|
+
if (!directiveMatch) {
|
|
1148
|
+
return `${importStmt}${code}`;
|
|
1149
|
+
}
|
|
1150
|
+
return code.replace(directiveMatch[0], `${directiveMatch[1]}${directiveMatch[2]}${importStmt}`);
|
|
1151
|
+
}
|
|
799
1152
|
function mangleCodeClassesSync(code, mangleMap) {
|
|
800
1153
|
function mangleClassString(classString) {
|
|
801
1154
|
return classString.split(/\s+/).filter(Boolean).map((cls) => {
|
|
@@ -971,8 +1324,20 @@ function mangleCodeClassesSync(code, mangleMap) {
|
|
|
971
1324
|
function createCsszyxPlugins(options = {}) {
|
|
972
1325
|
const manglingEnabled = options.production?.mangle !== false;
|
|
973
1326
|
const astBudgetOverride = options.build?.astBudgetLimit;
|
|
1327
|
+
const cacheRequested = (options.build?.cache ?? DEFAULT_BUILD_CONFIG.cache) !== false;
|
|
1328
|
+
const cacheVersionsKnown = PLUGIN_VERSION !== UNKNOWN_PACKAGE_VERSION && COMPILER_VERSION !== UNKNOWN_PACKAGE_VERSION;
|
|
1329
|
+
const cacheEnabled = cacheRequested && cacheVersionsKnown;
|
|
1330
|
+
if (cacheRequested && !cacheVersionsKnown && !_hasWarnedTransformCacheVersion) {
|
|
1331
|
+
_hasWarnedTransformCacheVersion = true;
|
|
1332
|
+
console.warn(
|
|
1333
|
+
"[csszyx] Transform cache disabled because package versions could not be resolved."
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
974
1336
|
const parserOverride = process.env.CSSZYX_PARSER;
|
|
975
|
-
const
|
|
1337
|
+
const defaultParser = DEFAULT_BUILD_CONFIG.parser ?? "rust";
|
|
1338
|
+
const parserMode = parserOverride === "babel" || parserOverride === "oxc" || parserOverride === "rust" ? parserOverride : options.build?.parser ?? defaultParser;
|
|
1339
|
+
let evictedCacheRoot = null;
|
|
1340
|
+
const transformMemoryCache = /* @__PURE__ */ new Map();
|
|
976
1341
|
const state = {
|
|
977
1342
|
classes: /* @__PURE__ */ new Set(),
|
|
978
1343
|
mangleMap: {},
|
|
@@ -1002,19 +1367,84 @@ function createCsszyxPlugins(options = {}) {
|
|
|
1002
1367
|
}
|
|
1003
1368
|
function transformConfiguredSource(source, filename) {
|
|
1004
1369
|
const compilerOptions = { astBudget: astBudgetOverride };
|
|
1005
|
-
|
|
1006
|
-
|
|
1370
|
+
const effectiveFilename = normalizeSourceFilename(filename);
|
|
1371
|
+
const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
|
|
1372
|
+
if (cacheEnabled) {
|
|
1373
|
+
evictTransformCacheOnce();
|
|
1007
1374
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1375
|
+
const cacheInput = {
|
|
1376
|
+
pluginVersion: PLUGIN_VERSION,
|
|
1377
|
+
compilerVersion: COMPILER_VERSION,
|
|
1378
|
+
parserMode,
|
|
1379
|
+
producer: parserMode,
|
|
1380
|
+
astBudget: astBudgetOverride,
|
|
1381
|
+
filename: effectiveFilename,
|
|
1382
|
+
source
|
|
1383
|
+
};
|
|
1384
|
+
if (parserMode === "rust") {
|
|
1385
|
+
ensureRustTransformAvailable();
|
|
1386
|
+
}
|
|
1387
|
+
const cacheKey = cacheEnabled ? createTransformCacheKey(cacheInput) : null;
|
|
1388
|
+
if (cacheEnabled && cacheKey) {
|
|
1389
|
+
const memoryCached = transformMemoryCache.get(cacheKey.key);
|
|
1390
|
+
if (memoryCached) {
|
|
1391
|
+
transformMemoryCache.delete(cacheKey.key);
|
|
1392
|
+
transformMemoryCache.set(cacheKey.key, memoryCached);
|
|
1393
|
+
return memoryCached;
|
|
1394
|
+
}
|
|
1395
|
+
const cached = readTransformCache(cacheRoot, cacheInput, cacheKey);
|
|
1396
|
+
if (cached) {
|
|
1397
|
+
rememberTransformCacheEntry(cacheKey.key, cached);
|
|
1398
|
+
return cached;
|
|
1399
|
+
}
|
|
1017
1400
|
}
|
|
1401
|
+
let result;
|
|
1402
|
+
if (parserMode === "babel") {
|
|
1403
|
+
result = transformSourceCode(source, effectiveFilename, compilerOptions);
|
|
1404
|
+
} else if (parserMode === "rust") {
|
|
1405
|
+
result = transformRust(source, effectiveFilename, compilerOptions);
|
|
1406
|
+
} else {
|
|
1407
|
+
try {
|
|
1408
|
+
result = transformOxc(source, effectiveFilename, compilerOptions);
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
result = transformSourceCode(source, effectiveFilename, compilerOptions);
|
|
1411
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1412
|
+
result.diagnostics.push(
|
|
1413
|
+
`[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
|
|
1414
|
+
);
|
|
1415
|
+
return result;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (cacheEnabled && cacheKey) {
|
|
1419
|
+
writeTransformCache(cacheRoot, cacheInput, result, cacheKey);
|
|
1420
|
+
rememberTransformCacheEntry(cacheKey.key, result);
|
|
1421
|
+
}
|
|
1422
|
+
return result;
|
|
1423
|
+
}
|
|
1424
|
+
function rememberTransformCacheEntry(key, result) {
|
|
1425
|
+
transformMemoryCache.delete(key);
|
|
1426
|
+
transformMemoryCache.set(key, result);
|
|
1427
|
+
if (transformMemoryCache.size <= TRANSFORM_MEMORY_CACHE_MAX_ENTRIES) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
const oldest = transformMemoryCache.keys().next().value;
|
|
1431
|
+
if (oldest) {
|
|
1432
|
+
transformMemoryCache.delete(oldest);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
function evictTransformCacheOnce() {
|
|
1436
|
+
if (!cacheEnabled) {
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
|
|
1440
|
+
if (evictedCacheRoot === cacheRoot) {
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
evictedCacheRoot = cacheRoot;
|
|
1444
|
+
evictOldTransformCacheEntries(cacheRoot, {
|
|
1445
|
+
maxAgeMs: TRANSFORM_CACHE_MAX_AGE_MS,
|
|
1446
|
+
maxEntries: TRANSFORM_CACHE_MAX_ENTRIES
|
|
1447
|
+
});
|
|
1018
1448
|
}
|
|
1019
1449
|
function writeSafelistFile(classes) {
|
|
1020
1450
|
if (classes.size === 0) {
|
|
@@ -1296,7 +1726,13 @@ ${sourceDirective}`
|
|
|
1296
1726
|
transformed = true;
|
|
1297
1727
|
}
|
|
1298
1728
|
} else {
|
|
1729
|
+
const transformStarted = performance.now();
|
|
1299
1730
|
const result = transformConfiguredSource(code, id);
|
|
1731
|
+
traceBenchTiming(
|
|
1732
|
+
"transform-hook",
|
|
1733
|
+
id,
|
|
1734
|
+
performance.now() - transformStarted
|
|
1735
|
+
);
|
|
1300
1736
|
transformedCode = result.code;
|
|
1301
1737
|
usesRuntime = result.usesRuntime;
|
|
1302
1738
|
usesMerge = result.usesMerge;
|
|
@@ -1340,11 +1776,10 @@ ${sourceDirective}`
|
|
|
1340
1776
|
if (usesColorVar) {
|
|
1341
1777
|
imports.push("__szColorVar");
|
|
1342
1778
|
}
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
);
|
|
1779
|
+
const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
|
|
1780
|
+
const needed = hasRuntimeImport ? imports.filter(
|
|
1781
|
+
(name) => !RUNTIME_HELPER_IMPORT_RE[name]?.test(transformedCode)
|
|
1782
|
+
) : imports;
|
|
1348
1783
|
if (needed.length > 0) {
|
|
1349
1784
|
const existingImport = transformedCode.match(
|
|
1350
1785
|
/^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
|
|
@@ -1357,18 +1792,7 @@ ${sourceDirective}`
|
|
|
1357
1792
|
} else {
|
|
1358
1793
|
const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
|
|
1359
1794
|
`;
|
|
1360
|
-
|
|
1361
|
-
/^['"]use (client|server)['"];?\s*/
|
|
1362
|
-
);
|
|
1363
|
-
if (directiveMatch) {
|
|
1364
|
-
const directive = directiveMatch[0];
|
|
1365
|
-
transformedCode = transformedCode.replace(
|
|
1366
|
-
directive,
|
|
1367
|
-
`${directive}${importStmt}`
|
|
1368
|
-
);
|
|
1369
|
-
} else {
|
|
1370
|
-
transformedCode = `${importStmt}${transformedCode}`;
|
|
1371
|
-
}
|
|
1795
|
+
transformedCode = insertRuntimeImport(transformedCode, importStmt);
|
|
1372
1796
|
}
|
|
1373
1797
|
transformed = true;
|
|
1374
1798
|
}
|
|
@@ -1398,6 +1822,11 @@ ${sourceDirective}`
|
|
|
1398
1822
|
globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
|
|
1399
1823
|
}
|
|
1400
1824
|
},
|
|
1825
|
+
watchChange(id, change) {
|
|
1826
|
+
if (change.event === "delete") {
|
|
1827
|
+
deleteRSCModuleRecord(state.rscModules, id);
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1401
1830
|
/**
|
|
1402
1831
|
* Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
|
|
1403
1832
|
* @param compiler - the Webpack compiler instance
|
|
@@ -1406,6 +1835,7 @@ ${sourceDirective}`
|
|
|
1406
1835
|
compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
|
|
1407
1836
|
const root = compiler.context || process.cwd();
|
|
1408
1837
|
state.rootDir = root;
|
|
1838
|
+
evictTransformCacheOnce();
|
|
1409
1839
|
if (state.classes.size === 0) {
|
|
1410
1840
|
prescanAndWriteClasses();
|
|
1411
1841
|
}
|
|
@@ -1429,6 +1859,7 @@ ${sourceDirective}`
|
|
|
1429
1859
|
configResolved(config) {
|
|
1430
1860
|
const root = config.root || process.cwd();
|
|
1431
1861
|
state.rootDir = root;
|
|
1862
|
+
evictTransformCacheOnce();
|
|
1432
1863
|
prescanAndWriteClasses();
|
|
1433
1864
|
runThemeScan(root, options.build?.scanCss);
|
|
1434
1865
|
},
|
|
@@ -1458,7 +1889,13 @@ ${sourceDirective}`
|
|
|
1458
1889
|
return;
|
|
1459
1890
|
}
|
|
1460
1891
|
try {
|
|
1892
|
+
const hmrTransformStarted = performance.now();
|
|
1461
1893
|
result = transformConfiguredSource(fileContent, ctx.file);
|
|
1894
|
+
traceBenchTiming(
|
|
1895
|
+
"handle-hot-update",
|
|
1896
|
+
ctx.file,
|
|
1897
|
+
performance.now() - hmrTransformStarted
|
|
1898
|
+
);
|
|
1462
1899
|
} catch {
|
|
1463
1900
|
return;
|
|
1464
1901
|
}
|
|
@@ -1719,4 +2156,4 @@ const esbuildPlugin = (options = {}) => {
|
|
|
1719
2156
|
};
|
|
1720
2157
|
};
|
|
1721
2158
|
|
|
1722
|
-
export { assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createRSCModuleRecord as c,
|
|
2159
|
+
export { assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createRSCModuleRecord as c, deleteRSCModuleRecord as d, esbuildPlugin as e, findRSCBoundaryViolation as f, findRSCGraphViolation as g, hasTokens as h, hasUseClientDirective as i, hasUseServerDirective as j, isRSCServerModule as k, mergeThemes as l, mangleCodeClassesSync as m, parseThemeBlocks as p, rollupPlugin as r, unplugin as u, vitePlugin as v, webpackPlugin as w };
|