@tbela99/css-parser 0.3.0 → 0.4.1
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/{LICENSE → LICENSE.md} +1 -1
- package/README.md +191 -80
- package/dist/config.json.js +20 -1
- package/dist/index-umd-web.js +3210 -1352
- package/dist/index.cjs +3211 -1353
- package/dist/index.d.ts +910 -0
- package/dist/lib/ast/expand.js +1 -1
- package/dist/lib/ast/features/calc.js +1 -2
- package/dist/lib/ast/features/inlinecssvariables.js +1 -1
- package/dist/lib/ast/features/shorthand.js +1 -1
- package/dist/lib/ast/math/expression.js +38 -3
- package/dist/lib/ast/math/math.js +2 -2
- package/dist/lib/ast/minify.js +0 -1
- package/dist/lib/ast/types.js +1 -0
- package/dist/lib/ast/utils/minifyfeature.js +4 -3
- package/dist/lib/parser/declaration/list.js +1 -1
- package/dist/lib/parser/declaration/map.js +129 -26
- package/dist/lib/parser/declaration/set.js +1 -1
- package/dist/lib/parser/parse.js +325 -303
- package/dist/lib/parser/tokenize.js +220 -223
- package/dist/lib/parser/utils/declaration.js +1 -1
- package/dist/lib/parser/utils/syntax.js +159 -23
- package/dist/lib/parser/utils/type.js +2 -2
- package/dist/lib/renderer/color/a98rgb.js +64 -0
- package/dist/lib/renderer/color/color.js +521 -0
- package/dist/lib/renderer/color/colormix.js +337 -0
- package/dist/lib/renderer/color/hex.js +92 -0
- package/dist/lib/renderer/color/hsl.js +118 -0
- package/dist/lib/renderer/color/hsv.js +20 -0
- package/dist/lib/renderer/color/hwb.js +101 -0
- package/dist/lib/renderer/color/lab.js +136 -0
- package/dist/lib/renderer/color/lch.js +79 -0
- package/dist/lib/renderer/color/oklab.js +121 -0
- package/dist/lib/renderer/color/oklch.js +65 -0
- package/dist/lib/renderer/color/p3.js +57 -0
- package/dist/lib/renderer/color/prophotorgb.js +56 -0
- package/dist/lib/renderer/color/rec2020.js +70 -0
- package/dist/lib/renderer/color/relativecolor.js +152 -0
- package/dist/lib/renderer/color/rgb.js +44 -0
- package/dist/lib/renderer/color/srgb.js +261 -0
- package/dist/lib/renderer/color/utils/components.js +20 -0
- package/dist/lib/renderer/color/utils/constants.js +191 -0
- package/dist/lib/renderer/color/utils/matrix.js +35 -0
- package/dist/lib/renderer/color/xyz.js +64 -0
- package/dist/lib/renderer/color/xyzd50.js +33 -0
- package/dist/lib/renderer/render.js +61 -32
- package/dist/node/index.js +1 -1
- package/dist/node/load.js +1 -1
- package/dist/web/index.js +1 -1
- package/package.json +15 -14
- package/dist/lib/ast/features/utils/math.js +0 -95
- package/dist/lib/iterable/set.js +0 -48
- package/dist/lib/iterable/weakmap.js +0 -53
- package/dist/lib/renderer/utils/calccolor.js +0 -238
- package/dist/lib/renderer/utils/color.js +0 -371
- package/dist/lib/renderer/utils/hex.js +0 -124
- package/dist/lib/renderer/utils/hsl.js +0 -49
- package/dist/lib/renderer/utils/hsv.js +0 -15
- package/dist/lib/renderer/utils/hwb.js +0 -50
- package/dist/lib/renderer/utils/rgb.js +0 -66
package/dist/lib/parser/parse.js
CHANGED
|
@@ -5,7 +5,7 @@ import { walkValues, walk } from '../ast/walk.js';
|
|
|
5
5
|
import { expand } from '../ast/expand.js';
|
|
6
6
|
import { parseDeclaration } from './utils/declaration.js';
|
|
7
7
|
import { renderToken } from '../renderer/render.js';
|
|
8
|
-
import { COLORS_NAMES } from '../renderer/utils/
|
|
8
|
+
import { COLORS_NAMES } from '../renderer/color/utils/constants.js';
|
|
9
9
|
import { tokenize } from './tokenize.js';
|
|
10
10
|
|
|
11
11
|
const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
|
|
@@ -50,13 +50,19 @@ async function doParse(iterator, options = {}) {
|
|
|
50
50
|
const errors = [];
|
|
51
51
|
const src = options.src;
|
|
52
52
|
const stack = [];
|
|
53
|
+
const stats = {
|
|
54
|
+
bytesIn: 0,
|
|
55
|
+
importedBytesIn: 0,
|
|
56
|
+
parse: `0ms`,
|
|
57
|
+
minify: `0ms`,
|
|
58
|
+
total: `0ms`
|
|
59
|
+
};
|
|
53
60
|
let ast = {
|
|
54
61
|
typ: EnumToken.StyleSheetNodeType,
|
|
55
62
|
chi: []
|
|
56
63
|
};
|
|
57
64
|
let tokens = [];
|
|
58
65
|
let map = new Map;
|
|
59
|
-
let bytesIn = 0;
|
|
60
66
|
let context = ast;
|
|
61
67
|
if (options.sourcemap) {
|
|
62
68
|
ast.loc = {
|
|
@@ -68,307 +74,21 @@ async function doParse(iterator, options = {}) {
|
|
|
68
74
|
src: ''
|
|
69
75
|
};
|
|
70
76
|
}
|
|
71
|
-
async function parseNode(results) {
|
|
72
|
-
let tokens = results.map(mapToken);
|
|
73
|
-
let i;
|
|
74
|
-
let loc;
|
|
75
|
-
for (i = 0; i < tokens.length; i++) {
|
|
76
|
-
if (tokens[i].typ == EnumToken.CommentTokenType || tokens[i].typ == EnumToken.CDOCOMMTokenType) {
|
|
77
|
-
const position = map.get(tokens[i]);
|
|
78
|
-
if (tokens[i].typ == EnumToken.CDOCOMMTokenType && context.typ != EnumToken.StyleSheetNodeType) {
|
|
79
|
-
errors.push({
|
|
80
|
-
action: 'drop',
|
|
81
|
-
message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`,
|
|
82
|
-
location: { src, ...position }
|
|
83
|
-
});
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
loc = {
|
|
87
|
-
sta: position,
|
|
88
|
-
src
|
|
89
|
-
};
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
context.chi.push(tokens[i]);
|
|
92
|
-
if (options.sourcemap) {
|
|
93
|
-
tokens[i].loc = loc;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else if (tokens[i].typ != EnumToken.WhitespaceTokenType) {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
tokens = tokens.slice(i);
|
|
101
|
-
if (tokens.length == 0) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
let delim = tokens.at(-1);
|
|
105
|
-
if (delim.typ == EnumToken.SemiColonTokenType || delim.typ == EnumToken.BlockStartTokenType || delim.typ == EnumToken.BlockEndTokenType) {
|
|
106
|
-
tokens.pop();
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
delim = { typ: EnumToken.SemiColonTokenType };
|
|
110
|
-
}
|
|
111
|
-
// @ts-ignore
|
|
112
|
-
while ([EnumToken.WhitespaceTokenType, EnumToken.BadStringTokenType, EnumToken.BadCommentTokenType].includes(tokens.at(-1)?.typ)) {
|
|
113
|
-
tokens.pop();
|
|
114
|
-
}
|
|
115
|
-
if (tokens.length == 0) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
if (tokens[0]?.typ == EnumToken.AtRuleTokenType) {
|
|
119
|
-
const atRule = tokens.shift();
|
|
120
|
-
const position = map.get(atRule);
|
|
121
|
-
if (atRule.val == 'charset') {
|
|
122
|
-
if (position.ind > 0) {
|
|
123
|
-
errors.push({
|
|
124
|
-
action: 'drop',
|
|
125
|
-
message: 'doParse: invalid @charset',
|
|
126
|
-
location: { src, ...position }
|
|
127
|
-
});
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
if (options.removeCharset) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// @ts-ignore
|
|
135
|
-
while ([EnumToken.WhitespaceTokenType].includes(tokens[0]?.typ)) {
|
|
136
|
-
tokens.shift();
|
|
137
|
-
}
|
|
138
|
-
if (atRule.val == 'import') {
|
|
139
|
-
// only @charset and @layer are accepted before @import
|
|
140
|
-
if (context.chi.length > 0) {
|
|
141
|
-
let i = context.chi.length;
|
|
142
|
-
while (i--) {
|
|
143
|
-
const type = context.chi[i].typ;
|
|
144
|
-
if (type == EnumToken.CommentNodeType) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (type != EnumToken.AtRuleNodeType) {
|
|
148
|
-
errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
const name = context.chi[i].nam;
|
|
152
|
-
if (name != 'charset' && name != 'import' && name != 'layer') {
|
|
153
|
-
errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// @ts-ignore
|
|
160
|
-
if (tokens[0]?.typ != EnumToken.StringTokenType && tokens[0]?.typ != EnumToken.UrlFunctionTokenType) {
|
|
161
|
-
errors.push({
|
|
162
|
-
action: 'drop',
|
|
163
|
-
message: 'doParse: invalid @import',
|
|
164
|
-
location: { src, ...position }
|
|
165
|
-
});
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
// @ts-ignore
|
|
169
|
-
if (tokens[0].typ == EnumToken.UrlFunctionTokenType && tokens[1]?.typ != EnumToken.UrlTokenTokenType && tokens[1]?.typ != EnumToken.StringTokenType) {
|
|
170
|
-
errors.push({
|
|
171
|
-
action: 'drop',
|
|
172
|
-
message: 'doParse: invalid @import',
|
|
173
|
-
location: { src, ...position }
|
|
174
|
-
});
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (atRule.val == 'import') {
|
|
179
|
-
// @ts-ignore
|
|
180
|
-
if (tokens[0].typ == EnumToken.UrlFunctionTokenType && tokens[1].typ == EnumToken.UrlTokenTokenType) {
|
|
181
|
-
tokens.shift();
|
|
182
|
-
// @ts-ignore
|
|
183
|
-
tokens[0].typ = EnumToken.StringTokenType;
|
|
184
|
-
// @ts-ignore
|
|
185
|
-
tokens[0].val = `"${tokens[0].val}"`;
|
|
186
|
-
}
|
|
187
|
-
// @ts-ignore
|
|
188
|
-
if (tokens[0].typ == EnumToken.StringTokenType) {
|
|
189
|
-
if (options.resolveImport) {
|
|
190
|
-
const url = tokens[0].val.slice(1, -1);
|
|
191
|
-
try {
|
|
192
|
-
// @ts-ignore
|
|
193
|
-
const root = await options.load(url, options.src).then((src) => {
|
|
194
|
-
return doParse(src, Object.assign({}, options, {
|
|
195
|
-
minify: false,
|
|
196
|
-
// @ts-ignore
|
|
197
|
-
src: options.resolve(url, options.src).absolute
|
|
198
|
-
}));
|
|
199
|
-
});
|
|
200
|
-
bytesIn += root.stats.bytesIn;
|
|
201
|
-
if (root.ast.chi.length > 0) {
|
|
202
|
-
// @todo - filter charset, layer and scope
|
|
203
|
-
context.chi.push(...root.ast.chi);
|
|
204
|
-
}
|
|
205
|
-
if (root.errors.length > 0) {
|
|
206
|
-
errors.push(...root.errors);
|
|
207
|
-
}
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
// @ts-ignore
|
|
212
|
-
errors.push({ action: 'ignore', message: 'doParse: ' + error.message, error });
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
// https://www.w3.org/TR/css-nesting-1/#conditionals
|
|
218
|
-
// allowed nesting at-rules
|
|
219
|
-
// there must be a top level rule in the stack
|
|
220
|
-
const raw = parseTokens(tokens, { minify: options.minify }).reduce((acc, curr) => {
|
|
221
|
-
acc.push(renderToken(curr, { removeComments: true }));
|
|
222
|
-
return acc;
|
|
223
|
-
}, []);
|
|
224
|
-
const node = {
|
|
225
|
-
typ: EnumToken.AtRuleNodeType,
|
|
226
|
-
nam: renderToken(atRule, { removeComments: true }),
|
|
227
|
-
val: raw.join('')
|
|
228
|
-
};
|
|
229
|
-
Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
|
|
230
|
-
if (delim.typ == EnumToken.BlockStartTokenType) {
|
|
231
|
-
node.chi = [];
|
|
232
|
-
}
|
|
233
|
-
loc = {
|
|
234
|
-
sta: position,
|
|
235
|
-
src
|
|
236
|
-
};
|
|
237
|
-
if (options.sourcemap) {
|
|
238
|
-
node.loc = loc;
|
|
239
|
-
}
|
|
240
|
-
// @ts-ignore
|
|
241
|
-
context.chi.push(node);
|
|
242
|
-
return delim.typ == EnumToken.BlockStartTokenType ? node : null;
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
// rule
|
|
246
|
-
if (delim.typ == EnumToken.BlockStartTokenType) {
|
|
247
|
-
const position = map.get(tokens[0]);
|
|
248
|
-
const uniq = new Map;
|
|
249
|
-
parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
|
|
250
|
-
if (curr.typ == EnumToken.WhitespaceTokenType) {
|
|
251
|
-
if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
|
|
252
|
-
trimWhiteSpace.includes(array[index + 1]?.typ) ||
|
|
253
|
-
combinators.includes(array[index - 1]?.val) ||
|
|
254
|
-
combinators.includes(array[index + 1]?.val)) {
|
|
255
|
-
return acc;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
let t = renderToken(curr, { minify: false });
|
|
259
|
-
if (t == ',') {
|
|
260
|
-
acc.push([]);
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
acc[acc.length - 1].push(t);
|
|
264
|
-
}
|
|
265
|
-
return acc;
|
|
266
|
-
}, [[]]).reduce((acc, curr) => {
|
|
267
|
-
acc.set(curr.join(''), curr);
|
|
268
|
-
return acc;
|
|
269
|
-
}, uniq);
|
|
270
|
-
const node = {
|
|
271
|
-
typ: EnumToken.RuleNodeType,
|
|
272
|
-
// @ts-ignore
|
|
273
|
-
sel: [...uniq.keys()].join(','),
|
|
274
|
-
chi: []
|
|
275
|
-
};
|
|
276
|
-
let raw = [...uniq.values()];
|
|
277
|
-
Object.defineProperty(node, 'raw', {
|
|
278
|
-
enumerable: false,
|
|
279
|
-
configurable: true,
|
|
280
|
-
writable: true,
|
|
281
|
-
value: raw
|
|
282
|
-
});
|
|
283
|
-
loc = {
|
|
284
|
-
sta: position,
|
|
285
|
-
src
|
|
286
|
-
};
|
|
287
|
-
if (options.sourcemap) {
|
|
288
|
-
node.loc = loc;
|
|
289
|
-
}
|
|
290
|
-
// @ts-ignore
|
|
291
|
-
context.chi.push(node);
|
|
292
|
-
return node;
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
// declaration
|
|
296
|
-
// @ts-ignore
|
|
297
|
-
let name = null;
|
|
298
|
-
// @ts-ignore
|
|
299
|
-
let value = null;
|
|
300
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
301
|
-
if (tokens[i].typ == EnumToken.CommentTokenType) {
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
if (tokens[i].typ == EnumToken.ColonTokenType) {
|
|
305
|
-
name = tokens.slice(0, i);
|
|
306
|
-
value = parseTokens(tokens.slice(i + 1), {
|
|
307
|
-
parseColor: options.parseColor,
|
|
308
|
-
src: options.src,
|
|
309
|
-
resolveUrls: options.resolveUrls,
|
|
310
|
-
resolve: options.resolve,
|
|
311
|
-
cwd: options.cwd
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (name == null) {
|
|
316
|
-
name = tokens;
|
|
317
|
-
}
|
|
318
|
-
const position = map.get(name[0]);
|
|
319
|
-
if (name.length > 0) {
|
|
320
|
-
for (let i = 1; i < name.length; i++) {
|
|
321
|
-
if (name[i].typ != EnumToken.WhitespaceTokenType && name[i].typ != EnumToken.CommentTokenType) {
|
|
322
|
-
errors.push({
|
|
323
|
-
action: 'drop',
|
|
324
|
-
message: 'doParse: invalid declaration',
|
|
325
|
-
location: { src, ...position }
|
|
326
|
-
});
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
if (value == null || value.length == 0) {
|
|
332
|
-
errors.push({
|
|
333
|
-
action: 'drop',
|
|
334
|
-
message: 'doParse: invalid declaration',
|
|
335
|
-
location: { src, ...position }
|
|
336
|
-
});
|
|
337
|
-
return null;
|
|
338
|
-
}
|
|
339
|
-
const node = {
|
|
340
|
-
typ: EnumToken.DeclarationNodeType,
|
|
341
|
-
// @ts-ignore
|
|
342
|
-
nam: renderToken(name.shift(), { removeComments: true }),
|
|
343
|
-
// @ts-ignore
|
|
344
|
-
val: value
|
|
345
|
-
};
|
|
346
|
-
const result = parseDeclaration(node, errors, src, position);
|
|
347
|
-
if (result != null) {
|
|
348
|
-
// @ts-ignore
|
|
349
|
-
context.chi.push(node);
|
|
350
|
-
}
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
function mapToken(token) {
|
|
356
|
-
const node = getTokenType(token.token, token.hint);
|
|
357
|
-
map.set(node, token.position);
|
|
358
|
-
return node;
|
|
359
|
-
}
|
|
360
77
|
const iter = tokenize(iterator);
|
|
361
78
|
let item;
|
|
362
79
|
while (item = iter.next().value) {
|
|
363
|
-
bytesIn = item.bytesIn;
|
|
80
|
+
stats.bytesIn = item.bytesIn;
|
|
81
|
+
//
|
|
364
82
|
// doParse error
|
|
365
83
|
if (item.hint != null && BadTokensTypes.includes(item.hint)) {
|
|
366
84
|
// bad token
|
|
367
85
|
continue;
|
|
368
86
|
}
|
|
369
|
-
|
|
87
|
+
if (item.hint != EnumToken.EOFTokenType) {
|
|
88
|
+
tokens.push(item);
|
|
89
|
+
}
|
|
370
90
|
if (item.token == ';' || item.token == '{') {
|
|
371
|
-
let node = await parseNode(tokens);
|
|
91
|
+
let node = await parseNode(tokens, context, stats, options, errors, src, map);
|
|
372
92
|
if (node != null) {
|
|
373
93
|
stack.push(node);
|
|
374
94
|
// @ts-ignore
|
|
@@ -395,7 +115,7 @@ async function doParse(iterator, options = {}) {
|
|
|
395
115
|
map = new Map;
|
|
396
116
|
}
|
|
397
117
|
else if (item.token == '}') {
|
|
398
|
-
await parseNode(tokens);
|
|
118
|
+
await parseNode(tokens, context, stats, options, errors, src, map);
|
|
399
119
|
const previousNode = stack.pop();
|
|
400
120
|
// @ts-ignore
|
|
401
121
|
context = stack[stack.length - 1] || ast;
|
|
@@ -408,12 +128,12 @@ async function doParse(iterator, options = {}) {
|
|
|
408
128
|
}
|
|
409
129
|
}
|
|
410
130
|
if (tokens.length > 0) {
|
|
411
|
-
await parseNode(tokens);
|
|
131
|
+
await parseNode(tokens, context, stats, options, errors, src, map);
|
|
412
132
|
}
|
|
413
133
|
while (stack.length > 0 && context != ast) {
|
|
414
134
|
const previousNode = stack.pop();
|
|
415
135
|
// @ts-ignore
|
|
416
|
-
context = stack[stack.length - 1]
|
|
136
|
+
context = stack[stack.length - 1] ?? ast;
|
|
417
137
|
// @ts-ignore
|
|
418
138
|
if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
|
|
419
139
|
context.chi.pop();
|
|
@@ -431,7 +151,7 @@ async function doParse(iterator, options = {}) {
|
|
|
431
151
|
// @ts-ignore
|
|
432
152
|
(typeof options.visitor.Declaration == 'function' || options.visitor.Declaration?.[result.node.nam] != null)) {
|
|
433
153
|
const callable = typeof options.visitor.Declaration == 'function' ? options.visitor.Declaration : options.visitor.Declaration[result.node.nam];
|
|
434
|
-
const results = callable(result.node);
|
|
154
|
+
const results = await callable(result.node);
|
|
435
155
|
if (results == null || (Array.isArray(results) && results.length == 0)) {
|
|
436
156
|
continue;
|
|
437
157
|
}
|
|
@@ -439,7 +159,7 @@ async function doParse(iterator, options = {}) {
|
|
|
439
159
|
result.parent.chi.splice(result.parent.chi.indexOf(result.node), 1, ...(Array.isArray(results) ? results : [results]));
|
|
440
160
|
}
|
|
441
161
|
else if (options.visitor.Rule != null && result.node.typ == EnumToken.RuleNodeType) {
|
|
442
|
-
const results = options.visitor.Rule(result.node);
|
|
162
|
+
const results = await options.visitor.Rule(result.node);
|
|
443
163
|
if (results == null || (Array.isArray(results) && results.length == 0)) {
|
|
444
164
|
continue;
|
|
445
165
|
}
|
|
@@ -451,7 +171,7 @@ async function doParse(iterator, options = {}) {
|
|
|
451
171
|
// @ts-ignore
|
|
452
172
|
(typeof options.visitor.AtRule == 'function' || options.visitor.AtRule?.[result.node.nam] != null)) {
|
|
453
173
|
const callable = typeof options.visitor.AtRule == 'function' ? options.visitor.AtRule : options.visitor.AtRule[result.node.nam];
|
|
454
|
-
const results = callable(result.node);
|
|
174
|
+
const results = await callable(result.node);
|
|
455
175
|
if (results == null || (Array.isArray(results) && results.length == 0)) {
|
|
456
176
|
continue;
|
|
457
177
|
}
|
|
@@ -469,11 +189,12 @@ async function doParse(iterator, options = {}) {
|
|
|
469
189
|
if (options.signal != null) {
|
|
470
190
|
options.signal.removeEventListener('abort', reject);
|
|
471
191
|
}
|
|
192
|
+
stats.bytesIn += stats.importedBytesIn;
|
|
472
193
|
resolve({
|
|
473
194
|
ast,
|
|
474
195
|
errors,
|
|
475
196
|
stats: {
|
|
476
|
-
|
|
197
|
+
...stats,
|
|
477
198
|
parse: `${(endParseTime - startTime).toFixed(2)}ms`,
|
|
478
199
|
minify: `${(endTime - endParseTime).toFixed(2)}ms`,
|
|
479
200
|
total: `${(endTime - startTime).toFixed(2)}ms`
|
|
@@ -481,6 +202,295 @@ async function doParse(iterator, options = {}) {
|
|
|
481
202
|
});
|
|
482
203
|
});
|
|
483
204
|
}
|
|
205
|
+
async function parseNode(results, context, stats, options, errors, src, map) {
|
|
206
|
+
let tokens = results.map((t) => mapToken(t, map));
|
|
207
|
+
let i;
|
|
208
|
+
let loc;
|
|
209
|
+
for (i = 0; i < tokens.length; i++) {
|
|
210
|
+
if (tokens[i].typ == EnumToken.CommentTokenType || tokens[i].typ == EnumToken.CDOCOMMTokenType) {
|
|
211
|
+
const position = map.get(tokens[i]);
|
|
212
|
+
if (tokens[i].typ == EnumToken.CDOCOMMTokenType && context.typ != EnumToken.StyleSheetNodeType) {
|
|
213
|
+
errors.push({
|
|
214
|
+
action: 'drop',
|
|
215
|
+
message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`,
|
|
216
|
+
location: { src, ...position }
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
loc = {
|
|
221
|
+
sta: position,
|
|
222
|
+
src
|
|
223
|
+
};
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
context.chi.push(tokens[i]);
|
|
226
|
+
if (options.sourcemap) {
|
|
227
|
+
tokens[i].loc = loc;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (tokens[i].typ != EnumToken.WhitespaceTokenType) {
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
tokens = tokens.slice(i);
|
|
235
|
+
if (tokens.length == 0) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
let delim = tokens.at(-1);
|
|
239
|
+
if (delim.typ == EnumToken.SemiColonTokenType || delim.typ == EnumToken.BlockStartTokenType || delim.typ == EnumToken.BlockEndTokenType) {
|
|
240
|
+
tokens.pop();
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
delim = { typ: EnumToken.SemiColonTokenType };
|
|
244
|
+
}
|
|
245
|
+
// @ts-ignore
|
|
246
|
+
while ([EnumToken.WhitespaceTokenType, EnumToken.BadStringTokenType, EnumToken.BadCommentTokenType].includes(tokens.at(-1)?.typ)) {
|
|
247
|
+
tokens.pop();
|
|
248
|
+
}
|
|
249
|
+
if (tokens.length == 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
if (tokens[0]?.typ == EnumToken.AtRuleTokenType) {
|
|
253
|
+
const atRule = tokens.shift();
|
|
254
|
+
const position = map.get(atRule);
|
|
255
|
+
if (atRule.val == 'charset') {
|
|
256
|
+
if (position.ind > 0) {
|
|
257
|
+
errors.push({
|
|
258
|
+
action: 'drop',
|
|
259
|
+
message: 'doParse: invalid @charset',
|
|
260
|
+
location: { src, ...position }
|
|
261
|
+
});
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
if (options.removeCharset) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// @ts-ignore
|
|
269
|
+
while ([EnumToken.WhitespaceTokenType].includes(tokens[0]?.typ)) {
|
|
270
|
+
tokens.shift();
|
|
271
|
+
}
|
|
272
|
+
if (atRule.val == 'import') {
|
|
273
|
+
// only @charset and @layer are accepted before @import
|
|
274
|
+
if (context.chi.length > 0) {
|
|
275
|
+
let i = context.chi.length;
|
|
276
|
+
while (i--) {
|
|
277
|
+
const type = context.chi[i].typ;
|
|
278
|
+
if (type == EnumToken.CommentNodeType) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (type != EnumToken.AtRuleNodeType) {
|
|
282
|
+
errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const name = context.chi[i].nam;
|
|
286
|
+
if (name != 'charset' && name != 'import' && name != 'layer') {
|
|
287
|
+
errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// @ts-ignore
|
|
294
|
+
if (tokens[0]?.typ != EnumToken.StringTokenType && tokens[0]?.typ != EnumToken.UrlFunctionTokenType) {
|
|
295
|
+
errors.push({
|
|
296
|
+
action: 'drop',
|
|
297
|
+
message: 'doParse: invalid @import',
|
|
298
|
+
location: { src, ...position }
|
|
299
|
+
});
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
// @ts-ignore
|
|
303
|
+
if (tokens[0].typ == EnumToken.UrlFunctionTokenType && tokens[1]?.typ != EnumToken.UrlTokenTokenType && tokens[1]?.typ != EnumToken.StringTokenType) {
|
|
304
|
+
errors.push({
|
|
305
|
+
action: 'drop',
|
|
306
|
+
message: 'doParse: invalid @import',
|
|
307
|
+
location: { src, ...position }
|
|
308
|
+
});
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (atRule.val == 'import') {
|
|
313
|
+
// @ts-ignore
|
|
314
|
+
if (tokens[0].typ == EnumToken.UrlFunctionTokenType && tokens[1].typ == EnumToken.UrlTokenTokenType) {
|
|
315
|
+
tokens.shift();
|
|
316
|
+
// @ts-ignore
|
|
317
|
+
tokens[0].typ = EnumToken.StringTokenType;
|
|
318
|
+
// @ts-ignore
|
|
319
|
+
tokens[0].val = `"${tokens[0].val}"`;
|
|
320
|
+
}
|
|
321
|
+
// @ts-ignore
|
|
322
|
+
if (tokens[0].typ == EnumToken.StringTokenType) {
|
|
323
|
+
if (options.resolveImport) {
|
|
324
|
+
const url = tokens[0].val.slice(1, -1);
|
|
325
|
+
try {
|
|
326
|
+
// @ts-ignore
|
|
327
|
+
const root = await options.load(url, options.src).then((src) => {
|
|
328
|
+
return doParse(src, Object.assign({}, options, {
|
|
329
|
+
minify: false,
|
|
330
|
+
// @ts-ignore
|
|
331
|
+
src: options.resolve(url, options.src).absolute
|
|
332
|
+
}));
|
|
333
|
+
});
|
|
334
|
+
stats.importedBytesIn += root.stats.bytesIn;
|
|
335
|
+
if (root.ast.chi.length > 0) {
|
|
336
|
+
// @todo - filter charset, layer and scope
|
|
337
|
+
context.chi.push(...root.ast.chi);
|
|
338
|
+
}
|
|
339
|
+
if (root.errors.length > 0) {
|
|
340
|
+
errors.push(...root.errors);
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
// @ts-ignore
|
|
346
|
+
errors.push({ action: 'ignore', message: 'doParse: ' + error.message, error });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// https://www.w3.org/TR/css-nesting-1/#conditionals
|
|
352
|
+
// allowed nesting at-rules
|
|
353
|
+
// there must be a top level rule in the stack
|
|
354
|
+
const raw = parseTokens(tokens, { minify: options.minify }).reduce((acc, curr) => {
|
|
355
|
+
acc.push(renderToken(curr, { removeComments: true }));
|
|
356
|
+
return acc;
|
|
357
|
+
}, []);
|
|
358
|
+
const node = {
|
|
359
|
+
typ: EnumToken.AtRuleNodeType,
|
|
360
|
+
nam: renderToken(atRule, { removeComments: true }),
|
|
361
|
+
val: raw.join('')
|
|
362
|
+
};
|
|
363
|
+
Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
|
|
364
|
+
if (delim.typ == EnumToken.BlockStartTokenType) {
|
|
365
|
+
node.chi = [];
|
|
366
|
+
}
|
|
367
|
+
loc = {
|
|
368
|
+
sta: position,
|
|
369
|
+
src
|
|
370
|
+
};
|
|
371
|
+
if (options.sourcemap) {
|
|
372
|
+
node.loc = loc;
|
|
373
|
+
}
|
|
374
|
+
// @ts-ignore
|
|
375
|
+
context.chi.push(node);
|
|
376
|
+
return delim.typ == EnumToken.BlockStartTokenType ? node : null;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
// rule
|
|
380
|
+
if (delim.typ == EnumToken.BlockStartTokenType) {
|
|
381
|
+
const position = map.get(tokens[0]);
|
|
382
|
+
const uniq = new Map;
|
|
383
|
+
parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
|
|
384
|
+
if (curr.typ == EnumToken.WhitespaceTokenType) {
|
|
385
|
+
if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
|
|
386
|
+
trimWhiteSpace.includes(array[index + 1]?.typ) ||
|
|
387
|
+
combinators.includes(array[index - 1]?.val) ||
|
|
388
|
+
combinators.includes(array[index + 1]?.val)) {
|
|
389
|
+
return acc;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
let t = renderToken(curr, { minify: false });
|
|
393
|
+
if (t == ',') {
|
|
394
|
+
acc.push([]);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
acc[acc.length - 1].push(t);
|
|
398
|
+
}
|
|
399
|
+
return acc;
|
|
400
|
+
}, [[]]).reduce((acc, curr) => {
|
|
401
|
+
acc.set(curr.join(''), curr);
|
|
402
|
+
return acc;
|
|
403
|
+
}, uniq);
|
|
404
|
+
const node = {
|
|
405
|
+
typ: EnumToken.RuleNodeType,
|
|
406
|
+
// @ts-ignore
|
|
407
|
+
sel: [...uniq.keys()].join(','),
|
|
408
|
+
chi: []
|
|
409
|
+
};
|
|
410
|
+
let raw = [...uniq.values()];
|
|
411
|
+
Object.defineProperty(node, 'raw', {
|
|
412
|
+
enumerable: false,
|
|
413
|
+
configurable: true,
|
|
414
|
+
writable: true,
|
|
415
|
+
value: raw
|
|
416
|
+
});
|
|
417
|
+
loc = {
|
|
418
|
+
sta: position,
|
|
419
|
+
src
|
|
420
|
+
};
|
|
421
|
+
if (options.sourcemap) {
|
|
422
|
+
node.loc = loc;
|
|
423
|
+
}
|
|
424
|
+
// @ts-ignore
|
|
425
|
+
context.chi.push(node);
|
|
426
|
+
return node;
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
// declaration
|
|
430
|
+
// @ts-ignore
|
|
431
|
+
let name = null;
|
|
432
|
+
// @ts-ignore
|
|
433
|
+
let value = null;
|
|
434
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
435
|
+
if (tokens[i].typ == EnumToken.CommentTokenType) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (tokens[i].typ == EnumToken.ColonTokenType) {
|
|
439
|
+
name = tokens.slice(0, i);
|
|
440
|
+
value = parseTokens(tokens.slice(i + 1), {
|
|
441
|
+
parseColor: options.parseColor,
|
|
442
|
+
src: options.src,
|
|
443
|
+
resolveUrls: options.resolveUrls,
|
|
444
|
+
resolve: options.resolve,
|
|
445
|
+
cwd: options.cwd
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (name == null) {
|
|
450
|
+
name = tokens;
|
|
451
|
+
}
|
|
452
|
+
const position = map.get(name[0]);
|
|
453
|
+
if (name.length > 0) {
|
|
454
|
+
for (let i = 1; i < name.length; i++) {
|
|
455
|
+
if (name[i].typ != EnumToken.WhitespaceTokenType && name[i].typ != EnumToken.CommentTokenType) {
|
|
456
|
+
errors.push({
|
|
457
|
+
action: 'drop',
|
|
458
|
+
message: 'doParse: invalid declaration',
|
|
459
|
+
location: { src, ...position }
|
|
460
|
+
});
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (value == null || value.length == 0) {
|
|
466
|
+
errors.push({
|
|
467
|
+
action: 'drop',
|
|
468
|
+
message: 'doParse: invalid declaration',
|
|
469
|
+
location: { src, ...position }
|
|
470
|
+
});
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
const node = {
|
|
474
|
+
typ: EnumToken.DeclarationNodeType,
|
|
475
|
+
// @ts-ignore
|
|
476
|
+
nam: renderToken(name.shift(), { removeComments: true }),
|
|
477
|
+
// @ts-ignore
|
|
478
|
+
val: value
|
|
479
|
+
};
|
|
480
|
+
const result = parseDeclaration(node, errors, src, position);
|
|
481
|
+
if (result != null) {
|
|
482
|
+
// @ts-ignore
|
|
483
|
+
context.chi.push(node);
|
|
484
|
+
}
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function mapToken(token, map) {
|
|
490
|
+
const node = getTokenType(token.token, token.hint);
|
|
491
|
+
map.set(node, token.position);
|
|
492
|
+
return node;
|
|
493
|
+
}
|
|
484
494
|
function parseString(src, options = { location: false }) {
|
|
485
495
|
return parseTokens([...tokenize(src)].map(t => {
|
|
486
496
|
const token = getTokenType(t.token, t.hint);
|
|
@@ -893,9 +903,21 @@ function parseTokens(tokens, options = {}) {
|
|
|
893
903
|
t.typ = EnumToken.ColorTokenType;
|
|
894
904
|
// @ts-ignore
|
|
895
905
|
t.kin = t.val;
|
|
896
|
-
if (t.chi[0].typ == EnumToken.IdenTokenType
|
|
906
|
+
if (t.chi[0].typ == EnumToken.IdenTokenType) {
|
|
907
|
+
if (t.chi[0].val == 'from') {
|
|
908
|
+
// @ts-ignore
|
|
909
|
+
t.cal = 'rel';
|
|
910
|
+
}
|
|
897
911
|
// @ts-ignore
|
|
898
|
-
t.
|
|
912
|
+
else if (t.val == 'color-mix' && t.chi[0].val == 'in') {
|
|
913
|
+
// @ts-ignore
|
|
914
|
+
t.cal = 'mix';
|
|
915
|
+
}
|
|
916
|
+
else if (t.val == 'color') {
|
|
917
|
+
// @ts-ignore
|
|
918
|
+
t.cal = 'col';
|
|
919
|
+
// t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
|
|
920
|
+
}
|
|
899
921
|
}
|
|
900
922
|
t.chi = t.chi.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommaTokenType, EnumToken.CommentTokenType].includes(t.typ));
|
|
901
923
|
continue;
|