zfb
GitHub リポジトリ

検索したい単語を入力

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

ソースと候補

ソースプランが完全な候補を見つける方法と、明示的な入力の検証方法を説明します。

候補を発見できるようにする

このページで説明するソース発見と生成ルールは zudo-wind のものです。抽出器はアプリを実行せずにファイルを読むため、可能なクラスを完全な表記でソースに残します。

function ActionButton({ active }: { active: boolean }) {
  const buttonClass = active
    ? "bg-accent text-white"
    : "bg-muted text-white";

  return <button className={buttonClass}>Continue</button>;
}

有限の条件式なら完全な候補が両方とも見つかります。実行時の値と接頭辞を連結しても候補にはなりません。ソースに現れない完全なアプリ候補は、所有者 ID ごとの wind.safelist に置きます。

必要なもの使用するもの
ソースに書かれたアプリのクラスbuild または単独コマンドのソースプラン
走査ファイルに現れない有限のアプリ候補wind.safelist
パッケージが公開するクラスwind.manifests のバージョン付きマニフェスト
常に走査対象から外すパスwind.sources.exclude
クラスを含むファイルがあるパッケージwind.sources.packageRoots

パッケージのディレクトリからスタイルシートと候補マニフェストを生成します。

zfb css --input src/styles.css --output dist/ui.css \
  --config wind.config.json --no-auto-source --source 'src/**/*.tsx'
zfb wind manifest --producer ui --output dist/wind.json \
  --config wind.config.json --no-auto-source --source 'src/**/*.tsx'
zfb wind audit --plan build

利用側は生成者 ui を指定し、マニフェストが使うトークンをすべて設定します。このサンプルは設定済みの色と余白で 2 つの完全な候補をコンパイルします。

完全な候補が解決される様子
HTML
<div class="wind-demo-status"><p class="bg-accent text-white p-2">Ready</p><p class="bg-muted text-white p-2">Queued</p></div>
CSS
@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
  :root {
    --zw-color-accent: #155eef;
    --zw-color-muted: #475467;
    --zw-color-white: #fff;
    --zw-spacing-unit: .25rem;
  }
}
.wind-demo-status {
  max-width: 18rem;
}

.wind-demo-status p {
  margin: 0 0 .5rem;
}
.p-2 {
  padding-top: 0.5rem;
  padding-right: 0.5rem;
  padding-bottom: 0.5rem;
  padding-left: 0.5rem;
}
.text-white {
  color: var(--zw-color-white);
}
.bg-accent {
  background-color: var(--zw-color-accent);
}
.bg-muted {
  background-color: var(--zw-color-muted);
}
この例の設定
{
  "wind": {
    "spec": 1,
    "reset": "none",
    "strict": true,
    "tokens": {
      "spacingUnit": "0.25rem",
      "colors": {
        "accent": "#155eef",
        "muted": "#475467",
        "white": "#ffffff"
      }
    },
    "breakpoints": {},
    "defaultTransitionTimingFunction": "ease",
    "authoredClasses": {
      "wind-demo-status": true
    },
    "safelist": {
      "docs-preview": [
        "bg-accent",
        "bg-muted",
        "p-2",
        "text-white"
      ]
    }
  }
}
表示用の補助 CSS
.wind-demo-status { max-width: 18rem; }
.wind-demo-status p { margin: 0 0 0.5rem; }

明示した glob の一致がすべて除外されると ZW010 で失敗します。一部が残る場合は警告し、そのファイルを走査します。次のレコードは全除外と一部除外のソースプランを示します。

<p class="p-4">Excluded source</p>

sample.html と excluded.html は上記と同じ HTML を含みます。設定で excluded.html を除外します。

zfb css --input input.css --output compiled.css --config zfb.config.json --project-root . --source 'excluded.html' --no-auto-source --no-default-highlight-styles

期待される診断: ZW010 (error)

この例の設定
{
  "wind": {
    "spec": 1,
    "reset": "none",
    "strict": true,
    "tokens": {
      "spacingUnit": "0.25rem"
    },
    "breakpoints": {},
    "defaultTransitionTimingFunction": "ease",
    "authoredClasses": {},
    "safelist": {},
    "sources": {
      "exclude": [
        "excluded.html"
      ]
    }
  }
}
表示用の補助 CSS
<p class="p-4">Accepted source</p>

sample.html と excluded.html は上記と同じ HTML を含みます。設定で excluded.html を除外します。

