zfb
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

defineConfig

完全な型推論つきで zfb プロジェクトの設定を定義します。

シグネチャ

defineConfig(config: ZfbConfig): ZfbConfig

defineConfigzfb/config からエクスポートされます。このヘルパーは identity 型で、受け取った引数をそのまま返します。唯一の役割は、エディタに ZfbConfig 形状に対する IntelliSense と型チェックを提供することです。実際のスキーマは設定ロード時に Rust の serde によって検証されるため、設定を TypeScript で書いても JSON で書いても同じルールが適用されます。

zfb/config インポートの型付け

zfb.config.tsベア specifier の zfb/config から defineConfig をインポートします。zfb は設定ロード時にそのインポートをランタイム専用のスタブにエイリアスし(そのため @takazudo/zfb パッケージを入れていなくても設定をパースできます)、node_modules に実体としての zfb/config モジュールは存在しません。公開されている型は スコープ付き のサブパス @takazudo/zfb/config 配下にあります。そのため素の tsc --noEmitzfb check が実行するもの)にはベアインポートを型付けする対象がなく、Cannot find module 'zfb/config' を報告します。

両者を橋渡しするには、スコープ付きパッケージの型を 再エクスポート する 1 行の ambient 宣言を使います。zfb-shim.d.tstsconfig に含まれるディレクトリへ置いてください。標準テンプレートでは components/zfb-shim.d.ts がそのまま使えます。代わりにプロジェクトルートへ置く場合は、"zfb-shim.d.ts"include に明示的に追加します。

// components/zfb-shim.d.ts — ベアの `zfb/config` specifier を型付けする。
declare module "zfb/config" {
  export * from "@takazudo/zfb/config";
}

必ず再エクスポートにしてください — ZfbConfig の形状をシムに手書きでコピーしては いけません。手で写したフィールドリストは静かにエンジンから遅れていきます。新しい設定フィールドが追加されるたびに手で足さない限り、zfb check が妥当なフィールドを TS2353: Object literal may only specify known properties で拒否します。export * は公開されている @takazudo/zfb/config を自動的に追従するため、シムが遅れることはありません。

設定の形状

