-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
111 lines (94 loc) · 3.39 KB
/
index.js
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
'use strict';
var postcss = require('postcss');
module.exports = postcss.plugin('postcss-simple-extend', function simpleExtend() {
return function(css, result) {
var definingAtRules = ['define-placeholder', 'define-extend', 'simple-extend-define'];
var extendingAtRules = ['extend', 'simple-extend-addto'];
var availablePlaceholders = {};
css.walkAtRules(function(atRule) {
if (definingAtRules.indexOf(atRule.name) !== -1) {
processDefinition(atRule);
} else if (extendingAtRules.indexOf(atRule.name) !== -1) {
processExtension(atRule);
}
});
// Remove placeholders that were never used
for (var p in availablePlaceholders) {
if (availablePlaceholders.hasOwnProperty(p) && !availablePlaceholders[p].selector) {
availablePlaceholders[p].remove();
}
}
function processDefinition(atRule) {
if (isBadDefinitionLocation(atRule)) {
atRule.remove();
return;
}
var definition = postcss.rule();
// Manually copy styling properties (semicolon, whitespace)
// to newly created and cloned nodes,
// cf. https://github.com/postcss/postcss/issues/85
definition.raws.semicolon = atRule.raws.semicolon;
atRule.nodes.forEach(function(node) {
if (isBadDefinitionNode(node)) return;
var clone = node.clone();
clone.raws.before = node.raws.before;
clone.after = node.after;
clone.raws.between = node.raws.between;
definition.append(clone);
});
atRule.parent.insertBefore(atRule, definition);
availablePlaceholders[atRule.params] = definition;
atRule.remove();
}
function processExtension(atRule) {
if (isBadExtensionLocation(atRule)) {
atRule.remove();
return;
}
var targetExt = getExtendable(atRule.params, atRule);
var selectorToAdd = atRule.parent.selector;
if (targetExt) {
targetExt.selector = (targetExt.selector)
? targetExt.selector + ',\n' + selectorToAdd
: selectorToAdd;
}
atRule.remove();
}
function isBadDefinitionNode(node) {
if (node.type === 'rule' || node.type === 'atrule') {
result.warn('Defining at-rules cannot contain statements', { node: node });
return true;
}
}
function getExtendable(extIdent, node) {
var targetExt = availablePlaceholders[extIdent];
if (!targetExt) {
result.warn('`' + extIdent + '`, has not (yet) been defined, so cannot be extended', { node: node });
}
return targetExt;
}
function isBadDefinitionLocation(atRule) {
if (atRule.parent.type !== 'root') {
result.warn('Defining at-rules must occur at the root level', { node: atRule });
return true;
}
}
function isBadExtensionLocation(atRule) {
if (atRule.parent.type === 'root') {
result.warn('Extending at-rules cannot occur at the root level', { node: atRule });
return true;
}
return hasMediaAncestor(atRule);
function hasMediaAncestor(node) {
var parent = node.parent;
if (parent.type === 'atrule' && parent.name === 'media') {
result.warn('Extending at-rules cannot occur inside a @media statement', { node: node });
return true;
}
if (parent.type !== 'root') {
return hasMediaAncestor(parent);
}
}
}
};
});