@metaobjectsdev/cli 1.0.9-rc.4 → 1.0.9-rc.6
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 +15 -0
- package/dist/src/commands/eject.d.ts +17 -0
- package/dist/src/commands/eject.d.ts.map +1 -1
- package/dist/src/commands/eject.js +87 -2
- package/dist/src/commands/eject.js.map +1 -1
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +18 -5
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/generator-help.d.ts +21 -0
- package/dist/src/commands/generator-help.d.ts.map +1 -0
- package/dist/src/commands/generator-help.js +43 -0
- package/dist/src/commands/generator-help.js.map +1 -0
- package/dist/src/commands/generator.d.ts +36 -0
- package/dist/src/commands/generator.d.ts.map +1 -0
- package/dist/src/commands/generator.js +333 -0
- package/dist/src/commands/generator.js.map +1 -0
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +10 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +5 -3
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/advisory.d.ts +14 -0
- package/dist/src/lib/advisory.d.ts.map +1 -1
- package/dist/src/lib/advisory.js +64 -5
- package/dist/src/lib/advisory.js.map +1 -1
- package/dist/src/lib/catalog-listing.d.ts +14 -4
- package/dist/src/lib/catalog-listing.d.ts.map +1 -1
- package/dist/src/lib/catalog-listing.js +56 -9
- package/dist/src/lib/catalog-listing.js.map +1 -1
- package/dist/src/lib/install-set.d.ts +12 -2
- package/dist/src/lib/install-set.d.ts.map +1 -1
- package/dist/src/lib/install-set.js +14 -2
- package/dist/src/lib/install-set.js.map +1 -1
- package/dist/src/lib/output.d.ts +1 -0
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +2 -0
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/runtime-eject.d.ts +80 -0
- package/dist/src/lib/runtime-eject.d.ts.map +1 -0
- package/dist/src/lib/runtime-eject.js +212 -0
- package/dist/src/lib/runtime-eject.js.map +1 -0
- package/dist/src/lib/unknown-attr-warnings.d.ts +18 -0
- package/dist/src/lib/unknown-attr-warnings.d.ts.map +1 -0
- package/dist/src/lib/unknown-attr-warnings.js +43 -0
- package/dist/src/lib/unknown-attr-warnings.js.map +1 -0
- package/package.json +10 -10
- package/src/commands/eject.ts +103 -3
- package/src/commands/gen.ts +18 -5
- package/src/commands/generator-help.ts +47 -0
- package/src/commands/generator.ts +390 -0
- package/src/commands/init.ts +10 -1
- package/src/commands/verify.ts +5 -4
- package/src/index.ts +8 -0
- package/src/lib/advisory.ts +71 -5
- package/src/lib/catalog-listing.ts +68 -7
- package/src/lib/install-set.ts +19 -2
- package/src/lib/output.ts +3 -0
- package/src/lib/runtime-eject.ts +263 -0
- package/src/lib/unknown-attr-warnings.ts +48 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// ADR-0034 Amendment 3 (2026-09-24 ruling) — ejecting a helper hands over ALL of its code.
|
|
2
|
+
//
|
|
3
|
+
// `meta eject routes` used to copy a generator whose OUTPUT still imported the HTTP adapter
|
|
4
|
+
// from `@metaobjectsdev/runtime-ts` — the mount helpers, filter parser, error envelopes and
|
|
5
|
+
// pagination, which is where most route defects have lived. Owning the generator while the
|
|
6
|
+
// code it emits calls into a package is half of owning it: a defect in the adapter still
|
|
7
|
+
// waits on an upstream release.
|
|
8
|
+
//
|
|
9
|
+
// So eject also copies the adapter SOURCE the output depends on — the transitive closure of
|
|
10
|
+
// relative imports from the entry module each generator's output names — into
|
|
11
|
+
// `codegen/runtime/`, laid out exactly as the package's own `src/` so every relative import
|
|
12
|
+
// in it resolves unchanged. Package imports are left as they are: what the closure reaches
|
|
13
|
+
// outside the package is `@metaobjectsdev/metadata` (core: the filter-op vocabulary and the
|
|
14
|
+
// canonical serializer) and third-party libraries (drizzle-orm, fastify, hono, zod, qs).
|
|
15
|
+
//
|
|
16
|
+
// The copies are VERBATIM. That is what makes "is my copy still the package's?" a byte
|
|
17
|
+
// comparison and "what did upstream change?" a plain `diff -r` against the installed
|
|
18
|
+
// package's `src/`, with no header to strip first.
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
21
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
22
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
23
|
+
import { HTTP_RUNTIME_MODULES, HTTP_RUNTIME_PACKAGE, OWNED_RUNTIME_DIR, } from "@metaobjectsdev/codegen-ts";
|
|
24
|
+
import { compareOwnedCopy } from "./owned-copy.js";
|
|
25
|
+
import { cliVersion } from "./version.js";
|
|
26
|
+
/**
|
|
27
|
+
* The ejectable generators whose OUTPUT imports the adapter tier, and the adapter modules
|
|
28
|
+
* it imports. `entity` is here for its allowlist TYPES only, which is one file.
|
|
29
|
+
*/
|
|
30
|
+
export const RUNTIME_ENTRIES = {
|
|
31
|
+
routes: ["drizzle-fastify"],
|
|
32
|
+
"routes-hono": ["hono"],
|
|
33
|
+
entity: ["allowlists"],
|
|
34
|
+
};
|
|
35
|
+
/** True iff ejecting `name` also copies adapter source. */
|
|
36
|
+
export function ejectsRuntime(name) {
|
|
37
|
+
return Object.hasOwn(RUNTIME_ENTRIES, name);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The installed `@metaobjectsdev/runtime-ts`: its `src/` and its manifest.
|
|
41
|
+
*
|
|
42
|
+
* Resolves the package ENTRY and walks up to the manifest that names it, never
|
|
43
|
+
* `"<pkg>/package.json"`: the exports map does not export that subpath, and under real Node
|
|
44
|
+
* asking for it throws ERR_PACKAGE_PATH_NOT_EXPORTED (see `install-set.ts`). `src/` ships
|
|
45
|
+
* in the published package (`files: ["dist", "src", ...]`).
|
|
46
|
+
*/
|
|
47
|
+
export function resolveRuntimePackage() {
|
|
48
|
+
const req = createRequire(import.meta.url);
|
|
49
|
+
let dir = dirname(req.resolve(HTTP_RUNTIME_PACKAGE));
|
|
50
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
51
|
+
const candidate = join(dir, "package.json");
|
|
52
|
+
if (existsSync(candidate)) {
|
|
53
|
+
const manifest = JSON.parse(readFileSync(candidate, "utf8"));
|
|
54
|
+
if (manifest.name === HTTP_RUNTIME_PACKAGE) {
|
|
55
|
+
const srcRoot = join(dir, "src");
|
|
56
|
+
if (!existsSync(srcRoot)) {
|
|
57
|
+
throw new Error(`${HTTP_RUNTIME_PACKAGE} at ${dir} ships no src/ — cannot copy the adapter source.`);
|
|
58
|
+
}
|
|
59
|
+
return { srcRoot, manifest };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const up = dirname(dir);
|
|
63
|
+
if (up === dir)
|
|
64
|
+
break;
|
|
65
|
+
dir = up;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`could not locate the installed ${HTTP_RUNTIME_PACKAGE} package.`);
|
|
68
|
+
}
|
|
69
|
+
/** Relative (`./`, `../`) and bare module specifiers a source file imports or re-exports. */
|
|
70
|
+
function specifiersOf(source) {
|
|
71
|
+
const out = [];
|
|
72
|
+
// `import … from "x"` and the re-export forms (`export { … } from`, `export * from`) only —
|
|
73
|
+
// a bare `export function f() { return Array.from("…") }` must not read as an import.
|
|
74
|
+
const STATEMENT = /(?:^|\n)\s*(?:import\s+[^;"']*?\s+from|export\s+(?:type\s+)?(?:\*(?:\s+as\s+\w+)?|\{[^}]*\})\s*from)\s*["']([^"']+)["']/g;
|
|
75
|
+
for (const m of source.matchAll(STATEMENT)) {
|
|
76
|
+
if (m[1] !== undefined)
|
|
77
|
+
out.push(m[1]);
|
|
78
|
+
}
|
|
79
|
+
for (const m of source.matchAll(/(?:^|\n)\s*import\s*["']([^"']+)["']/g)) {
|
|
80
|
+
if (m[1] !== undefined)
|
|
81
|
+
out.push(m[1]);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/** `@scope/name/sub` → `@scope/name`; `name/sub` → `name`. */
|
|
86
|
+
function packageNameOf(spec) {
|
|
87
|
+
const parts = spec.split("/");
|
|
88
|
+
return spec.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
89
|
+
}
|
|
90
|
+
function toPosix(p) {
|
|
91
|
+
return p.split(sep).join("/");
|
|
92
|
+
}
|
|
93
|
+
/** Every file the given adapter modules reach through relative imports, and what else they import. */
|
|
94
|
+
export function runtimeClosure(srcRoot, modules) {
|
|
95
|
+
const files = new Set();
|
|
96
|
+
const packages = new Set();
|
|
97
|
+
const queue = modules.map((m) => `${HTTP_RUNTIME_MODULES[m].file}.ts`);
|
|
98
|
+
while (queue.length > 0) {
|
|
99
|
+
const rel = queue.shift();
|
|
100
|
+
if (files.has(rel))
|
|
101
|
+
continue;
|
|
102
|
+
const abs = join(srcRoot, rel);
|
|
103
|
+
if (!existsSync(abs)) {
|
|
104
|
+
throw new Error(`${HTTP_RUNTIME_PACKAGE}: ${rel} is imported but not shipped in src/.`);
|
|
105
|
+
}
|
|
106
|
+
files.add(rel);
|
|
107
|
+
for (const spec of specifiersOf(readFileSync(abs, "utf8"))) {
|
|
108
|
+
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
109
|
+
const target = toPosix(join(dirname(rel), spec)).replace(/\.js$/, ".ts");
|
|
110
|
+
queue.push(target.endsWith(".ts") ? target : `${target}.ts`);
|
|
111
|
+
}
|
|
112
|
+
else if (!spec.startsWith("node:")) {
|
|
113
|
+
packages.add(packageNameOf(spec));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { files: [...files].sort(), packages: [...packages].sort() };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Copy the adapter source `names` emit imports of into `<cwd>/codegen/runtime/`.
|
|
121
|
+
* Never overwrites a file without `force`, the same rule the generator copy keeps.
|
|
122
|
+
*/
|
|
123
|
+
export async function ejectRuntime(cwd, names, force) {
|
|
124
|
+
const modules = [...new Set(names.flatMap((n) => RUNTIME_ENTRIES[n] ?? []))];
|
|
125
|
+
if (modules.length === 0)
|
|
126
|
+
return { files: [], packages: [] };
|
|
127
|
+
const { srcRoot } = resolveRuntimePackage();
|
|
128
|
+
const closure = runtimeClosure(srcRoot, modules);
|
|
129
|
+
const files = [];
|
|
130
|
+
for (const rel of closure.files) {
|
|
131
|
+
const source = await readFile(join(srcRoot, rel), "utf8");
|
|
132
|
+
const path = `${OWNED_RUNTIME_DIR}/${rel}`;
|
|
133
|
+
const abs = join(cwd, path);
|
|
134
|
+
const existing = existsSync(abs) ? await readFile(abs, "utf8") : undefined;
|
|
135
|
+
const comparison = existing === undefined ? undefined : await compareOwnedCopy(existing, source);
|
|
136
|
+
if (existing !== undefined && !force) {
|
|
137
|
+
files.push({ path, status: "preserved", comparison });
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
141
|
+
await writeFile(abs, source, "utf8");
|
|
142
|
+
files.push({ path, status: existing === undefined ? "created" : "replaced", comparison });
|
|
143
|
+
}
|
|
144
|
+
return { files, packages: closure.packages };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The install lines the copied source needs, with ranges read from the runtime package's
|
|
148
|
+
* own manifest (never restated here). A `@metaobjectsdev/*` package moves with the CLI.
|
|
149
|
+
* A package that ships no types of its own gets its `@types/*` sibling as a dev dependency
|
|
150
|
+
* when the runtime package itself develops against one.
|
|
151
|
+
*/
|
|
152
|
+
export function runtimeInstall(packages) {
|
|
153
|
+
const { manifest } = resolveRuntimePackage();
|
|
154
|
+
const known = { ...manifest.peerDependencies, ...manifest.dependencies };
|
|
155
|
+
const runtime = new Map();
|
|
156
|
+
const dev = new Map();
|
|
157
|
+
for (const p of packages) {
|
|
158
|
+
runtime.set(p, p.startsWith("@metaobjectsdev/") ? `^${cliVersion()}` : known[p]);
|
|
159
|
+
const typesPkg = `@types/${p.replace(/^@/, "").replace("/", "__")}`;
|
|
160
|
+
const typesRange = manifest.devDependencies?.[typesPkg];
|
|
161
|
+
if (typesRange !== undefined)
|
|
162
|
+
dev.set(typesPkg, typesRange);
|
|
163
|
+
}
|
|
164
|
+
return { runtime, dev };
|
|
165
|
+
}
|
|
166
|
+
function listTs(dir) {
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const entry of readdirSync(dir)) {
|
|
169
|
+
const abs = join(dir, entry);
|
|
170
|
+
if (statSync(abs).isDirectory())
|
|
171
|
+
out.push(...listTs(abs));
|
|
172
|
+
else if (entry.endsWith(".ts"))
|
|
173
|
+
out.push(abs);
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Every file under `<cwd>/codegen/runtime/`, compared to the installed package's source.
|
|
179
|
+
* Empty when the project owns no adapter copy. `not-in-package` is a file the package does
|
|
180
|
+
* not ship — your own addition, or one upstream deleted or renamed.
|
|
181
|
+
*/
|
|
182
|
+
export async function runtimeCopyStatus(cwd) {
|
|
183
|
+
const root = join(cwd, OWNED_RUNTIME_DIR);
|
|
184
|
+
if (!existsSync(root))
|
|
185
|
+
return [];
|
|
186
|
+
let srcRoot;
|
|
187
|
+
try {
|
|
188
|
+
srcRoot = resolveRuntimePackage().srcRoot;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
const rows = [];
|
|
194
|
+
for (const abs of listTs(root).sort()) {
|
|
195
|
+
const rel = toPosix(relative(root, abs));
|
|
196
|
+
const path = `${OWNED_RUNTIME_DIR}/${rel}`;
|
|
197
|
+
const upstream = join(srcRoot, rel);
|
|
198
|
+
if (!existsSync(upstream)) {
|
|
199
|
+
rows.push({ path, verdict: "not-in-package", localOnly: 0, referenceOnly: 0 });
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const cmp = await compareOwnedCopy(readFileSync(abs, "utf8"), readFileSync(upstream, "utf8"));
|
|
203
|
+
rows.push({ path, verdict: cmp.verdict, localOnly: cmp.localOnly, referenceOnly: cmp.referenceOnly });
|
|
204
|
+
}
|
|
205
|
+
return rows;
|
|
206
|
+
}
|
|
207
|
+
/** The command that shows exactly how one owned file differs from the installed package's. */
|
|
208
|
+
export function runtimeDiffCommand(path) {
|
|
209
|
+
const rel = path.startsWith(`${OWNED_RUNTIME_DIR}/`) ? path.slice(OWNED_RUNTIME_DIR.length + 1) : path;
|
|
210
|
+
return `diff -u node_modules/${HTTP_RUNTIME_PACKAGE}/src/${rel} ${OWNED_RUNTIME_DIR}/${rel}`;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=runtime-eject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-eject.js","sourceRoot":"","sources":["../../../src/lib/runtime-eject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type LoadMemoryOptions } from "@metaobjectsdev/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The trailing advice on every unknown-attribute warning `meta gen` prints. `gen` has no
|
|
4
|
+
* `--strict` flag of its own — `meta verify` is the strict door (ADR-0023), so that is what
|
|
5
|
+
* the warning names.
|
|
6
|
+
*/
|
|
7
|
+
export declare const UNKNOWN_ATTR_GEN_ADVICE: string;
|
|
8
|
+
/**
|
|
9
|
+
* `meta gen`'s unknown-attribute warnings: one line per ADR-0023 finding a STRICT load of
|
|
10
|
+
* the same collection raises, naming the attribute, the node and the file. Empty when the
|
|
11
|
+
* model is clean; the caller prints {@link UNKNOWN_ATTR_GEN_ADVICE} once after them.
|
|
12
|
+
*
|
|
13
|
+
* `gen` itself keeps loading leniently and keeps its exit code; this only ends the silence
|
|
14
|
+
* between it and `meta verify`, which fails on the very same file. Files are printed
|
|
15
|
+
* relative to `projectRoot` when the loader recorded an absolute path.
|
|
16
|
+
*/
|
|
17
|
+
export declare function unknownAttrWarnings(configDir: string, options: LoadMemoryOptions, projectRoot: string): Promise<string[]>;
|
|
18
|
+
//# sourceMappingURL=unknown-attr-warnings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unknown-attr-warnings.d.ts","sourceRoot":"","sources":["../../../src/lib/unknown-attr-warnings.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEvF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QAGG,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,iBAAiB,EAC1B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { relative, isAbsolute } from "node:path";
|
|
2
|
+
import { unknownAttributeFindings } from "@metaobjectsdev/sdk";
|
|
3
|
+
/**
|
|
4
|
+
* The trailing advice on every unknown-attribute warning `meta gen` prints. `gen` has no
|
|
5
|
+
* `--strict` flag of its own — `meta verify` is the strict door (ADR-0023), so that is what
|
|
6
|
+
* the warning names.
|
|
7
|
+
*/
|
|
8
|
+
export const UNKNOWN_ATTR_GEN_ADVICE = "meta gen loads leniently and generated anyway, but `meta verify` rejects this metadata " +
|
|
9
|
+
"(ADR-0023): fix or remove the attribute — a typo'd one (`isAbstrakt`, `@requird`) " +
|
|
10
|
+
"silently changes what is generated.";
|
|
11
|
+
/**
|
|
12
|
+
* `meta gen`'s unknown-attribute warnings: one line per ADR-0023 finding a STRICT load of
|
|
13
|
+
* the same collection raises, naming the attribute, the node and the file. Empty when the
|
|
14
|
+
* model is clean; the caller prints {@link UNKNOWN_ATTR_GEN_ADVICE} once after them.
|
|
15
|
+
*
|
|
16
|
+
* `gen` itself keeps loading leniently and keeps its exit code; this only ends the silence
|
|
17
|
+
* between it and `meta verify`, which fails on the very same file. Files are printed
|
|
18
|
+
* relative to `projectRoot` when the loader recorded an absolute path.
|
|
19
|
+
*/
|
|
20
|
+
export async function unknownAttrWarnings(configDir, options, projectRoot) {
|
|
21
|
+
let findings;
|
|
22
|
+
try {
|
|
23
|
+
findings = await unknownAttributeFindings(configDir, options);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// The lenient load already succeeded; a strict re-load that cannot even complete has
|
|
27
|
+
// nothing more precise to say, and must never turn an advisory into a failure.
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return findings.map((f) => {
|
|
31
|
+
const source = f.source;
|
|
32
|
+
const files = (source.files ?? [])
|
|
33
|
+
.filter((p) => p.length > 0)
|
|
34
|
+
.map((p) => (isAbsolute(p) ? relative(projectRoot, p) : p));
|
|
35
|
+
const where = [
|
|
36
|
+
files.length > 0 ? files.join(", ") : undefined,
|
|
37
|
+
source.jsonPath !== undefined && source.jsonPath.length > 0 ? `at ${source.jsonPath}` : undefined,
|
|
38
|
+
].filter((w) => w !== undefined).join(" ");
|
|
39
|
+
const head = `${f.code}: ${f.message}`;
|
|
40
|
+
return where.length > 0 ? `${head}\n in ${where}` : head;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=unknown-attr-warnings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unknown-attr-warnings.js","sourceRoot":"","sources":["../../../src/lib/unknown-attr-warnings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAA0B,MAAM,qBAAqB,CAAC;AAEvF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAClC,yFAAyF;IACzF,oFAAoF;IACpF,qCAAqC,CAAC;AAExC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,SAAiB,EACjB,OAA0B,EAC1B,WAAmB;IAEnB,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,wBAAwB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,+EAA+E;QAC/E,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,CAAC,CAAC,MAA0D,CAAC;QAC5E,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;aAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG;YACZ,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YAC/C,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;SAClG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/cli",
|
|
3
|
-
"version": "1.0.9-rc.
|
|
3
|
+
"version": "1.0.9-rc.6",
|
|
4
4
|
"description": "CLI for MetaObjects: scaffold, codegen, migrate, and drift-detection commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -51,15 +51,15 @@
|
|
|
51
51
|
"@biomejs/js-api": "^0.7.0",
|
|
52
52
|
"@biomejs/wasm-nodejs": "^1.9.4",
|
|
53
53
|
"@libsql/kysely-libsql": "^0.4.0",
|
|
54
|
-
"@metaobjectsdev/codegen-ts": "1.0.9-rc.
|
|
55
|
-
"@metaobjectsdev/codegen-ts-react": "1.0.9-rc.
|
|
56
|
-
"@metaobjectsdev/codegen-ts-tanstack": "1.0.9-rc.
|
|
57
|
-
"@metaobjectsdev/docs-site": "1.0.9-rc.
|
|
58
|
-
"@metaobjectsdev/metadata": "1.0.9-rc.
|
|
59
|
-
"@metaobjectsdev/migrate-ts": "1.0.9-rc.
|
|
60
|
-
"@metaobjectsdev/render": "1.0.9-rc.
|
|
61
|
-
"@metaobjectsdev/runtime-ts": "1.0.9-rc.
|
|
62
|
-
"@metaobjectsdev/sdk": "1.0.9-rc.
|
|
54
|
+
"@metaobjectsdev/codegen-ts": "1.0.9-rc.6",
|
|
55
|
+
"@metaobjectsdev/codegen-ts-react": "1.0.9-rc.6",
|
|
56
|
+
"@metaobjectsdev/codegen-ts-tanstack": "1.0.9-rc.6",
|
|
57
|
+
"@metaobjectsdev/docs-site": "1.0.9-rc.6",
|
|
58
|
+
"@metaobjectsdev/metadata": "1.0.9-rc.6",
|
|
59
|
+
"@metaobjectsdev/migrate-ts": "1.0.9-rc.6",
|
|
60
|
+
"@metaobjectsdev/render": "1.0.9-rc.6",
|
|
61
|
+
"@metaobjectsdev/runtime-ts": "1.0.9-rc.6",
|
|
62
|
+
"@metaobjectsdev/sdk": "1.0.9-rc.6",
|
|
63
63
|
"@toon-format/toon": "^2.3.0",
|
|
64
64
|
"jiti": "^2.4.0"
|
|
65
65
|
},
|
package/src/commands/eject.ts
CHANGED
|
@@ -25,7 +25,12 @@ import { log } from "../lib/log.js";
|
|
|
25
25
|
import { declaredDependencyNames, readPackageManifest } from "../lib/package-manifest.js";
|
|
26
26
|
import { compareOwnedCopy, type OwnedComparison } from "../lib/owned-copy.js";
|
|
27
27
|
import { composeCatalog, packageOf } from "../lib/catalog.js";
|
|
28
|
-
import { installSetFor, type InstallSet } from "../lib/install-set.js";
|
|
28
|
+
import { installSetFor, type InstallSet, type OwnedRuntimeInstall } from "../lib/install-set.js";
|
|
29
|
+
import {
|
|
30
|
+
ejectRuntime, ejectsRuntime, runtimeCopyStatus, runtimeDiffCommand, runtimeInstall,
|
|
31
|
+
type RuntimeEjectResult, type RuntimeFileResult,
|
|
32
|
+
} from "../lib/runtime-eject.js";
|
|
33
|
+
import { HTTP_RUNTIME_PACKAGE, OWNED_RUNTIME_DIR } from "@metaobjectsdev/codegen-ts";
|
|
29
34
|
import { emitStructured, type OutputFormat } from "../lib/format.js";
|
|
30
35
|
import {
|
|
31
36
|
ejectLibrary, ejectableLibraryNames, isLibraryName, libraryStaleness,
|
|
@@ -159,6 +164,12 @@ export interface EjectResult {
|
|
|
159
164
|
* cheapest thing that makes that condition observable.
|
|
160
165
|
*/
|
|
161
166
|
comparison?: OwnedComparison | undefined;
|
|
167
|
+
/**
|
|
168
|
+
* ADR-0034 Amendment 3 (2026-09-24): the HTTP-adapter source this generator's OUTPUT
|
|
169
|
+
* imports, copied into `codegen/runtime/` so the adopter owns that too. Absent for a
|
|
170
|
+
* generator whose output imports no adapter.
|
|
171
|
+
*/
|
|
172
|
+
runtime?: RuntimeEjectResult | undefined;
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
/** The `@metaobjectsdev/*` packages an ejected template imports, read from the file
|
|
@@ -241,8 +252,15 @@ export async function ejectGenerator(opts: EjectOptions): Promise<EjectResult> {
|
|
|
241
252
|
const comparison =
|
|
242
253
|
existing === undefined ? undefined : await compareOwnedCopy(existing, templateSource);
|
|
243
254
|
|
|
255
|
+
// The adapter source the OUTPUT imports is part of what is handed over — copied whether
|
|
256
|
+
// or not the generator file itself was already here, and under the same never-clobber
|
|
257
|
+
// rule, so re-running eject fills in a missing copy without touching a kept one.
|
|
258
|
+
const runtime = ejectsRuntime(opts.name)
|
|
259
|
+
? await ejectRuntime(opts.cwd, [opts.name], opts.force === true)
|
|
260
|
+
: undefined;
|
|
261
|
+
|
|
244
262
|
if (!opts.force && existing !== undefined) {
|
|
245
|
-
return { ...common, status: "preserved", comparison };
|
|
263
|
+
return { ...common, status: "preserved", comparison, runtime };
|
|
246
264
|
}
|
|
247
265
|
|
|
248
266
|
await mkdir(join(opts.cwd, OWNED_GENERATORS_DIR), { recursive: true });
|
|
@@ -251,9 +269,45 @@ export async function ejectGenerator(opts: EjectOptions): Promise<EjectResult> {
|
|
|
251
269
|
...common,
|
|
252
270
|
status: existing === undefined ? "created" : "replaced",
|
|
253
271
|
comparison,
|
|
272
|
+
runtime,
|
|
254
273
|
};
|
|
255
274
|
}
|
|
256
275
|
|
|
276
|
+
/**
|
|
277
|
+
* The owned HTTP-adapter copy under `codegen/runtime/`, file by file, against the installed
|
|
278
|
+
* `@metaobjectsdev/runtime-ts` it was copied from. Shared by `meta eject --list` and
|
|
279
|
+
* `meta gen --list`. Empty when the project owns no adapter copy.
|
|
280
|
+
*
|
|
281
|
+
* `identical` says an upgrade can be taken by re-copying (`meta eject <name> --force`);
|
|
282
|
+
* `DIFFERS` says the copy is yours in a way the package is not — your fix, or upstream's —
|
|
283
|
+
* and names the diff that tells which.
|
|
284
|
+
*/
|
|
285
|
+
export async function runtimeCopyLines(cwd: string): Promise<string[]> {
|
|
286
|
+
const rows = await runtimeCopyStatus(cwd);
|
|
287
|
+
if (rows.length === 0) return [];
|
|
288
|
+
const lines: string[] = [
|
|
289
|
+
`HTTP adapter source this project owns (${OWNED_RUNTIME_DIR}/), against the installed ${HTTP_RUNTIME_PACKAGE}:`,
|
|
290
|
+
];
|
|
291
|
+
const differing: string[] = [];
|
|
292
|
+
for (const r of rows) {
|
|
293
|
+
if (r.verdict === "identical") lines.push(` ${r.path} [identical to the package]`);
|
|
294
|
+
else if (r.verdict === "reformatted") lines.push(` ${r.path} [same content as the package, your formatting]`);
|
|
295
|
+
else if (r.verdict === "not-in-package") lines.push(` ${r.path} [not in the package — yours, or removed upstream]`);
|
|
296
|
+
else {
|
|
297
|
+
differing.push(r.path);
|
|
298
|
+
lines.push(
|
|
299
|
+
` ${r.path} [DIFFERS: ${r.referenceOnly} line(s) only in the package, ${r.localOnly} line(s) only in yours]`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (differing.length > 0) {
|
|
304
|
+
lines.push(" See the difference (upstream fix, or your own?) before taking an upgrade:");
|
|
305
|
+
for (const p of differing) lines.push(` ${runtimeDiffCommand(p)}`);
|
|
306
|
+
}
|
|
307
|
+
lines.push("");
|
|
308
|
+
return lines;
|
|
309
|
+
}
|
|
310
|
+
|
|
257
311
|
/**
|
|
258
312
|
* `--list` doubles as the staleness report for what this project ALREADY owns.
|
|
259
313
|
*
|
|
@@ -315,6 +369,7 @@ async function listOutput(cwd: string): Promise<string> {
|
|
|
315
369
|
);
|
|
316
370
|
lines.push("");
|
|
317
371
|
}
|
|
372
|
+
lines.push(...(await runtimeCopyLines(cwd)));
|
|
318
373
|
// FR-043 — the shipped libraries, and how far this project's copies have drifted.
|
|
319
374
|
lines.push("Shipped libraries (copy one into your own sources and own it):");
|
|
320
375
|
for (const name of ejectableLibraryNames()) lines.push(` ${name}`);
|
|
@@ -369,6 +424,8 @@ interface EjectedRow {
|
|
|
369
424
|
/** The two edits an adopter makes in `metaobjects.config.ts`. */
|
|
370
425
|
wire: { import: string; entry: string };
|
|
371
426
|
requires: readonly string[];
|
|
427
|
+
/** The adapter source copied beside it (ADR-0034 Amendment 3). Absent when none. */
|
|
428
|
+
runtime?: RuntimeFileResult[];
|
|
372
429
|
}
|
|
373
430
|
|
|
374
431
|
interface EjectPayload {
|
|
@@ -518,6 +575,33 @@ function reportOne(result: EjectResult, name: string): void {
|
|
|
518
575
|
"AND the entry.",
|
|
519
576
|
);
|
|
520
577
|
for (const line of result.dependencyNotes) log.info(line);
|
|
578
|
+
if (result.runtime !== undefined && result.runtime.files.length > 0) reportRuntime(result.runtime.files);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/** What the adapter copy handed over, and how the emitted code reaches it. */
|
|
582
|
+
function reportRuntime(files: readonly RuntimeFileResult[]): void {
|
|
583
|
+
const written = files.filter((f) => f.status !== "preserved");
|
|
584
|
+
// A kept file that already matches the package is not news (ejecting `entity routes`
|
|
585
|
+
// copies the shared allowlist file once, then finds it) — only a kept file that differs is.
|
|
586
|
+
const kept = files.filter((f) => f.status === "preserved" && f.comparison?.verdict !== "identical");
|
|
587
|
+
log.info(
|
|
588
|
+
`The code this generator emits imports its HTTP adapter from ${OWNED_RUNTIME_DIR}/, not from ` +
|
|
589
|
+
`${HTTP_RUNTIME_PACKAGE}: the adapter, filter parser, error envelopes and pagination are yours too.`,
|
|
590
|
+
);
|
|
591
|
+
if (written.length > 0) {
|
|
592
|
+
log.info(` Copied ${written.length} file(s) into ${OWNED_RUNTIME_DIR}/ (verbatim from the installed package).`);
|
|
593
|
+
}
|
|
594
|
+
const keptDiffering = kept.filter((f) => f.comparison?.verdict === "differs");
|
|
595
|
+
if (kept.length > 0) {
|
|
596
|
+
log.info(
|
|
597
|
+
` ${kept.length} file(s) were already there and were left untouched` +
|
|
598
|
+
(keptDiffering.length > 0 ? `, ${keptDiffering.length} of them differing from the package` : "") +
|
|
599
|
+
" (--force replaces them). `meta eject --list` compares each one.",
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
log.info(
|
|
603
|
+
` To import the package instead, pass \`runtimeImport: "${HTTP_RUNTIME_PACKAGE}"\` to the generator.`,
|
|
604
|
+
);
|
|
521
605
|
}
|
|
522
606
|
|
|
523
607
|
export async function ejectCommand(
|
|
@@ -575,6 +659,9 @@ export async function ejectCommand(
|
|
|
575
659
|
const rows: EjectedRow[] = [];
|
|
576
660
|
const libraries: LibraryEjectResult[] = [];
|
|
577
661
|
const templatePackages = new Set<string>();
|
|
662
|
+
// ADR-0034 Amendment 3: the generators whose adapter source was copied, and what it imports.
|
|
663
|
+
const runtimeGenerators = new Set<string>();
|
|
664
|
+
const runtimePackages = new Set<string>();
|
|
578
665
|
try {
|
|
579
666
|
for (const name of flags.names) {
|
|
580
667
|
if (isLibraryName(name)) {
|
|
@@ -585,12 +672,17 @@ export async function ejectCommand(
|
|
|
585
672
|
}
|
|
586
673
|
const result = await ejectGenerator({ cwd, name, force: flags.force });
|
|
587
674
|
for (const p of result.templatePackages) templatePackages.add(p);
|
|
675
|
+
if (result.runtime !== undefined) {
|
|
676
|
+
runtimeGenerators.add(name);
|
|
677
|
+
for (const p of result.runtime.packages) runtimePackages.add(p);
|
|
678
|
+
}
|
|
588
679
|
rows.push({
|
|
589
680
|
name,
|
|
590
681
|
path: result.path,
|
|
591
682
|
status: result.status,
|
|
592
683
|
wire: { import: result.importLine, entry: `${result.exportName}()` },
|
|
593
684
|
requires: catalog[name]?.requires ?? [],
|
|
685
|
+
...(result.runtime !== undefined ? { runtime: result.runtime.files } : {}),
|
|
594
686
|
});
|
|
595
687
|
if (fmt === "text") reportOne(result, name);
|
|
596
688
|
}
|
|
@@ -610,7 +702,15 @@ export async function ejectCommand(
|
|
|
610
702
|
.filter((n) => !isLibraryName(n))
|
|
611
703
|
.map((n) => catalog[n])
|
|
612
704
|
.filter((e) => e !== undefined);
|
|
613
|
-
|
|
705
|
+
// The copied adapter source imports its own dependencies (qs, the framework, the core
|
|
706
|
+
// `@metaobjectsdev/metadata`), and the generated code no longer imports the runtime
|
|
707
|
+
// package — so the install line trades one for the other.
|
|
708
|
+
let ownedRuntime: OwnedRuntimeInstall | undefined;
|
|
709
|
+
if (runtimeGenerators.size > 0) {
|
|
710
|
+
const { runtime, dev } = runtimeInstall([...runtimePackages]);
|
|
711
|
+
ownedRuntime = { generators: runtimeGenerators, runtime, dev };
|
|
712
|
+
}
|
|
713
|
+
const install = installSetFor(entries, [...templatePackages], ownedRuntime);
|
|
614
714
|
const configKeys = [...new Set(entries.flatMap((e) => e.configKeys ?? []))].sort();
|
|
615
715
|
|
|
616
716
|
if (fmt === "text") {
|
package/src/commands/gen.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { packageOfResolutionKey } from "@metaobjectsdev/metadata";
|
|
|
22
22
|
import type { MetaRoot } from "@metaobjectsdev/metadata";
|
|
23
23
|
import type { Collection } from "@metaobjectsdev/sdk";
|
|
24
24
|
import { reportLoadError } from "../lib/load-error.js";
|
|
25
|
+
import { UNKNOWN_ATTR_GEN_ADVICE, unknownAttrWarnings } from "../lib/unknown-attr-warnings.js";
|
|
25
26
|
import {
|
|
26
27
|
buildCatalogListing, renderCatalogText, wiredGeneratorNames, ownedGeneratorNames,
|
|
27
28
|
declaredDepsOf,
|
|
@@ -140,16 +141,27 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
let metadata;
|
|
144
|
+
const loadOptions = {
|
|
145
|
+
...collectionLoadOptions(genCollection),
|
|
146
|
+
...loadMemoryOptionsFrom(forgeConfig),
|
|
147
|
+
};
|
|
143
148
|
try {
|
|
144
|
-
metadata = await loadMemory(genCollection.configDir,
|
|
145
|
-
...collectionLoadOptions(genCollection),
|
|
146
|
-
...loadMemoryOptionsFrom(forgeConfig),
|
|
147
|
-
});
|
|
149
|
+
metadata = await loadMemory(genCollection.configDir, loadOptions);
|
|
148
150
|
} catch (err) {
|
|
149
151
|
reportLoadError(log, "failed to load metadata", err);
|
|
150
152
|
return 2;
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
// ADR-0023: gen loads leniently, so an unknown attribute (`isAbstrakt: true`, a
|
|
156
|
+
// misspelt `@required`) used to pass without a word while `meta verify` rejected the
|
|
157
|
+
// same file — and the typo changed what was generated. Say so, naming attribute, node
|
|
158
|
+
// and file. Advisory: the exit code is unchanged.
|
|
159
|
+
const unknownAttrFindings = await unknownAttrWarnings(genCollection.configDir, loadOptions, projectRoot);
|
|
160
|
+
const unknownAttrs = unknownAttrFindings.length > 0
|
|
161
|
+
? [...unknownAttrFindings, UNKNOWN_ATTR_GEN_ADVICE]
|
|
162
|
+
: [];
|
|
163
|
+
for (const w of unknownAttrs) log.warn(`warning: ${w}`);
|
|
164
|
+
|
|
153
165
|
// FR-023 §11.1 item 2 — refuse a positional that names ONLY objects imported
|
|
154
166
|
// from a dependency and excluded from output by the default exclusion rule.
|
|
155
167
|
// Distinct from the runner's existing "no entities matched" WARNING
|
|
@@ -252,7 +264,8 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
252
264
|
outDir: targetDirs.length > 1 ? targetDirs.join(", ") : forgeConfig.outDir,
|
|
253
265
|
dialect: forgeConfig.dialect,
|
|
254
266
|
dryRun: cliConfig.dryRun,
|
|
255
|
-
|
|
267
|
+
// Text mode already printed these to stderr above; the structured forms carry them.
|
|
268
|
+
warnings: fmt === "text" ? [] : unknownAttrs,
|
|
256
269
|
antiPatterns,
|
|
257
270
|
generatorCount: forgeConfig.generators?.length ?? 0,
|
|
258
271
|
...(migrateAdvice !== undefined ? { migrateAdvice } : {}),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The dependency-free half of `meta generator` — its flags and help text — so the CLI entry
|
|
2
|
+
// point can print `meta generator --help` without loading the codegen packages the command
|
|
3
|
+
// itself needs.
|
|
4
|
+
|
|
5
|
+
/** Where owned generators live — the directory `meta init` scaffolds and `meta eject` fills. */
|
|
6
|
+
export const OWNED_GENERATORS_DIR = "codegen/generators";
|
|
7
|
+
export const CONFIG_FILE = "metaobjects.config.ts";
|
|
8
|
+
|
|
9
|
+
export const GENERATOR_SCOPES = ["entity", "package", "model"] as const;
|
|
10
|
+
export type GeneratorScope = (typeof GENERATOR_SCOPES)[number];
|
|
11
|
+
|
|
12
|
+
export const GENERATOR_OPTIONS = {
|
|
13
|
+
"scope": { type: "string", default: "entity" },
|
|
14
|
+
"force": { type: "boolean", default: false },
|
|
15
|
+
"no-wire": { type: "boolean", default: false },
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export const GENERATOR_HELP = `meta generator — write a generator of your own
|
|
19
|
+
|
|
20
|
+
USAGE:
|
|
21
|
+
meta generator new <name> [--scope entity|package|model] [--force] [--no-wire]
|
|
22
|
+
|
|
23
|
+
Writes ${OWNED_GENERATORS_DIR}/<name>.ts — a small, commented, WORKING generator — and
|
|
24
|
+
adds its import and entry to ${CONFIG_FILE}, so \`meta gen\` runs it and
|
|
25
|
+
\`meta verify --codegen\` gates its output straight away. Then edit the emit to produce
|
|
26
|
+
what you need: OpenAPI, JSON Schema, a client, DTOs, docs — anything the model describes.
|
|
27
|
+
|
|
28
|
+
This is the primary way to get an output MetaObjects does not ship. Reach for
|
|
29
|
+
\`meta eject <name>\` instead only when a reference generator already emits something
|
|
30
|
+
close to what you want (\`meta gen --list\` is the catalog).
|
|
31
|
+
|
|
32
|
+
<name> is kebab-case (json-schema, openapi, service-layer). It names the file, the
|
|
33
|
+
generator's \`name\` in diagnostics, and the exported factory (jsonSchemaGenerator).
|
|
34
|
+
|
|
35
|
+
FLAGS:
|
|
36
|
+
--scope <scope> What the generator walks, one file per unit (default: entity):
|
|
37
|
+
entity one file per object in the model
|
|
38
|
+
package one file per metadata package
|
|
39
|
+
model one file for the whole model
|
|
40
|
+
--force Overwrite an existing ${OWNED_GENERATORS_DIR}/<name>.ts
|
|
41
|
+
--no-wire Write the file only; print the import and entry to add yourself
|
|
42
|
+
--help, -h Print this help
|
|
43
|
+
|
|
44
|
+
Every port's shape, and JSON Schema + OpenAPI 3.1 examples to copy: the guide
|
|
45
|
+
docs/recipes/write-your-own-generator.md in the MetaObjects repository
|
|
46
|
+
(github.com/metaobjectsdev), and the metaobjects-codegen skill \`meta init\` installs.
|
|
47
|
+
`;
|