@rrlab/cli 1.2.1 → 1.2.2-git-9a2ee7f.0
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/dist/cli.usage.kdl +11 -3
- package/dist/config.d.mts +1 -1
- package/dist/plugin/decide-scaffold.d.mts +1 -1
- package/dist/plugin/define-plugin.d.mts +1 -1
- package/dist/plugin/directory.d.mts +10 -1
- package/dist/plugin/directory.mjs +5 -0
- package/dist/plugin/index.d.mts +3 -3
- package/dist/plugin/pick-preset.d.mts +1 -1
- package/dist/plugin/registry.d.mts +3 -3
- package/dist/plugin/tool-service.d.mts +14 -1
- package/dist/plugin/tool-service.mjs +18 -0
- package/dist/plugin/types.d.mts +3 -3
- package/dist/run.mjs +38 -1
- package/dist/{tool-DKL6TauZ.d.mts → tool-z8C0qtc5.d.mts} +15 -1
- package/dist/{types-Iu4IyWof.d.mts → types-CNXhLCKf.d.mts} +2 -1
- package/package.json +1 -1
- package/src/actions/test.ts +20 -0
- package/src/lib/plugin/directory.ts +1 -0
- package/src/lib/plugin/index.ts +2 -0
- package/src/lib/plugin/tool-service.ts +18 -0
- package/src/lib/plugin/types.ts +2 -1
- package/src/program/commands/test.ts +36 -0
- package/src/program/index.ts +3 -0
- package/src/types/tool.ts +18 -0
package/dist/cli.usage.kdl
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @generated by @usage-spec/commander from Commander.js metadata
|
|
2
2
|
name rr
|
|
3
3
|
bin rr
|
|
4
|
-
version "1.2.
|
|
4
|
+
version "1.2.2-git-9a2ee7f.0"
|
|
5
5
|
usage "[options] [command]"
|
|
6
6
|
flag --about help="show credits & inspiration"
|
|
7
7
|
flag --usage help="print KDL spec for this CLI (https://kdl.dev)"
|
|
@@ -31,6 +31,14 @@ cmd format help="check & fix format issues" {
|
|
|
31
31
|
flag --fix help="format all the code"
|
|
32
32
|
cmd doctor help="check if the underlying tool is working correctly"
|
|
33
33
|
}
|
|
34
|
+
cmd test help="run the test suite" {
|
|
35
|
+
long_help "Passthrough to the configured test runner. Forwards every flag and argument (e.g. --project, --watch) — including --help — straight to the tool. Loads .env.test or .env by default. ('rr test doctor' is reserved for the health check.)"
|
|
36
|
+
flag --env help="load this env file before running (default: .env.test, then .env)" {
|
|
37
|
+
arg <ENV>
|
|
38
|
+
}
|
|
39
|
+
arg "[args]…" help="arguments forwarded to the test runner" required=#false var=#true
|
|
40
|
+
cmd doctor help="check if the underlying tool is working correctly"
|
|
41
|
+
}
|
|
34
42
|
cmd pack help="pack a ts library" {
|
|
35
43
|
long_help "Compiles TypeScript code into JavaScript and generates type declaration files, packaging the library for distribution."
|
|
36
44
|
cmd doctor help="check if the underlying tool is working correctly"
|
|
@@ -55,13 +63,13 @@ cmd plugins subcommand_required=#true help="manage @rrlab plugins" {
|
|
|
55
63
|
flag --force help="re-run install even if the plugin is already configured"
|
|
56
64
|
flag --yes help="skip prompts and use defaults (non-interactive)"
|
|
57
65
|
flag --dry-run help="show what would happen, without applying changes"
|
|
58
|
-
arg <name> help="plugin alias (ts|biome|oxc|tsdown), optionally with @<spec> e.g. biome@pr-226"
|
|
66
|
+
arg <name> help="plugin alias (ts|biome|oxc|tsdown|vitest), optionally with @<spec> e.g. biome@pr-226"
|
|
59
67
|
}
|
|
60
68
|
cmd remove help="uninstall an @rrlab plugin and undo its config files + deps" {
|
|
61
69
|
flag --yes help="skip the confirmation prompt"
|
|
62
70
|
flag --dry-run help="print the plan without applying changes"
|
|
63
71
|
arg <name> help="plugin alias to remove" {
|
|
64
|
-
choices ts biome oxc tsdown
|
|
72
|
+
choices ts biome oxc tsdown vitest
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
}
|
package/dist/config.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as Plugin, d as PluginServices, f as UninstallContext, i as InstallContext, l as PluginCapability, m as UninstallResult, o as InstallResult, u as PluginContext } from "../types-
|
|
1
|
+
import { c as Plugin, d as PluginServices, f as UninstallContext, i as InstallContext, l as PluginCapability, m as UninstallResult, o as InstallResult, u as PluginContext } from "../types-CNXhLCKf.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/plugin/define-plugin.d.ts
|
|
4
4
|
type PluginDefinition<TServices extends PluginServices> = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as PluginCapability } from "../types-
|
|
1
|
+
import { l as PluginCapability } from "../types-CNXhLCKf.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/plugin/directory.d.ts
|
|
4
4
|
declare const PLUGINS_DIRECTORY: {
|
|
@@ -22,6 +22,11 @@ declare const PLUGINS_DIRECTORY: {
|
|
|
22
22
|
readonly name: "tsdown";
|
|
23
23
|
readonly capabilities: ["pack"];
|
|
24
24
|
};
|
|
25
|
+
readonly vitest: {
|
|
26
|
+
readonly pkg: "@rrlab/vitest-plugin";
|
|
27
|
+
readonly name: "vitest";
|
|
28
|
+
readonly capabilities: ["test"];
|
|
29
|
+
};
|
|
25
30
|
};
|
|
26
31
|
type PluginName = keyof typeof PLUGINS_DIRECTORY;
|
|
27
32
|
declare function allPluginNames(): readonly PluginName[];
|
|
@@ -42,6 +47,10 @@ declare function providersOf(capability: PluginCapability): ({
|
|
|
42
47
|
readonly pkg: "@rrlab/tsdown-plugin";
|
|
43
48
|
readonly name: "tsdown";
|
|
44
49
|
readonly capabilities: ["pack"];
|
|
50
|
+
} | {
|
|
51
|
+
readonly pkg: "@rrlab/vitest-plugin";
|
|
52
|
+
readonly name: "vitest";
|
|
53
|
+
readonly capabilities: ["test"];
|
|
45
54
|
})[];
|
|
46
55
|
//#endregion
|
|
47
56
|
export { PLUGINS_DIRECTORY, PluginName, allPluginNames, isPluginName, providersOf };
|
package/dist/plugin/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as InstallFlags, c as Plugin, d as PluginServices, f as UninstallContext, g as ReleaseServiceOptions, h as ReleaseService, i as InstallContext, l as PluginCapability, m as UninstallResult, n as ClackPromptsSelectOption, o as InstallResult, p as UninstallFlags, r as FileOp, s as JsonEdit, t as ClackPrompts, u as PluginContext } from "../types-
|
|
2
|
-
import { a as Linter, c as StaticChecker, d as
|
|
1
|
+
import { a as InstallFlags, c as Plugin, d as PluginServices, f as UninstallContext, g as ReleaseServiceOptions, h as ReleaseService, i as InstallContext, l as PluginCapability, m as UninstallResult, n as ClackPromptsSelectOption, o as InstallResult, p as UninstallFlags, r as FileOp, s as JsonEdit, t as ClackPrompts, u as PluginContext } from "../types-CNXhLCKf.mjs";
|
|
2
|
+
import { a as Linter, c as StaticChecker, d as TestRunner, f as TypeCheckOptions, i as LintOptions, l as StaticCheckerOptions, n as FormatOptions, o as Packer, p as TypeChecker, r as Formatter, s as RunReport, t as Doctor, u as TestRunOptions } from "../tool-z8C0qtc5.mjs";
|
|
3
3
|
import { DecideScaffoldOptions, ScaffoldDecision, decideScaffold } from "./decide-scaffold.mjs";
|
|
4
4
|
import { PluginDefinition, definePlugin } from "./define-plugin.mjs";
|
|
5
5
|
import { PickPresetOptions, pickPreset } from "./pick-preset.mjs";
|
|
6
6
|
import { ToolService, ToolServiceOptions } from "./tool-service.mjs";
|
|
7
|
-
export { type ClackPrompts, type ClackPromptsSelectOption, type DecideScaffoldOptions, type Doctor, type FileOp, type FormatOptions, type Formatter, type InstallContext, type InstallFlags, type InstallResult, type JsonEdit, type LintOptions, type Linter, type Packer, type PickPresetOptions, type Plugin, type PluginCapability, type PluginContext, type PluginDefinition, type PluginServices, ReleaseService, type ReleaseServiceOptions, type RunReport, type ScaffoldDecision, type StaticChecker, type StaticCheckerOptions, ToolService, type ToolServiceOptions, type TypeCheckOptions, type TypeChecker, type UninstallContext, type UninstallFlags, type UninstallResult, decideScaffold, definePlugin, pickPreset };
|
|
7
|
+
export { type ClackPrompts, type ClackPromptsSelectOption, type DecideScaffoldOptions, type Doctor, type FileOp, type FormatOptions, type Formatter, type InstallContext, type InstallFlags, type InstallResult, type JsonEdit, type LintOptions, type Linter, type Packer, type PickPresetOptions, type Plugin, type PluginCapability, type PluginContext, type PluginDefinition, type PluginServices, ReleaseService, type ReleaseServiceOptions, type RunReport, type ScaffoldDecision, type StaticChecker, type StaticCheckerOptions, type TestRunOptions, type TestRunner, ToolService, type ToolServiceOptions, type TypeCheckOptions, type TypeChecker, type UninstallContext, type UninstallFlags, type UninstallResult, decideScaffold, definePlugin, pickPreset };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as Plugin, d as PluginServices, l as PluginCapability } from "../types-
|
|
2
|
-
import { a as Linter, c as StaticChecker, d as
|
|
1
|
+
import { c as Plugin, d as PluginServices, l as PluginCapability } from "../types-CNXhLCKf.mjs";
|
|
2
|
+
import { a as Linter, c as StaticChecker, d as TestRunner, o as Packer, p as TypeChecker, r as Formatter, t as Doctor } from "../tool-z8C0qtc5.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/plugin/registry.d.ts
|
|
5
5
|
type Provider<K extends PluginCapability> = {
|
|
@@ -11,7 +11,7 @@ declare class PluginRegistry {
|
|
|
11
11
|
register(plugin: Plugin, services: PluginServices): void;
|
|
12
12
|
getServiceOrThrow<K extends PluginCapability>(capability: K): NonNullable<PluginServices[K]>;
|
|
13
13
|
getService<K extends PluginCapability>(capability: K): NonNullable<PluginServices[K]> | undefined;
|
|
14
|
-
getAllServices(): NonNullable<(Linter & Doctor) | (Formatter & Doctor) | (StaticChecker & Doctor) | (TypeChecker & Doctor) | (Packer & Doctor) | undefined>[];
|
|
14
|
+
getAllServices(): NonNullable<(Linter & Doctor) | (Formatter & Doctor) | (StaticChecker & Doctor) | (TypeChecker & Doctor) | (Packer & Doctor) | (TestRunner & Doctor) | undefined>[];
|
|
15
15
|
providersOf<K extends PluginCapability>(capability: K): Provider<K>[];
|
|
16
16
|
providerOf<K extends PluginCapability>(capability: K): Provider<K> | undefined;
|
|
17
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as RunReport } from "../tool-
|
|
1
|
+
import { s as RunReport } from "../tool-z8C0qtc5.mjs";
|
|
2
2
|
import { ShellService } from "@vlandoss/clibuddy";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/plugin/tool-service.d.ts
|
|
@@ -39,6 +39,19 @@ declare class ToolService {
|
|
|
39
39
|
* and the caller can aggregate. See `decisions/013-check-stream-to-capture-contract.md`.
|
|
40
40
|
*/
|
|
41
41
|
runReport(args?: string[], options?: RunReportOptions): Promise<RunReport>;
|
|
42
|
+
/**
|
|
43
|
+
* Streams the command straight to the terminal (`stdio: "inherit"`) and
|
|
44
|
+
* returns its exit code — the counterpart to `runReport` for passthrough
|
|
45
|
+
* verbs (e.g. a test runner) where capturing would break watch mode, colors,
|
|
46
|
+
* interactive UI, and `--help` forwarding. `verbose: false` suppresses the
|
|
47
|
+
* `$ <cmd>` line so the wrapped tool fully owns the terminal. Takes an
|
|
48
|
+
* explicit `command` because a passthrough may need to wrap its bin (e.g.
|
|
49
|
+
* `node --env-file=… <bin>`) rather than spawn it directly.
|
|
50
|
+
*
|
|
51
|
+
* A missing exit code (signal-killed, e.g. OOM) is reported as failure (`1`),
|
|
52
|
+
* mirroring `runReport`'s strict `=== 0`.
|
|
53
|
+
*/
|
|
54
|
+
runStreamed(command: string, args?: string[], options?: RunReportOptions): Promise<number>;
|
|
42
55
|
doctor(): Promise<RunReport>;
|
|
43
56
|
}
|
|
44
57
|
//#endregion
|
|
@@ -41,6 +41,24 @@ var ToolService = class {
|
|
|
41
41
|
output: body ? `${header}\n${body}` : header
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Streams the command straight to the terminal (`stdio: "inherit"`) and
|
|
46
|
+
* returns its exit code — the counterpart to `runReport` for passthrough
|
|
47
|
+
* verbs (e.g. a test runner) where capturing would break watch mode, colors,
|
|
48
|
+
* interactive UI, and `--help` forwarding. `verbose: false` suppresses the
|
|
49
|
+
* `$ <cmd>` line so the wrapped tool fully owns the terminal. Takes an
|
|
50
|
+
* explicit `command` because a passthrough may need to wrap its bin (e.g.
|
|
51
|
+
* `node --env-file=… <bin>`) rather than spawn it directly.
|
|
52
|
+
*
|
|
53
|
+
* A missing exit code (signal-killed, e.g. OOM) is reported as failure (`1`),
|
|
54
|
+
* mirroring `runReport`'s strict `=== 0`.
|
|
55
|
+
*/
|
|
56
|
+
async runStreamed(command, args = [], options = {}) {
|
|
57
|
+
return (await (options.cwd ? this.#shellService.at(options.cwd) : this.#shellService).run(command, args, {
|
|
58
|
+
throwOnError: false,
|
|
59
|
+
verbose: false
|
|
60
|
+
})).exitCode ?? 1;
|
|
61
|
+
}
|
|
44
62
|
async doctor() {
|
|
45
63
|
const output = await this.#shellService.runCaptured(await this.getBinDir(), ["--help"], { throwOnError: false });
|
|
46
64
|
const ok = output.exitCode === 0;
|
package/dist/plugin/types.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as InstallFlags, c as Plugin, d as PluginServices, f as UninstallContext, i as InstallContext, l as PluginCapability, m as UninstallResult, n as ClackPromptsSelectOption, o as InstallResult, p as UninstallFlags, r as FileOp, s as JsonEdit, t as ClackPrompts, u as PluginContext } from "../types-
|
|
2
|
-
import { a as Linter, c as StaticChecker, d as
|
|
3
|
-
export { ClackPrompts, ClackPromptsSelectOption, type Doctor, FileOp, type FormatOptions, type Formatter, InstallContext, InstallFlags, InstallResult, JsonEdit, type LintOptions, type Linter, type Packer, Plugin, PluginCapability, PluginContext, PluginServices, type RunReport, type StaticChecker, type StaticCheckerOptions, type TypeCheckOptions, type TypeChecker, UninstallContext, UninstallFlags, UninstallResult };
|
|
1
|
+
import { a as InstallFlags, c as Plugin, d as PluginServices, f as UninstallContext, i as InstallContext, l as PluginCapability, m as UninstallResult, n as ClackPromptsSelectOption, o as InstallResult, p as UninstallFlags, r as FileOp, s as JsonEdit, t as ClackPrompts, u as PluginContext } from "../types-CNXhLCKf.mjs";
|
|
2
|
+
import { a as Linter, c as StaticChecker, d as TestRunner, f as TypeCheckOptions, i as LintOptions, l as StaticCheckerOptions, n as FormatOptions, o as Packer, p as TypeChecker, r as Formatter, s as RunReport, t as Doctor, u as TestRunOptions } from "../tool-z8C0qtc5.mjs";
|
|
3
|
+
export { ClackPrompts, ClackPromptsSelectOption, type Doctor, FileOp, type FormatOptions, type Formatter, InstallContext, InstallFlags, InstallResult, JsonEdit, type LintOptions, type Linter, type Packer, Plugin, PluginCapability, PluginContext, PluginServices, type RunReport, type StaticChecker, type StaticCheckerOptions, type TestRunOptions, type TestRunner, type TypeCheckOptions, type TypeChecker, UninstallContext, UninstallFlags, UninstallResult };
|
package/dist/run.mjs
CHANGED
|
@@ -53,6 +53,11 @@ const PLUGINS_DIRECTORY = {
|
|
|
53
53
|
pkg: "@rrlab/tsdown-plugin",
|
|
54
54
|
name: "tsdown",
|
|
55
55
|
capabilities: ["pack"]
|
|
56
|
+
},
|
|
57
|
+
vitest: {
|
|
58
|
+
pkg: "@rrlab/vitest-plugin",
|
|
59
|
+
name: "vitest",
|
|
60
|
+
capabilities: ["test"]
|
|
56
61
|
}
|
|
57
62
|
};
|
|
58
63
|
function allPluginNames() {
|
|
@@ -1578,6 +1583,38 @@ function createPluginsCommand(ctx) {
|
|
|
1578
1583
|
return cmd;
|
|
1579
1584
|
}
|
|
1580
1585
|
//#endregion
|
|
1586
|
+
//#region src/actions/test.ts
|
|
1587
|
+
/**
|
|
1588
|
+
* The action for the `test` passthrough. Unlike the captured verbs (lint, pack,
|
|
1589
|
+
* …) it doesn't drive the task-board: the runner streams straight to the
|
|
1590
|
+
* terminal and resolves with the tool's exit code, which we mirror onto
|
|
1591
|
+
* `process.exitCode` so `rr test` fails when the suite fails.
|
|
1592
|
+
*/
|
|
1593
|
+
async function testAction({ runner, options, args }) {
|
|
1594
|
+
const code = await runner.test({
|
|
1595
|
+
envFile: options.envFile,
|
|
1596
|
+
args
|
|
1597
|
+
});
|
|
1598
|
+
if (code !== 0) process.exitCode = code;
|
|
1599
|
+
}
|
|
1600
|
+
//#endregion
|
|
1601
|
+
//#region src/program/commands/test.ts
|
|
1602
|
+
function createTestCommand(ctx) {
|
|
1603
|
+
return createCommand("test").addCapabilities(["test"]).summary("run the test suite").description("Passthrough to the configured test runner. Forwards every flag and argument (e.g. --project, --watch) — including --help — straight to the tool. Loads .env.test or .env by default. ('rr test doctor' is reserved for the health check.)").allowUnknownOption(true).passThroughOptions(true).helpOption(false).option("--env <path>", "load this env file before running (default: .env.test, then .env)").argument("[args...]", "arguments forwarded to the test runner").action(async (args = [], options = {}) => {
|
|
1604
|
+
await testAction({
|
|
1605
|
+
ctx,
|
|
1606
|
+
runner: ctx.plugins.getServiceOrThrow("test"),
|
|
1607
|
+
options: { envFile: options.env },
|
|
1608
|
+
args
|
|
1609
|
+
});
|
|
1610
|
+
}).addDoctorCommand(async () => {
|
|
1611
|
+
await doctorOneAction({
|
|
1612
|
+
ctx,
|
|
1613
|
+
service: ctx.plugins.getServiceOrThrow("test")
|
|
1614
|
+
});
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
//#endregion
|
|
1581
1618
|
//#region src/program/commands/tscheck.ts
|
|
1582
1619
|
function createTsCheckCommand(ctx) {
|
|
1583
1620
|
return createCommand("tsc").alias("tscheck").addCapabilities(["typecheck"]).summary("check types errors").description("Checks type errors, ensuring that the code adheres to the defined type constraints and helps catch potential issues before runtime.").action(async () => {
|
|
@@ -1653,7 +1690,7 @@ var RunRunCmd = class extends Command {
|
|
|
1653
1690
|
async function createProgram(meta) {
|
|
1654
1691
|
const ctx = await new ContextService(path.dirname(dirnameOf(meta))).getContext();
|
|
1655
1692
|
const cmd = new RunRunCmd(ctx);
|
|
1656
|
-
cmd.commandsGroup("Code quality:").addCommand(createCheckCommand(ctx)).addCommand(createJsCheckCommand(ctx)).addCommand(createTsCheckCommand(ctx)).addCommand(createLintCommand(ctx)).addCommand(createFormatCommand(ctx)).commandsGroup("Build:").addCommand(createPackCommand(ctx)).commandsGroup("Maintenance:").addCommand(createCleanCommand()).addCommand(createDoctorCommand(ctx)).commandsGroup("Meta:").addCommand(createCompletionCommand()).addCommand(createPluginsCommand(ctx)).addCommand(createConfigCommand(ctx));
|
|
1693
|
+
cmd.commandsGroup("Code quality:").addCommand(createCheckCommand(ctx)).addCommand(createJsCheckCommand(ctx)).addCommand(createTsCheckCommand(ctx)).addCommand(createLintCommand(ctx)).addCommand(createFormatCommand(ctx)).commandsGroup("Testing:").addCommand(createTestCommand(ctx)).commandsGroup("Build:").addCommand(createPackCommand(ctx)).commandsGroup("Maintenance:").addCommand(createCleanCommand()).addCommand(createDoctorCommand(ctx)).commandsGroup("Meta:").addCommand(createCompletionCommand()).addCommand(createPluginsCommand(ctx)).addCommand(createConfigCommand(ctx));
|
|
1657
1694
|
return cmd;
|
|
1658
1695
|
}
|
|
1659
1696
|
//#endregion
|
|
@@ -39,5 +39,19 @@ type Packer = {
|
|
|
39
39
|
readonly ui: string;
|
|
40
40
|
pack: () => Promise<RunReport>;
|
|
41
41
|
};
|
|
42
|
+
type TestRunOptions = {
|
|
43
|
+
/** Env file to load before running (resolved by the provider). */envFile?: string; /** Arguments forwarded verbatim to the test runner. */
|
|
44
|
+
args: string[];
|
|
45
|
+
};
|
|
46
|
+
type TestRunner = {
|
|
47
|
+
readonly ui: string;
|
|
48
|
+
/**
|
|
49
|
+
* Streams a passthrough test run and resolves with the tool's exit code.
|
|
50
|
+
* Unlike the other verbs it returns a number, not a `RunReport`: a test
|
|
51
|
+
* runner needs an inherited TTY (watch mode, colors, `--help`), so its output
|
|
52
|
+
* is never captured for the board.
|
|
53
|
+
*/
|
|
54
|
+
test: (options: TestRunOptions) => Promise<number>;
|
|
55
|
+
};
|
|
42
56
|
//#endregion
|
|
43
|
-
export { Linter as a, StaticChecker as c,
|
|
57
|
+
export { Linter as a, StaticChecker as c, TestRunner as d, TypeCheckOptions as f, LintOptions as i, StaticCheckerOptions as l, FormatOptions as n, Packer as o, TypeChecker as p, Formatter as r, RunReport as s, Doctor as t, TestRunOptions as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Linter, c as StaticChecker, d as
|
|
1
|
+
import { a as Linter, c as StaticChecker, d as TestRunner, o as Packer, p as TypeChecker, r as Formatter, t as Doctor } from "./tool-z8C0qtc5.mjs";
|
|
2
2
|
import { Pkg, ShellService } from "@vlandoss/clibuddy";
|
|
3
3
|
import { AnyLogger } from "@vlandoss/loggy";
|
|
4
4
|
|
|
@@ -35,6 +35,7 @@ type PluginServices = {
|
|
|
35
35
|
jscheck?: StaticChecker & Doctor;
|
|
36
36
|
typecheck?: TypeChecker & Doctor;
|
|
37
37
|
pack?: Packer & Doctor;
|
|
38
|
+
test?: TestRunner & Doctor;
|
|
38
39
|
};
|
|
39
40
|
type PluginContext = {
|
|
40
41
|
shell: ShellService;
|
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ContextValue } from "#src/services/context.ts";
|
|
2
|
+
import type { Doctor, TestRunner } from "#src/types/tool.ts";
|
|
3
|
+
|
|
4
|
+
export type TestActionConfig = {
|
|
5
|
+
ctx: ContextValue;
|
|
6
|
+
runner: TestRunner & Doctor;
|
|
7
|
+
options: { envFile?: string };
|
|
8
|
+
args: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The action for the `test` passthrough. Unlike the captured verbs (lint, pack,
|
|
13
|
+
* …) it doesn't drive the task-board: the runner streams straight to the
|
|
14
|
+
* terminal and resolves with the tool's exit code, which we mirror onto
|
|
15
|
+
* `process.exitCode` so `rr test` fails when the suite fails.
|
|
16
|
+
*/
|
|
17
|
+
export async function testAction({ runner, options, args }: TestActionConfig): Promise<void> {
|
|
18
|
+
const code = await runner.test({ envFile: options.envFile, args });
|
|
19
|
+
if (code !== 0) process.exitCode = code;
|
|
20
|
+
}
|
|
@@ -11,6 +11,7 @@ export const PLUGINS_DIRECTORY = {
|
|
|
11
11
|
biome: { pkg: "@rrlab/biome-plugin", name: "biome", capabilities: ["format", "jscheck", "lint"] },
|
|
12
12
|
oxc: { pkg: "@rrlab/oxc-plugin", name: "oxc", capabilities: ["format", "lint", "jscheck", "typecheck"] },
|
|
13
13
|
tsdown: { pkg: "@rrlab/tsdown-plugin", name: "tsdown", capabilities: ["pack"] },
|
|
14
|
+
vitest: { pkg: "@rrlab/vitest-plugin", name: "vitest", capabilities: ["test"] },
|
|
14
15
|
} as const satisfies Record<string, PluginInfo>;
|
|
15
16
|
|
|
16
17
|
export type PluginName = keyof typeof PLUGINS_DIRECTORY;
|
package/src/lib/plugin/index.ts
CHANGED
|
@@ -70,6 +70,24 @@ export class ToolService {
|
|
|
70
70
|
return { ok: output.exitCode === 0, output: body ? `${header}\n${body}` : header };
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Streams the command straight to the terminal (`stdio: "inherit"`) and
|
|
75
|
+
* returns its exit code — the counterpart to `runReport` for passthrough
|
|
76
|
+
* verbs (e.g. a test runner) where capturing would break watch mode, colors,
|
|
77
|
+
* interactive UI, and `--help` forwarding. `verbose: false` suppresses the
|
|
78
|
+
* `$ <cmd>` line so the wrapped tool fully owns the terminal. Takes an
|
|
79
|
+
* explicit `command` because a passthrough may need to wrap its bin (e.g.
|
|
80
|
+
* `node --env-file=… <bin>`) rather than spawn it directly.
|
|
81
|
+
*
|
|
82
|
+
* A missing exit code (signal-killed, e.g. OOM) is reported as failure (`1`),
|
|
83
|
+
* mirroring `runReport`'s strict `=== 0`.
|
|
84
|
+
*/
|
|
85
|
+
async runStreamed(command: string, args: string[] = [], options: RunReportOptions = {}): Promise<number> {
|
|
86
|
+
const sh = options.cwd ? this.#shellService.at(options.cwd) : this.#shellService;
|
|
87
|
+
const result = await sh.run(command, args, { throwOnError: false, verbose: false });
|
|
88
|
+
return result.exitCode ?? 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
async doctor(): Promise<RunReport> {
|
|
74
92
|
const output = await this.#shellService.runCaptured(await this.getBinDir(), ["--help"], { throwOnError: false });
|
|
75
93
|
const ok = output.exitCode === 0;
|
package/src/lib/plugin/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Pkg, ShellService } from "@vlandoss/clibuddy";
|
|
2
2
|
import type { AnyLogger as Logger } from "@vlandoss/loggy";
|
|
3
3
|
import type { ReleaseService } from "#src/services/release.ts";
|
|
4
|
-
import type { Doctor, Formatter, Linter, Packer, StaticChecker, TypeChecker } from "#src/types/tool.ts";
|
|
4
|
+
import type { Doctor, Formatter, Linter, Packer, StaticChecker, TestRunner, TypeChecker } from "#src/types/tool.ts";
|
|
5
5
|
|
|
6
6
|
export type * from "#src/types/tool.ts";
|
|
7
7
|
|
|
@@ -13,6 +13,7 @@ export type PluginServices = {
|
|
|
13
13
|
jscheck?: StaticChecker & Doctor;
|
|
14
14
|
typecheck?: TypeChecker & Doctor;
|
|
15
15
|
pack?: Packer & Doctor;
|
|
16
|
+
test?: TestRunner & Doctor;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export type PluginContext = {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { doctorOneAction } from "#src/actions/doctor.ts";
|
|
2
|
+
import { testAction } from "#src/actions/test.ts";
|
|
3
|
+
import type { ContextValue } from "#src/services/context.ts";
|
|
4
|
+
import { createCommand } from "../base.ts";
|
|
5
|
+
|
|
6
|
+
type ActionOptions = {
|
|
7
|
+
env?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function createTestCommand(ctx: ContextValue) {
|
|
11
|
+
return (
|
|
12
|
+
createCommand("test")
|
|
13
|
+
.addCapabilities(["test"])
|
|
14
|
+
.summary("run the test suite")
|
|
15
|
+
.description(
|
|
16
|
+
"Passthrough to the configured test runner. Forwards every flag and argument (e.g. --project, --watch) — including --help — straight to the tool. Loads .env.test or .env by default. ('rr test doctor' is reserved for the health check.)",
|
|
17
|
+
)
|
|
18
|
+
// Pure passthrough: don't parse the tool's flags, don't capture --help.
|
|
19
|
+
// `--env` is consumed only when it precedes the forwarded args. It's not
|
|
20
|
+
// named `--env-file`: Node's early `--env-file` scanner would grab that
|
|
21
|
+
// token off the `rr` process's argv before Commander ever sees it.
|
|
22
|
+
.allowUnknownOption(true)
|
|
23
|
+
.passThroughOptions(true)
|
|
24
|
+
.helpOption(false)
|
|
25
|
+
.option("--env <path>", "load this env file before running (default: .env.test, then .env)")
|
|
26
|
+
.argument("[args...]", "arguments forwarded to the test runner")
|
|
27
|
+
.action(async (args: string[] = [], options: ActionOptions = {}) => {
|
|
28
|
+
const runner = ctx.plugins.getServiceOrThrow("test");
|
|
29
|
+
await testAction({ ctx, runner, options: { envFile: options.env }, args });
|
|
30
|
+
})
|
|
31
|
+
.addDoctorCommand(async () => {
|
|
32
|
+
const runner = ctx.plugins.getServiceOrThrow("test");
|
|
33
|
+
await doctorOneAction({ ctx, service: runner });
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
}
|
package/src/program/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { createJsCheckCommand } from "./commands/jscheck.ts";
|
|
|
11
11
|
import { createLintCommand } from "./commands/lint.ts";
|
|
12
12
|
import { createPackCommand } from "./commands/pack.ts";
|
|
13
13
|
import { createPluginsCommand } from "./commands/plugins.ts";
|
|
14
|
+
import { createTestCommand } from "./commands/test.ts";
|
|
14
15
|
import { createTsCheckCommand } from "./commands/tscheck.ts";
|
|
15
16
|
import { RunRunCmd } from "./root.ts";
|
|
16
17
|
|
|
@@ -29,6 +30,8 @@ export async function createProgram(meta: ImportMeta) {
|
|
|
29
30
|
.addCommand(createTsCheckCommand(ctx))
|
|
30
31
|
.addCommand(createLintCommand(ctx))
|
|
31
32
|
.addCommand(createFormatCommand(ctx))
|
|
33
|
+
.commandsGroup("Testing:")
|
|
34
|
+
.addCommand(createTestCommand(ctx))
|
|
32
35
|
.commandsGroup("Build:")
|
|
33
36
|
.addCommand(createPackCommand(ctx))
|
|
34
37
|
.commandsGroup("Maintenance:")
|
package/src/types/tool.ts
CHANGED
|
@@ -48,3 +48,21 @@ export type Packer = {
|
|
|
48
48
|
readonly ui: string;
|
|
49
49
|
pack: () => Promise<RunReport>;
|
|
50
50
|
};
|
|
51
|
+
|
|
52
|
+
export type TestRunOptions = {
|
|
53
|
+
/** Env file to load before running (resolved by the provider). */
|
|
54
|
+
envFile?: string;
|
|
55
|
+
/** Arguments forwarded verbatim to the test runner. */
|
|
56
|
+
args: string[];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type TestRunner = {
|
|
60
|
+
readonly ui: string;
|
|
61
|
+
/**
|
|
62
|
+
* Streams a passthrough test run and resolves with the tool's exit code.
|
|
63
|
+
* Unlike the other verbs it returns a number, not a `RunReport`: a test
|
|
64
|
+
* runner needs an inherited TTY (watch mode, colors, `--help`), so its output
|
|
65
|
+
* is never captured for the board.
|
|
66
|
+
*/
|
|
67
|
+
test: (options: TestRunOptions) => Promise<number>;
|
|
68
|
+
};
|