@metaobjectsdev/cli 1.0.3 → 1.0.4-rc.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 +13 -5
- package/dist/src/commands/eject.d.ts +2 -1
- package/dist/src/commands/eject.d.ts.map +1 -1
- package/dist/src/commands/eject.js +235 -75
- 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 +119 -29
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/init.d.ts +5 -9
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +80 -384
- 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 +40 -14
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +36 -14
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/advisory.d.ts +6 -0
- package/dist/src/lib/advisory.d.ts.map +1 -1
- package/dist/src/lib/advisory.js +10 -0
- package/dist/src/lib/advisory.js.map +1 -1
- package/dist/src/lib/args.d.ts +18 -2
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +15 -3
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/catalog-listing.d.ts +92 -0
- package/dist/src/lib/catalog-listing.d.ts.map +1 -0
- package/dist/src/lib/catalog-listing.js +268 -0
- package/dist/src/lib/catalog-listing.js.map +1 -0
- package/dist/src/lib/catalog.d.ts +41 -0
- package/dist/src/lib/catalog.d.ts.map +1 -0
- package/dist/src/lib/catalog.js +89 -0
- package/dist/src/lib/catalog.js.map +1 -0
- package/dist/src/lib/collection-load-options.d.ts +1 -1
- package/dist/src/lib/collection-load-options.d.ts.map +1 -1
- package/dist/src/lib/collection-load-options.js +6 -0
- package/dist/src/lib/collection-load-options.js.map +1 -1
- package/dist/src/lib/install-set.d.ts +31 -0
- package/dist/src/lib/install-set.d.ts.map +1 -0
- package/dist/src/lib/install-set.js +103 -0
- package/dist/src/lib/install-set.js.map +1 -0
- package/dist/src/lib/library-eject.d.ts +79 -0
- package/dist/src/lib/library-eject.d.ts.map +1 -0
- package/dist/src/lib/library-eject.js +241 -0
- package/dist/src/lib/library-eject.js.map +1 -0
- package/dist/src/lib/library-listing.d.ts +63 -0
- package/dist/src/lib/library-listing.d.ts.map +1 -0
- package/dist/src/lib/library-listing.js +178 -0
- package/dist/src/lib/library-listing.js.map +1 -0
- package/dist/src/lib/library-prefix-advisory.d.ts +17 -0
- package/dist/src/lib/library-prefix-advisory.d.ts.map +1 -0
- package/dist/src/lib/library-prefix-advisory.js +67 -0
- package/dist/src/lib/library-prefix-advisory.js.map +1 -0
- package/dist/src/lib/load-metaobjects-config.d.ts +1 -2
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +11 -17
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/requirement-check.d.ts +17 -2
- package/dist/src/lib/requirement-check.d.ts.map +1 -1
- package/dist/src/lib/requirement-check.js +51 -9
- package/dist/src/lib/requirement-check.js.map +1 -1
- package/package.json +10 -10
- package/src/commands/eject.ts +304 -90
- package/src/commands/gen.ts +128 -28
- package/src/commands/init.ts +82 -390
- package/src/commands/verify.ts +47 -15
- package/src/index.ts +36 -14
- package/src/lib/advisory.ts +13 -0
- package/src/lib/args.ts +29 -5
- package/src/lib/catalog-listing.ts +369 -0
- package/src/lib/catalog.ts +107 -0
- package/src/lib/collection-load-options.ts +7 -1
- package/src/lib/install-set.ts +116 -0
- package/src/lib/library-eject.ts +298 -0
- package/src/lib/library-listing.ts +258 -0
- package/src/lib/library-prefix-advisory.ts +78 -0
- package/src/lib/load-metaobjects-config.ts +15 -21
- package/src/lib/requirement-check.ts +71 -11
package/src/commands/verify.ts
CHANGED
|
@@ -14,7 +14,9 @@ import { emitStructured, type OutputFormat } from "../lib/format.js";
|
|
|
14
14
|
import {
|
|
15
15
|
antiPatternRows, missingBaseUrlRows, removedPropRows, ranSection, skippedSection, warnCapped,
|
|
16
16
|
type AdvisoryDiagnosticRow, type AdvisoryFindingRow, type AdvisorySection,
|
|
17
|
+
libraryPrefixRows,
|
|
17
18
|
} from "../lib/advisory.js";
|
|
19
|
+
import { scanForUnprovenancedLibraryPrefix } from "../lib/library-prefix-advisory.js";
|
|
18
20
|
import { warnIfAgentContextStale } from "../lib/agent-context-staleness.js";
|
|
19
21
|
import { warnIfManifestIgnored } from "../lib/manifest-ignored-check.js";
|
|
20
22
|
import { scanSourceForAntiPatterns } from "../lib/anti-patterns.js";
|
|
@@ -382,7 +384,7 @@ export async function verifyCommand(
|
|
|
382
384
|
// model. Warnings ONLY — never changes the exit code (bias to under-flagging).
|
|
383
385
|
// Suppressed with --no-antipatterns or META_NO_ANTIPATTERNS=1 for the rare
|
|
384
386
|
// noisy project (both opt-outs work on `meta verify` and `meta gen`).
|
|
385
|
-
runAntiPatternAdvisory();
|
|
387
|
+
await runAntiPatternAdvisory();
|
|
386
388
|
|
|
387
389
|
const exitCode = Math.max(
|
|
388
390
|
templateExit,
|
|
@@ -646,15 +648,20 @@ export async function verifyCommand(
|
|
|
646
648
|
say(
|
|
647
649
|
`meta verify — requirements: ${s.total} entries (${s.functional} functional, ` +
|
|
648
650
|
`${s.architectural} architectural) — ${parts.join(", ")}; ` +
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
//
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
651
|
+
// FR-043 §5.4 — a project whose every requirement came from a shipped library
|
|
652
|
+
// is not measured, and says so rather than printing a ratio. Silence would be
|
|
653
|
+
// worse than either: a missing clause reads as a clean coverage result.
|
|
654
|
+
(s.entitiesTotal === undefined
|
|
655
|
+
? `coverage: not measured (no project-authored requirements).`
|
|
656
|
+
: `${s.entitiesClaimed}/${s.entitiesTotal} entities claimed, ` +
|
|
657
|
+
`counted over ${collection.files.length} metadata file(s)` +
|
|
658
|
+
// FR-023 — only when there ARE dependencies. A project that declares none
|
|
659
|
+
// must print the sentence it printed before dependencies existed, to the
|
|
660
|
+
// byte: "0 from dependencies" is noise on every existing project, and this
|
|
661
|
+
// line is a SURFACE the no-dependency guarantee covers like any other.
|
|
662
|
+
(collection.dependencies.length > 0
|
|
663
|
+
? `, ${collection.dependencies.length} from dependencies.`
|
|
664
|
+
: `.`)),
|
|
658
665
|
);
|
|
659
666
|
if (s.undecided > 0) {
|
|
660
667
|
say(
|
|
@@ -674,8 +681,12 @@ export async function verifyCommand(
|
|
|
674
681
|
.filter((k) => (s.byStatus[k] ?? 0) > 0)
|
|
675
682
|
.map((k) => ({ status: k, count: s.byStatus[k] ?? 0 })),
|
|
676
683
|
undecided: s.undecided,
|
|
677
|
-
|
|
678
|
-
|
|
684
|
+
// Absent when coverage was not measured (FR-043 §5.4), never zeroed: a reader
|
|
685
|
+
// cannot tell `0/0 claimed` from "not measured", and the two mean opposite
|
|
686
|
+
// things.
|
|
687
|
+
...(s.entitiesTotal === undefined
|
|
688
|
+
? {}
|
|
689
|
+
: { entitiesClaimed: s.entitiesClaimed ?? 0, entitiesTotal: s.entitiesTotal }),
|
|
679
690
|
metadataFiles: collection.files.length,
|
|
680
691
|
};
|
|
681
692
|
}
|
|
@@ -777,7 +788,7 @@ export async function verifyCommand(
|
|
|
777
788
|
// Records its result EITHER WAY — a skip carries its reason rather than looking
|
|
778
789
|
// like a clean scan. Warnings only; nothing here reaches the exit code (the
|
|
779
790
|
// scanner's own header: bias to under-flagging, never a non-zero exit).
|
|
780
|
-
function runAntiPatternAdvisory(): void {
|
|
791
|
+
async function runAntiPatternAdvisory(): Promise<void> {
|
|
781
792
|
if (flags.noAntipatterns) {
|
|
782
793
|
antiPatternSection = skippedSection("suppressed by --no-antipatterns");
|
|
783
794
|
return;
|
|
@@ -816,10 +827,23 @@ export async function verifyCommand(
|
|
|
816
827
|
// advisory only — never breaks verify.
|
|
817
828
|
}
|
|
818
829
|
|
|
830
|
+
// FR-043 §3.5 — `metaobjects::` in this project's own metadata with no ejection
|
|
831
|
+
// provenance. Advisory because with the library NOT opted in nothing is broken; the
|
|
832
|
+
// opted-in case is refused at load instead.
|
|
833
|
+
let libraryPrefix: Awaited<ReturnType<typeof scanForUnprovenancedLibraryPrefix>> = [];
|
|
834
|
+
try {
|
|
835
|
+
libraryPrefix = await scanForUnprovenancedLibraryPrefix(
|
|
836
|
+
root, collection.ownFiles, collection.configDir,
|
|
837
|
+
);
|
|
838
|
+
} catch {
|
|
839
|
+
// Same discipline as its siblings: an advisory scan never breaks verify.
|
|
840
|
+
}
|
|
841
|
+
|
|
819
842
|
antiPatternSection = ranSection([
|
|
820
843
|
...antiPatternRows(findings),
|
|
821
844
|
...missingBaseUrlRows(baseUrl),
|
|
822
845
|
...removedPropRows(removedProps),
|
|
846
|
+
...libraryPrefixRows(libraryPrefix),
|
|
823
847
|
]);
|
|
824
848
|
if (findings.length > 0) {
|
|
825
849
|
log.warn(
|
|
@@ -842,6 +866,13 @@ export async function verifyCommand(
|
|
|
842
866
|
);
|
|
843
867
|
warnCapped(removedProps.map((f) => ` ${f.message}`), flags.limit, { structured });
|
|
844
868
|
}
|
|
869
|
+
if (libraryPrefix.length > 0) {
|
|
870
|
+
log.warn(
|
|
871
|
+
`meta verify — ${libraryPrefix.length} node(s) declared under "metaobjects::" with ` +
|
|
872
|
+
`no ejection provenance (advisory — does not fail the build):`,
|
|
873
|
+
);
|
|
874
|
+
warnCapped(libraryPrefix.map((f) => ` ${f.message}`), flags.limit, { structured });
|
|
875
|
+
}
|
|
845
876
|
}
|
|
846
877
|
|
|
847
878
|
// -- template (prompt / output) drift --------------------------------------
|
|
@@ -1572,8 +1603,9 @@ interface RequirementCounts {
|
|
|
1572
1603
|
architectural: number;
|
|
1573
1604
|
byStatus: { status: string; count: number }[];
|
|
1574
1605
|
undecided: number;
|
|
1575
|
-
|
|
1576
|
-
|
|
1606
|
+
/** Absent when coverage was not measured — FR-043 §5.4. */
|
|
1607
|
+
entitiesClaimed?: number;
|
|
1608
|
+
entitiesTotal?: number;
|
|
1577
1609
|
/** How many metadata files the two entity counts were taken over. */
|
|
1578
1610
|
metadataFiles: number;
|
|
1579
1611
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ const VERSION = cliVersion();
|
|
|
17
17
|
* human text. It is named ONCE and used by both the warning below and the help
|
|
18
18
|
* text above, so the two cannot drift apart.
|
|
19
19
|
*/
|
|
20
|
-
const FORMAT_AWARE_COMMANDS: readonly string[] = ["gen", "verify", "migrate", "types", "deps"];
|
|
20
|
+
const FORMAT_AWARE_COMMANDS: readonly string[] = ["gen", "verify", "migrate", "types", "deps", "eject"];
|
|
21
21
|
|
|
22
22
|
const HELP_TEXT = `meta — MetaObjects CLI (v${VERSION})
|
|
23
23
|
|
|
@@ -58,8 +58,14 @@ GLOBAL OPTIONS:
|
|
|
58
58
|
|
|
59
59
|
GEN FLAGS:
|
|
60
60
|
--dry-run Compute and print, don't write
|
|
61
|
-
--list Print the generator
|
|
62
|
-
|
|
61
|
+
--list Print the generator CATALOG (name, layer, tier, what it emits,
|
|
62
|
+
what to install) and exit. Codegen is opt-in: nothing runs until
|
|
63
|
+
you wire it, and this is where you choose. No config or metadata
|
|
64
|
+
required — add --format json for the same catalog, machine-readable.
|
|
65
|
+
--probe With --list: construct every catalog generator and dry-run it
|
|
66
|
+
against YOUR model, reporting how many files each would emit.
|
|
67
|
+
A count per generator beats any category label — and it
|
|
68
|
+
cannot go stale, because it runs the generators. Needs a project.
|
|
63
69
|
--baseline <default|adopt|fresh>
|
|
64
70
|
First-time-on-existing-file behavior. Default: refuse a file that
|
|
65
71
|
cannot be proved to be generated output. "adopt" records the files
|
|
@@ -156,8 +162,14 @@ USAGE:
|
|
|
156
162
|
|
|
157
163
|
FLAGS:
|
|
158
164
|
--dry-run Compute and print, don't write
|
|
159
|
-
--list Print the generator
|
|
160
|
-
|
|
165
|
+
--list Print the generator CATALOG (name, layer, tier, what it emits,
|
|
166
|
+
what to install) and exit. Codegen is opt-in: nothing runs until
|
|
167
|
+
you wire it, and this is where you choose. No config or metadata
|
|
168
|
+
required — add --format json for the same catalog, machine-readable.
|
|
169
|
+
--probe With --list: construct every catalog generator and dry-run it
|
|
170
|
+
against YOUR model, reporting how many files each would emit.
|
|
171
|
+
A count per generator beats any category label — and it cannot go
|
|
172
|
+
stale, because it runs the generators. Needs a project.
|
|
161
173
|
--baseline <default|adopt|fresh>
|
|
162
174
|
First-time-on-existing-file behavior. Default: refuse a file that
|
|
163
175
|
cannot be proved to be generated output. "adopt" records the files
|
|
@@ -183,23 +195,33 @@ limit on the machine-readable result.
|
|
|
183
195
|
|
|
184
196
|
NOTE: outDir, dialect, dbImport, extStyle are read from metaobjects.config.ts
|
|
185
197
|
`,
|
|
186
|
-
eject: `meta eject — copy
|
|
198
|
+
eject: `meta eject — copy reference generators into your repo so you own them
|
|
187
199
|
|
|
188
200
|
USAGE:
|
|
189
|
-
meta eject <name
|
|
201
|
+
meta eject <name>... Copy each generator into codegen/generators/<name>.ts
|
|
190
202
|
meta eject --list List every ejectable generator name, grouped by package
|
|
191
203
|
|
|
204
|
+
Codegen is opt-in — \`meta init\` wires nothing — so this is the door every generator
|
|
205
|
+
you run comes through. Take several at once: the install set is CONSOLIDATED, so
|
|
206
|
+
\`meta eject form hooks grid\` prints one install line, not three. An unknown name
|
|
207
|
+
refuses the whole call and copies nothing.
|
|
208
|
+
|
|
192
209
|
FLAGS:
|
|
193
210
|
--list List ejectable generators instead of copying one
|
|
194
211
|
--force Overwrite an already-ejected file (default: never clobber)
|
|
212
|
+
--format <toon|json|text> Output format (global flag). The structured form carries
|
|
213
|
+
each file's wire lines, the consolidated install set and the
|
|
214
|
+
config keys the ejected generators read.
|
|
195
215
|
--help, -h Print this help
|
|
196
216
|
|
|
197
|
-
\`meta init\` copies
|
|
198
|
-
|
|
199
|
-
the
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
217
|
+
\`meta init\` copies NO generators — codegen is opt-in, and codegen/generators/
|
|
218
|
+
starts empty on purpose. Everything you run comes through here (ADR-0034
|
|
219
|
+
scaffold-and-own): the file is YOURS to edit, and \`meta gen\` runs your copy, not
|
|
220
|
+
the package's. It prints the import line and the entry to add in
|
|
221
|
+
metaobjects.config.ts, and never overwrites a file you already own without --force.
|
|
222
|
+
|
|
223
|
+
Find names with \`meta gen --list\` — add --probe to see how many files each would
|
|
224
|
+
emit for your model.
|
|
203
225
|
`,
|
|
204
226
|
deps: `meta deps — sync a declared metadata dependency's committed snapshot
|
|
205
227
|
|
|
@@ -584,7 +606,7 @@ export async function run(argv: string[]): Promise<number> {
|
|
|
584
606
|
}
|
|
585
607
|
case "eject": {
|
|
586
608
|
const { ejectCommand } = await import("./commands/eject.js");
|
|
587
|
-
return ejectCommand(rest, cwd);
|
|
609
|
+
return ejectCommand(rest, cwd, fmt);
|
|
588
610
|
}
|
|
589
611
|
case "deps": {
|
|
590
612
|
const { depsCommand } = await import("./commands/deps.js");
|
package/src/lib/advisory.ts
CHANGED
|
@@ -150,6 +150,19 @@ export function removedPropRows(findings: readonly RemovedPropFinding[]): Adviso
|
|
|
150
150
|
}));
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
/** FR-043 §3.5 findings in the same row shape. */
|
|
154
|
+
export function libraryPrefixRows(
|
|
155
|
+
findings: readonly { file: string; fqn: string; message: string }[],
|
|
156
|
+
): AdvisoryFindingRow[] {
|
|
157
|
+
return findings.map((f) => ({
|
|
158
|
+
file: f.file,
|
|
159
|
+
line: 0,
|
|
160
|
+
rule: "library-prefix-unprovenanced",
|
|
161
|
+
construct: f.fqn,
|
|
162
|
+
message: f.message,
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
153
166
|
// ---------------------------------------------------------------------------
|
|
154
167
|
// text output
|
|
155
168
|
// ---------------------------------------------------------------------------
|
package/src/lib/args.ts
CHANGED
|
@@ -107,6 +107,12 @@ export interface GenFlags {
|
|
|
107
107
|
/** ADR-0021 D3 — print the stable-name generator registry and exit without
|
|
108
108
|
* running codegen. */
|
|
109
109
|
list: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* `--list` only: construct every catalog generator and dry-run it against THIS
|
|
112
|
+
* project's model, reporting a real file count per generator. Needs a project;
|
|
113
|
+
* plain `--list` deliberately does not (it describes the installed engine).
|
|
114
|
+
*/
|
|
115
|
+
probe: boolean;
|
|
110
116
|
/** Suppress the advisory anti-pattern (verify-as-teacher) pass. */
|
|
111
117
|
noAntipatterns: boolean;
|
|
112
118
|
/**
|
|
@@ -122,6 +128,7 @@ export const GEN_OPTIONS = {
|
|
|
122
128
|
"dry-run": { type: "boolean", default: false },
|
|
123
129
|
"baseline": { type: "string" },
|
|
124
130
|
"list": { type: "boolean", default: false },
|
|
131
|
+
"probe": { type: "boolean", default: false },
|
|
125
132
|
"no-antipatterns": { type: "boolean", default: false },
|
|
126
133
|
"limit": { type: "string" },
|
|
127
134
|
} as const;
|
|
@@ -149,6 +156,7 @@ export function parseGenArgs(argv: string[]): GenFlags {
|
|
|
149
156
|
entities: positionals,
|
|
150
157
|
baseline: (baselineRaw as "default" | "fresh" | "adopt" | undefined) ?? "default",
|
|
151
158
|
list: !!values.list,
|
|
159
|
+
probe: !!values.probe,
|
|
152
160
|
noAntipatterns: !!values["no-antipatterns"],
|
|
153
161
|
// Throws on a bad value; the command layer reports it and exits 2, exactly as
|
|
154
162
|
// it does for --baseline above.
|
|
@@ -602,8 +610,14 @@ export function parseMigrateArgs(argv: string[]): MigrateFlags {
|
|
|
602
610
|
// ---------------------------------------------------------------------------
|
|
603
611
|
|
|
604
612
|
export interface EjectFlags {
|
|
605
|
-
/**
|
|
606
|
-
|
|
613
|
+
/**
|
|
614
|
+
* The generator names to eject, in the order given. Empty when only --list was given.
|
|
615
|
+
*
|
|
616
|
+
* MANY, not one: choosing a client tier means `form hooks grid`, and three separate
|
|
617
|
+
* invocations produce three separate install lines for the same package. The
|
|
618
|
+
* consolidated install set is the point of taking them together.
|
|
619
|
+
*/
|
|
620
|
+
names: string[];
|
|
607
621
|
list: boolean;
|
|
608
622
|
/** Overwrite an already-ejected file; default false — eject never clobbers. */
|
|
609
623
|
force: boolean;
|
|
@@ -623,12 +637,22 @@ export function parseEjectArgs(argv: string[]): EjectFlags {
|
|
|
623
637
|
allowPositionals: true,
|
|
624
638
|
});
|
|
625
639
|
|
|
626
|
-
|
|
627
|
-
|
|
640
|
+
// `Set.add` returns the SET, which is always truthy — so the tempting
|
|
641
|
+
// `positionals.filter((n) => !seen.add(n))` never reports anything.
|
|
642
|
+
const seen = new Set<string>();
|
|
643
|
+
const duplicates: string[] = [];
|
|
644
|
+
for (const n of positionals) {
|
|
645
|
+
if (seen.has(n)) duplicates.push(n);
|
|
646
|
+
else seen.add(n);
|
|
647
|
+
}
|
|
648
|
+
if (duplicates.length > 0) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
`meta eject: repeated generator name(s): ${[...new Set(duplicates)].join(", ")}`,
|
|
651
|
+
);
|
|
628
652
|
}
|
|
629
653
|
|
|
630
654
|
return {
|
|
631
|
-
|
|
655
|
+
names: positionals,
|
|
632
656
|
list: !!values.list,
|
|
633
657
|
force: !!values.force,
|
|
634
658
|
};
|