HTMLからlink
要素で参照しているCSSの内容をstyle
属性に全部展開するNode.jsパッケージ、node-inliningを書いていた。HTMLとCSSを別々に普通に書き、このパッケージに含まれるCLIプログラムでコンパイルすると、HTMLメールとしてうまく機能するHTMLができあがるということになる。GitHubで推奨されている外部リソースに依存しない静的なエラー・ページを作成するためにも使えるかもしれない。
CLIプログラムはごく簡単に使うことができる。
$ npm install -g inlining $ inlining input.html >output.html
これでoutput.html
にインライン化されたHTMLファイルが吐かれる。処理例はREADMEの簡単な例やtestディレクトリーを見てくれればわかるはずだ。
Node.jsパッケージとしての利用は少しややこしくなる。
var inlining = require("inlining");
inlining(fs.readFileSync("input.html", "utf8"), function (result) {
console.log(result);
});
引数は以下の3つになる。
HTMLコードを直接渡すと処理して、コールバック関数が処理結果を引数として実行される。HTMLファイルのパスは相対パスを解決するために使っている。省略した場合はカレント・ディレクトリーになる。
パッケージ内では以下の様な順で処理される。
rel="stylesheet"
であるlink
要素を列挙href
属性の値をパスとして解決link
要素へstyle
属性の値に設定style
要素の内容としてhead
要素に追加link
要素を削除@media
ルールなどのstyle
属性へ記述できないルールセット群はそのまま残り、出力HTMLのhead/style
にそのままコピーされることになる。ここで詳細度が逆転してしまう可能性があるので、@media
ルールで上書きしたい場合は!important
フラグを駆使する必要がある。他、相対パスで指定された画像ファイルなどはDataURLで埋め込まれる。
内部ではHTMLをパースしてDOM API群を提供してくれるjsdomパッケージとおなじみCSSをパースしてくれるpostcssパッケージを利用した。jsdomはその存在は知っていたものの、初めてまともに使った。概ね使いやすかったが、やはりバグとはいえないまでも、いくつか特徴的な挙動は持つようだ。
例えばjsdomでHTMLElement.style.cssText
を使うとノーマライズされてしまう。そのためベンダー拡張プリフィックス付きのプロパティーや存在しないプロパティー(foo
とか)、そして未知のプロパティー(font-feature-settings
プロパティーとか)がうまく追加できなかった。仕方がないのでElement.setAttribute()
を使って強引にそのまま設定している。
またquerySelectorAll()
で::-moz-selection
擬似要素を含むものなど不明なセレクターを投げるとブラウザーと同じように例外を吐く。ブラウザーではそのまま処理は続行されるが、Node.js上では当然落ちる。使いづらいが挙動としては正しそうなため、try..catch
で握りつぶして無視した。
最後に完全なHTMLソースを手に入れることに少し苦労した。window.document.documentElement.innerHTML
だとhtml
要素が除外され、window.document.documentElement.outerHTML
だとDOCTYPEが拾えない。window.document.doctype
を使って連結するのは少しややこしすぎる。どうやら専用の非標準APIが用意されているようで、それを使うとうまく手に入れられた。
var jsdom = require("jsdom");
jsdom.env(
"<!DOCTYPE html><p>Lorem ipsum</p>",
function (errors, window) {
console.log(window.document.documentElement.innerHTML);
// <head></head><body><p>Lorem ipsum</p></body>
console.log(window.document.documentElement.outerHTML);
// <html><head></head><body><p>Lorem ipsum</p></body></html>
console.log(jsdom.serializeDocument(window.document));
// <!DOCTYPE html><html><head></head><body><p>Lorem ipsum</p></body></html>
}
);
他、MIMEタイプの推定にはmime
パッケージ、画像のDataURL化にはBuffer.toString("base64")
に当たるものを利用した。
とりあえず動くところまでという形で書いた。続きはわからないけれど、必要な機能はもうあまりなさそうだ。強いて言うのならHTMLファイル内の画像ファイルのDataURL化くらいだろうか。