@vlandoss/run-run 0.1.4-git-c3e109d.0 → 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 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
- 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() {
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
- 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() {
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.$`tsc --noEmit`;
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 allArgsAreValidCommands = args.every((arg) => !arg.startsWith("-")) && args.length > 1 && args[0] !== "tools";
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.4-git-c3e109d.0",
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.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 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,45 @@ 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
+ 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
- }