1 | import postcss from 'postcss';
|
---|
2 | import valueParser from 'postcss-values-parser';
|
---|
3 |
|
---|
4 | // return whether a node is a valid comma
|
---|
5 | var getComma = (node => Object(node).type === 'comma');
|
---|
6 |
|
---|
7 | const imageSetFunctionMatchRegExp = /^(-webkit-)?image-set$/i; // return a valid image
|
---|
8 |
|
---|
9 | var getImage = (node => // <url> | <image()> | <cross-fade()> | <gradient>
|
---|
10 | // the image-set() function can not be nested inside of itself
|
---|
11 | Object(node).type === 'func' && /^(cross-fade|image|(repeating-)?(conic|linear|radial)-gradient|url)$/i.test(node.value) && !(node.parent.parent && node.parent.parent.type === 'func' && imageSetFunctionMatchRegExp.test(node.parent.parent.value)) ? String(node) : Object(node).type === 'string' ? node.value : false);
|
---|
12 |
|
---|
13 | const dpiRatios = {
|
---|
14 | dpcm: 2.54,
|
---|
15 | dpi: 1,
|
---|
16 | dppx: 96,
|
---|
17 | x: 96
|
---|
18 | }; // return a valid @media rule
|
---|
19 |
|
---|
20 | var getMedia = ((node, mediasByDpr) => {
|
---|
21 | if (Object(node).type === 'number' && node.unit in dpiRatios) {
|
---|
22 | // calculate min-device-pixel-ratio and min-resolution
|
---|
23 | const dpi = Number(node.value) * dpiRatios[node.unit.toLowerCase()];
|
---|
24 | const dpr = Math.floor(dpi / dpiRatios.x * 100) / 100;
|
---|
25 |
|
---|
26 | if (dpi in mediasByDpr) {
|
---|
27 | return false;
|
---|
28 | } else {
|
---|
29 | const media = mediasByDpr[dpi] = postcss.atRule({
|
---|
30 | name: 'media',
|
---|
31 | params: `(-webkit-min-device-pixel-ratio: ${dpr}), (min-resolution: ${dpi}dpi)`
|
---|
32 | });
|
---|
33 | return media;
|
---|
34 | }
|
---|
35 | } else {
|
---|
36 | return false;
|
---|
37 | }
|
---|
38 | });
|
---|
39 |
|
---|
40 | var handleInvalidation = ((opts, message, word) => {
|
---|
41 | if (opts.oninvalid === 'warn') {
|
---|
42 | opts.decl.warn(opts.result, message, {
|
---|
43 | word: String(word)
|
---|
44 | });
|
---|
45 | } else if (opts.oninvalid === 'throw') {
|
---|
46 | throw opts.decl.error(message, {
|
---|
47 | word: String(word)
|
---|
48 | });
|
---|
49 | }
|
---|
50 | });
|
---|
51 |
|
---|
52 | var processImageSet = ((imageSetOptionNodes, decl, opts) => {
|
---|
53 | const parent = decl.parent;
|
---|
54 | const mediasByDpr = {};
|
---|
55 | let length = imageSetOptionNodes.length;
|
---|
56 | let index = -1;
|
---|
57 |
|
---|
58 | while (index < length) {
|
---|
59 | const _ref = [index < 0 ? true : getComma(imageSetOptionNodes[index]), getImage(imageSetOptionNodes[index + 1]), getMedia(imageSetOptionNodes[index + 2], mediasByDpr)],
|
---|
60 | comma = _ref[0],
|
---|
61 | value = _ref[1],
|
---|
62 | media = _ref[2]; // handle invalidations
|
---|
63 |
|
---|
64 | if (!comma) {
|
---|
65 | return handleInvalidation(opts, 'unexpected comma', imageSetOptionNodes[index]);
|
---|
66 | } else if (!value) {
|
---|
67 | return handleInvalidation(opts, 'unexpected image', imageSetOptionNodes[index + 1]);
|
---|
68 | } else if (!media) {
|
---|
69 | return handleInvalidation(opts, 'unexpected resolution', imageSetOptionNodes[index + 2]);
|
---|
70 | } // prepare @media { decl: <image> }
|
---|
71 |
|
---|
72 |
|
---|
73 | const parentClone = parent.clone().removeAll();
|
---|
74 | const declClone = decl.clone({
|
---|
75 | value
|
---|
76 | });
|
---|
77 | parentClone.append(declClone);
|
---|
78 | media.append(parentClone);
|
---|
79 | index += 3;
|
---|
80 | }
|
---|
81 |
|
---|
82 | const medias = Object.keys(mediasByDpr).sort((a, b) => a - b).map(params => mediasByDpr[params]); // conditionally prepend previous siblings
|
---|
83 |
|
---|
84 | if (medias.length) {
|
---|
85 | const firstDecl = medias[0].nodes[0].nodes[0];
|
---|
86 |
|
---|
87 | if (medias.length === 1) {
|
---|
88 | decl.value = firstDecl.value;
|
---|
89 | } else {
|
---|
90 | const siblings = parent.nodes;
|
---|
91 | const previousSiblings = siblings.slice(0, siblings.indexOf(decl)).concat(firstDecl);
|
---|
92 |
|
---|
93 | if (previousSiblings.length) {
|
---|
94 | const parentClone = parent.cloneBefore().removeAll();
|
---|
95 | parentClone.append(previousSiblings);
|
---|
96 | } // prepend any @media { decl: <image> } rules
|
---|
97 |
|
---|
98 |
|
---|
99 | parent.before(medias.slice(1)); // conditionally remove the current rule
|
---|
100 |
|
---|
101 | if (!opts.preserve) {
|
---|
102 | decl.remove(); // and then conditionally remove its parent
|
---|
103 |
|
---|
104 | if (!parent.nodes.length) {
|
---|
105 | parent.remove();
|
---|
106 | }
|
---|
107 | }
|
---|
108 | }
|
---|
109 | }
|
---|
110 | });
|
---|
111 |
|
---|
112 | const imageSetValueMatchRegExp = /(^|[^\w-])(-webkit-)?image-set\(/;
|
---|
113 | const imageSetFunctionMatchRegExp$1 = /^(-webkit-)?image-set$/i;
|
---|
114 | var index = postcss.plugin('postcss-image-set-function', opts => {
|
---|
115 | // prepare options
|
---|
116 | const preserve = 'preserve' in Object(opts) ? Boolean(opts.preserve) : true;
|
---|
117 | const oninvalid = 'oninvalid' in Object(opts) ? opts.oninvalid : 'ignore';
|
---|
118 | return (root, result) => {
|
---|
119 | // for every declaration
|
---|
120 | root.walkDecls(decl => {
|
---|
121 | const value = decl.value; // if a declaration likely uses an image-set() function
|
---|
122 |
|
---|
123 | if (imageSetValueMatchRegExp.test(value)) {
|
---|
124 | const valueAST = valueParser(value).parse(); // process every image-set() function
|
---|
125 |
|
---|
126 | valueAST.walkType('func', node => {
|
---|
127 | if (imageSetFunctionMatchRegExp$1.test(node.value)) {
|
---|
128 | processImageSet(node.nodes.slice(1, -1), decl, {
|
---|
129 | decl,
|
---|
130 | oninvalid,
|
---|
131 | preserve,
|
---|
132 | result
|
---|
133 | });
|
---|
134 | }
|
---|
135 | });
|
---|
136 | }
|
---|
137 | });
|
---|
138 | };
|
---|
139 | });
|
---|
140 |
|
---|
141 | export default index;
|
---|
142 | //# sourceMappingURL=index.es.mjs.map
|
---|