rainbowindex 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -2,28 +2,30 @@
2
2
  import {
3
3
  CSS_ENTRY_CANDIDATES,
4
4
  enumerateClassNames
5
- } from "./chunk-YLB6FGIG.mjs";
5
+ } from "./chunk-NXJZX6KI.mjs";
6
6
  import {
7
7
  DEFAULT_EXCLUDES,
8
8
  DEFAULT_PATTERNS,
9
9
  compileScannedProject,
10
10
  getFontPreloadLinks
11
- } from "./chunk-4SVFFDS2.mjs";
11
+ } from "./chunk-DT5HYIM3.mjs";
12
12
  import {
13
13
  MAX_DIRECTIVE_INPUT_SIZE,
14
+ codepointCompare,
15
+ extractClassesFromSource,
14
16
  extractDirectives,
15
17
  hasApplyLikeDirective,
16
18
  hasRIActivation,
17
19
  listVariants,
18
20
  resolveDirectives
19
- } from "./chunk-6DAHUFNU.mjs";
21
+ } from "./chunk-ZI5ZYNSU.mjs";
20
22
  import {
21
23
  devWarn
22
- } from "./chunk-5Y7EXXLS.mjs";
24
+ } from "./chunk-KRZL4IDK.mjs";
23
25
 
24
26
  // src/entries/cli.ts
25
27
  import { realpathSync } from "fs";
26
- import { resolve as resolve6 } from "path";
28
+ import { resolve as resolve7 } from "path";
27
29
  import { pathToFileURL } from "url";
28
30
 
29
31
  // src/cli/args.ts
@@ -153,6 +155,22 @@ Examples:
153
155
  rainbowindex generate-types
154
156
  rainbowindex generate-types --strict --css src/styles.css`
155
157
  },
158
+ scan: {
159
+ summary: "Show what the class scanner extracts (debugging)",
160
+ usage: " rainbowindex scan <file|glob...>",
161
+ flags: [],
162
+ positionals: "globs",
163
+ body: `Output:
164
+ One line per extracted class candidate, per file. Scanner warnings (skipped
165
+ over-long lines, unreadable files) print to stderr with [RI-NNNN] codes.
166
+ A class missing here was never seen by the scanner \u2014 check how it appears
167
+ in the markup. A class listed here that still does not generate fails
168
+ later \u2014 check the build warnings.
169
+
170
+ Examples:
171
+ rainbowindex scan src/components/icons/logo.tsx
172
+ rainbowindex scan "src/**/*.tsx"`
173
+ },
156
174
  "preload-fonts": {
157
175
  summary: 'Generate <link rel="preload"> tags',
158
176
  usage: " rainbowindex preload-fonts [options]",
@@ -261,6 +279,11 @@ function parseArgs(argv, callbacks) {
261
279
  if (opts.command === "create" && !opts.targetDir) {
262
280
  throw new Error("create requires a project directory. Example: rainbowindex create my-app");
263
281
  }
282
+ if (opts.command === "scan" && opts.globs.length === 0) {
283
+ throw new Error(
284
+ 'scan requires at least one file or glob. Example: rainbowindex scan "src/**/*.tsx"'
285
+ );
286
+ }
264
287
  if (opts.watch && !opts.output) {
265
288
  throw new Error(
266
289
  '--output is required with --watch. Example: rainbowindex "src/**/*.tsx" --watch -o dist/styles.css'
@@ -303,8 +326,7 @@ function printHelp(subcommand) {
303
326
  ${spec.usage}`,
304
327
  ...spec.intro ? [spec.intro] : [],
305
328
  `Options:
306
- ${renderFlagLines(spec.flags)}
307
- ${HELP_LINE}`,
329
+ ${[renderFlagLines(spec.flags), HELP_LINE].filter(Boolean).join("\n")}`,
308
330
  spec.body
309
331
  ];
310
332
  console.log(`
@@ -321,6 +343,7 @@ Usage:
321
343
  rainbowindex create <dir> Scaffold a Vite app with Rainbow Index ready
322
344
  rainbowindex generate-types Generate TypeScript types for ri()
323
345
  rainbowindex preload-fonts Generate font preload link tags
346
+ rainbowindex scan <file...> Show what the class scanner extracts
324
347
 
