-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.js
More file actions
134 lines (114 loc) · 3.68 KB
/
index.js
File metadata and controls
134 lines (114 loc) · 3.68 KB
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const postcss = require('postcss');
const balancedMatch = require('balanced-match');
const PLUGIN_NAME = 'postcss-srcset';
/**
* Srcset transformer plugin
*
* Use in this form:
* .cls {
* background-image: srcset('./path/to/my/image.png', 300 500 1200);
* }
*
* The first argument is the path to your image, the second argument is a
* space-separated list of sizes to scale the image to.
*
* The function will be converted to a collection of media queries that provide
* a different URL at each size.
*
* OPTIONS:
* function - the name of the function to transform. Defaults to `srcset`
* transformUrl - the function to transform the image url for the given size
* defaults to (url, size) => `${url}?cssSize=${size}`
* can return a promise
*/
module.exports = postcss.plugin(
PLUGIN_NAME,
(options = {}) =>
function(css) {
// In case someone wants to use an async transform function
var promises = [];
// Set default options
var funcName = options.function || 'srcset';
var transformUrl =
options.transformUrl || ((url, size) => `${url}?size=${size}`);
// Look for declerations that use srcset
css.walkDecls((decl) => {
if (decl.value.indexOf(funcName) === -1) {
return;
}
// Balanced match in case there are any functions-in-functions
// (shouldn't be but doesn't hurt to check)
let match = balancedMatch(`${funcName}(`, ')', decl.value);
// Can't do layers yet, need to figure out how different size sets
// combine into each media query
if (match.post.indexOf(`${funcName}(`) > -1) {
throw decl.error(
`${PLUGIN_NAME} does not currently support more than one ` +
`${funcName} call per declaration`
);
}
let [url, sizes] = postcss.list.comma(match.body);
// Strip string delemiters from start/end
url = url.replace(/^['"]|['"]$/g, '');
// Sizes to integer and in reverse order so the smallest sizes
// goes last and thus gets the highest specificity
sizes = postcss.list
.space(sizes)
.map(n => parseInt(n, 10))
.sort((a, b) => b - a);
// Url back to value (including any values that became before or after)
let unparse = url => `${match.pre}url('${url}')${match.post}`;
promises.push(
...sizes.map(size =>
Promise.resolve(transformUrl(url, size)).then(url =>
// Construct the new rules for each size
constructRule(decl, unparse(url), size)
)
)
);
// The default value should be the image at its largest (original) size
decl.value = unparse(url);
});
return Promise.all(promises);
}
);
/**
* Make a media query at-rule for a given size
*/
function makeQuery(size) {
return postcss.atRule({
name: 'media',
params: `(max-width: ${size}px)`
});
}
/**
* Clone a rule and remove all of its declarations
*/
function cloneRule(decl) {
return decl.parent.clone({
nodes: []
});
}
/**
* clone a delcaration and assign it a new value
*/
function cloneDecl(decl, value) {
var d = decl.clone({});
d.value = value;
return d;
}
/**
* Construct a new media query with rule and declaration
* and attatch it to the root
*/
function constructRule(decl, value, size) {
var query = makeQuery(size);
var rule = cloneRule(decl);
var newDecl = cloneDecl(decl, value);
query.source = decl.source;
rule.append(newDecl);
query.append(rule);
decl.root().append(query);
}
module.exports.default = module.exports;
module.exports.loader = require.resolve('./resize-loader');