@schemic/cli 0.1.0-alpha.2 → 0.1.0-alpha.4

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/lib/cli.js CHANGED
@@ -7,8 +7,13 @@ import {
7
7
  mkdirSync as mkdirSync3,
8
8
  writeFileSync as writeFileSync3
9
9
  } from "fs";
10
- import { dirname as dirname2, join as join3, relative as relative2 } from "path";
10
+ import { dirname as dirname3, join as join3, relative as relative2 } from "path";
11
11
  import { createInterface } from "readline/promises";
12
+
13
+ // package.json
14
+ var version = "0.1.0-alpha.4";
15
+
16
+ // src/cli/index.ts
12
17
  import {
13
18
  actionLabel,
14
19
  applyPull,
@@ -423,8 +428,9 @@ async function closeQuietly(conn) {
423
428
  }
424
429
 
425
430
  // src/cli/resolve.ts
431
+ import { readFileSync as readFileSync2 } from "fs";
426
432
  import { createRequire } from "module";
427
- import { join as join2 } from "path";
433
+ import { dirname as dirname2, join as join2 } from "path";
428
434
  import { pathToFileURL } from "url";
429
435
  import {
430
436
  driverNames,
@@ -433,23 +439,54 @@ import {
433
439
  loadProject,
434
440
  resolveConnectionConfig
435
441
  } from "@schemic/core";
442
+ function pickCompiled(node) {
443
+ if (typeof node === "string") return node;
444
+ if (node && typeof node === "object") {
445
+ const o = node;
446
+ return pickCompiled(o.import) ?? pickCompiled(o.default) ?? pickCompiled(o.require);
447
+ }
448
+ return void 0;
449
+ }
450
+ function compiledEntry(pkg, base) {
451
+ try {
452
+ const manifestPath = createRequire(base).resolve(`${pkg}/package.json`);
453
+ const m = JSON.parse(readFileSync2(manifestPath, "utf8"));
454
+ const rel = pickCompiled(m.exports?.["."]) ?? m.module ?? m.main ?? "index.js";
455
+ return join2(dirname2(manifestPath), rel);
456
+ } catch {
457
+ return null;
458
+ }
459
+ }
436
460
  async function ensureDriver(name) {
437
461
  if (driverNames().includes(name)) return;
438
462
  const pkg = `@schemic/${name}`;
439
- try {
440
- const fromCwd = createRequire(join2(process.cwd(), "noop.js"));
441
- await import(pathToFileURL(fromCwd.resolve(pkg)).href);
442
- } catch {
463
+ let lastErr;
464
+ let loaded = false;
465
+ for (const base of [join2(process.cwd(), "noop.js"), import.meta.url]) {
466
+ const entry = compiledEntry(pkg, base);
467
+ if (!entry) continue;
468
+ try {
469
+ await import(pathToFileURL(entry).href);
470
+ loaded = true;
471
+ break;
472
+ } catch (e) {
473
+ lastErr = e;
474
+ }
475
+ }
476
+ if (!loaded) {
443
477
  try {
444
478
  await import(pkg);
479
+ loaded = true;
445
480
  } catch (e) {
446
- throw new Error(
447
- `could not load the "${name}" database driver (package ${pkg}). Install it in your project, then re-run:
448
- bun add ${pkg}
449
- (${e instanceof Error ? e.message : String(e)})`
450
- );
481
+ lastErr = e;
451
482
  }
452
483
  }
484
+ if (!loaded)
485
+ throw new Error(
486
+ `could not load the "${name}" database driver (package ${pkg}). Install it in your project, then re-run:
487
+ bun add ${pkg}
488
+ (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`
489
+ );
453
490
  if (!driverNames().includes(name))
454
491
  throw new Error(`package ${pkg} did not register a "${name}" driver.`);
455
492
  }
@@ -730,7 +767,7 @@ var dbFlags = (cmd) => configFlag(cmd).option(
730
767
  var program = new Command();
731
768
  program.name("schemic").description(
732
769
  "Schema-as-code migrations for any database \u2014 generate DDL, diff, and migrate via drivers"
733
- ).version("0.1.0-alpha.0").showHelpAfterError("(run `schemic --help` for usage)").addHelpText(
770
+ ).version(version).showHelpAfterError("(run `schemic --help` for usage)").addHelpText(
734
771
  "after",
735
772
  `
736
773
  Examples:
@@ -1280,7 +1317,7 @@ configFlag(
1280
1317
  );
1281
1318
  if (existsSync3(target))
1282
1319
  throw new Error(`${relative2(config.root, target)} already exists.`);
1283
- mkdirSync3(dirname2(target), { recursive: true });
1320
+ mkdirSync3(dirname3(target), { recursive: true });
1284
1321
  writeFileSync3(target, content);
1285
1322
  console.log(
1286
1323
  `${ok2(relative2(config.root, target))} ${style2.dim("\u2014 author its fields, then `schemic gen`")}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schemic/cli",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "The Schemic CLI — driver-agnostic schema migrations (gen/migrate/diff/push/pull/check).",
5
5
  "license": "MIT",
6
6
  "author": "Vertio Solutions",
@@ -35,14 +35,14 @@
35
35
  "lint": "biome check ."
36
36
  },
37
37
  "dependencies": {
38
- "@schemic/core": "0.1.0-alpha.2",
38
+ "@schemic/core": "0.1.0-alpha.4",
39
39
  "commander": "^14.0.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@biomejs/biome": "^2.3.11",
43
43
  "@electric-sql/pglite": "^0.5.2",
44
- "@schemic/postgres": "0.1.0-alpha.2",
45
- "@schemic/surrealdb": "0.1.0-alpha.2",
44
+ "@schemic/postgres": "0.1.0-alpha.4",
45
+ "@schemic/surrealdb": "0.1.0-alpha.4",
46
46
  "@types/bun": "latest",
47
47
  "surrealdb": "^2.0.3",
48
48
  "tsup": "^8",
package/src/cli/index.ts CHANGED
@@ -6,6 +6,9 @@ import {
6
6
  } from "node:fs";
7
7
  import { dirname, join, relative } from "node:path";
8
8
  import { createInterface } from "node:readline/promises";
9
+ // The CLI's own version — sourced from package.json (inlined at build) so it never drifts from the
10
+ // published package version the way a hardcoded string does.
11
+ import { version as CLI_VERSION } from "../../package.json";
9
12
  import {
10
13
  actionLabel,
11
14
  applyPull,
@@ -284,7 +287,7 @@ program
284
287
  .description(
285
288
  "Schema-as-code migrations for any database — generate DDL, diff, and migrate via drivers",
286
289
  )
287
- .version("0.1.0-alpha.0")
290
+ .version(CLI_VERSION)
288
291
  .showHelpAfterError("(run `schemic --help` for usage)")
289
292
  .addHelpText(
290
293
  "after",
@@ -10,8 +10,9 @@
10
10
  // connects the sibling on demand (the dependency graph falls out of access; cycles error) and we close
11
11
  // anything it opened once resolution settles. The returned configs are connected FRESH by each command.
12
12
 
13
+ import { readFileSync } from "node:fs";
13
14
  import { createRequire } from "node:module";
14
- import { join } from "node:path";
15
+ import { dirname, join } from "node:path";
15
16
  import { pathToFileURL } from "node:url";
16
17
  import {
17
18
  type ConnectionEntry,
@@ -32,27 +33,77 @@ import {
32
33
  * (`@schemic/<name>`) that self-register with the core registry on import; the CLI itself contains no
33
34
  * dialect code and discovers the driver from the project's connection config at runtime. Idempotent.
34
35
  */
36
+ /** Pick a package's COMPILED entry from its exports, deliberately skipping the `bun` condition. */
37
+ function pickCompiled(node: unknown): string | undefined {
38
+ if (typeof node === "string") return node;
39
+ if (node && typeof node === "object") {
40
+ const o = node as Record<string, unknown>;
41
+ // import/default/require are the published (compiled) conditions; never `bun` (raw src/*.ts).
42
+ return (
43
+ pickCompiled(o.import) ??
44
+ pickCompiled(o.default) ??
45
+ pickCompiled(o.require)
46
+ );
47
+ }
48
+ return undefined;
49
+ }
50
+
51
+ /**
52
+ * Resolve a driver package to its COMPILED entry file, from `base`'s module scope. We read the
53
+ * manifest and pick the `import`/`default` export rather than letting the runtime choose the `bun`
54
+ * export condition (which points at raw `src/*.ts` for monorepo dev) — loading a PUBLISHED package's
55
+ * TypeScript source is fragile (every transitive src import must re-resolve at the consumer).
56
+ */
57
+ function compiledEntry(pkg: string, base: string): string | null {
58
+ try {
59
+ const manifestPath = createRequire(base).resolve(`${pkg}/package.json`);
60
+ const m = JSON.parse(readFileSync(manifestPath, "utf8")) as {
61
+ exports?: Record<string, unknown>;
62
+ module?: string;
63
+ main?: string;
64
+ };
65
+ const rel =
66
+ pickCompiled(m.exports?.["."]) ?? m.module ?? m.main ?? "index.js";
67
+ return join(dirname(manifestPath), rel);
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
35
73
  export async function ensureDriver(name: string): Promise<void> {
36
74
  if (driverNames().includes(name)) return;
37
75
  const pkg = `@schemic/${name}`;
38
- // Resolve the driver from the USER's project (cwd) first, then fall back to the CLI's own location.
39
- // The CLI is often run via `bunx`/`npx` from a temp dir, so a plain `import(pkg)` (resolved relative
40
- // to the CLI module) would MISS a driver installed in the user's project — resolve from cwd instead.
41
- try {
42
- const fromCwd = createRequire(join(process.cwd(), "noop.js"));
43
- await import(pathToFileURL(fromCwd.resolve(pkg)).href);
44
- } catch {
76
+ // Try the USER's project (cwd) first, then the CLI's own module scope — the CLI is often run via
77
+ // `bunx`/`npx` from a temp dir, so a driver installed in the user's project must be found by cwd.
78
+ let lastErr: unknown;
79
+ let loaded = false;
80
+ for (const base of [join(process.cwd(), "noop.js"), import.meta.url]) {
81
+ const entry = compiledEntry(pkg, base);
82
+ if (!entry) continue;
83
+ try {
84
+ await import(pathToFileURL(entry).href);
85
+ loaded = true;
86
+ break;
87
+ } catch (e) {
88
+ lastErr = e;
89
+ }
90
+ }
91
+ // Last resort: a plain specifier import (covers an unbuilt monorepo checkout, where lib is absent).
92
+ if (!loaded) {
45
93
  try {
46
94
  await import(pkg);
95
+ loaded = true;
47
96
  } catch (e) {
48
- throw new Error(
49
- `could not load the "${name}" database driver (package ${pkg}). ` +
50
- `Install it in your project, then re-run:\n bun add ${pkg}\n (${
51
- e instanceof Error ? e.message : String(e)
52
- })`,
53
- );
97
+ lastErr = e;
54
98
  }
55
99
  }
100
+ if (!loaded)
101
+ throw new Error(
102
+ `could not load the "${name}" database driver (package ${pkg}). ` +
103
+ `Install it in your project, then re-run:\n bun add ${pkg}\n (${
104
+ lastErr instanceof Error ? lastErr.message : String(lastErr)
105
+ })`,
106
+ );
56
107
  if (!driverNames().includes(name))
57
108
  throw new Error(`package ${pkg} did not register a "${name}" driver.`);
58
109
  }