Open7

Biomeへ移行する

kmkkiiikmkkiii

既存のプロジェクトに対してBiome移行を試してみる

> npm i --save-dev --save-exact @biomejs/biome@latest

added 3 packages, removed 81 packages, and audited 891 packages in 5s

157 packages are looking for funding
  run `npm fund` for details

3 vulnerabilities (2 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

何も考えずmigrateコマンド実行したけどセットアップしてないので設定ファイルがない。

> npx @biomejs/biome migrate
If this project has not yet been set up with Biome yet, please follow the Getting Started guide first.
migrate ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Migration has encountered an error: Biome couldn't find the Biome configuration file.
  

初期設定コマンド実行

> npx @biomejs/biome init   

Welcome to Biome! Let's get you started...

Files created 

  - biome.json
    Your project configuration. See https://biomejs.dev/reference/configuration

Next Steps 

  1. Setup an editor extension
     Get live errors as you type and format when you save.
     Learn more at https://biomejs.dev/guides/integrate-in-editor/

  2. Try a command
     biome check  checks formatting, import sorting, and lint rules.
     biome --help displays the available commands.

  3. Migrate from ESLint and Prettier
     biome migrate eslint   migrates your ESLint configuration to Biome.
     biome migrate prettier migrates your Prettier configuration to Biome.

  4. Read the documentation
     Find guides and documentation at https://biomejs.dev/guides/getting-started/

  5. Get involved with the community
     Ask questions and contribute on GitHub: https://github.com/biomejs/biome
     Seek for help on Discord: https://discord.gg/BypW39g6Yc

設定ファイルができた。デフォルトはタブ?インデントが深い

biome.json
{
	"$schema": "https://biomejs.dev/schemas/1.7.0/schema.json",
	"organizeImports": {
		"enabled": true
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true
		}
	}
}
kmkkiiikmkkiii

jsoncも対応してた!

> npx @biomejs/biome init --jsonc
kmkkiiikmkkiii

ESLintからの移行コマンド

>  npx biome migrate eslint --write
migrate ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Migration has encountered an error: The module '/app/node_modules/eslint-config-next/index.js' cannot be loaded. Make sure that the module exists.
  