すべてのキーは camelCase です。形状は Rust の serde デシリアライザ(crates/zfb/src/config.rs)によって強制されます。packages/zfb/src/config.ts の TypeScript 型は、エディタの IntelliSense のためにそれを反映しています。

  • outDir?: string — build と preview が使う出力ディレクトリです。dev ではライブ出力先ではなく、読み取り専用の事前ビルド済みシードとして扱われます。デフォルト: "dist"。build と preview では、明示的な --outdir がこの設定より優先されます。

  • publicDir?: string — 静的アセットのディレクトリ。そのままコピーされます。デフォルト: "public"

  • host?: string — dev / preview サーバーがバインドするホスト。

  • port?: number — dev / preview サーバーのポート。省略時の組み込みフォールバックはコマンドごとに異なり、zfb dev3000zfb preview4321 にフォールバックします。ここで port を設定すると両コマンドのフォールバックを上書きし、さらに CLI の --port フラグがこの設定値を上書きします。CLI リファレンスを参照してください。

  • allowedHosts?: string[] — 非 localhost インターフェースにバインドしたときに dev / preview サーバーが受け付ける Host ヘッダー値(Vite の server.allowedHosts に相当)。非ループバックバインドのときだけ参照されます。localhost・明示的にバインドしたホスト・IP リテラルの Host(127.0.0.1[::1]・起動バナーが表示する LAN URL など)は常に許可されます(DNS リバインディングには DNS 名が必要なため、生の IP は安全です)。エントリは完全一致(大文字小文字を区別せず、リクエスト側のポートは除去して比較)。".example.com" のような先頭ドット付きエントリはサブドメインにもマッチします。

  • framework?: "preact" | "react" — JSX フレームワークのランタイム。デフォルト: "preact"

  • collections?: CollectionDef[] — コンテンツコレクション。各エントリは以下を持ちます。

    • name: stringgetCollection 呼び出しで使う識別子。

    • path: string — プロジェクトルートからの相対ディレクトリ。

    • schema?: Record<string, unknown> — フロントマター検証用の任意の JSON Schema(zfb check で強制されます。ビルドではフロントマターの検証は行われません)。

    • include?: string[] — glob パターン(globset 方言、path からの相対)。設定すると、マッチするエントリのみが保持されます。

    • exclude?: string[] — glob パターン。マッチするエントリは除外されます(include の後に評価されます)。

    • idStripSuffix?: string — 各エントリの slug とモジュール specifier から取り除かれるサフィックス(例: 多言語レイアウトでの ".en")。

    • allowOutsideRoot?: boolean.. によってプロジェクトルートを抜け出す path をオプトインします(例: このパッケージの外にあるモノレポ共有のコンテンツディレクトリ)。デフォルトの falsepath をプロジェクトルート内に制限します。絶対パスはこのフラグにかかわらず拒否されます — 緩和されるのは .. 相対の脱出だけです。モノレポでの使用例や、プリセットの信頼・dev 監視に関する注意点についてはプロジェクトルート外のコレクションを参照してください。

  • tailwind?: { enabled?: boolean } — Tailwind オプション。enabled のデフォルトは true です。作成済みの CSS と CSS Modules は残したまま Tailwind を無効化するには、tailwind: { enabled: false } を設定します。

  • prefetch?: { disabled?: boolean } — プリフェッチオプション。disabled: true のとき、ランタイムのプリフェッチ配線はビルド時の meta タグを通じて完全にスキップされます。

  • minifyHtml?: boolean — プロダクション HTML ミニファイを有効にします。デフォルト: false(オフ)。ミニファイは Rust 側の後処理として実行され、Node.js のミニファイ subprocess は起動しません。初回バージョンは保守的で、レンダリングされた HTML ページだけを対象候補にし、ソースが .html の passthrough ページはそのまま残し、HTML 以外の出力はスキップします。zfb build --minify-html / --no-minify-html で 1 回のビルドだけ設定値を上書きできます。

  • strictBrokenLinks?: boolean — マークダウンのリンク検証が出す壊れたリンクの診断をエラーに引き上げ、警告にとどめる代わりに zfb build を非ゼロ終了で失敗させます。デフォルト: false(オフ。キー自体を省略しても構いません)。markdown.features.linkValidation がない場合、これを true にすると何もしないのではなく、デフォルト設定でリンク検証を強制的に有効化します。すでにある場合は、その failOnBroken だけが上書きされ、他の設定はすべて保持されます。zfb build --strict-broken / --no-strict-broken で 1 回のビルドだけ設定値を上書きできます。build 専用で、zfb dev は影響を受けません。対象は linkValidation の仕組みのみで、後述の別物である resolveMarkdownLinks.onBrokenLinks のつまみには影響しません。リンク検証を参照してください。

  • bundle?: BundleConfig — ページ / SSR、islands、クライアントスクリプト、module worker の各パスで共有される esbuild 設定。excludemainFieldsexternal--platform=neutral のページ / SSR パス向けのエスケープハッチで、loadersdefine は 4 つのパスすべてに適用されます。bundle 設定を参照してください。

  • plugins?: PluginConfig[] — ユーザー提供のプラグイン。各エントリは name(npm の specifier または ./ 相対パス)と任意の options(プラグインのフックに渡される任意の JSON オブジェクト)を持ちます。フックのコントラクト(setuppreBuildpostBuilddevMiddlewarepreviewMiddleware)の全体、および仮想モジュール・インポートエイリアス・開発専用の注入ルートについては Plugins を参照してください。

  • presets?: Partial<ZfbConfig>[] — バリデーション前にマージされる設定プリセット。各プリセットは部分的な ZfbConfig 形状のオブジェクトで、通常はプリセットパッケージのファクトリ関数の戻り値です。配列フィールド(pluginscollectionsextraWatchPathsallowedHosts)はプリセットから先頭に付与され、メイン設定のエントリは相対的な順序を維持します。スカラーおよびオブジェクトフィールドはキーの有無に基づきます。メイン設定がキーを提供している場合は、その値が組み込みデフォルトと同じでもメイン設定が勝ち、キーを省略した場合はそれを提供する最初のプリセットから補完されます。明示的な null はプリセット値をブロックします。プリセット内のネストした presets は再帰的に展開されません。プリセット作者は相対パスプラグインが正しく解決されるよう definePreset を使ってください。

  • adapter?: string — デプロイ先アダプタのパッケージ名。純粋な静的ビルドの場合は省略します。"@takazudo/zfb-adapter-cloudflare" のようなパッケージは SSR バンドルをデプロイ可能なエントリ(例: Cloudflare Workers Static Assets 向け、Pages 互換の dist/_worker.js)にラップします。

  • output?: "static" | "hybrid" | "auto" — プロジェクトの出力モード。"static" は、いずれかのルートが prerender = false をエクスポートしているとビルド開始時にエラーになります。"hybrid" は、現在 SSR ルートが存在しなくても常に V8 / SSR を有効にします。"auto"(デフォルト)はルートセットから検出します。

  • site?: string — 正規のオリジン URL(例: "https://example.com")。設定すると globalThis.__zfb.site が公開され、レイアウトで正規の <link> タグ、OpenGraph メタ、サイトマップの絶対 href、hreflang の代替言語指定を構築できます。絶対 HTTP/HTTPS URL である必要があります。サーバーサイドでの正規 URL 構築が不要なビルドでは省略してください。base とは別物です(後述)。

  • base?: string — アセット URL 向けの公開 URL プレフィックス。サイトがサブパス配下にデプロイされる場合(例: "/pj/my-site/")に使います。site とは別物です。base はアセット URL の前に付与され、site はメタデータで使う完全な正規オリジンです。

  • copyPublicWithBase?: booleanpublic/ のアセットを base サブパス配下へコピーするか(デフォルトの true)、dist/ ルートへフラットにコピーするか(false)。デプロイ処理が dist/ ツリー全体を base パスへ移動する場合(例: cp -a dist/. deploy-root/pj/site/)は、dist/<base>/<base>/... の二重ネストを避けるため false にします。静的アセット — デプロイ時に再配置するパイプライン向けのフラットコピーを参照してください。

  • stripMdExt?: boolean — MDX コンパイル時に内部リンクの href から .md / .mdx 拡張子を取り除き、末尾に / を付与します。デフォルト: false

  • trailingSlash?: boolean — base パスの書き換え時に、拡張子なしの絶対 href に末尾の / を付与します。デフォルト: false

  • markdown?: MarkdownConfig — Markdown / MDX のパースオプション。フィールド:

    • gfm?: GfmFlag — GFM 構文の切り替え(打ち消し線、テーブル、オートリンクなど)。GFMを参照してください。

    • toc?: { heading?: string; maxDepth?: number }heading(デフォルト "TOC")にマッチする最初の見出しの後に、maxDepth レベル(デフォルト 2)までの目次を挿入します。markdown.features.headingMarkerToc と同じ基盤プラグインと設定形状を配線しており、見出しマーカー TOCで説明しています。

    • externalLinks?: { target?: string; rel?: string[] } — 外部の <a> 要素に target / rel を付与します。外部リンクを参照してください。

    • cjkFriendly?: boolean — CJK に配慮した強調とオートリンク境界の処理。デフォルトは true(オン)。CJK フレンドリーな強調を参照してください。

    • hardBreaks?: boolean — ソフト改行を <br> に変換します。デフォルトは false(オフ)。ハード改行を参照してください。

    • features?: MarkdownFeaturesConfig — 機能ごとのオプトイン切り替え(mermaid、ディレクティブ、コードエンリッチメント、読了時間など)。Markdown Features の機能マップを参照してください。

  • resolveMarkdownLinks?: ResolveMarkdownLinksConfig — マークダウンリンクリゾルバの設定。[label](./other.mdx) のリンクをレンダリング後のルート URL に書き換えるには有効にします。拡張子なし(./other)およびディレクトリ形式(other/)のターゲットも解決され、{name}.mdx{name}.md{name}/index.mdx{name}/index.md の順に探索されます。相対ターゲットはソースファイルのディレクトリから解決されます。非 index ページからレンダリング後の URL(ソースファイルより 1 階層深い位置 — 例: section/article.mdx からの ../sibling/)を基準に書かれたディレクトリ形式リンクは、ファイル空間の候補がすべて外れた場合に URL 空間フォールバックで解決されます。フィールド:

    • enabled?: boolean — 何かを起こすには true に設定する必要があります。デフォルトは false で、リゾルバは何もせず、リンクはそのまま通過します。

    • docsDir?: string — レガシーの単一ディレクトリ形式。ハードコードされた /docs/ ルートプレフィックスに対してスキャンされます。dirs が空でなくなると無視されます。

    • dirs?: { dir: string; routePrefix: string }[] — ディレクトリごとの明示的なソースマップ(例: EN ドキュメントは src/content/docs//docs/、JA ドキュメントは src/content/docs-ja//ja/docs/)。docsDir より優先され、複数のドキュメントルート(ロケールミラー)を持つプロジェクトでは必須です。

    • onBrokenLinks?: "warn" | "error" | "ignore".md / .mdx リンクを解決できないときの挙動。デフォルトは "warn"(警告を出してビルドは継続)。

    全体の挙動についてはリンクの解決Markdown Features の機能マップを参照してください。

  • emitRoutesManifest?: booleanzfb build がビルド後のルートマニフェストを <outDir>/__zfb/routes.json に書き出すかどうか。デフォルト: true(書き出す)。false を設定すると抑制します。

  • extraWatchPaths?: string[] — プロジェクト内のソースルートに加えて、dev ウォッチャーが追跡する追加の絶対ファイルシステムパス。プロジェクトルート外のパスを監視する を参照してください。

  • watchPollFallback?: booleanzfb dev のファイル監視を、OS ネイティブの変更通知(macOS の FSEvents、Linux の inotify など)ではなくポーリングで行います。デフォルト: false(ネイティブ)。ネイティブバックエンドがイベントを届けないホスト — ネットワークマウント上のプロジェクトディレクトリ、一部の CI / サンドボックスコンテナ、停止した fseventsd など — で有効にします。ポーリングによるファイル監視を参照してください。

  • watchPollIntervalMs?: number — ポーリングバックエンドの再スキャン間隔(ミリ秒)。デフォルト: 50050 以上 10000 以下である必要があり、範囲外は設定ロード時のエラーになります。100 未満の値は警告つきで受理されます。効果があるのは watchPollFallbacktrue のときだけで、単体で設定した場合はエラーではなく警告つきで受理され、休眠状態になります。ポーリングによるファイル監視を参照してください。

以下のキーも同じ ZfbConfig 型の一部で、defineConfigzfb check は他のフィールドと同様に型チェックします。これらは Rust エンジンのコードレンダリングとプラグインの挙動を設定します。

  • codeHighlight — syntect の構文ハイライトテーマオプション。構文ハイライトのガイドを参照してください。フィールド:

    • theme?: string — シングルテーマモード。syntect の組み込み、またはユーザーが読み込んだテーマ名(例: "InspiredGitHub""Solarized (dark)")。デフォルトは "base16-ocean.dark"。トークンはインラインの color: で着色されます。themeLight / themeDark とは排他です。これらは Shiki("dracula" など)ではなく syntect のテーマ名です。

    • themesDir?: string.tmTheme ファイルのディレクトリ。プロジェクトルートからの相対です。各ファイルは宣言された name によって themethemeLightthemeDark から利用できます。シングル / デュアル両方のモードに適用されます。相対パスである必要があり、.. でルートから抜け出すことはできません。ディレクトリが存在しない場合はビルド開始時にエラーになります。

    • themeLight?: string — デュアルテーマハイライト用のライトモード syntect テーマ名。themeDark必ずセットで指定する必要があり、片方だけの指定はビルドエラーになります。theme とは排他です。ペアを設定すると、各ブロックが 2 回ハイライトされ、トークンはインラインの color: の代わりに --shiki-light / --shiki-dark カスタムプロパティを持ち、<pre> には class="syntect-dual"--shiki-light-bg / --shiki-dark-bg が付与され、利用側は light-dark() CSS ルールでアクティブな色を解決します。

    • themeDark?: string — デュアルテーマハイライト用のダークモード syntect テーマ名。themeLight必ずセットで指定し、theme とは排他です。デュアルモードの完全な仕様は themeLight を参照してください。

    • mode?: "inline" | "class" — トークンの出力モード。"inline"デフォルト)はトークンごとの色を style="color:…"(またはデュアルの --shiki-* プロパティ)に焼き込みます。"class" は代わりにトークンごとに意味的なロールクラスを出力し、色は再テーマ可能な CSS カスタムプロパティ経由で解決されます。クラスモードはすべてのテーマ関連オプション(themethemeLightthemeDarkthemesDir)と排他 です。テーマはクラス出力に影響しないため、mode: "class" と同時に指定するとビルドエラーになります。ガイドのクラスモードの節を参照してください。

    • classPrefix?: string — クラスモードのクラス名プレフィックス(デフォルトは "hi-" で、hi-roothi-kw、… を生成)。/^[A-Za-z][A-Za-z0-9_-]*$/ に一致する必要があります(先頭は ASCII 英字、残りは英字・数字・_-)。空・先頭が数字・その他一致しないプレフィックスはビルドエラーです。クラスモードでのみ意味を持ちます。同梱のデフォルトスタイルシートの .hi-* セレクタは設定したプレフィックスへ書き換えられますが、--zfb-hi-* カスタムプロパティは --zfb-hi-* のままです。

    • roleClasses?: Partial<Record<Role, string>> — クラスモードでのロールごとのクラス上書き(例: { keyword: "text-violet-600 dark:text-violet-400" })。キーは 18 個の固定ロール名(escapeoperatorcommentstringnumberconstantkeywordfunctiontypenamespacepropertyvariabletagattributepunctuationinserteddeletedheading)のいずれかである必要があり、未知のキーはビルドエラーです。値はスペース区切りで複数のクラスを持てますが、素の line トークンを含めることはできません(code-enrichment の行ラッパーと衝突します)。マッピングされた Tailwind ユーティリティは @source inline で自動セーフリスト化されます。tailwind.enabledfalse の状態で roleClasses を設定することは許可されますが、ビルド警告が出ます(オーサリング CSS 経路ではセーフリストを生成できません)。

    • defaultStylesheet?: boolean — 組み込みの --zfb-hi-* トークンスタイルシート(zfb-hi.css)を、まとめられた styles.css に注入するかどうか。デフォルトは true。クラスモードでのみ意味を持ちます。完全に自前のスタイルシートを使う場合は false に設定します。

  • pluginHookTimeoutSecs — プラグインフック呼び出しのタイムアウト(秒)。

