biome-to-oxc 3.0.1 → 3.6.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 +59 -4
- package/dist/bin/biome-to-oxc.d.mts +3 -4
- package/dist/bin/biome-to-oxc.mjs +1 -1
- package/dist/src/index.d.mts +76 -35
- package/dist/src/index.mjs +1 -1
- package/dist/{src-C66EI3xm.mjs → src-BN3SXk7r.mjs} +245 -23
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ Current capabilities:
|
|
|
22
22
|
|
|
23
23
|
- Converts `biome.json`/`biome.jsonc` to `.oxlintrc.json` and `.oxfmtrc.jsonc`
|
|
24
24
|
- Maps 80+ Biome linter rules to Oxlint equivalents (native `typescript/` rules)
|
|
25
|
+
- Substitutes a non-type-aware Oxlint rule for the three Biome rules that need no types but whose
|
|
26
|
+
only Oxlint counterpart is type-aware, so a migration without tsgolint keeps their coverage
|
|
25
27
|
- Transforms Biome formatter options to Oxfmt (Prettier-compatible)
|
|
26
28
|
- Normalizes `include`/`includes` field variations automatically
|
|
27
29
|
|
|
@@ -37,7 +39,10 @@ Current capabilities:
|
|
|
37
39
|
- Rule severities (`error`/`warn`/`off`; `info` and `on` are accepted with explicit lossy-mapping warnings)
|
|
38
40
|
- Explicit `printWidth` handling (no silent changes)
|
|
39
41
|
- The set of formatted files: Oxfmt-only formats (YAML, TOML, Markdown) are excluded, and
|
|
40
|
-
`sortPackageJson` is pinned off
|
|
42
|
+
`sortPackageJson` is pinned off unless the Biome config turns the matching assist action on, so
|
|
43
|
+
migrating never reformats files Biome left alone
|
|
44
|
+
- Assist actions: `organizeImports` and `useSortedPackageJson` become the Oxfmt options that
|
|
45
|
+
implement them, and every other `assist.actions.source` entry is reported as a loss
|
|
41
46
|
|
|
42
47
|
✅ **Refuses to Lose Behaviour Silently**
|
|
43
48
|
|
|
@@ -301,6 +306,52 @@ Oxlint's broader `restriction` category.
|
|
|
301
306
|
| bracketSpacing | bracketSpacing | Direct mapping |
|
|
302
307
|
| bracketSameLine | bracketSameLine | Direct mapping |
|
|
303
308
|
|
|
309
|
+
### Type-Aware Rule Fallbacks
|
|
310
|
+
|
|
311
|
+
Three Biome rules need no type information, but their only Oxlint counterpart is a type-aware
|
|
312
|
+
(tsgolint) rule. Type-aware linting is off unless the migration enables it, so mapping straight to
|
|
313
|
+
the tsgolint rule would drop diagnostics the Biome config did produce. When type-aware linting is
|
|
314
|
+
off, a non-type-aware Oxlint rule takes its place instead:
|
|
315
|
+
|
|
316
|
+
| Biome Rule | Type-aware mapping | Fallback when type-aware is off |
|
|
317
|
+
| ------------------------- | ------------------------------------------- | ---------------------------------------- |
|
|
318
|
+
| `useArrayFind` | `typescript/prefer-find` | `unicorn/prefer-array-find` |
|
|
319
|
+
| `useIncludes` | `typescript/prefer-includes` | `unicorn/prefer-includes` |
|
|
320
|
+
| `useStringStartsEndsWith` | `typescript/prefer-string-starts-ends-with` | `unicorn/prefer-string-starts-ends-with` |
|
|
321
|
+
|
|
322
|
+
The fallback replaces the type-aware rule rather than joining it, so turning type-aware linting on
|
|
323
|
+
later can never report the same line twice. The first two pairs report exactly what the Biome rule
|
|
324
|
+
reports; `unicorn/prefer-string-starts-ends-with` covers only the anchored-regex form of the four
|
|
325
|
+
Biome flags, so that substitution is reported as a semantic loss.
|
|
326
|
+
|
|
327
|
+
Every other Biome rule that maps only to a type-aware Oxlint rule — `noFloatingPromises`,
|
|
328
|
+
`noMisusedPromises`, `useOptionalChain` and the rest — needs types on both sides and has no
|
|
329
|
+
fallback.
|
|
330
|
+
|
|
331
|
+
### Assist Actions
|
|
332
|
+
|
|
333
|
+
Biome's assist actions are code actions rather than lint rules. Oxfmt implements two of them as
|
|
334
|
+
formatter options; the rest have no Oxc equivalent and are reported as semantic losses.
|
|
335
|
+
|
|
336
|
+
| Biome Assist Action | Oxfmt Option | Notes |
|
|
337
|
+
| ---------------------- | ----------------- | ---------------------------------------------------------------- |
|
|
338
|
+
| `organizeImports` | `sortImports` | `options.sortBareImports` → `sortImports.sortSideEffects` |
|
|
339
|
+
| `useSortedPackageJson` | `sortPackageJson` | Lifts the default pin that keeps Oxfmt from sorting package.json |
|
|
340
|
+
|
|
341
|
+
Only actions the Biome config names explicitly are migrated. Biome enables `organizeImports`
|
|
342
|
+
through its recommended set even when the config never mentions it, but both Oxfmt options default
|
|
343
|
+
to off here, so deriving them from a preset would start rewriting imports in projects whose Biome
|
|
344
|
+
config never asked for it. When a preset is what turned the actions on, the migration reports it.
|
|
345
|
+
|
|
346
|
+
Biome's `organizeImports` `groups` are matcher predicates (`:NODE:`, source globs) while Oxfmt's
|
|
347
|
+
are a fixed set of group names, so custom group ordering is reported as a loss rather than guessed
|
|
348
|
+
at. Per-override `assist` is not migrated either, and is likewise reported.
|
|
349
|
+
|
|
350
|
+
Biome and Oxfmt sort imports and `package.json` keys into different orders, so the first Oxfmt run
|
|
351
|
+
after migrating either action reorders once; the migration warns about it. And because Biome sorts
|
|
352
|
+
imports by default through its recommended assist set, a config that never settles
|
|
353
|
+
`organizeImports` also gets a warning: Oxfmt leaves imports alone unless `sortImports` is set.
|
|
354
|
+
|
|
304
355
|
## After Migration
|
|
305
356
|
|
|
306
357
|
1. **Review generated configs**
|
|
@@ -376,10 +427,14 @@ npx oxlint --type-aware --type-check
|
|
|
376
427
|
- Oxfmt is in beta; review formatting changes before replacing the existing formatter
|
|
377
428
|
- Overlapping `overrides` are migrated as-is. Biome and Oxfmt both merge matching overrides field
|
|
378
429
|
by field with later entries winning, so the two agree; a conformance test pins this
|
|
379
|
-
- Type-aware linting is stable as of `oxlint-tsgolint` v7 and requires TypeScript 7;
|
|
380
|
-
targeted typescript-eslint rules are currently implemented
|
|
381
|
-
-
|
|
430
|
+
- Type-aware linting is stable as of `oxlint-tsgolint` v7 and requires TypeScript 7; 60 of the 62
|
|
431
|
+
targeted typescript-eslint rules are currently implemented, and Oxlint 1.85 does not yet expose
|
|
432
|
+
`no-generated-empty-object-type`, so 59 can be enabled
|
|
433
|
+
- CSS, JSON and GraphQL formatter overrides are mapped but may need manual review
|
|
382
434
|
- Prettier plugin support is not available in Oxfmt
|
|
435
|
+
- Assist actions are migrated only when the Biome config names them explicitly; actions enabled
|
|
436
|
+
through `recommended` or a preset are reported rather than derived
|
|
437
|
+
- `organizeImports` custom `groups` and `identifierOrder` have no Oxfmt counterpart and are reported
|
|
383
438
|
- Some Biome rules still require JS plugin fallback until native rule parity is available
|
|
384
439
|
|
|
385
440
|
## Migration Report
|
|
@@ -7,7 +7,6 @@ interface CliRuntimeOptions {
|
|
|
7
7
|
stderr?: CliWriteStream;
|
|
8
8
|
stdout?: CliWriteStream;
|
|
9
9
|
}
|
|
10
|
-
declare function runCli(argv: string[], runtimeOptions?: CliRuntimeOptions): Promise<number>;
|
|
11
|
-
declare function createTerminationHandler(signalName: NodeJS.Signals, controller: AbortController, stderr: CliWriteStream): () => void;
|
|
12
|
-
//#endregion
|
|
13
|
-
export { createTerminationHandler, runCli };
|
|
10
|
+
export declare function runCli(argv: string[], runtimeOptions?: CliRuntimeOptions): Promise<number>;
|
|
11
|
+
export declare function createTerminationHandler(signalName: NodeJS.Signals, controller: AbortController, stderr: CliWriteStream): () => void;
|
|
12
|
+
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as readJsonFile, n as DefaultReporter, r as findClosestPackageJson, t as migrate } from "../src-
|
|
2
|
+
import { i as readJsonFile, n as DefaultReporter, r as findClosestPackageJson, t as migrate } from "../src-BN3SXk7r.mjs";
|
|
3
3
|
import { realpath } from "node:fs/promises";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
package/dist/src/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
|
-
interface BiomeConfig {
|
|
2
|
+
export interface BiomeConfig {
|
|
3
3
|
$schema?: string;
|
|
4
4
|
extends?: string | string[];
|
|
5
5
|
root?: boolean;
|
|
@@ -7,13 +7,15 @@ interface BiomeConfig {
|
|
|
7
7
|
vcs?: BiomeVcsConfig;
|
|
8
8
|
linter?: BiomeLinterConfig;
|
|
9
9
|
formatter?: BiomeFormatterConfig;
|
|
10
|
+
assist?: BiomeAssistConfig;
|
|
10
11
|
javascript?: BiomeJavaScriptConfig;
|
|
11
12
|
json?: BiomeJsonConfig;
|
|
12
13
|
css?: BiomeCssConfig;
|
|
14
|
+
graphql?: BiomeGraphqlConfig;
|
|
13
15
|
html?: Record<string, unknown>;
|
|
14
16
|
overrides?: BiomeOverride[];
|
|
15
17
|
}
|
|
16
|
-
interface BiomeFilesConfig {
|
|
18
|
+
export interface BiomeFilesConfig {
|
|
17
19
|
include?: string[];
|
|
18
20
|
includes?: string[];
|
|
19
21
|
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
@@ -22,14 +24,14 @@ interface BiomeFilesConfig {
|
|
|
22
24
|
ignoreUnknown?: boolean;
|
|
23
25
|
maxSize?: number;
|
|
24
26
|
}
|
|
25
|
-
interface BiomeVcsConfig {
|
|
27
|
+
export interface BiomeVcsConfig {
|
|
26
28
|
enabled?: boolean;
|
|
27
29
|
clientKind?: 'git';
|
|
28
30
|
useIgnoreFile?: boolean;
|
|
29
31
|
root?: string;
|
|
30
32
|
defaultBranch?: string;
|
|
31
33
|
}
|
|
32
|
-
interface BiomeLinterConfig {
|
|
34
|
+
export interface BiomeLinterConfig {
|
|
33
35
|
enabled?: boolean;
|
|
34
36
|
include?: string[];
|
|
35
37
|
includes?: string[];
|
|
@@ -38,24 +40,47 @@ interface BiomeLinterConfig {
|
|
|
38
40
|
ignore?: string[];
|
|
39
41
|
rules?: BiomeLinterRules;
|
|
40
42
|
}
|
|
41
|
-
interface BiomeLinterRules {
|
|
43
|
+
export interface BiomeLinterRules {
|
|
42
44
|
recommended?: boolean;
|
|
43
45
|
all?: boolean;
|
|
44
46
|
preset?: 'recommended' | 'all' | 'none';
|
|
45
47
|
[category: string]: boolean | BiomeRuleGroup | BiomeRuleSeverityValue | 'recommended' | 'all' | 'none' | undefined;
|
|
46
48
|
}
|
|
47
|
-
interface BiomeRuleGroup {
|
|
49
|
+
export interface BiomeRuleGroup {
|
|
48
50
|
recommended?: boolean;
|
|
49
51
|
all?: boolean;
|
|
50
52
|
preset?: 'recommended' | 'all' | 'none';
|
|
51
53
|
[rule: string]: BiomeRuleSeverity | boolean | 'recommended' | 'all' | 'none' | undefined;
|
|
52
54
|
}
|
|
53
|
-
type BiomeRuleSeverityValue = 'off' | 'on' | 'info' | 'warn' | 'error';
|
|
54
|
-
type BiomeRuleSeverity = BiomeRuleSeverityValue | {
|
|
55
|
+
export type BiomeRuleSeverityValue = 'off' | 'on' | 'info' | 'warn' | 'error';
|
|
56
|
+
export type BiomeRuleSeverity = BiomeRuleSeverityValue | {
|
|
55
57
|
level: BiomeRuleSeverityValue;
|
|
56
58
|
options?: unknown;
|
|
57
59
|
};
|
|
58
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Biome's assist actions. They are code actions rather than lint rules, and the two Oxc
|
|
62
|
+
* surfaces split them: the sorting actions belong to Oxfmt, everything else has no home.
|
|
63
|
+
*/
|
|
64
|
+
export interface BiomeAssistConfig {
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
includes?: string[];
|
|
67
|
+
actions?: BiomeAssistActions;
|
|
68
|
+
}
|
|
69
|
+
export interface BiomeAssistActions {
|
|
70
|
+
recommended?: boolean;
|
|
71
|
+
source?: BiomeAssistSource;
|
|
72
|
+
}
|
|
73
|
+
export interface BiomeAssistSource {
|
|
74
|
+
recommended?: boolean;
|
|
75
|
+
preset?: 'recommended' | 'all' | 'none';
|
|
76
|
+
[action: string]: BiomeAssistAction | boolean | 'recommended' | 'all' | 'none' | undefined;
|
|
77
|
+
}
|
|
78
|
+
export type BiomeAssistActionLevel = 'off' | 'on';
|
|
79
|
+
export type BiomeAssistAction = BiomeAssistActionLevel | {
|
|
80
|
+
level: BiomeAssistActionLevel;
|
|
81
|
+
options?: unknown;
|
|
82
|
+
};
|
|
83
|
+
export interface BiomeFormatterConfig {
|
|
59
84
|
enabled?: boolean;
|
|
60
85
|
include?: string[];
|
|
61
86
|
includes?: string[];
|
|
@@ -74,7 +99,7 @@ interface BiomeFormatterConfig {
|
|
|
74
99
|
trailingNewline?: boolean;
|
|
75
100
|
[key: string]: unknown;
|
|
76
101
|
}
|
|
77
|
-
interface BiomeJavaScriptConfig {
|
|
102
|
+
export interface BiomeJavaScriptConfig {
|
|
78
103
|
parser?: {
|
|
79
104
|
unsafeParameterDecoratorsEnabled?: boolean;
|
|
80
105
|
jsxEverywhere?: boolean;
|
|
@@ -104,7 +129,7 @@ interface BiomeJavaScriptConfig {
|
|
|
104
129
|
globals?: string[];
|
|
105
130
|
[key: string]: unknown;
|
|
106
131
|
}
|
|
107
|
-
interface BiomeJsonConfig {
|
|
132
|
+
export interface BiomeJsonConfig {
|
|
108
133
|
parser?: {
|
|
109
134
|
allowComments?: boolean;
|
|
110
135
|
allowTrailingCommas?: boolean;
|
|
@@ -123,7 +148,7 @@ interface BiomeJsonConfig {
|
|
|
123
148
|
enabled?: boolean;
|
|
124
149
|
};
|
|
125
150
|
}
|
|
126
|
-
interface BiomeCssConfig {
|
|
151
|
+
export interface BiomeCssConfig {
|
|
127
152
|
parser?: {
|
|
128
153
|
cssModules?: boolean;
|
|
129
154
|
};
|
|
@@ -140,7 +165,22 @@ interface BiomeCssConfig {
|
|
|
140
165
|
enabled?: boolean;
|
|
141
166
|
};
|
|
142
167
|
}
|
|
143
|
-
interface
|
|
168
|
+
export interface BiomeGraphqlConfig {
|
|
169
|
+
formatter?: {
|
|
170
|
+
enabled?: boolean;
|
|
171
|
+
bracketSpacing?: boolean;
|
|
172
|
+
indentStyle?: 'tab' | 'space';
|
|
173
|
+
indentWidth?: number;
|
|
174
|
+
lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
|
|
175
|
+
lineWidth?: number;
|
|
176
|
+
quoteStyle?: 'single' | 'double';
|
|
177
|
+
trailingNewline?: boolean;
|
|
178
|
+
};
|
|
179
|
+
linter?: {
|
|
180
|
+
enabled?: boolean;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export interface BiomeOverride {
|
|
144
184
|
include?: string[];
|
|
145
185
|
includes?: string[];
|
|
146
186
|
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
@@ -148,18 +188,20 @@ interface BiomeOverride {
|
|
|
148
188
|
ignore?: string[];
|
|
149
189
|
linter?: BiomeLinterConfig;
|
|
150
190
|
formatter?: BiomeFormatterConfig;
|
|
191
|
+
assist?: BiomeAssistConfig;
|
|
151
192
|
javascript?: BiomeJavaScriptConfig;
|
|
152
193
|
json?: BiomeJsonConfig;
|
|
153
194
|
css?: BiomeCssConfig;
|
|
195
|
+
graphql?: BiomeGraphqlConfig;
|
|
154
196
|
}
|
|
155
|
-
type FixStrategy = 'safe' | 'suggestions' | 'dangerous';
|
|
156
|
-
type TypeAwareProfile = 'standard' | 'strict';
|
|
157
|
-
type OxlintBuiltinPlugin = 'eslint' | 'react' | 'unicorn' | 'typescript' | 'oxc' | 'import' | 'jsdoc' | 'jest' | 'vitest' | 'jsx-a11y' | 'nextjs' | 'react-perf' | 'promise' | 'node' | 'vue';
|
|
158
|
-
type OxlintJsPlugin = string | {
|
|
197
|
+
export type FixStrategy = 'safe' | 'suggestions' | 'dangerous';
|
|
198
|
+
export type TypeAwareProfile = 'standard' | 'strict';
|
|
199
|
+
export type OxlintBuiltinPlugin = 'eslint' | 'react' | 'unicorn' | 'typescript' | 'oxc' | 'import' | 'jsdoc' | 'jest' | 'vitest' | 'jsx-a11y' | 'nextjs' | 'react-perf' | 'promise' | 'node' | 'vue';
|
|
200
|
+
export type OxlintJsPlugin = string | {
|
|
159
201
|
name: string;
|
|
160
202
|
specifier: string;
|
|
161
203
|
};
|
|
162
|
-
interface OxlintSettings {
|
|
204
|
+
export interface OxlintSettings {
|
|
163
205
|
jsdoc?: {
|
|
164
206
|
augmentsExtendsReplacesDocs?: boolean;
|
|
165
207
|
exemptDestructuredRootsFromChecks?: boolean;
|
|
@@ -195,7 +237,7 @@ interface OxlintSettings {
|
|
|
195
237
|
};
|
|
196
238
|
[key: string]: unknown;
|
|
197
239
|
}
|
|
198
|
-
interface OxlintConfig {
|
|
240
|
+
export interface OxlintConfig {
|
|
199
241
|
$schema?: string;
|
|
200
242
|
options?: {
|
|
201
243
|
typeAware?: boolean;
|
|
@@ -211,7 +253,7 @@ interface OxlintConfig {
|
|
|
211
253
|
ignorePatterns?: string[];
|
|
212
254
|
settings?: OxlintSettings;
|
|
213
255
|
}
|
|
214
|
-
interface OxlintOverride {
|
|
256
|
+
export interface OxlintOverride {
|
|
215
257
|
files: string[];
|
|
216
258
|
excludeFiles?: string[];
|
|
217
259
|
env?: Record<string, boolean>;
|
|
@@ -220,13 +262,13 @@ interface OxlintOverride {
|
|
|
220
262
|
jsPlugins?: OxlintJsPlugin[];
|
|
221
263
|
rules?: Record<string, OxlintRuleSeverity>;
|
|
222
264
|
}
|
|
223
|
-
type OxlintRuleSeverity = 'off' | 'warn' | 'error' | ['off' | 'warn' | 'error', ...unknown[]];
|
|
224
|
-
interface OxfmtOverride {
|
|
265
|
+
export type OxlintRuleSeverity = 'off' | 'warn' | 'error' | ['off' | 'warn' | 'error', ...unknown[]];
|
|
266
|
+
export interface OxfmtOverride {
|
|
225
267
|
files: string[];
|
|
226
268
|
excludeFiles?: string[];
|
|
227
269
|
options?: Partial<Omit<OxfmtConfig, '$schema' | 'ignorePatterns' | 'overrides'>>;
|
|
228
270
|
}
|
|
229
|
-
interface OxfmtConfig {
|
|
271
|
+
export interface OxfmtConfig {
|
|
230
272
|
$schema?: string;
|
|
231
273
|
printWidth?: number;
|
|
232
274
|
tabWidth?: number;
|
|
@@ -309,7 +351,7 @@ interface OxfmtConfig {
|
|
|
309
351
|
ignorePatterns?: string[];
|
|
310
352
|
overrides?: OxfmtOverride[];
|
|
311
353
|
}
|
|
312
|
-
interface MigrationOptions {
|
|
354
|
+
export interface MigrationOptions {
|
|
313
355
|
configPath?: string;
|
|
314
356
|
outputDir?: string;
|
|
315
357
|
dryRun?: boolean;
|
|
@@ -332,23 +374,23 @@ interface MigrationOptions {
|
|
|
332
374
|
report?: string;
|
|
333
375
|
signal?: AbortSignal;
|
|
334
376
|
}
|
|
335
|
-
interface PackageScriptUpdate {
|
|
377
|
+
export interface PackageScriptUpdate {
|
|
336
378
|
name: string;
|
|
337
379
|
before: string;
|
|
338
380
|
after: string;
|
|
339
381
|
}
|
|
340
|
-
interface PackageDependencyRemoval {
|
|
382
|
+
export interface PackageDependencyRemoval {
|
|
341
383
|
name: string;
|
|
342
384
|
dependencyType: 'dependencies' | 'devDependencies';
|
|
343
385
|
version?: string;
|
|
344
386
|
}
|
|
345
|
-
interface PackageDevDependencyChange {
|
|
387
|
+
export interface PackageDevDependencyChange {
|
|
346
388
|
name: string;
|
|
347
389
|
action: 'added' | 'already-present' | 'updated';
|
|
348
390
|
from?: string;
|
|
349
391
|
to: string;
|
|
350
392
|
}
|
|
351
|
-
interface PackageUpdateSummary {
|
|
393
|
+
export interface PackageUpdateSummary {
|
|
352
394
|
packageJsonPath: string;
|
|
353
395
|
found: boolean;
|
|
354
396
|
dryRun: boolean;
|
|
@@ -359,14 +401,14 @@ interface PackageUpdateSummary {
|
|
|
359
401
|
/** Lockfile that dependency changes invalidated, when one was detected. */
|
|
360
402
|
lockfile?: LockfileStatus;
|
|
361
403
|
}
|
|
362
|
-
interface LockfileStatus {
|
|
404
|
+
export interface LockfileStatus {
|
|
363
405
|
path: string;
|
|
364
406
|
/** Command that regenerates the lockfile from the updated manifest. */
|
|
365
407
|
installCommand: string;
|
|
366
408
|
/** True when this run changed dependencies, so the lockfile no longer matches. */
|
|
367
409
|
stale: boolean;
|
|
368
410
|
}
|
|
369
|
-
interface MigrationReport {
|
|
411
|
+
export interface MigrationReport {
|
|
370
412
|
/**
|
|
371
413
|
* True when the migration wrote every requested change without errors AND the
|
|
372
414
|
* generated configuration is a complete replacement for the Biome setup.
|
|
@@ -404,7 +446,7 @@ interface MigrationReport {
|
|
|
404
446
|
};
|
|
405
447
|
cleanup?: CleanupOutcome;
|
|
406
448
|
}
|
|
407
|
-
interface CleanupOutcome {
|
|
449
|
+
export interface CleanupOutcome {
|
|
408
450
|
/** Whether `--delete` was requested. */
|
|
409
451
|
requested: boolean;
|
|
410
452
|
/** Whether legacy Biome files were (or in dry-run, would be) removed. */
|
|
@@ -414,7 +456,7 @@ interface CleanupOutcome {
|
|
|
414
456
|
/** Legacy Biome files removed, or that would be removed in dry-run. */
|
|
415
457
|
files: string[];
|
|
416
458
|
}
|
|
417
|
-
interface Reporter {
|
|
459
|
+
export interface Reporter {
|
|
418
460
|
warn(message: string): void;
|
|
419
461
|
error(message: string): void;
|
|
420
462
|
info(message: string): void;
|
|
@@ -434,6 +476,5 @@ interface Reporter {
|
|
|
434
476
|
}
|
|
435
477
|
//#endregion
|
|
436
478
|
//#region src/index.d.ts
|
|
437
|
-
declare function migrate(options?: MigrationOptions, reporter?: Reporter): Promise<MigrationReport>;
|
|
438
|
-
//#endregion
|
|
439
|
-
export { BiomeConfig, BiomeCssConfig, BiomeFilesConfig, BiomeFormatterConfig, BiomeJavaScriptConfig, BiomeJsonConfig, BiomeLinterConfig, BiomeLinterRules, BiomeOverride, BiomeRuleGroup, BiomeRuleSeverity, BiomeRuleSeverityValue, BiomeVcsConfig, CleanupOutcome, FixStrategy, LockfileStatus, MigrationOptions, MigrationReport, OxfmtConfig, OxfmtOverride, OxlintBuiltinPlugin, OxlintConfig, OxlintJsPlugin, OxlintOverride, OxlintRuleSeverity, OxlintSettings, PackageDependencyRemoval, PackageDevDependencyChange, PackageScriptUpdate, PackageUpdateSummary, Reporter, TypeAwareProfile, migrate };
|
|
479
|
+
export declare function migrate(options?: MigrationOptions, reporter?: Reporter): Promise<MigrationReport>;
|
|
480
|
+
//#endregion
|
package/dist/src/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as migrate } from "../src-
|
|
1
|
+
import { t as migrate } from "../src-BN3SXk7r.mjs";
|
|
2
2
|
export { migrate };
|
|
@@ -147,8 +147,8 @@ function generateFeatureSpecificSuggestions(features) {
|
|
|
147
147
|
if (features.hasGraphQL) {
|
|
148
148
|
suggestions.push("");
|
|
149
149
|
suggestions.push("GraphQL detected:");
|
|
150
|
-
suggestions.push(" -
|
|
151
|
-
suggestions.push(" -
|
|
150
|
+
suggestions.push(" - Oxfmt formats .graphql and .gql files natively");
|
|
151
|
+
suggestions.push(" - Biome graphql.formatter settings are migrated to an Oxfmt override");
|
|
152
152
|
}
|
|
153
153
|
if (features.hasMonorepo) {
|
|
154
154
|
suggestions.push("");
|
|
@@ -306,9 +306,11 @@ const SUPPORTED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
|
|
|
306
306
|
"vcs",
|
|
307
307
|
"linter",
|
|
308
308
|
"formatter",
|
|
309
|
+
"assist",
|
|
309
310
|
"javascript",
|
|
310
311
|
"json",
|
|
311
312
|
"css",
|
|
313
|
+
"graphql",
|
|
312
314
|
"html",
|
|
313
315
|
"overrides"
|
|
314
316
|
]);
|
|
@@ -466,20 +468,59 @@ const BiomeCssConfigSchema = z.object({
|
|
|
466
468
|
}).passthrough().optional(),
|
|
467
469
|
linter: z.object({ enabled: z.boolean().optional() }).passthrough().optional()
|
|
468
470
|
}).passthrough();
|
|
471
|
+
const BiomeGraphqlConfigSchema = z.object({
|
|
472
|
+
formatter: z.object({
|
|
473
|
+
enabled: z.boolean().optional(),
|
|
474
|
+
bracketSpacing: z.boolean().optional(),
|
|
475
|
+
indentStyle: z.enum(["tab", "space"]).optional(),
|
|
476
|
+
indentWidth: z.number().int().optional(),
|
|
477
|
+
lineEnding: z.enum([
|
|
478
|
+
"lf",
|
|
479
|
+
"crlf",
|
|
480
|
+
"cr",
|
|
481
|
+
"auto"
|
|
482
|
+
]).optional(),
|
|
483
|
+
lineWidth: z.number().int().optional(),
|
|
484
|
+
quoteStyle: z.enum(["single", "double"]).optional(),
|
|
485
|
+
trailingNewline: z.boolean().optional()
|
|
486
|
+
}).passthrough().optional(),
|
|
487
|
+
linter: z.object({ enabled: z.boolean().optional() }).passthrough().optional()
|
|
488
|
+
}).passthrough();
|
|
469
489
|
const BiomeLinterConfigSchema = IncludeFieldsSchema.extend({
|
|
470
490
|
enabled: z.boolean().optional(),
|
|
471
491
|
ignore: z.array(z.string()).optional(),
|
|
472
492
|
rules: BiomeLinterRulesSchema.optional()
|
|
473
493
|
}).passthrough();
|
|
494
|
+
const BiomeAssistActionSchema = z.union([z.enum(["off", "on"]), z.object({
|
|
495
|
+
level: z.enum(["off", "on"]),
|
|
496
|
+
options: z.unknown().optional()
|
|
497
|
+
}).passthrough()]);
|
|
498
|
+
const BiomeAssistConfigSchema = z.object({
|
|
499
|
+
enabled: z.boolean().optional(),
|
|
500
|
+
includes: z.array(z.string()).optional(),
|
|
501
|
+
actions: z.object({
|
|
502
|
+
recommended: z.boolean().optional(),
|
|
503
|
+
source: z.object({
|
|
504
|
+
recommended: z.boolean().optional(),
|
|
505
|
+
preset: z.enum([
|
|
506
|
+
"recommended",
|
|
507
|
+
"all",
|
|
508
|
+
"none"
|
|
509
|
+
]).optional()
|
|
510
|
+
}).catchall(z.union([BiomeAssistActionSchema, z.boolean()])).optional()
|
|
511
|
+
}).passthrough().optional()
|
|
512
|
+
}).passthrough();
|
|
474
513
|
const BiomeOverrideSchema = z.object({
|
|
475
514
|
include: z.array(z.string()).optional(),
|
|
476
515
|
includes: z.array(z.string()).optional(),
|
|
477
516
|
ignore: z.array(z.string()).optional(),
|
|
478
517
|
linter: BiomeLinterConfigSchema.optional(),
|
|
479
518
|
formatter: BiomeFormatterConfigSchema.optional(),
|
|
519
|
+
assist: BiomeAssistConfigSchema.optional(),
|
|
480
520
|
javascript: BiomeJavaScriptConfigSchema.optional(),
|
|
481
521
|
json: BiomeJsonConfigSchema.optional(),
|
|
482
|
-
css: BiomeCssConfigSchema.optional()
|
|
522
|
+
css: BiomeCssConfigSchema.optional(),
|
|
523
|
+
graphql: BiomeGraphqlConfigSchema.optional()
|
|
483
524
|
}).passthrough();
|
|
484
525
|
const BiomeConfigSchema = z.object({
|
|
485
526
|
$schema: z.string().optional(),
|
|
@@ -501,9 +542,11 @@ const BiomeConfigSchema = z.object({
|
|
|
501
542
|
}).passthrough().optional(),
|
|
502
543
|
linter: BiomeLinterConfigSchema.optional(),
|
|
503
544
|
formatter: BiomeFormatterConfigSchema.optional(),
|
|
545
|
+
assist: BiomeAssistConfigSchema.optional(),
|
|
504
546
|
javascript: BiomeJavaScriptConfigSchema.optional(),
|
|
505
547
|
json: BiomeJsonConfigSchema.optional(),
|
|
506
548
|
css: BiomeCssConfigSchema.optional(),
|
|
549
|
+
graphql: BiomeGraphqlConfigSchema.optional(),
|
|
507
550
|
html: z.record(z.string(), z.unknown()).optional(),
|
|
508
551
|
overrides: z.array(BiomeOverrideSchema).optional()
|
|
509
552
|
}).passthrough();
|
|
@@ -751,6 +794,7 @@ const CSS_EXTENSIONS = [
|
|
|
751
794
|
"sass",
|
|
752
795
|
"less"
|
|
753
796
|
];
|
|
797
|
+
const GRAPHQL_EXTENSIONS = ["graphql", "gql"];
|
|
754
798
|
const JSON_FORMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
755
799
|
"enabled",
|
|
756
800
|
"expand",
|
|
@@ -774,6 +818,18 @@ const CSS_FORMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
|
774
818
|
...EXPLICIT_OXFMT_OPTION_KEYS$1,
|
|
775
819
|
...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES$1)
|
|
776
820
|
]);
|
|
821
|
+
const GRAPHQL_FORMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
822
|
+
"enabled",
|
|
823
|
+
"bracketSpacing",
|
|
824
|
+
"indentStyle",
|
|
825
|
+
"indentWidth",
|
|
826
|
+
"lineEnding",
|
|
827
|
+
"lineWidth",
|
|
828
|
+
"quoteStyle",
|
|
829
|
+
"trailingNewline",
|
|
830
|
+
...EXPLICIT_OXFMT_OPTION_KEYS$1,
|
|
831
|
+
...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES$1)
|
|
832
|
+
]);
|
|
777
833
|
/**
|
|
778
834
|
* Biome's `expand` and Oxfmt's `objectWrap` agree on two of three modes: Biome's `auto`
|
|
779
835
|
* (expand when the literal already has a newline after `{`) is Oxfmt's `preserve`, and
|
|
@@ -797,10 +853,12 @@ function generateOxfmtOverrides(biomeOverrides, reporter) {
|
|
|
797
853
|
if (!files || files.length === 0) continue;
|
|
798
854
|
const excludePatterns = [.../* @__PURE__ */ new Set([...override.ignore ?? [], ...override.exclude ?? []])];
|
|
799
855
|
const excludeFiles = excludePatterns.length > 0 ? excludePatterns : void 0;
|
|
856
|
+
if (override.assist) reporter.loss(`Biome override for ${files.join(", ")} configures assist actions; per-override assist is not migrated, so only the top-level assist configuration reached the Oxfmt config.`);
|
|
800
857
|
pushOverride(oxfmtOverrides, files, excludeFiles, mapBaseFormatterOptions(override.formatter, reporter));
|
|
801
858
|
pushScopedOverride(oxfmtOverrides, files, excludeFiles, mapJavaScriptFormatterOptions(override.javascript?.formatter, reporter), reporter, "javascript.formatter", JAVASCRIPT_EXTENSIONS);
|
|
802
859
|
pushScopedOverride(oxfmtOverrides, files, excludeFiles, mapJsonFormatterOptions(override.json?.formatter, reporter), reporter, "json.formatter", JSON_EXTENSIONS);
|
|
803
860
|
pushScopedOverride(oxfmtOverrides, files, excludeFiles, mapCssFormatterOptions(override.css?.formatter, reporter), reporter, "css.formatter", CSS_EXTENSIONS);
|
|
861
|
+
pushScopedOverride(oxfmtOverrides, files, excludeFiles, mapGraphqlFormatterOptions(override.graphql?.formatter, reporter), reporter, "graphql.formatter", GRAPHQL_EXTENSIONS);
|
|
804
862
|
}
|
|
805
863
|
return oxfmtOverrides;
|
|
806
864
|
}
|
|
@@ -893,6 +951,19 @@ function mapCssFormatterOptions(cssFormatter, reporter) {
|
|
|
893
951
|
applyExplicitFormatterOptionPassThrough$1([cssFormatter], options, reporter);
|
|
894
952
|
return options;
|
|
895
953
|
}
|
|
954
|
+
function mapGraphqlFormatterOptions(graphqlFormatter, reporter) {
|
|
955
|
+
const options = {};
|
|
956
|
+
if (!graphqlFormatter) return options;
|
|
957
|
+
warnAboutUnsupportedKeys(graphqlFormatter, GRAPHQL_FORMATTER_KEYS, "graphql.formatter", reporter);
|
|
958
|
+
if (graphqlFormatter.lineWidth !== void 0) options.printWidth = graphqlFormatter.lineWidth;
|
|
959
|
+
if (graphqlFormatter.indentStyle !== void 0) options.useTabs = graphqlFormatter.indentStyle === "tab";
|
|
960
|
+
if (graphqlFormatter.indentWidth !== void 0) options.tabWidth = graphqlFormatter.indentWidth;
|
|
961
|
+
if (graphqlFormatter.lineEnding !== void 0) options.endOfLine = graphqlFormatter.lineEnding;
|
|
962
|
+
if (graphqlFormatter.bracketSpacing !== void 0) options.bracketSpacing = graphqlFormatter.bracketSpacing;
|
|
963
|
+
if (graphqlFormatter.trailingNewline !== void 0) options.insertFinalNewline = graphqlFormatter.trailingNewline;
|
|
964
|
+
applyExplicitFormatterOptionPassThrough$1([graphqlFormatter], options, reporter);
|
|
965
|
+
return options;
|
|
966
|
+
}
|
|
896
967
|
function warnAboutUnsupportedKeys(formatter, supportedKeys, label, reporter) {
|
|
897
968
|
for (const [key, value] of Object.entries(formatter)) {
|
|
898
969
|
if (value === void 0) continue;
|
|
@@ -996,6 +1067,13 @@ const OXFMT_ONLY_IGNORE_PATTERNS = [
|
|
|
996
1067
|
"**/*.toml",
|
|
997
1068
|
"**/*.{md,mdx}"
|
|
998
1069
|
];
|
|
1070
|
+
/**
|
|
1071
|
+
* The two Biome assist actions Oxfmt implements. Every other `assist.actions.source` entry
|
|
1072
|
+
* is a code action with no formatter equivalent, so enabling one is a migration loss.
|
|
1073
|
+
*/
|
|
1074
|
+
const OXFMT_BACKED_ASSIST_ACTIONS = /* @__PURE__ */ new Set(["organizeImports", "useSortedPackageJson"]);
|
|
1075
|
+
/** Group-level switches that sit alongside the actions and are not actions themselves. */
|
|
1076
|
+
const ASSIST_GROUP_LEVEL_KEYS = /* @__PURE__ */ new Set(["recommended", "preset"]);
|
|
999
1077
|
function generateOxfmtConfig(biomeConfig, reporter, options = {}) {
|
|
1000
1078
|
const oxfmtConfig = { $schema: "./node_modules/oxfmt/configuration_schema.json" };
|
|
1001
1079
|
const { formatter } = biomeConfig;
|
|
@@ -1003,7 +1081,7 @@ function generateOxfmtConfig(biomeConfig, reporter, options = {}) {
|
|
|
1003
1081
|
warnAboutUnsupportedFormatterKeys(formatter, GLOBAL_FORMATTER_KEYS, "formatter", reporter);
|
|
1004
1082
|
warnAboutUnsupportedFormatterKeys(jsFormatter, JAVASCRIPT_FORMATTER_KEYS, "javascript.formatter", reporter);
|
|
1005
1083
|
if (formatter?.enabled === false) {
|
|
1006
|
-
if (!(jsFormatter?.enabled === true || biomeConfig.json?.formatter?.enabled === true || biomeConfig.css?.formatter?.enabled === true)) oxfmtConfig.ignorePatterns = ["**/*"];
|
|
1084
|
+
if (!(jsFormatter?.enabled === true || biomeConfig.json?.formatter?.enabled === true || biomeConfig.css?.formatter?.enabled === true || biomeConfig.graphql?.formatter?.enabled === true)) oxfmtConfig.ignorePatterns = ["**/*"];
|
|
1007
1085
|
else reporter.loss("Biome global formatting is disabled with a language formatter re-enabled; Oxfmt cannot represent that enable-only selection, so more files will be formatted than Biome formatted.");
|
|
1008
1086
|
}
|
|
1009
1087
|
const positiveSelectors = [...biomeConfig.files?.include ?? [], ...formatter?.include ?? []];
|
|
@@ -1011,6 +1089,7 @@ function generateOxfmtConfig(biomeConfig, reporter, options = {}) {
|
|
|
1011
1089
|
mapFormatterOptions(formatter, oxfmtConfig, reporter);
|
|
1012
1090
|
mapIgnorePatterns$1(biomeConfig, oxfmtConfig, options.biomeIgnorePatterns ?? []);
|
|
1013
1091
|
mapDisabledLanguageFormatters(biomeConfig, oxfmtConfig);
|
|
1092
|
+
mapAssistActions(biomeConfig.assist, oxfmtConfig, reporter);
|
|
1014
1093
|
applyExplicitFormatterOptionPassThrough([formatter], oxfmtConfig, reporter);
|
|
1015
1094
|
oxfmtConfig.sortPackageJson ??= false;
|
|
1016
1095
|
const languageOverrides = generateOxfmtOverrides([
|
|
@@ -1025,6 +1104,10 @@ function generateOxfmtConfig(biomeConfig, reporter, options = {}) {
|
|
|
1025
1104
|
{
|
|
1026
1105
|
include: ["**/*.{css,scss,sass,less}"],
|
|
1027
1106
|
css: biomeConfig.css
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
include: ["**/*.{graphql,gql}"],
|
|
1110
|
+
graphql: biomeConfig.graphql
|
|
1028
1111
|
}
|
|
1029
1112
|
], reporter);
|
|
1030
1113
|
if (languageOverrides.length > 0) oxfmtConfig.overrides = languageOverrides;
|
|
@@ -1047,6 +1130,7 @@ function mapDisabledLanguageFormatters(biomeConfig, oxfmtConfig) {
|
|
|
1047
1130
|
if (biomeConfig.javascript?.formatter?.enabled === false) disabledPatterns.push("**/*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}");
|
|
1048
1131
|
if (biomeConfig.json?.formatter?.enabled === false) disabledPatterns.push("**/*.{json,jsonc,json5}");
|
|
1049
1132
|
if (biomeConfig.css?.formatter?.enabled === false) disabledPatterns.push("**/*.{css,scss,sass,less}");
|
|
1133
|
+
if (biomeConfig.graphql?.formatter?.enabled === false) disabledPatterns.push("**/*.{graphql,gql}");
|
|
1050
1134
|
if (disabledPatterns.length > 0) oxfmtConfig.ignorePatterns = [.../* @__PURE__ */ new Set([...oxfmtConfig.ignorePatterns ?? [], ...disabledPatterns])];
|
|
1051
1135
|
}
|
|
1052
1136
|
function warnAboutUnsupportedFormatterKeys(formatter, supportedKeys, label, reporter) {
|
|
@@ -1080,6 +1164,67 @@ function mapFormatterOptions(formatter, oxfmtConfig, reporter) {
|
|
|
1080
1164
|
if (formatter?.trailingNewline !== void 0) oxfmtConfig.insertFinalNewline = formatter.trailingNewline;
|
|
1081
1165
|
if (formatter?.formatWithErrors) reporter.warn("Biome's formatWithErrors option is not supported in Oxfmt");
|
|
1082
1166
|
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Maps Biome's assist actions onto the Oxfmt options that implement them.
|
|
1169
|
+
*
|
|
1170
|
+
* Only actions the config turns on explicitly are migrated. Biome enables `organizeImports`
|
|
1171
|
+
* through its recommended set even when the config never mentions it, but both Oxfmt options
|
|
1172
|
+
* default to off here, so deriving them from a preset would start rewriting imports in
|
|
1173
|
+
* projects whose Biome config never asked for it.
|
|
1174
|
+
*/
|
|
1175
|
+
function mapAssistActions(assist, oxfmtConfig, reporter) {
|
|
1176
|
+
if (assist?.includes && assist.includes.length > 0) reporter.loss(`Biome assist.includes (${assist.includes.join(", ")}) cannot be represented in an Oxfmt config; Oxfmt applies its sorting options to every file it formats.`);
|
|
1177
|
+
if (assist?.enabled === false) return;
|
|
1178
|
+
const source = assist?.actions?.source;
|
|
1179
|
+
if (assist?.actions?.recommended || source?.recommended || source?.preset) reporter.loss("Biome assist actions were enabled through a preset rather than named individually; only the actions the config names explicitly were migrated to Oxfmt.");
|
|
1180
|
+
for (const [action, value] of Object.entries(source ?? {})) {
|
|
1181
|
+
if (ASSIST_GROUP_LEVEL_KEYS.has(action) || value === void 0) continue;
|
|
1182
|
+
if (readAssistActionLevel(value) !== "on") continue;
|
|
1183
|
+
if (!OXFMT_BACKED_ASSIST_ACTIONS.has(action)) {
|
|
1184
|
+
reporter.loss(`Biome assist action "${action}" has no Oxfmt equivalent and was not migrated; its code action is lost.`);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
if (action === "useSortedPackageJson") {
|
|
1188
|
+
oxfmtConfig.sortPackageJson = true;
|
|
1189
|
+
reporter.warn("Biome assist action useSortedPackageJson became Oxfmt's sortPackageJson; the two use different key orders, so the first Oxfmt run will reorder package.json once.");
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
oxfmtConfig.sortImports = mapOrganizeImportsOptions(value, reporter);
|
|
1193
|
+
reporter.warn("Biome assist action organizeImports became Oxfmt's sortImports; the two group and order imports differently, so the first Oxfmt run will reorder imports once.");
|
|
1194
|
+
}
|
|
1195
|
+
warnAboutDefaultOrganizeImports(source, oxfmtConfig, reporter);
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Biome runs `organizeImports` through its recommended assist set even when the config never
|
|
1199
|
+
* mentions it, so `biome check` sorts imports in essentially every project. Oxfmt's
|
|
1200
|
+
* `sortImports` defaults to off and deriving it from that default would start rewriting
|
|
1201
|
+
* imports in projects that never asked for it, so the difference is reported instead.
|
|
1202
|
+
*
|
|
1203
|
+
* Reported as a warning rather than a loss because it applies to nearly every migration, the
|
|
1204
|
+
* way the linter's category-preset approximation does.
|
|
1205
|
+
*/
|
|
1206
|
+
function warnAboutDefaultOrganizeImports(source, oxfmtConfig, reporter) {
|
|
1207
|
+
const organizeImports = source?.organizeImports;
|
|
1208
|
+
if (oxfmtConfig.sortImports !== void 0 || organizeImports !== void 0) return;
|
|
1209
|
+
reporter.warn("Biome sorts imports by default through its recommended assist set. Oxfmt does not sort imports unless asked, so the generated config leaves them alone; set \"sortImports\" in the Oxfmt config to keep sorting them.");
|
|
1210
|
+
}
|
|
1211
|
+
function readAssistActionLevel(value) {
|
|
1212
|
+
if (typeof value === "boolean") return value ? "on" : "off";
|
|
1213
|
+
if (typeof value === "string") return value === "on" ? "on" : "off";
|
|
1214
|
+
return value.level === "on" ? "on" : "off";
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Translates the one `organizeImports` option Oxfmt has a counterpart for. Biome's `groups`
|
|
1218
|
+
* are matcher predicates (`:NODE:`, source globs) while Oxfmt's are a fixed list of group
|
|
1219
|
+
* names, so a faithful translation is not available and the ordering is reported as lost.
|
|
1220
|
+
*/
|
|
1221
|
+
function mapOrganizeImportsOptions(value, reporter) {
|
|
1222
|
+
const options = typeof value === "object" && value.options && typeof value.options === "object" ? value.options : void 0;
|
|
1223
|
+
if (!options) return {};
|
|
1224
|
+
if (options.groups) reporter.loss("Biome assist action organizeImports option \"groups\" was not migrated; Biome's groups are matcher predicates while Oxfmt's are a fixed set of group names, so Oxfmt will sort imports into its default groups.");
|
|
1225
|
+
if (options.identifierOrder !== void 0 && options.identifierOrder !== "natural") reporter.loss(`Biome assist action organizeImports option "identifierOrder": ${JSON.stringify(options.identifierOrder)} has no Oxfmt equivalent; Oxfmt sorts identifiers with its own order.`);
|
|
1226
|
+
return typeof options.sortBareImports === "boolean" ? { sortSideEffects: options.sortBareImports } : {};
|
|
1227
|
+
}
|
|
1083
1228
|
function mapIgnorePatterns$1(biomeConfig, oxfmtConfig, additionalIgnorePatterns) {
|
|
1084
1229
|
const ignorePatterns = [
|
|
1085
1230
|
...oxfmtConfig.ignorePatterns ?? [],
|
|
@@ -1192,8 +1337,7 @@ const UNSUPPORTED_BIOME_RULE_GUIDANCE = {
|
|
|
1192
1337
|
noRedundantUseStrict: {
|
|
1193
1338
|
nativeAlternatives: ["unicorn/prefer-module"],
|
|
1194
1339
|
notes: "No native Oxlint equivalent for redundant `\"use strict\"` directives. For ESM, strict mode is already implicit."
|
|
1195
|
-
}
|
|
1196
|
-
useSingleVarDeclarator: { notes: "No native Oxlint equivalent for enforcing single declarators (ESLint `one-var`). Consider custom JS plugin policy if this is mandatory." }
|
|
1340
|
+
}
|
|
1197
1341
|
};
|
|
1198
1342
|
function collectUnsupportedBiomeRules(warnings) {
|
|
1199
1343
|
const unsupported = /* @__PURE__ */ new Set();
|
|
@@ -1356,28 +1500,40 @@ const OXLINT_CATEGORIES = [
|
|
|
1356
1500
|
"suspicious"
|
|
1357
1501
|
];
|
|
1358
1502
|
/**
|
|
1359
|
-
*
|
|
1503
|
+
* Every React Compiler rule Oxlint implements.
|
|
1360
1504
|
*
|
|
1361
|
-
* Oxlint 1.79 replaced the single `react/react-compiler` rule with 22 per-diagnostic rules
|
|
1362
|
-
*
|
|
1363
|
-
* `
|
|
1364
|
-
*
|
|
1505
|
+
* Oxlint 1.79 replaced the single `react/react-compiler` rule with 22 per-diagnostic rules,
|
|
1506
|
+
* spread across the `correctness`, `perf`, `restriction`, and `suspicious` categories. Biome's
|
|
1507
|
+
* `useReactCompiler` runs the compiler and reports every bailout it finds, so the whole set is
|
|
1508
|
+
* the equivalent: mapping only the rules Oxlint enables by default would drop diagnostics the
|
|
1509
|
+
* Biome rule did produce. Oxlint does not implement `gating` or `memoized-effect-dependencies`,
|
|
1510
|
+
* so there is nothing to enable for those two.
|
|
1365
1511
|
*
|
|
1366
1512
|
* @see https://oxc.rs/blog/2026-08-18-react-compiler-support
|
|
1367
1513
|
*/
|
|
1368
|
-
const
|
|
1514
|
+
const REACT_COMPILER_RULES = [
|
|
1515
|
+
"react/capitalized-calls",
|
|
1369
1516
|
"react/error-boundaries",
|
|
1517
|
+
"react/exhaustive-effect-dependencies",
|
|
1370
1518
|
"react/globals",
|
|
1519
|
+
"react/hooks",
|
|
1371
1520
|
"react/immutability",
|
|
1372
1521
|
"react/incompatible-library",
|
|
1522
|
+
"react/invariant",
|
|
1523
|
+
"react/memo-dependencies",
|
|
1524
|
+
"react/no-deriving-state-in-effects",
|
|
1373
1525
|
"react/preserve-manual-memoization",
|
|
1374
1526
|
"react/purity",
|
|
1375
1527
|
"react/refs",
|
|
1528
|
+
"react/rule-suppression",
|
|
1376
1529
|
"react/set-state-in-effect",
|
|
1377
1530
|
"react/set-state-in-render",
|
|
1378
1531
|
"react/static-components",
|
|
1532
|
+
"react/syntax",
|
|
1533
|
+
"react/todo",
|
|
1379
1534
|
"react/unsupported-syntax",
|
|
1380
|
-
"react/use-memo"
|
|
1535
|
+
"react/use-memo",
|
|
1536
|
+
"react/void-use-memo"
|
|
1381
1537
|
];
|
|
1382
1538
|
/**
|
|
1383
1539
|
* Mappings where the Oxlint rule covers only part of what the Biome rule checked. The note
|
|
@@ -1385,10 +1541,31 @@ const REACT_COMPILER_RECOMMENDED_RULES = [
|
|
|
1385
1541
|
*/
|
|
1386
1542
|
const PARTIAL_RULE_MAPPING_NOTES = {
|
|
1387
1543
|
noComponentHookFactories: "Biome rule noComponentHookFactories was mapped to react/no-unstable-nested-components, which reports nested component definitions but not nested custom hook definitions.",
|
|
1544
|
+
noInlineStyles: "Biome rule noInlineStyles was mapped to react/forbid-dom-props, which reports the `style` prop on JSX DOM elements but not `style` passed through React.createElement() or written in HTML, Vue, Svelte or Astro templates.",
|
|
1388
1545
|
noReactPropAssignments: "Biome rule noReactPropAssignments was mapped to the React Compiler rule react/immutability, which reports prop mutation alongside other mutations of values React treats as immutable.",
|
|
1546
|
+
noUnresolvedImports: "Biome rule noUnresolvedImports was mapped to import/named, which reports named imports a JavaScript module does not export but skips TypeScript files entirely and does not report import paths that cannot be resolved.",
|
|
1547
|
+
noUnsafeIframeSandbox: "Biome rule noUnsafeIframeSandbox was mapped to react/iframe-missing-sandbox, which reports `allow-scripts` combined with `allow-same-origin` only when the sandbox value is a plain string attribute, not a `{\"...\"}` or template-literal expression; it also reports iframes with no or an invalid sandbox attribute, which Biome leaves to useIframeSandbox.",
|
|
1389
1548
|
useExplicitType: "Biome rule useExplicitType was mapped to typescript/explicit-function-return-type, which requires return types on functions and methods but not types on variables or parameters.",
|
|
1390
|
-
|
|
1549
|
+
useStaticResponseMethods: "Biome rule useStaticResponseMethods was mapped to unicorn/prefer-response-static-json, which reports `new Response(JSON.stringify(...))` but not the `new Response(null, { status, headers: { Location } })` form Biome rewrites to Response.redirect()."
|
|
1391
1550
|
};
|
|
1551
|
+
/**
|
|
1552
|
+
* Biome rules whose only Oxlint counterpart is a type-aware rule, even though the Biome rule
|
|
1553
|
+
* itself needs no type information. Without type-aware linting the type-aware rule never runs,
|
|
1554
|
+
* so the migration would silently drop diagnostics the source config did produce. These
|
|
1555
|
+
* non-type-aware Oxlint rules take its place whenever the migration is not enabling type-aware
|
|
1556
|
+
* linting; they replace rather than accompany it, so switching the profile on later can never
|
|
1557
|
+
* report the same line twice.
|
|
1558
|
+
*
|
|
1559
|
+
* Every pair was checked by running both binaries over the same fixture.
|
|
1560
|
+
*/
|
|
1561
|
+
const TYPE_AWARE_RULE_FALLBACKS = {
|
|
1562
|
+
useArrayFind: "unicorn/prefer-array-find",
|
|
1563
|
+
useFind: "unicorn/prefer-array-find",
|
|
1564
|
+
useIncludes: "unicorn/prefer-includes",
|
|
1565
|
+
useStringStartsEndsWith: "unicorn/prefer-string-starts-ends-with"
|
|
1566
|
+
};
|
|
1567
|
+
/** Fallbacks that cover less than the type-aware rule they stand in for. */
|
|
1568
|
+
const TYPE_AWARE_FALLBACK_NOTES = { useStringStartsEndsWith: "Biome rule useStringStartsEndsWith was mapped to unicorn/prefer-string-starts-ends-with because type-aware linting is off; it reports anchored RegExp#test() prefix checks but not the indexOf(), slice() and charAt() comparisons Biome also reports. Enabling type-aware linting maps it to typescript/prefer-string-starts-ends-with instead, which covers all four." };
|
|
1392
1569
|
const BIOME_TO_OXLINT_RULE_MAP = {
|
|
1393
1570
|
noAccessKey: "jsx-a11y/no-access-key",
|
|
1394
1571
|
noAdjacentSpacesInRegex: "no-regex-spaces",
|
|
@@ -1486,6 +1663,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1486
1663
|
noImportCycles: "import/no-cycle",
|
|
1487
1664
|
noImplicitCoercions: "no-implicit-coercion",
|
|
1488
1665
|
noInnerDeclarations: "no-inner-declarations",
|
|
1666
|
+
noInlineStyles: "react/forbid-dom-props",
|
|
1489
1667
|
noImplicitBoolean: "no-implicit-coercion",
|
|
1490
1668
|
noImpliedEval: "no-implied-eval",
|
|
1491
1669
|
noIncrementDecrement: "no-plusplus",
|
|
@@ -1499,6 +1677,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1499
1677
|
noJsRestrictedProperties: "no-restricted-properties",
|
|
1500
1678
|
noJsxLiterals: "react/jsx-no-literals",
|
|
1501
1679
|
noJsxNamespace: "react/no-namespace",
|
|
1680
|
+
noJsxPropsBind: "react-perf/jsx-no-new-function-as-prop",
|
|
1502
1681
|
noLabelWithoutControl: "jsx-a11y/label-has-associated-control",
|
|
1503
1682
|
noLabelVar: "no-label-var",
|
|
1504
1683
|
noLoopFunc: "no-loop-func",
|
|
@@ -1551,6 +1730,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1551
1730
|
noRestrictedImports: "no-restricted-imports",
|
|
1552
1731
|
noRestrictedTypes: "typescript/no-restricted-types",
|
|
1553
1732
|
noReturnAssign: "no-return-assign",
|
|
1733
|
+
noReturnInFinally: "promise/no-return-in-finally",
|
|
1554
1734
|
noScriptUrl: "no-script-url",
|
|
1555
1735
|
noSelfCompare: "no-self-compare",
|
|
1556
1736
|
noSelfAssign: "no-self-assign",
|
|
@@ -1575,13 +1755,16 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1575
1755
|
noUnusedFunctionParameters: "no-unused-vars",
|
|
1576
1756
|
noUnusedImports: "no-unused-vars",
|
|
1577
1757
|
noUnnecessaryContinue: "no-continue",
|
|
1758
|
+
noUnmodifiedLoopCondition: "no-unmodified-loop-condition",
|
|
1578
1759
|
noUnnecessaryConditions: "typescript/no-unnecessary-condition",
|
|
1579
1760
|
noUnnecessaryTemplateExpression: "typescript/no-unnecessary-template-expression",
|
|
1580
1761
|
noUnreachable: "no-unreachable",
|
|
1581
1762
|
noUnreachableSuper: "constructor-super",
|
|
1763
|
+
noUnresolvedImports: "import/named",
|
|
1582
1764
|
noUnsafeDeclarationMerging: "typescript/no-unsafe-declaration-merging",
|
|
1583
1765
|
noUnsafeTypeAssertion: "typescript/no-unsafe-type-assertion",
|
|
1584
1766
|
noUnsafeFinally: "no-unsafe-finally",
|
|
1767
|
+
noUnsafeIframeSandbox: "react/iframe-missing-sandbox",
|
|
1585
1768
|
noUnsafeNegation: "no-unsafe-negation",
|
|
1586
1769
|
noUnsafeOptionalChaining: "no-unsafe-optional-chaining",
|
|
1587
1770
|
noUnsafePlusOperands: "typescript/restrict-plus-operands",
|
|
@@ -1649,12 +1832,14 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1649
1832
|
useConsistentBuiltinInstantiation: "no-new-wrappers",
|
|
1650
1833
|
useConsistentCurlyBraces: "curly",
|
|
1651
1834
|
useConsistentEnumValueType: "typescript/no-mixed-enums",
|
|
1835
|
+
useConsistentFunctionStyle: "func-style",
|
|
1652
1836
|
useConsistentMethodSignatures: "typescript/method-signature-style",
|
|
1653
1837
|
useConsistentTypeDefinitions: "typescript/consistent-type-definitions",
|
|
1654
1838
|
useConsistentMemberAccessibility: "typescript/explicit-member-accessibility",
|
|
1655
1839
|
useConsistentObjectDefinitions: "object-shorthand",
|
|
1656
1840
|
useConsistentTestIt: ["jest/consistent-test-it", "vitest/consistent-test-it"],
|
|
1657
1841
|
useConst: "prefer-const",
|
|
1842
|
+
useControlLabel: "jsx-a11y/control-has-associated-label",
|
|
1658
1843
|
useDateNow: "unicorn/prefer-date-now",
|
|
1659
1844
|
useDefaultParameterLast: "default-param-last",
|
|
1660
1845
|
useDefaultSwitchClause: "default-case",
|
|
@@ -1708,6 +1893,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1708
1893
|
useMathMinMax: "unicorn/prefer-math-min-max",
|
|
1709
1894
|
useMaxParams: "max-params",
|
|
1710
1895
|
useMediaCaption: "jsx-a11y/media-has-caption",
|
|
1896
|
+
useModernMathApis: "unicorn/prefer-modern-math-apis",
|
|
1711
1897
|
useNamedCaptureGroup: "prefer-named-capture-group",
|
|
1712
1898
|
useNamespaceKeyword: "typescript/prefer-namespace-keyword",
|
|
1713
1899
|
useNodejsImportProtocol: "unicorn/prefer-node-protocol",
|
|
@@ -1721,7 +1907,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1721
1907
|
useParseIntRadix: "radix",
|
|
1722
1908
|
useReadonlyClassProperties: "typescript/prefer-readonly",
|
|
1723
1909
|
useReactFunctionComponentDefinition: "react/function-component-definition",
|
|
1724
|
-
useReactCompiler:
|
|
1910
|
+
useReactCompiler: REACT_COMPILER_RULES,
|
|
1725
1911
|
useReactFunctionComponents: "react/prefer-function-component",
|
|
1726
1912
|
useReduceTypeParameter: "typescript/prefer-reduce-type-parameter",
|
|
1727
1913
|
useRegexLiterals: "prefer-regex-literals",
|
|
@@ -1735,6 +1921,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1735
1921
|
useSimplifiedLogicExpression: "unicorn/prefer-logical-operator-over-ternary",
|
|
1736
1922
|
useSpread: "prefer-spread",
|
|
1737
1923
|
useSpreadOverApply: "prefer-spread",
|
|
1924
|
+
useStaticResponseMethods: "unicorn/prefer-response-static-json",
|
|
1738
1925
|
useStringStartsEndsWith: "typescript/prefer-string-starts-ends-with",
|
|
1739
1926
|
useSymbolDescription: "symbol-description",
|
|
1740
1927
|
useTemplate: "prefer-template",
|
|
@@ -1753,8 +1940,10 @@ const BIOME_TO_OXLINT_RULE_MAP = {
|
|
|
1753
1940
|
useValidAutocomplete: "jsx-a11y/autocomplete-valid",
|
|
1754
1941
|
useValidForDirection: "for-direction",
|
|
1755
1942
|
useValidLang: "jsx-a11y/lang",
|
|
1943
|
+
useValidTestTitle: ["jest/valid-title", "vitest/valid-title"],
|
|
1756
1944
|
useValidTypeof: "valid-typeof",
|
|
1757
1945
|
useVarsOnTop: "vars-on-top",
|
|
1946
|
+
useVueBaseImport: "vue/prefer-import-from-vue",
|
|
1758
1947
|
useVueConsistentDefinePropsDeclaration: "vue/define-props-declaration",
|
|
1759
1948
|
useVueNextTickPromise: "vue/next-tick-style",
|
|
1760
1949
|
useYield: "require-yield"
|
|
@@ -1795,6 +1984,7 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
|
|
|
1795
1984
|
IIFEs: options?.skipIifes !== true
|
|
1796
1985
|
}];
|
|
1797
1986
|
if (biomeName === "useSingleVarDeclarator") return [severity, "never"];
|
|
1987
|
+
if (biomeName === "noInlineStyles") return [severity, { forbid: ["style"] }];
|
|
1798
1988
|
if (biomeName === "useImportExtensions") {
|
|
1799
1989
|
if (options?.extensionMappings !== void 0 || options?.forceJsExtensions !== void 0) reporter.loss("Biome rule useImportExtensions options \"extensionMappings\" and \"forceJsExtensions\" are not supported by Oxlint import/extensions; the rule requires an extension without rewriting it.");
|
|
1800
1990
|
return [severity, "ignorePackages"];
|
|
@@ -1805,6 +1995,11 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
|
|
|
1805
1995
|
"ts-nocheck": false,
|
|
1806
1996
|
"ts-check": false
|
|
1807
1997
|
}];
|
|
1998
|
+
if (biomeName === "useReactCompiler") {
|
|
1999
|
+
if (options?.compilationMode === "annotation") reporter.loss("Biome rule useReactCompiler option \"compilationMode\": \"annotation\" is not supported by Oxlint, which analyses every component and hook rather than only the functions carrying a \"use memo\" directive.");
|
|
2000
|
+
else if (options?.compilationMode === "all") reporter.loss("Biome rule useReactCompiler option \"compilationMode\": \"all\" is not supported by Oxlint, which analyses components and hooks only; React Compiler diagnostics in other functions are no longer reported.");
|
|
2001
|
+
return severity;
|
|
2002
|
+
}
|
|
1808
2003
|
if (!options) return severity;
|
|
1809
2004
|
if (biomeName === "noAmbiguousAnchorText") {
|
|
1810
2005
|
const { words } = options;
|
|
@@ -1870,6 +2065,17 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
|
|
|
1870
2065
|
if (Array.isArray(options.allowedNames) && options.allowedNames.every((value) => typeof value === "string")) oxlintOptions.allowedNames = options.allowedNames;
|
|
1871
2066
|
return Object.keys(oxlintOptions).length > 0 ? [severity, oxlintOptions] : severity;
|
|
1872
2067
|
}
|
|
2068
|
+
if (biomeName === "useConsistentFunctionStyle") {
|
|
2069
|
+
if (options.style !== "declaration") return severity;
|
|
2070
|
+
return [
|
|
2071
|
+
severity,
|
|
2072
|
+
"declaration",
|
|
2073
|
+
{
|
|
2074
|
+
allowArrowFunctions: options.allowArrowFunctions === true,
|
|
2075
|
+
allowTypeAnnotation: true
|
|
2076
|
+
}
|
|
2077
|
+
];
|
|
2078
|
+
}
|
|
1873
2079
|
if (biomeName === "useConsistentMethodSignatures") {
|
|
1874
2080
|
const { style } = options;
|
|
1875
2081
|
return style === "method" || style === "property" ? [severity, style] : severity;
|
|
@@ -1895,6 +2101,12 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
|
|
|
1895
2101
|
const namedComponents = options.namedComponents === "functionDeclaration" ? "function-declaration" : options.namedComponents === "functionExpression" ? "function-expression" : options.namedComponents === "arrowFunction" ? "arrow-function" : null;
|
|
1896
2102
|
return namedComponents ? [severity, { namedComponents }] : severity;
|
|
1897
2103
|
}
|
|
2104
|
+
if (biomeName === "useValidTestTitle") {
|
|
2105
|
+
const { disallowedWords } = options;
|
|
2106
|
+
if (!Array.isArray(disallowedWords) || disallowedWords.length === 0 || !disallowedWords.every((value) => typeof value === "string")) return severity;
|
|
2107
|
+
reporter.loss("Biome rule useValidTestTitle option \"disallowedWords\" was migrated, but Oxlint valid-title stops reporting titles with leading or trailing whitespace once disallowedWords is set.");
|
|
2108
|
+
return [severity, { disallowedWords }];
|
|
2109
|
+
}
|
|
1898
2110
|
if (biomeName === "useThisInClassMethods") {
|
|
1899
2111
|
const oxlintOptions = {};
|
|
1900
2112
|
if (options.ignoreClassesWithImplements === "all" || options.ignoreClassesWithImplements === "public-fields") oxlintOptions.ignoreClassesWithImplements = options.ignoreClassesWithImplements;
|
|
@@ -1934,7 +2146,13 @@ function mapBiomeRuleSeverity(severity, reporter, biomeRuleName) {
|
|
|
1934
2146
|
warnUnsupportedSeverityOnce(rawSeverity, reporter, biomeRuleName);
|
|
1935
2147
|
return "warn";
|
|
1936
2148
|
}
|
|
1937
|
-
function mapBiomeRuleToOxlintRules(biomeName, reporter) {
|
|
2149
|
+
function mapBiomeRuleToOxlintRules(biomeName, reporter, typeAware) {
|
|
2150
|
+
const fallback = typeAware ? void 0 : TYPE_AWARE_RULE_FALLBACKS[biomeName];
|
|
2151
|
+
if (fallback) {
|
|
2152
|
+
const fallbackNote = TYPE_AWARE_FALLBACK_NOTES[biomeName];
|
|
2153
|
+
if (fallbackNote) warnPartialMappingOnce(biomeName, fallbackNote, reporter);
|
|
2154
|
+
return [normalizeOxlintRuleName(fallback)];
|
|
2155
|
+
}
|
|
1938
2156
|
const mapped = BIOME_TO_OXLINT_RULE_MAP[biomeName];
|
|
1939
2157
|
if (mapped) {
|
|
1940
2158
|
const partialMappingNote = PARTIAL_RULE_MAPPING_NOTES[biomeName];
|
|
@@ -1984,7 +2202,8 @@ function warnUnsupportedSeverityOnce(rawSeverity, reporter, biomeRuleName) {
|
|
|
1984
2202
|
function mapBiomeCategoryToOxlint(biomeCategory) {
|
|
1985
2203
|
return BIOME_TO_OXLINT_CATEGORY_MAP[biomeCategory] || null;
|
|
1986
2204
|
}
|
|
1987
|
-
function extractRulesFromBiomeConfig(linterRules, reporter,
|
|
2205
|
+
function extractRulesFromBiomeConfig(linterRules, reporter, options = {}) {
|
|
2206
|
+
const { applyImplicitRecommended = false, typeAware = false } = options;
|
|
1988
2207
|
const rules = {};
|
|
1989
2208
|
const categories = {};
|
|
1990
2209
|
const sourceRulesConverted = /* @__PURE__ */ new Set();
|
|
@@ -2014,7 +2233,7 @@ function extractRulesFromBiomeConfig(linterRules, reporter, applyImplicitRecomme
|
|
|
2014
2233
|
if (ruleName === "recommended" || ruleName === "all" || ruleName === "preset") continue;
|
|
2015
2234
|
if (typeof ruleSeverity === "boolean" || ruleSeverity === void 0) continue;
|
|
2016
2235
|
if (!isBiomeRuleSeverity(ruleSeverity)) continue;
|
|
2017
|
-
const oxlintRuleNames = mapBiomeRuleToOxlintRules(ruleName, reporter);
|
|
2236
|
+
const oxlintRuleNames = mapBiomeRuleToOxlintRules(ruleName, reporter, typeAware);
|
|
2018
2237
|
if (oxlintRuleNames.length > 0) {
|
|
2019
2238
|
sourceRulesConverted.add(ruleName);
|
|
2020
2239
|
const oxlintSeverity = mapBiomeRuleOptionsToOxlintSeverity(ruleName, mapBiomeRuleSeverity(ruleSeverity, reporter, ruleName), ruleSeverity, reporter);
|
|
@@ -2066,7 +2285,7 @@ function isBiomeRuleSeverity(value) {
|
|
|
2066
2285
|
}
|
|
2067
2286
|
//#endregion
|
|
2068
2287
|
//#region src/overrides-transformer.ts
|
|
2069
|
-
function transformOverridesToOxlint(biomeOverrides, reporter) {
|
|
2288
|
+
function transformOverridesToOxlint(biomeOverrides, reporter, options = {}) {
|
|
2070
2289
|
const oxlintOverrides = [];
|
|
2071
2290
|
const sourceRulesConverted = /* @__PURE__ */ new Set();
|
|
2072
2291
|
const sourceRulesSkipped = /* @__PURE__ */ new Set();
|
|
@@ -2085,7 +2304,7 @@ function transformOverridesToOxlint(biomeOverrides, reporter) {
|
|
|
2085
2304
|
const excludeFiles = [.../* @__PURE__ */ new Set([...override.ignore ?? [], ...override.exclude ?? []])];
|
|
2086
2305
|
if (excludeFiles.length > 0) oxlintOverride.excludeFiles = excludeFiles;
|
|
2087
2306
|
if (override.linter?.rules) {
|
|
2088
|
-
const { rules, categories, ...ruleStats } = extractRulesFromBiomeConfig(override.linter.rules, reporter);
|
|
2307
|
+
const { rules, categories, ...ruleStats } = extractRulesFromBiomeConfig(override.linter.rules, reporter, { typeAware: options.typeAware ?? false });
|
|
2089
2308
|
for (const ruleName of ruleStats.sourceRulesConverted) sourceRulesConverted.add(ruleName);
|
|
2090
2309
|
for (const ruleName of ruleStats.sourceRulesSkipped) sourceRulesSkipped.add(ruleName);
|
|
2091
2310
|
if (Object.keys(rules).length > 0) oxlintOverride.rules = rules;
|
|
@@ -2148,7 +2367,10 @@ function generateOxlintConfig(biomeConfig, reporter, options = {}) {
|
|
|
2148
2367
|
categories: { ...DISABLED_CATEGORIES },
|
|
2149
2368
|
sourceRulesConverted: /* @__PURE__ */ new Set(),
|
|
2150
2369
|
sourceRulesSkipped: /* @__PURE__ */ new Set()
|
|
2151
|
-
} : extractRulesFromBiomeConfig(biomeConfig.linter?.rules ?? {}, reporter,
|
|
2370
|
+
} : extractRulesFromBiomeConfig(biomeConfig.linter?.rules ?? {}, reporter, {
|
|
2371
|
+
applyImplicitRecommended: true,
|
|
2372
|
+
typeAware: options.typeAware ?? false
|
|
2373
|
+
});
|
|
2152
2374
|
if (Object.keys(categories).length > 0) oxlintConfig.categories = categories;
|
|
2153
2375
|
if (Object.keys(rules).length > 0) oxlintConfig.rules = rules;
|
|
2154
2376
|
warnAboutUnrepresentableIncludes(biomeConfig, reporter);
|
|
@@ -2920,7 +3142,7 @@ async function runMigration(options, reporter, biomeConfigPath) {
|
|
|
2920
3142
|
if (biomeConfig.overrides) {
|
|
2921
3143
|
appendUniquePatterns(oxlintConfig, "ignorePatterns", collectDisabledOxlintOverridePatterns(biomeConfig.overrides));
|
|
2922
3144
|
appendUniquePatterns(oxfmtConfig, "ignorePatterns", collectDisabledOxfmtOverridePatterns(biomeConfig.overrides));
|
|
2923
|
-
const oxlintOverrideResult = transformOverridesToOxlint(biomeConfig.overrides, reporter);
|
|
3145
|
+
const oxlintOverrideResult = transformOverridesToOxlint(biomeConfig.overrides, reporter, { typeAware: typeAwareEnabled });
|
|
2924
3146
|
if (oxlintOverrideResult.overrides.length > 0) oxlintConfig.overrides = oxlintOverrideResult.overrides;
|
|
2925
3147
|
for (const ruleName of oxlintOverrideResult.sourceRulesConverted) convertedSourceRules.add(ruleName);
|
|
2926
3148
|
for (const ruleName of oxlintOverrideResult.sourceRulesSkipped) skippedSourceRules.add(ruleName);
|
|
@@ -3147,7 +3369,7 @@ async function buildSuggestions({ biomeIgnorePatterns, detectedIntegrations, opt
|
|
|
3147
3369
|
suggestions.push(" pnpm add -D oxlint-tsgolint@latest");
|
|
3148
3370
|
suggestions.push(` ${typeAwareCommand}`);
|
|
3149
3371
|
suggestions.push("Type-aware mode uses the tsgolint backend.");
|
|
3150
|
-
suggestions.push("Type-aware linting is stable as of tsgolint v7; it
|
|
3372
|
+
suggestions.push("Type-aware linting is stable as of tsgolint v7; it implements 60 of typescript-eslint’s 62 type-aware rules, and Oxlint does not yet expose no-generated-empty-object-type, so check those three if you relied on them.");
|
|
3151
3373
|
suggestions.push("TypeScript compatibility caveat: migrate deprecated tsconfig options if needed (typescript-go / TS7+ behavior).");
|
|
3152
3374
|
suggestions.push(`Resolved type-aware profile: ${typeAwareProfile}`);
|
|
3153
3375
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "biome-to-oxc",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Migrate from Biome to Oxc ecosystem (oxlint + oxfmt + oxlint-tsgolint)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"biome",
|
|
@@ -54,24 +54,24 @@
|
|
|
54
54
|
"commander": "15.0.0",
|
|
55
55
|
"jsonc-parser": "next",
|
|
56
56
|
"picocolors": "^1.1.1",
|
|
57
|
-
"zod": "^4.
|
|
57
|
+
"zod": "^4.6.5"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@arethetypeswrong/core": "^0.18.5",
|
|
61
|
-
"@biomejs/biome": "^2.5.
|
|
62
|
-
"@types/node": "^26.
|
|
63
|
-
"@vitest/coverage-v8": "^
|
|
64
|
-
"oxfmt": "^0.
|
|
65
|
-
"oxlint": "^1.
|
|
66
|
-
"oxlint-tsgolint": "^7.0.
|
|
61
|
+
"@biomejs/biome": "^2.5.14",
|
|
62
|
+
"@types/node": "^26.6.3",
|
|
63
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
64
|
+
"oxfmt": "^0.70.0",
|
|
65
|
+
"oxlint": "^1.85.0",
|
|
66
|
+
"oxlint-tsgolint": "^7.0.2003",
|
|
67
67
|
"publint": "^0.3.24",
|
|
68
|
-
"tsdown": "
|
|
68
|
+
"tsdown": "rc",
|
|
69
69
|
"typescript": "next",
|
|
70
|
-
"vitest": "^
|
|
70
|
+
"vitest": "^5.0.2"
|
|
71
71
|
},
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=24.19.0",
|
|
74
|
-
"pnpm": ">=11.
|
|
74
|
+
"pnpm": ">=11.25.0"
|
|
75
75
|
},
|
|
76
|
-
"packageManager": "pnpm@11.
|
|
76
|
+
"packageManager": "pnpm@11.25.0"
|
|
77
77
|
}
|