@rrlab/biome-plugin 0.0.1-git-06ed46c.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/README.md +40 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.mjs +190 -0
- package/package.json +52 -0
- package/src/index.ts +192 -0
- package/src/tool-versions.ts +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @rrlab/biome-plugin
|
|
2
|
+
|
|
3
|
+
Biome plugin for [`@rrlab/cli`](https://npmjs.com/package/@rrlab/cli). Provides `lint`, `format`, and `jsc` capabilities backed by [Biome](https://biomejs.dev).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
rr plugins add biome
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`rr plugins add` installs `@rrlab/biome-plugin`, adds `@biomejs/biome` as a `devDependency`, and (with your confirmation) scaffolds a `biome.json` extending [`@rrlab/biome-config`](https://npmjs.com/package/@rrlab/biome-config). When `biome.json` already exists you can choose to patch it, leave it alone, or overwrite it.
|
|
12
|
+
|
|
13
|
+
## What it provides
|
|
14
|
+
|
|
15
|
+
| Capability | Surface |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `lint` | `rr lint`, `rr lint doctor` |
|
|
18
|
+
| `format` | `rr format`, `rr format doctor` |
|
|
19
|
+
| `jsc` | `rr jsc` (lint + format together), `rr jsc doctor` |
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
The scaffolded `biome.json` is a thin wrapper:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"$schema": "https://biomejs.dev/schemas/2.4.4/schema.json",
|
|
28
|
+
"extends": ["@rrlab/biome-config"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Override any biome setting by adding it to your local `biome.json`. The preset lives in [`@rrlab/biome-config`](https://npmjs.com/package/@rrlab/biome-config) — bump the package to get updated defaults.
|
|
33
|
+
|
|
34
|
+
## Removal
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
rr plugins remove biome
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Removes `@biomejs/biome` + `@rrlab/biome-config` from `package.json`, edits or deletes `biome.json` (depending on whether you have other settings in it), and drops the `biome()` entry from `run-run.config.{ts,mts}`.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FormatOptions, Formatter, InstallContext, InstallResult, LintOptions, Linter, StaticChecker, StaticCheckerOptions, ToolService, UninstallContext, UninstallResult } from "@rrlab/cli/plugin";
|
|
2
|
+
import { ShellService } from "@vlandoss/clibuddy";
|
|
3
|
+
|
|
4
|
+
//#region src/tool-versions.d.ts
|
|
5
|
+
declare const TOOL_VERSIONS: {
|
|
6
|
+
readonly "@biomejs/biome": {
|
|
7
|
+
readonly install: "^2.0.0";
|
|
8
|
+
readonly peer: ">=2.0.0";
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/index.d.ts
|
|
13
|
+
declare class BiomeService extends ToolService implements Formatter, Linter, StaticChecker {
|
|
14
|
+
constructor(shellService: ShellService);
|
|
15
|
+
format(options: FormatOptions): Promise<void>;
|
|
16
|
+
lint(options: LintOptions): Promise<void>;
|
|
17
|
+
check(options: StaticCheckerOptions): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
declare function install(ctx: InstallContext): Promise<InstallResult>;
|
|
20
|
+
declare function uninstall(ctx: UninstallContext): Promise<UninstallResult>;
|
|
21
|
+
declare const biome: (options: void) => import("@rrlab/cli/plugin").Plugin;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { BiomeService, TOOL_VERSIONS, biome as default, install, uninstall };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ToolService, definePlugin } from "@rrlab/cli/plugin";
|
|
4
|
+
import { colorize, isCI } from "@vlandoss/clibuddy";
|
|
5
|
+
import { parse } from "comment-json";
|
|
6
|
+
//#region src/tool-versions.ts
|
|
7
|
+
const TOOL_VERSIONS = { "@biomejs/biome": {
|
|
8
|
+
install: "^2.0.0",
|
|
9
|
+
peer: ">=2.0.0"
|
|
10
|
+
} };
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/index.ts
|
|
13
|
+
const FROM = import.meta.url;
|
|
14
|
+
const UI = colorize("#61A5FA")("biome");
|
|
15
|
+
const COMMON_FLAGS = ["--colors=force", "--no-errors-on-unmatched"];
|
|
16
|
+
const BIOME_JSON = "biome.json";
|
|
17
|
+
const BIOME_CONFIG_PKG = "@rrlab/biome-config";
|
|
18
|
+
const BIOME_SCHEMA = "https://biomejs.dev/schemas/2.4.4/schema.json";
|
|
19
|
+
var BiomeService = class extends ToolService {
|
|
20
|
+
constructor(shellService) {
|
|
21
|
+
super({
|
|
22
|
+
pkg: "@biomejs/biome",
|
|
23
|
+
bin: "biome",
|
|
24
|
+
ui: UI,
|
|
25
|
+
shellService,
|
|
26
|
+
from: FROM
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async format(options) {
|
|
30
|
+
const args = ["format", ...COMMON_FLAGS];
|
|
31
|
+
if (options.fix) args.push("--fix");
|
|
32
|
+
await this.exec(args);
|
|
33
|
+
}
|
|
34
|
+
async lint(options) {
|
|
35
|
+
const args = [
|
|
36
|
+
"check",
|
|
37
|
+
...COMMON_FLAGS,
|
|
38
|
+
"--formatter-enabled=false"
|
|
39
|
+
];
|
|
40
|
+
if (options.fix) args.push("--fix", "--unsafe");
|
|
41
|
+
await this.exec(args);
|
|
42
|
+
}
|
|
43
|
+
async check(options) {
|
|
44
|
+
if (options.fix) await this.exec([
|
|
45
|
+
"check",
|
|
46
|
+
...COMMON_FLAGS,
|
|
47
|
+
"--fix"
|
|
48
|
+
]);
|
|
49
|
+
else if (options.fixStaged) await this.exec([
|
|
50
|
+
"check",
|
|
51
|
+
...COMMON_FLAGS,
|
|
52
|
+
"--fix",
|
|
53
|
+
"--staged"
|
|
54
|
+
]);
|
|
55
|
+
else await this.exec([isCI ? "ci" : "check", ...COMMON_FLAGS]);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
async function install(ctx) {
|
|
59
|
+
const scaffoldDecision = await decideScaffoldAction(ctx, await pathExists(path.join(ctx.appPkg.dirPath, BIOME_JSON)));
|
|
60
|
+
if (scaffoldDecision === "skip") return { devDependencies: { "@biomejs/biome": TOOL_VERSIONS["@biomejs/biome"].install } };
|
|
61
|
+
return {
|
|
62
|
+
devDependencies: {
|
|
63
|
+
"@biomejs/biome": TOOL_VERSIONS["@biomejs/biome"].install,
|
|
64
|
+
[BIOME_CONFIG_PKG]: "^0.1.0"
|
|
65
|
+
},
|
|
66
|
+
files: [scaffoldDecision === "create" || scaffoldDecision === "overwrite" ? {
|
|
67
|
+
kind: "create",
|
|
68
|
+
path: BIOME_JSON,
|
|
69
|
+
content: `${JSON.stringify({
|
|
70
|
+
$schema: BIOME_SCHEMA,
|
|
71
|
+
extends: [BIOME_CONFIG_PKG]
|
|
72
|
+
}, null, 2)}\n`,
|
|
73
|
+
overwrite: scaffoldDecision === "overwrite" || ctx.flags.force
|
|
74
|
+
} : {
|
|
75
|
+
kind: "edit-json",
|
|
76
|
+
path: BIOME_JSON,
|
|
77
|
+
edits: [{
|
|
78
|
+
op: "set",
|
|
79
|
+
path: "/$schema",
|
|
80
|
+
value: BIOME_SCHEMA,
|
|
81
|
+
mode: "if-missing"
|
|
82
|
+
}, {
|
|
83
|
+
op: "include",
|
|
84
|
+
path: "/extends",
|
|
85
|
+
value: BIOME_CONFIG_PKG,
|
|
86
|
+
position: "start"
|
|
87
|
+
}]
|
|
88
|
+
}]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function uninstall(ctx) {
|
|
92
|
+
const biomeJsonPath = path.join(ctx.appPkg.dirPath, BIOME_JSON);
|
|
93
|
+
const removeDependencies = ["@biomejs/biome", BIOME_CONFIG_PKG];
|
|
94
|
+
if (!await pathExists(biomeJsonPath)) return { removeDependencies };
|
|
95
|
+
let existing;
|
|
96
|
+
try {
|
|
97
|
+
existing = parse(await fs.readFile(biomeJsonPath, "utf8"));
|
|
98
|
+
} catch {}
|
|
99
|
+
const files = [];
|
|
100
|
+
if (existing) {
|
|
101
|
+
const otherExtends = (Array.isArray(existing.extends) ? existing.extends : []).filter((e) => e !== BIOME_CONFIG_PKG);
|
|
102
|
+
const { $schema: _schema, extends: _extends, ...rest } = existing;
|
|
103
|
+
const semanticKeys = Object.keys(rest);
|
|
104
|
+
if (otherExtends.length === 0 && semanticKeys.length === 0) files.push({
|
|
105
|
+
kind: "delete",
|
|
106
|
+
path: BIOME_JSON
|
|
107
|
+
});
|
|
108
|
+
else {
|
|
109
|
+
const edits = [{
|
|
110
|
+
op: "exclude",
|
|
111
|
+
path: "/extends",
|
|
112
|
+
value: BIOME_CONFIG_PKG
|
|
113
|
+
}];
|
|
114
|
+
if (otherExtends.length === 0) edits.push({
|
|
115
|
+
op: "unset",
|
|
116
|
+
path: "/extends"
|
|
117
|
+
});
|
|
118
|
+
files.push({
|
|
119
|
+
kind: "edit-json",
|
|
120
|
+
path: BIOME_JSON,
|
|
121
|
+
edits
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
removeDependencies,
|
|
127
|
+
files
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async function decideScaffoldAction(ctx, fileExists) {
|
|
131
|
+
if (!fileExists) {
|
|
132
|
+
if (ctx.flags.yes || ctx.flags.nonInteractive) return "create";
|
|
133
|
+
const choice = await ctx.prompts.confirm({
|
|
134
|
+
message: `Scaffold ${BIOME_JSON} with the @rrlab/biome-config preset?`,
|
|
135
|
+
initialValue: true
|
|
136
|
+
});
|
|
137
|
+
if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
|
|
138
|
+
return choice ? "create" : "skip";
|
|
139
|
+
}
|
|
140
|
+
if (ctx.flags.yes || ctx.flags.nonInteractive) return "patch";
|
|
141
|
+
const choice = await ctx.prompts.select({
|
|
142
|
+
message: `${BIOME_JSON} already exists. What do you want to do?`,
|
|
143
|
+
options: [
|
|
144
|
+
{
|
|
145
|
+
value: "patch",
|
|
146
|
+
label: "Patch — add @rrlab/biome-config to extends, keep my other settings"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
value: "skip",
|
|
150
|
+
label: "Skip — leave it alone"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
value: "overwrite",
|
|
154
|
+
label: "Overwrite — replace with a fresh scaffold"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
initialValue: "patch"
|
|
158
|
+
});
|
|
159
|
+
if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
|
|
160
|
+
return choice;
|
|
161
|
+
}
|
|
162
|
+
async function pathExists(p) {
|
|
163
|
+
try {
|
|
164
|
+
await fs.access(p);
|
|
165
|
+
return true;
|
|
166
|
+
} catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const biome = definePlugin(() => ({
|
|
171
|
+
name: "biome",
|
|
172
|
+
apiVersion: 1,
|
|
173
|
+
install,
|
|
174
|
+
uninstall,
|
|
175
|
+
async setup({ shell }) {
|
|
176
|
+
const svc = new BiomeService(shell);
|
|
177
|
+
try {
|
|
178
|
+
await svc.getBinDir();
|
|
179
|
+
} catch (_err) {
|
|
180
|
+
throw new Error("@rrlab/biome-plugin requires @biomejs/biome to be installed in the host project. Run: rr plugins add biome (or: pnpm add -D @biomejs/biome)");
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
lint: svc,
|
|
184
|
+
format: svc,
|
|
185
|
+
jsc: svc
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}));
|
|
189
|
+
//#endregion
|
|
190
|
+
export { BiomeService, TOOL_VERSIONS, biome as default, install, uninstall };
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rrlab/biome-plugin",
|
|
3
|
+
"version": "0.0.1-git-06ed46c.0",
|
|
4
|
+
"description": "Biome plugin for @rrlab/cli — provides lint, format, and jsc capabilities.",
|
|
5
|
+
"homepage": "https://github.com/variableland/dx/tree/main/run-run/biome-plugin#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
|
+
"directory": "run-run/biome-plugin"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "rcrd <rcrd@variable.land>",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"!src/**/__tests__",
|
|
27
|
+
"!src/**/*.test.*"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"comment-json": "4.2.5",
|
|
37
|
+
"@vlandoss/clibuddy": "0.6.1"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@biomejs/biome": ">=2.0.0",
|
|
41
|
+
"@rrlab/cli": "0.0.2-git-06ed46c.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@biomejs/biome": "2.4.4",
|
|
45
|
+
"@rrlab/cli": "0.0.2-git-06ed46c.0",
|
|
46
|
+
"@rrlab/tsdown-config": "^0.0.1-git-06ed46c.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"test": "vitest run"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
definePlugin,
|
|
5
|
+
type FileOp,
|
|
6
|
+
type FormatOptions,
|
|
7
|
+
type Formatter,
|
|
8
|
+
type InstallContext,
|
|
9
|
+
type InstallResult,
|
|
10
|
+
type Linter,
|
|
11
|
+
type LintOptions,
|
|
12
|
+
type StaticChecker,
|
|
13
|
+
type StaticCheckerOptions,
|
|
14
|
+
ToolService,
|
|
15
|
+
type UninstallContext,
|
|
16
|
+
type UninstallResult,
|
|
17
|
+
} from "@rrlab/cli/plugin";
|
|
18
|
+
import { colorize, isCI, type ShellService } from "@vlandoss/clibuddy";
|
|
19
|
+
import { parse as parseJsonc } from "comment-json";
|
|
20
|
+
import { TOOL_VERSIONS } from "./tool-versions.ts";
|
|
21
|
+
|
|
22
|
+
const FROM = import.meta.url;
|
|
23
|
+
const UI = colorize("#61A5FA")("biome");
|
|
24
|
+
const COMMON_FLAGS = ["--colors=force", "--no-errors-on-unmatched"];
|
|
25
|
+
const BIOME_JSON = "biome.json";
|
|
26
|
+
const BIOME_CONFIG_PKG = "@rrlab/biome-config";
|
|
27
|
+
const BIOME_SCHEMA = "https://biomejs.dev/schemas/2.4.4/schema.json";
|
|
28
|
+
|
|
29
|
+
export { TOOL_VERSIONS } from "./tool-versions.ts";
|
|
30
|
+
|
|
31
|
+
export class BiomeService extends ToolService implements Formatter, Linter, StaticChecker {
|
|
32
|
+
constructor(shellService: ShellService) {
|
|
33
|
+
super({ pkg: "@biomejs/biome", bin: "biome", ui: UI, shellService, from: FROM });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async format(options: FormatOptions) {
|
|
37
|
+
const args = ["format", ...COMMON_FLAGS];
|
|
38
|
+
if (options.fix) args.push("--fix");
|
|
39
|
+
await this.exec(args);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async lint(options: LintOptions) {
|
|
43
|
+
const args = ["check", ...COMMON_FLAGS, "--formatter-enabled=false"];
|
|
44
|
+
if (options.fix) args.push("--fix", "--unsafe");
|
|
45
|
+
await this.exec(args);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async check(options: StaticCheckerOptions): Promise<void> {
|
|
49
|
+
if (options.fix) {
|
|
50
|
+
await this.exec(["check", ...COMMON_FLAGS, "--fix"]);
|
|
51
|
+
} else if (options.fixStaged) {
|
|
52
|
+
await this.exec(["check", ...COMMON_FLAGS, "--fix", "--staged"]);
|
|
53
|
+
} else {
|
|
54
|
+
await this.exec([isCI ? "ci" : "check", ...COMMON_FLAGS]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function install(ctx: InstallContext): Promise<InstallResult> {
|
|
60
|
+
const biomeJsonPath = path.join(ctx.appPkg.dirPath, BIOME_JSON);
|
|
61
|
+
const fileExists = await pathExists(biomeJsonPath);
|
|
62
|
+
const scaffoldDecision = await decideScaffoldAction(ctx, fileExists);
|
|
63
|
+
|
|
64
|
+
if (scaffoldDecision === "skip") {
|
|
65
|
+
return { devDependencies: { "@biomejs/biome": TOOL_VERSIONS["@biomejs/biome"].install } };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const devDependencies: Record<string, string> = {
|
|
69
|
+
"@biomejs/biome": TOOL_VERSIONS["@biomejs/biome"].install,
|
|
70
|
+
[BIOME_CONFIG_PKG]: "^0.1.0",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const file: FileOp =
|
|
74
|
+
scaffoldDecision === "create" || scaffoldDecision === "overwrite"
|
|
75
|
+
? {
|
|
76
|
+
kind: "create",
|
|
77
|
+
path: BIOME_JSON,
|
|
78
|
+
content: `${JSON.stringify({ $schema: BIOME_SCHEMA, extends: [BIOME_CONFIG_PKG] }, null, 2)}\n`,
|
|
79
|
+
overwrite: scaffoldDecision === "overwrite" || ctx.flags.force,
|
|
80
|
+
}
|
|
81
|
+
: {
|
|
82
|
+
kind: "edit-json",
|
|
83
|
+
path: BIOME_JSON,
|
|
84
|
+
edits: [
|
|
85
|
+
{ op: "set", path: "/$schema", value: BIOME_SCHEMA, mode: "if-missing" },
|
|
86
|
+
{ op: "include", path: "/extends", value: BIOME_CONFIG_PKG, position: "start" },
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return { devDependencies, files: [file] };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function uninstall(ctx: UninstallContext): Promise<UninstallResult> {
|
|
94
|
+
const biomeJsonPath = path.join(ctx.appPkg.dirPath, BIOME_JSON);
|
|
95
|
+
const removeDependencies = ["@biomejs/biome", BIOME_CONFIG_PKG];
|
|
96
|
+
|
|
97
|
+
if (!(await pathExists(biomeJsonPath))) {
|
|
98
|
+
return { removeDependencies };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let existing: Record<string, unknown> | undefined;
|
|
102
|
+
try {
|
|
103
|
+
const text = await fs.readFile(biomeJsonPath, "utf8");
|
|
104
|
+
existing = parseJsonc(text) as Record<string, unknown>;
|
|
105
|
+
} catch {
|
|
106
|
+
/* malformed — skip surgical edits */
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const files: FileOp[] = [];
|
|
110
|
+
if (existing) {
|
|
111
|
+
const extendsArr = Array.isArray(existing.extends) ? existing.extends : [];
|
|
112
|
+
const otherExtends = extendsArr.filter((e) => e !== BIOME_CONFIG_PKG);
|
|
113
|
+
const { $schema: _schema, extends: _extends, ...rest } = existing;
|
|
114
|
+
const semanticKeys = Object.keys(rest);
|
|
115
|
+
if (otherExtends.length === 0 && semanticKeys.length === 0) {
|
|
116
|
+
files.push({ kind: "delete", path: BIOME_JSON });
|
|
117
|
+
} else {
|
|
118
|
+
const edits = [{ op: "exclude" as const, path: "/extends", value: BIOME_CONFIG_PKG }];
|
|
119
|
+
if (otherExtends.length === 0) {
|
|
120
|
+
edits.push({ op: "unset" as const, path: "/extends" } as never);
|
|
121
|
+
}
|
|
122
|
+
files.push({ kind: "edit-json", path: BIOME_JSON, edits });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { removeDependencies, files };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
type ExistingFileAction = "skip" | "patch" | "overwrite";
|
|
130
|
+
|
|
131
|
+
async function decideScaffoldAction(
|
|
132
|
+
ctx: InstallContext,
|
|
133
|
+
fileExists: boolean,
|
|
134
|
+
): Promise<"create" | "patch" | "overwrite" | "skip"> {
|
|
135
|
+
if (!fileExists) {
|
|
136
|
+
if (ctx.flags.yes || ctx.flags.nonInteractive) return "create";
|
|
137
|
+
const choice = await ctx.prompts.confirm({
|
|
138
|
+
message: `Scaffold ${BIOME_JSON} with the @rrlab/biome-config preset?`,
|
|
139
|
+
initialValue: true,
|
|
140
|
+
});
|
|
141
|
+
if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
|
|
142
|
+
return choice ? "create" : "skip";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (ctx.flags.yes || ctx.flags.nonInteractive) return "patch";
|
|
146
|
+
|
|
147
|
+
const choice = await ctx.prompts.select<ExistingFileAction>({
|
|
148
|
+
message: `${BIOME_JSON} already exists. What do you want to do?`,
|
|
149
|
+
options: [
|
|
150
|
+
{ value: "patch", label: "Patch — add @rrlab/biome-config to extends, keep my other settings" },
|
|
151
|
+
{ value: "skip", label: "Skip — leave it alone" },
|
|
152
|
+
{ value: "overwrite", label: "Overwrite — replace with a fresh scaffold" },
|
|
153
|
+
],
|
|
154
|
+
initialValue: "patch",
|
|
155
|
+
});
|
|
156
|
+
if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
|
|
157
|
+
return choice;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function pathExists(p: string): Promise<boolean> {
|
|
161
|
+
try {
|
|
162
|
+
await fs.access(p);
|
|
163
|
+
return true;
|
|
164
|
+
} catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const biome = definePlugin<void>(() => ({
|
|
170
|
+
name: "biome",
|
|
171
|
+
apiVersion: 1,
|
|
172
|
+
install,
|
|
173
|
+
uninstall,
|
|
174
|
+
async setup({ shell }) {
|
|
175
|
+
const svc = new BiomeService(shell);
|
|
176
|
+
try {
|
|
177
|
+
await svc.getBinDir();
|
|
178
|
+
} catch (_err) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"@rrlab/biome-plugin requires @biomejs/biome to be installed in the host project. " +
|
|
181
|
+
"Run: rr plugins add biome (or: pnpm add -D @biomejs/biome)",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
lint: svc,
|
|
186
|
+
format: svc,
|
|
187
|
+
jsc: svc,
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
export default biome;
|