zfb css --input input.css --output compiled.css --config zfb.config.json --project-root . --source '*.html' --no-auto-source --no-default-highlight-styles

期待される診断: ZW010 (warning)

この例の設定
{
  "wind": {
    "spec": 1,
    "reset": "none",
    "strict": true,
    "tokens": {
      "spacingUnit": "0.25rem"
    },
    "breakpoints": {},
    "defaultTransitionTimingFunction": "ease",
    "authoredClasses": {},
    "safelist": {},
    "sources": {
      "exclude": [
        "excluded.html"
      ]
    }
  }
}
表示用の補助 CSS

抽出器はアプリケーションを実行せず、実行時の値からクラスを推測しません。

技術リファレンス

ソースプラン

ソースプランは build と dev が走査するすべての入力を決めます。プロジェクトの規約上のルート、宣言されたソースルート、宣言されたパッケージルート、ミラールート、明示的なコマンドラインのソース、生成されたソース、候補マニフェストを含められます。パスは、それを指定したプロジェクト、パッケージ、プリセット、設定を基準に解決します。プロセスの作業ディレクトリは基準になりません。

pnpm-workspace.yaml が . を明示している場合、build と dev は既定でワークスペースルートパッケージも暗黙の Wind ソースに加えます。wind.sources.rootPackage: false はその暗黙の Wind ソースだけを除外します。プロジェクトのルート、import されたソース、明示したルート、パッケージルート、マニフェスト、safelist はそれぞれの方法で引き続き発見されます。true で暗黙のソースを戻せます。省略時はプリセットの値を継承し、プリセットに指定がなければ true です。この設定は別経路の CSS module / import 発見や、その監視方法を変更しません。単独用のソースプランは引き続きプロジェクト内に限られます。

zfb css と既定の zfb wind audit は単独用のソースプランを使います。これは規約上のルート、設定された wind.sources.roots、パッケージルート、除外、マニフェスト、明示的な --source の指定からなります。build と dev はさらに、パッケージ所有のルート、兄弟ミラールート、プラグインの仮想モジュールを加えます。zfb wind audit --plan build は同じ発見処理を実行するため、その一覧は zfb build がコンパイルする内容と一致します。どちらの監査モードも、レポートの前に対象のルートと除外を出力します。

build と dev が走査する拡張子は、大文字小文字を区別した .tsx、.ts、.jsx、.js、.mdx、.md です。単独の zfb css --source 経路では .html と .mjs も処理できます。この 2 種類は build と dev では自動走査されません。拡張子を上書きする公開設定はありません。

