PostCSS:如何根据当前规则中的声明有条件地添加新规则?

时间:2016-07-12 21:57:42

标签: javascript css postcss

您好我一直在尝试使用PostCSS,并且在编写插件时遇到了一些困难,该插件会在找到某些CSS属性时添加新规则。

我想要实现的目标......

启动CSS:

.selector {
    color: red;
    not-yet-standard-property-a: 10px;
    not-yet-standard-property-b: 20px;
}

在PostCSS插件之后:

.selector {
    color: red;
    not-yet-standard-property-a: 10px;
    not-yet-standard-property-b: 20px;
}

.ie .selector {
    standard-property-a: 10px;
    standard-property-b: 20px;
}

每当我看到其中一条not-yet-standard-property-*时,我就可以轻松添加新规则...

return function (css) {
    css.walkRules(function (rule) {
        rule.walkDecls('not-yet-standard-property-a', function (decl) {
            css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-a: '+ decl.value +' }');
        });
        rule.walkDecls('not-yet-standard-property-b', function (decl) {
            css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-b: '+ decl.value +' }');
        });
        rule.walkDecls('not-yet-standard-property-c', function (decl) {
            css.insertAfter(rule, '.ie ' + rule.selector + '{ standard-property-c: '+ decl.value +' }');
        });
    });
}

但输出并不理想......

.selector {
    color: red;
    not-yet-standard-property-a: 10px;
    not-yet-standard-property-b: 20px;
    not-yet-standard-property-c: 53px;
}
.ie .selector {
    standard-property-c: 53px;
}
.ie .selector {
    standard-property-b: 20px;
}
.ie .selector {
    standard-property-a: 10px;
}

理想情况下,新规则只会在遍历整个规则后添加一次,但PostCSS似乎不允许在walkRules函数内部使用条件,因此我不确定如何阻止它为每个规则创建新规则它看到的规则。

我已经链接到上面代码的演示。对于更好的架构的任何建议都会非常感激,就像我之前说过的那样,我对此很新。谢谢!

Demo

修改:已解决。非常感谢Andrey Sitnik!我将他的答案标记为正确,因为其中的概念是我所需要的。对于那些寻找完整解决方案的人,请参阅以下代码(和演示!):

function isNotYetStandardProp (propName) {
    if (propName.indexOf('not-yet-standard-property-') > -1) {
      return true;
    }
    else {
      return false;
    }
}

function transformPropName (propName) {
    return propName.replace("not-yet-", "");
}

var skipNext = false;
var convertedProps = [];
var newRule;
var newNode;

return function (css) {

    css.walkRules(function(rule) {
        // The skipNext flag is used to prevent walkRules from walking onto any
        // newly created rules and recursively creating new rules based on the
        // newly added rule.
        if (!skipNext) {
            // Reset to remove values from last loop
            convertedProps = [];
            newRule = null;
            newNode = null;

            // Search through nodes of current rule and build up array
            // containing each property that needs to change.
            rule.nodes.filter(function (node) {
                if ((node.type === 'decl') && isNotYetStandardProp(node.prop)) {
                    newNode = node.clone();
                    newNode.prop = transformPropName(node.prop); 
                    convertedProps.push(newNode);
                }
            });

            // If there were properties to convert in this rule create a new
            // rule to put them in.
            if (convertedProps.length > 0) {
                newRule = rule.cloneAfter({ selector: '.ie ' + rule.selector });
                newRule.removeAll().append(convertedProps);
                skipNext = true;
            }
        }
        else {
            // We only want to skipNext once.
            skipNext = false;
        }
    })

}

Demo of solution

1 个答案:

答案 0 :(得分:1)

walkDecls内部walkRules不需要rule.nodes,只需使用css.walkRules(rule => { const nonStandard = rule.nodes.filter(node => { return if node.type === 'decl' && checkPropName(node.prop); }); if ( nonStandard.length > 0 ) { const clone = rule.cloneAfter({ selector: '.ie ' + rule.selector }); clone.append(nonStandard); } })

locationInView