-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathindex.ts
118 lines (98 loc) · 3.03 KB
/
index.ts
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
import valueParser from 'postcss-value-parser';
import { processImageSet } from './lib/process-image-set';
import type { PluginCreator } from 'postcss';
import { handleInvalidation } from './lib/handle-invalidation';
import { hasFallback } from '@csstools/utilities';
const IMAGE_SET_VALUE_MATCH_REGEX = /(?:^|[^\w-])(?:-webkit-)?image-set\(/i;
const IMAGE_SET_FUNCTION_MATCH_REGEX = /^(?:-webkit-)?image-set$/i;
/** postcss-image-set-function plugin options */
export type pluginOptions = {
/** Preserve the original notation. default: true */
preserve?: boolean,
/**
* Determine how invalid usage of `image-set()` should be handled.
* By default, invalid usages of `image-set()` are ignored.
* They can be configured to emit a warning with `warn` or throw an exception with `throw`.
* default: 'ignore'
*/
onInvalid?: 'warn' | 'throw' | 'ignore' | false
};
const creator: PluginCreator<pluginOptions> = (opts?: pluginOptions) => {
// prepare options
const preserve = 'preserve' in Object(opts) ? Boolean(opts?.preserve) : true;
const oninvalid = 'onInvalid' in Object(opts) ? opts?.onInvalid : 'ignore';
if ('oninvalid' in Object(opts)) {
throw new Error('"oninvalid" was changed to "onInvalid" to match other plugins with similar options');
}
return {
postcssPlugin: 'postcss-image-set-function',
Declaration(decl, { result, postcss }): void {
const value = decl.value;
// if a declaration likely uses an image-set() function
if (!IMAGE_SET_VALUE_MATCH_REGEX.test(value)) {
return;
}
if (hasFallback(decl)) {
return;
}
let valueAST;
try {
valueAST = valueParser(value);
} catch {
decl.warn(
result,
`Failed to parse value '${value}' as an image-set function. Leaving the original value intact.`,
);
}
if (typeof valueAST === 'undefined') {
return;
}
// process every image-set() function
const imageSetFunctions: Array<{
imageSetFunction: valueParser.FunctionNode,
imageSetOptionNodes: Array<valueParser.Node>,
}> = [];
valueAST.walk((node) => {
if (node.type !== 'function') {
return;
}
if (!IMAGE_SET_FUNCTION_MATCH_REGEX.test(node.value)) {
return;
}
let foundNestedImageSet = false;
valueParser.walk(node.nodes, (child) => {
if (
child.type === 'function' &&
IMAGE_SET_FUNCTION_MATCH_REGEX.test(child.value)
) {
foundNestedImageSet = true;
}
});
if (foundNestedImageSet) {
handleInvalidation({
decl,
oninvalid,
result: result,
}, 'nested image-set functions are not allowed', valueParser.stringify(node));
return false;
}
const relevantNodes = node.nodes.filter((x) => {
return x.type !== 'comment' && x.type !== 'space';
});
imageSetFunctions.push({
imageSetFunction: node,
imageSetOptionNodes: relevantNodes,
});
});
processImageSet(imageSetFunctions, decl, {
decl,
oninvalid,
preserve,
result: result,
postcss: postcss,
});
},
};
};
creator.postcss = true;
export default creator;