@vlandoss/run-run 0.1.4 → 0.1.5-git-5823ad3.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/main.mjs +99 -63
- package/package.json +2 -2
- package/src/program/commands/build-lib.ts +5 -0
- package/src/program/commands/doctor.ts +21 -0
- package/src/program/commands/format.ts +4 -1
- package/src/program/commands/jscheck.ts +3 -2
- package/src/program/commands/lint.ts +4 -1
- package/src/program/commands/tscheck.ts +7 -1
- package/src/program/parse-args.ts +3 -1
- package/src/services/tool.ts +36 -11
- package/src/services/tsc.ts +9 -0
- package/src/types/tool.ts +16 -0
- package/src/utils/gracefullBinDir.ts +0 -14
package/dist/main.mjs
CHANGED
|
@@ -5,11 +5,11 @@ import fs from "node:fs";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import { lilconfig } from "lilconfig";
|
|
7
7
|
import { createLoggy } from "@vlandoss/loggy";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import memoize from "memoize";
|
|
8
10
|
import { glob } from "glob";
|
|
9
11
|
import { rimraf } from "rimraf";
|
|
10
12
|
import isCI from "is-ci";
|
|
11
|
-
import memoize from "memoize";
|
|
12
|
-
import path from "node:path";
|
|
13
13
|
//#region \0rolldown/runtime.js
|
|
14
14
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
15
15
|
//#endregion
|
|
@@ -225,9 +225,86 @@ function getBannerText(version) {
|
|
|
225
225
|
return `${lines.join("\n")}\n`;
|
|
226
226
|
}
|
|
227
227
|
//#endregion
|
|
228
|
+
//#region src/services/tool.ts
|
|
229
|
+
var ToolService = class {
|
|
230
|
+
#shellService;
|
|
231
|
+
#bin;
|
|
232
|
+
#ui;
|
|
233
|
+
constructor({ bin, ui, shellService }) {
|
|
234
|
+
this.#bin = bin;
|
|
235
|
+
this.#ui = ui ?? bin;
|
|
236
|
+
this.#shellService = shellService;
|
|
237
|
+
}
|
|
238
|
+
async exec(args) {
|
|
239
|
+
const shell = this.#shell();
|
|
240
|
+
return this.#run(shell, args);
|
|
241
|
+
}
|
|
242
|
+
async doctor() {
|
|
243
|
+
const shell = this.#shell().mute();
|
|
244
|
+
const output = await this.#run(shell, "--help");
|
|
245
|
+
return {
|
|
246
|
+
ok: output.exitCode === 0,
|
|
247
|
+
output
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
#shell = memoize((cwd) => {
|
|
251
|
+
const preferLocal = this.#getPreferLocal();
|
|
252
|
+
return this.#shellService.child({
|
|
253
|
+
...cwd && { cwd },
|
|
254
|
+
...preferLocal && { preferLocal }
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
#run(shell, args) {
|
|
258
|
+
return shell.$`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
259
|
+
}
|
|
260
|
+
#getPreferLocal() {
|
|
261
|
+
if (!this.getBinDir) return;
|
|
262
|
+
try {
|
|
263
|
+
const binPath = this.getBinDir();
|
|
264
|
+
return fs.statSync(binPath).isDirectory() ? binPath : path.dirname(binPath);
|
|
265
|
+
} catch {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
get bin() {
|
|
270
|
+
return this.#bin;
|
|
271
|
+
}
|
|
272
|
+
get ui() {
|
|
273
|
+
return this.#ui;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/services/tsdown.ts
|
|
278
|
+
var TsdownService = class extends ToolService {
|
|
279
|
+
constructor(shellService) {
|
|
280
|
+
super({
|
|
281
|
+
bin: "tsdown",
|
|
282
|
+
ui: TOOL_LABELS.TSDOWN,
|
|
283
|
+
shellService
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/program/commands/doctor.ts
|
|
289
|
+
function createDoctorSubcommand(service) {
|
|
290
|
+
return createCommand("doctor").summary("check if the underlying tool is working correctly").action(async function doctorAction() {
|
|
291
|
+
const debug = logger.subdebug("doctor");
|
|
292
|
+
const { ok, output } = await service.doctor();
|
|
293
|
+
if (ok) {
|
|
294
|
+
logger.success(`${service.ui} ok`);
|
|
295
|
+
debug("%O", output);
|
|
296
|
+
} else {
|
|
297
|
+
logger.error(`${service.ui} not working`);
|
|
298
|
+
debug("%O", output);
|
|
299
|
+
process.exit(output.exitCode ?? 1);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
228
304
|
//#region src/program/commands/build-lib.ts
|
|
229
305
|
function createBuildLibCommand(ctx) {
|
|
230
|
-
|
|
306
|
+
const tsdownService = new TsdownService(ctx.shell);
|
|
307
|
+
return createCommand("build:lib").summary(`build a ts library 🏗️ (${TOOL_LABELS.TSDOWN})`).description("Compiles TypeScript code into JavaScript and generates type declaration files, making it ready for distribution.").addCommand(createDoctorSubcommand(tsdownService)).action(async function buildAction() {
|
|
231
308
|
await ctx.shell.$`tsdown`;
|
|
232
309
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${TOOL_LABELS.TSDOWN} CLI to build the project.`);
|
|
233
310
|
}
|
|
@@ -271,49 +348,6 @@ function createConfigCommand(ctx) {
|
|
|
271
348
|
});
|
|
272
349
|
}
|
|
273
350
|
//#endregion
|
|
274
|
-
//#region src/utils/gracefullBinDir.ts
|
|
275
|
-
function gracefullBinDir(binPathResolver) {
|
|
276
|
-
try {
|
|
277
|
-
const binPath = binPathResolver();
|
|
278
|
-
return fs.statSync(binPath).isDirectory() ? binPath : path.dirname(binPath);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
logger.error("Error getting bin directory:", error);
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
//#endregion
|
|
285
|
-
//#region src/services/tool.ts
|
|
286
|
-
var ToolService = class {
|
|
287
|
-
#shellService;
|
|
288
|
-
#bin;
|
|
289
|
-
#ui;
|
|
290
|
-
constructor({ bin, ui, shellService }) {
|
|
291
|
-
this.#bin = bin;
|
|
292
|
-
this.#ui = ui ?? bin;
|
|
293
|
-
this.#shellService = shellService;
|
|
294
|
-
}
|
|
295
|
-
exec(args) {
|
|
296
|
-
const $ = this.#shell();
|
|
297
|
-
return this.#run($, args);
|
|
298
|
-
}
|
|
299
|
-
#shell = memoize((cwd) => {
|
|
300
|
-
const { getBinDir } = this;
|
|
301
|
-
return this.#shellService.child({
|
|
302
|
-
cwd,
|
|
303
|
-
...getBinDir && { preferLocal: gracefullBinDir(() => getBinDir()) }
|
|
304
|
-
}).$;
|
|
305
|
-
});
|
|
306
|
-
#run(shell, args) {
|
|
307
|
-
return shell`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
308
|
-
}
|
|
309
|
-
get bin() {
|
|
310
|
-
return this.#bin;
|
|
311
|
-
}
|
|
312
|
-
get ui() {
|
|
313
|
-
return this.#ui;
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
|
-
//#endregion
|
|
317
351
|
//#region src/services/biome.ts
|
|
318
352
|
var BiomeService = class extends ToolService {
|
|
319
353
|
constructor(shellService) {
|
|
@@ -371,7 +405,7 @@ function getToolService$2(ctx) {
|
|
|
371
405
|
}
|
|
372
406
|
function createFormatCommand(ctx) {
|
|
373
407
|
const toolService = getToolService$2(ctx);
|
|
374
|
-
return createCommand("format").summary(`check & fix format errors 🎨 (${toolService.ui})`).description("Checks the code for formatting issues and optionally fixes them, ensuring it adheres to the defined style standards.").option("--fix", "format all the code").action(async function formatAction(options) {
|
|
408
|
+
return createCommand("format").summary(`check & fix format errors 🎨 (${toolService.ui})`).description("Checks the code for formatting issues and optionally fixes them, ensuring it adheres to the defined style standards.").option("--fix", "format all the code").addCommand(createDoctorSubcommand(toolService)).action(async function formatAction(options) {
|
|
375
409
|
await toolService.format(options);
|
|
376
410
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to format the code.`);
|
|
377
411
|
}
|
|
@@ -379,7 +413,7 @@ function createFormatCommand(ctx) {
|
|
|
379
413
|
//#region src/program/commands/jscheck.ts
|
|
380
414
|
function createJsCheckCommand(ctx) {
|
|
381
415
|
const checkerService = new BiomeService(ctx.shell);
|
|
382
|
-
return createCommand("jsc").alias("jscheck").alias("check").summary(`check format and lint 🔍 (${checkerService.ui})`).description("Checks the code for formatting and linting issues, ensuring it adheres to the defined style and quality standards.").option("--fix", "try to fix issues automatically").option("--fix-staged", "try to fix staged files only").action(async function checkAction(options) {
|
|
416
|
+
return createCommand("jsc").alias("jscheck").alias("check").summary(`check format and lint 🔍 (${checkerService.ui})`).description("Checks the code for formatting and linting issues, ensuring it adheres to the defined style and quality standards.").option("--fix", "try to fix issues automatically").option("--fix-staged", "try to fix staged files only").addCommand(createDoctorSubcommand(checkerService)).action(async function checkAction(options) {
|
|
383
417
|
await checkerService.check(options);
|
|
384
418
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${checkerService.ui} CLI to check the code.`);
|
|
385
419
|
}
|
|
@@ -411,7 +445,7 @@ function getToolService$1(ctx) {
|
|
|
411
445
|
}
|
|
412
446
|
function createLintCommand(ctx) {
|
|
413
447
|
const toolService = getToolService$1(ctx);
|
|
414
|
-
return createCommand("lint").summary(`check & fix lint errors 🔍 (${toolService.ui})`).description("Checks the code for linting issues and optionally fixes them, ensuring it adheres to the defined quality standards.").option("-c, --check", "check if the code is valid", true).option("--fix", "try to fix all the code").action(async function lintAction(options) {
|
|
448
|
+
return createCommand("lint").summary(`check & fix lint errors 🔍 (${toolService.ui})`).description("Checks the code for linting issues and optionally fixes them, ensuring it adheres to the defined quality standards.").option("-c, --check", "check if the code is valid", true).option("--fix", "try to fix all the code").addCommand(createDoctorSubcommand(toolService)).action(async function lintAction(options) {
|
|
415
449
|
await toolService.lint(options);
|
|
416
450
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to lint the code.`);
|
|
417
451
|
}
|
|
@@ -467,17 +501,6 @@ function createTestStaticCommand(ctx) {
|
|
|
467
501
|
});
|
|
468
502
|
}
|
|
469
503
|
//#endregion
|
|
470
|
-
//#region src/services/tsdown.ts
|
|
471
|
-
var TsdownService = class extends ToolService {
|
|
472
|
-
constructor(shellService) {
|
|
473
|
-
super({
|
|
474
|
-
bin: "tsdown",
|
|
475
|
-
ui: TOOL_LABELS.TSDOWN,
|
|
476
|
-
shellService
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
//#endregion
|
|
481
504
|
//#region src/program/commands/tools.ts
|
|
482
505
|
function getToolService(bin, shell) {
|
|
483
506
|
switch (bin) {
|
|
@@ -498,6 +521,17 @@ function createToolsCommand(ctx) {
|
|
|
498
521
|
return createCommand("tools").description("expose the internal tools 🛠️").addCommand(createToolCommand("biome", ctx.shell)).addCommand(createToolCommand("oxfmt", ctx.shell)).addCommand(createToolCommand("oxlint", ctx.shell)).addCommand(createToolCommand("tsdown", ctx.shell));
|
|
499
522
|
}
|
|
500
523
|
//#endregion
|
|
524
|
+
//#region src/services/tsc.ts
|
|
525
|
+
var TscService = class extends ToolService {
|
|
526
|
+
constructor(shellService) {
|
|
527
|
+
super({
|
|
528
|
+
bin: "tsc",
|
|
529
|
+
ui: TOOL_LABELS.TSC,
|
|
530
|
+
shellService
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
//#endregion
|
|
501
535
|
//#region src/program/commands/tscheck.ts
|
|
502
536
|
const getPreScript = (scripts) => scripts?.pretsc ?? scripts?.pretypecheck;
|
|
503
537
|
async function typecheckAt({ dir, scripts, log, shell, run }) {
|
|
@@ -526,11 +560,12 @@ async function typecheckAt({ dir, scripts, log, shell, run }) {
|
|
|
526
560
|
function createTsCheckCommand(ctx) {
|
|
527
561
|
const { appPkg, shell, config: { config } } = ctx;
|
|
528
562
|
const toolUi = config.future?.oxc ? TOOL_LABELS.OXLINT : TOOL_LABELS.TSC;
|
|
529
|
-
|
|
563
|
+
const doctorService = config.future?.oxc ? new OxlintService(shell) : new TscService(shell);
|
|
564
|
+
return createCommand("tsc").alias("tscheck").summary(`check typescript errors 🧩 (${toolUi})`).description("Checks the TypeScript code for type errors, ensuring that the code adheres to the defined type constraints and helps catch potential issues before runtime.").addCommand(createDoctorSubcommand(doctorService)).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolUi} CLI to check the code.`).action(async function typecheckAction() {
|
|
530
565
|
const isTsProject = (dir) => appPkg.hasFile("tsconfig.json", dir);
|
|
531
566
|
const runTypecheck = async (shell) => {
|
|
532
567
|
if (config.future?.oxc) await new OxlintService(shell).exec(`--type-aware --type-check --report-unused-disable-directives`);
|
|
533
|
-
else await shell
|
|
568
|
+
else await new TscService(shell).exec(`--noEmit`);
|
|
534
569
|
};
|
|
535
570
|
if (!appPkg.isMonorepo()) {
|
|
536
571
|
if (!isTsProject(appPkg.dirPath)) {
|
|
@@ -578,7 +613,8 @@ async function createProgram(options) {
|
|
|
578
613
|
const debug = logger.subdebug("parseArgs");
|
|
579
614
|
function parseArgs(argv = process.argv) {
|
|
580
615
|
const args = argv.slice(2);
|
|
581
|
-
const
|
|
616
|
+
const lastArg = args[args.length - 1];
|
|
617
|
+
const allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools" && lastArg !== "doctor";
|
|
582
618
|
debug("args %O", args);
|
|
583
619
|
if (allArgsAreValidCommands) {
|
|
584
620
|
debug("multiple commands detected, adding 'run' command");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vlandoss/run-run",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5-git-5823ad3.0",
|
|
4
4
|
"description": "The CLI toolbox to fullstack common scripts in Variable Land",
|
|
5
5
|
"homepage": "https://github.com/variableland/dx/tree/main/packages/run-run#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"rimraf": "6.1.3",
|
|
59
59
|
"tsdown": "0.21.10",
|
|
60
60
|
"typescript": "6.0.3",
|
|
61
|
-
"@vlandoss/clibuddy": "0.1.
|
|
61
|
+
"@vlandoss/clibuddy": "0.1.2-git-5823ad3.0",
|
|
62
62
|
"@vlandoss/loggy": "0.1.1"
|
|
63
63
|
},
|
|
64
64
|
"publishConfig": {
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { createCommand } from "commander";
|
|
2
2
|
import { TOOL_LABELS } from "#src/program/ui.ts";
|
|
3
3
|
import type { Context } from "#src/services/ctx.ts";
|
|
4
|
+
import { TsdownService } from "#src/services/tsdown.ts";
|
|
5
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
4
6
|
|
|
5
7
|
export function createBuildLibCommand(ctx: Context) {
|
|
8
|
+
const tsdownService = new TsdownService(ctx.shell);
|
|
9
|
+
|
|
6
10
|
return createCommand("build:lib")
|
|
7
11
|
.summary(`build a ts library 🏗️ (${TOOL_LABELS.TSDOWN})`)
|
|
8
12
|
.description(
|
|
9
13
|
"Compiles TypeScript code into JavaScript and generates type declaration files, making it ready for distribution.",
|
|
10
14
|
)
|
|
15
|
+
.addCommand(createDoctorSubcommand(tsdownService))
|
|
11
16
|
.action(async function buildAction() {
|
|
12
17
|
await ctx.shell.$`tsdown`;
|
|
13
18
|
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import { logger } from "#src/services/logger.ts";
|
|
3
|
+
import type { Doctor } from "#src/types/tool.ts";
|
|
4
|
+
|
|
5
|
+
export function createDoctorSubcommand(service: Doctor) {
|
|
6
|
+
return createCommand("doctor")
|
|
7
|
+
.summary("check if the underlying tool is working correctly")
|
|
8
|
+
.action(async function doctorAction() {
|
|
9
|
+
const debug = logger.subdebug("doctor");
|
|
10
|
+
const { ok, output } = await service.doctor();
|
|
11
|
+
|
|
12
|
+
if (ok) {
|
|
13
|
+
logger.success(`${service.ui} ok`);
|
|
14
|
+
debug("%O", output);
|
|
15
|
+
} else {
|
|
16
|
+
logger.error(`${service.ui} not working`);
|
|
17
|
+
debug("%O", output);
|
|
18
|
+
process.exit(output.exitCode ?? 1);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -2,13 +2,15 @@ import { createCommand } from "commander";
|
|
|
2
2
|
import { BiomeService } from "#src/services/biome.ts";
|
|
3
3
|
import type { Context } from "#src/services/ctx.ts";
|
|
4
4
|
import { OxfmtService } from "#src/services/oxfmt.ts";
|
|
5
|
+
import type { ToolService } from "#src/services/tool.ts";
|
|
5
6
|
import type { Formatter } from "#src/types/tool.ts";
|
|
7
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
6
8
|
|
|
7
9
|
type ActionOptions = {
|
|
8
10
|
fix?: boolean;
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
function getToolService(ctx: Context): Formatter {
|
|
13
|
+
function getToolService(ctx: Context): ToolService & Formatter {
|
|
12
14
|
const { config } = ctx.config;
|
|
13
15
|
|
|
14
16
|
if (config.future?.oxc) {
|
|
@@ -27,6 +29,7 @@ export function createFormatCommand(ctx: Context) {
|
|
|
27
29
|
"Checks the code for formatting issues and optionally fixes them, ensuring it adheres to the defined style standards.",
|
|
28
30
|
)
|
|
29
31
|
.option("--fix", "format all the code")
|
|
32
|
+
.addCommand(createDoctorSubcommand(toolService))
|
|
30
33
|
.action(async function formatAction(options: ActionOptions) {
|
|
31
34
|
await toolService.format(options);
|
|
32
35
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createCommand } from "commander";
|
|
2
2
|
import { BiomeService } from "#src/services/biome.ts";
|
|
3
3
|
import type { Context } from "#src/services/ctx.ts";
|
|
4
|
-
import
|
|
4
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
5
5
|
|
|
6
6
|
type ActionOptions = {
|
|
7
7
|
fix?: boolean;
|
|
@@ -9,7 +9,7 @@ type ActionOptions = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export function createJsCheckCommand(ctx: Context) {
|
|
12
|
-
const checkerService
|
|
12
|
+
const checkerService = new BiomeService(ctx.shell);
|
|
13
13
|
|
|
14
14
|
return createCommand("jsc")
|
|
15
15
|
.alias("jscheck")
|
|
@@ -20,6 +20,7 @@ export function createJsCheckCommand(ctx: Context) {
|
|
|
20
20
|
)
|
|
21
21
|
.option("--fix", "try to fix issues automatically")
|
|
22
22
|
.option("--fix-staged", "try to fix staged files only")
|
|
23
|
+
.addCommand(createDoctorSubcommand(checkerService))
|
|
23
24
|
.action(async function checkAction(options: ActionOptions) {
|
|
24
25
|
await checkerService.check(options);
|
|
25
26
|
})
|
|
@@ -2,14 +2,16 @@ import { createCommand } from "commander";
|
|
|
2
2
|
import { BiomeService } from "#src/services/biome.ts";
|
|
3
3
|
import type { Context } from "#src/services/ctx.ts";
|
|
4
4
|
import { OxlintService } from "#src/services/oxlint.ts";
|
|
5
|
+
import type { ToolService } from "#src/services/tool.ts";
|
|
5
6
|
import type { Linter } from "#src/types/tool.ts";
|
|
7
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
6
8
|
|
|
7
9
|
type ActionOptions = {
|
|
8
10
|
check?: boolean;
|
|
9
11
|
fix?: boolean;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
|
-
function getToolService(ctx: Context): Linter {
|
|
14
|
+
function getToolService(ctx: Context): ToolService & Linter {
|
|
13
15
|
const { config } = ctx.config;
|
|
14
16
|
|
|
15
17
|
if (config.future?.oxc) {
|
|
@@ -29,6 +31,7 @@ export function createLintCommand(ctx: Context) {
|
|
|
29
31
|
)
|
|
30
32
|
.option("-c, --check", "check if the code is valid", true)
|
|
31
33
|
.option("--fix", "try to fix all the code")
|
|
34
|
+
.addCommand(createDoctorSubcommand(toolService))
|
|
32
35
|
.action(async function lintAction(options: ActionOptions) {
|
|
33
36
|
await toolService.lint(options);
|
|
34
37
|
})
|
|
@@ -4,7 +4,9 @@ import { createCommand } from "commander";
|
|
|
4
4
|
import type { Context } from "#src/services/ctx.ts";
|
|
5
5
|
import { logger } from "#src/services/logger.ts";
|
|
6
6
|
import { OxlintService } from "#src/services/oxlint.ts";
|
|
7
|
+
import { TscService } from "#src/services/tsc.ts";
|
|
7
8
|
import { TOOL_LABELS } from "../ui.ts";
|
|
9
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
8
10
|
|
|
9
11
|
type TypecheckAtOptions = {
|
|
10
12
|
dir: string;
|
|
@@ -53,6 +55,7 @@ export function createTsCheckCommand(ctx: Context) {
|
|
|
53
55
|
} = ctx;
|
|
54
56
|
|
|
55
57
|
const toolUi = config.future?.oxc ? TOOL_LABELS.OXLINT : TOOL_LABELS.TSC;
|
|
58
|
+
const doctorService = config.future?.oxc ? new OxlintService(shell) : new TscService(shell);
|
|
56
59
|
|
|
57
60
|
return createCommand("tsc")
|
|
58
61
|
.alias("tscheck")
|
|
@@ -60,6 +63,7 @@ export function createTsCheckCommand(ctx: Context) {
|
|
|
60
63
|
.description(
|
|
61
64
|
"Checks the TypeScript code for type errors, ensuring that the code adheres to the defined type constraints and helps catch potential issues before runtime.",
|
|
62
65
|
)
|
|
66
|
+
.addCommand(createDoctorSubcommand(doctorService))
|
|
63
67
|
.addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolUi} CLI to check the code.`)
|
|
64
68
|
.action(async function typecheckAction() {
|
|
65
69
|
const isTsProject = (dir: string) => appPkg.hasFile("tsconfig.json", dir);
|
|
@@ -69,7 +73,9 @@ export function createTsCheckCommand(ctx: Context) {
|
|
|
69
73
|
const oxlint = new OxlintService(shell);
|
|
70
74
|
await oxlint.exec(`--type-aware --type-check --report-unused-disable-directives`);
|
|
71
75
|
} else {
|
|
72
|
-
|
|
76
|
+
const tsc = new TscService(shell);
|
|
77
|
+
await tsc.exec(`--noEmit`);
|
|
78
|
+
// await shell.$`tsc --noEmit`;
|
|
73
79
|
}
|
|
74
80
|
};
|
|
75
81
|
|
|
@@ -4,7 +4,9 @@ const debug = logger.subdebug("parseArgs");
|
|
|
4
4
|
|
|
5
5
|
export function parseArgs(argv = process.argv) {
|
|
6
6
|
const args = argv.slice(2);
|
|
7
|
-
const
|
|
7
|
+
const lastArg = args[args.length - 1];
|
|
8
|
+
const allArgsAreValidCommands =
|
|
9
|
+
args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools" && lastArg !== "doctor";
|
|
8
10
|
|
|
9
11
|
debug("args %O", args);
|
|
10
12
|
|
package/src/services/tool.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
4
|
import memoize from "memoize";
|
|
3
|
-
import {
|
|
5
|
+
import type { DoctorResult } from "#src/types/tool.ts";
|
|
4
6
|
|
|
5
7
|
type CreateOptions = {
|
|
6
8
|
bin: string;
|
|
@@ -21,22 +23,45 @@ export abstract class ToolService {
|
|
|
21
23
|
|
|
22
24
|
getBinDir?(): string;
|
|
23
25
|
|
|
24
|
-
exec(args: string | string[]) {
|
|
25
|
-
const
|
|
26
|
-
return this.#run(
|
|
26
|
+
async exec(args: string | string[]) {
|
|
27
|
+
const shell = this.#shell();
|
|
28
|
+
return this.#run(shell, args);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async doctor(): Promise<DoctorResult> {
|
|
32
|
+
const shell = this.#shell().mute();
|
|
33
|
+
|
|
34
|
+
const output = await this.#run(shell, "--help");
|
|
35
|
+
const ok = output.exitCode === 0;
|
|
36
|
+
|
|
37
|
+
return { ok, output };
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
#shell = memoize((cwd?: string) => {
|
|
30
|
-
const
|
|
41
|
+
const preferLocal = this.#getPreferLocal();
|
|
31
42
|
|
|
32
43
|
return this.#shellService.child({
|
|
33
|
-
cwd,
|
|
34
|
-
...(
|
|
35
|
-
})
|
|
44
|
+
...(cwd && { cwd }),
|
|
45
|
+
...(preferLocal && { preferLocal }),
|
|
46
|
+
});
|
|
36
47
|
});
|
|
37
48
|
|
|
38
|
-
#run(shell:
|
|
39
|
-
return shell
|
|
49
|
+
#run(shell: ShellService, args: string | string[]) {
|
|
50
|
+
return shell.$`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#getPreferLocal() {
|
|
54
|
+
if (!this.getBinDir) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const binPath = this.getBinDir();
|
|
60
|
+
const isDir = fs.statSync(binPath).isDirectory();
|
|
61
|
+
return isDir ? binPath : path.dirname(binPath);
|
|
62
|
+
} catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
40
65
|
}
|
|
41
66
|
|
|
42
67
|
get bin() {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import { TOOL_LABELS } from "#src/program/ui.ts";
|
|
3
|
+
import { ToolService } from "./tool.ts";
|
|
4
|
+
|
|
5
|
+
export class TscService extends ToolService {
|
|
6
|
+
constructor(shellService: ShellService) {
|
|
7
|
+
super({ bin: "tsc", ui: TOOL_LABELS.TSC, shellService });
|
|
8
|
+
}
|
|
9
|
+
}
|
package/src/types/tool.ts
CHANGED
|
@@ -11,6 +11,22 @@ export type StaticCheckerOptions = {
|
|
|
11
11
|
fixStaged?: boolean;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
export type DoctorOutput = {
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
exitCode: number | null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type DoctorResult = {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
output: DoctorOutput;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type Doctor = {
|
|
26
|
+
ui: string;
|
|
27
|
+
doctor(): Promise<DoctorResult>;
|
|
28
|
+
};
|
|
29
|
+
|
|
14
30
|
export type Formatter = {
|
|
15
31
|
bin: string;
|
|
16
32
|
ui: string;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { logger } from "#src/services/logger.ts";
|
|
4
|
-
|
|
5
|
-
export function gracefullBinDir(binPathResolver: () => string) {
|
|
6
|
-
try {
|
|
7
|
-
const binPath = binPathResolver();
|
|
8
|
-
const isDir = fs.statSync(binPath).isDirectory();
|
|
9
|
-
return isDir ? binPath : path.dirname(binPath);
|
|
10
|
-
} catch (error) {
|
|
11
|
-
logger.error("Error getting bin directory:", error);
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
}
|