コンテンツにスキップ
svelte-vitals
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

設定ファイル

毎回フラグを指定する代わりに、svelte-vitals.config で一度だけ設定します。

--rules、--ignore、--fail-on、--weights を実行のたびに指定する代わりに、プロジェクトルートに svelte-vitals.config ファイルを置いて設定をまとめられます。CLI と Vite プラグイン はいずれもこのファイルを自動的に読み込みます(Vite プラグインは直接読み込みます。詳細は下記の Vite プラグインで設定ファイルを再利用する を参照)。

svelte-vitals install --client config-file を実行すると、以下のオプションをすべてコメントアウトした状態の雛形を生成できます。

探索場所

svelte-vitals は次のファイルを、この優先順で分析対象ディレクトリのみから探します(親ディレクトリへの上方探索は行いません。分析対象ディレクトリは SvelteKit プロジェクトのルートであり、vite.config.* が置かれている場所と同じです):

  1. svelte-vitals.config.js
  2. svelte-vitals.config.ts

最初に見つかったファイルを使います。どれも存在しなければ、この機能がなかった頃と同じく、svelte-vitals は組み込みのデフォルト設定で動きます。

install --client config-file が .ts を選ぶのは次の2条件がそろったときです。プロジェクトが TypeScript 志向であること(ルートに tsconfig.json または vite.config.ts)、かつ svelte-vitals が依存関係として宣言されていること。defineConfig の import は読み込み時に解決されるため、npx だけで使うプロジェクトには適用できません。

それ以外は .js を書き出します。どちらのファイルも ESM なので、プロジェクトは "type": "module"(SvelteKit のデフォルト)である必要があり、CommonJS プロジェクトはサポートされません。--force は既存ファイルの拡張子を維持し、形式を勝手に切り替えません。

特定の設定ファイルを指定して分析したい場合は --config <path> を使います。指定すると探索は行われず、その設定ファイルだけが使われます(マージはされません)。相対パスの基準は、解析対象ディレクトリではなくコマンドを実行したディレクトリです。たとえばリポジトリのルートで svelte-vitals apps/web --config shared/sv.config.js を実行すると、読み込まれるのは ./shared/sv.config.js です。受け付ける拡張子は .js と .ts のみで、ファイルが存在しない・読み込めない場合は exit 2 で終了します。設定をコミットする前に試したいとき、モノレポ内の複数アプリで一つの設定を共有したいときに使えます。

--config は CLI 専用のフラグです。Vite プラグインは従来どおり svelteVitals({ ... }) に渡した cwd(未指定なら Vite の設定ルート)から設定を解決します(後述)。プラグインにも同じ設定を共有したい場合は、共有ファイルを vite.config.ts で import してプラグインのオプションに展開してください。オプションは探索された設定ファイルより優先されます。

例

// svelte-vitals.config.ts
import { defineConfig } from 'svelte-vitals';

export default defineConfig({
  treatDynamicAs: 'warn',
  metaComponents: ['Seo'],
  rules: {
    'seo/json-ld': 'off'
  },
  failOn: 'warning',
  weights: {
    seo: 2
  }
});
// svelte-vitals.config.js
export default {
  treatDynamicAs: 'warn',
  metaComponents: ['Seo'],
  rules: {
    'seo/json-ld': 'off'
  },
  failOn: 'warning',
  weights: {
    seo: 2
  }
};

defineConfig は、渡したオブジェクトを組み込みのデフォルトにマージするだけの薄いヘルパーで、.ts ファイルでの型チェックとエディタの補完のために存在します。プレーンな export default {...} オブジェクトでも実行時の挙動はまったく同じです。.js ファイルではどちらにしても型チェックの恩恵はないので、(JavaScript タブが示すように)プレーンなオブジェクトのままで構いません。しかも import と違い、svelte-vitals を npx 経由でしか実行せず node_modules に存在しない場合でも動作します。

