thumbnail

※本ブログの目的と内容1、読者の方へ2

パッケージ

https://github.com/o3osatoshi/portfolio/tree/main/packages/config

役割

  • tsconfig … 型チェック
  • tsup … ビルド&バンドル
  • biome … フォーマット&リント
  • eslint … ソート

tsconfig

https://github.com/o3osatoshi/portfolio/tree/main/packages/config/tsconfig

型チェックのみに利用する。 共通の設定を base.json にまとめ、各パッケージ・アプリケーションで継承する。

base.json

厳格性に関する設定

厳格さをできる限り高める。

設定 解説
noUncheckedIndexedAccess 添字アクセスの未定義性を明示
arr[i]obj[key] の結果型に undefined が付き取りこぼし検出
exactOptionalPropertyTypes オプショナルと undefined を厳密に区別
「存在しない」と「存在して undefined」の混同を防止
noPropertyAccessFromIndexSignature インデックスシグネチャ経由の安易なアクセスを禁止
マップ/辞書型での誤アクセスを型で検出
noImplicitThis this の暗黙 any を禁止
コールバック等での文脈ミスを抑止
noImplicitReturns すべての分岐で return を要求
値の返し忘れを検出
noFallthroughCasesInSwitch switch の意図しないフォールスルーを禁止
break 書き忘れによるバグを防止
noImplicitOverride 派生クラスでの上書きに override を必須化
親の変更で別メソッド扱いになる事故を防ぐ
noUnusedLocals 未使用ローカル変数をエラーに
デッドコードを早期発見。意図的未使用は _ 始まりに
noUnusedParameters 未使用の関数引数をエラーに
API 設計を明確化。意図的未使用は _ 始まりに
useUnknownInCatchVariables catch (e)unknown とする
例外を型安全に絞り込みできる(型ガード必須)
forceConsistentCasingInFileNames ファイル名の大文字小文字ゆれをエラー化
OS 差によるビルド/実行不一致を防止
resolveJsonModule *.json を型付きで import 可能に
JSON 構造の型検査・補完が有効に
esModuleInterop CJS/ESM の相互運用を改善
default 周りの非互換を緩和しつつ型整合
isolatedModules 各ファイル単体で安全にトランスパイル
const enum 等を禁止しバンドラ互換性を確保
verbatimModuleSyntax import/export の書き換え最小化
ツリーシェイク精度向上・不要コード混入防止
skipLibCheck 外部型定義の再検査をスキップ
ビルド高速化。ただし外部 .d.ts の不整合は見逃す

互換性に関する設定

ESMをベースとしつつ、CJSやバンドラ挙動とも整合を取る。

設定 解説
esModuleInterop CJS と ESM の相互運用を改善(default 周りの扱いを円滑化)
既存の CJS ライブラリを import foo from "lib" で扱いやすく、互換性の罠を回避
isolatedModules 各ファイル単位で安全に変換できる制約を導入(バンドラ互換)
esbuild/tsup/Babel 等との相性が安定し、部分ビルドや並列ビルドで破綻しにくい
moduleDetection: "force" すべてのファイルをモジュールとして扱い、暗黙スクリプトを排除
グローバル汚染や読み込み順依存を避け、大規模構成での挙動を安定化
resolveJsonModule *.json を型付き import 可能にして設定/定数の取り回しを改善
JSON の構造に補完が効き、設定の互換性チェックがしやすい
skipLibCheck 外部 .d.ts の再検査を省き、外部依存の型ズレで止まりにくくする
依存更新に強く、CI/ローカルの型チェックも軽量化(厳密性はやや下げる)
verbatimModuleSyntax import/export の書き換えを最小化して意味を保持
ツリーシェイクの結果が安定し、最終成果物が軽量化しやすい(バンドラ互換性↑)
forceConsistentCasingInFileNames ファイル名の大文字小文字ゆれをエラー化
mac では動くのに CI(Linux) で壊れる、といったクロスOS不整合を事前に回避

next.json(Nextアプリ)

汎用設定

