rainbowindex 0.2.2 → 0.4.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.
@@ -1,18 +1,14 @@
1
1
  import {
2
- collectProjectClasses,
3
- finalizeProjectCompilation,
4
- resolveGoogleFonts,
5
- validateGlobPattern
6
- } from "./chunk-FHATRQMN.mjs";
2
+ compileScannedProject
3
+ } from "./chunk-4SVFFDS2.mjs";
7
4
  import {
8
5
  APPLY_ALIASES,
9
- DEFAULT_PROPERTY_GROUP,
10
6
  DIRECTIVE_NAMES_SET,
11
7
  MAX_DIRECTIVE_INPUT_SIZE,
12
- PROPERTY_GROUPS,
13
8
  RI_IMPORT_SPECIFIER_ALTERNATION,
14
- analyzeProjectCSS,
9
+ applyVariantWrappers,
15
10
  compileCSSFunctions,
11
+ computeSortKey,
16
12
  expandVariantGroups,
17
13
  forEachApplyClass,
18
14
  getCustomUtility,
@@ -22,17 +18,18 @@ import {
22
18
  parseUtility,
23
19
  pushWarningsDeduped,
24
20
  resolveUtilityDeclarations,
25
- resolveVariant
26
- } from "./chunk-W6XIBM4M.mjs";
21
+ resolveVariant,
22
+ splitSelectorList
23
+ } from "./chunk-6DAHUFNU.mjs";
27
24
 
28
25
  // src/integrations/postcss/index.ts
29
- import postcss from "postcss";
26
+ import postcss2 from "postcss";
30
27
  import { isAbsolute } from "path";
31
28
 
32
29
  // src/integrations/postcss/apply.ts
