From 76cb9c2a39d477a64824a985ade40507e3bbade1 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Fri, 13 Feb 2026 21:34:48 -0800 Subject: feat(vanilla): add testing infrastructure and tests (NK-wjnczv) --- .../node_modules/css-tree/lib/convertor/create.js | 28 + .../node_modules/css-tree/lib/convertor/index.js | 4 + vanilla/node_modules/css-tree/lib/data-patch.js | 6 + vanilla/node_modules/css-tree/lib/data.js | 118 ++++ .../css-tree/lib/definition-syntax/SyntaxError.js | 12 + .../css-tree/lib/definition-syntax/generate.js | 135 +++++ .../css-tree/lib/definition-syntax/index.js | 4 + .../css-tree/lib/definition-syntax/parse.js | 552 ++++++++++++++++++ .../css-tree/lib/definition-syntax/scanner.js | 109 ++++ .../css-tree/lib/definition-syntax/walk.js | 53 ++ .../node_modules/css-tree/lib/generator/create.js | 97 ++++ .../node_modules/css-tree/lib/generator/index.js | 4 + .../css-tree/lib/generator/sourceMap.js | 92 +++ .../css-tree/lib/generator/token-before.js | 182 ++++++ vanilla/node_modules/css-tree/lib/index.js | 30 + vanilla/node_modules/css-tree/lib/lexer/Lexer.js | 511 +++++++++++++++++ vanilla/node_modules/css-tree/lib/lexer/error.js | 123 ++++ .../css-tree/lib/lexer/generic-an-plus-b.js | 238 ++++++++ .../css-tree/lib/lexer/generic-const.js | 8 + .../css-tree/lib/lexer/generic-urange.js | 151 +++++ vanilla/node_modules/css-tree/lib/lexer/generic.js | 622 ++++++++++++++++++++ vanilla/node_modules/css-tree/lib/lexer/index.js | 1 + .../node_modules/css-tree/lib/lexer/match-graph.js | 527 +++++++++++++++++ vanilla/node_modules/css-tree/lib/lexer/match.js | 630 +++++++++++++++++++++ .../css-tree/lib/lexer/prepare-tokens.js | 50 ++ vanilla/node_modules/css-tree/lib/lexer/search.js | 61 ++ .../node_modules/css-tree/lib/lexer/structure.js | 169 ++++++ vanilla/node_modules/css-tree/lib/lexer/trace.js | 66 +++ vanilla/node_modules/css-tree/lib/lexer/units.js | 27 + .../css-tree/lib/parser/SyntaxError.js | 70 +++ vanilla/node_modules/css-tree/lib/parser/create.js | 350 ++++++++++++ vanilla/node_modules/css-tree/lib/parser/index.js | 4 + .../css-tree/lib/parser/parse-selector.js | 4 + .../node_modules/css-tree/lib/parser/sequence.js | 43 ++ .../css-tree/lib/syntax/atrule/container.js | 28 + .../css-tree/lib/syntax/atrule/font-face.js | 8 + .../css-tree/lib/syntax/atrule/import.js | 104 ++++ .../css-tree/lib/syntax/atrule/index.js | 23 + .../css-tree/lib/syntax/atrule/layer.js | 12 + .../css-tree/lib/syntax/atrule/media.js | 12 + .../css-tree/lib/syntax/atrule/nest.js | 12 + .../css-tree/lib/syntax/atrule/page.js | 12 + .../css-tree/lib/syntax/atrule/scope.js | 12 + .../css-tree/lib/syntax/atrule/starting-style.js | 8 + .../css-tree/lib/syntax/atrule/supports.js | 12 + .../css-tree/lib/syntax/config/generator.js | 5 + .../css-tree/lib/syntax/config/lexer.js | 10 + .../node_modules/css-tree/lib/syntax/config/mix.js | 123 ++++ .../css-tree/lib/syntax/config/parser-selector.js | 15 + .../css-tree/lib/syntax/config/parser.js | 45 ++ .../css-tree/lib/syntax/config/walker.js | 5 + vanilla/node_modules/css-tree/lib/syntax/create.js | 55 ++ .../css-tree/lib/syntax/function/expression.js | 7 + .../css-tree/lib/syntax/function/var.js | 39 ++ vanilla/node_modules/css-tree/lib/syntax/index.js | 10 + .../css-tree/lib/syntax/node/AnPlusB.js | 292 ++++++++++ .../css-tree/lib/syntax/node/Atrule.js | 100 ++++ .../css-tree/lib/syntax/node/AtrulePrelude.js | 47 ++ .../css-tree/lib/syntax/node/AttributeSelector.js | 147 +++++ .../node_modules/css-tree/lib/syntax/node/Block.js | 95 ++++ .../css-tree/lib/syntax/node/Brackets.js | 35 ++ .../node_modules/css-tree/lib/syntax/node/CDC.js | 19 + .../node_modules/css-tree/lib/syntax/node/CDO.js | 19 + .../css-tree/lib/syntax/node/ClassSelector.js | 24 + .../css-tree/lib/syntax/node/Combinator.js | 54 ++ .../css-tree/lib/syntax/node/Comment.js | 33 ++ .../css-tree/lib/syntax/node/Condition.js | 123 ++++ .../css-tree/lib/syntax/node/Declaration.js | 165 ++++++ .../css-tree/lib/syntax/node/DeclarationList.js | 62 ++ .../css-tree/lib/syntax/node/Dimension.js | 23 + .../css-tree/lib/syntax/node/Feature.js | 103 ++++ .../css-tree/lib/syntax/node/FeatureFunction.js | 63 +++ .../css-tree/lib/syntax/node/FeatureRange.js | 133 +++++ .../css-tree/lib/syntax/node/Function.js | 41 ++ .../css-tree/lib/syntax/node/GeneralEnclosed.js | 66 +++ .../node_modules/css-tree/lib/syntax/node/Hash.js | 23 + .../css-tree/lib/syntax/node/IdSelector.js | 26 + .../css-tree/lib/syntax/node/Identifier.js | 18 + .../node_modules/css-tree/lib/syntax/node/Layer.js | 28 + .../css-tree/lib/syntax/node/LayerList.js | 36 ++ .../css-tree/lib/syntax/node/MediaQuery.js | 102 ++++ .../css-tree/lib/syntax/node/MediaQueryList.js | 34 ++ .../css-tree/lib/syntax/node/NestingSelector.js | 22 + .../node_modules/css-tree/lib/syntax/node/Nth.js | 47 ++ .../css-tree/lib/syntax/node/Number.js | 18 + .../css-tree/lib/syntax/node/Operator.js | 21 + .../css-tree/lib/syntax/node/Parentheses.js | 34 ++ .../css-tree/lib/syntax/node/Percentage.js | 18 + .../lib/syntax/node/PseudoClassSelector.js | 65 +++ .../lib/syntax/node/PseudoElementSelector.js | 66 +++ .../node_modules/css-tree/lib/syntax/node/Ratio.js | 68 +++ .../node_modules/css-tree/lib/syntax/node/Raw.js | 41 ++ .../node_modules/css-tree/lib/syntax/node/Rule.js | 51 ++ .../node_modules/css-tree/lib/syntax/node/Scope.js | 66 +++ .../css-tree/lib/syntax/node/Selector.js | 31 + .../css-tree/lib/syntax/node/SelectorList.js | 35 ++ .../css-tree/lib/syntax/node/String.js | 19 + .../css-tree/lib/syntax/node/StyleSheet.js | 82 +++ .../lib/syntax/node/SupportsDeclaration.js | 34 ++ .../css-tree/lib/syntax/node/TypeSelector.js | 52 ++ .../css-tree/lib/syntax/node/UnicodeRange.js | 156 +++++ .../node_modules/css-tree/lib/syntax/node/Url.js | 52 ++ .../node_modules/css-tree/lib/syntax/node/Value.js | 19 + .../css-tree/lib/syntax/node/WhiteSpace.js | 27 + .../css-tree/lib/syntax/node/index-generate.js | 49 ++ .../lib/syntax/node/index-parse-selector.js | 17 + .../css-tree/lib/syntax/node/index-parse.js | 49 ++ .../node_modules/css-tree/lib/syntax/node/index.js | 49 ++ .../css-tree/lib/syntax/pseudo/index.js | 56 ++ .../css-tree/lib/syntax/pseudo/lang.js | 33 ++ .../css-tree/lib/syntax/scope/atrulePrelude.js | 5 + .../css-tree/lib/syntax/scope/default.js | 85 +++ .../css-tree/lib/syntax/scope/index.js | 3 + .../css-tree/lib/syntax/scope/selector.js | 94 +++ .../css-tree/lib/syntax/scope/value.js | 25 + .../css-tree/lib/tokenizer/OffsetToLocation.js | 87 +++ .../css-tree/lib/tokenizer/TokenStream.js | 316 +++++++++++ .../css-tree/lib/tokenizer/adopt-buffer.js | 9 + .../lib/tokenizer/char-code-definitions.js | 212 +++++++ .../node_modules/css-tree/lib/tokenizer/index.js | 513 +++++++++++++++++ .../node_modules/css-tree/lib/tokenizer/names.js | 28 + .../node_modules/css-tree/lib/tokenizer/types.js | 28 + .../node_modules/css-tree/lib/tokenizer/utils.js | 254 +++++++++ vanilla/node_modules/css-tree/lib/utils/List.js | 469 +++++++++++++++ vanilla/node_modules/css-tree/lib/utils/clone.js | 21 + .../css-tree/lib/utils/create-custom-error.js | 14 + vanilla/node_modules/css-tree/lib/utils/ident.js | 101 ++++ vanilla/node_modules/css-tree/lib/utils/index.js | 6 + vanilla/node_modules/css-tree/lib/utils/names.js | 106 ++++ vanilla/node_modules/css-tree/lib/utils/string.js | 99 ++++ vanilla/node_modules/css-tree/lib/utils/url.js | 108 ++++ vanilla/node_modules/css-tree/lib/version.js | 5 + vanilla/node_modules/css-tree/lib/walker/create.js | 287 ++++++++++ vanilla/node_modules/css-tree/lib/walker/index.js | 4 + 134 files changed, 11705 insertions(+) create mode 100644 vanilla/node_modules/css-tree/lib/convertor/create.js create mode 100644 vanilla/node_modules/css-tree/lib/convertor/index.js create mode 100644 vanilla/node_modules/css-tree/lib/data-patch.js create mode 100755 vanilla/node_modules/css-tree/lib/data.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/SyntaxError.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/generate.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/index.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/parse.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/scanner.js create mode 100644 vanilla/node_modules/css-tree/lib/definition-syntax/walk.js create mode 100644 vanilla/node_modules/css-tree/lib/generator/create.js create mode 100644 vanilla/node_modules/css-tree/lib/generator/index.js create mode 100644 vanilla/node_modules/css-tree/lib/generator/sourceMap.js create mode 100644 vanilla/node_modules/css-tree/lib/generator/token-before.js create mode 100644 vanilla/node_modules/css-tree/lib/index.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/Lexer.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/error.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/generic-an-plus-b.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/generic-const.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/generic-urange.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/generic.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/index.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/match-graph.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/match.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/prepare-tokens.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/search.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/structure.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/trace.js create mode 100644 vanilla/node_modules/css-tree/lib/lexer/units.js create mode 100644 vanilla/node_modules/css-tree/lib/parser/SyntaxError.js create mode 100644 vanilla/node_modules/css-tree/lib/parser/create.js create mode 100644 vanilla/node_modules/css-tree/lib/parser/index.js create mode 100644 vanilla/node_modules/css-tree/lib/parser/parse-selector.js create mode 100644 vanilla/node_modules/css-tree/lib/parser/sequence.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/container.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/font-face.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/import.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/index.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/layer.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/media.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/nest.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/page.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/scope.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/starting-style.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/atrule/supports.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/generator.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/lexer.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/mix.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/parser-selector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/parser.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/config/walker.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/create.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/function/expression.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/function/var.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/index.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/AnPlusB.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Atrule.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/AttributeSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Block.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Brackets.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/CDC.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/CDO.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/ClassSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Combinator.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Comment.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Condition.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Declaration.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/DeclarationList.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Dimension.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Feature.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/FeatureFunction.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/FeatureRange.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Function.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/GeneralEnclosed.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Hash.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/IdSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Identifier.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Layer.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/LayerList.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/MediaQuery.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/MediaQueryList.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/NestingSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Nth.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Number.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Operator.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Parentheses.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Percentage.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Ratio.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Raw.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Rule.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Scope.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Selector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/SelectorList.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/String.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/StyleSheet.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/SupportsDeclaration.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/TypeSelector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/UnicodeRange.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Url.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/Value.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/WhiteSpace.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/index-generate.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/index-parse-selector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/index-parse.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/node/index.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/pseudo/index.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/pseudo/lang.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/scope/default.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/scope/index.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/scope/selector.js create mode 100644 vanilla/node_modules/css-tree/lib/syntax/scope/value.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/OffsetToLocation.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/TokenStream.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/adopt-buffer.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/char-code-definitions.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/index.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/names.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/types.js create mode 100644 vanilla/node_modules/css-tree/lib/tokenizer/utils.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/List.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/clone.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/create-custom-error.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/ident.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/index.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/names.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/string.js create mode 100644 vanilla/node_modules/css-tree/lib/utils/url.js create mode 100644 vanilla/node_modules/css-tree/lib/version.js create mode 100644 vanilla/node_modules/css-tree/lib/walker/create.js create mode 100644 vanilla/node_modules/css-tree/lib/walker/index.js (limited to 'vanilla/node_modules/css-tree/lib') diff --git a/vanilla/node_modules/css-tree/lib/convertor/create.js b/vanilla/node_modules/css-tree/lib/convertor/create.js new file mode 100644 index 0000000..56e4333 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/convertor/create.js @@ -0,0 +1,28 @@ +import { List } from '../utils/List.js'; + +export function createConvertor(walk) { + return { + fromPlainObject(ast) { + walk(ast, { + enter(node) { + if (node.children && node.children instanceof List === false) { + node.children = new List().fromArray(node.children); + } + } + }); + + return ast; + }, + toPlainObject(ast) { + walk(ast, { + leave(node) { + if (node.children && node.children instanceof List) { + node.children = node.children.toArray(); + } + } + }); + + return ast; + } + }; +}; diff --git a/vanilla/node_modules/css-tree/lib/convertor/index.js b/vanilla/node_modules/css-tree/lib/convertor/index.js new file mode 100644 index 0000000..0f8d4af --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/convertor/index.js @@ -0,0 +1,4 @@ +import { createConvertor } from './create.js'; +import walker from '../walker/index.js'; + +export default createConvertor(walker); diff --git a/vanilla/node_modules/css-tree/lib/data-patch.js b/vanilla/node_modules/css-tree/lib/data-patch.js new file mode 100644 index 0000000..cd49cbe --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/data-patch.js @@ -0,0 +1,6 @@ +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const patch = require('../data/patch.json'); + +export default patch; diff --git a/vanilla/node_modules/css-tree/lib/data.js b/vanilla/node_modules/css-tree/lib/data.js new file mode 100755 index 0000000..162fa20 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/data.js @@ -0,0 +1,118 @@ +import { createRequire } from 'module'; +import patch from './data-patch.js'; + +const require = createRequire(import.meta.url); +const mdnAtrules = require('mdn-data/css/at-rules.json'); +const mdnProperties = require('mdn-data/css/properties.json'); +const mdnSyntaxes = require('mdn-data/css/syntaxes.json'); + +const hasOwn = Object.hasOwn || ((object, property) => Object.prototype.hasOwnProperty.call(object, property)); +const extendSyntax = /^\s*\|\s*/; + +function preprocessAtrules(dict) { + const result = Object.create(null); + + for (const [atruleName, atrule] of Object.entries(dict)) { + let descriptors = null; + + if (atrule.descriptors) { + descriptors = Object.create(null); + + for (const [name, descriptor] of Object.entries(atrule.descriptors)) { + descriptors[name] = descriptor.syntax; + } + } + + result[atruleName.substr(1)] = { + prelude: atrule.syntax.trim().replace(/\{(.|\s)+\}/, '').match(/^@\S+\s+([^;\{]*)/)[1].trim() || null, + descriptors + }; + } + + return result; +} + +function patchDictionary(dict, patchDict) { + const result = Object.create(null); + + // copy all syntaxes for an original dict + for (const [key, value] of Object.entries(dict)) { + if (value) { + result[key] = value.syntax || value; + } + } + + // apply a patch + for (const key of Object.keys(patchDict)) { + if (hasOwn(dict, key)) { + if (patchDict[key].syntax) { + result[key] = extendSyntax.test(patchDict[key].syntax) + ? result[key] + ' ' + patchDict[key].syntax.trim() + : patchDict[key].syntax; + } else { + delete result[key]; + } + } else { + if (patchDict[key].syntax) { + result[key] = patchDict[key].syntax.replace(extendSyntax, ''); + } + } + } + + return result; +} + +function preprocessPatchAtrulesDescritors(declarations) { + const result = {}; + + for (const [key, value] of Object.entries(declarations || {})) { + result[key] = typeof value === 'string' + ? { syntax: value } + : value; + } + + return result; +} + +function patchAtrules(dict, patchDict) { + const result = {}; + + // copy all syntaxes for an original dict + for (const key in dict) { + if (patchDict[key] === null) { + continue; + } + + const atrulePatch = patchDict[key] || {}; + + result[key] = { + prelude: key in patchDict && 'prelude' in atrulePatch + ? atrulePatch.prelude + : dict[key].prelude || null, + descriptors: patchDictionary( + dict[key].descriptors || {}, + preprocessPatchAtrulesDescritors(atrulePatch.descriptors) + ) + }; + } + + // apply a patch + for (const [key, atrulePatch] of Object.entries(patchDict)) { + if (atrulePatch && !hasOwn(dict, key)) { + result[key] = { + prelude: atrulePatch.prelude || null, + descriptors: atrulePatch.descriptors + ? patchDictionary({}, preprocessPatchAtrulesDescritors(atrulePatch.descriptors)) + : null + }; + } + } + + return result; +} + +export default { + types: patchDictionary(mdnSyntaxes, patch.types), + atrules: patchAtrules(preprocessAtrules(mdnAtrules), patch.atrules), + properties: patchDictionary(mdnProperties, patch.properties) +}; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/SyntaxError.js b/vanilla/node_modules/css-tree/lib/definition-syntax/SyntaxError.js new file mode 100644 index 0000000..5f63138 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/SyntaxError.js @@ -0,0 +1,12 @@ +import { createCustomError } from '../utils/create-custom-error.js'; + +export function SyntaxError(message, input, offset) { + return Object.assign(createCustomError('SyntaxError', message), { + input, + offset, + rawMessage: message, + message: message + '\n' + + ' ' + input + '\n' + + '--' + new Array((offset || input.length) + 1).join('-') + '^' + }); +}; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/generate.js b/vanilla/node_modules/css-tree/lib/definition-syntax/generate.js new file mode 100644 index 0000000..b2636d1 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/generate.js @@ -0,0 +1,135 @@ +function noop(value) { + return value; +} + +function generateMultiplier(multiplier) { + const { min, max, comma } = multiplier; + + if (min === 0 && max === 0) { + return comma ? '#?' : '*'; + } + + if (min === 0 && max === 1) { + return '?'; + } + + if (min === 1 && max === 0) { + return comma ? '#' : '+'; + } + + if (min === 1 && max === 1) { + return ''; + } + + return ( + (comma ? '#' : '') + + (min === max + ? '{' + min + '}' + : '{' + min + ',' + (max !== 0 ? max : '') + '}' + ) + ); +} + +function generateTypeOpts(node) { + switch (node.type) { + case 'Range': + return ( + ' [' + + (node.min === null ? '-∞' : node.min) + + ',' + + (node.max === null ? '∞' : node.max) + + ']' + ); + + default: + throw new Error('Unknown node type `' + node.type + '`'); + } +} + +function generateSequence(node, decorate, forceBraces, compact) { + const combinator = node.combinator === ' ' || compact ? node.combinator : ' ' + node.combinator + ' '; + const result = node.terms + .map(term => internalGenerate(term, decorate, forceBraces, compact)) + .join(combinator); + + if (node.explicit || forceBraces) { + return (compact || result[0] === ',' ? '[' : '[ ') + result + (compact ? ']' : ' ]'); + } + + return result; +} + +function internalGenerate(node, decorate, forceBraces, compact) { + let result; + + switch (node.type) { + case 'Group': + result = + generateSequence(node, decorate, forceBraces, compact) + + (node.disallowEmpty ? '!' : ''); + break; + + case 'Multiplier': + // return since node is a composition + return ( + internalGenerate(node.term, decorate, forceBraces, compact) + + decorate(generateMultiplier(node), node) + ); + + case 'Boolean': + result = ''; + break; + + case 'Type': + result = '<' + node.name + (node.opts ? decorate(generateTypeOpts(node.opts), node.opts) : '') + '>'; + break; + + case 'Property': + result = '<\'' + node.name + '\'>'; + break; + + case 'Keyword': + result = node.name; + break; + + case 'AtKeyword': + result = '@' + node.name; + break; + + case 'Function': + result = node.name + '('; + break; + + case 'String': + case 'Token': + result = node.value; + break; + + case 'Comma': + result = ','; + break; + + default: + throw new Error('Unknown node type `' + node.type + '`'); + } + + return decorate(result, node); +} + +export function generate(node, options) { + let decorate = noop; + let forceBraces = false; + let compact = false; + + if (typeof options === 'function') { + decorate = options; + } else if (options) { + forceBraces = Boolean(options.forceBraces); + compact = Boolean(options.compact); + if (typeof options.decorate === 'function') { + decorate = options.decorate; + } + } + + return internalGenerate(node, decorate, forceBraces, compact); +}; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/index.js b/vanilla/node_modules/css-tree/lib/definition-syntax/index.js new file mode 100644 index 0000000..0c24dbd --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/index.js @@ -0,0 +1,4 @@ +export { SyntaxError } from './SyntaxError.js'; +export { generate } from './generate.js'; +export { parse } from './parse.js'; +export { walk } from './walk.js'; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/parse.js b/vanilla/node_modules/css-tree/lib/definition-syntax/parse.js new file mode 100644 index 0000000..4e13ad8 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/parse.js @@ -0,0 +1,552 @@ +import { Scanner } from './scanner.js'; + +const TAB = 9; +const N = 10; +const F = 12; +const R = 13; +const SPACE = 32; +const EXCLAMATIONMARK = 33; // ! +const NUMBERSIGN = 35; // # +const AMPERSAND = 38; // & +const APOSTROPHE = 39; // ' +const LEFTPARENTHESIS = 40; // ( +const RIGHTPARENTHESIS = 41; // ) +const ASTERISK = 42; // * +const PLUSSIGN = 43; // + +const COMMA = 44; // , +const HYPERMINUS = 45; // - +const LESSTHANSIGN = 60; // < +const GREATERTHANSIGN = 62; // > +const QUESTIONMARK = 63; // ? +const COMMERCIALAT = 64; // @ +const LEFTSQUAREBRACKET = 91; // [ +const RIGHTSQUAREBRACKET = 93; // ] +const LEFTCURLYBRACKET = 123; // { +const VERTICALLINE = 124; // | +const RIGHTCURLYBRACKET = 125; // } +const INFINITY = 8734; // ∞ +const COMBINATOR_PRECEDENCE = { + ' ': 1, + '&&': 2, + '||': 3, + '|': 4 +}; + +function readMultiplierRange(scanner) { + let min = null; + let max = null; + + scanner.eat(LEFTCURLYBRACKET); + scanner.skipWs(); + + min = scanner.scanNumber(scanner); + scanner.skipWs(); + + if (scanner.charCode() === COMMA) { + scanner.pos++; + scanner.skipWs(); + + if (scanner.charCode() !== RIGHTCURLYBRACKET) { + max = scanner.scanNumber(scanner); + scanner.skipWs(); + } + } else { + max = min; + } + + scanner.eat(RIGHTCURLYBRACKET); + + return { + min: Number(min), + max: max ? Number(max) : 0 + }; +} + +function readMultiplier(scanner) { + let range = null; + let comma = false; + + switch (scanner.charCode()) { + case ASTERISK: + scanner.pos++; + + range = { + min: 0, + max: 0 + }; + + break; + + case PLUSSIGN: + scanner.pos++; + + range = { + min: 1, + max: 0 + }; + + break; + + case QUESTIONMARK: + scanner.pos++; + + range = { + min: 0, + max: 1 + }; + + break; + + case NUMBERSIGN: + scanner.pos++; + + comma = true; + + if (scanner.charCode() === LEFTCURLYBRACKET) { + range = readMultiplierRange(scanner); + } else if (scanner.charCode() === QUESTIONMARK) { + // https://www.w3.org/TR/css-values-4/#component-multipliers + // > the # and ? multipliers may be stacked as #? + // In this case just treat "#?" as a single multiplier + // { min: 0, max: 0, comma: true } + scanner.pos++; + range = { + min: 0, + max: 0 + }; + } else { + range = { + min: 1, + max: 0 + }; + } + + break; + + case LEFTCURLYBRACKET: + range = readMultiplierRange(scanner); + break; + + default: + return null; + } + + return { + type: 'Multiplier', + comma, + min: range.min, + max: range.max, + term: null + }; +} + +function maybeMultiplied(scanner, node) { + const multiplier = readMultiplier(scanner); + + if (multiplier !== null) { + multiplier.term = node; + + // https://www.w3.org/TR/css-values-4/#component-multipliers + // > The + and # multipliers may be stacked as +#; + // Represent "+#" as nested multipliers: + // { ..., + // term: { + // ..., + // term: node + // } + // } + if (scanner.charCode() === NUMBERSIGN && + scanner.charCodeAt(scanner.pos - 1) === PLUSSIGN) { + return maybeMultiplied(scanner, multiplier); + } + + return multiplier; + } + + return node; +} + +function maybeToken(scanner) { + const ch = scanner.peek(); + + if (ch === '') { + return null; + } + + return maybeMultiplied(scanner, { + type: 'Token', + value: ch + }); +} + +function readProperty(scanner) { + let name; + + scanner.eat(LESSTHANSIGN); + scanner.eat(APOSTROPHE); + + name = scanner.scanWord(); + + scanner.eat(APOSTROPHE); + scanner.eat(GREATERTHANSIGN); + + return maybeMultiplied(scanner, { + type: 'Property', + name + }); +} + +// https://drafts.csswg.org/css-values-3/#numeric-ranges +// 4.1. Range Restrictions and Range Definition Notation +// +// Range restrictions can be annotated in the numeric type notation using CSS bracketed +// range notation—[min,max]—within the angle brackets, after the identifying keyword, +// indicating a closed range between (and including) min and max. +// For example, indicates an integer between 0 and 10, inclusive. +function readTypeRange(scanner) { + // use null for Infinity to make AST format JSON serializable/deserializable + let min = null; // -Infinity + let max = null; // Infinity + let sign = 1; + + scanner.eat(LEFTSQUAREBRACKET); + + if (scanner.charCode() === HYPERMINUS) { + scanner.peek(); + sign = -1; + } + + if (sign == -1 && scanner.charCode() === INFINITY) { + scanner.peek(); + } else { + min = sign * Number(scanner.scanNumber(scanner)); + + if (scanner.isNameCharCode()) { + min += scanner.scanWord(); + } + } + + scanner.skipWs(); + scanner.eat(COMMA); + scanner.skipWs(); + + if (scanner.charCode() === INFINITY) { + scanner.peek(); + } else { + sign = 1; + + if (scanner.charCode() === HYPERMINUS) { + scanner.peek(); + sign = -1; + } + + max = sign * Number(scanner.scanNumber(scanner)); + + if (scanner.isNameCharCode()) { + max += scanner.scanWord(); + } + } + + scanner.eat(RIGHTSQUAREBRACKET); + + return { + type: 'Range', + min, + max + }; +} + +function readType(scanner) { + let name; + let opts = null; + + scanner.eat(LESSTHANSIGN); + name = scanner.scanWord(); + + // https://drafts.csswg.org/css-values-5/#boolean + if (name === 'boolean-expr') { + scanner.eat(LEFTSQUAREBRACKET); + + const implicitGroup = readImplicitGroup(scanner, RIGHTSQUAREBRACKET); + + scanner.eat(RIGHTSQUAREBRACKET); + scanner.eat(GREATERTHANSIGN); + + return maybeMultiplied(scanner, { + type: 'Boolean', + term: implicitGroup.terms.length === 1 + ? implicitGroup.terms[0] + : implicitGroup + }); + } + + if (scanner.charCode() === LEFTPARENTHESIS && + scanner.nextCharCode() === RIGHTPARENTHESIS) { + scanner.pos += 2; + name += '()'; + } + + if (scanner.charCodeAt(scanner.findWsEnd(scanner.pos)) === LEFTSQUAREBRACKET) { + scanner.skipWs(); + opts = readTypeRange(scanner); + } + + scanner.eat(GREATERTHANSIGN); + + return maybeMultiplied(scanner, { + type: 'Type', + name, + opts + }); +} + +function readKeywordOrFunction(scanner) { + const name = scanner.scanWord(); + + if (scanner.charCode() === LEFTPARENTHESIS) { + scanner.pos++; + + return { + type: 'Function', + name + }; + } + + return maybeMultiplied(scanner, { + type: 'Keyword', + name + }); +} + +function regroupTerms(terms, combinators) { + function createGroup(terms, combinator) { + return { + type: 'Group', + terms, + combinator, + disallowEmpty: false, + explicit: false + }; + } + + let combinator; + + combinators = Object.keys(combinators) + .sort((a, b) => COMBINATOR_PRECEDENCE[a] - COMBINATOR_PRECEDENCE[b]); + + while (combinators.length > 0) { + combinator = combinators.shift(); + + let i = 0; + let subgroupStart = 0; + + for (; i < terms.length; i++) { + const term = terms[i]; + + if (term.type === 'Combinator') { + if (term.value === combinator) { + if (subgroupStart === -1) { + subgroupStart = i - 1; + } + terms.splice(i, 1); + i--; + } else { + if (subgroupStart !== -1 && i - subgroupStart > 1) { + terms.splice( + subgroupStart, + i - subgroupStart, + createGroup(terms.slice(subgroupStart, i), combinator) + ); + i = subgroupStart + 1; + } + subgroupStart = -1; + } + } + } + + if (subgroupStart !== -1 && combinators.length) { + terms.splice( + subgroupStart, + i - subgroupStart, + createGroup(terms.slice(subgroupStart, i), combinator) + ); + } + } + + return combinator; +} + +function readImplicitGroup(scanner, stopCharCode) { + const combinators = Object.create(null); + const terms = []; + let token; + let prevToken = null; + let prevTokenPos = scanner.pos; + + while (scanner.charCode() !== stopCharCode && (token = peek(scanner, stopCharCode))) { + if (token.type !== 'Spaces') { + if (token.type === 'Combinator') { + // check for combinator in group beginning and double combinator sequence + if (prevToken === null || prevToken.type === 'Combinator') { + scanner.pos = prevTokenPos; + scanner.error('Unexpected combinator'); + } + + combinators[token.value] = true; + } else if (prevToken !== null && prevToken.type !== 'Combinator') { + combinators[' '] = true; // a b + terms.push({ + type: 'Combinator', + value: ' ' + }); + } + + terms.push(token); + prevToken = token; + prevTokenPos = scanner.pos; + } + } + + // check for combinator in group ending + if (prevToken !== null && prevToken.type === 'Combinator') { + scanner.pos -= prevTokenPos; + scanner.error('Unexpected combinator'); + } + + return { + type: 'Group', + terms, + combinator: regroupTerms(terms, combinators) || ' ', + disallowEmpty: false, + explicit: false + }; +} + +function readGroup(scanner, stopCharCode) { + let result; + + scanner.eat(LEFTSQUAREBRACKET); + result = readImplicitGroup(scanner, stopCharCode); + scanner.eat(RIGHTSQUAREBRACKET); + + result.explicit = true; + + if (scanner.charCode() === EXCLAMATIONMARK) { + scanner.pos++; + result.disallowEmpty = true; + } + + return result; +} + +function peek(scanner, stopCharCode) { + let code = scanner.charCode(); + + switch (code) { + case RIGHTSQUAREBRACKET: + // don't eat, stop scan a group + break; + + case LEFTSQUAREBRACKET: + return maybeMultiplied(scanner, readGroup(scanner, stopCharCode)); + + case LESSTHANSIGN: + return scanner.nextCharCode() === APOSTROPHE + ? readProperty(scanner) + : readType(scanner); + + case VERTICALLINE: + return { + type: 'Combinator', + value: scanner.substringToPos( + scanner.pos + (scanner.nextCharCode() === VERTICALLINE ? 2 : 1) + ) + }; + + case AMPERSAND: + scanner.pos++; + scanner.eat(AMPERSAND); + + return { + type: 'Combinator', + value: '&&' + }; + + case COMMA: + scanner.pos++; + return { + type: 'Comma' + }; + + case APOSTROPHE: + return maybeMultiplied(scanner, { + type: 'String', + value: scanner.scanString() + }); + + case SPACE: + case TAB: + case N: + case R: + case F: + return { + type: 'Spaces', + value: scanner.scanSpaces() + }; + + case COMMERCIALAT: + code = scanner.nextCharCode(); + + if (scanner.isNameCharCode(code)) { + scanner.pos++; + return { + type: 'AtKeyword', + name: scanner.scanWord() + }; + } + + return maybeToken(scanner); + + case ASTERISK: + case PLUSSIGN: + case QUESTIONMARK: + case NUMBERSIGN: + case EXCLAMATIONMARK: + // prohibited tokens (used as a multiplier start) + break; + + case LEFTCURLYBRACKET: + // LEFTCURLYBRACKET is allowed since mdn/data uses it w/o quoting + // check next char isn't a number, because it's likely a disjoined multiplier + code = scanner.nextCharCode(); + + if (code < 48 || code > 57) { + return maybeToken(scanner); + } + + break; + + default: + if (scanner.isNameCharCode(code)) { + return readKeywordOrFunction(scanner); + } + + return maybeToken(scanner); + } +} + +export function parse(source) { + const scanner = new Scanner(source); + const result = readImplicitGroup(scanner); + + if (scanner.pos !== source.length) { + scanner.error('Unexpected input'); + } + + // reduce redundant groups with single group term + if (result.terms.length === 1 && result.terms[0].type === 'Group') { + return result.terms[0]; + } + + return result; +}; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/scanner.js b/vanilla/node_modules/css-tree/lib/definition-syntax/scanner.js new file mode 100644 index 0000000..d0b1895 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/scanner.js @@ -0,0 +1,109 @@ +import { SyntaxError } from './SyntaxError.js'; + +const TAB = 9; +const N = 10; +const F = 12; +const R = 13; +const SPACE = 32; +const NAME_CHAR = new Uint8Array(128).map((_, idx) => + /[a-zA-Z0-9\-]/.test(String.fromCharCode(idx)) ? 1 : 0 +); + +export class Scanner { + constructor(str) { + this.str = str; + this.pos = 0; + } + + charCodeAt(pos) { + return pos < this.str.length ? this.str.charCodeAt(pos) : 0; + } + charCode() { + return this.charCodeAt(this.pos); + } + isNameCharCode(code = this.charCode()) { + return code < 128 && NAME_CHAR[code] === 1; + } + nextCharCode() { + return this.charCodeAt(this.pos + 1); + } + nextNonWsCode(pos) { + return this.charCodeAt(this.findWsEnd(pos)); + } + skipWs() { + this.pos = this.findWsEnd(this.pos); + } + findWsEnd(pos) { + for (; pos < this.str.length; pos++) { + const code = this.str.charCodeAt(pos); + if (code !== R && code !== N && code !== F && code !== SPACE && code !== TAB) { + break; + } + } + + return pos; + } + substringToPos(end) { + return this.str.substring(this.pos, this.pos = end); + } + eat(code) { + if (this.charCode() !== code) { + this.error('Expect `' + String.fromCharCode(code) + '`'); + } + + this.pos++; + } + peek() { + return this.pos < this.str.length ? this.str.charAt(this.pos++) : ''; + } + error(message) { + throw new SyntaxError(message, this.str, this.pos); + } + + scanSpaces() { + return this.substringToPos(this.findWsEnd(this.pos)); + } + scanWord() { + let end = this.pos; + + for (; end < this.str.length; end++) { + const code = this.str.charCodeAt(end); + if (code >= 128 || NAME_CHAR[code] === 0) { + break; + } + } + + if (this.pos === end) { + this.error('Expect a keyword'); + } + + return this.substringToPos(end); + } + scanNumber() { + let end = this.pos; + + for (; end < this.str.length; end++) { + const code = this.str.charCodeAt(end); + + if (code < 48 || code > 57) { + break; + } + } + + if (this.pos === end) { + this.error('Expect a number'); + } + + return this.substringToPos(end); + } + scanString() { + const end = this.str.indexOf('\'', this.pos + 1); + + if (end === -1) { + this.pos = this.str.length; + this.error('Expect an apostrophe'); + } + + return this.substringToPos(end + 1); + } +}; diff --git a/vanilla/node_modules/css-tree/lib/definition-syntax/walk.js b/vanilla/node_modules/css-tree/lib/definition-syntax/walk.js new file mode 100644 index 0000000..b8c7957 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/definition-syntax/walk.js @@ -0,0 +1,53 @@ +const noop = function() {}; + +function ensureFunction(value) { + return typeof value === 'function' ? value : noop; +} + +export function walk(node, options, context) { + function walk(node) { + enter.call(context, node); + + switch (node.type) { + case 'Group': + node.terms.forEach(walk); + break; + + case 'Multiplier': + case 'Boolean': + walk(node.term); + break; + + case 'Type': + case 'Property': + case 'Keyword': + case 'AtKeyword': + case 'Function': + case 'String': + case 'Token': + case 'Comma': + break; + + default: + throw new Error('Unknown type: ' + node.type); + } + + leave.call(context, node); + } + + let enter = noop; + let leave = noop; + + if (typeof options === 'function') { + enter = options; + } else if (options) { + enter = ensureFunction(options.enter); + leave = ensureFunction(options.leave); + } + + if (enter === noop && leave === noop) { + throw new Error('Neither `enter` nor `leave` walker handler is set or both aren\'t a function'); + } + + walk(node, context); +}; diff --git a/vanilla/node_modules/css-tree/lib/generator/create.js b/vanilla/node_modules/css-tree/lib/generator/create.js new file mode 100644 index 0000000..c542f4f --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/generator/create.js @@ -0,0 +1,97 @@ +import { tokenize, Delim, WhiteSpace } from '../tokenizer/index.js'; +import { generateSourceMap } from './sourceMap.js'; +import * as tokenBefore from './token-before.js'; + +const REVERSESOLIDUS = 0x005c; // U+005C REVERSE SOLIDUS (\) + +function processChildren(node, delimeter) { + if (typeof delimeter === 'function') { + let prev = null; + + node.children.forEach(node => { + if (prev !== null) { + delimeter.call(this, prev); + } + + this.node(node); + prev = node; + }); + + return; + } + + node.children.forEach(this.node, this); +} + +function processChunk(chunk) { + tokenize(chunk, (type, start, end) => { + this.token(type, chunk.slice(start, end)); + }); +} + +export function createGenerator(config) { + const types = new Map(); + + for (let [name, item] of Object.entries(config.node)) { + const fn = item.generate || item; + + if (typeof fn === 'function') { + types.set(name, item.generate || item); + } + } + + return function(node, options) { + let buffer = ''; + let prevCode = 0; + let handlers = { + node(node) { + if (types.has(node.type)) { + types.get(node.type).call(publicApi, node); + } else { + throw new Error('Unknown node type: ' + node.type); + } + }, + tokenBefore: tokenBefore.safe, + token(type, value) { + prevCode = this.tokenBefore(prevCode, type, value); + + this.emit(value, type, false); + + if (type === Delim && value.charCodeAt(0) === REVERSESOLIDUS) { + this.emit('\n', WhiteSpace, true); + } + }, + emit(value) { + buffer += value; + }, + result() { + return buffer; + } + }; + + if (options) { + if (typeof options.decorator === 'function') { + handlers = options.decorator(handlers); + } + + if (options.sourceMap) { + handlers = generateSourceMap(handlers); + } + + if (options.mode in tokenBefore) { + handlers.tokenBefore = tokenBefore[options.mode]; + } + } + + const publicApi = { + node: (node) => handlers.node(node), + children: processChildren, + token: (type, value) => handlers.token(type, value), + tokenize: processChunk + }; + + handlers.node(node); + + return handlers.result(); + }; +}; diff --git a/vanilla/node_modules/css-tree/lib/generator/index.js b/vanilla/node_modules/css-tree/lib/generator/index.js new file mode 100644 index 0000000..dc05116 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/generator/index.js @@ -0,0 +1,4 @@ +import { createGenerator } from './create.js'; +import config from '../syntax/config/generator.js'; + +export default createGenerator(config); diff --git a/vanilla/node_modules/css-tree/lib/generator/sourceMap.js b/vanilla/node_modules/css-tree/lib/generator/sourceMap.js new file mode 100644 index 0000000..0c5ff27 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/generator/sourceMap.js @@ -0,0 +1,92 @@ +import { SourceMapGenerator } from 'source-map-js/lib/source-map-generator.js'; + +const trackNodes = new Set(['Atrule', 'Selector', 'Declaration']); + +export function generateSourceMap(handlers) { + const map = new SourceMapGenerator(); + const generated = { + line: 1, + column: 0 + }; + const original = { + line: 0, // should be zero to add first mapping + column: 0 + }; + const activatedGenerated = { + line: 1, + column: 0 + }; + const activatedMapping = { + generated: activatedGenerated + }; + let line = 1; + let column = 0; + let sourceMappingActive = false; + + const origHandlersNode = handlers.node; + handlers.node = function(node) { + if (node.loc && node.loc.start && trackNodes.has(node.type)) { + const nodeLine = node.loc.start.line; + const nodeColumn = node.loc.start.column - 1; + + if (original.line !== nodeLine || + original.column !== nodeColumn) { + original.line = nodeLine; + original.column = nodeColumn; + + generated.line = line; + generated.column = column; + + if (sourceMappingActive) { + sourceMappingActive = false; + if (generated.line !== activatedGenerated.line || + generated.column !== activatedGenerated.column) { + map.addMapping(activatedMapping); + } + } + + sourceMappingActive = true; + map.addMapping({ + source: node.loc.source, + original, + generated + }); + } + } + + origHandlersNode.call(this, node); + + if (sourceMappingActive && trackNodes.has(node.type)) { + activatedGenerated.line = line; + activatedGenerated.column = column; + } + }; + + const origHandlersEmit = handlers.emit; + handlers.emit = function(value, type, auto) { + for (let i = 0; i < value.length; i++) { + if (value.charCodeAt(i) === 10) { // \n + line++; + column = 0; + } else { + column++; + } + } + + origHandlersEmit(value, type, auto); + }; + + const origHandlersResult = handlers.result; + handlers.result = function() { + if (sourceMappingActive) { + map.addMapping(activatedMapping); + } + + return { + css: origHandlersResult(), + map + }; + }; + + return handlers; +}; diff --git a/vanilla/node_modules/css-tree/lib/generator/token-before.js b/vanilla/node_modules/css-tree/lib/generator/token-before.js new file mode 100644 index 0000000..da3fed0 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/generator/token-before.js @@ -0,0 +1,182 @@ +import { + WhiteSpace, + Delim, + Ident, + Function as FunctionToken, + Url, + BadUrl, + AtKeyword, + Hash, + Percentage, + Dimension, + Number as NumberToken, + String as StringToken, + Colon, + LeftParenthesis, + RightParenthesis, + CDC +} from '../tokenizer/index.js'; + +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) + +const code = (type, value) => { + if (type === Delim) { + type = value; + } + + if (typeof type === 'string') { + const charCode = type.charCodeAt(0); + return charCode > 0x7F ? 0x8000 : charCode << 8; + } + + return type; +}; + +// https://www.w3.org/TR/css-syntax-3/#serialization +// The only requirement for serialization is that it must "round-trip" with parsing, +// that is, parsing the stylesheet must produce the same data structures as parsing, +// serializing, and parsing again, except for consecutive s, +// which may be collapsed into a single token. + +const specPairs = [ + [Ident, Ident], + [Ident, FunctionToken], + [Ident, Url], + [Ident, BadUrl], + [Ident, '-'], + [Ident, NumberToken], + [Ident, Percentage], + [Ident, Dimension], + [Ident, CDC], + [Ident, LeftParenthesis], + + [AtKeyword, Ident], + [AtKeyword, FunctionToken], + [AtKeyword, Url], + [AtKeyword, BadUrl], + [AtKeyword, '-'], + [AtKeyword, NumberToken], + [AtKeyword, Percentage], + [AtKeyword, Dimension], + [AtKeyword, CDC], + + [Hash, Ident], + [Hash, FunctionToken], + [Hash, Url], + [Hash, BadUrl], + [Hash, '-'], + [Hash, NumberToken], + [Hash, Percentage], + [Hash, Dimension], + [Hash, CDC], + + [Dimension, Ident], + [Dimension, FunctionToken], + [Dimension, Url], + [Dimension, BadUrl], + [Dimension, '-'], + [Dimension, NumberToken], + [Dimension, Percentage], + [Dimension, Dimension], + [Dimension, CDC], + + ['#', Ident], + ['#', FunctionToken], + ['#', Url], + ['#', BadUrl], + ['#', '-'], + ['#', NumberToken], + ['#', Percentage], + ['#', Dimension], + ['#', CDC], // https://github.com/w3c/csswg-drafts/pull/6874 + + ['-', Ident], + ['-', FunctionToken], + ['-', Url], + ['-', BadUrl], + ['-', '-'], + ['-', NumberToken], + ['-', Percentage], + ['-', Dimension], + ['-', CDC], // https://github.com/w3c/csswg-drafts/pull/6874 + + [NumberToken, Ident], + [NumberToken, FunctionToken], + [NumberToken, Url], + [NumberToken, BadUrl], + [NumberToken, NumberToken], + [NumberToken, Percentage], + [NumberToken, Dimension], + [NumberToken, '%'], + [NumberToken, CDC], // https://github.com/w3c/csswg-drafts/pull/6874 + + ['@', Ident], + ['@', FunctionToken], + ['@', Url], + ['@', BadUrl], + ['@', '-'], + ['@', CDC], // https://github.com/w3c/csswg-drafts/pull/6874 + + ['.', NumberToken], + ['.', Percentage], + ['.', Dimension], + + ['+', NumberToken], + ['+', Percentage], + ['+', Dimension], + + ['/', '*'] +]; +// validate with scripts/generate-safe +const safePairs = specPairs.concat([ + [Ident, Hash], + + [Dimension, Hash], + + [Hash, Hash], + + [AtKeyword, LeftParenthesis], + [AtKeyword, StringToken], + [AtKeyword, Colon], + + [Percentage, Percentage], + [Percentage, Dimension], + [Percentage, FunctionToken], + [Percentage, '-'], + + [RightParenthesis, Ident], + [RightParenthesis, FunctionToken], + [RightParenthesis, Percentage], + [RightParenthesis, Dimension], + [RightParenthesis, Hash], + [RightParenthesis, '-'] +]); + +function createMap(pairs) { + const isWhiteSpaceRequired = new Set( + pairs.map(([prev, next]) => (code(prev) << 16 | code(next))) + ); + + return function(prevCode, type, value) { + const nextCode = code(type, value); + const nextCharCode = value.charCodeAt(0); + const emitWs = + (nextCharCode === HYPHENMINUS && + type !== Ident && + type !== FunctionToken && + type !== CDC) || + (nextCharCode === PLUSSIGN) + ? isWhiteSpaceRequired.has(prevCode << 16 | nextCharCode << 8) + : isWhiteSpaceRequired.has(prevCode << 16 | nextCode); + + if (emitWs) { + this.emit(' ', WhiteSpace, true); + } + + return nextCode; + }; +} + +export const spec = createMap(specPairs); +export const safe = createMap(safePairs); diff --git a/vanilla/node_modules/css-tree/lib/index.js b/vanilla/node_modules/css-tree/lib/index.js new file mode 100644 index 0000000..22cf491 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/index.js @@ -0,0 +1,30 @@ +import syntax from './syntax/index.js'; + +export * from './version.js'; +export { default as createSyntax } from './syntax/create.js'; +export { List } from './utils/List.js'; +export { Lexer } from './lexer/Lexer.js'; +export { tokenTypes, tokenNames, TokenStream, OffsetToLocation } from './tokenizer/index.js'; +export * as definitionSyntax from './definition-syntax/index.js'; +export { clone } from './utils/clone.js'; +export * from './utils/names.js'; +export * as ident from './utils/ident.js'; +export * as string from './utils/string.js'; +export * as url from './utils/url.js'; +export const { + tokenize, + parse, + generate, + lexer, + createLexer, + + walk, + find, + findLast, + findAll, + + toPlainObject, + fromPlainObject, + + fork +} = syntax; diff --git a/vanilla/node_modules/css-tree/lib/lexer/Lexer.js b/vanilla/node_modules/css-tree/lib/lexer/Lexer.js new file mode 100644 index 0000000..d09f574 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/Lexer.js @@ -0,0 +1,511 @@ +import { SyntaxReferenceError, SyntaxMatchError } from './error.js'; +import * as names from '../utils/names.js'; +import { cssWideKeywords } from './generic-const.js'; +import { createGenericTypes } from './generic.js'; +import * as units from './units.js'; +import { parse, generate, walk } from '../definition-syntax/index.js'; +import prepareTokens from './prepare-tokens.js'; +import { buildMatchGraph } from './match-graph.js'; +import { matchAsTree } from './match.js'; +import * as trace from './trace.js'; +import { matchFragments } from './search.js'; +import { getStructureFromConfig } from './structure.js'; + +function dumpMapSyntax(map, compact, syntaxAsAst) { + const result = {}; + + for (const name in map) { + if (map[name].syntax) { + result[name] = syntaxAsAst + ? map[name].syntax + : generate(map[name].syntax, { compact }); + } + } + + return result; +} + +function dumpAtruleMapSyntax(map, compact, syntaxAsAst) { + const result = {}; + + for (const [name, atrule] of Object.entries(map)) { + result[name] = { + prelude: atrule.prelude && ( + syntaxAsAst + ? atrule.prelude.syntax + : generate(atrule.prelude.syntax, { compact }) + ), + descriptors: atrule.descriptors && dumpMapSyntax(atrule.descriptors, compact, syntaxAsAst) + }; + } + + return result; +} + +function valueHasVar(tokens) { + for (let i = 0; i < tokens.length; i++) { + if (tokens[i].value.toLowerCase() === 'var(') { + return true; + } + } + + return false; +} + +function syntaxHasTopLevelCommaMultiplier(syntax) { + const singleTerm = syntax.terms[0]; + + return ( + syntax.explicit === false && + syntax.terms.length === 1 && + singleTerm.type === 'Multiplier' && + singleTerm.comma === true + ); +} + +function buildMatchResult(matched, error, iterations) { + return { + matched, + iterations, + error, + ...trace + }; +} + +function matchSyntax(lexer, syntax, value, useCssWideKeywords) { + const tokens = prepareTokens(value, lexer.syntax); + let result; + + if (valueHasVar(tokens)) { + return buildMatchResult(null, new Error('Matching for a tree with var() is not supported')); + } + + if (useCssWideKeywords) { + result = matchAsTree(tokens, lexer.cssWideKeywordsSyntax, lexer); + } + + if (!useCssWideKeywords || !result.match) { + result = matchAsTree(tokens, syntax.match, lexer); + if (!result.match) { + return buildMatchResult( + null, + new SyntaxMatchError(result.reason, syntax.syntax, value, result), + result.iterations + ); + } + } + + return buildMatchResult(result.match, null, result.iterations); +} + +export class Lexer { + constructor(config, syntax, structure) { + this.cssWideKeywords = cssWideKeywords; + this.syntax = syntax; + this.generic = false; + this.units = { ...units }; + this.atrules = Object.create(null); + this.properties = Object.create(null); + this.types = Object.create(null); + this.structure = structure || getStructureFromConfig(config); + + if (config) { + if (config.cssWideKeywords) { + this.cssWideKeywords = config.cssWideKeywords; + } + + if (config.units) { + for (const group of Object.keys(units)) { + if (Array.isArray(config.units[group])) { + this.units[group] = config.units[group]; + } + } + } + + if (config.types) { + for (const [name, type] of Object.entries(config.types)) { + this.addType_(name, type); + } + } + + if (config.generic) { + this.generic = true; + for (const [name, value] of Object.entries(createGenericTypes(this.units))) { + this.addType_(name, value); + } + } + + if (config.atrules) { + for (const [name, atrule] of Object.entries(config.atrules)) { + this.addAtrule_(name, atrule); + } + } + + if (config.properties) { + for (const [name, property] of Object.entries(config.properties)) { + this.addProperty_(name, property); + } + } + } + + this.cssWideKeywordsSyntax = buildMatchGraph(this.cssWideKeywords.join(' | ')); + } + + checkStructure(ast) { + function collectWarning(node, message) { + warns.push({ node, message }); + } + + const structure = this.structure; + const warns = []; + + this.syntax.walk(ast, function(node) { + if (structure.hasOwnProperty(node.type)) { + structure[node.type].check(node, collectWarning); + } else { + collectWarning(node, 'Unknown node type `' + node.type + '`'); + } + }); + + return warns.length ? warns : false; + } + + createDescriptor(syntax, type, name, parent = null) { + const ref = { + type, + name + }; + const descriptor = { + type, + name, + parent, + serializable: typeof syntax === 'string' || (syntax && typeof syntax.type === 'string'), + syntax: null, + match: null, + matchRef: null // used for properties when a syntax referenced as <'property'> in other syntax definitions + }; + + if (typeof syntax === 'function') { + descriptor.match = buildMatchGraph(syntax, ref); + } else { + if (typeof syntax === 'string') { + // lazy parsing on first access + Object.defineProperty(descriptor, 'syntax', { + get() { + Object.defineProperty(descriptor, 'syntax', { + value: parse(syntax) + }); + + return descriptor.syntax; + } + }); + } else { + descriptor.syntax = syntax; + } + + // lazy graph build on first access + Object.defineProperty(descriptor, 'match', { + get() { + Object.defineProperty(descriptor, 'match', { + value: buildMatchGraph(descriptor.syntax, ref) + }); + + return descriptor.match; + } + }); + + if (type === 'Property') { + Object.defineProperty(descriptor, 'matchRef', { + get() { + const syntax = descriptor.syntax; + const value = syntaxHasTopLevelCommaMultiplier(syntax) + ? buildMatchGraph({ + ...syntax, + terms: [syntax.terms[0].term] + }, ref) + : null; + + Object.defineProperty(descriptor, 'matchRef', { + value + }); + + return value; + } + }); + } + } + + return descriptor; + } + addAtrule_(name, syntax) { + if (!syntax) { + return; + } + + this.atrules[name] = { + type: 'Atrule', + name: name, + prelude: syntax.prelude ? this.createDescriptor(syntax.prelude, 'AtrulePrelude', name) : null, + descriptors: syntax.descriptors + ? Object.keys(syntax.descriptors).reduce( + (map, descName) => { + map[descName] = this.createDescriptor(syntax.descriptors[descName], 'AtruleDescriptor', descName, name); + return map; + }, + Object.create(null) + ) + : null + }; + } + addProperty_(name, syntax) { + if (!syntax) { + return; + } + + this.properties[name] = this.createDescriptor(syntax, 'Property', name); + } + addType_(name, syntax) { + if (!syntax) { + return; + } + + this.types[name] = this.createDescriptor(syntax, 'Type', name); + } + + checkAtruleName(atruleName) { + if (!this.getAtrule(atruleName)) { + return new SyntaxReferenceError('Unknown at-rule', '@' + atruleName); + } + } + checkAtrulePrelude(atruleName, prelude) { + const error = this.checkAtruleName(atruleName); + + if (error) { + return error; + } + + const atrule = this.getAtrule(atruleName); + + if (!atrule.prelude && prelude) { + return new SyntaxError('At-rule `@' + atruleName + '` should not contain a prelude'); + } + + if (atrule.prelude && !prelude) { + if (!matchSyntax(this, atrule.prelude, '', false).matched) { + return new SyntaxError('At-rule `@' + atruleName + '` should contain a prelude'); + } + } + } + checkAtruleDescriptorName(atruleName, descriptorName) { + const error = this.checkAtruleName(atruleName); + + if (error) { + return error; + } + + const atrule = this.getAtrule(atruleName); + const descriptor = names.keyword(descriptorName); + + if (!atrule.descriptors) { + return new SyntaxError('At-rule `@' + atruleName + '` has no known descriptors'); + } + + if (!atrule.descriptors[descriptor.name] && + !atrule.descriptors[descriptor.basename]) { + return new SyntaxReferenceError('Unknown at-rule descriptor', descriptorName); + } + } + checkPropertyName(propertyName) { + if (!this.getProperty(propertyName)) { + return new SyntaxReferenceError('Unknown property', propertyName); + } + } + + matchAtrulePrelude(atruleName, prelude) { + const error = this.checkAtrulePrelude(atruleName, prelude); + + if (error) { + return buildMatchResult(null, error); + } + + const atrule = this.getAtrule(atruleName); + + if (!atrule.prelude) { + return buildMatchResult(null, null); + } + + return matchSyntax(this, atrule.prelude, prelude || '', false); + } + matchAtruleDescriptor(atruleName, descriptorName, value) { + const error = this.checkAtruleDescriptorName(atruleName, descriptorName); + + if (error) { + return buildMatchResult(null, error); + } + + const atrule = this.getAtrule(atruleName); + const descriptor = names.keyword(descriptorName); + + return matchSyntax(this, atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename], value, false); + } + matchDeclaration(node) { + if (node.type !== 'Declaration') { + return buildMatchResult(null, new Error('Not a Declaration node')); + } + + return this.matchProperty(node.property, node.value); + } + matchProperty(propertyName, value) { + // don't match syntax for a custom property at the moment + if (names.property(propertyName).custom) { + return buildMatchResult(null, new Error('Lexer matching doesn\'t applicable for custom properties')); + } + + const error = this.checkPropertyName(propertyName); + + if (error) { + return buildMatchResult(null, error); + } + + return matchSyntax(this, this.getProperty(propertyName), value, true); + } + matchType(typeName, value) { + const typeSyntax = this.getType(typeName); + + if (!typeSyntax) { + return buildMatchResult(null, new SyntaxReferenceError('Unknown type', typeName)); + } + + return matchSyntax(this, typeSyntax, value, false); + } + match(syntax, value) { + if (typeof syntax !== 'string' && (!syntax || !syntax.type)) { + return buildMatchResult(null, new SyntaxReferenceError('Bad syntax')); + } + + if (typeof syntax === 'string' || !syntax.match) { + syntax = this.createDescriptor(syntax, 'Type', 'anonymous'); + } + + return matchSyntax(this, syntax, value, false); + } + + findValueFragments(propertyName, value, type, name) { + return matchFragments(this, value, this.matchProperty(propertyName, value), type, name); + } + findDeclarationValueFragments(declaration, type, name) { + return matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name); + } + findAllFragments(ast, type, name) { + const result = []; + + this.syntax.walk(ast, { + visit: 'Declaration', + enter: (declaration) => { + result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name)); + } + }); + + return result; + } + + getAtrule(atruleName, fallbackBasename = true) { + const atrule = names.keyword(atruleName); + const atruleEntry = atrule.vendor && fallbackBasename + ? this.atrules[atrule.name] || this.atrules[atrule.basename] + : this.atrules[atrule.name]; + + return atruleEntry || null; + } + getAtrulePrelude(atruleName, fallbackBasename = true) { + const atrule = this.getAtrule(atruleName, fallbackBasename); + + return atrule && atrule.prelude || null; + } + getAtruleDescriptor(atruleName, name) { + return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators + ? this.atrules[atruleName].declarators[name] || null + : null; + } + getProperty(propertyName, fallbackBasename = true) { + const property = names.property(propertyName); + const propertyEntry = property.vendor && fallbackBasename + ? this.properties[property.name] || this.properties[property.basename] + : this.properties[property.name]; + + return propertyEntry || null; + } + getType(name) { + return hasOwnProperty.call(this.types, name) ? this.types[name] : null; + } + + validate() { + function syntaxRef(name, isType) { + return isType ? `<${name}>` : `<'${name}'>`; + } + + function validate(syntax, name, broken, descriptor) { + if (broken.has(name)) { + return broken.get(name); + } + + broken.set(name, false); + if (descriptor.syntax !== null) { + walk(descriptor.syntax, function(node) { + if (node.type !== 'Type' && node.type !== 'Property') { + return; + } + + const map = node.type === 'Type' ? syntax.types : syntax.properties; + const brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties; + + if (!hasOwnProperty.call(map, node.name)) { + errors.push(`${syntaxRef(name, broken === brokenTypes)} used missed syntax definition ${syntaxRef(node.name, node.type === 'Type')}`); + broken.set(name, true); + } else if (validate(syntax, node.name, brokenMap, map[node.name])) { + errors.push(`${syntaxRef(name, broken === brokenTypes)} used broken syntax definition ${syntaxRef(node.name, node.type === 'Type')}`); + broken.set(name, true); + } + }, this); + } + } + + const errors = []; + let brokenTypes = new Map(); + let brokenProperties = new Map(); + + for (const key in this.types) { + validate(this, key, brokenTypes, this.types[key]); + } + + for (const key in this.properties) { + validate(this, key, brokenProperties, this.properties[key]); + } + + const brokenTypesArray = [...brokenTypes.keys()].filter(name => brokenTypes.get(name)); + const brokenPropertiesArray = [...brokenProperties.keys()].filter(name => brokenProperties.get(name)); + + if (brokenTypesArray.length || brokenPropertiesArray.length) { + return { + errors, + types: brokenTypesArray, + properties: brokenPropertiesArray + }; + } + + return null; + } + dump(syntaxAsAst, pretty) { + return { + generic: this.generic, + cssWideKeywords: this.cssWideKeywords, + units: this.units, + types: dumpMapSyntax(this.types, !pretty, syntaxAsAst), + properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst), + atrules: dumpAtruleMapSyntax(this.atrules, !pretty, syntaxAsAst) + }; + } + toString() { + return JSON.stringify(this.dump()); + } +}; diff --git a/vanilla/node_modules/css-tree/lib/lexer/error.js b/vanilla/node_modules/css-tree/lib/lexer/error.js new file mode 100644 index 0000000..89a5400 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/error.js @@ -0,0 +1,123 @@ +import { createCustomError } from '../utils/create-custom-error.js'; +import { generate } from '../definition-syntax/generate.js'; + +const defaultLoc = { offset: 0, line: 1, column: 1 }; + +function locateMismatch(matchResult, node) { + const tokens = matchResult.tokens; + const longestMatch = matchResult.longestMatch; + const mismatchNode = longestMatch < tokens.length ? tokens[longestMatch].node || null : null; + const badNode = mismatchNode !== node ? mismatchNode : null; + let mismatchOffset = 0; + let mismatchLength = 0; + let entries = 0; + let css = ''; + let start; + let end; + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i].value; + + if (i === longestMatch) { + mismatchLength = token.length; + mismatchOffset = css.length; + } + + if (badNode !== null && tokens[i].node === badNode) { + if (i <= longestMatch) { + entries++; + } else { + entries = 0; + } + } + + css += token; + } + + if (longestMatch === tokens.length || entries > 1) { // last + start = fromLoc(badNode || node, 'end') || buildLoc(defaultLoc, css); + end = buildLoc(start); + } else { + start = fromLoc(badNode, 'start') || + buildLoc(fromLoc(node, 'start') || defaultLoc, css.slice(0, mismatchOffset)); + end = fromLoc(badNode, 'end') || + buildLoc(start, css.substr(mismatchOffset, mismatchLength)); + } + + return { + css, + mismatchOffset, + mismatchLength, + start, + end + }; +} + +function fromLoc(node, point) { + const value = node && node.loc && node.loc[point]; + + if (value) { + return 'line' in value ? buildLoc(value) : value; + } + + return null; +} + +function buildLoc({ offset, line, column }, extra) { + const loc = { + offset, + line, + column + }; + + if (extra) { + const lines = extra.split(/\n|\r\n?|\f/); + + loc.offset += extra.length; + loc.line += lines.length - 1; + loc.column = lines.length === 1 ? loc.column + extra.length : lines.pop().length + 1; + } + + return loc; +} + +export const SyntaxReferenceError = function(type, referenceName) { + const error = createCustomError( + 'SyntaxReferenceError', + type + (referenceName ? ' `' + referenceName + '`' : '') + ); + + error.reference = referenceName; + + return error; +}; + +export const SyntaxMatchError = function(message, syntax, node, matchResult) { + const error = createCustomError('SyntaxMatchError', message); + const { + css, + mismatchOffset, + mismatchLength, + start, + end + } = locateMismatch(matchResult, node); + + error.rawMessage = message; + error.syntax = syntax ? generate(syntax) : ''; + error.css = css; + error.mismatchOffset = mismatchOffset; + error.mismatchLength = mismatchLength; + error.message = message + '\n' + + ' syntax: ' + error.syntax + '\n' + + ' value: ' + (css || '') + '\n' + + ' --------' + new Array(error.mismatchOffset + 1).join('-') + '^'; + + Object.assign(error, start); + error.loc = { + source: (node && node.loc && node.loc.source) || '', + start, + end + }; + + return error; +}; diff --git a/vanilla/node_modules/css-tree/lib/lexer/generic-an-plus-b.js b/vanilla/node_modules/css-tree/lib/lexer/generic-an-plus-b.js new file mode 100644 index 0000000..347715c --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/generic-an-plus-b.js @@ -0,0 +1,238 @@ +import { + isDigit, + cmpChar, + Delim, + WhiteSpace, + Comment, + Ident, + Number as NumberToken, + Dimension +} from '../tokenizer/index.js'; + +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) +const N = 0x006E; // U+006E LATIN SMALL LETTER N (n) +const DISALLOW_SIGN = true; +const ALLOW_SIGN = false; + +function isDelim(token, code) { + return token !== null && token.type === Delim && token.value.charCodeAt(0) === code; +} + +function skipSC(token, offset, getNextToken) { + while (token !== null && (token.type === WhiteSpace || token.type === Comment)) { + token = getNextToken(++offset); + } + + return offset; +} + +function checkInteger(token, valueOffset, disallowSign, offset) { + if (!token) { + return 0; + } + + const code = token.value.charCodeAt(valueOffset); + + if (code === PLUSSIGN || code === HYPHENMINUS) { + if (disallowSign) { + // Number sign is not allowed + return 0; + } + valueOffset++; + } + + for (; valueOffset < token.value.length; valueOffset++) { + if (!isDigit(token.value.charCodeAt(valueOffset))) { + // Integer is expected + return 0; + } + } + + return offset + 1; +} + +// ... +// ... ['+' | '-'] +function consumeB(token, offset_, getNextToken) { + let sign = false; + let offset = skipSC(token, offset_, getNextToken); + + token = getNextToken(offset); + + if (token === null) { + return offset_; + } + + if (token.type !== NumberToken) { + if (isDelim(token, PLUSSIGN) || isDelim(token, HYPHENMINUS)) { + sign = true; + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + if (token === null || token.type !== NumberToken) { + return 0; + } + } else { + return offset_; + } + } + + if (!sign) { + const code = token.value.charCodeAt(0); + if (code !== PLUSSIGN && code !== HYPHENMINUS) { + // Number sign is expected + return 0; + } + } + + return checkInteger(token, sign ? 0 : 1, sign, offset); +} + +// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb +export default function anPlusB(token, getNextToken) { + /* eslint-disable brace-style*/ + let offset = 0; + + if (!token) { + return 0; + } + + // + if (token.type === NumberToken) { + return checkInteger(token, 0, ALLOW_SIGN, offset); // b + } + + // -n + // -n + // -n ['+' | '-'] + // -n- + // + else if (token.type === Ident && token.value.charCodeAt(0) === HYPHENMINUS) { + // expect 1st char is N + if (!cmpChar(token.value, 1, N)) { + return 0; + } + + switch (token.value.length) { + // -n + // -n + // -n ['+' | '-'] + case 2: + return consumeB(getNextToken(++offset), offset, getNextToken); + + // -n- + case 3: + if (token.value.charCodeAt(2) !== HYPHENMINUS) { + return 0; + } + + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + + // + default: + if (token.value.charCodeAt(2) !== HYPHENMINUS) { + return 0; + } + + return checkInteger(token, 3, DISALLOW_SIGN, offset); + } + } + + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + // '+'? n- + // '+'? + else if (token.type === Ident || (isDelim(token, PLUSSIGN) && getNextToken(offset + 1).type === Ident)) { + // just ignore a plus + if (token.type !== Ident) { + token = getNextToken(++offset); + } + + if (token === null || !cmpChar(token.value, 0, N)) { + return 0; + } + + switch (token.value.length) { + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + case 1: + return consumeB(getNextToken(++offset), offset, getNextToken); + + // '+'? n- + case 2: + if (token.value.charCodeAt(1) !== HYPHENMINUS) { + return 0; + } + + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + + // '+'? + default: + if (token.value.charCodeAt(1) !== HYPHENMINUS) { + return 0; + } + + return checkInteger(token, 2, DISALLOW_SIGN, offset); + } + } + + // + // + // + // + // ['+' | '-'] + else if (token.type === Dimension) { + let code = token.value.charCodeAt(0); + let sign = code === PLUSSIGN || code === HYPHENMINUS ? 1 : 0; + let i = sign; + + for (; i < token.value.length; i++) { + if (!isDigit(token.value.charCodeAt(i))) { + break; + } + } + + if (i === sign) { + // Integer is expected + return 0; + } + + if (!cmpChar(token.value, i, N)) { + return 0; + } + + // + // + // ['+' | '-'] + if (i + 1 === token.value.length) { + return consumeB(getNextToken(++offset), offset, getNextToken); + } else { + if (token.value.charCodeAt(i + 1) !== HYPHENMINUS) { + return 0; + } + + // + if (i + 2 === token.value.length) { + offset = skipSC(getNextToken(++offset), offset, getNextToken); + token = getNextToken(offset); + + return checkInteger(token, 0, DISALLOW_SIGN, offset); + } + // + else { + return checkInteger(token, i + 2, DISALLOW_SIGN, offset); + } + } + } + + return 0; +}; diff --git a/vanilla/node_modules/css-tree/lib/lexer/generic-const.js b/vanilla/node_modules/css-tree/lib/lexer/generic-const.js new file mode 100644 index 0000000..8efe6ae --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/generic-const.js @@ -0,0 +1,8 @@ +// https://drafts.csswg.org/css-cascade-5/ +export const cssWideKeywords = [ + 'initial', + 'inherit', + 'unset', + 'revert', + 'revert-layer' +]; diff --git a/vanilla/node_modules/css-tree/lib/lexer/generic-urange.js b/vanilla/node_modules/css-tree/lib/lexer/generic-urange.js new file mode 100644 index 0000000..d878409 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/generic-urange.js @@ -0,0 +1,151 @@ +import { + isHexDigit, + cmpChar, + Ident, + Delim, + Number as NumberToken, + Dimension +} from '../tokenizer/index.js'; + +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) +const QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?) +const U = 0x0075; // U+0075 LATIN SMALL LETTER U (u) + +function isDelim(token, code) { + return token !== null && token.type === Delim && token.value.charCodeAt(0) === code; +} + +function startsWith(token, code) { + return token.value.charCodeAt(0) === code; +} + +function hexSequence(token, offset, allowDash) { + let hexlen = 0; + + for (let pos = offset; pos < token.value.length; pos++) { + const code = token.value.charCodeAt(pos); + + if (code === HYPHENMINUS && allowDash && hexlen !== 0) { + hexSequence(token, offset + hexlen + 1, false); + return 6; // dissallow following question marks + } + + if (!isHexDigit(code)) { + return 0; // not a hex digit + } + + if (++hexlen > 6) { + return 0; // too many hex digits + }; + } + + return hexlen; +} + +function withQuestionMarkSequence(consumed, length, getNextToken) { + if (!consumed) { + return 0; // nothing consumed + } + + while (isDelim(getNextToken(length), QUESTIONMARK)) { + if (++consumed > 6) { + return 0; // too many question marks + } + + length++; + } + + return length; +} + +// https://drafts.csswg.org/css-syntax/#urange +// Informally, the production has three forms: +// U+0001 +// Defines a range consisting of a single code point, in this case the code point "1". +// U+0001-00ff +// Defines a range of codepoints between the first and the second value, in this case +// the range between "1" and "ff" (255 in decimal) inclusive. +// U+00?? +// Defines a range of codepoints where the "?" characters range over all hex digits, +// in this case defining the same as the value U+0000-00ff. +// In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit). +// +// = +// u '+' '?'* | +// u '?'* | +// u '?'* | +// u | +// u | +// u '+' '?'+ +export default function urange(token, getNextToken) { + let length = 0; + + // should start with `u` or `U` + if (token === null || token.type !== Ident || !cmpChar(token.value, 0, U)) { + return 0; + } + + token = getNextToken(++length); + if (token === null) { + return 0; + } + + // u '+' '?'* + // u '+' '?'+ + if (isDelim(token, PLUSSIGN)) { + token = getNextToken(++length); + if (token === null) { + return 0; + } + + if (token.type === Ident) { + // u '+' '?'* + return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken); + } + + if (isDelim(token, QUESTIONMARK)) { + // u '+' '?'+ + return withQuestionMarkSequence(1, ++length, getNextToken); + } + + // Hex digit or question mark is expected + return 0; + } + + // u '?'* + // u + // u + if (token.type === NumberToken) { + const consumedHexLength = hexSequence(token, 1, true); + if (consumedHexLength === 0) { + return 0; + } + + token = getNextToken(++length); + if (token === null) { + // u + return length; + } + + if (token.type === Dimension || token.type === NumberToken) { + // u + // u + if (!startsWith(token, HYPHENMINUS) || !hexSequence(token, 1, false)) { + return 0; + } + + return length + 1; + } + + // u '?'* + return withQuestionMarkSequence(consumedHexLength, length, getNextToken); + } + + // u '?'* + if (token.type === Dimension) { + return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken); + } + + return 0; +}; diff --git a/vanilla/node_modules/css-tree/lib/lexer/generic.js b/vanilla/node_modules/css-tree/lib/lexer/generic.js new file mode 100644 index 0000000..422e130 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/lexer/generic.js @@ -0,0 +1,622 @@ +import { cssWideKeywords } from './generic-const.js'; +import anPlusB from './generic-an-plus-b.js'; +import urange from './generic-urange.js'; +import { + isIdentifierStart, + isHexDigit, + isDigit, + cmpStr, + consumeNumber, + + Ident, + Function as FunctionToken, + AtKeyword, + Hash, + String as StringToken, + BadString, + Url, + BadUrl, + Delim, + Number as NumberToken, + Percentage, + Dimension, + WhiteSpace, + CDO, + CDC, + Colon, + Semicolon, + Comma, + LeftSquareBracket, + RightSquareBracket, + LeftParenthesis, + RightParenthesis, + LeftCurlyBracket, + RightCurlyBracket +} from '../tokenizer/index.js'; + +const calcFunctionNames = ['calc(', '-moz-calc(', '-webkit-calc(']; +const balancePair = new Map([ + [FunctionToken, RightParenthesis], + [LeftParenthesis, RightParenthesis], + [LeftSquareBracket, RightSquareBracket], + [LeftCurlyBracket, RightCurlyBracket] +]); + +// safe char code getter +function charCodeAt(str, index) { + return index < str.length ? str.charCodeAt(index) : 0; +} + +function eqStr(actual, expected) { + return cmpStr(actual, 0, actual.length, expected); +} + +function eqStrAny(actual, expected) { + for (let i = 0; i < expected.length; i++) { + if (eqStr(actual, expected[i])) { + return true; + } + } + + return false; +} + +// IE postfix hack, i.e. 123\0 or 123px\9 +function isPostfixIeHack(str, offset) { + if (offset !== str.length - 2) { + return false; + } + + return ( + charCodeAt(str, offset) === 0x005C && // U+005C REVERSE SOLIDUS (\) + isDigit(charCodeAt(str, offset + 1)) + ); +} + +function outOfRange(opts, value, numEnd) { + if (opts && opts.type === 'Range') { + const num = Number( + numEnd !== undefined && numEnd !== value.length + ? value.substr(0, numEnd) + : value + ); + + if (isNaN(num)) { + return true; + } + + // FIXME: when opts.min is a string it's a dimension, skip a range validation + // for now since it requires a type covertation which is not implmented yet + if (opts.min !== null && num < opts.min && typeof opts.min !== 'string') { + return true; + } + + // FIXME: when opts.max is a string it's a dimension, skip a range validation + // for now since it requires a type covertation which is not implmented yet + if (opts.max !== null && num > opts.max && typeof opts.max !== 'string') { + return true; + } + } + + return false; +} + +function consumeFunction(token, getNextToken) { + let balanceCloseType = 0; + let balanceStash = []; + let length = 0; + + // balanced token consuming + scan: + do { + switch (token.type) { + case RightCurlyBracket: + case RightParenthesis: + case RightSquareBracket: + if (token.type !== balanceCloseType) { + break scan; + } + + balanceCloseType = balanceStash.pop(); + + if (balanceStash.length === 0) { + length++; + break scan; + } + + break; + + case FunctionToken: + case LeftParenthesis: + case LeftSquareBracket: + case LeftCurlyBracket: + balanceStash.push(balanceCloseType); + balanceCloseType = balancePair.get(token.type); + break; + } + + length++; + } while (token = getNextToken(length)); + + return length; +} + +// TODO: implement +// can be used wherever , , ,