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.
Files changed (80) hide show
  1. package/README.md +59 -5
  2. package/dist/bin/biome-to-oxc.mjs +27 -44
  3. package/dist/bin/biome-to-oxc.mjs.map +1 -1
  4. package/dist/src/advanced-detection.d.mts.map +1 -1
  5. package/dist/src/advanced-detection.mjs +69 -2
  6. package/dist/src/advanced-detection.mjs.map +1 -0
  7. package/dist/src/config-loader.mjs +98 -2
  8. package/dist/src/config-loader.mjs.map +1 -0
  9. package/dist/src/eslint-detector.mjs +68 -2
  10. package/dist/src/eslint-detector.mjs.map +1 -0
  11. package/dist/src/formatter-mapper.d.mts.map +1 -1
  12. package/dist/src/formatter-mapper.mjs +84 -2
  13. package/dist/src/formatter-mapper.mjs.map +1 -0
  14. package/dist/src/index.d.mts +2 -2
  15. package/dist/src/index.d.mts.map +1 -1
  16. package/dist/src/index.mjs +75 -21
  17. package/dist/src/index.mjs.map +1 -1
  18. package/dist/src/js-plugin-scaffolder.d.mts +8 -0
  19. package/dist/src/js-plugin-scaffolder.d.mts.map +1 -0
  20. package/dist/src/js-plugin-scaffolder.mjs +71 -0
  21. package/dist/src/js-plugin-scaffolder.mjs.map +1 -0
  22. package/dist/src/overrides-transformer.mjs +29 -2
  23. package/dist/src/overrides-transformer.mjs.map +1 -0
  24. package/dist/src/oxfmt-overrides.mjs +70 -2
  25. package/dist/src/oxfmt-overrides.mjs.map +1 -0
  26. package/dist/src/oxlint-generator.d.mts +7 -2
  27. package/dist/src/oxlint-generator.d.mts.map +1 -1
  28. package/dist/src/oxlint-generator.mjs +124 -2
  29. package/dist/src/oxlint-generator.mjs.map +1 -0
  30. package/dist/src/package-updater.d.mts +4 -1
  31. package/dist/src/package-updater.d.mts.map +1 -1
  32. package/dist/src/package-updater.mjs +205 -2
  33. package/dist/src/package-updater.mjs.map +1 -0
  34. package/dist/src/prettier-detector.mjs +49 -2
  35. package/dist/src/prettier-detector.mjs.map +1 -0
  36. package/dist/src/report-writer.mjs +21 -2
  37. package/dist/src/report-writer.mjs.map +1 -0
  38. package/dist/src/reporter.d.mts.map +1 -1
  39. package/dist/src/reporter.mjs +25 -2
  40. package/dist/src/reporter.mjs.map +1 -0
  41. package/dist/src/rule-mapper.d.mts.map +1 -1
  42. package/dist/src/rule-mapper.mjs +154 -2
  43. package/dist/src/rule-mapper.mjs.map +1 -0
  44. package/dist/src/schema-normalizer.d.mts.map +1 -1
  45. package/dist/src/schema-normalizer.mjs +48 -2
  46. package/dist/src/schema-normalizer.mjs.map +1 -0
  47. package/dist/src/turbo-updater.mjs +48 -2
  48. package/dist/src/turbo-updater.mjs.map +1 -0
  49. package/dist/src/types.d.mts +77 -5
  50. package/dist/src/types.d.mts.map +1 -1
  51. package/package.json +10 -10
  52. package/dist/advanced-detection-Bekr41wZ.mjs +0 -71
  53. package/dist/advanced-detection-Bekr41wZ.mjs.map +0 -1
  54. package/dist/config-loader-Do081Mw1.mjs +0 -99
  55. package/dist/config-loader-Do081Mw1.mjs.map +0 -1
  56. package/dist/eslint-detector-CiNY3vrg.mjs +0 -69
  57. package/dist/eslint-detector-CiNY3vrg.mjs.map +0 -1
  58. package/dist/formatter-mapper-BMQ4nHqQ.mjs +0 -85
  59. package/dist/formatter-mapper-BMQ4nHqQ.mjs.map +0 -1
  60. package/dist/overrides-transformer-D8lb8ijd.mjs +0 -30
  61. package/dist/overrides-transformer-D8lb8ijd.mjs.map +0 -1
  62. package/dist/oxfmt-overrides-BJtFUPt9.mjs +0 -71
  63. package/dist/oxfmt-overrides-BJtFUPt9.mjs.map +0 -1
  64. package/dist/oxlint-generator-L7ui1BHE.mjs +0 -48
  65. package/dist/oxlint-generator-L7ui1BHE.mjs.map +0 -1
  66. package/dist/package-updater-DcxTVTb3.mjs +0 -162
  67. package/dist/package-updater-DcxTVTb3.mjs.map +0 -1
  68. package/dist/prettier-detector-aLE389_O.mjs +0 -50
  69. package/dist/prettier-detector-aLE389_O.mjs.map +0 -1
  70. package/dist/report-writer-BU-Lyzye.mjs +0 -22
  71. package/dist/report-writer-BU-Lyzye.mjs.map +0 -1
  72. package/dist/reporter-B1SJ0-DK.mjs +0 -26
  73. package/dist/reporter-B1SJ0-DK.mjs.map +0 -1
  74. package/dist/rule-mapper-CvipfcEK.mjs +0 -149
  75. package/dist/rule-mapper-CvipfcEK.mjs.map +0 -1
  76. package/dist/schema-normalizer-CaJrUFMX.mjs +0 -49
  77. package/dist/schema-normalizer-CaJrUFMX.mjs.map +0 -1
  78. package/dist/turbo-updater-3b20rC36.mjs +0 -49
  79. package/dist/turbo-updater-3b20rC36.mjs.map +0 -1
  80. 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
