rainbowindex 0.6.0 → 0.7.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +543 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +219 -65
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -4
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-KSNYSR3C.mjs → chunk-FJOZJIKB.mjs} +2499 -329
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-3LWJTLOJ.mjs → chunk-RC6DDE4L.mjs} +23 -15
  13. package/dist/chunk-TQJYVQPE.mjs +217 -0
  14. package/dist/chunk-W756NVYI.mjs +33 -0
  15. package/dist/chunk-WBESS2ZD.mjs +598 -0
  16. package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
  17. package/dist/{chunk-6U4IOFOS.mjs → chunk-XQGSG2HK.mjs} +199 -555
  18. package/dist/cli.mjs +1077 -123
  19. package/dist/{index-DSgpB6bS.d.ts → context-DcBtnnan.d.ts} +47 -103
  20. package/dist/editor.d.ts +71 -433
  21. package/dist/editor.mjs +51 -368
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-4Kyaq3IZ.d.ts → imports-C9esHd5Q.d.ts} +78 -84
  25. package/dist/index-CNqdL5U0.d.ts +56 -0
  26. package/dist/index-Czx-EUwh.d.ts +138 -0
  27. package/dist/index-DW8YSxTz.d.ts +104 -0
  28. package/dist/index.d.ts +46 -5
  29. package/dist/index.mjs +33 -9
  30. package/dist/oxlint.d.ts +21 -3
  31. package/dist/oxlint.mjs +19 -1
  32. package/dist/recipe.d.ts +111 -0
  33. package/dist/recipe.mjs +71 -0
  34. package/dist/safelist-CH3_PywB.d.ts +43 -0
  35. package/dist/session-CMaskdB7.d.ts +543 -0
  36. package/dist/tailwind.css +644 -0
  37. package/dist/theme-CIZiGlce.d.ts +115 -0
  38. package/dist/vite.d.ts +10 -1
  39. package/dist/vite.mjs +266 -118
  40. package/package.json +27 -5
  41. package/dist/chunk-WK6S4HTC.mjs +0 -1921
  42. package/dist/chunk-ZR7XJMUN.mjs +0 -251
  43. package/dist/safelist-CGCtF-Fr.d.ts +0 -96
package/dist/oxlint.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ import { L as LintRule } from './theme-CIZiGlce.js';
2
+ export { T as ThemeLintOptions, n as noConflictingClassesRule, l as noUnknownClassRule } from './theme-CIZiGlce.js';
3
+ import './session-CMaskdB7.js';
4
+ import './imports-C9esHd5Q.js';
5
+ import './index-Czx-EUwh.js';
6
+ import './context-DcBtnnan.js';
7
+
1
8
  /**
2
9
  * Oxlint plugin for Rainbow Index projects.
3
10
  *
@@ -9,17 +16,28 @@
9
16
  * export default defineConfig({
10
17
  * lint: {
11
18
  * jsPlugins: [{ name: "rainbowindex", specifier: "rainbowindex/oxlint" }],
12
- * rules: { "rainbowindex/prefer-ri": "error" },
19
+ * rules: {
20
+ * "rainbowindex/prefer-ri": "error",
21
+ * "rainbowindex/no-unknown-class": "error",
22
+ * "rainbowindex/no-conflicting-classes": "warn",
23
+ * },
13
24
  * },
14
25
  * });
15
26
  * ```
16
27
  *
28
+ * `prefer-ri` is local to this file: it reads one import statement and needs
29
+ * no theme. The other two come from `lint/rules.ts`, shared with the ESLint
30
+ * entry, because a rule that says what a class *means* has to answer the same
31
+ * way in both linters — it reads the project's compiled theme through the
32
+ * editor API, and there is one right answer.
33
+ *
17
34
  * The types below describe only the slice of the Oxlint rule API this plugin
18
35
  * touches. They are declared here rather than imported from `@oxlint/plugins`
19
36
  * because that package is a transitive dependency of Oxlint that a consumer
20
37
  * cannot resolve, and its `definePlugin` / `defineRule` helpers are identity
21
38
  * functions with no runtime behavior to reuse.
22
39
  */
