@vlandoss/run-run 0.0.1
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/README.md +46 -0
- package/bin.ts +6 -0
- package/package.json +46 -0
- package/src/main.ts +12 -0
- package/src/program/__tests__/__snapshots__/snapshots.test.ts.snap +154 -0
- package/src/program/__tests__/snapshots.test.ts +48 -0
- package/src/program/commands/clean.ts +46 -0
- package/src/program/commands/format.ts +30 -0
- package/src/program/commands/info-pkg.ts +36 -0
- package/src/program/commands/lint.ts +29 -0
- package/src/program/commands/test-static.ts +33 -0
- package/src/program/commands/typecheck.ts +25 -0
- package/src/program/index.ts +32 -0
- package/src/program/ui.ts +14 -0
- package/src/services/ctx.ts +41 -0
- package/src/services/logger.ts +5 -0
- package/src/utils/__tests__/get.test.ts +22 -0
- package/src/utils/get.ts +30 -0
- package/tsconfig.json +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 🦊 run-run
|
|
2
|
+
|
|
3
|
+
CLI toolbox to fullstack common scripts in Variable Land 👊
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Bun >= 1.2.4
|
|
8
|
+
|
|
9
|
+
## Toolbox
|
|
10
|
+
|
|
11
|
+
- [Biome](https://biomejs.dev)
|
|
12
|
+
- [TSC](https://www.typescriptlang.org)
|
|
13
|
+
- [rimraf](https://www.npmjs.com/package/rimraf)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add @vlandoss/run-run
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It will adds the `rr` and `run-run` command to your project
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> The documentation is WIP
|
|
27
|
+
|
|
28
|
+
Run the help command:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
rr help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Troubleshooting
|
|
35
|
+
|
|
36
|
+
To enable debug mode, set the `DEBUG` environment variable to `run-run:*` before running *any* command.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
DEBUG=run-run:* rr help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Additionally, there is an special command to display `package.json` information:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
rr info:pkg --help
|
|
46
|
+
```
|
package/bin.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vlandoss/run-run",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "The CLI toolbox to fullstack common scripts in Variable Land",
|
|
5
|
+
"homepage": "https://github.com/variableland/dx/tree/main/packages/run-run#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/variableland/dx/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/variableland/dx.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"module": "src/main.ts",
|
|
17
|
+
"bin": {
|
|
18
|
+
"rr": "./bin.ts",
|
|
19
|
+
"run-run": "./bin.ts"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin",
|
|
23
|
+
"src",
|
|
24
|
+
"plopfiles",
|
|
25
|
+
"tsconfig.json"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@biomejs/biome": "1.9.4",
|
|
29
|
+
"commander": "13.1.0",
|
|
30
|
+
"is-ci": "4.1.0",
|
|
31
|
+
"rimraf": "6.0.1",
|
|
32
|
+
"typescript": "5.8.2",
|
|
33
|
+
"@vlandoss/clibuddy": "0.0.1",
|
|
34
|
+
"@vlandoss/loggy": "0.0.1"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"bun": ">=1.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"test": "bun test",
|
|
44
|
+
"typecheck": "rr tsc"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Options, createProgram } from "./program";
|
|
2
|
+
import { logger } from "./services/logger";
|
|
3
|
+
|
|
4
|
+
export async function main(options: Options) {
|
|
5
|
+
try {
|
|
6
|
+
const { cmd } = await createProgram(options);
|
|
7
|
+
await cmd.parseAsync();
|
|
8
|
+
} catch (error) {
|
|
9
|
+
logger.error("Cannot run main successfully", error);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Bun Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`should match command: "help" 1`] = `
|
|
4
|
+
"🦊 R U N - R U N: The CLI toolbox to fullstack common scripts in Variable Land 👊
|
|
5
|
+
|
|
6
|
+
Usage: rr|run-run [options] [command]
|
|
7
|
+
|
|
8
|
+
Options:
|
|
9
|
+
-v, --version output the version number
|
|
10
|
+
-h, --help display help for command
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
format|fmt [options] format the code 🎨
|
|
14
|
+
lint [options] lint the code 🧹
|
|
15
|
+
test:static [options] check format and lint issues ✅
|
|
16
|
+
clean [options] delete dirty folders or files such as node_modules, etc
|
|
17
|
+
🗑️
|
|
18
|
+
typecheck|tsc check if TypeScript code is well typed 🎨
|
|
19
|
+
info:pkg [options] display run-run package.json ℹ️
|
|
20
|
+
help [command] display help for command
|
|
21
|
+
|
|
22
|
+
Acknowledgment:
|
|
23
|
+
- kcd-scripts: for main inspiration
|
|
24
|
+
https://github.com/kentcdodds/kcd-scripts
|
|
25
|
+
|
|
26
|
+
- peruvian news: in honor to Run Run
|
|
27
|
+
https://es.wikipedia.org/wiki/Run_Run
|
|
28
|
+
"
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
exports[`should match command: "--help" 1`] = `
|
|
32
|
+
"🦊 R U N - R U N: The CLI toolbox to fullstack common scripts in Variable Land 👊
|
|
33
|
+
|
|
34
|
+
Usage: rr|run-run [options] [command]
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
-v, --version output the version number
|
|
38
|
+
-h, --help display help for command
|
|
39
|
+
|
|
40
|
+
Commands:
|
|
41
|
+
format|fmt [options] format the code 🎨
|
|
42
|
+
lint [options] lint the code 🧹
|
|
43
|
+
test:static [options] check format and lint issues ✅
|
|
44
|
+
clean [options] delete dirty folders or files such as node_modules, etc
|
|
45
|
+
🗑️
|
|
46
|
+
typecheck|tsc check if TypeScript code is well typed 🎨
|
|
47
|
+
info:pkg [options] display run-run package.json ℹ️
|
|
48
|
+
help [command] display help for command
|
|
49
|
+
|
|
50
|
+
Acknowledgment:
|
|
51
|
+
- kcd-scripts: for main inspiration
|
|
52
|
+
https://github.com/kentcdodds/kcd-scripts
|
|
53
|
+
|
|
54
|
+
- peruvian news: in honor to Run Run
|
|
55
|
+
https://es.wikipedia.org/wiki/Run_Run
|
|
56
|
+
"
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
exports[`should match command: "--version" 1`] = `
|
|
60
|
+
"0.0.0-test
|
|
61
|
+
"
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
exports[`should match command: "-v" 1`] = `
|
|
65
|
+
"0.0.0-test
|
|
66
|
+
"
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
exports[`should match help message for command "format" 1`] = `
|
|
70
|
+
"Usage: rr format|fmt [options]
|
|
71
|
+
|
|
72
|
+
format the code 🎨
|
|
73
|
+
|
|
74
|
+
Options:
|
|
75
|
+
-c, --check check if the code is formatted (default: true)
|
|
76
|
+
-f, --fix format all the code
|
|
77
|
+
-h, --help display help for command
|
|
78
|
+
|
|
79
|
+
Under the hood, this command uses the biome CLI to format the code.
|
|
80
|
+
"
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
exports[`should match help message for command "lint" 1`] = `
|
|
84
|
+
"Usage: rr lint [options]
|
|
85
|
+
|
|
86
|
+
lint the code 🧹
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
-c, --check check if the code is valid (default: true)
|
|
90
|
+
-f, --fix try to fix all the code
|
|
91
|
+
-h, --help display help for command
|
|
92
|
+
|
|
93
|
+
Under the hood, this command uses the biome CLI to lint the code.
|
|
94
|
+
"
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
exports[`should match help message for command "test:static" 1`] = `
|
|
98
|
+
"Usage: rr test:static [options]
|
|
99
|
+
|
|
100
|
+
check format and lint issues ✅
|
|
101
|
+
|
|
102
|
+
Options:
|
|
103
|
+
-f, --fix try to fix issues automatically
|
|
104
|
+
--fix-staged try to fix staged files only
|
|
105
|
+
-h, --help display help for command
|
|
106
|
+
|
|
107
|
+
Under the hood, this command uses the biome CLI to check the code.
|
|
108
|
+
"
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
exports[`should match help message for command "clean" 1`] = `
|
|
112
|
+
"Usage: rr clean [options]
|
|
113
|
+
|
|
114
|
+
delete dirty folders or files such as node_modules, etc 🗑️
|
|
115
|
+
|
|
116
|
+
Options:
|
|
117
|
+
--only-dist delete 'dist' folders only
|
|
118
|
+
-h, --help display help for command
|
|
119
|
+
|
|
120
|
+
Under the hood, this command uses the rimraf.js to delete dirty folders or files.
|
|
121
|
+
"
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
exports[`should match help message for command "typecheck" 1`] = `
|
|
125
|
+
"Usage: rr typecheck|tsc [options]
|
|
126
|
+
|
|
127
|
+
check if TypeScript code is well typed 🎨
|
|
128
|
+
|
|
129
|
+
Options:
|
|
130
|
+
-h, --help display help for command
|
|
131
|
+
|
|
132
|
+
Under the hood, this command uses the TSC CLI to check the code.
|
|
133
|
+
"
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
exports[`should match help message for command "info:pkg" 1`] = `
|
|
137
|
+
"Usage: rr info:pkg [options]
|
|
138
|
+
|
|
139
|
+
display run-run package.json ℹ️
|
|
140
|
+
|
|
141
|
+
Options:
|
|
142
|
+
-f, --filter <filter> lodash get id like to filter info by
|
|
143
|
+
-c, --current display package.json where run-run will be executed
|
|
144
|
+
-h, --help display help for command
|
|
145
|
+
"
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
exports[`should match "format" command 1`] = `"biome format --no-errors-on-unmatched --colors=force"`;
|
|
149
|
+
|
|
150
|
+
exports[`should match "lint" command 1`] = `"biome check --colors=force --formatter-enabled=false"`;
|
|
151
|
+
|
|
152
|
+
exports[`should match "test:static" command 1`] = `"biome check --colors=force"`;
|
|
153
|
+
|
|
154
|
+
exports[`should match "typecheck" command 1`] = `"tsc --noEmit"`;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { afterEach, expect, test } from "bun:test";
|
|
2
|
+
import { createTestProgram, execCli, mocked, parseProgram } from "test/helpers";
|
|
3
|
+
|
|
4
|
+
const { cmd, ctx } = await createTestProgram();
|
|
5
|
+
const $ = ctx.shell.$;
|
|
6
|
+
|
|
7
|
+
const rootCommands = ["help", "--help", "--version", "-v"];
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
mocked($).mockClear();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
for (const cmd of rootCommands) {
|
|
14
|
+
test(`should match command: "${cmd}"`, async () => {
|
|
15
|
+
const { stdout } = await execCli(cmd);
|
|
16
|
+
|
|
17
|
+
expect(stdout).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const command of cmd.commands) {
|
|
22
|
+
const cmd = command.name();
|
|
23
|
+
|
|
24
|
+
test(`should match help message for command "${cmd}"`, async () => {
|
|
25
|
+
const { stdout } = await execCli(`${cmd} --help`);
|
|
26
|
+
|
|
27
|
+
expect(stdout).toMatchSnapshot();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// these command don't use shell ($) instance
|
|
32
|
+
const hardCommands = ["info:pkg", "clean"];
|
|
33
|
+
|
|
34
|
+
const easyTesteableCommands = cmd.commands.filter((command) => {
|
|
35
|
+
const isHard = hardCommands.some((cmd) => command.name() === cmd);
|
|
36
|
+
return !isHard;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
for (const command of easyTesteableCommands) {
|
|
40
|
+
const cmd = command.name();
|
|
41
|
+
|
|
42
|
+
test(`should match "${cmd}" command`, async () => {
|
|
43
|
+
await parseProgram([cmd]);
|
|
44
|
+
|
|
45
|
+
expect($).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(mocked($).mock.results[0]?.value).toMatchSnapshot();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { cwd } from "@vlandoss/clibuddy";
|
|
2
|
+
import { createCommand } from "commander";
|
|
3
|
+
import { rimraf } from "rimraf";
|
|
4
|
+
import { logger } from "~/services/logger";
|
|
5
|
+
|
|
6
|
+
export function createCleanCommand() {
|
|
7
|
+
return createCommand("clean")
|
|
8
|
+
.description("delete dirty folders or files such as node_modules, etc 🗑️")
|
|
9
|
+
.option("--only-dist", "delete 'dist' folders only")
|
|
10
|
+
.action(async function cleanCommandAction(options) {
|
|
11
|
+
try {
|
|
12
|
+
if (options.onlyDist) {
|
|
13
|
+
logger.info("Cleaning only 'dist' folders... ⌛");
|
|
14
|
+
|
|
15
|
+
await rimraf("**/dist", {
|
|
16
|
+
glob: {
|
|
17
|
+
cwd,
|
|
18
|
+
ignore: ["**/node_modules/**"],
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
logger.info("Done ✅");
|
|
23
|
+
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
logger.info("Cleaning all... ⌛");
|
|
28
|
+
|
|
29
|
+
const dirtyPaths = ["**/.turbo", "**/dist", "**/node_modules", "pnpm-lock.yaml", "bun.lock"];
|
|
30
|
+
|
|
31
|
+
logger.info(dirtyPaths.join("\n"));
|
|
32
|
+
|
|
33
|
+
await rimraf(dirtyPaths, {
|
|
34
|
+
glob: {
|
|
35
|
+
cwd,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
logger.info("Done ✅");
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error(error);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses the rimraf.js to delete dirty folders or files.");
|
|
46
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import type { Context } from "~/services/ctx";
|
|
3
|
+
import { logger } from "~/services/logger";
|
|
4
|
+
|
|
5
|
+
export function createFormatCommand(ctx: Context) {
|
|
6
|
+
return createCommand("format")
|
|
7
|
+
.alias("fmt")
|
|
8
|
+
.description("format the code 🎨")
|
|
9
|
+
.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 $ = ctx.shell.$;
|
|
13
|
+
const toolCmd = "biome format --no-errors-on-unmatched --colors=force";
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
if (options.fix) {
|
|
17
|
+
await $`${toolCmd} --fix`;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (options.check) {
|
|
22
|
+
await $`${toolCmd}`;
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
logger.error(error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to format the code.");
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import type { Context } from "~/services/ctx";
|
|
3
|
+
import { logger } from "~/services/logger";
|
|
4
|
+
import { get } from "~/utils/get";
|
|
5
|
+
|
|
6
|
+
export function createInfoPkgCommand(ctx: Context) {
|
|
7
|
+
return createCommand("info:pkg")
|
|
8
|
+
.description("display run-run package.json ℹ️")
|
|
9
|
+
.option("-f, --filter <filter>", "lodash get id like to filter info by")
|
|
10
|
+
.option("-c, --current", "display package.json where run-run will be executed")
|
|
11
|
+
.action(async function pkgAction(options) {
|
|
12
|
+
try {
|
|
13
|
+
const { appPkg, binPkg } = ctx;
|
|
14
|
+
|
|
15
|
+
const infoObject = options.current ? appPkg.info() : binPkg.info();
|
|
16
|
+
|
|
17
|
+
if (!options.filter) {
|
|
18
|
+
logger.info("%O", infoObject);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { filter } = options;
|
|
23
|
+
const subInfoObject = get(infoObject.packageJson, filter);
|
|
24
|
+
|
|
25
|
+
if (!subInfoObject) {
|
|
26
|
+
logger.info("No info found");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
logger.info("%O", { [filter]: subInfoObject });
|
|
31
|
+
} catch (error) {
|
|
32
|
+
logger.error(error);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import type { Context } from "~/services/ctx";
|
|
3
|
+
import { logger } from "~/services/logger";
|
|
4
|
+
|
|
5
|
+
export function createLintCommand(ctx: Context) {
|
|
6
|
+
return createCommand("lint")
|
|
7
|
+
.description("lint the code 🧹")
|
|
8
|
+
.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 $ = ctx.shell.$;
|
|
12
|
+
const toolCmd = "biome check --colors=force --formatter-enabled=false";
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (options.fix) {
|
|
16
|
+
await $`${toolCmd} --fix --unsafe`;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (options.check) {
|
|
21
|
+
await $`${toolCmd}`;
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error(error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to lint the code.");
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import isCI from "is-ci";
|
|
3
|
+
import type { Context } from "~/services/ctx";
|
|
4
|
+
import { logger } from "~/services/logger";
|
|
5
|
+
|
|
6
|
+
export function createTestStaticCommand(ctx: Context) {
|
|
7
|
+
return createCommand("test:static")
|
|
8
|
+
.description("check format and lint issues ✅")
|
|
9
|
+
.option("-f, --fix", "try to fix issues automatically")
|
|
10
|
+
.option("--fix-staged", "try to fix staged files only")
|
|
11
|
+
.action(async function testStaticAction(options) {
|
|
12
|
+
const $ = ctx.shell.$;
|
|
13
|
+
const toolCmd = (cmd = "check") => `biome ${cmd} --colors=force`;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
if (options.fix) {
|
|
17
|
+
await $`${toolCmd()} --fix --unsafe`;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (options.fixStaged) {
|
|
22
|
+
await $`${toolCmd()} --no-errors-on-unmatched --fix --unsafe --staged`;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await $`${toolCmd(isCI ? "ci" : "check")}`;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logger.error(error);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses the biome CLI to check the code.");
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createCommand } from "commander";
|
|
2
|
+
import type { Context } from "~/services/ctx";
|
|
3
|
+
import { logger } from "~/services/logger";
|
|
4
|
+
|
|
5
|
+
export function createTypecheckCommand(ctx: Context) {
|
|
6
|
+
return createCommand("typecheck")
|
|
7
|
+
.alias("tsc")
|
|
8
|
+
.description("check if TypeScript code is well typed 🎨")
|
|
9
|
+
.action(async function typecheckAction() {
|
|
10
|
+
const { appPkg } = ctx;
|
|
11
|
+
const $ = ctx.shell.$;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
if (appPkg?.hasFile("tsconfig.json")) {
|
|
15
|
+
await $`tsc --noEmit`;
|
|
16
|
+
} else {
|
|
17
|
+
logger.info("No tsconfig.json found. Skipping type checking.");
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error(error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
.addHelpText("afterAll", "\nUnder the hood, this command uses the TSC CLI to check the code.");
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getVersion } from "@vlandoss/clibuddy";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createContext } from "~/services/ctx";
|
|
4
|
+
import { createCleanCommand } from "./commands/clean";
|
|
5
|
+
import { createFormatCommand } from "./commands/format";
|
|
6
|
+
import { createInfoPkgCommand } from "./commands/info-pkg";
|
|
7
|
+
import { createLintCommand } from "./commands/lint";
|
|
8
|
+
import { createTestStaticCommand } from "./commands/test-static";
|
|
9
|
+
import { createTypecheckCommand } from "./commands/typecheck";
|
|
10
|
+
import { BANNER_TEXT, CREDITS_TEXT } from "./ui";
|
|
11
|
+
|
|
12
|
+
export type Options = {
|
|
13
|
+
binDir: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function createProgram(options: Options) {
|
|
17
|
+
const ctx = await createContext(options.binDir);
|
|
18
|
+
|
|
19
|
+
const cmd = new Command("rr")
|
|
20
|
+
.alias("run-run")
|
|
21
|
+
.version(getVersion(ctx.binPkg), "-v, --version")
|
|
22
|
+
.addHelpText("before", BANNER_TEXT)
|
|
23
|
+
.addHelpText("after", CREDITS_TEXT)
|
|
24
|
+
.addCommand(createFormatCommand(ctx))
|
|
25
|
+
.addCommand(createLintCommand(ctx))
|
|
26
|
+
.addCommand(createTestStaticCommand(ctx))
|
|
27
|
+
.addCommand(createCleanCommand())
|
|
28
|
+
.addCommand(createTypecheckCommand(ctx))
|
|
29
|
+
.addCommand(createInfoPkgCommand(ctx));
|
|
30
|
+
|
|
31
|
+
return { cmd, ctx };
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { colors } from "@vlandoss/clibuddy";
|
|
2
|
+
|
|
3
|
+
const UI_LOGO = `🦊 ${colors.blueBright("R")} ${colors.redBright("U")} ${colors.greenBright("N")} - ${colors.blueBright("R")} ${colors.redBright("U")} ${colors.greenBright("N")}`;
|
|
4
|
+
|
|
5
|
+
const COMPANY_LOGO = `${colors.redBright("Variable Land")} 👊`;
|
|
6
|
+
|
|
7
|
+
export const BANNER_TEXT = `${UI_LOGO}: The CLI toolbox to fullstack common scripts in ${COMPANY_LOGO}\n`;
|
|
8
|
+
|
|
9
|
+
export const CREDITS_TEXT = `\nAcknowledgment:
|
|
10
|
+
- kcd-scripts: for main inspiration
|
|
11
|
+
https://github.com/kentcdodds/kcd-scripts
|
|
12
|
+
|
|
13
|
+
- peruvian news: in honor to Run Run
|
|
14
|
+
https://es.wikipedia.org/wiki/Run_Run`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { type PkgService, type ShellService, createPkgService, createShellService, cwd } from "@vlandoss/clibuddy";
|
|
3
|
+
import { logger } from "./logger";
|
|
4
|
+
|
|
5
|
+
export type Context = {
|
|
6
|
+
binPkg: PkgService;
|
|
7
|
+
appPkg: PkgService;
|
|
8
|
+
shell: ShellService;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function createContext(binDir: string): Promise<Context> {
|
|
12
|
+
const debug = logger.subdebug("create-context-value");
|
|
13
|
+
|
|
14
|
+
const binPath = fs.realpathSync(binDir);
|
|
15
|
+
|
|
16
|
+
debug("bin path:", binPath);
|
|
17
|
+
debug("process cwd:", process.cwd());
|
|
18
|
+
|
|
19
|
+
const [appPkg, binPkg] = await Promise.all([createPkgService(cwd), createPkgService(binPath)]);
|
|
20
|
+
|
|
21
|
+
if (!binPkg) {
|
|
22
|
+
throw new Error("Could not find bin package.json");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!appPkg) {
|
|
26
|
+
throw new Error("Could not find app package.json");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
debug("app pkg info: %O", appPkg.info());
|
|
30
|
+
debug("bin pkg info: %O", binPkg.info());
|
|
31
|
+
|
|
32
|
+
const shell = createShellService({
|
|
33
|
+
localBaseBinPath: [binDir],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
appPkg,
|
|
38
|
+
binPkg,
|
|
39
|
+
shell,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
}
|