bundle 設定

ブラウザ向けの islands、クライアントスクリプト、module worker の各パスは、ページ / SSR パスと同じモード・loader・define 設定を使います。これにより、サーバーとブラウザのグラフで共有されるコードの意味が、バンドル境界をまたいでも変わりません。

バンドルモード

zfb は次の 3 つのコンパイル時置換を所有します。値は minify の有無ではなく、実行するコマンドによって決まります。

コマンドimport.meta.env.DEVimport.meta.env.PRODprocess.env.NODE_ENV
zfb devtruefalse"development"
zfb buildfalsetrue"production"

これらの値は islands、クライアントスクリプト、それらの module worker、ページ / SSR バンドルに適用されます。zfb はそれ以外の import.meta.env.* キーを合成しません。表にある 3 つのキーは予約されており、bundle.define から上書きできません。

bundle.excludebundle.mainFieldsbundle.external

ページ / SSR パスは esbuild の --platform=neutral でバンドルします。これは意図的に最小限で、main-fields の解決リストはデフォルトで空であり、CJS 専用の require() 経路でしか解決できないパッケージを拒否します。これら 3 つのフィールドは、neutral パスに到達する CJS 専用の依存(直接インポートされるか、eager な import.meta.glob(...) の展開で新たに取り込まれるもの)向けのエスケープハッチです。3 つともデフォルトでは未設定 / 空であり、このつまみを使わないビルドとバイト単位で同一です。

