1 | var postcss = require('postcss');
|
2 | var parser = require('postcss-selector-parser');
|
3 |
|
4 | function replace(nodes, parent) {
|
5 | var replaced = false;
|
6 | nodes.forEach(function (i) {
|
7 | if (i.type === 'nesting') {
|
8 | i.replaceWith(parent.clone());
|
9 | replaced = true;
|
10 | } else if (i.nodes) {
|
11 | if (replace(i.nodes, parent)) {
|
12 | replaced = true;
|
13 | }
|
14 | }
|
15 | });
|
16 | return replaced;
|
17 | }
|
18 |
|
19 | function selectors(parent, child) {
|
20 | var result = [];
|
21 | parent.selectors.forEach(function (i) {
|
22 | var parentNode = parser().process(i).res.first;
|
23 |
|
24 | child.selectors.forEach(function (j) {
|
25 | var node = parser().process(j).res.first;
|
26 | var replaced = replace(node.nodes, parentNode);
|
27 | if (!replaced) {
|
28 | node.prepend(parser.combinator({ value: ' ' }));
|
29 | node.prepend(parentNode.clone());
|
30 | }
|
31 | result.push(node.toString());
|
32 | });
|
33 | });
|
34 | return result;
|
35 | }
|
36 |
|
37 | function pickComment(comment, after) {
|
38 | if ( comment && comment.type === 'comment' ) {
|
39 | after.after(comment);
|
40 | return comment;
|
41 | } else {
|
42 | return after;
|
43 | }
|
44 | }
|
45 |
|
46 | function atruleChilds(rule, atrule) {
|
47 | var children = [];
|
48 | atrule.each(function (child) {
|
49 | if ( child.type === 'comment' ) {
|
50 | children.push( child );
|
51 | } if ( child.type === 'decl' ) {
|
52 | children.push( child );
|
53 | } else if ( child.type === 'rule' ) {
|
54 | child.selectors = selectors(rule, child);
|
55 | } else if ( child.type === 'atrule' ) {
|
56 | atruleChilds(rule, child);
|
57 | }
|
58 | });
|
59 | if ( children.length ) {
|
60 | var clone = rule.clone({ nodes: [] });
|
61 | for ( var i = 0; i < children.length; i++ ) {
|
62 | clone.append(children[i]);
|
63 | }
|
64 | atrule.prepend(clone);
|
65 | }
|
66 | }
|
67 |
|
68 | function processRule(rule, bubble, preserveEmpty) {
|
69 | var unwrapped = false;
|
70 | var after = rule;
|
71 | rule.each(function (child) {
|
72 | if ( child.type === 'rule' ) {
|
73 | unwrapped = true;
|
74 | child.selectors = selectors(rule, child);
|
75 | after = pickComment(child.prev(), after);
|
76 | after.after(child);
|
77 | after = child;
|
78 | } else if ( child.type === 'atrule' ) {
|
79 | if ( bubble.indexOf(child.name) !== -1 ) {
|
80 | unwrapped = true;
|
81 | atruleChilds(rule, child);
|
82 | after = pickComment(child.prev(), after);
|
83 | after.after(child);
|
84 | after = child;
|
85 | }
|
86 | }
|
87 | });
|
88 | if ( unwrapped && preserveEmpty !== true ) {
|
89 | rule.raws.semicolon = true;
|
90 | if ( rule.nodes.length === 0 ) rule.remove();
|
91 | }
|
92 | }
|
93 |
|
94 | module.exports = postcss.plugin('postcss-nested', function (opts) {
|
95 | var bubble = ['media', 'supports', 'document'];
|
96 | if ( opts && opts.bubble ) {
|
97 | bubble = bubble.concat(opts.bubble.map(function (i) {
|
98 | return i.replace(/^@/, '');
|
99 | }));
|
100 | }
|
101 | var preserveEmpty = opts ? opts.preserveEmpty : false;
|
102 |
|
103 | var process = function (node) {
|
104 | node.each(function (child) {
|
105 | if ( child.type === 'rule' ) {
|
106 | processRule(child, bubble, preserveEmpty);
|
107 | } else if ( child.type === 'atrule' ) {
|
108 | process(child);
|
109 | }
|
110 | });
|
111 | };
|
112 | return process;
|
113 | });
|