@csszyx/unplugin 0.6.2 → 0.8.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 +23 -1
- package/dist/css-mangler.cjs +23 -68
- 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} +9 -27
- package/dist/index.cjs +42 -1530
- package/dist/index.d.cts +101 -2
- package/dist/index.d.mts +154 -0
- package/dist/index.mjs +12 -0
- package/dist/shared/unplugin.BNsv2szs.cjs +1753 -0
- package/dist/{chunk-JGJOUK2R.js → shared/unplugin.DCv0RtVZ.mjs} +814 -376
- package/dist/vite.cjs +15 -1363
- package/dist/vite.d.cts +2 -2
- package/dist/{vite.d.ts → vite.d.mts} +1 -1
- package/dist/vite.mjs +16 -0
- package/dist/webpack.cjs +15 -1372
- package/dist/webpack.d.cts +2 -2
- package/dist/{webpack.d.ts → webpack.d.mts} +1 -1
- package/dist/webpack.mjs +16 -0
- package/package.json +51 -27
- package/dist/css-mangler.js +0 -14
- package/dist/index.d.ts +0 -58
- package/dist/index.js +0 -35
- 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
|
@@ -1,9 +1,312 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { transformSourceCode, transformOxc, transform } from '@csszyx/compiler';
|
|
6
|
+
import { encode, compute_mangle_checksum } from '@csszyx/core';
|
|
7
|
+
import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
|
|
8
|
+
import { preprocess } from '@csszyx/vue-adapter';
|
|
9
|
+
import { createUnplugin } from 'unplugin';
|
|
10
|
+
import { mangleCSSSync } from '../css-mangler.mjs';
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
4
12
|
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
const SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
|
|
14
|
+
const CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
|
|
15
|
+
const RUNTIME_MODULES = /* @__PURE__ */ new Set([
|
|
16
|
+
"@csszyx/runtime",
|
|
17
|
+
"@csszyx/runtime/lite",
|
|
18
|
+
"csszyx",
|
|
19
|
+
"csszyx/lite"
|
|
20
|
+
]);
|
|
21
|
+
const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
|
|
22
|
+
"_sz",
|
|
23
|
+
"_sz2",
|
|
24
|
+
"_sz3",
|
|
25
|
+
"_szIf",
|
|
26
|
+
"_szMerge",
|
|
27
|
+
"_szSwitch",
|
|
28
|
+
"__csszyx_runtime__"
|
|
29
|
+
]);
|
|
30
|
+
function hasUseServerDirective(code) {
|
|
31
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
32
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function hasUseClientDirective(code) {
|
|
42
|
+
for (const statement of readDirectivePrologue(code)) {
|
|
43
|
+
if (CLIENT_DIRECTIVE_RE.test(statement)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (SERVER_DIRECTIVE_RE.test(statement)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function isRSCServerModule(code, id) {
|
|
53
|
+
if (hasUseServerDirective(code)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (hasUseClientDirective(code)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return isNextAppRouterEntry(id);
|
|
60
|
+
}
|
|
61
|
+
function findRSCBoundaryViolation(code, id) {
|
|
62
|
+
if (!isRSCServerModule(code, id)) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
for (const imported of findRuntimeImports(code)) {
|
|
66
|
+
for (const symbol of imported.symbols) {
|
|
67
|
+
if (FORBIDDEN_SYMBOLS.has(symbol)) {
|
|
68
|
+
return {
|
|
69
|
+
symbol,
|
|
70
|
+
path: id,
|
|
71
|
+
importChain: [id, imported.source]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function createRSCModuleRecord(code, id) {
|
|
79
|
+
const normalized = normalizeModuleId(id);
|
|
80
|
+
return {
|
|
81
|
+
id: normalized,
|
|
82
|
+
isServer: isRSCServerModule(code, normalized),
|
|
83
|
+
isClient: hasUseClientDirective(code),
|
|
84
|
+
imports: findLocalImportSources(code).map((source) => resolveLocalModule(normalized, source)).filter((resolved) => resolved !== null),
|
|
85
|
+
runtimeImports: findRuntimeImports(code).filter(
|
|
86
|
+
(imported) => imported.symbols.some((symbol) => FORBIDDEN_SYMBOLS.has(symbol))
|
|
87
|
+
)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function findRSCGraphViolation(records) {
|
|
91
|
+
for (const root of records.values()) {
|
|
92
|
+
if (!root.isServer) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const violation = walkRSCGraph(root, records, [root.id], /* @__PURE__ */ new Set([root.id]));
|
|
96
|
+
if (violation) {
|
|
97
|
+
return {
|
|
98
|
+
symbol: violation.symbol,
|
|
99
|
+
path: root.id,
|
|
100
|
+
importChain: violation.importChain
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function assertNoRSCGraphViolation(records) {
|
|
107
|
+
const violation = findRSCGraphViolation(records);
|
|
108
|
+
if (!violation) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
throw new Error(formatRSCViolation(violation));
|
|
112
|
+
}
|
|
113
|
+
function isNextAppRouterEntry(id) {
|
|
114
|
+
const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
|
|
115
|
+
return /(^|\/)app\/.*\/?(?:page|layout|template|loading|error|not-found|global-error|default|route)\.[cm]?[tj]sx?$/.test(
|
|
116
|
+
clean
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function assertNoRSCBoundaryViolation(code, id) {
|
|
120
|
+
const violation = findRSCBoundaryViolation(code, id);
|
|
121
|
+
if (!violation) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
throw new Error(formatRSCViolation(violation));
|
|
125
|
+
}
|
|
126
|
+
function formatRSCViolation(violation) {
|
|
127
|
+
return `csszyxRSCViolation: ${violation.symbol} imported in Server Component ${violation.path}
|
|
128
|
+
Import chain: ${violation.importChain.join(" -> ")}`;
|
|
129
|
+
}
|
|
130
|
+
function walkRSCGraph(current, records, chain, seen) {
|
|
131
|
+
if (current.isClient) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const runtime = current.runtimeImports[0];
|
|
135
|
+
const symbol = runtime?.symbols.find((s) => FORBIDDEN_SYMBOLS.has(s));
|
|
136
|
+
if (runtime && symbol) {
|
|
137
|
+
return {
|
|
138
|
+
symbol,
|
|
139
|
+
path: chain[0] ?? current.id,
|
|
140
|
+
importChain: [...chain, runtime.source]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
for (const importedId of current.imports) {
|
|
144
|
+
if (seen.has(importedId)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const next = records.get(importedId);
|
|
148
|
+
if (!next) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
seen.add(importedId);
|
|
152
|
+
const violation = walkRSCGraph(next, records, [...chain, importedId], seen);
|
|
153
|
+
if (violation) {
|
|
154
|
+
return violation;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
function readDirectivePrologue(code) {
|
|
160
|
+
const out = [];
|
|
161
|
+
let i = code.charCodeAt(0) === 65279 ? 1 : 0;
|
|
162
|
+
while (i < code.length) {
|
|
163
|
+
i = skipWhitespaceAndComments(code, i);
|
|
164
|
+
const quote = code[i];
|
|
165
|
+
if (quote !== '"' && quote !== "'") {
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
let j = i + 1;
|
|
169
|
+
let escaped = false;
|
|
170
|
+
while (j < code.length) {
|
|
171
|
+
const ch = code[j];
|
|
172
|
+
if (escaped) {
|
|
173
|
+
escaped = false;
|
|
174
|
+
} else if (ch === "\\") {
|
|
175
|
+
escaped = true;
|
|
176
|
+
} else if (ch === quote) {
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
j++;
|
|
180
|
+
}
|
|
181
|
+
if (j >= code.length) {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
let end = j + 1;
|
|
185
|
+
while (end < code.length && /[ \t\r\n]/.test(code[end])) {
|
|
186
|
+
end++;
|
|
187
|
+
}
|
|
188
|
+
if (code[end] === ";") {
|
|
189
|
+
end++;
|
|
190
|
+
}
|
|
191
|
+
out.push(code.slice(i, end).trim());
|
|
192
|
+
i = end;
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function skipWhitespaceAndComments(code, start) {
|
|
197
|
+
let i = start;
|
|
198
|
+
while (i < code.length) {
|
|
199
|
+
while (i < code.length && /\s/.test(code[i])) {
|
|
200
|
+
i++;
|
|
201
|
+
}
|
|
202
|
+
if (code.startsWith("//", i)) {
|
|
203
|
+
const next = code.indexOf("\n", i + 2);
|
|
204
|
+
i = next === -1 ? code.length : next + 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (code.startsWith("/*", i)) {
|
|
208
|
+
const next = code.indexOf("*/", i + 2);
|
|
209
|
+
i = next === -1 ? code.length : next + 2;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
return i;
|
|
215
|
+
}
|
|
216
|
+
function findRuntimeImports(code) {
|
|
217
|
+
const imports = [];
|
|
218
|
+
const staticImportRe = /import\s+(?!type\b)([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
219
|
+
const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
|
|
220
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
221
|
+
for (const match of code.matchAll(staticImportRe)) {
|
|
222
|
+
const clause = match[1];
|
|
223
|
+
const source = match[2];
|
|
224
|
+
if (!RUNTIME_MODULES.has(source)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
imports.push({ source, symbols: readImportedSymbols(clause) });
|
|
228
|
+
}
|
|
229
|
+
for (const match of code.matchAll(sideEffectImportRe)) {
|
|
230
|
+
const source = match[1];
|
|
231
|
+
if (RUNTIME_MODULES.has(source)) {
|
|
232
|
+
imports.push({ source, symbols: [] });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const match of code.matchAll(dynamicImportRe)) {
|
|
236
|
+
const source = match[1];
|
|
237
|
+
if (RUNTIME_MODULES.has(source)) {
|
|
238
|
+
imports.push({ source, symbols: Array.from(FORBIDDEN_SYMBOLS) });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return imports;
|
|
242
|
+
}
|
|
243
|
+
function findLocalImportSources(code) {
|
|
244
|
+
const out = [];
|
|
245
|
+
const staticImportRe = /import\s+(?!type\b)(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
246
|
+
const exportFromRe = /export\s+(?!type\b)(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
247
|
+
const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
248
|
+
for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
|
|
249
|
+
for (const match of code.matchAll(re)) {
|
|
250
|
+
const source = match[1];
|
|
251
|
+
if (source.startsWith(".") || source.startsWith("/")) {
|
|
252
|
+
out.push(source);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
function normalizeModuleId(id) {
|
|
259
|
+
const clean = id.split("?")[0] ?? id;
|
|
260
|
+
try {
|
|
261
|
+
return fs.realpathSync.native(clean).replace(/\\/g, "/");
|
|
262
|
+
} catch {
|
|
263
|
+
return path.resolve(clean).replace(/\\/g, "/");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function resolveLocalModule(importer, source) {
|
|
267
|
+
const base = source.startsWith("/") ? source : path.resolve(path.dirname(importer), source);
|
|
268
|
+
const candidates = [
|
|
269
|
+
base,
|
|
270
|
+
`${base}.tsx`,
|
|
271
|
+
`${base}.ts`,
|
|
272
|
+
`${base}.jsx`,
|
|
273
|
+
`${base}.js`,
|
|
274
|
+
`${base}.mjs`,
|
|
275
|
+
`${base}.cjs`,
|
|
276
|
+
path.join(base, "index.tsx"),
|
|
277
|
+
path.join(base, "index.ts"),
|
|
278
|
+
path.join(base, "index.jsx"),
|
|
279
|
+
path.join(base, "index.js")
|
|
280
|
+
];
|
|
281
|
+
for (const candidate of candidates) {
|
|
282
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
283
|
+
return normalizeModuleId(candidate);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
function readImportedSymbols(clause) {
|
|
289
|
+
const symbols = [];
|
|
290
|
+
const named = clause.match(/\{([\s\S]*?)\}/);
|
|
291
|
+
if (named) {
|
|
292
|
+
for (const part of named[1].split(",")) {
|
|
293
|
+
const trimmed = part.trim();
|
|
294
|
+
if (!trimmed || trimmed.startsWith("type ")) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const sourceName = trimmed.replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
|
|
298
|
+
if (sourceName) {
|
|
299
|
+
symbols.push(sourceName);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (/\*\s+as\s+\w+/.test(clause)) {
|
|
304
|
+
symbols.push(...FORBIDDEN_SYMBOLS);
|
|
305
|
+
}
|
|
306
|
+
return symbols;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
|
|
7
310
|
function stripLayerWrappers(css) {
|
|
8
311
|
let result = "";
|
|
9
312
|
let i = 0;
|
|
@@ -45,8 +348,7 @@ function stripLayerWrappers(css) {
|
|
|
45
348
|
function extractThemeBlocks(css) {
|
|
46
349
|
const blocks = [];
|
|
47
350
|
const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
|
|
48
|
-
|
|
49
|
-
while ((match = themeStart.exec(css)) !== null) {
|
|
351
|
+
for (const match of css.matchAll(themeStart)) {
|
|
50
352
|
const openPos = css.indexOf("{", match.index);
|
|
51
353
|
let depth = 0;
|
|
52
354
|
let j = openPos;
|
|
@@ -97,14 +399,12 @@ function parseThemeBlocks(cssContent) {
|
|
|
97
399
|
const blocks = extractThemeBlocks(stripped);
|
|
98
400
|
const propPattern = /--([a-z][a-z0-9-]*)(?:\s*:[^;]+)?;/g;
|
|
99
401
|
for (const block of blocks) {
|
|
100
|
-
|
|
101
|
-
while ((match = propPattern.exec(block)) !== null) {
|
|
402
|
+
for (const match of block.matchAll(propPattern)) {
|
|
102
403
|
const categorized = categorizeProperty(match[1]);
|
|
103
404
|
if (categorized) {
|
|
104
405
|
result[categorized.category].add(categorized.token);
|
|
105
406
|
}
|
|
106
407
|
}
|
|
107
|
-
propPattern.lastIndex = 0;
|
|
108
408
|
}
|
|
109
409
|
return {
|
|
110
410
|
colors: [...result.colors].sort(),
|
|
@@ -144,17 +444,122 @@ function hasTokens(theme) {
|
|
|
144
444
|
return Object.values(theme).some((arr) => arr.length > 0);
|
|
145
445
|
}
|
|
146
446
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
447
|
+
const GLOB_MAGIC_RE = /[*?[\]{}]/;
|
|
448
|
+
const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
|
|
449
|
+
function normalizeFileId(id) {
|
|
450
|
+
return id.split(/[?#]/, 1)[0].replace(/\\/g, "/");
|
|
451
|
+
}
|
|
452
|
+
function normalizeRoot(rootDir) {
|
|
453
|
+
return path.resolve(rootDir).replace(/\\/g, "/");
|
|
454
|
+
}
|
|
455
|
+
function hasGlobMagic(pattern) {
|
|
456
|
+
return GLOB_MAGIC_RE.test(pattern);
|
|
457
|
+
}
|
|
458
|
+
function escapeRegExp(ch) {
|
|
459
|
+
return /[|\\{}()[\]^$+?.]/.test(ch) ? `\\${ch}` : ch;
|
|
460
|
+
}
|
|
461
|
+
function globToRegExp(pattern) {
|
|
462
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
463
|
+
let out = "^";
|
|
464
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
465
|
+
const ch = normalized[i];
|
|
466
|
+
if (ch === "*") {
|
|
467
|
+
if (normalized[i + 1] === "*") {
|
|
468
|
+
out += ".*";
|
|
469
|
+
i++;
|
|
470
|
+
} else {
|
|
471
|
+
out += "[^/]*";
|
|
472
|
+
}
|
|
473
|
+
} else if (ch === "?") {
|
|
474
|
+
out += "[^/]";
|
|
475
|
+
} else {
|
|
476
|
+
out += escapeRegExp(ch);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
out += "$";
|
|
480
|
+
return new RegExp(out);
|
|
481
|
+
}
|
|
482
|
+
function relativeToRoot(file, rootDir) {
|
|
483
|
+
const root = normalizeRoot(rootDir);
|
|
484
|
+
const normalizedFile = normalizeFileId(file);
|
|
485
|
+
return path.posix.relative(root, normalizedFile).replace(/\\/g, "/");
|
|
486
|
+
}
|
|
487
|
+
function matchesPattern(id, pattern, rootDir) {
|
|
488
|
+
const file = normalizeFileId(id);
|
|
489
|
+
const relative = relativeToRoot(file, rootDir);
|
|
490
|
+
if (pattern instanceof RegExp) {
|
|
491
|
+
pattern.lastIndex = 0;
|
|
492
|
+
return pattern.test(file) || pattern.test(relative);
|
|
493
|
+
}
|
|
494
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
495
|
+
if (hasGlobMagic(normalizedPattern)) {
|
|
496
|
+
const re = globToRegExp(normalizedPattern);
|
|
497
|
+
return re.test(file) || re.test(relative);
|
|
498
|
+
}
|
|
499
|
+
const absolutePattern = path.isAbsolute(normalizedPattern) ? normalizeFileId(normalizedPattern) : normalizeFileId(path.join(rootDir, normalizedPattern));
|
|
500
|
+
return file === absolutePattern || relative === normalizedPattern;
|
|
501
|
+
}
|
|
502
|
+
function matchesAnyPattern(id, patterns, rootDir) {
|
|
503
|
+
if (!patterns) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
507
|
+
return list.some((pattern) => matchesPattern(id, pattern, rootDir));
|
|
508
|
+
}
|
|
509
|
+
function expandFilePatterns(rootDir, patterns) {
|
|
510
|
+
const list = Array.isArray(patterns) ? patterns : [patterns];
|
|
511
|
+
const files = /* @__PURE__ */ new Set();
|
|
512
|
+
const walk = (dir) => {
|
|
513
|
+
let entries;
|
|
514
|
+
try {
|
|
515
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
516
|
+
} catch {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
for (const entry of entries) {
|
|
520
|
+
const full = path.join(dir, entry.name);
|
|
521
|
+
if (entry.isDirectory()) {
|
|
522
|
+
if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
523
|
+
walk(full);
|
|
524
|
+
}
|
|
525
|
+
} else {
|
|
526
|
+
files.add(path.resolve(full));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
let needsWalk = false;
|
|
531
|
+
for (const pattern of list) {
|
|
532
|
+
const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
|
|
533
|
+
if (hasGlobMagic(pattern)) {
|
|
534
|
+
needsWalk = true;
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (fs.existsSync(resolved)) {
|
|
538
|
+
files.add(path.resolve(resolved));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (needsWalk) {
|
|
542
|
+
walk(rootDir);
|
|
543
|
+
for (const file of Array.from(files)) {
|
|
544
|
+
if (!list.some(
|
|
545
|
+
(pattern) => hasGlobMagic(pattern) && matchesPattern(file, pattern, rootDir)
|
|
546
|
+
)) {
|
|
547
|
+
const isLiteralMatch = list.some((pattern) => {
|
|
548
|
+
if (hasGlobMagic(pattern)) {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
|
|
552
|
+
return normalizeFileId(path.resolve(resolved)) === normalizeFileId(file);
|
|
553
|
+
});
|
|
554
|
+
if (!isLiteralMatch) {
|
|
555
|
+
files.delete(file);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return Array.from(files).sort();
|
|
561
|
+
}
|
|
155
562
|
|
|
156
|
-
// src/html-transformer.ts
|
|
157
|
-
import { createHash } from "crypto";
|
|
158
563
|
function injectChecksum(html, checksum, minify = false) {
|
|
159
564
|
const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
|
|
160
565
|
const htmlTagPattern = /<html([^>]*)>/i;
|
|
@@ -164,16 +569,13 @@ function injectChecksum(html, checksum, minify = false) {
|
|
|
164
569
|
}
|
|
165
570
|
const existingAttrs = match[1];
|
|
166
571
|
const checksumAttr = ` ${attrName}="${checksum}"`;
|
|
167
|
-
return html.replace(
|
|
168
|
-
htmlTagPattern,
|
|
169
|
-
`<html${checksumAttr}${existingAttrs}>`
|
|
170
|
-
);
|
|
572
|
+
return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
|
|
171
573
|
}
|
|
172
574
|
function injectMangleMapScript(html, mangleMap, options = {}) {
|
|
173
575
|
const { prettyPrint = false } = options;
|
|
174
576
|
const jsonContent = prettyPrint ? JSON.stringify(mangleMap, null, 2) : JSON.stringify(mangleMap);
|
|
175
|
-
const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}
|
|
176
|
-
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})}}})()
|
|
577
|
+
const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}<\/script>`;
|
|
578
|
+
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>`;
|
|
177
579
|
const combined = `${scriptTag}
|
|
178
580
|
${debugScript}`;
|
|
179
581
|
if (html.includes("</head>")) {
|
|
@@ -214,7 +616,7 @@ function injectHydrationData(html, mangleMap, checksum, options = {}) {
|
|
|
214
616
|
function transformIndexHtml(html, mangleMap, checksum, options = {}) {
|
|
215
617
|
return injectHydrationData(html, mangleMap, checksum, options);
|
|
216
618
|
}
|
|
217
|
-
function buildRecoveryManifest(tokens, options
|
|
619
|
+
function buildRecoveryManifest(tokens, options) {
|
|
218
620
|
const stripped = options.production === true;
|
|
219
621
|
const strippedDevOnlyPaths = [];
|
|
220
622
|
const sorted = {};
|
|
@@ -235,7 +637,7 @@ function buildRecoveryManifest(tokens, options = {}) {
|
|
|
235
637
|
const checksum = fullChecksum.substring(0, 16);
|
|
236
638
|
const buildId = `${Date.now().toString(36)}-${fullChecksum.substring(0, 6)}`;
|
|
237
639
|
return {
|
|
238
|
-
manifest: { buildId, checksum, tokens: sorted },
|
|
640
|
+
manifest: { buildId, checksum, mangleChecksum: options.mangleChecksum, tokens: sorted },
|
|
239
641
|
strippedDevOnlyPaths
|
|
240
642
|
};
|
|
241
643
|
}
|
|
@@ -244,7 +646,7 @@ function injectRecoveryManifest(html, manifest) {
|
|
|
244
646
|
return html;
|
|
245
647
|
}
|
|
246
648
|
const json = JSON.stringify(manifest);
|
|
247
|
-
const scriptTag = `<script id="__SZ_RECOVERY_MANIFEST__" type="application/json">${json}
|
|
649
|
+
const scriptTag = `<script id="__SZ_RECOVERY_MANIFEST__" type="application/json">${json}<\/script>`;
|
|
248
650
|
if (html.includes("</head>")) {
|
|
249
651
|
return html.replace("</head>", `${scriptTag}
|
|
250
652
|
</head>`);
|
|
@@ -255,9 +657,6 @@ function injectRecoveryManifest(html, manifest) {
|
|
|
255
657
|
return html + scriptTag;
|
|
256
658
|
}
|
|
257
659
|
|
|
258
|
-
// src/theme-type-writer.ts
|
|
259
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
260
|
-
import { dirname } from "path";
|
|
261
660
|
function generateThemeDts(opts) {
|
|
262
661
|
const { theme, sourceFiles } = opts;
|
|
263
662
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -304,11 +703,10 @@ function writeThemeDts(opts) {
|
|
|
304
703
|
writeFileSync(opts.outputPath, content, "utf-8");
|
|
305
704
|
}
|
|
306
705
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
var RESOLVED_VIRTUAL_CHECKSUM_ID = "\0" + VIRTUAL_CHECKSUM_ID;
|
|
706
|
+
const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
|
|
707
|
+
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
|
|
708
|
+
const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
|
|
709
|
+
const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
|
|
312
710
|
function createMangleMapModule(mangleMap, checksum) {
|
|
313
711
|
return `/**
|
|
314
712
|
* Auto-generated mangle map for csszyx.
|
|
@@ -353,22 +751,14 @@ function resolveVirtualModule(id) {
|
|
|
353
751
|
return void 0;
|
|
354
752
|
}
|
|
355
753
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
var _hasWarnedTsConfig = false;
|
|
754
|
+
const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
|
|
755
|
+
const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
|
|
756
|
+
let _hasWarnedTsConfig = false;
|
|
360
757
|
function runThemeScan(rootDir, scanCss) {
|
|
361
758
|
if (!scanCss) {
|
|
362
759
|
return;
|
|
363
760
|
}
|
|
364
|
-
const
|
|
365
|
-
const sourceFiles = [];
|
|
366
|
-
for (const pattern of patterns) {
|
|
367
|
-
const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
|
|
368
|
-
if (fs.existsSync(resolved)) {
|
|
369
|
-
sourceFiles.push(resolved);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
761
|
+
const sourceFiles = expandFilePatterns(rootDir, scanCss).filter((file) => file.endsWith(".css"));
|
|
372
762
|
if (sourceFiles.length === 0) {
|
|
373
763
|
return;
|
|
374
764
|
}
|
|
@@ -389,9 +779,11 @@ function runThemeScan(rootDir, scanCss) {
|
|
|
389
779
|
if (fs.existsSync(cfgPath)) {
|
|
390
780
|
const content = fs.readFileSync(cfgPath, "utf-8");
|
|
391
781
|
if (!content.includes(".csszyx")) {
|
|
392
|
-
console.warn(
|
|
782
|
+
console.warn(
|
|
783
|
+
`
|
|
393
784
|
\x1B[33m\u26A0\uFE0F CSSzyx: Theme Auto-Scan enabled, but TypeScript isn't configured. Run "npx @csszyx/cli init" to fix.\x1B[0m
|
|
394
|
-
`
|
|
785
|
+
`
|
|
786
|
+
);
|
|
395
787
|
}
|
|
396
788
|
return true;
|
|
397
789
|
}
|
|
@@ -483,12 +875,12 @@ function mangleCodeClassesSync(code, mangleMap) {
|
|
|
483
875
|
return qm;
|
|
484
876
|
}
|
|
485
877
|
changed = true;
|
|
486
|
-
return
|
|
878
|
+
return `"${m}"`;
|
|
487
879
|
});
|
|
488
|
-
out +=
|
|
880
|
+
out += `\${${mangledInner}}`;
|
|
489
881
|
i = j;
|
|
490
882
|
}
|
|
491
|
-
return changed ?
|
|
883
|
+
return changed ? `className:\`${out}\`` : fullMatch;
|
|
492
884
|
});
|
|
493
885
|
{
|
|
494
886
|
const marker = "className:";
|
|
@@ -543,7 +935,7 @@ function mangleCodeClassesSync(code, mangleMap) {
|
|
|
543
935
|
const mangledStr = parts.map((p) => mangleMap[p] || p).join(" ");
|
|
544
936
|
if (mangledStr !== inner) {
|
|
545
937
|
changed = true;
|
|
546
|
-
return
|
|
938
|
+
return `"${mangledStr}"`;
|
|
547
939
|
}
|
|
548
940
|
return qm;
|
|
549
941
|
});
|
|
@@ -572,24 +964,58 @@ function mangleCodeClassesSync(code, mangleMap) {
|
|
|
572
964
|
if (!changed) {
|
|
573
965
|
return match;
|
|
574
966
|
}
|
|
575
|
-
return
|
|
967
|
+
return `"${mangled.join(" ")}"`;
|
|
576
968
|
});
|
|
577
969
|
return result;
|
|
578
970
|
}
|
|
579
971
|
function createCsszyxPlugins(options = {}) {
|
|
580
972
|
const manglingEnabled = options.production?.mangle !== false;
|
|
581
973
|
const astBudgetOverride = options.build?.astBudgetLimit;
|
|
974
|
+
const parserOverride = process.env.CSSZYX_PARSER;
|
|
975
|
+
const parserMode = parserOverride === "babel" || parserOverride === "oxc" ? parserOverride : options.build?.parser ?? "oxc";
|
|
582
976
|
const state = {
|
|
583
977
|
classes: /* @__PURE__ */ new Set(),
|
|
584
978
|
mangleMap: {},
|
|
585
979
|
checksum: "",
|
|
586
980
|
finalized: false,
|
|
587
981
|
rootDir: process.cwd(),
|
|
588
|
-
recoveryTokens: /* @__PURE__ */ new Map()
|
|
982
|
+
recoveryTokens: /* @__PURE__ */ new Map(),
|
|
983
|
+
rscModules: /* @__PURE__ */ new Map()
|
|
589
984
|
};
|
|
590
985
|
const SAFELIST_FILENAME = "csszyx-classes.html";
|
|
591
986
|
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
|
|
592
987
|
const IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", ".turbo"]);
|
|
988
|
+
function isUserExcluded(id) {
|
|
989
|
+
return matchesAnyPattern(id, options.exclude, state.rootDir);
|
|
990
|
+
}
|
|
991
|
+
function isUserIncluded(id) {
|
|
992
|
+
return !options.include || matchesAnyPattern(id, options.include, state.rootDir);
|
|
993
|
+
}
|
|
994
|
+
function isHardIgnored(id) {
|
|
995
|
+
return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
|
|
996
|
+
}
|
|
997
|
+
function shouldProcessSource(id) {
|
|
998
|
+
return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.[tj]sx?(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
|
|
999
|
+
}
|
|
1000
|
+
function shouldProcessCss(id) {
|
|
1001
|
+
return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
|
|
1002
|
+
}
|
|
1003
|
+
function transformConfiguredSource(source, filename) {
|
|
1004
|
+
const compilerOptions = { astBudget: astBudgetOverride };
|
|
1005
|
+
if (parserMode !== "oxc") {
|
|
1006
|
+
return transformSourceCode(source, filename, compilerOptions);
|
|
1007
|
+
}
|
|
1008
|
+
try {
|
|
1009
|
+
return transformOxc(source, filename, compilerOptions);
|
|
1010
|
+
} catch (err) {
|
|
1011
|
+
const result = transformSourceCode(source, filename, compilerOptions);
|
|
1012
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1013
|
+
result.diagnostics.push(
|
|
1014
|
+
`[csszyx] oxc parser fell back to Babel for ${filename}: ${reason}`
|
|
1015
|
+
);
|
|
1016
|
+
return result;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
593
1019
|
function writeSafelistFile(classes) {
|
|
594
1020
|
if (classes.size === 0) {
|
|
595
1021
|
return;
|
|
@@ -625,12 +1051,15 @@ function createCsszyxPlugins(options = {}) {
|
|
|
625
1051
|
}
|
|
626
1052
|
} else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
627
1053
|
const filePath = path.join(dir, entry.name);
|
|
1054
|
+
if (!shouldProcessSource(filePath)) {
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
628
1057
|
try {
|
|
629
1058
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
630
1059
|
if (!content.includes("sz=") && !content.includes("sz:")) {
|
|
631
1060
|
continue;
|
|
632
1061
|
}
|
|
633
|
-
const result =
|
|
1062
|
+
const result = transformConfiguredSource(content, filePath);
|
|
634
1063
|
if (!result.transformed) {
|
|
635
1064
|
continue;
|
|
636
1065
|
}
|
|
@@ -645,10 +1074,9 @@ function createCsszyxPlugins(options = {}) {
|
|
|
645
1074
|
}
|
|
646
1075
|
if (result.usesRuntime) {
|
|
647
1076
|
const szCallRe = /_sz\(\s*\{/g;
|
|
648
|
-
|
|
649
|
-
while ((szMatch = szCallRe.exec(result.code)) !== null) {
|
|
1077
|
+
for (const szMatch of result.code.matchAll(szCallRe)) {
|
|
650
1078
|
let depth = 1;
|
|
651
|
-
let idx = szMatch.index + szMatch[0].length;
|
|
1079
|
+
let idx = (szMatch.index ?? 0) + szMatch[0].length;
|
|
652
1080
|
while (idx < result.code.length && depth > 0) {
|
|
653
1081
|
if (result.code[idx] === "{") {
|
|
654
1082
|
depth++;
|
|
@@ -657,10 +1085,12 @@ function createCsszyxPlugins(options = {}) {
|
|
|
657
1085
|
}
|
|
658
1086
|
idx++;
|
|
659
1087
|
}
|
|
660
|
-
const objStr = result.code.slice(
|
|
1088
|
+
const objStr = result.code.slice(
|
|
1089
|
+
(szMatch.index ?? 0) + szMatch[0].length,
|
|
1090
|
+
idx - 1
|
|
1091
|
+
);
|
|
661
1092
|
const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
662
|
-
|
|
663
|
-
while ((kv = strKv.exec(objStr)) !== null) {
|
|
1093
|
+
for (const kv of objStr.matchAll(strKv)) {
|
|
664
1094
|
try {
|
|
665
1095
|
const val = kv[2] ?? kv[3];
|
|
666
1096
|
const r = transform({ [kv[1]]: val });
|
|
@@ -671,7 +1101,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
671
1101
|
}
|
|
672
1102
|
}
|
|
673
1103
|
const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
|
|
674
|
-
|
|
1104
|
+
for (const kv of objStr.matchAll(numKv)) {
|
|
675
1105
|
try {
|
|
676
1106
|
const r = transform({ [kv[1]]: parseFloat(kv[2]) });
|
|
677
1107
|
for (const c of r.className.split(/\s+/).filter(Boolean)) {
|
|
@@ -681,7 +1111,7 @@ function createCsszyxPlugins(options = {}) {
|
|
|
681
1111
|
}
|
|
682
1112
|
}
|
|
683
1113
|
const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
|
|
684
|
-
|
|
1114
|
+
for (const kv of objStr.matchAll(boolKv)) {
|
|
685
1115
|
try {
|
|
686
1116
|
const r = transform({ [kv[1]]: kv[2] === "true" });
|
|
687
1117
|
for (const c of r.className.split(/\s+/).filter(Boolean)) {
|
|
@@ -707,9 +1137,8 @@ function createCsszyxPlugins(options = {}) {
|
|
|
707
1137
|
function extractClasses(code) {
|
|
708
1138
|
const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
|
|
709
1139
|
const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
|
|
710
|
-
let match;
|
|
711
1140
|
for (const classPattern of [dqPattern, sqPattern]) {
|
|
712
|
-
|
|
1141
|
+
for (const match of code.matchAll(classPattern)) {
|
|
713
1142
|
const classes = match[1].split(/\s+/).filter(Boolean);
|
|
714
1143
|
for (const cls of classes) {
|
|
715
1144
|
state.classes.add(cls);
|
|
@@ -717,9 +1146,9 @@ function createCsszyxPlugins(options = {}) {
|
|
|
717
1146
|
}
|
|
718
1147
|
}
|
|
719
1148
|
const exprStart = /className=\{/g;
|
|
720
|
-
|
|
1149
|
+
for (const match of code.matchAll(exprStart)) {
|
|
721
1150
|
let depth = 1;
|
|
722
|
-
let i = match.index + match[0].length;
|
|
1151
|
+
let i = (match.index ?? 0) + match[0].length;
|
|
723
1152
|
while (i < code.length && depth > 0) {
|
|
724
1153
|
if (code[i] === "{") {
|
|
725
1154
|
depth++;
|
|
@@ -728,10 +1157,9 @@ function createCsszyxPlugins(options = {}) {
|
|
|
728
1157
|
}
|
|
729
1158
|
i++;
|
|
730
1159
|
}
|
|
731
|
-
const expr = code.slice(match.index + match[0].length, i - 1);
|
|
1160
|
+
const expr = code.slice((match.index ?? 0) + match[0].length, i - 1);
|
|
732
1161
|
const strPattern = /"([^"]+)"|'([^']+)'/g;
|
|
733
|
-
|
|
734
|
-
while ((strMatch = strPattern.exec(expr)) !== null) {
|
|
1162
|
+
for (const strMatch of expr.matchAll(strPattern)) {
|
|
735
1163
|
const str = strMatch[1] || strMatch[2];
|
|
736
1164
|
const classes = str.split(/\s+/).filter(Boolean);
|
|
737
1165
|
for (const cls of classes) {
|
|
@@ -765,313 +1193,327 @@ function createCsszyxPlugins(options = {}) {
|
|
|
765
1193
|
}
|
|
766
1194
|
return result;
|
|
767
1195
|
}
|
|
768
|
-
const prePlugin = createUnplugin(
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
return
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
if (
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1196
|
+
const prePlugin = createUnplugin(
|
|
1197
|
+
(_pluginOptions) => ({
|
|
1198
|
+
name: "csszyx:pre",
|
|
1199
|
+
enforce: "pre",
|
|
1200
|
+
/**
|
|
1201
|
+
* Resolves virtual module IDs for csszyx mangle-map and checksum modules.
|
|
1202
|
+
* @param id - the module ID to resolve
|
|
1203
|
+
* @returns resolved ID if virtual, null otherwise
|
|
1204
|
+
*/
|
|
1205
|
+
resolveId(id) {
|
|
1206
|
+
if (isVirtualModule(id)) {
|
|
1207
|
+
return resolveVirtualModule(id);
|
|
1208
|
+
}
|
|
1209
|
+
return null;
|
|
1210
|
+
},
|
|
1211
|
+
/**
|
|
1212
|
+
* Loads virtual module content — generates mangle map or checksum module code.
|
|
1213
|
+
* @param id - the resolved module ID to load
|
|
1214
|
+
* @returns generated module source if virtual, null otherwise
|
|
1215
|
+
*/
|
|
1216
|
+
load(id) {
|
|
1217
|
+
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
1218
|
+
finalizeMangleMap();
|
|
1219
|
+
return createMangleMapModule(state.mangleMap, state.checksum);
|
|
1220
|
+
}
|
|
1221
|
+
if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
|
|
1222
|
+
finalizeMangleMap();
|
|
1223
|
+
return createChecksumModule(state.checksum);
|
|
1224
|
+
}
|
|
1225
|
+
return null;
|
|
1226
|
+
},
|
|
1227
|
+
/**
|
|
1228
|
+
* Filters files for the pre-transform phase — source files plus CSS files.
|
|
1229
|
+
* CSS files need special handling to inject @source inline() for Tailwind class discovery.
|
|
1230
|
+
* @param id - the file path to check for inclusion
|
|
1231
|
+
* @returns true if the file should be transformed, false otherwise
|
|
1232
|
+
*/
|
|
1233
|
+
transformInclude(id) {
|
|
1234
|
+
if (shouldProcessCss(id)) {
|
|
1235
|
+
return true;
|
|
1236
|
+
}
|
|
1237
|
+
return shouldProcessSource(id);
|
|
1238
|
+
},
|
|
1239
|
+
/**
|
|
1240
|
+
* Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
|
|
1241
|
+
* For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
|
|
1242
|
+
* @param code - the source code to transform
|
|
1243
|
+
* @param id - the file path of the module being transformed
|
|
1244
|
+
* @returns transformed code with source map, or null if no changes were made
|
|
1245
|
+
*/
|
|
1246
|
+
transform(code, id) {
|
|
1247
|
+
if (!shouldProcessCss(id) && !shouldProcessSource(id)) {
|
|
1248
|
+
return null;
|
|
1249
|
+
}
|
|
1250
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1251
|
+
assertNoRSCBoundaryViolation(code, id);
|
|
1252
|
+
}
|
|
1253
|
+
if (/\.css(\?.*)?$/.test(id)) {
|
|
1254
|
+
const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
|
|
1255
|
+
if (hasTailwindImport && state.classes.size > 0) {
|
|
1256
|
+
const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
|
|
1257
|
+
if (candidates) {
|
|
1258
|
+
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
|
|
1259
|
+
const cssDir = path.dirname(id).replace(/\\/g, "/");
|
|
1260
|
+
let relPath = path.posix.relative(cssDir, safelistPath);
|
|
1261
|
+
if (!relPath.startsWith(".")) {
|
|
1262
|
+
relPath = `./${relPath}`;
|
|
1263
|
+
}
|
|
1264
|
+
const sourceDirective = `@source "${relPath}";
|
|
833
1265
|
`;
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1266
|
+
const transformed2 = code.replace(
|
|
1267
|
+
/(@import\s+["']tailwindcss[^"']*["'];)/,
|
|
1268
|
+
`$1
|
|
837
1269
|
${sourceDirective}`
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1270
|
+
);
|
|
1271
|
+
if (transformed2 !== code) {
|
|
1272
|
+
return { code: transformed2, map: null };
|
|
1273
|
+
}
|
|
841
1274
|
}
|
|
842
1275
|
}
|
|
1276
|
+
return null;
|
|
843
1277
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1278
|
+
let transformedCode = code;
|
|
1279
|
+
let usesRuntime = false;
|
|
1280
|
+
let usesMerge = false;
|
|
1281
|
+
let usesColorVar = false;
|
|
1282
|
+
let transformed = false;
|
|
1283
|
+
let szClasses;
|
|
1284
|
+
const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
|
|
1285
|
+
if (hasSzProp) {
|
|
1286
|
+
if (id.endsWith(".vue")) {
|
|
1287
|
+
const result = preprocess(code, options);
|
|
1288
|
+
if (result.transformed) {
|
|
1289
|
+
transformedCode = result.code;
|
|
1290
|
+
transformed = true;
|
|
1291
|
+
}
|
|
1292
|
+
} else if (id.endsWith(".svelte")) {
|
|
1293
|
+
const result = preprocess$1(code, options);
|
|
1294
|
+
if (result) {
|
|
1295
|
+
transformedCode = result.code;
|
|
1296
|
+
transformed = true;
|
|
1297
|
+
}
|
|
1298
|
+
} else {
|
|
1299
|
+
const result = transformConfiguredSource(code, id);
|
|
863
1300
|
transformedCode = result.code;
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
transformed = result.transformed;
|
|
873
|
-
szClasses = result.classes;
|
|
874
|
-
if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
|
|
875
|
-
for (const msg of result.diagnostics) {
|
|
876
|
-
this.warn(`[csszyx] ${id}
|
|
1301
|
+
usesRuntime = result.usesRuntime;
|
|
1302
|
+
usesMerge = result.usesMerge;
|
|
1303
|
+
usesColorVar = result.usesColorVar;
|
|
1304
|
+
transformed = result.transformed;
|
|
1305
|
+
szClasses = result.classes;
|
|
1306
|
+
if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
|
|
1307
|
+
for (const msg of result.diagnostics) {
|
|
1308
|
+
this.warn(`[csszyx] ${id}
|
|
877
1309
|
${msg}`);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
for (const [token, data] of result.recoveryTokens) {
|
|
1313
|
+
state.recoveryTokens.set(token, data);
|
|
878
1314
|
}
|
|
879
|
-
}
|
|
880
|
-
for (const [token, data] of result.recoveryTokens) {
|
|
881
|
-
state.recoveryTokens.set(token, data);
|
|
882
1315
|
}
|
|
883
1316
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
|
|
887
|
-
transformedCode = transformedCode.replace(/<html([^>]*)>/i, `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`);
|
|
888
|
-
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})}}})()\`}} />`;
|
|
889
|
-
if (transformedCode.includes("<body")) {
|
|
1317
|
+
if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
|
|
1318
|
+
const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
|
|
890
1319
|
transformedCode = transformedCode.replace(
|
|
891
|
-
|
|
892
|
-
|
|
1320
|
+
/<html([^>]*)>/i,
|
|
1321
|
+
`<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
|
|
893
1322
|
);
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
897
|
-
{
|
|
898
|
-
const imports = [];
|
|
899
|
-
if (usesRuntime) {
|
|
900
|
-
imports.push("_sz");
|
|
901
|
-
}
|
|
902
|
-
if (usesMerge) {
|
|
903
|
-
imports.push("_szMerge");
|
|
904
|
-
}
|
|
905
|
-
if (usesColorVar) {
|
|
906
|
-
imports.push("__szColorVar");
|
|
907
|
-
}
|
|
908
|
-
const needed = imports.filter(
|
|
909
|
-
(name) => !new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*from\\s*['"]@csszyx/runtime['"]`).test(transformedCode)
|
|
910
|
-
);
|
|
911
|
-
if (needed.length > 0) {
|
|
912
|
-
const existingImport = transformedCode.match(/^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m);
|
|
913
|
-
if (existingImport) {
|
|
1323
|
+
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})}}})()\`}} />`;
|
|
1324
|
+
if (transformedCode.includes("<body")) {
|
|
914
1325
|
transformedCode = transformedCode.replace(
|
|
915
|
-
|
|
916
|
-
`$
|
|
1326
|
+
/(<body[^>]*>)/i,
|
|
1327
|
+
`$1${debugScript}`
|
|
917
1328
|
);
|
|
918
|
-
} else {
|
|
919
|
-
const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
|
|
920
|
-
`;
|
|
921
|
-
const directiveMatch = transformedCode.match(/^['"]use (client|server)['"];?\s*/);
|
|
922
|
-
if (directiveMatch) {
|
|
923
|
-
const directive = directiveMatch[0];
|
|
924
|
-
transformedCode = transformedCode.replace(directive, `${directive}${importStmt}`);
|
|
925
|
-
} else {
|
|
926
|
-
transformedCode = `${importStmt}${transformedCode}`;
|
|
927
|
-
}
|
|
928
1329
|
}
|
|
929
1330
|
transformed = true;
|
|
930
1331
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1332
|
+
{
|
|
1333
|
+
const imports = [];
|
|
1334
|
+
if (usesRuntime) {
|
|
1335
|
+
imports.push("_sz");
|
|
1336
|
+
}
|
|
1337
|
+
if (usesMerge) {
|
|
1338
|
+
imports.push("_szMerge");
|
|
1339
|
+
}
|
|
1340
|
+
if (usesColorVar) {
|
|
1341
|
+
imports.push("__szColorVar");
|
|
1342
|
+
}
|
|
1343
|
+
const needed = imports.filter(
|
|
1344
|
+
(name) => !new RegExp(
|
|
1345
|
+
`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*from\\s*['"]@csszyx/runtime['"]`
|
|
1346
|
+
).test(transformedCode)
|
|
1347
|
+
);
|
|
1348
|
+
if (needed.length > 0) {
|
|
1349
|
+
const existingImport = transformedCode.match(
|
|
1350
|
+
/^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
|
|
1351
|
+
);
|
|
1352
|
+
if (existingImport) {
|
|
1353
|
+
transformedCode = transformedCode.replace(
|
|
1354
|
+
existingImport[0],
|
|
1355
|
+
`${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
|
|
1356
|
+
);
|
|
1357
|
+
} else {
|
|
1358
|
+
const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
|
|
1359
|
+
`;
|
|
1360
|
+
const directiveMatch = transformedCode.match(
|
|
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
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
transformed = true;
|
|
936
1374
|
}
|
|
937
|
-
} else {
|
|
938
|
-
extractClasses(transformedCode);
|
|
939
1375
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
/** Finalizes the mangle map after all source modules have been processed. */
|
|
945
|
-
buildEnd() {
|
|
946
|
-
finalizeMangleMap();
|
|
947
|
-
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
948
|
-
globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
|
|
949
|
-
}
|
|
950
|
-
},
|
|
951
|
-
/**
|
|
952
|
-
* Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
|
|
953
|
-
* @param compiler - the Webpack compiler instance
|
|
954
|
-
*/
|
|
955
|
-
webpack(compiler) {
|
|
956
|
-
compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
|
|
957
|
-
const root = compiler.context || process.cwd();
|
|
958
|
-
state.rootDir = root;
|
|
959
|
-
if (state.classes.size === 0) {
|
|
960
|
-
prescanAndWriteClasses();
|
|
1376
|
+
if (/\.[tj]sx?(\?.*)?$/.test(id)) {
|
|
1377
|
+
assertNoRSCBoundaryViolation(transformedCode, id);
|
|
1378
|
+
const record = createRSCModuleRecord(transformedCode, id);
|
|
1379
|
+
state.rscModules.set(record.id, record);
|
|
961
1380
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
|
|
967
|
-
const root = compiler.context || process.cwd();
|
|
968
|
-
for (const pattern of patterns) {
|
|
969
|
-
const resolved = path.isAbsolute(pattern) ? pattern : path.join(root, pattern);
|
|
970
|
-
if (fs.existsSync(resolved)) {
|
|
971
|
-
compilation.fileDependencies.add(resolved);
|
|
1381
|
+
if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
|
|
1382
|
+
if (szClasses !== void 0) {
|
|
1383
|
+
for (const cls of szClasses) {
|
|
1384
|
+
state.classes.add(cls);
|
|
972
1385
|
}
|
|
1386
|
+
} else {
|
|
1387
|
+
extractClasses(transformedCode);
|
|
973
1388
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
/**
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
state.rootDir = root;
|
|
986
|
-
prescanAndWriteClasses();
|
|
987
|
-
runThemeScan(root, options.build?.scanCss);
|
|
1389
|
+
return { code: transformedCode, map: null };
|
|
1390
|
+
}
|
|
1391
|
+
return null;
|
|
1392
|
+
},
|
|
1393
|
+
/** Finalizes the mangle map after all source modules have been processed. */
|
|
1394
|
+
buildEnd() {
|
|
1395
|
+
finalizeMangleMap();
|
|
1396
|
+
assertNoRSCGraphViolation(state.rscModules);
|
|
1397
|
+
if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
|
|
1398
|
+
globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
|
|
1399
|
+
}
|
|
988
1400
|
},
|
|
989
1401
|
/**
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
* @param ctx - HMR context containing the changed file
|
|
1402
|
+
* Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
|
|
1403
|
+
* @param compiler - the Webpack compiler instance
|
|
993
1404
|
*/
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
const resolved = path.isAbsolute(p) ? p : path.join(root, p);
|
|
1001
|
-
return ctx.file === resolved;
|
|
1002
|
-
});
|
|
1003
|
-
if (isWatched) {
|
|
1004
|
-
runThemeScan(root, scanCss);
|
|
1405
|
+
webpack(compiler) {
|
|
1406
|
+
compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
|
|
1407
|
+
const root = compiler.context || process.cwd();
|
|
1408
|
+
state.rootDir = root;
|
|
1409
|
+
if (state.classes.size === 0) {
|
|
1410
|
+
prescanAndWriteClasses();
|
|
1005
1411
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
fileContent = fs.readFileSync(ctx.file, "utf-8");
|
|
1016
|
-
} catch {
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
try {
|
|
1023
|
-
result = transformSourceCode(fileContent, ctx.file, { astBudget: astBudgetOverride });
|
|
1024
|
-
} catch {
|
|
1025
|
-
return;
|
|
1026
|
-
}
|
|
1027
|
-
if (!result.transformed) {
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
const sizeBefore = state.classes.size;
|
|
1031
|
-
for (const cls of result.classes) {
|
|
1032
|
-
state.classes.add(cls);
|
|
1033
|
-
}
|
|
1034
|
-
for (const [token, data] of result.recoveryTokens) {
|
|
1035
|
-
state.recoveryTokens.set(token, data);
|
|
1036
|
-
}
|
|
1037
|
-
if (state.classes.size > sizeBefore) {
|
|
1038
|
-
writeSafelistFile(state.classes);
|
|
1039
|
-
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
|
|
1040
|
-
ctx.server.watcher.emit("change", safelistPath);
|
|
1412
|
+
runThemeScan(root, options.build?.scanCss);
|
|
1413
|
+
});
|
|
1414
|
+
if (options.build?.scanCss) {
|
|
1415
|
+
compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
|
|
1416
|
+
const root = compiler.context || process.cwd();
|
|
1417
|
+
for (const file of expandFilePatterns(root, options.build?.scanCss ?? [])) {
|
|
1418
|
+
compilation.fileDependencies.add(file);
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1041
1421
|
}
|
|
1042
1422
|
},
|
|
1043
|
-
|
|
1044
|
-
order: "pre",
|
|
1423
|
+
vite: {
|
|
1045
1424
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
* Also
|
|
1048
|
-
* @param
|
|
1049
|
-
* @returns transformed HTML with injected hydration data
|
|
1425
|
+
* Vite hook: pre-scans source files when config is resolved.
|
|
1426
|
+
* Also runs theme scan to generate .csszyx/theme.d.ts if scanCss is configured.
|
|
1427
|
+
* @param config - the resolved Vite configuration object
|
|
1050
1428
|
*/
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1429
|
+
configResolved(config) {
|
|
1430
|
+
const root = config.root || process.cwd();
|
|
1431
|
+
state.rootDir = root;
|
|
1432
|
+
prescanAndWriteClasses();
|
|
1433
|
+
runThemeScan(root, options.build?.scanCss);
|
|
1434
|
+
},
|
|
1435
|
+
/**
|
|
1436
|
+
* Vite HMR hook: re-runs theme scan when a watched CSS file changes,
|
|
1437
|
+
* and incrementally updates csszyx-classes.html when a source file gains new sz classes.
|
|
1438
|
+
* @param ctx - HMR context containing the changed file
|
|
1439
|
+
*/
|
|
1440
|
+
handleHotUpdate(ctx) {
|
|
1441
|
+
const scanCss = options.build?.scanCss;
|
|
1442
|
+
if (scanCss) {
|
|
1443
|
+
const root = ctx.server.config.root || process.cwd();
|
|
1444
|
+
if (matchesAnyPattern(ctx.file, scanCss, root)) {
|
|
1445
|
+
runThemeScan(root, scanCss);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (!shouldProcessSource(ctx.file)) {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
let fileContent, result;
|
|
1452
|
+
try {
|
|
1453
|
+
fileContent = fs.readFileSync(ctx.file, "utf-8");
|
|
1454
|
+
} catch {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
try {
|
|
1461
|
+
result = transformConfiguredSource(fileContent, ctx.file);
|
|
1462
|
+
} catch {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
if (!result.transformed) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
const sizeBefore = state.classes.size;
|
|
1469
|
+
for (const cls of result.classes) {
|
|
1470
|
+
state.classes.add(cls);
|
|
1471
|
+
}
|
|
1472
|
+
for (const [token, data] of result.recoveryTokens) {
|
|
1473
|
+
state.recoveryTokens.set(token, data);
|
|
1474
|
+
}
|
|
1475
|
+
if (state.classes.size > sizeBefore) {
|
|
1476
|
+
writeSafelistFile(state.classes);
|
|
1477
|
+
const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
|
|
1478
|
+
ctx.server.watcher.emit("change", safelistPath);
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
transformIndexHtml: {
|
|
1482
|
+
order: "pre",
|
|
1483
|
+
/**
|
|
1484
|
+
* Injects hydration data (mangle map + checksum) into the HTML document.
|
|
1485
|
+
* Also mangles class attributes in SSR-rendered HTML so they match mangled CSS selectors.
|
|
1486
|
+
* @param html - the raw HTML string to transform
|
|
1487
|
+
* @returns transformed HTML with injected hydration data
|
|
1488
|
+
*/
|
|
1489
|
+
handler(html) {
|
|
1490
|
+
finalizeMangleMap();
|
|
1491
|
+
let result = transformIndexHtml(html, state.mangleMap, state.checksum, {
|
|
1492
|
+
mode: options.production?.injectChecksum === false ? "script" : "script",
|
|
1493
|
+
minify: process.env.NODE_ENV === "production"
|
|
1494
|
+
});
|
|
1495
|
+
if (state.recoveryTokens.size > 0) {
|
|
1496
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
1497
|
+
const { manifest, strippedDevOnlyPaths } = buildRecoveryManifest(
|
|
1498
|
+
state.recoveryTokens,
|
|
1499
|
+
{
|
|
1500
|
+
production: isProduction,
|
|
1501
|
+
mangleChecksum: state.checksum
|
|
1502
|
+
}
|
|
1066
1503
|
);
|
|
1504
|
+
if (strippedDevOnlyPaths.length > 0) {
|
|
1505
|
+
console.warn(
|
|
1506
|
+
`[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(", ")}`
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
result = injectRecoveryManifest(result, manifest);
|
|
1067
1510
|
}
|
|
1068
|
-
|
|
1511
|
+
return result;
|
|
1069
1512
|
}
|
|
1070
|
-
return result;
|
|
1071
1513
|
}
|
|
1072
1514
|
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1515
|
+
})
|
|
1516
|
+
);
|
|
1075
1517
|
const postPlugin = createUnplugin(() => ({
|
|
1076
1518
|
name: "csszyx:post",
|
|
1077
1519
|
enforce: "post",
|
|
@@ -1083,8 +1525,7 @@ ${sourceDirective}`
|
|
|
1083
1525
|
*/
|
|
1084
1526
|
webpack(compiler) {
|
|
1085
1527
|
compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
|
|
1086
|
-
const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE ||
|
|
1087
|
-
compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
|
|
1528
|
+
const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
|
|
1088
1529
|
compilation.hooks.processAssets.tap(
|
|
1089
1530
|
{
|
|
1090
1531
|
name: "csszyx:post",
|
|
@@ -1124,19 +1565,24 @@ ${sourceDirective}`
|
|
|
1124
1565
|
continue;
|
|
1125
1566
|
}
|
|
1126
1567
|
} catch (e) {
|
|
1127
|
-
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
|
|
1128
|
-
} else {
|
|
1568
|
+
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
|
|
1129
1569
|
throw e;
|
|
1130
1570
|
}
|
|
1131
1571
|
}
|
|
1132
1572
|
} else if (file.endsWith(".html")) {
|
|
1133
|
-
const mangledHtml = source.replace(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1573
|
+
const mangledHtml = source.replace(
|
|
1574
|
+
/\bclass="([^"]*)"/g,
|
|
1575
|
+
(_m, cls) => {
|
|
1576
|
+
const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
|
|
1577
|
+
return out !== cls ? `class="${out}"` : _m;
|
|
1578
|
+
}
|
|
1579
|
+
).replace(
|
|
1580
|
+
/\bclass='([^']*)'/g,
|
|
1581
|
+
(_m, cls) => {
|
|
1582
|
+
const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
|
|
1583
|
+
return out !== cls ? `class='${out}'` : _m;
|
|
1584
|
+
}
|
|
1585
|
+
);
|
|
1140
1586
|
if (mangledHtml !== source) {
|
|
1141
1587
|
compilation.updateAsset(
|
|
1142
1588
|
file,
|
|
@@ -1205,8 +1651,7 @@ ${sourceDirective}`
|
|
|
1205
1651
|
chunk.source = result.css;
|
|
1206
1652
|
}
|
|
1207
1653
|
} catch (e) {
|
|
1208
|
-
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
|
|
1209
|
-
} else {
|
|
1654
|
+
if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
|
|
1210
1655
|
throw e;
|
|
1211
1656
|
}
|
|
1212
1657
|
}
|
|
@@ -1232,13 +1677,13 @@ ${sourceDirective}`
|
|
|
1232
1677
|
}));
|
|
1233
1678
|
return { prePlugin, postPlugin };
|
|
1234
1679
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1680
|
+
const defaultInstance = createCsszyxPlugins();
|
|
1681
|
+
const unplugin = defaultInstance.prePlugin;
|
|
1682
|
+
const vitePlugin = (options = {}) => {
|
|
1238
1683
|
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
1239
1684
|
return [prePlugin.vite(options), postPlugin.vite(options)];
|
|
1240
1685
|
};
|
|
1241
|
-
|
|
1686
|
+
const webpackPlugin = (options = {}) => {
|
|
1242
1687
|
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
1243
1688
|
return {
|
|
1244
1689
|
/**
|
|
@@ -1251,11 +1696,14 @@ var webpackPlugin = (options = {}) => {
|
|
|
1251
1696
|
}
|
|
1252
1697
|
};
|
|
1253
1698
|
};
|
|
1254
|
-
|
|
1699
|
+
const rollupPlugin = (options = {}) => {
|
|
1255
1700
|
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
1256
|
-
return [
|
|
1701
|
+
return [
|
|
1702
|
+
prePlugin.rollup(options),
|
|
1703
|
+
postPlugin.rollup(options)
|
|
1704
|
+
];
|
|
1257
1705
|
};
|
|
1258
|
-
|
|
1706
|
+
const esbuildPlugin = (options = {}) => {
|
|
1259
1707
|
const { prePlugin, postPlugin } = createCsszyxPlugins(options);
|
|
1260
1708
|
return {
|
|
1261
1709
|
name: "csszyx",
|
|
@@ -1271,14 +1719,4 @@ var esbuildPlugin = (options = {}) => {
|
|
|
1271
1719
|
};
|
|
1272
1720
|
};
|
|
1273
1721
|
|
|
1274
|
-
export {
|
|
1275
|
-
parseThemeBlocks,
|
|
1276
|
-
mergeThemes,
|
|
1277
|
-
hasTokens,
|
|
1278
|
-
mangleCodeClassesSync,
|
|
1279
|
-
unplugin,
|
|
1280
|
-
vitePlugin,
|
|
1281
|
-
webpackPlugin,
|
|
1282
|
-
rollupPlugin,
|
|
1283
|
-
esbuildPlugin
|
|
1284
|
-
};
|
|
1722
|
+
export { assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createRSCModuleRecord as c, findRSCGraphViolation as d, esbuildPlugin as e, findRSCBoundaryViolation as f, hasUseClientDirective as g, hasTokens as h, hasUseServerDirective as i, isRSCServerModule as j, mergeThemes as k, mangleCodeClassesSync as m, parseThemeBlocks as p, rollupPlugin as r, unplugin as u, vitePlugin as v, webpackPlugin as w };
|