biome-to-oxc 0.5.2 → 0.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 -5
- package/dist/bin/biome-to-oxc.mjs +27 -44
- package/dist/bin/biome-to-oxc.mjs.map +1 -1
- package/dist/src/advanced-detection.d.mts.map +1 -1
- package/dist/src/advanced-detection.mjs +69 -2
- package/dist/src/advanced-detection.mjs.map +1 -0
- package/dist/src/config-loader.mjs +98 -2
- package/dist/src/config-loader.mjs.map +1 -0
- package/dist/src/eslint-detector.mjs +68 -2
- package/dist/src/eslint-detector.mjs.map +1 -0
- package/dist/src/formatter-mapper.d.mts.map +1 -1
- package/dist/src/formatter-mapper.mjs +84 -2
- package/dist/src/formatter-mapper.mjs.map +1 -0
- package/dist/src/index.d.mts +2 -2
- package/dist/src/index.d.mts.map +1 -1
- package/dist/src/index.mjs +75 -21
- package/dist/src/index.mjs.map +1 -1
- package/dist/src/js-plugin-scaffolder.d.mts +8 -0
- package/dist/src/js-plugin-scaffolder.d.mts.map +1 -0
- package/dist/src/js-plugin-scaffolder.mjs +71 -0
- package/dist/src/js-plugin-scaffolder.mjs.map +1 -0
- package/dist/src/overrides-transformer.mjs +29 -2
- package/dist/src/overrides-transformer.mjs.map +1 -0
- package/dist/src/oxfmt-overrides.mjs +70 -2
- package/dist/src/oxfmt-overrides.mjs.map +1 -0
- package/dist/src/oxlint-generator.d.mts +7 -2
- package/dist/src/oxlint-generator.d.mts.map +1 -1
- package/dist/src/oxlint-generator.mjs +124 -2
- package/dist/src/oxlint-generator.mjs.map +1 -0
- package/dist/src/package-updater.d.mts +4 -1
- package/dist/src/package-updater.d.mts.map +1 -1
- package/dist/src/package-updater.mjs +205 -2
- package/dist/src/package-updater.mjs.map +1 -0
- package/dist/src/prettier-detector.mjs +49 -2
- package/dist/src/prettier-detector.mjs.map +1 -0
- package/dist/src/report-writer.mjs +21 -2
- package/dist/src/report-writer.mjs.map +1 -0
- package/dist/src/reporter.d.mts.map +1 -1
- package/dist/src/reporter.mjs +25 -2
- package/dist/src/reporter.mjs.map +1 -0
- package/dist/src/rule-mapper.d.mts.map +1 -1
- package/dist/src/rule-mapper.mjs +154 -2
- package/dist/src/rule-mapper.mjs.map +1 -0
- package/dist/src/schema-normalizer.d.mts.map +1 -1
- package/dist/src/schema-normalizer.mjs +48 -2
- package/dist/src/schema-normalizer.mjs.map +1 -0
- package/dist/src/turbo-updater.mjs +48 -2
- package/dist/src/turbo-updater.mjs.map +1 -0
- package/dist/src/types.d.mts +77 -5
- package/dist/src/types.d.mts.map +1 -1
- package/package.json +10 -10
- package/dist/advanced-detection-Bekr41wZ.mjs +0 -71
- package/dist/advanced-detection-Bekr41wZ.mjs.map +0 -1
- package/dist/config-loader-Do081Mw1.mjs +0 -99
- package/dist/config-loader-Do081Mw1.mjs.map +0 -1
- package/dist/eslint-detector-CiNY3vrg.mjs +0 -69
- package/dist/eslint-detector-CiNY3vrg.mjs.map +0 -1
- package/dist/formatter-mapper-BMQ4nHqQ.mjs +0 -85
- package/dist/formatter-mapper-BMQ4nHqQ.mjs.map +0 -1
- package/dist/overrides-transformer-D8lb8ijd.mjs +0 -30
- package/dist/overrides-transformer-D8lb8ijd.mjs.map +0 -1
- package/dist/oxfmt-overrides-BJtFUPt9.mjs +0 -71
- package/dist/oxfmt-overrides-BJtFUPt9.mjs.map +0 -1
- package/dist/oxlint-generator-L7ui1BHE.mjs +0 -48
- package/dist/oxlint-generator-L7ui1BHE.mjs.map +0 -1
- package/dist/package-updater-DcxTVTb3.mjs +0 -162
- package/dist/package-updater-DcxTVTb3.mjs.map +0 -1
- package/dist/prettier-detector-aLE389_O.mjs +0 -50
- package/dist/prettier-detector-aLE389_O.mjs.map +0 -1
- package/dist/report-writer-BU-Lyzye.mjs +0 -22
- package/dist/report-writer-BU-Lyzye.mjs.map +0 -1
- package/dist/reporter-B1SJ0-DK.mjs +0 -26
- package/dist/reporter-B1SJ0-DK.mjs.map +0 -1
- package/dist/rule-mapper-CvipfcEK.mjs +0 -149
- package/dist/rule-mapper-CvipfcEK.mjs.map +0 -1
- package/dist/schema-normalizer-CaJrUFMX.mjs +0 -49
- package/dist/schema-normalizer-CaJrUFMX.mjs.map +0 -1
- package/dist/turbo-updater-3b20rC36.mjs +0 -49
- package/dist/turbo-updater-3b20rC36.mjs.map +0 -1
- package/dist/types-DCPpFKUB.mjs +0 -1
package/README.md
CHANGED
|
@@ -11,6 +11,20 @@ The Oxc project provides blazing-fast alternatives to existing JavaScript toolin
|
|
|
11
11
|
|
|
12
12
|
Both tools are production-ready and actively developed, even in alpha/beta stages.
|
|
13
13
|
|
|
14
|
+
## Current State (v0.6.0)
|
|
15
|
+
|
|
16
|
+
`biome-to-oxc` is currently in an advanced migration state focused on practical, production migration paths:
|
|
17
|
+
|
|
18
|
+
- ✅ Rich CLI controls for migration behavior (`type-aware-profile`, `fix-strategy`, `import-graph`, `js-plugins`)
|
|
19
|
+
- ✅ Rule + category migration with unsupported-rule reporting
|
|
20
|
+
- ✅ Oxfmt generation aligned with current sort key names (`sortImports`, `sortPackageJson`, `sortTailwindcss`)
|
|
21
|
+
- ✅ Strategy-aware script rewrites for safe/suggestions/dangerous fix workflows
|
|
22
|
+
- ✅ Monorepo-aware guidance and ignore migration recommendations
|
|
23
|
+
- ✅ Typed linting guidance with alpha stability + TypeScript compatibility caveats
|
|
24
|
+
- ✅ Reference datasets committed under `docs/*.tsv` for rules/options coverage analysis
|
|
25
|
+
|
|
26
|
+
If a Biome rule has no native Oxlint equivalent yet, migration reports now surface it clearly and can scaffold `jsPlugins` for compatibility workflows.
|
|
27
|
+
|
|
14
28
|
## Features
|
|
15
29
|
|
|
16
30
|
### Core Migration
|
|
@@ -43,7 +57,10 @@ Both tools are production-ready and actively developed, even in alpha/beta stage
|
|
|
43
57
|
|
|
44
58
|
- Detects TypeScript usage
|
|
45
59
|
- Provides `oxlint-tsgolint` installation guidance
|
|
46
|
-
-
|
|
60
|
+
- Supports profile-based guidance:
|
|
61
|
+
- `standard`: `oxlint --type-aware`
|
|
62
|
+
- `strict`: `oxlint --type-aware --type-check`
|
|
63
|
+
- Surfaces alpha stability + TS compatibility caveats
|
|
47
64
|
|
|
48
65
|
✅ **Turborepo Integration** (`--turborepo`)
|
|
49
66
|
|
|
@@ -91,11 +108,32 @@ Both tools are production-ready and actively developed, even in alpha/beta stage
|
|
|
91
108
|
|
|
92
109
|
✅ **Experimental Features** (Alpha/Beta)
|
|
93
110
|
|
|
94
|
-
- **Import Sorting**: `
|
|
95
|
-
- **Package.json Sorting**: `
|
|
96
|
-
- **Tailwind Integration**: `
|
|
111
|
+
- **Import Sorting**: `sortImports` with configurable order
|
|
112
|
+
- **Package.json Sorting**: `sortPackageJson` for consistent deps
|
|
113
|
+
- **Tailwind Integration**: `sortTailwindcss` for class optimization
|
|
97
114
|
- **Forward Compatibility**: Passes through unknown options for future features
|
|
98
115
|
|
|
116
|
+
✅ **Oxlint Config Enhancements**
|
|
117
|
+
|
|
118
|
+
- Preserves Oxlint defaults by omitting `plugins` when explicit plugin config is not required
|
|
119
|
+
- Emits plugin `settings` scaffolds for detected ecosystems (`react`, `jsx-a11y`, `next`, `vitest`, `jsdoc`)
|
|
120
|
+
- Optional import graph baseline (`--import-graph`) with configurable `import/no-cycle` depth
|
|
121
|
+
- Optional JS plugin scaffolding (`--js-plugins` + repeatable `--js-plugin`)
|
|
122
|
+
|
|
123
|
+
✅ **Coverage & Analysis Artifacts**
|
|
124
|
+
|
|
125
|
+
- `docs/oxlint-rules.tsv`: Oxlint rule inventory used for mapping validation
|
|
126
|
+
- `docs/oxfmt-rules.tsv`: Oxfmt configuration surface inventory
|
|
127
|
+
- `docs/tsgolint-rules.tsv`: typed linting implementation matrix
|
|
128
|
+
- `docs/oxlint-vs-tsgolint.tsv`: typed rule availability cross-reference
|
|
129
|
+
|
|
130
|
+
✅ **Script Rewrite Strategy Controls**
|
|
131
|
+
|
|
132
|
+
- Explicit fix strategy levels for rewritten scripts:
|
|
133
|
+
- `safe` → `--fix`
|
|
134
|
+
- `suggestions` → `--fix --fix-suggestions`
|
|
135
|
+
- `dangerous` → `--fix --fix-suggestions --fix-dangerously`
|
|
136
|
+
|
|
99
137
|
## Installation
|
|
100
138
|
|
|
101
139
|
```bash
|
|
@@ -131,6 +169,12 @@ Options:
|
|
|
131
169
|
--no-backup Skip backup of existing config files
|
|
132
170
|
--update-scripts Update package.json scripts to use oxlint/oxfmt
|
|
133
171
|
--type-aware Include type-aware linting guidance and dependencies
|
|
172
|
+
--type-aware-profile Type-aware profile: standard | strict
|
|
173
|
+
--fix-strategy Fix mode for rewritten scripts: safe | suggestions | dangerous
|
|
174
|
+
--js-plugins Emit jsPlugins scaffold for unsupported mappings
|
|
175
|
+
--js-plugin JS plugin specifier to scaffold (repeatable)
|
|
176
|
+
--import-graph Add import plugin + import/no-cycle baseline recipe
|
|
177
|
+
--import-cycle-max-depth Max depth for import/no-cycle (default: 3)
|
|
134
178
|
--turborepo Detect and update turbo.json for Turborepo integration
|
|
135
179
|
--eslint-bridge Provide ESLint bridge suggestions for running alongside ESLint
|
|
136
180
|
--prettier Detect Prettier config and provide migration suggestions
|
|
@@ -150,7 +194,7 @@ npx biome-to-oxc --dry-run --verbose
|
|
|
150
194
|
npx biome-to-oxc --update-scripts
|
|
151
195
|
|
|
152
196
|
# Full migration with all integrations
|
|
153
|
-
npx biome-to-oxc --update-scripts --type-aware --turborepo --eslint-bridge --prettier
|
|
197
|
+
npx biome-to-oxc --update-scripts --type-aware --type-aware-profile strict --fix-strategy suggestions --import-graph --js-plugins --js-plugin eslint-plugin-playwright --turborepo --eslint-bridge --prettier
|
|
154
198
|
|
|
155
199
|
# Generate detailed JSON report
|
|
156
200
|
npx biome-to-oxc --report migration-report.json
|
|
@@ -166,6 +210,12 @@ npx biome-to-oxc --turborepo --update-scripts
|
|
|
166
210
|
|
|
167
211
|
# TypeScript project with type-aware linting
|
|
168
212
|
npx biome-to-oxc --type-aware --verbose
|
|
213
|
+
|
|
214
|
+
# Strict type-aware profile + dangerous script fix strategy
|
|
215
|
+
npx biome-to-oxc --type-aware --type-aware-profile strict --update-scripts --fix-strategy dangerous
|
|
216
|
+
|
|
217
|
+
# Import graph baseline with custom cycle depth
|
|
218
|
+
npx biome-to-oxc --import-graph --import-cycle-max-depth 5
|
|
169
219
|
```
|
|
170
220
|
|
|
171
221
|
## Migration Process
|
|
@@ -257,6 +307,9 @@ npx biome-to-oxc --type-aware --verbose
|
|
|
257
307
|
|
|
258
308
|
# Run with type-aware rules
|
|
259
309
|
npx oxlint --type-aware
|
|
310
|
+
|
|
311
|
+
# Strict profile (include TS compiler diagnostics)
|
|
312
|
+
npx oxlint --type-aware --type-check
|
|
260
313
|
```
|
|
261
314
|
|
|
262
315
|
## Known Limitations
|
|
@@ -267,6 +320,7 @@ npx biome-to-oxc --type-aware --verbose
|
|
|
267
320
|
- Type-aware linting requires `oxlint-tsgolint` package (alpha)
|
|
268
321
|
- CSS and JSON formatter overrides are mapped but may need manual review
|
|
269
322
|
- Prettier plugin support is not available in Oxfmt
|
|
323
|
+
- Some Biome rules still require JS plugin fallback until native rule parity is available
|
|
270
324
|
|
|
271
325
|
## Migration Report
|
|
272
326
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "../reporter-B1SJ0-DK.mjs";
|
|
3
|
-
import "../config-loader-Do081Mw1.mjs";
|
|
4
2
|
import { migrate } from "../src/index.mjs";
|
|
5
3
|
import { program } from "commander";
|
|
6
4
|
import pc from "picocolors";
|
|
@@ -20,6 +18,16 @@ const MigrationOptionsSchema = z.object({
|
|
|
20
18
|
updateScripts: z.boolean().default(false),
|
|
21
19
|
verbose: z.boolean().default(false),
|
|
22
20
|
typeAware: z.boolean().default(false),
|
|
21
|
+
typeAwareProfile: z.enum(["standard", "strict"]).default("standard"),
|
|
22
|
+
fixStrategy: z.enum([
|
|
23
|
+
"safe",
|
|
24
|
+
"suggestions",
|
|
25
|
+
"dangerous"
|
|
26
|
+
]).default("safe"),
|
|
27
|
+
jsPlugins: z.boolean().default(false),
|
|
28
|
+
jsPlugin: z.array(z.string()).optional(),
|
|
29
|
+
importGraph: z.boolean().default(false),
|
|
30
|
+
importCycleMaxDepth: z.number().int().positive().default(3),
|
|
23
31
|
turborepo: z.boolean().default(false),
|
|
24
32
|
eslintBridge: z.boolean().default(false),
|
|
25
33
|
prettier: z.boolean().default(false),
|
|
@@ -42,7 +50,10 @@ process.on("uncaughtException", (error) => {
|
|
|
42
50
|
process.exit(1);
|
|
43
51
|
});
|
|
44
52
|
async function main() {
|
|
45
|
-
program.name("biome-to-oxc").version(packageJson.version).description(packageJson.description).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("--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("--
|
|
53
|
+
program.name("biome-to-oxc").version(packageJson.version).description(packageJson.description).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("--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-aware-profile <profile>", "Type-aware profile: standard (--type-aware) or strict (--type-aware --type-check)", "standard").option("--fix-strategy <strategy>", "Fix mode for rewritten scripts: safe | suggestions | dangerous", "safe").option("--js-plugins", "Emit jsPlugins scaffold when unsupported rules are detected").option("--js-plugin <specifier>", "JS plugin specifier to scaffold (repeatable). Example: eslint-plugin-playwright", (value, previous = []) => {
|
|
54
|
+
previous.push(value);
|
|
55
|
+
return previous;
|
|
56
|
+
}, []).option("--import-graph", "Add import graph baseline (import/no-cycle) to generated Oxlint config").option("--import-cycle-max-depth <depth>", "Max depth for import/no-cycle when --import-graph is enabled", (value) => Number.parseInt(value, 10), 3).option("--turborepo", "Detect and update turbo.json for Turborepo integration").option("--eslint-bridge", "Provide ESLint bridge suggestions for running alongside ESLint").option("--prettier", "Detect Prettier config and provide migration suggestions").option("--report <path>", "Write detailed JSON migration report to specified path").option("-v, --verbose", "Show detailed migration information").action(async (rawOptions) => {
|
|
46
57
|
try {
|
|
47
58
|
const opts = {
|
|
48
59
|
...rawOptions,
|
|
@@ -60,58 +71,30 @@ async function main() {
|
|
|
60
71
|
const migrationOptions = validationResult.data;
|
|
61
72
|
const report = await migrate(migrationOptions);
|
|
62
73
|
if (report.success) {
|
|
63
|
-
|
|
64
|
-
const heading = dryRun ? "✔ Migration preview" : "✔ Migration complete";
|
|
65
|
-
console.log(pc.green(`\n${heading}`));
|
|
66
|
-
console.log(pc.cyan(`\nConfiguration output${dryRun ? " (planned)" : ""}:`));
|
|
67
|
-
console.log(` ${dryRun ? "Would create" : "Created"} Oxlint config: ${report.summary.oxlintConfigPath}`);
|
|
68
|
-
console.log(` ${dryRun ? "Would create" : "Created"} Oxfmt config: ${report.summary.oxfmtConfigPath}`);
|
|
69
|
-
console.log(` Lint rules converted: ${report.summary.rulesConverted} (skipped: ${report.summary.rulesSkipped})`);
|
|
70
|
-
console.log(` Overrides converted: ${report.summary.overridesConverted} (formatter overrides: ${report.summary.formatterOverridesConverted})`);
|
|
74
|
+
migrationOptions.dryRun;
|
|
71
75
|
if (report.packageJson) {
|
|
72
76
|
const pkg = report.packageJson;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else if (!pkg.changed) console.log(" No package.json changes needed.");
|
|
76
|
-
else {
|
|
77
|
-
for (const removal of pkg.dependenciesRemoved) {
|
|
78
|
-
const versionLabel = removal.version ? ` (${removal.version})` : "";
|
|
79
|
-
console.log(` ${pkg.dryRun ? "Would remove" : "Removed"} ${removal.name} from ${removal.dependencyType}${versionLabel}`);
|
|
80
|
-
}
|
|
77
|
+
if (!pkg.found) {} else if (!pkg.changed) {} else {
|
|
78
|
+
for (const removal of pkg.dependenciesRemoved);
|
|
81
79
|
const devDepChanges = pkg.devDependencies.filter((change) => change.action !== "already-present");
|
|
82
|
-
for (const change of devDepChanges) if (change.action === "added")
|
|
83
|
-
|
|
84
|
-
const fromVersion = change.from ?? "unknown";
|
|
85
|
-
console.log(` ${pkg.dryRun ? "Would update" : "Updated"} devDependency ${change.name} ${fromVersion} → ${change.to}`);
|
|
86
|
-
}
|
|
87
|
-
for (const scriptChange of pkg.scriptsUpdated) console.log(` ${pkg.dryRun ? "Would update" : "Updated"} script "${scriptChange.name}": ${scriptChange.before} → ${scriptChange.after}`);
|
|
80
|
+
for (const change of devDepChanges) if (change.action === "added") {}
|
|
81
|
+
for (const _scriptChange of pkg.scriptsUpdated);
|
|
88
82
|
}
|
|
89
|
-
if (pkg.found && migrationOptions.updateScripts && pkg.scriptsUpdated.length === 0)
|
|
83
|
+
if (pkg.found && migrationOptions.updateScripts && pkg.scriptsUpdated.length === 0) {}
|
|
90
84
|
}
|
|
91
85
|
if (report.detectedIntegrations) {
|
|
92
|
-
|
|
93
|
-
if (integrations.length > 0) {
|
|
94
|
-
console.log(pc.cyan("\nDetected integrations:"));
|
|
95
|
-
console.log(` ${integrations.join(", ")}`);
|
|
96
|
-
}
|
|
86
|
+
if (Object.entries(report.detectedIntegrations).filter(([_, detected]) => detected).map(([name]) => name).length > 0) {}
|
|
97
87
|
}
|
|
98
88
|
if (report.warnings.length > 0) {
|
|
99
|
-
|
|
100
|
-
if (migrationOptions.verbose) report.warnings.forEach((w) => {
|
|
101
|
-
console.log(` - ${w}`);
|
|
102
|
-
});
|
|
103
|
-
else console.log(" Run with --verbose to see all warning details.");
|
|
89
|
+
if (migrationOptions.verbose) report.warnings.forEach((w) => {});
|
|
104
90
|
}
|
|
105
91
|
if (report.suggestions.length > 0) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
if (migrationOptions.verbose) report.suggestions.forEach((s) => {});
|
|
93
|
+
}
|
|
94
|
+
if (!migrationOptions.updateScripts) {}
|
|
95
|
+
if (migrationOptions.report) {
|
|
96
|
+
if (migrationOptions.dryRun) {}
|
|
111
97
|
}
|
|
112
|
-
if (!migrationOptions.updateScripts) console.log(pc.dim("\nNote: Script updates were skipped (use --update-scripts to enable)."));
|
|
113
|
-
if (migrationOptions.report) if (migrationOptions.dryRun) console.log(pc.dim(`\nReport output skipped in dry-run mode: ${migrationOptions.report}`));
|
|
114
|
-
else console.log(pc.green(`\nDetailed report written to: ${migrationOptions.report}`));
|
|
115
98
|
process.exit(0);
|
|
116
99
|
} else {
|
|
117
100
|
console.error(pc.red("\n✖ Migration failed."));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biome-to-oxc.mjs","names":[],"sources":["../../bin/biome-to-oxc.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport { migrate } from '../src/index.js';\nimport type { MigrationOptions } from '../src/types.js';\n\nconst packageJson = {\n name: 'biome-to-oxc',\n version: '0.1.0',\n description: 'Migrate from Biome to Oxc ecosystem (oxlint + oxfmt)',\n};\n\n// Zod schema for runtime validation\nconst MigrationOptionsSchema = z.object({\n configPath: z.string().optional(),\n outputDir: z.string().optional(),\n dryRun: z.boolean().default(false),\n noBackup: z.boolean().default(false),\n updateScripts: z.boolean().default(false),\n verbose: z.boolean().default(false),\n typeAware: z.boolean().default(false),\n turborepo: z.boolean().default(false),\n eslintBridge: z.boolean().default(false),\n prettier: z.boolean().default(false),\n report: z.string().optional(),\n});\n\n// Handle interrupt signals\nconst handleSignal = (signal: string, code: number) => {\n console.error(pc.red(`\\n✖ Received ${signal}, exiting...`));\n process.exit(code);\n};\n\nprocess.on('SIGINT', () => handleSignal('SIGINT', 130));\nprocess.on('SIGTERM', () => handleSignal('SIGTERM', 143));\n\nprocess.on('unhandledRejection', (reason) => {\n console.error(pc.red('\\n✖ Unhandled Promise Rejection:'));\n console.error(reason);\n process.exit(1);\n});\n\nprocess.on('uncaughtException', (error) => {\n console.error(pc.red('\\n✖ Uncaught Exception:'));\n console.error(error);\n process.exit(1);\n});\n\nasync function main() {\n program\n .name('biome-to-oxc')\n .version(packageJson.version)\n .description(packageJson.description)\n .option('-c, --config <path>', 'Path to biome.json or biome.jsonc')\n .option('-o, --output-dir <path>', 'Output directory for generated configs')\n .option('--dry-run', 'Show what would be done without making changes')\n .option('--no-backup', 'Skip backup of existing config files')\n .option('--update-scripts', 'Update package.json scripts to use oxlint/oxfmt')\n .option('--type-aware', 'Include type-aware linting guidance and dependencies')\n .option('--turborepo', 'Detect and update turbo.json for Turborepo integration')\n .option('--eslint-bridge', 'Provide ESLint bridge suggestions for running alongside ESLint')\n .option('--prettier', 'Detect Prettier config and provide migration suggestions')\n .option('--report <path>', 'Write detailed JSON migration report to specified path')\n .option('-v, --verbose', 'Show detailed migration information')\n .action(async (rawOptions) => {\n try {\n // Normalize options (commander transforms --no-backup to backup: false, we need to map it back)\n const opts = {\n ...rawOptions,\n noBackup: !rawOptions.backup, // map from commander's negation\n };\n // Remove the auxiliary backup prop from commander\n delete opts.backup;\n\n const validationResult = MigrationOptionsSchema.safeParse(opts);\n\n if (!validationResult.success) {\n console.error(pc.red('✖ Invalid options:'));\n validationResult.error.issues.forEach((err: z.ZodIssue) => {\n console.error(` - ${err.path.join('.')}: ${err.message}`);\n });\n process.exit(1);\n }\n\n const migrationOptions: MigrationOptions = validationResult.data;\n\n const report = await migrate(migrationOptions);\n\n if (report.success) {\n const dryRun = migrationOptions.dryRun ?? false;\n const heading = dryRun ? '✔ Migration preview' : '✔ Migration complete';\n console.log(pc.green(`\\n${heading}`));\n\n console.log(pc.cyan(`\\nConfiguration output${dryRun ? ' (planned)' : ''}:`));\n console.log(\n ` ${dryRun ? 'Would create' : 'Created'} Oxlint config: ${report.summary.oxlintConfigPath}`,\n );\n console.log(\n ` ${dryRun ? 'Would create' : 'Created'} Oxfmt config: ${report.summary.oxfmtConfigPath}`,\n );\n console.log(\n ` Lint rules converted: ${report.summary.rulesConverted} (skipped: ${report.summary.rulesSkipped})`,\n );\n console.log(\n ` Overrides converted: ${report.summary.overridesConverted} (formatter overrides: ${report.summary.formatterOverridesConverted})`,\n );\n\n if (report.packageJson) {\n const pkg = report.packageJson;\n console.log(pc.cyan(`\\npackage.json updates${pkg.dryRun ? ' (planned)' : ''}:`));\n\n if (!pkg.found) {\n console.log(' package.json not found; dependency and script updates skipped.');\n } else if (!pkg.changed) {\n console.log(' No package.json changes needed.');\n } else {\n for (const removal of pkg.dependenciesRemoved) {\n const versionLabel = removal.version ? ` (${removal.version})` : '';\n console.log(\n ` ${pkg.dryRun ? 'Would remove' : 'Removed'} ${removal.name} from ${removal.dependencyType}${versionLabel}`,\n );\n }\n\n const devDepChanges = pkg.devDependencies.filter(\n (change) => change.action !== 'already-present',\n );\n\n for (const change of devDepChanges) {\n if (change.action === 'added') {\n console.log(\n ` ${pkg.dryRun ? 'Would add' : 'Added'} devDependency ${change.name}@${change.to}`,\n );\n } else {\n const fromVersion = change.from ?? 'unknown';\n console.log(\n ` ${pkg.dryRun ? 'Would update' : 'Updated'} devDependency ${change.name} ${fromVersion} → ${change.to}`,\n );\n }\n }\n\n for (const scriptChange of pkg.scriptsUpdated) {\n console.log(\n ` ${pkg.dryRun ? 'Would update' : 'Updated'} script \"${scriptChange.name}\": ${scriptChange.before} → ${scriptChange.after}`,\n );\n }\n }\n\n if (pkg.found && migrationOptions.updateScripts && pkg.scriptsUpdated.length === 0) {\n console.log(' No Biome scripts found to update.');\n }\n }\n\n if (report.detectedIntegrations) {\n const integrations = Object.entries(report.detectedIntegrations)\n .filter(([_, detected]) => detected)\n .map(([name]) => name);\n\n if (integrations.length > 0) {\n console.log(pc.cyan('\\nDetected integrations:'));\n console.log(` ${integrations.join(', ')}`);\n }\n }\n\n if (report.warnings.length > 0) {\n console.log(pc.yellow(`\\nWarnings (${report.warnings.length}):`));\n if (migrationOptions.verbose) {\n report.warnings.forEach((w) => {\n console.log(` - ${w}`);\n });\n } else {\n console.log(' Run with --verbose to see all warning details.');\n }\n }\n\n if (report.suggestions.length > 0) {\n console.log(pc.cyan(`\\nNext steps (${report.suggestions.length}):`));\n if (migrationOptions.verbose) {\n report.suggestions.forEach((s) => {\n console.log(` - ${s}`);\n });\n } else {\n console.log(' Run with --verbose to see all suggestions.');\n }\n }\n\n if (!migrationOptions.updateScripts) {\n console.log(pc.dim('\\nNote: Script updates were skipped (use --update-scripts to enable).'));\n }\n\n if (migrationOptions.report) {\n if (migrationOptions.dryRun) {\n console.log(pc.dim(`\\nReport output skipped in dry-run mode: ${migrationOptions.report}`));\n } else {\n console.log(pc.green(`\\nDetailed report written to: ${migrationOptions.report}`));\n }\n }\n\n process.exit(0);\n } else {\n console.error(pc.red('\\n✖ Migration failed.'));\n\n if (report.errors.length > 0) {\n console.error(pc.red(`Errors (${report.errors.length}):`));\n report.errors.forEach((e) => {\n console.error(` - ${e}`);\n });\n }\n\n process.exit(1);\n }\n } catch (error) {\n console.error(pc.red('\\n✖ Unexpected error during migration:'));\n console.error(error instanceof Error ? error.stack : error);\n process.exit(1);\n }\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error) => {\n console.error(pc.red('✖ Fatal error:'), error);\n process.exit(1);\n});"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"biome-to-oxc.mjs","names":[],"sources":["../../bin/biome-to-oxc.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from 'commander';\nimport pc from 'picocolors';\nimport { z } from 'zod';\nimport { migrate } from '../src/index.js';\nimport type { MigrationOptions } from '../src/types.js';\n\nconst packageJson = {\n name: 'biome-to-oxc',\n version: '0.1.0',\n description: 'Migrate from Biome to Oxc ecosystem (oxlint + oxfmt)',\n};\n\n// Zod schema for runtime validation\nconst MigrationOptionsSchema = z.object({\n configPath: z.string().optional(),\n outputDir: z.string().optional(),\n dryRun: z.boolean().default(false),\n noBackup: z.boolean().default(false),\n updateScripts: z.boolean().default(false),\n verbose: z.boolean().default(false),\n typeAware: z.boolean().default(false),\n typeAwareProfile: z.enum(['standard', 'strict']).default('standard'),\n fixStrategy: z.enum(['safe', 'suggestions', 'dangerous']).default('safe'),\n jsPlugins: z.boolean().default(false),\n jsPlugin: z.array(z.string()).optional(),\n importGraph: z.boolean().default(false),\n importCycleMaxDepth: z.number().int().positive().default(3),\n turborepo: z.boolean().default(false),\n eslintBridge: z.boolean().default(false),\n prettier: z.boolean().default(false),\n report: z.string().optional(),\n});\n\n// Handle interrupt signals\nconst handleSignal = (signal: string, code: number) => {\n console.error(pc.red(`\\n✖ Received ${signal}, exiting...`));\n process.exit(code);\n};\n\nprocess.on('SIGINT', () => handleSignal('SIGINT', 130));\nprocess.on('SIGTERM', () => handleSignal('SIGTERM', 143));\n\nprocess.on('unhandledRejection', (reason) => {\n console.error(pc.red('\\n✖ Unhandled Promise Rejection:'));\n console.error(reason);\n process.exit(1);\n});\n\nprocess.on('uncaughtException', (error) => {\n console.error(pc.red('\\n✖ Uncaught Exception:'));\n console.error(error);\n process.exit(1);\n});\n\nasync function main() {\n program\n .name('biome-to-oxc')\n .version(packageJson.version)\n .description(packageJson.description)\n .option('-c, --config <path>', 'Path to biome.json or biome.jsonc')\n .option('-o, --output-dir <path>', 'Output directory for generated configs')\n .option('--dry-run', 'Show what would be done without making changes')\n .option('--no-backup', 'Skip backup of existing config files')\n .option('--update-scripts', 'Update package.json scripts to use oxlint/oxfmt')\n .option('--type-aware', 'Include type-aware linting guidance and dependencies')\n .option(\n '--type-aware-profile <profile>',\n 'Type-aware profile: standard (--type-aware) or strict (--type-aware --type-check)',\n 'standard',\n )\n .option(\n '--fix-strategy <strategy>',\n 'Fix mode for rewritten scripts: safe | suggestions | dangerous',\n 'safe',\n )\n .option('--js-plugins', 'Emit jsPlugins scaffold when unsupported rules are detected')\n .option(\n '--js-plugin <specifier>',\n 'JS plugin specifier to scaffold (repeatable). Example: eslint-plugin-playwright',\n (value: string, previous: string[] = []) => {\n previous.push(value);\n return previous;\n },\n [],\n )\n .option('--import-graph', 'Add import graph baseline (import/no-cycle) to generated Oxlint config')\n .option(\n '--import-cycle-max-depth <depth>',\n 'Max depth for import/no-cycle when --import-graph is enabled',\n (value: string) => Number.parseInt(value, 10),\n 3,\n )\n .option('--turborepo', 'Detect and update turbo.json for Turborepo integration')\n .option('--eslint-bridge', 'Provide ESLint bridge suggestions for running alongside ESLint')\n .option('--prettier', 'Detect Prettier config and provide migration suggestions')\n .option('--report <path>', 'Write detailed JSON migration report to specified path')\n .option('-v, --verbose', 'Show detailed migration information')\n .action(async (rawOptions) => {\n try {\n // Normalize options (commander transforms --no-backup to backup: false, we need to map it back)\n const opts = {\n ...rawOptions,\n noBackup: !rawOptions.backup, // map from commander's negation\n };\n // Remove the auxiliary backup prop from commander\n delete opts.backup;\n\n const validationResult = MigrationOptionsSchema.safeParse(opts);\n\n if (!validationResult.success) {\n console.error(pc.red('✖ Invalid options:'));\n validationResult.error.issues.forEach((err: z.ZodIssue) => {\n console.error(` - ${err.path.join('.')}: ${err.message}`);\n });\n process.exit(1);\n }\n\n const migrationOptions: MigrationOptions = validationResult.data;\n\n const report = await migrate(migrationOptions);\n\n if (report.success) {\n const dryRun = migrationOptions.dryRun ?? false;\n\n\n\n\n\n\n\n\n\n if (report.packageJson) {\n const pkg = report.packageJson;\n\n\n if (!pkg.found) {\n\n } else if (!pkg.changed) {\n\n } else {\n for (const removal of pkg.dependenciesRemoved) {\n\n\n }\n\n const devDepChanges = pkg.devDependencies.filter(\n (change) => change.action !== 'already-present',\n );\n\n for (const change of devDepChanges) {\n if (change.action === 'added') {\n\n } else {\n\n\n }\n }\n\n for (const _scriptChange of pkg.scriptsUpdated) {\n\n }\n }\n\n if (pkg.found && migrationOptions.updateScripts && pkg.scriptsUpdated.length === 0) {\n\n }\n }\n\n if (report.detectedIntegrations) {\n const integrations = Object.entries(report.detectedIntegrations)\n .filter(([_, detected]) => detected)\n .map(([name]) => name);\n\n if (integrations.length > 0) {\n\n\n }\n }\n\n if (report.warnings.length > 0) {\n\n if (migrationOptions.verbose) {\n report.warnings.forEach((w) => {\n\n });\n } else {\n\n }\n }\n\n if (report.suggestions.length > 0) {\n\n if (migrationOptions.verbose) {\n report.suggestions.forEach((s) => {\n\n });\n } else {\n\n }\n }\n\n if (!migrationOptions.updateScripts) {\n\n }\n\n if (migrationOptions.report) {\n if (migrationOptions.dryRun) {\n\n } else {\n\n }\n }\n\n process.exit(0);\n } else {\n console.error(pc.red('\\n✖ Migration failed.'));\n\n if (report.errors.length > 0) {\n console.error(pc.red(`Errors (${report.errors.length}):`));\n report.errors.forEach((e) => {\n console.error(` - ${e}`);\n });\n }\n\n process.exit(1);\n }\n } catch (error) {\n console.error(pc.red('\\n✖ Unexpected error during migration:'));\n console.error(error instanceof Error ? error.stack : error);\n process.exit(1);\n }\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error) => {\n console.error(pc.red('✖ Fatal error:'), error);\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced-detection.d.mts","names":[],"sources":["../../src/advanced-detection.ts"],"mappings":";;;KAEY,eAAA;EACV,MAAA;EACA,QAAA;EACA,WAAA;EACA,aAAA;EACA,gBAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,iBAGc,qBAAA,CACd,WAAA,EAAa,WAAA,EACb,QAAA,EAAU,QAAA,GACT,eAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"advanced-detection.d.mts","names":[],"sources":["../../src/advanced-detection.ts"],"mappings":";;;KAEY,eAAA;EACV,MAAA;EACA,QAAA;EACA,WAAA;EACA,aAAA;EACA,gBAAA;EACA,WAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,iBAGc,qBAAA,CACd,WAAA,EAAa,WAAA,EACb,QAAA,EAAU,QAAA,GACT,eAAA;AAAA,iBAmGa,kCAAA,CAAmC,QAAA,EAAU,eAAA"}
|
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/advanced-detection.ts
|
|
2
|
+
function detectProjectFeatures(biomeConfig, reporter) {
|
|
3
|
+
const features = {
|
|
4
|
+
hasVue: false,
|
|
5
|
+
hasReact: false,
|
|
6
|
+
hasTailwind: false,
|
|
7
|
+
hasTypeScript: false,
|
|
8
|
+
hasImportSorting: false,
|
|
9
|
+
hasMonorepo: false,
|
|
10
|
+
hasGraphQL: false,
|
|
11
|
+
hasCSS: false,
|
|
12
|
+
hasHTML: false
|
|
13
|
+
};
|
|
14
|
+
const allIncludes = [
|
|
15
|
+
...biomeConfig.files?.include || [],
|
|
16
|
+
...biomeConfig.linter?.include || [],
|
|
17
|
+
...biomeConfig.formatter?.include || []
|
|
18
|
+
];
|
|
19
|
+
features.hasVue = allIncludes.some((pattern) => pattern.includes(".vue") || pattern.toLowerCase().includes("vue"));
|
|
20
|
+
features.hasReact = allIncludes.some((pattern) => pattern.includes(".jsx") || pattern.includes(".tsx") || pattern.toLowerCase().includes("react")) || !!biomeConfig.javascript?.parser?.jsxEverywhere;
|
|
21
|
+
features.hasTailwind = allIncludes.some((pattern) => pattern.toLowerCase().includes("tailwind") || pattern.includes("tw-") || pattern.includes("tailwind.config"));
|
|
22
|
+
features.hasTypeScript = allIncludes.some((pattern) => pattern.includes(".ts") || pattern.includes(".tsx") || pattern.includes(".mts"));
|
|
23
|
+
if (biomeConfig.linter?.rules && typeof biomeConfig.linter.rules === "object") features.hasImportSorting = Object.keys(biomeConfig.linter.rules).some((key) => key.toLowerCase().includes("import") && (key.toLowerCase().includes("sort") || key.toLowerCase().includes("order")));
|
|
24
|
+
features.hasMonorepo = !!biomeConfig.extends && (Array.isArray(biomeConfig.extends) ? biomeConfig.extends.includes("//") : biomeConfig.extends === "//");
|
|
25
|
+
features.hasGraphQL = allIncludes.some((pattern) => pattern.includes(".graphql") || pattern.includes(".gql") || pattern.toLowerCase().includes("graphql"));
|
|
26
|
+
features.hasCSS = allIncludes.some((pattern) => pattern.includes(".css") || pattern.includes(".scss") || pattern.includes(".sass") || pattern.includes(".less")) || !!biomeConfig.css;
|
|
27
|
+
features.hasHTML = allIncludes.some((pattern) => pattern.includes(".html") || pattern.includes(".htm")) || !!biomeConfig.html;
|
|
28
|
+
const detectedFeatures = Object.entries(features).filter(([_, value]) => value).map(([key]) => key.replace("has", ""));
|
|
29
|
+
if (detectedFeatures.length > 0) reporter.info(`Detected project features: ${detectedFeatures.join(", ")}`);
|
|
30
|
+
return features;
|
|
31
|
+
}
|
|
32
|
+
function generateFeatureSpecificSuggestions(features) {
|
|
33
|
+
const suggestions = [];
|
|
34
|
+
if (features.hasVue) {
|
|
35
|
+
suggestions.push("");
|
|
36
|
+
suggestions.push("Vue.js detected:");
|
|
37
|
+
suggestions.push(" - vueIndentScriptAndStyle option configured");
|
|
38
|
+
suggestions.push(" - Consider using @vue/eslint-config-prettier for Vue-specific formatting");
|
|
39
|
+
}
|
|
40
|
+
if (features.hasTailwind) {
|
|
41
|
+
suggestions.push("");
|
|
42
|
+
suggestions.push("Tailwind CSS detected:");
|
|
43
|
+
suggestions.push(" - sortTailwindcss enabled with common class attributes");
|
|
44
|
+
suggestions.push(" - Supports: class, className, :class attributes");
|
|
45
|
+
suggestions.push(" - Supports: clsx, cn, classNames, tw functions");
|
|
46
|
+
}
|
|
47
|
+
if (features.hasImportSorting) {
|
|
48
|
+
suggestions.push("");
|
|
49
|
+
suggestions.push("Import sorting detected:");
|
|
50
|
+
suggestions.push(" - sortImports enabled");
|
|
51
|
+
suggestions.push(" - Imports will be sorted alphabetically with newlines between groups");
|
|
52
|
+
}
|
|
53
|
+
if (features.hasGraphQL) {
|
|
54
|
+
suggestions.push("");
|
|
55
|
+
suggestions.push("GraphQL detected:");
|
|
56
|
+
suggestions.push(" - Consider using graphql-prettier for GraphQL formatting");
|
|
57
|
+
suggestions.push(" - Oxfmt may support GraphQL in future releases");
|
|
58
|
+
}
|
|
59
|
+
if (features.hasMonorepo) {
|
|
60
|
+
suggestions.push("");
|
|
61
|
+
suggestions.push("Monorepo detected:");
|
|
62
|
+
suggestions.push(" - sortPackageJson enabled for all package.json files");
|
|
63
|
+
suggestions.push(" - Consider using workspace-specific overrides");
|
|
64
|
+
}
|
|
65
|
+
return suggestions;
|
|
66
|
+
}
|
|
2
67
|
|
|
3
|
-
|
|
68
|
+
//#endregion
|
|
69
|
+
export { detectProjectFeatures, generateFeatureSpecificSuggestions };
|
|
70
|
+
//# sourceMappingURL=advanced-detection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced-detection.mjs","names":[],"sources":["../../src/advanced-detection.ts"],"sourcesContent":["import type { BiomeConfig, Reporter } from './types.js';\n\nexport type ProjectFeatures = {\n hasVue: boolean;\n hasReact: boolean;\n hasTailwind: boolean;\n hasTypeScript: boolean;\n hasImportSorting: boolean;\n hasMonorepo: boolean;\n hasGraphQL: boolean;\n hasCSS: boolean;\n hasHTML: boolean;\n};\n\nexport function detectProjectFeatures(\n biomeConfig: BiomeConfig,\n reporter: Reporter,\n): ProjectFeatures {\n const features: ProjectFeatures = {\n hasVue: false,\n hasReact: false,\n hasTailwind: false,\n hasTypeScript: false,\n hasImportSorting: false,\n hasMonorepo: false,\n hasGraphQL: false,\n hasCSS: false,\n hasHTML: false,\n };\n\n \n\n const allIncludes = [\n ...(biomeConfig.files?.include || []),\n ...(biomeConfig.linter?.include || []),\n ...(biomeConfig.formatter?.include || []),\n ];\n\n // Vue detection\n features.hasVue = allIncludes.some(\n (pattern) => pattern.includes('.vue') || pattern.toLowerCase().includes('vue'),\n );\n\n // React detection\n features.hasReact =\n allIncludes.some(\n (pattern) =>\n pattern.includes('.jsx') ||\n pattern.includes('.tsx') ||\n pattern.toLowerCase().includes('react'),\n ) || !!biomeConfig.javascript?.parser?.jsxEverywhere;\n\n // Tailwind detection\n features.hasTailwind = allIncludes.some(\n (pattern) =>\n pattern.toLowerCase().includes('tailwind') ||\n pattern.includes('tw-') ||\n pattern.includes('tailwind.config'),\n );\n\n // TypeScript detection\n features.hasTypeScript = allIncludes.some(\n (pattern) => pattern.includes('.ts') || pattern.includes('.tsx') || pattern.includes('.mts'),\n );\n\n // Import sorting detection\n if (biomeConfig.linter?.rules && typeof biomeConfig.linter.rules === 'object') {\n features.hasImportSorting = Object.keys(biomeConfig.linter.rules).some(\n (key) =>\n key.toLowerCase().includes('import') &&\n (key.toLowerCase().includes('sort') || key.toLowerCase().includes('order')),\n );\n }\n\n // Monorepo detection\n features.hasMonorepo =\n !!biomeConfig.extends &&\n (Array.isArray(biomeConfig.extends)\n ? biomeConfig.extends.includes('//')\n : biomeConfig.extends === '//');\n\n // GraphQL detection\n features.hasGraphQL = allIncludes.some(\n (pattern) =>\n pattern.includes('.graphql') ||\n pattern.includes('.gql') ||\n pattern.toLowerCase().includes('graphql'),\n );\n\n // CSS detection\n features.hasCSS =\n allIncludes.some(\n (pattern) =>\n pattern.includes('.css') ||\n pattern.includes('.scss') ||\n pattern.includes('.sass') ||\n pattern.includes('.less'),\n ) || !!biomeConfig.css;\n\n // HTML detection\n features.hasHTML =\n allIncludes.some((pattern) => pattern.includes('.html') || pattern.includes('.htm')) ||\n !!(biomeConfig as any).html;\n\n // Log detected features\n const detectedFeatures = Object.entries(features)\n .filter(([_, value]) => value)\n .map(([key]) => key.replace('has', ''));\n\n if (detectedFeatures.length > 0) {\n reporter.info(`Detected project features: ${detectedFeatures.join(', ')}`);\n }\n\n return features;\n}\n\nexport function generateFeatureSpecificSuggestions(features: ProjectFeatures): string[] {\n const suggestions: string[] = [];\n\n if (features.hasVue) {\n suggestions.push('');\n suggestions.push('Vue.js detected:');\n suggestions.push(' - vueIndentScriptAndStyle option configured');\n suggestions.push(' - Consider using @vue/eslint-config-prettier for Vue-specific formatting');\n }\n\n if (features.hasTailwind) {\n suggestions.push('');\n suggestions.push('Tailwind CSS detected:');\n suggestions.push(' - sortTailwindcss enabled with common class attributes');\n suggestions.push(' - Supports: class, className, :class attributes');\n suggestions.push(' - Supports: clsx, cn, classNames, tw functions');\n }\n\n if (features.hasImportSorting) {\n suggestions.push('');\n suggestions.push('Import sorting detected:');\n suggestions.push(' - sortImports enabled');\n suggestions.push(' - Imports will be sorted alphabetically with newlines between groups');\n }\n\n if (features.hasGraphQL) {\n suggestions.push('');\n suggestions.push('GraphQL detected:');\n suggestions.push(' - Consider using graphql-prettier for GraphQL formatting');\n suggestions.push(' - Oxfmt may support GraphQL in future releases');\n }\n\n if (features.hasMonorepo) {\n suggestions.push('');\n suggestions.push('Monorepo detected:');\n suggestions.push(' - sortPackageJson enabled for all package.json files');\n suggestions.push(' - Consider using workspace-specific overrides');\n }\n\n return suggestions;\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,99 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeBiomeConfig } from "./schema-normalizer.mjs";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { parse } from "jsonc-parser";
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
//#region src/config-loader.ts
|
|
7
|
+
const BIOME_CONFIG_NAMES = ["biome.json", "biome.jsonc"];
|
|
8
|
+
async function fileExists(path) {
|
|
9
|
+
try {
|
|
10
|
+
await stat(path);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function findBiomeConfig(startDir) {
|
|
17
|
+
for (const name of BIOME_CONFIG_NAMES) {
|
|
18
|
+
const configPath = resolve(startDir, name);
|
|
19
|
+
if (await fileExists(configPath)) return configPath;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function findMonorepoRoot(startDir) {
|
|
23
|
+
let currentDir = startDir;
|
|
24
|
+
const root = resolve("/");
|
|
25
|
+
while (currentDir !== root) {
|
|
26
|
+
if (await fileExists(join(currentDir, ".git"))) return currentDir;
|
|
27
|
+
const packageJsonPath = join(currentDir, "package.json");
|
|
28
|
+
if (await fileExists(packageJsonPath)) try {
|
|
29
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
30
|
+
if (JSON.parse(content).workspaces) return currentDir;
|
|
31
|
+
} catch {}
|
|
32
|
+
const parentDir = dirname(currentDir);
|
|
33
|
+
if (parentDir === currentDir) break;
|
|
34
|
+
currentDir = parentDir;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function loadBiomeConfig(configPath, reporter) {
|
|
38
|
+
try {
|
|
39
|
+
const config = parse(await readFile(configPath, "utf-8"));
|
|
40
|
+
if (!config) throw new Error("Failed to parse Biome configuration");
|
|
41
|
+
return normalizeBiomeConfig(config, reporter);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
reporter.error(`Failed to load Biome config from ${configPath}: ${message}`);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function resolveBiomeExtends(config, configDir, reporter) {
|
|
49
|
+
if (!config.extends) return config;
|
|
50
|
+
const extendsArray = Array.isArray(config.extends) ? config.extends : [config.extends];
|
|
51
|
+
let mergedConfig = {};
|
|
52
|
+
for (const extendPath of extendsArray) {
|
|
53
|
+
let resolvedPath;
|
|
54
|
+
if (extendPath === "//") {
|
|
55
|
+
const monorepoRoot = await findMonorepoRoot(configDir);
|
|
56
|
+
if (!monorepoRoot) {
|
|
57
|
+
reporter.warn("Monorepo root (\"//\" extends) not found. Skipping this extends.");
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const rootConfigPath = await findBiomeConfig(monorepoRoot);
|
|
61
|
+
if (!rootConfigPath) {
|
|
62
|
+
reporter.warn(`No Biome config found at monorepo root: ${monorepoRoot}`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
resolvedPath = rootConfigPath;
|
|
66
|
+
} else resolvedPath = resolve(configDir, extendPath);
|
|
67
|
+
if (!await fileExists(resolvedPath)) {
|
|
68
|
+
reporter.warn(`Extended config not found: ${extendPath}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const resolvedExtendedConfig = await resolveBiomeExtends(await loadBiomeConfig(resolvedPath, reporter), dirname(resolvedPath), reporter);
|
|
73
|
+
mergedConfig = deepMerge(mergedConfig, resolvedExtendedConfig);
|
|
74
|
+
} catch {
|
|
75
|
+
reporter.warn(`Failed to load extended config: ${extendPath}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const { extends: _, ...configWithoutExtends } = config;
|
|
79
|
+
return deepMerge(mergedConfig, configWithoutExtends);
|
|
80
|
+
}
|
|
81
|
+
function deepMerge(target, source) {
|
|
82
|
+
if (!source) return target;
|
|
83
|
+
if (!target) return source;
|
|
84
|
+
const result = { ...target };
|
|
85
|
+
for (const key in source) {
|
|
86
|
+
const sourceValue = source[key];
|
|
87
|
+
const targetValue = result[key];
|
|
88
|
+
if (isObject(sourceValue) && isObject(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
|
|
89
|
+
else if (sourceValue !== void 0) result[key] = sourceValue;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
function isObject(value) {
|
|
94
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
//#endregion
|
|
98
|
+
export { findBiomeConfig, loadBiomeConfig, resolveBiomeExtends };
|
|
99
|
+
//# sourceMappingURL=config-loader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.mjs","names":["parseJsonc"],"sources":["../../src/config-loader.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\n // Keep synchronous check for simple path resolution if needed, but prefer async access usually.\n// Actually, strict async migration means avoiding blocking calls.\nimport { stat } from 'node:fs/promises';\nimport { resolve, dirname, join } from 'node:path';\nimport { parse as parseJsonc } from 'jsonc-parser';\nimport type { BiomeConfig, Reporter } from './types.js';\nimport { normalizeBiomeConfig } from './schema-normalizer.js';\n\nconst BIOME_CONFIG_NAMES = ['biome.json', 'biome.jsonc'];\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function findBiomeConfig(startDir: string): Promise<string | undefined> {\n for (const name of BIOME_CONFIG_NAMES) {\n const configPath = resolve(startDir, name);\n if (await fileExists(configPath)) {\n return configPath;\n }\n }\n return undefined;\n}\n\nasync function findMonorepoRoot(startDir: string): Promise<string | undefined> {\n let currentDir = startDir;\n const root = resolve('/');\n\n while (currentDir !== root) {\n if (await fileExists(join(currentDir, '.git'))) {\n return currentDir;\n }\n\n const packageJsonPath = join(currentDir, 'package.json');\n if (await fileExists(packageJsonPath)) {\n try {\n const content = await readFile(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(content);\n if (packageJson.workspaces) {\n return currentDir;\n }\n } catch {\n // Continue searching\n }\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n\n return undefined;\n}\n\nexport async function loadBiomeConfig(\n configPath: string,\n reporter: Reporter,\n): Promise<BiomeConfig> {\n try {\n const content = await readFile(configPath, 'utf-8');\n const config = parseJsonc(content) as BiomeConfig;\n\n if (!config) {\n throw new Error('Failed to parse Biome configuration');\n }\n\n const normalized = normalizeBiomeConfig(config, reporter);\n return normalized;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n reporter.error(`Failed to load Biome config from ${configPath}: ${message}`);\n throw error;\n }\n}\n\nexport async function resolveBiomeExtends(\n config: BiomeConfig,\n configDir: string,\n reporter: Reporter,\n): Promise<BiomeConfig> {\n if (!config.extends) {\n return config;\n }\n\n const extendsArray = Array.isArray(config.extends) ? config.extends : [config.extends];\n let mergedConfig: BiomeConfig = {};\n\n for (const extendPath of extendsArray) {\n let resolvedPath: string;\n\n if (extendPath === '//') {\n const monorepoRoot = await findMonorepoRoot(configDir);\n if (!monorepoRoot) {\n reporter.warn('Monorepo root (\"//\" extends) not found. Skipping this extends.');\n continue;\n }\n const rootConfigPath = await findBiomeConfig(monorepoRoot);\n if (!rootConfigPath) {\n reporter.warn(`No Biome config found at monorepo root: ${monorepoRoot}`);\n continue;\n }\n resolvedPath = rootConfigPath;\n } else {\n resolvedPath = resolve(configDir, extendPath);\n }\n\n if (!(await fileExists(resolvedPath))) {\n reporter.warn(`Extended config not found: ${extendPath}`);\n continue;\n }\n\n try {\n const extendedConfig = await loadBiomeConfig(resolvedPath, reporter);\n const extendedDir = dirname(resolvedPath);\n // Recursively resolve extends\n const resolvedExtendedConfig = await resolveBiomeExtends(\n extendedConfig,\n extendedDir,\n reporter,\n );\n\n mergedConfig = deepMerge(mergedConfig, resolvedExtendedConfig);\n } catch {\n reporter.warn(`Failed to load extended config: ${extendPath}`);\n }\n }\n\n const { extends: _, ...configWithoutExtends } = config;\n return deepMerge(mergedConfig, configWithoutExtends);\n}\n\nfunction deepMerge<T>(target: T, source: T): T {\n if (!source) return target;\n if (!target) return source;\n\n const result = { ...target };\n\n for (const key in source) {\n const sourceValue = source[key];\n const targetValue = result[key];\n\n if (isObject(sourceValue) && isObject(targetValue)) {\n result[key] = deepMerge(targetValue, sourceValue);\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue;\n }\n }\n\n return result;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"mappings":";;;;;;AASA,MAAM,qBAAqB,CAAC,cAAc,cAAc;AAExD,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;SACD;AACN,SAAO;;;AAIX,eAAsB,gBAAgB,UAA+C;AACnF,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,aAAa,QAAQ,UAAU,KAAK;AAC1C,MAAI,MAAM,WAAW,WAAW,CAC9B,QAAO;;;AAMb,eAAe,iBAAiB,UAA+C;CAC7E,IAAI,aAAa;CACjB,MAAM,OAAO,QAAQ,IAAI;AAEzB,QAAO,eAAe,MAAM;AAC1B,MAAI,MAAM,WAAW,KAAK,YAAY,OAAO,CAAC,CAC5C,QAAO;EAGT,MAAM,kBAAkB,KAAK,YAAY,eAAe;AACxD,MAAI,MAAM,WAAW,gBAAgB,CACnC,KAAI;GACF,MAAM,UAAU,MAAM,SAAS,iBAAiB,QAAQ;AAExD,OADoB,KAAK,MAAM,QAAQ,CACvB,WACd,QAAO;UAEH;EAKV,MAAM,YAAY,QAAQ,WAAW;AACrC,MAAI,cAAc,WAAY;AAC9B,eAAa;;;AAMjB,eAAsB,gBACpB,YACA,UACsB;AACtB,KAAI;EAEF,MAAM,SAASA,MADC,MAAM,SAAS,YAAY,QAAQ,CACjB;AAElC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,sCAAsC;AAIxD,SADmB,qBAAqB,QAAQ,SAAS;UAElD,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,WAAS,MAAM,oCAAoC,WAAW,IAAI,UAAU;AAC5E,QAAM;;;AAIV,eAAsB,oBACpB,QACA,WACA,UACsB;AACtB,KAAI,CAAC,OAAO,QACV,QAAO;CAGT,MAAM,eAAe,MAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,UAAU,CAAC,OAAO,QAAQ;CACtF,IAAI,eAA4B,EAAE;AAElC,MAAK,MAAM,cAAc,cAAc;EACrC,IAAI;AAEJ,MAAI,eAAe,MAAM;GACvB,MAAM,eAAe,MAAM,iBAAiB,UAAU;AACtD,OAAI,CAAC,cAAc;AACjB,aAAS,KAAK,mEAAiE;AAC/E;;GAEF,MAAM,iBAAiB,MAAM,gBAAgB,aAAa;AAC1D,OAAI,CAAC,gBAAgB;AACnB,aAAS,KAAK,2CAA2C,eAAe;AACxE;;AAEF,kBAAe;QAEf,gBAAe,QAAQ,WAAW,WAAW;AAG/C,MAAI,CAAE,MAAM,WAAW,aAAa,EAAG;AACrC,YAAS,KAAK,8BAA8B,aAAa;AACzD;;AAGF,MAAI;GAIF,MAAM,yBAAyB,MAAM,oBAHd,MAAM,gBAAgB,cAAc,SAAS,EAChD,QAAQ,aAAa,EAKvC,SACD;AAED,kBAAe,UAAU,cAAc,uBAAuB;UACvD;AACP,YAAS,KAAK,mCAAmC,aAAa;;;CAIlE,MAAM,EAAE,SAAS,GAAG,GAAG,yBAAyB;AAChD,QAAO,UAAU,cAAc,qBAAqB;;AAGtD,SAAS,UAAa,QAAW,QAAc;AAC7C,KAAI,CAAC,OAAQ,QAAO;AACpB,KAAI,CAAC,OAAQ,QAAO;CAEpB,MAAM,SAAS,EAAE,GAAG,QAAQ;AAE5B,MAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,cAAc,OAAO;EAC3B,MAAM,cAAc,OAAO;AAE3B,MAAI,SAAS,YAAY,IAAI,SAAS,YAAY,CAChD,QAAO,OAAO,UAAU,aAAa,YAAY;WACxC,gBAAgB,OACzB,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM"}
|
|
@@ -1,3 +1,69 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
//#region src/eslint-detector.ts
|
|
5
|
+
const ESLINT_CONFIG_NAMES = [
|
|
6
|
+
"eslint.config.js",
|
|
7
|
+
"eslint.config.mjs",
|
|
8
|
+
"eslint.config.cjs",
|
|
9
|
+
"eslint.config.ts",
|
|
10
|
+
"eslint.config.mts",
|
|
11
|
+
"eslint.config.cts",
|
|
12
|
+
".eslintrc.js",
|
|
13
|
+
".eslintrc.cjs",
|
|
14
|
+
".eslintrc.yaml",
|
|
15
|
+
".eslintrc.yml",
|
|
16
|
+
".eslintrc.json",
|
|
17
|
+
".eslintrc"
|
|
18
|
+
];
|
|
19
|
+
function detectESLint(projectDir) {
|
|
20
|
+
for (const name of ESLINT_CONFIG_NAMES) if (existsSync(resolve(projectDir, name))) return true;
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
function generateESLintBridgeSuggestions(reporter) {
|
|
24
|
+
const suggestions = [
|
|
25
|
+
"ESLint configuration detected. To run Oxlint alongside ESLint:",
|
|
26
|
+
"",
|
|
27
|
+
"1. Install eslint-plugin-oxlint:",
|
|
28
|
+
" pnpm add -D eslint-plugin-oxlint",
|
|
29
|
+
"",
|
|
30
|
+
"2. Add to your ESLint config (flat config):",
|
|
31
|
+
" import oxlint from \"eslint-plugin-oxlint\";",
|
|
32
|
+
" export default [",
|
|
33
|
+
" oxlint.configs.recommended,",
|
|
34
|
+
" // your other configs",
|
|
35
|
+
" ];",
|
|
36
|
+
"",
|
|
37
|
+
"3. Or for legacy config (.eslintrc):",
|
|
38
|
+
" {",
|
|
39
|
+
" \"extends\": [\"plugin:oxlint/recommended\"]",
|
|
40
|
+
" }",
|
|
41
|
+
"",
|
|
42
|
+
"4. Run both tools:",
|
|
43
|
+
" pnpm oxlint && pnpm eslint ."
|
|
44
|
+
];
|
|
45
|
+
reporter.info("ESLint bridge suggestions generated");
|
|
46
|
+
return suggestions;
|
|
47
|
+
}
|
|
48
|
+
function generateESLintFormatterBridgeSuggestions() {
|
|
49
|
+
return [
|
|
50
|
+
"",
|
|
51
|
+
"To integrate Oxfmt with ESLint:",
|
|
52
|
+
"",
|
|
53
|
+
"1. Install eslint-plugin-oxfmt:",
|
|
54
|
+
" pnpm add -D oxfmt eslint-plugin-oxfmt",
|
|
55
|
+
"",
|
|
56
|
+
"2. Add to your ESLint flat config:",
|
|
57
|
+
" import pluginOxfmt from \"eslint-plugin-oxfmt\";",
|
|
58
|
+
" export default [",
|
|
59
|
+
" {",
|
|
60
|
+
" ...pluginOxfmt.configs.recommended,",
|
|
61
|
+
" files: [\"**/*.{js,ts,mjs,cjs,jsx,tsx}\"],",
|
|
62
|
+
" }",
|
|
63
|
+
" ];"
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
export { detectESLint, generateESLintBridgeSuggestions, generateESLintFormatterBridgeSuggestions };
|
|
69
|
+
//# sourceMappingURL=eslint-detector.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint-detector.mjs","names":[],"sources":["../../src/eslint-detector.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { Reporter } from './types.js';\n\nconst ESLINT_CONFIG_NAMES = [\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.cts',\n '.eslintrc.js',\n '.eslintrc.cjs',\n '.eslintrc.yaml',\n '.eslintrc.yml',\n '.eslintrc.json',\n '.eslintrc',\n];\n\nexport function detectESLint(projectDir: string): boolean {\n for (const name of ESLINT_CONFIG_NAMES) {\n if (existsSync(resolve(projectDir, name))) {\n return true;\n }\n }\n return false;\n}\n\nexport function generateESLintBridgeSuggestions(reporter: Reporter): string[] {\n const suggestions: string[] = [\n 'ESLint configuration detected. To run Oxlint alongside ESLint:',\n '',\n '1. Install eslint-plugin-oxlint:',\n ' pnpm add -D eslint-plugin-oxlint',\n '',\n '2. Add to your ESLint config (flat config):',\n ' import oxlint from \"eslint-plugin-oxlint\";',\n ' export default [',\n ' oxlint.configs.recommended,',\n ' // your other configs',\n ' ];',\n '',\n '3. Or for legacy config (.eslintrc):',\n ' {',\n ' \"extends\": [\"plugin:oxlint/recommended\"]',\n ' }',\n '',\n '4. Run both tools:',\n ' pnpm oxlint && pnpm eslint .',\n ];\n\n reporter.info('ESLint bridge suggestions generated');\n return suggestions;\n}\n\nexport function generateESLintFormatterBridgeSuggestions(): string[] {\n return [\n '',\n 'To integrate Oxfmt with ESLint:',\n '',\n '1. Install eslint-plugin-oxfmt:',\n ' pnpm add -D oxfmt eslint-plugin-oxfmt',\n '',\n '2. Add to your ESLint flat config:',\n ' import pluginOxfmt from \"eslint-plugin-oxfmt\";',\n ' export default [',\n ' {',\n ' ...pluginOxfmt.configs.recommended,',\n ' files: [\"**/*.{js,ts,mjs,cjs,jsx,tsx}\"],',\n ' }',\n ' ];',\n ];\n}\n"],"mappings":";;;;AAIA,MAAM,sBAAsB;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,aAAa,YAA6B;AACxD,MAAK,MAAM,QAAQ,oBACjB,KAAI,WAAW,QAAQ,YAAY,KAAK,CAAC,CACvC,QAAO;AAGX,QAAO;;AAGT,SAAgB,gCAAgC,UAA8B;CAC5E,MAAM,cAAwB;EAC5B;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;AAED,UAAS,KAAK,sCAAsC;AACpD,QAAO;;AAGT,SAAgB,2CAAqD;AACnE,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter-mapper.d.mts","names":[],"sources":["../../src/formatter-mapper.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"formatter-mapper.d.mts","names":[],"sources":["../../src/formatter-mapper.ts"],"mappings":";;;iBAQgB,mBAAA,CAAoB,WAAA,EAAa,WAAA,EAAa,QAAA,EAAU,QAAA,GAAW,WAAA"}
|