@metaobjectsdev/cli 0.24.3 → 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 +182 -16
- 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 +26 -5
- 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 +187 -15
- package/src/commands/verify.ts +25 -5
- 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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Every ejectable name, in registry order (stable — matches `meta eject --list`). */
|
|
2
|
+
export declare function ejectableNames(): string[];
|
|
3
|
+
export interface EjectOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
name: string;
|
|
6
|
+
/** Overwrite an already-ejected file. Without it, eject NEVER clobbers — matching
|
|
7
|
+
* `writeOwnedGenerators`'s unconditional preserve-if-present rule in init.ts. */
|
|
8
|
+
force?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface EjectResult {
|
|
11
|
+
path: string;
|
|
12
|
+
importLine: string;
|
|
13
|
+
/** The symbol the template exports — the binding to REPLACE in the config. */
|
|
14
|
+
exportName: string;
|
|
15
|
+
/** The package the generator currently comes from, i.e. the import to remove. */
|
|
16
|
+
packageName: string;
|
|
17
|
+
/** Advisory lines about packages the ejected file imports but the project lacks. */
|
|
18
|
+
dependencyNotes: string[];
|
|
19
|
+
status: "created" | "preserved";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An ejected file is ordinary source in the adopter's repo, so its imports must be
|
|
23
|
+
* declared dependencies or their `tsc` reports TS2307 on the file we just told them
|
|
24
|
+
* they own — and under a strict (pnpm/npm) node_modules layout `meta gen` cannot
|
|
25
|
+
* resolve it either. `meta init` already calls this out by ADDING the two packages its
|
|
26
|
+
* four scaffolded generators need; the on-demand templates import two more
|
|
27
|
+
* (codegen-ts-react, codegen-ts-tanstack) that nothing declares.
|
|
28
|
+
*
|
|
29
|
+
* This REPORTS rather than edits: init is a scaffolder writing a whole project and has
|
|
30
|
+
* a manifest in hand, while eject copies one file into a repo whose dependency policy
|
|
31
|
+
* (workspace protocol, catalog, pinned ranges) is the adopter's. Naming the exact
|
|
32
|
+
* missing package and the version to match is the useful half; silently rewriting
|
|
33
|
+
* someone's manifest is not.
|
|
34
|
+
*/
|
|
35
|
+
export declare function dependencyNotesForTemplate(cwd: string, templateSource: string): Promise<string[]>;
|
|
36
|
+
export declare function ejectGenerator(opts: EjectOptions): Promise<EjectResult>;
|
|
37
|
+
export declare function ejectCommand(args: string[], cwd: string): Promise<number>;
|
|
38
|
+
//# sourceMappingURL=eject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eject.d.ts","sourceRoot":"","sources":["../../../src/commands/eject.ts"],"names":[],"mappings":"AA4DA,sFAAsF;AACtF,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AA2CD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb;sFACkF;IAClF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,oFAAoF;IACpF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;CACjC;AAoBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBvG;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8B7E;AAeD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0D/E"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// FR-040 §4.2(a) — `meta eject <generator>` takes ownership of any reference-template
|
|
2
|
+
// generator, in any package, at any time after `meta init`. ADR-0034 scaffold-and-own
|
|
3
|
+
// has `init` copy four of them eagerly (entity, queries, routes, barrel); this is the
|
|
4
|
+
// SAME copy operation, generalised to every ejectable name and callable on demand — for
|
|
5
|
+
// a generator you skipped at init time, or one a package gained since.
|
|
6
|
+
import { mkdir, writeFile, stat, readFile } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { cliVersion } from "../lib/version.js";
|
|
9
|
+
import * as coreTpl from "@metaobjectsdev/codegen-ts";
|
|
10
|
+
import * as reactTpl from "@metaobjectsdev/codegen-ts-react";
|
|
11
|
+
import * as tanstackTpl from "@metaobjectsdev/codegen-ts-tanstack";
|
|
12
|
+
import { parseEjectArgs } from "../lib/args.js";
|
|
13
|
+
import { log } from "../lib/log.js";
|
|
14
|
+
import { declaredDependencyNames, readPackageManifest } from "../lib/package-manifest.js";
|
|
15
|
+
// Mirrors `OWNED_GENERATORS_DIR` in init.ts's `writeOwnedGenerators` — same directory,
|
|
16
|
+
// same never-clobber-without-consent contract. Kept as its own local constant rather
|
|
17
|
+
// than shared: eject is a standalone operation on ANY name, not a byproduct of init,
|
|
18
|
+
// and the two call sites have no other state in common worth coupling over one string.
|
|
19
|
+
const OWNED_GENERATORS_DIR = "codegen/generators";
|
|
20
|
+
// One registry, three packages — a package that gains templates later registers itself
|
|
21
|
+
// here and `meta eject` picks it up with no other change.
|
|
22
|
+
//
|
|
23
|
+
// Each entry exposes its reference ROOT rather than a read function. The packages' own
|
|
24
|
+
// `readReferenceTemplate` narrows its parameter to a literal union, so calling it with
|
|
25
|
+
// a CLI-supplied `string` used to need a generic `asserts name is N` helper — ~20 lines
|
|
26
|
+
// to re-establish, for the compiler, a fact `resolveSource` has ALREADY established at
|
|
27
|
+
// runtime by selecting this entry via `names.includes(name)`. Reading from the root
|
|
28
|
+
// deletes that machinery without weakening anything: membership is still checked, once,
|
|
29
|
+
// where the untrusted value enters.
|
|
30
|
+
const SOURCES = [
|
|
31
|
+
{
|
|
32
|
+
packageName: "@metaobjectsdev/codegen-ts",
|
|
33
|
+
names: coreTpl.REFERENCE_GENERATOR_NAMES,
|
|
34
|
+
root: coreTpl.resolveReferenceRoot,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
packageName: "@metaobjectsdev/codegen-ts-react",
|
|
38
|
+
names: reactTpl.REFERENCE_GENERATOR_NAMES,
|
|
39
|
+
root: reactTpl.resolveReferenceRoot,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
packageName: "@metaobjectsdev/codegen-ts-tanstack",
|
|
43
|
+
names: tanstackTpl.REFERENCE_GENERATOR_NAMES,
|
|
44
|
+
root: tanstackTpl.resolveReferenceRoot,
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
function resolveSource(name) {
|
|
48
|
+
return SOURCES.find((s) => s.names.includes(name));
|
|
49
|
+
}
|
|
50
|
+
/** Every ejectable name, in registry order (stable — matches `meta eject --list`). */
|
|
51
|
+
export function ejectableNames() {
|
|
52
|
+
return SOURCES.flatMap((s) => s.names);
|
|
53
|
+
}
|
|
54
|
+
// Every reference template's header documents its own paste-ready import line, e.g.
|
|
55
|
+
// codegen-ts/src/reference/entity.ts:
|
|
56
|
+
// // Then import it LOCALLY in metaobjects.config.ts:
|
|
57
|
+
// // import { entityFile } from "./codegen/generators/entity.js";
|
|
58
|
+
// Extracting it here — rather than re-deriving an export symbol from the file name —
|
|
59
|
+
// means eject can never drift from what the template itself already tells a human to
|
|
60
|
+
// paste, and needs no per-name export-symbol map: a generator's exported symbol does
|
|
61
|
+
// NOT follow its file name (`hooks.ts` exports `tanstackQuery`, `routes-hono.ts`
|
|
62
|
+
// exports `routesFileHono`, `grid.ts` exports `tanstackGrid`).
|
|
63
|
+
const HEADER_IMPORT_RE = /^\/\/\s+(import \{ \w+ \} from "\.\/codegen\/generators\/[\w.-]+\.js";)\s*$/m;
|
|
64
|
+
/** The bound symbol out of the already-validated import line — same single source of
|
|
65
|
+
* truth as the line itself, so the "replace this binding" message can never name a
|
|
66
|
+
* symbol the template does not actually export. */
|
|
67
|
+
function extractExportName(importLine, name) {
|
|
68
|
+
const match = /^import \{ (\w+) \}/.exec(importLine);
|
|
69
|
+
if (!match?.[1]) {
|
|
70
|
+
throw new Error(`reference template "${name}" has an unparseable import line: ${importLine}`);
|
|
71
|
+
}
|
|
72
|
+
return match[1];
|
|
73
|
+
}
|
|
74
|
+
function extractImportLine(templateSource, name) {
|
|
75
|
+
const match = HEADER_IMPORT_RE.exec(templateSource);
|
|
76
|
+
if (!match?.[1]) {
|
|
77
|
+
throw new Error(`reference template "${name}" has no documented "// import { ... }" header line — ` +
|
|
78
|
+
"cannot report the import line to paste.");
|
|
79
|
+
}
|
|
80
|
+
return match[1];
|
|
81
|
+
}
|
|
82
|
+
async function fileExists(p) {
|
|
83
|
+
try {
|
|
84
|
+
return (await stat(p)).isFile();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The `@metaobjectsdev/*` packages an ejected template imports, read from the file
|
|
91
|
+
* itself rather than from a per-name table — the template is the only thing that
|
|
92
|
+
* knows, and a table would drift from it the moment a template gains an import. */
|
|
93
|
+
function requiredPackages(templateSource) {
|
|
94
|
+
const found = new Set();
|
|
95
|
+
// The optional trailing group matches a SUBPATH and is deliberately not captured: what
|
|
96
|
+
// has to be installed is the package, and `@metaobjectsdev/metadata/constants` is a
|
|
97
|
+
// real, documented subpath this codebase already uses (the browser-safe pure-constants
|
|
98
|
+
// entry). Requiring the closing quote straight after the package name — as this did —
|
|
99
|
+
// made any such import match nothing at all, so a template gaining one would get no
|
|
100
|
+
// note. That is the drift this function exists to prevent, reappearing inside the
|
|
101
|
+
// function itself.
|
|
102
|
+
for (const m of templateSource.matchAll(/from\s+"(@metaobjectsdev\/[\w-]+)(?:\/[\w./-]+)?"/g)) {
|
|
103
|
+
if (m[1] !== undefined)
|
|
104
|
+
found.add(m[1]);
|
|
105
|
+
}
|
|
106
|
+
return [...found].sort();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* An ejected file is ordinary source in the adopter's repo, so its imports must be
|
|
110
|
+
* declared dependencies or their `tsc` reports TS2307 on the file we just told them
|
|
111
|
+
* they own — and under a strict (pnpm/npm) node_modules layout `meta gen` cannot
|
|
112
|
+
* resolve it either. `meta init` already calls this out by ADDING the two packages its
|
|
113
|
+
* four scaffolded generators need; the on-demand templates import two more
|
|
114
|
+
* (codegen-ts-react, codegen-ts-tanstack) that nothing declares.
|
|
115
|
+
*
|
|
116
|
+
* This REPORTS rather than edits: init is a scaffolder writing a whole project and has
|
|
117
|
+
* a manifest in hand, while eject copies one file into a repo whose dependency policy
|
|
118
|
+
* (workspace protocol, catalog, pinned ranges) is the adopter's. Naming the exact
|
|
119
|
+
* missing package and the version to match is the useful half; silently rewriting
|
|
120
|
+
* someone's manifest is not.
|
|
121
|
+
*/
|
|
122
|
+
export async function dependencyNotesForTemplate(cwd, templateSource) {
|
|
123
|
+
const required = requiredPackages(templateSource);
|
|
124
|
+
if (required.length === 0)
|
|
125
|
+
return [];
|
|
126
|
+
const pkg = readPackageManifest(cwd);
|
|
127
|
+
if (pkg === undefined) {
|
|
128
|
+
// No readable manifest — say what the file needs and let the adopter place it.
|
|
129
|
+
return [`This file imports: ${required.join(", ")}. Make sure each is installed.`];
|
|
130
|
+
}
|
|
131
|
+
const declared = declaredDependencyNames(pkg);
|
|
132
|
+
const missing = required.filter((p) => !declared.has(p));
|
|
133
|
+
if (missing.length === 0)
|
|
134
|
+
return [];
|
|
135
|
+
return [
|
|
136
|
+
`The ejected file imports ${missing.join(", ")}, which your package.json does not ` +
|
|
137
|
+
"declare — your typecheck will report TS2307 until it does. Install with:",
|
|
138
|
+
` npm i -D ${missing.map((p) => `${p}@^${cliVersion()}`).join(" ")}`,
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
export async function ejectGenerator(opts) {
|
|
142
|
+
const source = resolveSource(opts.name);
|
|
143
|
+
if (source === undefined) {
|
|
144
|
+
throw new Error(`unknown generator "${opts.name}". Ejectable generators: ${ejectableNames().join(", ")}. ` +
|
|
145
|
+
"Run `meta eject --list` to see them grouped by package.");
|
|
146
|
+
}
|
|
147
|
+
const templateSource = await readFile(join(source.root(), `${opts.name}.ts`), "utf8");
|
|
148
|
+
const importLine = extractImportLine(templateSource, opts.name);
|
|
149
|
+
const exportName = extractExportName(importLine, opts.name);
|
|
150
|
+
const rel = `${OWNED_GENERATORS_DIR}/${opts.name}.ts`;
|
|
151
|
+
const abs = join(opts.cwd, rel);
|
|
152
|
+
const notes = await dependencyNotesForTemplate(opts.cwd, templateSource);
|
|
153
|
+
const common = {
|
|
154
|
+
path: rel,
|
|
155
|
+
importLine,
|
|
156
|
+
exportName,
|
|
157
|
+
packageName: source.packageName,
|
|
158
|
+
dependencyNotes: notes,
|
|
159
|
+
};
|
|
160
|
+
if (!opts.force && (await fileExists(abs))) {
|
|
161
|
+
return { ...common, status: "preserved" };
|
|
162
|
+
}
|
|
163
|
+
await mkdir(join(opts.cwd, OWNED_GENERATORS_DIR), { recursive: true });
|
|
164
|
+
await writeFile(abs, templateSource, "utf8");
|
|
165
|
+
return { ...common, status: "created" };
|
|
166
|
+
}
|
|
167
|
+
function listOutput() {
|
|
168
|
+
const lines = [];
|
|
169
|
+
lines.push("Ejectable generators (copy any of these into codegen/generators/ and own it):");
|
|
170
|
+
lines.push("");
|
|
171
|
+
for (const source of SOURCES) {
|
|
172
|
+
lines.push(`${source.packageName}:`);
|
|
173
|
+
lines.push(` ${source.names.join(", ")}`);
|
|
174
|
+
lines.push("");
|
|
175
|
+
}
|
|
176
|
+
lines.push("Run: meta eject <name>");
|
|
177
|
+
return lines.join("\n");
|
|
178
|
+
}
|
|
179
|
+
export async function ejectCommand(args, cwd) {
|
|
180
|
+
let flags;
|
|
181
|
+
try {
|
|
182
|
+
flags = parseEjectArgs(args);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
log.error(err.message);
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
188
|
+
if (flags.list) {
|
|
189
|
+
log.info(listOutput());
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
if (flags.name === undefined) {
|
|
193
|
+
log.error("meta eject requires a generator name, or --list to see what's ejectable.");
|
|
194
|
+
return 2;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const result = await ejectGenerator({ cwd, name: flags.name, force: flags.force });
|
|
198
|
+
if (result.status === "preserved") {
|
|
199
|
+
log.info(`${result.path} already exists — left untouched (pass --force to overwrite).`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
log.info(`Ejected "${flags.name}" -> ${result.path}. You own it now (ADR-0034 scaffold-and-own).`);
|
|
203
|
+
}
|
|
204
|
+
// REPLACE, never "paste". A generator reaches `generators: [...]` under ONE binding,
|
|
205
|
+
// so a reader told to "paste" gets a duplicate identifier at best — and at worst
|
|
206
|
+
// deletes nothing, keeps `formFile()` in the array bound to the PACKAGE import, and
|
|
207
|
+
// silently runs the packaged generator while editing the ejected file. That failure
|
|
208
|
+
// is invisible and is the exact one ejecting exists to prevent.
|
|
209
|
+
//
|
|
210
|
+
// But eject reads no config, so it cannot know WHICH of the three states this project
|
|
211
|
+
// is in, and stating one of them as fact is wrong in the other two — including for the
|
|
212
|
+
// four `meta init` scaffolds, whose config already imports from ./codegen/generators/,
|
|
213
|
+
// which is precisely the `meta eject <name> --force` re-sync case. Name the goal, then
|
|
214
|
+
// the three branches; the reader knows which one they are looking at.
|
|
215
|
+
log.info(`In metaobjects.config.ts, "${result.exportName}" must resolve to this file:`);
|
|
216
|
+
log.info(` ${result.importLine}`);
|
|
217
|
+
log.info(` - If it is imported from "${result.packageName}", REPLACE that import with the ` +
|
|
218
|
+
"line above. Adding a second one leaves `generators` bound to the PACKAGED " +
|
|
219
|
+
"generator, and your edits to this file do nothing.");
|
|
220
|
+
log.info(" - If it is already imported from ./codegen/generators/ (what `meta init` " +
|
|
221
|
+
"scaffolds), it points here already — nothing to change.");
|
|
222
|
+
log.info(` - If ${result.exportName}() is not in \`generators\` yet, add the import above ` +
|
|
223
|
+
"AND the entry.");
|
|
224
|
+
for (const line of result.dependencyNotes)
|
|
225
|
+
log.info(line);
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
log.error(err.message);
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=eject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eject.js","sourceRoot":"","sources":["../../../src/commands/eject.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,16 @@ export interface InitResult {
|
|
|
26
26
|
warnings: string[];
|
|
27
27
|
}
|
|
28
28
|
export declare function init(opts: InitOptions): Promise<InitResult>;
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The post-init message.
|
|
31
|
+
*
|
|
32
|
+
* @param dbStubWritten whether THIS run wrote {@link DB_STUB_REL_PATH}. Required rather
|
|
33
|
+
* than defaulted, because a default would silently restore the bug this parameter
|
|
34
|
+
* exists to close: the note used to be part of one static string and so claimed the
|
|
35
|
+
* stub on every run, including the `meta init --force` in a project keeping its own
|
|
36
|
+
* `metaobjects.config.ts`, where the stub is deliberately not written. Callers pass
|
|
37
|
+
* `result.created.includes(DB_STUB_REL_PATH)` — the same list the message describes.
|
|
38
|
+
*/
|
|
39
|
+
export declare function nextStepsBlock(dbStubWritten: boolean): string;
|
|
30
40
|
export declare function initCommand(args: string[], cwd: string): Promise<number>;
|
|
31
41
|
//# sourceMappingURL=init.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"AA8NA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4KAA4K;IAC5K,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAyQD,wBAAsB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA8KjE;AA0KD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,CAE7D;AAoBD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuD9E"}
|
|
@@ -8,11 +8,30 @@ import { resolveStack } from "../lib/detect-stack.js";
|
|
|
8
8
|
import { parseInitArgs } from "../lib/args.js";
|
|
9
9
|
import { log } from "../lib/log.js";
|
|
10
10
|
import { cliVersion } from "../lib/version.js";
|
|
11
|
+
import { declaredDependencyNames } from "../lib/package-manifest.js";
|
|
11
12
|
import { findWranglerConfig, parseWranglerConfig } from "@metaobjectsdev/migrate-ts";
|
|
12
|
-
import { readReferenceTemplate
|
|
13
|
+
import { readReferenceTemplate } from "@metaobjectsdev/codegen-ts";
|
|
13
14
|
// ADR-0034 scaffold-and-own — `meta init` copies the codegen reference templates into
|
|
14
15
|
// the consumer's repo so they OWN them; metaobjects.config.ts imports them locally.
|
|
15
16
|
const OWNED_GENERATORS_DIR = "codegen/generators";
|
|
17
|
+
// The FOUR reference generators `meta init` copies EAGERLY — deliberately an explicit
|
|
18
|
+
// literal, not derived from @metaobjectsdev/codegen-ts's REFERENCE_GENERATOR_NAMES (the
|
|
19
|
+
// full list of everything `meta eject` can copy). Looping over that array unconditionally
|
|
20
|
+
// used to mean init scaffolded whatever it contained: when a later task registered
|
|
21
|
+
// "routes-hono" there, init started writing an unwired Hono generator nothing in the
|
|
22
|
+
// scaffolded config imports, on every fresh project, silently. This constant is the
|
|
23
|
+
// scaffolded metaobjects.config.ts's import list (buildMetaobjectsConfigBody below) made
|
|
24
|
+
// explicit and checkable — anything else is eject-on-demand via `meta eject <name>`, which
|
|
25
|
+
// exists exactly so eager copying isn't the only way to take ownership of a template.
|
|
26
|
+
const SCAFFOLDED_GENERATOR_NAMES = ["entity", "queries", "routes", "barrel"];
|
|
27
|
+
// The scaffolded config's outDir + dbImport, as named constants so the throwing-stub
|
|
28
|
+
// path below is DERIVED from the same values the config template embeds rather than
|
|
29
|
+
// duplicated as a second literal that could drift from it.
|
|
30
|
+
const SCAFFOLD_OUT_DIR = "src/generated";
|
|
31
|
+
const SCAFFOLD_DB_IMPORT = "../db";
|
|
32
|
+
// "src/generated" + "../db" -> "src/db" -> "src/db.ts" (dbImport resolves relative
|
|
33
|
+
// to outDir, same as the module specifier a generated route file emits).
|
|
34
|
+
const DB_STUB_REL_PATH = `${join(SCAFFOLD_OUT_DIR, SCAFFOLD_DB_IMPORT)}.ts`;
|
|
16
35
|
const META_COMMON_JSON = JSON.stringify({
|
|
17
36
|
metadata: {
|
|
18
37
|
package: "",
|
|
@@ -75,9 +94,18 @@ import { routesFile } from "./codegen/generators/routes.js";
|
|
|
75
94
|
import { barrel } from "./codegen/generators/barrel.js";
|
|
76
95
|
|
|
77
96
|
export default defineConfig({
|
|
78
|
-
outDir: "
|
|
79
|
-
extStyle: "js", // ".js"-extensioned relative imports —
|
|
80
|
-
|
|
97
|
+
outDir: "${SCAFFOLD_OUT_DIR}",
|
|
98
|
+
extStyle: "js", // ".js"-extensioned relative imports — correct for Node ESM and \`tsc\` with
|
|
99
|
+
// nodenext, which is what a fresh project has. BUNDLERS DISAGREE: this fails
|
|
100
|
+
// outright under Turbopack (even between two generated files, so the whole
|
|
101
|
+
// generated tree goes unresolvable), while Vite and esbuild accept it and
|
|
102
|
+
// webpack needs \`resolve.extensionAlias\`. If a generated import fails to
|
|
103
|
+
// resolve, set "none" and retest — do not assume this line covers your bundler.
|
|
104
|
+
dbImport: "${SCAFFOLD_DB_IMPORT}", // routesFile() below emits \`import { db } from …\` — meta init
|
|
105
|
+
// scaffolded ${DB_STUB_REL_PATH} as a THROWING STUB (types clean, no
|
|
106
|
+
// driver chosen) so meta gen and tsc pass; replace it with your real
|
|
107
|
+
// Drizzle connection before running the app.
|
|
108
|
+
// (queriesFile() takes db as a parameter and never reads this.)
|
|
81
109
|
dialect: "${dialect}",
|
|
82
110
|
apiPrefix: "", // set to "/api" if your routes mount under /api
|
|
83
111
|
generators: [
|
|
@@ -94,10 +122,73 @@ export default defineConfig({
|
|
|
94
122
|
});
|
|
95
123
|
`;
|
|
96
124
|
}
|
|
97
|
-
|
|
125
|
+
// The throwing-stub scaffolded at `dbImport`'s resolved path (DB_STUB_REL_PATH,
|
|
126
|
+
// "src/db.ts" by default). It exists so `meta gen` and a fresh project's FIRST
|
|
127
|
+
// `tsc` both succeed with no driver chosen and no dependency added — deliberately
|
|
128
|
+
// NOT a real connection. Every generated route only ever passes `db` straight
|
|
129
|
+
// through to `mountCrudRoutes(...)`; nothing reads a property off it at import
|
|
130
|
+
// time, so a value typed `unknown` (not `any`) satisfies every call site while
|
|
131
|
+
// making a genuine runtime use (mountCrudRoutes calling `db.select()` etc.) throw
|
|
132
|
+
// immediately with an actionable message instead of failing to resolve at all.
|
|
133
|
+
// Built as an array of plain single-quoted lines (not a template literal) so the
|
|
134
|
+
// backticks and quotes inside the comment/message need no escaping.
|
|
135
|
+
const DB_STUB_BODY = [
|
|
136
|
+
"// `meta init` scaffolded this file because the generated Fastify routes",
|
|
137
|
+
'// `import { db } from "../db.js"` (see `dbImport` in metaobjects.config.ts) —',
|
|
138
|
+
"// a module that has to exist for `meta gen` and `tsc` to succeed. MetaObjects",
|
|
139
|
+
"// cannot fill it in for real without choosing a database driver on your",
|
|
140
|
+
"// behalf (better-sqlite3 vs @libsql/client vs pg vs postgres.js) and adding a",
|
|
141
|
+
"// dependency you may not want, so this is a STUB, not a connection.",
|
|
142
|
+
"//",
|
|
143
|
+
"// It type-checks and satisfies every generated import, but throws the first",
|
|
144
|
+
"// time anything actually touches `db` at runtime. Replace the export below",
|
|
145
|
+
"// with your real Drizzle connection, e.g.:",
|
|
146
|
+
"//",
|
|
147
|
+
'// import { drizzle } from "drizzle-orm/better-sqlite3";',
|
|
148
|
+
'// import Database from "better-sqlite3";',
|
|
149
|
+
'// export const db = drizzle(new Database("dev.sqlite"));',
|
|
150
|
+
"//",
|
|
151
|
+
"// (swap the driver import for your dialect — see",
|
|
152
|
+
"// https://github.com/metaobjectsdev/metaobjects/blob/main/docs/recipes/wiring-generated-queries.md",
|
|
153
|
+
"// for SQLite/libsql, Cloudflare D1, Postgres and multi-tenant setups.)",
|
|
154
|
+
"",
|
|
155
|
+
"const UNWIRED_MESSAGE =",
|
|
156
|
+
' "src/db.ts is still the scaffolded stub meta init wrote — it cannot choose " +',
|
|
157
|
+
" \"a database driver for you. Replace 'export const db = ...' below with \" +",
|
|
158
|
+
' "your real Drizzle connection, e.g.:\\n\\n" +',
|
|
159
|
+
" \" import { drizzle } from 'drizzle-orm/better-sqlite3';\\n\" +",
|
|
160
|
+
" \" import Database from 'better-sqlite3';\\n\" +",
|
|
161
|
+
" \" export const db = drizzle(new Database('dev.sqlite'));\\n\";",
|
|
162
|
+
"",
|
|
163
|
+
"function unwired(): never {",
|
|
164
|
+
" throw new Error(UNWIRED_MESSAGE);",
|
|
165
|
+
"}",
|
|
166
|
+
"",
|
|
167
|
+
"/**",
|
|
168
|
+
" * Stand-in for your real Drizzle database connection. Generated code only",
|
|
169
|
+
" * ever passes `db` straight through to `mountCrudRoutes(...)` — it never",
|
|
170
|
+
" * reads a property off it at import time — so this typechecks everywhere",
|
|
171
|
+
" * `db` is used, and throws the message above the first time anything really",
|
|
172
|
+
" * touches it.",
|
|
173
|
+
" */",
|
|
174
|
+
"export const db: unknown = new Proxy({}, { get: unwired });",
|
|
175
|
+
"",
|
|
176
|
+
].join("\n");
|
|
177
|
+
// Printed only when the stub was ACTUALLY written this run. It is gated on having just
|
|
178
|
+
// written the scaffolded config (see the db-stub block in `init`), so a re-run in a
|
|
179
|
+
// project that keeps its own config writes nothing — and a block claiming otherwise is
|
|
180
|
+
// the same "asserting things about its own scaffold that aren't true" defect the rest of
|
|
181
|
+
// this file was corrected for.
|
|
182
|
+
const DB_STUB_NOTE = `Also scaffolded ${DB_STUB_REL_PATH}: a THROWING STUB standing in for your database
|
|
183
|
+
connection, so the generated routes' \`db\` import resolves and the first tsc is clean.
|
|
184
|
+
Replace it with a real connection before running the app — until you do, the first
|
|
185
|
+
request that touches \`db\` throws with instructions.
|
|
186
|
+
`;
|
|
187
|
+
const SCAFFOLD_SUMMARY = `
|
|
98
188
|
Initialized metaobjects/ + .metaobjects/ + metaobjects.config.ts
|
|
99
189
|
Codegen generators copied to codegen/generators/ — they're YOURS to edit (ADR-0034 scaffold-and-own).
|
|
100
|
-
|
|
190
|
+
`;
|
|
191
|
+
const NEXT_STEPS = `
|
|
101
192
|
Next steps:
|
|
102
193
|
0. Everything here is ESM — package.json needs "type": "module" (init sets it
|
|
103
194
|
unless the project has CommonJS sources; without it the first tsc fails).
|
|
@@ -279,11 +370,16 @@ async function wireRootMemory(cwd, result, dryRun = false) {
|
|
|
279
370
|
* `codegen/generators/<name>.ts` so they own them. Each file is written only if absent,
|
|
280
371
|
* so a re-run with --force never clobbers a hand-edited generator. The scaffolded
|
|
281
372
|
* metaobjects.config.ts imports these local copies (not the package `/generators` export).
|
|
373
|
+
*
|
|
374
|
+
* Copies SCAFFOLDED_GENERATOR_NAMES only — the four the scaffolded config actually
|
|
375
|
+
* wires — not every name @metaobjectsdev/codegen-ts happens to register. Anything else
|
|
376
|
+
* (routes-hono, and any UI-tier template from codegen-ts-react/-tanstack) is reached with
|
|
377
|
+
* `meta eject <name>`, not by eager copying.
|
|
282
378
|
*/
|
|
283
379
|
async function writeOwnedGenerators(opts, result) {
|
|
284
380
|
const dir = join(opts.cwd, OWNED_GENERATORS_DIR);
|
|
285
381
|
await mkdir(dir, { recursive: true });
|
|
286
|
-
for (const name of
|
|
382
|
+
for (const name of SCAFFOLDED_GENERATOR_NAMES) {
|
|
287
383
|
const rel = `${OWNED_GENERATORS_DIR}/${name}.ts`;
|
|
288
384
|
const abs = join(dir, `${name}.ts`);
|
|
289
385
|
if (await fileExists(abs)) {
|
|
@@ -405,9 +501,9 @@ export async function init(opts) {
|
|
|
405
501
|
result.created.push(d);
|
|
406
502
|
result.created.push("metaobjects/meta.common.json", ".metaobjects/config.json", ".metaobjects/.gitignore", `.metaobjects/${PACKAGE_MANIFEST_FILE}`);
|
|
407
503
|
result.created.push(".metaobjects/AGENTS.md", ".metaobjects/CLAUDE.md", ".claude/skills/metaobjects-*", AGENT_CONTEXT_MANIFEST_PATH);
|
|
408
|
-
for (const name of
|
|
504
|
+
for (const name of SCAFFOLDED_GENERATOR_NAMES)
|
|
409
505
|
result.created.push(`${OWNED_GENERATORS_DIR}/${name}.ts`);
|
|
410
|
-
result.created.push("metaobjects.config.ts", ".gitignore");
|
|
506
|
+
result.created.push("metaobjects.config.ts", DB_STUB_REL_PATH, ".gitignore");
|
|
411
507
|
return result;
|
|
412
508
|
}
|
|
413
509
|
for (const d of dirs) {
|
|
@@ -450,10 +546,52 @@ export async function init(opts) {
|
|
|
450
546
|
await writeOwnedGenerators(opts, result);
|
|
451
547
|
// Scaffold metaobjects.config.ts at the project root. Never overwrite if it exists.
|
|
452
548
|
const forgeConfigPath = join(opts.cwd, "metaobjects.config.ts");
|
|
453
|
-
|
|
549
|
+
const wroteScaffoldedConfig = !(await fileExists(forgeConfigPath));
|
|
550
|
+
if (wroteScaffoldedConfig) {
|
|
454
551
|
await writeFile(forgeConfigPath, buildMetaobjectsConfigBody(opts.d1 ? "d1" : "sqlite"), "utf8");
|
|
455
552
|
result.created.push("metaobjects.config.ts");
|
|
456
553
|
}
|
|
554
|
+
// Scaffold the `dbImport` throwing stub at DB_STUB_REL_PATH ("src/db.ts" by
|
|
555
|
+
// default) — ONLY if absent, so a re-run never clobbers a user's real db module
|
|
556
|
+
// (same "write once" precedent as writeOwnedGenerators above). Without this, the
|
|
557
|
+
// scaffolded config declares `dbImport: "../db"` pointing at a module `meta init`
|
|
558
|
+
// never creates, and a fresh project's FIRST `tsc` fails to resolve it.
|
|
559
|
+
//
|
|
560
|
+
// Gated on having just WRITTEN that config, not merely on the stub being absent.
|
|
561
|
+
// DB_STUB_REL_PATH is derived from SCAFFOLD_OUT_DIR + SCAFFOLD_DB_IMPORT — the
|
|
562
|
+
// scaffold's own constants — so it describes where the SCAFFOLDED config points and
|
|
563
|
+
// nowhere else. Re-running `meta init` in a project that already has a config with
|
|
564
|
+
// its own `outDir`/`dbImport` preserves that config (above) and would otherwise still
|
|
565
|
+
// drop a src/db.ts that nothing in the project references: a stray file, in the
|
|
566
|
+
// adopter's application source, answering a question they had already answered.
|
|
567
|
+
const dbStubPath = join(opts.cwd, DB_STUB_REL_PATH);
|
|
568
|
+
const dbStubExists = await fileExists(dbStubPath);
|
|
569
|
+
if (wroteScaffoldedConfig && !dbStubExists) {
|
|
570
|
+
await mkdir(dirname(dbStubPath), { recursive: true });
|
|
571
|
+
await writeFile(dbStubPath, DB_STUB_BODY, "utf8");
|
|
572
|
+
result.created.push(DB_STUB_REL_PATH);
|
|
573
|
+
}
|
|
574
|
+
else if (dbStubExists) {
|
|
575
|
+
// "preserved" means a file we would have written was left alone. Skipping because
|
|
576
|
+
// this project keeps its own config is not preservation — there is nothing there.
|
|
577
|
+
result.preserved.push(DB_STUB_REL_PATH);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
// Neither written nor preserved: this project keeps its own config AND has no stub.
|
|
581
|
+
// Usually correct — its `dbImport` points at a real module somewhere else. But it is
|
|
582
|
+
// ALSO what a scaffolded project looks like after someone deletes or moves src/db.ts,
|
|
583
|
+
// and `meta init --force` cannot tell those apart: `wroteScaffoldedConfig` is only
|
|
584
|
+
// "no config existed", so it is false for the config init itself wrote. Silently
|
|
585
|
+
// doing nothing there leaves the scaffolded `dbImport: "../db"` and the generated
|
|
586
|
+
// routes' `import { db } from "../db.js"` pointing at nothing, and the adopter meets
|
|
587
|
+
// it as a TS2307 from `tsc` with no word from the command that could have said so.
|
|
588
|
+
// Say it here rather than writing: dropping a file into a project that owns its
|
|
589
|
+
// config is what the branch above deliberately refuses.
|
|
590
|
+
result.warnings.push(`${DB_STUB_REL_PATH} was not scaffolded — this project has its own ` +
|
|
591
|
+
"metaobjects.config.ts, so init leaves the database module to it. If that config's " +
|
|
592
|
+
`\`dbImport\` resolves to ${DB_STUB_REL_PATH}, create it or the generated routes will ` +
|
|
593
|
+
"not resolve.");
|
|
594
|
+
}
|
|
457
595
|
// Scaffold a minimal root .gitignore ONLY when the project has none — never
|
|
458
596
|
// clobber a user's existing one (they may have their own rules).
|
|
459
597
|
const rootGitignorePath = join(opts.cwd, ".gitignore");
|
|
@@ -513,12 +651,23 @@ async function ensureEsmPackageType(cwd, result) {
|
|
|
513
651
|
"code in a sub-directory with its own package.json declaring `\"type\": \"module\"`.");
|
|
514
652
|
return;
|
|
515
653
|
}
|
|
654
|
+
const declaredType = pkg.type; // read BEFORE the mutation below overwrites it
|
|
516
655
|
pkg.type = "module";
|
|
517
656
|
const added = addScaffoldDevDependencies(pkg);
|
|
518
657
|
// Preserve the file's existing indentation rather than reformatting someone's manifest.
|
|
519
658
|
const indent = /\n(\s+)"/.exec(raw)?.[1] ?? " ";
|
|
520
659
|
await writeFile(pkgPath, `${JSON.stringify(pkg, null, indent)}\n`, "utf8");
|
|
521
|
-
|
|
660
|
+
// Past tense, deliberately: this reports an edit already made. The imperative
|
|
661
|
+
// ("set `\"type\": \"module\"`") read as a TODO on the one line a newcomer sees
|
|
662
|
+
// last, so a scaffold that had just done the right thing looked like it had failed.
|
|
663
|
+
//
|
|
664
|
+
// And it must not claim the manifest was SILENT on the point: `npm init -y` writes
|
|
665
|
+
// `"type": "commonjs"` explicitly (npm 11.x), which is the dominant first-touch path,
|
|
666
|
+
// so "declared no module system" was false exactly where it is read most. Report what
|
|
667
|
+
// was actually there.
|
|
668
|
+
const previous = typeof declaredType === "string" ? declaredType : undefined;
|
|
669
|
+
result.warnings.push(`package.json ${previous === undefined ? "declared no module system" : `declared "type": "${previous}"`} — ` +
|
|
670
|
+
'set `"type": "module"` for you, because MetaObjects scaffolds and generates ESM, ' +
|
|
522
671
|
"which a CommonJS project cannot compile.");
|
|
523
672
|
if (added.length > 0) {
|
|
524
673
|
result.warnings.push(`added ${added.join(" + ")} to devDependencies — the scaffolded ` +
|
|
@@ -550,10 +699,17 @@ function addScaffoldDevDependencies(pkg) {
|
|
|
550
699
|
"@metaobjectsdev/metadata": `^${version}`,
|
|
551
700
|
};
|
|
552
701
|
const dev = (pkg.devDependencies ?? {});
|
|
553
|
-
|
|
702
|
+
// "Already declared" spans all four dependency fields — the shared rule, so this
|
|
703
|
+
// cannot drift from what `meta eject` means by the same words. It used to ask only
|
|
704
|
+
// dependencies + devDependencies, which meant a project declaring codegen-ts as a
|
|
705
|
+
// PEER dependency (correct for a library whose consumer supplies the version) got it
|
|
706
|
+
// added to devDependencies as well: the same package pinned twice in one manifest,
|
|
707
|
+
// and a second physical copy is the class-identity split this repo has been bitten
|
|
708
|
+
// by twice.
|
|
709
|
+
const declared = declaredDependencyNames(pkg);
|
|
554
710
|
const added = [];
|
|
555
711
|
for (const [name, range] of Object.entries(wanted)) {
|
|
556
|
-
if (
|
|
712
|
+
if (declared.has(name))
|
|
557
713
|
continue;
|
|
558
714
|
dev[name] = range;
|
|
559
715
|
added.push(name);
|
|
@@ -616,8 +772,18 @@ function buildD1MigrateBlock(cwd) {
|
|
|
616
772
|
}
|
|
617
773
|
return block;
|
|
618
774
|
}
|
|
619
|
-
|
|
620
|
-
|
|
775
|
+
/**
|
|
776
|
+
* The post-init message.
|
|
777
|
+
*
|
|
778
|
+
* @param dbStubWritten whether THIS run wrote {@link DB_STUB_REL_PATH}. Required rather
|
|
779
|
+
* than defaulted, because a default would silently restore the bug this parameter
|
|
780
|
+
* exists to close: the note used to be part of one static string and so claimed the
|
|
781
|
+
* stub on every run, including the `meta init --force` in a project keeping its own
|
|
782
|
+
* `metaobjects.config.ts`, where the stub is deliberately not written. Callers pass
|
|
783
|
+
* `result.created.includes(DB_STUB_REL_PATH)` — the same list the message describes.
|
|
784
|
+
*/
|
|
785
|
+
export function nextStepsBlock(dbStubWritten) {
|
|
786
|
+
return SCAFFOLD_SUMMARY + (dbStubWritten ? DB_STUB_NOTE : "") + NEXT_STEPS;
|
|
621
787
|
}
|
|
622
788
|
async function dirExists(p) {
|
|
623
789
|
try {
|
|
@@ -685,7 +851,7 @@ export async function initCommand(args, cwd) {
|
|
|
685
851
|
log.warn(w);
|
|
686
852
|
}
|
|
687
853
|
else {
|
|
688
|
-
log.info(nextStepsBlock());
|
|
854
|
+
log.info(nextStepsBlock(result.created.includes(DB_STUB_REL_PATH)));
|
|
689
855
|
// Surface any scaffold warnings (e.g. the #77 monorepo-subdir agent-context
|
|
690
856
|
// discovery warning) — these are otherwise dropped on the normal init path.
|
|
691
857
|
for (const w of result.warnings)
|