PostCSSアーキテクチャ
PostCSSアーキテクチャの一般的な概要です。コアへの貢献を希望するすべての方、またはこのツールのより深い理解を望むすべての方にとって役立つでしょう。
概要
このセクションでは、PostCSSの背後にあるアイデアについて説明します。
PostCSSの開発に深く掘り下げる前に、PostCSSとは何か、そしてPostCSSではないものを簡単に説明しましょう。
PostCSS
-
は、
Sass
やLess
のような**スタイルプリプロセッサではありません**。独自の構文とセマンティクスを定義するものではなく、実際には言語ではありません。PostCSSはCSSで動作し、上記で説明したツールと簡単に統合できます。つまり、有効なCSSであればPostCSSで処理できます。
-
は、CSS構文変換のためのツールです。
プラグインによって理解され変換できる、カスタムCSSのような構文を定義できます。つまり、PostCSSは厳密にはCSS仕様に関するものではなく、CSSの構文定義方法に関するものです。このように、PostCSSを基盤とするツールの構築に非常に役立つ、独自の構文構造(@ルールなど)を定義できます。PostCSSは、優れたCSS操作ツールを構築するためのフレームワークとしての役割を果たします。
-
は、CSSエコシステムにおいて大きな存在感を示しています。
Autoprefixer
、Stylelint
、CSSnano
など、多くの素晴らしいツールがPostCSSエコシステム上に構築されています。すでに暗黙的に使用している可能性が高いので、node_modules
を確認してみてください😀
ワークフロー
これは、PostCSSワークフロー全体の概要です。

上の図からわかるように、PostCSSアーキテクチャは非常にシンプルですが、一部は誤解される可能性があります。
「パーサー」と呼ばれる部分がありますが、この構造については後で詳しく説明します。今のところは、CSSのような構文を理解し、そのオブジェクト表現を作成できる構造と考えてください。
つまり、パーサーを作成する方法はいくつかあります。
-
文字列からASTへの変換を行う単一ファイルを作成する
この方法は非常に一般的です。たとえば、Reworkアナライザーはこのスタイルで記述されています。しかし、コードベースが大きくなると、コードの可読性が低下し、速度が低下します。
-
字句解析/構文解析ステップに分割する(ソース文字列→トークン→AST)
これはPostCSSで行っている方法であり、最も一般的な方法でもあります。
@babel/parser
(Babelの背後にあるパーサー)、CSSTree
など多くのパーサーがこの方法で記述されています。トークン化ステップと構文解析ステップを分離する主な理由は、パフォーマンスと複雑さの抽象化です。
2番目の方法が当社のニーズにとってより優れている理由を考えてみましょう。
まず第一に、文字列からトークンへのステップは、構文解析ステップよりも時間がかかります。大規模なソース文字列を操作し、文字単位で処理するため、パフォーマンスの観点から非常に非効率的な操作であり、一度だけ実行する必要があります。
しかし、一方で、トークンからASTへの変換は論理的により複雑であるため、このような分離により、非常に高速なトークナイザー(ただし、コードの可読性が低下することがあります)と、可読性の高い(ただし速度が遅い)パーサーを作成できます。
要約すると、2つのステップに分割することで、パフォーマンスとコードの可読性が向上します。
それでは、PostCSSワークフローで主要な役割を果たす構造をより詳しく見てみましょう。
コア構造
-
トークナイザー
lib/tokenize.js
トークナイザー(別名レクサー)は、構文解析において重要な役割を果たします。
CSS文字列を受け取り、トークンのリストを返します。
トークンは、
@rule
、comment
、word
などの構文の一部を記述する単純な構造です。より記述的なエラーのために、位置情報を含むこともできます。たとえば、次のCSSを考慮した場合
.className { color: #FFF; }
PostCSSからの対応するトークンは次のようになります。
[ ["word", ".className", 1, 1, 1, 10] ["space", " "] ["{", "{", 1, 12] ["space", " "] ["word", "color", 1, 14, 1, 18] [":", ":", 1, 19] ["space", " "] ["word", "#FFF" , 1, 21, 1, 23] [";", ";", 1, 24] ["space", " "] ["}", "}", 1, 26] ]
上記の例からわかるように、単一のトークンはリストとして表現され、
space
トークンには位置情報がありません。word
のような単一のトークンを詳しく見てみましょう。前述のように、各トークンはリストとして表され、このようなパターンに従います。const token = [ // represents token type 'word', // represents matched word '.className', // This two numbers represent start position of token. // It is optional value as we saw in the example above, // tokens like `space` don't have such information. // Here the first number is line number and the second one is corresponding column. 1, 1, // Next two numbers also optional and represent end position for multichar tokens like this one. Numbers follow same rule as was described above 1, 10 ]
トークン化を行う方法は数多くありますが、PostCSSのモットーはパフォーマンスとシンプルさです。トークン化は複雑なコンピューティング操作であり、構文解析時間の多く(約90%)を占めるため、PostCSSのトークナイザーは汚いように見えますが、速度を最適化するために設計されています。クラスのような高レベルの構造は、トークナイザーの速度を大幅に低下させる可能性があります。
PostCSSのトークナイザーは、
nextToken()
メソッドをパーサーに公開する一種のストリーミング/チェーンAPIを使用しています。このようにして、パーサーにクリーンなインターフェースを提供し、少数のトークンのみを格納することでメモリ使用量を削減します。 -
パーサー
lib/parse.js
、lib/parser.js
パーサーは、入力CSSの構文解析を担当する主要な構造です。パーサーは、抽象構文木(AST)と呼ばれる構造を生成し、後でプラグインによって変換できます。
パーサーはトークナイザーと連携して動作し、非常に非効率的な操作となるため、ソース文字列ではなくトークンを操作します。
主に、単一のトークンまたは複数のトークンを取得し、
Node
と呼ばれるASTの一部を構築するために、トークナイザーによって提供されるnextToken
メソッドとback
メソッドを使用します。PostCSSが生成できるノードタイプは複数ありますが、それらのすべては基本ノードクラスを継承します。
-
プロセッサ
lib/processor.js
プロセッサは、プラグインを初期化し、構文変換を実行する非常に単純な構造です。
公開APIメソッドはごくわずかしか公開していません。それらの説明はAPIにあります。
-
ストリングファイヤー
lib/stringify.js
、lib/stringifier.js
ストリングファイヤーは、変更されたASTを純粋なCSS文字列に変換する基本クラスです。ストリングファイヤーは、提供されたノードから開始してASTをトラバースし、対応するメソッドを呼び出してその生の文字列表現を生成します。
最も重要なメソッドは、初期ノードとセミコロンインジケーターを受け取る
Stringifier.stringify
です。stringifier.jsを確認することで、詳細を学ぶことができます。
APIリファレンス
より詳細なAPIドキュメントはこちらにあります。