@vlandoss/run-run 0.0.14-git-40ae44d.0 → 0.0.14-git-cf575e8.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 +7 -6
- package/src/program/commands/check.ts +7 -11
- package/src/program/commands/config.ts +6 -2
- package/src/program/commands/format.ts +19 -38
- package/src/program/commands/lint.ts +18 -37
- package/src/program/commands/tools.ts +26 -22
- package/src/program/commands/typecheck.ts +77 -67
- package/src/services/biome.ts +23 -2
- package/src/services/config.ts +40 -12
- package/src/services/ctx.ts +2 -2
- package/src/services/oxfmt.ts +13 -2
- package/src/services/oxlint.ts +13 -2
- package/src/services/tool.ts +19 -8
- package/src/types/config.ts +8 -0
- package/src/types/tool.ts +19 -0
- package/tools/oxfmt +0 -0
- package/tools/oxlint +0 -0
- package/src/utils/__tests__/get.test.ts +0 -22
- package/src/utils/get.ts +0 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vlandoss/run-run",
|
|
3
|
-
"version": "0.0.14-git-
|
|
3
|
+
"version": "0.0.14-git-cf575e8.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": {
|
|
@@ -19,28 +19,29 @@
|
|
|
19
19
|
"bin": {
|
|
20
20
|
"rr": "./bin.ts",
|
|
21
21
|
"run-run": "./bin.ts",
|
|
22
|
-
"biome": "./tools/biome"
|
|
22
|
+
"biome": "./tools/biome",
|
|
23
|
+
"oxfmt": "./tools/oxfmt",
|
|
24
|
+
"oxlint": "./tools/oxlint"
|
|
23
25
|
},
|
|
24
26
|
"files": [
|
|
25
|
-
"bin",
|
|
27
|
+
"bin.ts",
|
|
26
28
|
"src",
|
|
27
29
|
"tools",
|
|
28
|
-
"plopfiles",
|
|
29
30
|
"tsconfig.json"
|
|
30
31
|
],
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@biomejs/biome": "2.1.4",
|
|
33
|
-
"c12": "4.0.0-beta.3",
|
|
34
34
|
"commander": "13.1.0",
|
|
35
35
|
"glob": "^11.0.2",
|
|
36
36
|
"is-ci": "4.1.0",
|
|
37
|
+
"lilconfig": "^3.1.3",
|
|
37
38
|
"memoize": "^10.2.0",
|
|
38
39
|
"oxfmt": "^0.35.0",
|
|
39
40
|
"oxlint": "^1.50.0",
|
|
40
41
|
"oxlint-tsgolint": "^0.15.0",
|
|
41
42
|
"rimraf": "6.0.1",
|
|
42
43
|
"typescript": "5.8.2",
|
|
43
|
-
"@vlandoss/clibuddy": "0.0.
|
|
44
|
+
"@vlandoss/clibuddy": "0.0.6-git-cf575e8.0",
|
|
44
45
|
"@vlandoss/loggy": "0.0.5"
|
|
45
46
|
},
|
|
46
47
|
"publishConfig": {
|
|
@@ -10,20 +10,16 @@ export function createCheckCommand(ctx: Context) {
|
|
|
10
10
|
.option("-f, --fix", "try to fix issues automatically")
|
|
11
11
|
.option("--fix-staged", "try to fix staged files only")
|
|
12
12
|
.action(async function checkAction(options) {
|
|
13
|
-
const
|
|
14
|
-
const toolCmd = (cmd = "check") =>
|
|
13
|
+
const biome = new BiomeService(ctx.shell);
|
|
14
|
+
const toolCmd = (cmd = "check") => `${cmd} --colors=force`;
|
|
15
15
|
|
|
16
16
|
if (options.fix) {
|
|
17
|
-
await
|
|
18
|
-
|
|
17
|
+
await biome.exec(`${toolCmd()} --fix`);
|
|
18
|
+
} else if (options.fixStaged) {
|
|
19
|
+
await biome.exec(`${toolCmd()} --no-errors-on-unmatched --fix --staged`);
|
|
20
|
+
} else {
|
|
21
|
+
await biome.exec(`${toolCmd(isCI ? "ci" : "check")}`);
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
if (options.fixStaged) {
|
|
22
|
-
await $`${toolCmd()} --no-errors-on-unmatched --fix --staged`;
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
await $`${toolCmd(isCI ? "ci" : "check")}`;
|
|
27
23
|
})
|
|
28
24
|
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to check the code.");
|
|
29
25
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { colors } from "@vlandoss/clibuddy";
|
|
1
2
|
import { createCommand } from "commander";
|
|
2
3
|
import type { Context } from "#/services/ctx";
|
|
3
4
|
|
|
@@ -5,7 +6,10 @@ export function createConfigCommand(ctx: Context) {
|
|
|
5
6
|
return createCommand("config")
|
|
6
7
|
.alias("cfg")
|
|
7
8
|
.description("display the current config 🛠️")
|
|
8
|
-
.action(function configAction() {
|
|
9
|
-
|
|
9
|
+
.action(async function configAction() {
|
|
10
|
+
const { config, meta } = ctx.config;
|
|
11
|
+
console.log(colors.muted("Config:"));
|
|
12
|
+
console.log(config);
|
|
13
|
+
console.log(colors.muted(`Loaded from ${meta.filepath ? colors.link(meta.filepath) : "n/a"}`));
|
|
10
14
|
});
|
|
11
15
|
}
|
|
@@ -2,52 +2,33 @@ import { createCommand } from "commander";
|
|
|
2
2
|
import { BiomeService } from "#/services/biome";
|
|
3
3
|
import type { Context } from "#/services/ctx";
|
|
4
4
|
import { OxfmtService } from "#/services/oxfmt";
|
|
5
|
+
import type { Formatter } from "#/types/tool";
|
|
5
6
|
|
|
6
7
|
type ActionOptions = {
|
|
7
8
|
check?: boolean;
|
|
8
9
|
fix?: boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
.alias("format")
|
|
14
|
-
.description("format the code 🎨")
|
|
15
|
-
.option("-c, --check", "check if the code is formatted", true)
|
|
16
|
-
.option("-f, --fix", "format all the code");
|
|
12
|
+
function getToolService(ctx: Context): Formatter {
|
|
13
|
+
const { config } = ctx.config;
|
|
17
14
|
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
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";
|
|
15
|
+
if (config.future?.oxc) {
|
|
16
|
+
return new OxfmtService(ctx.shell);
|
|
17
|
+
}
|
|
39
18
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
19
|
+
return new BiomeService(ctx.shell);
|
|
20
|
+
}
|
|
44
21
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to format the code.");
|
|
50
|
-
}
|
|
22
|
+
export function createFormatCommand(ctx: Context) {
|
|
23
|
+
const toolService = getToolService(ctx);
|
|
51
24
|
|
|
52
|
-
return
|
|
25
|
+
return createCommand("fmt")
|
|
26
|
+
.alias("format")
|
|
27
|
+
.description("format the code 🎨")
|
|
28
|
+
.option("-c, --check", "check if the code is formatted", true)
|
|
29
|
+
.option("-f, --fix", "format all the code")
|
|
30
|
+
.action(async function formatAction(options: ActionOptions) {
|
|
31
|
+
await toolService.format(options);
|
|
32
|
+
})
|
|
33
|
+
.addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.bin} CLI to format the code.`);
|
|
53
34
|
}
|
|
@@ -2,51 +2,32 @@ import { createCommand } from "commander";
|
|
|
2
2
|
import { BiomeService } from "#/services/biome";
|
|
3
3
|
import type { Context } from "#/services/ctx";
|
|
4
4
|
import { OxlintService } from "#/services/oxlint";
|
|
5
|
+
import type { Linter } from "#/types/tool";
|
|
5
6
|
|
|
6
7
|
type ActionOptions = {
|
|
7
8
|
check?: boolean;
|
|
8
9
|
fix?: boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
.description("lint the code 🧹")
|
|
14
|
-
.option("-c, --check", "check if the code is valid", true)
|
|
15
|
-
.option("-f, --fix", "try to fix all the code");
|
|
12
|
+
function getToolService(ctx: Context): Linter {
|
|
13
|
+
const { config } = ctx.config;
|
|
16
14
|
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
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";
|
|
15
|
+
if (config.future?.oxc) {
|
|
16
|
+
return new OxlintService(ctx.shell);
|
|
17
|
+
}
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
19
|
+
return new BiomeService(ctx.shell);
|
|
20
|
+
}
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to lint the code.");
|
|
49
|
-
}
|
|
22
|
+
export function createLintCommand(ctx: Context) {
|
|
23
|
+
const toolService = getToolService(ctx);
|
|
50
24
|
|
|
51
|
-
return
|
|
25
|
+
return createCommand("lint")
|
|
26
|
+
.description("lint the code 🧹")
|
|
27
|
+
.option("-c, --check", "check if the code is valid", true)
|
|
28
|
+
.option("-f, --fix", "try to fix all the code")
|
|
29
|
+
.action(async function lintAction(options: ActionOptions) {
|
|
30
|
+
await toolService.lint(options);
|
|
31
|
+
})
|
|
32
|
+
.addHelpText("afterAll", `\nUnder the hood, this command uses the ${toolService.bin} CLI to lint the code.`);
|
|
52
33
|
}
|
|
@@ -1,41 +1,45 @@
|
|
|
1
|
+
import type { ShellService } from "@vlandoss/clibuddy";
|
|
1
2
|
import { createCommand } from "commander";
|
|
2
3
|
import { BiomeService } from "#/services/biome";
|
|
3
4
|
import type { Context } from "#/services/ctx";
|
|
4
5
|
import { OxfmtService } from "#/services/oxfmt";
|
|
5
6
|
import { OxlintService } from "#/services/oxlint";
|
|
7
|
+
import type { ToolService } from "#/services/tool";
|
|
6
8
|
|
|
7
9
|
type ActionParams = {
|
|
8
10
|
args: string[];
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
function getToolService(bin: string, shell: ShellService): ToolService {
|
|
14
|
+
switch (bin) {
|
|
15
|
+
case "biome":
|
|
16
|
+
return new BiomeService(shell);
|
|
17
|
+
case "oxfmt":
|
|
18
|
+
return new OxfmtService(shell);
|
|
19
|
+
case "oxlint":
|
|
20
|
+
return new OxlintService(shell);
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(`Unknown tool: ${bin}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createToolCommand(bin: string, shell: ShellService) {
|
|
27
|
+
const tool = getToolService(bin, shell);
|
|
28
|
+
|
|
29
|
+
return createCommand(tool.bin)
|
|
14
30
|
.helpCommand(false)
|
|
15
31
|
.helpOption(false)
|
|
16
32
|
.allowExcessArguments(true)
|
|
17
|
-
.allowUnknownOption(true)
|
|
33
|
+
.allowUnknownOption(true)
|
|
34
|
+
.action(async (_: unknown, { args }: ActionParams) => {
|
|
35
|
+
await tool.exec(args);
|
|
36
|
+
});
|
|
18
37
|
}
|
|
19
38
|
|
|
20
39
|
export function createToolsCommand(ctx: Context) {
|
|
21
40
|
return createCommand("tools")
|
|
22
41
|
.description("expose the internal tools 🛠️")
|
|
23
|
-
.addCommand(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
biomeService.execute(args);
|
|
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
|
-
}),
|
|
40
|
-
);
|
|
42
|
+
.addCommand(createToolCommand("biome", ctx.shell))
|
|
43
|
+
.addCommand(createToolCommand("oxfmt", ctx.shell))
|
|
44
|
+
.addCommand(createToolCommand("oxlint", ctx.shell));
|
|
41
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cwd } from "@vlandoss/clibuddy";
|
|
1
|
+
import { cwd, type ShellService } from "@vlandoss/clibuddy";
|
|
2
2
|
import type { AnyLogger } from "@vlandoss/loggy";
|
|
3
3
|
import { createCommand } from "commander";
|
|
4
4
|
import type { Context } from "#/services/ctx";
|
|
@@ -9,86 +9,96 @@ type TypecheckAtOptions = {
|
|
|
9
9
|
dir: string;
|
|
10
10
|
scripts: Record<string, string | undefined> | undefined;
|
|
11
11
|
log: AnyLogger;
|
|
12
|
+
shell: ShellService;
|
|
13
|
+
run: (shell: ShellService) => Promise<void>;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
const typecheckCommand = createCommand("tsc").alias("typecheck").description("check if TypeScript code is well typed 🎨");
|
|
16
|
+
const getPreScript = (scripts: Record<string, string | undefined> | undefined) => scripts?.pretsc ?? scripts?.pretypecheck;
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
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;
|
|
18
|
+
async function typecheckAt({ dir, scripts, log, shell, run }: TypecheckAtOptions) {
|
|
19
|
+
const shellAt = cwd === dir ? shell : shell.at(dir);
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
try {
|
|
22
|
+
const preScript = getPreScript(scripts);
|
|
23
|
+
if (preScript) {
|
|
24
|
+
log.start(`Running pre-script: ${preScript}`);
|
|
25
|
+
await shellAt.$`${preScript}`;
|
|
26
|
+
log.success("Pre-script completed");
|
|
27
|
+
}
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
log.start("Type checking started");
|
|
30
|
+
await run(shellAt);
|
|
31
|
+
log.success("Typecheck completed");
|
|
32
|
+
} catch (error) {
|
|
33
|
+
log.error("Typecheck failed");
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
export function createTypecheckCommand(ctx: Context) {
|
|
39
|
+
const {
|
|
40
|
+
appPkg,
|
|
41
|
+
shell,
|
|
42
|
+
config: { config },
|
|
43
|
+
} = ctx;
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
return createCommand("tsc")
|
|
46
|
+
.alias("typecheck")
|
|
47
|
+
.description("check if TypeScript code is well typed 🎨")
|
|
48
|
+
.addHelpText(
|
|
49
|
+
"afterAll",
|
|
50
|
+
`\nUnder the hood, this command uses the ${config.future?.oxc ? "oxlint" : "TypeScript"} CLI to check the code.`,
|
|
51
|
+
)
|
|
52
|
+
.action(async function typecheckAction() {
|
|
53
|
+
const isTsProject = (dir: string) => appPkg.hasFile("tsconfig.json", dir);
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
55
|
+
const runTypecheck = async (shell: ShellService) => {
|
|
56
|
+
if (config.future?.oxc) {
|
|
57
|
+
const oxlint = new OxlintService(shell);
|
|
58
|
+
await oxlint.exec(`--type-aware --type-check --report-unused-disable-directives`);
|
|
59
|
+
} else {
|
|
60
|
+
await shell.$`tsc --noEmit`;
|
|
52
61
|
}
|
|
62
|
+
};
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
await typecheckAt({
|
|
61
|
-
dir: appPkg.dirPath,
|
|
62
|
-
scripts: appPkg.packageJson.scripts,
|
|
63
|
-
log: logger,
|
|
64
|
-
});
|
|
65
|
-
|
|
64
|
+
if (!appPkg.isMonorepo()) {
|
|
65
|
+
if (!isTsProject(appPkg.dirPath)) {
|
|
66
|
+
logger.info("No tsconfig.json found, skipping typecheck");
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
await typecheckAt({
|
|
71
|
+
shell,
|
|
72
|
+
run: runTypecheck,
|
|
73
|
+
dir: appPkg.dirPath,
|
|
74
|
+
scripts: appPkg.packageJson.scripts,
|
|
75
|
+
log: logger,
|
|
76
|
+
});
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
namespace: "typecheck",
|
|
85
|
-
}),
|
|
86
|
-
}),
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
})
|
|
90
|
-
.addHelpText("afterAll", "\nUnder the hood, this command uses the TypeScript CLI to check the code.");
|
|
91
|
-
}
|
|
81
|
+
const projects = await appPkg.getWorkspaceProjects();
|
|
82
|
+
const tsProjects = projects.filter((project) => isTsProject(project.rootDir));
|
|
83
|
+
|
|
84
|
+
if (!tsProjects.length) {
|
|
85
|
+
logger.warn("No TypeScript projects found in the monorepo, skipping typecheck");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
await Promise.all(
|
|
90
|
+
tsProjects.map((p) =>
|
|
91
|
+
typecheckAt({
|
|
92
|
+
shell,
|
|
93
|
+
run: runTypecheck,
|
|
94
|
+
dir: p.rootDir,
|
|
95
|
+
scripts: p.manifest.scripts,
|
|
96
|
+
log: logger.child({
|
|
97
|
+
tag: p.manifest.name,
|
|
98
|
+
namespace: "typecheck",
|
|
99
|
+
}),
|
|
100
|
+
}),
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
});
|
|
94
104
|
}
|
package/src/services/biome.ts
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import type { FormatOptions, Formatter, Linter, LintOptions } from "#/types/tool";
|
|
2
3
|
import { ToolService } from "./tool";
|
|
3
4
|
|
|
4
|
-
export class BiomeService extends ToolService {
|
|
5
|
+
export class BiomeService extends ToolService implements Formatter, Linter {
|
|
5
6
|
constructor(shellService: ShellService) {
|
|
6
|
-
super({
|
|
7
|
+
super({ bin: "biome", shellService });
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
getBinDir() {
|
|
10
11
|
return require.resolve("@biomejs/biome/bin/biome");
|
|
11
12
|
}
|
|
13
|
+
|
|
14
|
+
async format(options: FormatOptions) {
|
|
15
|
+
const commonOptions = "format --no-errors-on-unmatched --colors=force";
|
|
16
|
+
|
|
17
|
+
if (options.fix) {
|
|
18
|
+
await this.exec(`${commonOptions} --fix`);
|
|
19
|
+
} else if (options.check) {
|
|
20
|
+
await this.exec(`${commonOptions}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async lint(options: LintOptions) {
|
|
25
|
+
const commonOptions = "check --colors=force --formatter-enabled=false";
|
|
26
|
+
|
|
27
|
+
if (options.fix) {
|
|
28
|
+
await this.exec(`${commonOptions} --fix --unsafe`);
|
|
29
|
+
} else if (options.check) {
|
|
30
|
+
await this.exec(`${commonOptions}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
12
33
|
}
|
package/src/services/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { type AsyncSearcher, lilconfig } from "lilconfig";
|
|
3
|
+
import type { ExportedConfig, UserConfig } from "#/types/config";
|
|
3
4
|
import { logger } from "./logger";
|
|
4
5
|
|
|
5
6
|
const DEFAULT_CONFIG: UserConfig = {
|
|
@@ -9,19 +10,46 @@ const DEFAULT_CONFIG: UserConfig = {
|
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export class ConfigService {
|
|
12
|
-
|
|
13
|
-
const debug = logger.subdebug("load-config");
|
|
13
|
+
#searcher: AsyncSearcher;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
constructor() {
|
|
16
|
+
this.#searcher = lilconfig("run-run", {
|
|
17
|
+
searchPlaces: ["run-run.config.ts"],
|
|
18
|
+
cache: true,
|
|
19
|
+
stopDir: os.homedir(),
|
|
20
|
+
loaders: {
|
|
21
|
+
".ts": (filepath: string) => import(filepath).then((mod) => mod.default),
|
|
22
|
+
},
|
|
19
23
|
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async load(): Promise<ExportedConfig> {
|
|
27
|
+
const debug = logger.subdebug("load-config");
|
|
28
|
+
|
|
29
|
+
const searchResult = await this.#searcher.search();
|
|
30
|
+
|
|
31
|
+
if (!searchResult || searchResult?.isEmpty) {
|
|
32
|
+
debug("loaded default config: %O", DEFAULT_CONFIG);
|
|
33
|
+
return {
|
|
34
|
+
config: DEFAULT_CONFIG,
|
|
35
|
+
meta: {
|
|
36
|
+
isDefault: true,
|
|
37
|
+
filepath: undefined,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const config = searchResult.config as UserConfig;
|
|
20
43
|
|
|
21
|
-
debug("loaded config: %O",
|
|
22
|
-
debug("config
|
|
23
|
-
debug("config file: %s", result.configFile);
|
|
44
|
+
debug("loaded config: %O", config);
|
|
45
|
+
debug("config file: %s", searchResult.filepath);
|
|
24
46
|
|
|
25
|
-
return
|
|
47
|
+
return {
|
|
48
|
+
config,
|
|
49
|
+
meta: {
|
|
50
|
+
isDefault: false,
|
|
51
|
+
filepath: searchResult.filepath,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
26
54
|
}
|
|
27
55
|
}
|
package/src/services/ctx.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { createPkgService, createShellService, cwd, type PkgService, type ShellService } from "@vlandoss/clibuddy";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ExportedConfig } from "#/types/config";
|
|
4
4
|
import { ConfigService } from "./config";
|
|
5
5
|
import { logger } from "./logger";
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ export type Context = {
|
|
|
8
8
|
binPkg: PkgService;
|
|
9
9
|
appPkg: PkgService;
|
|
10
10
|
shell: ShellService;
|
|
11
|
-
config:
|
|
11
|
+
config: ExportedConfig;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export async function createContext(binDir: string): Promise<Context> {
|
package/src/services/oxfmt.ts
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import type { FormatOptions, Formatter } from "#/types/tool";
|
|
2
3
|
import { ToolService } from "./tool";
|
|
3
4
|
|
|
4
|
-
export class OxfmtService extends ToolService {
|
|
5
|
+
export class OxfmtService extends ToolService implements Formatter {
|
|
5
6
|
constructor(shellService: ShellService) {
|
|
6
|
-
super({
|
|
7
|
+
super({ bin: "oxfmt", shellService });
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
getBinDir() {
|
|
10
11
|
return require.resolve("oxfmt/bin/oxfmt");
|
|
11
12
|
}
|
|
13
|
+
|
|
14
|
+
async format(options: FormatOptions) {
|
|
15
|
+
const commonOptions = "--no-error-on-unmatched-pattern";
|
|
16
|
+
|
|
17
|
+
if (options.fix) {
|
|
18
|
+
await this.exec(`${commonOptions} --fix`);
|
|
19
|
+
} else if (options.check) {
|
|
20
|
+
await this.exec(`${commonOptions} --check`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
}
|
package/src/services/oxlint.ts
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import type { ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import type { Linter, LintOptions } from "#/types/tool";
|
|
2
3
|
import { ToolService } from "./tool";
|
|
3
4
|
|
|
4
|
-
export class OxlintService extends ToolService {
|
|
5
|
+
export class OxlintService extends ToolService implements Linter {
|
|
5
6
|
constructor(shellService: ShellService) {
|
|
6
|
-
super({
|
|
7
|
+
super({ bin: "oxlint", shellService });
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
getBinDir() {
|
|
10
11
|
return require.resolve("oxlint/bin/oxlint");
|
|
11
12
|
}
|
|
13
|
+
|
|
14
|
+
async lint(options: LintOptions) {
|
|
15
|
+
const commonOptions = "--report-unused-disable-directives";
|
|
16
|
+
|
|
17
|
+
if (options.fix) {
|
|
18
|
+
await this.exec(`${commonOptions} --fix`);
|
|
19
|
+
} else if (options.check) {
|
|
20
|
+
await this.exec(`${commonOptions} --check`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
}
|
package/src/services/tool.ts
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
|
-
import type { ShellService } from "@vlandoss/clibuddy";
|
|
1
|
+
import type { Shell, ShellService } from "@vlandoss/clibuddy";
|
|
2
|
+
import memoize from "memoize";
|
|
2
3
|
import { gracefullBinDir } from "#/utils/gracefullBinDir";
|
|
3
4
|
|
|
4
5
|
type CreateOptions = {
|
|
5
|
-
|
|
6
|
+
bin: string;
|
|
6
7
|
shellService: ShellService;
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
export abstract class ToolService {
|
|
10
11
|
#shellService: ShellService;
|
|
11
|
-
#
|
|
12
|
+
#bin: string;
|
|
12
13
|
|
|
13
|
-
constructor({ cmd, shellService }: CreateOptions) {
|
|
14
|
-
this.#
|
|
14
|
+
constructor({ bin: cmd, shellService }: CreateOptions) {
|
|
15
|
+
this.#bin = cmd;
|
|
15
16
|
this.#shellService = shellService;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
abstract getBinDir(): string;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
exec(args: string | string[]) {
|
|
22
|
+
const $ = this.#shell();
|
|
23
|
+
return this.#run($, args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#shell = memoize((cwd?: string) => {
|
|
21
27
|
return this.#shellService.child({
|
|
28
|
+
cwd,
|
|
22
29
|
preferLocal: gracefullBinDir(() => this.getBinDir()),
|
|
23
30
|
}).$;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
#run(shell: Shell, args: string | string[]) {
|
|
34
|
+
return shell`${this.#bin} ${typeof args === "string" ? args : args.join(" ")}`;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
this
|
|
37
|
+
get bin() {
|
|
38
|
+
return this.#bin;
|
|
28
39
|
}
|
|
29
40
|
}
|
package/src/types/config.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type FormatOptions = {
|
|
2
|
+
check?: boolean;
|
|
3
|
+
fix?: boolean;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type LintOptions = {
|
|
7
|
+
check?: boolean;
|
|
8
|
+
fix?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type Formatter = {
|
|
12
|
+
bin: string;
|
|
13
|
+
format(options: FormatOptions): Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type Linter = {
|
|
17
|
+
bin: string;
|
|
18
|
+
lint(options: LintOptions): Promise<void>;
|
|
19
|
+
};
|
package/tools/oxfmt
CHANGED
|
File without changes
|
package/tools/oxlint
CHANGED
|
File without changes
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { expect, it } from "bun:test";
|
|
2
|
-
import { get } from "../get";
|
|
3
|
-
|
|
4
|
-
it("should get the value when the key is found", () => {
|
|
5
|
-
const obj = { a: 1 };
|
|
6
|
-
expect(get(obj, "a")).toBe(1);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("should get the value when the nested key is found", () => {
|
|
10
|
-
const obj = { a: { b: { c: 1 } } };
|
|
11
|
-
expect(get(obj, "a.b.c")).toBe(1);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should get undefined when the key is not found", () => {
|
|
15
|
-
const obj = { a: 1 };
|
|
16
|
-
expect(get(obj, "b")).toBeUndefined();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("should get undefined when the nested key is not found", () => {
|
|
20
|
-
const obj = { a: { b: { c: 1 } } };
|
|
21
|
-
expect(get(obj, "a.b.d")).toBeUndefined();
|
|
22
|
-
});
|
package/src/utils/get.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export function get(obj: unknown, key: string) {
|
|
2
|
-
if (typeof obj !== "object" || obj === null) {
|
|
3
|
-
return undefined;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
if (typeof key !== "string") {
|
|
7
|
-
throw new Error("Key must be a string");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const parts = key.split(".");
|
|
11
|
-
|
|
12
|
-
let value = {
|
|
13
|
-
...obj,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
for (let i = 0; i < parts.length; i++) {
|
|
17
|
-
const key = parts[i] as string;
|
|
18
|
-
|
|
19
|
-
// @ts-expect-error value is type to {}
|
|
20
|
-
const subValue = value[key];
|
|
21
|
-
|
|
22
|
-
if (!subValue) {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
value = subValue;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return value;
|
|
30
|
-
}
|