40
+
23
41
  interface ImportDeclarationNode {
24
42
  source: {
25
43
  value: string;
@@ -47,9 +65,9 @@ interface OxlintPlugin {
47
65
  meta: {
48
66
  name: string;
49
67
  };
50
- rules: Record<string, OxlintRule>;
68
+ rules: Record<string, OxlintRule | LintRule>;
51
69
  }
52
70
  declare const preferRiRule: OxlintRule;
53
71
  declare const plugin: OxlintPlugin;
54
72
 
55
- export { type OxlintPlugin, type OxlintRule, plugin as default, plugin, preferRiRule };
73
+ export { LintRule, type OxlintPlugin, type OxlintRule, plugin as default, plugin, preferRiRule };
package/dist/oxlint.mjs CHANGED
@@ -1,3 +1,15 @@
1
+ import {
2
+ noConflictingClassesRule,
3
+ noUnknownClassRule
4
+ } from "./chunk-TQJYVQPE.mjs";
5
+ import "./chunk-PZDVDEZJ.mjs";
6
+ import "./chunk-WBESS2ZD.mjs";
7
+ import "./chunk-W756NVYI.mjs";
8
+ import "./chunk-X66Z2YHT.mjs";
9
+ import "./chunk-FJOZJIKB.mjs";
10
+ import "./chunk-L56IRO7A.mjs";
11
+ import "./chunk-XQGSG2HK.mjs";
12
+
1
13
  // src/integrations/oxlint.ts
2
14
  var REPLACED_PACKAGES = {
3
15
  clsx: "composes conditional classes",
@@ -28,11 +40,17 @@ var preferRiRule = {
28
40
  };
29
41
  var plugin = {
30
42
  meta: { name: "rainbowindex" },
31
- rules: { "prefer-ri": preferRiRule }
43
+ rules: {
44
+ "prefer-ri": preferRiRule,
45
+ "no-unknown-class": noUnknownClassRule,
46
+ "no-conflicting-classes": noConflictingClassesRule
47
+ }
32
48
  };
33
49
  var oxlint_default = plugin;
34
50
  export {
35
51
  oxlint_default as default,
52
+ noConflictingClassesRule,
53
+ noUnknownClassRule,
36
54
  plugin,
37
55
  preferRiRule
38
56
  };
@@ -0,0 +1,111 @@
1
+ import { C as ClassInput } from './index-CNqdL5U0.js';
2
+ import './context-DcBtnnan.js';
3
+
4
+ /**
5
+ * `recipe()` — a typed variant layer whose output is ordinary class names.
6
+ *
7
+ * A component library needs to say "a button has a size and a tone, and these
8
+ * are the only valid values" without giving up plain CSS. `cva` and `tv` do
9
+ * that, and both merge with a table of Tailwind utilities: every utility this
10
+ * project defines is unknown to them, a `@utility` never resolves, and a theme
11
+ * change never reaches them. `recipe()` is the same idea over `ri()`, so the
12
+ * conflict resolution is the one the compiler emitted.
13
+ *
14
+ * The result is a string. Nothing is styled at runtime, nothing is injected,
15
+ * and the classes live in the config where the scanner already reads them —
16
+ * `recipe` sits beside `cva` and `tv` in the variant-helper list, so a build
17
+ * finds them with no extra configuration.
18
+ *
19
+ * ```ts
20
+ * const button = recipe({
21
+ * base: "inline-flex items-center rounded-card font-medium",
22
+ * variants: {
23
+ * tone: { solid: "bg-brand-600 text-white", quiet: "text-brand-700" },
24
+ * size: { sm: "h-8 px-3 text-sm", md: "h-10 px-4" },
25
+ * block: { true: "w-full" },
26
+ * },
27
+ * compoundVariants: [{ tone: "solid", size: "sm", class: "shadow-sm" }],
28
+ * defaultVariants: { tone: "solid", size: "md" },
29
+ * });
30
+ *
31
+ * button({ size: "sm" }); // → "inline-flex … bg-brand-600 … h-8 px-3 text-sm shadow-sm"
32
+ * button({ size: "xl" }); // ✗ Type error: "xl" is not a size
33
+ * ```
34
+ */
35
+
36
+ /** One variant option's classes — anything `ri()` accepts. */
37
+ type RecipeClassValue = ClassInput;
38
+ /** One variant group: option name → the classes that option adds. */
39
+ type VariantGroup = Record<string, RecipeClassValue>;
40
+ /** All of a recipe's variant groups, by name. */
41
+ type VariantShape = Record<string, VariantGroup>;
42
+ type OptionNames<Group> = Extract<keyof Group, string>;
43
+ /**
44
+ * A group whose only options are `true`/`false` takes a boolean, not the
45
+ * strings. The tuple brackets keep the check non-distributive, so a group with
46
+ * both options is still recognized as the boolean one.
47
+ */
48
+ type IsBooleanGroup<Group> = [OptionNames<Group>] extends ["true" | "false"] ? true : false;
49
+ /** The value a caller may pass for one variant group. */
50
+ type VariantValue<Group> = IsBooleanGroup<Group> extends true ? boolean : OptionNames<Group>;
51
+ /**
52
+ * The props a recipe accepts, one optional key per variant group.
53
+ *
54
+ * `null` is explicit opt-out: it suppresses the group's default rather than
55
+ * falling back to it, which is the only way to say "no size at all" for a
56
+ * recipe that defines a default size.
57
+ */
58
+ type VariantProps<V extends VariantShape> = {
59
+ [K in keyof V]?: VariantValue<V[K]> | null;
60
+ };
61
+ /** One compound rule: classes that apply when several variants line up. */
62
+ type CompoundVariant<V extends VariantShape> = {
63
+ [K in keyof V]?: VariantValue<V[K]> | ReadonlyArray<VariantValue<V[K]>>;
64
+ } & {
65
+ class?: RecipeClassValue;
66
+ /** Accepted alongside `class`, as `cva` and `tv` do. Both are applied. */
67
+ className?: RecipeClassValue;
68
+ };
69
+ interface RecipeConfig<V extends VariantShape> {
70
+ /** Classes every call starts from. */
71
+ base?: RecipeClassValue;
72
+ /** The variant groups. Their declaration order is their merge order. */
73
+ variants?: V;
74
+ /** Classes that apply only when several variants take given values. */
75
+ compoundVariants?: ReadonlyArray<CompoundVariant<V>>;
76
+ /** The value each group takes when the caller passes none. */
77
+ defaultVariants?: VariantProps<V>;
78
+ }
79
+ /** What a recipe is called with: its variants, plus per-call overrides. */
80
+ type RecipeProps<V extends VariantShape> = VariantProps<V> & {
81
+ class?: RecipeClassValue;
82
+ className?: RecipeClassValue;
83
+ };
84
+ interface Recipe<V extends VariantShape> {
85
+ (props?: RecipeProps<V>): string;
86
+ /** The config object this recipe was built from, for composing another. */
87
+ readonly config: RecipeConfig<V>;
88
+ }
89
+ interface RecipeOptions {
90
+ /**
91
+ * The merge function. Defaults to the module-level `ri()`.
92
+ *
93
+ * Pass `createRi(snapshot)` where the global one cannot be trusted: a
94
+ * multi-tenant SSR process compiling more than one theme, or a client
95
+ * bundle that imports the bound `ri` its generated snapshot exports.
96
+ */
97
+ merge?: (...inputs: ClassInput[]) => string;
98
+ }
99
+ /** The props type of an existing recipe, for a component's own props. */
100
+ type PropsOf<R> = R extends Recipe<infer V> ? RecipeProps<V> : never;
101
+ /**
102
+ * Build a recipe: a function from variant props to a merged class string.
103
+ *
104
+ * The merge order is base, then each variant group in declaration order, then
105
+ * the compound rules in array order, then the caller's own `class`/`className`
106
+ * — so a per-call override always wins, and a compound rule always beats the
107
+ * plain variants it is refining.
108
+ */
109
+ declare function recipe<V extends VariantShape>(config: RecipeConfig<V>, options?: RecipeOptions): Recipe<V>;
110
+
111
+ export { ClassInput, type CompoundVariant, type PropsOf, type Recipe, type RecipeClassValue, type RecipeConfig, type RecipeOptions, type RecipeProps, type VariantGroup, type VariantProps, type VariantShape, type VariantValue, recipe };
@@ -0,0 +1,71 @@
1
+ import {
2
+ devWarn,
3
+ ri
4
+ } from "./chunk-XQGSG2HK.mjs";
5
+
6
+ // src/recipe.ts
7
+ var COMPOUND_CLASS_KEYS = /* @__PURE__ */ new Set(["class", "className"]);
8
+ function compoundApplies(compound, selection) {
9
+ for (const key of Object.keys(compound)) {
10
+ if (COMPOUND_CLASS_KEYS.has(key)) continue;
11
+ const expected = compound[key];
12
+ if (expected === void 0) continue;
13
+ const actual = selection[key];
14
+ if (actual === void 0) return false;
15
+ if (Array.isArray(expected)) {
16
+ if (!expected.some((value) => String(value) === actual)) return false;
17
+ } else if (String(expected) !== actual) {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ function warnUnknownCompoundKeys(config) {
24
+ const groups = new Set(Object.keys(config.variants ?? {}));
25
+ const unknown = /* @__PURE__ */ new Set();
26
+ for (const compound of config.compoundVariants ?? []) {
27
+ for (const key of Object.keys(compound)) {
28
+ if (COMPOUND_CLASS_KEYS.has(key)) continue;
29
+ if (!groups.has(key)) unknown.add(key);
30
+ }
31
+ }
32
+ if (unknown.size === 0) return;
33
+ const named = [...unknown].map((key) => `"${key}"`).join(", ");
34
+ const known = [...groups].map((key) => `"${key}"`).join(", ") || "none";
35
+ devWarn(
36
+ `[RI-2013] recipe() compoundVariants name ${named}, which ${unknown.size === 1 ? "is not a variant group" : "are not variant groups"} of this recipe (defined: ${known}). Those rules can never apply. Check the spelling, or add the group to \`variants\`.`
37
+ );
38
+ }
39
+ function recipe(config, options = {}) {
40
+ const merge = options.merge ?? ri;
41
+ warnUnknownCompoundKeys(config);
42
+ const build = (props) => {
43
+ const parts = [config.base];
44
+ const selection = {};
45
+ const variants = config.variants;
46
+ if (variants) {
47
+ const supplied = props;
48
+ const defaults = config.defaultVariants;
49
+ for (const name of Object.keys(variants)) {
50
+ const fromProps = supplied?.[name];
51
+ const chosen = fromProps === void 0 ? defaults?.[name] : fromProps;
52
+ if (chosen === null || chosen === void 0) continue;
53
+ const option = String(chosen);
54
+ selection[name] = option;
55
+ const group = variants[name];
56
+ if (Object.hasOwn(group, option)) parts.push(group[option]);
57
+ }
58
+ }
59
+ for (const compound of config.compoundVariants ?? []) {
60
+ const entry = compound;
61
+ if (!compoundApplies(entry, selection)) continue;
62
+ parts.push(entry.class, entry.className);
63
+ }
64
+ if (props) parts.push(props.class, props.className);
65
+ return merge(...parts);
66
+ };
67
+ return Object.assign(build, { config });
68
+ }
69
+ export {
70
+ recipe
71
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `safelist()` — declare utility classes that must be emitted regardless of
3
+ * whether the consumer's source files reference them directly.
4
+ *
5
+ * At runtime this is a plain identity-join: pass any number of strings (and
6
+ * falsy values, which are filtered) and receive a single space-joined string
7
+ * suitable for `className`. The function performs no global registration, has
8
+ * no side effects, and is tree-shake-safe.
9
+ *
10
+ * The build-time meaning comes from the scanner: when the source-file
11
+ * extractor encounters a `safelist(...)` call, it extracts every literal
12
+ * string argument as a class declaration — so the classes get emitted in the
13
+ * final CSS even though the consumer's source never names them literally.
14
+ *
15
+ * Primary use case is component libraries that ship classNames inside their
16
+ * bundled code (e.g. a curated icon set whose strokes are described by
17
+ * utility classes). The library wraps its declarations in `safelist(...)`,
18
+ * the consumer's setup points the scanner at the library's `dist/`, and the
19
+ * classes flow through unchanged. The Vite plugin auto-discovers libraries
20
+ * that opt in via a `rainbowindex.safelistSources` field in their
21
+ * `package.json`, so consumers typically don't have to add `@source` lines
22
+ * by hand.
23
+ *
24
+ * const ICON_BASE = safelist("stroke-cap-round", "stroke-join-round");
25
+ * const SidebarLeft = defineIcon({
26
+ * primitives: SIDEBAR,
27
+ * className: safelist(ICON_BASE, "-scale-x-100"),
28
+ * });
29
+ *
30
+ * Scanner contract:
31
+ * - Only STATIC string literals at the call site are extracted. Values
32
+ * passed through variables (`safelist(ICON_BASE, ...)`) won't be re-read
33
+ * at the outer call site, but the original `safelist("stroke-cap-round",
34
+ * ...)` that produced `ICON_BASE` is itself extracted, so the classes are
35
+ * still covered.
36
+ * - Template literals with no `${…}` interpolation are extracted; templates
37
+ * with interpolation are skipped.
38
+ * - Falsy arguments are dropped at runtime so conditional fragments compose
39
+ * naturally: `safelist("flex", side === "left" && "flex-row-reverse")`.
40
+ */
41
+ declare function safelist(...parts: ReadonlyArray<string | false | null | undefined>): string;
42
+
43
+ export { safelist as s };