rainbowindex 0.2.1 → 0.3.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.
@@ -282,6 +282,21 @@ function computeStop(def, suffix) {
282
282
  h: roundedH
283
283
  };
284
284
  }
285
+ function computeDarkStop(def, suffix, darkConfig, darkOverride) {
286
+ const lightStop = generateStop(def, suffix);
287
+ const darkL = generateStop(def, 1e3 - suffix).l;
288
+ let extraChroma = 0;
289
+ let extraHue = 0;
290
+ if (darkOverride?.strategy === "shift") {
291
+ extraChroma = darkOverride.chromaDelta;
292
+ extraHue = darkOverride.hueDelta;
293
+ }
294
+ const darkH = lightStop.h + darkConfig.hueShift + extraHue;
295
+ const globalChromaBoost = def.chromaBoost !== false ? darkConfig.chromaBoost : 0;
296
+ const requestedChroma = lightStop.c + globalChromaBoost + extraChroma;
297
+ const darkC = Math.floor(gamutSafeChroma(darkL, requestedChroma, darkH) * 1e4) / 1e4;
298
+ return { stop: suffix, l: darkL, c: darkC, h: darkH };
299
+ }
285
300
  function formatOklch(l, c, h) {
286
301
  return `oklch(${l} ${c} ${h})`;
287
302
  }
