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.
package/dist/vite.mjs ADDED
@@ -0,0 +1,320 @@
1
+ import {
2
+ postcss_default
3
+ } from "./chunk-KCSNR2TV.mjs";
4
+ import {
5
+ expandApplyGroups,
6
+ findClosingBrace,
7
+ hasRIActivation,
8
+ isAtRuleBoundary,
9
+ isAtRuleNameChar,
10
+ isSourceFile
11
+ } from "./chunk-RPXZ3O6R.mjs";
12
+ import {
13
+ devWarn
14
+ } from "./chunk-5N4GPK26.mjs";
15
+
16
+ // src/integrations/vite.ts
17
+ import { existsSync } from "fs";
18
+ import { access, readFile, readdir } from "fs/promises";
19
+ import { join, relative, resolve } from "path";
20
+ var CSS_FILE_RE = /\.(?:module\.)?css$/;
21
+ var REMOVAL_BODY_DIRECTIVES = /* @__PURE__ */ new Set([
22
+ "color",
23
+ "text",
24
+ "spacing",
25
+ "breakpoint",
26
+ "rounded",
27
+ "shadow",
28
+ "weight",
29
+ "ease",
30
+ "blur",
31
+ "z",
32
+ "animate",
33
+ "leading",
34
+ "tracking",
35
+ "opacity",
36
+ "duration"
37
+ ]);
38
+ var KEYWORD_BODY_DIRECTIVES = /* @__PURE__ */ new Set(["fluid"]);
39
+ var REMOVAL_RE = /!([\w][\w-]*)\s*;/g;
40
+ var FLUID_KEYWORD_RE = /\b(no-parabolic|parabolic|no-shift|shift)\s*;?/g;
41
+ var COLOR_FLAG_RE = /(?<=[{;\s]|^)(inline|no-parabolic|parabolic)\s*(?:;|(?=}))/g;
42
+ function rewriteTopLevel(body, rewrite) {
43
+ if (!body.includes("{")) return rewrite(body);
44
+ let out = "";
45
+ let segStart = 0;
46
+ let depth = 0;
47
+ for (let i = 0; i < body.length; i++) {
48
+ const ch = body[i];
49
+ if (ch === "{") {
50
+ if (depth === 0) {
51
+ out += rewrite(body.slice(segStart, i));
52
+ segStart = i;
53
+ }
54
+ depth++;
55
+ } else if (ch === "}") {
56
+ if (depth > 0) depth--;
57
+ if (depth === 0) {
58
+ out += body.slice(segStart, i + 1);
59
+ segStart = i + 1;
60
+ }
61
+ }
62
+ }
63
+ out += depth === 0 ? rewrite(body.slice(segStart)) : body.slice(segStart);
64
+ return out;
65
+ }
66
+ function rewriteColorOptionFlag(_match, keyword) {
67
+ if (keyword === "inline") return "--ri-inline: true;";
68
+ const negated = keyword.startsWith("no-");
69
+ return `--ri-${negated ? keyword.slice(3) : keyword}: ${negated ? "false" : "true"};`;
70
+ }
71
+ function rewriteColorOptionFlags(body) {
72
+ if (!body.includes("{")) return body;
73
+ let out = "";
74
+ let segStart = 0;
75
+ let depth = 0;
76
+ let blockStart = -1;
77
+ for (let i = 0; i < body.length; i++) {
78
+ const ch = body[i];
79
+ if (ch === "{") {
80
+ if (depth === 0) {
81
+ out += body.slice(segStart, i + 1);
82
+ blockStart = i + 1;
83
+ }
84
+ depth++;
85
+ } else if (ch === "}") {
86
+ if (depth > 0) depth--;
87
+ if (depth === 0 && blockStart !== -1) {
88
+ out += body.slice(blockStart, i).replace(COLOR_FLAG_RE, rewriteColorOptionFlag);
89
+ out += "}";
90
+ segStart = i + 1;
91
+ blockStart = -1;
92
+ }
93
+ }
94
+ }
95
+ out += body.slice(segStart);
96
+ return out;
97
+ }
98
+ function rewriteDirectiveBodies(code) {
99
+ let out = "";
100
+ let last = 0;
101
+ let i = 0;
102
+ while (i < code.length) {
103
+ const at = code.indexOf("@", i);
104
+ if (at === -1) break;
105
+ if (!isAtRuleBoundary(code, at)) {
106
+ i = at + 1;
107
+ continue;
108
+ }
109
+ let nameEnd = at + 1;
110
+ while (nameEnd < code.length && isAtRuleNameChar(code.charCodeAt(nameEnd))) nameEnd++;
111
+ const name = code.slice(at + 1, nameEnd);
112
+ const removals = REMOVAL_BODY_DIRECTIVES.has(name);
113
+ const keywords = KEYWORD_BODY_DIRECTIVES.has(name);
114
+ if (!removals && !keywords) {
115
+ i = nameEnd;
116
+ continue;
117
+ }
118
+ let braceIdx = nameEnd;
119
+ while (braceIdx < code.length) {
120
+ const ch = code[braceIdx];
121
+ if (ch === "{" || ch === ";" || ch === "}") break;
122
+ braceIdx++;
123
+ }
124
+ if (code[braceIdx] !== "{") {
125
+ i = braceIdx + 1;
126
+ continue;
127
+ }
128
+ const close = findClosingBrace(code, braceIdx);
129
+ const bodyStart = braceIdx + 1;
130
+ const bodyEnd = close === -1 ? code.length : close;
131
+ let rewritten = rewriteTopLevel(code.slice(bodyStart, bodyEnd), (span) => {
132
+ let s = span;
133
+ if (removals) s = s.replace(REMOVAL_RE, "--ri-rm: $1;");
134
+ if (keywords) {
135
+ s = s.replace(FLUID_KEYWORD_RE, (_, kw) => {
136
+ const negated = kw.startsWith("no-");
137
+ return `--ri-${negated ? kw.slice(3) : kw}: ${negated ? "false" : "true"};`;
138
+ });
139
+ }
140
+ return s;
141
+ });
142
+ if (name === "color") rewritten = rewriteColorOptionFlags(rewritten);
143
+ out += code.slice(last, bodyStart) + rewritten;
144
+ last = bodyEnd;
145
+ i = bodyEnd;
146
+ }
147
+ if (last === 0) return code;
148
+ return out + code.slice(last);
149
+ }
150
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
151
+ var POSTCSS_CONFIG_FILES = [
152
+ "postcss.config.js",
153
+ "postcss.config.mjs",
154
+ "postcss.config.ts",
155
+ "postcss.config.cjs"
156
+ ];
157
+ function isIgnorableDirectoryReadError(err) {
158
+ return !!err && typeof err === "object" && "code" in err && (err.code === "ENOENT" || err.code === "ENOTDIR" || err.code === "EACCES" || err.code === "EPERM");
159
+ }
160
+ function hasLocalPostCSSConfig(root) {
161
+ return POSTCSS_CONFIG_FILES.some((name) => existsSync(resolve(root, name)));
162
+ }
163
+ function rainbowindexVite() {
164
+ let root = process.cwd();
165
+ let logger;
166
+ const riCSSFiles = /* @__PURE__ */ new Set();
167
+ const fileVersions = /* @__PURE__ */ new Map();
168
+ let hotUpdateCount = 0;
169
+ const PRUNE_INTERVAL = 50;
170
+ return {
171
+ name: "rainbowindex",
172
+ enforce: "pre",
173
+ async config(config) {
174
+ root = config.root ?? process.cwd();
175
+ if (!hasLocalPostCSSConfig(root)) {
176
+ return {
177
+ css: {
178
+ postcss: {
179
+ plugins: [postcss_default()]
180
+ }
181
+ }
182
+ };
183
+ }
184
+ return {};
185
+ },
186
+ configureServer(server) {
187
+ root = server.config?.root ?? process.cwd();
188
+ logger = {
189
+ info: (msg) => server.config.logger?.info?.(msg, { timestamp: true }),
190
+ warn: (msg) => server.config.logger?.warn?.(msg, { timestamp: true })
191
+ };
192
+ if (hasLocalPostCSSConfig(root)) {
193
+ logger.info("[rainbowindex] Using local PostCSS config \u2014 skipped auto-injection.");
194
+ } else {
195
+ logger.info("[rainbowindex] Injected PostCSS plugin (no local postcss.config.* found).");
196
+ }
197
+ server.httpServer?.once("listening", async () => {
198
+ const cssFiles = [];
199
+ const fileMap = server.moduleGraph.fileToModulesMap;
200
+ if (fileMap) {
201
+ for (const [file] of fileMap) {
202
+ if (CSS_FILE_RE.test(file)) {
203
+ cssFiles.push(file);
204
+ }
205
+ }
206
+ }
207
+ if (cssFiles.length === 0) {
208
+ const diskCSS = await findCSSFilesOnDisk(root);
209
+ cssFiles.push(...diskCSS);
210
+ }
211
+ await Promise.all(cssFiles.map((file) => checkCSSFileAsync(file)));
212
+ if (riCSSFiles.size === 0) {
213
+ logger?.warn(
214
+ `[RI-1602] rainbowindex Vite plugin is registered but no CSS entry with \`@import "rainbowindex"\` was found under ${root}. Create one (e.g. src/index.css) and import it from your app entry, then restart the dev server. Or run \`rainbowindex init\` to wire it up automatically.`
215
+ );
216
+ } else {
217
+ const list = [...riCSSFiles].map((f) => relative(root, f).replaceAll("\\", "/")).join(", ");
218
+ logger?.info(`[rainbowindex] CSS entries: ${list}`);
219
+ }
220
+ });
221
+ },
222
+ transform(code, id) {
223
+ const file = id.split("?")[0];
224
+ if (CSS_FILE_RE.test(file)) {
225
+ fileVersions.set(file, (fileVersions.get(file) ?? 0) + 1);
226
+ if (hasRIActivation(code)) {
227
+ riCSSFiles.add(file);
228
+ let safe = rewriteDirectiveBodies(code);
229
+ const expandWarnings = [];
230
+ safe = expandApplyGroups(safe, expandWarnings);
231
+ for (const w of expandWarnings) {
232
+ (logger?.warn ?? console.warn)(`[rainbowindex] ${w}`);
233
+ }
234
+ return safe !== code ? safe : null;
235
+ }
236
+ riCSSFiles.delete(file);
237
+ }
238
+ return null;
239
+ },
240
+ async handleHotUpdate({ file, server, modules }) {
241
+ if (++hotUpdateCount % PRUNE_INTERVAL === 0) {
242
+ await pruneDeletedFiles();
243
+ }
244
+ if (CSS_FILE_RE.test(file)) {
245
+ await checkCSSFileAsync(file);
246
+ return;
247
+ }
248
+ if (!isSourceFile(file)) return;
249
+ const extraModules = [];
250
+ for (const cssFile of riCSSFiles) {
251
+ const mods = server.moduleGraph.getModulesByFile(cssFile);
252
+ if (mods) {
253
+ for (const mod of mods) {
254
+ if (!modules.includes(mod)) {
255
+ extraModules.push(mod);
256
+ }
257
+ }
258
+ }
259
+ }
260
+ if (extraModules.length > 0) {
261
+ return [...modules, ...extraModules];
262
+ }
263
+ }
264
+ };
265
+ async function pruneDeletedFiles() {
266
+ const tracked = /* @__PURE__ */ new Set([...riCSSFiles, ...fileVersions.keys()]);
267
+ const checks = [...tracked].map(async (file) => {
268
+ try {
269
+ await access(file);
270
+ } catch {
271
+ riCSSFiles.delete(file);
272
+ fileVersions.delete(file);
273
+ }
274
+ });
275
+ await Promise.all(checks);
276
+ }
277
+ async function collectCSSFiles(dir, results) {
278
+ let entries;
279
+ try {
280
+ entries = await readdir(dir, { withFileTypes: true });
281
+ } catch (err) {
282
+ if (!isIgnorableDirectoryReadError(err)) {
283
+ const msg = err instanceof Error ? err.message : String(err);
284
+ devWarn(`[RI-1601] Failed to scan CSS files in "${dir}": ${msg}`);
285
+ }
286
+ return;
287
+ }
288
+ for (const entry of entries) {
289
+ if (entry.isDirectory()) {
290
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
291
+ await collectCSSFiles(join(dir, entry.name), results);
292
+ } else if (entry.isFile() && CSS_FILE_RE.test(entry.name)) {
293
+ results.push(join(dir, entry.name));
294
+ }
295
+ }
296
+ }
297
+ async function findCSSFilesOnDisk(root2) {
298
+ const results = [];
299
+ await collectCSSFiles(root2, results);
300
+ return results;
301
+ }
302
+ async function checkCSSFileAsync(file) {
303
+ const versionBefore = fileVersions.get(file) ?? 0;
304
+ try {
305
+ const raw = await readFile(file, "utf-8");
306
+ if ((fileVersions.get(file) ?? 0) !== versionBefore) return;
307
+ if (hasRIActivation(raw)) {
308
+ riCSSFiles.add(file);
309
+ } else {
310
+ riCSSFiles.delete(file);
311
+ }
312
+ } catch (_err) {
313
+ if ((fileVersions.get(file) ?? 0) !== versionBefore) return;
314
+ riCSSFiles.delete(file);
315
+ }
316
+ }
317
+ }
318
+ export {
319
+ rainbowindexVite as default
320
+ };
package/package.json CHANGED
@@ -1,5 +1,102 @@
1
1
  {
2
- "name": "rainbowindex",
3
- "version": "0.0.0",
4
- "description": "Coming soon"
5
- }
2
+ "name": "rainbowindex",
3
+ "version": "0.2.0",
4
+ "description": "CSS-first system for building and maintaining consistent user interfaces",
5
+ "keywords": [
6
+ "css",
7
+ "utility-css",
8
+ "atomic-css",
9
+ "design-tokens",
10
+ "design-system",
11
+ "theming",
12
+ "dark-mode",
13
+ "oklch",
14
+ "fluid-typography",
15
+ "google-fonts",
16
+ "postcss",
17
+ "postcss-plugin",
18
+ "vite-plugin",
19
+ "class-merge",
20
+ "classnames",
21
+ "tailwind",
22
+ "tailwindcss",
23
+ "cli"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/rainbowindex/rainbowindex.git"
28
+ },
29
+ "author": "rainbowindex (https://github.com/rainbowindex)",
30
+ "homepage": "https://rainbowindex.dev",
31
+ "bugs": "https://github.com/rainbowindex/rainbowindex/issues",
32
+ "type": "module",
33
+ "main": "./dist/index.mjs",
34
+ "types": "./dist/index.d.ts",
35
+ "style": "./dist/index.css",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "style": "./dist/index.css",
40
+ "browser": "./dist/browser.mjs",
41
+ "node": "./dist/index.mjs",
42
+ "default": "./dist/index.mjs"
43
+ },
44
+ "./index.css": "./dist/index.css",
45
+ "./vite": {
46
+ "types": "./dist/vite.d.ts",
47
+ "default": "./dist/vite.mjs"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "bin": {
52
+ "rainbowindex": "./dist/cli.mjs"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "CHANGELOG.md"
57
+ ],
58
+ "sideEffects": [
59
+ "**/*.css"
60
+ ],
61
+ "engines": {
62
+ "node": ">=20.19"
63
+ },
64
+ "license": "MIT",
65
+ "dependencies": {
66
+ "chokidar": "^4.0.3",
67
+ "lightningcss": "^1.25.0",
68
+ "tinyglobby": "^0.2.15"
69
+ },
70
+ "peerDependencies": {
71
+ "postcss": "^8.5.0",
72
+ "vite": "*"
73
+ },
74
+ "peerDependenciesMeta": {
75
+ "vite": {
76
+ "optional": true
77
+ }
78
+ },
79
+ "devDependencies": {
80
+ "@biomejs/biome": "^2.4.16",
81
+ "@types/node": "^25.9.1",
82
+ "@vitest/coverage-v8": "^4.1.0",
83
+ "postcss": "^8.5.8",
84
+ "tsup": "^8.5.1",
85
+ "typescript": "^6.0.3",
86
+ "vite": "8.0.0",
87
+ "vitest": "^4.0.17"
88
+ },
89
+ "scripts": {
90
+ "build": "tsup",
91
+ "check": "pnpm typecheck && pnpm lint && pnpm test && pnpm test:artifacts",
92
+ "test": "vitest run --exclude __tests__/cli/** --exclude __tests__/package.test.ts",
93
+ "test:coverage": "vitest run --coverage --exclude __tests__/cli/** --exclude __tests__/package.test.ts",
94
+ "test:artifacts": "pnpm build && vitest run __tests__/cli/cli.test.ts __tests__/package.test.ts",
95
+ "test:watch": "vitest",
96
+ "typecheck": "tsc --noEmit",
97
+ "lint": "biome lint .",
98
+ "lint:fix": "biome lint --write .",
99
+ "format": "biome format --write .",
100
+ "format:check": "biome format ."
101
+ }
102
+ }