@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 +50 -13
- package/package.json +4 -4
- package/src/cli/index.ts +4 -1
- package/src/cli/resolve.ts +65 -14
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
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
45
|
-
"@schemic/surrealdb": "0.1.0-alpha.
|
|
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(
|
|
290
|
+
.version(CLI_VERSION)
|
|
288
291
|
.showHelpAfterError("(run `schemic --help` for usage)")
|
|
289
292
|
.addHelpText(
|
|
290
293
|
"after",
|
package/src/cli/resolve.ts
CHANGED
|
@@ -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
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
}
|