.eslintignore has been successfully migrated.
./.eslintrc.js has been successfully migrated.
Run the command with the option --include-inspired to also migrate inspired rules.
biome.json
{
	"$schema": "https://biomejs.dev/schemas/1.7.0/schema.json",
	"organizeImports": { "enabled": true },
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": false,
			"complexity": {
				"noBannedTypes": "error",
				"noExtraBooleanCast": "error",
				"noMultipleSpacesInRegularExpressionLiterals": "error",
				"noUselessCatch": "error",
				"noUselessTypeConstraint": "error",
				"noWith": "error"
			},
			"correctness": {
				"noConstAssign": "error",
				"noConstantCondition": "error",
				"noEmptyCharacterClassInRegex": "error",
				"noEmptyPattern": "error",
				"noGlobalObjectCalls": "error",
				"noInnerDeclarations": "error",
				"noInvalidConstructorSuper": "error",
				"noNewSymbol": "error",
				"noNonoctalDecimalEscape": "error",
				"noPrecisionLoss": "error",
				"noSelfAssign": "error",
				"noSetterReturn": "error",
				"noSwitchDeclarations": "error",
				"noUndeclaredVariables": "error",
				"noUnreachable": "error",
				"noUnreachableSuper": "error",
				"noUnsafeFinally": "error",
				"noUnsafeOptionalChaining": "error",
				"noUnusedLabels": "error",
				"noUnusedVariables": "error",
				"useIsNan": "error",
				"useValidForDirection": "error",
				"useYield": "error"
			},
			"style": {
				"noNamespace": "error",
				"useAsConstAssertion": "error",
				"useBlockStatements": "off"
			},
			"suspicious": {
				"noAsyncPromiseExecutor": "error",
				"noCatchAssign": "error",
				"noClassAssign": "error",
				"noCompareNegZero": "error",
				"noControlCharactersInRegex": "error",
				"noDebugger": "error",
				"noDuplicateCase": "error",
				"noDuplicateClassMembers": "error",
				"noDuplicateObjectKeys": "error",
				"noDuplicateParameters": "error",
				"noEmptyBlockStatements": "error",
				"noExplicitAny": "error",
				"noExtraNonNullAssertion": "error",
				"noFallthroughSwitchClause": "error",
				"noFunctionAssign": "error",
				"noGlobalAssign": "error",
				"noImportAssign": "error",
				"noMisleadingCharacterClass": "error",
				"noMisleadingInstantiator": "error",
				"noPrototypeBuiltins": "error",
				"noRedeclare": "error",
				"noShadowRestrictedNames": "error",
				"noUnsafeDeclarationMerging": "error",
				"noUnsafeNegation": "error",
				"useAwait": "error",
				"useGetterReturn": "error",
				"useValidTypeof": "error"
			}
		},
		"ignore": ["src/generated/*", "src/**/*.d.ts"]
	},
	"overrides": [
		{
			"include": ["*.ts", "*.tsx", "*.mts", "*.cts"],
			"linter": {
				"rules": {
					"correctness": {
						"noConstAssign": "off",
						"noGlobalObjectCalls": "off",
						"noInvalidConstructorSuper": "off",
						"noNewSymbol": "off",
						"noSetterReturn": "off",
						"noUndeclaredVariables": "off",
						"noUnreachable": "off",
						"noUnreachableSuper": "off"
					},
					"style": {
						"noArguments": "error",
						"noVar": "error",
						"useConst": "error"
					},
					"suspicious": {
						"noDuplicateClassMembers": "off",
						"noDuplicateObjectKeys": "off",
						"noDuplicateParameters": "off",
						"noFunctionAssign": "off",
						"noImportAssign": "off",
						"noRedeclare": "off",
						"noUnsafeNegation": "off",
						"useGetterReturn": "off"
					}
				}
			}
		},
		{
			"include": ["*.ts", "*.tsx", "*.mts", "*.cts"],
			"linter": {
				"rules": {
					"correctness": {
						"noConstAssign": "off",
						"noGlobalObjectCalls": "off",
						"noInvalidConstructorSuper": "off",
						"noNewSymbol": "off",
						"noSetterReturn": "off",
						"noUndeclaredVariables": "off",
						"noUnreachable": "off",
						"noUnreachableSuper": "off"
					},
					"style": {
						"noArguments": "error",
						"noVar": "error",
						"useConst": "error"
					},
					"suspicious": {
						"noDuplicateClassMembers": "off",
						"noDuplicateObjectKeys": "off",
						"noDuplicateParameters": "off",
						"noFunctionAssign": "off",
						"noImportAssign": "off",
						"noRedeclare": "off",
						"noUnsafeNegation": "off",
						"useGetterReturn": "off"
					}
				}
			}
		}
	]
}
kmkkiiikmkkiii

Prettierからの移行コマンド

