@csszyx/unplugin 0.1.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.
@@ -0,0 +1,663 @@
1
+ import {
2
+ mangleCSSSync
3
+ } from "./chunk-4M7CPGP7.js";
4
+
5
+ // src/unplugin.ts
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import { transform, transformSourceCode } from "@csszyx/compiler";
9
+ import { compute_mangle_checksum, encode } from "@csszyx/core";
10
+ import { preprocess as sveltePreprocess } from "@csszyx/svelte-adapter";
11
+ import { preprocess as vuePreprocess } from "@csszyx/vue-adapter";
12
+ import { createUnplugin } from "unplugin";
13
+
14
+ // src/html-transformer.ts
15
+ function injectChecksum(html, checksum, minify = false) {
16
+ const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
17
+ const htmlTagPattern = /<html([^>]*)>/i;
18
+ const match = html.match(htmlTagPattern);
19
+ if (!match) {
20
+ return html;
21
+ }
22
+ const existingAttrs = match[1];
23
+ const checksumAttr = ` ${attrName}="${checksum}"`;
24
+ return html.replace(
25
+ htmlTagPattern,
26
+ `<html${checksumAttr}${existingAttrs}>`
27
+ );
28
+ }
29
+ function injectMangleMapScript(html, mangleMap, options = {}) {
30
+ const { prettyPrint = false } = options;
31
+ const jsonContent = prettyPrint ? JSON.stringify(mangleMap, null, 2) : JSON.stringify(mangleMap);
32
+ const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}</script>`;
33
+ 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>`;
34
+ const combined = `${scriptTag}
35
+ ${debugScript}`;
36
+ if (html.includes("</head>")) {
37
+ return html.replace("</head>", `${combined}
38
+ </head>`);
39
+ } else if (html.includes("</html>")) {
40
+ return html.replace("</html>", `${combined}
41
+ </html>`);
42
+ }
43
+ return html + combined;
44
+ }
45
+ function injectMangleMapAttribute(html, mangleMap, minify = false) {
46
+ const attrName = minify ? "data-sz-m" : "data-sz-map";
47
+ const jsonContent = JSON.stringify(mangleMap);
48
+ const htmlTagPattern = /<html([^>]*)>/i;
49
+ const match = html.match(htmlTagPattern);
50
+ if (!match) {
51
+ return html;
52
+ }
53
+ const existingAttrs = match[1];
54
+ const mapAttr = ` ${attrName}='${jsonContent}'`;
55
+ return html.replace(htmlTagPattern, `<html${mapAttr}${existingAttrs}>`);
56
+ }
57
+ function injectHydrationData(html, mangleMap, checksum, options = {}) {
58
+ const { mode = "script", minify = false } = options;
59
+ let result = html;
60
+ result = injectChecksum(result, checksum, minify);
61
+ if (mode === "inline") {
62
+ result = injectMangleMapAttribute(result, mangleMap, minify);
63
+ } else if (mode === "script") {
64
+ result = injectMangleMapScript(result, mangleMap, options);
65
+ } else if (mode === "both") {
66
+ result = injectMangleMapAttribute(result, mangleMap, minify);
67
+ result = injectMangleMapScript(result, mangleMap, options);
68
+ }
69
+ return result;
70
+ }
71
+ function transformIndexHtml(html, mangleMap, checksum, options = {}) {
72
+ return injectHydrationData(html, mangleMap, checksum, options);
73
+ }
74
+
75
+ // src/virtual-modules.ts
76
+ var VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
77
+ var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
78
+ var VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
79
+ var RESOLVED_VIRTUAL_CHECKSUM_ID = "\0" + VIRTUAL_CHECKSUM_ID;
80
+ function createMangleMapModule(mangleMap, checksum) {
81
+ return `/**
82
+ * Auto-generated mangle map for csszyx.
83
+ * This module is generated at build time and contains the mapping
84
+ * from original class names to mangled class names.
85
+ *
86
+ * @generated
87
+ */
88
+
89
+ export const mangleMap = ${JSON.stringify(mangleMap, null, 2)};
90
+
91
+ export const checksum = ${JSON.stringify(checksum)};
92
+
93
+ export default {
94
+ mangleMap,
95
+ checksum,
96
+ };
97
+ `;
98
+ }
99
+ function createChecksumModule(checksum) {
100
+ return `/**
101
+ * Auto-generated checksum for csszyx mangle map.
102
+ *
103
+ * @generated
104
+ */
105
+
106
+ export const checksum = ${JSON.stringify(checksum)};
107
+
108
+ export default checksum;
109
+ `;
110
+ }
111
+ function isVirtualModule(id) {
112
+ return id === VIRTUAL_MODULE_ID || id === VIRTUAL_CHECKSUM_ID;
113
+ }
114
+ function resolveVirtualModule(id) {
115
+ if (id === VIRTUAL_MODULE_ID) {
116
+ return RESOLVED_VIRTUAL_MODULE_ID;
117
+ }
118
+ if (id === VIRTUAL_CHECKSUM_ID) {
119
+ return RESOLVED_VIRTUAL_CHECKSUM_ID;
120
+ }
121
+ return void 0;
122
+ }
123
+
124
+ // src/unplugin.ts
125
+ var CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
126
+ var MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
127
+ function createCsszyxPlugins(options = {}) {
128
+ const manglingEnabled = options.production?.mangle !== false;
129
+ const state = {
130
+ classes: /* @__PURE__ */ new Set(),
131
+ mangleMap: {},
132
+ checksum: "",
133
+ finalized: false
134
+ };
135
+ const SAFELIST_FILENAME = "csszyx-classes.js";
136
+ const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
137
+ const IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", ".turbo"]);
138
+ function prescanAndWriteClasses(rootDir) {
139
+ const discoveredClasses = /* @__PURE__ */ new Set();
140
+ function scanDir(dir) {
141
+ let entries;
142
+ try {
143
+ entries = fs.readdirSync(dir, { withFileTypes: true });
144
+ } catch {
145
+ return;
146
+ }
147
+ for (const entry of entries) {
148
+ if (entry.isDirectory()) {
149
+ if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
150
+ scanDir(path.join(dir, entry.name));
151
+ }
152
+ } else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
153
+ const filePath = path.join(dir, entry.name);
154
+ try {
155
+ const content = fs.readFileSync(filePath, "utf-8");
156
+ if (!content.includes("sz=") && !content.includes("sz:")) {
157
+ continue;
158
+ }
159
+ const result = transformSourceCode(content);
160
+ if (!result.transformed) {
161
+ continue;
162
+ }
163
+ const classPattern = /class(?:Name)?=["']([^"']*)["']/g;
164
+ let match;
165
+ while ((match = classPattern.exec(result.code)) !== null) {
166
+ for (const cls of match[1].split(/\s+/).filter(Boolean)) {
167
+ discoveredClasses.add(cls);
168
+ }
169
+ }
170
+ const exprPattern = /className=\{/g;
171
+ while ((match = exprPattern.exec(result.code)) !== null) {
172
+ let depth = 1;
173
+ let i = match.index + match[0].length;
174
+ while (i < result.code.length && depth > 0) {
175
+ if (result.code[i] === "{") {
176
+ depth++;
177
+ } else if (result.code[i] === "}") {
178
+ depth--;
179
+ }
180
+ i++;
181
+ }
182
+ const expr = result.code.slice(match.index + match[0].length, i - 1);
183
+ const strPattern = /"([^"]+)"|'([^']+)'/g;
184
+ let strMatch;
185
+ while ((strMatch = strPattern.exec(expr)) !== null) {
186
+ const str = strMatch[1] || strMatch[2];
187
+ for (const cls of str.split(/\s+/).filter(Boolean)) {
188
+ discoveredClasses.add(cls);
189
+ }
190
+ }
191
+ }
192
+ if (result.usesRuntime) {
193
+ const szCallRe = /_sz\(\s*\{/g;
194
+ let szMatch;
195
+ while ((szMatch = szCallRe.exec(result.code)) !== null) {
196
+ let depth = 1;
197
+ let idx = szMatch.index + szMatch[0].length;
198
+ while (idx < result.code.length && depth > 0) {
199
+ if (result.code[idx] === "{") {
200
+ depth++;
201
+ } else if (result.code[idx] === "}") {
202
+ depth--;
203
+ }
204
+ idx++;
205
+ }
206
+ const objStr = result.code.slice(szMatch.index + szMatch[0].length, idx - 1);
207
+ const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
208
+ let kv;
209
+ while ((kv = strKv.exec(objStr)) !== null) {
210
+ try {
211
+ const val = kv[2] ?? kv[3];
212
+ const r = transform({ [kv[1]]: val });
213
+ for (const c of r.className.split(/\s+/).filter(Boolean)) {
214
+ discoveredClasses.add(c);
215
+ }
216
+ } catch {
217
+ }
218
+ }
219
+ const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
220
+ while ((kv = numKv.exec(objStr)) !== null) {
221
+ try {
222
+ const r = transform({ [kv[1]]: parseFloat(kv[2]) });
223
+ for (const c of r.className.split(/\s+/).filter(Boolean)) {
224
+ discoveredClasses.add(c);
225
+ }
226
+ } catch {
227
+ }
228
+ }
229
+ const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
230
+ while ((kv = boolKv.exec(objStr)) !== null) {
231
+ try {
232
+ const r = transform({ [kv[1]]: kv[2] === "true" });
233
+ for (const c of r.className.split(/\s+/).filter(Boolean)) {
234
+ discoveredClasses.add(c);
235
+ }
236
+ } catch {
237
+ }
238
+ }
239
+ }
240
+ }
241
+ } catch {
242
+ }
243
+ }
244
+ }
245
+ }
246
+ scanDir(rootDir);
247
+ for (const cls of discoveredClasses) {
248
+ state.classes.add(cls);
249
+ }
250
+ if (discoveredClasses.size > 0) {
251
+ const safelistPath = path.join(rootDir, SAFELIST_FILENAME);
252
+ const content = '// Auto-generated by csszyx \u2014 DO NOT EDIT\n// Tailwind CSS scans this file for class name detection\nexport default "' + Array.from(discoveredClasses).join(" ") + '";\n';
253
+ try {
254
+ const existing = fs.existsSync(safelistPath) ? fs.readFileSync(safelistPath, "utf-8") : "";
255
+ if (existing !== content) {
256
+ fs.writeFileSync(safelistPath, content);
257
+ }
258
+ } catch {
259
+ }
260
+ }
261
+ }
262
+ function extractClasses(code) {
263
+ const classPattern = /(?:class(?:Name)?|sz)[:=]\s*["']([^"']*)["']/g;
264
+ let match;
265
+ while ((match = classPattern.exec(code)) !== null) {
266
+ const classes = match[1].split(/\s+/).filter(Boolean);
267
+ for (const cls of classes) {
268
+ state.classes.add(cls);
269
+ }
270
+ }
271
+ const exprStart = /className=\{/g;
272
+ while ((match = exprStart.exec(code)) !== null) {
273
+ let depth = 1;
274
+ let i = match.index + match[0].length;
275
+ while (i < code.length && depth > 0) {
276
+ if (code[i] === "{") {
277
+ depth++;
278
+ } else if (code[i] === "}") {
279
+ depth--;
280
+ }
281
+ i++;
282
+ }
283
+ const expr = code.slice(match.index + match[0].length, i - 1);
284
+ const strPattern = /"([^"]+)"|'([^']+)'/g;
285
+ let strMatch;
286
+ while ((strMatch = strPattern.exec(expr)) !== null) {
287
+ const str = strMatch[1] || strMatch[2];
288
+ const classes = str.split(/\s+/).filter(Boolean);
289
+ for (const cls of classes) {
290
+ state.classes.add(cls);
291
+ }
292
+ }
293
+ }
294
+ }
295
+ function finalizeMangleMap() {
296
+ const sortedClasses = Array.from(state.classes);
297
+ const newMap = {};
298
+ for (let i = 0; i < sortedClasses.length; i++) {
299
+ newMap[sortedClasses[i]] = encode(i);
300
+ }
301
+ state.mangleMap = newMap;
302
+ state.checksum = compute_mangle_checksum(state.mangleMap);
303
+ state.finalized = true;
304
+ }
305
+ function mangleClassString(classString) {
306
+ return classString.split(/\s+/).map((cls) => state.mangleMap[cls] || cls).join(" ");
307
+ }
308
+ function mangleCodeClasses(code) {
309
+ let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*["']([^"']*)["']/g, (match, classes) => {
310
+ const mangled = mangleClassString(classes);
311
+ if (mangled === classes) {
312
+ return match;
313
+ }
314
+ return match.replace(classes, mangled);
315
+ });
316
+ result = result.replace(/className:([^,;}\])\n]+)/g, (fullMatch, expr) => {
317
+ let changed = false;
318
+ const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
319
+ const parts = inner.split(/\s+/).filter(Boolean);
320
+ if (parts.length === 0) {
321
+ return qm;
322
+ }
323
+ const mangledStr = parts.map((p) => state.mangleMap[p] || p).join(" ");
324
+ if (mangledStr !== inner) {
325
+ changed = true;
326
+ return '"' + mangledStr + '"';
327
+ }
328
+ return qm;
329
+ });
330
+ if (changed) {
331
+ return "className:" + mangled;
332
+ }
333
+ return fullMatch;
334
+ });
335
+ return result;
336
+ }
337
+ function replacePlaceholders(code) {
338
+ let result = code;
339
+ if (result.includes(CHECKSUM_PLACEHOLDER)) {
340
+ result = result.split(CHECKSUM_PLACEHOLDER).join(state.checksum);
341
+ }
342
+ if (result.includes(MANGLE_MAP_PLACEHOLDER)) {
343
+ result = result.split(MANGLE_MAP_PLACEHOLDER).join(JSON.stringify(state.mangleMap));
344
+ }
345
+ return result;
346
+ }
347
+ const prePlugin = createUnplugin((_pluginOptions) => ({
348
+ name: "csszyx:pre",
349
+ enforce: "pre",
350
+ /**
351
+ * Resolves virtual module IDs for csszyx mangle-map and checksum modules.
352
+ * @param id - the module ID to resolve
353
+ * @returns resolved ID if virtual, null otherwise
354
+ */
355
+ resolveId(id) {
356
+ if (isVirtualModule(id)) {
357
+ return resolveVirtualModule(id);
358
+ }
359
+ return null;
360
+ },
361
+ /**
362
+ * Loads virtual module content — generates mangle map or checksum module code.
363
+ * @param id - the resolved module ID to load
364
+ * @returns generated module source if virtual, null otherwise
365
+ */
366
+ load(id) {
367
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
368
+ finalizeMangleMap();
369
+ return createMangleMapModule(state.mangleMap, state.checksum);
370
+ }
371
+ if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
372
+ finalizeMangleMap();
373
+ return createChecksumModule(state.checksum);
374
+ }
375
+ return null;
376
+ },
377
+ /**
378
+ * Filters files for the pre-transform phase — only source files outside node_modules.
379
+ * @param id - the file path to check for inclusion
380
+ * @returns true if the file should be transformed, false otherwise
381
+ */
382
+ transformInclude(id) {
383
+ if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
384
+ return false;
385
+ }
386
+ return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
387
+ },
388
+ /**
389
+ * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
390
+ * @param code - the source code to transform
391
+ * @param id - the file path of the module being transformed
392
+ * @returns transformed code with source map, or null if no changes were made
393
+ */
394
+ transform(code, id) {
395
+ let transformedCode = code;
396
+ let usesRuntime = false;
397
+ let usesColorVar = false;
398
+ let transformed = false;
399
+ const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
400
+ if (hasSzProp) {
401
+ if (id.endsWith(".vue")) {
402
+ const result = vuePreprocess(code, options);
403
+ if (result.transformed) {
404
+ transformedCode = result.code;
405
+ transformed = true;
406
+ }
407
+ } else if (id.endsWith(".svelte")) {
408
+ const result = sveltePreprocess(code, options);
409
+ if (result) {
410
+ transformedCode = result.code;
411
+ transformed = true;
412
+ }
413
+ } else {
414
+ const result = transformSourceCode(code);
415
+ transformedCode = result.code;
416
+ usesRuntime = result.usesRuntime;
417
+ usesColorVar = result.usesColorVar;
418
+ transformed = result.transformed;
419
+ }
420
+ }
421
+ if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
422
+ const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
423
+ transformedCode = transformedCode.replace(/<html([^>]*)>/i, `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`);
424
+ 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})}}})()\`}} />`;
425
+ if (transformedCode.includes("<body")) {
426
+ transformedCode = transformedCode.replace(
427
+ /(<body[^>]*>)/i,
428
+ `$1${debugScript}`
429
+ );
430
+ }
431
+ transformed = true;
432
+ }
433
+ {
434
+ const imports = [];
435
+ if (usesRuntime) {
436
+ imports.push("_sz");
437
+ }
438
+ if (usesColorVar) {
439
+ imports.push("__szColorVar");
440
+ }
441
+ if (imports.length > 0 && !transformedCode.includes("from 'csszyx/lite'")) {
442
+ const importStmt = `import { ${imports.join(", ")} } from 'csszyx/lite';
443
+ `;
444
+ const directiveMatch = transformedCode.match(/^['"]use (client|server)['"];?\s*/);
445
+ if (directiveMatch) {
446
+ const directive = directiveMatch[0];
447
+ transformedCode = transformedCode.replace(directive, `${directive}${importStmt}`);
448
+ } else {
449
+ transformedCode = `${importStmt}${transformedCode}`;
450
+ }
451
+ transformed = true;
452
+ }
453
+ }
454
+ if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
455
+ extractClasses(transformedCode);
456
+ return { code: transformedCode, map: null };
457
+ }
458
+ return null;
459
+ },
460
+ /** Finalizes the mangle map after all source modules have been processed. */
461
+ buildEnd() {
462
+ finalizeMangleMap();
463
+ },
464
+ /**
465
+ * Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
466
+ * @param compiler - the Webpack compiler instance
467
+ */
468
+ webpack(compiler) {
469
+ compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
470
+ if (state.classes.size === 0) {
471
+ prescanAndWriteClasses(compiler.context || process.cwd());
472
+ }
473
+ });
474
+ },
475
+ vite: {
476
+ /**
477
+ * Vite hook: pre-scans source files when config is resolved.
478
+ * @param config - the resolved Vite configuration object
479
+ */
480
+ configResolved(config) {
481
+ prescanAndWriteClasses(config.root || process.cwd());
482
+ },
483
+ transformIndexHtml: {
484
+ order: "pre",
485
+ /**
486
+ * Injects hydration data (mangle map + checksum) into the HTML document.
487
+ * @param html - the raw HTML string to transform
488
+ * @returns transformed HTML with injected hydration data
489
+ */
490
+ handler(html) {
491
+ finalizeMangleMap();
492
+ return transformIndexHtml(html, state.mangleMap, state.checksum, {
493
+ mode: options.production?.injectChecksum === false ? "script" : "script",
494
+ minify: process.env.NODE_ENV === "production"
495
+ });
496
+ }
497
+ }
498
+ }
499
+ }));
500
+ const postPlugin = createUnplugin(() => ({
501
+ name: "csszyx:post",
502
+ enforce: "post",
503
+ // No transform hook — all mangling is deferred to asset processing
504
+ // where the complete mangle map is available.
505
+ /**
506
+ * Webpack hook: mangles CSS/JS class names in processAssets after compilation.
507
+ * @param compiler - the Webpack compiler instance
508
+ */
509
+ webpack(compiler) {
510
+ compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
511
+ const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || // eslint-disable-next-line @typescript-eslint/no-explicit-any
512
+ compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
513
+ compilation.hooks.processAssets.tap(
514
+ {
515
+ name: "csszyx:post",
516
+ stage: stage || 400
517
+ // Fallback integer
518
+ },
519
+ (assets) => {
520
+ finalizeMangleMap();
521
+ for (const file in assets) {
522
+ const asset = assets[file];
523
+ const source = asset.source().toString();
524
+ if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
525
+ if (file.endsWith(".css")) {
526
+ try {
527
+ const result = mangleCSSSync(source, state.mangleMap, {
528
+ debug: options.development?.debug,
529
+ from: file
530
+ });
531
+ if (result.transformedCount > 0) {
532
+ compilation.updateAsset(
533
+ file,
534
+ new compiler.webpack.sources.RawSource(result.css)
535
+ );
536
+ continue;
537
+ }
538
+ } catch (e) {
539
+ if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
540
+ } else {
541
+ throw e;
542
+ }
543
+ }
544
+ } else if (file.endsWith(".js")) {
545
+ let mangled = mangleCodeClasses(source);
546
+ mangled = replacePlaceholders(mangled);
547
+ if (mangled !== source) {
548
+ compilation.updateAsset(
549
+ file,
550
+ new compiler.webpack.sources.RawSource(mangled)
551
+ );
552
+ continue;
553
+ }
554
+ }
555
+ }
556
+ if (file.endsWith(".js") && (source.includes(CHECKSUM_PLACEHOLDER) || source.includes(MANGLE_MAP_PLACEHOLDER))) {
557
+ const replaced = replacePlaceholders(source);
558
+ if (replaced !== source) {
559
+ compilation.updateAsset(
560
+ file,
561
+ new compiler.webpack.sources.RawSource(replaced)
562
+ );
563
+ }
564
+ }
565
+ }
566
+ }
567
+ );
568
+ });
569
+ },
570
+ vite: {
571
+ /**
572
+ * Vite hook: mangles CSS selectors and JS class strings in the final bundle.
573
+ * @param _options - the output options (unused)
574
+ * @param bundle - the output bundle containing chunks and assets to process
575
+ */
576
+ generateBundle(_options, bundle) {
577
+ finalizeMangleMap();
578
+ for (const file in bundle) {
579
+ const chunk = bundle[file];
580
+ if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
581
+ if (chunk.type === "asset" && chunk.fileName.endsWith(".css")) {
582
+ const css = chunk.source.toString();
583
+ try {
584
+ const result = mangleCSSSync(css, state.mangleMap, {
585
+ debug: options.development?.debug,
586
+ from: file
587
+ });
588
+ if (result.transformedCount > 0) {
589
+ chunk.source = result.css;
590
+ }
591
+ } catch (e) {
592
+ if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
593
+ } else {
594
+ throw e;
595
+ }
596
+ }
597
+ continue;
598
+ } else if (chunk.type === "chunk") {
599
+ let mangledCode = mangleCodeClasses(chunk.code);
600
+ mangledCode = replacePlaceholders(mangledCode);
601
+ if (mangledCode !== chunk.code) {
602
+ chunk.code = mangledCode;
603
+ }
604
+ continue;
605
+ }
606
+ }
607
+ if (chunk.type === "chunk" && (chunk.code.includes(CHECKSUM_PLACEHOLDER) || chunk.code.includes(MANGLE_MAP_PLACEHOLDER))) {
608
+ const replaced = replacePlaceholders(chunk.code);
609
+ if (replaced !== chunk.code) {
610
+ chunk.code = replaced;
611
+ }
612
+ }
613
+ }
614
+ }
615
+ }
616
+ }));
617
+ return { prePlugin, postPlugin };
618
+ }
619
+ var defaultInstance = createCsszyxPlugins();
620
+ var unplugin = defaultInstance.prePlugin;
621
+ var vitePlugin = (options = {}) => {
622
+ const { prePlugin, postPlugin } = createCsszyxPlugins(options);
623
+ return [prePlugin.vite(options), postPlugin.vite(options)];
624
+ };
625
+ var webpackPlugin = (options = {}) => {
626
+ const { prePlugin, postPlugin } = createCsszyxPlugins(options);
627
+ return {
628
+ /**
629
+ * Applies both pre and post plugins to the Webpack compiler.
630
+ * @param compiler - the Webpack compiler instance to apply plugins to
631
+ */
632
+ apply(compiler) {
633
+ prePlugin.webpack(options).apply(compiler);
634
+ postPlugin.webpack(options).apply(compiler);
635
+ }
636
+ };
637
+ };
638
+ var rollupPlugin = (options = {}) => {
639
+ const { prePlugin, postPlugin } = createCsszyxPlugins(options);
640
+ return [prePlugin.rollup(options), postPlugin.rollup(options)];
641
+ };
642
+ var esbuildPlugin = (options = {}) => {
643
+ const { prePlugin, postPlugin } = createCsszyxPlugins(options);
644
+ return {
645
+ name: "csszyx",
646
+ /**
647
+ * Registers both pre and post plugin setup hooks with the esbuild build.
648
+ * @param build - the esbuild plugin build context
649
+ */
650
+ setup(build) {
651
+ prePlugin.esbuild(options).setup(build);
652
+ postPlugin.esbuild(options).setup(build);
653
+ }
654
+ };
655
+ };
656
+
657
+ export {
658
+ unplugin,
659
+ vitePlugin,
660
+ webpackPlugin,
661
+ rollupPlugin,
662
+ esbuildPlugin
663
+ };