@csszyx/unplugin 0.7.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 +32 -1
- package/dist/css-mangler.cjs +27 -72
- package/dist/css-mangler.d.cts +2 -1
- package/dist/{css-mangler.d.ts → css-mangler.d.mts} +2 -1
- package/dist/{chunk-4M7CPGP7.js → css-mangler.mjs} +13 -31
- package/dist/index.cjs +47 -1856
- package/dist/index.d.cts +15 -2
- package/dist/{index.d.ts → index.d.mts} +13 -3
- package/dist/index.mjs +16 -0
- package/dist/shared/unplugin.BEOG6ePC.mjs +2159 -0
- package/dist/shared/unplugin.CL0F6RZa.cjs +2192 -0
- package/dist/vite.cjs +24 -1678
- package/dist/vite.d.cts +2 -2
- package/dist/{vite.d.ts → vite.d.mts} +1 -1
- package/dist/vite.mjs +20 -0
- package/dist/webpack.cjs +24 -1687
- package/dist/webpack.d.cts +2 -2
- package/dist/{webpack.d.ts → webpack.d.mts} +1 -1
- package/dist/webpack.mjs +20 -0
- package/package.json +51 -27
- package/dist/chunk-GXGGTRUA.js +0 -1602
- package/dist/css-mangler.js +0 -14
- package/dist/index.js +0 -51
- package/dist/vite.js +0 -10
- package/dist/webpack.js +0 -10
- /package/dist/{unplugin-DUbr5w-N.d.cts → shared/unplugin.DUbr5w-N.d.cts} +0 -0
- /package/dist/{unplugin-DUbr5w-N.d.ts → shared/unplugin.DUbr5w-N.d.mts} +0 -0
|
@@ -0,0 +1,2159 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
|
+
import { performance } from 'node:perf_hooks';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transform } from '@csszyx/compiler';
|
|
9
|
+
import { encode, compute_mangle_checksum } from '@csszyx/core';
|
|
10
|
+
import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
|
|
11
|
+
import { DEFAULT_BUILD_CONFIG } from '@csszyx/types';
|
|
12
|
+
import { preprocess } from '@csszyx/vue-adapter';
|
|
13
|
+
import { createUnplugin } from 'unplugin';
|
|
14
|
+
import { mangleCSSSync } from '../css-mangler.mjs';
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
|
|
17
|
+
const SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
|
|
18
|
+
const CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
|
|
19
|
+
const RUNTIME_HELPER_MODULES = /* @__PURE__ */ new Set([
|
|
20
|
+
"@csszyx/runtime",
|
|
21
|
+
"@csszyx/runtime/lite",
|
|
22
|
+
"csszyx",
|
|
23
|
+
"csszyx/lite"
|
|
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();
|
|
29
|
+
const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
30
|
+
"_sz",
|
|
31
|
+
"_sz2",
|
|
32
|
+
"_sz3",
|
|
33
|
+
"_szIf",
|
|
34
|
+
"_szMerge",
|
|
35
|
+
"_szSwitch",
|
|
36
|
+
"__csszyx_runtime__"
|
|
37
|
+
]);
|
|
38
|
+
function hasUseServerDirective(code) {
|
|
39
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
40
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function hasUseClientDirective(code) {
|
|
50
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
51
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
function isRSCServerModule(code, id) {
|
|
61
|
+
if (hasUseServerDirective(code)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (hasUseClientDirective(code)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return isNextAppRouterEntry(id);
|
|
68
|
+
}
|
|
69
|
+
function findRSCBoundaryViolation(code, id) {
|
|
70
|
+
if (!isRSCServerModule(code, id)) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
for (const imported of findRuntimeImports(code)) {
|
|
74
|
+
for (const symbol of imported.symbols) {
|
|
75
|
+
if (FORBIDDEN_SYMBOLS.has(symbol)) {
|
|
76
|
+
return {
|
|
77
|
+
symbol,
|
|
78
|
+
path: id,
|
|
79
|
+
importChain: [id, imported.source]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function createRSCModuleRecord(code, id) {
|
|
87
|
+
const normalized = normalizeModuleId(id);
|
|
88
|
+
return {
|
|
89
|
+
id: normalized,
|
|
90
|
+
isServer: isRSCServerModule(code, normalized),
|
|
91
|
+
isClient: hasUseClientDirective(code),
|
|
92
|
+
imports: findLocalImportSources(code).map((source) => resolveLocalModule(normalized, source)).filter((resolved) => resolved !== null),
|
|
93
|
+
runtimeImports: findRuntimeImports(code).filter(
|
|
94
|
+
(imported) => imported.symbols.some((symbol) => FORBIDDEN_SYMBOLS.has(symbol))
|
|
95
|
+
)
|
|
96
|
+
};
|
|
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
|
+
}
|
|
112
|
+
function findRSCGraphViolation(records) {
|
|
113
|
+
for (const root of records.values()) {
|
|
114
|
+
if (!root.isServer) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const violation = walkRSCGraph(root, records, [root.id], /* @__PURE__ */ new Set([root.id]));
|
|
118
|
+
if (violation) {
|
|
119
|
+
return {
|
|
120
|
+
symbol: violation.symbol,
|
|
121
|
+
path: root.id,
|
|
122
|
+
importChain: violation.importChain
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function assertNoRSCGraphViolation(records) {
|
|
129
|
+
const violation = findRSCGraphViolation(records);
|
|
130
|
+
if (!violation) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
throw new Error(formatRSCViolation(violation));
|
|
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
|
+
]);
|
|
146
|
+
function isNextAppRouterEntry(id) {
|
|
147
|
+
const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
|
|
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);
|
|
155
|
+
}
|
|
156
|
+
function assertNoRSCBoundaryViolation(code, id) {
|
|
157
|
+
const violation = findRSCBoundaryViolation(code, id);
|
|
158
|
+
if (!violation) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
throw new Error(formatRSCViolation(violation));
|
|
162
|
+
}
|
|
163
|
+
function formatRSCViolation(violation) {
|
|
164
|
+
return `csszyxRSCViolation: ${violation.symbol} imported in Server Component ${violation.path}
|
|
165
|
+
Import chain: ${violation.importChain.join(" -> ")}`;
|
|
166
|
+
}
|
|
167
|
+
function walkRSCGraph(current, records, chain, seen) {
|
|
168
|
+
if (current.isClient) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const runtime = current.runtimeImports[0];
|
|
172
|
+
const symbol = runtime?.symbols.find((s) => FORBIDDEN_SYMBOLS.has(s));
|
|
173
|
+
if (runtime && symbol) {
|
|
174
|
+
return {
|
|
175
|
+
symbol,
|
|
176
|
+
path: chain[0] ?? current.id,
|
|
177
|
+
importChain: [...chain, runtime.source]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
for (const importedId of current.imports) {
|
|
181
|
+
if (seen.has(importedId)) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const next = records.get(importedId);
|
|
185
|
+
if (!next) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
seen.add(importedId);
|
|
189
|
+
const violation = walkRSCGraph(next, records, [...chain, importedId], seen);
|
|
190
|
+
if (violation) {
|
|
191
|
+
return violation;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
function readDirectivePrologue(code) {
|
|
197
|
+
const out = [];
|
|
198
|
+
let i = code.charCodeAt(0) === 65279 ? 1 : 0;
|
|
199
|
+
while (i < code.length) {
|
|
200
|
+
i = skipWhitespaceAndComments(code, i);
|
|
201
|
+
const quote = code[i];
|
|
202
|
+
if (quote !== '"' && quote !== "'") {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
let j = i + 1;
|
|
206
|
+
let escaped = false;
|
|
207
|
+
while (j < code.length) {
|
|
208
|
+
const ch = code[j];
|
|
209
|
+
if (escaped) {
|
|
210
|
+
escaped = false;
|
|
211
|
+
} else if (ch === "\\") {
|
|
212
|
+
escaped = true;
|
|
213
|
+
} else if (ch === quote) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
j++;
|
|
217
|
+
}
|
|
218
|
+
if (j >= code.length) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
let end = j + 1;
|
|
222
|
+
while (end < code.length && /[ \t\r\n]/.test(code[end])) {
|
|
223
|
+
end++;
|
|
224
|
+
}
|
|
225
|
+
if (code[end] === ";") {
|
|
226
|
+
end++;
|
|
227
|
+
}
|
|
228
|
+
out.push(code.slice(i, end).trim());
|
|
229
|
+
i = end;
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
function skipWhitespaceAndComments(code, start) {
|
|
234
|
+
let i = start;
|
|
235
|
+
while (i < code.length) {
|
|
236
|
+
while (i < code.length && /\s/.test(code[i])) {
|
|
237
|
+
i++;
|
|
238
|
+
}
|
|
239
|
+
if (code.startsWith("//", i)) {
|
|
240
|
+
const next = code.indexOf("\n", i + 2);
|
|
241
|
+
i = next === -1 ? code.length : next + 1;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (code.startsWith("/*", i)) {
|
|
245
|
+
const next = code.indexOf("*/", i + 2);
|
|
246
|
+
i = next === -1 ? code.length : next + 2;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
return i;
|
|
252
|
+
}
|
|
253
|
+
function findRuntimeImports(code) {
|
|
254
|
+
const imports = [];
|
|
255
|
+
const scanCode = stripCommentsForImportScan(code);
|
|
256
|
+
const staticImportRe = /import\s+(?!type\b)(\S(?:.*\S)?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
257
|
+
const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
|
|
258
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
259
|
+
for (const match of scanCode.matchAll(staticImportRe)) {
|
|
260
|
+
const clause = match[1];
|
|
261
|
+
const source = match[2];
|
|
262
|
+
if (!isRuntimeImportSource(source)) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
imports.push({ source, symbols: readRuntimeImportSymbols(source, clause) });
|
|
266
|
+
}
|
|
267
|
+
for (const match of scanCode.matchAll(sideEffectImportRe)) {
|
|
268
|
+
const source = match[1];
|
|
269
|
+
if (isRuntimeImportSource(source)) {
|
|
270
|
+
imports.push({
|
|
271
|
+
source,
|
|
272
|
+
symbols: isWholeRuntimeModuleForbidden(source) ? Array.from(FORBIDDEN_SYMBOLS) : []
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const match of scanCode.matchAll(dynamicImportRe)) {
|
|
277
|
+
const source = match[1];
|
|
278
|
+
if (isRuntimeImportSource(source)) {
|
|
279
|
+
imports.push({ source, symbols: Array.from(FORBIDDEN_SYMBOLS) });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return imports;
|
|
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
|
+
}
|
|
296
|
+
function findLocalImportSources(code) {
|
|
297
|
+
const out = [];
|
|
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;
|
|
300
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
301
|
+
for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
|
|
302
|
+
for (const match of code.matchAll(re)) {
|
|
303
|
+
const source = match[1];
|
|
304
|
+
if (source.startsWith(".") || source.startsWith("/")) {
|
|
305
|
+
out.push(source);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
function normalizeModuleId(id) {
|
|
312
|
+
const clean = id.split("?")[0] ?? id;
|
|
313
|
+
const cached = normalizedModuleIdCache.get(clean);
|
|
314
|
+
if (cached) {
|
|
315
|
+
return cached;
|
|
316
|
+
}
|
|
317
|
+
let normalized;
|
|
318
|
+
try {
|
|
319
|
+
normalized = fs.realpathSync.native(clean).replace(/\\/g, "/");
|
|
320
|
+
} catch {
|
|
321
|
+
normalized = path.resolve(clean).replace(/\\/g, "/");
|
|
322
|
+
}
|
|
323
|
+
normalizedModuleIdCache.set(clean, normalized);
|
|
324
|
+
return normalized;
|
|
325
|
+
}
|
|
326
|
+
function resolveLocalModule(importer, source) {
|
|
327
|
+
const cacheKey = `${importer}\0${source}`;
|
|
328
|
+
const cached = resolvedLocalModuleCache.get(cacheKey);
|
|
329
|
+
if (cached) {
|
|
330
|
+
return cached;
|
|
331
|
+
}
|
|
332
|
+
const base = source.startsWith("/") ? source : path.resolve(path.dirname(importer), source);
|
|
333
|
+
const candidates = [
|
|
334
|
+
base,
|
|
335
|
+
`${base}.tsx`,
|
|
336
|
+
`${base}.ts`,
|
|
337
|
+
`${base}.jsx`,
|
|
338
|
+
`${base}.js`,
|
|
339
|
+
`${base}.mjs`,
|
|
340
|
+
`${base}.cjs`,
|
|
341
|
+
path.join(base, "index.tsx"),
|
|
342
|
+
path.join(base, "index.ts"),
|
|
343
|
+
path.join(base, "index.jsx"),
|
|
344
|
+
path.join(base, "index.js")
|
|
345
|
+
];
|
|
346
|
+
for (const candidate of candidates) {
|
|
347
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
348
|
+
const resolved = normalizeModuleId(candidate);
|
|
349
|
+
resolvedLocalModuleCache.set(cacheKey, resolved);
|
|
350
|
+
return resolved;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
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
|
+
}
|
|
368
|
+
function readImportedSymbols(clause) {
|
|
369
|
+
const symbols = [];
|
|
370
|
+
const named = clause.match(/\{([\s\S]*?)\}/);
|
|
371
|
+
if (named) {
|
|
372
|
+
for (const part of named[1].split(",")) {
|
|
373
|
+
const trimmed = part.trim();
|
|
374
|
+
if (!trimmed || trimmed.startsWith("type ")) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const sourceName = trimmed.replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
|
|
378
|
+
if (sourceName) {
|
|
379
|
+
symbols.push(sourceName);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (/\*\s+as\s+\w+/.test(clause)) {
|
|
384
|
+
symbols.push(...FORBIDDEN_SYMBOLS);
|
|
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
|
+
}
|
|
394
|
+
return symbols;
|
|
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
|
+
}
|
|
452
|
+
|
|
453
|
+
const EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
|
|
454
|
+
function stripLayerWrappers(css) {
|
|
455
|
+
let result = "";
|
|
456
|
+
let i = 0;
|
|
457
|
+
while (i < css.length) {
|
|
458
|
+
const layerIdx = css.indexOf("@layer", i);
|
|
459
|
+
if (layerIdx === -1) {
|
|
460
|
+
result += css.slice(i);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
result += css.slice(i, layerIdx);
|
|
464
|
+
const openBrace = css.indexOf("{", layerIdx);
|
|
465
|
+
if (openBrace === -1) {
|
|
466
|
+
result += css.slice(layerIdx);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
let depth = 0;
|
|
470
|
+
let j = openBrace;
|
|
471
|
+
while (j < css.length) {
|
|
472
|
+
if (css[j] === "{") {
|
|
473
|
+
depth++;
|
|
474
|
+
}
|
|
475
|
+
if (css[j] === "}") {
|
|
476
|
+
depth--;
|
|
477
|
+
if (depth === 0) {
|
|
478
|
+
result += css.slice(openBrace + 1, j);
|
|
479
|
+
i = j + 1;
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
j++;
|
|
484
|
+
}
|
|
485
|
+
if (depth !== 0) {
|
|
486
|
+
result += css.slice(openBrace);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
function extractThemeBlocks(css) {
|
|
493
|
+
const blocks = [];
|
|
494
|
+
const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
|
|
495
|
+
for (const match of css.matchAll(themeStart)) {
|
|
496
|
+
const openPos = css.indexOf("{", match.index);
|
|
497
|
+
let depth = 0;
|
|
498
|
+
let j = openPos;
|
|
499
|
+
while (j < css.length) {
|
|
500
|
+
if (css[j] === "{") {
|
|
501
|
+
depth++;
|
|
502
|
+
}
|
|
503
|
+
if (css[j] === "}") {
|
|
504
|
+
depth--;
|
|
505
|
+
if (depth === 0) {
|
|
506
|
+
blocks.push(css.slice(openPos + 1, j));
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
j++;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return blocks;
|
|
514
|
+
}
|
|
515
|
+
function categorizeProperty(prop) {
|
|
516
|
+
const categoryMap = [
|
|
517
|
+
["color-", "colors"],
|
|
518
|
+
["spacing-", "spacings"],
|
|
519
|
+
["font-", "fonts"],
|
|
520
|
+
["radius-", "radii"],
|
|
521
|
+
["shadow-", "shadows"]
|
|
522
|
+
];
|
|
523
|
+
for (const [prefix, category] of categoryMap) {
|
|
524
|
+
if (prop.startsWith(prefix)) {
|
|
525
|
+
let token = prop.slice(prefix.length);
|
|
526
|
+
token = token.replace(/-\d+$/, "");
|
|
527
|
+
if (token) {
|
|
528
|
+
return { category, token };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
function parseThemeBlocks(cssContent) {
|
|
535
|
+
const result = {
|
|
536
|
+
colors: /* @__PURE__ */ new Set(),
|
|
537
|
+
spacings: /* @__PURE__ */ new Set(),
|
|
538
|
+
fonts: /* @__PURE__ */ new Set(),
|
|
539
|
+
radii: /* @__PURE__ */ new Set(),
|
|
540
|
+
shadows: /* @__PURE__ */ new Set()
|
|
541
|
+
};
|
|
542
|
+
const stripped = stripLayerWrappers(cssContent);
|
|
543
|
+
const blocks = extractThemeBlocks(stripped);
|
|
544
|
+
const propPattern = /--([a-z][a-z0-9-]*)(?:\s*:[^;]+)?;/g;
|
|
545
|
+
for (const block of blocks) {
|
|
546
|
+
for (const match of block.matchAll(propPattern)) {
|
|
547
|
+
const categorized = categorizeProperty(match[1]);
|
|
548
|
+
if (categorized) {
|
|
549
|
+
result[categorized.category].add(categorized.token);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
colors: [...result.colors].sort(),
|
|
555
|
+
spacings: [...result.spacings].sort(),
|
|
556
|
+
fonts: [...result.fonts].sort(),
|
|
557
|
+
radii: [...result.radii].sort(),
|
|
558
|
+
shadows: [...result.shadows].sort()
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function mergeThemes(themes) {
|
|
562
|
+
if (themes.length === 0) {
|
|
563
|
+
return { ...EMPTY_THEME };
|
|
564
|
+
}
|
|
565
|
+
const merged = {
|
|
566
|
+
colors: /* @__PURE__ */ new Set(),
|
|
567
|
+
spacings: /* @__PURE__ */ new Set(),
|
|
568
|
+
fonts: /* @__PURE__ */ new Set(),
|
|
569
|
+
radii: /* @__PURE__ */ new Set(),
|
|
570
|
+
shadows: /* @__PURE__ */ new Set()
|
|
571
|
+
};
|
|
572
|
+
for (const theme of themes) {
|
|
573
|
+
for (const cat of Object.keys(merged)) {
|
|
574
|
+
for (const token of theme[cat]) {
|
|
575
|
+
merged[cat].add(token);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
colors: [...merged.colors].sort(),
|
|
581
|
+
spacings: [...merged.spacings].sort(),
|
|
582
|
+
fonts: [...merged.fonts].sort(),
|
|
583
|
+
radii: [...merged.radii].sort(),
|
|
584
|
+
shadows: [...merged.shadows].sort()
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function hasTokens(theme) {
|
|
588
|
+
return Object.values(theme).some((arr) => arr.length > 0);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const GLOB_MAGIC_RE = /[*?[\]{}]/;
|
|
592
|
+
const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
|
|
593
|
+
function normalizeFileId(id) {
|
|
594
|
+
return id.split(/[?#]/, 1)[0].replace(/\\/g, "/");
|
|
595
|
+
}
|
|
596
|
+
function normalizeRoot(rootDir) {
|
|
597
|
+
return path.resolve(rootDir).replace(/\\/g, "/");
|
|
598
|
+
}
|
|
599
|
+
function hasGlobMagic(pattern) {
|
|
600
|
+
return GLOB_MAGIC_RE.test(pattern);
|
|
601
|
+
}
|
|
602
|
+
function escapeRegExp(ch) {
|
|
603
|
+
return /[|\\{}()[\]^$+?.]/.test(ch) ? `\\${ch}` : ch;
|
|
604
|
+
}
|
|
605
|
+
function globToRegExp(pattern) {
|
|
606
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
607
|
+
let out = "^";
|
|
608
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
609
|
+
const ch = normalized[i];
|
|
610
|
+
if (ch === "*") {
|
|
611
|
+
if (normalized[i + 1] === "*") {
|
|
612
|
+
out += ".*";
|
|
613
|
+
i++;
|
|
614
|
+
} else {
|
|
615
|
+
out += "[^/]*";
|
|
616
|
+
}
|
|
617
|
+
} else if (ch === "?") {
|
|
618
|
+
out += "[^/]";
|
|
619
|
+
} else {
|
|
620
|
+
out += escapeRegExp(ch);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
out += "$";
|
|
624
|
+
return new RegExp(out);
|
|
625
|
+
}
|
|
626
|
+
function relativeToRoot(file, rootDir) {
|
|
627
|
+
const root = normalizeRoot(rootDir);
|
|
628
|
+
const normalizedFile = normalizeFileId(file);
|
|
629
|
+
return path.posix.relative(root, normalizedFile).replace(/\\/g, "/");
|
|
630
|
+
}
|
|
631
|
+
function matchesPattern(id, pattern, rootDir) {
|
|
632
|
+
const file = normalizeFileId(id);
|
|
633
|
+
const relative = relativeToRoot(file, rootDir);
|
|
634
|
+
if (pattern instanceof RegExp) {
|
|
635
|
+
pattern.lastIndex = 0;
|
|
636
|
+
return pattern.test(file) || pattern.test(relative);
|
|
637
|
+
}
|
|
638
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
639
|
+
if (hasGlobMagic(normalizedPattern)) {
|
|
640
|
+
const re = globToRegExp(normalizedPattern);
|
|
641
|
+
return re.test(file) || re.test(relative);
|
|
642
|
+
}
|
|
643
|
+
const absolutePattern = path.isAbsolute(normalizedPattern) ? normalizeFileId(normalizedPattern) : normalizeFileId(path.join(rootDir, normalizedPattern));
|
|
644
|
+
return file === absolutePattern || relative === normalizedPattern;
|
|
645
|
+
}
|
|
646
|
+
function matchesAnyPattern(id, patterns, rootDir) {
|
|
647
|
+
if (!patterns) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
651
|
+
return list.some((pattern) => matchesPattern(id, pattern, rootDir));
|
|
652
|
+
}
|
|
653
|
+
function expandFilePatterns(rootDir, patterns) {
|
|
654
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
655
|
+
const files = /* @__PURE__ */ new Set();
|
|
656
|
+
const walk = (dir) => {
|
|
657
|
+
let entries;
|
|
658
|
+
try {
|
|
659
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
660
|
+
} catch {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
for (const entry of entries) {
|
|
664
|
+
const full = path.join(dir, entry.name);
|
|
665
|
+
if (entry.isDirectory()) {
|
|
666
|
+
if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
667
|
+
walk(full);
|
|
668
|
+
}
|
|
669
|
+
} else {
|
|
670
|
+
files.add(path.resolve(full));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
let needsWalk = false;
|
|
675
|
+
for (const pattern of list) {
|
|
676
|
+
const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
|
|
677
|
+
if (hasGlobMagic(pattern)) {
|
|
678
|
+
needsWalk = true;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
if (fs.existsSync(resolved)) {
|
|
682
|
+
files.add(path.resolve(resolved));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (needsWalk) {
|
|
686
|
+
walk(rootDir);
|
|
687
|
+
for (const file of Array.from(files)) {
|
|
688
|
+
if (!list.some(
|
|
689
|
+
(pattern) => hasGlobMagic(pattern) && matchesPattern(file, pattern, rootDir)
|
|
690
|
+
)) {
|
|
691
|
+
const isLiteralMatch = list.some((pattern) => {
|
|
692
|
+
if (hasGlobMagic(pattern)) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
|
|
696
|
+
return normalizeFileId(path.resolve(resolved)) === normalizeFileId(file);
|
|
697
|
+
});
|
|
698
|
+
if (!isLiteralMatch) {
|
|
699
|
+
files.delete(file);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return Array.from(files).sort();
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function injectChecksum(html, checksum, minify = false) {
|
|
708
|
+
const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
|
|
709
|
+
const htmlTagPattern = /<html([^>]*)>/i;
|
|
710
|
+
const match = html.match(htmlTagPattern);
|
|
711
|
+
if (!match) {
|
|
712
|
+
return html;
|
|
713
|
+
}
|
|
714
|
+
const existingAttrs = match[1];
|
|
715
|
+
const checksumAttr = ` ${attrName}="${checksum}"`;
|
|
716
|
+
return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
|
|
717
|
+
}
|
|
718
|
+
function injectMangleMapScript(html, mangleMap, options = {}) {
|
|
719
|
+
const { prettyPrint = false } = options;
|
|
720
|
+
const jsonContent = prettyPrint ? JSON.stringify(mangleMap, null, 2) : JSON.stringify(mangleMap);
|
|
721
|
+
const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}<\/script>`;
|
|
722
|
+
const debugScript = `<script>(function(){var m=${jsonContent};var r={};for(var k in m)r[m[k]]=k;var cs=document.documentElement.getAttribute("data-sz-checksum")||"";window.__csszyx={mangleMap:m,checksum:cs,decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()<\/script>`;
|
|
723
|
+
const combined = `${scriptTag}
|
|
724
|
+
${debugScript}`;
|
|
725
|
+
if (html.includes("</head>")) {
|
|
726
|
+
return html.replace("</head>", `${combined}
|
|
727
|
+
</head>`);
|
|
728
|
+
} else if (html.includes("</html>")) {
|
|
729
|
+
return html.replace("</html>", `${combined}
|
|
730
|
+
</html>`);
|
|
731
|
+
}
|
|
732
|
+
return html + combined;
|
|
733
|
+
}
|
|
734
|
+
function injectMangleMapAttribute(html, mangleMap, minify = false) {
|
|
735
|
+
const attrName = minify ? "data-sz-m" : "data-sz-map";
|
|
736
|
+
const jsonContent = JSON.stringify(mangleMap);
|
|
737
|
+
const htmlTagPattern = /<html([^>]*)>/i;
|
|
738
|
+
const match = html.match(htmlTagPattern);
|
|
739
|
+
if (!match) {
|
|
740
|
+
return html;
|
|
741
|
+
}
|
|
742
|
+
const existingAttrs = match[1];
|
|
743
|
+
const mapAttr = ` ${attrName}='${jsonContent}'`;
|
|
744
|
+
return html.replace(htmlTagPattern, `<html${mapAttr}${existingAttrs}>`);
|
|
745
|
+
}
|
|
746
|
+
function injectHydrationData(html, mangleMap, checksum, options = {}) {
|
|
747
|
+
const { mode = "script", minify = false } = options;
|
|
748
|
+
let result = html;
|
|
749
|
+
result = injectChecksum(result, checksum, minify);
|
|
750
|
+
if (mode === "inline") {
|
|
751
|
+
result = injectMangleMapAttribute(result, mangleMap, minify);
|
|
752
|
+
} else if (mode === "script") {
|
|
753
|
+
result = injectMangleMapScript(result, mangleMap, options);
|
|
754
|
+
} else if (mode === "both") {
|
|
755
|
+
result = injectMangleMapAttribute(result, mangleMap, minify);
|
|
756
|
+
result = injectMangleMapScript(result, mangleMap, options);
|
|
757
|
+
}
|
|
758
|
+
return result;
|
|
759
|
+
}
|
|
760
|
+
function transformIndexHtml(html, mangleMap, checksum, options = {}) {
|
|
761
|
+
return injectHydrationData(html, mangleMap, checksum, options);
|
|
762
|
+
}
|
|
763
|
+
function buildRecoveryManifest(tokens, options) {
|
|
764
|
+
const stripped = options.production === true;
|
|
765
|
+
const strippedDevOnlyPaths = [];
|
|
766
|
+
const sorted = {};
|
|
767
|
+
const sortedKeys = [...tokens.keys()].sort();
|
|
768
|
+
for (const key of sortedKeys) {
|
|
769
|
+
const data = tokens.get(key);
|
|
770
|
+
if (!data) {
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
if (stripped && data.mode === "dev-only") {
|
|
774
|
+
strippedDevOnlyPaths.push(data.path);
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
sorted[key] = stripped ? { mode: data.mode, component: data.component, path: "" } : data;
|
|
778
|
+
}
|
|
779
|
+
const serialised = JSON.stringify(sorted);
|
|
780
|
+
const fullChecksum = createHash("sha256").update(serialised).digest("hex");
|
|
781
|
+
const checksum = fullChecksum.substring(0, 16);
|
|
782
|
+
const buildId = `${Date.now().toString(36)}-${fullChecksum.substring(0, 6)}`;
|
|
783
|
+
return {
|
|
784
|
+
manifest: { buildId, checksum, mangleChecksum: options.mangleChecksum, tokens: sorted },
|
|
785
|
+
strippedDevOnlyPaths
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function injectRecoveryManifest(html, manifest) {
|
|
789
|
+
if (Object.keys(manifest.tokens).length === 0) {
|
|
790
|
+
return html;
|
|
791
|
+
}
|
|
792
|
+
const json = JSON.stringify(manifest);
|
|
793
|
+
const scriptTag = `<script id="__SZ_RECOVERY_MANIFEST__" type="application/json">${json}<\/script>`;
|
|
794
|
+
if (html.includes("</head>")) {
|
|
795
|
+
return html.replace("</head>", `${scriptTag}
|
|
796
|
+
</head>`);
|
|
797
|
+
} else if (html.includes("</html>")) {
|
|
798
|
+
return html.replace("</html>", `${scriptTag}
|
|
799
|
+
</html>`);
|
|
800
|
+
}
|
|
801
|
+
return html + scriptTag;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function generateThemeDts(opts) {
|
|
805
|
+
const { theme, sourceFiles } = opts;
|
|
806
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
807
|
+
const sources = sourceFiles.join(", ");
|
|
808
|
+
const toUnion = (tokens) => tokens.map((t) => `'${t}'`).join(" | ");
|
|
809
|
+
const entries = [];
|
|
810
|
+
if (theme.colors.length > 0) {
|
|
811
|
+
entries.push(` colors: ${toUnion(theme.colors)};`);
|
|
812
|
+
}
|
|
813
|
+
if (theme.spacings.length > 0) {
|
|
814
|
+
entries.push(` spacings: ${toUnion(theme.spacings)};`);
|
|
815
|
+
}
|
|
816
|
+
if (theme.fonts.length > 0) {
|
|
817
|
+
entries.push(` fonts: ${toUnion(theme.fonts)};`);
|
|
818
|
+
}
|
|
819
|
+
if (theme.radii.length > 0) {
|
|
820
|
+
entries.push(` radii: ${toUnion(theme.radii)};`);
|
|
821
|
+
}
|
|
822
|
+
if (theme.shadows.length > 0) {
|
|
823
|
+
entries.push(` shadows: ${toUnion(theme.shadows)};`);
|
|
824
|
+
}
|
|
825
|
+
return [
|
|
826
|
+
"// Auto-generated by csszyx theme-scanner \u2014 DO NOT EDIT",
|
|
827
|
+
`// Source: ${sources}`,
|
|
828
|
+
`// Updated: ${timestamp}`,
|
|
829
|
+
"",
|
|
830
|
+
"declare module '@csszyx/compiler' {",
|
|
831
|
+
" /**",
|
|
832
|
+
" * Custom design tokens extracted from @theme blocks.",
|
|
833
|
+
" * These tokens are surfaced in sz prop IntelliSense.",
|
|
834
|
+
" */",
|
|
835
|
+
" interface CustomTheme {",
|
|
836
|
+
...entries,
|
|
837
|
+
" }",
|
|
838
|
+
"}",
|
|
839
|
+
"",
|
|
840
|
+
"export {};",
|
|
841
|
+
""
|
|
842
|
+
].join("\n");
|
|
843
|
+
}
|
|
844
|
+
function writeThemeDts(opts) {
|
|
845
|
+
const content = generateThemeDts(opts);
|
|
846
|
+
mkdirSync(dirname(opts.outputPath), { recursive: true });
|
|
847
|
+
writeFileSync(opts.outputPath, content, "utf-8");
|
|
848
|
+
}
|
|
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
|
+
|
|
993
|
+
const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
|
|
994
|
+
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
995
|
+
const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
|
|
996
|
+
const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
|
|
997
|
+
function createMangleMapModule(mangleMap, checksum) {
|
|
998
|
+
return `/**
|
|
999
|
+
* Auto-generated mangle map for csszyx.
|
|
1000
|
+
* This module is generated at build time and contains the mapping
|
|
1001
|
+
* from original class names to mangled class names.
|
|
1002
|
+
*
|
|
1003
|
+
* @generated
|
|
1004
|
+
*/
|
|
1005
|
+
|
|
1006
|
+
export const mangleMap = ${JSON.stringify(mangleMap, null, 2)};
|
|
1007
|
+
|
|
1008
|
+
export const checksum = ${JSON.stringify(checksum)};
|
|
1009
|
+
|
|
1010
|
+
export default {
|
|
1011
|
+
mangleMap,
|
|
1012
|
+
checksum,
|
|
1013
|
+
};
|
|
1014
|
+
`;
|
|
1015
|
+
}
|
|
1016
|
+
function createChecksumModule(checksum) {
|
|
1017
|
+
return `/**
|
|
1018
|
+
* Auto-generated checksum for csszyx mangle map.
|
|
1019
|
+
*
|
|
1020
|
+
* @generated
|
|
1021
|
+
*/
|
|
1022
|
+
|
|
1023
|
+
export const checksum = ${JSON.stringify(checksum)};
|
|
1024
|
+
|
|
1025
|
+
export default checksum;
|
|
1026
|
+
`;
|
|
1027
|
+
}
|
|
1028
|
+
function isVirtualModule(id) {
|
|
1029
|
+
return id === VIRTUAL_MODULE_ID || id === VIRTUAL_CHECKSUM_ID;
|
|
1030
|
+
}
|
|
1031
|
+
function resolveVirtualModule(id) {
|
|
1032
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
1033
|
+
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
1034
|
+
}
|
|
1035
|
+
if (id === VIRTUAL_CHECKSUM_ID) {
|
|
1036
|
+
return RESOLVED_VIRTUAL_CHECKSUM_ID;
|
|
1037
|
+
}
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
|
|
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
|
+
};
|
|
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
|
+
}
|
|
1074
|
+
function runThemeScan(rootDir, scanCss) {
|
|
1075
|
+
if (!scanCss) {
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const sourceFiles = expandFilePatterns(rootDir, scanCss).filter((file) => file.endsWith(".css"));
|
|
1079
|
+
if (sourceFiles.length === 0) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const themes = sourceFiles.map((f) => {
|
|
1083
|
+
try {
|
|
1084
|
+
return parseThemeBlocks(fs.readFileSync(f, "utf-8"));
|
|
1085
|
+
} catch {
|
|
1086
|
+
return null;
|
|
1087
|
+
}
|
|
1088
|
+
}).filter((t) => t !== null);
|
|
1089
|
+
const merged = mergeThemes(themes);
|
|
1090
|
+
const outputPath = path.join(rootDir, ".csszyx", "theme.d.ts");
|
|
1091
|
+
writeThemeDts({ outputPath, theme: merged, sourceFiles });
|
|
1092
|
+
if (!_hasWarnedTsConfig) {
|
|
1093
|
+
_hasWarnedTsConfig = true;
|
|
1094
|
+
try {
|
|
1095
|
+
const checkFile = (cfgPath) => {
|
|
1096
|
+
if (fs.existsSync(cfgPath)) {
|
|
1097
|
+
const content = fs.readFileSync(cfgPath, "utf-8");
|
|
1098
|
+
if (!content.includes(".csszyx")) {
|
|
1099
|
+
console.warn(
|
|
1100
|
+
`
|
|
1101
|
+
\x1B[33m\u26A0\uFE0F CSSzyx: Theme Auto-Scan enabled, but TypeScript isn't configured. Run "npx @csszyx/cli init" to fix.\x1B[0m
|
|
1102
|
+
`
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
return true;
|
|
1106
|
+
}
|
|
1107
|
+
return false;
|
|
1108
|
+
};
|
|
1109
|
+
if (!checkFile(path.join(rootDir, "tsconfig.json"))) {
|
|
1110
|
+
checkFile(path.join(rootDir, "tsconfig.app.json"));
|
|
1111
|
+
}
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
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
|
+
}
|
|
1152
|
+
function mangleCodeClassesSync(code, mangleMap) {
|
|
1153
|
+
function mangleClassString(classString) {
|
|
1154
|
+
return classString.split(/\s+/).filter(Boolean).map((cls) => {
|
|
1155
|
+
return mangleMap[cls.replace(/\\(.)/g, "$1")] || cls;
|
|
1156
|
+
}).join(" ");
|
|
1157
|
+
}
|
|
1158
|
+
let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"((?:[^"\\]|\\.)*)"/g, (match, classes) => {
|
|
1159
|
+
const mangled = mangleClassString(classes);
|
|
1160
|
+
if (mangled === classes) {
|
|
1161
|
+
return match;
|
|
1162
|
+
}
|
|
1163
|
+
return match.replace(classes, mangled);
|
|
1164
|
+
}).replace(/(?:class(?:Name)?|sz)[:=]\s*'((?:[^'\\]|\\.)*)'/g, (match, classes) => {
|
|
1165
|
+
const mangled = mangleClassString(classes);
|
|
1166
|
+
if (mangled === classes) {
|
|
1167
|
+
return match;
|
|
1168
|
+
}
|
|
1169
|
+
return match.replace(classes, mangled);
|
|
1170
|
+
});
|
|
1171
|
+
result = result.replace(/className:\s*`([^`]+)`/g, (fullMatch, tplContent) => {
|
|
1172
|
+
let changed = false;
|
|
1173
|
+
let out = "";
|
|
1174
|
+
let i = 0;
|
|
1175
|
+
while (i < tplContent.length) {
|
|
1176
|
+
const interStart = tplContent.indexOf("${", i);
|
|
1177
|
+
if (interStart === -1) {
|
|
1178
|
+
const quasi2 = tplContent.slice(i);
|
|
1179
|
+
const trimmed2 = quasi2.trim();
|
|
1180
|
+
if (trimmed2) {
|
|
1181
|
+
const m = mangleClassString(trimmed2);
|
|
1182
|
+
if (m !== trimmed2) {
|
|
1183
|
+
changed = true;
|
|
1184
|
+
out += quasi2.replace(trimmed2, m);
|
|
1185
|
+
} else {
|
|
1186
|
+
out += quasi2;
|
|
1187
|
+
}
|
|
1188
|
+
} else {
|
|
1189
|
+
out += quasi2;
|
|
1190
|
+
}
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
const quasi = tplContent.slice(i, interStart);
|
|
1194
|
+
const trimmed = quasi.trim();
|
|
1195
|
+
if (trimmed) {
|
|
1196
|
+
const m = mangleClassString(trimmed);
|
|
1197
|
+
if (m !== trimmed) {
|
|
1198
|
+
changed = true;
|
|
1199
|
+
out += quasi.replace(trimmed, m);
|
|
1200
|
+
} else {
|
|
1201
|
+
out += quasi;
|
|
1202
|
+
}
|
|
1203
|
+
} else {
|
|
1204
|
+
out += quasi;
|
|
1205
|
+
}
|
|
1206
|
+
let j = interStart + 2;
|
|
1207
|
+
let depth = 0;
|
|
1208
|
+
while (j < tplContent.length) {
|
|
1209
|
+
if (tplContent[j] === "{") {
|
|
1210
|
+
depth++;
|
|
1211
|
+
} else if (tplContent[j] === "}") {
|
|
1212
|
+
if (depth === 0) {
|
|
1213
|
+
j++;
|
|
1214
|
+
break;
|
|
1215
|
+
}
|
|
1216
|
+
depth--;
|
|
1217
|
+
}
|
|
1218
|
+
j++;
|
|
1219
|
+
}
|
|
1220
|
+
const interInner = tplContent.slice(interStart + 2, j - 1);
|
|
1221
|
+
const mangledInner = interInner.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
1222
|
+
const parts = inner.split(/\s+/).filter(Boolean);
|
|
1223
|
+
if (parts.length === 0) {
|
|
1224
|
+
return qm;
|
|
1225
|
+
}
|
|
1226
|
+
const m = parts.map((p) => mangleMap[p] || p).join(" ");
|
|
1227
|
+
if (m === inner) {
|
|
1228
|
+
return qm;
|
|
1229
|
+
}
|
|
1230
|
+
changed = true;
|
|
1231
|
+
return `"${m}"`;
|
|
1232
|
+
});
|
|
1233
|
+
out += `\${${mangledInner}}`;
|
|
1234
|
+
i = j;
|
|
1235
|
+
}
|
|
1236
|
+
return changed ? `className:\`${out}\`` : fullMatch;
|
|
1237
|
+
});
|
|
1238
|
+
{
|
|
1239
|
+
const marker = "className:";
|
|
1240
|
+
let searchFrom = 0;
|
|
1241
|
+
let out = "";
|
|
1242
|
+
while (searchFrom < result.length) {
|
|
1243
|
+
const idx = result.indexOf(marker, searchFrom);
|
|
1244
|
+
if (idx === -1) {
|
|
1245
|
+
out += result.slice(searchFrom);
|
|
1246
|
+
break;
|
|
1247
|
+
}
|
|
1248
|
+
out += result.slice(searchFrom, idx + marker.length);
|
|
1249
|
+
const afterColon = idx + marker.length;
|
|
1250
|
+
let exprStart = afterColon;
|
|
1251
|
+
while (exprStart < result.length && result[exprStart] === " ") {
|
|
1252
|
+
exprStart++;
|
|
1253
|
+
}
|
|
1254
|
+
const firstChar = result[exprStart];
|
|
1255
|
+
if (firstChar === '"' || firstChar === "'" || firstChar === "`") {
|
|
1256
|
+
searchFrom = afterColon;
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
let depth = 0;
|
|
1260
|
+
let j = afterColon;
|
|
1261
|
+
while (j < result.length) {
|
|
1262
|
+
const ch = result[j];
|
|
1263
|
+
if (ch === "(" || ch === "[") {
|
|
1264
|
+
depth++;
|
|
1265
|
+
} else if (ch === ")" || ch === "]") {
|
|
1266
|
+
if (depth === 0) {
|
|
1267
|
+
break;
|
|
1268
|
+
}
|
|
1269
|
+
depth--;
|
|
1270
|
+
} else if (depth === 0 && (ch === "," || ch === ";" || ch === "\n" || ch === "}")) {
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1273
|
+
j++;
|
|
1274
|
+
}
|
|
1275
|
+
const expr = result.slice(afterColon, j);
|
|
1276
|
+
const qIdx = expr.indexOf("?");
|
|
1277
|
+
if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
|
|
1278
|
+
out += expr;
|
|
1279
|
+
searchFrom = j;
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
let changed = false;
|
|
1283
|
+
const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
|
|
1284
|
+
const parts = inner.split(/\s+/).filter(Boolean);
|
|
1285
|
+
if (parts.length === 0) {
|
|
1286
|
+
return qm;
|
|
1287
|
+
}
|
|
1288
|
+
const mangledStr = parts.map((p) => mangleMap[p] || p).join(" ");
|
|
1289
|
+
if (mangledStr !== inner) {
|
|
1290
|
+
changed = true;
|
|
1291
|
+
return `"${mangledStr}"`;
|
|
1292
|
+
}
|
|
1293
|
+
return qm;
|
|
1294
|
+
});
|
|
1295
|
+
out += changed ? mangled : expr;
|
|
1296
|
+
searchFrom = j;
|
|
1297
|
+
}
|
|
1298
|
+
result = out;
|
|
1299
|
+
}
|
|
1300
|
+
result = result.replace(/(?<=(?:[,(]|&&)\s*)"([^"]+)"/g, (match, inner) => {
|
|
1301
|
+
const tokens = inner.split(/\s+/).filter(Boolean);
|
|
1302
|
+
if (tokens.length === 0) {
|
|
1303
|
+
return match;
|
|
1304
|
+
}
|
|
1305
|
+
let changed = false;
|
|
1306
|
+
const mangled = [];
|
|
1307
|
+
for (const t of tokens) {
|
|
1308
|
+
const m = mangleMap[t];
|
|
1309
|
+
if (m === void 0) {
|
|
1310
|
+
return match;
|
|
1311
|
+
}
|
|
1312
|
+
if (m !== t) {
|
|
1313
|
+
changed = true;
|
|
1314
|
+
}
|
|
1315
|
+
mangled.push(m);
|
|
1316
|
+
}
|
|
1317
|
+
if (!changed) {
|
|
1318
|
+
return match;
|
|
1319
|
+
}
|
|
1320
|
+
return `"${mangled.join(" ")}"`;
|
|
1321
|
+
});
|
|
1322
|
+
return result;
|
|
1323
|
+
}
|
|
1324
|
+
function createCsszyxPlugins(options = {}) {
|
|
1325
|
+
const manglingEnabled = options.production?.mangle !== false;
|
|
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
|
+
}
|
|
1336
|
+
const parserOverride = process.env.CSSZYX_PARSER;
|
|
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();
|
|
1341
|
+
const state = {
|
|
1342
|
+
classes: /* @__PURE__ */ new Set(),
|
|
1343
|
+
mangleMap: {},
|
|
1344
|
+
checksum: "",
|
|
1345
|
+
finalized: false,
|
|
1346
|
+
rootDir: process.cwd(),
|
|
1347
|
+
recoveryTokens: /* @__PURE__ */ new Map(),
|
|
1348
|
+
rscModules: /* @__PURE__ */ new Map()
|
|
1349
|
+
};
|
|
1350
|
+
const SAFELIST_FILENAME = "csszyx-classes.html";
|
|
1351
|
+
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
|
|
1352
|
+
const IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", ".turbo"]);
|
|
1353
|
+
function isUserExcluded(id) {
|
|
1354
|
+
return matchesAnyPattern(id, options.exclude, state.rootDir);
|
|
1355
|
+
}
|
|
1356
|
+
function isUserIncluded(id) {
|
|
1357
|
+
return !options.include || matchesAnyPattern(id, options.include, state.rootDir);
|
|
1358
|
+
}
|
|
1359
|
+
function isHardIgnored(id) {
|
|
1360
|
+
return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
|
|
1361
|
+
}
|
|
1362
|
+
function shouldProcessSource(id) {
|
|
1363
|
+
return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.[tj]sx?(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
|
|
1364
|
+
}
|
|
1365
|
+
function shouldProcessCss(id) {
|
|
1366
|
+
return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
|
|
1367
|
+
}
|
|
1368
|
+
function transformConfiguredSource(source, filename) {
|
|
1369
|
+
const compilerOptions = { astBudget: astBudgetOverride };
|
|
1370
|
+
const effectiveFilename = normalizeSourceFilename(filename);
|
|
1371
|
+
const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
|
|
1372
|
+
if (cacheEnabled) {
|
|
1373
|
+
evictTransformCacheOnce();
|
|
1374
|
+
}
|
|
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
|
+
}
|
|
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
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
function writeSafelistFile(classes) {
|
|
1450
|
+
if (classes.size === 0) {
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
|
|
1454
|
+
const classList = Array.from(classes).join(" ");
|
|
1455
|
+
const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
|
|
1456
|
+
<!-- Tailwind CSS scans this file for class name detection -->
|
|
1457
|
+
<div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
|
|
1458
|
+
`;
|
|
1459
|
+
try {
|
|
1460
|
+
const existing = fs.existsSync(safelistPath) ? fs.readFileSync(safelistPath, "utf-8") : "";
|
|
1461
|
+
if (existing !== content) {
|
|
1462
|
+
fs.writeFileSync(safelistPath, content);
|
|
1463
|
+
}
|
|
1464
|
+
} catch {
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
function prescanAndWriteClasses() {
|
|
1468
|
+
const discoveredClasses = /* @__PURE__ */ new Set();
|
|
1469
|
+
const rawDiscoveredClasses = /* @__PURE__ */ new Set();
|
|
1470
|
+
function scanDir(dir) {
|
|
1471
|
+
let entries;
|
|
1472
|
+
try {
|
|
1473
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1474
|
+
} catch {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
for (const entry of entries) {
|
|
1478
|
+
if (entry.isDirectory()) {
|
|
1479
|
+
if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
1480
|
+
scanDir(path.join(dir, entry.name));
|
|
1481
|
+
}
|
|
1482
|
+
} else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
1483
|
+
const filePath = path.join(dir, entry.name);
|
|
1484
|
+
if (!shouldProcessSource(filePath)) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1489
|
+
if (!content.includes("sz=") && !content.includes("sz:")) {
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
const result = transformConfiguredSource(content, filePath);
|
|
1493
|
+
if (!result.transformed) {
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
for (const cls of result.classes) {
|
|
1497
|
+
discoveredClasses.add(cls);
|
|
1498
|
+
}
|
|
1499
|
+
for (const cls of result.rawClassNames) {
|
|
1500
|
+
rawDiscoveredClasses.add(cls);
|
|
1501
|
+
}
|
|
1502
|
+
for (const [token, data] of result.recoveryTokens) {
|
|
1503
|
+
state.recoveryTokens.set(token, data);
|
|
1504
|
+
}
|
|
1505
|
+
if (result.usesRuntime) {
|
|
1506
|
+
const szCallRe = /_sz\(\s*\{/g;
|
|
1507
|
+
for (const szMatch of result.code.matchAll(szCallRe)) {
|
|
1508
|
+
let depth = 1;
|
|
1509
|
+
let idx = (szMatch.index ?? 0) + szMatch[0].length;
|
|
1510
|
+
while (idx < result.code.length && depth > 0) {
|
|
1511
|
+
if (result.code[idx] === "{") {
|
|
1512
|
+
depth++;
|
|
1513
|
+
} else if (result.code[idx] === "}") {
|
|
1514
|
+
depth--;
|
|
1515
|
+
}
|
|
1516
|
+
idx++;
|
|
1517
|
+
}
|
|
1518
|
+
const objStr = result.code.slice(
|
|
1519
|
+
(szMatch.index ?? 0) + szMatch[0].length,
|
|
1520
|
+
idx - 1
|
|
1521
|
+
);
|
|
1522
|
+
const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
1523
|
+
for (const kv of objStr.matchAll(strKv)) {
|
|
1524
|
+
try {
|
|
1525
|
+
const val = kv[2] ?? kv[3];
|
|
1526
|
+
const r = transform({ [kv[1]]: val });
|
|
1527
|
+
for (const c of r.className.split(/\s+/).filter(Boolean)) {
|
|
1528
|
+
discoveredClasses.add(c);
|
|
1529
|
+
}
|
|
1530
|
+
} catch {
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
|
|
1534
|
+
for (const kv of objStr.matchAll(numKv)) {
|
|
1535
|
+
try {
|
|
1536
|
+
const r = transform({ [kv[1]]: parseFloat(kv[2]) });
|
|
1537
|
+
for (const c of r.className.split(/\s+/).filter(Boolean)) {
|
|
1538
|
+
discoveredClasses.add(c);
|
|
1539
|
+
}
|
|
1540
|
+
} catch {
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
|
|
1544
|
+
for (const kv of objStr.matchAll(boolKv)) {
|
|
1545
|
+
try {
|
|
1546
|
+
const r = transform({ [kv[1]]: kv[2] === "true" });
|
|
1547
|
+
for (const c of r.className.split(/\s+/).filter(Boolean)) {
|
|
1548
|
+
discoveredClasses.add(c);
|
|
1549
|
+
}
|
|
1550
|
+
} catch {
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
scanDir(state.rootDir);
|
|
1561
|
+
for (const cls of discoveredClasses) {
|
|
1562
|
+
state.classes.add(cls);
|
|
1563
|
+
}
|
|
1564
|
+
const safelistClasses = /* @__PURE__ */ new Set([...discoveredClasses, ...rawDiscoveredClasses]);
|
|
1565
|
+
writeSafelistFile(safelistClasses);
|
|
1566
|
+
}
|
|
1567
|
+
function extractClasses(code) {
|
|
1568
|
+
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
1569
|
+
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
1570
|
+
for (const classPattern of [dqPattern, sqPattern]) {
|
|
1571
|
+
for (const match of code.matchAll(classPattern)) {
|
|
1572
|
+
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
1573
|
+
for (const cls of classes) {
|
|
1574
|
+
state.classes.add(cls);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
const exprStart = /className=\{/g;
|
|
1579
|
+
for (const match of code.matchAll(exprStart)) {
|
|
1580
|
+
let depth = 1;
|
|
1581
|
+
let i = (match.index ?? 0) + match[0].length;
|
|
1582
|
+
while (i < code.length && depth > 0) {
|
|
1583
|
+
if (code[i] === "{") {
|
|
1584
|
+
depth++;
|
|
1585
|
+
} else if (code[i] === "}") {
|
|
1586
|
+
depth--;
|
|
1587
|
+
}
|
|
1588
|
+
i++;
|
|
1589
|
+
}
|
|
1590
|
+
const expr = code.slice((match.index ?? 0) + match[0].length, i - 1);
|
|
1591
|
+
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
1592
|
+
for (const strMatch of expr.matchAll(strPattern)) {
|
|
1593
|
+
const str = strMatch[1] || strMatch[2];
|
|
1594
|
+
const classes = str.split(/\s+/).filter(Boolean);
|
|
1595
|
+
for (const cls of classes) {
|
|
1596
|
+
state.classes.add(cls);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
function finalizeMangleMap() {
|
|
1602
|
+
const sortedClasses = Array.from(state.classes);
|
|
1603
|
+
const newMap = {};
|
|
1604
|
+
for (let i = 0; i < sortedClasses.length; i++) {
|
|
1605
|
+
newMap[sortedClasses[i]] = encode(i);
|
|
1606
|
+
}
|
|
1607
|
+
state.mangleMap = newMap;
|
|
1608
|
+
state.checksum = compute_mangle_checksum(state.mangleMap);
|
|
1609
|
+
state.finalized = true;
|
|
1610
|
+
}
|
|
1611
|
+
function mangleCodeClasses(code) {
|
|
1612
|
+
return mangleCodeClassesSync(code, state.mangleMap);
|
|
1613
|
+
}
|
|
1614
|
+
function replacePlaceholders(code) {
|
|
1615
|
+
let result = code;
|
|
1616
|
+
if (result.includes(CHECKSUM_PLACEHOLDER)) {
|
|
1617
|
+
result = result.split(CHECKSUM_PLACEHOLDER).join(state.checksum);
|
|
1618
|
+
}
|
|
1619
|
+
if (result.includes(MANGLE_MAP_PLACEHOLDER)) {
|
|
1620
|
+
const jsonMap = JSON.stringify(state.mangleMap);
|
|
1621
|
+
const escapedMap = result.includes("eval(") ? jsonMap.replace(/"/g, '\\"') : jsonMap;
|
|
1622
|
+
result = result.split(MANGLE_MAP_PLACEHOLDER).join(escapedMap);
|
|
1623
|
+
}
|
|
1624
|
+
return result;
|
|
1625
|
+
}
|
|
1626
|
+
const prePlugin = createUnplugin(
|
|
1627
|
+
(_pluginOptions) => ({
|
|
1628
|
+
name: "csszyx:pre",
|
|
1629
|
+
enforce: "pre",
|
|
1630
|
+
/**
|
|
1631
|
+
* Resolves virtual module IDs for csszyx mangle-map and checksum modules.
|
|
1632
|
+
* @param id - the module ID to resolve
|
|
1633
|
+
* @returns resolved ID if virtual, null otherwise
|
|
1634
|
+
*/
|
|
1635
|
+
resolveId(id) {
|
|
1636
|
+
if (isVirtualModule(id)) {
|
|
1637
|
+
return resolveVirtualModule(id);
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
},
|
|
1641
|
+
/**
|
|
1642
|
+
* Loads virtual module content — generates mangle map or checksum module code.
|
|
1643
|
+
* @param id - the resolved module ID to load
|
|
1644
|
+
* @returns generated module source if virtual, null otherwise
|
|
1645
|
+
*/
|
|
1646
|
+
load(id) {
|
|
1647
|
+
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
1648
|
+
finalizeMangleMap();
|
|
1649
|
+
return createMangleMapModule(state.mangleMap, state.checksum);
|
|
1650
|
+
}
|
|
1651
|
+
if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
|
|
1652
|
+
finalizeMangleMap();
|
|
1653
|
+
return createChecksumModule(state.checksum);
|
|
1654
|
+
}
|
|
1655
|
+
return null;
|
|
1656
|
+
},
|
|
1657
|
+
/**
|
|
1658
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
1659
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
1660
|
+
* @param id - the file path to check for inclusion
|
|
1661
|
+
* @returns true if the file should be transformed, false otherwise
|
|
1662
|
+
*/
|
|
1663
|
+
transformInclude(id) {
|
|
1664
|
+
if (shouldProcessCss(id)) {
|
|
1665
|
+
return true;
|
|
1666
|
+
}
|
|
1667
|
+
return shouldProcessSource(id);
|
|
1668
|
+
},
|
|
1669
|
+
/**
|
|
1670
|
+
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
1671
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
1672
|
+
* @param code - the source code to transform
|
|
1673
|
+
* @param id - the file path of the module being transformed
|
|
1674
|
+
* @returns transformed code with source map, or null if no changes were made
|
|
1675
|
+
*/
|
|
1676
|
+
transform(code, id) {
|
|
1677
|
+
if (!shouldProcessCss(id) && !shouldProcessSource(id)) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1681
|
+
assertNoRSCBoundaryViolation(code, id);
|
|
1682
|
+
}
|
|
1683
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
1684
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
1685
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
1686
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
1687
|
+
if (candidates) {
|
|
1688
|
+
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
|
|
1689
|
+
const cssDir = path.dirname(id).replace(/\\/g, "/");
|
|
1690
|
+
let relPath = path.posix.relative(cssDir, safelistPath);
|
|
1691
|
+
if (!relPath.startsWith(".")) {
|
|
1692
|
+
relPath = `./${relPath}`;
|
|
1693
|
+
}
|
|
1694
|
+
const sourceDirective = `@source "${relPath}";
|
|
1695
|
+
`;
|
|
1696
|
+
const transformed2 = code.replace(
|
|
1697
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
1698
|
+
`$1
|
|
1699
|
+
${sourceDirective}`
|
|
1700
|
+
);
|
|
1701
|
+
if (transformed2 !== code) {
|
|
1702
|
+
return { code: transformed2, map: null };
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
let transformedCode = code;
|
|
1709
|
+
let usesRuntime = false;
|
|
1710
|
+
let usesMerge = false;
|
|
1711
|
+
let usesColorVar = false;
|
|
1712
|
+
let transformed = false;
|
|
1713
|
+
let szClasses;
|
|
1714
|
+
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
1715
|
+
if (hasSzProp) {
|
|
1716
|
+
if (id.endsWith(".vue")) {
|
|
1717
|
+
const result = preprocess(code, options);
|
|
1718
|
+
if (result.transformed) {
|
|
1719
|
+
transformedCode = result.code;
|
|
1720
|
+
transformed = true;
|
|
1721
|
+
}
|
|
1722
|
+
} else if (id.endsWith(".svelte")) {
|
|
1723
|
+
const result = preprocess$1(code, options);
|
|
1724
|
+
if (result) {
|
|
1725
|
+
transformedCode = result.code;
|
|
1726
|
+
transformed = true;
|
|
1727
|
+
}
|
|
1728
|
+
} else {
|
|
1729
|
+
const transformStarted = performance.now();
|
|
1730
|
+
const result = transformConfiguredSource(code, id);
|
|
1731
|
+
traceBenchTiming(
|
|
1732
|
+
"transform-hook",
|
|
1733
|
+
id,
|
|
1734
|
+
performance.now() - transformStarted
|
|
1735
|
+
);
|
|
1736
|
+
transformedCode = result.code;
|
|
1737
|
+
usesRuntime = result.usesRuntime;
|
|
1738
|
+
usesMerge = result.usesMerge;
|
|
1739
|
+
usesColorVar = result.usesColorVar;
|
|
1740
|
+
transformed = result.transformed;
|
|
1741
|
+
szClasses = result.classes;
|
|
1742
|
+
if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
|
|
1743
|
+
for (const msg of result.diagnostics) {
|
|
1744
|
+
this.warn(`[csszyx] ${id}
|
|
1745
|
+
${msg}`);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
for (const [token, data] of result.recoveryTokens) {
|
|
1749
|
+
state.recoveryTokens.set(token, data);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
1754
|
+
const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
|
|
1755
|
+
transformedCode = transformedCode.replace(
|
|
1756
|
+
/<html([^>]*)>/i,
|
|
1757
|
+
`<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
|
|
1758
|
+
);
|
|
1759
|
+
const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var r={};for(var k in m)r[m[k]]=k;window.__csszyx={mangleMap:m,checksum:"${CHECKSUM_PLACEHOLDER}",decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()\`}} />`;
|
|
1760
|
+
if (transformedCode.includes("<body")) {
|
|
1761
|
+
transformedCode = transformedCode.replace(
|
|
1762
|
+
/(<body[^>]*>)/i,
|
|
1763
|
+
`$1${debugScript}`
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
transformed = true;
|
|
1767
|
+
}
|
|
1768
|
+
{
|
|
1769
|
+
const imports = [];
|
|
1770
|
+
if (usesRuntime) {
|
|
1771
|
+
imports.push("_sz");
|
|
1772
|
+
}
|
|
1773
|
+
if (usesMerge) {
|
|
1774
|
+
imports.push("_szMerge");
|
|
1775
|
+
}
|
|
1776
|
+
if (usesColorVar) {
|
|
1777
|
+
imports.push("__szColorVar");
|
|
1778
|
+
}
|
|
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;
|
|
1783
|
+
if (needed.length > 0) {
|
|
1784
|
+
const existingImport = transformedCode.match(
|
|
1785
|
+
/^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
|
|
1786
|
+
);
|
|
1787
|
+
if (existingImport) {
|
|
1788
|
+
transformedCode = transformedCode.replace(
|
|
1789
|
+
existingImport[0],
|
|
1790
|
+
`${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
|
|
1791
|
+
);
|
|
1792
|
+
} else {
|
|
1793
|
+
const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
|
|
1794
|
+
`;
|
|
1795
|
+
transformedCode = insertRuntimeImport(transformedCode, importStmt);
|
|
1796
|
+
}
|
|
1797
|
+
transformed = true;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1801
|
+
assertNoRSCBoundaryViolation(transformedCode, id);
|
|
1802
|
+
const record = createRSCModuleRecord(transformedCode, id);
|
|
1803
|
+
state.rscModules.set(record.id, record);
|
|
1804
|
+
}
|
|
1805
|
+
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
1806
|
+
if (szClasses !== void 0) {
|
|
1807
|
+
for (const cls of szClasses) {
|
|
1808
|
+
state.classes.add(cls);
|
|
1809
|
+
}
|
|
1810
|
+
} else {
|
|
1811
|
+
extractClasses(transformedCode);
|
|
1812
|
+
}
|
|
1813
|
+
return { code: transformedCode, map: null };
|
|
1814
|
+
}
|
|
1815
|
+
return null;
|
|
1816
|
+
},
|
|
1817
|
+
/** Finalizes the mangle map after all source modules have been processed. */
|
|
1818
|
+
buildEnd() {
|
|
1819
|
+
finalizeMangleMap();
|
|
1820
|
+
assertNoRSCGraphViolation(state.rscModules);
|
|
1821
|
+
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
1822
|
+
globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
|
|
1823
|
+
}
|
|
1824
|
+
},
|
|
1825
|
+
watchChange(id, change) {
|
|
1826
|
+
if (change.event === "delete") {
|
|
1827
|
+
deleteRSCModuleRecord(state.rscModules, id);
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1830
|
+
/**
|
|
1831
|
+
* Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
|
|
1832
|
+
* @param compiler - the Webpack compiler instance
|
|
1833
|
+
*/
|
|
1834
|
+
webpack(compiler) {
|
|
1835
|
+
compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
|
|
1836
|
+
const root = compiler.context || process.cwd();
|
|
1837
|
+
state.rootDir = root;
|
|
1838
|
+
evictTransformCacheOnce();
|
|
1839
|
+
if (state.classes.size === 0) {
|
|
1840
|
+
prescanAndWriteClasses();
|
|
1841
|
+
}
|
|
1842
|
+
runThemeScan(root, options.build?.scanCss);
|
|
1843
|
+
});
|
|
1844
|
+
if (options.build?.scanCss) {
|
|
1845
|
+
compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
|
|
1846
|
+
const root = compiler.context || process.cwd();
|
|
1847
|
+
for (const file of expandFilePatterns(root, options.build?.scanCss ?? [])) {
|
|
1848
|
+
compilation.fileDependencies.add(file);
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
},
|
|
1853
|
+
vite: {
|
|
1854
|
+
/**
|
|
1855
|
+
* Vite hook: pre-scans source files when config is resolved.
|
|
1856
|
+
* Also runs theme scan to generate .csszyx/theme.d.ts if scanCss is configured.
|
|
1857
|
+
* @param config - the resolved Vite configuration object
|
|
1858
|
+
*/
|
|
1859
|
+
configResolved(config) {
|
|
1860
|
+
const root = config.root || process.cwd();
|
|
1861
|
+
state.rootDir = root;
|
|
1862
|
+
evictTransformCacheOnce();
|
|
1863
|
+
prescanAndWriteClasses();
|
|
1864
|
+
runThemeScan(root, options.build?.scanCss);
|
|
1865
|
+
},
|
|
1866
|
+
/**
|
|
1867
|
+
* Vite HMR hook: re-runs theme scan when a watched CSS file changes,
|
|
1868
|
+
* and incrementally updates csszyx-classes.html when a source file gains new sz classes.
|
|
1869
|
+
* @param ctx - HMR context containing the changed file
|
|
1870
|
+
*/
|
|
1871
|
+
handleHotUpdate(ctx) {
|
|
1872
|
+
const scanCss = options.build?.scanCss;
|
|
1873
|
+
if (scanCss) {
|
|
1874
|
+
const root = ctx.server.config.root || process.cwd();
|
|
1875
|
+
if (matchesAnyPattern(ctx.file, scanCss, root)) {
|
|
1876
|
+
runThemeScan(root, scanCss);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
if (!shouldProcessSource(ctx.file)) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
let fileContent, result;
|
|
1883
|
+
try {
|
|
1884
|
+
fileContent = fs.readFileSync(ctx.file, "utf-8");
|
|
1885
|
+
} catch {
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
try {
|
|
1892
|
+
const hmrTransformStarted = performance.now();
|
|
1893
|
+
result = transformConfiguredSource(fileContent, ctx.file);
|
|
1894
|
+
traceBenchTiming(
|
|
1895
|
+
"handle-hot-update",
|
|
1896
|
+
ctx.file,
|
|
1897
|
+
performance.now() - hmrTransformStarted
|
|
1898
|
+
);
|
|
1899
|
+
} catch {
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
if (!result.transformed) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
const sizeBefore = state.classes.size;
|
|
1906
|
+
for (const cls of result.classes) {
|
|
1907
|
+
state.classes.add(cls);
|
|
1908
|
+
}
|
|
1909
|
+
for (const [token, data] of result.recoveryTokens) {
|
|
1910
|
+
state.recoveryTokens.set(token, data);
|
|
1911
|
+
}
|
|
1912
|
+
if (state.classes.size > sizeBefore) {
|
|
1913
|
+
writeSafelistFile(state.classes);
|
|
1914
|
+
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
|
|
1915
|
+
ctx.server.watcher.emit("change", safelistPath);
|
|
1916
|
+
}
|
|
1917
|
+
},
|
|
1918
|
+
transformIndexHtml: {
|
|
1919
|
+
order: "pre",
|
|
1920
|
+
/**
|
|
1921
|
+
* Injects hydration data (mangle map + checksum) into the HTML document.
|
|
1922
|
+
* Also mangles class attributes in SSR-rendered HTML so they match mangled CSS selectors.
|
|
1923
|
+
* @param html - the raw HTML string to transform
|
|
1924
|
+
* @returns transformed HTML with injected hydration data
|
|
1925
|
+
*/
|
|
1926
|
+
handler(html) {
|
|
1927
|
+
finalizeMangleMap();
|
|
1928
|
+
let result = transformIndexHtml(html, state.mangleMap, state.checksum, {
|
|
1929
|
+
mode: options.production?.injectChecksum === false ? "script" : "script",
|
|
1930
|
+
minify: process.env.NODE_ENV === "production"
|
|
1931
|
+
});
|
|
1932
|
+
if (state.recoveryTokens.size > 0) {
|
|
1933
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
1934
|
+
const { manifest, strippedDevOnlyPaths } = buildRecoveryManifest(
|
|
1935
|
+
state.recoveryTokens,
|
|
1936
|
+
{
|
|
1937
|
+
production: isProduction,
|
|
1938
|
+
mangleChecksum: state.checksum
|
|
1939
|
+
}
|
|
1940
|
+
);
|
|
1941
|
+
if (strippedDevOnlyPaths.length > 0) {
|
|
1942
|
+
console.warn(
|
|
1943
|
+
`[csszyx] Stripped ${strippedDevOnlyPaths.length} szRecover="dev-only" token(s) from the production manifest. Recovery for these elements is disabled in production by design. Sites: ${strippedDevOnlyPaths.join(", ")}`
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
result = injectRecoveryManifest(result, manifest);
|
|
1947
|
+
}
|
|
1948
|
+
return result;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
})
|
|
1953
|
+
);
|
|
1954
|
+
const postPlugin = createUnplugin(() => ({
|
|
1955
|
+
name: "csszyx:post",
|
|
1956
|
+
enforce: "post",
|
|
1957
|
+
// No transform hook — all mangling is deferred to asset processing
|
|
1958
|
+
// where the complete mangle map is available.
|
|
1959
|
+
/**
|
|
1960
|
+
* Webpack hook: mangles CSS/JS class names in processAssets after compilation.
|
|
1961
|
+
* @param compiler - the Webpack compiler instance
|
|
1962
|
+
*/
|
|
1963
|
+
webpack(compiler) {
|
|
1964
|
+
compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
|
|
1965
|
+
const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
|
|
1966
|
+
compilation.hooks.processAssets.tap(
|
|
1967
|
+
{
|
|
1968
|
+
name: "csszyx:post",
|
|
1969
|
+
stage: stage || 400
|
|
1970
|
+
// Fallback integer
|
|
1971
|
+
},
|
|
1972
|
+
(assets) => {
|
|
1973
|
+
finalizeMangleMap();
|
|
1974
|
+
const isWebpackDevMode = compiler.options.mode === "development";
|
|
1975
|
+
const manifestData = {
|
|
1976
|
+
version: "0.4.0",
|
|
1977
|
+
buildId: state.checksum,
|
|
1978
|
+
classes: Object.keys(state.mangleMap)
|
|
1979
|
+
};
|
|
1980
|
+
if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
|
|
1981
|
+
manifestData.mangleMap = state.mangleMap;
|
|
1982
|
+
}
|
|
1983
|
+
compilation.emitAsset(
|
|
1984
|
+
"csszyx-manifest.json",
|
|
1985
|
+
new compiler.webpack.sources.RawSource(JSON.stringify(manifestData))
|
|
1986
|
+
);
|
|
1987
|
+
for (const file in assets) {
|
|
1988
|
+
const asset = assets[file];
|
|
1989
|
+
const source = asset.source().toString();
|
|
1990
|
+
if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
|
|
1991
|
+
if (file.endsWith(".css")) {
|
|
1992
|
+
try {
|
|
1993
|
+
const result = mangleCSSSync(source, state.mangleMap, {
|
|
1994
|
+
debug: options.development?.debug,
|
|
1995
|
+
from: file
|
|
1996
|
+
});
|
|
1997
|
+
if (result.transformedCount > 0) {
|
|
1998
|
+
compilation.updateAsset(
|
|
1999
|
+
file,
|
|
2000
|
+
new compiler.webpack.sources.RawSource(result.css)
|
|
2001
|
+
);
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
} catch (e) {
|
|
2005
|
+
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
|
|
2006
|
+
throw e;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
} else if (file.endsWith(".html")) {
|
|
2010
|
+
const mangledHtml = source.replace(
|
|
2011
|
+
/\bclass="([^"]*)"/g,
|
|
2012
|
+
(_m, cls) => {
|
|
2013
|
+
const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
|
|
2014
|
+
return out !== cls ? `class="${out}"` : _m;
|
|
2015
|
+
}
|
|
2016
|
+
).replace(
|
|
2017
|
+
/\bclass='([^']*)'/g,
|
|
2018
|
+
(_m, cls) => {
|
|
2019
|
+
const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
|
|
2020
|
+
return out !== cls ? `class='${out}'` : _m;
|
|
2021
|
+
}
|
|
2022
|
+
);
|
|
2023
|
+
if (mangledHtml !== source) {
|
|
2024
|
+
compilation.updateAsset(
|
|
2025
|
+
file,
|
|
2026
|
+
new compiler.webpack.sources.RawSource(mangledHtml)
|
|
2027
|
+
);
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
} else if (file.endsWith(".js")) {
|
|
2031
|
+
let mangled = mangleCodeClasses(source);
|
|
2032
|
+
mangled = replacePlaceholders(mangled);
|
|
2033
|
+
if (mangled !== source) {
|
|
2034
|
+
compilation.updateAsset(
|
|
2035
|
+
file,
|
|
2036
|
+
new compiler.webpack.sources.RawSource(mangled)
|
|
2037
|
+
);
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
if (file.endsWith(".js") && (source.includes(CHECKSUM_PLACEHOLDER) || source.includes(MANGLE_MAP_PLACEHOLDER))) {
|
|
2043
|
+
const replaced = replacePlaceholders(source);
|
|
2044
|
+
if (replaced !== source) {
|
|
2045
|
+
compilation.updateAsset(
|
|
2046
|
+
file,
|
|
2047
|
+
new compiler.webpack.sources.RawSource(replaced)
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
);
|
|
2054
|
+
});
|
|
2055
|
+
},
|
|
2056
|
+
vite: {
|
|
2057
|
+
/**
|
|
2058
|
+
* Vite hook: mangles CSS selectors and JS class strings in the final bundle.
|
|
2059
|
+
* @param _options - the output options (unused)
|
|
2060
|
+
* @param bundle - the output bundle containing chunks and assets to process
|
|
2061
|
+
*/
|
|
2062
|
+
generateBundle(_options, bundle) {
|
|
2063
|
+
finalizeMangleMap();
|
|
2064
|
+
const manifestData = {
|
|
2065
|
+
version: "0.4.0",
|
|
2066
|
+
buildId: state.checksum,
|
|
2067
|
+
classes: Object.keys(state.mangleMap)
|
|
2068
|
+
};
|
|
2069
|
+
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
2070
|
+
manifestData.mangleMap = state.mangleMap;
|
|
2071
|
+
}
|
|
2072
|
+
this.emitFile({
|
|
2073
|
+
type: "asset",
|
|
2074
|
+
fileName: "csszyx-manifest.json",
|
|
2075
|
+
source: JSON.stringify(manifestData)
|
|
2076
|
+
});
|
|
2077
|
+
for (const file in bundle) {
|
|
2078
|
+
const chunk = bundle[file];
|
|
2079
|
+
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
2080
|
+
if (chunk.type === "asset" && chunk.fileName.endsWith(".css")) {
|
|
2081
|
+
const css = chunk.source.toString();
|
|
2082
|
+
try {
|
|
2083
|
+
const result = mangleCSSSync(css, state.mangleMap, {
|
|
2084
|
+
debug: options.development?.debug,
|
|
2085
|
+
from: file
|
|
2086
|
+
});
|
|
2087
|
+
if (result.transformedCount > 0) {
|
|
2088
|
+
chunk.source = result.css;
|
|
2089
|
+
}
|
|
2090
|
+
} catch (e) {
|
|
2091
|
+
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
|
|
2092
|
+
throw e;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
continue;
|
|
2096
|
+
} else if (chunk.type === "chunk") {
|
|
2097
|
+
let mangledCode = mangleCodeClasses(chunk.code);
|
|
2098
|
+
mangledCode = replacePlaceholders(mangledCode);
|
|
2099
|
+
if (mangledCode !== chunk.code) {
|
|
2100
|
+
chunk.code = mangledCode;
|
|
2101
|
+
}
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
if (chunk.type === "chunk" && (chunk.code.includes(CHECKSUM_PLACEHOLDER) || chunk.code.includes(MANGLE_MAP_PLACEHOLDER))) {
|
|
2106
|
+
const replaced = replacePlaceholders(chunk.code);
|
|
2107
|
+
if (replaced !== chunk.code) {
|
|
2108
|
+
chunk.code = replaced;
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}));
|
|
2115
|
+
return { prePlugin, postPlugin };
|
|
2116
|
+
}
|
|
2117
|
+
const defaultInstance = createCsszyxPlugins();
|
|
2118
|
+
const unplugin = defaultInstance.prePlugin;
|
|
2119
|
+
const vitePlugin = (options = {}) => {
|
|
2120
|
+
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
2121
|
+
return [prePlugin.vite(options), postPlugin.vite(options)];
|
|
2122
|
+
};
|
|
2123
|
+
const webpackPlugin = (options = {}) => {
|
|
2124
|
+
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
2125
|
+
return {
|
|
2126
|
+
/**
|
|
2127
|
+
* Applies both pre and post plugins to the Webpack compiler.
|
|
2128
|
+
* @param compiler - the Webpack compiler instance to apply plugins to
|
|
2129
|
+
*/
|
|
2130
|
+
apply(compiler) {
|
|
2131
|
+
prePlugin.webpack(options).apply(compiler);
|
|
2132
|
+
postPlugin.webpack(options).apply(compiler);
|
|
2133
|
+
}
|
|
2134
|
+
};
|
|
2135
|
+
};
|
|
2136
|
+
const rollupPlugin = (options = {}) => {
|
|
2137
|
+
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
2138
|
+
return [
|
|
2139
|
+
prePlugin.rollup(options),
|
|
2140
|
+
postPlugin.rollup(options)
|
|
2141
|
+
];
|
|
2142
|
+
};
|
|
2143
|
+
const esbuildPlugin = (options = {}) => {
|
|
2144
|
+
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
2145
|
+
return {
|
|
2146
|
+
name: "csszyx",
|
|
2147
|
+
/**
|
|
2148
|
+
* Registers both pre and post plugin setup hooks with the esbuild build.
|
|
2149
|
+
* @param build - the esbuild plugin build context
|
|
2150
|
+
*/
|
|
2151
|
+
setup(build) {
|
|
2152
|
+
const b = build;
|
|
2153
|
+
prePlugin.esbuild(options).setup(b);
|
|
2154
|
+
postPlugin.esbuild(options).setup(b);
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
};
|
|
2158
|
+
|
|
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 };
|