CSSWringに同じルールセット内で重複する定義(プロパティーとその値のセット)をルールセットから削除する機能をつけてる。最初、重複するプロパティーを問答無用で削除しようかと考えたんだけど、同じプロパティーを同じルールセット内でわざと使うことは結構あるのでダメそうだった。なので完全一致で削除するようにしようとしてる。
PostCSSでCSSをパースすると、各定義は以下のような構造になる(一部省略)。
{
type: 'decl',
before: '\n ',
prop: 'color',
between: ': ',
value: 'white'
}
type
に定義であることが明記されていて、prop
でプロパティー名、value
で値が拾える。またbefore
にプロパティー名の前にある文字列、between
にプロパティーと値の間の文字列が入ってる。
一致のチェックはprop
とvalue
を単純に連結させ、それをキーにしてその定義のインデックスをオブジェクトに格納していき、hasOwnProperty
で探すという形で良さそう。見つかったら重複しているとみなし、格納したインデックスを使って古い方を削除し、新しい方のみ残すようにする。
PostCSSでは全定義をなめるイテレータ(eachDecl()
)もあるけど、この場合はルールセット単位で作業するべきなのでeachRule()
の方で行う。
var postcss = require('postcss');
var css = fs.readFileSync('test.css', 'utf-8');
var root = postcss.parse(css);
root.eachRule(function (rule) {
var d = '';
var decls = {};
rule.each(function (decl, index) {
d = decl.prop + decl.value;
if (decls.hasOwnProperty(d)) {
decl.parent.remove(decls[d]);
}
decls[d] = index;
});
});
通常はこのようにprop
とvalue
が一致すれば良いけど、CSSWringには一部CSSハックを維持する設定がある。この設定がONの時にこれでは判別できない。対応しているCSSハックが格納されているのはbefore
とbetween
なので、それらも含めて完全一致させる必要があった。空白の違いにより重複が判別できなくなりそうだけど、CSSWringでは空白文字の圧縮は事前に行われているのでそこは考えなくて良さそう。上記コードだと8行目を以下のようにするだけ。
d = decl.before + decl.prop + decl.between + decl.value;
最初hasOwnProperty
ではなくdecls[d]
で存在チェックをしていて、インデックスが0の定義が重複していた時にちゃんとチェックできてないみたいなのやらかした……。