設定 解説
jsx: "preserve" JSX を TS が変換せずそのまま残し、後段(SWC/webpack)が処理できるようにする
use client の境界判定や Server/Client Component の最適化を Next に委譲でき、RSC 変換と整合が取れる
lib: ["ES2022","DOM","DOM.Iterable"] ブラウザ API(DOM/Iterable)とモダン標準 API を型として有効化
Client Component で DOM 補完が利く一方、Server 側で DOM を参照すると型で気づける(境界違反の早期発見)
moduleResolution: "Bundler" パッケージ解決をバンドラ挙動に合わせる(exports 条件・サブパス・エイリアスなど)
型解決と実行時解決が一致し、「型だけ赤い/実行は通る」ズレを解消。next/* 仮想モジュールとも相性良い
plugins: [{ "name": "next" }] Next.js 用 TS プラグインでフレームワーク固有の検査を強化
generateMetadata / generateStaticParams / Route Handlers などの型誤用を早期に検出できる

個別設定

設定 解説
include: ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts"] 型チェック対象を TS/TSX ファイルと Next.js が自動生成する型ファイルに限定

next-env.d.ts: Next.js 基本型(NextPagenext/image 等)を有効化。外すと Next 固有の型がエラーになる
.next/types/**/*.ts: App Router 専用に生成されるルート型を取り込み。外すと paramsgenerateMetadataany になり型安全性を失う

browser.json(React UIライブラリ)

汎用設定

設定 解説
jsx: "react-jsx" TypeScript に新しい React JSX 変換を使わせる設定
React.createElement を書かずにコンパクトなコードを扱える。UIライブラリとして利用される側でも、JSX の型推論が正しく効く
types: ["react","react-dom","node"] グローバルに読み込む型定義を指定
react/react-dom の型で JSX/DOM の型付けが安定。Node 型は UI 専用なら不要だが、SSRやStorybookでの Node API 利用を想定するなら役立つ

tsup

ビルド&バンドルに利用する。

共通設定

設定 解説
treeshake: true すべてのビルドでツリーシェイクを有効化
未使用コードを落としてバンドルを軽量化UI/Functions/Libraryすべてに有効

browserPreset(React UIライブラリ)

設定 解説
format: ["esm"] ESM のみ
モダンバンドラ/Next 環境は ESM 前提。CJS は不要かつ複雑化要因
platform: "browser" ブラウザ実行を前提に解決/ポリフィルを最適化
DOM 依存や Web API 前提コードが混在しうる UI 層に適合
target: "es2022" モダンブラウザ向け出力
Next/ESBuild の既定と親和性高く、不要なダウンレベル化を避ける
splitting: true コード分割を許可
複数エントリ/動的 import でのキャッシュ・再利用・初期ロード削減に有利
sourcemap: false 既定ではオフ
最終アプリ側のビルド/デバッグフローと二重になりがち。必要時のみON
minify: false 既定は可読性優先
Next 本体側で最終最適化されるため、ここでは縮小必須でない
external: ["react","react-dom","next", ...] React/Next を確実に external
ダブルバンドル回避とホストアプリ(Next)側の最適化を尊重

ビルド

Client Componentの成果物には、先頭に"use client";が必要。 しかし、tsup等でビルドすると、tsxファイル先頭の"use client";は削除されてしまう。 対策として、ビルド完了後に成果物の先頭に"use client";を後から付与する。

ポイント
  • ClientサイドのコードとSeverサイドのコードを別々にバレルする
  • 成果物はClientサイドとServerサイドのそれぞれでバンドルする
  • tsuponSuccessフックで、Clientサイドの成果物の先頭に"use client";を付与する

※コンポーネント毎にビルドし、それぞれに"use client";を付与する方法もあるが、管理が少々煩雑になる

設定
// tsup.client.config.mjs
export default await browserPreset({
  bundle: true,
  entry: { index: "src/index.client.ts" },
  onSuccess: "node ./scripts/ensure-use-client.mjs dist/client/index.js",
  outDir: "dist/client",
  splitting: false,
});
// tsup.server.config.mjs
export default await browserPreset({
  bundle: true,
  entry: { index: "src/index.server.ts" },
  outDir: "dist/server",
  splitting: false,
});

functionsPreset(Firebase Functions)

設定項目 解説
format: ["esm"] ESM 出力
Node 18+/22 環境での ESM サポートに合わせ、将来性と互換性を確保
platform: "node" Node 向け解決・ポリフィル
GCF/Functions の Node 実行に適合
target: "node22" Functions の実行環境(Node 22)に最適化
余計なトランスパイルを避け、起動速度/互換性を担保
splitting: false コード分割しない
単一ファイルのほうがデプロイ/コールドスタートの挙動が読みやすく、運用が安定
sourcemap: true 常時ソースマップ
本番障害解析(Stacktrace 解決)が重要Functions では運用保守性を優先
minify: isProd 本番/CI では縮小、それ以外は可読性優先
ランタイム転送量/起動時間を抑えつつ、ローカル開発はデバッグ性を維持
dts: false 型定義は配布不要
実行用アプリケーション配布(=ライブラリ配布ではない)ため

