既知の制限事項
design-token-lint が使用する静的解析アプローチに固有の制限事項。
design-token-lint は上限付きの静的ソーススキャンと正規表現ベースのマッチングを使用して、ソースファイルからクラス名を抽出します。このアプローチは高速でビルドステップが不要ですが、実行時にコードを評価することはできません。そのため、一部の動的パターンは正しく解析されません。
スキャンされる属性名とユーティリティ関数のセットは、設定ファイルの classAttributes と classFunctions でカスタマイズできます。デフォルトでは、className、class、cn、clsx、classNames、twMerge がスキャンされます。
これらはバグではなく、静的解析に固有の制限です。
サポート済み: 複数行の className
複数行の className 値はサポートされています:
<div
className="
p-4
bg-gray-500
"
>複数行にまたがるクラスは正しく抽出されます。
サポート済み: class:list のオブジェクトキー
Astro の class:list オブジェクト構文はサポートされています — 引用符付きキーのクラス名が抽出されます:
<div class:list={[{ "p-4": true, "m-8": isActive }]}>p-4 と m-8 の両方が抽出されてリントされます。
サポート済み: 複数行の関数呼び出し
cn()、clsx()、classNames()、twMerge() の呼び出しが複数行にまたがる場合もサポートされています — 複数行の className 値と同様に、括弧が閉じるまで引数が蓄積されます:
<div
className={cn(
"p-4",
"bg-gray-500",
isActive && "m-8",
)}
>3 つの文字列引数すべてが抽出されてリントされます。これは複数行にまたがる class:list={[...]} 配列にも適用されます。
制限事項: Vue/Svelte の動的なクラスバインディング
静的な class="..." 属性は、Vue や Svelte を含むどのテンプレート言語でも正しく抽出されます。動的なバインディング構文はそうではありません:
<!-- Not linted correctly — extracted as garbage tokens, no real violations reported -->
<div :class="isActive ? 'p-4' : 'm-8'">
<div v-bind:class="{ 'p-4': isActive }"><!-- Not linted at all — the `class:` directive isn't recognized, nothing is extracted -->
<div class:active={isActive} class:p-4={isBig}>Vue の :class/v-bind:class バインディングは class 属性のパターンにマッチしてしまうため(先頭の : はマッチを妨げません)、式の内容が抽出されトークンに分割されます — しかしその結果は句読点だらけのゴミ(isActive、?、'p-4'、:、'm-8')であり、いかなる禁止パターンにもマッチしないため、その中の違反は静かに見逃されます。Svelte の class:name ディレクティブはそもそもマッチしないため(class の直後に = が続きません)、完全にスキップされます — こちらも違反は報告されません。
回避策: 関連するクラスを静的な文字列(computed プロパティや、変数に代入した cn()/clsx() 呼び出しなど)に抽出してリンターにプレーンな文字列リテラルとして認識させるか、無視コメントでこのギャップを明示してください。
複数行の属性式
設定されたクラス属性のバランスした {...} 式は、物理的な複数行にまたがってスキャンされます。スキャナーはネストした配列、.join(...) 呼び出し、条件式、ネストしたデリミター、設定されたクラス関数呼び出しをたどり、各リテラルの物理的な行を保持します:
export const B = () => (
<div
className={[
'flex items-center',
'z-40',
].join(' ')}
/>
);
export const C = ({ active }: { active: boolean }) => (
<div
className={`
flex items-center
${active ? 'z-40' : ''}
`}
/>
);どちらの z-40 リテラルもソースの行でリントされます。条件式が複数行にまたがる場合でも、その各分岐のリテラル文字列がチェックされます。マルフォーム/未クローズ式に対しては、スキャナーの上限が物理50行のままです。上限を超えた入力は、無関係なソースとして解釈せずスキャンされません。
動的なテンプレートセグメント
実行時に生成されるクラス名は静的には評価できません:
// The runtime value is unknown, so no complete class name can be checked.
<div className={`p-${size} bg-${color}-500`}>補間式の中に書かれたリテラルクラス文字列は引き続きチェックされるため、${active ? 'z-40' : ''} のような条件式も対象です。リントで網羅したい動的な名前は、有限個のリテラル分岐へ書き換えるか、動的境界を明示する無視コメントを追加してください。
制限事項: エスケープされた引用符
エスケープされた引用符を含むクラス属性は正しく抽出されない場合があります:
// May extract incorrectly
<div className="p-4 \"m-8\"">正規表現パーサーは文字列リテラル内のエスケープされた引用符シーケンスを処理しません。
回避策: クラス属性内でエスケープされた引用符を避けてください。代わりに JSX の式構文を使用してください:
<div className={'p-4 m-8'}>制限事項: CSS/SCSS スキャン(v1、宣言ベースのみ)
オプトインの css 設定セクションは、Tailwind エクストラクターと同じ行/正規表現ベースのアプローチ(PostCSS なし、AST なし)を使って、プレーンな CSS/SCSS の宣言値(zIndex、colorLiterals)をスキャンします。/ コメント(複数行にまたがるものを含む)、文字列リテラル、url(...) 値、SCSS の / 行コメントを正しく扱います。以下は v1 の既知の偽陰性 です — リテラルは存在するが、設計上フラグされないものです:
/* Not flagged — custom-property and SCSS-variable definitions are treated as
token-definition sites, not usage sites */
:root {
--brand: #f00;
}
.a {
$brand: #f00;
}
/* Not flagged — SCSS maps and other nested/interpolated SCSS constructs */
.a {
$colors: (primary: #f00, secondary: #0f0);
}パレット定義ゾーン(:root、@theme、SCSS 変数宣言)と、代わりにパレットを参照すべきセマンティックトークンの 使用 サイトとを区別するには、ゾーン認識スキャンが必要です — これは後回しにされた後続パスであり、v1 の一部ではありません。単一宣言を超える風変わりな複数行構文にまたがって分割された値も、確実に捕捉される保証はありません。
これは宣言値のスキャンのみです — <style> ブロックや CSS-in-JS のテンプレートリテラル内の Tailwind ユーティリティクラスについては何も言いません。それらは css セクションのスコープの完全に外にとどまります(Tailwind クラスエクストラクターは .css/.scss ファイルをスキャンせず、CSS 宣言ルールは .tsx/.jsx ファイルをスキャンしません)。
まとめ
| パターン | サポート |
|---|---|
複数行の className | あり |
複数行の cn()/clsx()/class:list 呼び出し | あり |
| 静的な文字列リテラル | あり |
複数行の {...} クラス属性(配列、条件式、ネストした呼び出し) | あり — リテラルセグメント |
class:list のオブジェクトキー | あり |
カスタム属性名(classAttributes) | あり — 設定可能 |
カスタム関数名(classFunctions) | あり — 設定可能 |
Vue/Svelte の静的な class="..." 属性 | あり |
CSS/SCSS 宣言スキャン(オプトイン、css セクション) | あり — 宣言ベースのみ、上記参照 |
| 実行時に生成されるテンプレートセグメント | なし — 完全な動的名は評価できない |
| クラス文字列内のエスケープされた引用符 | なし — 誤抽出の可能性あり |
Vue の :class/v-bind:class バインディング | なし — ゴミトークンとして抽出される |
Svelte の class:name ディレクティブ | なし — まったく認識されない |
| リテラルを保持する CSS カスタムプロパティ/SCSS 変数の定義 | なし — トークン定義ゾーンの認識は後回し |
| SCSS マップおよびネスト/補間された構造 | なし — v1 のスコープ外 |