325
348
  Run \`rainbowindex <subcommand> --help\` for subcommand-specific options.
326
349
 
@@ -772,11 +795,40 @@ async function watchMode(opts, cwd) {
772
795
  process.on("SIGTERM", cleanup);
773
796
  }
774
797
 
798
+ // src/cli/scan.ts
799
+ import { readFile as readFile3 } from "fs/promises";
800
+ import { relative as relative2, resolve as resolve5 } from "path";
801
+ import { glob } from "tinyglobby";
802
+ async function scanFiles(opts, cwd) {
803
+ const files = [...new Set(await glob(opts.globs, { cwd }))].sort(codepointCompare);
804
+ if (files.length === 0) {
805
+ throw new Error(`No files matched ${opts.globs.map((g) => `"${g}"`).join(", ")}.`);
806
+ }
807
+ for (const file of files) {
808
+ const path = resolve5(cwd, file);
809
+ const warnings = [];
810
+ const classes = extractClassesFromSource(
811
+ { path, content: await readFile3(path, "utf-8") },
812
+ warnings
813
+ );
814
+ const sorted = [...classes].sort(codepointCompare);
815
+ console.log(
816
+ `${relative2(cwd, path)} (${sorted.length} class${sorted.length === 1 ? "" : "es"})`
817
+ );
818
+ for (const cls of sorted) {
819
+ console.log(` ${cls}`);
820
+ }
821
+ for (const warning of warnings) {
822
+ console.error(` ${warning}`);
823
+ }
824
+ }
825
+ }
826
+
775
827
  // src/cli/vite-setup.ts
776
828
  import { spawn } from "child_process";
777
829
  import { existsSync as existsSync3, readFileSync } from "fs";
778
- import { mkdir as mkdir2, readFile as readFile3, readdir, writeFile as writeFile3 } from "fs/promises";
779
- import { dirname as dirname3, relative as relative2, resolve as resolve5 } from "path";
830
+ import { mkdir as mkdir2, readFile as readFile4, readdir, writeFile as writeFile3 } from "fs/promises";
831
+ import { dirname as dirname3, relative as relative3, resolve as resolve6 } from "path";
780
832
  var VITE_CONFIG_FILES = [
781
833
  "vite.config.ts",
782
834
  "vite.config.mts",
@@ -839,32 +891,32 @@ async function initViteProject(opts, cwd, deps = {}) {
839
891
  ensureStylesheet(cssPath)
840
892
  ]);
841
893
  const entryChanged = cssCreated ? await ensureStylesheetImport(entryFile, cssPath) : false;
842
- const rootLabel = relative2(process.cwd(), cwd) || ".";
894
+ const rootLabel = relative3(process.cwd(), cwd) || ".";
843
895
  console.log(`[rainbowindex] Initialized Vite project: ${rootLabel}`);
844
896
  if (dependencyInstalled) {
845
897
  console.log("[rainbowindex] Added dev dependency: rainbowindex");
846
898
  }
847
899
  if (configCreated) {
848
900
  console.log(
849
- `[rainbowindex] Created Vite config: ${relative2(cwd, configPath).replaceAll("\\", "/")}`
901
+ `[rainbowindex] Created Vite config: ${relative3(cwd, configPath).replaceAll("\\", "/")}`
850
902
  );
851
903
  } else if (configChanged) {
852
904
  console.log(
853
- `[rainbowindex] Updated Vite config: ${relative2(cwd, configPath).replaceAll("\\", "/")}`
905
+ `[rainbowindex] Updated Vite config: ${relative3(cwd, configPath).replaceAll("\\", "/")}`
854
906
  );
855
907
  }
856
908
  if (cssCreated) {
857
909
  console.log(
858
- `[rainbowindex] Created stylesheet: ${relative2(cwd, cssPath).replaceAll("\\", "/")}`
910
+ `[rainbowindex] Created stylesheet: ${relative3(cwd, cssPath).replaceAll("\\", "/")}`
859
911
  );
860
912
  } else if (cssChanged) {
861
913
  console.log(
862
- `[rainbowindex] Updated stylesheet: ${relative2(cwd, cssPath).replaceAll("\\", "/")}`
914
+ `[rainbowindex] Updated stylesheet: ${relative3(cwd, cssPath).replaceAll("\\", "/")}`
863
915
  );
864
916
  }
865
917
  if (entryChanged && entryFile) {
866
918
  console.log(
867
- `[rainbowindex] Added stylesheet import: ${relative2(cwd, entryFile).replaceAll("\\", "/")}`
919
+ `[rainbowindex] Added stylesheet import: ${relative3(cwd, entryFile).replaceAll("\\", "/")}`
868
920
  );
869
921
  }
870
922
  if (!dependencyInstalled && !configChanged && !configCreated && !cssChanged && !cssCreated) {
@@ -888,7 +940,7 @@ async function createViteProject(opts, cwd, deps = {}) {
888
940
  const packageManager = deps.packageManager ?? detectPackageManager(cwd, deps.env);
889
941
  const runner = deps.runner ?? DEFAULT_RUNNER;
890
942
  const targetDir = opts.targetDir;
891
- const targetRoot = resolve5(cwd, targetDir);
943
+ const targetRoot = resolve6(cwd, targetDir);
892
944
  await ensureScaffoldTargetIsUsable(targetRoot);
893
945
  const template = opts.template || DEFAULT_CREATE_TEMPLATE;
894
946
  const scaffold = getCreateCommand(packageManager, targetDir, template);
@@ -899,15 +951,15 @@ async function createViteProject(opts, cwd, deps = {}) {
899
951
  packageManager,
900
952
  runner
901
953
  });
902
- const displayTarget = relative2(cwd, targetRoot) || ".";
954
+ const displayTarget = relative3(cwd, targetRoot) || ".";
903
955
  console.log(`[rainbowindex] Ready: ${displayTarget}`);
904
956
  console.log(`[rainbowindex] Next: cd ${displayTarget} && ${packageManager} run dev`);
905
957
  }
906
958
  function detectPackageManager(cwd, env = process.env) {
907
- if (existsSync3(resolve5(cwd, "pnpm-lock.yaml"))) return "pnpm";
908
- if (existsSync3(resolve5(cwd, "yarn.lock"))) return "yarn";
909
- if (existsSync3(resolve5(cwd, "bun.lock")) || existsSync3(resolve5(cwd, "bun.lockb"))) return "bun";
910
- if (existsSync3(resolve5(cwd, "package-lock.json")) || existsSync3(resolve5(cwd, "npm-shrinkwrap.json"))) {
959
+ if (existsSync3(resolve6(cwd, "pnpm-lock.yaml"))) return "pnpm";
960
+ if (existsSync3(resolve6(cwd, "yarn.lock"))) return "yarn";
961
+ if (existsSync3(resolve6(cwd, "bun.lock")) || existsSync3(resolve6(cwd, "bun.lockb"))) return "bun";
962
+ if (existsSync3(resolve6(cwd, "package-lock.json")) || existsSync3(resolve6(cwd, "npm-shrinkwrap.json"))) {
911
963
  return "npm";
912
964
  }
913
965
  const packageJSON = readPackageJSONSync(cwd);
@@ -952,12 +1004,12 @@ async function installRainbowIndex(cwd, packageManager, runner) {
952
1004
  async function ensureViteConfig(cwd, packageJSON, existingPath) {
953
1005
  if (!existingPath) {
954
1006
  const fileName = chooseViteConfigName(cwd, packageJSON);
955
- const configPath = resolve5(cwd, fileName);
1007
+ const configPath = resolve6(cwd, fileName);
956
1008
  const content = 'import { defineConfig } from "vite";\nimport rainbowindex from "rainbowindex/vite";\n\nexport default defineConfig({\n plugins: [rainbowindex()],\n});\n';
957
1009
  await writeFile3(configPath, content, "utf-8");
958
1010
  return { path: configPath, changed: true, created: true };
959
1011
  }
960
- const original = await readFile3(existingPath, "utf-8");
1012
+ const original = await readFile4(existingPath, "utf-8");
961
1013
  const updated = patchViteConfig(original, existingPath);
962
1014
  if (updated === original) {
963
1015
  return { path: existingPath, changed: false, created: false };
@@ -1086,14 +1138,14 @@ function findMatchingDelimiter(input, start, open, close) {
1086
1138
  return -1;
1087
1139
  }
1088
1140
  async function resolveStylesheetPath(cwd, cssFile) {
1089
- if (cssFile) return resolve5(cwd, cssFile);
1141
+ if (cssFile) return resolve6(cwd, cssFile);
1090
1142
  const importedCSS = await findImportedStylesheet(cwd);
1091
1143
  if (importedCSS) return importedCSS;
1092
1144
  for (const candidate of CSS_ENTRY_CANDIDATES) {
1093
- const fullPath = resolve5(cwd, candidate);
1145
+ const fullPath = resolve6(cwd, candidate);
1094
1146
  if (existsSync3(fullPath)) return fullPath;
1095
1147
  }
1096
- return existsSync3(resolve5(cwd, "src")) ? resolve5(cwd, "src/index.css") : resolve5(cwd, "index.css");
1148
+ return existsSync3(resolve6(cwd, "src")) ? resolve6(cwd, "src/index.css") : resolve6(cwd, "index.css");
1097
1149
  }
1098
1150
  async function ensureStylesheet(cssPath) {
1099
1151
  if (!existsSync3(cssPath)) {
@@ -1101,7 +1153,7 @@ async function ensureStylesheet(cssPath) {
1101
1153
  await writeFile3(cssPath, '@import "rainbowindex";\n', "utf-8");
1102
1154
  return { changed: true, created: true };
1103
1155
  }
1104
- const original = await readFile3(cssPath, "utf-8");
1156
+ const original = await readFile4(cssPath, "utf-8");
1105
1157
  if (/@import\s+["']rainbowindex["']/.test(original)) {
1106
1158
  return { changed: false, created: false };
1107
1159
  }
@@ -1128,7 +1180,7 @@ ${content}`;
1128
1180
  }