.ts 設定内の defineConfig の import はランタイムの import であり、型チェック時だけでなく、svelte-vitals がファイルを読み込む時に解決されます。そのため、プロジェクトの依存関係として svelte-vitals が宣言されている必要があります。import 元は、実際にインストールしているパッケージである svelte-vitals にしてください。(@svelte-vitals/core からも re-export されていますが、このパッケージは通常は推移的な依存関係であり、pnpm のデフォルトである厳格な node_modules の構成では、プロジェクトから推移的な依存関係を直接解決できません。)

利用可能なオプション

オプション 型 デフォルト 説明
treatDynamicAs 'pass' | 'warn' | 'fail' 'pass' メタデータの値が動的に設定されているルートをどう採点するか
metaComponents string[] [] 解析が追跡できない <head> メタデータ出力コンポーネント名(インストールしていない npm パッケージ由来など)。解決できるコンポーネントは自動的に追跡される
rules Record<string, 'off' | 'critical' | 'warning' | 'info' | { severity?, options? }> {} ルールごとの上書き(ルールを無効化する、重大度を変更する、オプションを設定する)
failOn 'critical' | 'warning' | 'info' 'critical' 実行を失敗させる(終了コード 1)最低重大度
weights Partial<Record<Category, number>> 各カテゴリ 1 組み合わせた Health スコア のカテゴリごとの重み
overrides RuleOverride[] (なし) ルート/ファイル単位のルール上書き(下記を参照)
seo { indexable?: boolean } (なし) indexable: false で、検索結果に出るページにだけ意味のあるルールをまとめて無効化(下記を参照)

Category は 'seo' | 'performance' | 'correctness' | 'security' | 'architecture' | 'a11y' です。

重み 0 は正当な値で、そのカテゴリを Health の平均から完全に除外します(カテゴリ自体のスコアは引き続き出力され、検出結果や終了コードの挙動にも影響しません)。ただし、結果に存在するすべてのカテゴリを 0 にすると平均の取りようがなくなるため、実行はエラー(終了コード 2)になります。

インデックスされないプロジェクト

非公開のアプリ、社内ツール、noindex の PWA は検索結果に出ません。検索結果の見た目を整えるためのルールは、そうしたプロジェクトではノイズにしかなりません。次の 1 行でまとめて無効化できます。

export default {
  seo: { indexable: false }
};

無効になるのは seo/canonical-url・seo/og-title・seo/og-description・seo/og-image・seo/og-url・seo/twitter-card・seo/json-ld・seo/sitemap-xml・seo/sitemap-in-robots・seo/title-length・seo/description-length・seo/description-presence・seo/duplicate-title・seo/duplicate-description・seo/hreflang・seo/ssr-disabled です。rules でそれぞれを 'off' と書いた場合とまったく同じ扱いになり、Health スコアの母数からも外れます。noindex が意図的であることはこのスイッチ自体が示しているので、seo/indexability も無効になります。

それ以外の SEO ルールは残ります。どれもインデックスされないページで役に立つからです。seo/title-presence はブラウザのタブやブックマークの名前になります。seo/html-lang・seo/viewport・seo/charset・seo/image-alt は、ページの表示や支援技術による読み上げに関わります。seo/single-h1 と seo/heading-level-skip が見ているのは文書構造です。seo/robots-txt も実際に機能します。noindex の meta タグはページがクロールされて初めて読まれるからです。JSON-LD の妥当性を見るルールも残ります。構造化データを置くのであれば、その内容は正しくあるべきです。

rules のエントリはこのスイッチより優先されるため、一部だけ残すこともできます。

export default {
  seo: { indexable: false },
  // チャットアプリのリンクプレビューは og:title を読むので、これだけは残す。
  rules: { 'seo/og-title': 'warning' }
};

ルールをルートやファイルにスコープする (overrides)

rules は全体に適用されます。overrides はマッチした箇所にだけルール設定を適用します。典型的なのは、意図的に非公開のルート(認証必須のページ)を SEO メタデータルールの対象外にしたいケースです:

