@metaobjectsdev/cli 1.0.12 → 1.1.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/README.md +32 -22
- package/dist/bin/meta.js +14 -2
- package/dist/bin/meta.js.map +1 -1
- package/dist/src/commands/docs.d.ts +1 -14
- package/dist/src/commands/docs.d.ts.map +1 -1
- package/dist/src/commands/docs.js +11 -835
- package/dist/src/commands/docs.js.map +1 -1
- package/dist/src/commands/eject.d.ts +24 -1
- package/dist/src/commands/eject.d.ts.map +1 -1
- package/dist/src/commands/eject.js +240 -69
- package/dist/src/commands/eject.js.map +1 -1
- package/dist/src/commands/gen.d.ts +7 -0
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +91 -21
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/generator.js +2 -2
- package/dist/src/commands/generator.js.map +1 -1
- package/dist/src/commands/init.d.ts +7 -0
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +71 -36
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +1 -1
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +224 -125
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/commands/types.d.ts.map +1 -1
- package/dist/src/commands/types.js +16 -2
- package/dist/src/commands/types.js.map +1 -1
- package/dist/src/commands/upgrade.d.ts.map +1 -1
- package/dist/src/commands/upgrade.js +4 -1
- package/dist/src/commands/upgrade.js.map +1 -1
- package/dist/src/commands/verify.d.ts.map +1 -1
- package/dist/src/commands/verify.js +189 -125
- package/dist/src/commands/verify.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +59 -72
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/agent-context-staleness.d.ts +8 -1
- package/dist/src/lib/agent-context-staleness.d.ts.map +1 -1
- package/dist/src/lib/agent-context-staleness.js +20 -5
- package/dist/src/lib/agent-context-staleness.js.map +1 -1
- package/dist/src/lib/agent-schema.d.ts +9 -0
- package/dist/src/lib/agent-schema.d.ts.map +1 -0
- package/dist/src/lib/agent-schema.js +61 -0
- package/dist/src/lib/agent-schema.js.map +1 -0
- package/dist/src/lib/allow.d.ts +6 -0
- package/dist/src/lib/allow.d.ts.map +1 -1
- package/dist/src/lib/allow.js +49 -5
- package/dist/src/lib/allow.js.map +1 -1
- package/dist/src/lib/anti-patterns.d.ts.map +1 -1
- package/dist/src/lib/anti-patterns.js +4 -2
- package/dist/src/lib/anti-patterns.js.map +1 -1
- package/dist/src/lib/args.d.ts +51 -10
- package/dist/src/lib/args.d.ts.map +1 -1
- package/dist/src/lib/args.js +44 -18
- package/dist/src/lib/args.js.map +1 -1
- package/dist/src/lib/catalog-listing.d.ts +21 -1
- package/dist/src/lib/catalog-listing.d.ts.map +1 -1
- package/dist/src/lib/catalog-listing.js +85 -16
- package/dist/src/lib/catalog-listing.js.map +1 -1
- package/dist/src/lib/catalog.d.ts +30 -0
- package/dist/src/lib/catalog.d.ts.map +1 -1
- package/dist/src/lib/catalog.js +40 -5
- package/dist/src/lib/catalog.js.map +1 -1
- package/dist/src/lib/codegen-drift.d.ts +31 -2
- package/dist/src/lib/codegen-drift.d.ts.map +1 -1
- package/dist/src/lib/codegen-drift.js +109 -4
- package/dist/src/lib/codegen-drift.js.map +1 -1
- 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 +1 -0
- package/dist/src/lib/collection-load-options.js.map +1 -1
- package/dist/src/lib/config.d.ts +4 -2
- package/dist/src/lib/config.d.ts.map +1 -1
- package/dist/src/lib/config.js +4 -2
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/decimal-precision-advisory.d.ts +10 -0
- package/dist/src/lib/decimal-precision-advisory.d.ts.map +1 -0
- package/dist/src/lib/decimal-precision-advisory.js +34 -0
- package/dist/src/lib/decimal-precision-advisory.js.map +1 -0
- package/dist/src/lib/deprecation-lint.d.ts.map +1 -1
- package/dist/src/lib/deprecation-lint.js +17 -26
- package/dist/src/lib/deprecation-lint.js.map +1 -1
- package/dist/src/lib/docs-removed.d.ts +13 -0
- package/dist/src/lib/docs-removed.d.ts.map +1 -0
- package/dist/src/lib/docs-removed.js +31 -0
- package/dist/src/lib/docs-removed.js.map +1 -0
- package/dist/src/lib/docs-site-eject.d.ts +13 -0
- package/dist/src/lib/docs-site-eject.d.ts.map +1 -0
- package/dist/src/lib/docs-site-eject.js +40 -0
- package/dist/src/lib/docs-site-eject.js.map +1 -0
- package/dist/src/lib/field-lint.d.ts +25 -0
- package/dist/src/lib/field-lint.d.ts.map +1 -0
- package/dist/src/lib/field-lint.js +102 -0
- package/dist/src/lib/field-lint.js.map +1 -0
- package/dist/src/lib/fk-index-advisory.d.ts +1 -2
- package/dist/src/lib/fk-index-advisory.d.ts.map +1 -1
- package/dist/src/lib/fk-index-advisory.js +4 -3
- package/dist/src/lib/fk-index-advisory.js.map +1 -1
- package/dist/src/lib/format.d.ts +7 -0
- package/dist/src/lib/format.d.ts.map +1 -1
- package/dist/src/lib/format.js +13 -0
- package/dist/src/lib/format.js.map +1 -1
- package/dist/src/lib/gen-migrate-advice.d.ts +3 -1
- package/dist/src/lib/gen-migrate-advice.d.ts.map +1 -1
- package/dist/src/lib/gen-migrate-advice.js +3 -4
- package/dist/src/lib/gen-migrate-advice.js.map +1 -1
- package/dist/src/lib/install-set.d.ts +10 -1
- package/dist/src/lib/install-set.d.ts.map +1 -1
- package/dist/src/lib/install-set.js +20 -2
- package/dist/src/lib/install-set.js.map +1 -1
- package/dist/src/lib/load-metaobjects-config.d.ts +13 -11
- package/dist/src/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +53 -75
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/manifest-ignored-check.d.ts +4 -1
- package/dist/src/lib/manifest-ignored-check.d.ts.map +1 -1
- package/dist/src/lib/manifest-ignored-check.js +9 -4
- package/dist/src/lib/manifest-ignored-check.js.map +1 -1
- package/dist/src/lib/output.d.ts +15 -0
- package/dist/src/lib/output.d.ts.map +1 -1
- package/dist/src/lib/output.js +9 -1
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/payload-field-tree.d.ts +4 -5
- package/dist/src/lib/payload-field-tree.d.ts.map +1 -1
- package/dist/src/lib/payload-field-tree.js +16 -31
- package/dist/src/lib/payload-field-tree.js.map +1 -1
- package/dist/src/lib/requirement-check.d.ts +7 -0
- package/dist/src/lib/requirement-check.d.ts.map +1 -1
- package/dist/src/lib/requirement-check.js +14 -7
- package/dist/src/lib/requirement-check.js.map +1 -1
- package/dist/src/lib/strict-args.d.ts +28 -0
- package/dist/src/lib/strict-args.d.ts.map +1 -0
- package/dist/src/lib/strict-args.js +49 -0
- package/dist/src/lib/strict-args.js.map +1 -0
- package/dist/src/lib/template-eject.d.ts +108 -0
- package/dist/src/lib/template-eject.d.ts.map +1 -0
- package/dist/src/lib/template-eject.js +438 -0
- package/dist/src/lib/template-eject.js.map +1 -0
- package/dist/src/lib/version.d.ts +9 -0
- package/dist/src/lib/version.d.ts.map +1 -1
- package/dist/src/lib/version.js +15 -0
- package/dist/src/lib/version.js.map +1 -1
- package/package.json +10 -10
- package/src/commands/docs.ts +11 -952
- package/src/commands/eject.ts +318 -73
- package/src/commands/gen.ts +94 -22
- package/src/commands/generator.ts +2 -2
- package/src/commands/init.ts +80 -38
- package/src/commands/migrate.ts +248 -117
- package/src/commands/types.ts +18 -2
- package/src/commands/upgrade.ts +5 -1
- package/src/commands/verify.ts +229 -136
- package/src/index.ts +59 -72
- package/src/lib/agent-context-staleness.ts +20 -5
- package/src/lib/agent-schema.ts +74 -0
- package/src/lib/allow.ts +57 -6
- package/src/lib/anti-patterns.ts +4 -2
- package/src/lib/args.ts +83 -28
- package/src/lib/catalog-listing.ts +105 -25
- package/src/lib/catalog.ts +51 -6
- package/src/lib/codegen-drift.ts +138 -3
- package/src/lib/collection-load-options.ts +2 -1
- package/src/lib/config.ts +5 -3
- package/src/lib/decimal-precision-advisory.ts +50 -0
- package/src/lib/deprecation-lint.ts +16 -24
- package/src/lib/docs-removed.ts +41 -0
- package/src/lib/docs-site-eject.ts +48 -0
- package/src/lib/field-lint.ts +122 -0
- package/src/lib/fk-index-advisory.ts +4 -4
- package/src/lib/format.ts +12 -0
- package/src/lib/gen-migrate-advice.ts +6 -4
- package/src/lib/install-set.ts +20 -1
- package/src/lib/load-metaobjects-config.ts +57 -78
- package/src/lib/manifest-ignored-check.ts +14 -10
- package/src/lib/output.ts +18 -1
- package/src/lib/payload-field-tree.ts +18 -35
- package/src/lib/requirement-check.ts +21 -6
- package/src/lib/strict-args.ts +52 -0
- package/src/lib/template-eject.ts +526 -0
- package/src/lib/version.ts +18 -0
- package/dist/src/lib/docs-drift.d.ts +0 -42
- package/dist/src/lib/docs-drift.d.ts.map +0 -1
- package/dist/src/lib/docs-drift.js +0 -264
- package/dist/src/lib/docs-drift.js.map +0 -1
- package/src/lib/docs-drift.ts +0 -312
package/src/commands/gen.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
1
|
+
import { dirname, join, relative } from "node:path";
|
|
2
2
|
import { parseGenArgs } from "../lib/args.js";
|
|
3
3
|
import { resolveGenConfig, resolveMigrateDefaults } from "../lib/config.js";
|
|
4
4
|
import { genMigrateAdvice } from "../lib/gen-migrate-advice.js";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
type AdvisoryFindingRow, type AdvisorySection,
|
|
17
17
|
} from "../lib/advisory.js";
|
|
18
18
|
import { loadMemory, resolveCollection } from "@metaobjectsdev/sdk";
|
|
19
|
-
import { runGen } from "@metaobjectsdev/codegen-ts";
|
|
19
|
+
import { declaredGeneratorNames, runGen, undeclaredGeneratorsMessage } from "@metaobjectsdev/codegen-ts";
|
|
20
20
|
import type { WriteStatus } from "@metaobjectsdev/codegen-ts";
|
|
21
21
|
import { packageOfResolutionKey } from "@metaobjectsdev/metadata";
|
|
22
22
|
import type { MetaRoot } from "@metaobjectsdev/metadata";
|
|
@@ -27,10 +27,13 @@ import {
|
|
|
27
27
|
buildCatalogListing, renderCatalogText, wiredGeneratorNames, ownedGeneratorNames,
|
|
28
28
|
declaredDepsOf,
|
|
29
29
|
} from "../lib/catalog-listing.js";
|
|
30
|
-
import { emitStructured } from "../lib/format.js";
|
|
30
|
+
import { emitStructured, narrate } from "../lib/format.js";
|
|
31
31
|
import { composeCatalog } from "../lib/catalog.js";
|
|
32
32
|
import { describeError } from "../lib/error-text.js";
|
|
33
33
|
import { findRuntimeBoundaryCrossings, runtimeBoundaryWarnings } from "../lib/runtime-boundary-advisory.js";
|
|
34
|
+
import { hasLegacyDocsBlock, legacyDocsConfigMessage, NO_CONFIG_MESSAGE } from "../lib/docs-removed.js";
|
|
35
|
+
import { buildAgentSchemaInput } from "../lib/agent-schema.js";
|
|
36
|
+
import { existsSync } from "node:fs";
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* Print a load failure with everything the loader's ADR-0009 envelope carried — the stable
|
|
@@ -127,13 +130,26 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
127
130
|
// would find no manifest there and silently skip the nudge. `meta verify` makes
|
|
128
131
|
// the same call for the same reason; the two commands describe this and the
|
|
129
132
|
// anti-pattern scan below as one advisory pass, so they must scan one tree.
|
|
130
|
-
|
|
133
|
+
//
|
|
134
|
+
// EVERY warning this run prints is also collected here, in the order printed, because
|
|
135
|
+
// the structured result carries them. They used to reach stderr only — and on a pipe,
|
|
136
|
+
// where the default format is the document, a reader of that document saw a clean run
|
|
137
|
+
// with every caveat missing. A new warning site adds to this list.
|
|
138
|
+
const warnings: string[] = [];
|
|
139
|
+
const advise = (line: string | undefined): void => { if (line !== undefined) warnings.push(line); };
|
|
140
|
+
advise(warnIfAgentContextStale(projectRoot));
|
|
131
141
|
// Advisory: the committed hash manifest is what makes hand-edit detection work on a
|
|
132
142
|
// machine that did not generate the output. Silent unless it is ignored. Keyed on
|
|
133
143
|
// projectRoot, not cwd, for the same reason its neighbour is — the manifest belongs to
|
|
134
144
|
// whichever directory `resolveCollection` decided the metadata lives in.
|
|
135
|
-
warnIfManifestIgnored(projectRoot);
|
|
145
|
+
advise(warnIfManifestIgnored(projectRoot));
|
|
136
146
|
|
|
147
|
+
// ADR-0034 Amendment 9: the config IS the list. With none there is nothing to generate,
|
|
148
|
+
// documentation included, and a run that wrote nothing silently would read as a pass.
|
|
149
|
+
if (!existsSync(join(projectRoot, "metaobjects.config.ts"))) {
|
|
150
|
+
log.error(NO_CONFIG_MESSAGE);
|
|
151
|
+
return 2;
|
|
152
|
+
}
|
|
137
153
|
let forgeConfig;
|
|
138
154
|
try {
|
|
139
155
|
forgeConfig = await loadMetaobjectsConfig(projectRoot);
|
|
@@ -141,6 +157,28 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
141
157
|
log.error(describeError(err));
|
|
142
158
|
return 2;
|
|
143
159
|
}
|
|
160
|
+
// A 1.0 `docs` block configured a command that no longer exists. Nothing reads it, so the
|
|
161
|
+
// run would write no documentation; it fails before writing anything instead.
|
|
162
|
+
if (hasLegacyDocsBlock(forgeConfig)) {
|
|
163
|
+
log.error(legacyDocsConfigMessage("meta gen"));
|
|
164
|
+
return 2;
|
|
165
|
+
}
|
|
166
|
+
// `--generators` narrows to DECLARED entries, by stable name; it never adds one.
|
|
167
|
+
const catalog = composeCatalog();
|
|
168
|
+
if (flags.generators !== undefined) {
|
|
169
|
+
let declared: string[];
|
|
170
|
+
try {
|
|
171
|
+
declared = declaredGeneratorNames(forgeConfig, catalog);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
log.error(describeError(err));
|
|
174
|
+
return 2;
|
|
175
|
+
}
|
|
176
|
+
const unknown = flags.generators.filter((n) => !declared.includes(n));
|
|
177
|
+
if (unknown.length > 0) {
|
|
178
|
+
log.error(`meta gen: ${undeclaredGeneratorsMessage(unknown, declared)}`);
|
|
179
|
+
return 2;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
144
182
|
|
|
145
183
|
let metadata;
|
|
146
184
|
const loadOptions = {
|
|
@@ -151,7 +189,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
151
189
|
metadata = await loadMemory(genCollection.configDir, loadOptions);
|
|
152
190
|
} catch (err) {
|
|
153
191
|
reportLoadError(log, "failed to load metadata", err);
|
|
154
|
-
return
|
|
192
|
+
return 1;
|
|
155
193
|
}
|
|
156
194
|
|
|
157
195
|
// ADR-0023: gen loads leniently, so an unknown attribute (`isAbstrakt: true`, a
|
|
@@ -162,7 +200,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
162
200
|
const unknownAttrs = unknownAttrFindings.length > 0
|
|
163
201
|
? [...unknownAttrFindings, UNKNOWN_ATTR_GEN_ADVICE]
|
|
164
202
|
: [];
|
|
165
|
-
for (const w of unknownAttrs) log.warn(`warning: ${w}`);
|
|
203
|
+
for (const w of unknownAttrs) { log.warn(`warning: ${w}`); warnings.push(w); }
|
|
166
204
|
|
|
167
205
|
// FR-023 §11.1 item 2 — refuse a positional that names ONLY objects imported
|
|
168
206
|
// from a dependency and excluded from output by the default exclusion rule.
|
|
@@ -185,7 +223,8 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
185
223
|
projectRoot,
|
|
186
224
|
// The COMPOSED catalog, so the requires / api-framework gates can see the react
|
|
187
225
|
// and tanstack entries too. codegen-ts only knows its own slice.
|
|
188
|
-
catalog
|
|
226
|
+
catalog,
|
|
227
|
+
...(flags.generators !== undefined && { generatorNames: flags.generators }),
|
|
189
228
|
baseline: flags.baseline,
|
|
190
229
|
// --dry-run must actually preview. This was previously passed only to the
|
|
191
230
|
// display object below, so a "preview" run wrote every file.
|
|
@@ -199,6 +238,15 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
199
238
|
// snapshot artifact), so sharedModelFile() defaults its `files` selection
|
|
200
239
|
// to exactly what this project itself declares.
|
|
201
240
|
sourceFiles: genCollection.ownFiles,
|
|
241
|
+
// The source DIRECTORIES, with each dependency artifact's, for a generator that loads
|
|
242
|
+
// the model with a loader of its own (the docs site).
|
|
243
|
+
sourceRoots: sourceRootsOf(genCollection),
|
|
244
|
+
// The physical schema `agent/schema.md` describes, built only if `agentDocsFile()` asks.
|
|
245
|
+
agentSchema: () => buildAgentSchemaInput(
|
|
246
|
+
metadata,
|
|
247
|
+
forgeConfig.dialect,
|
|
248
|
+
(m) => { const w = `[agent-docs] ${m}`; log.warn(w); warnings.push(w); },
|
|
249
|
+
),
|
|
202
250
|
// FR-043 §6 — the shipped-library selection. Always passed, including as `[]`:
|
|
203
251
|
// that is what tells the post-selection audit "this project opted into none",
|
|
204
252
|
// which is a different statement from a programmatic caller that never said.
|
|
@@ -210,20 +258,23 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
210
258
|
return 1;
|
|
211
259
|
}
|
|
212
260
|
|
|
213
|
-
for (const w of result.warnings) { log.warn(w); }
|
|
261
|
+
for (const w of result.warnings) { log.warn(w); warnings.push(w); }
|
|
214
262
|
|
|
215
263
|
// A first-run POINTER, not a warning: an empty selection is the designed state of a
|
|
216
264
|
// fresh `meta init`, so calling it a problem would make every new project start with
|
|
217
265
|
// one. It says what to do next and disappears the moment anything is wired.
|
|
218
266
|
if ((forgeConfig.generators?.length ?? 0) === 0) {
|
|
219
|
-
|
|
267
|
+
// stdout only in text format: a structured run carries the same pointer in the
|
|
268
|
+
// document's `help`, and a sentence in front of the document breaks `| jq`.
|
|
269
|
+
narrate(fmt,
|
|
220
270
|
"\nNothing is generated until you choose it — `generators: []` is what `meta init` " +
|
|
221
271
|
"scaffolds, by design.\n" +
|
|
222
|
-
" meta gen --list --probe the catalog
|
|
223
|
-
"would emit for YOUR model\n" +
|
|
224
|
-
" meta eject <name>...
|
|
225
|
-
"entry to wire, and what to install\n" +
|
|
226
|
-
"
|
|
272
|
+
" meta gen --list --probe the catalog: the import that runs each generator as " +
|
|
273
|
+
"shipped, and how many files it would emit for YOUR model\n" +
|
|
274
|
+
" meta eject <name>... copy one into your repo to change what it emits; it " +
|
|
275
|
+
"prints the import to swap in, the entry to wire, and what to install\n" +
|
|
276
|
+
"Documentation is chosen the same way: docsFile(), apiDocsFile() and the rest, with a " +
|
|
277
|
+
"`docs` target.",
|
|
227
278
|
);
|
|
228
279
|
}
|
|
229
280
|
|
|
@@ -241,7 +292,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
241
292
|
if (!cliConfig.dryRun) {
|
|
242
293
|
for (const w of runtimeBoundaryWarnings(
|
|
243
294
|
findRuntimeBoundaryCrossings(projectRoot, result.files.map((f) => f.path)),
|
|
244
|
-
)) log.warn(`warning: ${w}`);
|
|
295
|
+
)) { log.warn(`warning: ${w}`); warnings.push(w); }
|
|
245
296
|
}
|
|
246
297
|
|
|
247
298
|
const targetDirs = Array.from(new Set(
|
|
@@ -263,9 +314,9 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
263
314
|
metadata,
|
|
264
315
|
dialect: forgeConfig.dialect,
|
|
265
316
|
migrateDialect: migrateDefaults.dialect,
|
|
266
|
-
columnNamingStrategy: forgeConfig.columnNamingStrategy,
|
|
267
317
|
projectRoot,
|
|
268
318
|
migrateOutDir: migrateDefaults.outDir,
|
|
319
|
+
migrateFormat: migrateDefaults.format,
|
|
269
320
|
changedFiles: result.files.filter((f) => CHANGED_STATUSES.has(f.status)).map((f) => f.path),
|
|
270
321
|
});
|
|
271
322
|
|
|
@@ -275,7 +326,7 @@ export async function genCommand(args: string[], cwd: string, fmt: OutputFormat
|
|
|
275
326
|
dialect: forgeConfig.dialect,
|
|
276
327
|
dryRun: cliConfig.dryRun,
|
|
277
328
|
// Text mode already printed these to stderr above; the structured forms carry them.
|
|
278
|
-
warnings: fmt === "text" ? [] :
|
|
329
|
+
warnings: fmt === "text" ? [] : warnings,
|
|
279
330
|
antiPatterns,
|
|
280
331
|
generatorCount: forgeConfig.generators?.length ?? 0,
|
|
281
332
|
...(migrateAdvice !== undefined ? { migrateAdvice } : {}),
|
|
@@ -436,7 +487,7 @@ async function listCatalogCommand(
|
|
|
436
487
|
});
|
|
437
488
|
} catch (err) {
|
|
438
489
|
reportLoadError(log, "failed to load metadata", err);
|
|
439
|
-
return
|
|
490
|
+
return 1;
|
|
440
491
|
}
|
|
441
492
|
|
|
442
493
|
opts = {
|
|
@@ -451,9 +502,21 @@ async function listCatalogCommand(
|
|
|
451
502
|
// codegen config it was moved out of.
|
|
452
503
|
libraries: genCollection.libraries,
|
|
453
504
|
},
|
|
454
|
-
// The
|
|
455
|
-
// `
|
|
456
|
-
|
|
505
|
+
// The run inputs `meta gen` supplies below: `shared-model` cannot be counted without the
|
|
506
|
+
// collection's own files, `docs-site` without its source directories, and `agent-docs`
|
|
507
|
+
// leaves out its schema page without the schema. The schema's skip reasons are not
|
|
508
|
+
// printed: a count is what the probe reports.
|
|
509
|
+
probe: {
|
|
510
|
+
metadata,
|
|
511
|
+
scope: genCollection.inScope,
|
|
512
|
+
sourceFiles: genCollection.ownFiles,
|
|
513
|
+
sourceRoots: sourceRootsOf(genCollection),
|
|
514
|
+
agentSchema: () => buildAgentSchemaInput(
|
|
515
|
+
metadata,
|
|
516
|
+
forgeConfig.dialect,
|
|
517
|
+
() => {},
|
|
518
|
+
),
|
|
519
|
+
},
|
|
457
520
|
};
|
|
458
521
|
} else {
|
|
459
522
|
// No probe: still report `wired` / `owned` when a project happens to be here, since
|
|
@@ -483,3 +546,12 @@ async function listCatalogCommand(
|
|
|
483
546
|
else emitStructured(rows, fmt);
|
|
484
547
|
return 0;
|
|
485
548
|
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* The collection's metadata source directories, followed by the directory of each dependency's
|
|
552
|
+
* artifact: a loader that reads whole directories (the docs site's) needs both, or an
|
|
553
|
+
* `extends` onto an imported abstract would not resolve in it.
|
|
554
|
+
*/
|
|
555
|
+
export function sourceRootsOf(collection: Collection): string[] {
|
|
556
|
+
return [...collection.sourceRoots, ...new Set(collection.dependencies.map((d) => dirname(d.artifactPath)))];
|
|
557
|
+
}
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
-
import { parseArgs } from "node:util";
|
|
17
16
|
import { catalogEntry } from "../lib/catalog.js";
|
|
18
17
|
import { log } from "../lib/log.js";
|
|
18
|
+
import { parseCommandArgs } from "../lib/strict-args.js";
|
|
19
19
|
import { dependencyNotesForTemplate } from "./eject.js";
|
|
20
20
|
import {
|
|
21
21
|
CONFIG_FILE,
|
|
@@ -333,7 +333,7 @@ export async function generatorCommand(args: string[], cwd: string): Promise<num
|
|
|
333
333
|
let values: { scope?: string; force?: boolean; "no-wire"?: boolean };
|
|
334
334
|
let positionals: string[];
|
|
335
335
|
try {
|
|
336
|
-
({ values, positionals } =
|
|
336
|
+
({ values, positionals } = parseCommandArgs("generator", { args, options: GENERATOR_OPTIONS, strict: true, allowPositionals: true }));
|
|
337
337
|
} catch (err) {
|
|
338
338
|
log.error(describeError(err));
|
|
339
339
|
return 2;
|
package/src/commands/init.ts
CHANGED
|
@@ -11,13 +11,17 @@ import { assertKnownStackValues, resolveStack } from "../lib/detect-stack.js";
|
|
|
11
11
|
import { reportIgnoredScaffold } from "../lib/ignored-scaffold-check.js";
|
|
12
12
|
import { parseInitArgs } from "../lib/args.js";
|
|
13
13
|
import { log } from "../lib/log.js";
|
|
14
|
-
import { cliVersion } from "../lib/version.js";
|
|
14
|
+
import { cliVersion, isPrereleaseVersion } from "../lib/version.js";
|
|
15
|
+
import { currentContentHash } from "../lib/agent-context-staleness.js";
|
|
15
16
|
import { findWranglerConfig, parseWranglerConfig } from "@metaobjectsdev/migrate-ts";
|
|
16
17
|
import { DEFAULT_DOCS_DIR } from "@metaobjectsdev/codegen-ts";
|
|
17
18
|
import { describeError } from "../lib/error-text.js";
|
|
19
|
+
import { SUBTYPE_ROOT, TYPE_METADATA, TYPE_SUBTYPE_SEPARATOR } from "@metaobjectsdev/metadata/constants";
|
|
20
|
+
import { docsRefForVersion } from "@metaobjectsdev/metadata";
|
|
18
21
|
|
|
19
|
-
// ADR-0034 scaffold-and-own — `meta
|
|
20
|
-
// the consumer
|
|
22
|
+
// ADR-0034 scaffold-and-own — where `meta eject` and `meta generator new` put a generator
|
|
23
|
+
// the consumer OWNS. `meta init` scaffolds it empty; metaobjects.config.ts imports from it
|
|
24
|
+
// locally, or imports a stock generator from its package (Amendment 5).
|
|
21
25
|
const OWNED_GENERATORS_DIR = "codegen/generators";
|
|
22
26
|
|
|
23
27
|
|
|
@@ -31,13 +35,10 @@ const OWNED_GENERATORS_DIR = "codegen/generators";
|
|
|
31
35
|
// `meta gen --list` and by `meta eject routes`, to the adopter who actually chose it.
|
|
32
36
|
const SCAFFOLD_OUT_DIR = "src/generated";
|
|
33
37
|
|
|
38
|
+
// The canonical form of an empty root, byte-for-byte what `meta fmt` writes, so a
|
|
39
|
+
// freshly scaffolded project passes `meta fmt --check` (it used to fail it at once).
|
|
34
40
|
const META_COMMON_JSON = JSON.stringify(
|
|
35
|
-
{
|
|
36
|
-
metadata: {
|
|
37
|
-
package: "",
|
|
38
|
-
children: [] as unknown[],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
+
{ [`${TYPE_METADATA}${TYPE_SUBTYPE_SEPARATOR}${SUBTYPE_ROOT}`]: {} },
|
|
41
42
|
null,
|
|
42
43
|
2,
|
|
43
44
|
) + "\n";
|
|
@@ -107,33 +108,37 @@ export default defineConfig({
|
|
|
107
108
|
// would emit, so you can see what your metadata is already asking for. Group by
|
|
108
109
|
// \`layer\`: model / persistence / api / client / docs / capability.
|
|
109
110
|
//
|
|
110
|
-
//
|
|
111
|
+
// Every row carries the import line that runs its generator as shipped
|
|
112
|
+
// (\`wire.import\`). Add it at the top of this file:
|
|
113
|
+
//
|
|
114
|
+
// import { entityFile, queriesFile, barrel } from "@metaobjectsdev/codegen-ts/generators";
|
|
115
|
+
//
|
|
116
|
+
// then call each one in the array below: entityFile(), queriesFile(), barrel(). A
|
|
117
|
+
// generator's options are in its documentation. The row also lists what to install
|
|
118
|
+
// and the config keys the generator reads (\`dbImport\`, \`apiPrefix\`, \`extStyle\`, …).
|
|
119
|
+
// Add them here as you go.
|
|
120
|
+
//
|
|
121
|
+
// Need one to emit something different? Own it:
|
|
122
|
+
//
|
|
123
|
+
// meta eject entity
|
|
111
124
|
//
|
|
112
|
-
//
|
|
125
|
+
// copies that generator into ./codegen/generators/ — YOURS to edit — and prints the
|
|
126
|
+
// local import to swap in for the package one. \`meta gen\` then runs your copy.
|
|
113
127
|
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
128
|
+
// Documentation is chosen the same way: the \`docs\` layer of the catalog (docsFile(),
|
|
129
|
+
// apiDocsFile(), …) writes to a \`docs\` target you declare beside \`generators\`:
|
|
130
|
+
//
|
|
131
|
+
// targets: { docs: { outDir: "${DEFAULT_DOCS_DIR}" } },
|
|
132
|
+
//
|
|
133
|
+
// Nothing is generated that this file does not declare, documentation included.
|
|
118
134
|
//
|
|
119
135
|
// Need an output nothing in that catalog emits — OpenAPI, JSON Schema, a client, a
|
|
120
|
-
// service layer
|
|
136
|
+
// service layer? Write your own generator; that is the normal path:
|
|
121
137
|
//
|
|
122
138
|
// meta generator new openapi --scope model
|
|
123
139
|
//
|
|
124
140
|
// writes a working, commented generator into ./codegen/generators/ and adds it here.
|
|
125
141
|
generators: [],
|
|
126
|
-
|
|
127
|
-
docs: {
|
|
128
|
-
outDir: "${DEFAULT_DOCS_DIR}", // every surface lands here (run: meta docs).
|
|
129
|
-
// A SUB-directory on purpose: these pages are regenerated
|
|
130
|
-
// and overwritten, and docs/ itself is usually yours.
|
|
131
|
-
layout: "flat", // or "package" for multi-package models
|
|
132
|
-
// surfaces defaults to ["model", "api", "requirements", "agent"] — all four.
|
|
133
|
-
// Documentation is the one thing that IS on by default, because the requirements
|
|
134
|
-
// and agent surfaces emit ZERO files for a project with nothing to describe, and
|
|
135
|
-
// the always-on agent context points at the agent/ pages by name.
|
|
136
|
-
},
|
|
137
142
|
});
|
|
138
143
|
`;
|
|
139
144
|
}
|
|
@@ -150,14 +155,16 @@ Next steps:
|
|
|
150
155
|
1. Author entities under metaobjects/ (start from the scaffolded meta.common.json)
|
|
151
156
|
2. meta gen --list --probe # the catalog: every generator, grouped by layer, with
|
|
152
157
|
# how many files each would emit for YOUR model
|
|
153
|
-
3.
|
|
158
|
+
3. Wire the ones you want in metaobjects.config.ts: each catalog row prints the
|
|
159
|
+
import that runs it as shipped.
|
|
160
|
+
meta eject <name>... # need to change what one emits? copies it into
|
|
154
161
|
# codegen/generators/ (yours to edit) and prints the
|
|
155
|
-
# import
|
|
162
|
+
# import to swap in and what to install
|
|
156
163
|
meta generator new <name> # an output no reference emits? write your own — this
|
|
157
164
|
# scaffolds a working one and wires it
|
|
158
|
-
4. meta gen # generate from exactly what you wired
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
4. meta gen # generate from exactly what you wired, documentation
|
|
166
|
+
# included (the docs layer of the catalog)
|
|
167
|
+
5. Create your tables: meta migrate --from-db --db file:dev.sqlite --dialect sqlite --slug init --apply
|
|
161
168
|
A new database later (a fresh clone, CI, tests) replays the committed migrations:
|
|
162
169
|
meta migrate apply-pending --db file:other.sqlite
|
|
163
170
|
`;
|
|
@@ -205,6 +212,13 @@ export interface InitResult {
|
|
|
205
212
|
* just scaffolded over your work.
|
|
206
213
|
*/
|
|
207
214
|
refreshedDocsOnly?: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* True when the project was already initialized (`metaobjects/` or `.metaobjects/`
|
|
217
|
+
* exists) and the run was neither `--force` nor `--refresh-docs`: nothing was written.
|
|
218
|
+
* A re-run asks for a state that already holds, so it is a no-op that exits 0, not
|
|
219
|
+
* the error it used to be.
|
|
220
|
+
*/
|
|
221
|
+
alreadyInitialized?: boolean;
|
|
208
222
|
}
|
|
209
223
|
|
|
210
224
|
async function readManifest(cwd: string): Promise<Manifest | undefined> {
|
|
@@ -299,17 +313,31 @@ async function writeAgentContext(opts: InitOptions, result: InitResult): Promise
|
|
|
299
313
|
warnIfMonorepoSubdir(opts, result);
|
|
300
314
|
const prior = await readManifest(opts.cwd);
|
|
301
315
|
const stack = await stackForAgentContext(opts, prior);
|
|
302
|
-
let assembled = assemble({ contentRoot: resolveAgentContextRoot(), stack });
|
|
316
|
+
let assembled = assemble({ contentRoot: resolveAgentContextRoot(), stack, docsRef: docsRefForVersion(cliVersion()) });
|
|
303
317
|
if (opts.noSkills) assembled = assembled.filter((f) => !f.path.startsWith(".claude/skills/"));
|
|
304
318
|
|
|
319
|
+
const generatedBy = cliVersion();
|
|
320
|
+
const contentHash = currentContentHash().currentContentHash;
|
|
305
321
|
const decision = planScaffold({
|
|
306
322
|
stack, assembled, prior,
|
|
307
323
|
readCurrent: (rel) => {
|
|
308
324
|
const abs = join(opts.cwd, rel);
|
|
309
325
|
return existsSyncWrap(abs) ? readFileSyncWrap(abs, "utf8") : undefined;
|
|
310
326
|
},
|
|
311
|
-
generatedBy
|
|
327
|
+
generatedBy,
|
|
328
|
+
...(contentHash !== undefined && { contentHash }),
|
|
312
329
|
});
|
|
330
|
+
// A pre-release CLI stamps its own pre-release version into the committed
|
|
331
|
+
// .metaobjects/.agent-context.json ("generatedBy") and the skills/docs it scaffolds may
|
|
332
|
+
// differ from the eventual release's. tools/prerelease/detect-prerelease-pins.sh now
|
|
333
|
+
// flags that on the tree side; this is the other half — say so at the moment it is
|
|
334
|
+
// written, not only when someone later runs the detector.
|
|
335
|
+
if (isPrereleaseVersion(generatedBy)) {
|
|
336
|
+
result.warnings.push(
|
|
337
|
+
`agent context generated by pre-release CLI ${generatedBy} — refresh it again with ` +
|
|
338
|
+
`the release CLI before merging ('meta init --refresh-docs --docs-only').`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
313
341
|
|
|
314
342
|
// --force: overwrite hand-edited docs in place rather than parking the fresh copy
|
|
315
343
|
// at <path>.new (issue #163 — a forced (re)scaffold means "I mean it"; applies to
|
|
@@ -585,9 +613,10 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
585
613
|
}
|
|
586
614
|
|
|
587
615
|
if (exists && !opts.force && !opts.refreshDocs) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
616
|
+
if (metaobjectsExists) result.preserved.push(DEFAULT_METADATA_DIR);
|
|
617
|
+
if (agentDirExists) result.preserved.push(DEFAULT_METAOBJECTS_DIR);
|
|
618
|
+
result.alreadyInitialized = true;
|
|
619
|
+
return result;
|
|
591
620
|
}
|
|
592
621
|
|
|
593
622
|
const dirs = [
|
|
@@ -603,7 +632,10 @@ export async function init(opts: InitOptions): Promise<InitResult> {
|
|
|
603
632
|
".metaobjects/config.json",
|
|
604
633
|
".metaobjects/.gitignore",
|
|
605
634
|
);
|
|
606
|
-
|
|
635
|
+
// The agent context forecasts itself: writeAgentContext is dry-run aware and
|
|
636
|
+
// reports exactly the paths a real run writes — the root CLAUDE.md/AGENTS.md wiring
|
|
637
|
+
// included, which a hand-kept list here used to leave out.
|
|
638
|
+
await writeAgentContext(opts, result);
|
|
607
639
|
result.created.push(OWNED_GENERATORS_DIR, CODEGEN_TSCONFIG_REL);
|
|
608
640
|
result.created.push("metaobjects.config.ts", ".gitignore");
|
|
609
641
|
return result;
|
|
@@ -884,9 +916,19 @@ export async function initCommand(args: string[], cwd: string): Promise<number>
|
|
|
884
916
|
configOnly: flags.configOnly,
|
|
885
917
|
});
|
|
886
918
|
|
|
919
|
+
if (result.alreadyInitialized) {
|
|
920
|
+
log.info(
|
|
921
|
+
`meta init: already initialized (${result.preserved.join(" and ")} exist) — nothing written (no-op).\n` +
|
|
922
|
+
" meta init --refresh-docs update the agent docs after a CLI upgrade\n" +
|
|
923
|
+
" meta init --force re-scaffold the project files (your metadata is preserved)",
|
|
924
|
+
);
|
|
925
|
+
return 0;
|
|
926
|
+
}
|
|
927
|
+
|
|
887
928
|
if (flags.printOnly) {
|
|
888
929
|
log.info("Would create:");
|
|
889
930
|
for (const path of result.created) log.info(` ${path}`);
|
|
931
|
+
for (const w of result.warnings) log.info(` ${w}`);
|
|
890
932
|
return 0;
|
|
891
933
|
}
|
|
892
934
|
|