@metaobjectsdev/cli 0.15.2 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"load-metaobjects-config.d.ts","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAmFvE,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgC9F"}
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 for jiti's alias map, so a
57
- // user's metaobjects.config.ts can import @metaobjectsdev/codegen-ts* without
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
- return _require.resolve(specifier);
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
- // Use import.meta.url as base so jiti resolves workspace deps (@metaobjectsdev/*)
90
- // from the CLI's own node_modules, not from the user's project root.
91
- // The alias map redirects codegen-ts imports to the CLI's own copy so that
92
- // user projects don't need @metaobjectsdev/codegen-ts as a direct dependency.
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
- const raw = (await jiti.import(fullPath));
105
- // jiti's interopDefault doesn't always unwrap the default export when accessed
106
- // across module boundaries explicitly unwrap if present.
107
- const cfg = (raw && typeof raw === "object" && "default" in raw && raw.default
108
- ? raw.default
109
- : raw);
110
- if (!cfg || typeof cfg !== "object" || !Array.isArray(cfg.generators)) {
111
- throw new Error(`metaobjects.config.ts at ${fullPath} did not export a valid MetaobjectsGenConfig (missing 'generators' array).`);
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":"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"}
1
+ {"version":3,"file":"load-metaobjects-config.js","sourceRoot":"","sources":["../../../src/lib/load-metaobjects-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAGlC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C,6EAA6E;AAC7E,+EAA+E;AAC/E,mEAAmE;AACnE,EAAE;AACF,mFAAmF;AACnF,mFAAmF;AACnF,+FAA+F;AAC/F,gEAAgE;AAChE,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACjD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AAC7E,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChD,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,iDAAiD;AACjD,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,6BAA6B;AAC7B,MAAM,aAAa,GAAkD;IACnE,4BAA4B,EAAE;QAC5B,IAAI,EAAE,uDAAuD;QAC7D,GAAG,EAAE,sDAAsD;KAC5D;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,4EAA4E;IAC5E,0BAA0B,EAAE;QAC1B,IAAI,EAAE,qDAAqD;QAC3D,GAAG,EAAE,oDAAoD;KAC1D;IACD,uCAAuC,EAAE;QACvC,IAAI,EAAE,kEAAkE;QACxE,GAAG,EAAE,iEAAiE;KACvE;IACD,kCAAkC,EAAE;QAClC,IAAI,EAAE,6DAA6D;QACnE,GAAG,EAAE,4DAA4D;KAClE;IACD,qCAAqC,EAAE;QACrC,IAAI,EAAE,gEAAgE;QACtE,GAAG,EAAE,+DAA+D;KACrE;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,mBAAmB;QACzB,GAAG,EAAE,cAAc;KACpB;CACF,CAAC;AAEF,+DAA+D;AAC/D,uEAAuE;AACvE,sDAAsD;AACtD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAC1E,8EAA8E;AAC9E,0CAA0C;AAC1C,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,mEAAmE;AACnE,SAAS,aAAa,CAAC,SAAiB;IACtC,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACvC,4EAA4E;IAC5E,2DAA2D;IAC3D,IAAI,SAAS,KAAK,qBAAqB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/D,OAAO,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,wEAAwE;QACxE,yEAAyE;QACzE,0EAA0E;QAC1E,iDAAiD;QACjD,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACpD,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,UAAU,CAAC,aAAa,CAAC;gBAAE,OAAO,aAAa,CAAC;QACtD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzE,IAAI,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;QAC9C,CAAC;QACD,MAAM,IAAI,KAAK,CACb,kCAAkC,SAAS,uDAAuD,CACnG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,uBAAuB,CAAC,MAAc,EAAE,QAAgC;IAC/E,IAAI,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjE,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAC7D,gDAAgD;QAChD,2DAA2D;QAC3D,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,IAAI,MAAM,CAAC,sBAAsB,GAAG,KAAK,EAAE,GAAG,CAAC,EAC/C,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,GAAG,KAAK,EAAE,CAClE,CAAC;QACF,0EAA0E;QAC1E,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,IAAI,MAAM,CAAC,wBAAwB,GAAG,KAAK,EAAE,GAAG,CAAC,EACjD,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,GAAG,KAAK,EAAE,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,WAAmB;IAC7D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,sCAAsC,QAAQ,oCAAoC,CACnF,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QACnD,QAAQ,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC;IAED,0EAA0E;IAC1E,4EAA4E;IAC5E,6EAA6E;IAC7E,0EAA0E;IAC1E,uEAAuE;IACvE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE9D,0EAA0E;IAC1E,6EAA6E;IAC7E,uEAAuE;IACvE,6DAA6D;IAC7D,IAAI,QAAQ,GAAG,QAAQ,CAAC;IACxB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,4BAA4B,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;QACjF,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;YAC7C,QAAQ,GAAG,QAAQ,CAAC;YACpB,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,4EAA4E;YAC5E,QAAQ,GAAG,QAAQ,CAAC;QACtB,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,2EAA2E;IAC3E,6EAA6E;IAC7E,iBAAiB;IACjB,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACvC,cAAc,EAAE,IAAI;QACpB,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAA6D,CAAC;QACtG,+EAA+E;QAC/E,2DAA2D;QAC3D,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO;YAC5E,CAAC,CAAE,GAAyC,CAAC,OAAO;YACpD,CAAC,CAAC,GAAG,CAAyB,CAAC;QACjC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,4EAA4E,CAAC,CAAC;QACpI,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;YAAS,CAAC;QACT,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC;gBAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -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. Returns "bun" as the default when no lockfile
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,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAyB/E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY9E"}
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. Returns "bun" as the default when no lockfile
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;AAIjC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAW;IACpD,oEAAoE;IACpE,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,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM,CAAC,eAAe;QAC9C,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"}
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.2",
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.2",
53
- "@metaobjectsdev/codegen-ts-react": "0.15.2",
54
- "@metaobjectsdev/codegen-ts-tanstack": "0.15.2",
55
- "@metaobjectsdev/metadata": "0.15.2",
56
- "@metaobjectsdev/migrate-ts": "0.15.2",
57
- "@metaobjectsdev/render": "0.15.2",
58
- "@metaobjectsdev/runtime-ts": "0.15.2",
59
- "@metaobjectsdev/sdk": "0.15.2",
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 for jiti's alias map, so a
61
- // user's metaobjects.config.ts can import @metaobjectsdev/codegen-ts* without
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
- return _require.resolve(specifier);
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
- // Use import.meta.url as base so jiti resolves workspace deps (@metaobjectsdev/*)
97
- // from the CLI's own node_modules, not from the user's project root.
98
- // The alias map redirects codegen-ts imports to the CLI's own copy so that
99
- // user projects don't need @metaobjectsdev/codegen-ts as a direct dependency.
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
- const raw = (await jiti.import(fullPath)) as MetaobjectsGenConfig | { default: MetaobjectsGenConfig };
112
- // jiti's interopDefault doesn't always unwrap the default export when accessed
113
- // across module boundaries explicitly unwrap if present.
114
- const cfg = (raw && typeof raw === "object" && "default" in raw && raw.default
115
- ? (raw as { default: MetaobjectsGenConfig }).default
116
- : raw) as MetaobjectsGenConfig;
117
- if (!cfg || typeof cfg !== "object" || !Array.isArray(cfg.generators)) {
118
- throw new Error(`metaobjects.config.ts at ${fullPath} did not export a valid MetaobjectsGenConfig (missing 'generators' array).`);
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
  }
@@ -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. Returns "bun" as the default when no lockfile
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(dir: string): Promise<PackageManager> {
12
- // Check in the given dir first, then parent dirs up to the FS root.
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)