@@ -298,18 +313,8 @@ function generateColorVariables(name, def, suffixes, darkConfig = DEFAULT_DARK_C
298
313
  vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${lightValue});`);
299
314
  }
300
315
  } else {
301
- const darkL = generateStop(def, 1e3 - suffix).l;
302
- let extraChroma = 0;
303
- let extraHue = 0;
304
- if (darkOverride?.strategy === "shift") {
305
- extraChroma = darkOverride.chromaDelta;
306
- extraHue = darkOverride.hueDelta;
307
- }
308
- const darkH = lightStop.h + darkConfig.hueShift + extraHue;
309
- const globalChromaBoost = def.chromaBoost !== false ? darkConfig.chromaBoost : 0;
310
- const requestedChroma = lightStop.c + globalChromaBoost + extraChroma;
311
- const darkC = Math.floor(gamutSafeChroma(darkL, requestedChroma, darkH) * 1e4) / 1e4;
312
- const darkValue = formatOklch(darkL, darkC, darkH);
316
+ const darkStop = computeDarkStop(def, suffix, darkConfig, darkOverride);
317
+ const darkValue = formatOklch(darkStop.l, darkStop.c, darkStop.h);
313
318
  vars.push(`--color-${name}-${suffix}: light-dark(${lightValue}, ${darkValue});`);
314
319
  }
315
320
  }
@@ -2330,7 +2335,7 @@ function canonicalVariantPrefix(variantPrefix) {
2330
2335
  segments.sort();
2331
2336
  return `${segments.join(":")}:`;
2332
2337
  }
2333
- function mergeUncached(classes, resolve) {
2338
+ function mergeUncached(classes, resolve, trace) {
2334
2339
  const claimed = /* @__PURE__ */ new Set();
2335
2340
  const result = [];
2336
2341
  for (let i = classes.length - 1; i >= 0; i--) {
@@ -2353,13 +2358,27 @@ function mergeUncached(classes, resolve) {
2353
2358
  break;
2354
2359
  }
2355
2360
  }
2356
- if (dominated) continue;
2361
+ if (dominated) {
2362
+ if (trace) {
2363
+ const by = /* @__PURE__ */ new Set();
2364
+ for (const prop of props) {
2365
+ const claimer = trace.claimers.get(ns + prop);
2366
+ if (claimer !== void 0) by.add(claimer);
2367
+ }
2368
+ trace.dropped.push({ index: i, overriddenBy: [...by].sort((a, b) => a - b) });
2369
+ }
2370
+ continue;
2371
+ }
2357
2372
  for (const prop of props) {
2358
- claimed.add(ns + prop);
2373
+ const key = ns + prop;
2374
+ if (trace && !claimed.has(key)) trace.claimers.set(key, i);
2375
+ claimed.add(key);
2359
2376
  const longhands = OVERRIDES[prop];
2360
2377
  if (longhands !== void 0) {
2361
2378
  for (const lh of longhands) {
2362
- claimed.add(ns + lh);
2379
+ const longhandKey = ns + lh;
2380
+ if (trace && !claimed.has(longhandKey)) trace.claimers.set(longhandKey, i);
2381
+ claimed.add(longhandKey);
2363
2382
  }
2364
2383
  }
2365
2384
  }
@@ -2465,6 +2484,38 @@ function createRi(snapshot) {
2465
2484
  return mergeFrom(inputs, resolve, cache);
2466
2485
  };
2467
2486
  }
2487
+ function analyzeMerge(classes, snapshot) {
2488
+ const snap = snapshot ?? _latestSnapshot ?? {
2489
+ customStaticProps: _customStaticProps,
2490
+ textSizes: _textSizes,
2491
+ fontFamilies: _fontFamilies,
2492
+ colorNames: _colorNames
2493
+ };
2494
+ const resolve = (utility) => resolvePropsWith(
2495
+ utility,
2496
+ snap.customStaticProps,
2497
+ snap.textSizes,
2498
+ snap.fontFamilies,
2499
+ snap.colorNames
2500
+ );
2501
+ const trace = { claimers: /* @__PURE__ */ new Map(), dropped: [] };
2502
+ const output = mergeUncached(classes, resolve, trace);
2503
+ trace.dropped.sort((a, b) => a.index - b.index);
2504
+ const droppedIndexes = new Set(trace.dropped.map((d) => d.index));
2505
+ const kept = [];
2506
+ for (let i = 0; i < classes.length; i++) {
2507
+ if (!droppedIndexes.has(i)) kept.push(i);
2508
+ }
2509
+ return {
2510
+ output,
2511
+ kept,
2512
+ dropped: trace.dropped.map((d) => ({
2513
+ index: d.index,
2514
+ className: classes[d.index],
2515
+ overriddenBy: d.overriddenBy
2516
+ }))
2517
+ };
2518
+ }
2468
2519
  var MAX_FLATTEN_DEPTH = 10;
2469
2520
  var MAX_CLASS_NAME_LENGTH = 500;
2470
2521
  var MAX_TOTAL_CLASSES = 1e4;
@@ -2622,6 +2673,12 @@ export {
2622
2673
  isValidColorSuffix,
2623
2674
  DEFAULT_COLORS,
2624
2675
  DEFAULT_DARK_CONFIG,
2676
+ oklchToOklab,
2677
+ oklabToLinearSrgb,
2678
+ linearToSrgb,
2679
+ generateStop,
2680
+ computeDarkStop,
2681
+ formatOklch,
2625
2682
  checkPaletteContrast,
2626
2683
  generateAllColorVariables,
2627
2684
  generateThemeOverrides,
@@ -2654,6 +2711,7 @@ export {
2654
2711
  DEFAULT_TEXT_SIZES,
2655
2712
  ri,
2656
2713
  createRi,
2714
+ analyzeMerge,
2657
2715
  createCompilationContext,
2658
2716
  registerCustomUtility,
2659
2717
  registerCustomTextSizes,
package/dist/cli.mjs CHANGED
@@ -1,22 +1,29 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ CSS_ENTRY_CANDIDATES,
4
+ enumerateClassNames
5
+ } from "./chunk-RHOQSN2B.mjs";
2
6
  import {
3
7
  DEFAULT_EXCLUDES,
4
8
  DEFAULT_PATTERNS,
5
- MAX_DIRECTIVE_INPUT_SIZE,
6
- analyzeProjectCSS,
7
9
  collectProjectClasses,
8
- extractDirectives,
9
10
  finalizeProjectCompilation,
10
11
  getFontPreloadLinks,
11
- hasApplyLikeDirective,
12
- hasRIActivation,
13
- resolveDirectives,
14
12
  resolveGoogleFonts,
15
13
  validateGlobPattern
16
- } from "./chunk-RPXZ3O6R.mjs";
14
+ } from "./chunk-AZXWJ625.mjs";
15
+ import {
16
+ MAX_DIRECTIVE_INPUT_SIZE,
17
+ analyzeProjectCSS,
18
+ extractDirectives,
19
+ hasApplyLikeDirective,
20
+ hasRIActivation,
21
+ listVariants,
22
+ resolveDirectives
23
+ } from "./chunk-IE6N76PW.mjs";
17
24
  import {
18
25
  devWarn
19
- } from "./chunk-5N4GPK26.mjs";
26
+ } from "./chunk-SOMDX7V6.mjs";
20
27
 
21
28
  // src/entries/cli.ts
22
29
  import { realpathSync } from "fs";
@@ -102,7 +109,7 @@ function parseArgs(argv, callbacks) {
102
109
  i++;
103
110
  continue;
104
111
  }
105
- if (arg === "--minify") {
112
+ if (arg === "--minify" || arg === "--optimize") {
106
113
  opts.minify = true;
107
114
  i++;
108
115
  continue;
@@ -169,7 +176,7 @@ Usage:
169
176
  Options:
170
177
  -o, --output <file> Output CSS file path (omit to write to stdout)
171
178
  --watch Re-run on source-file changes (requires --output)
172
- --minify Minify output via LightningCSS
179
+ --minify Minify + browser fallbacks via LightningCSS (alias: --optimize)
173
180
  --css <file> CSS input with directives (auto-detected if omitted)
174
181
  -h, --help Show this help
175
182
 
@@ -284,7 +291,7 @@ Run \`rainbowindex <subcommand> --help\` for subcommand-specific options.
284
291
  Options:
285
292
  -o, --output <file> Output CSS file path
286
293
  --watch Watch for changes
287
- --minify Minify output via LightningCSS
294
+ --minify Minify + browser fallbacks via LightningCSS (alias: --optimize)
288
295
  --css <file> CSS file with directives (default: auto-detect)
289
296
  --template <name> Vite template for create (default: react-ts)
290
297
  --strict No string escape hatch in generated types
@@ -314,21 +321,9 @@ Examples:
314
321
  import { existsSync } from "fs";
315
322
  import { readFile, stat } from "fs/promises";
316
323
  import { resolve as resolve2 } from "path";
317
- var CSS_CANDIDATES = Object.freeze([
318
- "src/index.css",
319
- "src/style.css",
320
- "src/styles.css",
321
- "src/app.css",
322
- "src/global.css",
323
- "index.css",
324
- "style.css",
325
- "styles.css",
326
- "app.css",
327
- "global.css"
328
- ]);
329
324
  var MAX_CSS_FILE_SIZE = MAX_DIRECTIVE_INPUT_SIZE;
330
325
  async function findCSSFileAsync(cwd) {
331
- const candidates = CSS_CANDIDATES;
326
+ const candidates = CSS_ENTRY_CANDIDATES;
332
327
  const results = await Promise.all(
333
328
  candidates.map(async (candidate) => {
334
329
  const full = resolve2(cwd, candidate);
@@ -452,19 +447,37 @@ async function generateTypes(opts, cwd) {
452
447
  return false;
453
448
  });
454
449
  };
450
+ const literalSafe = (names) => names.filter((n) => !n.includes('"') && !n.includes("\\"));
451
+ const enumeration = enumerateClassNames(theme);
452
+ const variants = listVariants(theme);
455
453
  const colorNames = validateNames(Object.keys(theme.colors), "color");
456
454
  const textSizes = validateNames(Object.keys(theme.text), "text");
457
- const breakpoints = validateNames(Object.keys(theme.breakpoints), "breakpoint");
458
455
  const weightNames = validateNames(Object.keys(theme.weights), "weight");
459
- const staticCustomUtilities = validateNames(
460
- theme.customUtilities.filter((u) => !u.functional).map((u) => u.name),
461
- "utility"
456
+ const variantNames = literalSafe(variants.filter((v) => v.kind !== "pattern").map((v) => v.name));
457
+ const finiteClasses = literalSafe(enumeration.classes.map((c) => c.name));
458
+ const spacingRoots = validateNames(
459
+ enumeration.templates.filter((t) => t.kind === "spacing").map((t) => t.root),
460
+ "spacing root"
461
+ );
462
+ const numericRoots = validateNames(
463
+ enumeration.templates.filter((t) => t.kind === "number").map((t) => t.root),
464
+ "numeric root"
462
465
  );
463
466
  const functionalCustomUtilities = validateNames(
464
- theme.customUtilities.filter((u) => u.functional).map((u) => u.name),
467
+ enumeration.templates.filter((t) => t.kind === "custom").map((t) => t.root),
465
468
  "utility"
466
469
  );
467
470
  const union = (names) => names.length > 0 ? names.map((n) => `"${n}"`).join(" | ") : "never";
471
+ const unionLines = (names, perLine) => {
472
+ if (names.length === 0) return [" | never"];
473
+ const lines2 = [];
474
+ for (let i = 0; i < names.length; i += perLine) {
475
+ lines2.push(
476
+ ` | ${names.slice(i, i + perLine).map((n) => `"${n}"`).join(" | ")}`
477
+ );
478
+ }
479
+ return lines2;
480
+ };
468
481
  const lines = [
469
482
  "// rainbowindex-env.d.ts (auto-generated \u2014 do not edit)",
470
483
  "",
@@ -472,25 +485,44 @@ async function generateTypes(opts, cwd) {
472
485
  `type ColorStop = "50" | "100" | "150" | "200" | "250" | "300" | "350" | "400" | "450" | "500" | "550" | "600" | "650" | "700" | "750" | "800" | "850" | "900" | "950";`,
473
486
  "type SpacingToken = `${number}` | `${number}_${number}`;",
474
487
  `type TextSize = ${union(textSizes)};`,
475
- `type Variant = ${union(breakpoints)} | "hover" | "focus" | "focus-visible" | "active" | "disabled" | "dark" | "first" | "last" | "odd" | "even";`,
476
488
  `type WeightName = ${union(weightNames)};`,
477
489
  "",
478
- "type RainbowClass =",
479
- ' | `${"bg" | "text" | "border" | "outline" | "accent" | "caret" | "fill" | "stroke"}-${ColorName}-${ColorStop}`',
480
- ' | `${"p" | "px" | "py" | "pt" | "pb" | "pl" | "pr" | "ps" | "pe" | "pbs" | "pbe" | "m" | "mx" | "my" | "mt" | "mb" | "ml" | "mr" | "ms" | "me" | "mbs" | "mbe" | "gap" | "gap-x" | "gap-y"}-${SpacingToken}`',
481
- " | `text-${TextSize}`",
482
- " | `text-fluid-${TextSize}`",
483
- ' | `font-${"sans" | "serif" | "mono"}`',
484
- " | `font-${WeightName}`",
485
- " | `w-${SpacingToken}` | `h-${SpacingToken}` | `size-${SpacingToken}`"
490
+ "type Variant =",
491
+ ...unionLines(variantNames, 8),
492
+ " ;",
493
+ "",
494
+ "// Every finite class the compiler resolves for this theme \u2014 statics plus",
495
+ "// theme-token expansions, enumerated and probe-verified.",
496
+ "type RainbowStatic =",
497
+ ...unionLines(finiteClasses, 6),
498
+ " ;"
486
499
  ];
487
- if (staticCustomUtilities.length > 0) {
488
- lines.push(` | ${staticCustomUtilities.map((n) => `"${n}"`).join(" | ")}`);
500
+ if (spacingRoots.length > 0) {
501
+ lines.push("", `type SpacingRoot = ${union(spacingRoots)};`);
502
+ }
503
+ if (numericRoots.length > 0) {
504
+ lines.push(`type NumericRoot = ${union(numericRoots)};`);
505
+ }
506
+ lines.push("", "type RainbowBase =", " | RainbowStatic");
507
+ if (spacingRoots.length > 0) {
508
+ lines.push(" | `${SpacingRoot}-${SpacingToken}`");
509
+ }
510
+ if (numericRoots.length > 0) {
511
+ lines.push(" | `${NumericRoot}-${number}`");
489
512
  }
490
513
  if (functionalCustomUtilities.length > 0) {
491
514
  lines.push(` | ${functionalCustomUtilities.map((n) => `\`${n}-\${string}\``).join(" | ")}`);
492
515
  }
493
- lines.push(" | `${Variant}:${Exclude<RainbowClass, `${string}:${string}`>}`");
516
+ lines.push(" ;");
517
+ lines.push(
518
+ "",
519
+ "type RainbowClass =",
520
+ " | RainbowBase",
521
+ " // Variant-prefixed classes validate the variant name (chained prefixes",
522
+ " // match through the open remainder). Expanding Variant \xD7 RainbowBase",
523
+ " // eagerly would exceed TypeScript's union-size limits.",
524
+ " | `${Variant}:${string}`"
525
+ );
494
526
  if (!opts.strict) {
495
527
  lines.push(" | (string & {});");
496
528
  } else {
@@ -1054,7 +1086,7 @@ async function resolveStylesheetPath(cwd, cssFile) {
1054
1086
  if (cssFile) return resolve5(cwd, cssFile);
1055
1087
  const importedCSS = await findImportedStylesheet(cwd);
1056
1088
  if (importedCSS) return importedCSS;
1057
- for (const candidate of CSS_CANDIDATES) {
1089
+ for (const candidate of CSS_ENTRY_CANDIDATES) {
1058
1090
  const fullPath = resolve5(cwd, candidate);
1059
1091
  if (existsSync3(fullPath)) return fullPath;
1060
1092
  }
@@ -1201,7 +1233,7 @@ function readPackageJSONSync(cwd) {
1201
1233
 
1202
1234
  // src/entries/cli.ts
1203
1235
  function getVersion() {
1204
- return true ? "0.2.1" : "unknown";
1236
+ return true ? "0.3.0" : "unknown";
1205
1237
  }
1206
1238
  async function main() {
1207
1239
  const args = process.argv.slice(2);