@metaobjectsdev/cli 0.15.2-rc.1 → 0.15.3
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/lib/load-metaobjects-config.d.ts.map +1 -1
- package/dist/src/lib/load-metaobjects-config.js +105 -26
- package/dist/src/lib/load-metaobjects-config.js.map +1 -1
- package/dist/src/lib/pm-detect.d.ts +17 -3
- package/dist/src/lib/pm-detect.d.ts.map +1 -1
- package/dist/src/lib/pm-detect.js +8 -5
- package/dist/src/lib/pm-detect.js.map +1 -1
- package/package.json +9 -9
- package/src/lib/load-metaobjects-config.ts +111 -26
- package/src/lib/pm-detect.ts +25 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-metaobjects-config.d.ts","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-metaobjects-config.d.ts","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAmIvE,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmE9F"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
5
7
|
import { createJiti } from "jiti";
|
|
6
8
|
const CONFIG_FILE = "metaobjects.config.ts";
|
|
7
9
|
// Resolve @metaobjectsdev/codegen-ts from the CLI's own node_modules so that
|
|
@@ -53,8 +55,8 @@ const CLI_PKG_PATHS = {
|
|
|
53
55
|
src: "src/index.ts",
|
|
54
56
|
},
|
|
55
57
|
};
|
|
56
|
-
// Resolve a codegen specifier to an absolute path
|
|
57
|
-
//
|
|
58
|
+
// Resolve a codegen specifier to an absolute path, so a user's
|
|
59
|
+
// metaobjects.config.ts can import @metaobjectsdev/codegen-ts* without
|
|
58
60
|
// declaring it directly — the CLI's own copy is used.
|
|
59
61
|
//
|
|
60
62
|
// Standard module resolution is tried first: it follows whatever node_modules
|
|
@@ -62,6 +64,12 @@ const CLI_PKG_PATHS = {
|
|
|
62
64
|
// NOT nested under the CLI dir), or bun — and honors the package's export
|
|
63
65
|
// conditions. The CLI_PKG_PATHS fallback only kicks in when a specifier isn't
|
|
64
66
|
// require-resolvable from the CLI module.
|
|
67
|
+
//
|
|
68
|
+
// Under Bun's native loader `require.resolve` returns the TypeScript source
|
|
69
|
+
// path (via the "bun" export condition, e.g. `src/index.ts`). When a `.ts`
|
|
70
|
+
// path is returned, we prefer the compiled dist path (always a plain JS file)
|
|
71
|
+
// so the pre-processed config source references a deterministic artifact that
|
|
72
|
+
// won't be intercepted by stale ancestor node_modules directories.
|
|
65
73
|
function resolveCliPkg(specifier) {
|
|
66
74
|
const paths = CLI_PKG_PATHS[specifier];
|
|
67
75
|
// The cli self-reference always points at this package's own entry, never a
|
|
@@ -70,7 +78,17 @@ function resolveCliPkg(specifier) {
|
|
|
70
78
|
return resolve(_cliDir, _isCompiled ? paths.dist : paths.src);
|
|
71
79
|
}
|
|
72
80
|
try {
|
|
73
|
-
|
|
81
|
+
const resolved = _require.resolve(specifier);
|
|
82
|
+
// When Bun's native "bun" export condition returns a `.ts` source path,
|
|
83
|
+
// prefer the compiled dist path so the pre-processed config references a
|
|
84
|
+
// stable JS artifact that the loader can load without the "bun" condition
|
|
85
|
+
// redirecting it to a different module instance.
|
|
86
|
+
if (resolved.endsWith(".ts") && paths !== undefined) {
|
|
87
|
+
const distCandidate = resolve(_cliDir, paths.dist);
|
|
88
|
+
if (existsSync(distCandidate))
|
|
89
|
+
return distCandidate;
|
|
90
|
+
}
|
|
91
|
+
return resolved;
|
|
74
92
|
}
|
|
75
93
|
catch {
|
|
76
94
|
if (paths !== undefined) {
|
|
@@ -81,35 +99,96 @@ function resolveCliPkg(specifier) {
|
|
|
81
99
|
throw new Error(`metaobjects: could not resolve ${specifier} from the CLI — try reinstalling @metaobjectsdev/cli.`);
|
|
82
100
|
}
|
|
83
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Rewrite `from "specifier"` / `export … from "specifier"` / bare
|
|
104
|
+
* `import "specifier"` occurrences for a known set of package specifiers.
|
|
105
|
+
* Returns the original string unchanged when no substitutions are needed.
|
|
106
|
+
*
|
|
107
|
+
* This is necessary because jiti 2.x under Bun's native ESM loader does not
|
|
108
|
+
* apply its `alias` map — Bun intercepts the `import()` call and resolves
|
|
109
|
+
* modules itself, ignoring jiti's alias configuration. By rewriting the
|
|
110
|
+
* specifiers in the source text before loading, we guarantee that absolute
|
|
111
|
+
* paths are used regardless of which loader takes over.
|
|
112
|
+
*
|
|
113
|
+
* Relative imports (`./foo`, `../bar`) are intentionally NOT rewritten; they
|
|
114
|
+
* must continue to resolve relative to the config file's own directory.
|
|
115
|
+
*/
|
|
116
|
+
function rewriteImportSpecifiers(source, aliasMap) {
|
|
117
|
+
let result = source;
|
|
118
|
+
for (const [specifier, resolvedPath] of Object.entries(aliasMap)) {
|
|
119
|
+
const esc = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
120
|
+
// Rewrite: from "specifier" or from 'specifier'
|
|
121
|
+
// Covers: import { X } from "pkg", export { X } from "pkg"
|
|
122
|
+
result = result.replace(new RegExp(`(\\bfrom\\s+)(['"])${esc}\\2`, "g"), (_m, prefix, quote) => `${prefix}${quote}${resolvedPath}${quote}`);
|
|
123
|
+
// Rewrite: import "specifier" or import 'specifier' (side-effect imports)
|
|
124
|
+
result = result.replace(new RegExp(`(\\bimport\\s+)(['"])${esc}\\2`, "g"), (_m, prefix, quote) => `${prefix}${quote}${resolvedPath}${quote}`);
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
84
128
|
export async function loadMetaobjectsConfig(projectRoot) {
|
|
85
129
|
const fullPath = resolve(projectRoot, CONFIG_FILE);
|
|
86
130
|
if (!existsSync(fullPath)) {
|
|
87
131
|
throw new Error(`metaobjects.config.ts not found at ${fullPath}. Run 'meta init' to scaffold one.`);
|
|
88
132
|
}
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
133
|
+
// Build the canonical alias map: specifier → resolved absolute path.
|
|
134
|
+
const aliasMap = {};
|
|
135
|
+
for (const specifier of Object.keys(CLI_PKG_PATHS)) {
|
|
136
|
+
aliasMap[specifier] = resolveCliPkg(specifier);
|
|
137
|
+
}
|
|
138
|
+
// Pre-process the config file content to rewrite @metaobjectsdev/* import
|
|
139
|
+
// specifiers to resolved absolute paths. This is the primary mechanism that
|
|
140
|
+
// ensures the CLI's own copies are used: jiti's `alias` map is kept below as
|
|
141
|
+
// a belt-and-suspenders fallback for runtimes where jiti's transformer IS
|
|
142
|
+
// active, but under Bun the pre-processed source is the operative fix.
|
|
143
|
+
const original = await readFile(fullPath, "utf8");
|
|
144
|
+
const processed = rewriteImportSpecifiers(original, aliasMap);
|
|
145
|
+
// When any specifiers were rewritten, write the modified source to a temp
|
|
146
|
+
// file in the SAME directory as the original so that relative imports inside
|
|
147
|
+
// the config (e.g. `from "./codegen/generators/entity"`) still resolve
|
|
148
|
+
// correctly. The file is deleted in the finally block below.
|
|
149
|
+
let loadPath = fullPath;
|
|
150
|
+
let tempCreated = false;
|
|
151
|
+
if (processed !== original) {
|
|
152
|
+
const tempName = `.metaobjects-config-proc-${randomBytes(4).toString("hex")}.ts`;
|
|
153
|
+
const tempPath = resolve(dirname(fullPath), tempName);
|
|
154
|
+
try {
|
|
155
|
+
await writeFile(tempPath, processed, "utf8");
|
|
156
|
+
loadPath = tempPath;
|
|
157
|
+
tempCreated = true;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Temp write failed — fall back to the original file and let the loader
|
|
161
|
+
// attempt its own resolution (best-effort; may still fail in hostile envs).
|
|
162
|
+
loadPath = fullPath;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// jiti's alias map is a belt-and-suspenders fallback for runtimes where
|
|
166
|
+
// jiti's own transformer is active (non-Bun, or a future jiti version that
|
|
167
|
+
// re-enables alias under Bun). Under Bun the pre-processed file above is the
|
|
168
|
+
// operative fix.
|
|
93
169
|
const jiti = createJiti(import.meta.url, {
|
|
94
170
|
interopDefault: true,
|
|
95
|
-
alias:
|
|
96
|
-
"@metaobjectsdev/codegen-ts": resolveCliPkg("@metaobjectsdev/codegen-ts"),
|
|
97
|
-
"@metaobjectsdev/metadata": resolveCliPkg("@metaobjectsdev/metadata"),
|
|
98
|
-
"@metaobjectsdev/codegen-ts/generators": resolveCliPkg("@metaobjectsdev/codegen-ts/generators"),
|
|
99
|
-
"@metaobjectsdev/codegen-ts-react": resolveCliPkg("@metaobjectsdev/codegen-ts-react"),
|
|
100
|
-
"@metaobjectsdev/codegen-ts-tanstack": resolveCliPkg("@metaobjectsdev/codegen-ts-tanstack"),
|
|
101
|
-
"@metaobjectsdev/cli": resolveCliPkg("@metaobjectsdev/cli"),
|
|
102
|
-
},
|
|
171
|
+
alias: aliasMap,
|
|
103
172
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
173
|
+
try {
|
|
174
|
+
const raw = (await jiti.import(loadPath));
|
|
175
|
+
// jiti's interopDefault doesn't always unwrap the default export when accessed
|
|
176
|
+
// across module boundaries — explicitly unwrap if present.
|
|
177
|
+
const cfg = (raw && typeof raw === "object" && "default" in raw && raw.default
|
|
178
|
+
? raw.default
|
|
179
|
+
: raw);
|
|
180
|
+
if (!cfg || typeof cfg !== "object" || !Array.isArray(cfg.generators)) {
|
|
181
|
+
throw new Error(`metaobjects.config.ts at ${fullPath} did not export a valid MetaobjectsGenConfig (missing 'generators' array).`);
|
|
182
|
+
}
|
|
183
|
+
return cfg;
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
if (tempCreated) {
|
|
187
|
+
try {
|
|
188
|
+
unlinkSync(loadPath);
|
|
189
|
+
}
|
|
190
|
+
catch { /* best-effort cleanup */ }
|
|
191
|
+
}
|
|
112
192
|
}
|
|
113
|
-
return cfg;
|
|
114
193
|
}
|
|
115
194
|
//# sourceMappingURL=load-metaobjects-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-metaobjects-config.js","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"load-metaobjects-config.js","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
|
2
|
+
/**
|
|
3
|
+
* Options for `detectPackageManager`.
|
|
4
|
+
*/
|
|
5
|
+
export interface DetectPackageManagerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Stop walking up the directory tree at this path (inclusive). Useful in
|
|
8
|
+
* tests or isolated environments where walking above the project root would
|
|
9
|
+
* accidentally find a lockfile in an unrelated ancestor directory (e.g. a
|
|
10
|
+
* stale `/tmp/package-lock.json` on a shared CI machine).
|
|
11
|
+
*
|
|
12
|
+
* If omitted the walk continues all the way to the filesystem root.
|
|
13
|
+
*/
|
|
14
|
+
stopAt?: string;
|
|
15
|
+
}
|
|
2
16
|
/**
|
|
3
17
|
* Detect the package manager in use by looking for the lockfile closest to
|
|
4
|
-
* `dir`. Walks up to the root
|
|
5
|
-
* is found.
|
|
18
|
+
* `dir`. Walks up to the filesystem root (or `options.stopAt`, if supplied).
|
|
19
|
+
* Returns "bun" as the default when no lockfile is found.
|
|
6
20
|
*/
|
|
7
|
-
export declare function detectPackageManager(dir: string): Promise<PackageManager>;
|
|
21
|
+
export declare function detectPackageManager(dir: string, options?: DetectPackageManagerOptions): Promise<PackageManager>;
|
|
8
22
|
/**
|
|
9
23
|
* Returns the install command for a missing package, using the detected PM.
|
|
10
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pm-detect.d.ts","sourceRoot":"","sources":["../../../src/lib/pm-detect.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D;;;;GAIG;AACH,wBAAsB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"pm-detect.d.ts","sourceRoot":"","sources":["../../../src/lib/pm-detect.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,cAAc,CAAC,CA2BzB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY9E"}
|
|
@@ -2,11 +2,11 @@ import { access } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
/**
|
|
4
4
|
* Detect the package manager in use by looking for the lockfile closest to
|
|
5
|
-
* `dir`. Walks up to the root
|
|
6
|
-
* is found.
|
|
5
|
+
* `dir`. Walks up to the filesystem root (or `options.stopAt`, if supplied).
|
|
6
|
+
* Returns "bun" as the default when no lockfile is found.
|
|
7
7
|
*/
|
|
8
|
-
export async function detectPackageManager(dir) {
|
|
9
|
-
// Check in the given dir first, then parent dirs up to the FS root.
|
|
8
|
+
export async function detectPackageManager(dir, options) {
|
|
9
|
+
// Check in the given dir first, then parent dirs up to the FS root (or stopAt).
|
|
10
10
|
let current = dir;
|
|
11
11
|
while (true) {
|
|
12
12
|
const candidates = [
|
|
@@ -25,9 +25,12 @@ export async function detectPackageManager(dir) {
|
|
|
25
25
|
// not found, try next
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
// Honor the stopAt boundary before walking up further.
|
|
29
|
+
if (options?.stopAt !== undefined && current === options.stopAt)
|
|
30
|
+
break;
|
|
28
31
|
const parent = join(current, "..");
|
|
29
32
|
if (parent === current)
|
|
30
|
-
break; // reached root
|
|
33
|
+
break; // reached filesystem root
|
|
31
34
|
current = parent;
|
|
32
35
|
}
|
|
33
36
|
// Default: bun (most common for this toolchain)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pm-detect.js","sourceRoot":"","sources":["../../../src/lib/pm-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"pm-detect.js","sourceRoot":"","sources":["../../../src/lib/pm-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmBjC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAW,EACX,OAAqC;IAErC,gFAAgF;IAChF,IAAI,OAAO,GAAG,GAAG,CAAC;IAClB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,UAAU,GAA+B;YAC7C,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,EAAE,KAAK,CAAC;YAC3C,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;YACzC,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC;YACpC,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,KAAK,CAAC;YACnC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC;SACnC,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC;QACD,uDAAuD;QACvD,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,OAAO,CAAC,MAAM;YAAE,MAAM;QACvE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,CAAC,0BAA0B;QACzD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IACD,gDAAgD;IAChD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,GAAW;IAC3D,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC3C,QAAQ,EAAE,EAAE,CAAC;QACX,KAAK,KAAK;YACR,OAAO,eAAe,GAAG,EAAE,CAAC;QAC9B,KAAK,MAAM;YACT,OAAO,YAAY,GAAG,EAAE,CAAC;QAC3B,KAAK,MAAM;YACT,OAAO,YAAY,GAAG,EAAE,CAAC;QAC3B,KAAK,KAAK;YACR,OAAO,WAAW,GAAG,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/cli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "CLI for MetaObjects: scaffold, codegen, migrate, and drift-detection commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@libsql/kysely-libsql": "^0.4.0",
|
|
52
|
-
"@metaobjectsdev/codegen-ts": "0.15.
|
|
53
|
-
"@metaobjectsdev/codegen-ts-react": "0.15.
|
|
54
|
-
"@metaobjectsdev/codegen-ts-tanstack": "0.15.
|
|
55
|
-
"@metaobjectsdev/metadata": "0.15.
|
|
56
|
-
"@metaobjectsdev/migrate-ts": "0.15.
|
|
57
|
-
"@metaobjectsdev/render": "0.15.
|
|
58
|
-
"@metaobjectsdev/runtime-ts": "0.15.
|
|
59
|
-
"@metaobjectsdev/sdk": "0.15.
|
|
52
|
+
"@metaobjectsdev/codegen-ts": "0.15.3",
|
|
53
|
+
"@metaobjectsdev/codegen-ts-react": "0.15.3",
|
|
54
|
+
"@metaobjectsdev/codegen-ts-tanstack": "0.15.3",
|
|
55
|
+
"@metaobjectsdev/metadata": "0.15.3",
|
|
56
|
+
"@metaobjectsdev/migrate-ts": "0.15.3",
|
|
57
|
+
"@metaobjectsdev/render": "0.15.3",
|
|
58
|
+
"@metaobjectsdev/runtime-ts": "0.15.3",
|
|
59
|
+
"@metaobjectsdev/sdk": "0.15.3",
|
|
60
60
|
"@toon-format/toon": "^2.3.0",
|
|
61
61
|
"jiti": "^2.4.0"
|
|
62
62
|
},
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
5
7
|
import { createJiti } from "jiti";
|
|
6
8
|
import type { MetaobjectsGenConfig } from "@metaobjectsdev/codegen-ts";
|
|
7
9
|
|
|
@@ -57,8 +59,8 @@ const CLI_PKG_PATHS: Record<string, { dist: string; src: string }> = {
|
|
|
57
59
|
},
|
|
58
60
|
};
|
|
59
61
|
|
|
60
|
-
// Resolve a codegen specifier to an absolute path
|
|
61
|
-
//
|
|
62
|
+
// Resolve a codegen specifier to an absolute path, so a user's
|
|
63
|
+
// metaobjects.config.ts can import @metaobjectsdev/codegen-ts* without
|
|
62
64
|
// declaring it directly — the CLI's own copy is used.
|
|
63
65
|
//
|
|
64
66
|
// Standard module resolution is tried first: it follows whatever node_modules
|
|
@@ -66,6 +68,12 @@ const CLI_PKG_PATHS: Record<string, { dist: string; src: string }> = {
|
|
|
66
68
|
// NOT nested under the CLI dir), or bun — and honors the package's export
|
|
67
69
|
// conditions. The CLI_PKG_PATHS fallback only kicks in when a specifier isn't
|
|
68
70
|
// require-resolvable from the CLI module.
|
|
71
|
+
//
|
|
72
|
+
// Under Bun's native loader `require.resolve` returns the TypeScript source
|
|
73
|
+
// path (via the "bun" export condition, e.g. `src/index.ts`). When a `.ts`
|
|
74
|
+
// path is returned, we prefer the compiled dist path (always a plain JS file)
|
|
75
|
+
// so the pre-processed config source references a deterministic artifact that
|
|
76
|
+
// won't be intercepted by stale ancestor node_modules directories.
|
|
69
77
|
function resolveCliPkg(specifier: string): string {
|
|
70
78
|
const paths = CLI_PKG_PATHS[specifier];
|
|
71
79
|
// The cli self-reference always points at this package's own entry, never a
|
|
@@ -74,7 +82,16 @@ function resolveCliPkg(specifier: string): string {
|
|
|
74
82
|
return resolve(_cliDir, _isCompiled ? paths.dist : paths.src);
|
|
75
83
|
}
|
|
76
84
|
try {
|
|
77
|
-
|
|
85
|
+
const resolved = _require.resolve(specifier);
|
|
86
|
+
// When Bun's native "bun" export condition returns a `.ts` source path,
|
|
87
|
+
// prefer the compiled dist path so the pre-processed config references a
|
|
88
|
+
// stable JS artifact that the loader can load without the "bun" condition
|
|
89
|
+
// redirecting it to a different module instance.
|
|
90
|
+
if (resolved.endsWith(".ts") && paths !== undefined) {
|
|
91
|
+
const distCandidate = resolve(_cliDir, paths.dist);
|
|
92
|
+
if (existsSync(distCandidate)) return distCandidate;
|
|
93
|
+
}
|
|
94
|
+
return resolved;
|
|
78
95
|
} catch {
|
|
79
96
|
if (paths !== undefined) {
|
|
80
97
|
const candidate = resolve(_cliDir, _isCompiled ? paths.dist : paths.src);
|
|
@@ -86,6 +103,39 @@ function resolveCliPkg(specifier: string): string {
|
|
|
86
103
|
}
|
|
87
104
|
}
|
|
88
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Rewrite `from "specifier"` / `export … from "specifier"` / bare
|
|
108
|
+
* `import "specifier"` occurrences for a known set of package specifiers.
|
|
109
|
+
* Returns the original string unchanged when no substitutions are needed.
|
|
110
|
+
*
|
|
111
|
+
* This is necessary because jiti 2.x under Bun's native ESM loader does not
|
|
112
|
+
* apply its `alias` map — Bun intercepts the `import()` call and resolves
|
|
113
|
+
* modules itself, ignoring jiti's alias configuration. By rewriting the
|
|
114
|
+
* specifiers in the source text before loading, we guarantee that absolute
|
|
115
|
+
* paths are used regardless of which loader takes over.
|
|
116
|
+
*
|
|
117
|
+
* Relative imports (`./foo`, `../bar`) are intentionally NOT rewritten; they
|
|
118
|
+
* must continue to resolve relative to the config file's own directory.
|
|
119
|
+
*/
|
|
120
|
+
function rewriteImportSpecifiers(source: string, aliasMap: Record<string, string>): string {
|
|
121
|
+
let result = source;
|
|
122
|
+
for (const [specifier, resolvedPath] of Object.entries(aliasMap)) {
|
|
123
|
+
const esc = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
124
|
+
// Rewrite: from "specifier" or from 'specifier'
|
|
125
|
+
// Covers: import { X } from "pkg", export { X } from "pkg"
|
|
126
|
+
result = result.replace(
|
|
127
|
+
new RegExp(`(\\bfrom\\s+)(['"])${esc}\\2`, "g"),
|
|
128
|
+
(_m, prefix, quote) => `${prefix}${quote}${resolvedPath}${quote}`,
|
|
129
|
+
);
|
|
130
|
+
// Rewrite: import "specifier" or import 'specifier' (side-effect imports)
|
|
131
|
+
result = result.replace(
|
|
132
|
+
new RegExp(`(\\bimport\\s+)(['"])${esc}\\2`, "g"),
|
|
133
|
+
(_m, prefix, quote) => `${prefix}${quote}${resolvedPath}${quote}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
89
139
|
export async function loadMetaobjectsConfig(projectRoot: string): Promise<MetaobjectsGenConfig> {
|
|
90
140
|
const fullPath = resolve(projectRoot, CONFIG_FILE);
|
|
91
141
|
if (!existsSync(fullPath)) {
|
|
@@ -93,29 +143,64 @@ export async function loadMetaobjectsConfig(projectRoot: string): Promise<Metaob
|
|
|
93
143
|
`metaobjects.config.ts not found at ${fullPath}. Run 'meta init' to scaffold one.`,
|
|
94
144
|
);
|
|
95
145
|
}
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
146
|
+
|
|
147
|
+
// Build the canonical alias map: specifier → resolved absolute path.
|
|
148
|
+
const aliasMap: Record<string, string> = {};
|
|
149
|
+
for (const specifier of Object.keys(CLI_PKG_PATHS)) {
|
|
150
|
+
aliasMap[specifier] = resolveCliPkg(specifier);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Pre-process the config file content to rewrite @metaobjectsdev/* import
|
|
154
|
+
// specifiers to resolved absolute paths. This is the primary mechanism that
|
|
155
|
+
// ensures the CLI's own copies are used: jiti's `alias` map is kept below as
|
|
156
|
+
// a belt-and-suspenders fallback for runtimes where jiti's transformer IS
|
|
157
|
+
// active, but under Bun the pre-processed source is the operative fix.
|
|
158
|
+
const original = await readFile(fullPath, "utf8");
|
|
159
|
+
const processed = rewriteImportSpecifiers(original, aliasMap);
|
|
160
|
+
|
|
161
|
+
// When any specifiers were rewritten, write the modified source to a temp
|
|
162
|
+
// file in the SAME directory as the original so that relative imports inside
|
|
163
|
+
// the config (e.g. `from "./codegen/generators/entity"`) still resolve
|
|
164
|
+
// correctly. The file is deleted in the finally block below.
|
|
165
|
+
let loadPath = fullPath;
|
|
166
|
+
let tempCreated = false;
|
|
167
|
+
if (processed !== original) {
|
|
168
|
+
const tempName = `.metaobjects-config-proc-${randomBytes(4).toString("hex")}.ts`;
|
|
169
|
+
const tempPath = resolve(dirname(fullPath), tempName);
|
|
170
|
+
try {
|
|
171
|
+
await writeFile(tempPath, processed, "utf8");
|
|
172
|
+
loadPath = tempPath;
|
|
173
|
+
tempCreated = true;
|
|
174
|
+
} catch {
|
|
175
|
+
// Temp write failed — fall back to the original file and let the loader
|
|
176
|
+
// attempt its own resolution (best-effort; may still fail in hostile envs).
|
|
177
|
+
loadPath = fullPath;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// jiti's alias map is a belt-and-suspenders fallback for runtimes where
|
|
182
|
+
// jiti's own transformer is active (non-Bun, or a future jiti version that
|
|
183
|
+
// re-enables alias under Bun). Under Bun the pre-processed file above is the
|
|
184
|
+
// operative fix.
|
|
100
185
|
const jiti = createJiti(import.meta.url, {
|
|
101
186
|
interopDefault: true,
|
|
102
|
-
alias:
|
|
103
|
-
"@metaobjectsdev/codegen-ts": resolveCliPkg("@metaobjectsdev/codegen-ts"),
|
|
104
|
-
"@metaobjectsdev/metadata": resolveCliPkg("@metaobjectsdev/metadata"),
|
|
105
|
-
"@metaobjectsdev/codegen-ts/generators": resolveCliPkg("@metaobjectsdev/codegen-ts/generators"),
|
|
106
|
-
"@metaobjectsdev/codegen-ts-react": resolveCliPkg("@metaobjectsdev/codegen-ts-react"),
|
|
107
|
-
"@metaobjectsdev/codegen-ts-tanstack": resolveCliPkg("@metaobjectsdev/codegen-ts-tanstack"),
|
|
108
|
-
"@metaobjectsdev/cli": resolveCliPkg("@metaobjectsdev/cli"),
|
|
109
|
-
},
|
|
187
|
+
alias: aliasMap,
|
|
110
188
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const raw = (await jiti.import(loadPath)) as MetaobjectsGenConfig | { default: MetaobjectsGenConfig };
|
|
192
|
+
// jiti's interopDefault doesn't always unwrap the default export when accessed
|
|
193
|
+
// across module boundaries — explicitly unwrap if present.
|
|
194
|
+
const cfg = (raw && typeof raw === "object" && "default" in raw && raw.default
|
|
195
|
+
? (raw as { default: MetaobjectsGenConfig }).default
|
|
196
|
+
: raw) as MetaobjectsGenConfig;
|
|
197
|
+
if (!cfg || typeof cfg !== "object" || !Array.isArray(cfg.generators)) {
|
|
198
|
+
throw new Error(`metaobjects.config.ts at ${fullPath} did not export a valid MetaobjectsGenConfig (missing 'generators' array).`);
|
|
199
|
+
}
|
|
200
|
+
return cfg;
|
|
201
|
+
} finally {
|
|
202
|
+
if (tempCreated) {
|
|
203
|
+
try { unlinkSync(loadPath); } catch { /* best-effort cleanup */ }
|
|
204
|
+
}
|
|
119
205
|
}
|
|
120
|
-
return cfg;
|
|
121
206
|
}
|
package/src/lib/pm-detect.ts
CHANGED
|
@@ -3,13 +3,31 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Options for `detectPackageManager`.
|
|
8
|
+
*/
|
|
9
|
+
export interface DetectPackageManagerOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Stop walking up the directory tree at this path (inclusive). Useful in
|
|
12
|
+
* tests or isolated environments where walking above the project root would
|
|
13
|
+
* accidentally find a lockfile in an unrelated ancestor directory (e.g. a
|
|
14
|
+
* stale `/tmp/package-lock.json` on a shared CI machine).
|
|
15
|
+
*
|
|
16
|
+
* If omitted the walk continues all the way to the filesystem root.
|
|
17
|
+
*/
|
|
18
|
+
stopAt?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
/**
|
|
7
22
|
* Detect the package manager in use by looking for the lockfile closest to
|
|
8
|
-
* `dir`. Walks up to the root
|
|
9
|
-
* is found.
|
|
23
|
+
* `dir`. Walks up to the filesystem root (or `options.stopAt`, if supplied).
|
|
24
|
+
* Returns "bun" as the default when no lockfile is found.
|
|
10
25
|
*/
|
|
11
|
-
export async function detectPackageManager(
|
|
12
|
-
|
|
26
|
+
export async function detectPackageManager(
|
|
27
|
+
dir: string,
|
|
28
|
+
options?: DetectPackageManagerOptions,
|
|
29
|
+
): Promise<PackageManager> {
|
|
30
|
+
// Check in the given dir first, then parent dirs up to the FS root (or stopAt).
|
|
13
31
|
let current = dir;
|
|
14
32
|
while (true) {
|
|
15
33
|
const candidates: [string, PackageManager][] = [
|
|
@@ -27,8 +45,10 @@ export async function detectPackageManager(dir: string): Promise<PackageManager>
|
|
|
27
45
|
// not found, try next
|
|
28
46
|
}
|
|
29
47
|
}
|
|
48
|
+
// Honor the stopAt boundary before walking up further.
|
|
49
|
+
if (options?.stopAt !== undefined && current === options.stopAt) break;
|
|
30
50
|
const parent = join(current, "..");
|
|
31
|
-
if (parent === current) break; // reached root
|
|
51
|
+
if (parent === current) break; // reached filesystem root
|
|
32
52
|
current = parent;
|
|
33
53
|
}
|
|
34
54
|
// Default: bun (most common for this toolchain)
|