30
+ import postcss from "postcss";
33
31
  function makeDeclGroup(r) {
34
- const firstProp = r.declarations[0]?.property ?? "";
35
- const sortKey = PROPERTY_GROUPS[firstProp] ?? DEFAULT_PROPERTY_GROUP;
32
+ const sortKey = computeSortKey(r.declarations[0]?.property ?? "");
36
33
  return {
37
34
  sortKey,
38
35
  decls: r.declarations.map((d) => ({ decl: d, important: r.important }))
@@ -46,11 +43,8 @@ function flattenGroups(groups) {
46
43
  }
47
44
  var MAX_APPLY_DEPTH = 5;
48
45
  var MAX_APPLY_CLASSES = 500;
49
- function applySource(node, source) {
50
- if (source && typeof source === "object" && "source" in source) {
51
- const sourceNode = source;
52
- if (sourceNode.source) node.source = sourceNode.source;
53
- }
46
+ function applySource(node, from) {
47
+ if (from.source) node.source = from.source;
54
48
  return node;
55
49
  }
56
50
  var GROUP_VARIANT_RE = /^\.group(.+) &$/;
@@ -70,22 +64,6 @@ function findGroupAncestor(startRule, groupRoots) {
70
64
  }
71
65
  return null;
72
66
  }
73
- function splitSelectorList(selector) {
74
- const results = [];
75
- let depth = 0;
76
- let start = 0;
77
- for (let i = 0; i < selector.length; i++) {
78
- const ch = selector[i];
79
- if (ch === "(" || ch === "[") depth++;
80
- else if (ch === ")" || ch === "]") depth--;
81
- else if (ch === "," && depth === 0) {
82
- results.push(selector.slice(start, i).trim());
83
- start = i + 1;
84
- }
85
- }
86
- results.push(selector.slice(start).trim());
87
- return results.filter(Boolean);
88
- }
89
67
  function composeNestedSelectors(parts) {
90
68
  if (parts.length === 0) return "";
91
69
  let branches = splitSelectorList(parts[0]);
@@ -116,18 +94,7 @@ function resolveFullNestingSelector(rule) {
116
94
  }
117
95
  return composeNestedSelectors(parts);
118
96
  }
119
- function findDocRoot(node) {
120
- let current = node;
121
- while (current.parent) {
122
- current = current.parent;
123
- }
124
- return current;
125
- }
126
- function processApply(root, theme, warnings, postcss2) {
127
- if (!postcss2) {
128
- warnings.push("[RI-1006] @apply requires postcss but it was not provided.");
129
- return;
130
- }
97
+ function processApply(root, theme, warnings) {
131
98
  for (const alias of APPLY_ALIASES) {
132
99
  root.walkAtRules(alias, (atRule) => {
133
100
  atRule.name = "apply";
@@ -159,7 +126,6 @@ function processApply(root, theme, warnings, postcss2) {
159
126
  theme,
160
127
  warnings,
161
128
  customVariantMap,
162
- postcss2,
163
129
  groupRoots,
164
130
  checkedApplyRoots
165
131
  );
@@ -178,7 +144,7 @@ function processApply(root, theme, warnings, postcss2) {
178
144
  }
179
145
  }
180
146
  }
181
- function expandApply(atRule, classNames, theme, warnings, customVariantMap, postcss2, groupRoots, checkedApplyRoots) {
147
+ function expandApply(atRule, classNames, theme, warnings, customVariantMap, groupRoots, checkedApplyRoots) {
182
148
  const parentRule = atRule.parent;
183
149
  if (parentRule?.type !== "rule") {
184
150
  warnings.push("[RI-1006] @apply must be used inside a CSS rule, not at the top level.");
@@ -245,7 +211,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
245
211
  }
246
212
  for (const { decl, important } of flattenGroups(baseGroups)) {
247
213
  const node = applySource(
248
- postcss2.decl({
214
+ postcss.decl({
249
215
  prop: decl.property,
250
216
  value: decl.value,
251
217
  important
@@ -255,15 +221,15 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
255
221
  atRule.before(node);
256
222
  }
257
223
  for (const { block, important } of baseNestedBlocks) {
258
- atRule.before(buildNestedBlockNode(block, postcss2, atRule, important));
224
+ atRule.before(buildNestedBlockNode(block, atRule, important));
259
225
  }
260
226
  let insertAfter = parentRule;
261
227
  for (const [nestedSel, groups] of nestedGroups) {
262
228
  const sel = nestedSel.replaceAll("&", baseSelector);
263
- const rule = applySource(postcss2.rule({ selector: sel }), parentRule);
229
+ const rule = applySource(postcss.rule({ selector: sel }), parentRule);
264
230
  for (const { decl, important } of flattenGroups(groups)) {
265
231
  rule.append(
266
- applySource(postcss2.decl({ prop: decl.property, value: decl.value, important }), atRule)
232
+ applySource(postcss.decl({ prop: decl.property, value: decl.value, important }), atRule)
267
233
  );
268
234
  }
269
235
  parentContainer.insertAfter(insertAfter, rule);
@@ -284,7 +250,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
284
250
  };
285
251
  let docRootMemo = null;
286
252
  const docRoot = () => {
287
- if (docRootMemo === null) docRootMemo = findDocRoot(parentContainer);
253
+ if (docRootMemo === null) docRootMemo = parentContainer.root();
288
254
  return docRootMemo;
289
255
  };
290
256
  for (const [key, bucket] of variantBuckets) {
@@ -313,7 +279,6 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
313
279
  effectiveBase,
314
280
  groupVariantInfo.rewrittenWrappers,
315
281
  bucketDecls,
316
- postcss2,
317
282
  atRule,
318
283
  bucket.nestedSelector,
319
284
  bucket.nestedBlocks
@@ -326,7 +291,6 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
326
291
  fullNestingSelector(),
327
292
  bucket.wrappers,
328
293
  bucketDecls,
329
- postcss2,
330
294
  atRule,
331
295
  bucket.nestedSelector,
332
296
  bucket.nestedBlocks
@@ -339,7 +303,6 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, post
339
303
  baseSelector,
340
304
  bucket.wrappers,
341
305
  bucketDecls,
342
- postcss2,
343
306
  atRule,
344
307
  bucket.nestedSelector,
345
308
  bucket.nestedBlocks
@@ -451,60 +414,41 @@ function variantKey(wrappers, nestedSelector) {
451
414
  if (nestedSelector) key += `\0N${nestedSelector}`;
452
415
  return key;
453
416
  }
454
- function buildNestedBlockNode(block, postcss2, sourceNode, important) {
417
+ function buildNestedBlockNode(block, sourceNode, important) {
455
418
  let node;
456
419
  if (block.selector.charCodeAt(0) === 64) {
457
420
  const spaceIdx = block.selector.indexOf(" ");
458
421
  const name = spaceIdx === -1 ? block.selector.slice(1) : block.selector.slice(1, spaceIdx);
459
422
  const params = spaceIdx === -1 ? "" : block.selector.slice(spaceIdx + 1);
460
- node = applySource(postcss2.atRule({ name, params }), sourceNode);
423
+ node = applySource(postcss.atRule({ name, params }), sourceNode);
461
424
  } else {
462
- node = applySource(postcss2.rule({ selector: block.selector }), sourceNode);
425
+ node = applySource(postcss.rule({ selector: block.selector }), sourceNode);
463
426
  }
464
427
  for (const d of block.declarations) {
465
428
  node.append(
466
- applySource(postcss2.decl({ prop: d.property, value: d.value, important }), sourceNode)
429
+ applySource(postcss.decl({ prop: d.property, value: d.value, important }), sourceNode)
467
430
  );
468
431
  }
469
432
  for (const child of block.nested) {
470
- node.append(buildNestedBlockNode(child, postcss2, sourceNode, important));
433
+ node.append(buildNestedBlockNode(child, sourceNode, important));
471
434
  }
472
435
  return node;
473
436
  }
474
- function buildVariantNode(baseSelector, wrappers, decls, postcss2, sourceNode, nestedSelector, nestedBlocks) {
437
+ function buildVariantNode(baseSelector, wrappers, decls, sourceNode, nestedSelector, nestedBlocks) {
475
438
  if (decls.length === 0 && nestedBlocks.length === 0) return null;
476
- let selector = baseSelector;
477
- const atRules = [];
478
- let startingStyle = false;
479
- for (const w of wrappers) {
480
- if (w.selectorSuffix) {
481
- const branches = splitSelectorList(selector);
482
- const suffix = w.selectorSuffix;
483
- if (w.replaceAmpersand) {
484
- selector = branches.map((b) => suffix.replace(/&/g, b)).join(", ");
485
- } else {
486
- selector = branches.map((b) => b + suffix).join(", ");
487
- }
488
- }
489
- if (w.atRule) {
490
- atRules.push(w.atRule);
491
- }
492
- if (w.startingStyle) {
493
- startingStyle = true;
494
- }
495
- }
496
- const rule = applySource(postcss2.rule({ selector }), sourceNode);
439
+ const { selector, atRules, startingStyle } = applyVariantWrappers(baseSelector, wrappers);
440
+ const rule = applySource(postcss.rule({ selector }), sourceNode);
497
441
  let declTarget = rule;
498
442
  if (nestedSelector) {
499
- declTarget = applySource(postcss2.rule({ selector: nestedSelector }), sourceNode);
443
+ declTarget = applySource(postcss.rule({ selector: nestedSelector }), sourceNode);
500
444
  rule.append(declTarget);
501
445
  }
502
446
  if (startingStyle) {
503
- const startingAtRule = applySource(postcss2.atRule({ name: "starting-style" }), sourceNode);
447
+ const startingAtRule = applySource(postcss.atRule({ name: "starting-style" }), sourceNode);
504
448
  for (const { decl, important } of decls) {
505
449
  startingAtRule.append(
506
450
  applySource(
507
- postcss2.decl({ prop: decl.property, value: decl.value, important }),
451
+ postcss.decl({ prop: decl.property, value: decl.value, important }),
508
452
  sourceNode
509
453
  )
510
454
  );
@@ -514,14 +458,14 @@ function buildVariantNode(baseSelector, wrappers, decls, postcss2, sourceNode, n
514
458
  for (const { decl, important } of decls) {
515
459
  declTarget.append(
516
460
  applySource(
517
- postcss2.decl({ prop: decl.property, value: decl.value, important }),
461
+ postcss.decl({ prop: decl.property, value: decl.value, important }),
518
462
  sourceNode
519
463
  )
520
464
  );
521
465
  }
522
466
  }
523
467
  for (const { block, important } of nestedBlocks) {
524
- declTarget.append(buildNestedBlockNode(block, postcss2, sourceNode, important));
468
+ declTarget.append(buildNestedBlockNode(block, sourceNode, important));
525
469
  }
526
470
  if (atRules.length === 0) return rule;
527
471
  let node = rule;
@@ -530,7 +474,7 @@ function buildVariantNode(baseSelector, wrappers, decls, postcss2, sourceNode, n
530
474
  const spaceIdx = atRuleStr.indexOf(" ");
531
475
  const name = spaceIdx === -1 ? atRuleStr.slice(1) : atRuleStr.slice(1, spaceIdx);
532
476
  const params = spaceIdx === -1 ? "" : atRuleStr.slice(spaceIdx + 1);
533
- const wrapper = applySource(postcss2.atRule({ name, params }), sourceNode);
477
+ const wrapper = applySource(postcss.atRule({ name, params }), sourceNode);
534
478
  wrapper.append(node);
535
479
  node = wrapper;
536
480
  }
@@ -603,47 +547,24 @@ var rainbowindex = (options = {}) => {
603
547
  if (!hasActivation) {
604
548
  return;
605
549
  }
606
- const analysis = analyzeProjectCSS(rawCSS);
607
- const compilationWarnings = analysis.warnings;
608
- const warningSeen = analysis.warningSeen;
609
- const theme = analysis.theme;
610
- const sourceOverrides = [];
611
- if (options.sources) {
612
- for (const s of options.sources) {
613
- const err = validateGlobPattern(s);
614
- if (err) {
615
- pushWarningsDeduped(compilationWarnings, [`[RI-1015] ${err}`], warningSeen);
616
- continue;
617
- }
618
- sourceOverrides.push({ pattern: s, negated: false, inline: false });
619
- }
620
- }
621
- const scannedClasses = await collectProjectClasses(
622
- theme.sources,
623
- sourceOverrides,
624
- cwd,
625
- compilationWarnings,
626
- warningSeen
627
- );
628
- const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
629
- const compiled = await finalizeProjectCompilation({
550
+ const { compiled, warningSeen } = await compileScannedProject({
630
551
  css: rawCSS,
631
- classNames: scannedClasses,
632
- analysis,
633
- // Shared resolver: same fetch-timeout + RI-1213 policy as the CLI and
634
- // compileProject, so all surfaces emit identical bytes on slow networks.
635
- resolveFonts: resolveGoogleFonts
552
+ cwd,
553
+ surfacePatterns: options.sources,
554
+ onInvalidPattern: (err) => `[RI-1015] ${err}`
636
555
  });
556
+ const compilationWarnings = compiled.warnings;
557
+ const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
637
558
  stripRIDirectiveNodes(root);
638
559
  if (compiled.sections.length > 0) {
639
- const generatedRoot = postcss.parse(compiled.sections.join("\n\n"), { from });
560
+ const generatedRoot = postcss2.parse(compiled.sections.join("\n\n"), { from });
640
561
  root.prepend(generatedRoot.nodes);
641
562
  }
642
563
  const slotWarnings = [];
643
564
  warnStandaloneSlots(root, slotWarnings);
644
565
  pushWarningsDeduped(compilationWarnings, slotWarnings, warningSeen);
645
566
  const applyWarnings = [];
646
- processApply(root, compiled.theme, applyWarnings, postcss);
567
+ processApply(root, compiled.theme, applyWarnings);
647
568
  pushWarningsDeduped(compilationWarnings, applyWarnings, warningSeen);
648
569
  const cssFnWarnings = [];
649
570
  processCSSFunctions(root, compiled.theme, cssFnWarnings);
@@ -0,0 +1,244 @@
1
+ import {
2
+ ANIMATION_STATIC_NAMES,
3
+ BACKGROUND_STATIC_NAMES,
4
+ BORDER_STATIC_NAMES,
5
+ EFFECTS_STATIC_NAMES,
6
+ LAYOUT_STATIC_NAMES,
7
+ PREFIX_DISPATCH,
8
+ ROOT_GROUPS,
9
+ ROUNDED_CORNER_NAMES,
10
+ ROUNDED_SIDE_NAMES,
11
+ SPACING_SAMPLES,
12
+ STATIC_UTILITIES,
13
+ SVG_STATIC_NAMES,
14
+ TYPOGRAPHY_STATIC_NAMES,
15
+ codepointCompare,
16
+ parseUtility,
17
+ resolveUtilityDeclarations
18
+ } from "./chunk-6DAHUFNU.mjs";
19
+ import {
20
+ SPECIAL_COLORS
21
+ } from "./chunk-5Y7EXXLS.mjs";
22
+
23
+ // src/project/css-entry.ts
24
+ var CSS_ENTRY_CANDIDATES = Object.freeze([
25
+ "src/index.css",
26
+ "src/style.css",
27
+ "src/styles.css",
28
+ "src/app.css",
29
+ "src/global.css",
30
+ "index.css",
31
+ "style.css",
32
+ "styles.css",
33
+ "app.css",
34
+ "global.css"
35
+ ]);
36
+
37
+ // src/utilities/enumerate.ts
38
+ var COLOR_STOPS = Object.freeze([
39
+ "50",
40
+ "100",
41
+ "150",
42
+ "200",
43
+ "250",
44
+ "300",
45
+ "350",
46
+ "400",
47
+ "450",
48
+ "500",
49
+ "550",
50
+ "600",
51
+ "650",
52
+ "700",
53
+ "750",
54
+ "800",
55
+ "850",
56
+ "900",
57
+ "950"
58
+ ]);
59
+ var FRACTION_SAMPLES = Object.freeze([
60
+ "1/2",
61
+ "1/3",
62
+ "2/3",
63
+ "1/4",
64
+ "3/4",
65
+ "1/5",
66
+ "2/5",
67
+ "3/5",
68
+ "4/5",
69
+ "1/6",
70
+ "5/6"
71
+ ]);
72
+ var INT_SAMPLES = Object.freeze(["0", "1", "2", "3", "4", "6", "8", "10", "12"]);
73
+ var PERCENT_SAMPLES = Object.freeze([
74
+ "0",
75
+ "5",
76
+ "10",
77
+ "15",
78
+ "20",
79
+ "25",
80
+ "30",
81
+ "40",
82
+ "50",
83
+ "60",
84
+ "70",
85
+ "75",
86
+ "80",
87
+ "90",
88
+ "95",
89
+ "100"
90
+ ]);
91
+ var BUILTIN_FONT_SLOTS = Object.freeze(["sans", "serif", "mono"]);
92
+ var DURATION_SAMPLES = Object.freeze(["75", "100", "150", "200", "300", "500", "700", "1000"]);
93
+ var ROUNDED_SIDES = Object.freeze([
94
+ ...ROUNDED_SIDE_NAMES,
95
+ ...ROUNDED_CORNER_NAMES
96
+ ]);
97
+ function buildValueSpaces() {
98
+ const table = /* @__PURE__ */ new Map();
99
+ for (const { roots, spec } of ROOT_GROUPS) {
100
+ for (const root of roots) {
101
+ let entry = table.get(root);
102
+ if (!entry) {
103
+ entry = { kinds: /* @__PURE__ */ new Set(), keywords: /* @__PURE__ */ new Set() };
104
+ table.set(root, entry);
105
+ }
106
+ for (const kind of spec.kinds) entry.kinds.add(kind);
107
+ for (const kw of spec.keywords ?? []) entry.keywords.add(kw);
108
+ }
109
+ }
110
+ const out = /* @__PURE__ */ new Map();
111
+ for (const [root, entry] of table) {
112
+ out.set(root, {
113
+ kinds: Object.freeze([...entry.kinds]),
114
+ keywords: entry.keywords.size > 0 ? Object.freeze([...entry.keywords]) : void 0
115
+ });
116
+ }
117
+ return out;
118
+ }
119
+ var UTILITY_VALUE_SPACES = buildValueSpaces();
120
+ function candidateValues(kind, theme) {
121
+ switch (kind) {
122
+ case "color": {
123
+ const out = [];
124
+ for (const name of Object.keys(theme.colors)) {
125
+ for (const stop of COLOR_STOPS) out.push(`${name}-${stop}`);
126
+ }
127
+ return out;
128
+ }
129
+ case "special-color":
130
+ return Object.keys(SPECIAL_COLORS);
131
+ case "spacing":
132
+ return [...SPACING_SAMPLES];
133
+ case "fraction":
134
+ return [...FRACTION_SAMPLES];
135
+ case "text-size":
136
+ return Object.keys(theme.text);
137
+ case "fluid-text-size":
138
+ return Object.keys(theme.text).map((size) => `fluid-${size}`);
139
+ case "font-slot":
140
+ return [.../* @__PURE__ */ new Set([...BUILTIN_FONT_SLOTS, ...theme.fonts.map((slot) => slot.slot)])];
141
+ case "weight":
142
+ return Object.keys(theme.weights);
143
+ case "rounded":
144
+ return Object.keys(theme.rounded);
145
+ case "rounded-side": {
146
+ const tokens = Object.keys(theme.rounded);
147
+ const out = [];
148
+ for (const side of ROUNDED_SIDES) {
149
+ out.push(side);
150
+ for (const token of tokens) out.push(`${side}-${token}`);
151
+ }
152
+ return out;
153
+ }
154
+ case "shadow":
155
+ return Object.keys(theme.shadows);
156
+ case "z":
157
+ return Object.keys(theme.z);
158
+ case "ease":
159
+ return Object.keys(theme.easing);
160
+ case "blur":
161
+ return Object.keys(theme.blur);
162
+ case "animation":
163
+ return Object.keys(theme.animations);
164
+ case "leading":
165
+ return Object.keys(theme.leading);
166
+ case "tracking":
167
+ return Object.keys(theme.tracking);
168
+ case "opacity":
169
+ return Object.keys(theme.opacity);
170
+ case "duration":
171
+ return [.../* @__PURE__ */ new Set([...Object.keys(theme.duration), ...DURATION_SAMPLES])];
172
+ case "breakpoint":
173
+ return Object.keys(theme.breakpoints);
174
+ case "int":
175
+ return [...INT_SAMPLES];
176
+ case "percent":
177
+ return [...PERCENT_SAMPLES];
178
+ case "keywords":
179
+ return [];
180
+ }
181
+ }
182
+ function enumerateClassNames(theme) {
183
+ const seen = /* @__PURE__ */ new Set();
184
+ const classes = [];
185
+ const templates = [];
186
+ const probeWarnings = [];
187
+ const resolves = (name) => resolveUtilityDeclarations(parseUtility(name), theme, probeWarnings) !== null;
188
+ const emit = (name, kind, root) => {
189
+ if (seen.has(name)) return;
190
+ if (!resolves(name)) return;
191
+ seen.add(name);
192
+ classes.push({ name, kind, root });
193
+ };
194
+ for (const name of STATIC_UTILITIES) emit(name, "static", null);
195
+ for (const names of [
196
+ ANIMATION_STATIC_NAMES,
197
+ BORDER_STATIC_NAMES,
198
+ BACKGROUND_STATIC_NAMES,
199
+ EFFECTS_STATIC_NAMES,
200
+ LAYOUT_STATIC_NAMES,
201
+ SVG_STATIC_NAMES,
202
+ TYPOGRAPHY_STATIC_NAMES
203
+ ]) {
204
+ for (const name of names) emit(name, "static", null);
205
+ }
206
+ for (const custom of theme.customUtilities) {
207
+ if (custom.functional) {
208
+ templates.push({ root: custom.name, kind: "custom", example: `${custom.name}-value` });
209
+ } else {
210
+ emit(custom.name, "custom", null);
211
+ }
212
+ }
213
+ for (const root of PREFIX_DISPATCH.keys()) {
214
+ const spec = UTILITY_VALUE_SPACES.get(root);
215
+ if (!spec) continue;
216
+ let spacingHit = false;
217
+ let intHit = false;
218
+ for (const kind of spec.kinds) {
219
+ for (const value of candidateValues(kind, theme)) {
220
+ const name = `${root}-${value}`;
221
+ if (seen.has(name)) continue;
222
+ if (!resolves(name)) continue;
223
+ seen.add(name);
224
+ classes.push({ name, kind, root });
225
+ if (kind === "spacing") spacingHit = true;
226
+ if (kind === "int") intHit = true;
227
+ }
228
+ }
229
+ for (const keyword of spec.keywords ?? []) {
230
+ emit(`${root}-${keyword}`, "keywords", root);
231
+ }
232
+ if (spacingHit) templates.push({ root, kind: "spacing", example: `${root}-4` });
233
+ else if (intHit) templates.push({ root, kind: "number", example: `${root}-2` });
234
+ }
235
+ classes.sort((a, b) => codepointCompare(a.name, b.name));
236
+ templates.sort((a, b) => codepointCompare(a.root, b.root));
237
+ return { classes, templates };
238
+ }
239
+
240
+ export {
241
+ CSS_ENTRY_CANDIDATES,
242
+ UTILITY_VALUE_SPACES,
243
+ enumerateClassNames
244
+ };