bundle.exclude — バンドラーが esbuild グラフへ決して取り込んではいけないソースファイルを指定する、プロジェクト相対の glob パターン(gitignore スタイル。プロジェクトルートからの相対パスに対して POSIX 形式でマッチ)。マッチしたファイルはシャドウツリーへステージングされることも、eager な import.meta.glob(...) で展開されることもありません。CollectionDef.exclude(バンドルではなくコンテンツのフィルタリング)とは無関係です。

import { defineConfig } from "zfb/config";

export default defineConfig({
  bundle: {
    exclude: ["components/**/*.stories.tsx"],
  },
});

変更前: あるルートの import.meta.glob("./components/**/*.tsx")Button.stories.tsx を拾い、それが CJS 専用のテストモック用パッケージをインポートするため、neutral プラットフォームのビルドがそれを解決できずに失敗します。変更後: glob は *.stories.tsx ファイルを完全にスキップし、ビルドが成功します。

bundle.mainFields — neutral パス向けの明示的な esbuild --main-fields リスト。例えば ["main", "module"] を設定すると、main / module しか提供しない(exports マップを持たない)依存を解決できます。neutral はそうしなければ main-fields をまったく参照しないためです。

import { defineConfig } from "zfb/config";

export default defineConfig({
  bundle: {
    mainFields: ["main", "module"],
  },
});

