biome-to-oxc 2.0.0 → 3.0.1

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 CHANGED
@@ -135,6 +135,12 @@ ignore configuration before removing Biome.
135
135
  - `docs/oxfmt-rules.tsv`: Oxfmt configuration surface inventory
136
136
  - `docs/tsgolint-rules.tsv`: typed linting implementation matrix
137
137
  - `docs/oxlint-vs-tsgolint.tsv`: typed rule availability cross-reference
138
+ - `docs/biome-rules.tsv`: Biome rule inventory, so a mapping cannot key off a rule Biome dropped
139
+ - `docs/biome-formatter-options.tsv`: Biome formatter surface inventory
140
+
141
+ All six are generated from the schemas of the installed `oxlint`, `oxfmt`,
142
+ `oxlint-tsgolint`, and `@biomejs/biome` packages by `pnpm docs:sync`; `pnpm docs:check`
143
+ fails when they drift, and it runs as part of `pnpm check`.
138
144
 
139
145
  ✅ **Script Rewrite Strategy Controls**
140
146
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as readJsonFile, n as DefaultReporter, r as findClosestPackageJson, t as migrate } from "../src-CYMpPpfK.mjs";
2
+ import { i as readJsonFile, n as DefaultReporter, r as findClosestPackageJson, t as migrate } from "../src-C66EI3xm.mjs";
3
3
  import { realpath } from "node:fs/promises";
