cascivo 0.5.0 → 0.5.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/README.md +2 -2
- package/dist/{config-CYlTGdmE.mjs → config-C6GdrbvF.mjs} +4 -4
- package/dist/config-C6GdrbvF.mjs.map +1 -0
- package/dist/{drift-Ciw5qeEt.mjs → drift-qTzJ75Ds.mjs} +3 -2
- package/dist/{drift-Ciw5qeEt.mjs.map → drift-qTzJ75Ds.mjs.map} +1 -1
- package/dist/index.mjs +96 -8
- package/dist/index.mjs.map +1 -1
- package/dist/{peer-versions-B2bCgko-.mjs → lock-CW8UuEPJ.mjs} +2 -65
- package/dist/lock-CW8UuEPJ.mjs.map +1 -0
- package/dist/peer-versions-Cep8Brn3.mjs +68 -0
- package/dist/peer-versions-Cep8Brn3.mjs.map +1 -0
- package/package.json +4 -3
- package/dist/config-CYlTGdmE.mjs.map +0 -1
- package/dist/peer-versions-B2bCgko-.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[](https://github.com/cascivo/cascivo/blob/main/LICENSE)
|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
[npm](https://www.npmjs.com/package/cascivo) · [cascivo.com](https://cascivo.com) · [Docs](https://
|
|
13
|
+
[npm](https://www.npmjs.com/package/cascivo) · [cascivo.com](https://cascivo.com) · [Docs](https://cascivo.com/docs) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -80,6 +80,6 @@ pnpm add cascivo
|
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
|
-
[cascivo.com](https://cascivo.com) · [Docs](https://
|
|
83
|
+
[cascivo.com](https://cascivo.com) · [Docs](https://cascivo.com/docs) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/cascivo/cascivo) · AI agents: read [`llms.txt`](https://cascivo.com/llms.txt) (install steps + component index, plain text) or use [`@cascivo/mcp`](https://github.com/cascivo/cascivo/tree/main/packages/mcp) and [`registry.json`](https://github.com/cascivo/cascivo/blob/main/registry.json) · MIT
|
|
84
84
|
|
|
85
85
|
<div align="center"><a href="https://cascivo.com"><img src="https://cascivo.com/favicon.svg" width="28" height="28" alt="cascivo"></a></div>
|
|
@@ -28,9 +28,9 @@ var config_exports = /* @__PURE__ */ __exportAll({
|
|
|
28
28
|
resolveConfig: () => resolveConfig
|
|
29
29
|
});
|
|
30
30
|
/**
|
|
31
|
-
* Canonical host for hosted cascivo artifacts (registry.json, per-item
|
|
32
|
-
* r/<name>.json, marketplace.json). docs.cascivo.com
|
|
33
|
-
* Keep in sync with CASCIVO_HOST in packages/mcp.
|
|
31
|
+
* Canonical (and only) host for hosted cascivo artifacts (registry.json, per-item
|
|
32
|
+
* r/<name>.json, marketplace.json, /llms/*, /context/*). The legacy docs.cascivo.com
|
|
33
|
+
* subdomain is retired and 301s here. Keep in sync with CASCIVO_HOST in packages/mcp.
|
|
34
34
|
*/
|
|
35
35
|
const CASCIVO_HOST = "https://cascivo.com";
|
|
36
36
|
/** All first-party themes shipped by @cascivo/themes (selectable via data-theme). */
|
|
@@ -168,4 +168,4 @@ function installHint(pm, packages, opts = {}) {
|
|
|
168
168
|
//#endregion
|
|
169
169
|
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
170
|
|
|
171
|
-
//# sourceMappingURL=config-
|
|
171
|
+
//# sourceMappingURL=config-C6GdrbvF.mjs.map
|
|
@@ -0,0 +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,5 +1,6 @@
|
|
|
1
1
|
import { t as readFileSafe } from "./fs-m7ZvuBBm.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { c as findComponent, n as readLock, o as fetchRegistry, r as sha256 } from "./lock-CW8UuEPJ.mjs";
|
|
3
|
+
import { t as checkPeerVersions } from "./peer-versions-Cep8Brn3.mjs";
|
|
3
4
|
//#region src/commands/drift.ts
|
|
4
5
|
/**
|
|
5
6
|
* `cascivo doctor --drift` — compares installed components against the
|
|
@@ -53,4 +54,4 @@ async function runDoctorDrift(config, cwd = process.cwd()) {
|
|
|
53
54
|
//#endregion
|
|
54
55
|
export { runDoctorDrift };
|
|
55
56
|
|
|
56
|
-
//# sourceMappingURL=drift-
|
|
57
|
+
//# sourceMappingURL=drift-qTzJ75Ds.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drift-
|
|
1
|
+
{"version":3,"file":"drift-qTzJ75Ds.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 */\nexport async function runDoctorDrift(\n config: CascadeConfig,\n cwd: string = process.cwd(),\n): Promise<void> {\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 }\n\n const registry = await fetchRegistry(config.registry)\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}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,eAAsB,eACpB,QACA,MAAc,QAAQ,IAAI,GACX;CACf,MAAM,OAAO,MAAM,SAAS,GAAG;CAC/B,IAAI,CAAC,QAAQ,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;EACjD,QAAQ,IAAI,gDAAgD;EAC5D;CACF;CAEA,MAAM,WAAW,MAAM,cAAc,OAAO,QAAQ;CACpD,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;AAE9E"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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-C6GdrbvF.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
|
-
import { a as
|
|
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";
|
|
6
|
+
import { n as readInstalledPackageVersion, r as compareVersions, t as checkPeerVersions } from "./peer-versions-Cep8Brn3.mjs";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
8
|
import { argv, stdin, stdout } from "node:process";
|
|
8
9
|
import { pathToFileURL } from "node:url";
|
|
@@ -937,7 +938,7 @@ async function create(args, cwd = process.cwd()) {
|
|
|
937
938
|
const templateSpec = flagValue(args, "template");
|
|
938
939
|
if (templateSpec) {
|
|
939
940
|
const { add } = await Promise.resolve().then(() => add_exports);
|
|
940
|
-
const { loadConfig } = await import("./config-
|
|
941
|
+
const { loadConfig } = await import("./config-C6GdrbvF.mjs").then((n) => n.i);
|
|
941
942
|
console.log(`\nInstalling template "${templateSpec}"…`);
|
|
942
943
|
await add([templateSpec], await loadConfig(), {
|
|
943
944
|
cwd: targetDir,
|
|
@@ -956,6 +957,8 @@ async function create(args, cwd = process.cwd()) {
|
|
|
956
957
|
//#region src/commands/doctor.ts
|
|
957
958
|
var doctor_exports = /* @__PURE__ */ __exportAll({
|
|
958
959
|
checkProjectDependencies: () => checkProjectDependencies,
|
|
960
|
+
checkSignalsCompat: () => checkSignalsCompat,
|
|
961
|
+
checkSsrConfig: () => checkSsrConfig,
|
|
959
962
|
isAdopterProject: () => isAdopterProject,
|
|
960
963
|
runDoctor: () => runDoctor,
|
|
961
964
|
stripCommentsAndStrings: () => stripCommentsAndStrings
|
|
@@ -1011,6 +1014,85 @@ function checkProjectDependencies(cwd) {
|
|
|
1011
1014
|
});
|
|
1012
1015
|
return findings;
|
|
1013
1016
|
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Checks the installed `@preact/signals-react` against the installed React.
|
|
1019
|
+
* React 19 removed the `__SECRET_INTERNALS…` export that signals-react 2.x
|
|
1020
|
+
* imports, so a 2.x runtime on React 19 dies with a `SyntaxError` at module
|
|
1021
|
+
* evaluation (the 2026-07-20 report's blocker #2). cascivo peer-depends on
|
|
1022
|
+
* `>=3.0.0`, but a lockfile carried over from an earlier install can still pin
|
|
1023
|
+
* 2.x — this turns that into a diagnosed condition with a fix. Returns null when
|
|
1024
|
+
* either package is absent/unreadable (nothing reliable to advise on) or the
|
|
1025
|
+
* pairing is fine.
|
|
1026
|
+
*/
|
|
1027
|
+
async function checkSignalsCompat(cwd) {
|
|
1028
|
+
const [signals, react] = await Promise.all([readInstalledPackageVersion(cwd, "@preact/signals-react"), readInstalledPackageVersion(cwd, "react")]);
|
|
1029
|
+
if (signals === null) return null;
|
|
1030
|
+
let signalsBelow3;
|
|
1031
|
+
try {
|
|
1032
|
+
signalsBelow3 = compareVersions(signals, "3.0.0") < 0;
|
|
1033
|
+
} catch {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
if (!signalsBelow3) return null;
|
|
1037
|
+
const hint = installHint(detectPackageManager(cwd), ["@preact/signals-react@^3"]);
|
|
1038
|
+
let reactMajor = null;
|
|
1039
|
+
if (react !== null) {
|
|
1040
|
+
const m = /^(\d+)\./.exec(react.trim());
|
|
1041
|
+
reactMajor = m ? Number(m[1]) : null;
|
|
1042
|
+
}
|
|
1043
|
+
if (reactMajor !== null && reactMajor >= 19) return {
|
|
1044
|
+
severity: "error",
|
|
1045
|
+
detail: `@preact/signals-react ${signals} cannot run on React ${react} — React 19 removed an internal signals 2.x imports, so it fails at module load. Upgrade to signals-react 3.x.`,
|
|
1046
|
+
hint
|
|
1047
|
+
};
|
|
1048
|
+
return {
|
|
1049
|
+
severity: "warning",
|
|
1050
|
+
detail: `@preact/signals-react ${signals} is below the required 3.x floor. It works on React 18 today but breaks the moment you move to React 19; upgrade now.`,
|
|
1051
|
+
hint
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
/** Vite SSR frameworks whose default setup needs `ssr.noExternal` for cascivo. */
|
|
1055
|
+
const VITE_SSR_MARKERS = [
|
|
1056
|
+
"@tanstack/react-start",
|
|
1057
|
+
"vite-ssr",
|
|
1058
|
+
"@remix-run/dev"
|
|
1059
|
+
];
|
|
1060
|
+
const VITE_CONFIG_FILES = [
|
|
1061
|
+
"vite.config.ts",
|
|
1062
|
+
"vite.config.js",
|
|
1063
|
+
"vite.config.mjs",
|
|
1064
|
+
"vite.config.mts"
|
|
1065
|
+
];
|
|
1066
|
+
/**
|
|
1067
|
+
* Advisory: on a Vite SSR framework, cascivo's per-component `.css` side-effect
|
|
1068
|
+
* imports crash a bare server-side ESM loader unless the packages are marked
|
|
1069
|
+
* `ssr.noExternal` (or the `cascivoSsr()` plugin is used). This warns when a known
|
|
1070
|
+
* Vite SSR framework is present but no vite config mentions either — the exact
|
|
1071
|
+
* cliff the 2026-07-20 report hit (blocker #1). A text match, not a gate. Returns
|
|
1072
|
+
* null when there's no Vite SSR framework or the config already handles it.
|
|
1073
|
+
*/
|
|
1074
|
+
function checkSsrConfig(cwd) {
|
|
1075
|
+
let deps = {};
|
|
1076
|
+
try {
|
|
1077
|
+
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
|
|
1078
|
+
deps = {
|
|
1079
|
+
...pkg.dependencies,
|
|
1080
|
+
...pkg.devDependencies
|
|
1081
|
+
};
|
|
1082
|
+
} catch {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
const framework = VITE_SSR_MARKERS.find((m) => deps[m] !== void 0);
|
|
1086
|
+
if (framework === void 0) return null;
|
|
1087
|
+
for (const file of VITE_CONFIG_FILES) {
|
|
1088
|
+
const path = join(cwd, file);
|
|
1089
|
+
if (!existsSync(path)) continue;
|
|
1090
|
+
const config = readFileSync(path, "utf8");
|
|
1091
|
+
if (/noExternal/.test(config) && /@cascivo/.test(config)) return null;
|
|
1092
|
+
if (/cascivoSsr/.test(config)) return null;
|
|
1093
|
+
}
|
|
1094
|
+
return `${framework} is a Vite SSR framework, but no vite config marks the cascivo packages ssr.noExternal — an unconfigured SSR build crashes with \`Unknown file extension ".css"\`. Add \`ssr: { noExternal: [/^@cascivo\\//] }\` (or the cascivoSsr() plugin from @cascivo/vite-plugin). Recipe: https://cascivo.com/docs/using-with-vite-ssr.md`;
|
|
1095
|
+
}
|
|
1014
1096
|
const BANNED_HOOKS = [
|
|
1015
1097
|
"useState",
|
|
1016
1098
|
"useEffect",
|
|
@@ -2436,20 +2518,26 @@ async function run(args) {
|
|
|
2436
2518
|
case "doctor": {
|
|
2437
2519
|
const ci = rest.includes("--ci");
|
|
2438
2520
|
if (rest.includes("--drift")) {
|
|
2439
|
-
const { runDoctorDrift } = await import("./drift-
|
|
2521
|
+
const { runDoctorDrift } = await import("./drift-qTzJ75Ds.mjs");
|
|
2440
2522
|
await runDoctorDrift(await loadConfig());
|
|
2441
2523
|
} else {
|
|
2442
2524
|
const cwd = process.cwd();
|
|
2443
2525
|
const result = await runDoctor(cwd);
|
|
2444
|
-
const { checkProjectDependencies, isAdopterProject } = await Promise.resolve().then(() => doctor_exports);
|
|
2445
|
-
const
|
|
2526
|
+
const { checkProjectDependencies, checkSignalsCompat, checkSsrConfig, isAdopterProject } = await Promise.resolve().then(() => doctor_exports);
|
|
2527
|
+
const adopter = isAdopterProject(cwd);
|
|
2528
|
+
const deps = adopter ? checkProjectDependencies(cwd) : [];
|
|
2446
2529
|
const missingRequired = deps.filter((d) => d.required);
|
|
2447
|
-
|
|
2530
|
+
const signalsCompat = adopter ? await checkSignalsCompat(cwd) : null;
|
|
2531
|
+
const signalsError = signalsCompat?.severity === "error";
|
|
2532
|
+
const ssrHint = adopter ? checkSsrConfig(cwd) : null;
|
|
2533
|
+
if (result.passed && deps.length === 0 && signalsCompat === null && ssrHint === null) console.log("No violations found.");
|
|
2448
2534
|
else {
|
|
2449
2535
|
for (const v of result.violations) console.error(`[${v.rule}] ${v.detail}\n ${v.file}`);
|
|
2450
2536
|
for (const d of missingRequired) console.error(`[missing-dependency] ${d.package} is not in package.json — copied cascivo source needs it. Install: ${d.hint}`);
|
|
2537
|
+
if (signalsCompat) (signalsError ? console.error : console.log)(`[${signalsError ? "signals-incompatible" : "signals-outdated"}] ${signalsCompat.detail} Upgrade: ${signalsCompat.hint}`);
|
|
2538
|
+
if (ssrHint) console.log(`[ssr-config] ${ssrHint}`);
|
|
2451
2539
|
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}`);
|
|
2452
|
-
if (ci && (result.violations.length > 0 || missingRequired.length > 0)) process.exitCode = 1;
|
|
2540
|
+
if (ci && (result.violations.length > 0 || missingRequired.length > 0 || signalsError)) process.exitCode = 1;
|
|
2453
2541
|
}
|
|
2454
2542
|
}
|
|
2455
2543
|
break;
|