@metaobjectsdev/cli 0.23.2 → 0.24.1
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 +78 -0
- package/dist/src/commands/docs.d.ts.map +1 -1
- package/dist/src/commands/docs.js +157 -59
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/export.d.ts.map +1 -1
- package/dist/src/commands/export.js +28 -6
- package/dist/src/commands/export.js.map +1 -1
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +66 -22
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/init.d.ts +8 -0
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +142 -52
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +9 -2
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +399 -73
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/commands/prompt-snapshot.d.ts.map +1 -1
- package/dist/src/commands/prompt-snapshot.js +40 -18
- package/dist/src/commands/prompt-snapshot.js.map +1 -1
- package/dist/src/commands/upgrade.d.ts +2 -0
- package/dist/src/commands/upgrade.d.ts.map +1 -0
- package/dist/src/commands/upgrade.js +163 -0
- package/dist/src/commands/upgrade.js.map +1 -0
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +360 -48
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +55 -19
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/allow.d.ts.map +1 -1
- package/dist/src/lib/allow.js +4 -0
- package/dist/src/lib/allow.js.map +1 -1
- package/dist/src/lib/args.d.ts +16 -2
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +19 -3
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/codegen-drift.d.ts +8 -1
- package/dist/src/lib/codegen-drift.d.ts.map +1 -1
- package/dist/src/lib/codegen-drift.js +9 -1
- package/dist/src/lib/codegen-drift.js.map +1 -1
- package/dist/src/lib/detect-stack.d.ts +1 -1
- package/dist/src/lib/detect-stack.d.ts.map +1 -1
- package/dist/src/lib/detect-stack.js +22 -37
- package/dist/src/lib/detect-stack.js.map +1 -1
- package/dist/src/lib/load-metaobjects-config.d.ts +79 -1
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +123 -1
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/manifest-ignored-check.d.ts +12 -0
- package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -0
- package/dist/src/lib/manifest-ignored-check.js +74 -0
- package/dist/src/lib/manifest-ignored-check.js.map +1 -0
- package/dist/src/lib/migrate-scope.d.ts +49 -0
- package/dist/src/lib/migrate-scope.d.ts.map +1 -0
- package/dist/src/lib/migrate-scope.js +92 -0
- package/dist/src/lib/migrate-scope.js.map +1 -0
- package/dist/src/lib/output.d.ts +1 -1
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +9 -3
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/requirement-check.d.ts.map +1 -1
- package/dist/src/lib/requirement-check.js +10 -57
- package/dist/src/lib/requirement-check.js.map +1 -1
- package/package.json +11 -11
- package/src/commands/docs.ts +174 -61
- package/src/commands/export.ts +30 -6
- package/src/commands/gen.ts +68 -22
- package/src/commands/init.ts +161 -58
- package/src/commands/migrate.ts +423 -75
- package/src/commands/prompt-snapshot.ts +43 -18
- package/src/commands/upgrade.ts +179 -0
- package/src/commands/verify.ts +397 -48
- package/src/index.ts +55 -19
- package/src/lib/allow.ts +4 -0
- package/src/lib/args.ts +35 -4
- package/src/lib/codegen-drift.ts +9 -0
- package/src/lib/detect-stack.ts +20 -33
- package/src/lib/load-metaobjects-config.ts +129 -2
- package/src/lib/manifest-ignored-check.ts +75 -0
- package/src/lib/migrate-scope.ts +102 -0
- package/src/lib/output.ts +8 -4
- package/src/lib/requirement-check.ts +10 -58
- package/dist/src/lib/verified-by-scan.d.ts +0 -20
- package/dist/src/lib/verified-by-scan.d.ts.map +0 -1
- package/dist/src/lib/verified-by-scan.js +0 -344
- package/dist/src/lib/verified-by-scan.js.map +0 -1
- package/src/lib/verified-by-scan.ts +0 -375
|
@@ -14,8 +14,8 @@ import { parsePromptSnapshotArgs } from "../lib/args.js";
|
|
|
14
14
|
import { log } from "../lib/log.js";
|
|
15
15
|
import { FileProvider } from "../lib/file-provider.js";
|
|
16
16
|
import { snapshotPaths, unifiedDiff } from "../lib/snapshot.js";
|
|
17
|
-
import { loadMetaobjectsConfig } from "../lib/load-metaobjects-config.js";
|
|
18
|
-
import { loadMemory } from "@metaobjectsdev/sdk";
|
|
17
|
+
import { loadMemoryOptionsFrom, loadMetaobjectsConfig, resolveGenConfigDir } from "../lib/load-metaobjects-config.js";
|
|
18
|
+
import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
|
|
19
19
|
import { TYPE_TEMPLATE, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT } from "@metaobjectsdev/metadata";
|
|
20
20
|
import { render, ESCAPERS, type RenderFormat } from "@metaobjectsdev/render";
|
|
21
21
|
|
|
@@ -30,34 +30,59 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
|
|
|
30
30
|
return 2;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
33
|
+
// Where the metadata lives is `resolveCollection`'s decision, not this
|
|
34
|
+
// command's — `--check` is a drift GATE, so a project declaring `sources`
|
|
35
|
+
// elsewhere would otherwise gate against a stale `metaobjects/` (or report
|
|
36
|
+
// "no metaobjects/ found" for metadata it can see perfectly well). Discovery
|
|
37
|
+
// and load stay separate failure modes, the `meta gen` pattern: a broad catch
|
|
38
|
+
// around both reports a genuine ParseError as "no metadata found".
|
|
39
|
+
// `resolveCollection` raises ERR_COLLECTION_NOT_FOUND with its own message,
|
|
40
|
+
// replacing the hand-rolled ENOENT sniff that used to live here.
|
|
41
|
+
let collection;
|
|
42
|
+
try {
|
|
43
|
+
collection = await resolveCollection(cwd);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
log.error((err as Error).message);
|
|
46
|
+
return 2;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Everything project-relative below hangs off the DECLARING directory, not
|
|
50
|
+
// ambient cwd: `.metaobjects/snapshots/` is that config's own state, and the
|
|
51
|
+
// prompt text belongs to the same project as the metadata that references it.
|
|
52
|
+
// Identical to cwd for a run from the project root, which is the only
|
|
53
|
+
// invocation that worked before metadata sources were resolvable at all.
|
|
54
|
+
const projectRoot = collection.configDir;
|
|
55
|
+
|
|
56
|
+
// Best-effort load of metaobjects.config.ts to pick up consumer-supplied providers
|
|
57
|
+
// and shipped `libraries`. prompt-snapshot doesn't require codegen config; if it's absent
|
|
35
58
|
// or invalid, fall back to defaults — the loader still works for any
|
|
36
59
|
// metadata that only uses core+forge subtypes.
|
|
37
|
-
let
|
|
60
|
+
let configLoadOptions: ReturnType<typeof loadMemoryOptionsFrom> = {};
|
|
38
61
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
62
|
+
// `metaobjects.config.ts` gets its own nearest-ancestor walk, not `projectRoot`:
|
|
63
|
+
// it answers a different question from `.metaobjects/config.json` (design §4.6)
|
|
64
|
+
// and in a Maven- or pip-rooted monorepo the two legitimately sit in different
|
|
65
|
+
// directories, so reading it from the collection's would silently drop this
|
|
66
|
+
// package's providers (#326). Same path whenever the two files sit together.
|
|
67
|
+
configLoadOptions = loadMemoryOptionsFrom(
|
|
68
|
+
await loadMetaobjectsConfig(resolveGenConfigDir(cwd, projectRoot)),
|
|
69
|
+
);
|
|
41
70
|
} catch {
|
|
42
|
-
|
|
71
|
+
configLoadOptions = {};
|
|
43
72
|
}
|
|
44
73
|
|
|
45
74
|
let root;
|
|
46
75
|
try {
|
|
47
|
-
root = await loadMemory(
|
|
48
|
-
|
|
76
|
+
root = await loadMemory(collection.configDir, {
|
|
77
|
+
files: collection.files,
|
|
78
|
+
...configLoadOptions,
|
|
49
79
|
});
|
|
50
80
|
} catch (err) {
|
|
51
|
-
|
|
52
|
-
if (msg.includes("ENOENT") || msg.includes("no such") || msg.includes("cannot read")) {
|
|
53
|
-
log.error(`no metaobjects/ found in ${cwd}; run 'meta init' to scaffold`);
|
|
54
|
-
return 2;
|
|
55
|
-
}
|
|
56
|
-
log.error(`failed to load metadata: ${msg}`);
|
|
81
|
+
log.error(`failed to load metadata: ${(err as Error).message}`);
|
|
57
82
|
return 1;
|
|
58
83
|
}
|
|
59
84
|
|
|
60
|
-
const promptsDir = join(
|
|
85
|
+
const promptsDir = join(projectRoot, flags.prompts ?? DEFAULT_PROMPTS_DIR);
|
|
61
86
|
const provider = new FileProvider(promptsDir);
|
|
62
87
|
|
|
63
88
|
// ADR-0039: effective children — resolve rather than rely on root being unextended.
|
|
@@ -79,7 +104,7 @@ export async function promptSnapshotCommand(args: string[], cwd: string): Promis
|
|
|
79
104
|
// Absent/typeless required attrs are a loader-schema concern, not ours.
|
|
80
105
|
if (typeof textRef !== "string") continue;
|
|
81
106
|
|
|
82
|
-
const { dir, payloadPath, snapPath } = snapshotPaths(
|
|
107
|
+
const { dir, payloadPath, snapPath } = snapshotPaths(projectRoot, tmpl.name);
|
|
83
108
|
if (!existsSync(payloadPath)) {
|
|
84
109
|
log.info(`[${tmpl.name}] skipped — no payload at ${payloadPath}`);
|
|
85
110
|
skipped++;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// server/typescript/packages/cli/src/commands/upgrade.ts
|
|
2
|
+
//
|
|
3
|
+
// `meta upgrade` — rewrite retired vocabulary in this project's metadata.
|
|
4
|
+
//
|
|
5
|
+
// DELIBERATELY NOT `meta migrate`. That command owns DATABASE SCHEMA (ADR-0015) and an
|
|
6
|
+
// adopter reading `migrate` expects DDL. Overloading it with a metadata rewrite would make
|
|
7
|
+
// the most destructive command in the toolchain ambiguous about what it touches.
|
|
8
|
+
//
|
|
9
|
+
// IT DOES NOT LOAD THE METADATA, and cannot. Once vocabulary is deregistered, metadata
|
|
10
|
+
// carrying it fails the load — which is exactly the state this command exists to repair. So
|
|
11
|
+
// it resolves the file SET through `resolveCollection` (the single authority on where
|
|
12
|
+
// metadata lives) and hands each file's RAW TEXT to the rewriter. See
|
|
13
|
+
// `metadata/src/vocabulary-rewrite.ts` for why that is the only workable shape.
|
|
14
|
+
//
|
|
15
|
+
// DRY-RUN BY DEFAULT, per this repo's convention for anything that edits committed files.
|
|
16
|
+
// `--apply` writes. Refusals exit NON-ZERO even when every mechanical change succeeded, so
|
|
17
|
+
// CI cannot mistake a partial upgrade for a finished one.
|
|
18
|
+
|
|
19
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
20
|
+
import { extname, relative } from "node:path";
|
|
21
|
+
import { resolveCollection } from "@metaobjectsdev/sdk";
|
|
22
|
+
import { rewriteDocument } from "@metaobjectsdev/metadata";
|
|
23
|
+
import { log } from "../lib/log.js";
|
|
24
|
+
|
|
25
|
+
/** YAML authoring (ADR-0006). Rewritten by the `yaml`-backed arm, loaded on demand below. */
|
|
26
|
+
const YAML_EXTENSIONS = new Set([".yaml", ".yml"]);
|
|
27
|
+
|
|
28
|
+
interface UpgradeFlags {
|
|
29
|
+
apply: boolean;
|
|
30
|
+
maxVersion?: string;
|
|
31
|
+
projectRoot?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseArgs(argv: string[]): UpgradeFlags {
|
|
35
|
+
const flags: UpgradeFlags = { apply: false };
|
|
36
|
+
for (let i = 0; i < argv.length; i++) {
|
|
37
|
+
const a = argv[i] as string;
|
|
38
|
+
if (a === "--apply") flags.apply = true;
|
|
39
|
+
else if (a === "--to") {
|
|
40
|
+
const v = argv[++i];
|
|
41
|
+
if (v === undefined) throw new Error("--to needs a version");
|
|
42
|
+
flags.maxVersion = v;
|
|
43
|
+
} else if (a.startsWith("--to=")) flags.maxVersion = a.slice("--to=".length);
|
|
44
|
+
else if (a === "--help" || a === "-h") throw new Error("__help__");
|
|
45
|
+
else if (!a.startsWith("-")) flags.projectRoot = a;
|
|
46
|
+
else throw new Error(`unknown option: ${a}`);
|
|
47
|
+
}
|
|
48
|
+
return flags;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function upgradeCommand(args: string[], cwd: string): Promise<number> {
|
|
52
|
+
let flags: UpgradeFlags;
|
|
53
|
+
try {
|
|
54
|
+
flags = parseArgs(args);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if ((err as Error).message === "__help__") {
|
|
57
|
+
log.info(
|
|
58
|
+
"meta upgrade [<project>] [--to <version>] [--apply]\n\n" +
|
|
59
|
+
" Rewrites retired metadata vocabulary in JSON and YAML metadata alike.\n" +
|
|
60
|
+
" Previews by default; --apply writes.\n" +
|
|
61
|
+
" Retirements needing a human decision are REFUSED and listed with their guide.\n\n" +
|
|
62
|
+
" Exit: 0 clean · 1 refusals remain · 2 bad usage · 3 some files could not be read.",
|
|
63
|
+
);
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
log.error((err as Error).message);
|
|
67
|
+
return 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const projectRoot = flags.projectRoot ?? cwd;
|
|
71
|
+
|
|
72
|
+
let files: readonly string[];
|
|
73
|
+
try {
|
|
74
|
+
files = (await resolveCollection(projectRoot)).files;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
log.error((err as Error).message);
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let totalChanges = 0;
|
|
81
|
+
let totalRefusals = 0;
|
|
82
|
+
let filesChanged = 0;
|
|
83
|
+
let checked = 0;
|
|
84
|
+
// Files we could not READ AT ALL. Distinct from "checked and clean" in every report and in
|
|
85
|
+
// the exit code — conflating them is the whole of #339.
|
|
86
|
+
const notChecked: string[] = [];
|
|
87
|
+
|
|
88
|
+
// The YAML arm carries the `yaml` package, so it lives behind its own subpath and is
|
|
89
|
+
// loaded only when the estate actually contains YAML. Importing it eagerly would pull a
|
|
90
|
+
// Node-only dependency into every `meta` invocation.
|
|
91
|
+
const hasYaml = files.some((f) => YAML_EXTENSIONS.has(extname(f).toLowerCase()));
|
|
92
|
+
const rewriteYaml = hasYaml
|
|
93
|
+
? (await import("@metaobjectsdev/metadata/vocabulary-rewrite-yaml")).rewriteYamlDocument
|
|
94
|
+
: undefined;
|
|
95
|
+
|
|
96
|
+
const opts = flags.maxVersion !== undefined ? { maxVersion: flags.maxVersion } : {};
|
|
97
|
+
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
const rel = relative(projectRoot, file);
|
|
100
|
+
const before = await readFile(file, "utf8");
|
|
101
|
+
|
|
102
|
+
let r;
|
|
103
|
+
if (YAML_EXTENSIONS.has(extname(file).toLowerCase())) {
|
|
104
|
+
const y = rewriteYaml?.(before, opts);
|
|
105
|
+
// A document that does not parse was not examined, and must never be counted as clean.
|
|
106
|
+
if (y === undefined || y.unparseable) {
|
|
107
|
+
notChecked.push(rel);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
r = y;
|
|
111
|
+
} else {
|
|
112
|
+
r = rewriteDocument(before, opts);
|
|
113
|
+
}
|
|
114
|
+
checked++;
|
|
115
|
+
if (r.changes.length === 0 && r.refusals.length === 0) continue;
|
|
116
|
+
|
|
117
|
+
log.info(`\n${rel}`);
|
|
118
|
+
for (const c of r.changes) log.info(` ${c.line}: @${c.from} → ${c.to}`);
|
|
119
|
+
for (const f of r.refusals) {
|
|
120
|
+
log.warn(
|
|
121
|
+
` ${f.line}: ${f.subject}${f.value !== undefined ? `: ${f.value}` : ""} — needs a decision. ` +
|
|
122
|
+
`Retired in ${f.since}. ${f.migration !== undefined ? `See ${f.migration}` : f.why}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
totalChanges += r.changes.length;
|
|
127
|
+
totalRefusals += r.refusals.length;
|
|
128
|
+
if (r.changes.length > 0) {
|
|
129
|
+
filesChanged++;
|
|
130
|
+
if (flags.apply) await writeFile(file, r.text, "utf8");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
log.info("");
|
|
135
|
+
if (notChecked.length > 0) {
|
|
136
|
+
log.warn(
|
|
137
|
+
`${notChecked.length} file(s) could not be parsed and were NOT checked — fix these ` +
|
|
138
|
+
`first, then re-run:\n ${notChecked.join("\n ")}`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Every conclusion states how many files it is a conclusion ABOUT. "No retired vocabulary
|
|
143
|
+
// found" read on its own says the estate is clean, and it is the last line, so it is the
|
|
144
|
+
// one that sticks — on the estate that reported this, it was the opposite of the truth.
|
|
145
|
+
const scope = `${checked} file(s) checked${notChecked.length > 0 ? `, ${notChecked.length} NOT checked` : ""}`;
|
|
146
|
+
|
|
147
|
+
if (totalChanges === 0 && totalRefusals === 0) {
|
|
148
|
+
log.info(`meta upgrade — no retired vocabulary found (${scope}).`);
|
|
149
|
+
} else if (totalChanges === 0) {
|
|
150
|
+
// Refusals only. Reporting "rewrote 0 declarations", or advertising `--apply`, both
|
|
151
|
+
// promise an action guaranteed to change nothing and bury the fact that the remaining
|
|
152
|
+
// work is entirely human.
|
|
153
|
+
log.info("meta upgrade — nothing here can be rewritten automatically.");
|
|
154
|
+
} else if (flags.apply) {
|
|
155
|
+
log.info(`meta upgrade — rewrote ${totalChanges} declaration(s) across ${filesChanged} file(s).`);
|
|
156
|
+
} else {
|
|
157
|
+
log.info(
|
|
158
|
+
`meta upgrade — ${totalChanges} declaration(s) in ${filesChanged} file(s) can be rewritten. ` +
|
|
159
|
+
`Re-run with --apply to write.`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Non-zero while ANY refusal stands, applied or not. A partial upgrade that exited 0 would
|
|
164
|
+
// let CI record the migration as done while metadata still fails to load.
|
|
165
|
+
if (totalRefusals > 0) {
|
|
166
|
+
log.error(
|
|
167
|
+
`${totalRefusals} declaration(s) need a human decision and were left untouched — ` +
|
|
168
|
+
`see the guides listed above.`,
|
|
169
|
+
);
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// "I could not look" gets its OWN code. It used to share exit 1 with "work remains", so a
|
|
174
|
+
// script could not tell an estate needing decisions from one the tool never opened — and
|
|
175
|
+
// an adopter whose whole estate was skipped got a failure exit next to a message saying
|
|
176
|
+
// nothing was found.
|
|
177
|
+
if (notChecked.length > 0) return 3;
|
|
178
|
+
return 0;
|
|
179
|
+
}
|