@gjsify/lightningcss-wasm 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +37 -0
- package/src/async.mjs +74 -0
- package/src/browserslistToTargets.js +48 -0
- package/src/composeVisitors.js +450 -0
- package/src/flags.js +28 -0
- package/src/index.mjs +110 -0
- package/src/napi-wasm.mjs +1436 -0
- package/wasm/lightningcss_node.wasm +0 -0
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/lightningcss-wasm",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Phase D-2 POC: GJS-compatible loader for the lightningcss WASM bundle. Re-exports the `lightningcss-wasm` npm package's transform/bundle/bundleAsync API on top of SpiderMonkey 140's synchronous `new WebAssembly.Module/Instance` constructors. Reads the .wasm via @gjsify/fs (or Node's native fs), resolves randomness via globalThis.crypto. No WASI, no SharedArrayBuffer — single-shot CSS pipeline. Intended as the WASM-track counterpart to `@gjsify/lightningcss-native`; the better of the two wins the slot in `@gjsify/rolldown-plugin-gjsify`'s cssAsStringPlugin (see Phase D-2 decision matrix).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.mjs",
|
|
7
|
+
"module": "src/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"default": "./src/index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./wasm/lightningcss_node.wasm": "./wasm/lightningcss_node.wasm"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"wasm"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clear": "rm -rf lib tsconfig.tsbuildinfo || exit 0",
|
|
20
|
+
"check": "echo 'no typecheck — vendored JS only' && exit 0"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"gjs",
|
|
24
|
+
"lightningcss",
|
|
25
|
+
"wasm",
|
|
26
|
+
"css",
|
|
27
|
+
"minify",
|
|
28
|
+
"browserslist",
|
|
29
|
+
"napi-wasm"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@gjsify/fs": "^0.4.0",
|
|
33
|
+
"@gjsify/webcrypto": "^0.4.0"
|
|
34
|
+
},
|
|
35
|
+
"author": "Pascal Garber <pascal@artandcode.studio>",
|
|
36
|
+
"license": "MIT"
|
|
37
|
+
}
|
package/src/async.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
let cur_await_promise_sync;
|
|
2
|
+
export function await_promise_sync(promise_addr, result_addr, error_addr) {
|
|
3
|
+
cur_await_promise_sync(promise_addr, result_addr, error_addr);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const State = {
|
|
7
|
+
None: 0,
|
|
8
|
+
Unwinding: 1,
|
|
9
|
+
Rewinding: 2
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// This uses Binaryen's Asyncify transform to suspend native code execution while a promise is resolving.
|
|
13
|
+
// That allows synchronous Rust code to call async JavaScript functions without multi-threading.
|
|
14
|
+
// When Rust wants to await a promise, it calls await_promise_sync, which saves the stack state and unwinds.
|
|
15
|
+
// That causes the bundle function to return early. If a promise has been queued, we can then await it
|
|
16
|
+
// and "rewind" the function back to where it was before by calling it again. This time the result of
|
|
17
|
+
// the promise can be returned, and the function can continue where it left off.
|
|
18
|
+
// See the docs in https://github.com/WebAssembly/binaryen/blob/main/src/passes/Asyncify.cpp
|
|
19
|
+
// The code here is also partially based on https://github.com/GoogleChromeLabs/asyncify
|
|
20
|
+
export function createBundleAsync(env) {
|
|
21
|
+
let {instance, exports} = env;
|
|
22
|
+
let {asyncify_get_state, asyncify_start_unwind, asyncify_stop_unwind, asyncify_start_rewind, asyncify_stop_rewind} = instance.exports;
|
|
23
|
+
|
|
24
|
+
// allocate __asyncify_data
|
|
25
|
+
// Stack data goes right after the initial descriptor.
|
|
26
|
+
let DATA_ADDR = instance.exports.napi_wasm_malloc(8 + 4096);
|
|
27
|
+
let DATA_START = DATA_ADDR + 8;
|
|
28
|
+
let DATA_END = DATA_ADDR + 8 + 4096;
|
|
29
|
+
new Int32Array(env.memory.buffer, DATA_ADDR).set([DATA_START, DATA_END]);
|
|
30
|
+
|
|
31
|
+
function assertNoneState() {
|
|
32
|
+
if (asyncify_get_state() !== State.None) {
|
|
33
|
+
throw new Error(`Invalid async state ${asyncify_get_state()}, expected 0.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let promise, result, error;
|
|
38
|
+
cur_await_promise_sync = (promise_addr, result_addr, error_addr) => {
|
|
39
|
+
let state = asyncify_get_state();
|
|
40
|
+
if (state === State.Rewinding) {
|
|
41
|
+
asyncify_stop_rewind();
|
|
42
|
+
if (result != null) {
|
|
43
|
+
env.createValue(result, result_addr);
|
|
44
|
+
}
|
|
45
|
+
if (error != null) {
|
|
46
|
+
env.createValue(error, error_addr);
|
|
47
|
+
}
|
|
48
|
+
promise = result = error = null;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
assertNoneState();
|
|
52
|
+
promise = env.get(promise_addr);
|
|
53
|
+
asyncify_start_unwind(DATA_ADDR);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return async function bundleAsync(options) {
|
|
57
|
+
assertNoneState();
|
|
58
|
+
let res = exports.bundle(options);
|
|
59
|
+
while (asyncify_get_state() === State.Unwinding) {
|
|
60
|
+
asyncify_stop_unwind();
|
|
61
|
+
try {
|
|
62
|
+
result = await promise;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
error = err;
|
|
65
|
+
}
|
|
66
|
+
assertNoneState();
|
|
67
|
+
asyncify_start_rewind(DATA_ADDR);
|
|
68
|
+
res = exports.bundle(options);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
assertNoneState();
|
|
72
|
+
return res;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const BROWSER_MAPPING = {
|
|
2
|
+
and_chr: 'chrome',
|
|
3
|
+
and_ff: 'firefox',
|
|
4
|
+
ie_mob: 'ie',
|
|
5
|
+
op_mob: 'opera',
|
|
6
|
+
and_qq: null,
|
|
7
|
+
and_uc: null,
|
|
8
|
+
baidu: null,
|
|
9
|
+
bb: null,
|
|
10
|
+
kaios: null,
|
|
11
|
+
op_mini: null,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function browserslistToTargets(browserslist) {
|
|
15
|
+
let targets = {};
|
|
16
|
+
for (let browser of browserslist) {
|
|
17
|
+
let [name, v] = browser.split(' ');
|
|
18
|
+
if (BROWSER_MAPPING[name] === null) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let version = parseVersion(v);
|
|
23
|
+
if (version == null) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (targets[name] == null || version < targets[name]) {
|
|
28
|
+
targets[name] = version;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return targets;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseVersion(version) {
|
|
36
|
+
let [major, minor = 0, patch = 0] = version
|
|
37
|
+
.split('-')[0]
|
|
38
|
+
.split('.')
|
|
39
|
+
.map(v => parseInt(v, 10));
|
|
40
|
+
|
|
41
|
+
if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (major << 16) | (minor << 8) | patch;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export {browserslistToTargets};
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @typedef {import('./index').Visitor} Visitor */
|
|
3
|
+
/** @typedef {import('./index').VisitorFunction} VisitorFunction */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Composes multiple visitor objects into a single one.
|
|
7
|
+
* @param {(Visitor | VisitorFunction)[]} visitors
|
|
8
|
+
* @return {Visitor | VisitorFunction}
|
|
9
|
+
*/
|
|
10
|
+
function composeVisitors(visitors) {
|
|
11
|
+
if (visitors.length === 1) {
|
|
12
|
+
return visitors[0];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (visitors.some(v => typeof v === 'function')) {
|
|
16
|
+
return (opts) => {
|
|
17
|
+
let v = visitors.map(v => typeof v === 'function' ? v(opts) : v);
|
|
18
|
+
return composeVisitors(v);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @type Visitor */
|
|
23
|
+
let res = {};
|
|
24
|
+
composeSimpleVisitors(res, visitors, 'StyleSheet');
|
|
25
|
+
composeSimpleVisitors(res, visitors, 'StyleSheetExit');
|
|
26
|
+
composeObjectVisitors(res, visitors, 'Rule', ruleVisitor, wrapCustomAndUnknownAtRule);
|
|
27
|
+
composeObjectVisitors(res, visitors, 'RuleExit', ruleVisitor, wrapCustomAndUnknownAtRule);
|
|
28
|
+
composeObjectVisitors(res, visitors, 'Declaration', declarationVisitor, wrapCustomProperty);
|
|
29
|
+
composeObjectVisitors(res, visitors, 'DeclarationExit', declarationVisitor, wrapCustomProperty);
|
|
30
|
+
composeSimpleVisitors(res, visitors, 'Url');
|
|
31
|
+
composeSimpleVisitors(res, visitors, 'Color');
|
|
32
|
+
composeSimpleVisitors(res, visitors, 'Image');
|
|
33
|
+
composeSimpleVisitors(res, visitors, 'ImageExit');
|
|
34
|
+
composeSimpleVisitors(res, visitors, 'Length');
|
|
35
|
+
composeSimpleVisitors(res, visitors, 'Angle');
|
|
36
|
+
composeSimpleVisitors(res, visitors, 'Ratio');
|
|
37
|
+
composeSimpleVisitors(res, visitors, 'Resolution');
|
|
38
|
+
composeSimpleVisitors(res, visitors, 'Time');
|
|
39
|
+
composeSimpleVisitors(res, visitors, 'CustomIdent');
|
|
40
|
+
composeSimpleVisitors(res, visitors, 'DashedIdent');
|
|
41
|
+
composeArrayFunctions(res, visitors, 'MediaQuery');
|
|
42
|
+
composeArrayFunctions(res, visitors, 'MediaQueryExit');
|
|
43
|
+
composeSimpleVisitors(res, visitors, 'SupportsCondition');
|
|
44
|
+
composeSimpleVisitors(res, visitors, 'SupportsConditionExit');
|
|
45
|
+
composeArrayFunctions(res, visitors, 'Selector');
|
|
46
|
+
composeTokenVisitors(res, visitors, 'Token', 'token', false);
|
|
47
|
+
composeTokenVisitors(res, visitors, 'Function', 'function', false);
|
|
48
|
+
composeTokenVisitors(res, visitors, 'FunctionExit', 'function', true);
|
|
49
|
+
composeTokenVisitors(res, visitors, 'Variable', 'var', false);
|
|
50
|
+
composeTokenVisitors(res, visitors, 'VariableExit', 'var', true);
|
|
51
|
+
composeTokenVisitors(res, visitors, 'EnvironmentVariable', 'env', false);
|
|
52
|
+
composeTokenVisitors(res, visitors, 'EnvironmentVariableExit', 'env', true);
|
|
53
|
+
return res;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { composeVisitors };
|
|
57
|
+
|
|
58
|
+
function wrapCustomAndUnknownAtRule(k, f) {
|
|
59
|
+
if (k === 'unknown') {
|
|
60
|
+
return (value => f({ type: 'unknown', value }));
|
|
61
|
+
}
|
|
62
|
+
if (k === 'custom') {
|
|
63
|
+
return (value => f({ type: 'custom', value }));
|
|
64
|
+
}
|
|
65
|
+
return f;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function wrapCustomProperty(k, f) {
|
|
69
|
+
return k === 'custom' ? (value => f({ property: 'custom', value })) : f;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {import('./index').Visitor['Rule']} f
|
|
74
|
+
* @param {import('./ast').Rule} item
|
|
75
|
+
*/
|
|
76
|
+
function ruleVisitor(f, item) {
|
|
77
|
+
if (typeof f === 'object') {
|
|
78
|
+
if (item.type === 'unknown') {
|
|
79
|
+
let v = f.unknown;
|
|
80
|
+
if (typeof v === 'object') {
|
|
81
|
+
v = v[item.value.name];
|
|
82
|
+
}
|
|
83
|
+
return v?.(item.value);
|
|
84
|
+
}
|
|
85
|
+
if (item.type === 'custom') {
|
|
86
|
+
let v = f.custom;
|
|
87
|
+
if (typeof v === 'object') {
|
|
88
|
+
v = v[item.value.name];
|
|
89
|
+
}
|
|
90
|
+
return v?.(item.value);
|
|
91
|
+
}
|
|
92
|
+
return f[item.type]?.(item);
|
|
93
|
+
}
|
|
94
|
+
return f?.(item);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {import('./index').Visitor['Declaration']} f
|
|
99
|
+
* @param {import('./ast').Declaration} item
|
|
100
|
+
*/
|
|
101
|
+
function declarationVisitor(f, item) {
|
|
102
|
+
if (typeof f === 'object') {
|
|
103
|
+
/** @type {string} */
|
|
104
|
+
let name = item.property;
|
|
105
|
+
if (item.property === 'unparsed') {
|
|
106
|
+
name = item.value.propertyId.property;
|
|
107
|
+
} else if (item.property === 'custom') {
|
|
108
|
+
let v = f.custom;
|
|
109
|
+
if (typeof v === 'object') {
|
|
110
|
+
v = v[item.value.name];
|
|
111
|
+
}
|
|
112
|
+
return v?.(item.value);
|
|
113
|
+
}
|
|
114
|
+
return f[name]?.(item);
|
|
115
|
+
}
|
|
116
|
+
return f?.(item);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
* @param {Visitor[]} visitors
|
|
122
|
+
* @param {string} key
|
|
123
|
+
* @returns {[any[], boolean, Set<string>]}
|
|
124
|
+
*/
|
|
125
|
+
function extractObjectsOrFunctions(visitors, key) {
|
|
126
|
+
let values = [];
|
|
127
|
+
let hasFunction = false;
|
|
128
|
+
let allKeys = new Set();
|
|
129
|
+
for (let visitor of visitors) {
|
|
130
|
+
let v = visitor[key];
|
|
131
|
+
if (v) {
|
|
132
|
+
if (typeof v === 'function') {
|
|
133
|
+
hasFunction = true;
|
|
134
|
+
} else {
|
|
135
|
+
for (let key in v) {
|
|
136
|
+
allKeys.add(key);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
values.push(v);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return [values, hasFunction, allKeys];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @template {keyof Visitor} K
|
|
147
|
+
* @param {Visitor} res
|
|
148
|
+
* @param {Visitor[]} visitors
|
|
149
|
+
* @param {K} key
|
|
150
|
+
* @param {(visitor: Visitor[K], item: any) => any | any[] | void} apply
|
|
151
|
+
* @param {(k: string, f: any) => any} wrapKey
|
|
152
|
+
*/
|
|
153
|
+
function composeObjectVisitors(res, visitors, key, apply, wrapKey) {
|
|
154
|
+
let [values, hasFunction, allKeys] = extractObjectsOrFunctions(visitors, key);
|
|
155
|
+
if (values.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (values.length === 1) {
|
|
160
|
+
res[key] = values[0];
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let f = createArrayVisitor(visitors, (visitor, item) => apply(visitor[key], item));
|
|
165
|
+
if (hasFunction) {
|
|
166
|
+
res[key] = f;
|
|
167
|
+
} else {
|
|
168
|
+
/** @type {any} */
|
|
169
|
+
let v = {};
|
|
170
|
+
for (let k of allKeys) {
|
|
171
|
+
v[k] = wrapKey(k, f);
|
|
172
|
+
}
|
|
173
|
+
res[key] = v;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {Visitor} res
|
|
179
|
+
* @param {Visitor[]} visitors
|
|
180
|
+
* @param {string} key
|
|
181
|
+
* @param {import('./ast').TokenOrValue['type']} type
|
|
182
|
+
* @param {boolean} isExit
|
|
183
|
+
*/
|
|
184
|
+
function composeTokenVisitors(res, visitors, key, type, isExit) {
|
|
185
|
+
let [values, hasFunction, allKeys] = extractObjectsOrFunctions(visitors, key);
|
|
186
|
+
if (values.length === 0) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (values.length === 1) {
|
|
191
|
+
res[key] = values[0];
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let f = createTokenVisitor(visitors, type, isExit);
|
|
196
|
+
if (hasFunction) {
|
|
197
|
+
res[key] = f;
|
|
198
|
+
} else {
|
|
199
|
+
let v = {};
|
|
200
|
+
for (let key of allKeys) {
|
|
201
|
+
v[key] = f;
|
|
202
|
+
}
|
|
203
|
+
res[key] = v;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {Visitor[]} visitors
|
|
209
|
+
* @param {import('./ast').TokenOrValue['type']} type
|
|
210
|
+
*/
|
|
211
|
+
function createTokenVisitor(visitors, type, isExit) {
|
|
212
|
+
let v = createArrayVisitor(visitors, (visitor, /** @type {import('./ast').TokenOrValue} */ item) => {
|
|
213
|
+
let f;
|
|
214
|
+
switch (item.type) {
|
|
215
|
+
case 'token':
|
|
216
|
+
f = visitor.Token;
|
|
217
|
+
if (typeof f === 'object') {
|
|
218
|
+
f = f[item.value.type];
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
case 'function':
|
|
222
|
+
f = isExit ? visitor.FunctionExit : visitor.Function;
|
|
223
|
+
if (typeof f === 'object') {
|
|
224
|
+
f = f[item.value.name];
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
case 'var':
|
|
228
|
+
f = isExit ? visitor.VariableExit : visitor.Variable;
|
|
229
|
+
break;
|
|
230
|
+
case 'env':
|
|
231
|
+
f = isExit ? visitor.EnvironmentVariableExit : visitor.EnvironmentVariable;
|
|
232
|
+
if (typeof f === 'object') {
|
|
233
|
+
let name;
|
|
234
|
+
switch (item.value.name.type) {
|
|
235
|
+
case 'ua':
|
|
236
|
+
case 'unknown':
|
|
237
|
+
name = item.value.name.value;
|
|
238
|
+
break;
|
|
239
|
+
case 'custom':
|
|
240
|
+
name = item.value.name.ident;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
f = f[name];
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
case 'color':
|
|
247
|
+
f = visitor.Color;
|
|
248
|
+
break;
|
|
249
|
+
case 'url':
|
|
250
|
+
f = visitor.Url;
|
|
251
|
+
break;
|
|
252
|
+
case 'length':
|
|
253
|
+
f = visitor.Length;
|
|
254
|
+
break;
|
|
255
|
+
case 'angle':
|
|
256
|
+
f = visitor.Angle;
|
|
257
|
+
break;
|
|
258
|
+
case 'time':
|
|
259
|
+
f = visitor.Time;
|
|
260
|
+
break;
|
|
261
|
+
case 'resolution':
|
|
262
|
+
f = visitor.Resolution;
|
|
263
|
+
break;
|
|
264
|
+
case 'dashed-ident':
|
|
265
|
+
f = visitor.DashedIdent;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!f) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
let res = f(item.value);
|
|
274
|
+
switch (item.type) {
|
|
275
|
+
case 'color':
|
|
276
|
+
case 'url':
|
|
277
|
+
case 'length':
|
|
278
|
+
case 'angle':
|
|
279
|
+
case 'time':
|
|
280
|
+
case 'resolution':
|
|
281
|
+
case 'dashed-ident':
|
|
282
|
+
if (Array.isArray(res)) {
|
|
283
|
+
res = res.map(value => ({ type: item.type, value }))
|
|
284
|
+
} else if (res) {
|
|
285
|
+
res = { type: item.type, value: res };
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return res;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return value => v({ type, value });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @param {Visitor[]} visitors
|
|
298
|
+
* @param {string} key
|
|
299
|
+
*/
|
|
300
|
+
function extractFunctions(visitors, key) {
|
|
301
|
+
let functions = [];
|
|
302
|
+
for (let visitor of visitors) {
|
|
303
|
+
let f = visitor[key];
|
|
304
|
+
if (f) {
|
|
305
|
+
functions.push(f);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return functions;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @param {Visitor} res
|
|
313
|
+
* @param {Visitor[]} visitors
|
|
314
|
+
* @param {string} key
|
|
315
|
+
*/
|
|
316
|
+
function composeSimpleVisitors(res, visitors, key) {
|
|
317
|
+
let functions = extractFunctions(visitors, key);
|
|
318
|
+
if (functions.length === 0) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (functions.length === 1) {
|
|
323
|
+
res[key] = functions[0];
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
res[key] = arg => {
|
|
328
|
+
let mutated = false;
|
|
329
|
+
for (let f of functions) {
|
|
330
|
+
let res = f(arg);
|
|
331
|
+
if (res) {
|
|
332
|
+
arg = res;
|
|
333
|
+
mutated = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return mutated ? arg : undefined;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @param {Visitor} res
|
|
343
|
+
* @param {Visitor[]} visitors
|
|
344
|
+
* @param {string} key
|
|
345
|
+
*/
|
|
346
|
+
function composeArrayFunctions(res, visitors, key) {
|
|
347
|
+
let functions = extractFunctions(visitors, key);
|
|
348
|
+
if (functions.length === 0) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (functions.length === 1) {
|
|
353
|
+
res[key] = functions[0];
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
res[key] = createArrayVisitor(functions, (f, item) => f(item));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @template T
|
|
362
|
+
* @template V
|
|
363
|
+
* @param {T[]} visitors
|
|
364
|
+
* @param {(visitor: T, item: V) => V | V[] | void} apply
|
|
365
|
+
* @returns {(item: V) => V | V[] | void}
|
|
366
|
+
*/
|
|
367
|
+
function createArrayVisitor(visitors, apply) {
|
|
368
|
+
let seen = new Bitset(visitors.length);
|
|
369
|
+
return arg => {
|
|
370
|
+
let arr = [arg];
|
|
371
|
+
let mutated = false;
|
|
372
|
+
seen.clear();
|
|
373
|
+
for (let i = 0; i < arr.length; i++) {
|
|
374
|
+
// For each value, call all visitors. If a visitor returns a new value,
|
|
375
|
+
// we start over, but skip the visitor that generated the value or saw
|
|
376
|
+
// it before (to avoid cycles). This way, visitors can be composed in any order.
|
|
377
|
+
for (let v = 0; v < visitors.length && i < arr.length;) {
|
|
378
|
+
if (seen.get(v)) {
|
|
379
|
+
v++;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let item = arr[i];
|
|
384
|
+
let visitor = visitors[v];
|
|
385
|
+
let res = apply(visitor, item);
|
|
386
|
+
if (Array.isArray(res)) {
|
|
387
|
+
if (res.length === 0) {
|
|
388
|
+
arr.splice(i, 1);
|
|
389
|
+
} else if (res.length === 1) {
|
|
390
|
+
arr[i] = res[0];
|
|
391
|
+
} else {
|
|
392
|
+
arr.splice(i, 1, ...res);
|
|
393
|
+
}
|
|
394
|
+
mutated = true;
|
|
395
|
+
seen.set(v);
|
|
396
|
+
v = 0;
|
|
397
|
+
} else if (res) {
|
|
398
|
+
arr[i] = res;
|
|
399
|
+
mutated = true;
|
|
400
|
+
seen.set(v);
|
|
401
|
+
v = 0;
|
|
402
|
+
} else {
|
|
403
|
+
v++;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!mutated) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return arr.length === 1 ? arr[0] : arr;
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
class Bitset {
|
|
417
|
+
constructor(maxBits = 32) {
|
|
418
|
+
this.bits = 0;
|
|
419
|
+
this.more = maxBits > 32 ? new Uint32Array(Math.ceil((maxBits - 32) / 32)) : null;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** @param {number} bit */
|
|
423
|
+
get(bit) {
|
|
424
|
+
if (bit >= 32 && this.more) {
|
|
425
|
+
let i = Math.floor((bit - 32) / 32);
|
|
426
|
+
let b = bit % 32;
|
|
427
|
+
return Boolean(this.more[i] & (1 << b));
|
|
428
|
+
} else {
|
|
429
|
+
return Boolean(this.bits & (1 << bit));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** @param {number} bit */
|
|
434
|
+
set(bit) {
|
|
435
|
+
if (bit >= 32 && this.more) {
|
|
436
|
+
let i = Math.floor((bit - 32) / 32);
|
|
437
|
+
let b = bit % 32;
|
|
438
|
+
this.more[i] |= 1 << b;
|
|
439
|
+
} else {
|
|
440
|
+
this.bits |= 1 << bit;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
clear() {
|
|
445
|
+
this.bits = 0;
|
|
446
|
+
if (this.more) {
|
|
447
|
+
this.more.fill(0);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
package/src/flags.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// This file is autogenerated by build-prefixes.js. DO NOT EDIT!
|
|
2
|
+
|
|
3
|
+
export const Features = {
|
|
4
|
+
Nesting: 1,
|
|
5
|
+
NotSelectorList: 2,
|
|
6
|
+
DirSelector: 4,
|
|
7
|
+
LangSelectorList: 8,
|
|
8
|
+
IsSelector: 16,
|
|
9
|
+
TextDecorationThicknessPercent: 32,
|
|
10
|
+
MediaIntervalSyntax: 64,
|
|
11
|
+
MediaRangeSyntax: 128,
|
|
12
|
+
CustomMediaQueries: 256,
|
|
13
|
+
ClampFunction: 512,
|
|
14
|
+
ColorFunction: 1024,
|
|
15
|
+
OklabColors: 2048,
|
|
16
|
+
LabColors: 4096,
|
|
17
|
+
P3Colors: 8192,
|
|
18
|
+
HexAlphaColors: 16384,
|
|
19
|
+
SpaceSeparatedColorNotation: 32768,
|
|
20
|
+
FontFamilySystemUi: 65536,
|
|
21
|
+
DoublePositionGradients: 131072,
|
|
22
|
+
VendorPrefixes: 262144,
|
|
23
|
+
LogicalProperties: 524288,
|
|
24
|
+
LightDark: 1048576,
|
|
25
|
+
Selectors: 31,
|
|
26
|
+
MediaQueries: 448,
|
|
27
|
+
Colors: 1113088,
|
|
28
|
+
};
|