@tbela99/css-parser 1.3.3 → 1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +64 -48
  3. package/dist/config.json.js +3 -0
  4. package/dist/index-umd-web.js +2266 -631
  5. package/dist/index.cjs +2271 -620
  6. package/dist/index.d.ts +522 -181
  7. package/dist/lib/ast/expand.js +5 -10
  8. package/dist/lib/ast/features/calc.js +3 -2
  9. package/dist/lib/ast/features/inlinecssvariables.js +5 -3
  10. package/dist/lib/ast/features/prefix.js +1 -1
  11. package/dist/lib/ast/features/shorthand.js +1 -0
  12. package/dist/lib/ast/features/transform.js +13 -19
  13. package/dist/lib/ast/features/type.js +1 -1
  14. package/dist/lib/ast/minify.js +6 -3
  15. package/dist/lib/ast/transform/compute.js +2 -4
  16. package/dist/lib/ast/transform/matrix.js +20 -20
  17. package/dist/lib/ast/transform/minify.js +105 -12
  18. package/dist/lib/ast/transform/rotate.js +11 -11
  19. package/dist/lib/ast/transform/scale.js +6 -6
  20. package/dist/lib/ast/transform/skew.js +4 -4
  21. package/dist/lib/ast/transform/translate.js +3 -3
  22. package/dist/lib/ast/transform/utils.js +30 -37
  23. package/dist/lib/ast/types.js +76 -5
  24. package/dist/lib/ast/walk.js +77 -58
  25. package/dist/lib/fs/resolve.js +69 -10
  26. package/dist/lib/parser/declaration/list.js +6 -1
  27. package/dist/lib/parser/parse.js +1169 -312
  28. package/dist/lib/parser/tokenize.js +33 -20
  29. package/dist/lib/parser/utils/declaration.js +54 -0
  30. package/dist/lib/parser/utils/hash.js +86 -0
  31. package/dist/lib/parser/utils/text.js +8 -0
  32. package/dist/lib/renderer/render.js +26 -7
  33. package/dist/lib/syntax/color/relativecolor.js +0 -3
  34. package/dist/lib/syntax/syntax.js +36 -18
  35. package/dist/lib/validation/at-rules/container.js +11 -0
  36. package/dist/lib/validation/at-rules/counter-style.js +11 -0
  37. package/dist/lib/validation/at-rules/font-feature-values.js +11 -0
  38. package/dist/lib/validation/at-rules/keyframes.js +11 -0
  39. package/dist/lib/validation/at-rules/layer.js +11 -0
  40. package/dist/lib/validation/at-rules/media.js +11 -0
  41. package/dist/lib/validation/at-rules/page-margin-box.js +11 -0
  42. package/dist/lib/validation/at-rules/page.js +11 -0
  43. package/dist/lib/validation/at-rules/supports.js +11 -0
  44. package/dist/lib/validation/at-rules/when.js +11 -0
  45. package/dist/lib/validation/config.js +0 -2
  46. package/dist/lib/validation/config.json.js +36 -4
  47. package/dist/lib/validation/parser/parse.js +53 -2
  48. package/dist/lib/validation/syntax.js +204 -36
  49. package/dist/lib/validation/syntaxes/compound-selector.js +1 -2
  50. package/dist/lib/validation/syntaxes/relative-selector-list.js +2 -5
  51. package/dist/node.js +60 -18
  52. package/dist/types.d.ts +17 -0
  53. package/dist/types.js +20 -0
  54. package/dist/web.js +43 -17
  55. package/package.json +20 -17
  56. package/dist/lib/validation/parser/types.js +0 -54
@@ -1,25 +1,26 @@
1
- import { isIdentStart, isIdent, isIdentColor, mathFuncs, isColor, parseColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHexColor, isHash, mediaTypes } from '../syntax/syntax.js';
2
- import { EnumToken, ColorType, ValidationLevel, SyntaxValidationResult } from '../ast/types.js';
3
- import { minify, definedPropertySettings, combinators } from '../ast/minify.js';
4
- import { walkValues, WalkerValueEvent, walk, WalkerOptionEnum } from '../ast/walk.js';
1
+ import { isIdentStart, isIdent, isIdentColor, mathFuncs, isColor, parseColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, parseDimension, isHexColor, isHash, mediaTypes } from '../syntax/syntax.js';
2
+ import { EnumToken, ColorType, ValidationLevel, ModuleCaseTransformEnum, ModuleScopeEnumOptions, SyntaxValidationResult } from '../ast/types.js';
3
+ import { definedPropertySettings, minify, combinators } from '../ast/minify.js';
4
+ import { walkValues, WalkerEvent, walk, WalkerOptionEnum } from '../ast/walk.js';
5
5
  import { expand } from '../ast/expand.js';
6
6
  import './utils/config.js';
7
7
  import { parseDeclarationNode } from './utils/declaration.js';
8
+ import { camelize, dasherize } from './utils/text.js';
8
9
  import { renderToken } from '../renderer/render.js';
9
10
  import '../renderer/sourcemap/lib/encode.js';
10
11
  import { funcLike, timingFunc, timelineFunc, COLORS_NAMES, systemColors, deprecatedSystemColors, colorsFunc } from '../syntax/color/utils/constants.js';
11
12
  import { buildExpression } from '../ast/math/expression.js';
12
13
  import { tokenize, tokenizeStream } from './tokenize.js';
13
14
  import '../validation/config.js';
14
- import '../validation/parser/types.js';
15
15
  import '../validation/parser/parse.js';
16
16
  import { validateSelector } from '../validation/selector.js';
17
17
  import { validateAtRule } from '../validation/atrule.js';
18
18
  import { splitTokenList } from '../validation/utils/list.js';
19
19
  import '../validation/syntaxes/complex-selector.js';
20
20
  import { validateKeyframeSelector } from '../validation/syntaxes/keyframe-selector.js';
21
- import { evaluateSyntax } from '../validation/syntax.js';
21
+ import { isNodeAllowedInContext, evaluateSyntax } from '../validation/syntax.js';
22
22
  import { validateAtRuleKeyframes } from '../validation/at-rules/keyframes.js';
23
+ import { hashAlgorithms, hash } from './utils/hash.js';
23
24
 
24
25
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
25
26
  const trimWhiteSpace = [EnumToken.CommentTokenType, EnumToken.GtTokenType, EnumToken.GteTokenType, EnumToken.LtTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType];
