@tbela99/css-parser 0.5.1 → 0.5.3

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![npm](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftbela99%2Fcss-parser%2Fmaster%2Fpackage.json&query=version&logo=npm&label=npm&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40tbela99%2Fcss-parser)](https://www.npmjs.com/package/@tbela99/css-parser) [![cov](https://tbela99.github.io/css-parser/badges/coverage.svg)](https://github.com/tbela99/css-parser/actions) [![NPM Downloads](https://img.shields.io/npm/dy/%40tbela99%2Fcss-parser)](https://www.npmjs.com/package/@tbela99/css-parser)
1
+ [![npm](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Ftbela99%2Fcss-parser%2Fmaster%2Fpackage.json&query=version&logo=npm&label=npm&link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2F%40tbela99%2Fcss-parser)](https://www.npmjs.com/package/@tbela99/css-parser) [![cov](https://tbela99.github.io/css-parser/badges/coverage.svg)](https://github.com/tbela99/css-parser/actions) [![NPM Downloads](https://img.shields.io/npm/dm/%40tbela99%2Fcss-parser)](https://www.npmjs.com/package/@tbela99/css-parser)
2
2
 
3
3
  # css-parser
4
4
 
@@ -497,30 +497,36 @@ for (const {node, parent, root} of walk(ast)) {
497
497
 
498
498
  ### Comment
499
499
 
500
- - typ: string 'Comment'
500
+ - typ: number
501
501
  - val: string, the comment
502
502
 
503
503
  ### Declaration
504
504
 
505
- - typ: string 'Declaration'
505
+ - typ: number
506
506
  - nam: string, declaration name
507
507
  - val: array of tokens
508
508
 
509
509
  ### Rule
510
510
 
511
- - typ: string 'Rule'
511
+ - typ: number
512
512
  - sel: string, css selector
513
513
  - chi: array of children
514
514
 
515
515
  ### AtRule
516
516
 
517
- - typ: string 'AtRule'
517
+ - typ: number
518
518
  - nam: string. AtRule name
519
519
  - val: rule prelude
520
520
 
521
521
  ### AtRuleStyleSheet
522
522
 
523
- - typ: string 'Stylesheet'
523
+ - typ: number
524
+ - chi: array of children
525
+
526
+ ### KeyFrameRule
527
+
528
+ - typ: number
529
+ - sel: string, css selector
524
530
  - chi: array of children
525
531
 
526
532
  ## Sourcemap
@@ -81,6 +81,7 @@
81
81
  EnumToken[EnumToken["FractionTokenType"] = 69] = "FractionTokenType";
82
82
  EnumToken[EnumToken["IdenListTokenType"] = 70] = "IdenListTokenType";
83
83
  EnumToken[EnumToken["GridTemplateFuncTokenType"] = 71] = "GridTemplateFuncTokenType";
84
+ EnumToken[EnumToken["KeyFrameRuleNodeType"] = 72] = "KeyFrameRuleNodeType";
84
85
  /* aliases */
85
86
  EnumToken[EnumToken["Time"] = 25] = "Time";
86
87
  EnumToken[EnumToken["Iden"] = 7] = "Iden";
@@ -1480,7 +1481,7 @@
1480
1481
  return val / 4.5;
1481
1482
  }
1482
1483
  return sign * (Math.pow((abs + α - 1) / α, 1 / 0.45));
1483
- }).concat(a == null || a == 1 ? [] : [a]);
1484
+ }).concat([] );
1484
1485
  }