変更前: The "main" field here was ignored. Main fields must be configured explicitly when using the "neutral" platform. 変更後: esbuild は main そして module を参照し、依存を解決します。

bundle.external — neutral パス向けに --external としてマークするベア specifier。esbuild はそれらの解決を試みる代わりに、バンドルせずそのまま残します。mainFields だけでは満たせない CJS 専用の依存向けのもう 1 つのエスケープハッチです。フレームワークが提供する externals に追加されます。

import { defineConfig } from "zfb/config";

export default defineConfig({
  bundle: {
    external: ["some-cjs-only-package"],
  },
});

変更前: esbuild は --platform=neutral の下で some-cjs-only-package の解決を試みて失敗します。変更後: esbuild はそのベアインポートを解決せず external としてマークします。

bundle.loaders

loaders を使うと、追加の esbuild loader をファイル拡張子へ割り当てられます。

import { defineConfig } from "zfb/config";

export default defineConfig({
  bundle: {
    loaders: {
      ".fixture": "text",
      ".data": "binary",
    },
  },
});

キーは . で始める必要があります。inline-only v1 で使える値は textjsonbase64dataurlbinaryempty です。filecopy は sibling アセットを生成しますが、これらのバンドルパイプラインはその出力を公開しないため拒否されます。.css.module.css.mdx.md は zfb が予約しており、上書きできません。

