rainbowindex 0.1.4 → 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/CHANGELOG.md +122 -0
- package/{LICENSE.md → LICENSE} +3 -3
- package/README.md +262 -125
- package/dist/browser.d.ts +3 -1321
- package/dist/browser.mjs +41 -20
- package/dist/chunk-5N4GPK26.mjs +2664 -0
- package/dist/chunk-KCSNR2TV.mjs +671 -0
- package/dist/chunk-PD4ZXGJ6.mjs +14 -0
- package/dist/chunk-RPXZ3O6R.mjs +10518 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +1258 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +266 -5207
- package/dist/index.mjs +70 -68
- package/dist/optimize-6NWJVT6W.mjs +24 -0
- package/dist/safelist-DRk1XXxi.d.ts +239 -0
- package/dist/vite.d.ts +5 -0
- package/dist/vite.mjs +320 -0
- package/package.json +79 -66
- package/css/index.css +0 -5
- package/css/preflight.css +0 -370
- package/css/theme.css +0 -308
- package/dist/browser.mjs.map +0 -1
- package/dist/index.cjs +0 -69
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -5230
- package/dist/index.mjs.map +0 -1
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,1258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_EXCLUDES,
|
|
4
|
+
DEFAULT_PATTERNS,
|
|
5
|
+
MAX_DIRECTIVE_INPUT_SIZE,
|
|
6
|
+
analyzeProjectCSS,
|
|
7
|
+
collectProjectClasses,
|
|
8
|
+
extractDirectives,
|
|
9
|
+
finalizeProjectCompilation,
|
|
10
|
+
getFontPreloadLinks,
|
|
11
|
+
hasApplyLikeDirective,
|
|
12
|
+
hasRIActivation,
|
|
13
|
+
resolveDirectives,
|
|
14
|
+
resolveGoogleFonts,
|
|
15
|
+
validateGlobPattern
|
|
16
|
+
} from "./chunk-RPXZ3O6R.mjs";
|
|
17
|
+
import {
|
|
18
|
+
devWarn
|
|
19
|
+
} from "./chunk-5N4GPK26.mjs";
|
|
20
|
+
|
|
21
|
+
// src/entries/cli.ts
|
|
22
|
+
import { realpathSync } from "fs";
|
|
23
|
+
import { resolve as resolve6 } from "path";
|
|
24
|
+
import { pathToFileURL } from "url";
|
|
25
|
+
|
|
26
|
+
// src/cli/args.ts
|
|
27
|
+
import { relative, resolve } from "path";
|
|
28
|
+
function parseArgs(argv, callbacks) {
|
|
29
|
+
if (argv[0] === "framework") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Framework runners were removed. Use your project's Vite scripts directly, such as "pnpm dev" or "pnpm build".`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const opts = {
|
|
35
|
+
command: "build",
|
|
36
|
+
globs: [],
|
|
37
|
+
watch: false,
|
|
38
|
+
minify: false,
|
|
39
|
+
strict: false,
|
|
40
|
+
subcommandExplicit: false,
|
|
41
|
+
earlyExit: false
|
|
42
|
+
};
|
|
43
|
+
let i = 0;
|
|
44
|
+
while (i < argv.length) {
|
|
45
|
+
const arg = argv[i];
|
|
46
|
+
if (arg === "build" && !opts.subcommandExplicit && opts.globs.length === 0) {
|
|
47
|
+
opts.command = "build";
|
|
48
|
+
opts.subcommandExplicit = true;
|
|
49
|
+
i++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (arg === "generate-types") {
|
|
53
|
+
opts.command = "generate-types";
|
|
54
|
+
opts.subcommandExplicit = true;
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (arg === "init" || arg === "--init") {
|
|
59
|
+
opts.command = "init";
|
|
60
|
+
opts.subcommandExplicit = true;
|
|
61
|
+
i++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (arg === "create" || arg === "--create") {
|
|
65
|
+
opts.command = "create";
|
|
66
|
+
opts.subcommandExplicit = true;
|
|
67
|
+
i++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg === "preload-fonts") {
|
|
71
|
+
opts.command = "preload-fonts";
|
|
72
|
+
opts.subcommandExplicit = true;
|
|
73
|
+
i++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (arg === "-o" || arg === "--output") {
|
|
77
|
+
if (i + 1 >= argv.length) {
|
|
78
|
+
throw new Error(`Missing value for ${arg}`);
|
|
79
|
+
}
|
|
80
|
+
opts.output = argv[++i];
|
|
81
|
+
i++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (arg === "--css") {
|
|
85
|
+
if (i + 1 >= argv.length) {
|
|
86
|
+
throw new Error("Missing value for --css");
|
|
87
|
+
}
|
|
88
|
+
opts.cssFile = argv[++i];
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === "--template") {
|
|
93
|
+
if (i + 1 >= argv.length) {
|
|
94
|
+
throw new Error("Missing value for --template");
|
|
95
|
+
}
|
|
96
|
+
opts.template = argv[++i];
|
|
97
|
+
i++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg === "--watch") {
|
|
101
|
+
opts.watch = true;
|
|
102
|
+
i++;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (arg === "--minify") {
|
|
106
|
+
opts.minify = true;
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (arg === "--strict") {
|
|
111
|
+
opts.strict = true;
|
|
112
|
+
i++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (arg === "--version" || arg === "-v") {
|
|
116
|
+
console.log(callbacks.getVersion());
|
|
117
|
+
opts.earlyExit = true;
|
|
118
|
+
return opts;
|
|
119
|
+
}
|
|
120
|
+
if (arg === "--help" || arg === "-h") {
|
|
121
|
+
callbacks.printHelp(opts.subcommandExplicit ? opts.command : void 0);
|
|
122
|
+
opts.earlyExit = true;
|
|
123
|
+
return opts;
|
|
124
|
+
}
|
|
125
|
+
if (arg.startsWith("-")) {
|
|
126
|
+
throw new Error(`Unknown option "${arg}". Run rainbowindex --help for usage.`);
|
|
127
|
+
}
|
|
128
|
+
if (opts.command === "create") {
|
|
129
|
+
if (opts.targetDir) {
|
|
130
|
+
throw new Error(`Unexpected extra argument "${arg}" for create.`);
|
|
131
|
+
}
|
|
132
|
+
opts.targetDir = arg;
|
|
133
|
+
i++;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (opts.command === "init") {
|
|
137
|
+
throw new Error(`Unexpected extra argument "${arg}" for init.`);
|
|
138
|
+
}
|
|
139
|
+
opts.globs.push(arg);
|
|
140
|
+
i++;
|
|
141
|
+
}
|
|
142
|
+
if (opts.command === "create" && !opts.targetDir) {
|
|
143
|
+
throw new Error("create requires a project directory. Example: rainbowindex create my-app");
|
|
144
|
+
}
|
|
145
|
+
if (opts.watch && !opts.output) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
'--output is required with --watch. Example: rainbowindex "src/**/*.tsx" --watch -o dist/styles.css'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
if (opts.output) {
|
|
151
|
+
const resolved = resolve(process.cwd(), opts.output);
|
|
152
|
+
const rel = relative(process.cwd(), resolved);
|
|
153
|
+
if (rel.startsWith("..") || resolve(rel) === rel) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Output path "${opts.output}" resolves outside the project root. Use a relative path within the project directory.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return opts;
|
|
160
|
+
}
|
|
161
|
+
var SUBCOMMAND_HELP = {
|
|
162
|
+
build: `
|
|
163
|
+
\u{1F308} rainbowindex build \u2014 Generate CSS from source files
|
|
164
|
+
|
|
165
|
+
Usage:
|
|
166
|
+
rainbowindex <glob...> [options]
|
|
167
|
+
rainbowindex --css <file> [options]
|
|
168
|
+
|
|
169
|
+
Options:
|
|
170
|
+
-o, --output <file> Output CSS file path (omit to write to stdout)
|
|
171
|
+
--watch Re-run on source-file changes (requires --output)
|
|
172
|
+
--minify Minify output via LightningCSS
|
|
173
|
+
--css <file> CSS input with directives (auto-detected if omitted)
|
|
174
|
+
-h, --help Show this help
|
|
175
|
+
|
|
176
|
+
Inputs:
|
|
177
|
+
Pass one or more globs to scan for class names. If no globs are passed and
|
|
178
|
+
a CSS file with @source directives is found, those are used instead.
|
|
179
|
+
|
|
180
|
+
Examples:
|
|
181
|
+
rainbowindex "src/**/*.tsx" -o dist/styles.css
|
|
182
|
+
rainbowindex "src/**/*.tsx" --watch -o dist/styles.css
|
|
183
|
+
rainbowindex --css src/styles.css -o dist/styles.css --minify
|
|
184
|
+
`,
|
|
185
|
+
init: `
|
|
186
|
+
\u{1F308} rainbowindex init \u2014 Wire Rainbow Index into the current Vite app
|
|
187
|
+
|
|
188
|
+
Usage:
|
|
189
|
+
rainbowindex init [options]
|
|
190
|
+
|
|
191
|
+
What this does:
|
|
192
|
+
1. Installs rainbowindex as a dev dependency (if not already installed).
|
|
193
|
+
2. Creates or patches vite.config.* to register the Vite plugin.
|
|
194
|
+
3. Creates or updates a CSS entry file with @import "rainbowindex".
|
|
195
|
+
4. Adds the CSS import to your entry file (src/main.tsx, src/main.ts, \u2026).
|
|
196
|
+
|
|
197
|
+
Options:
|
|
198
|
+
--css <file> Stylesheet path to create/patch (default: src/index.css)
|
|
199
|
+
-h, --help Show this help
|
|
200
|
+
|
|
201
|
+
Notes:
|
|
202
|
+
Detects pnpm / yarn / bun / npm from lockfiles, package.json#packageManager,
|
|
203
|
+
and npm_config_user_agent. Requires an existing Vite project \u2014 if you do not
|
|
204
|
+
have one yet, use \`rainbowindex create <dir>\`.
|
|
205
|
+
|
|
206
|
+
Examples:
|
|
207
|
+
rainbowindex init
|
|
208
|
+
rainbowindex init --css src/styles.css
|
|
209
|
+
`,
|
|
210
|
+
create: `
|
|
211
|
+
\u{1F308} rainbowindex create \u2014 Scaffold a Vite app with Rainbow Index ready
|
|
212
|
+
|
|
213
|
+
Usage:
|
|
214
|
+
rainbowindex create <dir> [options]
|
|
215
|
+
|
|
216
|
+
Options:
|
|
217
|
+
--template <name> Vite template (default: react-ts)
|
|
218
|
+
Examples: vanilla, vanilla-ts, react, react-ts, vue, vue-ts, svelte, svelte-ts
|
|
219
|
+
--css <file> Stylesheet path inside the new app (default: src/index.css)
|
|
220
|
+
-h, --help Show this help
|
|
221
|
+
|
|
222
|
+
What this does:
|
|
223
|
+
1. Runs \`create vite\` for the given template.
|
|
224
|
+
2. Runs \`rainbowindex init\` inside the new directory.
|
|
225
|
+
|
|
226
|
+
Examples:
|
|
227
|
+
rainbowindex create my-app
|
|
228
|
+
rainbowindex create my-app --template vue-ts
|
|
229
|
+
`,
|
|
230
|
+
"generate-types": `
|
|
231
|
+
\u{1F308} rainbowindex generate-types \u2014 Generate TypeScript types for ri() autocomplete
|
|
232
|
+
|
|
233
|
+
Usage:
|
|
234
|
+
rainbowindex generate-types [options]
|
|
235
|
+
|
|
236
|
+
Options:
|
|
237
|
+
--css <file> CSS input with @color/@text/@utility/etc directives
|
|
238
|
+
--strict Drop the \`string & {}\` escape hatch from the union
|
|
239
|
+
-h, --help Show this help
|
|
240
|
+
|
|
241
|
+
Output:
|
|
242
|
+
Writes \`rainbowindex-env.d.ts\` to the current working directory. Include it
|
|
243
|
+
in tsconfig.json#include (or the equivalent) so the editor picks it up.
|
|
244
|
+
|
|
245
|
+
Examples:
|
|
246
|
+
rainbowindex generate-types
|
|
247
|
+
rainbowindex generate-types --strict --css src/styles.css
|
|
248
|
+
`,
|
|
249
|
+
"preload-fonts": `
|
|
250
|
+
\u{1F308} rainbowindex preload-fonts \u2014 Generate <link rel="preload"> tags
|
|
251
|
+
|
|
252
|
+
Usage:
|
|
253
|
+
rainbowindex preload-fonts [options]
|
|
254
|
+
|
|
255
|
+
Options:
|
|
256
|
+
--css <file> CSS input with @font directives
|
|
257
|
+
-h, --help Show this help
|
|
258
|
+
|
|
259
|
+
Output:
|
|
260
|
+
Prints <link rel="preload"> tags to stdout for fonts declared in the @font
|
|
261
|
+
block (faces marked preload), or via @font-face.
|
|
262
|
+
|
|
263
|
+
Examples:
|
|
264
|
+
rainbowindex preload-fonts --css src/styles.css > preload.html
|
|
265
|
+
`
|
|
266
|
+
};
|
|
267
|
+
function printHelp(subcommand) {
|
|
268
|
+
if (subcommand) {
|
|
269
|
+
console.log(SUBCOMMAND_HELP[subcommand]);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
console.log(`
|
|
273
|
+
\u{1F308} Rainbow Index CLI
|
|
274
|
+
|
|
275
|
+
Usage:
|
|
276
|
+
rainbowindex <glob> [options] Generate CSS from source files
|
|
277
|
+
rainbowindex init Wire Rainbow Index into the current Vite app
|
|
278
|
+
rainbowindex create <dir> Scaffold a Vite app with Rainbow Index ready
|
|
279
|
+
rainbowindex generate-types Generate TypeScript types for ri()
|
|
280
|
+
rainbowindex preload-fonts Generate font preload link tags
|
|
281
|
+
|
|
282
|
+
Run \`rainbowindex <subcommand> --help\` for subcommand-specific options.
|
|
283
|
+
|
|
284
|
+
Options:
|
|
285
|
+
-o, --output <file> Output CSS file path
|
|
286
|
+
--watch Watch for changes
|
|
287
|
+
--minify Minify output via LightningCSS
|
|
288
|
+
--css <file> CSS file with directives (default: auto-detect)
|
|
289
|
+
--template <name> Vite template for create (default: react-ts)
|
|
290
|
+
--strict No string escape hatch in generated types
|
|
291
|
+
-v, --version Show version
|
|
292
|
+
-h, --help Show this help
|
|
293
|
+
|
|
294
|
+
Environment Variables:
|
|
295
|
+
RI_CACHE_DIR Override font metadata cache directory (default: node_modules/.cache/rainbowindex)
|
|
296
|
+
RI_FONT_CACHE_TTL Font cache max age in seconds (default: 604800 = 7 days)
|
|
297
|
+
RI_FETCH_FONTS=0 Disable network requests for Google Fonts metadata (default: enabled)
|
|
298
|
+
RI_OFFLINE=1 Skip network requests, use cached font data only
|
|
299
|
+
RI_DEBUG=1 Enable debug logging
|
|
300
|
+
|
|
301
|
+
Diagnostics:
|
|
302
|
+
Warnings carry [RI-NNNN] codes. Full table: https://rainbowindex.dev/docs/diagnostics
|
|
303
|
+
|
|
304
|
+
Examples:
|
|
305
|
+
rainbowindex init
|
|
306
|
+
rainbowindex create my-app --template react-ts
|
|
307
|
+
rainbowindex "src/**/*.tsx" -o dist/styles.css
|
|
308
|
+
rainbowindex "src/**/*.tsx" --watch -o dist/styles.css
|
|
309
|
+
rainbowindex generate-types --strict
|
|
310
|
+
`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/cli/css-file.ts
|
|
314
|
+
import { existsSync } from "fs";
|
|
315
|
+
import { readFile, stat } from "fs/promises";
|
|
316
|
+
import { resolve as resolve2 } from "path";
|
|
317
|
+
var CSS_CANDIDATES = Object.freeze([
|
|
318
|
+
"src/index.css",
|
|
319
|
+
"src/style.css",
|
|
320
|
+
"src/styles.css",
|
|
321
|
+
"src/app.css",
|
|
322
|
+
"src/global.css",
|
|
323
|
+
"index.css",
|
|
324
|
+
"style.css",
|
|
325
|
+
"styles.css",
|
|
326
|
+
"app.css",
|
|
327
|
+
"global.css"
|
|
328
|
+
]);
|
|
329
|
+
var MAX_CSS_FILE_SIZE = MAX_DIRECTIVE_INPUT_SIZE;
|
|
330
|
+
async function findCSSFileAsync(cwd) {
|
|
331
|
+
const candidates = CSS_CANDIDATES;
|
|
332
|
+
const results = await Promise.all(
|
|
333
|
+
candidates.map(async (candidate) => {
|
|
334
|
+
const full = resolve2(cwd, candidate);
|
|
335
|
+
try {
|
|
336
|
+
const fileSize = (await stat(full)).size;
|
|
337
|
+
if (fileSize > MAX_CSS_FILE_SIZE) return null;
|
|
338
|
+
const content = await readFile(full, "utf-8");
|
|
339
|
+
return hasRIActivation(content) ? full : null;
|
|
340
|
+
} catch (err) {
|
|
341
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
console.warn(
|
|
345
|
+
`[RI-1404] Could not read candidate CSS file "${full}" \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
346
|
+
);
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
);
|
|
351
|
+
return results.find((r) => r !== null) ?? null;
|
|
352
|
+
}
|
|
353
|
+
async function readCSSFileAsync(cssFile) {
|
|
354
|
+
const fileSize = (await stat(cssFile)).size;
|
|
355
|
+
if (fileSize > MAX_CSS_FILE_SIZE) {
|
|
356
|
+
throw new Error(`CSS file "${cssFile}" exceeds ${MAX_CSS_FILE_SIZE / 1048576} MB limit.`);
|
|
357
|
+
}
|
|
358
|
+
const content = await readFile(cssFile, "utf-8");
|
|
359
|
+
return content.charCodeAt(0) === 65279 ? content.slice(1) : content;
|
|
360
|
+
}
|
|
361
|
+
async function loadProjectCSS(opts, cwd) {
|
|
362
|
+
if (opts.cssFile) {
|
|
363
|
+
const cssFile2 = resolve2(cwd, opts.cssFile);
|
|
364
|
+
if (!existsSync(cssFile2)) {
|
|
365
|
+
throw new Error(`[RI-1605] CSS input file not found: ${cssFile2}`);
|
|
366
|
+
}
|
|
367
|
+
return { css: await readCSSFileAsync(cssFile2), cssFile: cssFile2 };
|
|
368
|
+
}
|
|
369
|
+
const cssFile = await findCSSFileAsync(cwd);
|
|
370
|
+
if (!cssFile) return { css: "", cssFile: null };
|
|
371
|
+
return { css: await readCSSFileAsync(cssFile), cssFile };
|
|
372
|
+
}
|
|
373
|
+
async function loadProjectTheme(opts, cwd) {
|
|
374
|
+
const { css } = await loadProjectCSS(opts, cwd);
|
|
375
|
+
const parseWarnings = [];
|
|
376
|
+
const directives = extractDirectives(css, parseWarnings);
|
|
377
|
+
const theme = resolveDirectives(directives);
|
|
378
|
+
for (const w of parseWarnings) console.error(w);
|
|
379
|
+
return theme;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/cli/build.ts
|
|
383
|
+
async function buildCSS(opts, cwd) {
|
|
384
|
+
const { css: cssSource, cssFile } = await loadProjectCSS(opts, cwd);
|
|
385
|
+
const analysis = analyzeProjectCSS(cssSource);
|
|
386
|
+
const { theme, warnings, warningSeen } = analysis;
|
|
387
|
+
const validGlobs = [];
|
|
388
|
+
for (const g of opts.globs) {
|
|
389
|
+
const err = validateGlobPattern(g);
|
|
390
|
+
if (err) {
|
|
391
|
+
console.error(`[RI-1404] CLI glob pattern rejected: ${err}`);
|
|
392
|
+
} else {
|
|
393
|
+
validGlobs.push(g);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const cliSources = validGlobs.map((g) => ({ pattern: g, negated: false, inline: false }));
|
|
397
|
+
const scannedClasses = await collectProjectClasses(
|
|
398
|
+
theme.sources,
|
|
399
|
+
cliSources,
|
|
400
|
+
cwd,
|
|
401
|
+
warnings,
|
|
402
|
+
warningSeen
|
|
403
|
+
);
|
|
404
|
+
const buildWarnings = [];
|
|
405
|
+
if (hasApplyLikeDirective(cssSource)) {
|
|
406
|
+
buildWarnings.push(
|
|
407
|
+
'[RI-1009] @apply is only supported via the PostCSS plugin. To expand @apply: (1) add `postcss.config.js` with `import rainbowindex from "rainbowindex"; export default { plugins: [rainbowindex()] };`, or (2) use the Vite plugin (`import rainbowindex from "rainbowindex/vite"`), which wires PostCSS automatically. The CLI does not run PostCSS so it cannot expand @apply.'
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
const compiled = await finalizeProjectCompilation({
|
|
411
|
+
css: cssSource,
|
|
412
|
+
classNames: scannedClasses,
|
|
413
|
+
analysis,
|
|
414
|
+
resolveFonts: resolveGoogleFonts
|
|
415
|
+
});
|
|
416
|
+
if (compiled.classNames.length === 0) {
|
|
417
|
+
const hadAnyGlob = opts.globs.length > 0;
|
|
418
|
+
const hadAnySource = theme.sources.length > 0;
|
|
419
|
+
if (!hadAnyGlob && !hadAnySource) {
|
|
420
|
+
buildWarnings.push(
|
|
421
|
+
'[RI-1603] No source files were scanned and no utility classes were compiled. Either pass a glob (`rainbowindex "src/**/*.{ts,tsx}" -o out.css`) or add `@source "src/**/*.{ts,tsx}";` to your CSS input.'
|
|
422
|
+
);
|
|
423
|
+
} else {
|
|
424
|
+
buildWarnings.push(
|
|
425
|
+
`[RI-1603] No utility classes were found in the scanned sources. Verify your glob/@source patterns match files that contain class names${cssFile ? ` (CSS input: ${cssFile})` : ""}.`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
430
|
+
for (const warning of [...compiled.warnings, ...buildWarnings]) {
|
|
431
|
+
if (!emitted.has(warning)) {
|
|
432
|
+
console.error(warning);
|
|
433
|
+
emitted.add(warning);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return { css: compiled.css, theme: compiled.theme, cssFile };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/cli/generate-types.ts
|
|
440
|
+
import { existsSync as existsSync2 } from "fs";
|
|
441
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
442
|
+
import { resolve as resolve3 } from "path";
|
|
443
|
+
async function generateTypes(opts, cwd) {
|
|
444
|
+
const theme = await loadProjectTheme(opts, cwd);
|
|
445
|
+
const SAFE_NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
446
|
+
const validateNames = (names, context) => {
|
|
447
|
+
return names.filter((n) => {
|
|
448
|
+
if (SAFE_NAME_RE.test(n)) return true;
|
|
449
|
+
console.error(
|
|
450
|
+
`[RI-1014] Skipping ${context} name "${n}" \u2014 contains characters unsafe for TypeScript type generation.`
|
|
451
|
+
);
|
|
452
|
+
return false;
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
const colorNames = validateNames(Object.keys(theme.colors), "color");
|
|
456
|
+
const textSizes = validateNames(Object.keys(theme.text), "text");
|
|
457
|
+
const breakpoints = validateNames(Object.keys(theme.breakpoints), "breakpoint");
|
|
458
|
+
const weightNames = validateNames(Object.keys(theme.weights), "weight");
|
|
459
|
+
const staticCustomUtilities = validateNames(
|
|
460
|
+
theme.customUtilities.filter((u) => !u.functional).map((u) => u.name),
|
|
461
|
+
"utility"
|
|
462
|
+
);
|
|
463
|
+
const functionalCustomUtilities = validateNames(
|
|
464
|
+
theme.customUtilities.filter((u) => u.functional).map((u) => u.name),
|
|
465
|
+
"utility"
|
|
466
|
+
);
|
|
467
|
+
const union = (names) => names.length > 0 ? names.map((n) => `"${n}"`).join(" | ") : "never";
|
|
468
|
+
const lines = [
|
|
469
|
+
"// rainbowindex-env.d.ts (auto-generated \u2014 do not edit)",
|
|
470
|
+
"",
|
|
471
|
+
`type ColorName = ${union(colorNames)};`,
|
|
472
|
+
`type ColorStop = "50" | "100" | "150" | "200" | "250" | "300" | "350" | "400" | "450" | "500" | "550" | "600" | "650" | "700" | "750" | "800" | "850" | "900" | "950";`,
|
|
473
|
+
"type SpacingToken = `${number}` | `${number}_${number}`;",
|
|
474
|
+
`type TextSize = ${union(textSizes)};`,
|
|
475
|
+
`type Variant = ${union(breakpoints)} | "hover" | "focus" | "focus-visible" | "active" | "disabled" | "dark" | "first" | "last" | "odd" | "even";`,
|
|
476
|
+
`type WeightName = ${union(weightNames)};`,
|
|
477
|
+
"",
|
|
478
|
+
"type RainbowClass =",
|
|
479
|
+
' | `${"bg" | "text" | "border" | "outline" | "accent" | "caret" | "fill" | "stroke"}-${ColorName}-${ColorStop}`',
|
|
480
|
+
' | `${"p" | "px" | "py" | "pt" | "pb" | "pl" | "pr" | "ps" | "pe" | "pbs" | "pbe" | "m" | "mx" | "my" | "mt" | "mb" | "ml" | "mr" | "ms" | "me" | "mbs" | "mbe" | "gap" | "gap-x" | "gap-y"}-${SpacingToken}`',
|
|
481
|
+
" | `text-${TextSize}`",
|
|
482
|
+
" | `text-fluid-${TextSize}`",
|
|
483
|
+
' | `font-${"sans" | "serif" | "mono"}`',
|
|
484
|
+
" | `font-${WeightName}`",
|
|
485
|
+
" | `w-${SpacingToken}` | `h-${SpacingToken}` | `size-${SpacingToken}`"
|
|
486
|
+
];
|
|
487
|
+
if (staticCustomUtilities.length > 0) {
|
|
488
|
+
lines.push(` | ${staticCustomUtilities.map((n) => `"${n}"`).join(" | ")}`);
|
|
489
|
+
}
|
|
490
|
+
if (functionalCustomUtilities.length > 0) {
|
|
491
|
+
lines.push(` | ${functionalCustomUtilities.map((n) => `\`${n}-\${string}\``).join(" | ")}`);
|
|
492
|
+
}
|
|
493
|
+
lines.push(" | `${Variant}:${Exclude<RainbowClass, `${string}:${string}`>}`");
|
|
494
|
+
if (!opts.strict) {
|
|
495
|
+
lines.push(" | (string & {});");
|
|
496
|
+
} else {
|
|
497
|
+
lines.push(" ;");
|
|
498
|
+
}
|
|
499
|
+
lines.push(
|
|
500
|
+
"",
|
|
501
|
+
'declare module "rainbowindex" {',
|
|
502
|
+
" type ClassInput = RainbowClass | false | null | undefined | ClassInput[];",
|
|
503
|
+
" export function ri(...classes: ClassInput[]): string;",
|
|
504
|
+
" export function createRi(snapshot: unknown): (...classes: ClassInput[]) => string;",
|
|
505
|
+
"}",
|
|
506
|
+
""
|
|
507
|
+
);
|
|
508
|
+
const outputPath = resolve3(cwd, "rainbowindex-env.d.ts");
|
|
509
|
+
const newContent = lines.join("\n");
|
|
510
|
+
if (existsSync2(outputPath)) {
|
|
511
|
+
const existing = await readFile2(outputPath, "utf-8");
|
|
512
|
+
if (!existing.startsWith("// rainbowindex-env.d.ts (auto-generated")) {
|
|
513
|
+
const backupPath = `${outputPath}.bak`;
|
|
514
|
+
try {
|
|
515
|
+
await writeFile(backupPath, existing);
|
|
516
|
+
console.warn(
|
|
517
|
+
`[RI-1604] "${outputPath}" exists and does not appear to be auto-generated. Original saved to "${backupPath}" before overwrite.`
|
|
518
|
+
);
|
|
519
|
+
} catch (err) {
|
|
520
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
521
|
+
console.error(
|
|
522
|
+
`[RI-1604] "${outputPath}" exists with hand edits and the backup write to "${backupPath}" failed: ${msg}. Aborting to avoid data loss \u2014 move the file aside and re-run.`
|
|
523
|
+
);
|
|
524
|
+
process.exitCode = 1;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
try {
|
|
530
|
+
await writeFile(outputPath, newContent);
|
|
531
|
+
} catch (err) {
|
|
532
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
533
|
+
console.error(`[rainbowindex] Failed to write types file "${outputPath}": ${msg}`);
|
|
534
|
+
process.exitCode = 1;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
console.log(`[rainbowindex] Generated types: ${outputPath}`);
|
|
538
|
+
console.log(
|
|
539
|
+
'[rainbowindex] Add it to tsconfig.json#include (e.g. "include": ["src", "rainbowindex-env.d.ts"]) so the editor picks up RainbowClass autocomplete.'
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/cli/preload-fonts.ts
|
|
544
|
+
async function preloadFonts(opts, cwd) {
|
|
545
|
+
const theme = await loadProjectTheme(opts, cwd);
|
|
546
|
+
const links = getFontPreloadLinks(theme.fonts);
|
|
547
|
+
if (links.length === 0) {
|
|
548
|
+
console.log("[rainbowindex] No font preload links to generate.");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
for (const link of links) {
|
|
552
|
+
const safeHref = link.href.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
553
|
+
console.log(
|
|
554
|
+
`<link rel="preload" href="${safeHref}" as="${link.as}" type="${link.type}" crossorigin>`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/cli/watch.ts
|
|
560
|
+
import { mkdir } from "fs/promises";
|
|
561
|
+
import { resolve as resolve4, dirname as dirname2 } from "path";
|
|
562
|
+
|
|
563
|
+
// src/cli/atomic-write.ts
|
|
564
|
+
import { writeFile as writeFile2, rename, unlink } from "fs/promises";
|
|
565
|
+
import { dirname, join } from "path";
|
|
566
|
+
function isIgnorableCleanupError(err) {
|
|
567
|
+
return !!err && typeof err === "object" && "code" in err && (err.code === "ENOENT" || err.code === "EPERM");
|
|
568
|
+
}
|
|
569
|
+
async function writeFileAtomic(filePath, content) {
|
|
570
|
+
const dir = dirname(filePath);
|
|
571
|
+
const tmp = join(dir, `.ri-tmp-${process.pid}-${Date.now()}`);
|
|
572
|
+
try {
|
|
573
|
+
await writeFile2(tmp, content);
|
|
574
|
+
await rename(tmp, filePath);
|
|
575
|
+
} catch (err) {
|
|
576
|
+
try {
|
|
577
|
+
await unlink(tmp);
|
|
578
|
+
} catch (cleanupErr) {
|
|
579
|
+
if (!isIgnorableCleanupError(cleanupErr)) {
|
|
580
|
+
const msg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr);
|
|
581
|
+
devWarn(`[RI-DEV] Failed to remove temp file "${tmp}": ${msg}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
throw err;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/cli/watch.ts
|
|
589
|
+
async function minifyIfRequested(css, opts) {
|
|
590
|
+
if (!opts.minify) return css;
|
|
591
|
+
const { optimizeCSS } = await import("./optimize-6NWJVT6W.mjs");
|
|
592
|
+
return optimizeCSS(css);
|
|
593
|
+
}
|
|
594
|
+
async function buildAndWrite(opts, cwd, outputFile) {
|
|
595
|
+
const result = await buildCSS(opts, cwd);
|
|
596
|
+
const css = await minifyIfRequested(result.css, opts);
|
|
597
|
+
const outputPath = resolve4(cwd, outputFile);
|
|
598
|
+
await mkdir(dirname2(outputPath), { recursive: true });
|
|
599
|
+
await writeFileAtomic(outputPath, css);
|
|
600
|
+
return result;
|
|
601
|
+
}
|
|
602
|
+
async function watchMode(opts, cwd) {
|
|
603
|
+
if (!opts.output) {
|
|
604
|
+
throw new Error("--output is required with --watch");
|
|
605
|
+
}
|
|
606
|
+
const outputFile = opts.output;
|
|
607
|
+
const initialBuild = await buildAndWrite(opts, cwd, outputFile);
|
|
608
|
+
console.log(`[rainbowindex] Built: ${opts.output}`);
|
|
609
|
+
let chokidar;
|
|
610
|
+
try {
|
|
611
|
+
chokidar = await import("chokidar");
|
|
612
|
+
} catch (err) {
|
|
613
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
614
|
+
throw new Error(`Watch mode could not load "chokidar" (a bundled dependency): ${detail}`);
|
|
615
|
+
}
|
|
616
|
+
const watchPaths = opts.globs.length > 0 ? [...opts.globs] : [...DEFAULT_PATTERNS];
|
|
617
|
+
if (initialBuild.cssFile) watchPaths.push(initialBuild.cssFile);
|
|
618
|
+
const watcher = chokidar.watch(watchPaths, {
|
|
619
|
+
cwd,
|
|
620
|
+
ignored: [...DEFAULT_EXCLUDES],
|
|
621
|
+
ignoreInitial: true,
|
|
622
|
+
awaitWriteFinish: { stabilityThreshold: 50 }
|
|
623
|
+
});
|
|
624
|
+
let debounceTimer = null;
|
|
625
|
+
let building = false;
|
|
626
|
+
let pendingRebuild = false;
|
|
627
|
+
let buildDoneResolve = null;
|
|
628
|
+
let buildDonePromise = null;
|
|
629
|
+
let consecutiveErrors = 0;
|
|
630
|
+
let errorsPaused = false;
|
|
631
|
+
const MAX_CONSECUTIVE_ERRORS = 5;
|
|
632
|
+
const MIN_REBUILD_INTERVAL_MS = 500;
|
|
633
|
+
let lastBuildStart = 0;
|
|
634
|
+
const currentWatchedPaths = new Set(watchPaths);
|
|
635
|
+
const MAX_WATCHED_PATHS = 1e4;
|
|
636
|
+
const syncSourceWatchPaths = (theme) => {
|
|
637
|
+
const newPaths = theme.sources.filter((s) => !s.negated && !s.inline).map((s) => s.pattern);
|
|
638
|
+
const newPathSet = new Set(newPaths);
|
|
639
|
+
for (const p of currentWatchedPaths) {
|
|
640
|
+
if (!newPathSet.has(p) && !watchPaths.includes(p)) {
|
|
641
|
+
watcher.unwatch(p);
|
|
642
|
+
currentWatchedPaths.delete(p);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
for (const p of newPaths) {
|
|
646
|
+
if (!currentWatchedPaths.has(p)) {
|
|
647
|
+
if (currentWatchedPaths.size >= MAX_WATCHED_PATHS) {
|
|
648
|
+
console.warn(
|
|
649
|
+
`[rainbowindex] Watched path count (${currentWatchedPaths.size}) exceeds ${MAX_WATCHED_PATHS} limit. This may cause EMFILE errors. Narrow your @source patterns or increase the OS file descriptor limit (ulimit -n).`
|
|
650
|
+
);
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
watcher.add(p);
|
|
654
|
+
currentWatchedPaths.add(p);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
syncSourceWatchPaths(initialBuild.theme);
|
|
659
|
+
const rebuild = () => {
|
|
660
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
661
|
+
debounceTimer = setTimeout(async () => {
|
|
662
|
+
if (building) {
|
|
663
|
+
pendingRebuild = true;
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (errorsPaused) {
|
|
667
|
+
errorsPaused = false;
|
|
668
|
+
consecutiveErrors = 0;
|
|
669
|
+
} else if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
670
|
+
console.error(
|
|
671
|
+
`[rainbowindex] Paused rebuilds after ${MAX_CONSECUTIVE_ERRORS} consecutive errors. Fix the issue and save a file to resume.`
|
|
672
|
+
);
|
|
673
|
+
errorsPaused = true;
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const now = Date.now();
|
|
677
|
+
const elapsed = now - lastBuildStart;
|
|
678
|
+
if (lastBuildStart > 0 && elapsed < MIN_REBUILD_INTERVAL_MS) {
|
|
679
|
+
pendingRebuild = true;
|
|
680
|
+
setTimeout(rebuild, MIN_REBUILD_INTERVAL_MS - elapsed);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
lastBuildStart = now;
|
|
684
|
+
building = true;
|
|
685
|
+
buildDonePromise = new Promise((resolveBuild) => {
|
|
686
|
+
buildDoneResolve = resolveBuild;
|
|
687
|
+
});
|
|
688
|
+
try {
|
|
689
|
+
const result = await buildAndWrite(opts, cwd, outputFile);
|
|
690
|
+
console.log(`[rainbowindex] Rebuilt: ${outputFile}`);
|
|
691
|
+
consecutiveErrors = 0;
|
|
692
|
+
errorsPaused = false;
|
|
693
|
+
syncSourceWatchPaths(result.theme);
|
|
694
|
+
} catch (err) {
|
|
695
|
+
consecutiveErrors++;
|
|
696
|
+
console.error("[rainbowindex] Build error:", err);
|
|
697
|
+
} finally {
|
|
698
|
+
building = false;
|
|
699
|
+
buildDoneResolve?.();
|
|
700
|
+
buildDoneResolve = null;
|
|
701
|
+
buildDonePromise = null;
|
|
702
|
+
if (pendingRebuild) {
|
|
703
|
+
pendingRebuild = false;
|
|
704
|
+
rebuild();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}, 100);
|
|
708
|
+
};
|
|
709
|
+
watcher.on("change", rebuild);
|
|
710
|
+
watcher.on("add", rebuild);
|
|
711
|
+
watcher.on("unlink", rebuild);
|
|
712
|
+
watcher.on("error", (err) => {
|
|
713
|
+
console.error("[rainbowindex] Watcher error:", err);
|
|
714
|
+
});
|
|
715
|
+
let cleanupCalled = false;
|
|
716
|
+
const cleanup = () => {
|
|
717
|
+
if (cleanupCalled) return;
|
|
718
|
+
cleanupCalled = true;
|
|
719
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
720
|
+
pendingRebuild = false;
|
|
721
|
+
const doExit = () => {
|
|
722
|
+
watcher.close().then(() => {
|
|
723
|
+
console.log("\n[rainbowindex] Watcher stopped.");
|
|
724
|
+
process.exit(0);
|
|
725
|
+
}).catch(() => {
|
|
726
|
+
process.exit(1);
|
|
727
|
+
});
|
|
728
|
+
};
|
|
729
|
+
if (building && buildDonePromise) {
|
|
730
|
+
const timeout = new Promise((r) => setTimeout(r, 5e3));
|
|
731
|
+
Promise.race([buildDonePromise, timeout]).then(doExit);
|
|
732
|
+
} else {
|
|
733
|
+
doExit();
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
process.on("SIGINT", cleanup);
|
|
737
|
+
process.on("SIGTERM", cleanup);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/cli/vite-setup.ts
|
|
741
|
+
import { spawn } from "child_process";
|
|
742
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
743
|
+
import { mkdir as mkdir2, readFile as readFile3, readdir, writeFile as writeFile3 } from "fs/promises";
|
|
744
|
+
import { dirname as dirname3, relative as relative2, resolve as resolve5 } from "path";
|
|
745
|
+
var VITE_CONFIG_FILES = [
|
|
746
|
+
"vite.config.ts",
|
|
747
|
+
"vite.config.mts",
|
|
748
|
+
"vite.config.js",
|
|
749
|
+
"vite.config.mjs",
|
|
750
|
+
"vite.config.cts",
|
|
751
|
+
"vite.config.cjs"
|
|
752
|
+
];
|
|
753
|
+
var ENTRY_FILES = [
|
|
754
|
+
"src/main.tsx",
|
|
755
|
+
"src/main.ts",
|
|
756
|
+
"src/main.jsx",
|
|
757
|
+
"src/main.js",
|
|
758
|
+
"main.tsx",
|
|
759
|
+
"main.ts",
|
|
760
|
+
"main.jsx",
|
|
761
|
+
"main.js"
|
|
762
|
+
];
|
|
763
|
+
var DEFAULT_CREATE_TEMPLATE = "react-ts";
|
|
764
|
+
var DEFAULT_RUNNER = {
|
|
765
|
+
run(command, args, cwd) {
|
|
766
|
+
return new Promise((resolveRun, rejectRun) => {
|
|
767
|
+
const child = spawn(command, args, {
|
|
768
|
+
cwd,
|
|
769
|
+
stdio: "inherit"
|
|
770
|
+
});
|
|
771
|
+
child.on("error", rejectRun);
|
|
772
|
+
child.on("exit", (code, signal) => {
|
|
773
|
+
if (code === 0) {
|
|
774
|
+
resolveRun();
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
rejectRun(
|
|
778
|
+
new Error(
|
|
779
|
+
signal ? `Command "${command}" terminated by signal ${signal}.` : `Command "${command} ${args.join(" ")}" failed with exit code ${code ?? 1}.`
|
|
780
|
+
)
|
|
781
|
+
);
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
async function initViteProject(opts, cwd, deps = {}) {
|
|
787
|
+
const project = loadViteProject(cwd);
|
|
788
|
+
const packageManager = deps.packageManager ?? detectPackageManager(cwd, deps.env);
|
|
789
|
+
const runner = deps.runner ?? DEFAULT_RUNNER;
|
|
790
|
+
const [cssPath, entryFile] = await Promise.all([
|
|
791
|
+
resolveStylesheetPath(cwd, opts.cssFile),
|
|
792
|
+
findEntryFile(cwd)
|
|
793
|
+
]);
|
|
794
|
+
let dependencyInstalled = false;
|
|
795
|
+
if (!hasRainbowIndexDependency(project.packageJSON)) {
|
|
796
|
+
await installRainbowIndex(cwd, packageManager, runner);
|
|
797
|
+
dependencyInstalled = true;
|
|
798
|
+
}
|
|
799
|
+
const [
|
|
800
|
+
{ path: configPath, changed: configChanged, created: configCreated },
|
|
801
|
+
{ changed: cssChanged, created: cssCreated }
|
|
802
|
+
] = await Promise.all([
|
|
803
|
+
ensureViteConfig(cwd, project.packageJSON, project.viteConfigPath),
|
|
804
|
+
ensureStylesheet(cssPath)
|
|
805
|
+
]);
|
|
806
|
+
const entryChanged = cssCreated ? await ensureStylesheetImport(entryFile, cssPath) : false;
|
|
807
|
+
const rootLabel = relative2(process.cwd(), cwd) || ".";
|
|
808
|
+
console.log(`[rainbowindex] Initialized Vite project: ${rootLabel}`);
|
|
809
|
+
if (dependencyInstalled) {
|
|
810
|
+
console.log("[rainbowindex] Added dev dependency: rainbowindex");
|
|
811
|
+
}
|
|
812
|
+
if (configCreated) {
|
|
813
|
+
console.log(
|
|
814
|
+
`[rainbowindex] Created Vite config: ${relative2(cwd, configPath).replaceAll("\\", "/")}`
|
|
815
|
+
);
|
|
816
|
+
} else if (configChanged) {
|
|
817
|
+
console.log(
|
|
818
|
+
`[rainbowindex] Updated Vite config: ${relative2(cwd, configPath).replaceAll("\\", "/")}`
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
if (cssCreated) {
|
|
822
|
+
console.log(
|
|
823
|
+
`[rainbowindex] Created stylesheet: ${relative2(cwd, cssPath).replaceAll("\\", "/")}`
|
|
824
|
+
);
|
|
825
|
+
} else if (cssChanged) {
|
|
826
|
+
console.log(
|
|
827
|
+
`[rainbowindex] Updated stylesheet: ${relative2(cwd, cssPath).replaceAll("\\", "/")}`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
if (entryChanged && entryFile) {
|
|
831
|
+
console.log(
|
|
832
|
+
`[rainbowindex] Added stylesheet import: ${relative2(cwd, entryFile).replaceAll("\\", "/")}`
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
if (!dependencyInstalled && !configChanged && !configCreated && !cssChanged && !cssCreated) {
|
|
836
|
+
console.log("[rainbowindex] Rainbow Index was already wired for Vite.");
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
console.log("");
|
|
840
|
+
console.log("[rainbowindex] Next steps:");
|
|
841
|
+
console.log(" 1. Restart your dev server (Vite must reload vite.config to pick up the plugin).");
|
|
842
|
+
console.log(
|
|
843
|
+
" 2. Add utility classes to your components \u2014 autocomplete works after `rainbowindex generate-types`."
|
|
844
|
+
);
|
|
845
|
+
console.log(
|
|
846
|
+
" 3. Customize via CSS directives (@color, @text, @spacing). See https://github.com/rainbowindex/rainbowindex#theming"
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
async function createViteProject(opts, cwd, deps = {}) {
|
|
850
|
+
if (!opts.targetDir) {
|
|
851
|
+
throw new Error("create requires a project directory. Example: rainbowindex create my-app");
|
|
852
|
+
}
|
|
853
|
+
const packageManager = deps.packageManager ?? detectPackageManager(cwd, deps.env);
|
|
854
|
+
const runner = deps.runner ?? DEFAULT_RUNNER;
|
|
855
|
+
const targetDir = opts.targetDir;
|
|
856
|
+
const targetRoot = resolve5(cwd, targetDir);
|
|
857
|
+
await ensureScaffoldTargetIsUsable(targetRoot);
|
|
858
|
+
const template = opts.template || DEFAULT_CREATE_TEMPLATE;
|
|
859
|
+
const scaffold = getCreateCommand(packageManager, targetDir, template);
|
|
860
|
+
console.log(`[rainbowindex] Creating Vite app with template "${template}"...`);
|
|
861
|
+
await runner.run(scaffold.command, scaffold.args, cwd);
|
|
862
|
+
await initViteProject({ cssFile: opts.cssFile }, targetRoot, {
|
|
863
|
+
...deps,
|
|
864
|
+
packageManager,
|
|
865
|
+
runner
|
|
866
|
+
});
|
|
867
|
+
const displayTarget = relative2(cwd, targetRoot) || ".";
|
|
868
|
+
console.log(`[rainbowindex] Ready: ${displayTarget}`);
|
|
869
|
+
console.log(`[rainbowindex] Next: cd ${displayTarget} && ${packageManager} run dev`);
|
|
870
|
+
}
|
|
871
|
+
function detectPackageManager(cwd, env = process.env) {
|
|
872
|
+
if (existsSync3(resolve5(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
873
|
+
if (existsSync3(resolve5(cwd, "yarn.lock"))) return "yarn";
|
|
874
|
+
if (existsSync3(resolve5(cwd, "bun.lock")) || existsSync3(resolve5(cwd, "bun.lockb"))) return "bun";
|
|
875
|
+
if (existsSync3(resolve5(cwd, "package-lock.json")) || existsSync3(resolve5(cwd, "npm-shrinkwrap.json"))) {
|
|
876
|
+
return "npm";
|
|
877
|
+
}
|
|
878
|
+
const packageJSON = readPackageJSONSync(cwd);
|
|
879
|
+
const packageManagerField = packageJSON?.packageManager ?? "";
|
|
880
|
+
if (packageManagerField.startsWith("pnpm@")) return "pnpm";
|
|
881
|
+
if (packageManagerField.startsWith("yarn@")) return "yarn";
|
|
882
|
+
if (packageManagerField.startsWith("bun@")) return "bun";
|
|
883
|
+
if (packageManagerField.startsWith("npm@")) return "npm";
|
|
884
|
+
const userAgent = env.npm_config_user_agent ?? "";
|
|
885
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm";
|
|
886
|
+
if (userAgent.startsWith("yarn/")) return "yarn";
|
|
887
|
+
if (userAgent.startsWith("bun/")) return "bun";
|
|
888
|
+
return "npm";
|
|
889
|
+
}
|
|
890
|
+
function loadViteProject(cwd) {
|
|
891
|
+
const packageJSON = readPackageJSONSync(cwd);
|
|
892
|
+
const hasViteDependency = packageJSON?.dependencies?.vite || packageJSON?.devDependencies?.vite || packageJSON?.optionalDependencies?.vite;
|
|
893
|
+
const hasViteScript = Object.values(packageJSON?.scripts ?? {}).some(
|
|
894
|
+
(script) => /\bvite\b/.test(script)
|
|
895
|
+
);
|
|
896
|
+
const viteConfigPath = findExistingViteConfig(cwd);
|
|
897
|
+
if (!hasViteDependency && !hasViteScript && !viteConfigPath) {
|
|
898
|
+
throw new Error(
|
|
899
|
+
`"${cwd}" does not look like a Vite project. Add Vite first or run rainbowindex create <app-name>.`
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return { packageJSON, viteConfigPath };
|
|
903
|
+
}
|
|
904
|
+
async function ensureScaffoldTargetIsUsable(targetRoot) {
|
|
905
|
+
if (!existsSync3(targetRoot)) return;
|
|
906
|
+
const entries = await readdir(targetRoot);
|
|
907
|
+
if (entries.length > 0) {
|
|
908
|
+
throw new Error(
|
|
909
|
+
`Target directory "${targetRoot}" already exists and is not empty. Choose a new directory.`
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
async function installRainbowIndex(cwd, packageManager, runner) {
|
|
914
|
+
const install = getInstallCommand(packageManager);
|
|
915
|
+
await runner.run(install.command, install.args, cwd);
|
|
916
|
+
}
|
|
917
|
+
async function ensureViteConfig(cwd, packageJSON, existingPath) {
|
|
918
|
+
if (!existingPath) {
|
|
919
|
+
const fileName = chooseViteConfigName(cwd, packageJSON);
|
|
920
|
+
const configPath = resolve5(cwd, fileName);
|
|
921
|
+
const content = 'import { defineConfig } from "vite";\nimport rainbowindex from "rainbowindex/vite";\n\nexport default defineConfig({\n plugins: [rainbowindex()],\n});\n';
|
|
922
|
+
await writeFile3(configPath, content, "utf-8");
|
|
923
|
+
return { path: configPath, changed: true, created: true };
|
|
924
|
+
}
|
|
925
|
+
const original = await readFile3(existingPath, "utf-8");
|
|
926
|
+
const updated = patchViteConfig(original, existingPath);
|
|
927
|
+
if (updated === original) {
|
|
928
|
+
return { path: existingPath, changed: false, created: false };
|
|
929
|
+
}
|
|
930
|
+
await writeFile3(existingPath, updated, "utf-8");
|
|
931
|
+
return { path: existingPath, changed: true, created: false };
|
|
932
|
+
}
|
|
933
|
+
var VITE_CONFIG_MANUAL_SNIPPET = `
|
|
934
|
+
import { defineConfig } from "vite";
|
|
935
|
+
import rainbowindex from "rainbowindex/vite";
|
|
936
|
+
|
|
937
|
+
export default defineConfig({
|
|
938
|
+
plugins: [rainbowindex()],
|
|
939
|
+
});
|
|
940
|
+
`;
|
|
941
|
+
function configPatchError(configPath, reason) {
|
|
942
|
+
return new Error(
|
|
943
|
+
`Could not update "${configPath}" automatically (${reason}).
|
|
944
|
+
|
|
945
|
+
Add the following to your Vite config manually:
|
|
946
|
+
${VITE_CONFIG_MANUAL_SNIPPET}`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
function patchViteConfig(source, configPath) {
|
|
950
|
+
let next = source;
|
|
951
|
+
if (!/["']rainbowindex\/vite["']/.test(next)) {
|
|
952
|
+
next = insertImportStatement(next, 'import rainbowindex from "rainbowindex/vite";');
|
|
953
|
+
}
|
|
954
|
+
if (/\brainbowindex\s*\(/.test(next)) {
|
|
955
|
+
return next;
|
|
956
|
+
}
|
|
957
|
+
const pluginsMatch = /\bplugins\s*:\s*\[/.exec(next);
|
|
958
|
+
if (pluginsMatch) {
|
|
959
|
+
const arrayStart = next.indexOf("[", pluginsMatch.index);
|
|
960
|
+
const arrayEnd = findMatchingDelimiter(next, arrayStart, "[", "]");
|
|
961
|
+
if (arrayEnd === -1) {
|
|
962
|
+
throw configPatchError(configPath, "could not find the end of the plugins array");
|
|
963
|
+
}
|
|
964
|
+
const inner = next.slice(arrayStart + 1, arrayEnd).trim();
|
|
965
|
+
const injection = inner.length === 0 ? "rainbowindex()" : "rainbowindex(), ";
|
|
966
|
+
return `${next.slice(0, arrayStart + 1)}${injection}${next.slice(arrayStart + 1)}`;
|
|
967
|
+
}
|
|
968
|
+
const objectStart = findConfigObjectStart(next);
|
|
969
|
+
if (objectStart === -1) {
|
|
970
|
+
throw configPatchError(configPath, "no `defineConfig({...})` or `export default {...}` found");
|
|
971
|
+
}
|
|
972
|
+
return `${next.slice(0, objectStart + 1)}
|
|
973
|
+
plugins: [rainbowindex()],${next.slice(objectStart + 1)}`;
|
|
974
|
+
}
|
|
975
|
+
function insertImportStatement(source, statement) {
|
|
976
|
+
let insertAt = 0;
|
|
977
|
+
const importRe = /^\s*import .*$/gm;
|
|
978
|
+
for (const match of source.matchAll(importRe)) {
|
|
979
|
+
insertAt = match.index + match[0].length;
|
|
980
|
+
if (source[insertAt] === "\n") insertAt++;
|
|
981
|
+
}
|
|
982
|
+
if (insertAt === 0) {
|
|
983
|
+
return `${statement}
|
|
984
|
+
|
|
985
|
+
${source}`;
|
|
986
|
+
}
|
|
987
|
+
return `${source.slice(0, insertAt)}${statement}
|
|
988
|
+
${source.slice(insertAt)}`;
|
|
989
|
+
}
|
|
990
|
+
function findConfigObjectStart(source) {
|
|
991
|
+
const defineConfigMatch = /defineConfig\s*\(\s*\{/.exec(source);
|
|
992
|
+
if (defineConfigMatch) {
|
|
993
|
+
return source.indexOf("{", defineConfigMatch.index);
|
|
994
|
+
}
|
|
995
|
+
const exportDefaultMatch = /export\s+default\s+\{/.exec(source);
|
|
996
|
+
if (exportDefaultMatch) {
|
|
997
|
+
return source.indexOf("{", exportDefaultMatch.index);
|
|
998
|
+
}
|
|
999
|
+
return -1;
|
|
1000
|
+
}
|
|
1001
|
+
function findMatchingDelimiter(input, start, open, close) {
|
|
1002
|
+
let depth = 0;
|
|
1003
|
+
let quote = null;
|
|
1004
|
+
let inLineComment = false;
|
|
1005
|
+
let inBlockComment = false;
|
|
1006
|
+
for (let i = start; i < input.length; i++) {
|
|
1007
|
+
const ch = input[i];
|
|
1008
|
+
const next = input[i + 1];
|
|
1009
|
+
if (inLineComment) {
|
|
1010
|
+
if (ch === "\n") inLineComment = false;
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
if (inBlockComment) {
|
|
1014
|
+
if (ch === "*" && next === "/") {
|
|
1015
|
+
inBlockComment = false;
|
|
1016
|
+
i++;
|
|
1017
|
+
}
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (quote) {
|
|
1021
|
+
if (ch === "\\") {
|
|
1022
|
+
i++;
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
if (ch === quote) quote = null;
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
if (ch === "/" && next === "/") {
|
|
1029
|
+
inLineComment = true;
|
|
1030
|
+
i++;
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (ch === "/" && next === "*") {
|
|
1034
|
+
inBlockComment = true;
|
|
1035
|
+
i++;
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
1039
|
+
quote = ch;
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (ch === open) {
|
|
1043
|
+
depth++;
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (ch === close) {
|
|
1047
|
+
depth--;
|
|
1048
|
+
if (depth === 0) return i;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return -1;
|
|
1052
|
+
}
|
|
1053
|
+
async function resolveStylesheetPath(cwd, cssFile) {
|
|
1054
|
+
if (cssFile) return resolve5(cwd, cssFile);
|
|
1055
|
+
const importedCSS = await findImportedStylesheet(cwd);
|
|
1056
|
+
if (importedCSS) return importedCSS;
|
|
1057
|
+
for (const candidate of CSS_CANDIDATES) {
|
|
1058
|
+
const fullPath = resolve5(cwd, candidate);
|
|
1059
|
+
if (existsSync3(fullPath)) return fullPath;
|
|
1060
|
+
}
|
|
1061
|
+
return existsSync3(resolve5(cwd, "src")) ? resolve5(cwd, "src/index.css") : resolve5(cwd, "index.css");
|
|
1062
|
+
}
|
|
1063
|
+
async function ensureStylesheet(cssPath) {
|
|
1064
|
+
if (!existsSync3(cssPath)) {
|
|
1065
|
+
await mkdir2(dirname3(cssPath), { recursive: true });
|
|
1066
|
+
await writeFile3(cssPath, '@import "rainbowindex";\n', "utf-8");
|
|
1067
|
+
return { changed: true, created: true };
|
|
1068
|
+
}
|
|
1069
|
+
const original = await readFile3(cssPath, "utf-8");
|
|
1070
|
+
if (/@import\s+["']rainbowindex["']/.test(original)) {
|
|
1071
|
+
return { changed: false, created: false };
|
|
1072
|
+
}
|
|
1073
|
+
const updated = insertRainbowImport(original);
|
|
1074
|
+
await writeFile3(cssPath, updated, "utf-8");
|
|
1075
|
+
return { changed: true, created: false };
|
|
1076
|
+
}
|
|
1077
|
+
function insertRainbowImport(source) {
|
|
1078
|
+
const bom = source.charCodeAt(0) === 65279 ? "\uFEFF" : "";
|
|
1079
|
+
const content = bom ? source.slice(1) : source;
|
|
1080
|
+
const charsetMatch = /^\s*@charset\s+[^;]+;\s*/i.exec(content);
|
|
1081
|
+
if (charsetMatch) {
|
|
1082
|
+
return `${bom}${charsetMatch[0]}@import "rainbowindex";
|
|
1083
|
+
|
|
1084
|
+
${content.slice(charsetMatch[0].length)}`;
|
|
1085
|
+
}
|
|
1086
|
+
if (content.trim().length === 0) {
|
|
1087
|
+
return `${bom}@import "rainbowindex";
|
|
1088
|
+
`;
|
|
1089
|
+
}
|
|
1090
|
+
return `${bom}@import "rainbowindex";
|
|
1091
|
+
|
|
1092
|
+
${content}`;
|
|
1093
|
+
}
|
|
1094
|
+
async function ensureStylesheetImport(entryFile, cssPath) {
|
|
1095
|
+
if (!entryFile || !existsSync3(entryFile)) return false;
|
|
1096
|
+
const original = await readFile3(entryFile, "utf-8");
|
|
1097
|
+
const relativePath = toImportPath(dirname3(entryFile), cssPath);
|
|
1098
|
+
if (hasStylesheetImport(original, relativePath)) {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
const updated = insertImportStatement(original, `import "${relativePath}";`);
|
|
1102
|
+
await writeFile3(entryFile, updated, "utf-8");
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
async function findImportedStylesheet(cwd) {
|
|
1106
|
+
for (const entryFile of ENTRY_FILES) {
|
|
1107
|
+
const fullPath = resolve5(cwd, entryFile);
|
|
1108
|
+
if (!existsSync3(fullPath)) continue;
|
|
1109
|
+
const content = await readFile3(fullPath, "utf-8");
|
|
1110
|
+
const matches = content.matchAll(
|
|
1111
|
+
/import\s+(?:.+?\s+from\s+)?["']([^"']+\.css(?:\?[^"']*)?)["']/g
|
|
1112
|
+
);
|
|
1113
|
+
for (const match of matches) {
|
|
1114
|
+
const importPath = match[1].split("?")[0];
|
|
1115
|
+
if (!importPath.startsWith(".")) continue;
|
|
1116
|
+
const stylesheet = resolve5(dirname3(fullPath), importPath);
|
|
1117
|
+
if (existsSync3(stylesheet)) return stylesheet;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
async function findEntryFile(cwd) {
|
|
1123
|
+
for (const entryFile of ENTRY_FILES) {
|
|
1124
|
+
const fullPath = resolve5(cwd, entryFile);
|
|
1125
|
+
if (existsSync3(fullPath)) return fullPath;
|
|
1126
|
+
}
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
function hasStylesheetImport(source, importPath) {
|
|
1130
|
+
const escapedPath = importPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1131
|
+
return new RegExp(`import\\s+(?:.+?\\s+from\\s+)?["']${escapedPath}["']`).test(source);
|
|
1132
|
+
}
|
|
1133
|
+
function toImportPath(fromDir, targetFile) {
|
|
1134
|
+
const rel = relative2(fromDir, targetFile).replaceAll("\\", "/");
|
|
1135
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
1136
|
+
}
|
|
1137
|
+
function findExistingViteConfig(cwd) {
|
|
1138
|
+
for (const name of VITE_CONFIG_FILES) {
|
|
1139
|
+
const fullPath = resolve5(cwd, name);
|
|
1140
|
+
if (existsSync3(fullPath)) return fullPath;
|
|
1141
|
+
}
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
function chooseViteConfigName(cwd, packageJSON) {
|
|
1145
|
+
if (existsSync3(resolve5(cwd, "tsconfig.json")) || existsSync3(resolve5(cwd, "tsconfig.app.json")) || existsSync3(resolve5(cwd, "src/main.ts")) || existsSync3(resolve5(cwd, "src/main.tsx")) || packageJSON?.dependencies?.typescript || packageJSON?.devDependencies?.typescript) {
|
|
1146
|
+
return "vite.config.ts";
|
|
1147
|
+
}
|
|
1148
|
+
return packageJSON?.type === "module" ? "vite.config.js" : "vite.config.mjs";
|
|
1149
|
+
}
|
|
1150
|
+
function hasRainbowIndexDependency(packageJSON) {
|
|
1151
|
+
if (!packageJSON) return false;
|
|
1152
|
+
return Boolean(
|
|
1153
|
+
packageJSON.dependencies?.rainbowindex || packageJSON.devDependencies?.rainbowindex || packageJSON.optionalDependencies?.rainbowindex
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
function getCreateCommand(packageManager, targetDir, template) {
|
|
1157
|
+
switch (packageManager) {
|
|
1158
|
+
case "pnpm":
|
|
1159
|
+
return {
|
|
1160
|
+
command: "pnpm",
|
|
1161
|
+
args: ["create", "vite", targetDir, "--template", template]
|
|
1162
|
+
};
|
|
1163
|
+
case "yarn":
|
|
1164
|
+
return {
|
|
1165
|
+
command: "yarn",
|
|
1166
|
+
args: ["create", "vite", targetDir, "--template", template]
|
|
1167
|
+
};
|
|
1168
|
+
case "bun":
|
|
1169
|
+
return {
|
|
1170
|
+
command: "bun",
|
|
1171
|
+
args: ["create", "vite", targetDir, "--template", template]
|
|
1172
|
+
};
|
|
1173
|
+
default:
|
|
1174
|
+
return {
|
|
1175
|
+
command: "npm",
|
|
1176
|
+
args: ["create", "vite@latest", targetDir, "--", "--template", template]
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
function getInstallCommand(packageManager) {
|
|
1181
|
+
switch (packageManager) {
|
|
1182
|
+
case "pnpm":
|
|
1183
|
+
return { command: "pnpm", args: ["add", "-D", "rainbowindex"] };
|
|
1184
|
+
case "yarn":
|
|
1185
|
+
return { command: "yarn", args: ["add", "-D", "rainbowindex"] };
|
|
1186
|
+
case "bun":
|
|
1187
|
+
return { command: "bun", args: ["add", "-d", "rainbowindex"] };
|
|
1188
|
+
default:
|
|
1189
|
+
return { command: "npm", args: ["install", "-D", "rainbowindex"] };
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
function readPackageJSONSync(cwd) {
|
|
1193
|
+
const packagePath = resolve5(cwd, "package.json");
|
|
1194
|
+
if (!existsSync3(packagePath)) return null;
|
|
1195
|
+
try {
|
|
1196
|
+
return JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
1197
|
+
} catch {
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/entries/cli.ts
|
|
1203
|
+
function getVersion() {
|
|
1204
|
+
return true ? "0.2.0" : "unknown";
|
|
1205
|
+
}
|
|
1206
|
+
async function main() {
|
|
1207
|
+
const args = process.argv.slice(2);
|
|
1208
|
+
if (args.length === 0) {
|
|
1209
|
+
printHelp();
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const opts = parseArgs(args, { getVersion, printHelp });
|
|
1213
|
+
if (opts.earlyExit) return;
|
|
1214
|
+
const cwd = process.cwd();
|
|
1215
|
+
switch (opts.command) {
|
|
1216
|
+
case "init":
|
|
1217
|
+
await initViteProject(opts, cwd);
|
|
1218
|
+
break;
|
|
1219
|
+
case "create":
|
|
1220
|
+
await createViteProject(opts, cwd);
|
|
1221
|
+
break;
|
|
1222
|
+
case "generate-types":
|
|
1223
|
+
await generateTypes(opts, cwd);
|
|
1224
|
+
break;
|
|
1225
|
+
case "preload-fonts":
|
|
1226
|
+
await preloadFonts(opts, cwd);
|
|
1227
|
+
break;
|
|
1228
|
+
case "build": {
|
|
1229
|
+
if (opts.watch) {
|
|
1230
|
+
await watchMode(opts, cwd);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
if (opts.output) {
|
|
1234
|
+
await buildAndWrite(opts, cwd, opts.output);
|
|
1235
|
+
console.log(`[rainbowindex] Built: ${opts.output}`);
|
|
1236
|
+
} else {
|
|
1237
|
+
const { css } = await buildCSS(opts, cwd);
|
|
1238
|
+
process.stdout.write(await minifyIfRequested(css, opts));
|
|
1239
|
+
}
|
|
1240
|
+
break;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
var isDirectExecution = (() => {
|
|
1245
|
+
const entry = process.argv[1];
|
|
1246
|
+
if (entry === void 0) return false;
|
|
1247
|
+
try {
|
|
1248
|
+
return import.meta.url === pathToFileURL(realpathSync(resolve6(entry))).href;
|
|
1249
|
+
} catch {
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
})();
|
|
1253
|
+
if (isDirectExecution) {
|
|
1254
|
+
void main().catch((err) => {
|
|
1255
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
1256
|
+
process.exitCode = 1;
|
|
1257
|
+
});
|
|
1258
|
+
}
|