1485
1486
  function lrec20202rec2020(r, g, b, a) {
1486
1487
  // convert an array of linear-light rec2020 RGB in the range 0.0-1.0
@@ -2510,7 +2511,15 @@
2510
2511
  * @param tokens
2511
2512
  */
2512
2513
  function evaluate(tokens) {
2513
- const nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
2514
+ let nodes;
2515
+ try {
2516
+ nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
2517
+ }
2518
+ catch (e) {
2519
+ // console.error({tokens});
2520
+ // console.error(e);
2521
+ return tokens;
2522
+ }
2514
2523
  if (nodes.length <= 1) {
2515
2524
  return nodes;
2516
2525
  }
@@ -3036,7 +3045,7 @@
3036
3045
  return result;
3037
3046
  }
3038
3047
  function updateSourceMap(node, options, cache, sourcemap, position, str) {
3039
- if ([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType].includes(node.typ)) {
3048
+ if ([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyFrameRuleNodeType].includes(node.typ)) {
3040
3049
  let src = node.loc?.src ?? '';
3041
3050
  let output = options.output ?? '';
3042
3051
  if (!(src in cache)) {
@@ -3096,6 +3105,7 @@
3096
3105
  }, '');
3097
3106
  case exports.EnumToken.AtRuleNodeType:
3098
3107
  case exports.EnumToken.RuleNodeType:
3108
+ case exports.EnumToken.KeyFrameRuleNodeType:
3099
3109
  if (data.typ == exports.EnumToken.AtRuleNodeType && !('chi' in data)) {
3100
3110
  return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
3101
3111
  }
@@ -6446,6 +6456,9 @@
6446
6456
  const position = map.get(tokens[0]);
6447
6457
  const uniq = new Map;
6448
6458
  parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
6459
+ if (curr.typ == exports.EnumToken.CommentTokenType) {
6460
+ return acc;
6461
+ }
6449
6462
  if (curr.typ == exports.EnumToken.WhitespaceTokenType) {
6450
6463
  if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
6451
6464
  trimWhiteSpace.includes(array[index + 1]?.typ) ||
@@ -6467,7 +6480,7 @@
6467
6480
  return acc;
6468
6481
  }, uniq);
6469
6482
  const node = {
6470
- typ: exports.EnumToken.RuleNodeType,
6483
+ typ: context.typ == exports.EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? exports.EnumToken.KeyFrameRuleNodeType : exports.EnumToken.RuleNodeType,
6471
6484
  // @ts-ignore
6472
6485
  sel: [...uniq.keys()].join(','),
6473
6486
  chi: []
@@ -7251,7 +7264,8 @@
7251
7264
  for (const t of walkValues(tokens)) {
7252
7265
  if (t.value.typ == exports.EnumToken.LiteralTokenType) {
7253
7266
  if (t.value.val == '&') {
7254
- t.value.val = replace;
7267
+ const rule = splitRule(replace);
7268
+ t.value.val = rule.length > 1 ? ':is(' + replace + ')' : replace;
7255
7269
  }
7256
7270
  else if (t.value.val.length > 1 && t.value.val.charAt(0) == '&') {
7257
7271
  t.value.val = replaceCompoundLiteral(t.value.val, replace);
@@ -8406,7 +8420,7 @@
8406
8420
  const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering);
8407
8421
  function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) {
8408
8422
  if (!('nodes' in context)) {
8409
- context.nodes = new WeakSet;
8423
+ context.nodes = new Set;
8410
8424
  }
8411
8425
  if (context.nodes.has(ast)) {
8412
8426
  return ast;
@@ -8435,7 +8449,7 @@
8435
8449
  curr.splice(0, 2);
8436
8450
  }
8437
8451
  else if (combinators.includes(curr[1])) {
8438
- curr.splice(0, 1);
8452
+ curr.shift();
8439
8453
  }
8440
8454
  }
8441
8455
  else if (ast.typ == exports.EnumToken.RuleNodeType && (isIdent(curr[0]) || isFunction(curr[0]))) {
@@ -8637,7 +8651,7 @@
8637
8651
  }
8638
8652
  if (shouldMerge) {
8639
8653
  // @ts-ignore
8640
- if ((node.typ == exports.EnumToken.RuleNodeType && node.sel == previous.sel) ||
8654
+ if (((node.typ == exports.EnumToken.RuleNodeType || node.typ == exports.EnumToken.KeyFrameRuleNodeType) && node.sel == previous.sel) ||
8641
8655
  // @ts-ignore
8642
8656
  (node.typ == exports.EnumToken.AtRuleNodeType) && node.val != 'font-face' && node.val == previous.val) {
8643
8657
  // @ts-ignore
@@ -8656,32 +8670,33 @@
8656
8670
  nodeIndex = i;
8657
8671
  continue;
8658
8672
  }
8659
- else if (node.typ == exports.EnumToken.RuleNodeType && previous?.typ == exports.EnumToken.RuleNodeType) {
8673
+ else if (node.typ == previous?.typ && [exports.EnumToken.KeyFrameRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) {
8660
8674
  const intersect = diff(previous, node, reducer, options);
8661
8675
  if (intersect != null) {
8662
8676
  if (intersect.node1.chi.length == 0) {
8663
8677
  // @ts-ignore
8664
8678
  ast.chi.splice(i--, 1);
8665
8679
  // @ts-ignore
8666
- node = ast.chi[i];
8680
+ // node = ast.chi[i];
8667
8681
  }
8668
8682
  else {
8669
8683
  // @ts-ignore
8670
8684
  ast.chi.splice(i, 1, intersect.node1);
8671
- node = intersect.node1;
8685
+ // node = ast.chi intersect.node1;
8672
8686
  }
8673
8687
  if (intersect.node2.chi.length == 0) {
8674
8688
  // @ts-ignore
8675
8689
  ast.chi.splice(nodeIndex, 1, intersect.result);
8676
- previous = intersect.result;
8677
8690
  }
8678
8691
  else {
8679
8692
  // @ts-ignore
8680
8693
  ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2);
8681
- previous = intersect.result;
8682
8694
  // @ts-ignore
8683
- i = nodeIndex;
8695
+ i = (nodeIndex ?? 0) + 1;
8684
8696
  }
8697
+ reduceRuleSelector(intersect.result);
8698
+ previous = intersect.result;
8699
+ nodeIndex = i;
8685
8700
  }
8686
8701
  }
8687
8702
  }
@@ -8811,11 +8826,16 @@
8811
8826
  // combinator
8812
8827
  if (combinators.includes(optimized.at(-1))) {
8813
8828
  const combinator = optimized.pop();
8814
- selector.forEach(selector => selector.unshift(combinator));
8829
+ selector.forEach((selector) => selector.unshift(combinator));
8815
8830
  }
8816
8831
  let reducible = optimized.length == 1;
8817
- if (optimized[0] == '&' && optimized[1] == ' ') {
8818
- optimized.splice(0, 2);
8832
+ if (optimized[0] == '&') {
8833
+ if (optimized[1] == ' ') {
8834
+ optimized.splice(0, 2);
8835
+ }
8836
+ // else if (combinators.includes(optimized[1])) {
8837
+ //
8838
+ // }
8819
8839
  }
8820
8840
  if (optimized.length == 0 ||
8821
8841
  (optimized[0].charAt(0) == '&' ||
@@ -8823,7 +8843,7 @@
8823
8843
  return {
8824
8844
  match: false,
8825
8845
  optimized,
8826
- selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
8846
+ selector: selector.map((selector) => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : (selector)),
8827
8847
  reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
8828
8848
  };
8829
8849
  }
@@ -8903,6 +8923,18 @@
8903
8923
  result.push([]);
8904
8924
  continue;
8905
8925
  }
8926
+ if (chr == ':') {
8927
+ if (str !== '') {
8928
+ // @ts-ignore
8929
+ result.at(-1).push(str);
8930
+ str = '';
8931
+ }
8932
+ if (buffer.charAt(i + 1) == ':') {
8933
+ chr += buffer.charAt(++i);
8934
+ }
8935
+ str += chr;
8936
+ continue;
8937
+ }
8906
8938
  str += chr;
8907
8939
  if (chr == '\\') {
8908
8940
  str += buffer.charAt(++i);
@@ -9204,6 +9236,52 @@
9204
9236
  const raw1 = node1.raw;
9205
9237
  // @ts-ignore
9206
9238
  const raw2 = node2.raw;
9239
+ if (raw1 != null && raw2 != null) {
9240
+ const prefixes1 = new Set;
9241
+ const prefixes2 = new Set;
9242
+ for (const token1 of raw1) {
9243
+ for (const t of token1) {
9244
+ if (t[0] == ':') {
9245
+ const matches = t.match(/::?-([a-z]+)-/);
9246
+ if (matches == null) {
9247
+ continue;
9248
+ }
9249
+ prefixes1.add(matches[1]);
9250
+ if (prefixes1.size > 1) {
9251
+ break;
9252
+ }
9253
+ }
9254
+ }
9255
+ if (prefixes1.size > 1) {
9256
+ break;
9257
+ }
9258
+ }
9259
+ for (const token2 of raw2) {
9260
+ for (const t of token2) {
9261
+ if (t[0] == ':') {
9262
+ const matches = t.match(/::?-([a-z]+)-/);
9263
+ if (matches == null) {
9264
+ continue;
9265
+ }
9266
+ prefixes2.add(matches[1]);
9267
+ if (prefixes2.size > 1) {
9268
+ break;
9269
+ }
9270
+ }
9271
+ }
9272
+ if (prefixes2.size > 1) {
9273
+ break;
9274
+ }
9275
+ }
9276
+ if (prefixes1.size != prefixes2.size) {
9277
+ return null;
9278
+ }
9279
+ for (const prefix of prefixes1) {
9280
+ if (!prefixes2.has(prefix)) {
9281
+ return null;
9282
+ }
9283
+ }
9284
+ }
9207
9285
  // @ts-ignore
9208
9286
  node1 = { ...node1, chi: node1.chi.slice() };
9209
9287
  node2 = { ...node2, chi: node2.chi.slice() };
@@ -9240,7 +9318,7 @@
9240
9318
  const result = (intersect.length == 0 ? null : {
9241
9319
  ...node1,
9242
9320
  // @ts-ignore
9243
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
9321
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) ?? splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) ?? splitRule(n2.sel))])].join(','),
9244
9322
  chi: intersect.reverse()
9245
9323
  });
9246
9324
  if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0)) {
@@ -9264,6 +9342,11 @@
9264
9342
  Object.defineProperty(node, 'optimized', { ...definedPropertySettings, value: optimized });
9265
9343
  }