1129
1181
  async function ensureStylesheetImport(entryFile, cssPath) {
1130
1182
  if (!entryFile || !existsSync3(entryFile)) return false;
1131
- const original = await readFile3(entryFile, "utf-8");
1183
+ const original = await readFile4(entryFile, "utf-8");
1132
1184
  const relativePath = toImportPath(dirname3(entryFile), cssPath);
1133
1185
  if (hasStylesheetImport(original, relativePath)) {
1134
1186
  return false;
@@ -1139,16 +1191,16 @@ async function ensureStylesheetImport(entryFile, cssPath) {
1139
1191
  }
1140
1192
  async function findImportedStylesheet(cwd) {
1141
1193
  for (const entryFile of ENTRY_FILES) {
1142
- const fullPath = resolve5(cwd, entryFile);
1194
+ const fullPath = resolve6(cwd, entryFile);
1143
1195
  if (!existsSync3(fullPath)) continue;
1144
- const content = await readFile3(fullPath, "utf-8");
1196
+ const content = await readFile4(fullPath, "utf-8");
1145
1197
  const matches = content.matchAll(
1146
1198
  /import\s+(?:.+?\s+from\s+)?["']([^"']+\.css(?:\?[^"']*)?)["']/g
1147
1199
  );
1148
1200
  for (const match of matches) {
1149
1201
  const importPath = match[1].split("?")[0];
1150
1202
  if (!importPath.startsWith(".")) continue;
1151
- const stylesheet = resolve5(dirname3(fullPath), importPath);
1203
+ const stylesheet = resolve6(dirname3(fullPath), importPath);
1152
1204
  if (existsSync3(stylesheet)) return stylesheet;
1153
1205
  }
1154
1206
  }
@@ -1156,7 +1208,7 @@ async function findImportedStylesheet(cwd) {
1156
1208
  }
1157
1209
  async function findEntryFile(cwd) {
1158
1210
  for (const entryFile of ENTRY_FILES) {
1159
- const fullPath = resolve5(cwd, entryFile);
1211
+ const fullPath = resolve6(cwd, entryFile);
1160
1212
  if (existsSync3(fullPath)) return fullPath;
1161
1213
  }
1162
1214
  return null;
@@ -1166,18 +1218,18 @@ function hasStylesheetImport(source, importPath) {
1166
1218
  return new RegExp(`import\\s+(?:.+?\\s+from\\s+)?["']${escapedPath}["']`).test(source);
1167
1219
  }
1168
1220
  function toImportPath(fromDir, targetFile) {
1169
- const rel = relative2(fromDir, targetFile).replaceAll("\\", "/");
1221
+ const rel = relative3(fromDir, targetFile).replaceAll("\\", "/");
1170
1222
  return rel.startsWith(".") ? rel : `./${rel}`;
1171
1223
  }
1172
1224
  function findExistingViteConfig(cwd) {
1173
1225
  for (const name of VITE_CONFIG_FILES) {
1174
- const fullPath = resolve5(cwd, name);
1226
+ const fullPath = resolve6(cwd, name);
1175
1227
  if (existsSync3(fullPath)) return fullPath;
1176
1228
  }
1177
1229
  return null;
1178
1230
  }
1179
1231
  function chooseViteConfigName(cwd, packageJSON) {
1180
- if (existsSync3(resolve5(cwd, "tsconfig.json")) || existsSync3(resolve5(cwd, "tsconfig.app.json")) || existsSync3(resolve5(cwd, "src/main.ts")) || existsSync3(resolve5(cwd, "src/main.tsx")) || packageJSON?.dependencies?.typescript || packageJSON?.devDependencies?.typescript) {
1232
+ if (existsSync3(resolve6(cwd, "tsconfig.json")) || existsSync3(resolve6(cwd, "tsconfig.app.json")) || existsSync3(resolve6(cwd, "src/main.ts")) || existsSync3(resolve6(cwd, "src/main.tsx")) || packageJSON?.dependencies?.typescript || packageJSON?.devDependencies?.typescript) {
1181
1233
  return "vite.config.ts";
1182
1234
  }
1183
1235
  return packageJSON?.type === "module" ? "vite.config.js" : "vite.config.mjs";
@@ -1225,7 +1277,7 @@ function getInstallCommand(packageManager) {
1225
1277
  }
1226
1278
  }
1227
1279
  function readPackageJSONSync(cwd) {
1228
- const packagePath = resolve5(cwd, "package.json");
1280
+ const packagePath = resolve6(cwd, "package.json");
1229
1281
  if (!existsSync3(packagePath)) return null;
1230
1282
  try {
1231
1283
  return JSON.parse(readFileSync(packagePath, "utf-8"));
@@ -1236,7 +1288,7 @@ function readPackageJSONSync(cwd) {
1236
1288
 
1237
1289
  // src/entries/cli.ts
1238
1290
  function getVersion() {
1239
- return true ? "0.4.0" : "unknown";
1291
+ return true ? "0.5.0" : "unknown";
1240
1292
  }
1241
1293
  async function main() {
1242
1294
  const args = process.argv.slice(2);
@@ -1260,6 +1312,9 @@ async function main() {
1260
1312
  case "preload-fonts":
1261
1313
  await preloadFonts(opts, cwd);
1262
1314
  break;
1315
+ case "scan":
1316
+ await scanFiles(opts, cwd);
1317
+ break;
1263
1318
  case "build": {
1264
1319
  if (opts.watch) {
1265
1320
  await watchMode(opts, cwd);
@@ -1280,7 +1335,7 @@ var isDirectExecution = (() => {
1280
1335
  const entry = process.argv[1];
1281
1336
  if (entry === void 0) return false;
1282
1337
  try {
1283
- return import.meta.url === pathToFileURL(realpathSync(resolve6(entry))).href;
1338
+ return import.meta.url === pathToFileURL(realpathSync(resolve7(entry))).href;
1284
1339
  } catch {
1285
1340
  return false;
1286
1341
  }
package/dist/editor.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ParsedDirective, R as ResolvedTheme } from './index-BB2HoeMj.js';
2
- export { b as createThemeSnapshot } from './index-BB2HoeMj.js';
1
+ import { P as ParsedDirective, R as ResolvedTheme } from './index-Dp6i5TSv.js';
2
+ export { b as createThemeSnapshot } from './index-Dp6i5TSv.js';
3
3
  import { b as CompilationSnapshot, C as ColorDefinition } from './context-ruu2x_jR.js';
4
4
  export { e as defaultTheme } from './context-ruu2x_jR.js';
5
5
 
package/dist/editor.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  CSS_ENTRY_CANDIDATES,
3
3
  UTILITY_VALUE_SPACES,
4
4
  enumerateClassNames
5
- } from "./chunk-YLB6FGIG.mjs";
5
+ } from "./chunk-NXJZX6KI.mjs";
6
6
  import {
7
7
  isSourceFile
8
8
  } from "./chunk-3HRMFZGE.mjs";
@@ -27,7 +27,7 @@ import {
27
27
  parseUtility,
28
28
  severityForCode,
29
29
  warningCode
30
- } from "./chunk-6DAHUFNU.mjs";
30
+ } from "./chunk-ZI5ZYNSU.mjs";
31
31
  import {
32
32
  computeDarkStop,
33
33
  defaultTheme,
@@ -38,7 +38,7 @@ import {
38
38
  oklabToLinearSrgb,
39
39
  oklchToOklab,
40
40
  resolverFor
41
- } from "./chunk-5Y7EXXLS.mjs";
41
+ } from "./chunk-KRZL4IDK.mjs";
42
42
 
43
43
  // src/engine/inspector.ts
44
44
  var RESOLUTION_CACHE_CAP = 1e4;
@@ -383,7 +383,7 @@ function createEditorSession(options = {}) {
383
383
  }
384
384
 
385
385
  // src/entries/editor.ts
386
- var version = true ? "0.4.0" : "0.0.0-dev";
386
+ var version = true ? "0.5.0" : "0.0.0-dev";
387
387
  var EDITOR_API_VERSION = 1;
388
388
  var editorCapabilities = Object.freeze([
389
389
  "class-candidates",
@@ -12,11 +12,9 @@ interface FontFace {
12
12
  style: string;
13
13
  /** font-display strategy. */
14
14
  display: string;
15
- /** Unicode subsets — used as a Google Fonts URL hint. */
16
- subset: string;
17
15
  /** Optional unicode-range descriptor emitted into the @font-face (local subsetting). */
18
16
  unicodeRange?: string;
19
- /** Per-face preload override; when undefined the slot-level default applies. */
17
+ /** Whether to emit a preload link for this face's font file. */
20
18
  preload?: boolean;
21
19
  /** Whether the weight was explicitly set by the user (not a default).
22
20
  * Used by refreshFontWeightDefaults() to avoid overriding user intent. */
@@ -38,19 +36,25 @@ interface FontSlot {
38
36
  features: string | null;
39
37
  /** Font variation settings — applied via the font-<slot> utility. */
40
38
  variation: string | null;
41
- /** Slot-level preload default for faces that don't set their own. */
42
- preload: boolean;
43
39
  /** One or more faces — each emits an @font-face for local providers. */
44
40
  faces: FontFace[];
45
- /** User-specified fallback font for metrics-adjusted @font-face. */
46
- metricsFallback?: string;
47
- /** User-specified size-adjust percentage. */
41
+ /**
42
+ * CLS-fallback metrics config from the `metrics:` key. Absent = automatic
43
+ * (from the built-in table when the family is known); `null` = disabled via
44
+ * `metrics: none`; an object overrides the fallback font and/or the numbers.
45
+ */
46
+ metrics?: FontMetricsConfig | null;
47
+ }
48
+ /**
49
+ * Parsed `metrics:` value. `fallback` picks the local font to metric-match.
50
+ * The four override percentages are all-present or all-absent (the parser
51
+ * enforces arity); when absent they are computed from the built-in table.
52
+ */
53
+ interface FontMetricsConfig {
54
+ fallback?: string;
48
55
  sizeAdjust?: number;
49
- /** User-specified ascent-override percentage. */
50
56
  ascent?: number;
51
- /** User-specified descent-override percentage. */
52
57
  descent?: number;
53
- /** User-specified line-gap-override percentage. */
54
58
  lineGap?: number;
55
59
  }
56
60
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { PluginCreator } from 'postcss';
2
2
  export { C as ColorDefinition, a as CompilationContext, b as CompilationSnapshot, F as FluidConfig, T as TextSize, c as Theme, d as createCompilationContext, e as defaultTheme, f as finalizeCompilationContext, r as registerColorNames, g as registerCustomFontFamilies, h as registerCustomTextSizes, i as registerCustomUtility } from './context-ruu2x_jR.js';
3
3
  export { D as DEFAULT_TEXT_SIZES, c as createRi, r as ri, s as safelist } from './safelist-D9-Plqta.js';
4
- import { R as ResolvedTheme, P as ParsedDirective } from './index-BB2HoeMj.js';
5
- export { C as CompilationResult, a as CompiledRule, c as createCompiler } from './index-BB2HoeMj.js';
4
+ import { R as ResolvedTheme, P as ParsedDirective } from './index-Dp6i5TSv.js';
5
+ export { C as CompilationResult, a as CompiledRule, c as createCompiler } from './index-Dp6i5TSv.js';
6
6
 
7
7
  interface RainbowIndexOptions {
8
8
  sources?: string[];
package/dist/index.mjs CHANGED
@@ -3,17 +3,17 @@ import {
3
3
  } from "./chunk-PD4ZXGJ6.mjs";
4
4
  import {
5
5
  postcss_default
6
- } from "./chunk-TLR6RP5L.mjs";
6
+ } from "./chunk-CMB6BHVE.mjs";
7
7
  import {
8
8
  finalizeProjectCompilation,
9
9
  resolveGoogleFonts
10
- } from "./chunk-4SVFFDS2.mjs";
10
+ } from "./chunk-DT5HYIM3.mjs";
11
11
  import {
12
12
  analyzeProjectCSS,
13
13
  createCompiler,
14
14
  extractClassesFromSource,
15
15
  pushWarningsDeduped
16
- } from "./chunk-6DAHUFNU.mjs";
16
+ } from "./chunk-ZI5ZYNSU.mjs";
17
17
  import {
18
18
  DEFAULT_TEXT_SIZES,
19
19
  createCompilationContext,
@@ -25,7 +25,7 @@ import {
25
25
  registerCustomTextSizes,
26
26
  registerCustomUtility,
27
27
  ri
28
- } from "./chunk-5Y7EXXLS.mjs";
28
+ } from "./chunk-KRZL4IDK.mjs";
29
29
 
30
30
  // src/project/index.ts
31
31
  async function compileProject(options) {
package/dist/vite.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  postcss_default
3
- } from "./chunk-TLR6RP5L.mjs";
4
- import "./chunk-4SVFFDS2.mjs";
3
+ } from "./chunk-CMB6BHVE.mjs";
4
+ import "./chunk-DT5HYIM3.mjs";
5
5
  import {
6
6
  isSourceFile
7
7
  } from "./chunk-3HRMFZGE.mjs";
@@ -12,10 +12,10 @@ import {
12
12
  hasRIActivation,
13
13
  isAtRuleBoundary,
14
14
  isAtRuleNameChar
15
- } from "./chunk-6DAHUFNU.mjs";
15
+ } from "./chunk-ZI5ZYNSU.mjs";
16
16
  import {
17
17
  devWarn
18
- } from "./chunk-5Y7EXXLS.mjs";
18
+ } from "./chunk-KRZL4IDK.mjs";
19
19
 
20
20
  // src/integrations/vite.ts
21
21
  import { existsSync } from "fs";
@@ -57,61 +57,33 @@ var KEYWORD_BODY_DIRECTIVES = new Set(
57
57
  var REMOVAL_RE = /!([\w][\w-]*)\s*;/g;
58
58
  var FLUID_KEYWORD_RE = /\b(no-parabolic|parabolic|no-shift|shift)\s*;?/g;
59
59
  var COLOR_FLAG_RE = /(?<=[{;\s]|^)(inline|no-parabolic|parabolic)\s*(?:;|(?=}))/g;
60
- function rewriteTopLevel(body, rewrite) {
61
- if (!body.includes("{")) return rewrite(body);
60
+ function mapTopLevelSpans(body, mapOutside, mapBlock) {
61
+ let brace = body.indexOf("{");
62
+ if (brace === -1) return mapOutside(body);
62
63
  let out = "";
63
64
  let segStart = 0;
64
- let depth = 0;
65
- for (let i = 0; i < body.length; i++) {
66
- const ch = body[i];
67
- if (ch === "{") {
68
- if (depth === 0) {
69
- out += rewrite(body.slice(segStart, i));
70
- segStart = i;
71
- }
72
- depth++;
73
- } else if (ch === "}") {
74
- if (depth > 0) depth--;
75
- if (depth === 0) {
76
- out += body.slice(segStart, i + 1);
77
- segStart = i + 1;
78
- }
79
- }
65
+ while (brace !== -1) {
66
+ out += mapOutside(body.slice(segStart, brace));
67
+ const close = findClosingBrace(body, brace);
68
+ if (close === -1) return out + body.slice(brace);
69
+ out += `{${mapBlock(body.slice(brace + 1, close))}}`;
70
+ segStart = close + 1;
71
+ brace = body.indexOf("{", segStart);
80
72
  }
81
- out += depth === 0 ? rewrite(body.slice(segStart)) : body.slice(segStart);
82
- return out;
73
+ return out + mapOutside(body.slice(segStart));
83
74
  }
75
+ var keepSpan = (span) => span;
84
76
  function rewriteColorOptionFlag(_match, keyword) {
85
77
  if (keyword === "inline") return "--ri-inline: true;";
86
78
  const negated = keyword.startsWith("no-");
87
79
  return `--ri-${negated ? keyword.slice(3) : keyword}: ${negated ? "false" : "true"};`;
88
80
  }
89
81
  function rewriteColorOptionFlags(body) {
90
- if (!body.includes("{")) return body;
91
- let out = "";
92
- let segStart = 0;
93
- let depth = 0;
94
- let blockStart = -1;
95
- for (let i = 0; i < body.length; i++) {
96
- const ch = body[i];
97
- if (ch === "{") {
98
- if (depth === 0) {
99
- out += body.slice(segStart, i + 1);
100
- blockStart = i + 1;
101
- }
102
- depth++;
103
- } else if (ch === "}") {
104
- if (depth > 0) depth--;
105
- if (depth === 0 && blockStart !== -1) {
106
- out += body.slice(blockStart, i).replace(COLOR_FLAG_RE, rewriteColorOptionFlag);
107
- out += "}";
108
- segStart = i + 1;
109
- blockStart = -1;
110
- }
111
- }
112
- }
113
- out += body.slice(segStart);
114
- return out;
82
+ return mapTopLevelSpans(
83
+ body,
84
+ keepSpan,
85
+ (interior) => interior.replace(COLOR_FLAG_RE, rewriteColorOptionFlag)
86
+ );
115
87
  }
116
88
  function rewriteDirectiveBodies(code) {
117
89
  let out = "";
@@ -146,17 +118,21 @@ function rewriteDirectiveBodies(code) {
146
118
  const close = findClosingBrace(code, braceIdx);
147
119
  const bodyStart = braceIdx + 1;
148
120
  const bodyEnd = close === -1 ? code.length : close;
149
- let rewritten = rewriteTopLevel(code.slice(bodyStart, bodyEnd), (span) => {
150
- let s = span;
151
- if (removals) s = s.replace(REMOVAL_RE, "--ri-rm: $1;");
152
- if (keywords) {
153
- s = s.replace(FLUID_KEYWORD_RE, (_, kw) => {
154
- const negated = kw.startsWith("no-");
155
- return `--ri-${negated ? kw.slice(3) : kw}: ${negated ? "false" : "true"};`;
156
- });
157
- }
158
- return s;
159
- });
121
+ let rewritten = mapTopLevelSpans(
122
+ code.slice(bodyStart, bodyEnd),
123
+ (span) => {
124
+ let s = span;
125
+ if (removals) s = s.replace(REMOVAL_RE, "--ri-rm: $1;");
126
+ if (keywords) {
127
+ s = s.replace(FLUID_KEYWORD_RE, (_, kw) => {
128
+ const negated = kw.startsWith("no-");
129
+ return `--ri-${negated ? kw.slice(3) : kw}: ${negated ? "false" : "true"};`;
130
+ });
131
+ }
132
+ return s;
133
+ },
134
+ keepSpan
135
+ );
160
136
  if (name === "color") rewritten = rewriteColorOptionFlags(rewritten);
161
137
  out += code.slice(last, bodyStart) + rewritten;
162
138
  last = bodyEnd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rainbowindex",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "CSS-first system for building and maintaining consistent user interfaces",
5
5
  "keywords": [
6
6
  "css",
@@ -97,6 +97,7 @@
97
97
  },
98
98
  "devDependencies": {
99
99
  "@biomejs/biome": "^2.4.16",
100
+ "@capsizecss/metrics": "^4.2.0",
100
101
  "@types/node": "^25.9.1",
101
102
  "@vitest/coverage-v8": "^4.1.0",
102
103
  "postcss": "^8.5.8",