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.
- package/CHANGELOG.md +543 -0
- package/LICENSE +16 -17
- package/NOTICE.md +106 -0
- package/README.md +219 -65
- package/dist/browser.d.ts +4 -2
- package/dist/browser.mjs +12 -4
- package/dist/chunk-2T7V5XLK.mjs +912 -0
- package/dist/chunk-6OORICWF.mjs +16 -0
- package/dist/{chunk-KSNYSR3C.mjs → chunk-FJOZJIKB.mjs} +2499 -329
- package/dist/chunk-L56IRO7A.mjs +491 -0
- package/dist/chunk-PZDVDEZJ.mjs +196 -0
- package/dist/{chunk-3LWJTLOJ.mjs → chunk-RC6DDE4L.mjs} +23 -15
- package/dist/chunk-TQJYVQPE.mjs +217 -0
- package/dist/chunk-W756NVYI.mjs +33 -0
- package/dist/chunk-WBESS2ZD.mjs +598 -0
- package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
- package/dist/{chunk-6U4IOFOS.mjs → chunk-XQGSG2HK.mjs} +199 -555
- package/dist/cli.mjs +1077 -123
- package/dist/{index-DSgpB6bS.d.ts → context-DcBtnnan.d.ts} +47 -103
- package/dist/editor.d.ts +71 -433
- package/dist/editor.mjs +51 -368
- package/dist/eslint.d.ts +16 -0
- package/dist/eslint.mjs +32 -0
- package/dist/{index-4Kyaq3IZ.d.ts → imports-C9esHd5Q.d.ts} +78 -84
- package/dist/index-CNqdL5U0.d.ts +56 -0
- package/dist/index-Czx-EUwh.d.ts +138 -0
- package/dist/index-DW8YSxTz.d.ts +104 -0
- package/dist/index.d.ts +46 -5
- package/dist/index.mjs +33 -9
- package/dist/oxlint.d.ts +21 -3
- package/dist/oxlint.mjs +19 -1
- package/dist/recipe.d.ts +111 -0
- package/dist/recipe.mjs +71 -0
- package/dist/safelist-CH3_PywB.d.ts +43 -0
- package/dist/session-CMaskdB7.d.ts +543 -0
- package/dist/tailwind.css +644 -0
- package/dist/theme-CIZiGlce.d.ts +115 -0
- package/dist/vite.d.ts +10 -1
- package/dist/vite.mjs +266 -118
- package/package.json +27 -5
- package/dist/chunk-WK6S4HTC.mjs +0 -1921
- package/dist/chunk-ZR7XJMUN.mjs +0 -251
- 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: {
|
|
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: {
|
|
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
|
};
|
package/dist/recipe.d.ts
ADDED
|
@@ -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 };
|
package/dist/recipe.mjs
ADDED
|
@@ -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 };
|