@cmssy/cli 16.11.1 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +83 -29
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2247,35 +2247,19 @@ function objectType(fields, depth) {
|
|
|
2247
2247
|
if (!body.length) return "Record<string, unknown>";
|
|
2248
2248
|
return ["{", ...body, `${INDENT.repeat(depth)}}`].join("\n");
|
|
2249
2249
|
}
|
|
2250
|
-
var PREAMBLE =
|
|
2251
|
-
|
|
2250
|
+
var PREAMBLE = `import type {
|
|
2251
|
+
CmssyLocalizedValue as CmssyLocalized,
|
|
2252
|
+
ResolvedMedia as CmssyMedia,
|
|
2253
|
+
FileFieldValue as CmssyFile,
|
|
2254
|
+
CmssyModelRecord as CmssyRecordOf,
|
|
2255
|
+
} from "@cmssy/core";
|
|
2252
2256
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
export interface CmssyMedia {
|
|
2259
|
-
id: string;
|
|
2260
|
-
url: string | null;
|
|
2261
|
-
visibility: "public" | "private";
|
|
2262
|
-
alt?: string;
|
|
2263
|
-
width?: number;
|
|
2264
|
-
height?: number;
|
|
2257
|
+
export type { CmssyLocalized, CmssyMedia, CmssyFile, CmssyRecordOf };`;
|
|
2258
|
+
function preambleImports() {
|
|
2259
|
+
const clause = /import type \{([^}]*)\} from "@cmssy\/core";/.exec(PREAMBLE);
|
|
2260
|
+
if (!clause?.[1]) return [];
|
|
2261
|
+
return clause[1].split(",").map((entry) => entry.trim().split(/\s+as\s+/)[0]?.trim() ?? "").filter((name) => name.length > 0);
|
|
2265
2262
|
}
|
|
2266
|
-
|
|
2267
|
-
/** What a file field holds. Mirrors \`FileFieldValue\` in @cmssy/types. */
|
|
2268
|
-
export type CmssyFile = string;
|
|
2269
|
-
|
|
2270
|
-
/** A record as \`public.model.records\` returns it, with \`data\` typed. */
|
|
2271
|
-
export interface CmssyRecordOf<Data> {
|
|
2272
|
-
id: string;
|
|
2273
|
-
modelId: string;
|
|
2274
|
-
status: string;
|
|
2275
|
-
createdAt: string;
|
|
2276
|
-
updatedAt: string;
|
|
2277
|
-
data: Data;
|
|
2278
|
-
}`;
|
|
2279
2263
|
function generateModelTypes(models, options) {
|
|
2280
2264
|
const command = options.command ?? "npx @cmssy/cli types";
|
|
2281
2265
|
const sorted = [...models].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
@@ -2434,11 +2418,72 @@ async function request(deps, endpoint, query, variables) {
|
|
|
2434
2418
|
if (!body.data) throw new CliError("the delivery API returned no data");
|
|
2435
2419
|
return body.data;
|
|
2436
2420
|
}
|
|
2421
|
+
function ancestors(from) {
|
|
2422
|
+
const chain = [];
|
|
2423
|
+
let dir = resolvePath(from);
|
|
2424
|
+
for (let steps = dir.split(/[\\/]/).length + 1; steps > 0; steps -= 1) {
|
|
2425
|
+
chain.push(dir);
|
|
2426
|
+
const parent = dirname4(dir);
|
|
2427
|
+
if (parent === dir) break;
|
|
2428
|
+
dir = parent;
|
|
2429
|
+
}
|
|
2430
|
+
return chain;
|
|
2431
|
+
}
|
|
2432
|
+
function exportedNames(declarations) {
|
|
2433
|
+
const source = declarations.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/^\s*\/\/.*$/gm, " ");
|
|
2434
|
+
const found = /* @__PURE__ */ new Set();
|
|
2435
|
+
for (const [, clause] of source.matchAll(/export\s+(?:type\s+)?\{([^}]*)\}/g)) {
|
|
2436
|
+
for (const entry of (clause ?? "").split(",")) {
|
|
2437
|
+
const parts = entry.trim().split(/\s+as\s+/);
|
|
2438
|
+
const name = (parts[1] ?? parts[0] ?? "").trim();
|
|
2439
|
+
if (name.length > 0) found.add(name);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
const declared = /export\s+(?:declare\s+)?(?:abstract\s+)?(?:type|interface|class|const|let|var|function|enum|namespace)\s+(\w+)/g;
|
|
2443
|
+
for (const [, name] of source.matchAll(declared)) {
|
|
2444
|
+
if (name !== void 0) found.add(name);
|
|
2445
|
+
}
|
|
2446
|
+
return found;
|
|
2447
|
+
}
|
|
2448
|
+
function coreShapeGap(cwd) {
|
|
2449
|
+
const names = preambleImports();
|
|
2450
|
+
if (names.length === 0) return null;
|
|
2451
|
+
let manifest = null;
|
|
2452
|
+
let manifestPath = "";
|
|
2453
|
+
for (const dir of ancestors(cwd)) {
|
|
2454
|
+
const candidate = resolvePath(dir, "node_modules/@cmssy/core/package.json");
|
|
2455
|
+
try {
|
|
2456
|
+
manifest = JSON.parse(readFileSync8(candidate, "utf8"));
|
|
2457
|
+
manifestPath = candidate;
|
|
2458
|
+
break;
|
|
2459
|
+
} catch {
|
|
2460
|
+
manifest = null;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
if (!manifest) return null;
|
|
2464
|
+
let declarations;
|
|
2465
|
+
try {
|
|
2466
|
+
declarations = readFileSync8(
|
|
2467
|
+
resolvePath(dirname4(manifestPath), manifest.types ?? "dist/index.d.ts"),
|
|
2468
|
+
"utf8"
|
|
2469
|
+
);
|
|
2470
|
+
} catch {
|
|
2471
|
+
return null;
|
|
2472
|
+
}
|
|
2473
|
+
const exported = exportedNames(declarations);
|
|
2474
|
+
const missing = names.filter((name) => !exported.has(name));
|
|
2475
|
+
return missing.length > 0 ? { version: manifest.version ?? "unknown", missing } : null;
|
|
2476
|
+
}
|
|
2437
2477
|
function describeDrift(previous, next) {
|
|
2438
2478
|
const declared = (source, pattern) => new Set([...source.matchAll(pattern)].map((match) => match[1] ?? ""));
|
|
2439
|
-
const body = (source) => source.split("/** Every model in the")[0] ?? source;
|
|
2440
2479
|
const models = /export interface (\w+)Data/g;
|
|
2441
2480
|
const fields = /^\s{2}(\w+)\??:/gm;
|
|
2481
|
+
const body = (source) => {
|
|
2482
|
+
const head = source.search(/export interface \w+Data\b/);
|
|
2483
|
+
if (head === -1) return "";
|
|
2484
|
+
const scoped = source.slice(head);
|
|
2485
|
+
return scoped.split("/** Every model in the")[0] ?? scoped;
|
|
2486
|
+
};
|
|
2442
2487
|
const lines = [];
|
|
2443
2488
|
const report = (label, before, after) => {
|
|
2444
2489
|
const added = [...after].filter((name) => !before.has(name));
|
|
@@ -2446,7 +2491,7 @@ function describeDrift(previous, next) {
|
|
|
2446
2491
|
if (added.length) lines.push(`+ ${label}: ${added.join(", ")}`);
|
|
2447
2492
|
if (removed.length) lines.push(`- ${label}: ${removed.join(", ")}`);
|
|
2448
2493
|
};
|
|
2449
|
-
report("models", declared(previous, models), declared(next, models));
|
|
2494
|
+
report("models", declared(body(previous), models), declared(body(next), models));
|
|
2450
2495
|
report("fields", declared(body(previous), fields), declared(body(next), fields));
|
|
2451
2496
|
return lines.length ? lines : ["the generated output differs"];
|
|
2452
2497
|
}
|
|
@@ -2578,6 +2623,15 @@ async function runTypes(options, deps) {
|
|
|
2578
2623
|
const inside = relative(deps.cwd, outPath);
|
|
2579
2624
|
const shown = inside && !inside.startsWith("..") ? inside : outPath;
|
|
2580
2625
|
const source = generateModelTypes(models, { workspace });
|
|
2626
|
+
const gap = coreShapeGap(deps.cwd);
|
|
2627
|
+
if (gap) {
|
|
2628
|
+
deps.log(
|
|
2629
|
+
`cmssy: the installed @cmssy/core ${gap.version} does not export ${gap.missing.join(", ")}`
|
|
2630
|
+
);
|
|
2631
|
+
deps.log(
|
|
2632
|
+
` ${shown} imports those shapes - upgrade @cmssy/core to ${cliVersion()} or newer`
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2581
2635
|
let previous = null;
|
|
2582
2636
|
try {
|
|
2583
2637
|
previous = readFileSync8(outPath, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "The cmssy CLI: `cmssy init` wires cmssy into an existing Next.js, Astro or React Router app; `cmssy link` connects it to a workspace and verifies the editor wiring.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"esbuild": "^0.27.7",
|
|
31
|
-
"@cmssy/core": "
|
|
31
|
+
"@cmssy/core": "17.0.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^20.0.0",
|