cascivo 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{config-C6GdrbvF.mjs → config-D7ddWN_9.mjs} +27 -4
- package/dist/{config-C6GdrbvF.mjs.map → config-D7ddWN_9.mjs.map} +1 -1
- package/dist/{drift-qTzJ75Ds.mjs → drift-B02BbFN_.mjs} +22 -15
- package/dist/drift-B02BbFN_.mjs.map +1 -0
- package/dist/generated/audit-contract.json +73 -1
- package/dist/index.mjs +191 -23
- package/dist/index.mjs.map +1 -1
- package/dist/lock-CW8UuEPJ.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/drift-qTzJ75Ds.mjs.map +0 -1
|
@@ -25,6 +25,7 @@ var config_exports = /* @__PURE__ */ __exportAll({
|
|
|
25
25
|
installHint: () => installHint,
|
|
26
26
|
isPackageManager: () => isPackageManager,
|
|
27
27
|
loadConfig: () => loadConfig,
|
|
28
|
+
pinSpecifier: () => pinSpecifier,
|
|
28
29
|
resolveConfig: () => resolveConfig
|
|
29
30
|
});
|
|
30
31
|
/**
|
|
@@ -152,12 +153,34 @@ function detectPackageManager(cwd = process.cwd(), opts = {}) {
|
|
|
152
153
|
}
|
|
153
154
|
return "npm";
|
|
154
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Pin every `@cascivo/*` package to an explicit version specifier.
|
|
158
|
+
*
|
|
159
|
+
* A bare name lets the package manager reuse whatever it already has resolvable. In a pnpm
|
|
160
|
+
* workspace a sibling package's lockfile entry won and `@cascivo/i18n` resolved to **0.2.14**
|
|
161
|
+
* while latest was **0.16.0** — then cascivo warned the adopter about the version it had
|
|
162
|
+
* just installed itself. `add name@latest` (or the registry's known floor) makes the intent
|
|
163
|
+
* explicit instead of leaving it to resolution order.
|
|
164
|
+
*
|
|
165
|
+
* Non-cascivo packages (`@preact/signals-react`) keep their bare name: their version is the
|
|
166
|
+
* app's business, and cascivo has no floor to assert.
|
|
167
|
+
*/
|
|
168
|
+
function pinSpecifier(pkg, floors = {}) {
|
|
169
|
+
if (!pkg.startsWith("@cascivo/") && pkg !== "cascivo") return pkg;
|
|
170
|
+
if (pkg.includes("@", 1)) return pkg;
|
|
171
|
+
const floor = floors[pkg];
|
|
172
|
+
if (floor?.startsWith(">=")) return `${pkg}@${floor.slice(2)} - x`;
|
|
173
|
+
return `${pkg}@latest`;
|
|
174
|
+
}
|
|
155
175
|
/** The install subcommand each package manager uses to add dependencies. */
|
|
156
176
|
function installCommand(pm, packages, opts = {}) {
|
|
177
|
+
const verb = pm === "npm" ? "install" : "add";
|
|
178
|
+
const devFlag = opts.dev ? [pm === "npm" ? "--save-dev" : "-D"] : [];
|
|
179
|
+
const specs = packages.map((p) => opts.pin && (p.startsWith("@cascivo/") || p === "cascivo") ? `${p}@${opts.pin}` : pinSpecifier(p, opts.floors ?? {}));
|
|
157
180
|
return [pm, [
|
|
158
|
-
|
|
159
|
-
...
|
|
160
|
-
...
|
|
181
|
+
verb,
|
|
182
|
+
...devFlag,
|
|
183
|
+
...specs
|
|
161
184
|
]];
|
|
162
185
|
}
|
|
163
186
|
/** Human-readable install command a user can copy-paste, e.g. `pnpm add -D cascivo`. */
|
|
@@ -168,4 +191,4 @@ function installHint(pm, packages, opts = {}) {
|
|
|
168
191
|
//#endregion
|
|
169
192
|
export { detectPackageManager as a, isPackageManager as c, config_exports as i, loadConfig as l, DEFAULT_CONFIG as n, installCommand as o, THEMES as r, installHint as s, CASCIVO_HOST as t, __exportAll as u };
|
|
170
193
|
|
|
171
|
-
//# sourceMappingURL=config-
|
|
194
|
+
//# sourceMappingURL=config-D7ddWN_9.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-C6GdrbvF.mjs","names":[],"sources":["../src/utils/config.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\n/**\n * Canonical (and only) host for hosted cascivo artifacts (registry.json, per-item\n * r/<name>.json, marketplace.json, /llms/*, /context/*). The legacy docs.cascivo.com\n * subdomain is retired and 301s here. Keep in sync with CASCIVO_HOST in packages/mcp.\n */\nexport const CASCIVO_HOST = 'https://cascivo.com'\n\n/** All first-party themes shipped by @cascivo/themes (selectable via data-theme). */\nexport const THEMES = [\n 'light',\n 'dark',\n 'warm',\n 'flat',\n 'minimal',\n 'midnight',\n 'pastel',\n 'brutalist',\n 'corporate',\n 'terminal',\n 'cyberpunk',\n 'arcade',\n] as const\n\nexport type ThemeName = (typeof THEMES)[number]\n\nexport type RegistryNamespaceConfig =\n | string\n | { url: string; headers?: Record<string, string>; params?: Record<string, string> }\n\nexport interface CascadeConfig {\n /** URL of the registry.json index. */\n registry: string\n /** Directory (relative to project root) where components are written. */\n outputDir: string\n /** Default theme imported by `cascade init`. */\n theme: ThemeName\n /** Namespace → registry URL template (with {name} placeholder) or auth config. */\n registries?: Record<string, RegistryNamespaceConfig>\n /** Whether to copy test files (*.contract.test.tsx) when adding components. */\n tests?: boolean\n}\n\nexport const DEFAULT_CONFIG: CascadeConfig = {\n // Canonical hosted registry index (served from the landing site, documented in\n // llms.txt). Prefer this over a branch's GitHub raw URL, which 404s for\n // unauthenticated/private-repo requests and breaks `cascivo list`/`add`.\n registry: `${CASCIVO_HOST}/registry.json`,\n outputDir: 'src/components/ui',\n theme: 'light',\n}\n\nconst CONFIG_FILES = ['cascivo.config.ts', 'cascivo.config.js', 'cascivo.config.mjs']\n\n/** Apply defaults over a (possibly partial) user config object. */\nexport function resolveConfig(partial: Partial<CascadeConfig> | null | undefined): CascadeConfig {\n return { ...DEFAULT_CONFIG, ...partial }\n}\n\n/** Let env vars override config — used by the MCP server to pass `outputDir`. */\nexport function applyEnvOverrides(\n config: CascadeConfig,\n env: NodeJS.ProcessEnv = process.env,\n): CascadeConfig {\n return {\n ...config,\n ...(env.CASCIVO_REGISTRY ? { registry: env.CASCIVO_REGISTRY } : {}),\n ...(env.CASCIVO_OUTPUT_DIR ? { outputDir: env.CASCIVO_OUTPUT_DIR } : {}),\n }\n}\n\n/**\n * Locate and load `cascivo.config.{ts,js,mjs}` from `cwd`. Falls back to the\n * default config when no file is found or the file cannot be loaded. Env vars\n * (`CASCIVO_REGISTRY`, `CASCIVO_OUTPUT_DIR`) take precedence.\n */\nexport async function loadConfig(cwd: string = process.cwd()): Promise<CascadeConfig> {\n for (const file of CONFIG_FILES) {\n const path = join(cwd, file)\n if (!existsSync(path)) continue\n try {\n const mod = (await import(pathToFileURL(path).href)) as {\n default?: Partial<CascadeConfig>\n }\n return applyEnvOverrides(resolveConfig(mod.default ?? (mod as Partial<CascadeConfig>)))\n } catch {\n // Unloadable config (e.g. unsupported TS syntax) — fall back to defaults.\n return applyEnvOverrides(resolveConfig(null))\n }\n }\n return applyEnvOverrides(resolveConfig(null))\n}\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun'\n\n/** Narrow an arbitrary string to a known PackageManager. */\nexport function isPackageManager(value: string | undefined): value is PackageManager {\n return value === 'pnpm' || value === 'yarn' || value === 'npm' || value === 'bun'\n}\n\n/**\n * Lock files in probe order. `bun.lock` (bun ≥ 1.2 text lockfile) sits beside the\n * legacy binary `bun.lockb`; `package-lock.json` is npm's marker so a walk that\n * reaches an npm workspace root still resolves to npm rather than the default.\n */\nconst PM_LOCKFILES: readonly [string, PackageManager][] = [\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['bun.lockb', 'bun'],\n ['bun.lock', 'bun'],\n ['package-lock.json', 'npm'],\n]\n\n/** The package manager that invoked this process, from `npm_config_user_agent`. */\nfunction pmFromUserAgent(ua: string | undefined): PackageManager | undefined {\n if (!ua) return undefined\n const name = ua.split('/')[0]\n return isPackageManager(name) ? name : undefined\n}\n\n/** The `packageManager` corepack field of a package.json, if it names a known PM. */\nfunction pmFromPackageJson(dir: string): PackageManager | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n packageManager?: string\n }\n const name = pkg.packageManager?.split('@')[0]\n return isPackageManager(name) ? name : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Detect the package manager in use. Precedence, highest first:\n * 1. explicit override (the `--package-manager`/`--pm` flag)\n * 2. `CASCIVO_PACKAGE_MANAGER` env var\n * 3. `npm_config_user_agent` (the PM that spawned the CLI, e.g. `pnpm dlx`)\n * 4. an upward walk from `cwd` for a lock file or `packageManager` field —\n * this is what makes detection work inside a workspace, where the lock\n * file lives at the repo root, not in the app subdirectory the user runs\n * the CLI from. The walk stops after a directory containing `.git`.\n * 5. default `npm`.\n */\nexport function detectPackageManager(\n cwd: string = process.cwd(),\n opts: { override?: string; env?: NodeJS.ProcessEnv } = {},\n): PackageManager {\n const env = opts.env ?? process.env\n\n if (isPackageManager(opts.override)) return opts.override\n\n const envPm = env.CASCIVO_PACKAGE_MANAGER\n if (isPackageManager(envPm)) return envPm\n\n const uaPm = pmFromUserAgent(env.npm_config_user_agent)\n if (uaPm) return uaPm\n\n let current = cwd\n for (;;) {\n for (const [file, pm] of PM_LOCKFILES) {\n if (existsSync(join(current, file))) return pm\n }\n const fromField = pmFromPackageJson(current)\n if (fromField) return fromField\n // A `.git` directory marks the repo root — do not walk past it.\n if (existsSync(join(current, '.git'))) break\n const parent = dirname(current)\n if (parent === current) break\n current = parent\n }\n\n return 'npm'\n}\n\n/** The install subcommand each package manager uses to add dependencies. */\nexport function installCommand(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean } = {},\n): [string, string[]] {\n const verb = pm === 'npm' ? 'install' : 'add'\n const devFlag = opts.dev ? [pm === 'npm' ? '--save-dev' : '-D'] : []\n return [pm, [verb, ...devFlag, ...packages]]\n}\n\n/** Human-readable install command a user can copy-paste, e.g. `pnpm add -D cascivo`. */\nexport function installHint(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean } = {},\n): string {\n const [cmd, args] = installCommand(pm, packages, opts)\n return `${cmd} ${args.join(' ')}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-D7ddWN_9.mjs","names":[],"sources":["../src/utils/config.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\n/**\n * Canonical (and only) host for hosted cascivo artifacts (registry.json, per-item\n * r/<name>.json, marketplace.json, /llms/*, /context/*). The legacy docs.cascivo.com\n * subdomain is retired and 301s here. Keep in sync with CASCIVO_HOST in packages/mcp.\n */\nexport const CASCIVO_HOST = 'https://cascivo.com'\n\n/** All first-party themes shipped by @cascivo/themes (selectable via data-theme). */\nexport const THEMES = [\n 'light',\n 'dark',\n 'warm',\n 'flat',\n 'minimal',\n 'midnight',\n 'pastel',\n 'brutalist',\n 'corporate',\n 'terminal',\n 'cyberpunk',\n 'arcade',\n] as const\n\nexport type ThemeName = (typeof THEMES)[number]\n\nexport type RegistryNamespaceConfig =\n | string\n | { url: string; headers?: Record<string, string>; params?: Record<string, string> }\n\nexport interface CascadeConfig {\n /** URL of the registry.json index. */\n registry: string\n /** Directory (relative to project root) where components are written. */\n outputDir: string\n /** Default theme imported by `cascade init`. */\n theme: ThemeName\n /** Namespace → registry URL template (with {name} placeholder) or auth config. */\n registries?: Record<string, RegistryNamespaceConfig>\n /** Whether to copy test files (*.contract.test.tsx) when adding components. */\n tests?: boolean\n}\n\nexport const DEFAULT_CONFIG: CascadeConfig = {\n // Canonical hosted registry index (served from the landing site, documented in\n // llms.txt). Prefer this over a branch's GitHub raw URL, which 404s for\n // unauthenticated/private-repo requests and breaks `cascivo list`/`add`.\n registry: `${CASCIVO_HOST}/registry.json`,\n outputDir: 'src/components/ui',\n theme: 'light',\n}\n\nconst CONFIG_FILES = ['cascivo.config.ts', 'cascivo.config.js', 'cascivo.config.mjs']\n\n/** Apply defaults over a (possibly partial) user config object. */\nexport function resolveConfig(partial: Partial<CascadeConfig> | null | undefined): CascadeConfig {\n return { ...DEFAULT_CONFIG, ...partial }\n}\n\n/** Let env vars override config — used by the MCP server to pass `outputDir`. */\nexport function applyEnvOverrides(\n config: CascadeConfig,\n env: NodeJS.ProcessEnv = process.env,\n): CascadeConfig {\n return {\n ...config,\n ...(env.CASCIVO_REGISTRY ? { registry: env.CASCIVO_REGISTRY } : {}),\n ...(env.CASCIVO_OUTPUT_DIR ? { outputDir: env.CASCIVO_OUTPUT_DIR } : {}),\n }\n}\n\n/**\n * Locate and load `cascivo.config.{ts,js,mjs}` from `cwd`. Falls back to the\n * default config when no file is found or the file cannot be loaded. Env vars\n * (`CASCIVO_REGISTRY`, `CASCIVO_OUTPUT_DIR`) take precedence.\n */\nexport async function loadConfig(cwd: string = process.cwd()): Promise<CascadeConfig> {\n for (const file of CONFIG_FILES) {\n const path = join(cwd, file)\n if (!existsSync(path)) continue\n try {\n const mod = (await import(pathToFileURL(path).href)) as {\n default?: Partial<CascadeConfig>\n }\n return applyEnvOverrides(resolveConfig(mod.default ?? (mod as Partial<CascadeConfig>)))\n } catch {\n // Unloadable config (e.g. unsupported TS syntax) — fall back to defaults.\n return applyEnvOverrides(resolveConfig(null))\n }\n }\n return applyEnvOverrides(resolveConfig(null))\n}\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun'\n\n/** Narrow an arbitrary string to a known PackageManager. */\nexport function isPackageManager(value: string | undefined): value is PackageManager {\n return value === 'pnpm' || value === 'yarn' || value === 'npm' || value === 'bun'\n}\n\n/**\n * Lock files in probe order. `bun.lock` (bun ≥ 1.2 text lockfile) sits beside the\n * legacy binary `bun.lockb`; `package-lock.json` is npm's marker so a walk that\n * reaches an npm workspace root still resolves to npm rather than the default.\n */\nconst PM_LOCKFILES: readonly [string, PackageManager][] = [\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['bun.lockb', 'bun'],\n ['bun.lock', 'bun'],\n ['package-lock.json', 'npm'],\n]\n\n/** The package manager that invoked this process, from `npm_config_user_agent`. */\nfunction pmFromUserAgent(ua: string | undefined): PackageManager | undefined {\n if (!ua) return undefined\n const name = ua.split('/')[0]\n return isPackageManager(name) ? name : undefined\n}\n\n/** The `packageManager` corepack field of a package.json, if it names a known PM. */\nfunction pmFromPackageJson(dir: string): PackageManager | undefined {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n packageManager?: string\n }\n const name = pkg.packageManager?.split('@')[0]\n return isPackageManager(name) ? name : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Detect the package manager in use. Precedence, highest first:\n * 1. explicit override (the `--package-manager`/`--pm` flag)\n * 2. `CASCIVO_PACKAGE_MANAGER` env var\n * 3. `npm_config_user_agent` (the PM that spawned the CLI, e.g. `pnpm dlx`)\n * 4. an upward walk from `cwd` for a lock file or `packageManager` field —\n * this is what makes detection work inside a workspace, where the lock\n * file lives at the repo root, not in the app subdirectory the user runs\n * the CLI from. The walk stops after a directory containing `.git`.\n * 5. default `npm`.\n */\nexport function detectPackageManager(\n cwd: string = process.cwd(),\n opts: { override?: string; env?: NodeJS.ProcessEnv } = {},\n): PackageManager {\n const env = opts.env ?? process.env\n\n if (isPackageManager(opts.override)) return opts.override\n\n const envPm = env.CASCIVO_PACKAGE_MANAGER\n if (isPackageManager(envPm)) return envPm\n\n const uaPm = pmFromUserAgent(env.npm_config_user_agent)\n if (uaPm) return uaPm\n\n let current = cwd\n for (;;) {\n for (const [file, pm] of PM_LOCKFILES) {\n if (existsSync(join(current, file))) return pm\n }\n const fromField = pmFromPackageJson(current)\n if (fromField) return fromField\n // A `.git` directory marks the repo root — do not walk past it.\n if (existsSync(join(current, '.git'))) break\n const parent = dirname(current)\n if (parent === current) break\n current = parent\n }\n\n return 'npm'\n}\n\n/**\n * Pin every `@cascivo/*` package to an explicit version specifier.\n *\n * A bare name lets the package manager reuse whatever it already has resolvable. In a pnpm\n * workspace a sibling package's lockfile entry won and `@cascivo/i18n` resolved to **0.2.14**\n * while latest was **0.16.0** — then cascivo warned the adopter about the version it had\n * just installed itself. `add name@latest` (or the registry's known floor) makes the intent\n * explicit instead of leaving it to resolution order.\n *\n * Non-cascivo packages (`@preact/signals-react`) keep their bare name: their version is the\n * app's business, and cascivo has no floor to assert.\n */\nexport function pinSpecifier(pkg: string, floors: Record<string, string> = {}): string {\n if (!pkg.startsWith('@cascivo/') && pkg !== 'cascivo') return pkg\n if (pkg.includes('@', 1)) return pkg // already carries an explicit version\n const floor = floors[pkg]\n // `>=x.y.z` is not an installable specifier on its own; widen it to a range the package\n // manager understands, so a known floor is honoured rather than silently ignored.\n if (floor?.startsWith('>=')) return `${pkg}@${floor.slice(2)} - x`\n return `${pkg}@latest`\n}\n\n/** The install subcommand each package manager uses to add dependencies. */\nexport function installCommand(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean; floors?: Record<string, string>; pin?: string } = {},\n): [string, string[]] {\n const verb = pm === 'npm' ? 'install' : 'add'\n const devFlag = opts.dev ? [pm === 'npm' ? '--save-dev' : '-D'] : []\n const specs = packages.map((p) =>\n opts.pin && (p.startsWith('@cascivo/') || p === 'cascivo')\n ? `${p}@${opts.pin}`\n : pinSpecifier(p, opts.floors ?? {}),\n )\n return [pm, [verb, ...devFlag, ...specs]]\n}\n\n/** Human-readable install command a user can copy-paste, e.g. `pnpm add -D cascivo`. */\nexport function installHint(\n pm: PackageManager,\n packages: string[],\n opts: { dev?: boolean } = {},\n): string {\n const [cmd, args] = installCommand(pm, packages, opts)\n return `${cmd} ${args.join(' ')}`\n}\n"],"mappings":"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"}
|
|
@@ -2,25 +2,28 @@ import { t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
|
|
|
2
2
|
import { c as findComponent, n as readLock, o as fetchRegistry, r as sha256 } from "./lock-CW8UuEPJ.mjs";
|
|
3
3
|
import { t as checkPeerVersions } from "./peer-versions-Cep8Brn3.mjs";
|
|
4
4
|
//#region src/commands/drift.ts
|
|
5
|
-
/**
|
|
6
|
-
* `cascivo doctor --drift` — compares installed components against the
|
|
7
|
-
* registry. Two drift classes:
|
|
8
|
-
*
|
|
9
|
-
* 1. Local-edit drift: an installed file's content no longer matches what
|
|
10
|
-
* was copied at install time (hand edits, or deleted after install).
|
|
11
|
-
* 2. Peer-version drift: the currently-registered component source needs a
|
|
12
|
-
* newer `@cascivo/*` peer package (per `peerVersions`) than what's
|
|
13
|
-
* actually installed in node_modules — the dashboard-feedback failure
|
|
14
|
-
* mode (DataTable referencing an i18n builtin key an older published
|
|
15
|
-
* @cascivo/i18n build doesn't have).
|
|
16
|
-
*/
|
|
17
5
|
async function runDoctorDrift(config, cwd = process.cwd()) {
|
|
18
6
|
const lock = await readLock(cwd);
|
|
19
7
|
if (!lock || Object.keys(lock.items).length === 0) {
|
|
20
8
|
console.log("No installed components found in cascivo.lock.");
|
|
21
|
-
return
|
|
9
|
+
return {
|
|
10
|
+
ran: false,
|
|
11
|
+
reason: "no components installed (cascivo.lock is empty or missing)",
|
|
12
|
+
issues: 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
let registry;
|
|
16
|
+
try {
|
|
17
|
+
registry = await fetchRegistry(config.registry);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
const reason = `could not reach the registry (${error instanceof Error ? error.message : String(error)})`;
|
|
20
|
+
console.log(`Drift check skipped: ${reason}`);
|
|
21
|
+
return {
|
|
22
|
+
ran: false,
|
|
23
|
+
reason,
|
|
24
|
+
issues: 0
|
|
25
|
+
};
|
|
22
26
|
}
|
|
23
|
-
const registry = await fetchRegistry(config.registry);
|
|
24
27
|
let driftCount = 0;
|
|
25
28
|
for (const [name, entry] of Object.entries(lock.items)) {
|
|
26
29
|
const current = findComponent(registry, name);
|
|
@@ -50,8 +53,12 @@ async function runDoctorDrift(config, cwd = process.cwd()) {
|
|
|
50
53
|
console.log(`\n${driftCount} drift issue(s) found.`);
|
|
51
54
|
process.exitCode = 1;
|
|
52
55
|
} else console.log("No drift detected — installed components match the registry.");
|
|
56
|
+
return {
|
|
57
|
+
ran: true,
|
|
58
|
+
issues: driftCount
|
|
59
|
+
};
|
|
53
60
|
}
|
|
54
61
|
//#endregion
|
|
55
62
|
export { runDoctorDrift };
|
|
56
63
|
|
|
57
|
-
//# sourceMappingURL=drift-
|
|
64
|
+
//# sourceMappingURL=drift-B02BbFN_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift-B02BbFN_.mjs","names":[],"sources":["../src/commands/drift.ts"],"sourcesContent":["import type { CascadeConfig } from '../utils/config.js'\nimport { readFileSafe } from '../utils/fs.js'\nimport { readLock, sha256 } from '../utils/lock.js'\nimport { checkPeerVersions } from '../utils/peer-versions.js'\nimport { fetchRegistry, findComponent } from '../utils/registry.js'\n\n/**\n * `cascivo doctor --drift` — compares installed components against the\n * registry. Two drift classes:\n *\n * 1. Local-edit drift: an installed file's content no longer matches what\n * was copied at install time (hand edits, or deleted after install).\n * 2. Peer-version drift: the currently-registered component source needs a\n * newer `@cascivo/*` peer package (per `peerVersions`) than what's\n * actually installed in node_modules — the dashboard-feedback failure\n * mode (DataTable referencing an i18n builtin key an older published\n * @cascivo/i18n build doesn't have).\n */\n/**\n * What a drift run actually managed to do.\n *\n * `runDoctorDrift` used to return void, so the default `cascivo doctor` could not run it and\n * still say something honest. That is how \"No violations found.\" got printed by a run that\n * had never looked: the two were disjoint branches, and `--drift` reported five real issues\n * on the same project.\n */\nexport interface DriftOutcome {\n /** False when the check could not run at all (no lockfile, offline, unreachable registry). */\n ran: boolean\n /** Why it could not run — printed instead of an unqualified \"clean\". */\n reason?: string\n issues: number\n}\n\nexport async function runDoctorDrift(\n config: CascadeConfig,\n cwd: string = process.cwd(),\n): Promise<DriftOutcome> {\n const lock = await readLock(cwd)\n if (!lock || Object.keys(lock.items).length === 0) {\n console.log('No installed components found in cascivo.lock.')\n return {\n ran: false,\n reason: 'no components installed (cascivo.lock is empty or missing)',\n issues: 0,\n }\n }\n\n let registry: Awaited<ReturnType<typeof fetchRegistry>>\n try {\n registry = await fetchRegistry(config.registry)\n } catch (error) {\n const reason = `could not reach the registry (${error instanceof Error ? error.message : String(error)})`\n console.log(`Drift check skipped: ${reason}`)\n return { ran: false, reason, issues: 0 }\n }\n let driftCount = 0\n\n for (const [name, entry] of Object.entries(lock.items)) {\n const current = findComponent(registry, name)\n if (!current) continue\n\n for (const [path, lockedHash] of Object.entries(entry.files)) {\n const content = await readFileSafe(path)\n if (content === null) {\n console.log(`${name}: ${path} is missing (installed, then deleted)`)\n driftCount++\n continue\n }\n if (sha256(content) !== lockedHash) {\n console.log(`${name}: ${path} has local edits (differs from the version installed)`)\n driftCount++\n }\n }\n\n if (current.peerVersions) {\n const violations = await checkPeerVersions(cwd, current.peerVersions)\n for (const v of violations) {\n const installedDesc = v.installed ? `${v.installed} is installed` : 'it is not installed'\n console.log(`${name}: needs ${v.pkg} ${v.required}, but ${installedDesc}.`)\n driftCount++\n }\n }\n }\n\n if (driftCount > 0) {\n console.log(`\\n${driftCount} drift issue(s) found.`)\n process.exitCode = 1\n } else {\n console.log('No drift detected — installed components match the registry.')\n }\n return { ran: true, issues: driftCount }\n}\n"],"mappings":";;;;AAkCA,eAAsB,eACpB,QACA,MAAc,QAAQ,IAAI,GACH;CACvB,MAAM,OAAO,MAAM,SAAS,GAAG;CAC/B,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;EACjD,QAAQ,IAAI,gDAAgD;EAC5D,OAAO;GACL,KAAK;GACL,QAAQ;GACR,QAAQ;EACV;CACF;CAEA,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,cAAc,OAAO,QAAQ;CAChD,SAAS,OAAO;EACd,MAAM,SAAS,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;EACvG,QAAQ,IAAI,wBAAwB,QAAQ;EAC5C,OAAO;GAAE,KAAK;GAAO;GAAQ,QAAQ;EAAE;CACzC;CACA,IAAI,aAAa;CAEjB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,KAAK,GAAG;EACtD,MAAM,UAAU,cAAc,UAAU,IAAI;EAC5C,IAAI,CAAC,SAAS;EAEd,KAAK,MAAM,CAAC,MAAM,eAAe,OAAO,QAAQ,MAAM,KAAK,GAAG;GAC5D,MAAM,UAAU,MAAM,aAAa,IAAI;GACvC,IAAI,YAAY,MAAM;IACpB,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sCAAsC;IACnE;IACA;GACF;GACA,IAAI,OAAO,OAAO,MAAM,YAAY;IAClC,QAAQ,IAAI,GAAG,KAAK,IAAI,KAAK,sDAAsD;IACnF;GACF;EACF;EAEA,IAAI,QAAQ,cAAc;GACxB,MAAM,aAAa,MAAM,kBAAkB,KAAK,QAAQ,YAAY;GACpE,KAAK,MAAM,KAAK,YAAY;IAC1B,MAAM,gBAAgB,EAAE,YAAY,GAAG,EAAE,UAAU,iBAAiB;IACpE,QAAQ,IAAI,GAAG,KAAK,UAAU,EAAE,IAAI,GAAG,EAAE,SAAS,QAAQ,cAAc,EAAE;IAC1E;GACF;EACF;CACF;CAEA,IAAI,aAAa,GAAG;EAClB,QAAQ,IAAI,KAAK,WAAW,uBAAuB;EACnD,QAAQ,WAAW;CACrB,OACE,QAAQ,IAAI,8DAA8D;CAE5E,OAAO;EAAE,KAAK;EAAM,QAAQ;CAAW;AACzC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.16.
|
|
2
|
+
"version": "0.16.1",
|
|
3
3
|
"tokens": [
|
|
4
4
|
{ "name": "--cascivo-gray-0", "resolvedDefault": "oklch(1 0 0)" },
|
|
5
5
|
{ "name": "--cascivo-gray-50", "resolvedDefault": "oklch(0.985 0.002 264)" },
|
|
@@ -92,6 +92,14 @@
|
|
|
92
92
|
{ "name": "--cascivo-text-2xl", "resolvedDefault": "1.5rem" },
|
|
93
93
|
{ "name": "--cascivo-text-3xl", "resolvedDefault": "1.875rem" },
|
|
94
94
|
{ "name": "--cascivo-text-4xl", "resolvedDefault": "2.25rem" },
|
|
95
|
+
{ "name": "--cascivo-font-size-xs", "resolvedDefault": "0.75rem" },
|
|
96
|
+
{ "name": "--cascivo-font-size-sm", "resolvedDefault": "0.875rem" },
|
|
97
|
+
{ "name": "--cascivo-font-size-base", "resolvedDefault": "1rem" },
|
|
98
|
+
{ "name": "--cascivo-font-size-lg", "resolvedDefault": "1.125rem" },
|
|
99
|
+
{ "name": "--cascivo-font-size-xl", "resolvedDefault": "1.25rem" },
|
|
100
|
+
{ "name": "--cascivo-font-size-2xl", "resolvedDefault": "1.5rem" },
|
|
101
|
+
{ "name": "--cascivo-font-size-3xl", "resolvedDefault": "1.875rem" },
|
|
102
|
+
{ "name": "--cascivo-font-size-4xl", "resolvedDefault": "2.25rem" },
|
|
95
103
|
{ "name": "--cascivo-font-normal", "resolvedDefault": "400" },
|
|
96
104
|
{ "name": "--cascivo-font-medium", "resolvedDefault": "500" },
|
|
97
105
|
{ "name": "--cascivo-font-semibold", "resolvedDefault": "600" },
|
|
@@ -1117,6 +1125,16 @@
|
|
|
1117
1125
|
{ "name": "className", "type": "string", "required": false }
|
|
1118
1126
|
]
|
|
1119
1127
|
},
|
|
1128
|
+
{
|
|
1129
|
+
"name": "InfiniteScroll",
|
|
1130
|
+
"props": [
|
|
1131
|
+
{ "name": "onLoadMore", "type": "() => Promise<unknown> | unknown", "required": true },
|
|
1132
|
+
{ "name": "disabled", "type": "boolean", "required": false },
|
|
1133
|
+
{ "name": "rootMargin", "type": "string", "required": false },
|
|
1134
|
+
{ "name": "labels", "type": "{ loadMore?: string; loading?: string }", "required": false },
|
|
1135
|
+
{ "name": "className", "type": "string", "required": false }
|
|
1136
|
+
]
|
|
1137
|
+
},
|
|
1120
1138
|
{
|
|
1121
1139
|
"name": "InlineLoading",
|
|
1122
1140
|
"props": [
|
|
@@ -1180,6 +1198,18 @@
|
|
|
1180
1198
|
{ "name": "labels", "type": "{ required?: string }", "required": false }
|
|
1181
1199
|
]
|
|
1182
1200
|
},
|
|
1201
|
+
{
|
|
1202
|
+
"name": "LargeTitleHeader",
|
|
1203
|
+
"props": [
|
|
1204
|
+
{ "name": "title", "type": "string", "required": true },
|
|
1205
|
+
{ "name": "children", "type": "React.ReactNode", "required": true },
|
|
1206
|
+
{ "name": "leading", "type": "React.ReactNode", "required": false },
|
|
1207
|
+
{ "name": "actions", "type": "React.ReactNode", "required": false },
|
|
1208
|
+
{ "name": "level", "type": "1 | 2 | 3", "required": false },
|
|
1209
|
+
{ "name": "collapseDistance", "type": "number", "required": false },
|
|
1210
|
+
{ "name": "className", "type": "string", "required": false }
|
|
1211
|
+
]
|
|
1212
|
+
},
|
|
1183
1213
|
{
|
|
1184
1214
|
"name": "Link",
|
|
1185
1215
|
"props": [
|
|
@@ -1531,6 +1561,20 @@
|
|
|
1531
1561
|
{ "name": "format", "type": "Intl.RelativeTimeFormatOptions", "required": false }
|
|
1532
1562
|
]
|
|
1533
1563
|
},
|
|
1564
|
+
{
|
|
1565
|
+
"name": "ReorderList",
|
|
1566
|
+
"props": [
|
|
1567
|
+
{ "name": "value", "type": "ReorderItem[]", "required": true },
|
|
1568
|
+
{ "name": "onValueChange", "type": "(value: ReorderItem[]) => void", "required": true },
|
|
1569
|
+
{ "name": "disabled", "type": "boolean", "required": false },
|
|
1570
|
+
{
|
|
1571
|
+
"name": "labels",
|
|
1572
|
+
"type": "{ handle?: string; grabbed?: string; moved?: string; dropped?: string; cancelled?: string }",
|
|
1573
|
+
"required": false
|
|
1574
|
+
},
|
|
1575
|
+
{ "name": "className", "type": "string", "required": false }
|
|
1576
|
+
]
|
|
1577
|
+
},
|
|
1534
1578
|
{
|
|
1535
1579
|
"name": "Resizable",
|
|
1536
1580
|
"props": [
|
|
@@ -1984,10 +2028,38 @@
|
|
|
1984
2028
|
{ "name": "avatarProps", "type": "AvatarProps", "required": false }
|
|
1985
2029
|
]
|
|
1986
2030
|
},
|
|
2031
|
+
{
|
|
2032
|
+
"name": "VirtualList",
|
|
2033
|
+
"props": [
|
|
2034
|
+
{ "name": "items", "type": "Item[]", "required": true },
|
|
2035
|
+
{ "name": "itemHeight", "type": "number", "required": true },
|
|
2036
|
+
{ "name": "height", "type": "number", "required": true },
|
|
2037
|
+
{
|
|
2038
|
+
"name": "renderItem",
|
|
2039
|
+
"type": "(item: Item, index: number) => React.ReactNode",
|
|
2040
|
+
"required": true
|
|
2041
|
+
},
|
|
2042
|
+
{ "name": "overscan", "type": "number", "required": false },
|
|
2043
|
+
{ "name": "ariaLabel", "type": "string", "required": false },
|
|
2044
|
+
{ "name": "className", "type": "string", "required": false }
|
|
2045
|
+
]
|
|
2046
|
+
},
|
|
1987
2047
|
{
|
|
1988
2048
|
"name": "VisuallyHidden",
|
|
1989
2049
|
"props": [{ "name": "children", "type": "ReactNode", "required": true }]
|
|
1990
2050
|
},
|
|
2051
|
+
{
|
|
2052
|
+
"name": "WheelPicker",
|
|
2053
|
+
"props": [
|
|
2054
|
+
{ "name": "options", "type": "WheelPickerOption[]", "required": true },
|
|
2055
|
+
{ "name": "value", "type": "string", "required": true },
|
|
2056
|
+
{ "name": "onValueChange", "type": "(value: string) => void", "required": true },
|
|
2057
|
+
{ "name": "visibleCount", "type": "number", "required": false },
|
|
2058
|
+
{ "name": "itemHeight", "type": "number", "required": false },
|
|
2059
|
+
{ "name": "ariaLabel", "type": "string", "required": false },
|
|
2060
|
+
{ "name": "className", "type": "string", "required": false }
|
|
2061
|
+
]
|
|
2062
|
+
},
|
|
1991
2063
|
{
|
|
1992
2064
|
"name": "AppFrame",
|
|
1993
2065
|
"props": [
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-
|
|
2
|
+
import { a as detectPackageManager, c as isPackageManager, l as loadConfig, n as DEFAULT_CONFIG$1, o as installCommand, r as THEMES, s as installHint, t as CASCIVO_HOST, u as __exportAll } from "./config-D7ddWN_9.mjs";
|
|
3
3
|
import { n as resolveOutputPath, r as writeFileSafe, t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
|
|
4
4
|
import { r as fetchTextRetry, t as fetchJson } from "./http-CJZ5W0Fa.mjs";
|
|
5
5
|
import { a as writeLock, c as findComponent, i as updateLockEntry, n as readLock, o as fetchRegistry, r as sha256, s as fileName, t as createLock } from "./lock-CW8UuEPJ.mjs";
|
|
@@ -24,7 +24,11 @@ import { createInterface } from "node:readline/promises";
|
|
|
24
24
|
function installPackages(packages, cwd = process.cwd(), opts = {}) {
|
|
25
25
|
if (packages.length === 0) return true;
|
|
26
26
|
const pm = opts.pm ?? detectPackageManager(cwd);
|
|
27
|
-
const [cmd, args] = installCommand(pm, packages, {
|
|
27
|
+
const [cmd, args] = installCommand(pm, packages, {
|
|
28
|
+
dev: opts.dev ?? false,
|
|
29
|
+
...opts.floors ? { floors: opts.floors } : {},
|
|
30
|
+
...opts.pin ? { pin: opts.pin } : {}
|
|
31
|
+
});
|
|
28
32
|
console.log(`Installing ${packages.join(", ")} with ${cmd}…`);
|
|
29
33
|
if (spawnSync(cmd, args, {
|
|
30
34
|
cwd,
|
|
@@ -351,6 +355,17 @@ async function add(names, config, opts = {}) {
|
|
|
351
355
|
}
|
|
352
356
|
const { resolved, missing } = resolveBareClosure(registry, bareSpecs);
|
|
353
357
|
for (const name of missing) console.error(`Component "${name}" not found in registry. Run "cascivo list".`);
|
|
358
|
+
const announced = /* @__PURE__ */ new Set();
|
|
359
|
+
for (const { entry, requested } of resolved) {
|
|
360
|
+
if (!requested || !entry.install || announced.has(entry.install)) continue;
|
|
361
|
+
announced.add(entry.install);
|
|
362
|
+
console.log(`\n"${entry.name}" ships in the ${entry.install} npm package — no source is copied into your project. Updates come from your package manager, not \`cascivo update\`.`);
|
|
363
|
+
}
|
|
364
|
+
for (const { entry, requested } of resolved) {
|
|
365
|
+
if (!requested || !entry.deprecated) continue;
|
|
366
|
+
const { replacement, since, note } = entry.deprecated;
|
|
367
|
+
console.warn(`\n⚠ "${entry.name}" is deprecated since ${since}. Use "${replacement}" instead:\n cascivo add ${replacement}\n` + (note ? ` ${note}\n` : "") + ` Adding "${entry.name}" anyway — it still works.\n`);
|
|
368
|
+
}
|
|
354
369
|
const peerFloors = {};
|
|
355
370
|
for (const { entry } of resolved) for (const [pkg, floor] of Object.entries(entry.peerVersions ?? {})) peerFloors[pkg] = floor;
|
|
356
371
|
const npmPackages = /* @__PURE__ */ new Set();
|
|
@@ -499,11 +514,11 @@ function resolvePackageManagerFlag(args) {
|
|
|
499
514
|
* and GETTING-STARTED.md tells adopters to pin exactly. Regenerate with `pnpm regen`.
|
|
500
515
|
*/
|
|
501
516
|
const CASCIVO_VERSIONS = {
|
|
502
|
-
"@cascivo/react": "0.16.
|
|
503
|
-
"@cascivo/themes": "0.4.
|
|
504
|
-
"@cascivo/charts": "0.16.
|
|
505
|
-
"@cascivo/icons": "0.3.
|
|
506
|
-
"@cascivo/eslint-config": "0.2.
|
|
517
|
+
"@cascivo/react": "0.16.1",
|
|
518
|
+
"@cascivo/themes": "0.4.11",
|
|
519
|
+
"@cascivo/charts": "0.16.1",
|
|
520
|
+
"@cascivo/icons": "0.3.8",
|
|
521
|
+
"@cascivo/eslint-config": "0.2.2"
|
|
507
522
|
};
|
|
508
523
|
/** `@cascivo/core`'s declared `@preact/signals-react` peer range. */
|
|
509
524
|
const SIGNALS_PEER = ">=3.0.0";
|
|
@@ -667,7 +682,7 @@ function indexHtml(opts) {
|
|
|
667
682
|
<title>${opts.name}</title>
|
|
668
683
|
<style>
|
|
669
684
|
@layer vendor, cascivo.reset, cascivo.base, cascivo.tokens, cascivo.component,
|
|
670
|
-
cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
|
|
685
|
+
cascivo.platform, cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
|
|
671
686
|
/* cascivo.example is this app's own slot — above the component/blocks layers so your
|
|
672
687
|
styles win, below cascivo.override which stays free for one-off hotfixes. The
|
|
673
688
|
generated AGENTS.md tells the agent to write there, and this statement is what makes
|
|
@@ -816,7 +831,9 @@ import cascivo from '@cascivo/eslint-config'
|
|
|
816
831
|
|
|
817
832
|
export default [
|
|
818
833
|
js.configs.recommended,
|
|
819
|
-
|
|
834
|
+
// NOTE the \`.flat\` — the plugin exports both \`configs['recommended-latest']\` (the legacy
|
|
835
|
+
// eslintrc shape, which applies NOTHING here and reports no error) and this one.
|
|
836
|
+
reactHooks.configs.flat['recommended-latest'],
|
|
820
837
|
// Spread LAST — flat config is last-wins. This turns off \`react-hooks/immutability\`,
|
|
821
838
|
// which reports cascivo's signal writes (\`signal.value = next\`) as errors.
|
|
822
839
|
// See https://cascivo.com/docs/using-with-strict-eslint.md
|
|
@@ -880,7 +897,7 @@ This app's declared layer order (in \`index.html\`):
|
|
|
880
897
|
|
|
881
898
|
\`\`\`css
|
|
882
899
|
@layer vendor, cascivo.reset, cascivo.base, cascivo.tokens, cascivo.component,
|
|
883
|
-
cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
|
|
900
|
+
cascivo.platform, cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
|
|
884
901
|
\`\`\`
|
|
885
902
|
|
|
886
903
|
### Worked example — nesting, not new layers
|
|
@@ -1011,7 +1028,7 @@ async function create(args, cwd = process.cwd()) {
|
|
|
1011
1028
|
const templateSpec = flagValue(args, "template");
|
|
1012
1029
|
if (templateSpec) {
|
|
1013
1030
|
const { add } = await Promise.resolve().then(() => add_exports);
|
|
1014
|
-
const { loadConfig } = await import("./config-
|
|
1031
|
+
const { loadConfig } = await import("./config-D7ddWN_9.mjs").then((n) => n.i);
|
|
1015
1032
|
console.log(`\nInstalling template "${templateSpec}"…`);
|
|
1016
1033
|
await add([templateSpec], await loadConfig(), {
|
|
1017
1034
|
cwd: targetDir,
|
|
@@ -1030,6 +1047,7 @@ async function create(args, cwd = process.cwd()) {
|
|
|
1030
1047
|
//#region src/commands/doctor.ts
|
|
1031
1048
|
var doctor_exports = /* @__PURE__ */ __exportAll({
|
|
1032
1049
|
checkDuplicateCore: () => checkDuplicateCore,
|
|
1050
|
+
checkFormatterIgnore: () => checkFormatterIgnore,
|
|
1033
1051
|
checkProjectDependencies: () => checkProjectDependencies,
|
|
1034
1052
|
checkSignalsCompat: () => checkSignalsCompat,
|
|
1035
1053
|
checkSsrConfig: () => checkSsrConfig,
|
|
@@ -1250,6 +1268,38 @@ const VITE_CONFIG_FILES = [
|
|
|
1250
1268
|
* cliff the 2026-07-20 report hit (blocker #1). A text match, not a gate. Returns
|
|
1251
1269
|
* null when there's no Vite SSR framework or the config already handles it.
|
|
1252
1270
|
*/
|
|
1271
|
+
/**
|
|
1272
|
+
* Warn when the vendored components dir is not excluded from the project's formatter.
|
|
1273
|
+
*
|
|
1274
|
+
* Owning the code means your formatter rewrites it, and `cascivo update` then reports drift
|
|
1275
|
+
* on files you never edited. `cascivo init` writes the exclusion for you, but a project that
|
|
1276
|
+
* adopted cascivo before that existed — or that added Prettier afterwards — never got it.
|
|
1277
|
+
*/
|
|
1278
|
+
function checkFormatterIgnore(cwd, outputDir) {
|
|
1279
|
+
const pairs = [{
|
|
1280
|
+
configs: [
|
|
1281
|
+
".prettierrc",
|
|
1282
|
+
".prettierrc.json",
|
|
1283
|
+
".prettierrc.js",
|
|
1284
|
+
"prettier.config.js"
|
|
1285
|
+
],
|
|
1286
|
+
ignore: ".prettierignore"
|
|
1287
|
+
}, {
|
|
1288
|
+
configs: [".oxfmtrc", ".oxfmtrc.json"],
|
|
1289
|
+
ignore: ".oxfmtignore"
|
|
1290
|
+
}];
|
|
1291
|
+
const dir = outputDir.replace(/\/+$/, "");
|
|
1292
|
+
for (const { configs, ignore } of pairs) {
|
|
1293
|
+
if (!configs.some((f) => existsSync(join(cwd, f)))) continue;
|
|
1294
|
+
let content = "";
|
|
1295
|
+
try {
|
|
1296
|
+
content = readFileSync(join(cwd, ignore), "utf8");
|
|
1297
|
+
} catch {}
|
|
1298
|
+
if (content.split("\n").some((l) => l.trim().replace(/\/+$/, "") === dir)) continue;
|
|
1299
|
+
return `"${dir}/" is not excluded from your formatter. Running it will rewrite code you own, and \`cascivo update\` will then report drift on files you never edited. Add "${dir}/" to ${ignore}.`;
|
|
1300
|
+
}
|
|
1301
|
+
return null;
|
|
1302
|
+
}
|
|
1253
1303
|
function checkSsrConfig(cwd) {
|
|
1254
1304
|
let deps = {};
|
|
1255
1305
|
try {
|
|
@@ -1478,9 +1528,7 @@ async function generate(args, config) {
|
|
|
1478
1528
|
}
|
|
1479
1529
|
const { readFileSync } = await import("node:fs");
|
|
1480
1530
|
const configJson = readFileSync(inputArg, "utf-8");
|
|
1481
|
-
const
|
|
1482
|
-
await fetchRegistry(config.registry);
|
|
1483
|
-
const tsx = generateTsx(viewConfig, /* @__PURE__ */ new Map(), componentsDirArg ?? config.outputDir ?? "./src/components/ui");
|
|
1531
|
+
const tsx = generateTsx(JSON.parse(configJson), /* @__PURE__ */ new Map(), componentsDirArg ?? config.outputDir ?? "./src/components/ui");
|
|
1484
1532
|
const outPath = outArg ?? join(dirname(inputArg), `${basename(inputArg, ".json")}.tsx`);
|
|
1485
1533
|
writeFileSync(outPath, tsx, "utf-8");
|
|
1486
1534
|
console.log(`Generated ${outPath}`);
|
|
@@ -1570,6 +1618,85 @@ function hintEslintIfPresent(cwd) {
|
|
|
1570
1618
|
console.log("\nESLint: copied cascivo source may trip a strict host config on stylistic rules.");
|
|
1571
1619
|
console.log(" Scope them off your components dir — see docs/USING-WITH-STRICT-ESLINT.md");
|
|
1572
1620
|
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Write dependency entries into `package.json` so a failed install still leaves a
|
|
1623
|
+
* DECLARATIVE-complete project, one `install` away from working.
|
|
1624
|
+
*
|
|
1625
|
+
* The reported failure: one unrelated bad version range elsewhere in the adopter's
|
|
1626
|
+
* `package.json` made `pnpm add` exit non-zero. cascivo had already written
|
|
1627
|
+
* `cascivo.config.ts`, so the project claimed to be cascivo-configured with none of the
|
|
1628
|
+
* runtime present, and the printed advice ("install them yourself") was the same command
|
|
1629
|
+
* that had just failed. Recording the dependencies is the difference between "recoverable
|
|
1630
|
+
* with one command" and "figure out what was supposed to be here".
|
|
1631
|
+
*
|
|
1632
|
+
* Never overwrites an entry that already exists — the app's own pin wins.
|
|
1633
|
+
*/
|
|
1634
|
+
function recordDependencies(cwd, packages, opts) {
|
|
1635
|
+
const pkgPath = join(cwd, "package.json");
|
|
1636
|
+
if (!existsSync(pkgPath)) return [];
|
|
1637
|
+
let pkg;
|
|
1638
|
+
try {
|
|
1639
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
1640
|
+
} catch {
|
|
1641
|
+
return [];
|
|
1642
|
+
}
|
|
1643
|
+
const field = opts.dev ? "devDependencies" : "dependencies";
|
|
1644
|
+
const deps = pkg[field] ??= {};
|
|
1645
|
+
const added = [];
|
|
1646
|
+
for (const name of packages) {
|
|
1647
|
+
if (deps[name] !== void 0) continue;
|
|
1648
|
+
deps[name] = "latest";
|
|
1649
|
+
added.push(name);
|
|
1650
|
+
}
|
|
1651
|
+
if (added.length === 0) return [];
|
|
1652
|
+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
1653
|
+
return added;
|
|
1654
|
+
}
|
|
1655
|
+
/** Formatter ignore files, in the order a project is likely to use them. */
|
|
1656
|
+
const FORMATTER_IGNORES = [{
|
|
1657
|
+
config: [
|
|
1658
|
+
".prettierrc",
|
|
1659
|
+
".prettierrc.json",
|
|
1660
|
+
".prettierrc.js",
|
|
1661
|
+
"prettier.config.js"
|
|
1662
|
+
],
|
|
1663
|
+
ignore: ".prettierignore"
|
|
1664
|
+
}, {
|
|
1665
|
+
config: [".oxfmtrc", ".oxfmtrc.json"],
|
|
1666
|
+
ignore: ".oxfmtignore"
|
|
1667
|
+
}];
|
|
1668
|
+
/**
|
|
1669
|
+
* Exclude the vendored components dir from the project's formatter.
|
|
1670
|
+
*
|
|
1671
|
+
* Owning the code means your formatter reformats it, and then `cascivo update` reports drift
|
|
1672
|
+
* on files you never touched. This ACTS rather than hints: the ESLint equivalent below only
|
|
1673
|
+
* prints a pointer, and an adopter ran `prettier --write .` before ever reading it.
|
|
1674
|
+
*
|
|
1675
|
+
* Idempotent, and never rewrites an existing line.
|
|
1676
|
+
*/
|
|
1677
|
+
function ensureFormatterIgnore(cwd, outputDir) {
|
|
1678
|
+
const line = `${outputDir.replace(/\/+$/, "")}/`;
|
|
1679
|
+
for (const { config, ignore } of FORMATTER_IGNORES) {
|
|
1680
|
+
if (!(config.some((f) => existsSync(join(cwd, f))) || hasPrettierKeyInPackageJson(cwd, ignore))) continue;
|
|
1681
|
+
const path = join(cwd, ignore);
|
|
1682
|
+
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
1683
|
+
if (current.split("\n").some((l) => l.trim() === line)) continue;
|
|
1684
|
+
const banner = "# cascivo: vendored component source — you own it, so do not reformat it\n";
|
|
1685
|
+
writeFileSync(path, current === "" ? banner + line + "\n" : `${current.replace(/\n*$/, "\n")}\n${banner}${line}\n`, "utf8");
|
|
1686
|
+
console.log(`\nAdded "${line}" to ${ignore} (so your formatter does not rewrite copied source).`);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
/** `prettier` key in package.json counts as a Prettier config — only relevant to .prettierignore. */
|
|
1690
|
+
function hasPrettierKeyInPackageJson(cwd, ignore) {
|
|
1691
|
+
if (ignore !== ".prettierignore") return false;
|
|
1692
|
+
const pkgPath = join(cwd, "package.json");
|
|
1693
|
+
if (!existsSync(pkgPath)) return false;
|
|
1694
|
+
try {
|
|
1695
|
+
return "prettier" in JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
1696
|
+
} catch {
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1573
1700
|
/** The "here's everything you need" summary, printed once at the end of init. */
|
|
1574
1701
|
function printDependencySummary() {
|
|
1575
1702
|
console.log("\nDependencies");
|
|
@@ -1607,13 +1734,36 @@ async function init(args = [], cwd = process.cwd()) {
|
|
|
1607
1734
|
pm,
|
|
1608
1735
|
dev: true
|
|
1609
1736
|
});
|
|
1610
|
-
if (!runtimeOk || !devOk)
|
|
1737
|
+
if (!runtimeOk || !devOk) {
|
|
1738
|
+
const recorded = [...runtimeOk ? [] : recordDependencies(cwd, RUNTIME_DEPS, { dev: false }), ...devOk ? [] : recordDependencies(cwd, DEV_DEPS, { dev: true })];
|
|
1739
|
+
console.error("\nInstall failed — cascivo.config.ts was written but the packages are not installed.");
|
|
1740
|
+
if (recorded.length > 0) {
|
|
1741
|
+
console.error(`Wrote ${recorded.length} dependency entries to package.json: ${recorded.join(", ")}`);
|
|
1742
|
+
console.error(`Recover with:\n ${pm} install`);
|
|
1743
|
+
} else console.error(`Recover with:\n ${installHint(pm, RUNTIME_DEPS)}\n ${installHint(pm, DEV_DEPS, { dev: true })}`);
|
|
1744
|
+
process.exitCode = 1;
|
|
1745
|
+
}
|
|
1611
1746
|
}
|
|
1612
|
-
console.log("\
|
|
1613
|
-
console.log(` import '@cascivo/
|
|
1614
|
-
console.log("
|
|
1747
|
+
console.log("\nStylesheets — import these once, in this order, in your entry file:");
|
|
1748
|
+
console.log(` import '@cascivo/tokens' // primitive tokens — every --cascivo-* value`);
|
|
1749
|
+
console.log(` import '@cascivo/themes/${theme}.css'${" ".repeat(Math.max(1, 16 - theme.length))}// the ${theme} theme's semantic values`);
|
|
1750
|
+
console.log(` // …then your component CSS (\`cascivo add\` writes .module.css beside each component)`);
|
|
1751
|
+
console.log("\nThen set the theme on your root element:");
|
|
1615
1752
|
console.log(` <html data-theme="${theme}">`);
|
|
1753
|
+
console.log("\nSwitching themes at runtime? Use a bundle instead of the single theme:");
|
|
1754
|
+
console.log(" import '@cascivo/themes/light-dark.css' // light + dark — the common case");
|
|
1755
|
+
console.log(" import '@cascivo/themes/all.css' // all twelve themes");
|
|
1756
|
+
console.log(" import { ThemeProvider } from '@cascivo/core'");
|
|
1757
|
+
console.log("\nIn YOUR components, when you read a signal during render:");
|
|
1758
|
+
console.log(" import { useSignals } from '@cascivo/core'");
|
|
1759
|
+
console.log(" function MyComponent() {");
|
|
1760
|
+
console.log(" useSignals() // ← first statement, or the component never re-renders");
|
|
1761
|
+
console.log(" return <span>{count.value}</span>");
|
|
1762
|
+
console.log(" }");
|
|
1763
|
+
console.log("\nAdding a chart later? Charts ship as an npm package with their own stylesheet:");
|
|
1764
|
+
console.log(" import '@cascivo/charts/styles.css' // `cascivo add <chart>` reminds you");
|
|
1616
1765
|
printDependencySummary();
|
|
1766
|
+
ensureFormatterIgnore(cwd, DEFAULT_CONFIG$1.outputDir);
|
|
1617
1767
|
hintEslintIfPresent(cwd);
|
|
1618
1768
|
console.log("\nAdd components with: cascivo add <name>");
|
|
1619
1769
|
}
|
|
@@ -1628,12 +1778,22 @@ const TYPE_LABELS = {
|
|
|
1628
1778
|
flow: "Flow (npm: @cascivo/flow)",
|
|
1629
1779
|
editor: "Editor (npm: @cascivo/editor)"
|
|
1630
1780
|
};
|
|
1781
|
+
/**
|
|
1782
|
+
* Deprecation marker for the listing.
|
|
1783
|
+
*
|
|
1784
|
+
* Shown at DISCOVERY time on purpose. `overflow-menu` carried a `@deprecated` JSDoc in its
|
|
1785
|
+
* source for months, which an adopter only meets after they have already vendored the file —
|
|
1786
|
+
* and it pointed at an import path that cannot resolve on either install path.
|
|
1787
|
+
*/
|
|
1788
|
+
function deprecationSuffix(c) {
|
|
1789
|
+
return c.deprecated ? ` ⚠ deprecated → ${c.deprecated.replacement}` : "";
|
|
1790
|
+
}
|
|
1631
1791
|
/** Render a group of entries as an aligned text table (no section header). */
|
|
1632
1792
|
function formatGroup(entries) {
|
|
1633
1793
|
const rows = entries.map((c) => [
|
|
1634
1794
|
c.name,
|
|
1635
1795
|
c.category,
|
|
1636
|
-
c.description
|
|
1796
|
+
c.description + deprecationSuffix(c)
|
|
1637
1797
|
]);
|
|
1638
1798
|
const headers = [
|
|
1639
1799
|
"Name",
|
|
@@ -2697,12 +2857,12 @@ async function run(args) {
|
|
|
2697
2857
|
case "doctor": {
|
|
2698
2858
|
const ci = rest.includes("--ci");
|
|
2699
2859
|
if (rest.includes("--drift")) {
|
|
2700
|
-
const { runDoctorDrift } = await import("./drift-
|
|
2860
|
+
const { runDoctorDrift } = await import("./drift-B02BbFN_.mjs");
|
|
2701
2861
|
await runDoctorDrift(await loadConfig());
|
|
2702
2862
|
} else {
|
|
2703
2863
|
const cwd = process.cwd();
|
|
2704
2864
|
const result = await runDoctor(cwd);
|
|
2705
|
-
const { checkDuplicateCore, checkProjectDependencies, checkSignalsCompat, checkSsrConfig, detectInstallPath } = await Promise.resolve().then(() => doctor_exports);
|
|
2865
|
+
const { checkDuplicateCore, checkFormatterIgnore, checkProjectDependencies, checkSignalsCompat, checkSsrConfig, detectInstallPath } = await Promise.resolve().then(() => doctor_exports);
|
|
2706
2866
|
const adopter = detectInstallPath(cwd) !== "unknown";
|
|
2707
2867
|
const deps = adopter ? checkProjectDependencies(cwd) : [];
|
|
2708
2868
|
const missingRequired = deps.filter((d) => d.required);
|
|
@@ -2710,15 +2870,23 @@ async function run(args) {
|
|
|
2710
2870
|
const signalsError = signalsCompat?.severity === "error";
|
|
2711
2871
|
const ssrHint = adopter ? checkSsrConfig(cwd) : null;
|
|
2712
2872
|
const duplicateCore = adopter ? await checkDuplicateCore(cwd) : null;
|
|
2713
|
-
|
|
2873
|
+
const formatterHint = adopter ? checkFormatterIgnore(cwd, (await loadConfig()).outputDir) : null;
|
|
2874
|
+
const { runDoctorDrift } = await import("./drift-B02BbFN_.mjs");
|
|
2875
|
+
const driftOutcome = adopter ? await runDoctorDrift(await loadConfig(), cwd) : {
|
|
2876
|
+
ran: false,
|
|
2877
|
+
reason: "no cascivo install detected",
|
|
2878
|
+
issues: 0
|
|
2879
|
+
};
|
|
2880
|
+
if (result.passed && deps.length === 0 && signalsCompat === null && ssrHint === null && duplicateCore === null && formatterHint === null && driftOutcome.issues === 0) console.log(driftOutcome.ran ? "No violations found." : `No violations found (drift: not checked — ${driftOutcome.reason}).`);
|
|
2714
2881
|
else {
|
|
2715
2882
|
for (const v of result.violations) console.error(`[${v.rule}] ${v.detail}\n ${v.file}`);
|
|
2716
2883
|
for (const d of missingRequired) console.error(d.kind === "forbidden" ? `[forbidden-dependency] ${d.package} must not be a direct dependency here. Remove it — ${d.hint}` : `[missing-dependency] ${d.package} is not in package.json. Install: ${d.hint}`);
|
|
2717
2884
|
if (signalsCompat) (signalsError ? console.error : console.log)(`[${signalsError ? "signals-incompatible" : "signals-outdated"}] ${signalsCompat.detail} Upgrade: ${signalsCompat.hint}`);
|
|
2718
2885
|
if (ssrHint) console.log(`[ssr-config] ${ssrHint}`);
|
|
2886
|
+
if (formatterHint) console.log(`[formatter-drift] ${formatterHint}`);
|
|
2719
2887
|
if (duplicateCore) console.error(`[duplicate-core] More than one @cascivo/core is installed (root: ${duplicateCore.root ?? "none"}; ${duplicateCore.nested.join("; ")}). ` + duplicateCore.hint);
|
|
2720
2888
|
for (const d of deps.filter((x) => !x.required)) console.log(`[optional] ${d.package} is not installed; add it when a component or chart needs it: ${d.hint}`);
|
|
2721
|
-
if (ci && (result.violations.length > 0 || missingRequired.length > 0 || signalsError || duplicateCore !== null)) process.exitCode = 1;
|
|
2889
|
+
if (ci && (result.violations.length > 0 || missingRequired.length > 0 || signalsError || duplicateCore !== null || driftOutcome.issues > 0)) process.exitCode = 1;
|
|
2722
2890
|
}
|
|
2723
2891
|
}
|
|
2724
2892
|
break;
|