|
1 |
| -import postcss from 'postcss'; |
2 |
| -import selectorParser from 'postcss-selector-parser'; |
| 1 | +import postcss from 'postcss' |
| 2 | +import selectorParser from 'postcss-selector-parser' |
| 3 | +import load from 'postcss-load-config' |
| 4 | +import { Meteor } from 'meteor/meteor' |
3 | 5 |
|
4 |
| -scopeId = postcss.plugin('scope-id', function (opts) { |
5 |
| - return function (root) { |
6 |
| - var keyframes = Object.create(null) |
| 6 | +let loaded |
7 | 7 |
|
8 |
| - root.each(function rewriteSelector (node) { |
9 |
| - if (!node.selector) { |
10 |
| - // handle media queries |
11 |
| - if (node.type === 'atrule') { |
12 |
| - if (node.name === 'media') { |
13 |
| - node.each(rewriteSelector) |
14 |
| - } else if (node.name === 'keyframes') { |
15 |
| - // register keyframes |
16 |
| - keyframes[node.params] = node.params = node.params + '-' + opts.id |
17 |
| - } |
| 8 | +loadPostcssConfig = Meteor.wrapAsync(function (cb) { |
| 9 | + let error = null |
| 10 | + if (!loaded) { |
| 11 | + loaded = load().catch(err => { |
| 12 | + // postcss-load-config throws error when no config file is found, |
| 13 | + // but for us it's optional. only emit other errors |
| 14 | + if (err.message.indexOf('No PostCSS Config found') < 0) { |
| 15 | + error = err |
| 16 | + error.message = 'PostCSS config Error: '.red + error.message |
| 17 | + } |
| 18 | + }) |
| 19 | + } |
| 20 | + |
| 21 | + loaded.then(config => { |
| 22 | + let plugins = [] |
| 23 | + let options = {} |
| 24 | + |
| 25 | + // merge postcss config file |
| 26 | + if (config && config.plugins) { |
| 27 | + plugins = plugins.concat(config.plugins) |
| 28 | + } |
| 29 | + if (config && config.options) { |
| 30 | + options = Object.assign({}, config.options, options) |
| 31 | + } |
| 32 | + |
| 33 | + cb(error, { |
| 34 | + plugins, |
| 35 | + options |
| 36 | + }) |
| 37 | + }) |
| 38 | +}) |
| 39 | + |
| 40 | +scopeId = postcss.plugin('add-id', ({ id }) => root => { |
| 41 | + const keyframes = Object.create(null) |
| 42 | + |
| 43 | + root.each(function rewriteSelector (node) { |
| 44 | + if (!node.selector) { |
| 45 | + // handle media queries |
| 46 | + if (node.type === 'atrule') { |
| 47 | + if (node.name === 'media' || node.name === 'supports') { |
| 48 | + node.each(rewriteSelector) |
| 49 | + } else if (/-?keyframes$/.test(node.name)) { |
| 50 | + // register keyframes |
| 51 | + keyframes[node.params] = node.params = node.params + '-' + id |
18 | 52 | }
|
19 |
| - return |
20 | 53 | }
|
21 |
| - node.selector = selectorParser(function (selectors) { |
22 |
| - selectors.each(function (selector) { |
23 |
| - var node = null |
24 |
| - selector.each(function (n) { |
25 |
| - // ">>>" combinator |
26 |
| - if (n.type === 'combinator' && n.value === '>>>') { |
27 |
| - n.value = ' ' |
28 |
| - n.spaces.before = n.spaces.after = '' |
29 |
| - return false |
30 |
| - } |
31 |
| - // /deep/ alias for >>>, since >>> doesn't work in SASS |
32 |
| - if (n.type === 'tag' && n.value === '/deep/') { |
33 |
| - var next = n.next() |
34 |
| - if (next.type === 'combinator' && next.value === ' ') { |
35 |
| - next.remove() |
36 |
| - } |
37 |
| - n.remove() |
38 |
| - return false |
| 54 | + return |
| 55 | + } |
| 56 | + node.selector = selectorParser(selectors => { |
| 57 | + selectors.each(selector => { |
| 58 | + let node = null |
| 59 | + selector.each(n => { |
| 60 | + // ">>>" combinator |
| 61 | + if (n.type === 'combinator' && n.value === '>>>') { |
| 62 | + n.value = ' ' |
| 63 | + n.spaces.before = n.spaces.after = '' |
| 64 | + return false |
| 65 | + } |
| 66 | + // /deep/ alias for >>>, since >>> doesn't work in SASS |
| 67 | + if (n.type === 'tag' && n.value === '/deep/') { |
| 68 | + const next = n.next() |
| 69 | + if (next.type === 'combinator' && next.value === ' ') { |
| 70 | + next.remove() |
39 | 71 | }
|
40 |
| - if (n.type !== 'pseudo' && n.type !== 'combinator') { |
41 |
| - node = n |
| 72 | + n.remove() |
| 73 | + return false |
| 74 | + } |
| 75 | + if (n.type !== 'pseudo' && n.type !== 'combinator') { |
| 76 | + node = n |
| 77 | + } |
| 78 | + }) |
| 79 | + selector.insertAfter(node, selectorParser.attribute({ |
| 80 | + attribute: id |
| 81 | + })) |
| 82 | + }) |
| 83 | + }).process(node.selector).result |
| 84 | + }) |
| 85 | + |
| 86 | + // If keyframes are found in this <style>, find and rewrite animation names |
| 87 | + // in declarations. |
| 88 | + // Caveat: this only works for keyframes and animation rules in the same |
| 89 | + // <style> element. |
| 90 | + if (Object.keys(keyframes).length) { |
| 91 | + root.walkDecls(decl => { |
| 92 | + // individual animation-name declaration |
| 93 | + if (/-?animation-name$/.test(decl.prop)) { |
| 94 | + decl.value = decl.value.split(',') |
| 95 | + .map(v => keyframes[v.trim()] || v.trim()) |
| 96 | + .join(',') |
| 97 | + } |
| 98 | + // shorthand |
| 99 | + if (/-?animation$/.test(decl.prop)) { |
| 100 | + decl.value = decl.value.split(',') |
| 101 | + .map(v => { |
| 102 | + const vals = v.split(/\s+/) |
| 103 | + const name = vals[0] |
| 104 | + if (keyframes[name]) { |
| 105 | + return [keyframes[name]].concat(vals.slice(1)).join(' ') |
| 106 | + } else { |
| 107 | + return v |
42 | 108 | }
|
43 | 109 | })
|
44 |
| - selector.insertAfter(node, selectorParser.attribute({ |
45 |
| - attribute: opts.id |
46 |
| - })) |
47 |
| - }) |
48 |
| - }).process(node.selector).result |
| 110 | + .join(',') |
| 111 | + } |
49 | 112 | })
|
| 113 | + } |
| 114 | +}) |
50 | 115 |
|
51 |
| - // If keyframes are found in this <style>, find and rewrite animation names |
52 |
| - // in declarations. |
53 |
| - // Caveat: this only works for keyframes and animation rules in the same |
54 |
| - // <style> element. |
55 |
| - if (Object.keys(keyframes).length) { |
56 |
| - root.walkDecls(decl => { |
57 |
| - // individual animation-name declaration |
58 |
| - if (/-?animation-name$/.test(decl.prop)) { |
59 |
| - decl.value = decl.value.split(',') |
60 |
| - .map(v => keyframes[v.trim()] || v.trim()) |
61 |
| - .join(',') |
62 |
| - } |
63 |
| - // shorthand |
64 |
| - if (/-?animation$/.test(decl.prop)) { |
65 |
| - decl.value = decl.value.split(',') |
66 |
| - .map(v => { |
67 |
| - var vals = v.split(/\s+/) |
68 |
| - var name = vals[0] |
69 |
| - if (keyframes[name]) { |
70 |
| - return [keyframes[name]].concat(vals.slice(1)).join(' ') |
71 |
| - } else { |
72 |
| - return v |
73 |
| - } |
74 |
| - }) |
75 |
| - .join(',') |
76 |
| - } |
77 |
| - }) |
| 116 | +trimCSS = postcss.plugin('trim', opts => css => { |
| 117 | + css.walk(({ type, raws }) => { |
| 118 | + if (type === 'rule' || type === 'atrule') { |
| 119 | + raws.before = raws.after = '\n' |
78 | 120 | }
|
79 |
| - } |
| 121 | + }) |
80 | 122 | })
|
0 commit comments