sprockets-css-purgerの姿を想像する - @ledsun blogの続きです。
tailwindcssからPurgeCSSを呼び出している箇所
https://github.com/tailwindlabs/tailwindcss/blob/v2.2.15/src/lib/purgeUnusedStyles.js#L188-L220
const purgeCSS = new PurgeCSS() purgeCSS.options = { ...defaultOptions, defaultExtractor: (content) => { const transformer = getTransformer(config) return defaultExtractor(transformer(content)) }, extractors: fileSpecificExtractors, ...purgeOptions, safelist: standardizeSafelist(purgeOptions.safelist), } if (purgeCSS.options.variables) { purgeCSS.variablesStructure.safelist = purgeCSS.options.safelist.variables || [] } const fileFormatContents = content.filter((o) => typeof o === 'string') const rawFormatContents = content.filter((o) => typeof o === 'object') const cssFileSelectors = await purgeCSS.extractSelectorsFromFiles( fileFormatContents, purgeCSS.options.extractors ) const cssRawSelectors = await purgeCSS.extractSelectorsFromString( rawFormatContents, purgeCSS.options.extractors ) const cssSelectors = mergeExtractorSelectors(cssFileSelectors, cssRawSelectors) purgeCSS.walkThroughCSS(css, cssSelectors) if (purgeCSS.options.fontFace) purgeCSS.removeUnusedFontFaces() if (purgeCSS.options.keyframes) purgeCSS.removeUnusedKeyframes() if (purgeCSS.options.variables) purgeCSS.removeUnusedCSSVariables()
PurgeCSSを初期化します。
const purgeCSS = new PurgeCSS()
オプションオブジェクトを作ります。
purgeCSS.options = { ...defaultOptions, defaultExtractor: (content) => { const transformer = getTransformer(config) return defaultExtractor(transformer(content)) }, extractors: fileSpecificExtractors, ...purgeOptions, safelist: standardizeSafelist(purgeOptions.safelist), }
- defaultOptions
- defaultExtractor
- fileSpecificExtractors
- purgeOptions
あたりはソースコードの上の方を見ないとどんな値が入るかわかりません。 またpurgeOptionsあたりは、ユーザーが設定する値に依存すると予想できます。 PurgeCSSのオプションの意味を確認してみた方が良さそうです。
if (purgeCSS.options.variables) { purgeCSS.variablesStructure.safelist = purgeCSS.options.safelist.variables || [] }
オプションの値を詰め替えています。 なぜこの作業が必要なのかはpurgeCSSのAPIを見ないとわからなさそうです。
const fileFormatContents = content.filter((o) => typeof o === 'string') const rawFormatContents = content.filter((o) => typeof o === 'object') const cssFileSelectors = await purgeCSS.extractSelectorsFromFiles( fileFormatContents, purgeCSS.options.extractors ) const cssRawSelectors = await purgeCSS.extractSelectorsFromString( rawFormatContents, purgeCSS.options.extractors )
contentには文字列とオブジェクトが入るようです。 オブジェクトに入る形式がよくわかりません。
const cssSelectors = mergeExtractorSelectors(cssFileSelectors, cssRawSelectors)
purgeCSS.walkThroughCSS(css, cssSelectors)
これがpurgeCSSのメイン処理のようです。 cssとcssSelectorsにどのような値が期待されているのか、purgeCSSのAPIを確認すると良さそうです。
if (purgeCSS.options.fontFace) purgeCSS.removeUnusedFontFaces() if (purgeCSS.options.keyframes) purgeCSS.removeUnusedKeyframes() if (purgeCSS.options.variables) purgeCSS.removeUnusedCSSVariables()
これはわかりやすいです。 オプションがあったらpurgeCSSのオプショナルな機能を実行します。
これを見るだけでも、tailwindcssは、予想していたよりも複雑な処理をしてからpugreCSSを呼び出しています。 これを踏まえて、tailwindcssとpugreCSSの境界をどこに設けるか考えてみましょう。
tailwindcssとpugreCSSの境界
雑に2パターンが考えられます。
- tailwindcssのオプション型のオブジェクトを受け取って、tailwindcssと同じ処理をしてpurgeCSSを呼び出す
- purgeCSSのオプション型のオブジェクトを受け取って、そのままpugreCSSを呼び出す
2の方が、汎用的なAPIになるはずです。 ですが、呼び出し側は複雑なロジックが必要です。 少なくとも、前述のようにさっと読み流せない程度には複雑です。 そんな使い方を要求できるのでしょうか?
1は1で、tailwindcssのロジックへの追従をどうするのか?という問題があります。 tailwindcssのバージョンアップ毎にロジックを修正していくのでしょうか?
第3のパターンとして、purgeCSSの機能をすべてを使えない、制限されたシンプルなAPIを新しく設計した方がいいのかもしれません。 WebPackerのイメージです。