> npx biome migrate prettier --write
.prettierignore has been successfully migrated.
./prettier.config.js has been successfully migrated.
biome.json
{
	"$schema": "https://biomejs.dev/schemas/1.7.0/schema.json",
	"formatter": {
		"enabled": true,
		"formatWithErrors": false,
		"indentStyle": "space",
		"indentWidth": 2,
		"lineEnding": "lf",
		"lineWidth": 80,
		"attributePosition": "auto",
		"ignore": ["src/generated/*"]
	},
	"organizeImports": { "enabled": true },
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": false,
			"complexity": {
				"noBannedTypes": "error",
				"noExtraBooleanCast": "error",
				"noMultipleSpacesInRegularExpressionLiterals": "error",
				"noUselessCatch": "error",
				"noUselessTypeConstraint": "error",
				"noWith": "error"
			},
			"correctness": {
				"noConstAssign": "error",
				"noConstantCondition": "error",
				"noEmptyCharacterClassInRegex": "error",
				"noEmptyPattern": "error",
				"noGlobalObjectCalls": "error",
				"noInnerDeclarations": "error",
				"noInvalidConstructorSuper": "error",
				"noNewSymbol": "error",
				"noNonoctalDecimalEscape": "error",
				"noPrecisionLoss": "error",
				"noSelfAssign": "error",
				"noSetterReturn": "error",
				"noSwitchDeclarations": "error",
				"noUndeclaredVariables": "error",
				"noUnreachable": "error",
				"noUnreachableSuper": "error",
				"noUnsafeFinally": "error",
				"noUnsafeOptionalChaining": "error",
				"noUnusedLabels": "error",
				"noUnusedVariables": "error",
				"useIsNan": "error",
				"useValidForDirection": "error",
				"useYield": "error"
			},
			"style": {
				"noNamespace": "error",
				"useAsConstAssertion": "error",
				"useBlockStatements": "off"
			},
			"suspicious": {
				"noAsyncPromiseExecutor": "error",
				"noCatchAssign": "error",
				"noClassAssign": "error",
				"noCompareNegZero": "error",
				"noControlCharactersInRegex": "error",
				"noDebugger": "error",
				"noDuplicateCase": "error",
				"noDuplicateClassMembers": "error",
				"noDuplicateObjectKeys": "error",
				"noDuplicateParameters": "error",
				"noEmptyBlockStatements": "error",
				"noExplicitAny": "error",
				"noExtraNonNullAssertion": "error",
				"noFallthroughSwitchClause": "error",
				"noFunctionAssign": "error",
				"noGlobalAssign": "error",
				"noImportAssign": "error",
				"noMisleadingCharacterClass": "error",
				"noMisleadingInstantiator": "error",
				"noPrototypeBuiltins": "error",
				"noRedeclare": "error",
				"noShadowRestrictedNames": "error",
				"noUnsafeDeclarationMerging": "error",
				"noUnsafeNegation": "error",
				"useAwait": "error",
				"useGetterReturn": "error",
				"useValidTypeof": "error"
			}
		},
		"ignore": ["src/generated/*", "src/**/*.d.ts"]
	},
	"javascript": {
		"formatter": {
			"jsxQuoteStyle": "double",
			"quoteProperties": "asNeeded",
			"trailingComma": "es5",
			"semicolons": "asNeeded",
			"arrowParentheses": "always",
			"bracketSpacing": true,
			"bracketSameLine": false,
			"quoteStyle": "single",
			"attributePosition": "auto"
		}
	},
	"overrides": [
		{
			"include": ["*.ts", "*.tsx", "*.mts", "*.cts"],
			"linter": {
				"rules": {
					"correctness": {
						"noConstAssign": "off",
						"noGlobalObjectCalls": "off",
						"noInvalidConstructorSuper": "off",
						"noNewSymbol": "off",
						"noSetterReturn": "off",
						"noUndeclaredVariables": "off",
						"noUnreachable": "off",
						"noUnreachableSuper": "off"
					},
					"style": {
						"noArguments": "error",
						"noVar": "error",
						"useConst": "error"
					},
					"suspicious": {
						"noDuplicateClassMembers": "off",
						"noDuplicateObjectKeys": "off",
						"noDuplicateParameters": "off",
						"noFunctionAssign": "off",
						"noImportAssign": "off",
						"noRedeclare": "off",
						"noUnsafeNegation": "off",
						"useGetterReturn": "off"
					}
				}
			}
		},
		{
			"include": ["*.ts", "*.tsx", "*.mts", "*.cts"],
			"linter": {
				"rules": {
					"correctness": {
						"noConstAssign": "off",
						"noGlobalObjectCalls": "off",
						"noInvalidConstructorSuper": "off",
						"noNewSymbol": "off",
						"noSetterReturn": "off",
						"noUndeclaredVariables": "off",
						"noUnreachable": "off",
						"noUnreachableSuper": "off"
					},
					"style": {
						"noArguments": "error",
						"noVar": "error",
						"useConst": "error"
					},
					"suspicious": {
						"noDuplicateClassMembers": "off",
						"noDuplicateObjectKeys": "off",
						"noDuplicateParameters": "off",
						"noFunctionAssign": "off",
						"noImportAssign": "off",
						"noRedeclare": "off",
						"noUnsafeNegation": "off",
						"useGetterReturn": "off"
					}
				}
			}
		}
	]
}

kmkkiiikmkkiii

package.jsonのscript変更

package.json
{
  "name": "app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "tsc": "tsc --noEmit",
-    "lint": "next lint",
-    "prettier": "prettier --check .",
-    "format": "next lint --fix && prettier --write .",
+    "lint": "biome lint --apply .",
+    "format": "biome format .",
+    "check": "biome check --apply .",
  },
  ...
}
kmkkiiikmkkiii

biome check実行してみたけど爆速。
一部import orderでeslintに怒らている部分があったので、移行できていないルールがないか要確認

> npm run check
...
Skipped 39 suggested fixes.
If you wish to apply the suggested (unsafe) fixes, use the command biome check --apply-unsafe

The number of diagnostics exceeds the number allowed by Biome.
Diagnostics not shown: 327.
Checked 272 files in 163ms. Fixed 58 files.
Found 27 errors.
check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Some errors were emitted while applying fixes.