-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
110 lines (92 loc) · 2.57 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
const postcss = require('postcss');
const escRgx = require('escape-string-regexp');
module.exports = postcss.plugin('postcss-variable-media', (options = {}) => {
options = Object.assign({
consolidate: true
}, options);
let breakpoints = options.breakpoints || {},
breakpointRegex = registerBreakpoints(breakpoints),
registry = {};
/**
* Create regex to identify breakpoints
*
* @param {object} breakpoints
* @returns {RegExp}
*/
function registerBreakpoints(breakpoints) {
let register = [];
Object.keys(breakpoints).forEach(name => {
register.push('(^' + escRgx(name) + '$)');
});
if (! register.length) {
return false;
}
return new RegExp(register.join('|'));
}
/**
* Turn breakpoint to media
*
* @param {postcss.Container} rule - AtRule
* @returns {*}
*/
function convertBreakpointToMedia(rule) {
rule.params = `(min-width: ${breakpoints[rule.name]}px)`;
rule.name = 'media';
rule.raws.afterName = ' ';
rule.raws.between = ' ';
return rule;
}
/**
* Add breakpoint to registry
*
* @param {object} rule - AtRule
*/
function addToRegistry(rule) {
let name = rule.name;
if (registry.hasOwnProperty(name)) {
// `each` allows for safe looping while modifying the
// array being looped over. `append` is removing the rule
// from the array being looped over, so it is necessary
// to use this method instead of forEach
rule.each(r => registry[name].append(r));
} else {
registry[name] = rule.clone();
}
}
return (root, result) => {
if (! breakpointRegex) {
return root.warn(result, 'No breakpoints registered.');
}
if (options.consolidate) {
root.walkAtRules(rule => {
if (breakpointRegex.test(rule.name)) {
addToRegistry(rule);
rule.remove();
}
});
Object.keys(registry).forEach(key => {
let rule = registry[key];
let before = rule.raws.before;
convertBreakpointToMedia(rule);
root.append(rule);
// This is a hack to ensure that more readable unminified format
// is retained for top level at-rules that are
// being appended. root.append was causing before: '\n'
// to be stripped out so that it was only before: ''
let appendedRule = root.nodes[root.nodes.length - 1];
if (appendedRule.prev()) {
appendedRule.raws.before = '\n';
}
});
// Reset registry after finishing each file/module
// Use mqpacker for consolidating across multiple stylesheets
registry = {};
} else {
root.walkAtRules(rule => {
if (breakpointRegex.test(rule.name)) {
convertBreakpointToMedia(rule);
}
});
}
};
});