@csszyx/unplugin 0.11.8 → 0.11.10

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.
Files changed (39) hide show
  1. package/dist/index.cjs +4 -4
  2. package/dist/index.d.cts +2 -2
  3. package/dist/index.d.mts +2 -2
  4. package/dist/index.mjs +4 -4
  5. package/dist/next-prebuild.cjs +4 -4
  6. package/dist/next-prebuild.d.cts +1 -1
  7. package/dist/next-prebuild.d.mts +1 -1
  8. package/dist/next-prebuild.mjs +4 -4
  9. package/dist/next-turbo-loader.cjs +5 -5
  10. package/dist/next-turbo-loader.d.cts +1 -1
  11. package/dist/next-turbo-loader.d.mts +1 -1
  12. package/dist/next-turbo-loader.mjs +5 -5
  13. package/dist/next-watcher.cjs +2 -2
  14. package/dist/next-watcher.mjs +2 -2
  15. package/dist/shared/{unplugin.Rl8sIkHi.mjs → unplugin.-yLpX1Ck.mjs} +3 -3
  16. package/dist/shared/{unplugin.TtmP77BS.mjs → unplugin.B3XoSRB8.mjs} +1 -1
  17. package/dist/shared/{unplugin.DdtOZANG.cjs → unplugin.BZq6FKn2.cjs} +3 -3
  18. package/dist/shared/{unplugin.CcA7jP1r.cjs → unplugin.BkRah5Ot.cjs} +1 -1
  19. package/dist/shared/{unplugin.DVCQC5wq.cjs → unplugin.CKIOVNOg.cjs} +9 -0
  20. package/dist/shared/{unplugin.DtPQz8Kg.cjs → unplugin.CRuVOB4Q.cjs} +147 -52
  21. package/dist/shared/{unplugin.DtYxS8M8.mjs → unplugin.CVMbUfH5.mjs} +145 -50
  22. package/dist/shared/{unplugin.CScQRdTp.d.mts → unplugin.CXV7fOa2.d.cts} +1 -1
  23. package/dist/shared/{unplugin.CScQRdTp.d.cts → unplugin.CXV7fOa2.d.mts} +1 -1
  24. package/dist/shared/{unplugin.CNwV3RrQ.d.cts → unplugin.CjCKWnoX.d.cts} +7 -1
  25. package/dist/shared/{unplugin.CNwV3RrQ.d.mts → unplugin.CjCKWnoX.d.mts} +7 -1
  26. package/dist/shared/{unplugin.DmuOKVS4.cjs → unplugin.Cnxm1DcC.cjs} +2 -2
  27. package/dist/shared/{unplugin.Dg1tGPSd.mjs → unplugin.DMcbmP01.mjs} +47 -47
  28. package/dist/shared/{unplugin.C9dMG7ye.cjs → unplugin.DblMogcN.cjs} +47 -47
  29. package/dist/shared/{unplugin.nnsa7OVW.mjs → unplugin.Lhzkcj-A.mjs} +2 -2
  30. package/dist/shared/{unplugin.D-K57LNR.mjs → unplugin.fOnGXWgS.mjs} +9 -1
  31. package/dist/vite.cjs +4 -4
  32. package/dist/vite.d.cts +1 -1
  33. package/dist/vite.d.mts +1 -1
  34. package/dist/vite.mjs +4 -4
  35. package/dist/webpack.cjs +4 -4
  36. package/dist/webpack.d.cts +1 -1
  37. package/dist/webpack.d.mts +1 -1
  38. package/dist/webpack.mjs +4 -4
  39. package/package.json +17 -17
@@ -13,10 +13,10 @@ const svelteAdapter = require('@csszyx/svelte-adapter');
13
13
  const types = require('@csszyx/types');
14
14
  const vueAdapter = require('@csszyx/vue-adapter');
15
15
  const unplugin$1 = require('unplugin');
16
+ const transformCache = require('./unplugin.CKIOVNOg.cjs');
16
17
  const cssMangler = require('../css-mangler.cjs');
17
- const runtimeImportScan = require('./unplugin.DmuOKVS4.cjs');
18
- const transformCache = require('./unplugin.DVCQC5wq.cjs');
19
- const htmlEscape = require('./unplugin.CcA7jP1r.cjs');
18
+ const runtimeImportScan = require('./unplugin.Cnxm1DcC.cjs');
19
+ const htmlEscape = require('./unplugin.BkRah5Ot.cjs');
20
20
  const postcss = require('postcss');
21
21
  const valueParser = require('postcss-value-parser');
22
22
 
@@ -456,8 +456,8 @@ function scanCssSourceWithOptionalCache(file, cacheDir) {
456
456
  return result;
457
457
  }