wind.sources.roots は宣言元プロジェクトまたはプリセットより厳密に下のディレクトリを追加します。gitignore、標準のスキップ対象ディレクトリ、wind.sources.exclude が適用されます。packageRoots は別のパッケージ走査チャネルです。通常の .ts と .test.ts は候補ですが、.d.ts は常に除外され、audit には excluded **/*.d.ts (declaration files) と表示されます。

抽出器が読むもの

抽出器は、JSX と HTML の class / className 属性、リテラルのマップ、スクリプト内の文字列とテンプレートリテラル、Markdown と MDX 内の生の HTML / JSX 属性から完全な候補文字列を探します。補間内にネストしたリテラルや、クラスリスト API に渡されたリテラル文字列も走査します。診断用のソース位置を保持しながら、ソース言語のエスケープと HTML エンティティを復号します。

コメント、Markdown の本文、インラインコード、フェンス付きコードは無視します。HTML を含むスクリプト文字列は、内部のクラス属性を検査します。文字列を読むだけでアプリケーションを実行することはありません。

候補は完全なトークンでなければなりません。有限のマップなら既知の選択肢を示せます。

const stateClasses = ["text-muted", "text-accent"];
const className = stateClasses[active ? 1 : 0];

text-muted と text-accent はどちらも完全なリテラルとして存在するため、個別に抽出できます。ユーティリティとして解決されるかどうかは設定次第です。bg- のような接頭辞と実行時の値を連結しても候補にはなりません。bg- や -500 のような断片は展開されないため、動的に組み立てたクラスは対象外です。可能性が有限なら、リテラルのマップか明示的な候補ソースを使ってください。

補間の境界では、静的トークンが単独で完全な場合だけ候補になれます。たとえば完全なトークンの直後に補間があれば、動的テキストに隣接している印を付けて保持することがあります。両側のテキストを結合してトークンを作ることはありません。

wind.sources.ignoreAttributes にはクラスではない prop の css、html、js、displayJs、title などを指定できます。組み込みの href、src、content、name、rel、type に追加し、入れ子のマークアップ、補間、クラスヘルパー呼び出しも含む prop の値全体を除外します。ほかの class 属性は走査されます。推測的なリテラルでは任意 CSS 形式の外の句読点ノイズを除き、Unicode と形式内の句読点は残します。埋め込みマークアップからは class 属性を一度だけ取り出します。クエリーセレクターやスクリプト本文のヒューリスティックはありません。

クラス位置と確信度

候補の位置によって検査の厳しさが決まります。class または className 属性に直接書いたテキストはクラス位置です。値がそのような属性に確実に届く完全な文字列もクラス位置です。

  • 条件式の分岐、または &&、||、?? の後ろのオペランド

  • クラスの値として使うテンプレートリテラルの静的な部分 (補間内の文字列を含む)

  • クラスヘルパー clsx、cn、cx、classNames、classnames の引数、配列要素、オブジェクトのキー

  • クラス式から参照される同じモジュールの const の初期化式 (別名経由を含む)

const linkClass = "px-3 text-ink"; // クラス位置: 下で参照される
const label = "ring-2"; // リテラル: クラス属性に届かない

export const Link = ({ active }) => (
  <a class={`flex ${active ? "border-accent" : "border-muted"}`}>
    <span class={linkClass}>{label}</span>
  </a>
);

定数を追跡するのは、const でちょうど 1 回だけ宣言され、インポート、再宣言、再代入がない場合だけです。比較のオペランド、ほかの関数の引数、メンバーやインデックスのアクセス、文字列連結はたどりません。関係のない定数やテストのタイトルにある適格な完全文字列は、確信度の低いリテラルです。モジュール指定子や URL または相対パスだけのリテラルは、候補として分類せず読み飛ばします。クラス位置にある無効または未対応のユーティリティは、直接書いたクラスと同じくビルドを失敗させます。同じテキストがリテラルにあれば監査専用情報です。

wind.sources.classHelpers は追加のローカルなクラスリストヘルパー識別子を宣言します。プリセットとプロジェクトの名前は組み込み名に加え、重複なく決定的に結合します。対応する文字列・テンプレート、配列、オブジェクト引数からクラスを抽出しますが、cva や twMerge の任意の意味は推測しません。@takazudo/zfb/zudo-react から値として import した h(確認済みの named import alias を含む)の直接の第2引数 props オブジェクトでは、裸または引用符付きの class と className キーを認識します。対応する条件分岐、テンプレート、同一モジュールの定数、設定済みヘルパーを値からたどれます。className の認識は移行時の走査のためだけで、renderer で有効にはなりません。任意のデータオブジェクト、無関係の factory、型専用 import、隠蔽された binding、label、無関係な位置の三項式、第2引数の外の props は確信度の低いままです。認識済みで不正な候補は、通常の build と wind audit --fail-on error でクラス位置のエラーになります。

明示的な候補の追加

safelist の各項目は所有者 ID から完全な候補の配列への対応です。正規表現、波括弧展開、補間の構文はありません。列挙した各項目はユーティリティ候補またはサポートされるマーカーでなければなりません。ただし完全な文字列が authoredClasses にあれば除きます。

パッケージはバージョン付き JSON マニフェストを公開できます。利用側はそのパスを wind.manifests の生成者 ID に指定します。マニフェストの producer はその ID と一致する必要があります。形式は次のとおりです。

{
  "schemaVersion": 1,
  "specVersion": 1,
  "producer": "widgets",
  "candidates": ["group", "p-0", "hover:bg-panel"]
}

スキーマのバージョンは言語のバージョンとは別です。マニフェストに含められるのは候補だけです。トークンの定義、コードの実行、依存関係からの暗黙の発見はできません。利用側はマニフェストの候補に必要なトークンを設定します。パスは宣言した設定またはパッケージを基準に解決します。ファイルの欠落、無効な JSON やスキーマ、未対応のソース拡張子はエラーです。未知または無効な明示的項目の診断には、候補とマニフェスト内の番号が残ります。重複する文字列は重複排除されます。2 つの宣言元が同じ生成者を主張すると、走査順で選ばずにエラーになります。生成者の置換や削除では、それまでの候補も取り消されます。

パッケージから候補を公開する

スタイルを配布するパッケージに、サイトの config やページは必要ありません。クラスが前提とするトークン設定を、package.json の隣の wind.config.json のような通常の zfb の config ファイルに置き、--config で渡します。zfb.config.* ファイルがないので、パッケージがサイトとして扱われることはありません。config ファイルのディレクトリがデフォルトのプロジェクトルートになるため、以下のコマンドはパッケージのディレクトリで実行します。

zfb css --input src/styles.css --output dist/ui.css \
  --config wind.config.json --no-auto-source --source 'src/**/*.tsx'
