all files / plugins/postcss/src/ index.js

97.44% Statements 76/78
82.61% Branches 38/46
90% Functions 18/20
100% Lines 68/68
1 statement Ignored     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                     15×   11× 11×       11×                             14× 18×             11×                                                                    
var postcss = require('postcss')
 
var own = {}.hasOwnProperty
 
 
module.exports = function(plugin) {
  // cache the PostCSS processor. Its creation is delayed so that
  // more plugins can be added through $postcss
  var processor
 
  return {filter: function(next) {
 
    function unknownBlock(type){
      /* istanbul ignore next */
      next.err('j2c-plugin-postcss doesn\'t know how to handle PostCSS nodes of type "' + type + '".')
    }
    // combines successive `j2c-postcss-plugin`-based plugins for efficiency.
    if (own.call(next,'$postcss')) return next.$postcss(plugin)
 
    var plugins = plugin ? [plugin] : []
    var parent, root, done, inRule
 
    // `handlers` and `block` turn the PostCSS tree into
    // j2c streams after processing.
    var handlers = {
      atrule: function (node) {
        var kind = node.name.replace(/^-[a-zA-Z_]\w+-/, '')
        if (node.nodes) {
          var params = ''
          Eif (node.params) {
            params = node.params
          }
          next.atrule('@'+node.name, kind, params, node.raws ? node.raws.j2cBlock || true : true)
          block(node.nodes)
          next._atrule()
        } else {
          next.atrule('@'+node.name, kind, node.params)
        }
      },
      comment: function(){},
      decl: function (node) {
        if (own.call(node, 'raws') && own.call(node.raws, 'j2cRaw')) next.raw(node.raws.j2cRaw)
        else if (own.call(node, 'raws') && own.call(node.raws, 'j2cErr')) next.err(node.raws.j2cErr)
        else next.decl(node.prop, node.value)
      },
      rule: function (node) {
        if (own.call(node, 'raws') && own.call(node.raws, 'j2cRaw')) next.raw(node.raws.j2cRaw)
        else if (own.call(node, 'raws') && own.call(node.raws, 'j2cErr')) next.err(node.raws.j2cErr)
        else {
          next.rule(node.selector)
          block(node.nodes)
          next._rule()
        }
      }
    }
 
    function block(nodes) {
      nodes.forEach(function (node) {
        (handlers[node.type]||unknownBlock(node.type))(node)
      })
    }
 
    // These filters turn the streams into a PostCSS tree.
    // Once done, `this.x()` turns back the tree into a
    // series of streams.
    return {
      $postcss: function(plugin) {
        plugins = [plugin].concat(plugins)
        return this
      },
      init: function(){
        parent = postcss.root()
        root = parent
        done = false
        inRule = false
        next.init()
      },
      done: function() {
        Eif (!done) {
          done = true
          // initialize the processor if needed
          processor = processor || postcss(plugins)
          Iif (root !== parent) throw new Error("Missing '}'")
          var options = {stringifier: function () {}}
          var result = root.toResult(options)
 
          // process and convert back to j2c streams
          block(processor.process(result, options).root.nodes)
        }
        return next.done()
      },
      // Use custom properties to pass raw declarations through the plugins unaltered
      err: function(msg) {
        if (inRule) parent.push(postcss.decl({prop: 'j2c-err', value: 'j2c-err', raws: {j2cErr: msg}}))
        else parent.push(postcss.rule({selector: 'j2c-err', raws:{j2cErr: msg}, nodes: [
          postcss.decl({prop: 'color', value: 'red'})
        ]}))
      },
      raw: function(txt) {
        if (inRule) parent.push(postcss.decl({prop: 'j2c-raw', value: 'j2c-raw', raws: {j2cRaw: txt}}))
        else parent.push(postcss.rule({selector: 'j2c-raw', raws: {j2cRaw: txt}, nodes: [
          postcss.decl({prop: 'color', value: 'red'})
        ]}))
      },
      atrule: function(rule, kind, params, takesBlock) {
        var node = {name: rule.slice(1), raws: {j2cBlock: takesBlock}}
        Eif (params !== '') node.params = params
        node = postcss.atRule(node)
        parent.push(node)
        if (takesBlock) {
          Iif (takesBlock == 'decl') inRule = true
          node.nodes = []
          parent = node
        }
      },
      _atrule: function () {
        inRule = false
        parent = parent.parent
      },
      decl: function (prop, value) {
        parent.push(postcss.decl({prop: prop, value: value}))
      },
      rule: function (selector) {
        inRule = true
        var rule = postcss.rule({selector: selector})
        parent.push(rule)
        parent = rule
      },
      _rule: function () {
        inRule = false
        parent = parent.parent
      }
    }
  }}
}