?raw インポートはこのマップを使いません。import text from "./file.ext?raw" は、拡張子や設定済み loader にかかわらず、常に妥当な UTF-8 テキストを読み込みます。

bundle.define

define の値は自動的に引用符が付く文字列ではなく、esbuild の生の置換式です。

import { defineConfig } from "zfb/config";

export default defineConfig({
  bundle: {
    define: {
      __APP_NAME__: '"my-app"', // 置換式自身に JSON の引用符を含める
      __FEATURE_ENABLED__: "true",
      __BUILD_META__: '{"channel":"preview"}',
    },
  },
});

内側の JSON 引用符を含まない値は、文字列リテラルではなく生の式としてパースされます(その結果、拒否される場合もあります)。値はそのまま転送され、環境変数の PUBLIC_ ポリシーではフィルタリングされません。

bundle.define のすべての値を公開コードとして扱ってください

bundle.define は信頼された運用者が記述するビルド入力であり、secret store や入力サニタイズの境界ではありません。設定した各定義はブラウザバンドルから利用できます。到達可能なコードが使わない定義は esbuild が削除する場合もありますが、tree-shaking は機密性を保証しません。認証情報、非公開トークン、信頼できない式をこのマップへ入れないでください。

TypeScript ambient declarations

zfb はモード値、loader の結果、raw module、define の置換をバンドル時に提供しますが、それらの TypeScript 宣言は生成しません。zfb checktsc --noEmit を実行するため、これらの機能を TypeScript ソースで使うには、tsconfig に含まれる .d.ts ファイルへ対応する ambient 宣言を追加する必要があります。たとえば、上の .fixture 用 text loader と 3 つの bundle.define エントリは、標準テンプレートの tsconfig にすでに含まれる components/zfb-env.d.ts で次のように型付けできます。

// components/zfb-env.d.ts
interface ImportMetaEnv {
  readonly DEV: boolean;
  readonly PROD: boolean;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

declare module "*?raw" {
  const source: string;
  export default source;
}

declare module "*.fixture" {
  const contents: string;
  export default contents;
}

declare const __APP_NAME__: string;
declare const __FEATURE_ENABLED__: boolean;
declare const __BUILD_META__: { channel: string };

ファイル名とディレクトリ自体に特別な意味はなく、tsconfig.jsoninclude に含まれていれば十分です。プロジェクトルートの zfb-env.d.ts を使う場合は、そのパスを include に明示的に追加してください。include の外にある宣言ファイルは tsc から見えません。

これらの宣言は型情報だけを提供します。esbuild を設定したり、ランタイム値を注入したり、zfb がサポートしない import を有効にしたりするものではありません。カスタム module pattern とグローバルの型は、bundle.loaders および bundle.define と一致させてください。一致していないと、zfb checktsc が誤ったランタイム上の前提を受け入れたり、zfb がバンドルできるコードを拒否したりします。raw import の結果は zfb の固定コントラクトであるため、*?raw は常に string を返すものとして宣言できます。

プロジェクトルート外のパスを監視する

extraWatchPaths を使うと、プロジェクトツリー外のファイルが変更されたときに zfb dev がライブリロードできます。プロジェクトが兄弟リポジトリからコンテンツを読み込む場合、コードとともにコンテンツを同梱する file: 依存、共有ファイルシステムのディレクトリなどで便利です。

import { defineConfig } from "zfb/config";

export default defineConfig({
  extraWatchPaths: [
    "/home/me/knowledge-base",
    "/srv/shared-content",
  ],
});

セマンティクス:

  • 絶対パスのみ。 各エントリは絶対パスである必要があります。相対パスは設定ロード時に extraWatchPaths[N]: ... must be an absolute path エラーで拒否されます。dev ウォッチャーは各エントリをプロジェクトルートの外でそのまま登録するため、相対パスを解決するためのアンカーを持ちません。

  • 正規化。 各エントリは zfb dev 起動時に一度だけ正規化されます(Path::canonicalize)。シンボリックリンクは解決され、以降のイベントは正規形でリビルドロジックに到達します。そのため、ウォッチャーが発するパスは、設定した値に対して realpath を実行したときに得られる形と一致します。

  • 起動時に存在しない場合。 設定されたパスが zfb dev 起動時点で存在しない場合は、警告とともにスキップされます。ウォッチャーはそのパスが後から出現するかをポーリングしません。dev サーバーがすでに動いている状態でディレクトリを作成した場合は、それを認識させるために zfb dev を再起動してください。

  • 再帰的。 各エントリは再帰的に監視されます。起動後に作成されたサブディレクトリも、OS レベルの再帰監視によって自動的に拾われます。