- - Suggests type-aware linting commands
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**: `experimentalSortImports` with configurable order
95
- - **Package.json Sorting**: `experimentalSortPackageJson` for consistent deps
96
- - **Tailwind Integration**: `experimentalTailwindcss` for class optimization
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("--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) => {
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
- const dryRun = migrationOptions.dryRun ?? false;
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
- console.log(pc.cyan(`\npackage.json updates${pkg.dryRun ? " (planned)" : ""}:`));
74
- if (!pkg.found) console.log(" package.json not found; dependency and script updates skipped.");
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") console.log(` ${pkg.dryRun ? "Would add" : "Added"} devDependency ${change.name}@${change.to}`);
83
- else {
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) console.log(" No Biome scripts found to update.");
83
+ if (pkg.found && migrationOptions.updateScripts && pkg.scriptsUpdated.length === 0) {}
90
84
  }
91
85
  if (report.detectedIntegrations) {
92
- const integrations = Object.entries(report.detectedIntegrations).filter(([_, detected]) => detected).map(([name]) => name);
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
- console.log(pc.yellow(`\nWarnings (${report.warnings.length}):`));
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
- console.log(pc.cyan(`\nNext steps (${report.suggestions.length}):`));
107
- if (migrationOptions.verbose) report.suggestions.forEach((s) => {
108
- console.log(` - ${s}`);
109
- });
110
- else console.log(" Run with --verbose to see all suggestions.");
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":";;;;;;;;;AAQA,MAAM,cAAc;CAClB,MAAM;CACN,SAAS;CACT,aAAa;CACd;AAGD,MAAM,yBAAyB,EAAE,OAAO;CACtC,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,QAAQ,EAAE,SAAS,CAAC,QAAQ,MAAM;CAClC,UAAU,EAAE,SAAS,CAAC,QAAQ,MAAM;CACpC,eAAe,EAAE,SAAS,CAAC,QAAQ,MAAM;CACzC,SAAS,EAAE,SAAS,CAAC,QAAQ,MAAM;CACnC,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM;CACrC,WAAW,EAAE,SAAS,CAAC,QAAQ,MAAM;CACrC,cAAc,EAAE,SAAS,CAAC,QAAQ,MAAM;CACxC,UAAU,EAAE,SAAS,CAAC,QAAQ,MAAM;CACpC,QAAQ,EAAE,QAAQ,CAAC,UAAU;CAC9B,CAAC;AAGF,MAAM,gBAAgB,QAAgB,SAAiB;AACrD,SAAQ,MAAM,GAAG,IAAI,gBAAgB,OAAO,cAAc,CAAC;AAC3D,SAAQ,KAAK,KAAK;;AAGpB,QAAQ,GAAG,gBAAgB,aAAa,UAAU,IAAI,CAAC;AACvD,QAAQ,GAAG,iBAAiB,aAAa,WAAW,IAAI,CAAC;AAEzD,QAAQ,GAAG,uBAAuB,WAAW;AAC3C,SAAQ,MAAM,GAAG,IAAI,mCAAmC,CAAC;AACzD,SAAQ,MAAM,OAAO;AACrB,SAAQ,KAAK,EAAE;EACf;AAEF,QAAQ,GAAG,sBAAsB,UAAU;AACzC,SAAQ,MAAM,GAAG,IAAI,0BAA0B,CAAC;AAChD,SAAQ,MAAM,MAAM;AACpB,SAAQ,KAAK,EAAE;EACf;AAEF,eAAe,OAAO;AACpB,SACG,KAAK,eAAe,CACpB,QAAQ,YAAY,QAAQ,CAC5B,YAAY,YAAY,YAAY,CACpC,OAAO,uBAAuB,oCAAoC,CAClE,OAAO,2BAA2B,yCAAyC,CAC3E,OAAO,aAAa,iDAAiD,CACrE,OAAO,eAAe,uCAAuC,CAC7D,OAAO,oBAAoB,kDAAkD,CAC7E,OAAO,gBAAgB,uDAAuD,CAC9E,OAAO,eAAe,yDAAyD,CAC/E,OAAO,mBAAmB,iEAAiE,CAC3F,OAAO,cAAc,2DAA2D,CAChF,OAAO,mBAAmB,yDAAyD,CACnF,OAAO,iBAAiB,sCAAsC,CAC9D,OAAO,OAAO,eAAe;AAC5B,MAAI;GAEF,MAAM,OAAO;IACX,GAAG;IACH,UAAU,CAAC,WAAW;IACvB;AAED,UAAO,KAAK;GAEZ,MAAM,mBAAmB,uBAAuB,UAAU,KAAK;AAE/D,OAAI,CAAC,iBAAiB,SAAS;AAC7B,YAAQ,MAAM,GAAG,IAAI,qBAAqB,CAAC;AAC3C,qBAAiB,MAAM,OAAO,SAAS,QAAoB;AACzD,aAAQ,MAAM,OAAO,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,IAAI,UAAU;MAC1D;AACF,YAAQ,KAAK,EAAE;;GAGjB,MAAM,mBAAqC,iBAAiB;GAE5D,MAAM,SAAS,MAAM,QAAQ,iBAAiB;AAE9C,OAAI,OAAO,SAAS;IAClB,MAAM,SAAS,iBAAiB,UAAU;IAC1C,MAAM,UAAU,SAAS,wBAAwB;AACjD,YAAQ,IAAI,GAAG,MAAM,KAAK,UAAU,CAAC;AAErC,YAAQ,IAAI,GAAG,KAAK,yBAAyB,SAAS,eAAe,GAAG,GAAG,CAAC;AAC5E,YAAQ,IACN,KAAK,SAAS,iBAAiB,UAAU,kBAAkB,OAAO,QAAQ,mBAC3E;AACD,YAAQ,IACN,KAAK,SAAS,iBAAiB,UAAU,iBAAiB,OAAO,QAAQ,kBAC1E;AACD,YAAQ,IACN,2BAA2B,OAAO,QAAQ,eAAe,aAAa,OAAO,QAAQ,aAAa,GACnG;AACD,YAAQ,IACN,0BAA0B,OAAO,QAAQ,mBAAmB,yBAAyB,OAAO,QAAQ,4BAA4B,GACjI;AAED,QAAI,OAAO,aAAa;KACtB,MAAM,MAAM,OAAO;AACnB,aAAQ,IAAI,GAAG,KAAK,yBAAyB,IAAI,SAAS,eAAe,GAAG,GAAG,CAAC;AAEhF,SAAI,CAAC,IAAI,MACP,SAAQ,IAAI,mEAAmE;cACtE,CAAC,IAAI,QACd,SAAQ,IAAI,oCAAoC;UAC3C;AACL,WAAK,MAAM,WAAW,IAAI,qBAAqB;OAC7C,MAAM,eAAe,QAAQ,UAAU,KAAK,QAAQ,QAAQ,KAAK;AACjE,eAAQ,IACN,KAAK,IAAI,SAAS,iBAAiB,UAAU,GAAG,QAAQ,KAAK,QAAQ,QAAQ,iBAAiB,eAC/F;;MAGH,MAAM,gBAAgB,IAAI,gBAAgB,QACvC,WAAW,OAAO,WAAW,kBAC/B;AAED,WAAK,MAAM,UAAU,cACnB,KAAI,OAAO,WAAW,QACpB,SAAQ,IACN,KAAK,IAAI,SAAS,cAAc,QAAQ,iBAAiB,OAAO,KAAK,GAAG,OAAO,KAChF;WACI;OACL,MAAM,cAAc,OAAO,QAAQ;AACnC,eAAQ,IACN,KAAK,IAAI,SAAS,iBAAiB,UAAU,iBAAiB,OAAO,KAAK,GAAG,YAAY,KAAK,OAAO,KACtG;;AAIL,WAAK,MAAM,gBAAgB,IAAI,eAC7B,SAAQ,IACN,KAAK,IAAI,SAAS,iBAAiB,UAAU,WAAW,aAAa,KAAK,KAAK,aAAa,OAAO,KAAK,aAAa,QACtH;;AAIL,SAAI,IAAI,SAAS,iBAAiB,iBAAiB,IAAI,eAAe,WAAW,EAC/E,SAAQ,IAAI,sCAAsC;;AAItD,QAAI,OAAO,sBAAsB;KAC/B,MAAM,eAAe,OAAO,QAAQ,OAAO,qBAAqB,CAC7D,QAAQ,CAAC,GAAG,cAAc,SAAS,CACnC,KAAK,CAAC,UAAU,KAAK;AAExB,SAAI,aAAa,SAAS,GAAG;AAC3B,cAAQ,IAAI,GAAG,KAAK,2BAA2B,CAAC;AAChD,cAAQ,IAAI,KAAK,aAAa,KAAK,KAAK,GAAG;;;AAI/C,QAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,aAAQ,IAAI,GAAG,OAAO,eAAe,OAAO,SAAS,OAAO,IAAI,CAAC;AACjE,SAAI,iBAAiB,QACnB,QAAO,SAAS,SAAS,MAAM;AAC7B,cAAQ,IAAI,OAAO,IAAI;OACvB;SAEF,SAAQ,IAAI,mDAAmD;;AAInE,QAAI,OAAO,YAAY,SAAS,GAAG;AACjC,aAAQ,IAAI,GAAG,KAAK,iBAAiB,OAAO,YAAY,OAAO,IAAI,CAAC;AACpE,SAAI,iBAAiB,QACnB,QAAO,YAAY,SAAS,MAAM;AAChC,cAAQ,IAAI,OAAO,IAAI;OACvB;SAEF,SAAQ,IAAI,+CAA+C;;AAI/D,QAAI,CAAC,iBAAiB,cACpB,SAAQ,IAAI,GAAG,IAAI,wEAAwE,CAAC;AAG9F,QAAI,iBAAiB,OACnB,KAAI,iBAAiB,OACnB,SAAQ,IAAI,GAAG,IAAI,4CAA4C,iBAAiB,SAAS,CAAC;QAE1F,SAAQ,IAAI,GAAG,MAAM,iCAAiC,iBAAiB,SAAS,CAAC;AAIrF,YAAQ,KAAK,EAAE;UACV;AACL,YAAQ,MAAM,GAAG,IAAI,wBAAwB,CAAC;AAE9C,QAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,aAAQ,MAAM,GAAG,IAAI,WAAW,OAAO,OAAO,OAAO,IAAI,CAAC;AAC1D,YAAO,OAAO,SAAS,MAAM;AAC3B,cAAQ,MAAM,OAAO,IAAI;OACzB;;AAGJ,YAAQ,KAAK,EAAE;;WAEV,OAAO;AACd,WAAQ,MAAM,GAAG,IAAI,yCAAyC,CAAC;AAC/D,WAAQ,MAAM,iBAAiB,QAAQ,MAAM,QAAQ,MAAM;AAC3D,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,OAAM,QAAQ,WAAW,QAAQ,KAAK;;AAGxC,MAAM,CAAC,OAAO,UAAU;AACtB,SAAQ,MAAM,GAAG,IAAI,iBAAiB,EAAE,MAAM;AAC9C,SAAQ,KAAK,EAAE;EACf"}
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,iBAsGa,kCAAA,CAAmC,QAAA,EAAU,eAAA"}
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
- import { n as generateFeatureSpecificSuggestions, t as detectProjectFeatures } from "../advanced-detection-Bekr41wZ.mjs";
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
- export { detectProjectFeatures, generateFeatureSpecificSuggestions };
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 { n as loadBiomeConfig, r as resolveBiomeExtends, t as findBiomeConfig } from "../config-loader-Do081Mw1.mjs";
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
- export { findBiomeConfig, loadBiomeConfig, resolveBiomeExtends };
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 { n as generateESLintBridgeSuggestions, r as generateESLintFormatterBridgeSuggestions, t as detectESLint } from "../eslint-detector-CiNY3vrg.mjs";
1
+ import { resolve } from "node:path";
2
+ import { existsSync } from "node:fs";
2
3
 
3
- export { detectESLint, generateESLintBridgeSuggestions, generateESLintFormatterBridgeSuggestions };
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":";;;iBAUgB,mBAAA,CAAoB,WAAA,EAAa,WAAA,EAAa,QAAA,EAAU,QAAA,GAAW,WAAA"}
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"}