@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 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
- 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.").action(async function buildAction() {
231
- await ctx.shell.$`tsdown`;
232
- }).addHelpText("afterAll", `\nUnder the hood, this command uses the ${TOOL_LABELS.TSDOWN} CLI to build the project.`);
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
- 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.").addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolUi} CLI to check the code.`).action(async function typecheckAction() {
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.$`tsc --noEmit`;
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 allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools";
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.4",
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.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 🏗️ (${TOOL_LABELS.TSDOWN})`)
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 ctx.shell.$`tsdown`;
16
+ await tsdownService.buildLib();
13
17
  })
14
- .addHelpText("afterAll", `\nUnder the hood, this command uses the ${TOOL_LABELS.TSDOWN} CLI to build the project.`);
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 type { StaticChecker } from "#src/types/tool.ts";
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: StaticChecker = new BiomeService(ctx.shell);
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
- await shell.$`tsc --noEmit`;
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 allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools";
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
 
@@ -1,6 +1,8 @@
1
- import type { Shell, ShellService } from "@vlandoss/clibuddy";
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 { gracefullBinDir } from "#src/utils/gracefullBinDir.ts";
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: string | string[]) {
25
- const $ = this.#shell();
26
- return this.#run($, args);
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 { getBinDir } = this;
41
+ const preferLocal = this.#getPreferLocal();
31
42
 
32
43
  return this.#shellService.child({
33
- cwd,
34
- ...(getBinDir && { preferLocal: gracefullBinDir(() => getBinDir()) }),
35
- }).$;
44
+ ...(cwd && { cwd }),
45
+ ...(preferLocal && { preferLocal }),
46
+ });
36
47
  });
37
48
 
38
- #run(shell: Shell, args: string | string[]) {
39
- return shell`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
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
+ }
@@ -6,4 +6,8 @@ export class TsdownService extends ToolService {
6
6
  constructor(shellService: ShellService) {
7
7
  super({ bin: "tsdown", ui: TOOL_LABELS.TSDOWN, shellService });
8
8
  }
9
+
10
+ async buildLib() {
11
+ await this.exec();
12
+ }
9
13
  }
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
- }