export default {
  overrides: [
    // (app) ルートグループ配下:SEO チェックを一切行わない。
    { files: 'src/routes/(app)/**', rules: { seo: 'off' } },
    // /admin とその配下:seo/title-presence は残すが info に格下げ。
    { route: '/admin/**', rules: { 'seo/title-presence': 'info' } }
  ]
};

各エントリは rules(キーはルール ID またはカテゴリ名、値は 'off' | 'critical' | 'warning' | 'info'、または後述のルールオプションで説明するオブジェクト形式)と、少なくとも1つのスコープを持ちます:

  • route:レポートに表示されるルート ID(例: /blog/[slug])にマッチする glob。SvelteKit の (group) セグメントはルート ID に含まれない点に注意してください(src/routes/(app)/dashboard は /dashboard として報告されます)。グループを対象にするには files を使います。
  • files:検出結果のソースパス(例: src/routes/(app)/dashboard/+page.svelte)にマッチする glob。

glob の構文はあえて最小限に絞ってあります:* はパスセグメント内にマッチ、** はセグメントをまたいでマッチ、末尾の /** はプレフィックス自体にもマッチします(/admin/** は /admin そのものにもマッチ)。それ以外((、)、[、] を含む)はすべてリテラルです。エントリは route / files の glob のいずれかがマッチしたときにマッチします。

押さえておきたい挙動:

  • 'off' はマッチした検出結果を完全に取り除きます。実行を失敗させず、スコアも下げません。そのルールがそこでは実行されなかったのと同じ扱いです。一方、重大度の値を指定した場合は、取り除くのではなくその重大度に再分類します。
  • エントリは順に評価され、後のエントリが勝ちます。1つのエントリ内では、ルール ID のキーが重大度を指定している場合に限りカテゴリのキーに勝ちます。オプションだけを持つ(severity を指定しない)ルール ID のキーはオプションだけを反映し、カテゴリのキーの重大度はそのまま有効であり続け、上書きはしません。マッチした箇所ではオーバーライドが常にグローバルの rules 設定に勝ちます。
  • ルートにもファイルにも紐付かない検出結果(robots.txt などプロジェクト全体のチェック)には一切影響しません。そちらは rules を使ってください。
  • overrides が効くのは CLI と Vite プラグインのビルド時ゲートです。ライブダッシュボードのリクエストごとのレイヤーは、ページがレンダリングされるたびにその場でスコアリングするため overrides を適用しません。追加したばかりのオーバーライドを確認するときは、CLI(またはビルドゲート)を正とみなしてください。
  • 「アプリのこの部分は恒久的に対象外」というポリシーには、suppressions ファイルより overrides を選んでください:suppressions はファイルを書き出した時点で存在した検出結果だけを受け入れるため、あとから追加したルートは再び失敗します。overrides は同じ glob 配下の新しいルートにもマッチし続けます。

ルールオプション

単なる重大度の文字列に加えて、ルール設定にはオブジェクト形式 { severity?, options? } も指定できます:

export default {
  rules: {
    'architecture/prop-count': { options: { max: 10 } },
    'performance/heavy-import': { options: { packages: { 'chart.js': 'import chart.js/auto' } } }
  },
  overrides: [{ files: 'src/lib/**', rules: { 'architecture/prop-count': { options: { max: 4 } } } }]
};

severity は省略可能です。省略すると、そのルールの組み込みの重大度を保ったままオプションだけを変更できます。{ severity: 'off', ... } はいつも通りそのルールを無効化し、一緒に指定した options はその場合無効(inert)になります。文字列だけの形式('off'、'critical'、'warning'、'info')も変わらず使えます。オブジェクト形式はそれらを置き換えるものではなく、追加の選択肢です。

オプションは overrides エントリの中でもトップレベルの rules マップと同じように使え、他の override 設定と同様に route / files でスコープできます。ただし指定できるのはルール ID のキーだけで、カテゴリのキーには指定できません:カテゴリには重大度を指定できますが、オプションはどのルール向けかが分からなければ意味を持たないためです。

層は「組み込みのデフォルト → rules → マッチする overrides エントリを順に」の順で重なり、組み合わさり方はオプションの種類で決まります。

  • 整数は置き換えです。最後に設定された値が勝ちます。
  • リストとマップは、組み込みのセットを破棄せず追加します。以降のリリースで組み込みが増えても恩恵を受け続けられます。
  • マップの既存キーは、エントリを残したまま値だけが指定値になります。1つのパッケージについて組み込みの対処方法だけを書き換えたいときに使います。

オプションを受け付けるのは一部のルールだけです。オプションを受け付けないルールに options を渡すのは、未知のオプション名や型の誤ったオプション値と同じく致命的な設定エラーになります。以下のルールはオプションを受け付け、それ以外のルールは重大度文字列 / off のみを受け付けます。各ルールのページには「設定」節があり、正確なオプション名とデフォルト値が書かれています。

  • architecture/prop-count と architecture/component-size は整数の max を取ります。
  • seo/title-length と seo/description-length は整数の min / max を取ります。
  • performance/heavy-import は、パッケージ指定子 → 対処方法の文字列という packages マップを取ります。組み込みリストに追加されます。
  • performance/preconnect は、ホスト名の origins リストを 取ります。組み込みリストに追加されます。
  • architecture/private-scope-import は、プライベートな ディレクトリを指定する scopes の glob リストを取ります。設定するまでこのルールは何も出力しません。
  • architecture/unit-entry-file は、どのディレクトリが ユニットかを宣言する units / pascalCaseUnits / exclude の glob を取ります。設定するまでこのルールは 何も出力しません。
  • architecture/directory-naming は、ディレクトリ glob → 記法の集合 の directories マップと exclude の glob を取ります。directories を設定するまでこのルールは 何も出力しません。
  • architecture/reserved-directory-names は、 scopes(ディレクトリ glob → 直下に置ける名前)、unitScopes(起点 glob → ユニット直下に置ける 名前)、anyCaseUnitScopes(同様、大文字小文字を問わないユニット用)、exclude を取ります。いずれかの スコープマップを設定するまで無効です。
  • architecture/route-component-import は、 ルートエントリを手動でインポートしてよいサテライトファイル(stories、test、spec)を指定する exemptImporters の glob リストを取ります。組み込みリストに追加されます。
  • architecture/doc-link-target は、プロジェクトのルートを 表す URL プレフィックスを指定する urlRoots のリストを取ります。設定するまでこのルールは何も出力しません。

インポートエイリアス

インポートを追跡するルール(architecture/private-scope-import、architecture/route-component-import、 security/shared-state-import、security/handler-state-write)は、プロジェクトが svelte.config.{js,ts} で宣言しているエイリアス、つまり kit.alias と、$lib を移動している場合の kit.files.lib を通じて指定子を解決します。svelte-vitals はこれらを 静的に、SvelteKit がエイリアスを構築するのと同じ順序で読み取り、ビルド時とまったく同じく最初にマッチ したエイリアスが使われます。

CLI の静的モードは、ルートの <head> と見出しを解決するためにコンポーネントのインポートをたどる際にも これらのエイリアスに従います($lib や相対インポートだけでなく、$components などのカスタムエイリアス 経由でインポートされたコンポーネントが設定する <title>/meta/JSON-LD/<h1> も対象になります)。そのため、 この経路を読む SEO 系ルールはすべて、どのエイリアスを経由してたどり着いたコンテンツかにかかわらず同じ ように扱います。

値がプロジェクトルートより上を指すエイリアス(モノレポのアプリにおける $plugins: '../../plugins' など)も たどります。設定がアプリをそこからビルドすると宣言しているので、svelte-vitals はそのディレクトリ内のファイルと、 それらの間の相対インポートを読みます。プロジェクトルートより上にあるそれ以外の場所は読みません。そうした ディレクトリ以外へルートから出る相対インポートは、解決されないままです。

モノレポでは、アプリの package.json が workspace: の範囲("@repo/ui": "workspace:*")か、 リポジトリ内を指す link:/file: のパスで宣言している同じリポジトリのパッケージもたどります。 svelte-vitals はアプリより上で最も近い pnpm-workspace.yaml か package.json の workspaces から そのパッケージを name で見つけ、インポートをパッケージの exports(ない場合はファイル配置)で解決します。 exports がパッケージの dist/ 以下のファイルを指していて、チェックアウトにそのファイルがない場合 (ソースからビルドするパッケージで、まだビルドしていない場合)は、src/ か src/lib/ 以下の同じパスから読みます。 node_modules/<そのパッケージ> を指す kit.alias はパッケージのディレクトリを読むため、 チェックアウトがインストール済みかどうかで結果は変わりません。探索は .git のあるディレクトリで止まります。 このように宣言されていないパッケージと、リポジトリ外のものは読みません。 対象は <head> と見出しの走査だけです。この節の冒頭に挙げたルールのインポート解決は変わりません。

チェックアウトがインストール済みなら、<head> と見出しの走査は、アプリがレジストリから宣言している Svelte パッケージ("@acme/ui": "^2.1.0"、catalog: や npm: の範囲など)のコンポーネントもたどります。 import { Head } from '@acme/ui' のようなコンポーネントの <title>、メタタグ、見出しは、それを描画するルートに数えます。 パッケージは Node と同じく、アプリのディレクトリから上へ最初に見つかる node_modules/<name> から探します。 探索は .git のあるディレクトリで止まり、.git がなければアプリ自身の node_modules だけを見ます。 pnpm のリンクもそのままたどって読みます。インポートは svelte 条件付きの exports、または svelte フィールドで解決し、 そのどちらかを公開しているパッケージだけを読みます。既知のアダプタがあるパッケージ(svelte-meta-tags、svelte-seo、svead)は、 引き続きアダプタが読みます。アクセシビリティのルールと、この節の冒頭に挙げたルールは、これらのパッケージの中を見ません。 インストールしていないチェックアウトでは何も変わらないため、そうしたコンポーネントは metaComponents で宣言してください。

以下のケースは解決されません:

  • エイリアスの値が文字列リテラル以外から(テンプレートリテラル、変数、path.resolve(dir) などで) 計算される場合。指定子は「見えないもの」として扱われ、何も報告されません。文字列リテラルだけを渡す resolve(...)/path.join(...)(先頭に __dirname、import.meta.dirname、process.cwd() を置く形も含む)と fileURLToPath(new URL('./src/x', import.meta.url)) は、プロジェクトルートからの相対パスとして読みます。
  • エイリアスの値がリテラルの絶対パスである場合。/opt/shared/src のような POSIX パスや、 C:\shared\src のような Windows のドライブレター付きパスなど。プロジェクトの外にあるファイルを指す ため、対象として扱いません。
  • kit.files.lib 自体がプレーンな文字列ではなく計算される場合。この場合 $lib 自体が解決不能になるため、 上記のルールは $lib/... から始まる指定子に沈黙するだけでなく、 security/handler-state-write の lib server/ 免除も、 ディレクトリの移動先を推測せず無効のままになります。
  • svelte.config.{js,ts} ではなく vite.config.ts の sveltekit() プラグインに SvelteKit のオプションを 渡しているプロジェクトの場合。この場合も同様に何も報告されません。
  • kit.alias オブジェクトにスプレッド({ ...shared, '$a': 'src/a' })や計算されたキー ({ [key]: 'src/a' })が含まれる場合。未知のキーはそれより後ろに宣言されたどのエントリでも上書き しうるため、そのエントリ1つだけでなく kit.alias 全体が読み取り不能として扱われます。隣に書か れたリテラルなエイリアスも、これに引きずられて解決されなくなります($lib は常に先頭の位置を占め るため、何にも上書きされず影響を受けません)。

優先順位

各フィールドについて、次のうち最初に設定されているものが優先されます:CLI フラグ > 設定ファイル > 組み込みのデフォルト。この優先順位はフィールドごとに独立して働きます。一度きりの --fail-on info を指定しても、設定ファイルの他のフィールドはそのまま生きています。

一つ例外があります。--rules と --ignore はどちらも「どのルールを実行するか」を選ぶだけで、有効なままのルールをどう設定するかには関与しません。--rules は名前を挙げたルール ID に実行を絞り込み、その ID については設定ファイルのトップレベルの rules に書かれた off を上書きします。ルールを無効化することそのものが選択だからです。それ以外、名前を挙げたルールの重大度やオプションは設定ファイルの値をそのまま引き継ぎます。--ignore は向きが違うだけです。名前を挙げたルール ID に off エントリを追加するだけで、これは rules(設定ファイル、あるいは --rules によって絞り込まれた後の値)の上にレイヤーとして重ねて適用されます。名前を挙げていないルールには何も起こらず、直前のレイヤーが解決した値、すなわち設定ファイルの設定、あるいは --rules によって除外されていれば off を、そのまま保持します。両方が同じルールを名指ししたときは --ignore が --rules に勝ちます。プログラムから、あるいは Vite プラグインのオプションとして明示的に渡した rules の値は、これまで通り設定ファイルの rules を丸ごと置き換えます。

overrides に対応する CLI フラグはありません。ルートのポリシーはコミットされるファイルに置くべきものだからです。CLI と Action にとっては設定ファイルが唯一のソースで、Vite プラグインだけは追加でプラグインオプションとしても受け取ります(いつも通り、オプション > ファイル)。

バリデーション

  • 無効で svelte-vitals が停止する(終了コード 2):ファイルを読み込めない(構文エラー、または default export がない場合)、rules 内に未知のルール ID がある、weights 内に未知のカテゴリまたは負の値や数値でない値がある場合、overrides のエントリが不正な場合({ route/files, rules } の形でない、スコープが文字列でも空でない文字列配列でもない、rules 内のキーが既知のルール ID でもカテゴリ名でもない)、rules または overrides エントリの rules 内のルール設定が不正な場合(off/critical/warning/info 以外の文字列、未知のキーを含むオブジェクト形式、オブジェクト内の不正な severity、オプションを受け付けないルールへの options、未知のオプション名、型が誤っているか範囲外、またはオプションが宣言する文法に合わないオプション値(タグ名のリストにセレクタを書いた場合)、カテゴリのキーへの options、または min と max の両方を持つルールで min が max を超える範囲になっている場合のいずれか)。
  • 無効だが無視され、警告が出る(分析は続行される):認識できない treatDynamicAs または failOn の値(フラグ/デフォルトにフォールバック)、認識できないトップレベルキー(将来の設定フィールドとの前方互換性のため)。

TypeScript の設定ファイル

svelte-vitals.config.ts はサポート対象のすべての Node でそのまま動作します(最低要件の 24.16 以降は TypeScript の型をフラグなしでネイティブに除去します)。

Vite プラグインで設定ファイルを再利用する

@svelte-vitals/vite は CLI と同じ方法で svelte-vitals.config.* を読み込みます。追加の配線は不要です。ビルドゲートとライブダッシュボードのどちらも、プロジェクトルート(svelteVitals({ ... }) に渡す cwd、省略時は Vite の config root)から解決し、CLI と同じフィールド単位の優先順位を適用します: プラグインオプション > 設定ファイル > 組み込みのデフォルト。weights も含みます。

import { sveltekit } from '@sveltejs/kit/vite';
import { svelteVitals } from '@svelte-vitals/vite';

export default {
  plugins: [sveltekit(), svelteVitals({ report: 'console' })]
};

プロジェクトルートに svelte-vitals.config ファイルを置くだけで、上記のプラグインはその treatDynamicAs / metaComponents / rules / failOn / weights / overrides を自動的に読み込みます。vite.config.ts 側で自分でファイルを import する必要はありません。設定ファイルの非致命的な警告(未知のトップレベルキー、無効な列挙値など)は、CLI と同じ svelte-vitals: というプレフィックス付きの文言でコンソールに出力されます。