@mudah-cli/mudah 0.8.0 → 0.9.2
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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/autocomplete.d.ts +8 -0
- package/dist/autocomplete.js +38 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/built-ins.d.ts +9 -0
- package/dist/built-ins.js +144 -0
- package/dist/built-ins.js.map +1 -0
- package/dist/commands/autocomplete.command.d.ts +11 -0
- package/dist/commands/autocomplete.command.js +16 -0
- package/dist/commands/autocomplete.command.js.map +1 -0
- package/dist/commands/build.command.d.ts +1 -0
- package/dist/commands/build.command.js +2 -1
- package/dist/commands/build.command.js.map +1 -1
- package/dist/commands/cache.command.d.ts +11 -0
- package/dist/commands/cache.command.js +47 -0
- package/dist/commands/cache.command.js.map +1 -0
- package/dist/commands/complete.command.d.ts +13 -0
- package/dist/commands/complete.command.js +23 -0
- package/dist/commands/complete.command.js.map +1 -0
- package/dist/commands/config.diff.command.d.ts +8 -0
- package/dist/commands/config.diff.command.js +96 -0
- package/dist/commands/config.diff.command.js.map +1 -0
- package/dist/commands/config.set.command.d.ts +8 -0
- package/dist/commands/config.set.command.js +66 -0
- package/dist/commands/config.set.command.js.map +1 -0
- package/dist/commands/config.show.command.d.ts +8 -0
- package/dist/commands/config.show.command.js +38 -0
- package/dist/commands/config.show.command.js.map +1 -0
- package/dist/commands/config.source.command.d.ts +8 -0
- package/dist/commands/config.source.command.js +42 -0
- package/dist/commands/config.source.command.js.map +1 -0
- package/dist/commands/config.validate.command.d.ts +14 -0
- package/dist/commands/config.validate.command.js +63 -0
- package/dist/commands/config.validate.command.js.map +1 -0
- package/dist/commands/dev.command.d.ts +1 -0
- package/dist/commands/dev.command.js +1 -0
- package/dist/commands/dev.command.js.map +1 -1
- package/dist/commands/docs.widgets.command.d.ts +11 -0
- package/dist/commands/docs.widgets.command.js +17 -0
- package/dist/commands/docs.widgets.command.js.map +1 -0
- package/dist/commands/doctor.command.d.ts +1 -0
- package/dist/commands/doctor.command.js +45 -11
- package/dist/commands/doctor.command.js.map +1 -1
- package/dist/commands/gallery.command.d.ts +11 -0
- package/dist/commands/gallery.command.js +84 -0
- package/dist/commands/gallery.command.js.map +1 -0
- package/dist/commands/graph.command.d.ts +10 -0
- package/dist/commands/graph.command.js +21 -0
- package/dist/commands/graph.command.js.map +1 -0
- package/dist/commands/help.command.d.ts +1 -0
- package/dist/commands/help.command.js +2 -2
- package/dist/commands/help.command.js.map +1 -1
- package/dist/commands/info.command.d.ts +8 -0
- package/dist/commands/info.command.js +61 -0
- package/dist/commands/info.command.js.map +1 -0
- package/dist/commands/lsp.command.d.ts +12 -0
- package/dist/commands/lsp.command.js +57 -0
- package/dist/commands/lsp.command.js.map +1 -0
- package/dist/commands/make.command.d.ts +5 -0
- package/dist/commands/make.command.js +270 -48
- package/dist/commands/make.command.js.map +1 -1
- package/dist/commands/plugins.list.command.d.ts +12 -0
- package/dist/commands/plugins.list.command.js +29 -0
- package/dist/commands/plugins.list.command.js.map +1 -0
- package/dist/commands/plugins.update.command.d.ts +29 -0
- package/dist/commands/plugins.update.command.js +78 -0
- package/dist/commands/plugins.update.command.js.map +1 -0
- package/dist/commands/plugins.watch.command.d.ts +10 -0
- package/dist/commands/plugins.watch.command.js +37 -0
- package/dist/commands/plugins.watch.command.js.map +1 -0
- package/dist/commands/replay.command.d.ts +10 -0
- package/dist/commands/replay.command.js +50 -0
- package/dist/commands/replay.command.js.map +1 -0
- package/dist/commands/telemetry.command.d.ts +12 -0
- package/dist/commands/telemetry.command.js +55 -0
- package/dist/commands/telemetry.command.js.map +1 -0
- package/dist/commands/test.command.d.ts +13 -0
- package/dist/commands/test.command.js +83 -0
- package/dist/commands/test.command.js.map +1 -0
- package/dist/commands/tutorial.command.d.ts +12 -0
- package/dist/commands/tutorial.command.js +57 -0
- package/dist/commands/tutorial.command.js.map +1 -0
- package/dist/commands/version.command.d.ts +1 -0
- package/dist/commands/version.command.js +1 -0
- package/dist/commands/version.command.js.map +1 -1
- package/dist/commands/watch.command.d.ts +13 -0
- package/dist/commands/watch.command.js +62 -0
- package/dist/commands/watch.command.js.map +1 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/lsp.d.ts +147 -0
- package/dist/lsp.js +506 -0
- package/dist/lsp.js.map +1 -0
- package/dist/run.d.ts +4 -2
- package/dist/run.js +355 -38
- package/dist/run.js.map +1 -1
- package/dist/watcher.d.ts +20 -1
- package/dist/watcher.js +41 -4
- package/dist/watcher.js.map +1 -1
- package/package.json +27 -10
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Command } from '@mudah-cli/console';
|
|
2
|
-
import { renderCommandHelp, renderCommandList } from '@mudah-cli/console';
|
|
1
|
+
import { Command, renderCommandHelp, renderCommandList } from '@mudah-cli/console';
|
|
3
2
|
/**
|
|
4
3
|
* Built-in `help` command: lists all commands, or shows details for one.
|
|
5
4
|
*/
|
|
6
5
|
export default class HelpCommand extends Command {
|
|
7
6
|
signature = 'help {command?}';
|
|
8
7
|
description = 'Show help for the CLI or a specific command';
|
|
8
|
+
descriptionKey = 'cmd.help.description';
|
|
9
9
|
kernel;
|
|
10
10
|
constructor(kernel) {
|
|
11
11
|
super();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.command.js","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,
|
|
1
|
+
{"version":3,"file":"help.command.js","sourceRoot":"","sources":["../../src/commands/help.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAsB,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvG;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC9C,SAAS,GAAG,iBAAiB,CAAC;IAC9B,WAAW,GAAG,6CAA6C,CAAC;IAC5D,cAAc,GAAG,sBAAsB,CAAC;IAEhC,MAAM,CAAgB;IAE9B,YAAY,MAAqB;QAC/B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,MAAM,IAAI,EAAE,kCAAkC,CAAC,CAAC;YAC5F,CAAC;YACD,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAClG,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from '@mudah-cli/console';
|
|
2
|
+
/** Built-in `info` command: dump runtime, app, and config info. */
|
|
3
|
+
export default class InfoCommand extends Command {
|
|
4
|
+
signature: string;
|
|
5
|
+
description: string;
|
|
6
|
+
descriptionKey: string;
|
|
7
|
+
handle(): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command } from '@mudah-cli/console';
|
|
2
|
+
/** Built-in `info` command: dump runtime, app, and config info. */
|
|
3
|
+
export default class InfoCommand extends Command {
|
|
4
|
+
signature = 'info [--json]';
|
|
5
|
+
description = 'Show runtime, app, and config information';
|
|
6
|
+
descriptionKey = 'cmd.info.description';
|
|
7
|
+
async handle() {
|
|
8
|
+
const app = this.app;
|
|
9
|
+
const output = this.output;
|
|
10
|
+
const plugins = typeof app.plugins === 'function' ? app.plugins() : [];
|
|
11
|
+
const pluginSummary = plugins.map((plugin) => ({
|
|
12
|
+
name: plugin.name,
|
|
13
|
+
providers: plugin.providers.length,
|
|
14
|
+
}));
|
|
15
|
+
let health;
|
|
16
|
+
if (typeof app.health === 'function') {
|
|
17
|
+
health = await app.health();
|
|
18
|
+
}
|
|
19
|
+
if (output.isMachineReadable || this.option('json') === true) {
|
|
20
|
+
output.emit('data', 'info', {
|
|
21
|
+
name: app.manifest.name,
|
|
22
|
+
version: app.manifest.version,
|
|
23
|
+
node: process.version,
|
|
24
|
+
plugins: pluginSummary,
|
|
25
|
+
...(health === undefined ? {} : { health }),
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
output.section('Runtime');
|
|
30
|
+
output.keyValue('node', process.version);
|
|
31
|
+
output.keyValue('platform', `${process.platform} ${process.arch}`);
|
|
32
|
+
output.section('Application');
|
|
33
|
+
output.keyValue('name', app.manifest.name);
|
|
34
|
+
output.keyValue('version', app.manifest.version);
|
|
35
|
+
output.keyValue('basePath', app.basePath);
|
|
36
|
+
output.keyValue('bin', app.manifest.bin);
|
|
37
|
+
if (pluginSummary.length > 0) {
|
|
38
|
+
output.section('Plugins');
|
|
39
|
+
for (const plugin of pluginSummary) {
|
|
40
|
+
output.keyValue(plugin.name, `${plugin.providers} provider(s)`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (health !== undefined && health.length > 0) {
|
|
44
|
+
output.section('Health');
|
|
45
|
+
for (const row of health) {
|
|
46
|
+
const detail = row.detail ? ` ${row.detail}` : '';
|
|
47
|
+
output.keyValue(row.provider, `${row.status} (${row.latencyMs}ms)${detail}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const configKeys = Object.keys(app.config().all());
|
|
51
|
+
if (configKeys.length > 0) {
|
|
52
|
+
output.section('Configuration');
|
|
53
|
+
for (const key of configKeys) {
|
|
54
|
+
output.keyValue(key, String(app.config().get(key)));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
output.line();
|
|
58
|
+
output.success('info complete');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=info.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.command.js","sourceRoot":"","sources":["../../src/commands/info.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@mudah-cli/console';
|
|
2
|
+
/**
|
|
3
|
+
* Built-in `lsp` command: stdio JSON-RPC (Content-Length framed, with a
|
|
4
|
+
* newline-delimited fallback for probes).
|
|
5
|
+
*/
|
|
6
|
+
export default class LspCommand extends Command {
|
|
7
|
+
signature: string;
|
|
8
|
+
description: string;
|
|
9
|
+
descriptionKey: string;
|
|
10
|
+
handle(): Promise<number>;
|
|
11
|
+
private serveStdio;
|
|
12
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from '@mudah-cli/console';
|
|
2
|
+
import { decodeLspFrames, encodeLspFrame, handleLspMessage, initializeResult, publishDiagnostics, uriOf, } from '../lsp.js';
|
|
3
|
+
/**
|
|
4
|
+
* Built-in `lsp` command: stdio JSON-RPC (Content-Length framed, with a
|
|
5
|
+
* newline-delimited fallback for probes).
|
|
6
|
+
*/
|
|
7
|
+
export default class LspCommand extends Command {
|
|
8
|
+
signature = 'lsp [--stdio] [--probe]';
|
|
9
|
+
description = 'Language server for mudah.json keys and command signatures';
|
|
10
|
+
descriptionKey = 'cmd.lsp.description';
|
|
11
|
+
async handle() {
|
|
12
|
+
if (this.option('probe') === true) {
|
|
13
|
+
const ready = initializeResult();
|
|
14
|
+
this.output.raw(`${JSON.stringify({ jsonrpc: '2.0', id: 1, result: ready })}\n`);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
if (this.option('stdio') === true) {
|
|
18
|
+
return this.serveStdio();
|
|
19
|
+
}
|
|
20
|
+
this.output.success('mudah-lsp ready');
|
|
21
|
+
this.output.muted('Pass --stdio to speak JSON-RPC on stdin/stdout, or --probe to print initialize.');
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
serveStdio() {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let buffer = '';
|
|
27
|
+
const write = (msg) => {
|
|
28
|
+
process.stdout.write(encodeLspFrame(msg));
|
|
29
|
+
};
|
|
30
|
+
process.stdin.setEncoding('utf8');
|
|
31
|
+
process.stdin.on('data', (chunk) => {
|
|
32
|
+
buffer += chunk;
|
|
33
|
+
const decoded = decodeLspFrames(buffer);
|
|
34
|
+
buffer = decoded.rest;
|
|
35
|
+
for (const msg of decoded.messages) {
|
|
36
|
+
if (msg.method === 'exit') {
|
|
37
|
+
resolve(0);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const reply = handleLspMessage(msg);
|
|
41
|
+
if (reply !== undefined)
|
|
42
|
+
write(reply);
|
|
43
|
+
if (msg.method === 'textDocument/didOpen' || msg.method === 'textDocument/didChange') {
|
|
44
|
+
const notice = publishDiagnostics(uriOf(msg.params));
|
|
45
|
+
if (notice !== undefined)
|
|
46
|
+
write(notice);
|
|
47
|
+
}
|
|
48
|
+
if (msg.method === 'shutdown') {
|
|
49
|
+
resolve(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
process.stdin.on('end', () => resolve(0));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=lsp.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lsp.command.js","sourceRoot":"","sources":["../../src/commands/lsp.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAEhB,kBAAkB,EAClB,KAAK,GACN,MAAM,WAAW,CAAC;AAEnB;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,OAAO;IAC7C,SAAS,GAAG,yBAAyB,CAAC;IACtC,WAAW,GAAG,4DAA4D,CAAC;IAC3E,cAAc,GAAG,qBAAqB,CAAC;IAEvC,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;YACjF,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iFAAiF,CAAC,CAAC;QACrG,OAAO,CAAC,CAAC;IACX,CAAC;IAEO,UAAU;QAChB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,CAAC,GAAe,EAAQ,EAAE;gBACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBACzC,MAAM,IAAI,KAAK,CAAC;gBAChB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACxC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gBACtB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACnC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;wBAC1B,OAAO,CAAC,CAAC,CAAC,CAAC;wBACX,OAAO;oBACT,CAAC;oBACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACpC,IAAI,KAAK,KAAK,SAAS;wBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBACtC,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAsB,IAAI,GAAG,CAAC,MAAM,KAAK,wBAAwB,EAAE,CAAC;wBACrF,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;wBACrD,IAAI,MAAM,KAAK,SAAS;4BAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC1C,CAAC;oBACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;wBAC9B,OAAO,CAAC,CAAC,CAAC,CAAC;oBACb,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -5,5 +5,10 @@ import { Command } from '@mudah-cli/console';
|
|
|
5
5
|
export default class MakeCommand extends Command {
|
|
6
6
|
signature: string;
|
|
7
7
|
description: string;
|
|
8
|
+
descriptionKey: string;
|
|
8
9
|
handle(): Promise<void>;
|
|
10
|
+
private writeTui;
|
|
11
|
+
private hintNext;
|
|
12
|
+
private writeNew;
|
|
13
|
+
private writePlugin;
|
|
9
14
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
1
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { isAbsolute, join, relative } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
3
5
|
import { Command } from '@mudah-cli/console';
|
|
6
|
+
/** Peer range for a generated plugin, matching the installed framework version. */
|
|
7
|
+
function mudahRange() {
|
|
8
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
|
|
9
|
+
return `^${pkg.version}`;
|
|
10
|
+
}
|
|
4
11
|
function kebab(name) {
|
|
5
12
|
return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
6
13
|
}
|
|
@@ -10,6 +17,8 @@ function pascal(name) {
|
|
|
10
17
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
11
18
|
.join('');
|
|
12
19
|
}
|
|
20
|
+
const TUI_RECIPES = ['picker', 'wizard', 'dashboard', 'form'];
|
|
21
|
+
const HINT = 'Use: make command {name} | make tui {picker|wizard|dashboard|form} {name?} | make provider {name} | make config {name} | make plugin {name}';
|
|
13
22
|
const COMMAND_TEMPLATE = (command, className) => `import { Command } from '@mudah-cli/mudah';
|
|
14
23
|
|
|
15
24
|
export default class ${className} extends Command {
|
|
@@ -17,14 +26,30 @@ export default class ${className} extends Command {
|
|
|
17
26
|
description = 'Describe ${command} in one line.';
|
|
18
27
|
|
|
19
28
|
async handle() {
|
|
20
|
-
this.output.success(
|
|
29
|
+
this.output.success(\`Hello from ${command}!\`);
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
`;
|
|
33
|
+
const COMMAND_TEST = (command) => `import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { describe, it } from 'vitest';
|
|
35
|
+
import { TestApp } from '@mudah-cli/mudah/testing';
|
|
36
|
+
|
|
37
|
+
const appDir = fileURLToPath(new URL('..', import.meta.url));
|
|
38
|
+
|
|
39
|
+
describe('${command}', () => {
|
|
40
|
+
it('runs', async () => {
|
|
41
|
+
const app = await TestApp.create({ cwd: appDir });
|
|
42
|
+
const result = await app.dispatch(['${command}']);
|
|
43
|
+
result.exit(0).outContains('Hello from ${command}!');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
`;
|
|
24
47
|
const PROVIDER_TEMPLATE = (className) => `import { ServiceProvider } from '@mudah-cli/mudah';
|
|
25
48
|
|
|
26
49
|
export default class ${className} extends ServiceProvider {
|
|
27
|
-
register(): void {
|
|
50
|
+
register(): void {
|
|
51
|
+
// this.app.singleton('db', () => new Db());
|
|
52
|
+
}
|
|
28
53
|
|
|
29
54
|
boot(): void {}
|
|
30
55
|
}
|
|
@@ -35,23 +60,65 @@ export default defineConfig({
|
|
|
35
60
|
// Add configuration for this component.
|
|
36
61
|
});
|
|
37
62
|
`;
|
|
38
|
-
const
|
|
63
|
+
const PLUGIN_PACKAGE = (name) => `${JSON.stringify({
|
|
64
|
+
name,
|
|
65
|
+
version: '0.1.0',
|
|
66
|
+
private: true,
|
|
67
|
+
description: `${name} — a Mudah plugin`,
|
|
68
|
+
type: 'module',
|
|
69
|
+
license: 'MIT',
|
|
70
|
+
keywords: ['mudah-plugin', 'cli'],
|
|
71
|
+
exports: { '.': './src/index.ts' },
|
|
72
|
+
dependencies: { '@mudah-cli/mudah': mudahRange() },
|
|
73
|
+
}, null, 2)}\n`;
|
|
74
|
+
const PLUGIN_INDEX = (name, className) => `import { Command, ServiceProvider } from '@mudah-cli/mudah';
|
|
75
|
+
|
|
76
|
+
/** Binds services this plugin contributes. */
|
|
77
|
+
export class ${className}Provider extends ServiceProvider {
|
|
78
|
+
register(): void {}
|
|
79
|
+
|
|
80
|
+
boot(): void {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class ${className}HelloCommand extends Command {
|
|
84
|
+
signature = '${name}:hello';
|
|
85
|
+
description = 'Hello from the ${name} plugin';
|
|
86
|
+
groupDescription = '${className} plugin';
|
|
87
|
+
|
|
88
|
+
async handle(): Promise<number> {
|
|
89
|
+
this.output.success('Hello from ${name}');
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const providers = [${className}Provider];
|
|
95
|
+
export const commands = [${className}HelloCommand];
|
|
96
|
+
`;
|
|
39
97
|
const TUI_PICKER = (command, className) => `import { Command } from '@mudah-cli/mudah';
|
|
40
|
-
import {
|
|
98
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
99
|
+
|
|
100
|
+
const ITEMS = ['one', 'two', 'three'];
|
|
41
101
|
|
|
42
102
|
export default class ${className} extends Command {
|
|
43
|
-
signature = '${command}';
|
|
103
|
+
signature = '${command} {item?}';
|
|
44
104
|
description = 'Pick one item';
|
|
45
105
|
|
|
46
106
|
async handle(): Promise<number> {
|
|
107
|
+
const flagged = this.arg('item');
|
|
108
|
+
if (flagged) {
|
|
109
|
+
if (!ITEMS.includes(flagged)) {
|
|
110
|
+
throw this.usageError(\`Unknown item "\${flagged}".\`, \`Known: \${ITEMS.join(', ')}\`);
|
|
111
|
+
}
|
|
112
|
+
this.output.success(\`Picked \${flagged}.\`);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
47
115
|
if (process.stdout.isTTY !== true) {
|
|
48
116
|
this.output.error('This command needs an interactive terminal.');
|
|
117
|
+
this.output.hint('Pass an item: ${command} one');
|
|
49
118
|
return 2;
|
|
50
119
|
}
|
|
51
|
-
const screen = Screen.picker({ title: '${command}', items:
|
|
52
|
-
const
|
|
53
|
-
screen.attach(program);
|
|
54
|
-
const code = await program.run();
|
|
120
|
+
const screen = Screen.picker({ title: '${command}', items: ITEMS });
|
|
121
|
+
const code = await screen.run();
|
|
55
122
|
const picked = screen.result();
|
|
56
123
|
if (picked) this.output.success(\`Picked \${picked}.\`);
|
|
57
124
|
return code;
|
|
@@ -59,16 +126,20 @@ export default class ${className} extends Command {
|
|
|
59
126
|
}
|
|
60
127
|
`;
|
|
61
128
|
const TUI_WIZARD = (command, className) => `import { Command } from '@mudah-cli/mudah';
|
|
62
|
-
import {
|
|
129
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
63
130
|
|
|
64
131
|
export default class ${className} extends Command {
|
|
65
|
-
signature = '${command}';
|
|
132
|
+
signature = '${command} [--env=] [--note=]';
|
|
66
133
|
description = 'Multi-step wizard';
|
|
67
134
|
|
|
68
135
|
async handle(): Promise<number> {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
136
|
+
const envFlag = this.option('env');
|
|
137
|
+
const noteFlag = this.option('note');
|
|
138
|
+
if (typeof envFlag === 'string' || process.stdout.isTTY !== true) {
|
|
139
|
+
const env = typeof envFlag === 'string' && envFlag.length > 0 ? envFlag : 'staging';
|
|
140
|
+
const note = typeof noteFlag === 'string' ? noteFlag : '';
|
|
141
|
+
this.output.success(JSON.stringify({ env, note }));
|
|
142
|
+
return 0;
|
|
72
143
|
}
|
|
73
144
|
const screen = Screen.wizard({
|
|
74
145
|
title: '${command}',
|
|
@@ -77,9 +148,7 @@ export default class ${className} extends Command {
|
|
|
77
148
|
{ name: 'note', kind: 'text', label: 'Note' },
|
|
78
149
|
],
|
|
79
150
|
});
|
|
80
|
-
const
|
|
81
|
-
screen.attach(program);
|
|
82
|
-
const code = await program.run();
|
|
151
|
+
const code = await screen.run();
|
|
83
152
|
const result = screen.result();
|
|
84
153
|
if (result) this.output.success(JSON.stringify(result));
|
|
85
154
|
return code;
|
|
@@ -87,81 +156,234 @@ export default class ${className} extends Command {
|
|
|
87
156
|
}
|
|
88
157
|
`;
|
|
89
158
|
const TUI_DASHBOARD = (command, className) => `import { Command } from '@mudah-cli/mudah';
|
|
90
|
-
import {
|
|
159
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
160
|
+
|
|
161
|
+
const ROWS = [
|
|
162
|
+
['api', 'ok'],
|
|
163
|
+
['worker', 'ok'],
|
|
164
|
+
];
|
|
91
165
|
|
|
92
166
|
export default class ${className} extends Command {
|
|
93
|
-
signature = '${command}';
|
|
167
|
+
signature = '${command} [--dump]';
|
|
94
168
|
description = 'Full-screen dashboard';
|
|
95
169
|
|
|
96
170
|
async handle(): Promise<number> {
|
|
97
|
-
if (process.stdout.isTTY !== true) {
|
|
98
|
-
this.output.
|
|
99
|
-
|
|
171
|
+
if (this.option('dump') === true || process.stdout.isTTY !== true) {
|
|
172
|
+
this.output.section('${command}');
|
|
173
|
+
for (const [name, status] of ROWS) this.output.keyValue(name ?? '', status ?? '');
|
|
174
|
+
return 0;
|
|
100
175
|
}
|
|
101
176
|
const screen = Screen.dashboard({
|
|
102
177
|
title: '${command}',
|
|
103
178
|
sidebar: ['esc to quit', 'drag the split'],
|
|
104
179
|
columns: [{ header: 'name' }, { header: 'status' }],
|
|
105
|
-
rows:
|
|
180
|
+
rows: ROWS,
|
|
106
181
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
182
|
+
return screen.run({ mouse: true });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
const TUI_FORM = (command, className) => `import { Command, s } from '@mudah-cli/mudah';
|
|
187
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
188
|
+
|
|
189
|
+
const schema = s.object({
|
|
190
|
+
name: s.string(),
|
|
191
|
+
live: s.boolean(),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export default class ${className} extends Command {
|
|
195
|
+
signature = '${command} [--name=] [--live]';
|
|
196
|
+
description = 'Schema-backed form';
|
|
197
|
+
|
|
198
|
+
async handle(): Promise<number> {
|
|
199
|
+
const named = this.option('name');
|
|
200
|
+
if (typeof named === 'string' || this.option('live') === true || process.stdout.isTTY !== true) {
|
|
201
|
+
this.output.success(
|
|
202
|
+
JSON.stringify({ name: typeof named === 'string' ? named : '', live: this.option('live') === true }),
|
|
203
|
+
);
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
const screen = Screen.form({ title: '${command}', schema });
|
|
207
|
+
const code = await screen.run();
|
|
208
|
+
const values = screen.result();
|
|
209
|
+
if (values) this.output.success(JSON.stringify(values));
|
|
210
|
+
return code;
|
|
110
211
|
}
|
|
111
212
|
}
|
|
112
213
|
`;
|
|
214
|
+
const TUI_PICKER_TEST = (command) => `import { fileURLToPath } from 'node:url';
|
|
215
|
+
import { describe, expect, it } from 'vitest';
|
|
216
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
217
|
+
import { TestApp, TestTui } from '@mudah-cli/mudah/testing';
|
|
218
|
+
|
|
219
|
+
const appDir = fileURLToPath(new URL('..', import.meta.url));
|
|
220
|
+
|
|
221
|
+
describe('${command}', () => {
|
|
222
|
+
it('picks from an argument without a TTY', async () => {
|
|
223
|
+
const app = await TestApp.create({ cwd: appDir });
|
|
224
|
+
const result = await app.dispatch(['${command}', 'two']);
|
|
225
|
+
result.exit(0).outContains('Picked two.');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('renders the picker', () => {
|
|
229
|
+
const screen = Screen.picker({ title: '${command}', items: ['one', 'two', 'three'] });
|
|
230
|
+
const tui = TestTui.mount(screen.root, { cols: 40, rows: 10 });
|
|
231
|
+
expect(tui.snapshot()).toContain('${command}');
|
|
232
|
+
tui.send('down').send('enter');
|
|
233
|
+
expect(screen.result()).toBe('two');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
`;
|
|
237
|
+
const TUI_SCREEN_TEST = (command, recipe) => {
|
|
238
|
+
if (recipe === 'picker')
|
|
239
|
+
return TUI_PICKER_TEST(command);
|
|
240
|
+
const factory = recipe === 'wizard'
|
|
241
|
+
? `Screen.wizard({ title: '${command}', steps: [{ name: 'env', kind: 'pick', items: ['staging', 'production'] }] })`
|
|
242
|
+
: recipe === 'form'
|
|
243
|
+
? `Screen.form({ title: '${command}', schema: s.object({ name: s.string() }) })`
|
|
244
|
+
: `Screen.dashboard({ title: '${command}', sidebar: ['esc'], columns: [{ header: 'name' }], rows: [['api']] })`;
|
|
245
|
+
const extraImport = recipe === 'form' ? `\nimport { s } from '@mudah-cli/mudah';` : '';
|
|
246
|
+
const cli = recipe === 'wizard'
|
|
247
|
+
? `['${command}', '--env=production']`
|
|
248
|
+
: recipe === 'form'
|
|
249
|
+
? `['${command}', '--name=Ada']`
|
|
250
|
+
: `['${command}', '--dump']`;
|
|
251
|
+
const expectOut = recipe === 'wizard' ? 'production' : recipe === 'form' ? 'Ada' : 'api';
|
|
252
|
+
return `import { fileURLToPath } from 'node:url';
|
|
253
|
+
import { describe, expect, it } from 'vitest';
|
|
254
|
+
import { Screen } from '@mudah-cli/mudah/tui';
|
|
255
|
+
import { TestApp, TestTui } from '@mudah-cli/mudah/testing';${extraImport}
|
|
256
|
+
|
|
257
|
+
const appDir = fileURLToPath(new URL('..', import.meta.url));
|
|
258
|
+
|
|
259
|
+
describe('${command}', () => {
|
|
260
|
+
it('runs without a TTY', async () => {
|
|
261
|
+
const app = await TestApp.create({ cwd: appDir });
|
|
262
|
+
const result = await app.dispatch(${cli});
|
|
263
|
+
result.exit(0).outContains('${expectOut}');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('renders the ${recipe}', () => {
|
|
267
|
+
const screen = ${factory};
|
|
268
|
+
const tui = TestTui.mount(screen.root, { cols: 60, rows: 16 });
|
|
269
|
+
expect(tui.snapshot()).toContain('${command}');
|
|
270
|
+
expect(tui.tree().role).toBeTruthy();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
`;
|
|
274
|
+
};
|
|
275
|
+
function tuiSource(recipe, command, className) {
|
|
276
|
+
if (recipe === 'wizard')
|
|
277
|
+
return TUI_WIZARD(command, className);
|
|
278
|
+
if (recipe === 'dashboard')
|
|
279
|
+
return TUI_DASHBOARD(command, className);
|
|
280
|
+
if (recipe === 'form')
|
|
281
|
+
return TUI_FORM(command, className);
|
|
282
|
+
return TUI_PICKER(command, className);
|
|
283
|
+
}
|
|
113
284
|
/**
|
|
114
285
|
* Built-in `make` command: scaffold commands, providers, config files, and TUI recipes.
|
|
115
286
|
*/
|
|
116
287
|
export default class MakeCommand extends Command {
|
|
117
|
-
signature = 'make {type} {name}';
|
|
118
|
-
description = 'Scaffold a command, provider, config file,
|
|
288
|
+
signature = 'make {type} {name} {extra?}';
|
|
289
|
+
description = 'Scaffold a command, provider, config file, TUI screen, or plugin';
|
|
290
|
+
descriptionKey = 'cmd.make.description';
|
|
119
291
|
async handle() {
|
|
120
292
|
const type = this.arg('type');
|
|
121
293
|
const name = this.arg('name');
|
|
122
|
-
|
|
123
|
-
|
|
294
|
+
const extra = this.arg('extra');
|
|
295
|
+
if (!['command', 'provider', 'config', 'tui', 'plugin'].includes(type)) {
|
|
296
|
+
throw this.usageError(`Unknown make type "${type}".`, HINT);
|
|
297
|
+
}
|
|
298
|
+
for (const part of [name, extra]) {
|
|
299
|
+
if (part !== undefined && !/^[a-zA-Z][a-zA-Z0-9-]*$/.test(part)) {
|
|
300
|
+
throw this.usageError(`Invalid name "${part}".`, 'Use letters, numbers, and dashes (e.g. "deploy-site").');
|
|
301
|
+
}
|
|
124
302
|
}
|
|
125
|
-
if (
|
|
126
|
-
throw this.usageError(`
|
|
303
|
+
if (extra && type !== 'tui') {
|
|
304
|
+
throw this.usageError(`"${type}" takes one name.`, HINT);
|
|
127
305
|
}
|
|
128
306
|
const k = kebab(name);
|
|
129
307
|
const className = pascal(name);
|
|
308
|
+
if (type === 'plugin') {
|
|
309
|
+
await this.writePlugin(k, className);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (type === 'tui') {
|
|
313
|
+
await this.writeTui(k, extra);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
130
316
|
let filePath;
|
|
131
317
|
let content;
|
|
318
|
+
let testPath;
|
|
319
|
+
let testContent;
|
|
132
320
|
if (type === 'command') {
|
|
133
321
|
filePath = join(this.app.basePath, 'src', 'commands', `${k}.command.ts`);
|
|
134
322
|
content = COMMAND_TEMPLATE(k, className);
|
|
323
|
+
testPath = join(this.app.basePath, 'test', `${k}.test.ts`);
|
|
324
|
+
testContent = COMMAND_TEST(k);
|
|
135
325
|
}
|
|
136
326
|
else if (type === 'provider') {
|
|
137
327
|
filePath = join(this.app.basePath, 'src', 'providers', `${className}Provider.ts`);
|
|
138
328
|
content = PROVIDER_TEMPLATE(`${className}Provider`);
|
|
139
329
|
}
|
|
140
|
-
else if (type === 'tui') {
|
|
141
|
-
if (!['picker', 'wizard', 'dashboard'].includes(k)) {
|
|
142
|
-
throw this.usageError(`Unknown TUI recipe "${name}".`, 'Use: make tui {picker|wizard|dashboard}');
|
|
143
|
-
}
|
|
144
|
-
filePath = join(this.app.basePath, 'src', 'commands', `${k}.command.ts`);
|
|
145
|
-
content = k === 'wizard' ? TUI_WIZARD(k, className) : k === 'dashboard' ? TUI_DASHBOARD(k, className) : TUI_PICKER(k, className);
|
|
146
|
-
}
|
|
147
330
|
else {
|
|
148
331
|
filePath = join(this.app.basePath, 'config', `${k}.ts`);
|
|
149
332
|
content = CONFIG_TEMPLATE;
|
|
150
333
|
}
|
|
334
|
+
await this.writeNew(filePath, content);
|
|
335
|
+
this.output.success(`Created ${relative(this.app.basePath, filePath)}`);
|
|
336
|
+
if (testPath && testContent) {
|
|
337
|
+
await this.writeNew(testPath, testContent);
|
|
338
|
+
this.output.success(`Created ${relative(this.app.basePath, testPath)}`);
|
|
339
|
+
}
|
|
340
|
+
this.hintNext(type === 'command' ? k : undefined);
|
|
341
|
+
}
|
|
342
|
+
async writeTui(recipe, extra) {
|
|
343
|
+
if (!TUI_RECIPES.includes(recipe)) {
|
|
344
|
+
throw this.usageError(`Unknown TUI recipe "${recipe}".`, 'Use: make tui {picker|wizard|dashboard|form} {name?}');
|
|
345
|
+
}
|
|
346
|
+
const command = extra ? kebab(extra) : recipe;
|
|
347
|
+
const className = pascal(command);
|
|
348
|
+
const filePath = join(this.app.basePath, 'src', 'commands', `${command}.command.ts`);
|
|
349
|
+
const testPath = join(this.app.basePath, 'test', `${command}.test.ts`);
|
|
350
|
+
await this.writeNew(filePath, tuiSource(recipe, command, className));
|
|
351
|
+
await this.writeNew(testPath, TUI_SCREEN_TEST(command, recipe));
|
|
352
|
+
this.output.success(`Created ${relative(this.app.basePath, filePath)}`);
|
|
353
|
+
this.output.success(`Created ${relative(this.app.basePath, testPath)}`);
|
|
354
|
+
this.hintNext(command);
|
|
355
|
+
}
|
|
356
|
+
hintNext(command) {
|
|
357
|
+
const bin = this.app.manifest.bin;
|
|
358
|
+
if (command) {
|
|
359
|
+
this.output.hint(`Next: node bin/${bin}.js ${command} then npm test`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
async writeNew(filePath, content) {
|
|
151
363
|
if (isAbsolute(filePath) === false || !filePath.startsWith(this.app.basePath)) {
|
|
152
364
|
throw this.usageError('Refusing to write outside the app directory.');
|
|
153
365
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
await writeFile(filePath, content, { flag: 'wx' });
|
|
366
|
+
if (existsSync(filePath)) {
|
|
367
|
+
throw this.usageError(`${relative(this.app.basePath, filePath)} already exists.`);
|
|
157
368
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
369
|
+
await mkdir(join(filePath, '..'), { recursive: true });
|
|
370
|
+
await writeFile(filePath, content, { flag: 'wx' });
|
|
371
|
+
}
|
|
372
|
+
async writePlugin(k, className) {
|
|
373
|
+
const dir = join(this.app.basePath, `${k}-plugin`);
|
|
374
|
+
if (isAbsolute(dir) === false || !dir.startsWith(this.app.basePath)) {
|
|
375
|
+
throw this.usageError('Refusing to write outside the app directory.');
|
|
163
376
|
}
|
|
164
|
-
|
|
377
|
+
const pkgPath = join(dir, 'package.json');
|
|
378
|
+
const srcPath = join(dir, 'src', 'index.ts');
|
|
379
|
+
if (existsSync(pkgPath) || existsSync(srcPath)) {
|
|
380
|
+
throw this.usageError(`${relative(this.app.basePath, dir)} already exists.`);
|
|
381
|
+
}
|
|
382
|
+
await mkdir(join(dir, 'src'), { recursive: true });
|
|
383
|
+
await writeFile(pkgPath, PLUGIN_PACKAGE(k), { flag: 'wx' });
|
|
384
|
+
await writeFile(srcPath, PLUGIN_INDEX(k, className), { flag: 'wx' });
|
|
385
|
+
this.output.success(`Created ${relative(this.app.basePath, srcPath)}`);
|
|
386
|
+
this.output.hint(`Next: npm install ./${k}-plugin`);
|
|
165
387
|
}
|
|
166
388
|
}
|
|
167
389
|
//# sourceMappingURL=make.command.js.map
|