Template Toolkit と Maud の条件付きクラス抽出をサポート

tailwindlabs/tailwindcss

この PR は、Template Toolkit の %]…[% デリミタと Rust の Maud テンプレートにおける条件付きクラス構文を、Tailwind CSS のクラススキャナが正しく認識できるように前処理を追加します。結果として、従来漏れが発生していたクラスが抽出対象に含まれるようになります。

背景

Template Toolkit のデリミタ [% … %] が単語境界として認識されなかった ため、[% IF $cond %]bg-white/40[% END %] のようにデリミタ直後に続くクラス名がスキャンされませんでした。Issue #20233 では、[] が境界文字に含まれていないことが原因と指摘されており、テンプレートエンジン全般で類似の問題が報告されていました。

Maud の条件付きクラス p.text-black[condition] も同様に抽出できなかった ことが別途報告され、[ がそのままクラス名の一部として扱われるため、text-black が候補から外れていました。これらの問題は、HTML 以外のテンプレートファイルで Tailwind のユーティリティクラスを利用した際にビルド結果が期待通りにならない原因となっていました。

技術的な変更

Template Toolkit 用の前処理モジュール template_toolkit.rs を新規追加 し、[%%] をそれぞれスペースに置換します。これによりデリミタが単語境界となり、クラス名が正しく分離されます。

use crate::cursor;
use crate::extractor::pre_processors::pre_processor::PreProcessor;

#[derive(Debug, Default)]
pub struct TemplateToolkit;

impl PreProcessor for TemplateToolkit {
    fn process(&self, content: &[u8]) -> Vec<u8> {
        let len = content.len();
        let mut result = content.to_vec();
        let mut cursor = cursor::Cursor::new(content);

        while cursor.pos < len {
            match (cursor.curr(), cursor.next()) {
                (b'[', b'%') => result[cursor.pos] = b' ',
                (b'%', b']') => result[cursor.pos + 1] = b' ',
                _ => {}
            }
            cursor.advance();
        }
        result
    }
}

pre_process_input の拡張 により、拡張子 .tt, .tt2, .tx のファイルに対して TemplateToolkit 前処理が適用されます。HTML ファイルではコストを回避するためにこのロジックは呼び出されません。

match extension {
    "tt" | "tt2" | "tx" => TemplateToolkit.process(&content),
    // 既存ケース省略
    _ => content,
}

Rust 前処理 (rust.rs) に条件付きクラス対応を追加 し、[ が現れた際に前文字が - でなければスペースへ置換します。これにより p.text-black[condition][ が境界となり、text-black が抽出対象になります。

b'[' => {
    bracket_stack.push(cursor.curr());
    // `p.flex[condition]` の場合はスペースに置換。`-` 前の場合は任意値として除外。
    if !matches!(cursor.prev(), b'-') {
        result[cursor.pos] = b' ';
    }
}

テストケースが複数追加 され、Template Toolkit と Maud の両シナリオで期待通りにクラスが抽出されることを自動検証しています。テストは mod tests 以下に配置され、assert_extract_candidates_contains ヘルパーで確認されています。

#[test]
fn test_template_toolkit_syntax() {
    assert_extract_candidates_contains(
        &pre_process_input(
            r#"<div class=\"[% IF $is_open %]bg-white/40[% ELSE %]bg-white/10[% END %]\"></div>"#, 
            "tx",
        ),
        vec!["bg-white/40", "bg-white/10"],
    );
}

設計判断

前処理レイヤに機能を組み込む方針 が採用されました。スキャナ本体のトークナイザを変更せず、入力文字列を事前に加工するだけで既存ロジックを再利用できる点が評価されています。

ファイル拡張子ベースのディスパッチ によって、.html では余計な前処理コストが発生しません。PR の説明にもあるように、Template Toolkit の構文は特定拡張子でのみ使用されるため、対象を絞ることでパフォーマンスへの影響を最小化しています。

シンプルな置換ロジック(スペースへの置換)を選択したことで、正規表現や構文解析を導入するリスクを回避し、後方互換性を保持しつつ機能追加が実現できました。既存の true/false の判定ロジックには影響を与えていません。

まとめ

Template Toolkit の %]…[% デリミタと Maud の条件付きクラス構文を前処理で対応することで、Tailwind CSS のクラス抽出が正確になり、テンプレートエンジン固有の記法でもユーティリティクラスが利用可能になりました。実装は拡張子判定と文字置換という軽量手法にとどめ、既存コードへの侵入度を最小限に抑えた設計が特徴です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
9e1da4cc

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的な変更、設計判断(任意)とまとめが明確に分かれており、総論→各論→結論の構成が守られています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックのファイル名付きハイライトは正しい形式ですが、PRリンクが `[PR #20269](URL)` となっており、期待される `[ #20269 ](URL)` 形式と異なります。リンク記法が不完全ですが可読性には支障ありません。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

専門的なエンジニア向けの内容で、余計な初心者向け説明はなく、適切な技術レベルです。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論パラグラフ、各論、結論(まとめ)で構成され、段落はトピックセンテンスで始まり 1段落1トピック、長さも適切です。空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供された Diff と一致しており、ファイル名・変更内容ともに正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

PreProcessor、TemplateToolkit、Maud などの用語は PR と一致しており、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的な説明は PR の目的・変更点と整合しており、誤った主張や因果関係の破綻はありません。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

すべての主張は PR タイトル・説明・Diff に裏付けられており、根拠のない推測や外部知識の付加はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #20269 と Issue 番号 #20233 が正しく記載されています。数値的な誤りはありません。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の内容(Template Toolkit と Maud のクラス抽出サポート)を正確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

外部のバージョン情報やリリース日程など、PR に記載されていない情報は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現は使用されておらず、PR の記述と矛盾する表現もありません。