@vlandoss/run-run 0.0.13 โ†’ 0.0.14-git-b29f4ca.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vlandoss/run-run",
3
- "version": "0.0.13",
3
+ "version": "0.0.14-git-b29f4ca.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": {
@@ -13,7 +13,9 @@
13
13
  "license": "MIT",
14
14
  "author": "rcrd <rcrd@variable.land>",
15
15
  "type": "module",
16
- "module": "src/main.ts",
16
+ "exports": {
17
+ "./config": "./src/lib/config.ts"
18
+ },
17
19
  "bin": {
18
20
  "rr": "./bin.ts",
19
21
  "run-run": "./bin.ts",
@@ -28,14 +30,18 @@
28
30
  ],
29
31
  "dependencies": {
30
32
  "@biomejs/biome": "2.1.4",
33
+ "c12": "4.0.0-beta.3",
31
34
  "commander": "13.1.0",
32
35
  "glob": "^11.0.2",
33
36
  "is-ci": "4.1.0",
34
37
  "memoize": "^10.2.0",
38
+ "oxfmt": "^0.35.0",
39
+ "oxlint": "^1.50.0",
40
+ "oxlint-tsgolint": "^0.15.0",
35
41
  "rimraf": "6.0.1",
36
42
  "typescript": "5.8.2",
37
- "@vlandoss/clibuddy": "0.0.5",
38
- "@vlandoss/loggy": "0.0.5"
43
+ "@vlandoss/loggy": "0.0.5",
44
+ "@vlandoss/clibuddy": "0.0.5"
39
45
  },
40
46
  "publishConfig": {
41
47
  "access": "public"
@@ -0,0 +1,5 @@
1
+ import type { UserConfig } from "../types/config";
2
+
3
+ export function defineConfig(config: UserConfig) {
4
+ return config;
5
+ }
@@ -1,4 +1,4 @@
1
- // Bun Snapshot v1, https://goo.gl/fbAQLP
1
+ // Bun Snapshot v1, https://bun.sh/docs/test/snapshots
2
2
 
3
3
  exports[`should match all root commands: root-command-help 1`] = `
4
4
  "๐ŸฆŠ R U N - R U N: The CLI toolbox to fullstack common scripts in Variable Land ๐Ÿ‘Š
@@ -10,6 +10,7 @@ Options:
10
10
  -h, --help display help for command
11
11
 
12
12
  Commands:
13
+ config|cfg display the current config ๐Ÿ› ๏ธ
13
14
  check|test:static [options] check format and lint issues ๐Ÿ”
14
15
  lint [options] lint the code ๐Ÿงน
15
16
  fmt|format [options] format the code ๐ŸŽจ
@@ -39,6 +40,7 @@ Options:
39
40
  -h, --help display help for command
40
41
 
41
42
  Commands:
43
+ config|cfg display the current config ๐Ÿ› ๏ธ
42
44
  check|test:static [options] check format and lint issues ๐Ÿ”
43
45
  lint [options] lint the code ๐Ÿงน
44
46
  fmt|format [options] format the code ๐ŸŽจ
@@ -79,6 +81,16 @@ Options:
79
81
  "
80
82
  `;
81
83
 
84
+ exports[`should match help messages for all commands: help-command-config 1`] = `
85
+ "Usage: rr config|cfg [options]
86
+
87
+ display the current config ๐Ÿ› ๏ธ
88
+
89
+ Options:
90
+ -h, --help display help for command
91
+ "
92
+ `;
93
+
82
94
  exports[`should match help messages for all commands: help-command-check 1`] = `
83
95
  "Usage: rr check|test:static [options]
84
96
 
@@ -169,6 +181,8 @@ Options:
169
181
 
170
182
  Commands:
171
183
  biome
184
+ oxfmt
185
+ oxlint
172
186
  help [command] display help for command
173
187
  "
174
188
  `;
@@ -0,0 +1,11 @@
1
+ import { createCommand } from "commander";
2
+ import type { Context } from "#/services/ctx";
3
+
4
+ export function createConfigCommand(ctx: Context) {
5
+ return createCommand("config")
6
+ .alias("cfg")
7
+ .description("display the current config ๐Ÿ› ๏ธ")
8
+ .action(function configAction() {
9
+ console.log(ctx.config);
10
+ });
11
+ }
@@ -1,25 +1,53 @@
1
1
  import { createCommand } from "commander";
2
2
  import { BiomeService } from "#/services/biome";
3
3
  import type { Context } from "#/services/ctx";
4
+ import { OxfmtService } from "#/services/oxfmt";
5
+
6
+ type ActionOptions = {
7
+ check?: boolean;
8
+ fix?: boolean;
9
+ };
4
10
 
5
11
  export function createFormatCommand(ctx: Context) {
6
- return createCommand("fmt")
12
+ const fmtCommand = createCommand("fmt")
7
13
  .alias("format")
8
14
  .description("format the code ๐ŸŽจ")
9
15
  .option("-c, --check", "check if the code is formatted", true)
10
- .option("-f, --fix", "format all the code")
11
- .action(async function formatAction(options) {
12
- const { $ } = new BiomeService(ctx.shell);
13
- const toolCmd = "biome format --no-errors-on-unmatched --colors=force";
14
-
15
- if (options.fix) {
16
- await $`${toolCmd} --fix`;
17
- return;
18
- }
19
-
20
- if (options.check) {
21
- await $`${toolCmd}`;
22
- }
23
- })
24
- .addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to format the code.");
16
+ .option("-f, --fix", "format all the code");
17
+
18
+ if (ctx.config.future?.oxc) {
19
+ fmtCommand
20
+ .action(async function formatAction(options: ActionOptions) {
21
+ const { $ } = new OxfmtService(ctx.shell);
22
+ const toolCmd = "oxfmt --no-error-on-unmatched-pattern";
23
+
24
+ if (options.fix) {
25
+ await $`${toolCmd} --fix`;
26
+ return;
27
+ }
28
+
29
+ if (options.check) {
30
+ await $`${toolCmd} --check`;
31
+ }
32
+ })
33
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the oxfmt CLI to format the code.");
34
+ } else {
35
+ fmtCommand
36
+ .action(async function formatAction(options: ActionOptions) {
37
+ const { $ } = new BiomeService(ctx.shell);
38
+ const toolCmd = "biome format --no-errors-on-unmatched --colors=force";
39
+
40
+ if (options.fix) {
41
+ await $`${toolCmd} --fix`;
42
+ return;
43
+ }
44
+
45
+ if (options.check) {
46
+ await $`${toolCmd}`;
47
+ }
48
+ })
49
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to format the code.");
50
+ }
51
+
52
+ return fmtCommand;
25
53
  }
@@ -1,24 +1,52 @@
1
1
  import { createCommand } from "commander";
2
2
  import { BiomeService } from "#/services/biome";
3
3
  import type { Context } from "#/services/ctx";
4
+ import { OxlintService } from "#/services/oxlint";
5
+
6
+ type ActionOptions = {
7
+ check?: boolean;
8
+ fix?: boolean;
9
+ };
4
10
 
5
11
  export function createLintCommand(ctx: Context) {
6
- return createCommand("lint")
12
+ const lintCommand = createCommand("lint")
7
13
  .description("lint the code ๐Ÿงน")
8
14
  .option("-c, --check", "check if the code is valid", true)
9
- .option("-f, --fix", "try to fix all the code")
10
- .action(async function lintAction(options) {
11
- const { $ } = new BiomeService(ctx.shell);
12
- const toolCmd = "biome check --colors=force --formatter-enabled=false";
13
-
14
- if (options.fix) {
15
- await $`${toolCmd} --fix --unsafe`;
16
- return;
17
- }
18
-
19
- if (options.check) {
20
- await $`${toolCmd}`;
21
- }
22
- })
23
- .addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to lint the code.");
15
+ .option("-f, --fix", "try to fix all the code");
16
+
17
+ if (ctx.config.future?.oxc) {
18
+ lintCommand
19
+ .action(async function lintAction(options: ActionOptions) {
20
+ const { $ } = new OxlintService(ctx.shell);
21
+ const toolCmd = "oxlint --report-unused-disable-directives";
22
+
23
+ if (options.fix) {
24
+ await $`${toolCmd} --fix`;
25
+ return;
26
+ }
27
+
28
+ if (options.check) {
29
+ await $`${toolCmd} --check`;
30
+ }
31
+ })
32
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the oxlint CLI to lint the code.");
33
+ } else {
34
+ lintCommand
35
+ .action(async function lintAction(options: ActionOptions) {
36
+ const { $ } = new BiomeService(ctx.shell);
37
+ const toolCmd = "biome check --colors=force --formatter-enabled=false";
38
+
39
+ if (options.fix) {
40
+ await $`${toolCmd} --fix --unsafe`;
41
+ return;
42
+ }
43
+
44
+ if (options.check) {
45
+ await $`${toolCmd}`;
46
+ }
47
+ })
48
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to lint the code.");
49
+ }
50
+
51
+ return lintCommand;
24
52
  }
@@ -1,6 +1,12 @@
1
1
  import { createCommand } from "commander";
2
2
  import { BiomeService } from "#/services/biome";
3
3
  import type { Context } from "#/services/ctx";
4
+ import { OxfmtService } from "#/services/oxfmt";
5
+ import { OxlintService } from "#/services/oxlint";
6
+
7
+ type ActionParams = {
8
+ args: string[];
9
+ };
4
10
 
5
11
  function createToolCommand(toolBin: string) {
6
12
  // biome-ignore format: I prefer multi-line here
@@ -15,9 +21,21 @@ export function createToolsCommand(ctx: Context) {
15
21
  return createCommand("tools")
16
22
  .description("expose the internal tools ๐Ÿ› ๏ธ")
17
23
  .addCommand(
18
- createToolCommand("biome").action((_: unknown, { args }: { args: string[] }) => {
24
+ createToolCommand("biome").action((_: unknown, { args }: ActionParams) => {
19
25
  const biomeService = new BiomeService(ctx.shell);
20
26
  biomeService.execute(args);
21
27
  }),
28
+ )
29
+ .addCommand(
30
+ createToolCommand("oxfmt").action((_: unknown, { args }: ActionParams) => {
31
+ const oxfmtService = new OxfmtService(ctx.shell);
32
+ oxfmtService.execute(args);
33
+ }),
34
+ )
35
+ .addCommand(
36
+ createToolCommand("oxlint").action((_: unknown, { args }: ActionParams) => {
37
+ const oxlintService = new OxlintService(ctx.shell);
38
+ oxlintService.execute(args);
39
+ }),
22
40
  );
23
41
  }
@@ -3,6 +3,7 @@ import type { AnyLogger } from "@vlandoss/loggy";
3
3
  import { createCommand } from "commander";
4
4
  import type { Context } from "#/services/ctx";
5
5
  import { logger } from "#/services/logger";
6
+ import { OxlintService } from "#/services/oxlint";
6
7
 
7
8
  type TypecheckAtOptions = {
8
9
  dir: string;
@@ -11,71 +12,83 @@ type TypecheckAtOptions = {
11
12
  };
12
13
 
13
14
  export function createTypecheckCommand(ctx: Context) {
14
- return createCommand("tsc")
15
- .alias("typecheck")
16
- .description("check if TypeScript code is well typed ๐ŸŽจ")
17
- .action(async function typecheckAction() {
18
- const { appPkg, shell } = ctx;
15
+ const typecheckCommand = createCommand("tsc").alias("typecheck").description("check if TypeScript code is well typed ๐ŸŽจ");
19
16
 
20
- const isTsProject = (dir: string) => appPkg.hasFile("tsconfig.json", dir);
17
+ if (ctx.config.future?.oxc) {
18
+ typecheckCommand
19
+ .action(async function typecheckAction() {
20
+ const { $ } = new OxlintService(ctx.shell);
21
+ await $`oxlint --type-aware --type-check --report-unused-disable-directives`;
22
+ })
23
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the oxlint CLI to check the code.");
24
+ } else {
25
+ typecheckCommand
26
+ .action(async function typecheckAction() {
27
+ const { appPkg, shell } = ctx;
21
28
 
22
- const getPreScript = (scripts: Record<string, string | undefined> | undefined) => scripts?.pretsc ?? scripts?.pretypecheck;
29
+ const isTsProject = (dir: string) => appPkg.hasFile("tsconfig.json", dir);
23
30
 
24
- async function typecheckAt({ dir, scripts, log }: TypecheckAtOptions) {
25
- const shellAt = cwd === dir ? shell : shell.at(dir);
31
+ const getPreScript = (scripts: Record<string, string | undefined> | undefined) =>
32
+ scripts?.pretsc ?? scripts?.pretypecheck;
26
33
 
27
- try {
28
- const preScript = getPreScript(scripts);
29
- if (preScript) {
30
- log.start(`Running pre-script: ${preScript}`);
31
- await shellAt.$`${preScript}`;
32
- log.success("Pre-script completed");
33
- }
34
+ async function typecheckAt({ dir, scripts, log }: TypecheckAtOptions) {
35
+ const shellAt = cwd === dir ? shell : shell.at(dir);
34
36
 
35
- log.start("Type checking started");
36
- await shellAt.$`tsc --noEmit`;
37
- log.success("Typecheck completed");
38
- } catch (error) {
39
- log.error("Typecheck failed");
40
- throw error;
41
- }
42
- }
37
+ try {
38
+ const preScript = getPreScript(scripts);
39
+ if (preScript) {
40
+ log.start(`Running pre-script: ${preScript}`);
41
+ await shellAt.$`${preScript}`;
42
+ log.success("Pre-script completed");
43
+ }
43
44
 
44
- if (!appPkg.isMonorepo()) {
45
- if (!isTsProject(appPkg.dirPath)) {
46
- logger.info("No tsconfig.json found, skipping typecheck");
47
- return;
45
+ log.start("Type checking started");
46
+ await shellAt.$`tsc --noEmit`;
47
+ log.success("Typecheck completed");
48
+ } catch (error) {
49
+ log.error("Typecheck failed");
50
+ throw error;
51
+ }
48
52
  }
49
53
 
50
- await typecheckAt({
51
- dir: appPkg.dirPath,
52
- scripts: appPkg.packageJson.scripts,
53
- log: logger,
54
- });
54
+ if (!appPkg.isMonorepo()) {
55
+ if (!isTsProject(appPkg.dirPath)) {
56
+ logger.info("No tsconfig.json found, skipping typecheck");
57
+ return;
58
+ }
55
59
 
56
- return;
57
- }
60
+ await typecheckAt({
61
+ dir: appPkg.dirPath,
62
+ scripts: appPkg.packageJson.scripts,
63
+ log: logger,
64
+ });
58
65
 
59
- const projects = await appPkg.getWorkspaceProjects();
60
- const tsProjects = projects.filter((project) => isTsProject(project.rootDir));
66
+ return;
67
+ }
61
68
 
62
- if (!tsProjects.length) {
63
- logger.warn("No TypeScript projects found in the monorepo, skipping typecheck");
64
- return;
65
- }
69
+ const projects = await appPkg.getWorkspaceProjects();
70
+ const tsProjects = projects.filter((project) => isTsProject(project.rootDir));
66
71
 
67
- await Promise.all(
68
- tsProjects.map((p) =>
69
- typecheckAt({
70
- dir: p.rootDir,
71
- scripts: p.manifest.scripts,
72
- log: logger.child({
73
- tag: p.manifest.name,
74
- namespace: "typecheck",
72
+ if (!tsProjects.length) {
73
+ logger.warn("No TypeScript projects found in the monorepo, skipping typecheck");
74
+ return;
75
+ }
76
+
77
+ await Promise.all(
78
+ tsProjects.map((p) =>
79
+ typecheckAt({
80
+ dir: p.rootDir,
81
+ scripts: p.manifest.scripts,
82
+ log: logger.child({
83
+ tag: p.manifest.name,
84
+ namespace: "typecheck",
85
+ }),
75
86
  }),
76
- }),
77
- ),
78
- );
79
- })
80
- .addHelpText("afterAll", "\nUnder the hood, this command uses the TypeScript CLI to check the code.");
87
+ ),
88
+ );
89
+ })
90
+ .addHelpText("afterAll", "\nUnder the hood, this command uses the TypeScript CLI to check the code.");
91
+ }
92
+
93
+ return typecheckCommand;
81
94
  }
@@ -3,6 +3,7 @@ import { createCommand } from "commander";
3
3
  import { createContext } from "#/services/ctx";
4
4
  import { createCheckCommand } from "./commands/check";
5
5
  import { createCleanCommand } from "./commands/clean";
6
+ import { createConfigCommand } from "./commands/config";
6
7
  import { createFormatCommand } from "./commands/format";
7
8
  import { createLintCommand } from "./commands/lint";
8
9
  import { createPkgsCommand } from "./commands/pkgs";
@@ -27,6 +28,7 @@ export async function createProgram(options: Options) {
27
28
  .addCommand(createRunCommand(ctx), {
28
29
  hidden: true,
29
30
  })
31
+ .addCommand(createConfigCommand(ctx))
30
32
  .addCommand(createCheckCommand(ctx))
31
33
  .addCommand(createLintCommand(ctx))
32
34
  .addCommand(createFormatCommand(ctx))
@@ -1,25 +1,12 @@
1
1
  import type { ShellService } from "@vlandoss/clibuddy";
2
- import { gracefullBinDir } from "#/utils/gracefullBinDir";
3
- import type { ToolService } from "./models";
4
-
5
- export class BiomeService implements ToolService {
6
- #shellService: ShellService;
2
+ import { ToolService } from "./tool";
7
3
 
4
+ export class BiomeService extends ToolService {
8
5
  constructor(shellService: ShellService) {
9
- this.#shellService = shellService;
10
- }
11
-
12
- get $() {
13
- return this.#shellService.child({
14
- preferLocal: this.#getBinDir(),
15
- }).$;
16
- }
17
-
18
- async execute(args: string[]): Promise<void> {
19
- this.$`biome ${args.join(" ")}`;
6
+ super({ cmd: "biome", shellService });
20
7
  }
21
8
 
22
- #getBinDir() {
23
- return gracefullBinDir(() => require.resolve("@biomejs/biome/bin/biome"));
9
+ getBinDir() {
10
+ return require.resolve("@biomejs/biome/bin/biome");
24
11
  }
25
12
  }
@@ -0,0 +1,27 @@
1
+ import { loadConfig } from "c12";
2
+ import type { UserConfig } from "#/types/config";
3
+ import { logger } from "./logger";
4
+
5
+ const DEFAULT_CONFIG: UserConfig = {
6
+ future: {
7
+ oxc: false,
8
+ },
9
+ };
10
+
11
+ export class ConfigService {
12
+ async load(cwd?: string) {
13
+ const debug = logger.subdebug("load-config");
14
+
15
+ const result = await loadConfig<UserConfig>({
16
+ cwd,
17
+ name: "run-run",
18
+ defaultConfig: DEFAULT_CONFIG,
19
+ });
20
+
21
+ debug("loaded config: %O", result.config);
22
+ debug("config cwd: %s", result.cwd);
23
+ debug("config file: %s", result.configFile);
24
+
25
+ return result.config;
26
+ }
27
+ }
@@ -1,15 +1,18 @@
1
1
  import fs from "node:fs";
2
2
  import { createPkgService, createShellService, cwd, type PkgService, type ShellService } from "@vlandoss/clibuddy";
3
+ import type { UserConfig } from "#/types/config";
4
+ import { ConfigService } from "./config";
3
5
  import { logger } from "./logger";
4
6
 
5
7
  export type Context = {
6
8
  binPkg: PkgService;
7
9
  appPkg: PkgService;
8
10
  shell: ShellService;
11
+ config: UserConfig;
9
12
  };
10
13
 
11
14
  export async function createContext(binDir: string): Promise<Context> {
12
- const debug = logger.subdebug("create-context-value");
15
+ const debug = logger.subdebug("create-context");
13
16
 
14
17
  const binPath = fs.realpathSync(binDir);
15
18
 
@@ -36,9 +39,13 @@ export async function createContext(binDir: string): Promise<Context> {
36
39
 
37
40
  debug("shell service options: %O", shell.options);
38
41
 
42
+ const configService = new ConfigService();
43
+ const config = await configService.load();
44
+
39
45
  return {
40
46
  appPkg,
41
47
  binPkg,
42
48
  shell,
49
+ config,
43
50
  };
44
51
  }
@@ -0,0 +1,12 @@
1
+ import type { ShellService } from "@vlandoss/clibuddy";
2
+ import { ToolService } from "./tool";
3
+
4
+ export class OxfmtService extends ToolService {
5
+ constructor(shellService: ShellService) {
6
+ super({ cmd: "oxfmt", shellService });
7
+ }
8
+
9
+ getBinDir() {
10
+ return require.resolve("oxfmt/bin/oxfmt");
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { ShellService } from "@vlandoss/clibuddy";
2
+ import { ToolService } from "./tool";
3
+
4
+ export class OxlintService extends ToolService {
5
+ constructor(shellService: ShellService) {
6
+ super({ cmd: "oxlint", shellService });
7
+ }
8
+
9
+ getBinDir() {
10
+ return require.resolve("oxlint/bin/oxlint");
11
+ }
12
+ }
@@ -0,0 +1,29 @@
1
+ import type { ShellService } from "@vlandoss/clibuddy";
2
+ import { gracefullBinDir } from "#/utils/gracefullBinDir";
3
+
4
+ type CreateOptions = {
5
+ cmd: string;
6
+ shellService: ShellService;
7
+ };
8
+
9
+ export abstract class ToolService {
10
+ #shellService: ShellService;
11
+ #cmd: string;
12
+
13
+ constructor({ cmd, shellService }: CreateOptions) {
14
+ this.#cmd = cmd;
15
+ this.#shellService = shellService;
16
+ }
17
+
18
+ abstract getBinDir(): string;
19
+
20
+ get $() {
21
+ return this.#shellService.child({
22
+ preferLocal: gracefullBinDir(() => this.getBinDir()),
23
+ }).$;
24
+ }
25
+
26
+ async execute(args: string[]): Promise<void> {
27
+ this.$`${this.#cmd} ${args.join(" ")}`;
28
+ }
29
+ }
@@ -0,0 +1,5 @@
1
+ export type UserConfig = {
2
+ future?: {
3
+ oxc?: boolean;
4
+ };
5
+ };
package/tools/oxfmt ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ bunx rr tools oxfmt "$@"
package/tools/oxlint ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ bunx rr tools oxlint "$@"
@@ -1,6 +0,0 @@
1
- import type { Shell } from "@vlandoss/clibuddy";
2
-
3
- export interface ToolService {
4
- $: Shell;
5
- execute(args: string[]): Promise<void>;
6
- }