@metaobjectsdev/cli 0.24.4 → 0.24.5
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/src/commands/eject.d.ts +38 -0
- package/dist/src/commands/eject.d.ts.map +1 -0
- package/dist/src/commands/eject.js +233 -0
- package/dist/src/commands/eject.js.map +1 -0
- package/dist/src/commands/init.d.ts +11 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +180 -17
- 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 +8 -1
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/args.d.ts +8 -0
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +19 -0
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/detect-stack.d.ts.map +1 -1
- package/dist/src/lib/detect-stack.js +4 -13
- package/dist/src/lib/detect-stack.js.map +1 -1
- package/dist/src/lib/package-manifest.d.ts +27 -0
- package/dist/src/lib/package-manifest.d.ts.map +1 -0
- package/dist/src/lib/package-manifest.js +52 -0
- package/dist/src/lib/package-manifest.js.map +1 -0
- package/package.json +11 -11
- package/src/commands/eject.ts +282 -0
- package/src/commands/init.ts +185 -16
- package/src/commands/verify.ts +8 -1
- package/src/index.ts +24 -0
- package/src/lib/args.ts +34 -0
- package/src/lib/detect-stack.ts +4 -11
- package/src/lib/package-manifest.ts +58 -0
package/src/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ COMMANDS:
|
|
|
19
19
|
init --config-only Write only .metaobjects/config.json — for a Maven- or pip-rooted project
|
|
20
20
|
agent-docs Scaffold only the agent-context (.metaobjects/ + .claude/skills/) — canonical redirect target for all language ports
|
|
21
21
|
gen [<entity>...] Codegen TS targets from your declared metadata
|
|
22
|
+
eject <generator> Copy a reference generator into codegen/generators/ to own it (any time after init)
|
|
23
|
+
eject --list List every ejectable generator name, grouped by package
|
|
22
24
|
types [query] Search the metadata vocabulary (types, subtypes, @attrs) by name or description
|
|
23
25
|
export Flatten loaded metadata to one canonical JSON artifact
|
|
24
26
|
docs [<project-root>] --out <dir> Generate neutral metadata documentation (entity + template pages; --site for HTML site)
|
|
@@ -113,6 +115,24 @@ field.enum). Warnings only — it never fails the build. Opt out with --no-antip
|
|
|
113
115
|
or META_NO_ANTIPATTERNS=1.
|
|
114
116
|
|
|
115
117
|
NOTE: outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts
|
|
118
|
+
`,
|
|
119
|
+
eject: `meta eject — copy a reference generator into your repo so you own it
|
|
120
|
+
|
|
121
|
+
USAGE:
|
|
122
|
+
meta eject <name> Copy generator <name> into codegen/generators/<name>.ts
|
|
123
|
+
meta eject --list List every ejectable generator name, grouped by package
|
|
124
|
+
|
|
125
|
+
FLAGS:
|
|
126
|
+
--list List ejectable generators instead of copying one
|
|
127
|
+
--force Overwrite an already-ejected file (default: never clobber)
|
|
128
|
+
--help, -h Print this help
|
|
129
|
+
|
|
130
|
+
\`meta init\` copies four generators (entity, queries, routes, barrel) into
|
|
131
|
+
codegen/generators/ automatically (ADR-0034 scaffold-and-own). \`meta eject\` is
|
|
132
|
+
the same operation for ANY generator — one you skipped at init time, a UI-tier
|
|
133
|
+
generator like form/hooks/grid, or one a package gains later. It prints the
|
|
134
|
+
import line to paste into metaobjects.config.ts, and it never overwrites a
|
|
135
|
+
file you already own unless you pass --force.
|
|
116
136
|
`,
|
|
117
137
|
verify: `meta verify — drift gate (templates / DB schema / codegen / migration replay)
|
|
118
138
|
|
|
@@ -374,6 +394,10 @@ export async function run(argv: string[]): Promise<number> {
|
|
|
374
394
|
const { genCommand } = await import("./commands/gen.js");
|
|
375
395
|
return genCommand(rest, cwd, fmt);
|
|
376
396
|
}
|
|
397
|
+
case "eject": {
|
|
398
|
+
const { ejectCommand } = await import("./commands/eject.js");
|
|
399
|
+
return ejectCommand(rest, cwd);
|
|
400
|
+
}
|
|
377
401
|
case "export": {
|
|
378
402
|
const { exportCommand } = await import("./commands/export.js");
|
|
379
403
|
return exportCommand(rest, cwd);
|
package/src/lib/args.ts
CHANGED
|
@@ -490,3 +490,37 @@ export function parseMigrateArgs(argv: string[]): MigrateFlags {
|
|
|
490
490
|
applyPending,
|
|
491
491
|
};
|
|
492
492
|
}
|
|
493
|
+
|
|
494
|
+
// ---------------------------------------------------------------------------
|
|
495
|
+
// eject flags — FR-040 §4.2(a)
|
|
496
|
+
// ---------------------------------------------------------------------------
|
|
497
|
+
|
|
498
|
+
export interface EjectFlags {
|
|
499
|
+
/** The generator name to eject; undefined when only --list was given. */
|
|
500
|
+
name: string | undefined;
|
|
501
|
+
list: boolean;
|
|
502
|
+
/** Overwrite an already-ejected file; default false — eject never clobbers. */
|
|
503
|
+
force: boolean;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export function parseEjectArgs(argv: string[]): EjectFlags {
|
|
507
|
+
const { values, positionals } = parseArgs({
|
|
508
|
+
args: argv,
|
|
509
|
+
options: {
|
|
510
|
+
"list": { type: "boolean", default: false },
|
|
511
|
+
"force": { type: "boolean", default: false },
|
|
512
|
+
},
|
|
513
|
+
strict: true,
|
|
514
|
+
allowPositionals: true,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
if (positionals.length > 1) {
|
|
518
|
+
throw new Error(`meta eject takes at most one generator name; got: ${positionals.join(", ")}`);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return {
|
|
522
|
+
name: positionals[0],
|
|
523
|
+
list: !!values.list,
|
|
524
|
+
force: !!values.force,
|
|
525
|
+
};
|
|
526
|
+
}
|
package/src/lib/detect-stack.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { resolveCollection } from "@metaobjectsdev/sdk";
|
|
4
|
+
import { declaredDependencyNames, readPackageManifest } from "./package-manifest.js";
|
|
4
5
|
import {
|
|
5
6
|
detectStack, detectConcerns, makeStack,
|
|
6
7
|
type ServerLang, type ClientFramework, type Stack, type ProjectProbe,
|
|
@@ -8,17 +9,9 @@ import {
|
|
|
8
9
|
} from "@metaobjectsdev/sdk/agent-context";
|
|
9
10
|
|
|
10
11
|
function depNames(cwd: string): Set<string> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, Record<string, string>>;
|
|
16
|
-
for (const key of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
17
|
-
for (const name of Object.keys(pkg[key] ?? {})) out.add(name);
|
|
18
|
-
}
|
|
19
|
-
} catch { /* unreadable manifest — treat as no deps */ }
|
|
20
|
-
}
|
|
21
|
-
return out;
|
|
12
|
+
// An unreadable or absent manifest reads as no deps, which is what stack detection
|
|
13
|
+
// wants: it probes, it does not require.
|
|
14
|
+
return declaredDependencyNames(readPackageManifest(cwd) ?? {});
|
|
22
15
|
}
|
|
23
16
|
|
|
24
17
|
// Cheap substring probe, not a metamodel load: matches both canonical JSON's
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// One answer to "what does this project declare as a dependency".
|
|
2
|
+
//
|
|
3
|
+
// Three commands ask it — `meta init` (is this package already declared, or must the
|
|
4
|
+
// scaffold add it?), `meta eject` (will the ejected file's imports resolve, or does the
|
|
5
|
+
// adopter's tsc report TS2307?), and stack detection (which frameworks is this project
|
|
6
|
+
// on?). They had three hand-rolled JSON-parse-and-union blocks covering DIFFERENT field
|
|
7
|
+
// sets, and the asymmetry was the cost: `eject` reported a peer-declared package as
|
|
8
|
+
// missing and told the adopter to install what they already had — advice that, followed,
|
|
9
|
+
// adds a second physical copy of a package whose class identity is load-bearing (the
|
|
10
|
+
// ts-poet split in 0.21.6 and the metadata node-guard split are both that bug).
|
|
11
|
+
//
|
|
12
|
+
// A fix to one had no way to find the other two: nothing referenced anything, and no
|
|
13
|
+
// shared name connected them to a grep.
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
/** The dependency-bearing fields of a package.json, all optional. */
|
|
18
|
+
export interface PackageManifest {
|
|
19
|
+
dependencies?: Record<string, string>;
|
|
20
|
+
devDependencies?: Record<string, string>;
|
|
21
|
+
peerDependencies?: Record<string, string>;
|
|
22
|
+
optionalDependencies?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Every package name the manifest declares, across ALL FOUR dependency fields.
|
|
27
|
+
*
|
|
28
|
+
* All four, because the question every caller is really asking is "will this resolve,
|
|
29
|
+
* and will their typecheck be happy" — not "is it in one particular field". A library
|
|
30
|
+
* consuming MetaObjects through `peerDependencies` (the correct declaration for a
|
|
31
|
+
* package whose consumer supplies the version) has it declared, and so does one using
|
|
32
|
+
* `optionalDependencies`.
|
|
33
|
+
*/
|
|
34
|
+
export function declaredDependencyNames(pkg: PackageManifest): Set<string> {
|
|
35
|
+
const out = new Set<string>();
|
|
36
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"] as const) {
|
|
37
|
+
for (const name of Object.keys(pkg[field] ?? {})) out.add(name);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Read and parse `<cwd>/package.json`.
|
|
44
|
+
*
|
|
45
|
+
* `undefined` distinguishes "no readable manifest" from "a manifest declaring nothing",
|
|
46
|
+
* which callers report differently: with no manifest there is nothing to compare
|
|
47
|
+
* against, so the honest message names what a file needs rather than claiming it is
|
|
48
|
+
* missing from a list that was never read.
|
|
49
|
+
*/
|
|
50
|
+
export function readPackageManifest(cwd: string): PackageManifest | undefined {
|
|
51
|
+
const path = join(cwd, "package.json");
|
|
52
|
+
if (!existsSync(path)) return undefined;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(readFileSync(path, "utf8")) as PackageManifest;
|
|
55
|
+
} catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|