#biome (4)
問題
Biome v2.4.7 で Vue SFC の <template> 部分が biome format でフォーマットされない。
解決策
biome.json に3つの設定が必須:
{
"files": {
"includes": ["**", ".dotdir/**"]
},
"html": {
"experimentalFullSupportEnabled": true,
"formatter": {
"enabled": true
}
}
}
重要なポイント
experimentalFullSupportEnabled: true— v2.3で追加。Vue/Svelte/Astro の SFC内の全パート(template, script, style)の完全な解析を有効化。これなしでは<script>と<style>のみ処理html.formatter.enabled: true— デフォルトはfalse。biome formatコマンドが HTMLを処理するための明示的な有効化が必要。biome check --writeはlint経由で動くため気づきにくいfiles.includesにドットディレクトリを明示 —.vitepress/**のようなドットで始まるディレクトリはデフォルトスキャン対象外
既知の制限
- 複雑な TypeScript ジェネリクス(
ref<InstanceType<typeof X>>)でパースエラーが発生するケースあり v-for="item of items"のof構文がパースエラーになる(inは問題なし)- パースエラーが出るファイルはフォーマットがスキップされる(ファイルは壊れない)
formatWithErrors: trueは危険 — パースエラー時にSFCタグが消える・コードが壊れるリスクがある
1. TypeScript設定
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"types": ["vitepress/client"]
},
"include": [".vitepress/**/*.ts", ".vitepress/**/*.vue"],
"vueCompilerOptions": {
"vitePressExtensions": [".md"]
}
}
ポイント
vitepress/clientが内部でvite/clientを参照し.vueモジュールの型宣言を含むため、env.d.tsやshims-vue.d.tsは不要vueCompilerOptions.vitePressExtensionsでMarkdown内のVueコードもVolarが認識moduleResolution: "bundler"はVite系プロジェクトの必須設定
pnpm strict mode での注意
VitePressの内部依存(vue等)が node_modules/ 直下にhoistされない。Cannot find module vue エラーが出る場合は pnpm add -D vue で明示的にインストールする。
テーマの型付け
EnhanceAppContext を使うと型展開が深すぎて Excessive stack depth comparing types (ts 2321) になる。必要な部分だけ型付けして回避する。
import type { App } from "vue";
export default {
extends: DefaultTheme,
enhanceApp({ app }: { app: App }) {
// ...
},
};
2. サイドバー構築
ネスト構造の設計
VitePressのサイドバーは collapsed フラグでツリーを制御可能。
{
text: 'ドキュメント',
collapsed: false,
items: [
{
text: 'セクションA',
collapsed: true,
items: [
{ text: 'ページ1', link: '/page1' },
{ text: 'ページ2', link: '/page2' }
]
}
]
}
設計指針
- 深すぎるネストは避ける: 3階層(ルート→カテゴリ→ページ)が可読性の限界
- collapsed デフォルト: 関連トピックは折りたたんで関心別アクセスを支援
- Notionのデータベース構造(parent category → pages)をそのままネスト構造に変換すると自然
- ページ移行後、サイドバー生成スクリプトで自動構築可能
3. lefthook + Biome 設定
lefthook.yml
pre-commit:
commands:
biome:
glob: '*.{ts,js,json,jsonc,vue,css}'
run: pnpm biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
stage_fixed: true
stage_fixed: true で自動修正後のファイルを再stageしてくれる。
4. Markdown変換パターン(Notion → VitePress)
ブロックタイプ別変換
| Notion | Markdown |
|---|---|
| Paragraph | 通常段落 |
| Heading 1-3 | #, ##, ### |
| Bulleted List | - item |
| Numbered List | 1. item |
| Code Block | ```code``` |
| Toggle | <details><summary> |
| Callout | > [icon] メッセージ |
| Quote | > 引用文 |
リンク変換
- Notion内部リンク → ページIDから
slugにマッピングして相対パスに変換 - 外部リンク → そのまま保持
- ファイルリンク → CDN URLに変換(必要に応じて)
ビルド時検証
VitePressビルドでデッドリンクが検出される。Notionリンク残存の有無を自動検知可能。
問題
Zedで .vue ファイルを保存すると、CLIの biome format と微妙に異なるフォーマットが適用される。インデントや閉じタグ前の空白など、細かい差分が毎回発生する。
原因
.zed/settings.json で "formatter": "language_server" と指定すると、Vueファイルでは Volar(Vue LSP)が優先的にフォーマッタとして使われる。BiomeのLSPも動いているが、Volarが先に処理してしまう。
解決策
プロジェクトの .zed/settings.json でVue.jsのフォーマッタにBiome LSPを 名前指定 する。
{
"languages": {
"Vue.js": {
"formatter": {
"language_server": {
"name": "biome"
}
}
}
}
}
"formatter": "language_server" (名前なし)ではなく、"name": "biome" で明示することがポイント。
補足
external コマンドで pnpm biome format --stdin-file-path を呼ぶ方法でも動くが、LSP経由の方がZedのエコシステムに沿った正攻法。
Biome 2.xからSvelteファイルのフォーマットに対応した。biome.jsonで以下のように設定する。
{
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"files": {
"includes": ["**/*.svelte"]
}
}
ただし、Svelteの<script>タグ内のみがフォーマット対象。テンプレート部分(HTML)は対象外なので注意。
# フォーマット実行
pnpm biome format --write "src/**/*.svelte"