  • リビルドの範囲。 これらのパスからのイベントは依存グラフのカバー範囲外にあります(グラフはツリー内のエッジのみを追跡します)。そのため、ツリー内の同等の編集よりも保守的に広範なリビルドをトリガーします。これは意図的なトレードオフで、ルート外のソースについては精度よりも正しさを優先しています。

セキュリティ上の注意。 オプトイン専用です。$HOME/ のような無制限のディレクトリを指定しないでください。Linux では再帰ウォッチャーがすべてのサブディレクトリを登録するため、大きなツリーでは inotify の max_user_watches 上限(多くのディストリビューションでデフォルト約 8192)にすぐ到達する可能性があります。広大なソースを監視する必要がある場合は、実際に編集するファイルを含む最も狭いサブツリーを監視してください。

これは dev モードの機能です。プロダクションビルド(zfb build)はファイルシステムを一度スナップショットし、ウォッチャーのイベントに依存しないため、extraWatchPaths は出力される成果物に影響しません。

ルート外のパスを監視することと、そこからインポートできることは別問題です

extraWatchPaths は、プロジェクトルート外のファイル変更を dev ウォッチャーが検知できるようにするだけで、そのファイルをインポート可能にするわけではありません。zfb はビルド時にプロジェクトルートを一時ディレクトリへシャドウコピーしてから esbuild を実行します。プロジェクトルートの外へ抜け出る相対インポート(例:../../../../packages/ui/src/button.tsx)は、このシャドウコピーの外側を指すことになり、ビルドは Could not resolve "..." エラーで失敗します。エラーメッセージには、インポート元ファイルの実際の(シャドウではない)パスと、このシャドウコピーの境界に関する説明が含まれます。

回避策: ルート外のターゲットを相対インポートではなくパッケージインポートとして公開してください。ターゲットパッケージの package.json にワイルドカードの exports エントリ(例:"./src/*": "./src/*")を追加し、パッケージ specifier(例:@scope/pkg/src/button.tsx)でインポートします — node_modulesfile: / workspace リンクされたパッケージを含む)はシャドウコピーに含まれるため、パッケージ specifier によるインポートは通常どおり解決されます。詳しい背景は issue #1385 を参照してください。現時点では診断メッセージの改善のみで、この抜け道自体は引き続きサポート対象外です。

ポーリングによるファイル監視

zfb dev は通常、OS 自身のファイルシステム変更通知(macOS では FSEvents、Linux では inotify、その他のプラットフォームでも同等の仕組み)から編集を検知します。これが最速の経路であり、デフォルトです。しかしホストによっては、この通知がまったく届きません。ネットワークマウント上のプロジェクトディレクトリ、一部の CI / サンドボックスコンテナ、macOS の fseventsd が停止している場合などです。zfb dev 自体は正常に動いているのに、ホットリロードだけが死んだように見えます。

watchPollFallback: true は、このネイティブバックエンドをポーリング方式に切り替えます。変更が通知されるのを待つ代わりに、監視対象のルートを一定間隔で再スキャンする方式です。

import { defineConfig } from "zfb/config";

export default defineConfig({
  watchPollFallback: true,
  watchPollIntervalMs: 500,
});

どんなときに使うか

先回りして有効にしないでください。ポーリングにはネイティブ通知にはかからない CPU コストがあり、反応も遅くなります。特定のマシンで実際にホットリロードが壊れているときに、はじめて有効にします。

その判断材料は dev サーバー自身が出してくれます。起動時にウォッチャーの生存自己チェックが走り、自分でファイルシステムに変更を加えて、それを観測できるかどうかを待ちます。期限内に何も届かなかった場合、zfb devhot-reload looks dead on this machine という警告を目立つ形で表示し、よくある原因(fseventsd の停止、Dropbox / OneDrive / iCloud で同期されたプロジェクトディレクトリがイベントを横取りしている、アンチウイルス / EDR ソフトウェアがファイルシステム呼び出しをフックしている)を挙げたうえで、試すべき対処として watchPollFallback: true を名指しします。この警告が、このフラグを設定する最もはっきりした合図です。

一方で、警告が出ないことは根拠としては弱いことに注意してください。自己チェックはマーカーを、dev サーバーが実際に監視しているルートとは重ならないよう意図的に分離したスクラッチディレクトリへ書き込みます(重なってしまうと、プローブ自身の書き込みがプロジェクトの変更と見なされ、チェックのたびにリビルドの嵐を引き起こすためです)。したがって測っているのは、その 1 か所についてバックエンドがイベントを届けるかどうかであって、監視対象すべてのパスについてではありません。特定の監視対象パスだけで問題が起きている場合 — たとえば extraWatchPaths に指定したネットワークマウントがイベントを届けなくなった場合 — 自己チェックは通ったままホットリロードだけが壊れていることがあり得ます。特定のディレクトリを編集してもリビルドが走らず、それでも警告が出ていないのであれば、やはりポーリングバックエンドを試してみる価値があります。