@@ -39,16 +40,20 @@ const enumTokenHints = new Set([
39
40
  function reject(reason) {
40
41
  throw new Error(reason ?? 'Parsing aborted');
41
42
  }
42
- function normalizeVisitorKeyName(keyName) {
43
- return keyName.replace(/-([a-z])/g, (all, one) => one.toUpperCase());
44
- }
43
+ /**
44
+ * replace token in its parent node
45
+ * @param parent
46
+ * @param value
47
+ * @param replacement
48
+ */
45
49
  function replaceToken(parent, value, replacement) {
46
- // @ts-ignore
47
- if ('parent' in value && value.parent != replacement.parent) {
48
- Object.defineProperty(replacement, 'parent', {
49
- ...definedPropertySettings,
50
- value: value.parent
51
- });
50
+ for (const node of (Array.isArray(replacement) ? replacement : [replacement])) {
51
+ if ('parent' in value && value.parent != node.parent) {
52
+ Object.defineProperty(node, 'parent', {
53
+ ...definedPropertySettings,
54
+ value: value.parent
55
+ });
56
+ }
52
57
  }
53
58
  if (parent.typ == EnumToken.BinaryExpressionTokenType) {
54
59
  if (parent.l == value) {
@@ -59,22 +64,138 @@ function replaceToken(parent, value, replacement) {
59
64
  }
60
65
  }
61
66
  else {
62
- // @ts-ignore
63
67
  const target = 'val' in parent && Array.isArray(parent.val) ? parent.val : parent.chi;
64
68
  // @ts-ignore
65
69
  const index = target.indexOf(value);
66
70
  if (index == -1) {
67
71
  return;
68
72
  }
69
- // @ts-ignore
70
73
  target.splice(index, 1, ...(Array.isArray(replacement) ? replacement : [replacement]));
71
74
  }
72
75
  }
76
+ /**
77
+ * transform case of key name
78
+ * @param key
79
+ * @param how
80
+ *
81
+ * @throws Error
82
+ * @private
83
+ */
84
+ function getKeyName(key, how) {
85
+ switch (how) {
86
+ case ModuleCaseTransformEnum.CamelCase:
87
+ case ModuleCaseTransformEnum.CamelCaseOnly:
88
+ return camelize(key);
89
+ case ModuleCaseTransformEnum.DashCase:
90
+ case ModuleCaseTransformEnum.DashCaseOnly:
91
+ return dasherize(key);
92
+ }
93
+ return key;
94
+ }
95
+ /**
96
+ * generate scoped name
97
+ * @param localName
98
+ * @param filePath
99
+ * @param pattern
100
+ * @param hashLength
101
+ *
102
+ * @throws Error
103
+ * @private
104
+ */
105
+ async function generateScopedName(localName, filePath, pattern, hashLength = 5) {
106
+ if (localName.startsWith('--')) {
107
+ localName = localName.slice(2);
108
+ }
109
+ const matches = /.*?(([^/]+)\/)?([^/\\]*?)(\.([^?/]+))?([?].*)?$/.exec(filePath);
110
+ const folder = matches?.[2]?.replace?.(/[^A-Za-z0-9_-]/g, "_") ?? '';
111
+ const fileBase = matches?.[3] ?? '';
112
+ const ext = matches?.[5] ?? '';
113
+ const path = filePath.replace(/[^A-Za-z0-9_-]/g, "_");
114
+ // sanitize localName for safe char set (replace spaces/illegal chars)
115
+ const safeLocal = localName.replace(/[^A-Za-z0-9_-]/g, "_");
116
+ const hashString = `${localName}::${filePath}`;
117
+ let result = '';
118
+ let inParens = 0;
119
+ let key = '';
120
+ let position = 0;
121
+ // Compose final scoped name. Ensure the entire class doesn't start with digit:
122
+ for (const char of pattern) {
123
+ position += char.length;
124
+ if (char == '[') {
125
+ inParens++;
126
+ if (inParens != 1) {
127
+ throw new Error(`Unexpected character: '${char} at position ${position - 1}' in pattern '${pattern}'`);
128
+ }
129
+ continue;
130
+ }
131
+ if (char == ']') {
132
+ inParens--;
133
+ if (inParens != 0) {
134
+ throw new Error(`Unexpected character: '${char}:${position - 1}'`);
135
+ }
136
+ let hashAlgo = null;
137
+ let length = null;
138
+ if (key.includes(':')) {
139
+ const parts = key.split(':');
140
+ if (parts.length == 2) {
141
+ // @ts-ignore
142
+ [key, length] = parts;
143
+ // @ts-ignore
144
+ if (key == 'hash' && hashAlgorithms.includes(length)) {
145
+ // @ts-ignore
146
+ hashAlgo = length;
147
+ length = null;
148
+ }
149
+ }
150
+ if (parts.length == 3) {
151
+ // @ts-ignore
152
+ [key, hashAlgo, length] = parts;
153
+ }
154
+ if (length != null && !Number.isInteger(+length)) {
155
+ throw new Error(`Unsupported hash length: '${length}'. expecting format [hash:length] or [hash:hash-algo:length]`);
156
+ }
157
+ }
158
+ switch (key) {
159
+ case 'hash':
160
+ result += await hash(hashString, length ?? hashLength, hashAlgo);
161
+ break;
162
+ case 'name':
163
+ result += length != null ? fileBase.slice(0, +length) : fileBase;
164
+ break;
165
+ case 'local':
166
+ result += length != null ? safeLocal.slice(0, +length) : localName;
167
+ break;
168
+ case 'ext':
169
+ result += length != null ? ext.slice(0, +length) : ext;
170
+ break;
171
+ case 'path':
172
+ result += length != null ? path.slice(0, +length) : path;
173
+ break;
174
+ case 'folder':
175
+ result += length != null ? folder.slice(0, +length) : folder;
176
+ break;
177
+ default:
178
+ throw new Error(`Unsupported key: '${key}'`);
179
+ }
180
+ key = '';
181
+ continue;
182
+ }
183
+ if (inParens > 0) {
184
+ key += char;
185
+ }
186
+ else {
187
+ result += char;
188
+ }
189
+ }
190
+ // if leading char is digit, prefix underscore (very rare)
191
+ return (/^[0-9]/.test(result) ? '_' : '') + result;
192
+ }
73
193
  /**
74
194
  * parse css string
75
195
  * @param iter
76
196
  * @param options
77
197
  *
198
+ * @throws Error
78
199
  * @private
79
200
  */
80
201
  async function doParse(iter, options = {}) {
@@ -106,6 +227,9 @@ async function doParse(iter, options = {}) {
106
227
  if (typeof options.validation == 'boolean') {
107
228
  options.validation = options.validation ? ValidationLevel.All : ValidationLevel.None;
108
229
  }
230
+ if (options.module) {
231
+ options.expandNestingRules = true;
232
+ }
109
233
  if (options.expandNestingRules) {
110
234
  options.nestingRules = false;
111
235
  }
@@ -119,6 +243,8 @@ async function doParse(iter, options = {}) {
119
243
  const stats = {
120
244
  src: options.src ?? '',
121
245
  bytesIn: 0,
246
+ nodesCount: 0,
247
+ tokensCount: 0,
122
248
  importedBytesIn: 0,
123
249
  parse: `0ms`,
124
250
  minify: `0ms`,
@@ -147,14 +273,107 @@ async function doParse(iter, options = {}) {
147
273
  src: ''
148
274
  };
149
275
  }
150
- let item;
151
- let node;
276
+ let valuesHandlers;
277
+ let preValuesHandlers;
278
+ let postValuesHandlers;
279
+ let preVisitorsHandlersMap;
280
+ let visitorsHandlersMap;
281
+ let postVisitorsHandlersMap;
152
282
  const rawTokens = [];
153
283
  const imports = [];
284
+ let item;
285
+ let node;
154
286
  // @ts-ignore ignore error
155
287
  let isAsync = typeof iter[Symbol.asyncIterator] === 'function';
288
+ if (options.visitor != null) {
289
+ valuesHandlers = new Map;
290
+ preValuesHandlers = new Map;
291
+ postValuesHandlers = new Map;
292
+ preVisitorsHandlersMap = new Map;
293
+ visitorsHandlersMap = new Map;
294
+ postVisitorsHandlersMap = new Map;
295
+ const visitors = Object.entries(options.visitor);
296
+ let key;
297
+ let value;
298
+ let i;
299
+ for (i = 0; i < visitors.length; i++) {
300
+ key = visitors[i][0];
301
+ value = visitors[i][1];
302
+ if (Number.isInteger(+key)) {
303
+ visitors.splice(i + 1, 0, ...Object.entries(value));
304
+ continue;
305
+ }
306
+ if (Array.isArray(value)) {
307
+ // @ts-ignore
308
+ visitors.splice(i + 1, 0, ...value.map((item) => [key, item]));
309
+ continue;
310
+ }
311
+ if (key in EnumToken) {
312
+ if (typeof value == 'function') {
313
+ if (!valuesHandlers.has(EnumToken[key])) {
314
+ valuesHandlers.set(EnumToken[key], []);
315
+ }
316
+ valuesHandlers.get(EnumToken[key]).push(value);
317
+ }
318
+ else if (typeof value == 'object' && 'type' in value && 'handler' in value && value.type in WalkerEvent) {
319
+ if (value.type == WalkerEvent.Enter) {
320
+ if (!preValuesHandlers.has(EnumToken[key])) {
321
+ preValuesHandlers.set(EnumToken[key], []);
322
+ }
323
+ preValuesHandlers.get(EnumToken[key]).push(value.handler);
324
+ }
325
+ else if (value.type == WalkerEvent.Leave) {
326
+ if (!postValuesHandlers.has(EnumToken[key])) {
327
+ postValuesHandlers.set(EnumToken[key], []);
328
+ }
329
+ postValuesHandlers.get(EnumToken[key]).push(value.handler);
330
+ }
331
+ }
332
+ else {
333
+ errors.push({ action: 'ignore', message: `doParse: visitor.${key} is not a valid key name` });
334
+ }
335
+ }
336
+ else if (['Declaration', 'Rule', 'AtRule', 'KeyframesRule', 'KeyframesAtRule'].includes(key)) {
337
+ if (typeof value == 'function') {
338
+ if (!visitorsHandlersMap.has(key)) {
339
+ visitorsHandlersMap.set(key, []);
340
+ }
341
+ visitorsHandlersMap.get(key).push(value);
342
+ }
343
+ else if (typeof value == 'object') {
344
+ if ('type' in value && 'handler' in value && value.type in WalkerEvent) {
345
+ if (value.type == WalkerEvent.Enter) {
346
+ if (!preVisitorsHandlersMap.has(key)) {
347
+ preVisitorsHandlersMap.set(key, []);
348
+ }
349
+ preVisitorsHandlersMap.get(key).push(value.handler);
350
+ }
351
+ else if (value.type == WalkerEvent.Leave) {
352
+ if (!postVisitorsHandlersMap.has(key)) {
353
+ postVisitorsHandlersMap.set(key, []);
354
+ }
355
+ postVisitorsHandlersMap.get(key).push(value.handler);
356
+ }
357
+ }
358
+ else {
359
+ if (!visitorsHandlersMap.has(key)) {
360
+ visitorsHandlersMap.set(key, []);
361
+ }
362
+ visitorsHandlersMap.get(key).push(value);
363
+ }
364
+ }
365
+ else {
366
+ errors.push({ action: 'ignore', message: `doParse: visitor.${key} is not a valid key name` });
367
+ }
368
+ }
369
+ else {
370
+ errors.push({ action: 'ignore', message: `doParse: visitor.${key} is not a valid key name` });
371
+ }
372
+ }
373
+ }
156
374
  while (item = isAsync ? (await iter.next()).value : iter.next().value) {
157
375
  stats.bytesIn = item.bytesIn;
376
+ stats.tokensCount++;
158
377
  rawTokens.push(item);
159
378
  if (item.hint != null && BadTokensTypes.includes(item.hint)) {
160
379
  const node = getTokenType(item.token, item.hint);
@@ -182,7 +401,7 @@ async function doParse(iter, options = {}) {
182
401
  ast.loc.end = item.end;
183
402
  }
184
403
  if (item.token == ';' || item.token == '{') {
185
- node = parseNode(tokens, context, options, errors, src, map, rawTokens);
404
+ node = parseNode(tokens, context, options, errors, src, map, rawTokens, stats);
186
405
  rawTokens.length = 0;
187
406
  if (node != null) {
188
407
  if ('chi' in node) {
@@ -226,7 +445,7 @@ async function doParse(iter, options = {}) {
226
445
  map = new Map;
227
446
  }
228
447
  else if (item.token == '}') {
229
- parseNode(tokens, context, options, errors, src, map, rawTokens);
448
+ parseNode(tokens, context, options, errors, src, map, rawTokens, stats);
230
449
  rawTokens.length = 0;
231
450
  if (context.loc != null) {
232
451
  context.loc.end = item.end;
@@ -247,7 +466,7 @@ async function doParse(iter, options = {}) {
247
466
  }
248
467
  }
249
468
  if (tokens.length > 0) {
250
- node = parseNode(tokens, context, options, errors, src, map, rawTokens);
469
+ node = parseNode(tokens, context, options, errors, src, map, rawTokens, stats);
251
470
  rawTokens.length = 0;
252
471
  if (node != null) {
253
472
  if (node.typ == EnumToken.AtRuleNodeType && node.nam == 'import') {
@@ -276,6 +495,7 @@ async function doParse(iter, options = {}) {
276
495
  const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
277
496
  stream,
278
497
  buffer: '',
498
+ offset: 0,
279
499
  position: { ind: 0, lin: 1, col: 1 },
280
500
  currentPosition: { ind: -1, lin: 1, col: 0 }
281
501
  }), Object.assign({}, options, {
@@ -310,256 +530,675 @@ async function doParse(iter, options = {}) {
310
530
  if (options.expandNestingRules) {
311
531
  ast = expand(ast);
312
532
  }
313
- const valuesHandlers = new Map;
314
- const preValuesHandlers = new Map;
315
- const postValuesHandlers = new Map;
316
- const preVisitorsHandlersMap = new Map;
317
- const visitorsHandlersMap = new Map;
318
- const postVisitorsHandlersMap = new Map;
319
- const allValuesHandlers = [];
533
+ let replacement;
534
+ let callable;
320
535
  if (options.visitor != null) {
321
- for (const [key, value] of Object.entries(options.visitor)) {
322
- if (key in EnumToken) {
323
- if (typeof value == 'function') {
324
- valuesHandlers.set(EnumToken[key], value);
325
- }
326
- else if (typeof value == 'object' && 'type' in value && 'handler' in value && value.type in WalkerValueEvent) {
327
- if (WalkerValueEvent[value.type] == WalkerValueEvent.Enter) {
328
- preValuesHandlers.set(EnumToken[key], value.handler);
536
+ for (const result of walk(ast)) {
537
+ if (valuesHandlers.size > 0 || preVisitorsHandlersMap.size > 0 || visitorsHandlersMap.size > 0 || postVisitorsHandlersMap.size > 0) {
538
+ if ((result.node.typ == EnumToken.DeclarationNodeType &&
539
+ (preVisitorsHandlersMap.has('Declaration') || visitorsHandlersMap.has('Declaration') || postVisitorsHandlersMap.has('Declaration'))) ||
540
+ (result.node.typ == EnumToken.AtRuleNodeType && (preVisitorsHandlersMap.has('AtRule') || visitorsHandlersMap.has('AtRule') || postVisitorsHandlersMap.has('AtRule'))) ||
541
+ (result.node.typ == EnumToken.KeyframesAtRuleNodeType && (preVisitorsHandlersMap.has('KeyframesAtRule') || visitorsHandlersMap.has('KeyframesAtRule') || postVisitorsHandlersMap.has('KeyframesAtRule')))) {
542
+ const handlers = [];
543
+ const key = result.node.typ == EnumToken.DeclarationNodeType ? 'Declaration' : result.node.typ == EnumToken.AtRuleNodeType ? 'AtRule' : 'KeyframesAtRule';
544
+ if (preVisitorsHandlersMap.has(key)) {
545
+ // @ts-ignore
546
+ handlers.push(...preVisitorsHandlersMap.get(key));
329
547
  }
330
- else if (WalkerValueEvent[value.type] == WalkerValueEvent.Leave) {
331
- postValuesHandlers.set(EnumToken[key], value.handler);
548
+ if (visitorsHandlersMap.has(key)) {
549
+ // @ts-ignore
550
+ handlers.push(...visitorsHandlersMap.get(key));
551
+ }
552
+ if (postVisitorsHandlersMap.has(key)) {
553
+ // @ts-ignore
554
+ handlers.push(...postVisitorsHandlersMap.get(key));
555
+ }
556
+ let node = result.node;
557
+ for (const handler of handlers) {
558
+ callable = typeof handler == 'function' ? handler : handler[camelize(node.typ == EnumToken.DeclarationNodeType || node.typ == EnumToken.AtRuleNodeType ? node.nam : node.val)];
559
+ if (callable == null) {
560
+ continue;
561
+ }
562
+ replacement = callable(node, result.parent);
563
+ if (replacement == null) {
564
+ continue;
565
+ }
566
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
567
+ if (replacement) {
568
+ replacement = await replacement;
569
+ }
570
+ if (replacement == null || replacement == node) {
571
+ continue;
572
+ }
573
+ // @ts-ignore
574
+ node = replacement;
575
+ //
576
+ if (Array.isArray(node)) {
577
+ break;
578
+ }
579
+ }
580
+ if (node != result.node) {
581
+ // @ts-ignore
582
+ replaceToken(result.parent, result.node, node);
332
583
  }
333
584
  }
334
- else {
335
- console.warn(`doParse: visitor.${key} is not a valid key name`);
336
- }
337
- }
338
- else if (['Declaration', 'Rule', 'AtRule', 'KeyframesRule', 'KeyframesAtRule'].includes(key)) {
339
- if (typeof value == 'function') {
340
- visitorsHandlersMap.set(key, value);
341
- }
342
- else if (typeof value == 'object') {
343
- if ('type' in value && 'handler' in value && value.type in WalkerValueEvent) {
344
- if (WalkerValueEvent[value.type] == WalkerValueEvent.Enter) {
345
- preVisitorsHandlersMap.set(key, value.handler);
585
+ else if ((result.node.typ == EnumToken.RuleNodeType && (preVisitorsHandlersMap.has('Rule') || visitorsHandlersMap.has('Rule') || postVisitorsHandlersMap.has('Rule'))) ||
586
+ (result.node.typ == EnumToken.KeyFramesRuleNodeType && (preVisitorsHandlersMap.has('KeyframesRule') || visitorsHandlersMap.has('KeyframesRule') || postVisitorsHandlersMap.has('KeyframesRule')))) {
587
+ const handlers = [];
588
+ const key = result.node.typ == EnumToken.RuleNodeType ? 'Rule' : 'KeyframesRule';
589
+ if (preVisitorsHandlersMap.has(key)) {
590
+ handlers.push(...preVisitorsHandlersMap.get(key));
591
+ }
592
+ if (visitorsHandlersMap.has(key)) {
593
+ handlers.push(...visitorsHandlersMap.get(key));
594
+ }
595
+ if (postVisitorsHandlersMap.has(key)) {
596
+ handlers.push(...postVisitorsHandlersMap.get(key));
597
+ }
598
+ let node = result.node;
599
+ for (const callable of handlers) {
600
+ replacement = callable(node, result.parent);
601
+ if (replacement == null) {
602
+ continue;
603
+ }
604
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
605
+ if (replacement) {
606
+ replacement = await replacement;
346
607
  }
347
- else if (WalkerValueEvent[value.type] == WalkerValueEvent.Leave) {
348
- postVisitorsHandlersMap.set(key, value.handler);
608
+ if (replacement == null || replacement == node) {
609
+ continue;
610
+ }
611
+ // @ts-ignore
612
+ node = replacement;
613
+ //
614
+ if (Array.isArray(node)) {
615
+ break;
349
616
  }
350
617
  }
351
- else {
352
- visitorsHandlersMap.set(key, value);
618
+ // @ts-ignore
619
+ if (node != result.node) {
620
+ // @ts-ignore
621
+ replaceToken(result.parent, result.node, node);
353
622
  }
354
623
  }
355
- else {
356
- console.warn(`doParse: visitor.${key} is not a valid key name`);
624
+ else if (valuesHandlers.size > 0) {
625
+ let node = null;
626
+ node = result.node;
627
+ if (valuesHandlers.has(node.typ)) {
628
+ for (const valueHandler of valuesHandlers.get(node.typ)) {
629
+ callable = valueHandler;
630
+ replacement = callable(node, result.parent);
631
+ if (replacement == null) {
632
+ continue;
633
+ }
634
+ isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
635
+ if (isAsync) {
636
+ replacement = await replacement;
637
+ }
638
+ if (replacement != null && replacement != node) {
639
+ node = replacement;
640
+ }
641
+ }
642
+ }
643
+ if (node != result.node) {
644
+ // @ts-ignore
645
+ replaceToken(result.parent, value, node);
646
+ }
647
+ const tokens = 'tokens' in result.node ? result.node.tokens : [];
648
+ if ('val' in result.node && Array.isArray(result.node.val)) {
649
+ tokens.push(...result.node.val);
650
+ }
651
+ if (tokens.length == 0) {
652
+ continue;
653
+ }
654
+ for (const { value, parent, root } of walkValues(tokens, result.node)) {
655
+ node = value;
656
+ if (valuesHandlers.has(node.typ)) {
657
+ for (const valueHandler of valuesHandlers.get(node.typ)) {
658
+ callable = valueHandler;
659
+ let result = callable(node, parent, root);
660
+ if (result == null) {
661
+ continue;
662
+ }
663
+ isAsync = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction';
664
+ if (isAsync) {
665
+ result = await result;
666
+ }
667
+ if (result != null && result != node) {
668
+ node = result;
669
+ }
670
+ //
671
+ if (Array.isArray(node)) {
672
+ break;
673
+ }
674
+ }
675
+ }
676
+ if (node != value) {
677
+ // @ts-ignore
678
+ replaceToken(parent, value, node);
679
+ }
680
+ }
357
681
  }
358
682
  }
359
- else {
360
- console.warn(`doParse: visitor.${key} is not a valid key name`);
683
+ }
684
+ }
685
+ if (options.minify) {
686
+ if (ast.chi.length > 0) {
687
+ let passes = options.pass ?? 1;
688
+ while (passes--) {
689
+ minify(ast, options, true, errors, false);
361
690
  }
362
691
  }
363
- if (preValuesHandlers.size > 0) {
364
- allValuesHandlers.push(preValuesHandlers);
692
+ }
693
+ stats.bytesIn += stats.importedBytesIn;
694
+ let endTime = performance.now();
695
+ const result = {
696
+ ast,
697
+ errors,
698
+ stats: {
699
+ ...stats,
700
+ parse: `${(endParseTime - startTime).toFixed(2)}ms`,
701
+ minify: `${(endTime - endParseTime).toFixed(2)}ms`,
702
+ total: `${(endTime - startTime).toFixed(2)}ms`
365
703
  }
366
- if (valuesHandlers.size > 0) {
367
- allValuesHandlers.push(valuesHandlers);
704
+ };
705
+ if (options.module) {
706
+ const moduleSettings = {
707
+ hashLength: 5,
708
+ filePath: '',
709
+ scoped: ModuleScopeEnumOptions.Local,
710
+ naming: ModuleCaseTransformEnum.IgnoreCase,
711
+ pattern: '',
712
+ generateScopedName,
713
+ ...(typeof options.module != 'object' ? {} : options.module)
714
+ };
715
+ const parseModuleTime = performance.now();
716
+ const namesMapping = {};
717
+ const global = new Set;
718
+ const processed = new Set;
719
+ const pattern = typeof options.module == 'boolean' ? null : moduleSettings.pattern;
720
+ const importMapping = {};
721
+ const cssVariablesMap = {};
722
+ const importedCssVariables = {};
723
+ let mapping = {};
724
+ let revMapping = {};
725
+ let filePath = typeof options.module == 'boolean' ? options.src : (moduleSettings.filePath ?? options.src);
726
+ filePath = filePath === '' ? options.src : options.resolve(filePath, options.dirname(options.src), options.cwd).relative;
727
+ if (typeof options.module == 'number') {
728
+ if (options.module & ModuleCaseTransformEnum.CamelCase) {
729
+ moduleSettings.naming = ModuleCaseTransformEnum.CamelCase;
730
+ }
731
+ else if (options.module & ModuleCaseTransformEnum.CamelCaseOnly) {
732
+ moduleSettings.naming = ModuleCaseTransformEnum.CamelCaseOnly;
733
+ }
734
+ else if (options.module & ModuleCaseTransformEnum.DashCase) {
735
+ moduleSettings.naming = ModuleCaseTransformEnum.DashCase;
736
+ }
737
+ else if (options.module & ModuleCaseTransformEnum.DashCaseOnly) {
738
+ moduleSettings.naming = ModuleCaseTransformEnum.DashCaseOnly;
739
+ }
740
+ if (options.module & ModuleScopeEnumOptions.Global) {
741
+ moduleSettings.scoped = ModuleScopeEnumOptions.Global;
742
+ }
743
+ if (options.module & ModuleScopeEnumOptions.Pure) {
744
+ // @ts-ignore
745
+ moduleSettings.scoped |= ModuleScopeEnumOptions.Pure;
746
+ }
747
+ if (options.module & ModuleScopeEnumOptions.ICSS) {
748
+ // @ts-ignore
749
+ moduleSettings.scoped |= ModuleScopeEnumOptions.ICSS;
750
+ }
368
751
  }
369
- if (postValuesHandlers.size > 0) {
370
- allValuesHandlers.push(postValuesHandlers);
752
+ if (typeof moduleSettings.scoped == 'boolean') {
753
+ moduleSettings.scoped = moduleSettings.scoped ? ModuleScopeEnumOptions.Local : ModuleScopeEnumOptions.Global;
371
754
  }
372
- }
373
- for (const result of walk(ast)) {
374
- // if (result.parent != null && !isNodeAllowedInContext(result.node, result.parent as AstNode)) {
375
- //
376
- // errors.push({
377
- // action: 'drop',
378
- // message: `${EnumToken[result.parent.typ]}: child ${EnumToken[result.node.typ]}${result.node.typ == EnumToken.DeclarationNodeType ? ` '${(result.node as AstDeclaration).nam}'` : result.node.typ == EnumToken.AtRuleNodeType || result.node.typ == EnumToken.KeyframesAtRuleNodeType ? ` '@${(result.node as AstAtRule).nam}'` : ''} not allowed in context${result.parent.typ == EnumToken.AtRuleNodeType ? ` '@${(result.parent as AstAtRule).nam}'` : result.parent.typ == EnumToken.StyleSheetNodeType ? ` 'stylesheet'` : ''}`,
379
- // // @ts-ignore
380
- // location: result.node.loc ?? map.get(result.node ) ?? null
381
- // });
382
- //
383
- // // @ts-ignore
384
- // removeNode(result.node, result.parent as AstNode);
385
- // continue;
386
- // }
387
- if (allValuesHandlers.length > 0 || preVisitorsHandlersMap.size > 0 || visitorsHandlersMap.size > 0 || postVisitorsHandlersMap.size > 0) {
388
- if ((result.node.typ == EnumToken.DeclarationNodeType &&
389
- (preVisitorsHandlersMap.has('Declaration') || visitorsHandlersMap.has('Declaration') || postVisitorsHandlersMap.has('Declaration'))) ||
390
- (result.node.typ == EnumToken.AtRuleNodeType && (preVisitorsHandlersMap.has('AtRule') || visitorsHandlersMap.has('AtRule') || postVisitorsHandlersMap.has('AtRule'))) ||
391
- (result.node.typ == EnumToken.KeyframesAtRuleNodeType && (preVisitorsHandlersMap.has('KeyframesAtRule') || visitorsHandlersMap.has('KeyframesAtRule') || postVisitorsHandlersMap.has('KeyframesAtRule')))) {
392
- const handlers = [];
393
- const key = result.node.typ == EnumToken.DeclarationNodeType ? 'Declaration' : result.node.typ == EnumToken.AtRuleNodeType ? 'AtRule' : 'KeyframesAtRule';
394
- if (preVisitorsHandlersMap.has(key)) {
395
- // @ts-ignore
396
- handlers.push(preVisitorsHandlersMap.get(key));
755
+ moduleSettings.filePath = filePath;
756
+ moduleSettings.pattern = pattern != null && pattern !== '' ? pattern : (filePath === '' ? `[local]_[hash]` : `[local]_[hash]_[name]`);
757
+ for (const { node, parent } of walk(ast)) {
758
+ if (node.typ == EnumToken.CssVariableImportTokenType) {
759
+ const url = node.val.find(t => t.typ == EnumToken.StringTokenType).val.slice(1, -1);
760
+ const src = options.resolve(url, options.dirname(options.src), options.cwd);
761
+ const result = options.load(url, options.src);
762
+ const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction' ? await result : result;
763
+ const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
764
+ stream,
765
+ buffer: '',
766
+ offset: 0,
767
+ position: { ind: 0, lin: 1, col: 1 },
768
+ currentPosition: { ind: -1, lin: 1, col: 0 }
769
+ }), Object.assign({}, options, {
770
+ minify: false,
771
+ setParent: false,
772
+ src: src.relative
773
+ }));
774
+ cssVariablesMap[node.nam] = root.cssModuleVariables;
775
+ parent.chi.splice(parent.chi.indexOf(node), 1);
776
+ continue;
777
+ }
778
+ if (node.typ == EnumToken.CssVariableDeclarationMapTokenType) {
779
+ const from = node.from.find(t => t.typ == EnumToken.IdenTokenType || isIdentColor(t));
780
+ if (!(from.val in cssVariablesMap)) {
781
+ errors.push({
782
+ node,
783
+ message: `could not resolve @value import from '${from.val}'`,
784
+ action: 'drop'
785
+ });
397
786
  }
398
- if (visitorsHandlersMap.has(key)) {
399
- // @ts-ignore
400
- handlers.push(visitorsHandlersMap.get(key));
787
+ else {
788
+ for (const token of node.vars) {
789
+ if (token.typ == EnumToken.IdenTokenType || isIdentColor(token)) {
790
+ if (!(token.val in cssVariablesMap[from.val])) {
791
+ errors.push({
792
+ node,
793
+ message: `value '${token.val}' is not exported from '${from.val}'`,
794
+ action: 'drop'
795
+ });
796
+ continue;
797
+ }
798
+ result.cssModuleVariables ??= {};
799
+ result.cssModuleVariables[token.val] = importedCssVariables[token.val] = cssVariablesMap[from.val][token.val];
800
+ }
801
+ }
401
802
  }
402
- if (postVisitorsHandlersMap.has(key)) {
403
- // @ts-ignore
404
- handlers.push(postVisitorsHandlersMap.get(key));
803
+ parent.chi.splice(parent.chi.indexOf(node), 1);
804
+ continue;
805
+ }
806
+ if (node.typ == EnumToken.CssVariableTokenType) {
807
+ if (parent?.typ == EnumToken.StyleSheetNodeType) {
808
+ if (result.cssModuleVariables == null) {
809
+ result.cssModuleVariables = {};
810
+ }
811
+ result.cssModuleVariables[node.nam] = node;
405
812
  }
406
- let callable;
407
- let node = result.node;
408
- for (const handler of handlers) {
409
- callable = typeof handler == 'function' ? handler : handler[normalizeVisitorKeyName(node.typ == EnumToken.DeclarationNodeType || node.typ == EnumToken.AtRuleNodeType ? node.nam : node.val)];
410
- if (callable == null) {
411
- continue;
813
+ parent.chi.splice(parent.chi.indexOf(node), 1);
814
+ continue;
815
+ }
816
+ if (node.typ == EnumToken.DeclarationNodeType) {
817
+ if (node.nam.startsWith('--')) {
818
+ if (!(node.nam in namesMapping)) {
819
+ let result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? node.nam : moduleSettings.generateScopedName(node.nam, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
820
+ let value = result instanceof Promise ? await result : result;
821
+ mapping[node.nam] = '--' + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(value, moduleSettings.naming) : value);
822
+ revMapping[node.nam] = node.nam;
412
823
  }
413
- let replacement = callable(node, result.parent);
414
- if (replacement == null) {
415
- continue;
824
+ node.nam = mapping[node.nam];
825
+ }
826
+ if ('composes' == node.nam.toLowerCase()) {
827
+ const tokens = [];
828
+ let isValid = true;
829
+ for (const token of node.val) {
830
+ if (token.typ == EnumToken.ComposesSelectorNodeType) {
831
+ if (!(token.r == null || token.r.typ == EnumToken.StringTokenType || token.r.typ == EnumToken.IdenTokenType)) {
832
+ errors.push({
833
+ action: 'drop',
834
+ message: `composes '${EnumToken[token.r.typ]}' is not supported`,
835
+ node
836
+ });
837
+ isValid = false;
838
+ break;
839
+ }
840
+ tokens.push(token);
841
+ }
416
842
  }
417
- isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
418
- if (replacement) {
419
- replacement = await replacement;
843
+ // find parent rule
844
+ let parentRule = node.parent;
845
+ while (parentRule != null && parentRule.typ != EnumToken.RuleNodeType) {
846
+ parentRule = parentRule.parent;
420
847
  }
421
- if (replacement == null || replacement == node) {
848
+ if (!isValid || tokens.length == 0) {
849
+ if (tokens.length == 0) {
850
+ errors.push({
851
+ action: 'drop',
852
+ message: `composes is empty`,
853
+ node
854
+ });
855
+ }
856
+ parentRule.chi.splice(parentRule.chi.indexOf(node), 1);
422
857
  continue;
423
858
  }
424
- // @ts-ignore
425
- node = replacement;
426
- //
427
- if (Array.isArray(node)) {
428
- break;
859
+ for (const token of tokens) {
860
+ // composes: a b c;
861
+ if (token.r == null) {
862
+ for (const rule of token.l) {
863
+ if (rule.typ == EnumToken.WhitespaceTokenType || rule.typ == EnumToken.CommentTokenType) {
864
+ continue;
865
+ }
866
+ if (!(rule.val in mapping)) {
867
+ let result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? rule.val : moduleSettings.generateScopedName(rule.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
868
+ let value = result instanceof Promise ? await result : result;
869
+ mapping[rule.val] = (rule.typ == EnumToken.DashedIdenTokenType ? '--' : '') + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(value, moduleSettings.naming) : value);
870
+ revMapping[mapping[rule.val]] = rule.val;
871
+ }
872
+ if (parentRule != null) {
873
+ for (const tk of parentRule.tokens) {
874
+ if (tk.typ == EnumToken.ClassSelectorTokenType) {
875
+ const val = tk.val.slice(1);
876
+ if (val in revMapping) {
877
+ const key = revMapping[val];
878
+ mapping[key] = [...new Set([...mapping[key].split(' '), mapping[rule.val]])].join(' ');
879
+ }
880
+ }
881
+ }
882
+ }
883
+ }
884
+ }
885
+ // composes: a b c from 'file.css';
886
+ else if (token.r.typ == EnumToken.String) {
887
+ const url = token.r.val.slice(1, -1);
888
+ const src = options.resolve(url, options.dirname(options.src), options.cwd);
889
+ const result = options.load(url, options.src);
890
+ const stream = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction' ? await result : result;
891
+ const root = await doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
892
+ stream,
893
+ buffer: '',
894
+ offset: 0,
895
+ position: { ind: 0, lin: 1, col: 1 },
896
+ currentPosition: { ind: -1, lin: 1, col: 0 }
897
+ }), Object.assign({}, options, {
898
+ minify: false,
899
+ setParent: false,
900
+ src: src.relative
901
+ }));
902
+ const srcIndex = (src.relative.startsWith('/') || src.relative.startsWith('../') ? '' : './') + src.relative;
903
+ if (Object.keys(root.mapping).length > 0) {
904
+ importMapping[srcIndex] = {};
905
+ }
906
+ if (parentRule != null) {
907
+ for (const tk of parentRule.tokens) {
908
+ if (tk.typ == EnumToken.ClassSelectorTokenType) {
909
+ const val = tk.val.slice(1);
910
+ if (val in revMapping) {
911
+ const key = revMapping[val];
912
+ const values = [];
913
+ for (const iden of token.l) {
914
+ if (iden.typ != EnumToken.IdenTokenType && iden.typ != EnumToken.DashedIdenTokenType) {
915
+ continue;
916
+ }
917
+ if (!(iden.val in root.mapping)) {
918
+ const result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? iden.val : moduleSettings.generateScopedName(iden.val, srcIndex, moduleSettings.pattern, moduleSettings.hashLength);
919
+ let value = result instanceof Promise ? await result : result;
920
+ root.mapping[iden.val] = (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(value, moduleSettings.naming) : value);
921
+ root.revMapping[root.mapping[iden.val]] = iden.val;
922
+ }
923
+ importMapping[srcIndex][iden.val] = root.mapping[iden.val];
924
+ values.push(root.mapping[iden.val]);
925
+ }
926
+ mapping[key] = [...new Set([...mapping[key].split(' '), ...values])].join(' ');
927
+ }
928
+ }
929
+ }
930
+ }
931
+ }
932
+ // composes: a b c from global;
933
+ else if (token.r.typ == EnumToken.IdenTokenType) {
934
+ // global
935
+ if (parentRule != null) {
936
+ if ('global' == token.r.val.toLowerCase()) {
937
+ for (const tk of parentRule.tokens) {
938
+ if (tk.typ == EnumToken.ClassSelectorTokenType) {
939
+ const val = tk.val.slice(1);
940
+ if (val in revMapping) {
941
+ const key = revMapping[val];
942
+ mapping[key] = [...new Set([...mapping[key].split(' '), ...(token.l.reduce((acc, curr) => {
943
+ if (curr.typ == EnumToken.IdenTokenType) {
944
+ acc.push(curr.val);
945
+ }
946
+ return acc;
947
+ }, []))])].join(' ');
948
+ }
949
+ }
950
+ }
951
+ }
952
+ else {
953
+ errors.push({
954
+ action: 'drop',
955
+ message: `composes '${token.r.val}' is not supported`,
956
+ node
957
+ });
958
+ }
959
+ }
960
+ }
429
961
  }
962
+ parentRule.chi.splice(parentRule.chi.indexOf(node), 1);
430
963
  }
431
- if (node != result.node) {
432
- // @ts-ignore
433
- replaceToken(result.parent, result.node, node);
964
+ if (node.typ == EnumToken.DeclarationNodeType && ['grid-column', 'grid-column-start', 'grid-column-end', 'grid-row', 'grid-row-start', 'grid-row-end', 'grid-template', 'grid-template-columns', 'grid-template-rows'].includes(node.nam)) {
965
+ for (const { value } of walkValues(node.val, node)) {
966
+ if (value.typ != EnumToken.IdenTokenType) {
967
+ continue;
968
+ }
969
+ let idenToken = value.val;
970
+ let suffix = '';
971
+ if (idenToken.endsWith('-start')) {
972
+ suffix = '-start';
973
+ idenToken = idenToken.slice(0, -6);
974
+ }
975
+ else if (idenToken.endsWith('-end')) {
976
+ suffix = '-end';
977
+ idenToken = idenToken.slice(0, -4);
978
+ }
979
+ if (!(idenToken in mapping)) {
980
+ let result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? idenToken : moduleSettings.generateScopedName(idenToken, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
981
+ if (result instanceof Promise) {
982
+ result = await result;
983
+ }
984
+ mapping[idenToken] = result;
985
+ revMapping[result] = idenToken;
986
+ if (suffix !== '') {
987
+ idenToken += suffix;
988
+ if (!(idenToken in mapping)) {
989
+ mapping[idenToken] = result + suffix;
990
+ revMapping[result + suffix] = idenToken;
991
+ }
992
+ }
993
+ }
994
+ value.val = mapping[idenToken];
995
+ }
434
996
  }
435
- }
436
- else if ((result.node.typ == EnumToken.RuleNodeType && (preVisitorsHandlersMap.has('Rule') || visitorsHandlersMap.has('Rule') || postVisitorsHandlersMap.has('Rule'))) ||
437
- (result.node.typ == EnumToken.KeyFramesRuleNodeType && (preVisitorsHandlersMap.has('KeyframesRule') || visitorsHandlersMap.has('KeyframesRule') || postVisitorsHandlersMap.has('KeyframesRule')))) {
438
- const handlers = [];
439
- const key = result.node.typ == EnumToken.RuleNodeType ? 'Rule' : 'KeyframesRule';
440
- if (preVisitorsHandlersMap.has(key)) {
441
- // @ts-ignore
442
- handlers.push(preVisitorsHandlersMap.get(key));
997
+ else if (node.nam == 'grid-template-areas' || node.nam == 'grid-template') {
998
+ for (let i = 0; i < node.val.length; i++) {
999
+ if (node.val[i].typ == EnumToken.String) {
1000
+ const tokens = parseString(node.val[i].val.slice(1, -1), { location: true });
1001
+ for (const { value } of walkValues(tokens)) {
1002
+ if (value.typ == EnumToken.IdenTokenType || value.typ == EnumToken.DashedIdenTokenType) {
1003
+ if (value.val in mapping) {
1004
+ value.val = mapping[value.val];
1005
+ }
1006
+ else {
1007
+ let result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
1008
+ if (result instanceof Promise) {
1009
+ result = await result;
1010
+ }
1011
+ mapping[value.val] = result;
1012
+ revMapping[result] = value.val;
1013
+ value.val = result;
1014
+ }
1015
+ }
1016
+ }
1017
+ node.val[i].val = node.val[i].val.charAt(0) + tokens.reduce((acc, curr) => acc + renderToken(curr), '') + node.val[i].val.charAt(node.val[i].val.length - 1);
1018
+ }
1019
+ }
443
1020
  }
444
- if (visitorsHandlersMap.has(key)) {
445
- // @ts-ignore
446
- handlers.push(visitorsHandlersMap.get(key));
1021
+ else if (node.nam == 'animation' || node.nam == 'animation-name') {
1022
+ for (const { value } of walkValues(node.val, node)) {
1023
+ if (value.typ == EnumToken.IdenTokenType && ![
1024
+ 'none', 'infinite', 'normal', 'reverse', 'alternate',
1025
+ 'alternate-reverse', 'forwards', 'backwards', 'both',
1026
+ 'running', 'paused', 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out',
1027
+ 'step-start', 'step-end', 'jump-start', 'jump-end',
1028
+ 'jump-none', 'jump-both', 'start', 'end',
1029
+ 'inherit', 'initial', 'unset'
1030
+ ].includes(value.val)) {
1031
+ if (!(value.val in mapping)) {
1032
+ const result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
1033
+ mapping[value.val] = result instanceof Promise ? await result : result;
1034
+ revMapping[mapping[value.val]] = value.val;
1035
+ }
1036
+ value.val = mapping[value.val];
1037
+ }
1038
+ }
447
1039
  }
448
- if (postVisitorsHandlersMap.has(key)) {
449
- // @ts-ignore
450
- handlers.push(postVisitorsHandlersMap.get(key));
1040
+ for (const { value, parent } of walkValues(node.val, node)) {
1041
+ if (value.typ == EnumToken.DashedIdenTokenType) {
1042
+ if (!(value.val in mapping)) {
1043
+ const result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
1044
+ let val = result instanceof Promise ? await result : result;
1045
+ mapping[value.val] = '--' + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(val, moduleSettings.naming) : val);
1046
+ revMapping[mapping[value.val]] = value.val;
1047
+ }
1048
+ value.val = mapping[value.val];
1049
+ }
1050
+ else if ((value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) {
1051
+ replaceToken(parent, value, importedCssVariables[value.val].val);
1052
+ }
451
1053
  }
452
- let node = result.node;
453
- for (const callable of handlers) {
454
- // @ts-ignore
455
- let replacement = callable(node, result.parent);
456
- if (replacement == null) {
457
- continue;
1054
+ }
1055
+ else if (node.typ == EnumToken.RuleNodeType) {
1056
+ if (node.tokens == null) {
1057
+ Object.defineProperty(node, 'tokens', {
1058
+ ...definedPropertySettings,
1059
+ value: parseSelector(parseString(node.sel, { location: true }))
1060
+ });
1061
+ }
1062
+ let hasIdOrClass = false;
1063
+ for (const { value } of walkValues(node.tokens, node,
1064
+ // @ts-ignore
1065
+ (value, parent) => {
1066
+ if (value.typ == EnumToken.PseudoClassTokenType) {
1067
+ const val = value.val.toLowerCase();
1068
+ switch (val) {
1069
+ case ':local':
1070
+ case ':global':
1071
+ {
1072
+ let index = parent.tokens.indexOf(value);
1073
+ parent.tokens.splice(index, 1);
1074
+ if (parent.tokens[index]?.typ == EnumToken.WhitespaceTokenType || parent.tokens[index]?.typ == EnumToken.DescendantCombinatorTokenType) {
1075
+ parent.tokens.splice(index, 1);
1076
+ }
1077
+ if (val == ':global') {
1078
+ for (; index < parent.tokens.length; index++) {
1079
+ if (parent.tokens[index].typ == EnumToken.CommaTokenType ||
1080
+ ([EnumToken.PseudoClassFuncTokenType, EnumToken.PseudoClassTokenType].includes(parent.tokens[index].typ) &&
1081
+ [':global', ':local'].includes(parent.tokens[index].val.toLowerCase()))) {
1082
+ break;
1083
+ }
1084
+ global.add(parent.tokens[index]);
1085
+ }
1086
+ }
1087
+ }
1088
+ break;
1089
+ }
458
1090
  }
459
- isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
460
- if (replacement) {
461
- replacement = await replacement;
1091
+ else if (value.typ == EnumToken.PseudoClassFuncTokenType) {
1092
+ switch (value.val.toLowerCase()) {
1093
+ case ':global':
1094
+ for (const token of value.chi) {
1095
+ global.add(token);
1096
+ }
1097
+ parent.tokens.splice(parent.tokens.indexOf(value), 1, ...value.chi);
1098
+ break;
1099
+ case ':local':
1100
+ parent.tokens.splice(parent.tokens.indexOf(value), 1, ...value.chi);
1101
+ break;
1102
+ }
462
1103
  }
463
- if (replacement == null || replacement == node) {
464
- continue;
1104
+ })) {
1105
+ if (value.typ == EnumToken.HashTokenType || value.typ == EnumToken.ClassSelectorTokenType) {
1106
+ hasIdOrClass = true;
465
1107
  }
466
- // @ts-ignore
467
- node = replacement;
468
- //
469
- if (Array.isArray(node)) {
470
- break;
1108
+ if (processed.has(value)) {
1109
+ continue;
471
1110
  }
472
- }
473
- // @ts-ignore
474
- if (node != result.node) {
475
- // @ts-ignore
476
- replaceToken(result.parent, result.node, node);
477
- }
478
- }
479
- else if (allValuesHandlers.length > 0) {
480
- let callable;
481
- let node = null;
482
- node = result.node;
483
- for (const valueHandler of allValuesHandlers) {
484
- if (valueHandler.has(node.typ)) {
485
- callable = valueHandler.get(node.typ);
486
- let replacement = callable(node, result.parent);
487
- if (replacement == null) {
1111
+ processed.add(value);
1112
+ if (value.typ == EnumToken.PseudoClassTokenType) ;
1113
+ else if (value.typ == EnumToken.PseudoClassFuncTokenType) ;
1114
+ else {
1115
+ if (global.has(value)) {
488
1116
  continue;
489
1117
  }
490
- isAsync = replacement instanceof Promise || Object.getPrototypeOf(replacement).constructor.name == 'AsyncFunction';
491
- if (isAsync) {
492
- replacement = await replacement;
493
- }
494
- if (replacement != null && replacement != node) {
495
- node = replacement;
1118
+ if (value.typ == EnumToken.ClassSelectorTokenType) {
1119
+ const val = value.val.slice(1);
1120
+ if (!(val in mapping)) {
1121
+ const result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? val : moduleSettings.generateScopedName(val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
1122
+ let value = result instanceof Promise ? await result : result;
1123
+ mapping[val] = (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(value, moduleSettings.naming) : value);
1124
+ revMapping[mapping[val]] = val;
1125
+ }
1126
+ value.val = '.' + mapping[val];
496
1127
  }
497
1128
  }
498
1129
  }
499
- if (node != result.node) {
500
- // @ts-ignore
501
- replaceToken(result.parent, value, node);
1130
+ if (moduleSettings.scoped & ModuleScopeEnumOptions.Pure) {
1131
+ if (!hasIdOrClass) {
1132
+ throw new Error(`pure module: No id or class found in selector '${node.sel}' at '${node.loc?.src ?? ''}':${node.loc?.sta?.lin ?? ''}:${node.loc?.sta?.col ?? ''}`);
1133
+ }
502
1134
  }
503
- const tokens = 'tokens' in result.node ? result.node.tokens : [];
504
- if ('val' in result.node && Array.isArray(result.node.val)) {
505
- tokens.push(...result.node.val);
1135
+ node.sel = '';
1136
+ for (const token of node.tokens) {
1137
+ node.sel += renderToken(token);
506
1138
  }
507
- if (tokens.length == 0) {
508
- continue;
1139
+ }
1140
+ else if (node.typ == EnumToken.AtRuleNodeType || node.typ == EnumToken.KeyframesAtRuleNodeType) {
1141
+ const val = node.nam.toLowerCase();
1142
+ if (node.tokens == null) {
1143
+ Object.defineProperty(node, 'tokens', {
1144
+ ...definedPropertySettings,
1145
+ // @ts-ignore
1146
+ value: parseAtRulePrelude(parseString(node.val), node)
1147
+ });
509
1148
  }
510
- for (const { value, parent, root } of walkValues(tokens, result.node)) {
511
- node = value;
512
- for (const valueHandler of allValuesHandlers) {
513
- if (valueHandler.has(node.typ)) {
514
- callable = valueHandler.get(node.typ);
515
- let result = callable(node, parent, root);
516
- if (result == null) {
517
- continue;
518
- }
519
- isAsync = result instanceof Promise || Object.getPrototypeOf(result).constructor.name == 'AsyncFunction';
520
- if (isAsync) {
521
- result = await result;
1149
+ if (val == 'property' || val == 'keyframes') {
1150
+ const prefix = val == 'property' ? '--' : '';
1151
+ for (const value of node.tokens) {
1152
+ if ((prefix == '--' && value.typ == EnumToken.DashedIdenTokenType) || (prefix == '' && value.typ == EnumToken.IdenTokenType)) {
1153
+ if (!(value.val in mapping)) {
1154
+ const result = (moduleSettings.scoped & ModuleScopeEnumOptions.Global) ? value.val : moduleSettings.generateScopedName(value.val, moduleSettings.filePath, moduleSettings.pattern, moduleSettings.hashLength);
1155
+ let val = result instanceof Promise ? await result : result;
1156
+ mapping[value.val] = prefix + (moduleSettings.naming & ModuleCaseTransformEnum.DashCaseOnly || moduleSettings.naming & ModuleCaseTransformEnum.CamelCaseOnly ? getKeyName(val, moduleSettings.naming) : val);
1157
+ revMapping[mapping[value.val]] = value.val;
522
1158
  }
523
- if (result != null && result != node) {
524
- node = result;
525
- }
526
- //
527
- if (Array.isArray(node)) {
528
- break;
1159
+ value.val = mapping[value.val];
1160
+ }
1161
+ }
1162
+ node.val = node.tokens.reduce((a, b) => a + renderToken(b), '');
1163
+ }
1164
+ else {
1165
+ let isReplaced = false;
1166
+ for (const { value, parent } of walkValues(node.tokens, node)) {
1167
+ if (EnumToken.MediaQueryConditionTokenType == parent.typ && value != parent.l) {
1168
+ if ((value.typ == EnumToken.IdenTokenType || isIdentColor(value)) && value.val in importedCssVariables) {
1169
+ isReplaced = true;
1170
+ parent.r.splice(parent.r.indexOf(value), 1, ...importedCssVariables[value.val].val);
529
1171
  }
530
1172
  }
531
1173
  }
532
- if (node != value) {
533
- // @ts-ignore
534
- replaceToken(parent, value, node);
1174
+ if (isReplaced) {
1175
+ node.val = node.tokens.reduce((a, b) => a + renderToken(b), '');
535
1176
  }
536
1177
  }
537
1178
  }
538
1179
  }
539
- }
540
- if (options.minify) {
541
- if (ast.chi.length > 0) {
542
- let passes = options.pass ?? 1;
543
- while (passes--) {
544
- minify(ast, options, true, errors, false);
545
- }
1180
+ if (moduleSettings.naming != ModuleCaseTransformEnum.IgnoreCase) {
1181
+ revMapping = {};
1182
+ mapping = Object.entries(mapping).reduce((acc, [key, value]) => {
1183
+ const keyName = getKeyName(key, moduleSettings.naming);
1184
+ acc[keyName] = value;
1185
+ revMapping[value] = keyName;
1186
+ return acc;
1187
+ }, {});
546
1188
  }
1189
+ result.mapping = mapping;
1190
+ result.revMapping = revMapping;
1191
+ if ((moduleSettings.scoped & ModuleScopeEnumOptions.ICSS) && Object.keys(importMapping).length > 0) {
1192
+ result.importMapping = importMapping;
1193
+ }
1194
+ endTime = performance.now();
1195
+ result.stats.module = `${(endTime - parseModuleTime).toFixed(2)}ms`;
1196
+ result.stats.total = `${(endTime - startTime).toFixed(2)}ms`;
547
1197
  }
548
- const endTime = performance.now();
549
1198
  if (options.signal != null) {
550
1199
  options.signal.removeEventListener('abort', reject);
551
1200
  }
552
- stats.bytesIn += stats.importedBytesIn;
553
- return {
554
- ast,
555
- errors,
556
- stats: {
557
- ...stats,
558
- parse: `${(endParseTime - startTime).toFixed(2)}ms`,
559
- minify: `${(endTime - endParseTime).toFixed(2)}ms`,
560
- total: `${(endTime - startTime).toFixed(2)}ms`
561
- }
562
- };
1201
+ return result;
563
1202
  }
564
1203
  function getLastNode(context) {
565
1204
  let i = context.chi.length;
@@ -571,7 +1210,7 @@ function getLastNode(context) {
571
1210
  }
572
1211
  return null;
573
1212
  }
574
- function parseNode(results, context, options, errors, src, map, rawTokens) {
1213
+ function parseNode(results, context, options, errors, src, map, rawTokens, stats) {
575
1214
  let tokens = [];
576
1215
  for (const t of results) {
577
1216
  const node = getTokenType(t.token, t.hint);
@@ -594,9 +1233,12 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
594
1233
  }
595
1234
  loc = location;
596
1235
  context.chi.push(tokens[i]);
597
- if (options.sourcemap) {
598
- tokens[i].loc = loc;
599
- }
1236
+ stats.nodesCount++;
1237
+ Object.defineProperty(tokens[i], 'loc', {
1238
+ ...definedPropertySettings,
1239
+ value: loc,
1240
+ enumerable: options.sourcemap !== false
1241
+ });
600
1242
  }
601
1243
  else if (tokens[i].typ != EnumToken.WhitespaceTokenType) {
602
1244
  break;
@@ -737,10 +1379,10 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
737
1379
  }
738
1380
  }
739
1381
  const t = parseAtRulePrelude(parseTokens(tokens, { minify: options.minify }), atRule);
740
- const raw = t.reduce((acc, curr) => {
741
- acc.push(renderToken(curr, { removeComments: true, convertColor: false }));
742
- return acc;
743
- }, []);
1382
+ const raw = [];
1383
+ for (const curr of t) {
1384
+ raw.push(renderToken(curr, { removeComments: true, convertColor: false }));
1385
+ }
744
1386
  const nam = renderToken(atRule, { removeComments: true });
745
1387
  // @ts-ignore
746
1388
  const node = {
@@ -756,10 +1398,12 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
756
1398
  node.chi = [];
757
1399
  }
758
1400
  loc = map.get(atRule);
759
- if (options.sourcemap) {
760
- node.loc = loc;
761
- node.loc.end = { ...map.get(delim).end };
762
- }
1401
+ Object.defineProperty(node, 'loc', {
1402
+ ...definedPropertySettings,
1403
+ value: loc,
1404
+ enumerable: options.sourcemap !== false
1405
+ });
1406
+ node.loc.end = { ...map.get(delim).end };
763
1407
  let isValid = true;
764
1408
  if (node.nam == 'else') {
765
1409
  const prev = getLastNode(context);
@@ -772,21 +1416,173 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
772
1416
  isValid = false;
773
1417
  }
774
1418
  }
1419
+ if (node.nam == 'value') {
1420
+ let i = 0;
1421
+ while (i < tokens.length) {
1422
+ if (tokens[i].typ == EnumToken.WhitespaceTokenType || tokens[i].typ == EnumToken.CommentTokenType) {
1423
+ i++;
1424
+ continue;
1425
+ }
1426
+ break;
1427
+ }
1428
+ if (i < tokens.length) {
1429
+ if (tokens[i].typ == EnumToken.IdenTokenType || isIdentColor(tokens[i])) {
1430
+ let k = i + 1;
1431
+ while (k < tokens.length) {
1432
+ if (tokens[k].typ == EnumToken.WhitespaceTokenType || tokens[k].typ == EnumToken.CommentTokenType) {
1433
+ k++;
1434
+ continue;
1435
+ }
1436
+ // var or import
1437
+ if (tokens[k].typ == EnumToken.ColonTokenType) {
1438
+ let j = k;
1439
+ while (++j < tokens.length) {
1440
+ if (tokens[j].typ != EnumToken.WhitespaceTokenType && tokens[j].typ != EnumToken.CommentTokenType) {
1441
+ break;
1442
+ }
1443
+ }
1444
+ let offset = k + 1;
1445
+ while (offset < tokens.length && tokens[offset].typ == EnumToken.WhitespaceTokenType) {
1446
+ offset++;
1447
+ }
1448
+ if (tokens[j].typ == EnumToken.StringTokenType) {
1449
+ Object.assign(node, {
1450
+ typ: EnumToken.CssVariableImportTokenType,
1451
+ nam: tokens[i].val,
1452
+ val: tokens.slice(offset)
1453
+ });
1454
+ delete node.tokens;
1455
+ // @ts-ignore
1456
+ delete node.raw;
1457
+ context.chi.push(node);
1458
+ return null;
1459
+ }
1460
+ Object.assign(node, {
1461
+ typ: EnumToken.CssVariableTokenType,
1462
+ nam: tokens[i].val,
1463
+ val: tokens.slice(offset)
1464
+ });
1465
+ context.chi.push(node);
1466
+ return null;
1467
+ }
1468
+ if (tokens[k].typ == EnumToken.PseudoClassTokenType) {
1469
+ Object.assign(tokens[k], {
1470
+ typ: EnumToken.IdenTokenType,
1471
+ val: tokens[k].val.slice(1)
1472
+ });
1473
+ Object.assign(node, {
1474
+ typ: EnumToken.CssVariableTokenType,
1475
+ nam: tokens[i].val,
1476
+ val: tokens.slice(k)
1477
+ });
1478
+ context.chi.push(node);
1479
+ return null;
1480
+ }
1481
+ if (tokens[k].typ == EnumToken.CommaTokenType) {
1482
+ let j = i;
1483
+ while (++j < tokens.length) {
1484
+ if (tokens[j].typ == EnumToken.IdenTokenType && tokens[j].val.toLowerCase() == 'from') {
1485
+ const vars = tokens.slice(i, j);
1486
+ const from = tokens.slice(j + 1);
1487
+ let l = 0;
1488
+ let expect = EnumToken.IdenTokenType;
1489
+ for (; l < vars.length; l++) {
1490
+ if (vars[l].typ == EnumToken.WhitespaceTokenType || vars[l].typ == EnumToken.CommentTokenType) {
1491
+ continue;
1492
+ }
1493
+ if (expect == vars[l].typ || (expect == EnumToken.IdenTokenType && isIdentColor(vars[l]))) {
1494
+ expect = expect == EnumToken.CommaTokenType ? EnumToken.IdenTokenType : EnumToken.CommaTokenType;
1495
+ continue;
1496
+ }
1497
+ errors.push({
1498
+ action: 'drop',
1499
+ node: node,
1500
+ location: map.get(vars[l]) ?? location,
1501
+ message: `expecting '${EnumToken[expect]}' but found ${renderToken(vars[l])}`
1502
+ });
1503
+ return null;
1504
+ }
1505
+ l = 0;
1506
+ expect = EnumToken.IdenTokenType;
1507
+ for (; l < from.length; l++) {
1508
+ if (from[l].typ == EnumToken.WhitespaceTokenType || from[l].typ == EnumToken.CommentTokenType) {
1509
+ continue;
1510
+ }
1511
+ if (expect == from[l].typ || isIdentColor(from[l])) {
1512
+ while (++l < from.length) {
1513
+ if (from[l].typ == EnumToken.WhitespaceTokenType || from[l].typ == EnumToken.CommentTokenType) {
1514
+ continue;
1515
+ }
1516
+ errors.push({
1517
+ action: 'drop',
1518
+ node: node,
1519
+ location: map.get(from[l]) ?? location,
1520
+ message: `unexpected '${renderToken(from[l])}'`
1521
+ });
1522
+ return null;
1523
+ }
1524
+ break;
1525
+ }
1526
+ errors.push({
1527
+ action: 'drop',
1528
+ node: node,
1529
+ location: map.get(from[l]) ?? location,
1530
+ message: `expecting <string> but found ${renderToken(from[l])}`
1531
+ });
1532
+ return null;
1533
+ }
1534
+ // @ts-ignore
1535
+ delete node.nam;
1536
+ // @ts-ignore
1537
+ delete node.val;
1538
+ Object.assign(node, {
1539
+ typ: EnumToken.CssVariableDeclarationMapTokenType,
1540
+ vars,
1541
+ from
1542
+ });
1543
+ context.chi.push(node);
1544
+ return null;
1545
+ }
1546
+ }
1547
+ }
1548
+ k++;
1549
+ }
1550
+ Object.assign(node, {
1551
+ typ: EnumToken.CssVariableTokenType,
1552
+ nam: tokens[i].val,
1553
+ val: tokens.slice(k)
1554
+ });
1555
+ context.chi.push(node);
1556
+ return null;
1557
+ }
1558
+ }
1559
+ }
775
1560
  // @ts-ignore
776
- const valid = options.validation == ValidationLevel.None ? {
1561
+ const skipValidate = (options.validation & ValidationLevel.AtRule) == 0;
1562
+ const isAllowed = skipValidate || isNodeAllowedInContext(node, context);
1563
+ // @ts-ignore
1564
+ const valid = skipValidate ? {
777
1565
  valid: SyntaxValidationResult.Valid,
778
1566
  error: '',
779
1567
  node,
780
1568
  syntax: '@' + node.nam
781
- } : isValid ? (node.typ == EnumToken.KeyframesAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
1569
+ } : !isAllowed ? {
1570
+ valid: SyntaxValidationResult.Drop,
1571
+ node,
1572
+ syntax: '@' + node.nam,
1573
+ error: `${EnumToken[context.typ]}: child ${EnumToken[node.typ]} not allowed in context${context.typ == EnumToken.AtRuleNodeType ? ` '@${context.nam}'` : context.typ == EnumToken.StyleSheetNodeType ? ` 'stylesheet'` : ''}`} : isValid ? (node.typ == EnumToken.KeyframesAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
782
1574
  valid: SyntaxValidationResult.Drop,
783
1575
  node,
784
1576
  syntax: '@' + node.nam,
785
1577
  error: '@' + node.nam + ' not allowed here'};
786
1578
  if (valid.valid == SyntaxValidationResult.Drop) {
1579
+ let message = '';
1580
+ for (const token of tokens) {
1581
+ message += renderToken(token, { minify: false });
1582
+ }
787
1583
  errors.push({
788
1584
  action: 'drop',
789
- message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
1585
+ message: valid.error + ' - "' + message + '"',
790
1586
  node,
791
1587
  // @ts-ignore
792
1588
  location: { src, ...(map.get(valid.node) ?? location) }
@@ -795,13 +1591,17 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
795
1591
  node.typ = EnumToken.InvalidAtRuleTokenType;
796
1592
  }
797
1593
  else {
798
- node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, {
799
- minify: false,
800
- convertColor: false,
801
- removeComments: true
802
- }), '');
1594
+ node.val = '';
1595
+ for (const token of node.tokens) {
1596
+ node.val += renderToken(token, {
1597
+ minify: false,
1598
+ convertColor: false,
1599
+ removeComments: true
1600
+ });
1601
+ }
803
1602
  }
804
1603
  context.chi.push(node);
1604
+ stats.nodesCount++;
805
1605
  Object.defineProperties(node, {
806
1606
  parent: { ...definedPropertySettings, value: context },
807
1607
  validSyntax: { ...definedPropertySettings, value: valid.valid == SyntaxValidationResult.Valid }
@@ -899,16 +1699,23 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
899
1699
  value: tokens.slice()
900
1700
  });
901
1701
  loc = location;
902
- if (options.sourcemap) {
903
- node.loc = loc;
904
- }
905
- // @ts-ignore
1702
+ Object.defineProperty(node, 'loc', {
1703
+ ...definedPropertySettings,
1704
+ value: loc,
1705
+ enumerable: options.sourcemap !== false
1706
+ });
906
1707
  context.chi.push(node);
907
1708
  Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
908
1709
  // @ts-ignore
909
- const valid = options.validation == ValidationLevel.None ? {
1710
+ const skipValidate = (options.validation & ValidationLevel.Selector) == 0;
1711
+ const isAllowed = skipValidate || isNodeAllowedInContext(node, context);
1712
+ // @ts-ignore
1713
+ const valid = skipValidate ? {
910
1714
  valid: SyntaxValidationResult.Valid,
911
1715
  error: null
1716
+ } : !isAllowed ? {
1717
+ valid: SyntaxValidationResult.Drop,
1718
+ error: `${EnumToken[context.typ]}: child ${EnumToken[node.typ]} not allowed in context${context.typ == EnumToken.AtRuleNodeType ? ` '@${context.nam}'` : context.typ == EnumToken.StyleSheetNodeType ? ` 'stylesheet'` : ''}`
912
1719
  } : ruleType == EnumToken.KeyFramesRuleNodeType ? validateKeyframeSelector(tokens) : validateSelector(tokens, options, context);
913
1720
  if (valid.valid != SyntaxValidationResult.Valid) {
914
1721
  // @ts-ignore
@@ -1018,11 +1825,13 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
1018
1825
  nam,
1019
1826
  val: []
1020
1827
  };
1021
- if (options.sourcemap) {
1022
- node.loc = location;
1023
- node.loc.end = { ...map.get(delim).end };
1024
- }
1828
+ Object.defineProperty(node, 'loc', {
1829
+ ...definedPropertySettings,
1830
+ value: location,
1831
+ enumerable: options.sourcemap !== false
1832
+ });
1025
1833
  context.chi.push(node);
1834
+ stats.nodesCount++;
1026
1835
  }
1027
1836
  return null;
1028
1837
  }
@@ -1046,22 +1855,31 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
1046
1855
  nam,
1047
1856
  val: value
1048
1857
  };
1049
- if (options.sourcemap) {
1050
- node.loc = location;
1051
- node.loc.end = { ...map.get(delim).end };
1052
- }
1858
+ Object.defineProperty(node, 'loc', {
1859
+ ...definedPropertySettings,
1860
+ value: location,
1861
+ enumerable: options.sourcemap !== false
1862
+ });
1863
+ node.loc.end = { ...map.get(delim).end };
1053
1864
  // do not allow declarations in style sheets
1054
1865
  if (context.typ == EnumToken.StyleSheetNodeType && options.lenient) {
1055
- // @ts-ignore
1056
- node.typ = EnumToken.InvalidDeclarationNodeType;
1866
+ Object.assign(node, { typ: EnumToken.InvalidDeclarationNodeType });
1057
1867
  context.chi.push(node);
1868
+ stats.nodesCount++;
1058
1869
  return null;
1059
1870
  }
1060
1871
  const result = parseDeclarationNode(node, errors, location);
1061
1872
  Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
1062
1873
  if (result != null) {
1063
- if (options.validation == ValidationLevel.All) {
1064
- const valid = evaluateSyntax(result, options);
1874
+ if (options.validation & ValidationLevel.Declaration) {
1875
+ const isAllowed = isNodeAllowedInContext(node, context);
1876
+ // @ts-ignore
1877
+ const valid = !isAllowed ? {
1878
+ valid: SyntaxValidationResult.Drop,
1879
+ error: `${EnumToken[node.typ]} not allowed in context${context.typ == EnumToken.AtRuleNodeType ? ` '@${context.nam}'` : context.typ == EnumToken.StyleSheetNodeType ? ` 'stylesheet'` : ''}`,
1880
+ node,
1881
+ syntax: null
1882
+ } : evaluateSyntax(result, context, options);
1065
1883
  Object.defineProperty(result, 'validSyntax', {
1066
1884
  ...definedPropertySettings,
1067
1885
  value: valid.valid == SyntaxValidationResult.Valid
@@ -1077,11 +1895,11 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
1077
1895
  if (!options.lenient) {
1078
1896
  return null;
1079
1897
  }
1080
- // @ts-ignore
1081
- node.typ = EnumToken.InvalidDeclarationNodeType;
1898
+ Object.assign(node, { typ: EnumToken.InvalidDeclarationNodeType });
1082
1899
  }
1083
1900
  }
1084
1901
  context.chi.push(result);
1902
+ stats.nodesCount++;
1085
1903
  }
1086
1904
  return null;
1087
1905
  }
@@ -1093,7 +1911,6 @@ function parseNode(results, context, options, errors, src, map, rawTokens) {
1093
1911
  * @param atRule
1094
1912
  */
1095
1913
  function parseAtRulePrelude(tokens, atRule) {
1096
- // @ts-ignore
1097
1914
  for (const { value, parent } of walkValues(tokens, null, null, true)) {
1098
1915
  if (value.typ == EnumToken.CommentTokenType ||
1099
1916
  value.typ == EnumToken.WhitespaceTokenType ||
@@ -1129,16 +1946,14 @@ function parseAtRulePrelude(tokens, atRule) {
1129
1946
  }
1130
1947
  if (atRule.val == 'page' && value.typ == EnumToken.PseudoClassTokenType) {
1131
1948
  if ([':left', ':right', ':first', ':blank'].includes(value.val)) {
1132
- // @ts-ignore
1133
- value.typ = EnumToken.PseudoPageTokenType;
1949
+ Object.assign(value, { typ: EnumToken.PseudoPageTokenType });
1134
1950
  }
1135
1951
  }
1136
1952
  if (atRule.val == 'layer') {
1137
1953
  if (parent == null && value.typ == EnumToken.LiteralTokenType) {
1138
1954
  if (value.val.charAt(0) == '.') {
1139
1955
  if (isIdent(value.val.slice(1))) {
1140
- // @ts-ignore
1141
- value.typ = EnumToken.ClassSelectorTokenType;
1956
+ Object.assign(value, { typ: EnumToken.ClassSelectorTokenType });
1142
1957
  }
1143
1958
  }
1144
1959
  }
@@ -1147,8 +1962,7 @@ function parseAtRulePrelude(tokens, atRule) {
1147
1962
  if (value.typ == EnumToken.IdenTokenType) {
1148
1963
  if (parent == null && mediaTypes.some((t) => {
1149
1964
  if (val === t) {
1150
- // @ts-ignore
1151
- value.typ = EnumToken.MediaFeatureTokenType;
1965
+ Object.assign(value, { typ: EnumToken.MediaFeatureTokenType });
1152
1966
  return true;
1153
1967
  }
1154
1968
  return false;
@@ -1156,18 +1970,15 @@ function parseAtRulePrelude(tokens, atRule) {
1156
1970
  continue;
1157
1971
  }
1158
1972
  if (value.typ == EnumToken.IdenTokenType && 'and' === val) {
1159
- // @ts-ignore
1160
- value.typ = EnumToken.MediaFeatureAndTokenType;
1973
+ Object.assign(value, { typ: EnumToken.MediaFeatureAndTokenType });
1161
1974
  continue;
1162
1975
  }
1163
1976
  if (value.typ == EnumToken.IdenTokenType && 'or' === val) {
1164
- // @ts-ignore
1165
- value.typ = EnumToken.MediaFeatureOrTokenType;
1977
+ Object.assign(value, { typ: EnumToken.MediaFeatureOrTokenType });
1166
1978
  continue;
1167
1979
  }
1168
1980
  if (value.typ == EnumToken.IdenTokenType &&
1169
1981
  ['not', 'only'].some((t) => val === t)) {
1170
- // @ts-ignore
1171
1982
  const array = parent?.chi ?? tokens;
1172
1983
  const startIndex = array.indexOf(value);
1173
1984
  let index = startIndex + 1;
@@ -1212,12 +2023,15 @@ function parseAtRulePrelude(tokens, atRule) {
1212
2023
  if (value.chi[i].typ == EnumToken.CommentTokenType || value.chi[i].typ == EnumToken.WhitespaceTokenType) {
1213
2024
  continue;
1214
2025
  }
1215
- if (value.chi[i].typ == EnumToken.LiteralTokenType && value.chi[i].val.startsWith(':') && isDimension(value.chi[i].val.slice(1))) {
1216
- value.chi.splice(i, 1, {
1217
- typ: EnumToken.ColonTokenType,
1218
- }, Object.assign(value.chi[i], parseDimension(value.chi[i].val.slice(1))));
1219
- i--;
1220
- continue;
2026
+ if (value.chi[i].typ == EnumToken.LiteralTokenType && value.chi[i].val.startsWith(':')) {
2027
+ const dimension = parseDimension(value.chi[i].val.slice(1));
2028
+ if (dimension != null) {
2029
+ value.chi.splice(i, 1, {
2030
+ typ: EnumToken.ColonTokenType,
2031
+ }, Object.assign(value.chi[i], dimension));
2032
+ i--;
2033
+ continue;
2034
+ }
1221
2035
  }
1222
2036
  if (nameIndex != -1 && value.chi[i].typ == EnumToken.PseudoClassTokenType) {
1223
2037
  value.chi.splice(i, 1, {
@@ -1244,11 +2058,10 @@ function parseAtRulePrelude(tokens, atRule) {
1244
2058
  const val = value.chi.splice(valueIndex, 1)[0];
1245
2059
  const node = value.chi.splice(nameIndex, 1)[0];
1246
2060
  // 'background'
1247
- // @ts-ignore
1248
2061
  if (node.typ == EnumToken.ColorTokenType && node.kin == ColorType.DPSYS) {
2062
+ Object.assign(node, { typ: EnumToken.IdenTokenType });
1249
2063
  // @ts-ignore
1250
2064
  delete node.kin;
1251
- node.typ = EnumToken.IdenTokenType;
1252
2065
  }
1253
2066
  while (value.chi[0]?.typ == EnumToken.WhitespaceTokenType) {
1254
2067
  value.chi.shift();
@@ -1282,6 +2095,7 @@ async function parseDeclarations(declaration) {
1282
2095
  return doParse(tokenize({
1283
2096
  stream: `.x{${declaration}}`,
1284
2097
  buffer: '',
2098
+ offset: 0,
1285
2099
  position: { ind: 0, lin: 1, col: 1 },
1286
2100
  currentPosition: { ind: -1, lin: 1, col: 0 }
1287
2101
  }), { setParent: false, minify: false, validation: false }).then(result => {
@@ -1303,43 +2117,35 @@ function parseSelector(tokens) {
1303
2117
  }
1304
2118
  if (parent == null) {
1305
2119
  if (value.typ == EnumToken.GtTokenType) {
1306
- // @ts-ignore
1307
- value.typ = EnumToken.ChildCombinatorTokenType;
2120
+ Object.assign(value, { typ: EnumToken.ChildCombinatorTokenType });
1308
2121
  }
1309
2122
  else if (value.typ == EnumToken.LiteralTokenType) {
1310
2123
  if (value.val.charAt(0) == '&') {
1311
- // @ts-ignore
1312
- value.typ = EnumToken.NestingSelectorTokenType;
2124
+ Object.assign(value, { typ: EnumToken.NestingSelectorTokenType });
1313
2125
  // @ts-ignore
1314
2126
  delete value.val;
1315
2127
  }
1316
2128
  else if (value.val.charAt(0) == '.') {
1317
2129
  if (!isIdent(value.val.slice(1))) {
1318
- // @ts-ignore
1319
- value.typ = EnumToken.InvalidClassSelectorTokenType;
2130
+ Object.assign(value, { typ: EnumToken.InvalidClassSelectorTokenType });
1320
2131
  }
1321
2132
  else {
1322
- // @ts-ignore
1323
- value.typ = EnumToken.ClassSelectorTokenType;
2133
+ Object.assign(value, { typ: EnumToken.ClassSelectorTokenType });
1324
2134
  }
1325
2135
  }
1326
2136
  if (['*', '>', '+', '~'].includes(value.val)) {
1327
2137
  switch (value.val) {
1328
2138
  case '*':
1329
- // @ts-ignore
1330
- value.typ = EnumToken.UniversalSelectorTokenType;
2139
+ Object.assign(value, { typ: EnumToken.UniversalSelectorTokenType });
1331
2140
  break;
1332
2141
  case '>':
1333
- // @ts-ignore
1334
- value.typ = EnumToken.ChildCombinatorTokenType;
2142
+ Object.assign(value, { typ: EnumToken.ChildCombinatorTokenType });
1335
2143
  break;
1336
2144
  case '+':
1337
- // @ts-ignore
1338
- value.typ = EnumToken.NextSiblingCombinatorTokenType;
2145
+ Object.assign(value, { typ: EnumToken.NextSiblingCombinatorTokenType });
1339
2146
  break;
1340
2147
  case '~':
1341
- // @ts-ignore
1342
- value.typ = EnumToken.SubsequentSiblingCombinatorTokenType;
2148
+ Object.assign(value, { typ: EnumToken.SubsequentSiblingCombinatorTokenType });
1343
2149
  break;
1344
2150
  }
1345
2151
  // @ts-ignore
@@ -1352,12 +2158,10 @@ function parseSelector(tokens) {
1352
2158
  if (!isIdent(value.val.slice(1))) {
1353
2159
  continue;
1354
2160
  }
1355
- // @ts-ignore
1356
- value.typ = EnumToken.HashTokenType;
2161
+ Object.assign(value, { typ: EnumToken.HashTokenType });
1357
2162
  }
1358
2163
  else {
1359
- // @ts-ignore
1360
- value.typ = EnumToken.IdenTokenType;
2164
+ Object.assign(value, { typ: EnumToken.IdenTokenType });
1361
2165
  }
1362
2166
  // @ts-ignore
1363
2167
  delete value.kin;
@@ -1412,6 +2216,7 @@ function parseString(src, options = { location: false }) {
1412
2216
  const parseInfo = {
1413
2217
  stream: src,
1414
2218
  buffer: '',
2219
+ offset: 0,
1415
2220
  position: { ind: 0, lin: 1, col: 1 },
1416
2221
  currentPosition: { ind: -1, lin: 1, col: 0 }
1417
2222
  };
@@ -1420,15 +2225,17 @@ function parseString(src, options = { location: false }) {
1420
2225
  return acc;
1421
2226
  }
1422
2227
  const token = getTokenType(t.token, t.hint);
1423
- if (options.location) {
1424
- Object.assign(token, { loc: t.sta });
1425
- }
2228
+ Object.defineProperty(token, 'loc', {
2229
+ ...definedPropertySettings,
2230
+ value: { sta: t.sta },
2231
+ enumerable: options.location !== false
2232
+ });
1426
2233
  acc.push(token);
1427
2234
  return acc;
1428
2235
  }, []));
1429
2236
  }
1430
2237
  /**
1431
- * get token type from a string
2238
+ * get the token type from a string
1432
2239
  * @param val
1433
2240
  * @param hint
1434
2241
  */
@@ -1464,7 +2271,7 @@ function getTokenType(val, hint) {
1464
2271
  case '>':
1465
2272
  return { typ: EnumToken.GtTokenType };
1466
2273
  }
1467
- if (isPseudo(val)) {
2274
+ if (val.charAt(0) == ':' && isPseudo(val)) {
1468
2275
  return val.endsWith('(') ? {
1469
2276
  typ: EnumToken.PseudoClassFuncTokenType,
1470
2277
  val: val.slice(0, -1),
@@ -1481,13 +2288,13 @@ function getTokenType(val, hint) {
1481
2288
  val
1482
2289
  });
1483
2290
  }
1484
- if (isAtKeyword(val)) {
2291
+ if (val.charAt(0) == '@' && isAtKeyword(val)) {
1485
2292
  return {
1486
2293
  typ: EnumToken.AtRuleTokenType,
1487
2294
  val: val.slice(1)
1488
2295
  };
1489
2296
  }
1490
- if (isFunction(val)) {
2297
+ if (val.endsWith('(') && isFunction(val)) {
1491
2298
  val = val.slice(0, -1);
1492
2299
  if (val == 'url') {
1493
2300
  return {
@@ -1535,14 +2342,9 @@ function getTokenType(val, hint) {
1535
2342
  val: +val.slice(0, -1)
1536
2343
  };
1537
2344
  }
1538
- if (isFlex(val)) {
1539
- return {
1540
- typ: EnumToken.FlexTokenType,
1541
- val: +val.slice(0, -2)
1542
- };
1543
- }
1544
- if (isDimension(val)) {
1545
- return parseDimension(val);
2345
+ const dimension = parseDimension(val);
2346
+ if (dimension != null) {
2347
+ return dimension;
1546
2348
  }
1547
2349
  const v = val.toLowerCase();
1548
2350
  if (v == 'currentcolor' || v == 'transparent' || v in COLORS_NAMES) {
@@ -1572,6 +2374,12 @@ function getTokenType(val, hint) {
1572
2374
  val
1573
2375
  };
1574
2376
  }
2377
+ if (val.charAt(0) == '.' && isIdent(val.slice(1))) {
2378
+ return {
2379
+ typ: EnumToken.ClassSelectorTokenType,
2380
+ val
2381
+ };
2382
+ }
1575
2383
  if (val.charAt(0) == '#' && isHexColor(val)) {
1576
2384
  return {
1577
2385
  typ: EnumToken.ColorTokenType,
@@ -1619,6 +2427,55 @@ function getTokenType(val, hint) {
1619
2427
  function parseTokens(tokens, options = {}) {
1620
2428
  for (let i = 0; i < tokens.length; i++) {
1621
2429
  const t = tokens[i];
2430
+ if (t.typ == EnumToken.IdenTokenType && t.val == 'from' && i > 0) {
2431
+ const left = [];
2432
+ const right = [];
2433
+ let foundLeft = 0;
2434
+ let foundRight = 0;
2435
+ let k = i;
2436
+ let l = i;
2437
+ while (k > 0) {
2438
+ if (tokens[k - 1].typ == EnumToken.CommentTokenType || tokens[k - 1].typ == EnumToken.WhitespaceTokenType) {
2439
+ left.push(tokens[--k]);
2440
+ continue;
2441
+ }
2442
+ if (tokens[k - 1].typ == EnumToken.IdenTokenType || tokens[k - 1].typ == EnumToken.DashedIdenTokenType) {
2443
+ foundLeft++;
2444
+ left.push(tokens[--k]);
2445
+ continue;
2446
+ }
2447
+ break;
2448
+ }
2449
+ while (++l < tokens.length) {
2450
+ if (tokens[l].typ == EnumToken.CommentTokenType || tokens[l].typ == EnumToken.WhitespaceTokenType) {
2451
+ right.push(tokens[l]);
2452
+ continue;
2453
+ }
2454
+ if (tokens[l].typ == EnumToken.IdenTokenType || tokens[l].typ == EnumToken.StringTokenType) {
2455
+ foundRight++;
2456
+ right.push(tokens[l]);
2457
+ continue;
2458
+ }
2459
+ break;
2460
+ }
2461
+ if (foundLeft > 0 && foundRight == 1) {
2462
+ while (left?.[0].typ == EnumToken.WhitespaceTokenType) {
2463
+ left.shift();
2464
+ }
2465
+ while (left.at(-1)?.typ == EnumToken.WhitespaceTokenType) {
2466
+ left.pop();
2467
+ }
2468
+ tokens.splice(k, l - k + 1, {
2469
+ typ: EnumToken.ComposesSelectorNodeType,
2470
+ l: left,
2471
+ r: right.reduce((a, b) => {
2472
+ return a == null ? b : b.typ == EnumToken.IdenTokenType || b.typ == EnumToken.StringTokenType ? b : a;
2473
+ }, null)
2474
+ });
2475
+ i = k;
2476
+ continue;
2477
+ }
2478
+ }
1622
2479
  if (t.typ == EnumToken.WhitespaceTokenType && ((i == 0 ||
1623
2480
  i + 1 == tokens.length ||
1624
2481
  [EnumToken.CommaTokenType, EnumToken.GteTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType].includes(tokens[i + 1].typ)) ||
@@ -1902,4 +2759,4 @@ function parseTokens(tokens, options = {}) {
1902
2759
  return tokens;
1903
2760
  }
1904
2761
 
1905
- export { doParse, getTokenType, parseAtRulePrelude, parseDeclarations, parseSelector, parseString, parseTokens, replaceToken, urlTokenMatcher };
2762
+ export { doParse, generateScopedName, getKeyName, getTokenType, parseAtRulePrelude, parseDeclarations, parseSelector, parseString, parseTokens, replaceToken, urlTokenMatcher };