cascivo 0.2.0 → 0.3.4
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/LICENSE +1 -1
- package/README.md +10 -1
- package/dist/{audit-BRn4MeXD.mjs → audit-InQqdY1s.mjs} +3 -3
- package/dist/{audit-BRn4MeXD.mjs.map → audit-InQqdY1s.mjs.map} +1 -1
- package/dist/{config-CsnCR5eh.mjs → config-BsyJbMvD.mjs} +28 -4
- package/dist/config-BsyJbMvD.mjs.map +1 -0
- package/dist/drift-Ciw5qeEt.mjs +56 -0
- package/dist/drift-Ciw5qeEt.mjs.map +1 -0
- package/dist/{eject-B92gZhLX.mjs → eject-Br8V7I3m.mjs} +3 -3
- package/dist/{eject-B92gZhLX.mjs.map → eject-Br8V7I3m.mjs.map} +1 -1
- package/dist/{http-CLkdTpxD.mjs → http-CJZ5W0Fa.mjs} +56 -5
- package/dist/http-CJZ5W0Fa.mjs.map +1 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +536 -222
- package/dist/index.mjs.map +1 -1
- package/dist/peer-versions-B2bCgko-.mjs +244 -0
- package/dist/peer-versions-B2bCgko-.mjs.map +1 -0
- package/dist/{template-init-DGEgatij.mjs → template-init-C46fRXo-.mjs} +2 -2
- package/dist/{template-init-DGEgatij.mjs.map → template-init-C46fRXo-.mjs.map} +1 -1
- package/package.json +4 -3
- package/readme.body.md +10 -1
- package/dist/config-CsnCR5eh.mjs.map +0 -1
- package/dist/drift-D7JFzpP_.mjs +0 -8
- package/dist/drift-D7JFzpP_.mjs.map +0 -1
- package/dist/http-CLkdTpxD.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as detectPackageManager, c as __exportAll, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as loadConfig, t as CASCIVO_HOST } from "./config-BsyJbMvD.mjs";
|
|
3
3
|
import { n as resolveOutputPath, r as writeFileSafe, t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
|
|
4
|
-
import { t as fetchJson } from "./http-
|
|
4
|
+
import { r as fetchTextRetry, t as fetchJson } from "./http-CJZ5W0Fa.mjs";
|
|
5
|
+
import { a as updateLockEntry, c as fileName, i as sha256, l as findComponent, n as createLock, o as writeLock, r as readLock, s as fetchRegistry, t as checkPeerVersions } from "./peer-versions-B2bCgko-.mjs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
5
7
|
import { argv, stdin, stdout } from "node:process";
|
|
6
8
|
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
10
|
import { basename, dirname, join, resolve } from "node:path";
|
|
8
11
|
import { spawnSync } from "node:child_process";
|
|
9
|
-
import {
|
|
10
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
11
|
-
import { createHash } from "node:crypto";
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
12
13
|
import { asTemplateMeta, buildRegistry, isTemplateItem, parseLegacyRegistry, validateIndex, validateTemplate } from "@cascivo/registry";
|
|
13
14
|
import { createInterface } from "node:readline/promises";
|
|
14
15
|
//#region src/utils/exec.ts
|
|
@@ -31,138 +32,6 @@ function installPackages(packages, cwd = process.cwd()) {
|
|
|
31
32
|
return true;
|
|
32
33
|
}
|
|
33
34
|
//#endregion
|
|
34
|
-
//#region src/utils/registry.ts
|
|
35
|
-
function asStringArray(value) {
|
|
36
|
-
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
37
|
-
}
|
|
38
|
-
/** Validate and normalize raw JSON into a typed Registry. Throws on bad shape. */
|
|
39
|
-
function parseRegistry(raw) {
|
|
40
|
-
if (typeof raw !== "object" || raw === null) throw new Error("Invalid registry: expected an object");
|
|
41
|
-
const obj = raw;
|
|
42
|
-
if (!Array.isArray(obj.components)) throw new Error("Invalid registry: \"components\" must be an array");
|
|
43
|
-
const components = obj.components.map((entry, i) => {
|
|
44
|
-
if (typeof entry !== "object" || entry === null) throw new Error(`Invalid registry: component at index ${i} is not an object`);
|
|
45
|
-
const c = entry;
|
|
46
|
-
if (typeof c.name !== "string") throw new Error(`Invalid registry: component at index ${i} is missing "name"`);
|
|
47
|
-
const rawType = c.type;
|
|
48
|
-
const type = rawType === "component" || rawType === "layout" || rawType === "block" || rawType === "chart" ? rawType : void 0;
|
|
49
|
-
const rawMeta = c.meta;
|
|
50
|
-
const metaName = typeof rawMeta === "object" && rawMeta !== null && typeof rawMeta.name === "string" ? rawMeta.name : c.name;
|
|
51
|
-
const result = {
|
|
52
|
-
name: c.name,
|
|
53
|
-
description: typeof c.description === "string" ? c.description : "",
|
|
54
|
-
category: typeof c.category === "string" ? c.category : "",
|
|
55
|
-
version: typeof c.version === "string" ? c.version : "0.0.0",
|
|
56
|
-
files: asStringArray(c.files),
|
|
57
|
-
dependencies: asStringArray(c.dependencies),
|
|
58
|
-
tags: asStringArray(c.tags),
|
|
59
|
-
meta: { name: metaName }
|
|
60
|
-
};
|
|
61
|
-
if (type !== void 0) result.type = type;
|
|
62
|
-
if (typeof c.install === "string") result.install = c.install;
|
|
63
|
-
if (Array.isArray(c.registryDependencies)) {
|
|
64
|
-
const deps = asStringArray(c.registryDependencies);
|
|
65
|
-
if (deps.length > 0) result.registryDependencies = deps;
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
});
|
|
69
|
-
const blocks = Array.isArray(obj.blocks) ? obj.blocks.flatMap((entry) => {
|
|
70
|
-
if (typeof entry !== "object" || entry === null) return [];
|
|
71
|
-
const b = entry;
|
|
72
|
-
if (typeof b.name !== "string") return [];
|
|
73
|
-
const rawScreenshot = typeof b.screenshot === "object" && b.screenshot !== null ? b.screenshot : {};
|
|
74
|
-
return [{
|
|
75
|
-
name: b.name,
|
|
76
|
-
type: "block",
|
|
77
|
-
displayName: typeof b.displayName === "string" ? b.displayName : b.name,
|
|
78
|
-
description: typeof b.description === "string" ? b.description : "",
|
|
79
|
-
category: typeof b.category === "string" ? b.category : "",
|
|
80
|
-
version: typeof b.version === "string" ? b.version : "0.0.0",
|
|
81
|
-
files: asStringArray(b.files),
|
|
82
|
-
dependencies: asStringArray(b.dependencies),
|
|
83
|
-
tags: asStringArray(b.tags),
|
|
84
|
-
screenshot: {
|
|
85
|
-
light: typeof rawScreenshot.light === "string" ? rawScreenshot.light : "",
|
|
86
|
-
dark: typeof rawScreenshot.dark === "string" ? rawScreenshot.dark : ""
|
|
87
|
-
}
|
|
88
|
-
}];
|
|
89
|
-
}) : [];
|
|
90
|
-
return {
|
|
91
|
-
version: typeof obj.version === "string" ? obj.version : "0.0.0",
|
|
92
|
-
generatedAt: typeof obj.generatedAt === "string" ? obj.generatedAt : "",
|
|
93
|
-
components,
|
|
94
|
-
blocks
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
/** Fetch and parse the registry from a URL. */
|
|
98
|
-
async function fetchRegistry(url) {
|
|
99
|
-
const res = await fetch(url);
|
|
100
|
-
if (!res.ok) throw new Error(`Failed to fetch registry from ${url}: ${res.status} ${res.statusText}`);
|
|
101
|
-
return parseRegistry(await res.json());
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Find a component by name (case-insensitive, full name or unambiguous suffix).
|
|
105
|
-
*
|
|
106
|
-
* Examples:
|
|
107
|
-
* "layout/app-shell" → exact match on full name
|
|
108
|
-
* "app-shell" → suffix match if exactly one entry ends with "/app-shell"
|
|
109
|
-
* "button" → exact match (no slash, no suffix ambiguity)
|
|
110
|
-
*/
|
|
111
|
-
function findComponent(registry, name) {
|
|
112
|
-
const target = name.toLowerCase();
|
|
113
|
-
if (target.startsWith("block/")) {
|
|
114
|
-
const blockName = target.slice(6);
|
|
115
|
-
const block = (registry.blocks ?? []).find((b) => b.name.toLowerCase() === blockName);
|
|
116
|
-
if (!block) throw new Error(`Block "${blockName}" not found in registry.`);
|
|
117
|
-
return block;
|
|
118
|
-
}
|
|
119
|
-
const exact = registry.components.find((c) => c.name.toLowerCase() === target);
|
|
120
|
-
if (exact) return exact;
|
|
121
|
-
const suffix = `/${target}`;
|
|
122
|
-
const matches = registry.components.filter((c) => c.name.toLowerCase().endsWith(suffix));
|
|
123
|
-
if (matches.length === 1) return matches[0];
|
|
124
|
-
}
|
|
125
|
-
/** Extract the file name from a registry file URL. */
|
|
126
|
-
function fileName(url) {
|
|
127
|
-
const stripped = url.split(/[?#]/)[0] ?? url;
|
|
128
|
-
return stripped.split("/").pop() || stripped;
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/utils/lock.ts
|
|
132
|
-
function sha256(content) {
|
|
133
|
-
return `sha256-${createHash("sha256").update(content).digest("hex")}`;
|
|
134
|
-
}
|
|
135
|
-
const LOCK_FILENAME = "cascade.lock";
|
|
136
|
-
async function readLock(cwd = process.cwd()) {
|
|
137
|
-
const path = join(cwd, LOCK_FILENAME);
|
|
138
|
-
if (!existsSync(path)) return null;
|
|
139
|
-
try {
|
|
140
|
-
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
141
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
142
|
-
return raw;
|
|
143
|
-
} catch {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async function writeLock(lock, cwd = process.cwd()) {
|
|
148
|
-
await writeFile(join(cwd, LOCK_FILENAME), `${JSON.stringify(lock, null, 2)}\n`, "utf8");
|
|
149
|
-
}
|
|
150
|
-
function createLock() {
|
|
151
|
-
return {
|
|
152
|
-
lockVersion: 1,
|
|
153
|
-
items: {}
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function updateLockEntry(lock, name, entry) {
|
|
157
|
-
return {
|
|
158
|
-
...lock,
|
|
159
|
-
items: {
|
|
160
|
-
...lock.items,
|
|
161
|
-
[name]: entry
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
//#endregion
|
|
166
35
|
//#region src/utils/resolve.ts
|
|
167
36
|
const GITHUB_RE = /^([^/]+)\/([^/]+)\/([^#]+)(?:#(.+))?$/;
|
|
168
37
|
function expandEnv(str, env = process.env) {
|
|
@@ -240,7 +109,7 @@ async function resolveItemUrl(addr, config, env = process.env, resolveNamespaceH
|
|
|
240
109
|
const hookResult = await resolveNamespaceHook(ns);
|
|
241
110
|
if (hookResult) return { url: resolveNamespaceUrl(ns, name, hookResult, env) };
|
|
242
111
|
}
|
|
243
|
-
if (ns === "@cascade") return { url:
|
|
112
|
+
if (ns === "@cascivo" || ns === "@cascade") return { url: `${CASCIVO_HOST}/r/${name}.json` };
|
|
244
113
|
throw new Error(`Unknown namespace "${ns}". Add it to registries in cascivo.config.ts or check the directory at cascivo.com`);
|
|
245
114
|
}
|
|
246
115
|
}
|
|
@@ -276,7 +145,7 @@ async function resolveClosure(specs, config, env = process.env, resolveNamespace
|
|
|
276
145
|
}
|
|
277
146
|
//#endregion
|
|
278
147
|
//#region src/utils/directory.ts
|
|
279
|
-
const DIRECTORY_URL =
|
|
148
|
+
const DIRECTORY_URL = `${CASCIVO_HOST}/r/registries.json`;
|
|
280
149
|
async function resolveFromDirectory(ns, fetchFn) {
|
|
281
150
|
try {
|
|
282
151
|
let data;
|
|
@@ -299,15 +168,31 @@ async function resolveFromDirectory(ns, fetchFn) {
|
|
|
299
168
|
var add_exports = /* @__PURE__ */ __exportAll({
|
|
300
169
|
LAYOUT_ALIASES: () => LAYOUT_ALIASES,
|
|
301
170
|
add: () => add,
|
|
171
|
+
isThirdParty: () => isThirdParty,
|
|
302
172
|
resolveBareClosure: () => resolveBareClosure,
|
|
303
173
|
resolveTemplateTarget: () => resolveTemplateTarget
|
|
304
174
|
});
|
|
305
175
|
/** Dependencies always present in a cascade project — never auto-installed. */
|
|
306
176
|
const ASSUMED_DEPS = new Set(["react", "react-dom"]);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Copied components read `--cascivo-*` custom properties; without
|
|
179
|
+
* @cascivo/tokens or @cascivo/themes imported somewhere, they render unstyled.
|
|
180
|
+
* Print the wiring once after a successful install if neither is a dependency.
|
|
181
|
+
*/
|
|
182
|
+
function hintThemesIfMissing(cwd) {
|
|
183
|
+
let deps = {};
|
|
184
|
+
try {
|
|
185
|
+
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
186
|
+
deps = {
|
|
187
|
+
...pkg.dependencies,
|
|
188
|
+
...pkg.devDependencies
|
|
189
|
+
};
|
|
190
|
+
} catch {}
|
|
191
|
+
if (deps["@cascivo/themes"] !== void 0 || deps["@cascivo/tokens"] !== void 0) return;
|
|
192
|
+
console.log("\nStyling: cascivo components read --cascivo-* tokens, which this project does not import yet.");
|
|
193
|
+
console.log(" npm install @cascivo/themes");
|
|
194
|
+
console.log(" import '@cascivo/themes/all' // once, in your entry file");
|
|
195
|
+
console.log(" <html data-theme=\"light\"> // or any container");
|
|
311
196
|
}
|
|
312
197
|
/** Resolve where a template's own file is written — relative to the project root. */
|
|
313
198
|
function resolveTemplateTarget(cwd, target) {
|
|
@@ -362,6 +247,14 @@ function resolveBareClosure(registry, bareSpecs) {
|
|
|
362
247
|
missing
|
|
363
248
|
};
|
|
364
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* First-party items are hosted on cascivo.com or the cascivo/cascivo GitHub
|
|
252
|
+
* repo (raw file URLs). Anything else gets a review-before-use notice.
|
|
253
|
+
*/
|
|
254
|
+
const FIRST_PARTY_URL = /^https:\/\/(cascivo\.com|raw\.githubusercontent\.com\/cascivo\/cascivo)\//;
|
|
255
|
+
function isThirdParty(itemUrl) {
|
|
256
|
+
return !FIRST_PARTY_URL.test(itemUrl);
|
|
257
|
+
}
|
|
365
258
|
function isMultiRegistrySpec(name) {
|
|
366
259
|
return name.startsWith("@") || name.startsWith("http://") || name.startsWith("https://") || name.startsWith("./") || name.startsWith("/") || name.split("/").length >= 3;
|
|
367
260
|
}
|
|
@@ -372,7 +265,17 @@ async function add(names, config, opts = {}) {
|
|
|
372
265
|
}
|
|
373
266
|
const cwd = opts.cwd ?? process.cwd();
|
|
374
267
|
const multiSpecs = names.filter(isMultiRegistrySpec);
|
|
375
|
-
|
|
268
|
+
let bareSpecs = names.filter((n) => !isMultiRegistrySpec(n));
|
|
269
|
+
let registry = null;
|
|
270
|
+
if (bareSpecs.length > 0) {
|
|
271
|
+
registry = await fetchRegistry(config.registry);
|
|
272
|
+
const templateNames = new Set(registry.templates.map((t) => t.toLowerCase()));
|
|
273
|
+
const templateSpecs = bareSpecs.filter((n) => templateNames.has(n.toLowerCase()));
|
|
274
|
+
if (templateSpecs.length > 0) {
|
|
275
|
+
bareSpecs = bareSpecs.filter((n) => !templateNames.has(n.toLowerCase()));
|
|
276
|
+
multiSpecs.push(...templateSpecs.map((n) => `@cascivo/${n.toLowerCase()}`));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
376
279
|
let lock = await readLock(cwd) ?? createLock();
|
|
377
280
|
if (multiSpecs.length > 0) {
|
|
378
281
|
const plan = await resolveClosure(multiSpecs, config, process.env, resolveFromDirectory);
|
|
@@ -389,10 +292,16 @@ async function add(names, config, opts = {}) {
|
|
|
389
292
|
}
|
|
390
293
|
return;
|
|
391
294
|
}
|
|
392
|
-
const isThirdParty = (itemUrl) => !itemUrl.includes("cascivo.com");
|
|
393
295
|
for (const { item, itemUrl, registryBase } of plan.items) {
|
|
394
296
|
if (isThirdParty(itemUrl)) console.log(`\nReview before use — files from non-directory registry: ${registryBase}\n` + (item.files ?? []).map((f) => ` ${f.url}`).join("\n"));
|
|
395
|
-
|
|
297
|
+
let fileHashes;
|
|
298
|
+
try {
|
|
299
|
+
fileHashes = await installItem(item, config, cwd);
|
|
300
|
+
} catch (e) {
|
|
301
|
+
console.error(`Failed to install ${item.name}: ${e instanceof Error ? e.message : e}`);
|
|
302
|
+
process.exitCode = 1;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
396
305
|
lock = updateLockEntry(lock, item.name, {
|
|
397
306
|
registry: registryBase,
|
|
398
307
|
version: item.version,
|
|
@@ -401,9 +310,13 @@ async function add(names, config, opts = {}) {
|
|
|
401
310
|
});
|
|
402
311
|
}
|
|
403
312
|
await writeLock(lock, cwd);
|
|
404
|
-
|
|
313
|
+
if (bareSpecs.length === 0) {
|
|
314
|
+
hintThemesIfMissing(cwd);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
405
317
|
}
|
|
406
|
-
|
|
318
|
+
if (bareSpecs.length === 0) return;
|
|
319
|
+
registry ??= await fetchRegistry(config.registry);
|
|
407
320
|
const missingDeps = /* @__PURE__ */ new Set();
|
|
408
321
|
const registryBase = config.registry.replace(/\/registry\.json$/, "").replace(/\/r$/, "");
|
|
409
322
|
for (const spec of bareSpecs) {
|
|
@@ -414,18 +327,31 @@ async function add(names, config, opts = {}) {
|
|
|
414
327
|
}
|
|
415
328
|
const { resolved, missing } = resolveBareClosure(registry, bareSpecs);
|
|
416
329
|
for (const name of missing) console.error(`Component "${name}" not found in registry. Run "cascivo list".`);
|
|
330
|
+
const peerFloors = {};
|
|
331
|
+
for (const { entry } of resolved) for (const [pkg, floor] of Object.entries(entry.peerVersions ?? {})) peerFloors[pkg] = floor;
|
|
417
332
|
for (const { entry, requested } of resolved) {
|
|
418
|
-
if (entry.
|
|
419
|
-
const pkg = entry.install
|
|
420
|
-
console.log(`
|
|
333
|
+
if (entry.install) {
|
|
334
|
+
const pkg = entry.install;
|
|
335
|
+
console.log(`"${entry.name}" is distributed as the npm package ${pkg}.`);
|
|
421
336
|
console.log(`Install it with: npm install ${pkg}`);
|
|
422
337
|
console.log(`Then import: import { ${entry.meta.name} } from '${pkg}'`);
|
|
338
|
+
if (entry.styles) console.log(`And import its stylesheet once: import '${entry.styles}'`);
|
|
423
339
|
continue;
|
|
424
340
|
}
|
|
425
341
|
const outputName = entry.name.includes("/") ? entry.name.split("/").pop() : entry.name;
|
|
426
342
|
const fileHashes = {};
|
|
427
|
-
|
|
428
|
-
|
|
343
|
+
let fetched;
|
|
344
|
+
try {
|
|
345
|
+
fetched = await Promise.all(entry.files.map(async (url) => ({
|
|
346
|
+
url,
|
|
347
|
+
content: await fetchTextRetry(url)
|
|
348
|
+
})));
|
|
349
|
+
} catch (e) {
|
|
350
|
+
console.error(`Failed to install ${entry.name}: ${e instanceof Error ? e.message : e}`);
|
|
351
|
+
process.exitCode = 1;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
for (const { url, content } of fetched) {
|
|
429
355
|
const dest = resolveOutputPath(config.outputDir, outputName, fileName(url), cwd);
|
|
430
356
|
await writeFileSafe(dest, content);
|
|
431
357
|
fileHashes[dest] = sha256(content);
|
|
@@ -441,7 +367,15 @@ async function add(names, config, opts = {}) {
|
|
|
441
367
|
console.log(`Added ${entry.name} to ${config.outputDir}/${outputName}/${suffix}`);
|
|
442
368
|
}
|
|
443
369
|
if (missingDeps.size > 0) installPackages([...missingDeps]);
|
|
370
|
+
if (Object.keys(peerFloors).length > 0) {
|
|
371
|
+
const violations = await checkPeerVersions(cwd, peerFloors);
|
|
372
|
+
for (const v of violations) {
|
|
373
|
+
console.error(`\nWarning: ${v.pkg} ${v.installed ? `${v.installed} is` : "is not"} installed, but the copied component source needs ${v.pkg} ${v.required}. Run: npm install ${v.pkg}@latest`);
|
|
374
|
+
process.exitCode = 1;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
444
377
|
await writeLock(lock, cwd);
|
|
378
|
+
hintThemesIfMissing(cwd);
|
|
445
379
|
}
|
|
446
380
|
/**
|
|
447
381
|
* Install a single resolved item, returning a map of written path → content
|
|
@@ -455,13 +389,14 @@ async function installItem(item, config, cwd) {
|
|
|
455
389
|
const missingDeps = /* @__PURE__ */ new Set();
|
|
456
390
|
const template = isTemplateItem(item);
|
|
457
391
|
const outputName = item.name.includes("/") ? item.name.split("/").pop() : item.name;
|
|
458
|
-
|
|
459
|
-
|
|
392
|
+
const fetched = await Promise.all((item.files ?? []).map(async (file) => ({
|
|
393
|
+
file,
|
|
394
|
+
content: await fetchTextRetry(file.url)
|
|
395
|
+
})));
|
|
396
|
+
for (const { file, content } of fetched) {
|
|
460
397
|
const dest = template && file.target ? resolveTemplateTarget(cwd, file.target) : resolveOutputPath(config.outputDir, outputName, fileName(file.url), cwd);
|
|
461
398
|
await writeFileSafe(dest, content);
|
|
462
399
|
fileHashes[dest] = sha256(content);
|
|
463
|
-
} catch {
|
|
464
|
-
console.warn(` Could not fetch ${file.url}`);
|
|
465
400
|
}
|
|
466
401
|
for (const dep of item.dependencies ?? []) if (!ASSUMED_DEPS.has(dep)) missingDeps.add(dep);
|
|
467
402
|
if (missingDeps.size > 0) installPackages([...missingDeps]);
|
|
@@ -473,11 +408,6 @@ async function installItem(item, config, cwd) {
|
|
|
473
408
|
}
|
|
474
409
|
//#endregion
|
|
475
410
|
//#region src/commands/create.ts
|
|
476
|
-
const THEMES$2 = [
|
|
477
|
-
"light",
|
|
478
|
-
"dark",
|
|
479
|
-
"warm"
|
|
480
|
-
];
|
|
481
411
|
/** Version specifier used for every `@cascivo/*` dependency in generated apps. */
|
|
482
412
|
const CASCIVO_DEP = "latest";
|
|
483
413
|
/** Slug suitable for a union-member string literal: lower-kebab, alnum only. */
|
|
@@ -721,7 +651,7 @@ function cascivoConfig(opts) {
|
|
|
721
651
|
return `import type { CascadeConfig } from 'cascivo'
|
|
722
652
|
|
|
723
653
|
const config: CascadeConfig = {
|
|
724
|
-
registry: '
|
|
654
|
+
registry: '${CASCIVO_HOST}/registry.json',
|
|
725
655
|
outputDir: 'src/components/ui',
|
|
726
656
|
theme: '${opts.theme}',
|
|
727
657
|
}
|
|
@@ -807,7 +737,7 @@ function buildScaffold(opts) {
|
|
|
807
737
|
}))
|
|
808
738
|
];
|
|
809
739
|
}
|
|
810
|
-
function flagValue(args, key) {
|
|
740
|
+
function flagValue$1(args, key) {
|
|
811
741
|
const eq = args.find((a) => a.startsWith(`--${key}=`));
|
|
812
742
|
if (eq) return eq.slice(key.length + 3);
|
|
813
743
|
const idx = args.indexOf(`--${key}`);
|
|
@@ -822,8 +752,8 @@ const DEFAULT_SECTIONS = [
|
|
|
822
752
|
async function create(args, cwd = process.cwd()) {
|
|
823
753
|
const yes = args.includes("--yes") || args.includes("-y");
|
|
824
754
|
const nameArg = args.find((a) => !a.startsWith("-"));
|
|
825
|
-
const themeArg = flagValue(args, "theme");
|
|
826
|
-
const sectionsArg = flagValue(args, "sections");
|
|
755
|
+
const themeArg = flagValue$1(args, "theme");
|
|
756
|
+
const sectionsArg = flagValue$1(args, "sections");
|
|
827
757
|
const rl = !yes && stdin.isTTY ? createInterface({
|
|
828
758
|
input: stdin,
|
|
829
759
|
output: stdout
|
|
@@ -833,8 +763,8 @@ async function create(args, cwd = process.cwd()) {
|
|
|
833
763
|
if (!name && rl) name = (await rl.question("Project name? [my-cascivo-app]: ")).trim();
|
|
834
764
|
name = name || "my-cascivo-app";
|
|
835
765
|
let theme = (themeArg ?? "").toLowerCase();
|
|
836
|
-
if (!THEMES
|
|
837
|
-
const resolvedTheme = THEMES
|
|
766
|
+
if (!THEMES.includes(theme) && rl) theme = (await rl.question(`Theme? (${THEMES.join("/")}) [light]: `)).trim().toLowerCase();
|
|
767
|
+
const resolvedTheme = THEMES.includes(theme) ? theme : "light";
|
|
838
768
|
let sectionsInput = sectionsArg;
|
|
839
769
|
if (!sectionsInput && rl) sectionsInput = (await rl.question(`Nav sections? (comma-separated) [${DEFAULT_SECTIONS.join(", ")}]: `)).trim();
|
|
840
770
|
const sections = (sectionsInput ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -852,10 +782,10 @@ async function create(args, cwd = process.cwd()) {
|
|
|
852
782
|
const files = buildScaffold(opts);
|
|
853
783
|
for (const file of files) await writeFileSafe(join(targetDir, file.path), file.contents);
|
|
854
784
|
console.log(`\nCreated ${name} with the ${resolvedTheme} theme (${files.length} files).`);
|
|
855
|
-
const templateSpec = flagValue(args, "template");
|
|
785
|
+
const templateSpec = flagValue$1(args, "template");
|
|
856
786
|
if (templateSpec) {
|
|
857
787
|
const { add } = await Promise.resolve().then(() => add_exports);
|
|
858
|
-
const { loadConfig } = await import("./config-
|
|
788
|
+
const { loadConfig } = await import("./config-BsyJbMvD.mjs").then((n) => n.i);
|
|
859
789
|
console.log(`\nInstalling template "${templateSpec}"…`);
|
|
860
790
|
await add([templateSpec], await loadConfig(), { cwd: targetDir });
|
|
861
791
|
}
|
|
@@ -876,6 +806,47 @@ const BANNED_HOOKS = [
|
|
|
876
806
|
"useContext",
|
|
877
807
|
"useReducer"
|
|
878
808
|
];
|
|
809
|
+
/**
|
|
810
|
+
* Blank out comments and string/template-literal contents so identifier scans
|
|
811
|
+
* only match real code. A mention of `useEffect` in a comment must not trip
|
|
812
|
+
* the no-react-hooks rule. Single pass — a `//` inside a string (URLs) is not
|
|
813
|
+
* treated as a comment.
|
|
814
|
+
*/
|
|
815
|
+
function stripCommentsAndStrings(source) {
|
|
816
|
+
let out = "";
|
|
817
|
+
let i = 0;
|
|
818
|
+
const n = source.length;
|
|
819
|
+
while (i < n) {
|
|
820
|
+
const c = source[i];
|
|
821
|
+
const next = source[i + 1];
|
|
822
|
+
if (c === "/" && next === "/") while (i < n && source[i] !== "\n") i++;
|
|
823
|
+
else if (c === "/" && next === "*") {
|
|
824
|
+
i += 2;
|
|
825
|
+
while (i < n && !(source[i] === "*" && source[i + 1] === "/")) i++;
|
|
826
|
+
i += 2;
|
|
827
|
+
} else if (c === "'" || c === "\"") {
|
|
828
|
+
i++;
|
|
829
|
+
while (i < n && source[i] !== c) {
|
|
830
|
+
if (source[i] === "\\") i++;
|
|
831
|
+
i++;
|
|
832
|
+
}
|
|
833
|
+
i++;
|
|
834
|
+
out += c + c;
|
|
835
|
+
} else if (c === "`") {
|
|
836
|
+
i++;
|
|
837
|
+
while (i < n && source[i] !== "`") {
|
|
838
|
+
if (source[i] === "\\") i++;
|
|
839
|
+
i++;
|
|
840
|
+
}
|
|
841
|
+
i++;
|
|
842
|
+
out += "``";
|
|
843
|
+
} else {
|
|
844
|
+
out += c;
|
|
845
|
+
i++;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return out;
|
|
849
|
+
}
|
|
879
850
|
async function runDoctor(root) {
|
|
880
851
|
const violations = [];
|
|
881
852
|
const componentsDir = join(root, "packages", "components", "src");
|
|
@@ -890,7 +861,8 @@ async function runDoctor(root) {
|
|
|
890
861
|
const tsxPath = join(componentsDir, name, `${name}.tsx`);
|
|
891
862
|
if (!existsSync(tsxPath)) continue;
|
|
892
863
|
const content = readFileSync(tsxPath, "utf8");
|
|
893
|
-
|
|
864
|
+
const code = stripCommentsAndStrings(content);
|
|
865
|
+
for (const hook of BANNED_HOOKS) if (new RegExp(`\\b${hook}\\b`).test(code)) violations.push({
|
|
894
866
|
file: tsxPath,
|
|
895
867
|
rule: "no-react-hooks",
|
|
896
868
|
detail: `Banned hook '${hook}' in ${name}.tsx`
|
|
@@ -1034,19 +1006,21 @@ async function generate(args, config) {
|
|
|
1034
1006
|
}
|
|
1035
1007
|
//#endregion
|
|
1036
1008
|
//#region src/commands/init.ts
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
];
|
|
1009
|
+
function flagValue(args, key) {
|
|
1010
|
+
const eq = args.find((a) => a.startsWith(`--${key}=`));
|
|
1011
|
+
if (eq) return eq.slice(key.length + 3);
|
|
1012
|
+
const idx = args.indexOf(`--${key}`);
|
|
1013
|
+
const next = idx !== -1 ? args[idx + 1] : void 0;
|
|
1014
|
+
return next && !next.startsWith("--") ? next : void 0;
|
|
1015
|
+
}
|
|
1042
1016
|
async function promptTheme() {
|
|
1043
1017
|
const rl = createInterface({
|
|
1044
1018
|
input: stdin,
|
|
1045
1019
|
output: stdout
|
|
1046
1020
|
});
|
|
1047
1021
|
try {
|
|
1048
|
-
const answer = (await rl.question(
|
|
1049
|
-
return THEMES
|
|
1022
|
+
const answer = (await rl.question(`Theme? (${THEMES.join("/")}) [light]: `)).trim().toLowerCase();
|
|
1023
|
+
return THEMES.includes(answer) ? answer : "light";
|
|
1050
1024
|
} finally {
|
|
1051
1025
|
rl.close();
|
|
1052
1026
|
}
|
|
@@ -1055,17 +1029,25 @@ function configFileContents(theme) {
|
|
|
1055
1029
|
return `import type { CascadeConfig } from 'cascivo'
|
|
1056
1030
|
|
|
1057
1031
|
const config: CascadeConfig = {
|
|
1058
|
-
registry: '${DEFAULT_CONFIG.registry}',
|
|
1059
|
-
outputDir: '${DEFAULT_CONFIG.outputDir}',
|
|
1032
|
+
registry: '${DEFAULT_CONFIG$1.registry}',
|
|
1033
|
+
outputDir: '${DEFAULT_CONFIG$1.outputDir}',
|
|
1060
1034
|
theme: '${theme}',
|
|
1061
1035
|
}
|
|
1062
1036
|
|
|
1063
1037
|
export default config
|
|
1064
1038
|
`;
|
|
1065
1039
|
}
|
|
1066
|
-
async function init(cwd = process.cwd()) {
|
|
1040
|
+
async function init(args = [], cwd = process.cwd()) {
|
|
1041
|
+
const yes = args.includes("--yes") || args.includes("-y");
|
|
1042
|
+
const themeArg = flagValue(args, "theme")?.toLowerCase();
|
|
1043
|
+
if (themeArg !== void 0 && !THEMES.includes(themeArg)) {
|
|
1044
|
+
console.error(`Unknown theme "${themeArg}". Available: ${THEMES.join(", ")}`);
|
|
1045
|
+
process.exitCode = 1;
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1067
1048
|
installPackages(["@cascivo/core", "@cascivo/tokens"], cwd);
|
|
1068
|
-
const
|
|
1049
|
+
const interactive = themeArg === void 0 && !yes && stdin.isTTY;
|
|
1050
|
+
const theme = themeArg ?? (interactive ? await promptTheme() : "light");
|
|
1069
1051
|
await writeFileSafe(join(cwd, "cascivo.config.ts"), configFileContents(theme));
|
|
1070
1052
|
console.log(`\nCreated cascivo.config.ts (theme: ${theme})`);
|
|
1071
1053
|
console.log("Import the theme in your root CSS or entry file:");
|
|
@@ -1078,9 +1060,12 @@ async function init(cwd = process.cwd()) {
|
|
|
1078
1060
|
//#region src/commands/list.ts
|
|
1079
1061
|
const TYPE_LABELS = {
|
|
1080
1062
|
component: "Components",
|
|
1081
|
-
layout: "Layouts",
|
|
1082
|
-
block: "Blocks",
|
|
1083
|
-
section: "Sections"
|
|
1063
|
+
layout: "Layouts (copy-paste)",
|
|
1064
|
+
block: "Blocks (copy-paste)",
|
|
1065
|
+
section: "Sections (copy-paste)",
|
|
1066
|
+
chart: "Charts (npm: @cascivo/charts)",
|
|
1067
|
+
flow: "Flow (npm: @cascivo/flow)",
|
|
1068
|
+
editor: "Editor (npm: @cascivo/editor)"
|
|
1084
1069
|
};
|
|
1085
1070
|
/** Render a group of entries as an aligned text table (no section header). */
|
|
1086
1071
|
function formatGroup(entries) {
|
|
@@ -1202,7 +1187,7 @@ async function search(args, config) {
|
|
|
1202
1187
|
const registryFilter = nsFlag !== -1 ? args[nsFlag + 1] : void 0;
|
|
1203
1188
|
const query = args.filter((a, i) => a !== "--registry" && args[i - 1] !== "--registry").join(" ").trim();
|
|
1204
1189
|
if (!query) {
|
|
1205
|
-
console.error("Usage:
|
|
1190
|
+
console.error("Usage: cascivo search <query> [--registry @ns]");
|
|
1206
1191
|
return;
|
|
1207
1192
|
}
|
|
1208
1193
|
const results = [];
|
|
@@ -1239,25 +1224,224 @@ async function search(args, config) {
|
|
|
1239
1224
|
}
|
|
1240
1225
|
}
|
|
1241
1226
|
//#endregion
|
|
1227
|
+
//#region ../theme-kit/src/config.ts
|
|
1228
|
+
const DEFAULT_CONFIG = {
|
|
1229
|
+
baseMode: "light",
|
|
1230
|
+
accentHue: 250,
|
|
1231
|
+
accentChroma: .2,
|
|
1232
|
+
radiusBase: .375,
|
|
1233
|
+
fontFamily: "system",
|
|
1234
|
+
presetId: "light"
|
|
1235
|
+
};
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region ../theme-kit/src/hash.ts
|
|
1238
|
+
/** Decode a hash produced by {@link configToHash}. Returns null on malformed
|
|
1239
|
+
* input; unknown/missing fields fall back to {@link DEFAULT_CONFIG}. */
|
|
1240
|
+
function hashToConfig(hash) {
|
|
1241
|
+
if (!hash) return null;
|
|
1242
|
+
try {
|
|
1243
|
+
const base64 = hash.replace(/-/g, "+").replace(/_/g, "/");
|
|
1244
|
+
const json = atob(base64);
|
|
1245
|
+
const parsed = JSON.parse(json);
|
|
1246
|
+
return {
|
|
1247
|
+
baseMode: parsed.baseMode === "dark" ? "dark" : "light",
|
|
1248
|
+
accentHue: typeof parsed.accentHue === "number" ? parsed.accentHue : DEFAULT_CONFIG.accentHue,
|
|
1249
|
+
accentChroma: typeof parsed.accentChroma === "number" ? parsed.accentChroma : DEFAULT_CONFIG.accentChroma,
|
|
1250
|
+
radiusBase: typeof parsed.radiusBase === "number" ? parsed.radiusBase : DEFAULT_CONFIG.radiusBase,
|
|
1251
|
+
fontFamily: parsed.fontFamily ?? DEFAULT_CONFIG.fontFamily,
|
|
1252
|
+
presetId: parsed.presetId ?? null
|
|
1253
|
+
};
|
|
1254
|
+
} catch {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
//#endregion
|
|
1259
|
+
//#region ../theme-kit/src/css.ts
|
|
1260
|
+
const FONT_STACKS = {
|
|
1261
|
+
geometric: "'Futura', 'Century Gothic', 'Trebuchet MS', ui-sans-serif, sans-serif",
|
|
1262
|
+
humanist: "'Gill Sans', 'Trebuchet MS', 'Calibri', ui-sans-serif, sans-serif",
|
|
1263
|
+
transitional: "'Optima', 'Candara', 'Palatino Linotype', ui-sans-serif, sans-serif",
|
|
1264
|
+
mono: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace"
|
|
1265
|
+
};
|
|
1266
|
+
const BASE_LIGHT = `
|
|
1267
|
+
color-scheme: light;
|
|
1268
|
+
--cascivo-color-background: oklch(1 0 0);
|
|
1269
|
+
--cascivo-color-surface: oklch(0.985 0.002 264);
|
|
1270
|
+
--cascivo-color-surface-2: oklch(0.967 0.003 264);
|
|
1271
|
+
--cascivo-color-bg: var(--cascivo-color-background);
|
|
1272
|
+
--cascivo-color-bg-subtle: var(--cascivo-color-surface);
|
|
1273
|
+
--cascivo-color-surface-raised: var(--cascivo-color-surface);
|
|
1274
|
+
--cascivo-color-surface-overlay: var(--cascivo-color-background);
|
|
1275
|
+
--cascivo-color-border: oklch(0.928 0.006 264);
|
|
1276
|
+
--cascivo-color-border-strong: oklch(0.872 0.008 264);
|
|
1277
|
+
--cascivo-border-subtle: oklch(0.928 0.006 264);
|
|
1278
|
+
--cascivo-border-default: oklch(0.872 0.008 264);
|
|
1279
|
+
--cascivo-border-strong: oklch(0.707 0.015 264);
|
|
1280
|
+
--cascivo-color-foreground: oklch(0.145 0.005 264);
|
|
1281
|
+
--cascivo-color-foreground-muted: oklch(0.554 0.018 264);
|
|
1282
|
+
--cascivo-color-text: var(--cascivo-color-foreground);
|
|
1283
|
+
--cascivo-color-text-subtle: oklch(0.446 0.018 264);
|
|
1284
|
+
--cascivo-color-text-muted: oklch(0.707 0.015 264);
|
|
1285
|
+
--cascivo-color-text-on-accent: oklch(1 0 0);
|
|
1286
|
+
--cascivo-color-text-on-destructive: oklch(1 0 0);
|
|
1287
|
+
--cascivo-color-primary: oklch(0.205 0 0);
|
|
1288
|
+
--cascivo-color-primary-fg: oklch(0.985 0 0);
|
|
1289
|
+
--cascivo-color-primary-hover: oklch(0.27 0 0);
|
|
1290
|
+
--cascivo-color-primary-active: oklch(0.32 0 0);
|
|
1291
|
+
--cascivo-color-active-bg: oklch(0.145 0.005 264 / 6%);
|
|
1292
|
+
--cascivo-color-destructive: oklch(0.54 0.188 22);
|
|
1293
|
+
--cascivo-color-destructive-hover: oklch(0.448 0.17 22);
|
|
1294
|
+
--cascivo-color-destructive-subtle: oklch(0.971 0.013 22);
|
|
1295
|
+
--cascivo-color-error: oklch(0.628 0.188 22);
|
|
1296
|
+
--cascivo-color-warning: oklch(0.768 0.145 75);
|
|
1297
|
+
--cascivo-color-success: oklch(0.648 0.15 145);
|
|
1298
|
+
--cascivo-color-success-subtle: oklch(0.982 0.018 145);
|
|
1299
|
+
--cascivo-color-warning-subtle: oklch(0.98 0.02 75);
|
|
1300
|
+
--cascivo-color-info: oklch(0.546 0.224 250);
|
|
1301
|
+
--cascivo-color-info-subtle: oklch(0.97 0.025 250);
|
|
1302
|
+
--cascivo-color-success-foreground: oklch(0.45 0.14 145);
|
|
1303
|
+
--cascivo-color-warning-foreground: oklch(0.5 0.14 75);
|
|
1304
|
+
--cascivo-shadow-xs: 0 1px 2px oklch(0 0 0 / 0.05);
|
|
1305
|
+
--cascivo-shadow-sm: 0 1px 3px oklch(0 0 0 / 0.07), 0 1px 2px oklch(0 0 0 / 0.04);
|
|
1306
|
+
--cascivo-shadow-md: 0 2px 8px oklch(0 0 0 / 0.07), 0 1px 2px oklch(0 0 0 / 0.04);
|
|
1307
|
+
--cascivo-shadow-overlay: 0 4px 32px oklch(0 0 0 / 0.16), 0 0 0 1px oklch(0 0 0 / 0.04);
|
|
1308
|
+
--cascivo-shadow-lg: var(--cascivo-shadow-overlay);
|
|
1309
|
+
--cascivo-ring-width: 2px;
|
|
1310
|
+
--cascivo-ring-offset: 0px;
|
|
1311
|
+
--cascivo-ring-color: color-mix(in oklch, var(--cascivo-color-accent) 55%, transparent);
|
|
1312
|
+
--cascivo-focus-ring: 0 0 0 var(--cascivo-ring-width) var(--cascivo-ring-color);`;
|
|
1313
|
+
const BASE_DARK = `
|
|
1314
|
+
color-scheme: dark;
|
|
1315
|
+
--cascivo-color-background: oklch(0.145 0.005 250);
|
|
1316
|
+
--cascivo-color-surface: oklch(0.185 0.007 250);
|
|
1317
|
+
--cascivo-color-surface-2: oklch(0.22 0.008 250);
|
|
1318
|
+
--cascivo-color-bg: var(--cascivo-color-background);
|
|
1319
|
+
--cascivo-color-bg-subtle: var(--cascivo-color-surface);
|
|
1320
|
+
--cascivo-color-surface-raised: var(--cascivo-color-surface-2);
|
|
1321
|
+
--cascivo-color-surface-overlay: var(--cascivo-color-surface);
|
|
1322
|
+
--cascivo-color-border: oklch(1 0 0 / 10%);
|
|
1323
|
+
--cascivo-color-border-strong: oklch(1 0 0 / 16%);
|
|
1324
|
+
--cascivo-border-subtle: oklch(1 0 0 / 0.06);
|
|
1325
|
+
--cascivo-border-default: oklch(1 0 0 / 0.1);
|
|
1326
|
+
--cascivo-border-strong: oklch(1 0 0 / 0.2);
|
|
1327
|
+
--cascivo-color-foreground: oklch(0.985 0.002 264);
|
|
1328
|
+
--cascivo-color-foreground-muted: oklch(0.707 0.015 264);
|
|
1329
|
+
--cascivo-color-text: var(--cascivo-color-foreground);
|
|
1330
|
+
--cascivo-color-text-subtle: oklch(0.707 0.015 264);
|
|
1331
|
+
--cascivo-color-text-muted: oklch(0.554 0.018 264);
|
|
1332
|
+
--cascivo-color-text-on-accent: oklch(0.145 0.005 250);
|
|
1333
|
+
--cascivo-color-text-on-destructive: oklch(1 0 0);
|
|
1334
|
+
--cascivo-color-primary: oklch(0.922 0 0);
|
|
1335
|
+
--cascivo-color-primary-fg: oklch(0.205 0 0);
|
|
1336
|
+
--cascivo-color-primary-hover: oklch(0.86 0 0);
|
|
1337
|
+
--cascivo-color-primary-active: oklch(0.8 0 0);
|
|
1338
|
+
--cascivo-color-active-bg: oklch(1 0 0 / 8%);
|
|
1339
|
+
--cascivo-color-destructive: oklch(0.72 0.16 22);
|
|
1340
|
+
--cascivo-color-destructive-hover: oklch(0.628 0.188 22);
|
|
1341
|
+
--cascivo-color-destructive-subtle: oklch(0.628 0.188 22 / 0.1);
|
|
1342
|
+
--cascivo-color-error: oklch(0.72 0.16 22);
|
|
1343
|
+
--cascivo-color-warning: oklch(0.82 0.13 75);
|
|
1344
|
+
--cascivo-color-success: oklch(0.72 0.13 145);
|
|
1345
|
+
--cascivo-color-success-subtle: oklch(0.72 0.13 145 / 0.1);
|
|
1346
|
+
--cascivo-color-warning-subtle: oklch(0.82 0.13 75 / 0.1);
|
|
1347
|
+
--cascivo-color-info: oklch(0.65 0.2 250);
|
|
1348
|
+
--cascivo-color-info-subtle: oklch(0.65 0.2 250 / 0.1);
|
|
1349
|
+
--cascivo-color-success-foreground: oklch(0.72 0.13 145);
|
|
1350
|
+
--cascivo-color-warning-foreground: oklch(0.82 0.13 75);
|
|
1351
|
+
--cascivo-shadow-xs: none;
|
|
1352
|
+
--cascivo-shadow-sm: 0 1px 3px oklch(0 0 0 / 0.4);
|
|
1353
|
+
--cascivo-shadow-md: 0 2px 8px oklch(0 0 0 / 0.45);
|
|
1354
|
+
--cascivo-shadow-overlay: 0 4px 32px oklch(0 0 0 / 0.6), 0 0 0 1px oklch(1 0 0 / 0.06);
|
|
1355
|
+
--cascivo-shadow-lg: var(--cascivo-shadow-overlay);
|
|
1356
|
+
--cascivo-ring-width: 2px;
|
|
1357
|
+
--cascivo-ring-offset: 0px;
|
|
1358
|
+
--cascivo-ring-color: color-mix(in oklch, var(--cascivo-color-accent) 65%, transparent);
|
|
1359
|
+
--cascivo-focus-ring: 0 0 0 var(--cascivo-ring-width) var(--cascivo-ring-color);`;
|
|
1360
|
+
function configToCSS(config, options = {}) {
|
|
1361
|
+
const { previewMode = false, themeName = "create-custom" } = options;
|
|
1362
|
+
const light = config.baseMode === "light";
|
|
1363
|
+
const h = config.accentHue;
|
|
1364
|
+
const c = config.accentChroma;
|
|
1365
|
+
const acc = (l, chroma = c) => `oklch(${l} ${chroma.toFixed(3)} ${h})`;
|
|
1366
|
+
const lines = [
|
|
1367
|
+
`/* Add to your project CSS after importing the base theme: */`,
|
|
1368
|
+
`/* @import '@cascivo/themes/${config.baseMode}.css'; */`,
|
|
1369
|
+
`/* @import './theme.css'; */`,
|
|
1370
|
+
``,
|
|
1371
|
+
`@layer cascivo.theme {`,
|
|
1372
|
+
` [data-theme="${themeName}"] {`
|
|
1373
|
+
];
|
|
1374
|
+
if (previewMode) {
|
|
1375
|
+
lines.push(light ? BASE_LIGHT : BASE_DARK);
|
|
1376
|
+
lines.push(``);
|
|
1377
|
+
}
|
|
1378
|
+
lines.push(` /* ── Accent ─────────────────────────────────── */`, ` --cascivo-color-accent: ${acc(light ? .52 : .65)};`, ` --cascivo-color-accent-foreground: ${light ? "oklch(1 0 0)" : `oklch(0.145 0.005 ${h})`};`, ` --cascivo-color-accent-hover: ${acc(light ? .45 : .707)};`, ` --cascivo-color-accent-active: ${acc(light ? .373 : .808, Math.min(c, .155))};`, ` --cascivo-color-accent-subtle: ${acc(light ? .97 : .623, .025)};`, ` --cascivo-color-accent-muted: ${acc(light ? .932 : .623, light ? .055 : c * .2)};`, ``, ` /* ── Radius ─────────────────────────────────── */`, ` --cascivo-radius-base: ${config.radiusBase}rem;`, ` --cascivo-radius-control: var(--cascivo-radius-base);`, ` --cascivo-radius-surface: calc(var(--cascivo-radius-base) * 1.66);`, ` --cascivo-radius-indicator: calc(var(--cascivo-radius-base) / 2);`, ` --cascivo-radius-full: 9999px;`, ` --cascivo-radius-component: var(--cascivo-radius-base);`, ` --cascivo-radius-button: var(--cascivo-radius-base);`, ` --cascivo-radius-input: var(--cascivo-radius-base);`, ` --cascivo-radius-card: calc(var(--cascivo-radius-base) * 1.66);`, ` --cascivo-radius-badge: var(--cascivo-radius-full);`, ` --cascivo-radius-modal: calc(var(--cascivo-radius-base) * 2);`);
|
|
1379
|
+
if (config.fontFamily !== "system") {
|
|
1380
|
+
lines.push(``);
|
|
1381
|
+
lines.push(` /* ── Font ───────────────────────────────────── */`);
|
|
1382
|
+
lines.push(` --cascivo-font-sans: ${FONT_STACKS[config.fontFamily] ?? FONT_STACKS["humanist"]};`);
|
|
1383
|
+
}
|
|
1384
|
+
lines.push(` }`);
|
|
1385
|
+
lines.push(`}`);
|
|
1386
|
+
return lines.join("\n");
|
|
1387
|
+
}
|
|
1388
|
+
//#endregion
|
|
1242
1389
|
//#region src/commands/theme.ts
|
|
1243
|
-
const
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
"
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1390
|
+
const THEME_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
1391
|
+
/** Read a `--key value` or `--key=value` flag from an argv slice. */
|
|
1392
|
+
function flag(args, key) {
|
|
1393
|
+
const eq = args.find((a) => a.startsWith(`--${key}=`));
|
|
1394
|
+
if (eq) return eq.slice(key.length + 3);
|
|
1395
|
+
const idx = args.indexOf(`--${key}`);
|
|
1396
|
+
const next = idx !== -1 ? args[idx + 1] : void 0;
|
|
1397
|
+
return next && !next.startsWith("--") ? next : void 0;
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* `cascivo theme create <name> --from <hash>` — turn a /create theme-builder
|
|
1401
|
+
* config (the `#hash` in the builder URL, or its "Copy CLI command" output) into
|
|
1402
|
+
* an owned `<name>.theme.css` in the project. Uses the same generator the builder
|
|
1403
|
+
* previews with, so the file matches the preview exactly.
|
|
1404
|
+
*/
|
|
1405
|
+
async function themeCreate(args, cwd) {
|
|
1406
|
+
const name = args.find((a) => !a.startsWith("--"));
|
|
1407
|
+
const hash = flag(args, "from");
|
|
1408
|
+
const dryRun = args.includes("--dry-run");
|
|
1409
|
+
if (!name || !hash) {
|
|
1410
|
+
console.error("Usage: cascivo theme create <name> --from <hash> [--out <path>]");
|
|
1411
|
+
console.error("Get <hash> from the theme builder at https://cascivo.com/create (Copy CLI command).");
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
if (!THEME_NAME_RE.test(name)) {
|
|
1415
|
+
console.error(`Invalid theme name "${name}". Use lowercase letters, digits, and dashes.`);
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
const config = hashToConfig(hash);
|
|
1419
|
+
if (!config) {
|
|
1420
|
+
console.error("Could not decode --from. Copy the command again from the theme builder.");
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
const css = configToCSS(config, { themeName: name });
|
|
1424
|
+
if (dryRun) {
|
|
1425
|
+
console.log(css);
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
await writeFileSafe(resolve(cwd, flag(args, "out") ?? `${name}.theme.css`), css);
|
|
1429
|
+
console.log(`\n✓ Wrote ${name}.theme.css`);
|
|
1430
|
+
console.log(`\nImport the base theme and your overlay in your entry CSS:`);
|
|
1431
|
+
console.log(` @import '@cascivo/themes/${config.baseMode}.css';`);
|
|
1432
|
+
console.log(` @import './${name}.theme.css';`);
|
|
1433
|
+
console.log(`Then set it on a container:`);
|
|
1434
|
+
console.log(` <div data-theme="${name}">…</div>`);
|
|
1435
|
+
}
|
|
1257
1436
|
async function theme(args) {
|
|
1258
1437
|
const [sub, name] = args;
|
|
1438
|
+
if (sub === "create") {
|
|
1439
|
+
await themeCreate(args.slice(1), process.cwd());
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1259
1442
|
if (sub !== "add" || !name) {
|
|
1260
|
-
console.error(`Usage:
|
|
1443
|
+
console.error(`Usage: cascivo theme add <${THEMES.join("|")}>`);
|
|
1444
|
+
console.error(` cascivo theme create <name> --from <hash>`);
|
|
1261
1445
|
return;
|
|
1262
1446
|
}
|
|
1263
1447
|
if (!THEMES.includes(name)) {
|
|
@@ -1490,11 +1674,6 @@ async function confirm(question) {
|
|
|
1490
1674
|
rl.close();
|
|
1491
1675
|
}
|
|
1492
1676
|
}
|
|
1493
|
-
async function fetchText(url) {
|
|
1494
|
-
const res = await fetch(url);
|
|
1495
|
-
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
1496
|
-
return res.text();
|
|
1497
|
-
}
|
|
1498
1677
|
async function update(name, config, opts = {}) {
|
|
1499
1678
|
if (opts.check) return updateCheck(config);
|
|
1500
1679
|
if (!name) {
|
|
@@ -1532,9 +1711,9 @@ async function threeWayUpdate(name, upstreamEntry, baseItem, config, opts) {
|
|
|
1532
1711
|
const fname = fileName(url);
|
|
1533
1712
|
const baseFile = baseItem.files.find((f) => fileName(f.url) === fname);
|
|
1534
1713
|
const dest = resolveOutputPath(config.outputDir, outputName, fname);
|
|
1535
|
-
const upstream = await
|
|
1714
|
+
const upstream = await fetchTextRetry(url);
|
|
1536
1715
|
const local = existsSync(dest) ? await readFile(dest, "utf8") : "";
|
|
1537
|
-
const base = baseFile ? await
|
|
1716
|
+
const base = baseFile ? await fetchTextRetry(baseFile.url) : "";
|
|
1538
1717
|
if (base === "" && local === "") {
|
|
1539
1718
|
summaries.push({
|
|
1540
1719
|
file: fname,
|
|
@@ -1604,12 +1783,13 @@ async function twoWayUpdate(name, entry, config, opts) {
|
|
|
1604
1783
|
if (!entry) return;
|
|
1605
1784
|
const pending = [];
|
|
1606
1785
|
for (const url of entry.files) {
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1786
|
+
let latest;
|
|
1787
|
+
try {
|
|
1788
|
+
latest = await fetchTextRetry(url);
|
|
1789
|
+
} catch (e) {
|
|
1790
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
1610
1791
|
return;
|
|
1611
1792
|
}
|
|
1612
|
-
const latest = await res.text();
|
|
1613
1793
|
const dest = resolveOutputPath(config.outputDir, entry.name, fileName(url));
|
|
1614
1794
|
const current = await readFileSafe(dest);
|
|
1615
1795
|
if (current === null) {
|
|
@@ -1647,7 +1827,14 @@ async function updateCheck(config) {
|
|
|
1647
1827
|
for (const [name, entry] of Object.entries(lock.items)) {
|
|
1648
1828
|
const current = findComponent(registry, name);
|
|
1649
1829
|
if (!current) continue;
|
|
1650
|
-
if (current.
|
|
1830
|
+
if (current.fileHashes && Object.keys(current.fileHashes).length > 0) {
|
|
1831
|
+
const lockByBase = new Map(Object.entries(entry.files).map(([dest, hash]) => [dest.split("/").pop(), hash]));
|
|
1832
|
+
const changed = Object.entries(current.fileHashes).filter(([file, hash]) => lockByBase.get(file) !== hash);
|
|
1833
|
+
if (changed.length > 0) {
|
|
1834
|
+
console.log(`${name}: ${changed.length} file(s) changed upstream (${entry.version} → ${current.version})`);
|
|
1835
|
+
outdated++;
|
|
1836
|
+
}
|
|
1837
|
+
} else if (current.version !== entry.version) {
|
|
1651
1838
|
console.log(`${name}: ${entry.version} → ${current.version}`);
|
|
1652
1839
|
outdated++;
|
|
1653
1840
|
}
|
|
@@ -1668,6 +1855,10 @@ function formatItem(item) {
|
|
|
1668
1855
|
if (item.author) lines.push(`Author: ${item.author}`);
|
|
1669
1856
|
if (item.license) lines.push(`License: ${item.license}`);
|
|
1670
1857
|
if (item.homepage) lines.push(`Homepage: ${item.homepage}`);
|
|
1858
|
+
if (item.install) {
|
|
1859
|
+
lines.push(`Distribution: npm package — pnpm add ${item.install}`);
|
|
1860
|
+
if (item.styles) lines.push(`Stylesheet: import '${item.styles}' (required)`);
|
|
1861
|
+
} else if (!isTemplateItem(item)) lines.push(`Distribution: copy-paste — npx cascivo add ${item.name}`);
|
|
1671
1862
|
if (isTemplateItem(item)) try {
|
|
1672
1863
|
const meta = asTemplateMeta(item.meta);
|
|
1673
1864
|
lines.push(`\nTemplate: ${meta.intent}`);
|
|
@@ -1696,7 +1887,7 @@ function formatItem(item) {
|
|
|
1696
1887
|
async function view(args, config) {
|
|
1697
1888
|
const spec = args[0];
|
|
1698
1889
|
if (!spec) {
|
|
1699
|
-
console.error("Usage:
|
|
1890
|
+
console.error("Usage: cascivo view <component-spec>");
|
|
1700
1891
|
return;
|
|
1701
1892
|
}
|
|
1702
1893
|
const { url, headers, params } = await resolveItemUrl(parseAddress(spec), config);
|
|
@@ -1708,7 +1899,7 @@ async function view(args, config) {
|
|
|
1708
1899
|
}
|
|
1709
1900
|
//#endregion
|
|
1710
1901
|
//#region src/index.ts
|
|
1711
|
-
const VERSION = "
|
|
1902
|
+
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
1712
1903
|
const HELP = `cascivo ${VERSION} — the CSS-native, signal-driven, AI-first design system
|
|
1713
1904
|
|
|
1714
1905
|
Usage: cascivo <command> [options]
|
|
@@ -1722,23 +1913,146 @@ Commands:
|
|
|
1722
1913
|
update [component] Update installed components (--check: list outdated)
|
|
1723
1914
|
search <query> Search components across registries
|
|
1724
1915
|
view <spec> View a component before installing
|
|
1725
|
-
theme add
|
|
1916
|
+
theme <add|create> Install a first-party theme, or build a custom one from /create
|
|
1726
1917
|
eject <component> Eject specific tokens into a scoped local override file
|
|
1727
1918
|
generate <config.json> Generate TSX from a ViewConfig JSON file
|
|
1728
1919
|
doctor [--ci] Check components for rule violations
|
|
1729
1920
|
audit --ai <paths...> Audit AI-generated code against the cascivo contract
|
|
1730
1921
|
registry build Build a static registry from a cascivo-registry.json file
|
|
1731
1922
|
template init <name> Scaffold a new template (source + manifest + registry entry)
|
|
1923
|
+
tokens import <file> Import external design tokens as cascivo overrides
|
|
1732
1924
|
|
|
1733
1925
|
Run "cascivo <command> --help" for details.`;
|
|
1926
|
+
const THEME_LIST = THEMES.join(" | ");
|
|
1927
|
+
const COMMAND_HELP = {
|
|
1928
|
+
create: `Usage: cascivo create [name] [options]
|
|
1929
|
+
|
|
1930
|
+
Scaffold a new ready-to-run app — Vite + React + TypeScript, pre-wired with the
|
|
1931
|
+
cascivo app shell, side navigation, header, and a theme.
|
|
1932
|
+
|
|
1933
|
+
Options:
|
|
1934
|
+
--template <spec> Start from a marketplace template (@ns/name or owner/repo/name)
|
|
1935
|
+
--theme <name> Theme to install (${THEME_LIST})
|
|
1936
|
+
--sections "<a, b>" Comma-separated nav section labels (one component each)
|
|
1937
|
+
--yes, -y Accept defaults, never prompt`,
|
|
1938
|
+
init: `Usage: cascivo init [options]
|
|
1939
|
+
|
|
1940
|
+
Set up cascivo in the current project: installs @cascivo/core + @cascivo/tokens
|
|
1941
|
+
and writes cascivo.config.ts.
|
|
1942
|
+
|
|
1943
|
+
Options:
|
|
1944
|
+
--theme <name> Theme to configure (${THEME_LIST})
|
|
1945
|
+
--yes, -y Accept defaults, never prompt (implied when stdin is not a TTY)`,
|
|
1946
|
+
add: `Usage: cascivo add <component...> [options]
|
|
1947
|
+
|
|
1948
|
+
Copy component source (TSX + CSS module) from the registry into your project,
|
|
1949
|
+
resolving component dependencies. Also installs templates (@ns/name) and
|
|
1950
|
+
third-party components (owner/repo/name).
|
|
1951
|
+
|
|
1952
|
+
Options:
|
|
1953
|
+
--dry-run Show what would be written without writing
|
|
1954
|
+
--yes, -y Skip confirmation prompts`,
|
|
1955
|
+
list: `Usage: cascivo list [options]
|
|
1956
|
+
|
|
1957
|
+
List components available in the configured registry.
|
|
1958
|
+
|
|
1959
|
+
Options:
|
|
1960
|
+
--installed Only list components already installed in this project`,
|
|
1961
|
+
update: `Usage: cascivo update [component] [options]
|
|
1962
|
+
|
|
1963
|
+
Update installed components to the current registry version. Without a
|
|
1964
|
+
component argument, updates everything in the lockfile.
|
|
1965
|
+
|
|
1966
|
+
Options:
|
|
1967
|
+
--check List outdated components without writing
|
|
1968
|
+
--yes, -y Skip confirmation prompts`,
|
|
1969
|
+
search: `Usage: cascivo search <query> [options]
|
|
1970
|
+
|
|
1971
|
+
Search components across the configured registries.
|
|
1972
|
+
|
|
1973
|
+
Options:
|
|
1974
|
+
--registry <@ns> Restrict the search to one registry namespace`,
|
|
1975
|
+
view: `Usage: cascivo view <spec>
|
|
1976
|
+
|
|
1977
|
+
Preview a component or template (files, dependencies, description) before
|
|
1978
|
+
installing. <spec> is a bare name, @ns/name, or owner/repo/name.`,
|
|
1979
|
+
theme: `Usage: cascivo theme add <name>
|
|
1980
|
+
cascivo theme create <name> --from <hash> [--out <path>]
|
|
1981
|
+
|
|
1982
|
+
add Install @cascivo/themes and print the import + data-theme wiring.
|
|
1983
|
+
create Turn a theme-builder config into an owned <name>.theme.css. Get <hash>
|
|
1984
|
+
from https://cascivo.com/create ("Copy CLI command"). --dry-run prints
|
|
1985
|
+
the CSS without writing.
|
|
1986
|
+
|
|
1987
|
+
Themes: ${THEME_LIST}`,
|
|
1988
|
+
eject: `Usage: cascivo eject <component> [options]
|
|
1989
|
+
|
|
1990
|
+
Eject specific tokens into a scoped local override file so you can restyle a
|
|
1991
|
+
component without forking it.
|
|
1992
|
+
|
|
1993
|
+
Options:
|
|
1994
|
+
--tokens <a,b> Comma-separated token names to eject (default: all)
|
|
1995
|
+
--scope <sel> CSS selector the overrides are scoped to
|
|
1996
|
+
--out <file> Output file path
|
|
1997
|
+
--dry-run Print the override file without writing`,
|
|
1998
|
+
generate: `Usage: cascivo generate <config.json> [options]
|
|
1999
|
+
|
|
2000
|
+
Generate TSX from a ViewConfig JSON file (see the MCP scaffold_view tool).
|
|
2001
|
+
|
|
2002
|
+
Options:
|
|
2003
|
+
--out <file> Output file (default: stdout)
|
|
2004
|
+
--components-dir <dir> Components import base (default: ./src/components/ui)`,
|
|
2005
|
+
doctor: `Usage: cascivo doctor [options]
|
|
2006
|
+
|
|
2007
|
+
Check components in this repo for cascivo rule violations (banned React hooks,
|
|
2008
|
+
hardcoded strings, missing @cascivo/react exports).
|
|
2009
|
+
|
|
2010
|
+
Options:
|
|
2011
|
+
--ci Exit non-zero when violations are found
|
|
2012
|
+
--drift Compare installed components against the registry (copy-paste drift)`,
|
|
2013
|
+
audit: `Usage: cascivo audit --ai <paths...> [options]
|
|
2014
|
+
|
|
2015
|
+
Audit AI-generated code against the cascivo contract: hard-coded values that
|
|
2016
|
+
should be tokens, invented props, missing required props, raw strings where
|
|
2017
|
+
i18n is expected.
|
|
2018
|
+
|
|
2019
|
+
Options:
|
|
2020
|
+
--fix Rewrite unambiguous CSS literals to their token equivalents
|
|
2021
|
+
--json Machine-readable output
|
|
2022
|
+
--level <name> Minimum finding level to report (error | warn; default error)`,
|
|
2023
|
+
registry: `Usage: cascivo registry build [dir]
|
|
2024
|
+
|
|
2025
|
+
Build a static registry (registry.json + file payloads) from a
|
|
2026
|
+
cascivo-registry.json manifest, ready to host on any static file server.`,
|
|
2027
|
+
template: `Usage: cascivo template init <name> [options]
|
|
2028
|
+
|
|
2029
|
+
Scaffold a new template: source, manifest, and registry entry.
|
|
2030
|
+
|
|
2031
|
+
Options:
|
|
2032
|
+
--category <name> Template category (e.g. dashboard)
|
|
2033
|
+
--framework <name> Target framework (default react-vite)
|
|
2034
|
+
--components <a,b> Registry components the template composes
|
|
2035
|
+
--repo <owner/repo> Repository the template will be hosted in`,
|
|
2036
|
+
tokens: `Usage: cascivo tokens import <file>
|
|
2037
|
+
|
|
2038
|
+
Import external design tokens (W3C design-tokens JSON) as cascivo token
|
|
2039
|
+
overrides.`
|
|
2040
|
+
};
|
|
1734
2041
|
async function run(args) {
|
|
1735
2042
|
const [command, ...rest] = args;
|
|
2043
|
+
if (command !== void 0 && (rest.includes("--help") || rest.includes("-h"))) {
|
|
2044
|
+
const commandHelp = COMMAND_HELP[command];
|
|
2045
|
+
if (commandHelp !== void 0) {
|
|
2046
|
+
console.log(commandHelp);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
1736
2050
|
switch (command) {
|
|
1737
2051
|
case "create":
|
|
1738
2052
|
await create(rest);
|
|
1739
2053
|
break;
|
|
1740
2054
|
case "init":
|
|
1741
|
-
await init();
|
|
2055
|
+
await init(rest);
|
|
1742
2056
|
break;
|
|
1743
2057
|
case "add":
|
|
1744
2058
|
await add(rest, await loadConfig(), {
|
|
@@ -1765,7 +2079,7 @@ async function run(args) {
|
|
|
1765
2079
|
await theme(rest);
|
|
1766
2080
|
break;
|
|
1767
2081
|
case "eject": {
|
|
1768
|
-
const { eject } = await import("./eject-
|
|
2082
|
+
const { eject } = await import("./eject-Br8V7I3m.mjs");
|
|
1769
2083
|
const flag = (key) => {
|
|
1770
2084
|
const eq = rest.find((r) => r.startsWith(`--${key}=`));
|
|
1771
2085
|
if (eq) return eq.slice(key.length + 3);
|
|
@@ -1794,7 +2108,7 @@ async function run(args) {
|
|
|
1794
2108
|
break;
|
|
1795
2109
|
case "template":
|
|
1796
2110
|
if (rest[0] === "init") {
|
|
1797
|
-
const { templateInit } = await import("./template-init-
|
|
2111
|
+
const { templateInit } = await import("./template-init-C46fRXo-.mjs");
|
|
1798
2112
|
await templateInit(rest.slice(1));
|
|
1799
2113
|
} else {
|
|
1800
2114
|
console.error(`Unknown template subcommand: ${rest[0]}`);
|
|
@@ -1804,8 +2118,8 @@ async function run(args) {
|
|
|
1804
2118
|
case "doctor": {
|
|
1805
2119
|
const ci = rest.includes("--ci");
|
|
1806
2120
|
if (rest.includes("--drift")) {
|
|
1807
|
-
const { runDoctorDrift } = await import("./drift-
|
|
1808
|
-
await runDoctorDrift(
|
|
2121
|
+
const { runDoctorDrift } = await import("./drift-Ciw5qeEt.mjs");
|
|
2122
|
+
await runDoctorDrift(await loadConfig());
|
|
1809
2123
|
} else {
|
|
1810
2124
|
const result = await runDoctor(process.cwd());
|
|
1811
2125
|
if (result.passed) console.log("No violations found.");
|
|
@@ -1817,7 +2131,7 @@ async function run(args) {
|
|
|
1817
2131
|
break;
|
|
1818
2132
|
}
|
|
1819
2133
|
case "audit": {
|
|
1820
|
-
const { audit } = await import("./audit-
|
|
2134
|
+
const { audit } = await import("./audit-InQqdY1s.mjs");
|
|
1821
2135
|
await audit(rest, await loadConfig());
|
|
1822
2136
|
break;
|
|
1823
2137
|
}
|