biome-to-oxc 1.4.0 → 2.0.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 +32 -11
- package/dist/bin/biome-to-oxc.mjs +9 -8
- package/dist/src/index.d.mts +59 -6
- package/dist/src/index.mjs +1 -1
- package/dist/{src-B1tF4LEv.mjs → src-CYMpPpfK.mjs} +602 -149
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -28,17 +28,27 @@ Current capabilities:
|
|
|
28
28
|
✅ **Preserves Representable Setup**
|
|
29
29
|
|
|
30
30
|
- Ignore patterns, override file globs, and override exclusions
|
|
31
|
-
-
|
|
31
|
+
- Negated `includes` exceptions (`!pattern`), translated into `ignorePatterns` for both tools
|
|
32
|
+
- `.biomeignore` patterns migrated into the Oxlint **and** Oxfmt `ignorePatterns`. Biome 2.x does
|
|
33
|
+
not read `.biomeignore` itself, so this deliberately narrows scope; the migration says so
|
|
32
34
|
- Linter configuration overrides
|
|
33
35
|
- Formatter configuration overrides when they can be represented as Oxfmt file-glob overrides
|
|
34
36
|
- JavaScript globals
|
|
35
37
|
- Rule severities (`error`/`warn`/`off`; `info` and `on` are accepted with explicit lossy-mapping warnings)
|
|
36
38
|
- Explicit `printWidth` handling (no silent changes)
|
|
37
|
-
-
|
|
39
|
+
- The set of formatted files: Oxfmt-only formats (YAML, TOML, Markdown) are excluded, and
|
|
40
|
+
`sortPackageJson` is pinned off, so migrating never reformats files Biome left alone
|
|
41
|
+
|
|
42
|
+
✅ **Refuses to Lose Behaviour Silently**
|
|
43
|
+
|
|
44
|
+
Anything the generated configs provably do not reproduce is recorded as a **semantic loss** and
|
|
45
|
+
listed under `losses` in the report. Losses make the migration report `success: false` and block
|
|
46
|
+
both `--delete` and removal of the `@biomejs/biome` dependency, so a lossy conversion can never
|
|
47
|
+
leave a project without a working linter or formatter. Resolve the listed losses and re-run.
|
|
38
48
|
|
|
39
49
|
Positive-only Biome `files`/`linter`/`formatter` include selection has no direct Oxc config
|
|
40
|
-
equivalent
|
|
41
|
-
|
|
50
|
+
equivalent and is reported as a loss; pass equivalent paths to the Oxc CLI or review the generated
|
|
51
|
+
ignore configuration before removing Biome.
|
|
42
52
|
|
|
43
53
|
### Advanced Features
|
|
44
54
|
|
|
@@ -207,11 +217,14 @@ npx biome-to-oxc --report migration-report.json
|
|
|
207
217
|
# Print the migration report to stdout as JSON
|
|
208
218
|
npx biome-to-oxc --json
|
|
209
219
|
|
|
210
|
-
# Migrate with custom output directory
|
|
211
|
-
|
|
220
|
+
# Migrate with a custom output directory.
|
|
221
|
+
# Oxlint and Oxfmt resolve config globs inside the config file's own directory and reject `..`,
|
|
222
|
+
# so the output directory must be the Biome project root or an ancestor of it. A directory below
|
|
223
|
+
# the project root is rejected whenever the migration would emit any glob.
|
|
224
|
+
npx biome-to-oxc --output-dir ../shared-config
|
|
212
225
|
|
|
213
226
|
# Rewritten scripts explicitly load configs placed outside the package root
|
|
214
|
-
npx biome-to-oxc --output-dir
|
|
227
|
+
npx biome-to-oxc --output-dir ../shared-config --update-scripts
|
|
215
228
|
|
|
216
229
|
# Migrate without creating backups
|
|
217
230
|
npx biome-to-oxc --no-backup
|
|
@@ -341,16 +354,24 @@ npx oxlint --type-aware --type-check
|
|
|
341
354
|
|
|
342
355
|
## Known Limitations
|
|
343
356
|
|
|
344
|
-
- Not all Biome rules have direct Oxlint equivalents (
|
|
357
|
+
- Not all Biome rules have direct Oxlint equivalents (reported as semantic losses)
|
|
345
358
|
- Some advanced Biome features may not be supported
|
|
346
359
|
- Biome recommended/all presets and group-level severities are approximated with Oxlint categories;
|
|
347
|
-
preset membership is not identical between the tools
|
|
360
|
+
preset membership is not identical between the tools. This approximation is reported as a warning
|
|
361
|
+
rather than a loss, because it applies to essentially every migration
|
|
348
362
|
- Positive-only Biome include selection cannot be encoded directly in Oxlint/Oxfmt config files
|
|
363
|
+
- Generated config globs are resolved inside the config file's directory, so `--output-dir` must
|
|
364
|
+
point at the Biome project root or an ancestor of it
|
|
365
|
+
- `formatter.lineEnding: "auto"` has no Oxfmt equivalent (Oxfmt always writes LF)
|
|
366
|
+
- Lockfiles are not regenerated. When dependencies change, the report names the lockfile and the
|
|
367
|
+
install command needed before any frozen-lockfile install
|
|
349
368
|
- Oxlint overrides cannot carry category presets; explicit override rules are migrated and category
|
|
350
369
|
presets are reported for manual review
|
|
351
370
|
- Oxfmt is in beta; review formatting changes before replacing the existing formatter
|
|
352
|
-
-
|
|
353
|
-
|
|
371
|
+
- Overlapping `overrides` are migrated as-is. Biome and Oxfmt both merge matching overrides field
|
|
372
|
+
by field with later entries winning, so the two agree; a conformance test pins this
|
|
373
|
+
- Type-aware linting is stable as of `oxlint-tsgolint` v7 and requires TypeScript 7; 59 of the 61
|
|
374
|
+
targeted typescript-eslint rules are currently implemented
|
|
354
375
|
- CSS and JSON formatter overrides are mapped but may need manual review
|
|
355
376
|
- Prettier plugin support is not available in Oxfmt
|
|
356
377
|
- Some Biome rules still require JS plugin fallback until native rule parity is available
|
|
@@ -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-CYMpPpfK.mjs";
|
|
3
3
|
import { realpath } from "node:fs/promises";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -19,7 +19,6 @@ const MigrationOptionsSchema = z.object({
|
|
|
19
19
|
delete: z.boolean().default(false),
|
|
20
20
|
noBackup: z.boolean().default(false),
|
|
21
21
|
updateScripts: z.boolean().default(false),
|
|
22
|
-
dom: z.boolean().default(false),
|
|
23
22
|
verbose: z.boolean().default(false),
|
|
24
23
|
typeAware: z.boolean().default(false),
|
|
25
24
|
typeCheck: z.boolean().default(false),
|
|
@@ -63,14 +62,16 @@ async function runCli(argv, runtimeOptions = {}) {
|
|
|
63
62
|
}
|
|
64
63
|
return 2;
|
|
65
64
|
}
|
|
66
|
-
const
|
|
65
|
+
const migrationOptions = {
|
|
67
66
|
...validationResult.data,
|
|
68
67
|
signal: runtimeOptions.signal
|
|
69
|
-
}
|
|
68
|
+
};
|
|
69
|
+
const reporter = new DefaultReporter({
|
|
70
70
|
verbose: validationResult.data.verbose && !parsedCliOptions.json,
|
|
71
71
|
stdout,
|
|
72
72
|
stderr
|
|
73
|
-
})
|
|
73
|
+
});
|
|
74
|
+
const report = await migrate(migrationOptions, reporter);
|
|
74
75
|
if (parsedCliOptions.json) stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
75
76
|
else if (report.success) writeHumanSuccessSummary(report, validationResult.data.dryRun, stdout);
|
|
76
77
|
return report.success ? 0 : 1;
|
|
@@ -105,7 +106,7 @@ function createCommand(packageMetadata, streams) {
|
|
|
105
106
|
writeOut: (chunk) => {
|
|
106
107
|
streams.stdout.write(chunk);
|
|
107
108
|
}
|
|
108
|
-
}).option("-c, --config <path>", "Path to biome.json or biome.jsonc").option("-o, --output-dir <path>", "Output directory for generated configs").option("--dry-run", "Show what would be done without making changes").option("--delete", "Delete legacy Biome files after migration (biome.json/biome.jsonc and .biomeignore)").option("--no-backup", "Skip backup of existing config files").option("--update-scripts", "Update package.json scripts to use oxlint/oxfmt").
|
|
109
|
+
}).option("-c, --config <path>", "Path to biome.json or biome.jsonc").option("-o, --output-dir <path>", "Output directory for generated configs").option("--dry-run", "Show what would be done without making changes").option("--delete", "Delete legacy Biome files after migration (biome.json/biome.jsonc and .biomeignore)").option("--no-backup", "Skip backup of existing config files").option("--update-scripts", "Update package.json scripts to use oxlint/oxfmt").option("--type-aware", "Include type-aware linting guidance and dependencies").option("--type-check", "Enable strict typed linting mode (implies --type-aware)").addOption(new Option("--type-aware-profile <profile>", "Type-aware profile: standard (--type-aware) or strict (--type-aware --type-check)").choices(["standard", "strict"]).default("standard")).addOption(new Option("--fix-strategy <strategy>", "Fix mode for rewritten scripts: safe | suggestions | dangerous").choices([
|
|
109
110
|
"safe",
|
|
110
111
|
"suggestions",
|
|
111
112
|
"dangerous"
|
|
@@ -113,7 +114,8 @@ function createCommand(packageMetadata, streams) {
|
|
|
113
114
|
return command;
|
|
114
115
|
}
|
|
115
116
|
async function loadCliPackageMetadata() {
|
|
116
|
-
const
|
|
117
|
+
const packageDirectory = dirname(fileURLToPath(import.meta.url));
|
|
118
|
+
const packageManifestPath = await findClosestPackageJson(packageDirectory);
|
|
117
119
|
if (!packageManifestPath) throw new Error("Unable to locate the CLI package manifest.");
|
|
118
120
|
return readJsonFile(packageManifestPath, CliPackageMetadataSchema, `CLI package manifest at ${packageManifestPath}`);
|
|
119
121
|
}
|
|
@@ -128,7 +130,6 @@ function normalizeMigrationOptions(options) {
|
|
|
128
130
|
delete: options.delete,
|
|
129
131
|
noBackup: options.backup === false,
|
|
130
132
|
updateScripts: options.updateScripts,
|
|
131
|
-
dom: options.dom,
|
|
132
133
|
verbose: options.verbose,
|
|
133
134
|
typeAware,
|
|
134
135
|
typeCheck,
|
package/dist/src/index.d.mts
CHANGED
|
@@ -16,6 +16,8 @@ interface BiomeConfig {
|
|
|
16
16
|
interface BiomeFilesConfig {
|
|
17
17
|
include?: string[];
|
|
18
18
|
includes?: string[];
|
|
19
|
+
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
20
|
+
exclude?: string[];
|
|
19
21
|
ignore?: string[];
|
|
20
22
|
ignoreUnknown?: boolean;
|
|
21
23
|
maxSize?: number;
|
|
@@ -31,6 +33,8 @@ interface BiomeLinterConfig {
|
|
|
31
33
|
enabled?: boolean;
|
|
32
34
|
include?: string[];
|
|
33
35
|
includes?: string[];
|
|
36
|
+
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
37
|
+
exclude?: string[];
|
|
34
38
|
ignore?: string[];
|
|
35
39
|
rules?: BiomeLinterRules;
|
|
36
40
|
}
|
|
@@ -55,11 +59,13 @@ interface BiomeFormatterConfig {
|
|
|
55
59
|
enabled?: boolean;
|
|
56
60
|
include?: string[];
|
|
57
61
|
includes?: string[];
|
|
62
|
+
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
63
|
+
exclude?: string[];
|
|
58
64
|
ignore?: string[];
|
|
59
65
|
formatWithErrors?: boolean;
|
|
60
66
|
indentStyle?: 'tab' | 'space';
|
|
61
67
|
indentWidth?: number;
|
|
62
|
-
lineEnding?: 'lf' | 'crlf' | 'cr';
|
|
68
|
+
lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
|
|
63
69
|
lineWidth?: number;
|
|
64
70
|
attributePosition?: 'auto' | 'multiline';
|
|
65
71
|
bracketSpacing?: boolean;
|
|
@@ -82,7 +88,7 @@ interface BiomeJavaScriptConfig {
|
|
|
82
88
|
bracketSpacing?: boolean;
|
|
83
89
|
indentStyle?: 'tab' | 'space';
|
|
84
90
|
indentWidth?: number;
|
|
85
|
-
lineEnding?: 'lf' | 'crlf' | 'cr';
|
|
91
|
+
lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
|
|
86
92
|
lineWidth?: number;
|
|
87
93
|
[key: string]: unknown;
|
|
88
94
|
};
|
|
@@ -101,7 +107,7 @@ interface BiomeJsonConfig {
|
|
|
101
107
|
enabled?: boolean;
|
|
102
108
|
indentStyle?: 'tab' | 'space';
|
|
103
109
|
indentWidth?: number;
|
|
104
|
-
lineEnding?: 'lf' | 'crlf' | 'cr';
|
|
110
|
+
lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
|
|
105
111
|
lineWidth?: number;
|
|
106
112
|
trailingCommas?: 'none' | 'all';
|
|
107
113
|
};
|
|
@@ -117,7 +123,7 @@ interface BiomeCssConfig {
|
|
|
117
123
|
enabled?: boolean;
|
|
118
124
|
indentStyle?: 'tab' | 'space';
|
|
119
125
|
indentWidth?: number;
|
|
120
|
-
lineEnding?: 'lf' | 'crlf' | 'cr';
|
|
126
|
+
lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
|
|
121
127
|
lineWidth?: number;
|
|
122
128
|
quoteStyle?: 'single' | 'double';
|
|
123
129
|
};
|
|
@@ -128,6 +134,8 @@ interface BiomeCssConfig {
|
|
|
128
134
|
interface BiomeOverride {
|
|
129
135
|
include?: string[];
|
|
130
136
|
includes?: string[];
|
|
137
|
+
/** Negated `includes` entries (`!pattern`), split out during normalization. */
|
|
138
|
+
exclude?: string[];
|
|
131
139
|
ignore?: string[];
|
|
132
140
|
linter?: BiomeLinterConfig;
|
|
133
141
|
formatter?: BiomeFormatterConfig;
|
|
@@ -298,7 +306,6 @@ interface MigrationOptions {
|
|
|
298
306
|
delete?: boolean;
|
|
299
307
|
noBackup?: boolean;
|
|
300
308
|
updateScripts?: boolean;
|
|
301
|
-
dom?: boolean;
|
|
302
309
|
verbose?: boolean;
|
|
303
310
|
typeAware?: boolean;
|
|
304
311
|
typeCheck?: boolean;
|
|
@@ -338,21 +345,45 @@ interface PackageUpdateSummary {
|
|
|
338
345
|
dependenciesRemoved: PackageDependencyRemoval[];
|
|
339
346
|
devDependencies: PackageDevDependencyChange[];
|
|
340
347
|
changed: boolean;
|
|
348
|
+
/** Lockfile that dependency changes invalidated, when one was detected. */
|
|
349
|
+
lockfile?: LockfileStatus;
|
|
350
|
+
}
|
|
351
|
+
interface LockfileStatus {
|
|
352
|
+
path: string;
|
|
353
|
+
/** Command that regenerates the lockfile from the updated manifest. */
|
|
354
|
+
installCommand: string;
|
|
355
|
+
/** True when this run changed dependencies, so the lockfile no longer matches. */
|
|
356
|
+
stale: boolean;
|
|
341
357
|
}
|
|
342
358
|
interface MigrationReport {
|
|
359
|
+
/**
|
|
360
|
+
* True when the migration wrote every requested change without errors AND the
|
|
361
|
+
* generated configuration is a complete replacement for the Biome setup.
|
|
362
|
+
* A migration with semantic losses reports `success: false` because the project
|
|
363
|
+
* still depends on behaviour that Oxc does not reproduce.
|
|
364
|
+
*/
|
|
343
365
|
success: boolean;
|
|
344
366
|
warnings: string[];
|
|
345
367
|
errors: string[];
|
|
368
|
+
/** Source behaviour the generated target configs provably do not reproduce. */
|
|
369
|
+
losses: string[];
|
|
346
370
|
suggestions: string[];
|
|
347
371
|
packageJson?: PackageUpdateSummary;
|
|
348
372
|
summary: {
|
|
349
373
|
biomeConfigPath: string;
|
|
350
374
|
oxlintConfigPath: string;
|
|
351
375
|
oxfmtConfigPath: string;
|
|
376
|
+
/** Distinct Biome source rules that produced at least one Oxlint rule. */
|
|
352
377
|
rulesConverted: number;
|
|
378
|
+
/** Distinct Biome source rules with no Oxlint equivalent. */
|
|
353
379
|
rulesSkipped: number;
|
|
380
|
+
/** Oxlint rule entries emitted (one source rule can emit several). */
|
|
381
|
+
oxlintRulesEmitted: number;
|
|
354
382
|
overridesConverted: number;
|
|
383
|
+
/** Oxfmt overrides derived from Biome `overrides` entries. */
|
|
355
384
|
formatterOverridesConverted: number;
|
|
385
|
+
/** Oxfmt overrides synthesized to carry per-language Biome formatter settings. */
|
|
386
|
+
formatterLanguageOverrides: number;
|
|
356
387
|
};
|
|
357
388
|
detectedIntegrations?: {
|
|
358
389
|
turborepo?: boolean;
|
|
@@ -360,16 +391,38 @@ interface MigrationReport {
|
|
|
360
391
|
prettier?: boolean;
|
|
361
392
|
typescript?: boolean;
|
|
362
393
|
};
|
|
394
|
+
cleanup?: CleanupOutcome;
|
|
395
|
+
}
|
|
396
|
+
interface CleanupOutcome {
|
|
397
|
+
/** Whether `--delete` was requested. */
|
|
398
|
+
requested: boolean;
|
|
399
|
+
/** Whether legacy Biome files were (or in dry-run, would be) removed. */
|
|
400
|
+
performed: boolean;
|
|
401
|
+
/** Why cleanup was withheld, when it was requested but not performed. */
|
|
402
|
+
blockedReason?: string;
|
|
403
|
+
/** Legacy Biome files removed, or that would be removed in dry-run. */
|
|
404
|
+
files: string[];
|
|
363
405
|
}
|
|
364
406
|
interface Reporter {
|
|
365
407
|
warn(message: string): void;
|
|
366
408
|
error(message: string): void;
|
|
367
409
|
info(message: string): void;
|
|
410
|
+
/**
|
|
411
|
+
* Records a semantic-loss diagnostic: source behaviour that the generated target
|
|
412
|
+
* configuration provably does not reproduce.
|
|
413
|
+
*
|
|
414
|
+
* Unlike {@link Reporter.warn}, a loss blocks destructive cleanup (`--delete`) and
|
|
415
|
+
* removal of the Biome dependency, because the migrated project is not yet a
|
|
416
|
+
* complete replacement for the Biome setup. Losses are also recorded as warnings so
|
|
417
|
+
* that existing warning consumers keep seeing them.
|
|
418
|
+
*/
|
|
419
|
+
loss(message: string): void;
|
|
368
420
|
getWarnings(): string[];
|
|
369
421
|
getErrors(): string[];
|
|
422
|
+
getLosses(): string[];
|
|
370
423
|
}
|
|
371
424
|
//#endregion
|
|
372
425
|
//#region src/index.d.ts
|
|
373
426
|
declare function migrate(options?: MigrationOptions, reporter?: Reporter): Promise<MigrationReport>;
|
|
374
427
|
//#endregion
|
|
375
|
-
export { BiomeConfig, BiomeCssConfig, BiomeFilesConfig, BiomeFormatterConfig, BiomeJavaScriptConfig, BiomeJsonConfig, BiomeLinterConfig, BiomeLinterRules, BiomeOverride, BiomeRuleGroup, BiomeRuleSeverity, BiomeRuleSeverityValue, BiomeVcsConfig, FixStrategy, MigrationOptions, MigrationReport, OxfmtConfig, OxfmtOverride, OxlintBuiltinPlugin, OxlintConfig, OxlintJsPlugin, OxlintOverride, OxlintRuleSeverity, OxlintSettings, PackageDependencyRemoval, PackageDevDependencyChange, PackageScriptUpdate, PackageUpdateSummary, Reporter, TypeAwareProfile, migrate };
|
|
428
|
+
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 };
|
package/dist/src/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as migrate } from "../src-
|
|
1
|
+
import { t as migrate } from "../src-CYMpPpfK.mjs";
|
|
2
2
|
export { migrate };
|