@vlandoss/run-run 0.1.4 → 0.1.5-git-2e07910.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 +105 -65
- package/package.json +2 -2
- package/src/program/commands/build-lib.ts +8 -4
- 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 +40 -11
- package/src/services/tsc.ts +9 -0
- package/src/services/tsdown.ts +4 -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,11 +225,92 @@ 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
|
+
if (!args) return shell.$`${this.#bin}`;
|
|
259
|
+
return shell.$`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
260
|
+
}
|
|
261
|
+
#getPreferLocal() {
|
|
262
|
+
if (!this.getBinDir) return;
|
|
263
|
+
try {
|
|
264
|
+
const binPath = this.getBinDir();
|
|
265
|
+
return fs.statSync(binPath).isDirectory() ? binPath : path.dirname(binPath);
|
|
266
|
+
} catch {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
get bin() {
|
|
271
|
+
return this.#bin;
|
|
272
|
+
}
|
|
273
|
+
get ui() {
|
|
274
|
+
return this.#ui;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/services/tsdown.ts
|
|
279
|
+
var TsdownService = class extends ToolService {
|
|
280
|
+
constructor(shellService) {
|
|
281
|
+
super({
|
|
282
|
+
bin: "tsdown",
|
|
283
|
+
ui: TOOL_LABELS.TSDOWN,
|
|
284
|
+
shellService
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
async buildLib() {
|
|
288
|
+
await this.exec();
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/program/commands/doctor.ts
|
|
293
|
+
function createDoctorSubcommand(service) {
|
|
294
|
+
return createCommand("doctor").summary("check if the underlying tool is working correctly").action(async function doctorAction() {
|
|
295
|
+
const debug = logger.subdebug("doctor");
|
|
296
|
+
const { ok, output } = await service.doctor();
|
|
297
|
+
if (ok) {
|
|
298
|
+
logger.success(`${service.ui} ok`);
|
|
299
|
+
debug("%O", output);
|
|
300
|
+
} else {
|
|
301
|
+
logger.error(`${service.ui} not working`);
|
|
302
|
+
debug("%O", output);
|
|
303
|
+
process.exit(output.exitCode ?? 1);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
228
308
|
//#region src/program/commands/build-lib.ts
|
|
229
309
|
function createBuildLibCommand(ctx) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
310
|
+
const tsdownService = new TsdownService(ctx.shell);
|
|
311
|
+
return createCommand("build:lib").summary(`build a ts library 🏗️ (${tsdownService.ui})`).description("Compiles TypeScript code into JavaScript and generates type declaration files, making it ready for distribution.").addCommand(createDoctorSubcommand(tsdownService)).action(async function buildAction() {
|
|
312
|
+
await tsdownService.buildLib();
|
|
313
|
+
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${tsdownService.ui} CLI to build the project.`);
|
|
233
314
|
}
|
|
234
315
|
//#endregion
|
|
235
316
|
//#region src/program/commands/clean.ts
|
|
@@ -271,49 +352,6 @@ function createConfigCommand(ctx) {
|
|
|
271
352
|
});
|
|
272
353
|
}
|
|
273
354
|
//#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
355
|
//#region src/services/biome.ts
|
|
318
356
|
var BiomeService = class extends ToolService {
|
|
319
357
|
constructor(shellService) {
|
|
@@ -371,7 +409,7 @@ function getToolService$2(ctx) {
|
|
|
371
409
|
}
|
|
372
410
|
function createFormatCommand(ctx) {
|
|
373
411
|
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) {
|
|
412
|
+
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
413
|
await toolService.format(options);
|
|
376
414
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to format the code.`);
|
|
377
415
|
}
|
|
@@ -379,7 +417,7 @@ function createFormatCommand(ctx) {
|
|
|
379
417
|
//#region src/program/commands/jscheck.ts
|
|
380
418
|
function createJsCheckCommand(ctx) {
|
|
381
419
|
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) {
|
|
420
|
+
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
421
|
await checkerService.check(options);
|
|
384
422
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${checkerService.ui} CLI to check the code.`);
|
|
385
423
|
}
|
|
@@ -411,7 +449,7 @@ function getToolService$1(ctx) {
|
|
|
411
449
|
}
|
|
412
450
|
function createLintCommand(ctx) {
|
|
413
451
|
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) {
|
|
452
|
+
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
453
|
await toolService.lint(options);
|
|
416
454
|
}).addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.ui} CLI to lint the code.`);
|
|
417
455
|
}
|
|
@@ -467,17 +505,6 @@ function createTestStaticCommand(ctx) {
|
|
|
467
505
|
});
|
|
468
506
|
}
|
|
469
507
|
//#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
508
|
//#region src/program/commands/tools.ts
|
|
482
509
|
function getToolService(bin, shell) {
|
|
483
510
|
switch (bin) {
|
|
@@ -498,6 +525,17 @@ function createToolsCommand(ctx) {
|
|
|
498
525
|
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
526
|
}
|
|
500
527
|
//#endregion
|
|
528
|
+
//#region src/services/tsc.ts
|
|
529
|
+
var TscService = class extends ToolService {
|
|
530
|
+
constructor(shellService) {
|
|
531
|
+
super({
|
|
532
|
+
bin: "tsc",
|
|
533
|
+
ui: TOOL_LABELS.TSC,
|
|
534
|
+
shellService
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
//#endregion
|
|
501
539
|
//#region src/program/commands/tscheck.ts
|
|
502
540
|
const getPreScript = (scripts) => scripts?.pretsc ?? scripts?.pretypecheck;
|
|
503
541
|
async function typecheckAt({ dir, scripts, log, shell, run }) {
|
|
@@ -526,11 +564,12 @@ async function typecheckAt({ dir, scripts, log, shell, run }) {
|
|
|
526
564
|
function createTsCheckCommand(ctx) {
|
|
527
565
|
const { appPkg, shell, config: { config } } = ctx;
|
|
528
566
|
const toolUi = config.future?.oxc ? TOOL_LABELS.OXLINT : TOOL_LABELS.TSC;
|
|
529
|
-
|
|
567
|
+
const doctorService = config.future?.oxc ? new OxlintService(shell) : new TscService(shell);
|
|
568
|
+
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
569
|
const isTsProject = (dir) => appPkg.hasFile("tsconfig.json", dir);
|
|
531
570
|
const runTypecheck = async (shell) => {
|
|
532
571
|
if (config.future?.oxc) await new OxlintService(shell).exec(`--type-aware --type-check --report-unused-disable-directives`);
|
|
533
|
-
else await shell
|
|
572
|
+
else await new TscService(shell).exec(`--noEmit`);
|
|
534
573
|
};
|
|
535
574
|
if (!appPkg.isMonorepo()) {
|
|
536
575
|
if (!isTsProject(appPkg.dirPath)) {
|
|
@@ -578,7 +617,8 @@ async function createProgram(options) {
|
|
|
578
617
|
const debug = logger.subdebug("parseArgs");
|
|
579
618
|
function parseArgs(argv = process.argv) {
|
|
580
619
|
const args = argv.slice(2);
|
|
581
|
-
const
|
|
620
|
+
const lastArg = args[args.length - 1];
|
|
621
|
+
const allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools" && lastArg !== "doctor";
|
|
582
622
|
debug("args %O", args);
|
|
583
623
|
if (allArgsAreValidCommands) {
|
|
584
624
|
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-2e07910.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-2e07910.0",
|
|
62
62
|
"@vlandoss/loggy": "0.1.1"
|
|
63
63
|
},
|
|
64
64
|
"publishConfig": {
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { createCommand } from "commander";
|
|
2
|
-
import { TOOL_LABELS } from "#src/program/ui.ts";
|
|
3
2
|
import type { Context } from "#src/services/ctx.ts";
|
|
3
|
+
import { TsdownService } from "#src/services/tsdown.ts";
|
|
4
|
+
import { createDoctorSubcommand } from "./doctor.ts";
|
|
4
5
|
|
|
5
6
|
export function createBuildLibCommand(ctx: Context) {
|
|
7
|
+
const tsdownService = new TsdownService(ctx.shell);
|
|
8
|
+
|
|
6
9
|
return createCommand("build:lib")
|
|
7
|
-
.summary(`build a ts library 🏗️ (${
|
|
10
|
+
.summary(`build a ts library 🏗️ (${tsdownService.ui})`)
|
|
8
11
|
.description(
|
|
9
12
|
"Compiles TypeScript code into JavaScript and generates type declaration files, making it ready for distribution.",
|
|
10
13
|
)
|
|
14
|
+
.addCommand(createDoctorSubcommand(tsdownService))
|
|
11
15
|
.action(async function buildAction() {
|
|
12
|
-
await
|
|
16
|
+
await tsdownService.buildLib();
|
|
13
17
|
})
|
|
14
|
-
.addHelpText("afterAll", `\nUnder the hood, this command uses the ${
|
|
18
|
+
.addHelpText("afterAll", `\nUnder the hood, this command uses the ${tsdownService.ui} CLI to build the project.`);
|
|
15
19
|
}
|
|
@@ -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,49 @@ export abstract class ToolService {
|
|
|
21
23
|
|
|
22
24
|
getBinDir?(): string;
|
|
23
25
|
|
|
24
|
-
exec(args
|
|
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
|
-
|
|
49
|
+
#run(shell: ShellService, args?: string | string[]) {
|
|
50
|
+
if (!args) {
|
|
51
|
+
return shell.$`${this.#bin}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return shell.$`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#getPreferLocal() {
|
|
58
|
+
if (!this.getBinDir) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const binPath = this.getBinDir();
|
|
64
|
+
const isDir = fs.statSync(binPath).isDirectory();
|
|
65
|
+
return isDir ? binPath : path.dirname(binPath);
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
40
69
|
}
|
|
41
70
|
|
|
42
71
|
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/services/tsdown.ts
CHANGED
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
|
-
}
|