@basaltkit/cli 1.1.0 → 1.2.1
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/README.md +7 -1
- package/dist/builtins.d.ts +16 -0
- package/dist/builtins.js +36 -0
- package/dist/command.d.ts +25 -0
- package/dist/command.js +3 -0
- package/dist/dev.d.ts +49 -0
- package/dist/dev.js +126 -0
- package/dist/index.d.ts +9 -193
- package/dist/index.js +9 -420
- package/dist/io.d.ts +14 -0
- package/dist/io.js +49 -0
- package/dist/parse.d.ts +7 -0
- package/dist/parse.js +23 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +12 -0
- package/dist/publish.d.ts +35 -0
- package/dist/publish.js +110 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.js +36 -0
- package/dist/upgrade.d.ts +48 -0
- package/dist/upgrade.js +112 -0
- package/package.json +9 -10
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type BasaltApp } from '@basaltkit/core';
|
|
2
|
+
import type { CommandIo } from './command.js';
|
|
3
|
+
export interface RunCliOptions {
|
|
4
|
+
app: BasaltApp;
|
|
5
|
+
/** Defaults to process.argv.slice(2). */
|
|
6
|
+
argv?: string[];
|
|
7
|
+
/** Defaults to console output — swap in tests. */
|
|
8
|
+
io?: CommandIo;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Boots the app, resolves the command (built-ins + everything registered in
|
|
12
|
+
* the 'commands' metadata bucket), runs it and shuts the app down.
|
|
13
|
+
* Returns the process exit code instead of calling process.exit — the bin
|
|
14
|
+
* wrapper decides what to do with it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runCli(options: RunCliOptions): Promise<number>;
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ensureMetadata } from '@basaltkit/core';
|
|
2
|
+
import { builtinCommands } from './builtins.js';
|
|
3
|
+
import { consoleIo } from './io.js';
|
|
4
|
+
import { parseArgv } from './parse.js';
|
|
5
|
+
/**
|
|
6
|
+
* Boots the app, resolves the command (built-ins + everything registered in
|
|
7
|
+
* the 'commands' metadata bucket), runs it and shuts the app down.
|
|
8
|
+
* Returns the process exit code instead of calling process.exit — the bin
|
|
9
|
+
* wrapper decides what to do with it.
|
|
10
|
+
*/
|
|
11
|
+
export async function runCli(options) {
|
|
12
|
+
const { app } = options;
|
|
13
|
+
const io = options.io ?? consoleIo();
|
|
14
|
+
const { command, args, flags } = parseArgv(options.argv ?? process.argv.slice(2));
|
|
15
|
+
if (app.phase === 'created')
|
|
16
|
+
await app.boot();
|
|
17
|
+
try {
|
|
18
|
+
const registered = ensureMetadata(app.container).get('commands');
|
|
19
|
+
const commands = [...builtinCommands(), ...registered];
|
|
20
|
+
if (command === undefined || command === 'list') {
|
|
21
|
+
io.log('Available commands:');
|
|
22
|
+
io.table(commands.map(({ name, description }) => ({ command: name, description: description ?? '' })));
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
const found = commands.find((candidate) => candidate.name === command);
|
|
26
|
+
if (!found) {
|
|
27
|
+
io.error(`Unknown command "${command}". Run "basalt list" to see what is available.`);
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
const code = await found.handle({ app, container: app.container, io, args, flags });
|
|
31
|
+
return code ?? 0;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await app.shutdown();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type CommandDefinition } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Filesystem surface an upgrade migration works against. Injected so migrations
|
|
4
|
+
* (and `runUpgrade`) are testable with an in-memory tree — no disk needed.
|
|
5
|
+
*/
|
|
6
|
+
export interface UpgradeFs {
|
|
7
|
+
/** Recursively lists files under `dir` (relative paths). Skips node_modules/dist/.git. */
|
|
8
|
+
list(dir: string): Promise<string[]>;
|
|
9
|
+
read(path: string): Promise<string>;
|
|
10
|
+
write(path: string, content: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/** A single edit a migration wants to make — surfaced for `--dry` before writing. */
|
|
13
|
+
export interface Edit {
|
|
14
|
+
path: string;
|
|
15
|
+
before: string;
|
|
16
|
+
after: string;
|
|
17
|
+
}
|
|
18
|
+
/** A versioned, idempotent codemod. `plan` computes edits without writing them. */
|
|
19
|
+
export interface Migration {
|
|
20
|
+
id: string;
|
|
21
|
+
description: string;
|
|
22
|
+
plan(fs: UpgradeFs, dir: string): Promise<Edit[]>;
|
|
23
|
+
}
|
|
24
|
+
export interface UpgradeReport {
|
|
25
|
+
migration: string;
|
|
26
|
+
changed: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A codemod that rewrites the deprecated `@machize/*` npm scope to `@basaltkit/*`
|
|
30
|
+
* across package.json and source imports. Unambiguous (scoped rename), so a plain
|
|
31
|
+
* text replace is safe and idempotent.
|
|
32
|
+
*/
|
|
33
|
+
export declare const renameMachizeScope: Migration;
|
|
34
|
+
/** All migrations shipped with the CLI, in application order. */
|
|
35
|
+
export declare const MIGRATIONS: Migration[];
|
|
36
|
+
/**
|
|
37
|
+
* Runs the selected migrations. With `dry`, computes and reports edits without
|
|
38
|
+
* writing. Returns a per-migration report of the files it changed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runUpgrade(migrations: Migration[], fs: UpgradeFs, options: {
|
|
41
|
+
dir: string;
|
|
42
|
+
dry?: boolean;
|
|
43
|
+
only?: string;
|
|
44
|
+
}): Promise<UpgradeReport[]>;
|
|
45
|
+
/** Node-backed {@link UpgradeFs}. Recursive, skipping build/vcs directories. */
|
|
46
|
+
export declare function nodeUpgradeFs(): UpgradeFs;
|
|
47
|
+
/** `basalt upgrade [--dir=<path>] [--dry] [--only=<migration-id>]` */
|
|
48
|
+
export declare const upgradeCommand: CommandDefinition;
|
package/dist/upgrade.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { defineCommand } from './command.js';
|
|
2
|
+
/**
|
|
3
|
+
* A codemod that rewrites the deprecated `@machize/*` npm scope to `@basaltkit/*`
|
|
4
|
+
* across package.json and source imports. Unambiguous (scoped rename), so a plain
|
|
5
|
+
* text replace is safe and idempotent.
|
|
6
|
+
*/
|
|
7
|
+
export const renameMachizeScope = {
|
|
8
|
+
id: 'rename-machize-scope',
|
|
9
|
+
description: 'Rewrite the deprecated @machize/* scope to @basaltkit/*',
|
|
10
|
+
async plan(fs, dir) {
|
|
11
|
+
const files = (await fs.list(dir)).filter((p) => /\.(t|j)sx?$|\.json$/.test(p));
|
|
12
|
+
const edits = [];
|
|
13
|
+
for (const path of files) {
|
|
14
|
+
const before = await fs.read(path);
|
|
15
|
+
if (!before.includes('@machize/'))
|
|
16
|
+
continue;
|
|
17
|
+
const after = before.split('@machize/').join('@basaltkit/');
|
|
18
|
+
if (after !== before)
|
|
19
|
+
edits.push({ path, before, after });
|
|
20
|
+
}
|
|
21
|
+
return edits;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
/** All migrations shipped with the CLI, in application order. */
|
|
25
|
+
export const MIGRATIONS = [renameMachizeScope];
|
|
26
|
+
/**
|
|
27
|
+
* Runs the selected migrations. With `dry`, computes and reports edits without
|
|
28
|
+
* writing. Returns a per-migration report of the files it changed.
|
|
29
|
+
*/
|
|
30
|
+
export async function runUpgrade(migrations, fs, options) {
|
|
31
|
+
const selected = options.only ? migrations.filter((m) => m.id === options.only) : migrations;
|
|
32
|
+
const reports = [];
|
|
33
|
+
for (const migration of selected) {
|
|
34
|
+
const edits = await migration.plan(fs, options.dir);
|
|
35
|
+
if (!options.dry) {
|
|
36
|
+
for (const edit of edits)
|
|
37
|
+
await fs.write(edit.path, edit.after);
|
|
38
|
+
}
|
|
39
|
+
reports.push({ migration: migration.id, changed: edits.map((e) => e.path) });
|
|
40
|
+
}
|
|
41
|
+
return reports;
|
|
42
|
+
}
|
|
43
|
+
/** Node-backed {@link UpgradeFs}. Recursive, skipping build/vcs directories. */
|
|
44
|
+
export function nodeUpgradeFs() {
|
|
45
|
+
const SKIP = new Set(['node_modules', 'dist', '.git', '.next', 'build', 'coverage']);
|
|
46
|
+
return {
|
|
47
|
+
async list(dir) {
|
|
48
|
+
const { readdir } = await import('node:fs/promises');
|
|
49
|
+
const { join, relative } = await import('node:path');
|
|
50
|
+
const out = [];
|
|
51
|
+
const walk = async (current) => {
|
|
52
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
if (!SKIP.has(entry.name))
|
|
56
|
+
await walk(join(current, entry.name));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
out.push(relative(dir, join(current, entry.name)));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
await walk(dir);
|
|
64
|
+
return out;
|
|
65
|
+
},
|
|
66
|
+
async read(path) {
|
|
67
|
+
const { readFile } = await import('node:fs/promises');
|
|
68
|
+
const { isAbsolute, join } = await import('node:path');
|
|
69
|
+
return readFile(isAbsolute(path) ? path : join(process.cwd(), path), 'utf8');
|
|
70
|
+
},
|
|
71
|
+
async write(path, content) {
|
|
72
|
+
const { writeFile } = await import('node:fs/promises');
|
|
73
|
+
const { isAbsolute, join } = await import('node:path');
|
|
74
|
+
await writeFile(isAbsolute(path) ? path : join(process.cwd(), path), content, 'utf8');
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** `basalt upgrade [--dir=<path>] [--dry] [--only=<migration-id>]` */
|
|
79
|
+
export const upgradeCommand = defineCommand({
|
|
80
|
+
name: 'upgrade',
|
|
81
|
+
description: 'Apply framework upgrade codemods (--dry to preview, --only=<id>)',
|
|
82
|
+
async handle({ io, flags }) {
|
|
83
|
+
const dir = typeof flags['dir'] === 'string' ? flags['dir'] : process.cwd();
|
|
84
|
+
const dry = flags['dry'] === true;
|
|
85
|
+
const only = typeof flags['only'] === 'string' ? flags['only'] : undefined;
|
|
86
|
+
if (only && !MIGRATIONS.some((m) => m.id === only)) {
|
|
87
|
+
io.error(`Unknown migration "${only}". Available: ${MIGRATIONS.map((m) => m.id).join(', ')}.`);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
const reports = await runUpgrade(MIGRATIONS, nodeUpgradeFs(), {
|
|
91
|
+
dir,
|
|
92
|
+
dry,
|
|
93
|
+
...(only ? { only } : {}),
|
|
94
|
+
});
|
|
95
|
+
let total = 0;
|
|
96
|
+
for (const report of reports) {
|
|
97
|
+
if (report.changed.length === 0)
|
|
98
|
+
continue;
|
|
99
|
+
total += report.changed.length;
|
|
100
|
+
io.log(`${dry ? '[dry] ' : ''}${report.migration}: ${report.changed.length} file(s)`);
|
|
101
|
+
for (const path of report.changed)
|
|
102
|
+
io.log(` ${path}`);
|
|
103
|
+
}
|
|
104
|
+
if (total === 0)
|
|
105
|
+
io.log('Nothing to upgrade — everything is up to date.');
|
|
106
|
+
else if (dry)
|
|
107
|
+
io.log(`\n${total} file(s) would change. Re-run without --dry to apply.`);
|
|
108
|
+
else
|
|
109
|
+
io.log(`\nApplied ${total} change(s).`);
|
|
110
|
+
return 0;
|
|
111
|
+
},
|
|
112
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basaltkit/cli",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "The 'basalt' command framework for Basalt apps: define commands, run them against a booted app, and inspect routes and schedules.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,13 +14,12 @@
|
|
|
14
14
|
"dist"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@basaltkit/core": "^1.
|
|
17
|
+
"@basaltkit/core": "^1.1.2"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"vitest": "^3.1.0",
|
|
20
|
+
"@types/node": "^26.3.0",
|
|
21
|
+
"typescript": "^7.0.2",
|
|
22
|
+
"vitest": "^4.1.11",
|
|
24
23
|
"@basaltkit/tsconfig": "^0.24.0"
|
|
25
24
|
},
|
|
26
25
|
"publishConfig": {
|
|
@@ -28,11 +27,11 @@
|
|
|
28
27
|
},
|
|
29
28
|
"repository": {
|
|
30
29
|
"type": "git",
|
|
31
|
-
"url": "git+https://github.com/
|
|
30
|
+
"url": "git+https://github.com/basaltkit/basalt.git",
|
|
32
31
|
"directory": "packages/cli"
|
|
33
32
|
},
|
|
34
|
-
"homepage": "https://github.com/
|
|
35
|
-
"bugs": "https://github.com/
|
|
33
|
+
"homepage": "https://github.com/basaltkit/basalt/tree/main/packages/cli#readme",
|
|
34
|
+
"bugs": "https://github.com/basaltkit/basalt/issues",
|
|
36
35
|
"keywords": [
|
|
37
36
|
"basalt",
|
|
38
37
|
"typescript",
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
"commands"
|
|
41
40
|
],
|
|
42
41
|
"scripts": {
|
|
43
|
-
"build": "
|
|
42
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
43
|
"test": "vitest run",
|
|
45
44
|
"typecheck": "tsc --noEmit"
|
|
46
45
|
}
|