ビルド

Firebaseの仕様から、ビルドまわり(特に依存管理)が少々特殊になっている。 詳細はこちらの投稿を参照のこと。

publicDualPreset(外部配布ライブラリ)

設定項目 解説
format: ["esm","cjs"] デュアル出力
依然として CJS を要求するツール/環境に配慮しつつ、モダン環境の ESM も提供採用障壁を下げる
platform: "node"
target: "es2022"
ライブラリ出力としてモダン Node/バンドラで扱いやすい設定
余計なダウンレベル化を避けつつ、幅広いツールチェーンで安定動作
splitting: true コード分割許可
複数エントリや副次的モジュールを持つ配布に対応消費側のバンドラにとっても再利用性が高い
sourcemap: isProd 本番ビルドでソースマップを同梱
利用者のデバッグ体験向上開発時は不要、配布時のみ付けるバランス
minify: false 既定は非縮小
配布ライブラリは利用側で最終最適化されることが多く、可読性・差分レビューを重視必要に応じて ON 可

biome

フォーマット&リントに利用する。

base.json

設定 解説
assist.actions.source.organizeImports インポート整理の自動修正をオフ
ESLintの perfectionist を優先
linter.domains.project Project ドメインの推奨ルールを有効化
依存関係や import サイクルなどを検知
linter.domains.test Test ドメインの推奨ルールを有効化
テストコードの品質と誤用防止に寄与
linter.rules.complexity.useLiteralKeys リテラルキー強制をオフ
TS の noPropertyAccessFromIndexSignature を優先し、ブラケット記法を許容

適用

全体にbiomeを反映させるため、ルートに以下のbiome.jsonを配置する。

// /biome.json
{
  "extends": ["@o3osatoshi/config/biome/base.json"],
  "root": true
}

next.json(Nextアプリ)

設定 解説
linter.domains.next Next.js ドメインの推奨ルールを有効化
Next.js 固有のアンチパターン防止やベストプラクティスを自動検知

適用

個別にbiome.jsonを配置することで、設定の上書きが可能。nextやreact用の設定反映範囲を限定できる。

// /apps/web/biome.json
{
  "extends": [
    "@o3osatoshi/config/biome/base.json",
    "@o3osatoshi/config/biome/react.json",
    "@o3osatoshi/config/biome/next.json"
  ],
  "root": false
}

read.json(Reactアプリ)

設定 解説
linter.domains.react React ドメインの推奨ルールを有効化
Hooks や JSX に関するベストプラクティスを自動検知
linter.rules.nursery.useSortedClasses JSX の className 等をソート
clsx, cva, tw 系関数や classList 属性のクラス順序を強制
linter.rules.suspicious.noDuplicateJsxProps JSX 属性の重複を禁止
予期しない上書きやバグを防止

適用

// /packages/ui/biome.json
{
  "extends": [
    "@o3osatoshi/config/biome/base.json",
    "@o3osatoshi/config/biome/react.json"
  ],
  "root": false
}

eslint

ソートのみに利用する。 biomeのカバー範囲がまだまだ狭いため、補完的にeslintを利用する。

config

ライブラリ毎にconfigを作成し、一つにまとめてexportする。

import jsonc from "./jsonc.mjs";
import perfectionist from "./perfectionist.mjs";
import vitest from "./vitest.mjs";

export default [
  {
    ignores: [
      "**/node_modules/**",
      "**/dist/**",
      "**/generated/**",
      "**/.next/**",
      "**/.turbo/**",
      "**/.idea/**",
      "**/storybook-static/**",
    ],
  },
  ...perfectionist,
  ...jsonc,
  ...vitest,
];

適用

プロジェクトルートでは、importしたconfigをそのままexportする。

// /eslint.config.mjs
import config from "@o3osatoshi/config/eslint/config.mjs";

export default [...config];
  1. 本ブログは「技術的自由研究の備忘録」を目的としている。ソースコードは GitHubリポジトリ に公開している 

  2. お気づきの点や改善案があれば、遠慮なくお知らせいただきたい。ご意見やご感想を歓迎します