すでにポーリングバックエンドが有効な状態で同じ警告が出た場合は、その旨を伝えたうえで別の対処を提案します。watchPollIntervalMs を大きくする、あるいは監視対象のディレクトリがプロセスから到達できなくなっていないか(たとえばネットワークマウントが切れていないか)を確認する、といった内容です。すでに有効になっているフォールバックを重ねて勧めても意味がないためです。

間隔の指定

  • 省略時は 500ms。

  • 有効範囲は 5010000ms(両端を含む)。 範囲外は設定ロード時に失敗します。小さすぎるとポーリングスレッドがビジーループし、大きすぎるとホットリロードが壊れているように感じられるためです。

  • 100ms 未満は警告つきで受理されます。 これほど短い間隔での再スキャンは、大きなプロジェクトツリーでは無視できない CPU コストになり得ます。

  • フラグなしでは休眠します。 watchPollFallbackfalse のまま watchPollIntervalMs を設定した場合、警告は出るものの受理され、フォールバックを有効にするまで効果はありません。これは意図的にエラーにしていません。後からオプトインする可能性のあるプロジェクトのために、プリセット側で間隔をあらかじめ用意しておけるようにするためです。

ネイティブバックエンドとの挙動の違い

イベントが届いた後の処理 — 変更の分類、デバウンス、動的な監視登録 — は両バックエンドで共通で、まったく同じように振る舞います。異なるのは、変更をどう検知するかという入口の部分だけです。

  • レイテンシはおよそポーリング間隔 1 回分(に加えてデバウンスの待ち時間)で、OS イベントの数ミリ秒後ではありません。デフォルトの 500ms では、リビルドが始まるまでに 0.5 秒ほど余分にかかると考えてください。

  • 1 回の間隔の内に作成して削除したファイルは、イベントがまったく発生しません。 次のスキャン時点でそのパスは存在しないため、作成も削除も報告されません。

  • 変更の検出は mtime ベースで、粒度は秒単位です。 ファイルの内容は比較されません。上書きしても mtime が同じ秒のままであれば、後続の変更で mtime が進むまで検知されません。

  • 新しく作成されたディレクトリの子は、それぞれ個別の作成イベントとして届きます。 これは意図的な差異であり、むしろ改善です。ネイティブバックエンドでは、ディレクトリの作成だけが報告され、その子が個別のイベントとして現れないことがあります。

このフラグが影響するのは zfb dev だけです。プロダクションビルドはファイルシステムを一度スナップショットするだけでウォッチャーのイベントに依存しないため、どちらのキーも出力される成果物には影響しません。

// zfb.config.ts — 推奨される形式
import { defineConfig } from "zfb/config";

export default defineConfig({
  outDir: "dist",
  framework: "preact",
  collections: [
    {
      name: "blog",
      path: "content/blog",
    },
  ],
  tailwind: { enabled: true },
});

ローダーは zfb.config.ts(推奨)と zfb.config.json(レガシーのフォールバック)を受け付けます。zfb.config.json のみが存在する場合は serde_json 経由で読み込まれます。./...../...、または絶対パスとして宣言されたプラグインパスは設定ファイルからの相対で解決されます("@takazudo/some-plugin" のような npm specifier はどちらの形式でも動作します)。

// zfb.config.json — レガシー形式。現在もサポート
{
  "outDir": "dist",
  "framework": "preact",
  "collections": [
    {
      "name": "blog",
      "path": "content/blog"
    }
  ],
  "tailwind": { "enabled": true }
}

バリデーション

ローダーは以下のルールを強制し、JSON のパース失敗についてはファイルパスと line:column を添えてエラーを報告します。

  • コレクション名は一意である必要があります。

  • path は絶対パスにできません。

  • path はプロジェクトルートから抜け出す .. セグメントを含めることができません — ただし、そのコレクションが allowOutsideRoot: true を設定している場合はこのチェックだけが緩和されます(絶対パスは引き続き拒否されます)。

  • bundle.loaders のキーは . で始まり、inline loader を使い、zfb が予約した拡張子を上書きしない必要があります。

  • bundle.define は、バンドルモードが所有する DEVPRODNODE_ENV の置換を上書きできません。

  • watchPollIntervalMs50 以上 10000 以下である必要があります。100 未満の値、および watchPollFallback: true なしで設定された値は、いずれも拒否されず警告つきで受理されます。

Revision History

作成更新