9266
9344
  if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
9345
+ for (const selector of optimized.selector) {
9346
+ if (selector.length > 1 && selector[0] == '&' && combinators.includes(selector[1])) {
9347
+ selector.shift();
9348
+ }
9349
+ }
9267
9350
  const raw = [
9268
9351
  [
9269
9352
  optimized.optimized[0], ':is('
@@ -9391,16 +9474,18 @@
9391
9474
  return response.text();
9392
9475
  }
9393
9476
  async function load(url, currentFile) {
9477
+ let t;
9394
9478
  if (matchUrl.test(url)) {
9395
- return fetch(url).then(parseResponse);
9479
+ t = new URL(url);
9396
9480
  }
9397
- if (matchUrl.test(currentFile)) {
9398
- return fetch(new URL(url, currentFile)).then(parseResponse);
9481
+ else if (matchUrl.test(currentFile)) {
9482
+ t = new URL(url, currentFile);
9483
+ }
9484
+ else {
9485
+ const path = resolve(url, currentFile).absolute;
9486
+ t = new URL(path, self.origin);
9399
9487
  }
9400
- const path = resolve(url, currentFile).absolute;
9401
- const t = new URL(path, self.origin);
9402
- // return fetch(new URL(url, new URL(currentFile, self.location.href).href)).then(parseResponse);
9403
- return fetch(url, t.origin != self.location.origin ? { mode: 'cors' } : {}).then(parseResponse);
9488
+ return fetch(t, t.origin != self.origin ? { mode: 'cors' } : {}).then(parseResponse);
9404
9489
  }
9405
9490
 
9406
9491
  function render(data, options = {}) {
package/dist/index.cjs CHANGED
@@ -79,6 +79,7 @@ exports.EnumToken = void 0;
79
79
  EnumToken[EnumToken["FractionTokenType"] = 69] = "FractionTokenType";
80
80
  EnumToken[EnumToken["IdenListTokenType"] = 70] = "IdenListTokenType";
81
81
  EnumToken[EnumToken["GridTemplateFuncTokenType"] = 71] = "GridTemplateFuncTokenType";
82
+ EnumToken[EnumToken["KeyFrameRuleNodeType"] = 72] = "KeyFrameRuleNodeType";
82
83
  /* aliases */
83
84
  EnumToken[EnumToken["Time"] = 25] = "Time";
84
85
  EnumToken[EnumToken["Iden"] = 7] = "Iden";
@@ -1478,7 +1479,7 @@ function rec20202lrec2020(r, g, b, a) {
1478
1479
  return val / 4.5;
1479
1480
  }
1480
1481
  return sign * (Math.pow((abs + α - 1) / α, 1 / 0.45));
1481
- }).concat(a == null || a == 1 ? [] : [a]);
1482
+ }).concat([] );
1482
1483
  }
