rainbowindex 0.0.0 → 0.2.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.
@@ -0,0 +1,671 @@
1
+ import {
2
+ APPLY_ALIASES,
3
+ DEFAULT_PROPERTY_GROUP,
4
+ DIRECTIVE_NAMES_SET,
5
+ MAX_DIRECTIVE_INPUT_SIZE,
6
+ PROPERTY_GROUPS,
7
+ RI_IMPORT_SPECIFIER_ALTERNATION,
8
+ analyzeProjectCSS,
9
+ collectProjectClasses,
10
+ compileCSSFunctions,
11
+ expandVariantGroups,
12
+ finalizeProjectCompilation,
13
+ forEachApplyClass,
14
+ getCustomUtility,
15
+ hasApplyLikeDirective,
16
+ hasCSSFunctions,
17
+ hasRIActivation,
18
+ parseUtility,
19
+ pushWarningsDeduped,
20
+ resolveGoogleFonts,
21
+ resolveUtilityDeclarations,
22
+ resolveVariant,
23
+ validateGlobPattern
24
+ } from "./chunk-RPXZ3O6R.mjs";
25
+
26
+ // src/integrations/postcss/index.ts
27
+ import postcss from "postcss";
28
+ import { isAbsolute } from "path";
29
+
30
+ // src/integrations/postcss/apply.ts
31
+ function makeDeclGroup(r) {
32
+ const firstProp = r.declarations[0]?.property ?? "";
33
+ const sortKey = PROPERTY_GROUPS[firstProp] ?? DEFAULT_PROPERTY_GROUP;
34
+ return {
35
+ sortKey,
36
+ decls: r.declarations.map((d) => ({ decl: d, important: r.important }))
37
+ };
38
+ }
39
+ function flattenGroups(groups) {
40
+ const sorted = [...groups].sort((a, b) => a.sortKey - b.sortKey);
41
+ const out = [];
42
+ for (const g of sorted) out.push(...g.decls);
43
+ return out;
44
+ }
45
+ var MAX_APPLY_DEPTH = 5;
46
+ var MAX_APPLY_CLASSES = 500;
47
+ function applySource(node, source) {
48
+ if (source && typeof source === "object" && "source" in source) {
49
+ const sourceNode = source;
50
+ if (sourceNode.source) node.source = sourceNode.source;
51
+ }
52
+ return node;
53
+ }
54
+ var GROUP_VARIANT_RE = /^\.group(.+) &$/;
55
+ function findGroupAncestor(startRule, groupRoots) {
56
+ if (groupRoots.has(startRule)) {
57
+ return { groupRootSelector: resolveFullNestingSelector(startRule) };
58
+ }
59
+ let current = startRule.parent;
60
+ while (current) {
61
+ if (current.type === "rule") {
62
+ const rule = current;
63
+ if (groupRoots.has(rule)) {
64
+ return { groupRootSelector: resolveFullNestingSelector(rule) };
65
+ }
66
+ }
67
+ current = current.parent;
68
+ }
69
+ return null;
70
+ }
71
+ function splitSelectorList(selector) {
72
+ const results = [];
73
+ let depth = 0;
74
+ let start = 0;
75
+ for (let i = 0; i < selector.length; i++) {
76
+ const ch = selector[i];
77
+ if (ch === "(" || ch === "[") depth++;
78
+ else if (ch === ")" || ch === "]") depth--;
79
+ else if (ch === "," && depth === 0) {
80
+ results.push(selector.slice(start, i).trim());
81
+ start = i + 1;
82
+ }
83
+ }
84
+ results.push(selector.slice(start).trim());
85
+ return results.filter(Boolean);
86
+ }
87
+ function composeNestedSelectors(parts) {
88
+ if (parts.length === 0) return "";
89
+ let branches = splitSelectorList(parts[0]);
90
+ for (let i = 1; i < parts.length; i++) {
91
+ const childBranches = splitSelectorList(parts[i]);
92
+ const next = [];
93
+ for (const parent of branches) {
94
+ for (const child of childBranches) {
95
+ if (child.includes("&")) {
96
+ next.push(child.replace(/&/g, parent));
97
+ } else {
98
+ next.push(`${parent} ${child}`);
99
+ }
100
+ }
101
+ }
102
+ branches = next;
103
+ }
104
+ return branches.join(", ");
105
+ }
106
+ function resolveFullNestingSelector(rule) {
107
+ const parts = [rule.selector];
108
+ let current = rule.parent;
109
+ while (current) {
110
+ if (current.type === "rule") {
111
+ parts.unshift(current.selector);
112
+ }
113
+ current = current.parent;
114
+ }
115
+ return composeNestedSelectors(parts);
116
+ }
117
+ function findDocRoot(node) {
118
+ let current = node;
119
+ while (current.parent) {
120
+ current = current.parent;
121
+ }
122
+ return current;
123
+ }
124
+ function processApply(root, theme, warnings, postcss2) {
125
+ if (!postcss2) {
126
+ warnings.push("[RI-1006] @apply requires postcss but it was not provided.");
127
+ return;
128
+ }
129
+ for (const alias of APPLY_ALIASES) {
130
+ root.walkAtRules(alias, (atRule) => {
131
+ atRule.name = "apply";
132
+ });
133
+ }
134
+ const customVariantMap = new Map(theme.customVariants.map((cv) => [cv.name, cv]));
135
+ const checkedApplyRoots = /* @__PURE__ */ new Set();
136
+ for (let depth = 0; depth < MAX_APPLY_DEPTH; depth++) {
137
+ const applyNodes = [];
138
+ const classListByNode = /* @__PURE__ */ new Map();
139
+ const groupRoots = /* @__PURE__ */ new Set();
140
+ root.walkAtRules("apply", (atRule) => {
141
+ applyNodes.push(atRule);
142
+ const params = expandVariantGroups(atRule.params, warnings);
143
+ const classNames = params.trim().split(/\s+/).filter(Boolean);
144
+ classListByNode.set(atRule, classNames);
145
+ if (classNames.includes("group")) {
146
+ const parent = atRule.parent;
147
+ if (parent && parent.type === "rule") {
148
+ groupRoots.add(parent);
149
+ }
150
+ }
151
+ });
152
+ if (applyNodes.length === 0) break;
153
+ for (const atRule of applyNodes) {
154
+ expandApply(
155
+ atRule,
156
+ classListByNode.get(atRule) ?? [],
157
+ theme,
158
+ warnings,
159
+ customVariantMap,
160
+ postcss2,
161
+ groupRoots,
162
+ checkedApplyRoots
163
+ );
164
+ }
165
+ }
166
+ const remaining = [];
167
+ root.walkAtRules("apply", (atRule) => {
168
+ remaining.push(atRule);
169
+ });
170
+ if (remaining.length > 0) {
171
+ warnings.push(
172
+ `[RI-1006] @apply recursion depth limit reached (${MAX_APPLY_DEPTH}). ${remaining.length} remaining @apply directive(s) will not be expanded.`
173
+ );
174
+ for (const atRule of remaining) {
175
+ atRule.remove();
176
+ }
177
+ }
178
+ }
179
+ function expandApply(atRule, classNames, theme, warnings, customVariantMap, postcss2, groupRoots, checkedApplyRoots) {
180
+ const parentRule = atRule.parent;
181
+ if (parentRule?.type !== "rule") {
182
+ warnings.push("[RI-1006] @apply must be used inside a CSS rule, not at the top level.");
183
+ atRule.remove();
184
+ return;
185
+ }
186
+ const baseSelector = parentRule.selector;
187
+ const parentContainer = parentRule.parent;
188
+ if (!parentContainer) {
189
+ atRule.remove();
190
+ return;
191
+ }
192
+ if (classNames.length > MAX_APPLY_CLASSES) {
193
+ warnings.push(
194
+ `[RI-1006] @apply contains ${classNames.length} classes (limit: ${MAX_APPLY_CLASSES}). Split into multiple @apply directives or reduce class count.`
195
+ );
196
+ atRule.remove();
197
+ return;
198
+ }
199
+ const resolved = [];
200
+ for (const className of classNames) {
201
+ const r = resolveClassName(className, theme, warnings, customVariantMap, checkedApplyRoots);
202
+ if (r) resolved.push(r);
203
+ }
204
+ const baseGroups = [];
205
+ const baseNestedBlocks = [];
206
+ const nestedGroups = /* @__PURE__ */ new Map();
207
+ const variantBuckets = /* @__PURE__ */ new Map();
208
+ for (const r of resolved) {
209
+ const hasVariants = r.variants.length > 0;
210
+ const hasNested = !!r.nestedSelector;
211
+ const group = makeDeclGroup(r);
212
+ if (!hasVariants && !hasNested) {
213
+ baseGroups.push(group);
214
+ if (r.nested) {
215
+ for (const block of r.nested) baseNestedBlocks.push({ block, important: r.important });
216
+ }
217
+ } else if (!hasVariants && hasNested) {
218
+ const key = r.nestedSelector;
219
+ if (!key) continue;
220
+ let list = nestedGroups.get(key);
221
+ if (!list) {
222
+ list = [];
223
+ nestedGroups.set(key, list);
224
+ }
225
+ list.push(group);
226
+ } else {
227
+ const key = variantKey(r.variants, r.nestedSelector);
228
+ let bucket = variantBuckets.get(key);
229
+ if (!bucket) {
230
+ bucket = {
231
+ wrappers: r.variants,
232
+ nestedSelector: r.nestedSelector,
233
+ groups: [],
234
+ nestedBlocks: []
235
+ };
236
+ variantBuckets.set(key, bucket);
237
+ }
238
+ bucket.groups.push(group);
239
+ if (r.nested) {
240
+ for (const block of r.nested) bucket.nestedBlocks.push({ block, important: r.important });
241
+ }
242
+ }
243
+ }
244
+ for (const { decl, important } of flattenGroups(baseGroups)) {
245
+ const node = applySource(
246
+ postcss2.decl({
247
+ prop: decl.property,
248
+ value: decl.value,
249
+ important
250
+ }),
251
+ atRule
252
+ );
253
+ atRule.before(node);
254
+ }
255
+ for (const { block, important } of baseNestedBlocks) {
256
+ atRule.before(buildNestedBlockNode(block, postcss2, atRule, important));
257
+ }
258
+ let insertAfter = parentRule;
259
+ for (const [nestedSel, groups] of nestedGroups) {
260
+ const sel = nestedSel.replaceAll("&", baseSelector);
261
+ const rule = applySource(postcss2.rule({ selector: sel }), parentRule);
262
+ for (const { decl, important } of flattenGroups(groups)) {
263
+ rule.append(
264
+ applySource(postcss2.decl({ prop: decl.property, value: decl.value, important }), atRule)
265
+ );
266
+ }
267
+ parentContainer.insertAfter(insertAfter, rule);
268
+ insertAfter = rule;
269
+ }
270
+ const groupInfoByKey = /* @__PURE__ */ new Map();
271
+ let hasGroupVariants = false;
272
+ for (const [key, bucket] of variantBuckets) {
273
+ const info = resolveGroupVariantInfo(bucket.wrappers, parentRule, groupRoots);
274
+ groupInfoByKey.set(key, info);
275
+ if (info) hasGroupVariants = true;
276
+ }
277
+ let fullSelectorMemo = null;
278
+ const fullNestingSelector = () => {
279
+ if (fullSelectorMemo === null)
280
+ fullSelectorMemo = resolveFullNestingSelector(parentRule);
281
+ return fullSelectorMemo;
282
+ };
283
+ let docRootMemo = null;
284
+ const docRoot = () => {
285
+ if (docRootMemo === null) docRootMemo = findDocRoot(parentContainer);
286
+ return docRootMemo;
287
+ };
288
+ for (const [key, bucket] of variantBuckets) {
289
+ const groupVariantInfo = groupInfoByKey.get(key) ?? null;
290
+ const bucketDecls = flattenGroups(bucket.groups);
291
+ if (groupVariantInfo) {
292
+ const fullSelector = fullNestingSelector();
293
+ const { groupRootSelector } = groupVariantInfo;
294
+ const rootBranches = splitSelectorList(groupRootSelector);
295
+ const fullBranches = splitSelectorList(fullSelector);
296
+ const baseBranches = [];
297
+ for (const fb of fullBranches) {
298
+ let stripped = false;
299
+ for (const rb of rootBranches) {
300
+ if (fb.startsWith(rb)) {
301
+ const after = fb.slice(rb.length);
302
+ baseBranches.push(after.startsWith(" ") ? after.slice(1) : after);
303
+ stripped = true;
304
+ break;
305
+ }
306
+ }
307
+ if (!stripped) baseBranches.push(fb);
308
+ }
309
+ const effectiveBase = [...new Set(baseBranches)].join(", ");
310
+ const node = buildVariantNode(
311
+ effectiveBase,
312
+ groupVariantInfo.rewrittenWrappers,
313
+ bucketDecls,
314
+ postcss2,
315
+ atRule,
316
+ bucket.nestedSelector,
317
+ bucket.nestedBlocks
318
+ );
319
+ if (node) {
320
+ docRoot().append(node);
321
+ }
322
+ } else if (hasGroupVariants) {
323
+ const node = buildVariantNode(
324
+ fullNestingSelector(),
325
+ bucket.wrappers,
326
+ bucketDecls,
327
+ postcss2,
328
+ atRule,
329
+ bucket.nestedSelector,
330
+ bucket.nestedBlocks
331
+ );
332
+ if (node) {
333
+ docRoot().append(node);
334
+ }
335
+ } else {
336
+ const node = buildVariantNode(
337
+ baseSelector,
338
+ bucket.wrappers,
339
+ bucketDecls,
340
+ postcss2,
341
+ atRule,
342
+ bucket.nestedSelector,
343
+ bucket.nestedBlocks
344
+ );
345
+ if (node) {
346
+ parentContainer.insertAfter(insertAfter, node);
347
+ insertAfter = node;
348
+ }
349
+ }
350
+ }
351
+ atRule.remove();
352
+ }
353
+ function resolveGroupVariantInfo(wrappers, parentRule, groupRoots) {
354
+ if (groupRoots.size === 0) return null;
355
+ const hasGroupVariant = wrappers.some(
356
+ (w) => w.selectorSuffix && w.replaceAmpersand && GROUP_VARIANT_RE.test(w.selectorSuffix)
357
+ );
358
+ if (!hasGroupVariant) return null;
359
+ const groupInfo = findGroupAncestor(parentRule, groupRoots);
360
+ if (!groupInfo) return null;
361
+ const rewrittenWrappers = wrappers.map((w) => {
362
+ if (!w.selectorSuffix || !w.replaceAmpersand) return w;
363
+ const match = w.selectorSuffix.match(GROUP_VARIANT_RE);
364
+ if (!match) return w;
365
+ const pseudo = match[1];
366
+ const rootBranches = splitSelectorList(groupInfo.groupRootSelector);
367
+ const newSuffix = rootBranches.map((b) => `${b}${pseudo} &`).join(", ");
368
+ return { ...w, selectorSuffix: newSuffix };
369
+ });
370
+ return { rewrittenWrappers, groupRootSelector: groupInfo.groupRootSelector };
371
+ }
372
+ var MARKER_CLASSES = /* @__PURE__ */ new Set(["group"]);
373
+ var MAX_CUSTOM_APPLY_DEPTH = 5;
374
+ function findCustomUtility(utility, value, theme) {
375
+ const target = value === null ? utility : `${utility}-${value}`;
376
+ const cu = getCustomUtility(theme, target);
377
+ return cu && !cu.functional ? cu : void 0;
378
+ }
379
+ function checkCustomApplyWarnings(cu, theme, warnings, visiting = /* @__PURE__ */ new Set()) {
380
+ if (!hasApplyLikeDirective(cu.body)) return;
381
+ const innerClasses = [];
382
+ forEachApplyClass(cu.body, (cls) => innerClasses.push(cls));
383
+ if (innerClasses.length === 0) return;
384
+ if (visiting.has(cu.name)) {
385
+ warnings.push(
386
+ `[RI-1005] Circular @apply detected in @utility "${cu.name}" \u2014 skipping inner @apply.`
387
+ );
388
+ return;
389
+ }
390
+ if (visiting.size >= MAX_CUSTOM_APPLY_DEPTH) {
391
+ warnings.push(
392
+ `[RI-1005] @apply recursion depth limit reached in @utility "${cu.name}" \u2014 skipping inner @apply.`
393
+ );
394
+ return;
395
+ }
396
+ visiting.add(cu.name);
397
+ for (const innerClass of innerClasses) {
398
+ const parsed = parseUtility(innerClass);
399
+ const innerCu = findCustomUtility(parsed.utility, parsed.value, theme);
400
+ if (innerCu) checkCustomApplyWarnings(innerCu, theme, warnings, visiting);
401
+ }
402
+ visiting.delete(cu.name);
403
+ }
404
+ function resolveClassName(className, theme, warnings, customVariantMap, checkedApplyRoots) {
405
+ const parsed = parseUtility(className);
406
+ if (parsed.variants.length === 0 && MARKER_CLASSES.has(parsed.utility) && parsed.value === null) {
407
+ return null;
408
+ }
409
+ const utilResult = resolveUtilityDeclarations(parsed, theme, warnings);
410
+ if (!utilResult) {
411
+ warnings.push(`[RI-1005] Unknown utility "${className}" in @apply \u2014 skipping.`);
412
+ return null;
413
+ }
414
+ const declarations = [...utilResult.declarations];
415
+ const cu = findCustomUtility(parsed.utility, parsed.value, theme);
416
+ if (cu && !checkedApplyRoots.has(cu.name)) {
417
+ checkedApplyRoots.add(cu.name);
418
+ checkCustomApplyWarnings(cu, theme, warnings);
419
+ }
420
+ const variantWrappers = [];
421
+ for (const variant of parsed.variants) {
422
+ const wrapper = resolveVariant(variant, theme, customVariantMap);
423
+ if (!wrapper) {
424
+ warnings.push(
425
+ `[RI-1004] Unknown variant "${variant}" in @apply "${className}" \u2014 skipping. Check spelling, or register the variant with \`@custom ${variant} { ... }\`. Built-in variants: hover, focus, dark, sm/md/lg/xl, data-[attr], arbitrary [selector].`
426
+ );
427
+ return null;
428
+ }
429
+ variantWrappers.push(wrapper);
430
+ }
431
+ return {
432
+ declarations,
433
+ important: parsed.important,
434
+ nestedSelector: utilResult.nestedSelector,
435
+ nested: utilResult.nested,
436
+ variants: variantWrappers
437
+ };
438
+ }
439
+ function variantKey(wrappers, nestedSelector) {
440
+ const parts = [];
441
+ for (const w of wrappers) {
442
+ const sel = w.selectorSuffix ?? "";
443
+ const at = w.atRule ?? "";
444
+ parts.push(
445
+ `${sel.length}:${sel}${at.length}:${at}${w.startingStyle ? "1" : "0"}${w.replaceAmpersand ? "1" : "0"}`
446
+ );
447
+ }
448
+ let key = parts.join("\0");
449
+ if (nestedSelector) key += `\0N${nestedSelector}`;
450
+ return key;
451
+ }
452
+ function buildNestedBlockNode(block, postcss2, sourceNode, important) {
453
+ let node;
454
+ if (block.selector.charCodeAt(0) === 64) {
455
+ const spaceIdx = block.selector.indexOf(" ");
456
+ const name = spaceIdx === -1 ? block.selector.slice(1) : block.selector.slice(1, spaceIdx);
457
+ const params = spaceIdx === -1 ? "" : block.selector.slice(spaceIdx + 1);
458
+ node = applySource(postcss2.atRule({ name, params }), sourceNode);
459
+ } else {
460
+ node = applySource(postcss2.rule({ selector: block.selector }), sourceNode);
461
+ }
462
+ for (const d of block.declarations) {
463
+ node.append(
464
+ applySource(postcss2.decl({ prop: d.property, value: d.value, important }), sourceNode)
465
+ );
466
+ }
467
+ for (const child of block.nested) {
468
+ node.append(buildNestedBlockNode(child, postcss2, sourceNode, important));
469
+ }
470
+ return node;
471
+ }
472
+ function buildVariantNode(baseSelector, wrappers, decls, postcss2, sourceNode, nestedSelector, nestedBlocks) {
473
+ if (decls.length === 0 && nestedBlocks.length === 0) return null;
474
+ let selector = baseSelector;
475
+ const atRules = [];
476
+ let startingStyle = false;
477
+ for (const w of wrappers) {
478
+ if (w.selectorSuffix) {
479
+ const branches = splitSelectorList(selector);
480
+ const suffix = w.selectorSuffix;
481
+ if (w.replaceAmpersand) {
482
+ selector = branches.map((b) => suffix.replace(/&/g, b)).join(", ");
483
+ } else {
484
+ selector = branches.map((b) => b + suffix).join(", ");
485
+ }
486
+ }
487
+ if (w.atRule) {
488
+ atRules.push(w.atRule);
489
+ }
490
+ if (w.startingStyle) {
491
+ startingStyle = true;
492
+ }
493
+ }
494
+ const rule = applySource(postcss2.rule({ selector }), sourceNode);
495
+ let declTarget = rule;
496
+ if (nestedSelector) {
497
+ declTarget = applySource(postcss2.rule({ selector: nestedSelector }), sourceNode);
498
+ rule.append(declTarget);
499
+ }
500
+ if (startingStyle) {
501
+ const startingAtRule = applySource(postcss2.atRule({ name: "starting-style" }), sourceNode);
502
+ for (const { decl, important } of decls) {
503
+ startingAtRule.append(
504
+ applySource(
505
+ postcss2.decl({ prop: decl.property, value: decl.value, important }),
506
+ sourceNode
507
+ )
508
+ );
509
+ }
510
+ declTarget.append(startingAtRule);
511
+ } else {
512
+ for (const { decl, important } of decls) {
513
+ declTarget.append(
514
+ applySource(
515
+ postcss2.decl({ prop: decl.property, value: decl.value, important }),
516
+ sourceNode
517
+ )
518
+ );
519
+ }
520
+ }
521
+ for (const { block, important } of nestedBlocks) {
522
+ declTarget.append(buildNestedBlockNode(block, postcss2, sourceNode, important));
523
+ }
524
+ if (atRules.length === 0) return rule;
525
+ let node = rule;
526
+ for (let i = atRules.length - 1; i >= 0; i--) {
527
+ const atRuleStr = atRules[i];
528
+ const spaceIdx = atRuleStr.indexOf(" ");
529
+ const name = spaceIdx === -1 ? atRuleStr.slice(1) : atRuleStr.slice(1, spaceIdx);
530
+ const params = spaceIdx === -1 ? "" : atRuleStr.slice(spaceIdx + 1);
531
+ const wrapper = applySource(postcss2.atRule({ name, params }), sourceNode);
532
+ wrapper.append(node);
533
+ node = wrapper;
534
+ }
535
+ return node;
536
+ }
537
+
538
+ // src/integrations/postcss/index.ts
539
+ var RI_IMPORT_PARAMS_RE = new RegExp(
540
+ `^(?:url\\(\\s*)?["'](?:${RI_IMPORT_SPECIFIER_ALTERNATION})["']\\s*\\)?(?:\\s+.+)?$`,
541
+ "i"
542
+ );
543
+ function isRainbowIndexImport(atRule) {
544
+ if (atRule.name !== "import") return false;
545
+ return RI_IMPORT_PARAMS_RE.test(atRule.params.trim());
546
+ }
547
+ function stripRIDirectiveNodes(root) {
548
+ const toRemove = [];
549
+ for (const node of root.nodes ?? []) {
550
+ if (node.type === "atrule") {
551
+ const atRule = node;
552
+ const isApplyLike = atRule.name === "apply" || atRule.name === "slot" || APPLY_ALIASES.includes(atRule.name);
553
+ if (isRainbowIndexImport(atRule) || DIRECTIVE_NAMES_SET.has(atRule.name) && !isApplyLike) {
554
+ toRemove.push(atRule);
555
+ }
556
+ }
557
+ }
558
+ for (const node of toRemove) {
559
+ node.remove();
560
+ }
561
+ }
562
+ function warnStandaloneSlots(root, warnings) {
563
+ root.walkAtRules("slot", (atRule) => {
564
+ warnings.push(
565
+ '[RI-1037] @slot is only valid inside @custom (e.g. `@custom hocus { &:hover { @slot; } }`). To style a slotted element directly, write `[data-slot="name"] { \u2026 }`.'
566
+ );
567
+ atRule.remove();
568
+ });
569
+ }
570
+ function processCSSFunctions(root, theme, warnings) {
571
+ root.walkDecls((decl) => {
572
+ if (hasCSSFunctions(decl.value)) {
573
+ decl.value = compileCSSFunctions(decl.value, theme, warnings);
574
+ }
575
+ });
576
+ }
577
+ var rainbowindex = (options = {}) => {
578
+ return {
579
+ postcssPlugin: "rainbowindex",
580
+ async Once(root, { result }) {
581
+ try {
582
+ const cwd = options.cwd || process.cwd();
583
+ if (!cwd || typeof cwd !== "string" || cwd.includes("\0")) {
584
+ throw new Error(
585
+ "[RI-0002] options.cwd is invalid (empty, non-string, or contains null bytes)."
586
+ );
587
+ }
588
+ if (!isAbsolute(cwd)) {
589
+ throw new Error(
590
+ `[RI-0002] options.cwd must be an absolute path, got relative path: "${cwd}".`
591
+ );
592
+ }
593
+ const rawCSS = root.toString();
594
+ if (rawCSS.length > MAX_DIRECTIVE_INPUT_SIZE) {
595
+ result.warn(
596
+ `[RI-1019] CSS input exceeds ${MAX_DIRECTIVE_INPUT_SIZE / 1048576} MB limit (${(rawCSS.length / 1048576).toFixed(1)} MB). Skipping rainbowindex processing.`
597
+ );
598
+ return;
599
+ }
600
+ const hasActivation = hasRIActivation(rawCSS);
601
+ if (!hasActivation) {
602
+ return;
603
+ }
604
+ const analysis = analyzeProjectCSS(rawCSS);
605
+ const compilationWarnings = analysis.warnings;
606
+ const warningSeen = analysis.warningSeen;
607
+ const theme = analysis.theme;
608
+ const sourceOverrides = [];
609
+ if (options.sources) {
610
+ for (const s of options.sources) {
611
+ const err = validateGlobPattern(s);
612
+ if (err) {
613
+ pushWarningsDeduped(compilationWarnings, [`[RI-1015] ${err}`], warningSeen);
614
+ continue;
615
+ }
616
+ sourceOverrides.push({ pattern: s, negated: false, inline: false });
617
+ }
618
+ }
619
+ const scannedClasses = await collectProjectClasses(
620
+ theme.sources,
621
+ sourceOverrides,
622
+ cwd,
623
+ compilationWarnings,
624
+ warningSeen
625
+ );
626
+ const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
627
+ const compiled = await finalizeProjectCompilation({
628
+ css: rawCSS,
629
+ classNames: scannedClasses,
630
+ analysis,
631
+ // Shared resolver: same fetch-timeout + RI-1213 policy as the CLI and
632
+ // compileProject, so all surfaces emit identical bytes on slow networks.
633
+ resolveFonts: resolveGoogleFonts
634
+ });
635
+ stripRIDirectiveNodes(root);
636
+ if (compiled.sections.length > 0) {
637
+ const generatedRoot = postcss.parse(compiled.sections.join("\n\n"), { from });
638
+ root.prepend(generatedRoot.nodes);
639
+ }
640
+ const slotWarnings = [];
641
+ warnStandaloneSlots(root, slotWarnings);
642
+ pushWarningsDeduped(compilationWarnings, slotWarnings, warningSeen);
643
+ const applyWarnings = [];
644
+ processApply(root, compiled.theme, applyWarnings, postcss);
645
+ pushWarningsDeduped(compilationWarnings, applyWarnings, warningSeen);
646
+ const cssFnWarnings = [];
647
+ processCSSFunctions(root, compiled.theme, cssFnWarnings);
648
+ pushWarningsDeduped(compilationWarnings, cssFnWarnings, warningSeen);
649
+ for (const warning of compilationWarnings) {
650
+ result.warn(warning);
651
+ }
652
+ } catch (err) {
653
+ const message = err instanceof Error ? err.message : String(err);
654
+ if (message.startsWith("[RI-")) throw err;
655
+ const sourceFile = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<input>";
656
+ const location = err && typeof err === "object" && "line" in err ? `:${err.line}${"column" in err ? `:${err.column}` : ""}` : "";
657
+ const hint = err && typeof err === "object" && "name" in err && err.name === "CssSyntaxError" ? " (CSS syntax error \u2014 check the CSS input near the location above)" : /glob|pattern/i.test(message) ? " (glob/source error \u2014 check your @source patterns and the CWD passed to the plugin)" : "";
658
+ throw new Error(
659
+ `[RI-0001] rainbowindex PostCSS plugin failed at ${sourceFile}${location}: ${message}${hint}`,
660
+ { cause: err }
661
+ );
662
+ }
663
+ }
664
+ };
665
+ };
666
+ rainbowindex.postcss = true;
667
+ var postcss_default = rainbowindex;
668
+
669
+ export {
670
+ postcss_default
671
+ };
@@ -0,0 +1,14 @@
1
+ // src/safelist.ts
2
+ function safelist(...parts) {
3
+ let out = "";
4
+ for (const part of parts) {
5
+ if (!part) continue;
6
+ if (out) out += " ";
7
+ out += part;
8
+ }
9
+ return out;
10
+ }
11
+
12
+ export {
13
+ safelist
14
+ };