rainbowindex 0.5.0 → 0.5.1

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.
@@ -12,8 +12,6 @@ import {
12
12
  DEFAULT_EASING,
13
13
  DEFAULT_FLUID,
14
14
  DEFAULT_LEADING,
15
- DEFAULT_ROUNDED,
16
- DEFAULT_ROUNDED_ROOF,
17
15
  DEFAULT_SHADOWS,
18
16
  DEFAULT_SUPERELLIPSE_SCALE,
19
17
  DEFAULT_TEXT,
@@ -42,7 +40,7 @@ import {
42
40
  registerCustomTextSizes,
43
41
  registerCustomUtility,
44
42
  snapshotCompilationContext
45
- } from "./chunk-KRZL4IDK.mjs";
43
+ } from "./chunk-4UKFK2GE.mjs";
46
44
 
47
45
  // src/shared.ts
48
46
  function isRIDebug() {
@@ -1743,8 +1741,6 @@ function resolveDirectives(directives, attribution) {
1743
1741
  text: { ...DEFAULT_TEXT },
1744
1742
  spacing: { base: "0.25rem" },
1745
1743
  breakpoints: { ...DEFAULT_BREAKPOINTS },
1746
- rounded: { ...DEFAULT_ROUNDED },
1747
- roundedRoof: DEFAULT_ROUNDED_ROOF,
1748
1744
  roundedShape: null,
1749
1745
  roundedShapeScale: 1,
1750
1746
  shadows: { ...DEFAULT_SHADOWS },
@@ -1866,20 +1862,15 @@ function resolveDirectives(directives, attribution) {
1866
1862
  theme.roundedShapeScale = defaultScaleForShape(shape);
1867
1863
  }
1868
1864
  if (directive.body) {
1869
- const { entries, removals } = parseKeyValueBody(
1870
- directive.body,
1871
- theme.warnings,
1872
- "rounded"
1873
- );
1874
- let roofValue;
1865
+ const { entries } = parseKeyValueBody(directive.body, theme.warnings, "rounded");
1875
1866
  let scaleValue;
1876
- const overrides = {};
1877
1867
  for (const [k, v] of entries) {
1878
- if (k === "--roof") roofValue = v;
1879
- else if (k === "--corner-scale") scaleValue = v;
1880
- else overrides[k] = v;
1868
+ if (k === "--corner-scale") scaleValue = v;
1869
+ else
1870
+ theme.warnings.push(
1871
+ `[RI-1122] Unknown @rounded option "${k}" \u2014 supported: --corner-scale. Radii are numeric: rounded-{n} is n * --spacing.`
1872
+ );
1881
1873
  }
1882
- if (roofValue !== void 0) theme.roundedRoof = roofValue;
1883
1874
  if (scaleValue !== void 0) {
1884
1875
  const n = Number.parseFloat(scaleValue);
1885
1876
  if (!Number.isNaN(n) && n > 0) {
@@ -1890,13 +1881,6 @@ function resolveDirectives(directives, attribution) {
1890
1881
  );
1891
1882
  }
1892
1883
  }
1893
- theme.rounded = mergeWithRemovals(
1894
- theme.rounded,
1895
- overrides,
1896
- removals,
1897
- theme.warnings,
1898
- "rounded"
1899
- );
1900
1884
  }
1901
1885
  break;
1902
1886
  }
@@ -3101,21 +3085,80 @@ function findClosest(input, candidates, maxDistance) {
3101
3085
  return best;
3102
3086
  }
3103
3087
 
3088
+ // src/utilities/helpers.ts
3089
+ var INTEGER_RE = /^\d+$/;
3090
+ var DECIMAL_RE = /^\d+(?:[._]\d+)?$/;
3091
+ function single(property, value) {
3092
+ return { declarations: [{ property, value }] };
3093
+ }
3094
+ function multi(...pairs) {
3095
+ return {
3096
+ declarations: pairs.map(([property, value]) => ({ property, value }))
3097
+ };
3098
+ }
3099
+ function extractArbitrary(value) {
3100
+ if (!value) return null;
3101
+ return value.startsWith("[") && value.endsWith("]") ? decodeArbitraryValue(value.slice(1, -1)) : null;
3102
+ }
3103
+ function normalizeDecimalToken(value) {
3104
+ return value.replaceAll("_", ".");
3105
+ }
3106
+ function spacingLookup(value, negative = false) {
3107
+ if (value === "px") return negative ? "-1px" : "1px";
3108
+ if (!DECIMAL_RE.test(value)) return null;
3109
+ const normalized = normalizeDecimalToken(value);
3110
+ const num = Number(normalized);
3111
+ if (!Number.isFinite(num) || num < 0) return null;
3112
+ if (num === 0) return "0px";
3113
+ const expr = `calc(${normalized} * var(--spacing))`;
3114
+ return negative ? `calc(${normalized} * var(--spacing) * -1)` : expr;
3115
+ }
3116
+ function deepFreezeUtilityMap(map) {
3117
+ for (const value of Object.values(map)) {
3118
+ Object.freeze(value.declarations);
3119
+ Object.freeze(value);
3120
+ }
3121
+ return Object.freeze(map);
3122
+ }
3123
+
3104
3124
  // src/utilities/custom.ts
3105
- var customUtilityMapCache = /* @__PURE__ */ new WeakMap();
3106
- function getCustomUtility(theme, name) {
3107
- return getCustomUtilityMap(theme).get(name);
3108
- }
3109
- function getCustomUtilityMap(theme) {
3110
- let map = customUtilityMapCache.get(theme);
3111
- if (!map) {
3112
- map = /* @__PURE__ */ new Map();
3125
+ var customUtilityIndexCache = /* @__PURE__ */ new WeakMap();
3126
+ function getCustomUtilityIndex(theme) {
3127
+ let index = customUtilityIndexCache.get(theme);
3128
+ if (!index) {
3129
+ const statics = /* @__PURE__ */ new Map();
3130
+ const functional = /* @__PURE__ */ new Map();
3113
3131
  for (const cu of theme.customUtilities) {
3114
- map.set(cu.name, cu);
3132
+ (cu.functional ? functional : statics).set(cu.name, cu);
3115
3133
  }
3116
- customUtilityMapCache.set(theme, map);
3134
+ index = {
3135
+ statics,
3136
+ functional: [...functional.values()].sort((a, b) => b.name.length - a.name.length)
3137
+ };
3138
+ customUtilityIndexCache.set(theme, index);
3117
3139
  }
3118
- return map;
3140
+ return index;
3141
+ }
3142
+ var VALUE_PLACEHOLDER = "var(--value)";
3143
+ var UNSAFE_SUFFIX_RE = /[;{}]/;
3144
+ function functionalValue(suffix) {
3145
+ const arbitrary = extractArbitrary(suffix);
3146
+ if (arbitrary !== null) return arbitrary;
3147
+ return UNSAFE_SUFFIX_RE.test(suffix) ? null : suffix;
3148
+ }
3149
+ function matchCustomUtility(utility, value, negative, theme) {
3150
+ const index = getCustomUtilityIndex(theme);
3151
+ const full = value === null ? utility : `${utility}-${value}`;
3152
+ const exact = index.statics.get(full);
3153
+ if (exact) return { cu: exact, substitution: null };
3154
+ if (negative || value === null) return null;
3155
+ for (const cu of index.functional) {
3156
+ if (full.length <= cu.name.length + 1) continue;
3157
+ if (!full.startsWith(cu.name) || full.charCodeAt(cu.name.length) !== 45) continue;
3158
+ const substitution = functionalValue(full.slice(cu.name.length + 1));
3159
+ return substitution === null ? null : { cu, substitution };
3160
+ }
3161
+ return null;
3119
3162
  }
3120
3163
  var SELECTOR_WS_COLLAPSE_RE = /\s+/g;
3121
3164
  function parseCustomUtilityBody(body) {
@@ -3247,10 +3290,10 @@ function buildNestedBlock(node, theme, expansion, resolve) {
3247
3290
  if (declarations.length === 0 && nested.length === 0) return null;
3248
3291
  return { selector: node.selector, declarations, nested };
3249
3292
  }
3250
- function resolveCustomUtility(utility, value, theme, resolve, visiting) {
3251
- const map = getCustomUtilityMap(theme);
3252
- const cu = value === null ? map.get(utility) : map.get(`${utility}-${value}`);
3253
- if (!cu || cu.functional) return null;
3293
+ function resolveCustomUtility(utility, value, negative, theme, resolve, visiting) {
3294
+ const match = matchCustomUtility(utility, value, negative, theme);
3295
+ if (!match) return null;
3296
+ const { cu, substitution } = match;
3254
3297
  const tree = getParsedBody(cu.body);
3255
3298
  let expansion = null;
3256
3299
  if (hasApplyLikeDirective(cu.body)) {
@@ -3270,10 +3313,23 @@ function resolveCustomUtility(utility, value, theme, resolve, visiting) {
3270
3313
  if (block) nested.push(block);
3271
3314
  }
3272
3315
  if (expansion) expansion.delete(cu.name);
3316
+ if (substitution !== null) substituteValue(declarations, nested, substitution);
3273
3317
  if (declarations.length === 0 && nested.length === 0) return null;
3274
3318
  if (nested.length === 0) return { declarations };
3275
3319
  return { declarations, nested };
3276
3320
  }
3321
+ function substituteValue(declarations, nested, value) {
3322
+ for (let i = 0; i < declarations.length; i++) {
3323
+ const d = declarations[i];
3324
+ if (d.value.includes(VALUE_PLACEHOLDER)) {
3325
+ declarations[i] = {
3326
+ property: d.property,
3327
+ value: d.value.replaceAll(VALUE_PLACEHOLDER, value)
3328
+ };
3329
+ }
3330
+ }
3331
+ for (const block of nested) substituteValue(block.declarations, block.nested, value);
3332
+ }
3277
3333
  function extractCustomUtilityRootInfo(body) {
3278
3334
  const tree = getParsedBody(body);
3279
3335
  const seen = /* @__PURE__ */ new Set();
@@ -3287,42 +3343,6 @@ function extractCustomUtilityRootInfo(body) {
3287
3343
  return { properties, applyClasses: [...tree.applyClasses] };
3288
3344
  }
3289
3345
 
3290
- // src/utilities/helpers.ts
3291
- var INTEGER_RE = /^\d+$/;
3292
- var DECIMAL_RE = /^\d+(?:[._]\d+)?$/;
3293
- function single(property, value) {
3294
- return { declarations: [{ property, value }] };
3295
- }
3296
- function multi(...pairs) {
3297
- return {
3298
- declarations: pairs.map(([property, value]) => ({ property, value }))
3299
- };
3300
- }
3301
- function extractArbitrary(value) {
3302
- if (!value) return null;
3303
- return value.startsWith("[") && value.endsWith("]") ? decodeArbitraryValue(value.slice(1, -1)) : null;
3304
- }
3305
- function normalizeDecimalToken(value) {
3306
- return value.replaceAll("_", ".");
3307
- }
3308
- function spacingLookup(value, negative = false) {
3309
- if (value === "px") return negative ? "-1px" : "1px";
3310
- if (!DECIMAL_RE.test(value)) return null;
3311
- const normalized = normalizeDecimalToken(value);
3312
- const num = Number(normalized);
3313
- if (!Number.isFinite(num) || num < 0) return null;
3314
- if (num === 0) return "0px";
3315
- const expr = `calc(${normalized} * var(--spacing))`;
3316
- return negative ? `calc(${normalized} * var(--spacing) * -1)` : expr;
3317
- }
3318
- function deepFreezeUtilityMap(map) {
3319
- for (const value of Object.values(map)) {
3320
- Object.freeze(value.declarations);
3321
- Object.freeze(value);
3322
- }
3323
- return Object.freeze(map);
3324
- }
3325
-
3326
3346
  // src/utilities/spacing.ts
3327
3347
  function resolveSpacing(val, negative, warnings, allowAuto = true) {
3328
3348
  if (val === null) return null;
@@ -5243,13 +5263,9 @@ var ROUNDED_CORNER = Object.freeze({
5243
5263
  var ROUNDED_CORNER_ENTRIES = Object.entries(ROUNDED_CORNER);
5244
5264
  var ROUNDED_SIDE_NAMES = Object.freeze(Object.keys(ROUNDED_SIDE));
5245
5265
  var ROUNDED_CORNER_NAMES = Object.freeze(Object.keys(ROUNDED_CORNER));
5246
- function resolveRadius(name, theme) {
5266
+ function resolveRadius(name) {
5247
5267
  if (name === "none") return "0px";
5248
5268
  if (name === "full") return "calc(infinity * 1px)";
5249
- if (Object.hasOwn(theme.rounded, name)) {
5250
- const base = `var(--rounded-${name})`;
5251
- return `calc(${base} * var(--ri-rounded-scale, 1))`;
5252
- }
5253
5269
  if (DECIMAL_RE.test(name)) {
5254
5270
  const n = Number.parseFloat(normalizeDecimalToken(name));
5255
5271
  if (n === 0) return "0px";
@@ -5296,7 +5312,7 @@ function resolveDivide(full, axis) {
5296
5312
  var BORDER_DIR_PROPS_ENTRIES = Object.entries(
5297
5313
  BORDER_DIR_PROPS
5298
5314
  ).map(([prefix, props]) => [`${prefix}-`, props[0]]);
5299
- function borderGenerator(_utility, _value, full, negative, theme, _warnings, dataType) {
5315
+ function borderGenerator(_utility, _value, full, negative, _theme, _warnings, dataType) {
5300
5316
  if (dataType === "color") return null;
5301
5317
  if (Object.hasOwn(STATIC_BORDER, full)) return STATIC_BORDER[full];
5302
5318
  if (full.startsWith("corner-")) {
@@ -5305,31 +5321,19 @@ function borderGenerator(_utility, _value, full, negative, theme, _warnings, dat
5305
5321
  return multi(["corner-shape", arb], ["--ri-rounded-scale", "1"]);
5306
5322
  }
5307
5323
  }
5308
- if (full === "rounded") {
5309
- const r = resolveRadius("sm", theme) ?? "0.25rem";
5310
- return single("border-radius", r);
5311
- }
5312
5324
  if (full.startsWith("rounded-")) {
5313
5325
  const rest = full.slice(8);
5314
5326
  for (const [suffix, prop] of ROUNDED_CORNER_ENTRIES) {
5315
- if (rest === suffix) {
5316
- const r2 = resolveRadius("sm", theme);
5317
- if (r2) return single(prop, r2);
5318
- }
5319
5327
  if (rest.startsWith(suffix) && rest.charCodeAt(suffix.length) === 45) {
5320
5328
  const size = rest.slice(suffix.length + 1);
5321
- const r2 = resolveRadius(size, theme);
5329
+ const r2 = resolveRadius(size);
5322
5330
  if (r2) return single(prop, r2);
5323
5331
  }
5324
5332
  }
5325
5333
  for (const [suffix, props] of ROUNDED_SIDE_ENTRIES) {
5326
- if (rest === suffix) {
5327
- const r2 = resolveRadius("sm", theme);
5328
- if (r2) return multi(...props.map((p) => [p, r2]));
5329
- }
5330
5334
  if (rest.startsWith(suffix) && rest.charCodeAt(suffix.length) === 45) {
5331
5335
  const size = rest.slice(suffix.length + 1);
5332
- const r2 = resolveRadius(size, theme);
5336
+ const r2 = resolveRadius(size);
5333
5337
  if (r2) return multi(...props.map((p) => [p, r2]));
5334
5338
  }
5335
5339
  }
@@ -5343,7 +5347,7 @@ function borderGenerator(_utility, _value, full, negative, theme, _warnings, dat
5343
5347
  }
5344
5348
  return null;
5345
5349
  }
5346
- const r = resolveRadius(rest, theme);
5350
+ const r = resolveRadius(rest);
5347
5351
  if (r) return single("border-radius", r);
5348
5352
  }
5349
5353
  if (full.startsWith("border-")) {
@@ -7338,7 +7342,14 @@ function resolveUtility(utility, value, negative, theme, warnings, visiting, dat
7338
7342
  if (result) return result;
7339
7343
  }
7340
7344
  }
7341
- const customResult = resolveCustomUtility(utility, value, theme, resolveUtility, visiting);
7345
+ const customResult = resolveCustomUtility(
7346
+ utility,
7347
+ value,
7348
+ negative,
7349
+ theme,
7350
+ resolveUtility,
7351
+ visiting
7352
+ );
7342
7353
  if (customResult) return customResult;
7343
7354
  return null;
7344
7355
  }
@@ -8004,7 +8015,6 @@ var COLOR_STOP_REF_RE = /var\(--color-([a-z][a-z0-9]*(?:-[a-z][a-z0-9]*)*)(?:-(\
8004
8015
  var SHADOW_VAR_REF_RE = /var\(--shadow-([a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
8005
8016
  var TEXT_VAR_REF_RE = /var\(--text-([a-z0-9]+(?:-[a-z0-9]+)*?)(?:-leading)?\)/g;
8006
8017
  var FONT_VAR_REF_RE = /var\(--font-([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\)/g;
8007
- var ROUNDED_VAR_REF_RE = /var\(--rounded-([a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
8008
8018
  var ANIMATE_VAR_REF_RE = /var\(--animate-([a-z0-9]+(?:-[a-z0-9]+)*)\)/g;
8009
8019
 
8010
8020
  // src/engine/support-blocks.ts
@@ -8312,7 +8322,6 @@ var RE_COLOR_SINGLE = /^color-([\w-]+)$/;
8312
8322
  var RE_ANIMATE = /^animate-([\w-]+)$/;
8313
8323
  var RE_TEXT = /^text-([\w-]+)$/;
8314
8324
  var RE_TEXT_LEADING = /^text-([\w-]+)-leading$/;
8315
- var RE_ROUNDED = /^rounded-([\w-]+)$/;
8316
8325
  var RE_SHADOW = /^shadow-([\w-]+)$/;
8317
8326
  var RE_BREAKPOINT = /^breakpoint-([\w-]+)$/;
8318
8327
  var RE_FONT_SUB_VAR = /--(?:features|variations)$/;
@@ -8358,10 +8367,6 @@ function lookupThemeValue(varName, theme, inline = false) {
8358
8367
  if (getFontSlots(theme).has(baseSlot)) return inline ? null : `var(${varName})`;
8359
8368
  return null;
8360
8369
  }
8361
- if (name === "rounded-roof") return theme.roundedRoof;
8362
- const roundedMatch = name.startsWith("rounded-") ? name.match(RE_ROUNDED) : null;
8363
- if (roundedMatch && Object.hasOwn(theme.rounded, roundedMatch[1]))
8364
- return theme.rounded[roundedMatch[1]];
8365
8370
  const shadowMatch = name.startsWith("shadow-") ? name.match(RE_SHADOW) : null;
8366
8371
  if (shadowMatch && Object.hasOwn(theme.shadows, shadowMatch[1]))
8367
8372
  return theme.shadows[shadowMatch[1]];
@@ -8410,10 +8415,6 @@ function collectThemeVariableNames(theme) {
8410
8415
  names.push(`--text-${size}`);
8411
8416
  names.push(`--text-${size}-leading`);
8412
8417
  }
8413
- names.push("--rounded-roof");
8414
- for (const name of Object.keys(theme.rounded)) {
8415
- names.push(`--rounded-${name}`);
8416
- }
8417
8418
  for (const name of Object.keys(theme.shadows)) {
8418
8419
  names.push(`--shadow-${name}`);
8419
8420
  }
@@ -8574,11 +8575,6 @@ function scanStringForTokenUsage(str, result) {
8574
8575
  result.usedFonts.add(m[1]);
8575
8576
  }
8576
8577
  }
8577
- if (str.includes("var(--rounded-")) {
8578
- for (const m of str.matchAll(ROUNDED_VAR_REF_RE)) {
8579
- result.usedRounded.add(m[1]);
8580
- }
8581
- }
8582
8578
  if (str.includes("var(--shadow-")) {
8583
8579
  for (const m of str.matchAll(SHADOW_VAR_REF_RE)) {
8584
8580
  result.usedShadows.add(m[1]);
@@ -8630,13 +8626,13 @@ function createEmptyCompilationResult() {
8630
8626
  usedColorStops: /* @__PURE__ */ new Map(),
8631
8627
  usedTextSizes: /* @__PURE__ */ new Set(),
8632
8628
  usedFonts: /* @__PURE__ */ new Set(),
8633
- usedRounded: /* @__PURE__ */ new Set(),
8634
8629
  usedShadows: /* @__PURE__ */ new Set(),
8635
8630
  usedAnimations: /* @__PURE__ */ new Set(),
8636
8631
  warnings: []
8637
8632
  };
8638
8633
  }
8639
8634
  var classCompileMemo = /* @__PURE__ */ new WeakMap();
8635
+ var CLASS_COMPILE_MEMO_CAP = 5e4;
8640
8636
  function compileClassEntry(raw, theme, customVariantMap, breakpointWeights, variantMemo) {
8641
8637
  const scratch = createEmptyCompilationResult();
8642
8638
  const parsed = parseUtility(raw);
@@ -8650,6 +8646,7 @@ function compileClassEntry(raw, theme, customVariantMap, breakpointWeights, vari
8650
8646
  variantMemo
8651
8647
  );
8652
8648
  const support = new Array(SUPPORT_BLOCKS.length).fill(false);
8649
+ let arbitraryWarning = null;
8653
8650
  if (rule) {
8654
8651
  for (let i = 0; i < SUPPORT_BLOCKS.length; i++) {
8655
8652
  const block = SUPPORT_BLOCKS[i];
@@ -8661,18 +8658,16 @@ function compileClassEntry(raw, theme, customVariantMap, breakpointWeights, vari
8661
8658
  const bracketContent = parsed.value.replace(/^\[|\]$/g, "");
8662
8659
  if (shouldWarnUnresolvedArbitrary(bracketContent)) {
8663
8660
  const truncated = parsed.raw.length > 100 ? `${parsed.raw.slice(0, 100)}...` : parsed.raw;
8664
- scratch.warnings.push(
8665
- `[RI-1002] Could not resolve arbitrary utility "${truncated}". Arbitrary utilities use \`[property:value]\` syntax \u2014 e.g. \`[padding:1rem]\` or \`[mask-type:luminance]\`. Check that the property name is a known CSS property and the value is well-formed (no stray spaces, quoted strings escaped). If you meant to set a CSS variable, use \`[--my-var:value]\`.`
8666
- );
8661
+ arbitraryWarning = `[RI-1002] Could not resolve arbitrary utility "${truncated}". Arbitrary utilities use \`[property:value]\` syntax \u2014 e.g. \`[padding:1rem]\` or \`[mask-type:luminance]\`. Check that the property name is a known CSS property and the value is well-formed (no stray spaces, quoted strings escaped). If you meant to set a CSS variable, use \`[--my-var:value]\`.`;
8667
8662
  }
8668
8663
  }
8669
8664
  return {
8670
8665
  rule,
8671
8666
  warnings: scratch.warnings,
8667
+ arbitraryWarning,
8672
8668
  usedColorStops: scratch.usedColorStops,
8673
8669
  usedTextSizes: scratch.usedTextSizes,
8674
8670
  usedFonts: scratch.usedFonts,
8675
- usedRounded: scratch.usedRounded,
8676
8671
  usedShadows: scratch.usedShadows,
8677
8672
  usedAnimations: scratch.usedAnimations,
8678
8673
  support
@@ -8707,7 +8702,7 @@ function registerThemeOnContext(ctx, theme) {
8707
8702
  }
8708
8703
  }
8709
8704
  if (properties.length > 0) {
8710
- registerCustomUtility(ctx, cu.name, properties);
8705
+ registerCustomUtility(ctx, cu.name, properties, cu.functional);
8711
8706
  }
8712
8707
  }
8713
8708
  }
@@ -8716,7 +8711,7 @@ function createThemeSnapshot(theme) {
8716
8711
  registerThemeOnContext(ctx, theme);
8717
8712
  return snapshotCompilationContext(ctx);
8718
8713
  }
8719
- function compileInternal(classNames, theme, variantMapCache) {
8714
+ function compileInternal(classNames, theme, variantMapCache, authored) {
8720
8715
  const ctx = createCompilationContext();
8721
8716
  registerThemeOnContext(ctx, theme);
8722
8717
  const customVariantMap = theme.customVariants.length === 0 ? _emptyVariantMap : variantMapCache.get(theme) ?? (() => {
@@ -8740,12 +8735,16 @@ function compileInternal(classNames, theme, variantMapCache) {
8740
8735
  seen.add(raw);
8741
8736
  let entry = classMemo.get(raw);
8742
8737
  if (!entry) {
8738
+ if (classMemo.size >= CLASS_COMPILE_MEMO_CAP) classMemo.clear();
8743
8739
  entry = compileClassEntry(raw, theme, customVariantMap, breakpointWeights, variantMemo);
8744
8740
  classMemo.set(raw, entry);
8745
8741
  }
8746
8742
  if (entry.warnings.length > 0) {
8747
8743
  pushWarningsDeduped(result.warnings, entry.warnings, warnSeen);
8748
8744
  }
8745
+ if (entry.arbitraryWarning !== null && (authored === void 0 || authored.has(raw))) {
8746
+ pushWarningsDeduped(result.warnings, [entry.arbitraryWarning], warnSeen);
8747
+ }
8749
8748
  for (const [hue, stops] of entry.usedColorStops) {
8750
8749
  let set = result.usedColorStops.get(hue);
8751
8750
  if (!set) {
@@ -8756,7 +8755,6 @@ function compileInternal(classNames, theme, variantMapCache) {
8756
8755
  }
8757
8756
  for (const v of entry.usedTextSizes) result.usedTextSizes.add(v);
8758
8757
  for (const v of entry.usedFonts) result.usedFonts.add(v);
8759
- for (const v of entry.usedRounded) result.usedRounded.add(v);
8760
8758
  for (const v of entry.usedShadows) result.usedShadows.add(v);
8761
8759
  for (const v of entry.usedAnimations) result.usedAnimations.add(v);
8762
8760
  if (!entry.rule) continue;
@@ -8813,7 +8811,7 @@ function createCompiler() {
8813
8811
  const variantMapCache = /* @__PURE__ */ new WeakMap();
8814
8812
  return {
8815
8813
  fontOutputCache,
8816
- compile(classNames, theme) {
8814
+ compile(classNames, theme, authored) {
8817
8815
  const list = typeof classNames === "string" ? classNames.split(WS_RE3).filter(Boolean) : classNames;
8818
8816
  if (list == null || typeof list[Symbol.iterator] !== "function") {
8819
8817
  throw new TypeError(
@@ -8825,7 +8823,7 @@ function createCompiler() {
8825
8823
  `[RI-2007] compile() expected theme to be a ResolvedTheme object, got ${typeof theme}.`
8826
8824
  );
8827
8825
  }
8828
- const { result, ctx } = compileInternal(list, theme, variantMapCache);
8826
+ const { result, ctx } = compileInternal(list, theme, variantMapCache, authored);
8829
8827
  latestSnapshot = snapshotCompilationContext(ctx);
8830
8828
  fontOutputCache.clear();
8831
8829
  return result;
@@ -9213,7 +9211,8 @@ function readAssignedValue(source, start) {
9213
9211
  value: source.slice(i + 1, end2),
9214
9212
  end: end2,
9215
9213
  valueStart: i + 1,
9216
- quoted: true
9214
+ quoted: true,
9215
+ bare: false
9217
9216
  };
9218
9217
  }
9219
9218
  if (ch === "`") {
@@ -9222,7 +9221,8 @@ function readAssignedValue(source, start) {
9222
9221
  value: source.slice(i + 1, end2),
9223
9222
  end: end2,
9224
9223
  valueStart: i + 1,
9225
- quoted: false
9224
+ quoted: false,
9225
+ bare: false
9226
9226
  };
9227
9227
  }
9228
9228
  if (ch in BRACKET_PAIRS) {
@@ -9232,7 +9232,8 @@ function readAssignedValue(source, start) {
9232
9232
  value: source.slice(i + 1, end2),
9233
9233
  end: end2,
9234
9234
  valueStart: i + 1,
9235
- quoted: false
9235
+ quoted: false,
9236
+ bare: false
9236
9237
  };
9237
9238
  }
9238
9239
  let end = i;
@@ -9241,7 +9242,8 @@ function readAssignedValue(source, start) {
9241
9242
  value: source.slice(i, end),
9242
9243
  end: end - 1,
9243
9244
  valueStart: i,
9244
- quoted: false
9245
+ quoted: false,
9246
+ bare: true
9245
9247
  };
9246
9248
  }
9247
9249
  function splitTopLevelArgs(source) {
@@ -9324,12 +9326,22 @@ function collectTokensInLiteral(source, start, quoteCode, out) {
9324
9326
  }
9325
9327
 
9326
9328
  // src/scanner/sinks.ts
9329
+ function isClassListOrigin(origin) {
9330
+ return origin !== "plain" && origin !== "expression";
9331
+ }
9327
9332
  var SetSink = class {
9328
9333
  constructor(classes) {
9329
9334
  this.classes = classes;
9330
9335
  }
9331
9336
  classes;
9332
9337
  wantsPositions = false;
9338
+ origin = "plain";
9339
+ get inClassList() {
9340
+ return isClassListOrigin(this.origin);
9341
+ }
9342
+ setOrigin(origin) {
9343
+ this.origin = origin;
9344
+ }
9333
9345
  add(value) {
9334
9346
  this.classes.add(value);
9335
9347
  }
@@ -9345,6 +9357,12 @@ var CandidateCollector = class {
9345
9357
  /** value -> byCandidate keys, so delete() is O(occurrences of the value). */
9346
9358
  keysByValue = /* @__PURE__ */ new Map();
9347
9359
  contexts = [];
9360
+ /** Candidates a context-aware collector tokenized itself — the only ones
9361
+ * eligible to inherit a context's origin (see finish()). */
9362
+ contextual = /* @__PURE__ */ new WeakSet();
9363
+ get inClassList() {
9364
+ return isClassListOrigin(this.origin);
9365
+ }
9348
9366
  setOrigin(origin) {
9349
9367
  this.origin = origin;
9350
9368
  this.helperName = null;
@@ -9362,10 +9380,26 @@ var CandidateCollector = class {
9362
9380
  id: this.contexts.length
9363
9381
  });
9364
9382
  }
9383
+ markExpression(start, end) {
9384
+ if (end <= start) return;
9385
+ this.contexts.push({
9386
+ start,
9387
+ end,
9388
+ origin: "expression",
9389
+ helper: null,
9390
+ id: this.contexts.length
9391
+ });
9392
+ }
9365
9393
  add(value, start, end, prefixStart, prefixEnd) {
9366
9394
  const key = `${start}:${end}:${value}`;
9367
- if (this.byCandidate.has(key)) return;
9395
+ const contextual = this.origin !== "plain";
9396
+ const existing = this.byCandidate.get(key);
9397
+ if (existing) {
9398
+ if (contextual) this.contextual.add(existing);
9399
+ return;
9400
+ }
9368
9401
  const candidate = { value, start, end, origin: "plain" };
9402
+ if (contextual) this.contextual.add(candidate);
9369
9403
  if (prefixStart >= 0) candidate.groupPrefix = { start: prefixStart, end: prefixEnd };
9370
9404
  this.byCandidate.set(key, candidate);
9371
9405
  let keys = this.keysByValue.get(value);
@@ -9411,7 +9445,7 @@ var CandidateCollector = class {
9411
9445
  }
9412
9446
  }
9413
9447
  }
9414
- if (best) {
9448
+ if (best && this.contextual.has(candidate)) {
9415
9449
  candidate.origin = best.origin;
9416
9450
  if (best.helper) candidate.helperName = best.helper;
9417
9451
  if (best.origin === "helper" || best.origin === "safelist") {
@@ -9459,6 +9493,10 @@ var HAS_UPPERCASE_RE = /[A-Z]/;
9459
9493
  var BRACKET_WHITESPACE_RE = /\[[^\]]*\s+[^\]]*\]/;
9460
9494
  var INDEX_ACCESS_RE = /\[\d*\]$/;
9461
9495
  var PROPERTY_ACCESS_RE = /^[\w.@]+\[[^\]]+\]$/;
9496
+ function warnBracketWhitespace(warnings, cls) {
9497
+ const message = `[RI-1412] Class "${cls}" has whitespace inside its arbitrary value, so it can never match an element \u2014 class attributes, @a/@apply, and safelist() all split on whitespace. Use "_" for a space (\`bg-[url('a_b')]\` emits \`url('a b')\`) and "\\_" for a literal underscore. The class was skipped.`;
9498
+ if (!warnings.includes(message)) warnings.push(message);
9499
+ }
9462
9500
  function scanClassTokens(sink, source, baseOffset, warnings) {
9463
9501
  const wantsPositions = sink.wantsPositions;
9464
9502
  let filteredSource = source;
@@ -9514,10 +9552,17 @@ function scanClassTokens(sink, source, baseOffset, warnings) {
9514
9552
  if (!cls) continue;
9515
9553
  const unbanged = cls.endsWith("!") ? cls.slice(0, -1) : cls;
9516
9554
  const base = unbanged.replace(VARIANT_STRIP_RE, "");
9517
- if (HAS_UPPERCASE_RE.test(base.replace(BRACKET_SPAN_RE, ""))) continue;
9518
- if (BRACKET_WHITESPACE_RE.test(base)) continue;
9519
- if (INDEX_ACCESS_RE.test(base)) continue;
9520
- if (PROPERTY_ACCESS_RE.test(base)) continue;
9555
+ if (!base.includes("[")) {
9556
+ if (HAS_UPPERCASE_RE.test(base)) continue;
9557
+ } else {
9558
+ if (HAS_UPPERCASE_RE.test(base.replace(BRACKET_SPAN_RE, ""))) continue;
9559
+ if (INDEX_ACCESS_RE.test(base)) continue;
9560
+ if (PROPERTY_ACCESS_RE.test(base)) continue;
9561
+ if (BRACKET_WHITESPACE_RE.test(base)) {
9562
+ if (warnings && sink.inClassList) warnBracketWhitespace(warnings, cls);
9563
+ continue;
9564
+ }
9565
+ }
9521
9566
  if (!wantsPositions) {
9522
9567
  sink.add(cls, 0, 0, -1, -1);
9523
9568
  continue;
@@ -9602,6 +9647,16 @@ function collectTemplateLiteralClasses(sink, source, start, base, warnings) {
9602
9647
  }
9603
9648
  });
9604
9649
  }
9650
+ function isEqualityOperand(source, open, close) {
9651
+ let before = open - 1;
9652
+ while (before >= 0 && /\s/.test(source[before])) before--;
9653
+ if (before >= 1 && source[before] === "=" && (source[before - 1] === "=" || source[before - 1] === "!")) {
9654
+ return true;
9655
+ }
9656
+ let after = close + 1;
9657
+ while (after < source.length && /\s/.test(source[after])) after++;
9658
+ return (source[after] === "=" || source[after] === "!") && source[after + 1] === "=";
9659
+ }
9605
9660
  function collectStringLiteralClasses(sink, source, base, warnings) {
9606
9661
  let i = 0;
9607
9662
  while (i < source.length) {
@@ -9611,6 +9666,7 @@ function collectStringLiteralClasses(sink, source, base, warnings) {
9611
9666
  const raw = source.slice(i + 1, end);
9612
9667
  const value = raw.trim();
9613
9668
  if (value) {
9669
+ if (isEqualityOperand(source, i, end)) sink.markExpression?.(base + i, base + end + 1);
9614
9670
  scanClassTokens(
9615
9671
  sink,
9616
9672
  value,
@@ -9640,7 +9696,7 @@ function collectAssignedValues(sink, source, regex, visitor = scanClassTokens, b
9640
9696
  if (!value) continue;
9641
9697
  sink.markContext?.(base + parsed.valueStart, base + parsed.valueStart + raw.length);
9642
9698
  const valueOffset = base + parsed.valueStart + (raw.length - raw.trimStart().length);
9643
- if (parsed.quoted && visitor !== scanClassTokens) {
9699
+ if ((parsed.quoted || parsed.bare) && visitor !== scanClassTokens) {
9644
9700
  scanClassTokens(sink, value, valueOffset, warnings);
9645
9701
  }
9646
9702
  visitor(sink, value, valueOffset, warnings);
@@ -9896,6 +9952,13 @@ var EXTRACTORS = [
9896
9952
  extract: extractJSXTSX
9897
9953
  }
9898
9954
  ];
9955
+ var SVG_GEOMETRY_VALUE_RE = /((?<![\w-])(?:d|points)\s*=\s*["'])([^"']*)/g;
9956
+ function blankSvgGeometry(content) {
9957
+ return content.replace(
9958
+ SVG_GEOMETRY_VALUE_RE,
9959
+ (_match, head, value) => head + " ".repeat(value.length)
9960
+ );
9961
+ }
9899
9962
  function warnOverLongLines(input, warnings) {
9900
9963
  const content = input.content;
9901
9964
  if (!warnings || content.length <= MAX_LINE_LENGTH) return;
@@ -9916,23 +9979,27 @@ function warnOverLongLines(input, warnings) {
9916
9979
  }
9917
9980
  function extractInto(sink, input, warnings) {
9918
9981
  warnOverLongLines(input, warnings);
9982
+ const scanned = {
9983
+ path: input.path,
9984
+ content: blankSvgGeometry(input.content)
9985
+ };
9919
9986
  let handled = false;
9920
9987
  for (const extractor of EXTRACTORS) {
9921
- if (extractor.test(input)) {
9922
- extractor.extract(sink, input, warnings);
9988
+ if (extractor.test(scanned)) {
9989
+ extractor.extract(sink, scanned, warnings);
9923
9990
  handled = true;
9924
9991
  break;
9925
9992
  }
9926
9993
  }
9927
9994
  if (!handled) {
9928
9995
  sink.setOrigin?.("plain");
9929
- scanClassTokens(sink, input.content, 0, warnings);
9996
+ scanClassTokens(sink, scanned.content, 0, warnings);
9930
9997
  }
9931
- if (input.content.includes("safelist")) {
9998
+ if (scanned.content.includes("safelist")) {
9932
9999
  sink.setOrigin?.("safelist");
9933
10000
  collectCallArguments(
9934
10001
  sink,
9935
- input.content,
10002
+ scanned.content,
9936
10003
  SAFELIST_CALL_RE,
9937
10004
  collectStringLiteralClasses,
9938
10005
  0,
@@ -10025,7 +10092,7 @@ export {
10025
10092
  MAX_DIRECTIVE_INPUT_SIZE,
10026
10093
  extractDirectives,
10027
10094
  parseUtility,
10028
- getCustomUtility,
10095
+ matchCustomUtility,
10029
10096
  forEachApplyClass,
10030
10097
  BACKGROUND_STATIC_NAMES,
10031
10098
  TYPOGRAPHY_STATIC_NAMES,