1483
1484
  function lrec20202rec2020(r, g, b, a) {
1484
1485
  // convert an array of linear-light rec2020 RGB in the range 0.0-1.0
@@ -2508,7 +2509,15 @@ function simplify(a, b) {
2508
2509
  * @param tokens
2509
2510
  */
2510
2511
  function evaluate(tokens) {
2511
- const nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
2512
+ let nodes;
2513
+ try {
2514
+ nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
2515
+ }
2516
+ catch (e) {
2517
+ // console.error({tokens});
2518
+ // console.error(e);
2519
+ return tokens;
2520
+ }
2512
2521
  if (nodes.length <= 1) {
2513
2522
  return nodes;
2514
2523
  }
@@ -3034,7 +3043,7 @@ function doRender(data, options = {}) {
3034
3043
  return result;
3035
3044
  }
3036
3045
  function updateSourceMap(node, options, cache, sourcemap, position, str) {
3037
- if ([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType].includes(node.typ)) {
3046
+ if ([exports.EnumToken.RuleNodeType, exports.EnumToken.AtRuleNodeType, exports.EnumToken.KeyFrameRuleNodeType].includes(node.typ)) {
3038
3047
  let src = node.loc?.src ?? '';
3039
3048
  let output = options.output ?? '';
3040
3049
  if (!(src in cache)) {
@@ -3094,6 +3103,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
3094
3103
  }, '');
3095
3104
  case exports.EnumToken.AtRuleNodeType:
3096
3105
  case exports.EnumToken.RuleNodeType:
3106
+ case exports.EnumToken.KeyFrameRuleNodeType:
3097
3107
  if (data.typ == exports.EnumToken.AtRuleNodeType && !('chi' in data)) {
3098
3108
  return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
3099
3109
  }
@@ -6444,6 +6454,9 @@ async function parseNode(results, context, stats, options, errors, src, map) {
6444
6454
  const position = map.get(tokens[0]);
6445
6455
  const uniq = new Map;
6446
6456
  parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
6457
+ if (curr.typ == exports.EnumToken.CommentTokenType) {
6458
+ return acc;
6459
+ }
6447
6460
  if (curr.typ == exports.EnumToken.WhitespaceTokenType) {
6448
6461
  if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
6449
6462
  trimWhiteSpace.includes(array[index + 1]?.typ) ||
@@ -6465,7 +6478,7 @@ async function parseNode(results, context, stats, options, errors, src, map) {
6465
6478
  return acc;
6466
6479
  }, uniq);
6467
6480
  const node = {
6468
- typ: exports.EnumToken.RuleNodeType,
6481
+ typ: context.typ == exports.EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? exports.EnumToken.KeyFrameRuleNodeType : exports.EnumToken.RuleNodeType,
6469
6482
  // @ts-ignore
6470
6483
  sel: [...uniq.keys()].join(','),
6471
6484
  chi: []
@@ -7249,7 +7262,8 @@ function replaceCompound(input, replace) {
7249
7262
  for (const t of walkValues(tokens)) {
7250
7263
  if (t.value.typ == exports.EnumToken.LiteralTokenType) {
7251
7264
  if (t.value.val == '&') {
7252
- t.value.val = replace;
7265
+ const rule = splitRule(replace);
7266
+ t.value.val = rule.length > 1 ? ':is(' + replace + ')' : replace;
7253
7267
  }
7254
7268
  else if (t.value.val.length > 1 && t.value.val.charAt(0) == '&') {
7255
7269
  t.value.val = replaceCompoundLiteral(t.value.val, replace);
@@ -8404,7 +8418,7 @@ const notEndingWith = ['(', '['].concat(combinators);
8404
8418
  const features = Object.values(allFeatures).sort((a, b) => a.ordering - b.ordering);
8405
8419
  function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) {
8406
8420
  if (!('nodes' in context)) {
8407
- context.nodes = new WeakSet;
8421
+ context.nodes = new Set;
8408
8422
  }
8409
8423
  if (context.nodes.has(ast)) {
8410
8424
  return ast;
@@ -8433,7 +8447,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
8433
8447
  curr.splice(0, 2);
8434
8448
  }
8435
8449
  else if (combinators.includes(curr[1])) {
8436
- curr.splice(0, 1);
8450
+ curr.shift();
8437
8451
  }
8438
8452
  }
8439
8453
  else if (ast.typ == exports.EnumToken.RuleNodeType && (isIdent(curr[0]) || isFunction(curr[0]))) {
@@ -8635,7 +8649,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
8635
8649
  }
8636
8650
  if (shouldMerge) {
8637
8651
  // @ts-ignore
8638
- if ((node.typ == exports.EnumToken.RuleNodeType && node.sel == previous.sel) ||
8652
+ if (((node.typ == exports.EnumToken.RuleNodeType || node.typ == exports.EnumToken.KeyFrameRuleNodeType) && node.sel == previous.sel) ||
8639
8653
  // @ts-ignore
8640
8654
  (node.typ == exports.EnumToken.AtRuleNodeType) && node.val != 'font-face' && node.val == previous.val) {
8641
8655
  // @ts-ignore
@@ -8654,32 +8668,33 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
8654
8668
  nodeIndex = i;
8655
8669
  continue;
8656
8670
  }
8657
- else if (node.typ == exports.EnumToken.RuleNodeType && previous?.typ == exports.EnumToken.RuleNodeType) {
8671
+ else if (node.typ == previous?.typ && [exports.EnumToken.KeyFrameRuleNodeType, exports.EnumToken.RuleNodeType].includes(node.typ)) {
8658
8672
  const intersect = diff(previous, node, reducer, options);
8659
8673
  if (intersect != null) {
8660
8674
  if (intersect.node1.chi.length == 0) {
8661
8675
  // @ts-ignore
8662
8676
  ast.chi.splice(i--, 1);
8663
8677
  // @ts-ignore
8664
- node = ast.chi[i];
8678
+ // node = ast.chi[i];
8665
8679
  }
8666
8680
  else {
8667
8681
  // @ts-ignore
8668
8682
  ast.chi.splice(i, 1, intersect.node1);
8669
- node = intersect.node1;
8683
+ // node = ast.chi intersect.node1;
8670
8684
  }
8671
8685
  if (intersect.node2.chi.length == 0) {
8672
8686
  // @ts-ignore
8673
8687
  ast.chi.splice(nodeIndex, 1, intersect.result);
8674
- previous = intersect.result;
8675
8688
  }
8676
8689
  else {
8677
8690
  // @ts-ignore
8678
8691
  ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2);
8679
- previous = intersect.result;
8680
8692
  // @ts-ignore
8681
- i = nodeIndex;
8693
+ i = (nodeIndex ?? 0) + 1;
8682
8694
  }
8695
+ reduceRuleSelector(intersect.result);
8696
+ previous = intersect.result;
8697
+ nodeIndex = i;
8683
8698
  }
8684
8699
  }
8685
8700
  }
@@ -8809,11 +8824,16 @@ function reduceSelector(selector) {
8809
8824
  // combinator
8810
8825
  if (combinators.includes(optimized.at(-1))) {
8811
8826
  const combinator = optimized.pop();
8812
- selector.forEach(selector => selector.unshift(combinator));
8827
+ selector.forEach((selector) => selector.unshift(combinator));
8813
8828
  }
8814
8829
  let reducible = optimized.length == 1;
8815
- if (optimized[0] == '&' && optimized[1] == ' ') {
8816
- optimized.splice(0, 2);
8830
+ if (optimized[0] == '&') {
8831
+ if (optimized[1] == ' ') {
8832
+ optimized.splice(0, 2);
8833
+ }
8834
+ // else if (combinators.includes(optimized[1])) {
8835
+ //
8836
+ // }
8817
8837
  }
8818
8838
  if (optimized.length == 0 ||
8819
8839
  (optimized[0].charAt(0) == '&' ||
@@ -8821,7 +8841,7 @@ function reduceSelector(selector) {
8821
8841
  return {
8822
8842
  match: false,
8823
8843
  optimized,
8824
- selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
8844
+ selector: selector.map((selector) => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : (selector)),
8825
8845
  reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
8826
8846
  };
8827
8847
  }
@@ -8901,6 +8921,18 @@ function splitRule(buffer) {
8901
8921
  result.push([]);
8902
8922
  continue;
8903
8923
  }
8924
+ if (chr == ':') {
8925
+ if (str !== '') {
8926
+ // @ts-ignore
8927
+ result.at(-1).push(str);
8928
+ str = '';
8929
+ }
8930
+ if (buffer.charAt(i + 1) == ':') {
8931
+ chr += buffer.charAt(++i);
8932
+ }
8933
+ str += chr;
8934
+ continue;
8935
+ }
8904
8936
  str += chr;
8905
8937
  if (chr == '\\') {
8906
8938
  str += buffer.charAt(++i);
@@ -9202,6 +9234,52 @@ function diff(n1, n2, reducer, options = {}) {
9202
9234
  const raw1 = node1.raw;
9203
9235
  // @ts-ignore
9204
9236
  const raw2 = node2.raw;
9237
+ if (raw1 != null && raw2 != null) {
9238
+ const prefixes1 = new Set;
9239
+ const prefixes2 = new Set;
9240
+ for (const token1 of raw1) {
9241
+ for (const t of token1) {
9242
+ if (t[0] == ':') {
9243
+ const matches = t.match(/::?-([a-z]+)-/);
9244
+ if (matches == null) {
9245
+ continue;
9246
+ }
9247
+ prefixes1.add(matches[1]);
9248
+ if (prefixes1.size > 1) {
9249
+ break;
9250
+ }
9251
+ }
9252
+ }
9253
+ if (prefixes1.size > 1) {
9254
+ break;
9255
+ }
9256
+ }
9257
+ for (const token2 of raw2) {
9258
+ for (const t of token2) {
9259
+ if (t[0] == ':') {
9260
+ const matches = t.match(/::?-([a-z]+)-/);
9261
+ if (matches == null) {
9262
+ continue;
9263
+ }
9264
+ prefixes2.add(matches[1]);
9265
+ if (prefixes2.size > 1) {
9266
+ break;
9267
+ }
9268
+ }
9269
+ }
9270
+ if (prefixes2.size > 1) {
9271
+ break;
9272
+ }
9273
+ }
9274
+ if (prefixes1.size != prefixes2.size) {
9275
+ return null;
9276
+ }
9277
+ for (const prefix of prefixes1) {
9278
+ if (!prefixes2.has(prefix)) {
9279
+ return null;
9280
+ }
9281
+ }
9282
+ }
9205
9283
  // @ts-ignore
9206
9284
  node1 = { ...node1, chi: node1.chi.slice() };
9207
9285
  node2 = { ...node2, chi: node2.chi.slice() };
@@ -9238,7 +9316,7 @@ function diff(n1, n2, reducer, options = {}) {
9238
9316
  const result = (intersect.length == 0 ? null : {
9239
9317
  ...node1,
9240
9318
  // @ts-ignore
9241
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
9319
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) ?? splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) ?? splitRule(n2.sel))])].join(','),
9242
9320
  chi: intersect.reverse()
9243
9321
  });
9244
9322
  if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0)) {
@@ -9262,6 +9340,11 @@ function reduceRuleSelector(node) {
9262
9340
  Object.defineProperty(node, 'optimized', { ...definedPropertySettings, value: optimized });
9263
9341
  }
9264
9342
  if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
9343
+ for (const selector of optimized.selector) {
9344
+ if (selector.length > 1 && selector[0] == '&' && combinators.includes(selector[1])) {
9345
+ selector.shift();
9346
+ }
9347
+ }
9265
9348
  const raw = [
9266
9349
  [
9267
9350
  optimized.optimized[0], ':is('
package/dist/index.d.ts CHANGED
@@ -71,6 +71,7 @@ declare enum EnumToken {
71
71
  FractionTokenType = 69,
72
72
  IdenListTokenType = 70,
73
73
  GridTemplateFuncTokenType = 71,
74
+ KeyFrameRuleNodeType = 72,
74
75
  Time = 25,
75
76
  Iden = 7,
76
77
  EOF = 47,
@@ -664,6 +665,7 @@ export declare interface AstDeclaration extends Node {
664
665
  typ: EnumToken.DeclarationNodeType
665
666
  }
666
667
 
668
+
667
669
  export declare interface AstRule extends Node {
668
670
 
669
671
  typ: EnumToken.RuleNodeType;
@@ -121,7 +121,8 @@ function replaceCompound(input, replace) {
121
121
  for (const t of walkValues(tokens)) {
122
122
  if (t.value.typ == EnumToken.LiteralTokenType) {
123
123
  if (t.value.val == '&') {
124
- t.value.val = replace;
124
+ const rule = splitRule(replace);
125
+ t.value.val = rule.length > 1 ? ':is(' + replace + ')' : replace;
125
126
  }
126
127
  else if (t.value.val.length > 1 && t.value.val.charAt(0) == '&') {
127
128
  t.value.val = replaceCompoundLiteral(t.value.val, replace);
@@ -7,7 +7,15 @@ import { reduceNumber } from '../../renderer/render.js';
7
7
  * @param tokens
8
8
  */
9
9
  function evaluate(tokens) {
10
- const nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
10
+ let nodes;
11
+ try {
12
+ nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
13
+ }
14
+ catch (e) {
15
+ // console.error({tokens});
16
+ // console.error(e);
17
+ return tokens;
18
+ }
11
19
  if (nodes.length <= 1) {
12
20
  return nodes;
13
21
  }
@@ -14,7 +14,7 @@ const notEndingWith = ['(', '['].concat(combinators);
14
14
  const features = Object.values(index).sort((a, b) => a.ordering - b.ordering);
15
15
  function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) {
16
16
  if (!('nodes' in context)) {
17
- context.nodes = new WeakSet;
17
+ context.nodes = new Set;
18
18
  }
19
19
  if (context.nodes.has(ast)) {
20
20
  return ast;
@@ -43,7 +43,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
43
43
  curr.splice(0, 2);
44
44
  }
45
45
  else if (combinators.includes(curr[1])) {
46
- curr.splice(0, 1);
46
+ curr.shift();
47
47
  }
48
48
  }
49
49
  else if (ast.typ == EnumToken.RuleNodeType && (isIdent(curr[0]) || isFunction(curr[0]))) {
@@ -245,7 +245,7 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
245
245
  }
246
246
  if (shouldMerge) {
247
247
  // @ts-ignore
248
- if ((node.typ == EnumToken.RuleNodeType && node.sel == previous.sel) ||
248
+ if (((node.typ == EnumToken.RuleNodeType || node.typ == EnumToken.KeyFrameRuleNodeType) && node.sel == previous.sel) ||
249
249
  // @ts-ignore
250
250
  (node.typ == EnumToken.AtRuleNodeType) && node.val != 'font-face' && node.val == previous.val) {
251
251
  // @ts-ignore
@@ -264,32 +264,33 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
264
264
  nodeIndex = i;
265
265
  continue;
266
266
  }
267
- else if (node.typ == EnumToken.RuleNodeType && previous?.typ == EnumToken.RuleNodeType) {
267
+ else if (node.typ == previous?.typ && [EnumToken.KeyFrameRuleNodeType, EnumToken.RuleNodeType].includes(node.typ)) {
268
268
  const intersect = diff(previous, node, reducer, options);
269
269
  if (intersect != null) {
270
270
  if (intersect.node1.chi.length == 0) {
271
271
  // @ts-ignore
272
272
  ast.chi.splice(i--, 1);
273
273
  // @ts-ignore
274
- node = ast.chi[i];
274
+ // node = ast.chi[i];
275
275
  }
276
276
  else {
277
277
  // @ts-ignore
278
278
  ast.chi.splice(i, 1, intersect.node1);
279
- node = intersect.node1;
279
+ // node = ast.chi intersect.node1;
280
280
  }
281
281
  if (intersect.node2.chi.length == 0) {
282
282
  // @ts-ignore
283
283
  ast.chi.splice(nodeIndex, 1, intersect.result);
284
- previous = intersect.result;
285
284
  }
286
285
  else {
287
286
  // @ts-ignore
288
287
  ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2);
289
- previous = intersect.result;
290
288
  // @ts-ignore
291
- i = nodeIndex;
289
+ i = (nodeIndex ?? 0) + 1;
292
290
  }
291
+ reduceRuleSelector(intersect.result);
292
+ previous = intersect.result;
293
+ nodeIndex = i;
293
294
  }
294
295
  }
295
296
  }
@@ -419,11 +420,16 @@ function reduceSelector(selector) {
419
420
  // combinator
420
421
  if (combinators.includes(optimized.at(-1))) {
421
422
  const combinator = optimized.pop();
422
- selector.forEach(selector => selector.unshift(combinator));
423
+ selector.forEach((selector) => selector.unshift(combinator));
423
424
  }
424
425
  let reducible = optimized.length == 1;
425
- if (optimized[0] == '&' && optimized[1] == ' ') {
426
- optimized.splice(0, 2);
426
+ if (optimized[0] == '&') {
427
+ if (optimized[1] == ' ') {
428
+ optimized.splice(0, 2);
429
+ }
430
+ // else if (combinators.includes(optimized[1])) {
431
+ //
432
+ // }
427
433
  }
428
434
  if (optimized.length == 0 ||
429
435
  (optimized[0].charAt(0) == '&' ||
@@ -431,7 +437,7 @@ function reduceSelector(selector) {
431
437
  return {
432
438
  match: false,
433
439
  optimized,
434
- selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
440
+ selector: selector.map((selector) => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : (selector)),
435
441
  reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
436
442
  };
437
443
  }
@@ -511,6 +517,18 @@ function splitRule(buffer) {
511
517
  result.push([]);
512
518
  continue;
513
519
  }
520
+ if (chr == ':') {
521
+ if (str !== '') {
522
+ // @ts-ignore
523
+ result.at(-1).push(str);
524
+ str = '';
525
+ }
526
+ if (buffer.charAt(i + 1) == ':') {
527
+ chr += buffer.charAt(++i);
528
+ }
529
+ str += chr;
530
+ continue;
531
+ }
514
532
  str += chr;
515
533
  if (chr == '\\') {
516
534
  str += buffer.charAt(++i);
@@ -812,6 +830,52 @@ function diff(n1, n2, reducer, options = {}) {
812
830
  const raw1 = node1.raw;
813
831
  // @ts-ignore
814
832
  const raw2 = node2.raw;
833
+ if (raw1 != null && raw2 != null) {
834
+ const prefixes1 = new Set;
835
+ const prefixes2 = new Set;
836
+ for (const token1 of raw1) {
837
+ for (const t of token1) {
838
+ if (t[0] == ':') {
839
+ const matches = t.match(/::?-([a-z]+)-/);
840
+ if (matches == null) {
841
+ continue;
842
+ }
843
+ prefixes1.add(matches[1]);
844
+ if (prefixes1.size > 1) {
845
+ break;
846
+ }
847
+ }
848
+ }
849
+ if (prefixes1.size > 1) {
850
+ break;
851
+ }
852
+ }
853
+ for (const token2 of raw2) {
854
+ for (const t of token2) {
855
+ if (t[0] == ':') {
856
+ const matches = t.match(/::?-([a-z]+)-/);
857
+ if (matches == null) {
858
+ continue;
859
+ }
860
+ prefixes2.add(matches[1]);
861
+ if (prefixes2.size > 1) {
862
+ break;
863
+ }
864
+ }
865
+ }
866
+ if (prefixes2.size > 1) {
867
+ break;
868
+ }
869
+ }
870
+ if (prefixes1.size != prefixes2.size) {
871
+ return null;
872
+ }
873
+ for (const prefix of prefixes1) {
874
+ if (!prefixes2.has(prefix)) {
875
+ return null;
876
+ }
877
+ }
878
+ }
815
879
  // @ts-ignore
816
880
  node1 = { ...node1, chi: node1.chi.slice() };
817
881
  node2 = { ...node2, chi: node2.chi.slice() };
@@ -848,7 +912,7 @@ function diff(n1, n2, reducer, options = {}) {
848
912
  const result = (intersect.length == 0 ? null : {
849
913
  ...node1,
850
914
  // @ts-ignore
851
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
915
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) ?? splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) ?? splitRule(n2.sel))])].join(','),
852
916
  chi: intersect.reverse()
853
917
  });
854
918
  if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + doRender(curr, options).code.length, 0)) {
@@ -872,6 +936,11 @@ function reduceRuleSelector(node) {
872
936
  Object.defineProperty(node, 'optimized', { ...definedPropertySettings, value: optimized });
873
937
  }
874
938
  if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
939
+ for (const selector of optimized.selector) {
940
+ if (selector.length > 1 && selector[0] == '&' && combinators.includes(selector[1])) {
941
+ selector.shift();
942
+ }
943
+ }
875
944
  const raw = [
876
945
  [
877
946
  optimized.optimized[0], ':is('
@@ -75,6 +75,7 @@ var EnumToken;
75
75
  EnumToken[EnumToken["FractionTokenType"] = 69] = "FractionTokenType";
76
76
  EnumToken[EnumToken["IdenListTokenType"] = 70] = "IdenListTokenType";
77
77
  EnumToken[EnumToken["GridTemplateFuncTokenType"] = 71] = "GridTemplateFuncTokenType";
78
+ EnumToken[EnumToken["KeyFrameRuleNodeType"] = 72] = "KeyFrameRuleNodeType";
78
79
  /* aliases */
79
80
  EnumToken[EnumToken["Time"] = 25] = "Time";
80
81
  EnumToken[EnumToken["Iden"] = 7] = "Iden";
@@ -421,6 +421,9 @@ async function parseNode(results, context, stats, options, errors, src, map) {
421
421
  const position = map.get(tokens[0]);
422
422
  const uniq = new Map;
423
423
  parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
424
+ if (curr.typ == EnumToken.CommentTokenType) {
425
+ return acc;
426
+ }
424
427
  if (curr.typ == EnumToken.WhitespaceTokenType) {
425
428
  if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
426
429
  trimWhiteSpace.includes(array[index + 1]?.typ) ||
@@ -442,7 +445,7 @@ async function parseNode(results, context, stats, options, errors, src, map) {
442
445
  return acc;
443
446
  }, uniq);
444
447
  const node = {
445
- typ: EnumToken.RuleNodeType,
448
+ typ: context.typ == EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType,
446
449
  // @ts-ignore
447
450
  sel: [...uniq.keys()].join(','),
448
451
  chi: []
@@ -28,7 +28,7 @@ function rec20202lrec2020(r, g, b, a) {
28
28
  return val / 4.5;
29
29
  }
30
30
  return sign * (Math.pow((abs + α - 1) / α, 1 / 0.45));
31
- }).concat(a == null || a == 1 ? [] : [a]);
31
+ }).concat([] );
32
32
  }
33
33
  function lrec20202rec2020(r, g, b, a) {
34
34
  // convert an array of linear-light rec2020 RGB in the range 0.0-1.0
@@ -98,7 +98,7 @@ function doRender(data, options = {}) {
98
98
  return result;
99
99
  }
100
100
  function updateSourceMap(node, options, cache, sourcemap, position, str) {
101
- if ([EnumToken.RuleNodeType, EnumToken.AtRuleNodeType].includes(node.typ)) {
101
+ if ([EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, EnumToken.KeyFrameRuleNodeType].includes(node.typ)) {
102
102
  let src = node.loc?.src ?? '';
103
103
  let output = options.output ?? '';
104
104
  if (!(src in cache)) {
@@ -158,6 +158,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
158
158
  }, '');
159
159
  case EnumToken.AtRuleNodeType:
160
160
  case EnumToken.RuleNodeType:
161
+ case EnumToken.KeyFrameRuleNodeType:
161
162
  if (data.typ == EnumToken.AtRuleNodeType && !('chi' in data)) {
162
163
  return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
163
164
  }
package/dist/web/load.js CHANGED
@@ -7,16 +7,18 @@ function parseResponse(response) {
7
7
  return response.text();
8
8
  }
9
9
  async function load(url, currentFile) {
10
+ let t;
10
11
  if (matchUrl.test(url)) {
11
- return fetch(url).then(parseResponse);
12
+ t = new URL(url);
12
13
  }
13
- if (matchUrl.test(currentFile)) {
14
- return fetch(new URL(url, currentFile)).then(parseResponse);
14
+ else if (matchUrl.test(currentFile)) {
15
+ t = new URL(url, currentFile);
15
16
  }
16
- const path = resolve(url, currentFile).absolute;
17
- const t = new URL(path, self.origin);
18
- // return fetch(new URL(url, new URL(currentFile, self.location.href).href)).then(parseResponse);
19
- return fetch(url, t.origin != self.location.origin ? { mode: 'cors' } : {}).then(parseResponse);
17
+ else {
18
+ const path = resolve(url, currentFile).absolute;
19
+ t = new URL(path, self.origin);
20
+ }
21
+ return fetch(t, t.origin != self.origin ? { mode: 'cors' } : {}).then(parseResponse);
20
22
  }
21
23
 
22
24
  export { load };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tbela99/css-parser",
3
3
  "description": "CSS parser for node and the browser",
4
- "version": "0.5.1",
4
+ "version": "0.5.3",
5
5
  "exports": {
6
6
  ".": "./dist/node/index.js",
7
7
  "./umd": "./dist/index-umd-web.js",
@@ -10,6 +10,9 @@
10
10
  },
11
11
  "type": "module",
12
12
  "typings": "dist/index.d.ts",
13
+ "directories": {
14
+ "test": "test"
15
+ },
13
16
  "scripts": {
14
17
  "build": "rollup -c;./build.sh dist/index.d.ts 'declare interface' 'declare type'",
15
18
  "test": "web-test-runner \"test/**/web.spec.js\" --node-resolve --playwright --browsers chromium firefox webkit --root-dir=.; mocha --reporter-options='maxDiffSize=1801920' \"test/**/node.spec.js\"",
package/syntax.md ADDED
@@ -0,0 +1,62 @@
1
+
2
+ - postfix (+|?|*)
3
+
4
+ - Expression
5
+ - Statement
6
+ - Literals
7
+ - Unary Expression
8
+ - Binary Expressions
9
+ - Parentheses
10
+
11
+ Grammar
12
+
13
+ ```text
14
+ expression → literal
15
+ | unary
16
+ | binary
17
+ | grouping ;
18
+
19
+ literal → NUMBER | STRING | "true" | "false" | "nil" ;
20
+ grouping → "(" expression ")" ;
21
+ unary → ( "-" | "!" ) expression ;
22
+ binary → expression operator expression ;
23
+ operator → "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ;
24
+ ```
25
+
26
+ Syntax tree
27
+
28
+ ```text
29
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#question_mark
30
+
31
+ syntax: "keyword | <'property'> | <function>"
32
+ syntax: "<angle> | [ [ left-side | far-left | left | center-left | center | center-right | right | far-right | right-side ] || behind ] | leftwards | rightwards"
33
+ syntax: "none | [ [<dashed-ident> || <try-tactic>] | inset-area( <'inset-area'> ) ]#"
34
+ syntax: "snapInterval( <percentage>, <percentage> ) | snapList( <percentage># )"
35
+ syntax: "[ center | [ [ left | right | x-start | x-end ]? <length-percentage>? ]! ]#"
36
+ syntax: "<outline-radius>{1,4} [ / <outline-radius>{1,4} ]?
37
+ syntax: "[ [ <url> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing ] ]"
38
+ syntax: "[ [ <'font-style'> || <font-variant-css21> || <'font-weight'> || <'font-stretch'> ]? <'font-size'> [ / <'line-height'> ]? <'font-family'> ] | caption | icon | menu | message-box | small-caption | status-bar"
39
+ syntax: "false | true"
40
+ ```
41
+
42
+ ||: at least one option must appear in any order each component appears at most once. example border property <'border-width'> || <'border-style'> || <'border-color'>
43
+
44
+ |: exclusive options. at least one must be present
45
+
46
+ &&: tokens appear in any order
47
+
48
+ juxtaposition: space separated and exact order
49
+
50
+ *: postfix. the entity appears 0 or many times
51
+
52
+ +: postfix. apppears at least once.
53
+
54
+ {a,b}: postfix. appears at least A, at most B.
55
+
56
+ #: postfix, repeated one or more times, separated by comma ','
57
+
58
+ !: postfix. the group is required and must produce at least one value.
59
+
60
+
61
+ | -> []
62
+ | -> []