-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
106 lines (87 loc) · 2.27 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
const t = require('tcomb');
const plugin = 'postcss-property-lookup';
const lookupPattern = /@\(?([a-z-]+)\)?\b/g;
const LogLevel = t.enums.of(['error', 'warn'], 'LogLevel');
const PluginOptions = t.struct(
{
logLevel: LogLevel,
},
'PluginOptions'
);
const defaultOptions = {
logLevel: 'warn',
};
function resolveLookup(rule, orig, prop, log, result) {
const resolvedValue = closest(rule, prop, log, result);
if (!resolvedValue) {
log(`Unable to find property ${orig} in ${rule.selector}`, rule, result);
}
return resolvedValue;
}
function eachDecl(container, callback) {
container.each((node) => {
if (node.type === 'decl') {
callback(node);
}
// Recurse through child declarations of a media rule
if (node.type === 'atrule') {
eachDecl(node, callback);
}
});
}
function closest(container, prop, log, result) {
if (!container) {
return '';
}
let resolvedValue;
eachDecl(container, (decl) => {
if (decl.prop === prop) {
resolvedValue = decl.value;
}
});
// Ignore a reference to itself
// e.g a {color: @color;}
if (resolvedValue && resolvedValue.replace('@', '') === prop) {
return '';
}
if (!resolvedValue) {
return closest(container.parent, prop, log, result);
}
if (resolvedValue.indexOf('@') === -1) {
return resolvedValue;
}
return resolvedValue.replace(
lookupPattern,
(orig, property) => resolveLookup(container, orig, property, log, result)
);
}
module.exports = function propertyLookup(options) {
const errorContext = {plugin};
options = new PluginOptions(Object.assign({}, defaultOptions, options));
const log = {
warn(message, rule, result) {
rule.warn(result, message);
},
error(message, rule) {
throw rule.error(message, errorContext);
},
}[options.logLevel];
if (!log) {
throw new Error(`Invalid logLevel: ${options.logLevel}`);
}
return {
postcssPlugin: plugin,
Rule(rule, {result}) {
eachDecl(rule, (decl) => {
if (decl.value.indexOf('@') === -1) {
return;
}
decl.value = decl.value.replace(
lookupPattern,
(orig, property) => resolveLookup(rule, orig, property, log, result)
);
});
},
};
};
module.exports.postcss = true;