4
4
  import { dirname } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -19,6 +19,7 @@ const MigrationOptionsSchema = z.object({
19
19
  delete: z.boolean().default(false),
20
20
  noBackup: z.boolean().default(false),
21
21
  updateScripts: z.boolean().default(false),
22
+ dom: z.boolean().default(false),
22
23
  verbose: z.boolean().default(false),
23
24
  typeAware: z.boolean().default(false),
24
25
  typeCheck: z.boolean().default(false),
@@ -106,7 +107,7 @@ function createCommand(packageMetadata, streams) {
106
107
  writeOut: (chunk) => {
107
108
  streams.stdout.write(chunk);
108
109
  }
109
- }).option("-c, --config <path>", "Path to biome.json or biome.jsonc").option("-o, --output-dir <path>", "Output directory for generated configs").option("--dry-run", "Show what would be done without making changes").option("--delete", "Delete legacy Biome files after migration (biome.json/biome.jsonc and .biomeignore)").option("--no-backup", "Skip backup of existing config files").option("--update-scripts", "Update package.json scripts to use oxlint/oxfmt").option("--type-aware", "Include type-aware linting guidance and dependencies").option("--type-check", "Enable strict typed linting mode (implies --type-aware)").addOption(new Option("--type-aware-profile <profile>", "Type-aware profile: standard (--type-aware) or strict (--type-aware --type-check)").choices(["standard", "strict"]).default("standard")).addOption(new Option("--fix-strategy <strategy>", "Fix mode for rewritten scripts: safe | suggestions | dangerous").choices([
110
+ }).option("-c, --config <path>", "Path to biome.json or biome.jsonc").option("-o, --output-dir <path>", "Output directory for generated configs").option("--dry-run", "Show what would be done without making changes").option("--delete", "Delete legacy Biome files after migration (biome.json/biome.jsonc and .biomeignore)").option("--no-backup", "Skip backup of existing config files").option("--update-scripts", "Update package.json scripts to use oxlint/oxfmt").addOption(new Option("--dom").hideHelp()).option("--type-aware", "Include type-aware linting guidance and dependencies").option("--type-check", "Enable strict typed linting mode (implies --type-aware)").addOption(new Option("--type-aware-profile <profile>", "Type-aware profile: standard (--type-aware) or strict (--type-aware --type-check)").choices(["standard", "strict"]).default("standard")).addOption(new Option("--fix-strategy <strategy>", "Fix mode for rewritten scripts: safe | suggestions | dangerous").choices([
110
111
  "safe",
111
112
  "suggestions",
112
113
  "dangerous"
@@ -130,6 +131,7 @@ function normalizeMigrationOptions(options) {
130
131
  delete: options.delete,
131
132
  noBackup: options.backup === false,
132
133
  updateScripts: options.updateScripts,
134
+ dom: options.dom,
133
135
  verbose: options.verbose,
134
136
  typeAware,
135
137
  typeCheck,
@@ -69,6 +69,9 @@ interface BiomeFormatterConfig {
69
69
  lineWidth?: number;
70
70
  attributePosition?: 'auto' | 'multiline';
71
71
  bracketSpacing?: boolean;
72
+ bracketSameLine?: boolean;
73
+ expand?: 'auto' | 'always' | 'never';
74
+ trailingNewline?: boolean;
72
75
  [key: string]: unknown;
73
76
  }
74
77
  interface BiomeJavaScriptConfig {
@@ -86,10 +89,13 @@ interface BiomeJavaScriptConfig {
86
89
  arrowParentheses?: 'always' | 'asNeeded';
87
90
  bracketSameLine?: boolean;
88
91
  bracketSpacing?: boolean;
92
+ expand?: 'auto' | 'always' | 'never';
89
93
  indentStyle?: 'tab' | 'space';
90
94
  indentWidth?: number;
91
95
  lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
92
96
  lineWidth?: number;
97
+ operatorLinebreak?: 'after' | 'before';
98
+ trailingNewline?: boolean;
93
99
  [key: string]: unknown;
94
100
  };
95
101
  linter?: {
@@ -105,11 +111,13 @@ interface BiomeJsonConfig {
105
111
  };
106
112
  formatter?: {
107
113
  enabled?: boolean;
114
+ expand?: 'auto' | 'always' | 'never';
108
115
  indentStyle?: 'tab' | 'space';
109
116
  indentWidth?: number;
110
117
  lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
111
118
  lineWidth?: number;
112
119
  trailingCommas?: 'none' | 'all';
120
+ trailingNewline?: boolean;
113
121
  };
114
122
  linter?: {
115
123
  enabled?: boolean;
@@ -126,6 +134,7 @@ interface BiomeCssConfig {
126
134
  lineEnding?: 'lf' | 'crlf' | 'cr' | 'auto';
127
135
  lineWidth?: number;
128
136
  quoteStyle?: 'single' | 'double';
137
+ trailingNewline?: boolean;
129
138
  };
130
139
  linter?: {
131
140
  enabled?: boolean;
@@ -233,6 +242,7 @@ interface OxfmtConfig {
233
242
  endOfLine?: 'lf' | 'crlf' | 'cr';
234
243
  singleAttributePerLine?: boolean;
235
244
  objectWrap?: 'preserve' | 'collapse';
245
+ experimentalOperatorPosition?: 'start' | 'end';
236
246
  insertFinalNewline?: boolean;
237
247
  embeddedLanguageFormatting?: 'auto' | 'off';
238
248
  htmlWhitespaceSensitivity?: 'css' | 'strict' | 'ignore';
@@ -306,6 +316,7 @@ interface MigrationOptions {
306
316
  delete?: boolean;
307
317
  noBackup?: boolean;
308
318
  updateScripts?: boolean;
319
+ dom?: boolean;
309
320
  verbose?: boolean;
310
321
  typeAware?: boolean;
311
322
  typeCheck?: boolean;
@@ -1,2 +1,2 @@
1
- import { t as migrate } from "../src-CYMpPpfK.mjs";
1
+ import { t as migrate } from "../src-C66EI3xm.mjs";
2
2
  export { migrate };
@@ -373,7 +373,14 @@ const BiomeFormatterConfigSchema = IncludeFieldsSchema.extend({
373
373
  ]).optional(),
374
374
  lineWidth: z.number().int().optional(),
375
375
  attributePosition: z.enum(["auto", "multiline"]).optional(),
376
- bracketSpacing: z.boolean().optional()
376
+ bracketSpacing: z.boolean().optional(),
377
+ bracketSameLine: z.boolean().optional(),
378
+ expand: z.enum([
379
+ "auto",
380
+ "always",
381
+ "never"
382
+ ]).optional(),
383
+ trailingNewline: z.boolean().optional()
377
384
  }).passthrough();
378
385
  const BiomeJavaScriptFormatterSchema = z.object({
379
386
  enabled: z.boolean().optional(),
@@ -389,6 +396,11 @@ const BiomeJavaScriptFormatterSchema = z.object({
389
396
  arrowParentheses: z.enum(["always", "asNeeded"]).optional(),
390
397
  bracketSameLine: z.boolean().optional(),
391
398
  bracketSpacing: z.boolean().optional(),
399
+ expand: z.enum([
400
+ "auto",
401
+ "always",
402
+ "never"
403
+ ]).optional(),
392
404
  indentStyle: z.enum(["tab", "space"]).optional(),
393
405
  indentWidth: z.number().int().optional(),
394
406
  lineEnding: z.enum([
@@ -397,7 +409,9 @@ const BiomeJavaScriptFormatterSchema = z.object({
397
409
  "cr",
398
410
  "auto"
399
411
  ]).optional(),
400
- lineWidth: z.number().int().optional()
412
+ lineWidth: z.number().int().optional(),
413
+ operatorLinebreak: z.enum(["after", "before"]).optional(),
414
+ trailingNewline: z.boolean().optional()
401
415
  }).passthrough();
402
416
  const BiomeJavaScriptConfigSchema = z.object({
403
417
  parser: z.object({
@@ -415,6 +429,11 @@ const BiomeJsonConfigSchema = z.object({
415
429
  }).passthrough().optional(),
416
430
  formatter: z.object({
417
431
  enabled: z.boolean().optional(),
432
+ expand: z.enum([
433
+ "auto",
434
+ "always",
435
+ "never"
436
+ ]).optional(),
418
437
  indentStyle: z.enum(["tab", "space"]).optional(),
419
438
  indentWidth: z.number().int().optional(),
420
439
  lineEnding: z.enum([
@@ -424,7 +443,8 @@ const BiomeJsonConfigSchema = z.object({
424
443
  "auto"
425
444
  ]).optional(),
426
445
  lineWidth: z.number().int().optional(),
427
- trailingCommas: z.enum(["none", "all"]).optional()
446
+ trailingCommas: z.enum(["none", "all"]).optional(),
447
+ trailingNewline: z.boolean().optional()
428
448
  }).passthrough().optional(),
429
449
  linter: z.object({ enabled: z.boolean().optional() }).passthrough().optional()
430
450
  }).passthrough();
@@ -441,7 +461,8 @@ const BiomeCssConfigSchema = z.object({
441
461
  "auto"
442
462
  ]).optional(),
443
463
  lineWidth: z.number().int().optional(),
444
- quoteStyle: z.enum(["single", "double"]).optional()
464
+ quoteStyle: z.enum(["single", "double"]).optional(),
465
+ trailingNewline: z.boolean().optional()
445
466
  }).passthrough().optional(),
446
467
  linter: z.object({ enabled: z.boolean().optional() }).passthrough().optional()
447
468
  }).passthrough();
@@ -692,6 +713,7 @@ function generateESLintFormatterBridgeSuggestions() {
692
713
  //#region src/oxfmt-overrides.ts
693
714
  const EXPLICIT_OXFMT_OPTION_KEYS$1 = [
694
715
  "objectWrap",
716
+ "experimentalOperatorPosition",
695
717
  "insertFinalNewline",
696
718
  "embeddedLanguageFormatting",
697
719
  "htmlWhitespaceSensitivity",
@@ -731,11 +753,13 @@ const CSS_EXTENSIONS = [
731
753
  ];
732
754
  const JSON_FORMATTER_KEYS = /* @__PURE__ */ new Set([
733
755
  "enabled",
756
+ "expand",
734
757
  "indentStyle",
735
758
  "indentWidth",
736
759
  "lineEnding",
737
760
  "lineWidth",
738
761
  "trailingCommas",
762
+ "trailingNewline",
739
763
  ...EXPLICIT_OXFMT_OPTION_KEYS$1,
740
764
  ...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES$1)
741
765
  ]);
@@ -746,9 +770,25 @@ const CSS_FORMATTER_KEYS = /* @__PURE__ */ new Set([
746
770
  "lineEnding",
747
771
  "lineWidth",
748
772
  "quoteStyle",
773
+ "trailingNewline",
749
774
  ...EXPLICIT_OXFMT_OPTION_KEYS$1,
750
775
  ...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES$1)
751
776
  ]);
777
+ /**
778
+ * Biome's `expand` and Oxfmt's `objectWrap` agree on two of three modes: Biome's `auto`
779
+ * (expand when the literal already has a newline after `{`) is Oxfmt's `preserve`, and
780
+ * Biome's `never` is Oxfmt's `collapse`. Biome's `always` has no Oxfmt counterpart.
781
+ */
782
+ function mapBiomeExpandToObjectWrap(expand, label, reporter) {
783
+ if (expand === "auto") return "preserve";
784
+ if (expand === "never") return "collapse";
785
+ if (expand === "always") reporter.loss(`Biome ${label}.expand "always" has no Oxfmt equivalent; Oxfmt's objectWrap only offers "preserve" and "collapse", so objects and arrays that fit on one line will not be forced to expand.`);
786
+ }
787
+ /** Biome places the operator after or before the break; Oxfmt names the same two positions. */
788
+ function mapBiomeOperatorLinebreakToOxfmt(operatorLinebreak) {
789
+ if (operatorLinebreak === "after") return "end";
790
+ if (operatorLinebreak === "before") return "start";
791
+ }
752
792
  function generateOxfmtOverrides(biomeOverrides, reporter) {
753
793
  if (!biomeOverrides || biomeOverrides.length === 0) return [];
754
794
  const oxfmtOverrides = [];
@@ -792,7 +832,11 @@ function mapBaseFormatterOptions(formatter, reporter) {
792
832
  if (formatter.indentWidth !== void 0) options.tabWidth = formatter.indentWidth;
793
833
  if (formatter.lineEnding !== void 0) options.endOfLine = formatter.lineEnding;
794
834
  if (formatter.bracketSpacing !== void 0) options.bracketSpacing = formatter.bracketSpacing;
835
+ if (formatter.bracketSameLine !== void 0) options.bracketSameLine = formatter.bracketSameLine;
795
836
  if (formatter.attributePosition !== void 0) options.singleAttributePerLine = formatter.attributePosition === "multiline";
837
+ const objectWrap = mapBiomeExpandToObjectWrap(formatter.expand, "formatter", reporter);
838
+ if (objectWrap) options.objectWrap = objectWrap;
839
+ if (formatter.trailingNewline !== void 0) options.insertFinalNewline = formatter.trailingNewline;
796
840
  if (formatter.formatWithErrors) reporter.warn("Biome's formatWithErrors option is not supported in Oxfmt overrides");
797
841
  applyExplicitFormatterOptionPassThrough$1([formatter], options, reporter);
798
842
  return options;
@@ -812,6 +856,12 @@ function mapJavaScriptFormatterOptions(jsFormatter, reporter) {
812
856
  if (jsFormatter.arrowParentheses !== void 0) options.arrowParens = jsFormatter.arrowParentheses === "always" ? "always" : "avoid";
813
857
  if (jsFormatter.bracketSpacing !== void 0) options.bracketSpacing = jsFormatter.bracketSpacing;
814
858
  if (jsFormatter.bracketSameLine !== void 0) options.bracketSameLine = jsFormatter.bracketSameLine;
859
+ if (jsFormatter.attributePosition !== void 0) options.singleAttributePerLine = jsFormatter.attributePosition === "multiline";
860
+ const objectWrap = mapBiomeExpandToObjectWrap(jsFormatter.expand, "javascript.formatter", reporter);
861
+ if (objectWrap) options.objectWrap = objectWrap;
862
+ const operatorPosition = mapBiomeOperatorLinebreakToOxfmt(jsFormatter.operatorLinebreak);
863
+ if (operatorPosition) options.experimentalOperatorPosition = operatorPosition;
864
+ if (jsFormatter.trailingNewline !== void 0) options.insertFinalNewline = jsFormatter.trailingNewline;
815
865
  applyExplicitFormatterOptionPassThrough$1([jsFormatter], options, reporter);
816
866
  return options;
817
867
  }
@@ -824,6 +874,9 @@ function mapJsonFormatterOptions(jsonFormatter, reporter) {
824
874
  if (jsonFormatter.indentWidth !== void 0) options.tabWidth = jsonFormatter.indentWidth;
825
875
  if (jsonFormatter.lineEnding !== void 0) options.endOfLine = jsonFormatter.lineEnding;
826
876
  if (jsonFormatter.trailingCommas !== void 0) options.trailingComma = jsonFormatter.trailingCommas === "none" ? "none" : "all";
877
+ const objectWrap = mapBiomeExpandToObjectWrap(jsonFormatter.expand, "json.formatter", reporter);
878
+ if (objectWrap) options.objectWrap = objectWrap;
879
+ if (jsonFormatter.trailingNewline !== void 0) options.insertFinalNewline = jsonFormatter.trailingNewline;
827
880
  applyExplicitFormatterOptionPassThrough$1([jsonFormatter], options, reporter);
828
881
  return options;
829
882
  }
@@ -836,6 +889,7 @@ function mapCssFormatterOptions(cssFormatter, reporter) {
836
889
  if (cssFormatter.indentWidth !== void 0) options.tabWidth = cssFormatter.indentWidth;
837
890
  if (cssFormatter.lineEnding !== void 0) options.endOfLine = cssFormatter.lineEnding;
838
891
  if (cssFormatter.quoteStyle !== void 0) options.singleQuote = cssFormatter.quoteStyle === "single";
892
+ if (cssFormatter.trailingNewline !== void 0) options.insertFinalNewline = cssFormatter.trailingNewline;
839
893
  applyExplicitFormatterOptionPassThrough$1([cssFormatter], options, reporter);
840
894
  return options;
841
895
  }
@@ -878,6 +932,7 @@ function patternTargetsExtension(pattern, extensions) {
878
932
  //#region src/formatter-mapper.ts
879
933
  const EXPLICIT_OXFMT_OPTION_KEYS = [
880
934
  "objectWrap",
935
+ "experimentalOperatorPosition",
881
936
  "insertFinalNewline",
882
937
  "embeddedLanguageFormatting",
883
938
  "htmlWhitespaceSensitivity",
@@ -906,6 +961,9 @@ const GLOBAL_FORMATTER_KEYS = /* @__PURE__ */ new Set([
906
961
  "lineWidth",
907
962
  "attributePosition",
908
963
  "bracketSpacing",
964
+ "bracketSameLine",
965
+ "expand",
966
+ "trailingNewline",
909
967
  ...EXPLICIT_OXFMT_OPTION_KEYS,
910
968
  ...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES)
911
969
  ]);
@@ -919,10 +977,13 @@ const JAVASCRIPT_FORMATTER_KEYS = /* @__PURE__ */ new Set([
919
977
  "arrowParentheses",
920
978
  "bracketSameLine",
921
979
  "bracketSpacing",
980
+ "expand",
922
981
  "indentStyle",
923
982
  "indentWidth",
924
983
  "lineEnding",
925
984
  "lineWidth",
985
+ "operatorLinebreak",
986
+ "trailingNewline",
926
987
  ...EXPLICIT_OXFMT_OPTION_KEYS,
927
988
  ...Object.keys(LEGACY_EXPLICIT_OXFMT_OPTION_ALIASES)
928
989
  ]);
@@ -1010,9 +1071,13 @@ function mapFormatterOptions(formatter, oxfmtConfig, reporter) {
1010
1071
  oxfmtConfig.arrowParens = "always";
1011
1072
  const bracketSpacing = formatter?.bracketSpacing;
1012
1073
  if (bracketSpacing !== void 0) oxfmtConfig.bracketSpacing = bracketSpacing;
1074
+ if (formatter?.bracketSameLine !== void 0) oxfmtConfig.bracketSameLine = formatter.bracketSameLine;
1013
1075
  const attributePosition = formatter?.attributePosition;
1014
1076
  if (attributePosition === "multiline") oxfmtConfig.singleAttributePerLine = true;
1015
1077
  else if (attributePosition === "auto") oxfmtConfig.singleAttributePerLine = false;
1078
+ const objectWrap = mapBiomeExpandToObjectWrap(formatter?.expand, "formatter", reporter);
1079
+ if (objectWrap) oxfmtConfig.objectWrap = objectWrap;
1080
+ if (formatter?.trailingNewline !== void 0) oxfmtConfig.insertFinalNewline = formatter.trailingNewline;
1016
1081
  if (formatter?.formatWithErrors) reporter.warn("Biome's formatWithErrors option is not supported in Oxfmt");
1017
1082
  }
1018
1083
  function mapIgnorePatterns$1(biomeConfig, oxfmtConfig, additionalIgnorePatterns) {
@@ -1290,6 +1355,40 @@ const OXLINT_CATEGORIES = [
1290
1355
  "style",
1291
1356
  "suspicious"
1292
1357
  ];
1358
+ /**
1359
+ * The React Compiler rules Oxlint activates for ESLint's `react-hooks/recommended` preset.
1360
+ *
1361
+ * Oxlint 1.79 replaced the single `react/react-compiler` rule with 22 per-diagnostic rules;
1362
+ * Biome's `useReactCompiler` runs the compiler and reports every bailout it finds, so the
1363
+ * `recommended` slice is the closest equivalent. The remaining rules default to off upstream
1364
+ * and would fire on code Biome never flagged.
1365
+ *
1366
+ * @see https://oxc.rs/blog/2026-08-18-react-compiler-support
1367
+ */
1368
+ const REACT_COMPILER_RECOMMENDED_RULES = [
1369
+ "react/error-boundaries",
1370
+ "react/globals",
1371
+ "react/immutability",
1372
+ "react/incompatible-library",
1373
+ "react/preserve-manual-memoization",
1374
+ "react/purity",
1375
+ "react/refs",
1376
+ "react/set-state-in-effect",
1377
+ "react/set-state-in-render",
1378
+ "react/static-components",
1379
+ "react/unsupported-syntax",
1380
+ "react/use-memo"
1381
+ ];
1382
+ /**
1383
+ * Mappings where the Oxlint rule covers only part of what the Biome rule checked. The note
1384
+ * is reported once per migration so the narrowing is visible rather than silent.
1385
+ */
1386
+ const PARTIAL_RULE_MAPPING_NOTES = {
1387
+ noComponentHookFactories: "Biome rule noComponentHookFactories was mapped to react/no-unstable-nested-components, which reports nested component definitions but not nested custom hook definitions.",
1388
+ noReactPropAssignments: "Biome rule noReactPropAssignments was mapped to the React Compiler rule react/immutability, which reports prop mutation alongside other mutations of values React treats as immutable.",
1389
+ useExplicitType: "Biome rule useExplicitType was mapped to typescript/explicit-function-return-type, which requires return types on functions and methods but not types on variables or parameters.",
1390
+ useReactCompiler: `Biome rule useReactCompiler was mapped to Oxlint's React Compiler rules for ESLint's recommended preset (${REACT_COMPILER_RECOMMENDED_RULES.join(", ")}); the compiler rules that are off by default upstream were not enabled. See https://oxc.rs/blog/2026-08-18-react-compiler-support`
1391
+ };
1293
1392
  const BIOME_TO_OXLINT_RULE_MAP = {
1294
1393
  noAccessKey: "jsx-a11y/no-access-key",
1295
1394
  noAdjacentSpacesInRegex: "no-regex-spaces",
@@ -1311,6 +1410,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1311
1410
  noBaseToString: "typescript/no-base-to-string",
1312
1411
  noBeforeInteractiveScriptOutsideDocument: "nextjs/no-before-interactive-script-outside-document",
1313
1412
  noBitwiseOperators: "no-bitwise",
1413
+ noBlankTarget: "react/jsx-no-target-blank",
1314
1414
  noCatchAssign: "no-ex-assign",
1315
1415
  noChildrenProp: "react/no-children-prop",
1316
1416
  noClassAssign: "no-class-assign",
@@ -1321,6 +1421,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1321
1421
  "typescript/no-require-imports",
1322
1422
  "typescript/no-var-requires"
1323
1423
  ],
1424
+ noComponentHookFactories: "react/no-unstable-nested-components",
1324
1425
  noCompareNegZero: "no-compare-neg-zero",
1325
1426
  noConfusingLabels: "no-labels",
1326
1427
  noConfusingVoidType: "typescript/no-invalid-void-type",
@@ -1358,18 +1459,24 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1358
1459
  noEqualsToNull: "no-eq-null",
1359
1460
  noExcessiveClassesPerFile: "max-classes-per-file",
1360
1461
  noExcessiveCognitiveComplexity: "complexity",
1462
+ noExcessiveLinesPerFile: "max-lines",
1463
+ noExcessiveLinesPerFunction: "max-lines-per-function",
1361
1464
  noExcessiveNestedCallbacks: "max-nested-callbacks",
1362
1465
  noExplicitAny: "typescript/no-explicit-any",
1363
1466
  noExtendNative: "no-extend-native",
1364
1467
  noExtraBooleanCast: "no-extra-boolean-cast",
1365
1468
  noExtraNonNullAssertion: "typescript/no-extra-non-null-assertion",
1366
1469
  noFallthroughSwitchClause: "no-fallthrough",
1470
+ noFlatMapIdentity: "unicorn/prefer-array-flat",
1367
1471
  noFloatingPromises: "typescript/no-floating-promises",
1368
1472
  noFocusedTests: ["jest/no-focused-tests", "vitest/no-focused-tests"],
1369
1473
  noForEach: "unicorn/no-array-for-each",
1370
1474
  noFunctionAssign: "no-func-assign",
1371
1475
  noGlobalAssign: "no-global-assign",
1476
+ noGlobalDirnameFilename: "unicorn/prefer-module",
1372
1477
  noGlobalEval: "no-eval",
1478
+ noGlobalIsFinite: "unicorn/prefer-number-properties",
1479
+ noGlobalIsNan: "unicorn/prefer-number-properties",
1373
1480
  noGlobalObjectCalls: "no-obj-calls",
1374
1481
  noHeaderScope: "jsx-a11y/scope",
1375
1482
  noHeadElement: "nextjs/no-head-element",
@@ -1389,6 +1496,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1389
1496
  noIrregularWhitespace: "no-irregular-whitespace",
1390
1497
  noInteractiveElementToNoninteractiveRole: "jsx-a11y/no-interactive-element-to-noninteractive-role",
1391
1498
  noInvalidNewBuiltin: "no-new-native-nonconstructor",
1499
+ noJsRestrictedProperties: "no-restricted-properties",
1392
1500
  noJsxLiterals: "react/jsx-no-literals",
1393
1501
  noJsxNamespace: "react/no-namespace",
1394
1502
  noLabelWithoutControl: "jsx-a11y/label-has-associated-control",
@@ -1403,6 +1511,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1403
1511
  noMultilineString: "no-multi-str",
1404
1512
  noMultiStr: "no-multi-str",
1405
1513
  noNegationElse: "no-negated-condition",
1514
+ noNegationInEqualityCheck: "unicorn/no-negation-in-equality-check",
1406
1515
  noNewSymbol: "no-new-native-nonconstructor",
1407
1516
  noNodejsModules: "import/no-nodejs-modules",
1408
1517
  noNamespace: "typescript/no-namespace",
@@ -1426,12 +1535,14 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1426
1535
  noProto: "no-proto",
1427
1536
  noPrototypeBuiltins: "no-prototype-builtins",
1428
1537
  noRedeclare: "no-redeclare",
1538
+ noReactPropAssignments: "react/immutability",
1429
1539
  noRedundantAlt: "jsx-a11y/img-redundant-alt",
1430
1540
  noRedundantRoles: "jsx-a11y/no-redundant-roles",
1431
1541
  noReactStringRefs: "react/no-string-refs",
1432
1542
  noDuplicateTestHooks: ["jest/no-duplicate-hooks", "vitest/no-duplicate-hooks"],
1433
1543
  noConditionalExpect: ["jest/no-conditional-expect", "vitest/no-conditional-expect"],
1434
1544
  noExcessiveNestedTestSuites: ["jest/max-nested-describe", "vitest/max-nested-describe"],
1545
+ noExportsInTest: "jest/no-export",
1435
1546
  noIdenticalTestTitle: ["jest/no-identical-title", "vitest/no-identical-title"],
1436
1547
  noMisplacedAssertion: ["jest/no-standalone-expect", "vitest/no-standalone-expect"],
1437
1548
  noRenderReturnValue: "react/no-render-return-value",
@@ -1469,6 +1580,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1469
1580
  noUnreachable: "no-unreachable",
1470
1581
  noUnreachableSuper: "constructor-super",
1471
1582
  noUnsafeDeclarationMerging: "typescript/no-unsafe-declaration-merging",
1583
+ noUnsafeTypeAssertion: "typescript/no-unsafe-type-assertion",
1472
1584
  noUnsafeFinally: "no-unsafe-finally",
1473
1585
  noUnsafeNegation: "no-unsafe-negation",
1474
1586
  noUnsafeOptionalChaining: "no-unsafe-optional-chaining",
@@ -1479,8 +1591,10 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1479
1591
  noUnusedVariables: "no-unused-vars",
1480
1592
  noUnwantedPolyfillio: "nextjs/no-unwanted-polyfillio",
1481
1593
  noUselessCatch: "no-useless-catch",
1594
+ noUselessContinue: "no-continue",
1482
1595
  noUselessCatchBinding: "unicorn/prefer-optional-catch-binding",
1483
1596
  noUselessConstructor: "no-useless-constructor",
1597
+ noUselessElse: "no-else-return",
1484
1598
  noUselessEmptyExport: "typescript/no-useless-empty-export",
1485
1599
  noUselessEscapeInRegex: "no-useless-escape",
1486
1600
  noUselessEscapeInString: "no-useless-escape",
@@ -1497,6 +1611,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1497
1611
  noUselessTypeConstraint: "typescript/no-unnecessary-type-constraint",
1498
1612
  noUselessTypeConversion: "typescript/no-unnecessary-type-conversion",
1499
1613
  noUselessUndefined: "unicorn/no-useless-undefined",
1614
+ noUselessUndefinedInitialization: "unicorn/no-useless-undefined",
1500
1615
  noVoid: "no-void",
1501
1616
  noVar: "no-var",
1502
1617
  noVoidElementsWithChildren: "react/void-dom-elements-no-children",
@@ -1537,6 +1652,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1537
1652
  useConsistentMethodSignatures: "typescript/method-signature-style",
1538
1653
  useConsistentTypeDefinitions: "typescript/consistent-type-definitions",
1539
1654
  useConsistentMemberAccessibility: "typescript/explicit-member-accessibility",
1655
+ useConsistentObjectDefinitions: "object-shorthand",
1540
1656
  useConsistentTestIt: ["jest/consistent-test-it", "vitest/consistent-test-it"],
1541
1657
  useConst: "prefer-const",
1542
1658
  useDateNow: "unicorn/prefer-date-now",
@@ -1551,6 +1667,8 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1551
1667
  useErrorMessage: "unicorn/error-message",
1552
1668
  useExpect: ["jest/expect-expect", "vitest/expect-expect"],
1553
1669
  useExplicitLengthCheck: "unicorn/explicit-length-check",
1670
+ useExplicitReturnType: "typescript/explicit-function-return-type",
1671
+ useExplicitType: "typescript/explicit-function-return-type",
1554
1672
  useExponentiationOperator: "prefer-exponentiation-operator",
1555
1673
  useExportsLast: "import/exports-last",
1556
1674
  useExportType: "typescript/consistent-type-exports",
@@ -1573,6 +1691,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1573
1691
  useHookAtTopLevel: "react/rules-of-hooks",
1574
1692
  useIframeSandbox: "react/iframe-missing-sandbox",
1575
1693
  useIframeTitle: "jsx-a11y/iframe-has-title",
1694
+ useImportExtensions: "import/extensions",
1576
1695
  useImportsFirst: "import/first",
1577
1696
  useImportType: "typescript/consistent-type-imports",
1578
1697
  useIncludes: "typescript/prefer-includes",
@@ -1602,6 +1721,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1602
1721
  useParseIntRadix: "radix",
1603
1722
  useReadonlyClassProperties: "typescript/prefer-readonly",
1604
1723
  useReactFunctionComponentDefinition: "react/function-component-definition",
1724
+ useReactCompiler: REACT_COMPILER_RECOMMENDED_RULES,
1605
1725
  useReactFunctionComponents: "react/prefer-function-component",
1606
1726
  useReduceTypeParameter: "typescript/prefer-reduce-type-parameter",
1607
1727
  useRegexLiterals: "prefer-regex-literals",
@@ -1611,6 +1731,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1611
1731
  useSelfClosingElements: "react/self-closing-comp",
1612
1732
  useShorthandAssign: "operator-assignment",
1613
1733
  useShorthandFunctionType: "typescript/prefer-function-type",
1734
+ useSingleVarDeclarator: "one-var",
1614
1735
  useSimplifiedLogicExpression: "unicorn/prefer-logical-operator-over-ternary",
1615
1736
  useSpread: "prefer-spread",
1616
1737
  useSpreadOverApply: "prefer-spread",
@@ -1639,6 +1760,7 @@ const BIOME_TO_OXLINT_RULE_MAP = {
1639
1760
  useYield: "require-yield"
1640
1761
  };
1641
1762
  const WARNED_UNMAPPED_RULES_BY_REPORTER = /* @__PURE__ */ new WeakMap();
1763
+ const WARNED_PARTIAL_MAPPINGS_BY_REPORTER = /* @__PURE__ */ new WeakMap();
1642
1764
  const WARNED_UNSUPPORTED_SEVERITIES_BY_REPORTER = /* @__PURE__ */ new WeakMap();
1643
1765
  const OXLINT_SEVERITY_NORMALIZATION = {
1644
1766
  allow: "off",
@@ -1663,6 +1785,20 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
1663
1785
  if (biomeName === "noExcessiveNestedCallbacks") return [severity, { max: options && isNonNegativeInteger(options.max) ? options.max : 5 }];
1664
1786
  if (biomeName === "useErrorCause") return [severity, { requireCatchParameter: options && typeof options.requireCatchParameter === "boolean" ? options.requireCatchParameter : true }];
1665
1787
  if (biomeName === "useMaxParams") return [severity, { max: options && isNonNegativeInteger(options.max) ? options.max : 4 }];
1788
+ if (biomeName === "noExcessiveLinesPerFile") return [severity, {
1789
+ max: options && isNonNegativeInteger(options.maxLines) ? options.maxLines : 300,
1790
+ skipBlankLines: options?.skipBlankLines === true
1791
+ }];
1792
+ if (biomeName === "noExcessiveLinesPerFunction") return [severity, {
1793
+ max: options && isNonNegativeInteger(options.maxLines) ? options.maxLines : 50,
1794
+ skipBlankLines: options?.skipBlankLines === true,
1795
+ IIFEs: options?.skipIifes !== true
1796
+ }];
1797
+ if (biomeName === "useSingleVarDeclarator") return [severity, "never"];
1798
+ if (biomeName === "useImportExtensions") {
1799
+ if (options?.extensionMappings !== void 0 || options?.forceJsExtensions !== void 0) reporter.loss("Biome rule useImportExtensions options \"extensionMappings\" and \"forceJsExtensions\" are not supported by Oxlint import/extensions; the rule requires an extension without rewriting it.");
1800
+ return [severity, "ignorePackages"];
1801
+ }
1666
1802
  if (biomeName === "noTsIgnore") return [severity, {
1667
1803
  "ts-ignore": true,
1668
1804
  "ts-expect-error": false,
@@ -1674,6 +1810,10 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
1674
1810
  const { words } = options;
1675
1811
  return Array.isArray(words) && words.every((value) => typeof value === "string") ? [severity, { words }] : severity;
1676
1812
  }
1813
+ if (biomeName === "noConsole") {
1814
+ const { allow } = options;
1815
+ return Array.isArray(allow) && allow.every((value) => typeof value === "string") ? [severity, { allow }] : severity;
1816
+ }
1677
1817
  if (biomeName === "noEmptySource") {
1678
1818
  if (options.allowComments === true) reporter.loss("Biome rule noEmptySource option \"allowComments\" is not supported by Oxlint unicorn/no-empty-file and was not migrated.");
1679
1819
  return severity;
@@ -1714,6 +1854,22 @@ function mapBiomeRuleOptionsToOxlintSeverity(biomeName, severity, biomeSeverity,
1714
1854
  { requireReturnForObjectLiteral: options.requireForObjectLiteral }
1715
1855
  ] : [severity, mode];
1716
1856
  }
1857
+ if (biomeName === "noJsRestrictedProperties") {
1858
+ const { entries } = options;
1859
+ if (!Array.isArray(entries) || !entries.every(isRecord)) return severity;
1860
+ return [severity, ...entries];
1861
+ }
1862
+ if (biomeName === "useConsistentObjectDefinitions") {
1863
+ const { syntax } = options;
1864
+ return syntax === "shorthand" ? [severity, "always"] : syntax === "explicit" ? [severity, "never"] : severity;
1865
+ }
1866
+ if (biomeName === "useExplicitReturnType") {
1867
+ const oxlintOptions = {};
1868
+ if (typeof options.allowExpressions === "boolean") oxlintOptions.allowExpressions = options.allowExpressions;
1869
+ if (typeof options.allowIifes === "boolean") oxlintOptions.allowIIFEs = options.allowIifes;
1870
+ if (Array.isArray(options.allowedNames) && options.allowedNames.every((value) => typeof value === "string")) oxlintOptions.allowedNames = options.allowedNames;
1871
+ return Object.keys(oxlintOptions).length > 0 ? [severity, oxlintOptions] : severity;
1872
+ }
1717
1873
  if (biomeName === "useConsistentMethodSignatures") {
1718
1874
  const { style } = options;
1719
1875
  return style === "method" || style === "property" ? [severity, style] : severity;
@@ -1780,10 +1936,24 @@ function mapBiomeRuleSeverity(severity, reporter, biomeRuleName) {
1780
1936
  }
1781
1937
  function mapBiomeRuleToOxlintRules(biomeName, reporter) {
1782
1938
  const mapped = BIOME_TO_OXLINT_RULE_MAP[biomeName];
1783
- if (mapped) return [...new Set((Array.isArray(mapped) ? mapped : [mapped]).map((ruleName) => normalizeOxlintRuleName(ruleName)))];
1939
+ if (mapped) {
1940
+ const partialMappingNote = PARTIAL_RULE_MAPPING_NOTES[biomeName];
1941
+ if (partialMappingNote) warnPartialMappingOnce(biomeName, partialMappingNote, reporter);
1942
+ return [...new Set((Array.isArray(mapped) ? mapped : [mapped]).map((ruleName) => normalizeOxlintRuleName(ruleName)))];
1943
+ }
1784
1944
  warnUnmappedRuleOnce(biomeName, reporter);
1785
1945
  return [];
1786
1946
  }
1947
+ function warnPartialMappingOnce(biomeName, note, reporter) {
1948
+ let warnedRules = WARNED_PARTIAL_MAPPINGS_BY_REPORTER.get(reporter);
1949
+ if (!warnedRules) {
1950
+ warnedRules = /* @__PURE__ */ new Set();
1951
+ WARNED_PARTIAL_MAPPINGS_BY_REPORTER.set(reporter, warnedRules);
1952
+ }
1953
+ if (warnedRules.has(biomeName)) return;
1954
+ warnedRules.add(biomeName);
1955
+ reporter.loss(note);
1956
+ }
1787
1957
  function warnUnmappedRuleOnce(biomeName, reporter) {
1788
1958
  let warnedRules = WARNED_UNMAPPED_RULES_BY_REPORTER.get(reporter);
1789
1959
  if (!warnedRules) {
@@ -2121,15 +2291,19 @@ async function updatePackageJson(projectDir, reporter, dryRun, options = {}) {
2121
2291
  summary.found = true;
2122
2292
  let modified = false;
2123
2293
  const updateScriptsEnabled = options.updateScripts ?? false;
2294
+ const domModeEnabled = options.dom ?? false;
2124
2295
  const removeBiomeAllowed = options.removeBiome ?? false;
2125
2296
  const typeAwareProfile = options.typeAwareProfile ?? "standard";
2126
2297
  const typeCheckEnabled = options.typeCheck ?? typeAwareProfile === "strict";
2127
2298
  const typeAwareEnabled = options.typeAware ?? (typeCheckEnabled || typeAwareProfile === "strict");
2128
- const needsTypeAwareDependency = typeAwareEnabled || typeCheckEnabled;
2299
+ const needsTypeAwareDependency = typeAwareEnabled || typeCheckEnabled || domModeEnabled;
2129
2300
  const fixStrategy = options.fixStrategy ?? "safe";
2130
2301
  const oxlintCommand = buildConfiguredCommand("oxlint", packageJsonPath, options.oxlintConfigPath);
2131
2302
  const oxfmtCommand = buildConfiguredCommand("oxfmt", packageJsonPath, options.oxfmtConfigPath);
2132
- if (updateScriptsEnabled && packageJson.scripts) modified = updateScripts(packageJson.scripts, reporter, summary.scriptsUpdated, {
2303
+ if (domModeEnabled) {
2304
+ packageJson.scripts ??= {};
2305
+ modified = applyDomScriptPreset(packageJson.scripts, summary.scriptsUpdated, oxlintCommand, oxfmtCommand) || modified;
2306
+ } else if (updateScriptsEnabled && packageJson.scripts) modified = updateScripts(packageJson.scripts, reporter, summary.scriptsUpdated, {
2133
2307
  typeAwareEnabled,
2134
2308
  typeCheckEnabled,
2135
2309
  fixStrategy,
@@ -2204,6 +2378,32 @@ async function detectLockfile(packageRoot, dependenciesChanged) {
2204
2378
  };
2205
2379
  }
2206
2380
  }
2381
+ function applyDomScriptPreset(scripts, updates, oxlintCommand, oxfmtCommand) {
2382
+ let modified = false;
2383
+ const preset = {
2384
+ check: `${oxlintCommand} . && ${oxfmtCommand} --check .`,
2385
+ "check:fix": `${oxlintCommand} --fix . && ${oxfmtCommand} --write .`,
2386
+ format: `${oxfmtCommand} --write .`,
2387
+ "format:check": `${oxfmtCommand} --check .`,
2388
+ lint: `${oxlintCommand} -f github . > lint.md 2>&1`,
2389
+ "lint:fix": `${oxlintCommand} -f stylish --fix .`,
2390
+ "lint:fix-unsafe": `${oxlintCommand} -f stylish --react-plugin --import-plugin --react-perf-plugin --nextjs-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions --fix-dangerously .`,
2391
+ "check:fix-suggestions": `${oxlintCommand} -f stylish --react-plugin --import-plugin --react-perf-plugin --nextjs-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions . && ${oxfmtCommand} --write .`,
2392
+ "type-check": "tsc --noEmit"
2393
+ };
2394
+ for (const [scriptName, scriptValue] of Object.entries(preset)) {
2395
+ const before = scripts[scriptName];
2396
+ if (before === scriptValue) continue;
2397
+ scripts[scriptName] = scriptValue;
2398
+ updates.push({
2399
+ name: scriptName,
2400
+ before: before ?? "<missing>",
2401
+ after: scriptValue
2402
+ });
2403
+ modified = true;
2404
+ }
2405
+ return modified;
2406
+ }
2207
2407
  function updateScripts(scripts, reporter, updates, options) {
2208
2408
  let modified = false;
2209
2409
  const lintBase = buildLintBaseCommand(options.oxlintCommand, options.typeAwareEnabled, options.typeCheckEnabled);
@@ -2810,6 +3010,7 @@ async function runMigration(options, reporter, biomeConfigPath) {
2810
3010
  const packageJsonErrorCount = reporter.getErrors().length;
2811
3011
  packageJsonSummary = await updatePackageJson(projectDir, reporter, false, {
2812
3012
  updateScripts: options.updateScripts,
3013
+ dom: options.dom,
2813
3014
  removeBiome: cleanupAllowed,
2814
3015
  typeAware: typeAwareEnabled,
2815
3016
  typeCheck: typeCheckEnabled,
@@ -2838,6 +3039,7 @@ async function runMigration(options, reporter, biomeConfigPath) {
2838
3039
  const packageJsonErrorCount = reporter.getErrors().length;
2839
3040
  packageJsonSummary = await updatePackageJson(projectDir, reporter, true, {
2840
3041
  updateScripts: options.updateScripts,
3042
+ dom: options.dom,
2841
3043
  removeBiome: cleanupAllowed,
2842
3044
  typeAware: typeAwareEnabled,
2843
3045
  typeCheck: typeCheckEnabled,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biome-to-oxc",
3
- "version": "2.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Migrate from Biome to Oxc ecosystem (oxlint + oxfmt + oxlint-tsgolint)",
5
5
  "keywords": [
6
6
  "biome",
@@ -32,6 +32,24 @@
32
32
  "types": "./dist/src/index.d.mts",
33
33
  "import": "./dist/src/index.mjs"
34
34
  },
35
+ "scripts": {
36
+ "test": "vitest",
37
+ "test:run": "vitest --run",
38
+ "test:coverage": "vitest --run --coverage",
39
+ "format": "oxfmt --write .",
40
+ "format:check": "oxfmt --check .",
41
+ "lint": "oxlint .",
42
+ "lint:fix": "oxlint --fix .",
43
+ "lint:fix-unsafe": "oxlint --import-plugin --node-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions --fix-dangerously .",
44
+ "check:fix-suggestions": "oxlint --import-plugin --node-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions . && oxfmt --write .",
45
+ "typecheck": "tsc --noEmit",
46
+ "docs:sync": "node scripts/sync-rule-inventories.ts",
47
+ "docs:check": "node scripts/sync-rule-inventories.ts --check",
48
+ "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm docs:check && pnpm test:run",
49
+ "build": "tsdown",
50
+ "dev": "tsdown --watch",
51
+ "prepublishOnly": "pnpm check && pnpm build"
52
+ },
35
53
  "dependencies": {
36
54
  "commander": "15.0.0",
37
55
  "jsonc-parser": "next",
@@ -40,34 +58,20 @@
40
58
  },
41
59
  "devDependencies": {
42
60
  "@arethetypeswrong/core": "^0.18.5",
43
- "@biomejs/biome": "^2.5.7",
61
+ "@biomejs/biome": "^2.5.9",
44
62
  "@types/node": "^26.2.0",
45
- "@vitest/coverage-v8": "^4.1.10",
46
- "oxfmt": "^0.63.0",
47
- "oxlint": "^1.78.0",
63
+ "@vitest/coverage-v8": "^4.1.11",
64
+ "oxfmt": "^0.64.0",
65
+ "oxlint": "^1.79.0",
48
66
  "oxlint-tsgolint": "^7.0.2001",
49
- "publint": "^0.3.23",
67
+ "publint": "^0.3.24",
50
68
  "tsdown": "^0.22.14",
51
- "typescript": "^7.0.2",
52
- "vitest": "^4.1.10"
69
+ "typescript": "next",
70
+ "vitest": "^4.1.11"
53
71
  },
54
72
  "engines": {
55
73
  "node": ">=24.19.0",
56
- "pnpm": ">=11.21.0"
74
+ "pnpm": ">=11.22.0"
57
75
  },
58
- "scripts": {
59
- "test": "vitest",
60
- "test:run": "vitest --run",
61
- "test:coverage": "vitest --run --coverage",
62
- "format": "oxfmt --write .",
63
- "format:check": "oxfmt --check .",
64
- "lint": "oxlint .",
65
- "lint:fix": "oxlint --fix .",
66
- "lint:fix-unsafe": "oxlint --import-plugin --node-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions --fix-dangerously .",
67
- "check:fix-suggestions": "oxlint --import-plugin --node-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions . && oxfmt --write .",
68
- "typecheck": "tsc --noEmit",
69
- "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:run",
70
- "build": "tsdown",
71
- "dev": "tsdown --watch"
72
- }
73
- }
76
+ "packageManager": "pnpm@11.22.0"
77
+ }