zfb wind manifest --producer ui --output dist/wind.json \
  --config wind.config.json --no-auto-source --source 'src/**/*.tsx'

zfb wind manifest はビルドと同じ抽出器とコンパイラで全ソースを 1 プロセスで処理し、解決されたユーティリティとマーカーだけを書き込みます。クラス位置の候補が解決できなければ、dist/wind.json に触れずに失敗します。設定済みの outDir と --output のパスは走査対象外なので、以前の dist/ のビルド結果が次のマニフェストに混ざることはありません。両方のファイルを package.json から export します。たとえば "./ui.css": "./dist/ui.css" と "./wind.json": "./dist/wind.json" です。

利用側は wind.manifests でマニフェストを宣言し、候補が使うトークンをすべて設定します。両方をまとめて配布するにはプリセットが最も簡単です。definePreset("@example/ui", { wind: { tokens, manifests: { ui: { path: "./dist/wind.json" } } } }) は、利用側がどこにインストールしても、インストール済みのプリセットパッケージを基準に ./dist/wind.json を解決します。トークンを持たない利用側では、最初に解決できなかったマニフェスト項目で自身のビルドが失敗するため、トークンの集合はパッケージの公開契約の一部として扱ってください。zfb wind manifest を参照してください。

手書きのクラス

CSS で自分で書くルールのために、完全な候補表記を予約するには authoredClasses を使います。各キーを true にします。バリアント付きの候補全体を手書きとして残す場合は、バリアント連鎖も含めてください。一致すると、どの発生元でもユーティリティの検証、出力、生成 CSS、候補の診断が抑止されます。これにより、手書きのセレクタで使うクラスを、ユーティリティとして解釈させずにマークアップに残せます。

許可リストは完全一致です。接頭辞、パターン、部分的な名前は受け付けません。無効な設定や、スタイルシートに残ったディレクティブは抑止できません。

除外と更新

以前の出力は走査しません。除外には 3 種類あり、適用範囲が異なります。

  • 出力先とスクラッチのパス。 設定された outDir(既定は dist)、zfb css --output のファイル、zfb 自身のスクラッチとキャッシュのディレクトリは、明示的な --source のファイルやパッケージルートも含むすべてのルートから除外されます。再び含めることはできません。outDir を別にすれば、プロジェクトの出力ではない dist 以下の明示的な --source のファイルは走査されます。

  • スキップするディレクトリ名。 ディレクトリのルートを走査するとき、node_modules、dist、build、target、.turbo、.next、.vercel、.zfb-build、.zfb という名前のサブディレクトリと、.git や .cache などの隠しディレクトリをスキップします。これは走査の枝刈りにすぎません。ルート自体がそのようなディレクトリである場合や、明示的な --source の glob が一致したファイルは走査されるため、--source 'build/**/*.tsx' はそれらのファイルを読みます。宣言されたパッケージルートは、自身の node_modules と dist もたどります。

  • 無視ファイル。 Gitignore の規則は各ルート内で適用しますが、宣言されたパッケージルートは例外です。プロジェクトは、パッケージルートがたどる必要のある node_modules と dist をまさに無視していることが多いため、規則を使わずに走査します。

ディレクトリのシンボリックリンクはたどりません。wind.sources.exclude による除外は、宣言元ルート内の一致するファイルを、明示的な --source のファイルも含むすべてのルートから取り除きます。上記の除外対象を再び含めることはできません。パッケージルートは wind.sources.packageRoots で宣言します。設定を参照してください。明示的な --source 指定の一致がすべて除外されると ZW010 で失敗し、除外された一致と有効な一致が混在する場合は警告して有効なファイルを走査します。

ソースインデックスは、各候補をどの所有者が供給したか追跡します。追加、削除、名前変更、マニフェスト変更、ソースプラン変更があると、該当する所有者の集合を置き換えます。ファイルや宣言を消せば、その候補も消えます。削除された候補を保険として保持することはありません。規約上のルートがなければスキップしますが、必須ルート、マニフェストの欠落、結果がない明示的な CLI glob はエラーです。

関連項目

Revision History

作成更新