458
458
  function normalizeBuildAssetPath(rootDir, fileName) {
459
- return (path__namespace.isAbsolute(fileName) ? fileName : path__namespace.join(rootDir, fileName)).replace(
460
- /\\/g,
459
+ return (path__namespace.isAbsolute(fileName) ? fileName : path__namespace.join(rootDir, fileName)).replaceAll(
460
+ "\\",
461
461
  "/"
462
462
  );
463
463
  }
@@ -1180,15 +1180,44 @@ function stripLayerWrappers(css) {
1180
1180
  }
1181
1181
  return result;
1182
1182
  }
1183
+ function themePreludeStart(css, at) {
1184
+ const cursor = at + "@theme".length;
1185
+ const next = css[cursor];
1186
+ const validBoundary = next === "{" || next === " " || next === " " || next === "\n" || next === "\r";
1187
+ return validBoundary ? cursor : null;
1188
+ }
1189
+ function readThemeBlock(css, start) {
1190
+ for (let cursor = start; cursor < css.length; cursor++) {
1191
+ const character = css[cursor];
1192
+ if (character === "{") {
1193
+ const close = findMatchingBrace(css, cursor);
1194
+ return {
1195
+ body: close === -1 ? null : css.slice(cursor + 1, close),
1196
+ end: close === -1 ? cursor : close
1197
+ };
1198
+ }
1199
+ if (character === ";" || character === "}" || character === "@") {
1200
+ return { body: null, end: cursor };
1201
+ }
1202
+ }
1203
+ return { body: null, end: css.length };
1204
+ }
1183
1205
  function extractThemeBlocks(css) {
1184
1206
  const blocks = [];
1185
- const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
1186
- for (const match of css.matchAll(themeStart)) {
1187
- const openPos = css.indexOf("{", match.index);
1188
- const closePos = findMatchingBrace(css, openPos);
1189
- if (closePos !== -1) {
1190
- blocks.push(css.slice(openPos + 1, closePos));
1207
+ let searchFrom = 0;
1208
+ for (; ; ) {
1209
+ const at = css.indexOf("@theme", searchFrom);
1210
+ if (at === -1) {
1211
+ break;
1212
+ }
1213
+ const preludeStart = themePreludeStart(css, at);
1214
+ if (preludeStart === null) {
1215
+ searchFrom = at + "@theme".length;
1216
+ continue;
1191
1217
  }
1218
+ const match = readThemeBlock(css, preludeStart);
1219
+ if (match.body !== null) blocks.push(match.body);
1220
+ searchFrom = Math.max(match.end, at + "@theme".length);
1192
1221
  }
1193
1222
  return blocks;
1194
1223
  }
@@ -1358,6 +1387,26 @@ const SIMPLE_ESCAPES = {
1358
1387
  v: "\v",
1359
1388
  "0": "\0"
1360
1389
  };
1390
+ function decodeBracedUnicodeEscape(value, cursor) {
1391
+ if (value[cursor] !== "u" || value[cursor + 1] !== "{") return null;
1392
+ const close = value.indexOf("}", cursor + 2);
1393
+ const hex = close === -1 ? "" : value.slice(cursor + 2, close);
1394
+ const codePoint = hex && /^[\dA-F]+$/i.test(hex) ? Number.parseInt(hex, 16) : -1;
1395
+ if (codePoint < 0 || codePoint > 1114111) return null;
1396
+ return { value: String.fromCodePoint(codePoint), cursor: close };
1397
+ }
1398
+ function decodeFixedWidthEscape(value, cursor) {
1399
+ const escaped = value[cursor];
1400
+ let width = 0;
1401
+ if (escaped === "x") width = 2;
1402
+ if (escaped === "u") width = 4;
1403
+ const hex = value.slice(cursor + 1, cursor + width + 1);
1404
+ if (width === 0 || !isFixedHex(hex, width)) return null;
1405
+ return {
1406
+ value: String.fromCodePoint(Number.parseInt(hex, 16)),
1407
+ cursor: cursor + width
1408
+ };
1409
+ }
1361
1410
  function isFixedHex(value, width) {
1362
1411
  if (value.length !== width) return false;
1363
1412
  for (const character of value) {
@@ -1373,24 +1422,10 @@ function decodeEscape(value, cursor) {
1373
1422
  if (escaped === "\r" && value[cursor + 1] === "\n") {
1374
1423
  return { value: "", cursor: cursor + 1 };
1375
1424
  }
1376
- if (escaped === "u" && value[cursor + 1] === "{") {
1377
- const close = value.indexOf("}", cursor + 2);
1378
- const hex2 = close === -1 ? "" : value.slice(cursor + 2, close);
1379
- const codePoint = hex2 && /^[\dA-F]+$/i.test(hex2) ? Number.parseInt(hex2, 16) : -1;
1380
- if (codePoint >= 0 && codePoint <= 1114111) {
1381
- return { value: String.fromCodePoint(codePoint), cursor: close };
1382
- }
1383
- }
1384
- let width = 0;
1385
- if (escaped === "x") width = 2;
1386
- else if (escaped === "u") width = 4;
1387
- const hex = value.slice(cursor + 1, cursor + width + 1);
1388
- if (width > 0 && isFixedHex(hex, width)) {
1389
- return {
1390
- value: String.fromCodePoint(Number.parseInt(hex, 16)),
1391
- cursor: cursor + width
1392
- };
1393
- }
1425
+ const bracedUnicode = decodeBracedUnicodeEscape(value, cursor);
1426
+ if (bracedUnicode) return bracedUnicode;
1427
+ const fixedWidth = decodeFixedWidthEscape(value, cursor);
1428
+ if (fixedWidth) return fixedWidth;
1394
1429
  const decoded = escaped === "\n" || escaped === "\r" ? "" : escaped;
1395
1430
  return { value: decoded, cursor };
1396
1431
  }
@@ -2196,9 +2231,9 @@ let _hasWarnedTransformCacheVersion = false;
2196
2231
  let _hasWarnedNativeFallback = false;
2197
2232
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
2198
2233
  const _babelFallbackFiles = /* @__PURE__ */ new Set();
2199
- const requireFromHere = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.DtPQz8Kg.cjs', document.baseURI).href)));
2234
+ const requireFromHere = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.CRuVOB4Q.cjs', document.baseURI).href)));
2200
2235
  const PLUGIN_VERSION = findPackageVersionFromFile(
2201
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.DtPQz8Kg.cjs', document.baseURI).href))),
2236
+ node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.CRuVOB4Q.cjs', document.baseURI).href))),
2202
2237
  UNKNOWN_PACKAGE_VERSION
2203
2238
  );
2204
2239
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -2561,6 +2596,9 @@ function rewriteCssWithValidatedGlobalVarPlan(css, filePath, result) {
2561
2596
  });
2562
2597
  return rewrite.css;
2563
2598
  }
2599
+ function isCssSyntaxError(error) {
2600
+ return !!error && typeof error === "object" && "name" in error && error.name === "CssSyntaxError";
2601
+ }
2564
2602
  function assertGlobalVarPlanMatchesEarlyAliases(result, expectedEntries) {
2565
2603
  const actualEntries = [...result.plan.aliases.entries()].sort(
2566
2604
  ([left], [right]) => left.localeCompare(right)
@@ -2850,6 +2888,14 @@ function scanClassExpression(source, from) {
2850
2888
  }
2851
2889
  return index;
2852
2890
  }
2891
+ function findTemplateInterpolationEnd(templateContent, bodyStart) {
2892
+ let depth = 0;
2893
+ for (let cursor = bodyStart; cursor < templateContent.length; cursor++) {
2894
+ if (templateContent[cursor] === "{") depth++;
2895
+ else if (templateContent[cursor] === "}" && depth-- === 0) return cursor + 1;
2896
+ }
2897
+ return templateContent.length;
2898
+ }
2853
2899
  function mangleCodeClassesSync(code, mangleMap) {
2854
2900
  function mangleClassString(classString) {
2855
2901
  return classString.split(/\s+/).filter(Boolean).map((cls) => {
@@ -2888,14 +2934,6 @@ function mangleCodeClassesSync(code, mangleMap) {
2888
2934
  changed: mangled !== trimmed
2889
2935
  };
2890
2936
  }
2891
- function findTemplateInterpolationEnd(templateContent, bodyStart) {
2892
- let depth = 0;
2893
- for (let cursor = bodyStart; cursor < templateContent.length; cursor++) {
2894
- if (templateContent[cursor] === "{") depth++;
2895
- else if (templateContent[cursor] === "}" && depth-- === 0) return cursor + 1;
2896
- }
2897
- return templateContent.length;
2898
- }
2899
2937
  function mangleTemplateInterpolation(inner) {
2900
2938
  let changed = false;
2901
2939
  const value = inner.replace(/"([^"]*)"/g, (quoted, classString) => {
@@ -3145,6 +3183,7 @@ function createCsszyxPlugins(options = {}) {
3145
3183
  const state = {
3146
3184
  classes: /* @__PURE__ */ new Set(),
3147
3185
  parsedTheme: null,
3186
+ autoThemeCssFiles: [],
3148
3187
  sawTailwindEntry: false,
3149
3188
  sawAnyCss: false,
3150
3189
  tailwindWarningEmitted: false,
@@ -3245,6 +3284,59 @@ function createCsszyxPlugins(options = {}) {
3245
3284
  );
3246
3285
  }
3247
3286
  }
3287
+ function runAutoThemeScan(rootDir) {
3288
+ if (options.build?.scanCss) {
3289
+ return;
3290
+ }
3291
+ refreshCompileSourceDirs();
3292
+ const cssFiles = [];
3293
+ const walk = (dir) => {
3294
+ let entries;
3295
+ try {
3296
+ entries = fs__namespace.readdirSync(dir, { withFileTypes: true });
3297
+ } catch {
3298
+ return;
3299
+ }
3300
+ for (const entry of entries) {
3301
+ if (entry.isDirectory()) {
3302
+ if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
3303
+ walk(path__namespace.join(dir, entry.name));
3304
+ }
3305
+ continue;
3306
+ }
3307
+ if (entry.name.endsWith(".css")) {
3308
+ cssFiles.push(path__namespace.join(dir, entry.name));
3309
+ }
3310
+ }
3311
+ };
3312
+ walk(rootDir);
3313
+ const normRoot = normalizeForMatch(rootDir);
3314
+ for (const sourceDir of compileSourceDirs) {
3315
+ if (sourceDir === normRoot || sourceDir.startsWith(`${normRoot}/`)) {
3316
+ continue;
3317
+ }
3318
+ walk(sourceDir);
3319
+ }
3320
+ const themes = [];
3321
+ const themeFiles = [];
3322
+ for (const file of cssFiles) {
3323
+ let content;
3324
+ try {
3325
+ content = fs__namespace.readFileSync(file, "utf-8");
3326
+ } catch {
3327
+ continue;
3328
+ }
3329
+ if (!content.includes("@theme")) {
3330
+ continue;
3331
+ }
3332
+ themes.push(parseThemeBlocks(content));
3333
+ themeFiles.push(file);
3334
+ }
3335
+ state.autoThemeCssFiles = themeFiles;
3336
+ if (themes.length > 0) {
3337
+ state.parsedTheme = mergeThemes(themes);
3338
+ }
3339
+ }
3248
3340
  function isHardIgnored(id) {
3249
3341
  refreshCompileSourceDirs();
3250
3342
  return isHardIgnoredPath(id, compileSourceDirs);
@@ -3333,7 +3425,7 @@ function createCsszyxPlugins(options = {}) {
3333
3425
  }
3334
3426
  function runBabelFallback(source, effectiveFilename, compilerOptions, error) {
3335
3427
  const result = compiler.transformSourceCode(source, effectiveFilename, compilerOptions);
3336
- const reason = error instanceof Error ? error.message : String(error);
3428
+ const reason = transformCache.babelFallbackReason(error);
3337
3429
  result.diagnostics.push(
3338
3430
  `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
3339
3431
  );
@@ -4090,9 +4182,7 @@ ${transformedCode}`;
4090
4182
  emitWarning(missingTailwindEntryMessage(state.ownedClasses.size));
4091
4183
  }
4092
4184
  if (!state.contentScopeWarningEmitted && options.contentScopeCheck !== false && state.sawTailwindEntry && !state.tailwindEntryScoped) {
4093
- if (state.inMonorepo === void 0) {
4094
- state.inMonorepo = isMonorepoPackage(state.rootDir);
4095
- }
4185
+ state.inMonorepo ??= isMonorepoPackage(state.rootDir);
4096
4186
  if (shouldWarnUnscopedMonorepo(
4097
4187
  state.sawTailwindEntry,
4098
4188
  state.tailwindEntryScoped,
@@ -4144,6 +4234,7 @@ ${transformedCode}`;
4144
4234
  prescanAndWriteClasses();
4145
4235
  }
4146
4236
  state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4237
+ runAutoThemeScan(root);
4147
4238
  });
4148
4239
  if (options.build?.scanCss) {
4149
4240
  compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
@@ -4170,6 +4261,7 @@ ${transformedCode}`;
4170
4261
  evictTransformCacheOnce();
4171
4262
  prescanAndWriteClasses();
4172
4263
  state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4264
+ runAutoThemeScan(root);
4173
4265
  },
4174
4266
  /**
4175
4267
  * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
@@ -4179,17 +4271,23 @@ ${transformedCode}`;
4179
4271
  handleHotUpdate(ctx) {
4180
4272
  prescanResultHandoff.clear();
4181
4273
  const scanCss = options.build?.scanCss;
4274
+ const reloadThemeGroupsModule = () => {
4275
+ const themeGroupsModule = ctx.server.moduleGraph.getModuleById(
4276
+ RESOLVED_THEME_GROUPS_VIRTUAL_ID
4277
+ );
4278
+ if (themeGroupsModule) {
4279
+ ctx.server.moduleGraph.invalidateModule(themeGroupsModule);
4280
+ }
4281
+ };
4182
4282
  if (scanCss) {
4183
4283
  const root = ctx.server.config.root || process.cwd();
4184
4284
  if (matchesAnyPattern(ctx.file, scanCss, root)) {
4185
4285
  state.parsedTheme = runThemeScan(root, scanCss) ?? state.parsedTheme;
4186
- const themeGroupsModule = ctx.server.moduleGraph.getModuleById(
4187
- RESOLVED_THEME_GROUPS_VIRTUAL_ID
4188
- );
4189
- if (themeGroupsModule) {
4190
- ctx.server.moduleGraph.invalidateModule(themeGroupsModule);
4191
- }
4286
+ reloadThemeGroupsModule();
4192
4287
  }
4288
+ } else if (ctx.file.endsWith(".css")) {
4289
+ runAutoThemeScan(ctx.server.config.root || process.cwd());
4290
+ reloadThemeGroupsModule();
4193
4291
  }
4194
4292
  if (!shouldProcessSource(ctx.file)) {
4195
4293
  return;
@@ -4309,9 +4407,6 @@ ${transformedCode}`;
4309
4407
  }
4310
4408
  return manifest;
4311
4409
  }
4312
- function isCssSyntaxError(error) {
4313
- return !!error && typeof error === "object" && "name" in error && error.name === "CssSyntaxError";
4314
- }
4315
4410
  function rewriteOutputCss(source, file, shouldMangle, mangledSources, externalClasses) {
4316
4411
  let css = rewriteCssWithValidatedGlobalVarPlan(
4317
4412
  source,
@@ -13,10 +13,10 @@ import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
13
13
  import { isTailwindReservedCustomProperty, CSSZYX_GLOBAL_ALIAS_PREFIX, isCsszyxGlobalAliasCustomProperty, DEFAULT_BUILD_CONFIG, validateGlobalVarMangleConfig } from '@csszyx/types';
14
14
  import { preprocess } from '@csszyx/vue-adapter';
15
15
  import { createUnplugin } from 'unplugin';
16
+ import { n as normalizePathSeparators, r as resolveTransformCacheDir, c as createTransformCacheKey, w as writeTransformCache, e as evictOldTransformCacheEntries, a as readTransformCache, d as evictMemoryCacheToBudget, b as babelFallbackReason } from './unplugin.fOnGXWgS.mjs';
16
17
  import { mangleCSSSync } from '../css-mangler.mjs';
17
- import { i as importsRuntimeHelper, f as findRuntimeImportClause, a as insertAfterUseDirective } from './unplugin.nnsa7OVW.mjs';
18
- import { n as normalizePathSeparators, r as resolveTransformCacheDir, c as createTransformCacheKey, w as writeTransformCache, e as evictOldTransformCacheEntries, a as readTransformCache, b as evictMemoryCacheToBudget } from './unplugin.D-K57LNR.mjs';
19
- import { s as sortStrings, a as replaceEveryLiteral, u as unicodeEscape, b as escapeDoubleQuotedString, c as escapeSingleQuotedString, e as escapeHtmlAttribute, r as renderTailwindScannerCandidates } from './unplugin.TtmP77BS.mjs';
18
+ import { i as importsRuntimeHelper, f as findRuntimeImportClause, a as insertAfterUseDirective } from './unplugin.Lhzkcj-A.mjs';
19
+ import { s as sortStrings, a as replaceEveryLiteral, u as unicodeEscape, b as escapeDoubleQuotedString, c as escapeSingleQuotedString, e as escapeHtmlAttribute, r as renderTailwindScannerCandidates } from './unplugin.B3XoSRB8.mjs';
20
20
  import postcss from 'postcss';
21
21
  import valueParser from 'postcss-value-parser';
22
22
 
@@ -436,8 +436,8 @@ function scanCssSourceWithOptionalCache(file, cacheDir) {
436
436
  return result;
437
437
  }
438
438
  function normalizeBuildAssetPath(rootDir, fileName) {
439
- return (path.isAbsolute(fileName) ? fileName : path.join(rootDir, fileName)).replace(
440
- /\\/g,
439
+ return (path.isAbsolute(fileName) ? fileName : path.join(rootDir, fileName)).replaceAll(
440
+ "\\",
441
441
  "/"
442
442
  );
443
443
  }
@@ -1160,15 +1160,44 @@ function stripLayerWrappers(css) {
1160
1160
  }
1161
1161
  return result;
1162
1162
  }
1163
+ function themePreludeStart(css, at) {
1164
+ const cursor = at + "@theme".length;
1165
+ const next = css[cursor];
1166
+ const validBoundary = next === "{" || next === " " || next === " " || next === "\n" || next === "\r";
1167
+ return validBoundary ? cursor : null;
1168
+ }
1169
+ function readThemeBlock(css, start) {
1170
+ for (let cursor = start; cursor < css.length; cursor++) {
1171
+ const character = css[cursor];
1172
+ if (character === "{") {
1173
+ const close = findMatchingBrace(css, cursor);
1174
+ return {
1175
+ body: close === -1 ? null : css.slice(cursor + 1, close),
1176
+ end: close === -1 ? cursor : close
1177
+ };
1178
+ }
1179
+ if (character === ";" || character === "}" || character === "@") {
1180
+ return { body: null, end: cursor };
1181
+ }
1182
+ }
1183
+ return { body: null, end: css.length };
1184
+ }
1163
1185
  function extractThemeBlocks(css) {
1164
1186
  const blocks = [];
1165
- const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
1166
- for (const match of css.matchAll(themeStart)) {
1167
- const openPos = css.indexOf("{", match.index);
1168
- const closePos = findMatchingBrace(css, openPos);
1169
- if (closePos !== -1) {
1170
- blocks.push(css.slice(openPos + 1, closePos));
1187
+ let searchFrom = 0;
1188
+ for (; ; ) {
1189
+ const at = css.indexOf("@theme", searchFrom);
1190
+ if (at === -1) {
1191
+ break;
1192
+ }
1193
+ const preludeStart = themePreludeStart(css, at);
1194
+ if (preludeStart === null) {
1195
+ searchFrom = at + "@theme".length;
1196
+ continue;
1171
1197
  }
1198
+ const match = readThemeBlock(css, preludeStart);
1199
+ if (match.body !== null) blocks.push(match.body);
1200
+ searchFrom = Math.max(match.end, at + "@theme".length);
1172
1201
  }
1173
1202
  return blocks;
1174
1203
  }
@@ -1338,6 +1367,26 @@ const SIMPLE_ESCAPES = {
1338
1367
  v: "\v",
1339
1368
  "0": "\0"
1340
1369
  };
1370
+ function decodeBracedUnicodeEscape(value, cursor) {
1371
+ if (value[cursor] !== "u" || value[cursor + 1] !== "{") return null;
1372
+ const close = value.indexOf("}", cursor + 2);
1373
+ const hex = close === -1 ? "" : value.slice(cursor + 2, close);
1374
+ const codePoint = hex && /^[\dA-F]+$/i.test(hex) ? Number.parseInt(hex, 16) : -1;
1375
+ if (codePoint < 0 || codePoint > 1114111) return null;
1376
+ return { value: String.fromCodePoint(codePoint), cursor: close };
1377
+ }
1378
+ function decodeFixedWidthEscape(value, cursor) {
1379
+ const escaped = value[cursor];
1380
+ let width = 0;
1381
+ if (escaped === "x") width = 2;
1382
+ if (escaped === "u") width = 4;
1383
+ const hex = value.slice(cursor + 1, cursor + width + 1);
1384
+ if (width === 0 || !isFixedHex(hex, width)) return null;
1385
+ return {
1386
+ value: String.fromCodePoint(Number.parseInt(hex, 16)),
1387
+ cursor: cursor + width
1388
+ };
1389
+ }
1341
1390
  function isFixedHex(value, width) {
1342
1391
  if (value.length !== width) return false;
1343
1392
  for (const character of value) {
@@ -1353,24 +1402,10 @@ function decodeEscape(value, cursor) {
1353
1402
  if (escaped === "\r" && value[cursor + 1] === "\n") {
1354
1403
  return { value: "", cursor: cursor + 1 };
1355
1404
  }
1356
- if (escaped === "u" && value[cursor + 1] === "{") {
1357
- const close = value.indexOf("}", cursor + 2);
1358
- const hex2 = close === -1 ? "" : value.slice(cursor + 2, close);
1359
- const codePoint = hex2 && /^[\dA-F]+$/i.test(hex2) ? Number.parseInt(hex2, 16) : -1;
1360
- if (codePoint >= 0 && codePoint <= 1114111) {
1361
- return { value: String.fromCodePoint(codePoint), cursor: close };
1362
- }
1363
- }
1364
- let width = 0;
1365
- if (escaped === "x") width = 2;
1366
- else if (escaped === "u") width = 4;
1367
- const hex = value.slice(cursor + 1, cursor + width + 1);
1368
- if (width > 0 && isFixedHex(hex, width)) {
1369
- return {
1370
- value: String.fromCodePoint(Number.parseInt(hex, 16)),
1371
- cursor: cursor + width
1372
- };
1373
- }
1405
+ const bracedUnicode = decodeBracedUnicodeEscape(value, cursor);
1406
+ if (bracedUnicode) return bracedUnicode;
1407
+ const fixedWidth = decodeFixedWidthEscape(value, cursor);
1408
+ if (fixedWidth) return fixedWidth;
1374
1409
  const decoded = escaped === "\n" || escaped === "\r" ? "" : escaped;
1375
1410
  return { value: decoded, cursor };
1376
1411
  }
@@ -2541,6 +2576,9 @@ function rewriteCssWithValidatedGlobalVarPlan(css, filePath, result) {
2541
2576
  });
2542
2577
  return rewrite.css;
2543
2578
  }
2579
+ function isCssSyntaxError(error) {
2580
+ return !!error && typeof error === "object" && "name" in error && error.name === "CssSyntaxError";
2581
+ }
2544
2582
  function assertGlobalVarPlanMatchesEarlyAliases(result, expectedEntries) {
2545
2583
  const actualEntries = [...result.plan.aliases.entries()].sort(
2546
2584
  ([left], [right]) => left.localeCompare(right)
@@ -2830,6 +2868,14 @@ function scanClassExpression(source, from) {
2830
2868
  }
2831
2869
  return index;
2832
2870
  }
2871
+ function findTemplateInterpolationEnd(templateContent, bodyStart) {
2872
+ let depth = 0;
2873
+ for (let cursor = bodyStart; cursor < templateContent.length; cursor++) {
2874
+ if (templateContent[cursor] === "{") depth++;
2875
+ else if (templateContent[cursor] === "}" && depth-- === 0) return cursor + 1;
2876
+ }
2877
+ return templateContent.length;
2878
+ }
2833
2879
  function mangleCodeClassesSync(code, mangleMap) {
2834
2880
  function mangleClassString(classString) {
2835
2881
  return classString.split(/\s+/).filter(Boolean).map((cls) => {
@@ -2868,14 +2914,6 @@ function mangleCodeClassesSync(code, mangleMap) {
2868
2914
  changed: mangled !== trimmed
2869
2915
  };
2870
2916
  }
2871
- function findTemplateInterpolationEnd(templateContent, bodyStart) {
2872
- let depth = 0;
2873
- for (let cursor = bodyStart; cursor < templateContent.length; cursor++) {
2874
- if (templateContent[cursor] === "{") depth++;
2875
- else if (templateContent[cursor] === "}" && depth-- === 0) return cursor + 1;
2876
- }
2877
- return templateContent.length;
2878
- }
2879
2917
  function mangleTemplateInterpolation(inner) {
2880
2918
  let changed = false;
2881
2919
  const value = inner.replace(/"([^"]*)"/g, (quoted, classString) => {
@@ -3125,6 +3163,7 @@ function createCsszyxPlugins(options = {}) {
3125
3163
  const state = {
3126
3164
  classes: /* @__PURE__ */ new Set(),
3127
3165
  parsedTheme: null,
3166
+ autoThemeCssFiles: [],
3128
3167
  sawTailwindEntry: false,
3129
3168
  sawAnyCss: false,
3130
3169
  tailwindWarningEmitted: false,
@@ -3225,6 +3264,59 @@ function createCsszyxPlugins(options = {}) {
3225
3264
  );
3226
3265
  }
3227
3266
  }
3267
+ function runAutoThemeScan(rootDir) {
3268
+ if (options.build?.scanCss) {
3269
+ return;
3270
+ }
3271
+ refreshCompileSourceDirs();
3272
+ const cssFiles = [];
3273
+ const walk = (dir) => {
3274
+ let entries;
3275
+ try {
3276
+ entries = fs.readdirSync(dir, { withFileTypes: true });
3277
+ } catch {
3278
+ return;
3279
+ }
3280
+ for (const entry of entries) {
3281
+ if (entry.isDirectory()) {
3282
+ if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
3283
+ walk(path.join(dir, entry.name));
3284
+ }
3285
+ continue;
3286
+ }
3287
+ if (entry.name.endsWith(".css")) {
3288
+ cssFiles.push(path.join(dir, entry.name));
3289
+ }
3290
+ }
3291
+ };
3292
+ walk(rootDir);
3293
+ const normRoot = normalizeForMatch(rootDir);
3294
+ for (const sourceDir of compileSourceDirs) {
3295
+ if (sourceDir === normRoot || sourceDir.startsWith(`${normRoot}/`)) {
3296
+ continue;
3297
+ }
3298
+ walk(sourceDir);
3299
+ }
3300
+ const themes = [];
3301
+ const themeFiles = [];
3302
+ for (const file of cssFiles) {
3303
+ let content;
3304
+ try {
3305
+ content = fs.readFileSync(file, "utf-8");
3306
+ } catch {
3307
+ continue;
3308
+ }
3309
+ if (!content.includes("@theme")) {
3310
+ continue;
3311
+ }
3312
+ themes.push(parseThemeBlocks(content));
3313
+ themeFiles.push(file);
3314
+ }
3315
+ state.autoThemeCssFiles = themeFiles;
3316
+ if (themes.length > 0) {
3317
+ state.parsedTheme = mergeThemes(themes);
3318
+ }
3319
+ }
3228
3320
  function isHardIgnored(id) {
3229
3321
  refreshCompileSourceDirs();
3230
3322
  return isHardIgnoredPath(id, compileSourceDirs);
@@ -3313,7 +3405,7 @@ function createCsszyxPlugins(options = {}) {
3313
3405
  }
3314
3406
  function runBabelFallback(source, effectiveFilename, compilerOptions, error) {
3315
3407
  const result = transformSourceCode(source, effectiveFilename, compilerOptions);
3316
- const reason = error instanceof Error ? error.message : String(error);
3408
+ const reason = babelFallbackReason(error);
3317
3409
  result.diagnostics.push(
3318
3410
  `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
3319
3411
  );
@@ -4070,9 +4162,7 @@ ${transformedCode}`;
4070
4162
  emitWarning(missingTailwindEntryMessage(state.ownedClasses.size));
4071
4163
  }
4072
4164
  if (!state.contentScopeWarningEmitted && options.contentScopeCheck !== false && state.sawTailwindEntry && !state.tailwindEntryScoped) {
4073
- if (state.inMonorepo === void 0) {
4074
- state.inMonorepo = isMonorepoPackage(state.rootDir);
4075
- }
4165
+ state.inMonorepo ??= isMonorepoPackage(state.rootDir);
4076
4166
  if (shouldWarnUnscopedMonorepo(
4077
4167
  state.sawTailwindEntry,
4078
4168
  state.tailwindEntryScoped,
@@ -4124,6 +4214,7 @@ ${transformedCode}`;
4124
4214
  prescanAndWriteClasses();
4125
4215
  }
4126
4216
  state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4217
+ runAutoThemeScan(root);
4127
4218
  });
4128
4219
  if (options.build?.scanCss) {
4129
4220
  compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
@@ -4150,6 +4241,7 @@ ${transformedCode}`;
4150
4241
  evictTransformCacheOnce();
4151
4242
  prescanAndWriteClasses();
4152
4243
  state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4244
+ runAutoThemeScan(root);
4153
4245
  },
4154
4246
  /**
4155
4247
  * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
@@ -4159,17 +4251,23 @@ ${transformedCode}`;
4159
4251
  handleHotUpdate(ctx) {
4160
4252
  prescanResultHandoff.clear();
4161
4253
  const scanCss = options.build?.scanCss;
4254
+ const reloadThemeGroupsModule = () => {
4255
+ const themeGroupsModule = ctx.server.moduleGraph.getModuleById(
4256
+ RESOLVED_THEME_GROUPS_VIRTUAL_ID
4257
+ );
4258
+ if (themeGroupsModule) {
4259
+ ctx.server.moduleGraph.invalidateModule(themeGroupsModule);
4260
+ }
4261
+ };
4162
4262
  if (scanCss) {
4163
4263
  const root = ctx.server.config.root || process.cwd();
4164
4264
  if (matchesAnyPattern(ctx.file, scanCss, root)) {
4165
4265
  state.parsedTheme = runThemeScan(root, scanCss) ?? state.parsedTheme;
4166
- const themeGroupsModule = ctx.server.moduleGraph.getModuleById(
4167
- RESOLVED_THEME_GROUPS_VIRTUAL_ID
4168
- );
4169
- if (themeGroupsModule) {
4170
- ctx.server.moduleGraph.invalidateModule(themeGroupsModule);
4171
- }
4266
+ reloadThemeGroupsModule();
4172
4267
  }
4268
+ } else if (ctx.file.endsWith(".css")) {
4269
+ runAutoThemeScan(ctx.server.config.root || process.cwd());
4270
+ reloadThemeGroupsModule();
4173
4271
  }
4174
4272
  if (!shouldProcessSource(ctx.file)) {
4175
4273
  return;
@@ -4289,9 +4387,6 @@ ${transformedCode}`;
4289
4387
  }
4290
4388
  return manifest;
4291
4389
  }
4292
- function isCssSyntaxError(error) {
4293
- return !!error && typeof error === "object" && "name" in error && error.name === "CssSyntaxError";
4294
- }
4295
4390
  function rewriteOutputCss(source, file, shouldMangle, mangledSources, externalClasses) {
4296
4391
  let css = rewriteCssWithValidatedGlobalVarPlan(
4297
4392
  source,
@@ -12,4 +12,4 @@ interface NextSourceTransformOutput {
12
12
  producer: TransformCacheProducer;
13
13
  }
14
14
 
15
- export type { NextSourceParserMode as N, NextSourceTransformOutput as a };
15
+ export type { NextSourceTransformOutput as N, NextSourceParserMode as a };
@@ -12,4 +12,4 @@ interface NextSourceTransformOutput {
12
12
  producer: TransformCacheProducer;
13
13
  }
14
14
 
15
- export type { NextSourceParserMode as N, NextSourceTransformOutput as a };
15
+ export type { NextSourceTransformOutput as N, NextSourceParserMode as a };
@@ -334,7 +334,7 @@ declare function findLocalImportSources(code: string): string[];
334
334
  *
335
335
  * Supports:
336
336
  * - Multiple @theme blocks per file
337
- * - @theme inline { } syntax (inline keyword ignored)
337
+ * - @theme option keywords (inline, static, reference, combinations ignored)
338
338
  * - @theme inside @layer (two-pass strip)
339
339
  * - --color-brand-50 shade suffixes (deduped to 'brand')
340
340
  * - Multi-file merge via mergeThemes()
@@ -407,6 +407,12 @@ interface PluginState {
407
407
  classes: Set<string>;
408
408
  /** Merged @theme scan result — feeds the theme-groups virtual module. */
409
409
  parsedTheme: ParsedTheme | null;
410
+ /**
411
+ * CSS files the zero-config @theme auto-scan found tokens in (only when
412
+ * `build.scanCss` is unset). Dev HMR re-scans when one of these — or any
413
+ * other .css file — changes, mirroring the explicit scanCss reload path.
414
+ */
415
+ autoThemeCssFiles: string[];
410
416
  /**
411
417
  * True once any processed CSS file was seen importing `tailwindcss`. Used to
412
418
  * warn at build end when csszyx generated classes but nothing makes Tailwind