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) --- .../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 ++ 81 files changed, 3957 insertions(+) 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 (limited to 'vanilla/node_modules/css-tree/lib/syntax') diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/container.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/container.js new file mode 100644 index 0000000..aa550de --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/container.js @@ -0,0 +1,28 @@ +import { Ident } from '../../tokenizer/index.js'; + +// https://drafts.csswg.org/css-contain-3/#container-rule +// The keywords `none`, `and`, `not`, and `or` are excluded from the above. +const nonContainerNameKeywords = new Set(['none', 'and', 'not', 'or']); + +export default { + parse: { + prelude() { + const children = this.createList(); + + if (this.tokenType === Ident) { + const name = this.substring(this.tokenStart, this.tokenEnd); + + if (!nonContainerNameKeywords.has(name.toLowerCase())) { + children.push(this.Identifier()); + } + } + + children.push(this.Condition('container')); + + return children; + }, + block(nested = false) { + return this.Block(nested); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/font-face.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/font-face.js new file mode 100644 index 0000000..48a0570 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/font-face.js @@ -0,0 +1,8 @@ +export default { + parse: { + prelude: null, + block() { + return this.Block(true); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/import.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/import.js new file mode 100644 index 0000000..4e5a637 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/import.js @@ -0,0 +1,104 @@ +import { + String as StringToken, + Ident, + Url, + Function as FunctionToken, + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + +function parseWithFallback(parse, fallback) { + return this.parseWithFallback( + () => { + try { + return parse.call(this); + } finally { + this.skipSC(); + if (this.lookupNonWSType(0) !== RightParenthesis) { + this.error(); + } + } + }, + fallback || (() => this.Raw(null, true)) + ); +} + +const parseFunctions = { + layer() { + this.skipSC(); + + const children = this.createList(); + const node = parseWithFallback.call(this, this.Layer); + + if (node.type !== 'Raw' || node.value !== '') { + children.push(node); + } + + return children; + }, + supports() { + this.skipSC(); + + const children = this.createList(); + const node = parseWithFallback.call( + this, + this.Declaration, + () => parseWithFallback.call(this, () => this.Condition('supports')) + ); + + if (node.type !== 'Raw' || node.value !== '') { + children.push(node); + } + + return children; + } +}; + +export default { + parse: { + prelude() { + const children = this.createList(); + + switch (this.tokenType) { + case StringToken: + children.push(this.String()); + break; + + case Url: + case FunctionToken: + children.push(this.Url()); + break; + + default: + this.error('String or url() is expected'); + } + + this.skipSC(); + + if (this.tokenType === Ident && + this.cmpStr(this.tokenStart, this.tokenEnd, 'layer')) { + children.push(this.Identifier()); + } else if ( + this.tokenType === FunctionToken && + this.cmpStr(this.tokenStart, this.tokenEnd, 'layer(') + ) { + children.push(this.Function(null, parseFunctions)); + } + + this.skipSC(); + + if (this.tokenType === FunctionToken && + this.cmpStr(this.tokenStart, this.tokenEnd, 'supports(')) { + children.push(this.Function(null, parseFunctions)); + } + + if (this.lookupNonWSType(0) === Ident || + this.lookupNonWSType(0) === LeftParenthesis) { + children.push(this.MediaQueryList()); + } + + return children; + }, + block: null + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/index.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/index.js new file mode 100644 index 0000000..46ac7f8 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/index.js @@ -0,0 +1,23 @@ +import container from './container.js'; +import fontFace from './font-face.js'; +import importAtrule from './import.js'; +import layer from './layer.js'; +import media from './media.js'; +import nest from './nest.js'; +import page from './page.js'; +import scope from './scope.js'; +import startingStyle from './starting-style.js'; +import supports from './supports.js'; + +export default { + container, + 'font-face': fontFace, + import: importAtrule, + layer, + media, + nest, + page, + scope, + 'starting-style': startingStyle, + supports +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/layer.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/layer.js new file mode 100644 index 0000000..232eb29 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/layer.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.LayerList() + ); + }, + block() { + return this.Block(false); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/media.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/media.js new file mode 100644 index 0000000..24d4608 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/media.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.MediaQueryList() + ); + }, + block(nested = false) { + return this.Block(nested); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/nest.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/nest.js new file mode 100644 index 0000000..99fc15e --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/nest.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.SelectorList() + ); + }, + block() { + return this.Block(true); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/page.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/page.js new file mode 100644 index 0000000..99fc15e --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/page.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.SelectorList() + ); + }, + block() { + return this.Block(true); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/scope.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/scope.js new file mode 100644 index 0000000..7185ca1 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/scope.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.Scope() + ); + }, + block(nested = false) { + return this.Block(nested); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/starting-style.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/starting-style.js new file mode 100644 index 0000000..b964704 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/starting-style.js @@ -0,0 +1,8 @@ +export default { + parse: { + prelude: null, + block(nested = false) { + return this.Block(nested); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/atrule/supports.js b/vanilla/node_modules/css-tree/lib/syntax/atrule/supports.js new file mode 100644 index 0000000..bdfffce --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/atrule/supports.js @@ -0,0 +1,12 @@ +export default { + parse: { + prelude() { + return this.createSingleNodeList( + this.Condition('supports') + ); + }, + block(nested = false) { + return this.Block(nested); + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/generator.js b/vanilla/node_modules/css-tree/lib/syntax/config/generator.js new file mode 100644 index 0000000..82e874c --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/generator.js @@ -0,0 +1,5 @@ +import * as node from '../node/index-generate.js'; + +export default { + node +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/lexer.js b/vanilla/node_modules/css-tree/lib/syntax/config/lexer.js new file mode 100644 index 0000000..b479ac5 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/lexer.js @@ -0,0 +1,10 @@ +import { cssWideKeywords } from '../../lexer/generic-const.js'; +import definitions from '../../data.js'; +import * as node from '../node/index.js'; + +export default { + generic: true, + cssWideKeywords, + ...definitions, + node +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/mix.js b/vanilla/node_modules/css-tree/lib/syntax/config/mix.js new file mode 100644 index 0000000..f722ceb --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/mix.js @@ -0,0 +1,123 @@ +function appendOrSet(a, b) { + if (typeof b === 'string' && /^\s*\|/.test(b)) { + return typeof a === 'string' + ? a + b + : b.replace(/^\s*\|\s*/, ''); + } + + return b || null; +} + +function sliceProps(obj, props) { + const result = Object.create(null); + + for (const [key, value] of Object.entries(obj)) { + if (value) { + result[key] = {}; + for (const prop of Object.keys(value)) { + if (props.includes(prop)) { + result[key][prop] = value[prop]; + } + } + } + } + + return result; +} + +export default function mix(dest, src) { + const result = { ...dest }; + + for (const [prop, value] of Object.entries(src)) { + switch (prop) { + case 'generic': + result[prop] = Boolean(value); + break; + + case 'cssWideKeywords': + result[prop] = dest[prop] + ? [...dest[prop], ...value] + : value || []; + break; + + case 'units': + result[prop] = { ...dest[prop] }; + for (const [name, patch] of Object.entries(value)) { + result[prop][name] = Array.isArray(patch) ? patch : []; + } + break; + + case 'atrules': + result[prop] = { ...dest[prop] }; + + for (const [name, atrule] of Object.entries(value)) { + const exists = result[prop][name] || {}; + const current = result[prop][name] = { + prelude: exists.prelude || null, + descriptors: { + ...exists.descriptors + } + }; + + if (!atrule) { + continue; + } + + current.prelude = atrule.prelude + ? appendOrSet(current.prelude, atrule.prelude) + : current.prelude || null; + + for (const [descriptorName, descriptorValue] of Object.entries(atrule.descriptors || {})) { + current.descriptors[descriptorName] = descriptorValue + ? appendOrSet(current.descriptors[descriptorName], descriptorValue) + : null; + } + + if (!Object.keys(current.descriptors).length) { + current.descriptors = null; + } + } + break; + + case 'types': + case 'properties': + result[prop] = { ...dest[prop] }; + for (const [name, syntax] of Object.entries(value)) { + result[prop][name] = appendOrSet(result[prop][name], syntax); + } + break; + + case 'scope': + case 'features': + result[prop] = { ...dest[prop] }; + for (const [name, props] of Object.entries(value)) { + result[prop][name] = { ...result[prop][name], ...props }; + } + break; + + case 'parseContext': + result[prop] = { + ...dest[prop], + ...value + }; + break; + + case 'atrule': + case 'pseudo': + result[prop] = { + ...dest[prop], + ...sliceProps(value, ['parse']) + }; + break; + + case 'node': + result[prop] = { + ...dest[prop], + ...sliceProps(value, ['name', 'structure', 'parse', 'generate', 'walkContext']) + }; + break; + } + } + + return result; +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/parser-selector.js b/vanilla/node_modules/css-tree/lib/syntax/config/parser-selector.js new file mode 100644 index 0000000..c9e0ee2 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/parser-selector.js @@ -0,0 +1,15 @@ +import { Selector } from '../scope/index.js'; +import pseudo from '../pseudo/index.js'; +import * as node from '../node/index-parse-selector.js'; + +export default { + parseContext: { + default: 'SelectorList', + selectorList: 'SelectorList', + selector: 'Selector' + }, + scope: { Selector }, + atrule: {}, + pseudo, + node +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/parser.js b/vanilla/node_modules/css-tree/lib/syntax/config/parser.js new file mode 100644 index 0000000..0b455aa --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/parser.js @@ -0,0 +1,45 @@ +import * as scope from '../scope/index.js'; +import atrule from '../atrule/index.js'; +import pseudo from '../pseudo/index.js'; +import * as node from '../node/index-parse.js'; + +export default { + parseContext: { + default: 'StyleSheet', + stylesheet: 'StyleSheet', + atrule: 'Atrule', + atrulePrelude(options) { + return this.AtrulePrelude(options.atrule ? String(options.atrule) : null); + }, + mediaQueryList: 'MediaQueryList', + mediaQuery: 'MediaQuery', + condition(options) { + return this.Condition(options.kind); + }, + rule: 'Rule', + selectorList: 'SelectorList', + selector: 'Selector', + block() { + return this.Block(true); + }, + declarationList: 'DeclarationList', + declaration: 'Declaration', + value: 'Value' + }, + features: { + supports: { + selector() { + return this.Selector(); + } + }, + container: { + style() { + return this.Declaration(); + } + } + }, + scope, + atrule, + pseudo, + node +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/config/walker.js b/vanilla/node_modules/css-tree/lib/syntax/config/walker.js new file mode 100644 index 0000000..215d024 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/config/walker.js @@ -0,0 +1,5 @@ +import * as node from '../node/index.js'; + +export default { + node +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/create.js b/vanilla/node_modules/css-tree/lib/syntax/create.js new file mode 100644 index 0000000..9bb32c5 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/create.js @@ -0,0 +1,55 @@ +import { tokenize } from '../tokenizer/index.js'; +import { createParser } from '../parser/create.js'; +import { createGenerator } from '../generator/create.js'; +import { createConvertor } from '../convertor/create.js'; +import { createWalker } from '../walker/create.js'; +import { Lexer } from '../lexer/Lexer.js'; +import mix from './config/mix.js'; + +function createSyntax(config) { + const parse = createParser(config); + const walk = createWalker(config); + const generate = createGenerator(config); + const { fromPlainObject, toPlainObject } = createConvertor(walk); + + const syntax = { + lexer: null, + createLexer: config => new Lexer(config, syntax, syntax.lexer.structure), + + tokenize, + parse, + generate, + + walk, + find: walk.find, + findLast: walk.findLast, + findAll: walk.findAll, + + fromPlainObject, + toPlainObject, + + fork(extension) { + const base = mix({}, config); // copy of config + + return createSyntax( + typeof extension === 'function' + ? extension(base) // TODO: remove Object.assign as second parameter + : mix(base, extension) + ); + } + }; + + syntax.lexer = new Lexer({ + generic: config.generic, + cssWideKeywords: config.cssWideKeywords, + units: config.units, + types: config.types, + atrules: config.atrules, + properties: config.properties, + node: config.node + }, syntax); + + return syntax; +}; + +export default config => createSyntax(mix({}, config)); diff --git a/vanilla/node_modules/css-tree/lib/syntax/function/expression.js b/vanilla/node_modules/css-tree/lib/syntax/function/expression.js new file mode 100644 index 0000000..040f826 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/function/expression.js @@ -0,0 +1,7 @@ +// legacy IE function +// expression( ) +export default function() { + return this.createSingleNodeList( + this.Raw(null, false) + ); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/function/var.js b/vanilla/node_modules/css-tree/lib/syntax/function/var.js new file mode 100644 index 0000000..010dc0a --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/function/var.js @@ -0,0 +1,39 @@ +import { Comma, WhiteSpace } from '../../tokenizer/index.js'; + +// var( , ? ) +export default function() { + const children = this.createList(); + + this.skipSC(); + + // NOTE: Don't check more than a first argument is an ident, rest checks are for lexer + children.push(this.Identifier()); + + this.skipSC(); + + if (this.tokenType === Comma) { + children.push(this.Operator()); + + const startIndex = this.tokenIndex; + const value = this.parseCustomProperty + ? this.Value(null) + : this.Raw(this.consumeUntilExclamationMarkOrSemicolon, false); + + if (value.type === 'Value' && value.children.isEmpty) { + for (let offset = startIndex - this.tokenIndex; offset <= 0; offset++) { + if (this.lookupType(offset) === WhiteSpace) { + value.children.appendData({ + type: 'WhiteSpace', + loc: null, + value: ' ' + }); + break; + } + } + } + + children.push(value); + } + + return children; +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/index.js b/vanilla/node_modules/css-tree/lib/syntax/index.js new file mode 100644 index 0000000..c8c9152 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/index.js @@ -0,0 +1,10 @@ +import createSyntax from './create.js'; +import lexerConfig from './config/lexer.js'; +import parserConfig from './config/parser.js'; +import walkerConfig from './config/walker.js'; + +export default createSyntax({ + ...lexerConfig, + ...parserConfig, + ...walkerConfig +}); diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/AnPlusB.js b/vanilla/node_modules/css-tree/lib/syntax/node/AnPlusB.js new file mode 100644 index 0000000..05c7e44 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/AnPlusB.js @@ -0,0 +1,292 @@ +import { + isDigit, + WhiteSpace, + Comment, + Ident, + Number, + 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 checkInteger(offset, disallowSign) { + let pos = this.tokenStart + offset; + const code = this.charCodeAt(pos); + + if (code === PLUSSIGN || code === HYPHENMINUS) { + if (disallowSign) { + this.error('Number sign is not allowed'); + } + pos++; + } + + for (; pos < this.tokenEnd; pos++) { + if (!isDigit(this.charCodeAt(pos))) { + this.error('Integer is expected', pos); + } + } +} + +function checkTokenIsInteger(disallowSign) { + return checkInteger.call(this, 0, disallowSign); +} + +function expectCharCode(offset, code) { + if (!this.cmpChar(this.tokenStart + offset, code)) { + let msg = ''; + + switch (code) { + case N: + msg = 'N is expected'; + break; + case HYPHENMINUS: + msg = 'HyphenMinus is expected'; + break; + } + + this.error(msg, this.tokenStart + offset); + } +} + +// ... +// ... ['+' | '-'] +function consumeB() { + let offset = 0; + let sign = 0; + let type = this.tokenType; + + while (type === WhiteSpace || type === Comment) { + type = this.lookupType(++offset); + } + + if (type !== Number) { + if (this.isDelim(PLUSSIGN, offset) || + this.isDelim(HYPHENMINUS, offset)) { + sign = this.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS; + + do { + type = this.lookupType(++offset); + } while (type === WhiteSpace || type === Comment); + + if (type !== Number) { + this.skip(offset); + checkTokenIsInteger.call(this, DISALLOW_SIGN); + } + } else { + return null; + } + } + + if (offset > 0) { + this.skip(offset); + } + + if (sign === 0) { + type = this.charCodeAt(this.tokenStart); + if (type !== PLUSSIGN && type !== HYPHENMINUS) { + this.error('Number sign is expected'); + } + } + + checkTokenIsInteger.call(this, sign !== 0); + return sign === HYPHENMINUS ? '-' + this.consume(Number) : this.consume(Number); +} + +// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb +export const name = 'AnPlusB'; +export const structure = { + a: [String, null], + b: [String, null] +}; + +export function parse() { + /* eslint-disable brace-style*/ + const start = this.tokenStart; + let a = null; + let b = null; + + // + if (this.tokenType === Number) { + checkTokenIsInteger.call(this, ALLOW_SIGN); + b = this.consume(Number); + } + + // -n + // -n + // -n ['+' | '-'] + // -n- + // + else if (this.tokenType === Ident && this.cmpChar(this.tokenStart, HYPHENMINUS)) { + a = '-1'; + + expectCharCode.call(this, 1, N); + + switch (this.tokenEnd - this.tokenStart) { + // -n + // -n + // -n ['+' | '-'] + case 2: + this.next(); + b = consumeB.call(this); + break; + + // -n- + case 3: + expectCharCode.call(this, 2, HYPHENMINUS); + + this.next(); + this.skipSC(); + + checkTokenIsInteger.call(this, DISALLOW_SIGN); + + b = '-' + this.consume(Number); + break; + + // + default: + expectCharCode.call(this, 2, HYPHENMINUS); + checkInteger.call(this, 3, DISALLOW_SIGN); + this.next(); + + b = this.substrToCursor(start + 2); + } + } + + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + // '+'? n- + // '+'? + else if (this.tokenType === Ident || (this.isDelim(PLUSSIGN) && this.lookupType(1) === Ident)) { + let sign = 0; + a = '1'; + + // just ignore a plus + if (this.isDelim(PLUSSIGN)) { + sign = 1; + this.next(); + } + + expectCharCode.call(this, 0, N); + + switch (this.tokenEnd - this.tokenStart) { + // '+'? n + // '+'? n + // '+'? n ['+' | '-'] + case 1: + this.next(); + b = consumeB.call(this); + break; + + // '+'? n- + case 2: + expectCharCode.call(this, 1, HYPHENMINUS); + + this.next(); + this.skipSC(); + + checkTokenIsInteger.call(this, DISALLOW_SIGN); + + b = '-' + this.consume(Number); + break; + + // '+'? + default: + expectCharCode.call(this, 1, HYPHENMINUS); + checkInteger.call(this, 2, DISALLOW_SIGN); + this.next(); + + b = this.substrToCursor(start + sign + 1); + } + } + + // + // + // + // + // ['+' | '-'] + else if (this.tokenType === Dimension) { + const code = this.charCodeAt(this.tokenStart); + const sign = code === PLUSSIGN || code === HYPHENMINUS; + let i = this.tokenStart + sign; + + for (; i < this.tokenEnd; i++) { + if (!isDigit(this.charCodeAt(i))) { + break; + } + } + + if (i === this.tokenStart + sign) { + this.error('Integer is expected', this.tokenStart + sign); + } + + expectCharCode.call(this, i - this.tokenStart, N); + a = this.substring(start, i); + + // + // + // ['+' | '-'] + if (i + 1 === this.tokenEnd) { + this.next(); + b = consumeB.call(this); + } else { + expectCharCode.call(this, i - this.tokenStart + 1, HYPHENMINUS); + + // + if (i + 2 === this.tokenEnd) { + this.next(); + this.skipSC(); + checkTokenIsInteger.call(this, DISALLOW_SIGN); + b = '-' + this.consume(Number); + } + // + else { + checkInteger.call(this, i - this.tokenStart + 2, DISALLOW_SIGN); + this.next(); + b = this.substrToCursor(i + 1); + } + } + } else { + this.error(); + } + + if (a !== null && a.charCodeAt(0) === PLUSSIGN) { + a = a.substr(1); + } + + if (b !== null && b.charCodeAt(0) === PLUSSIGN) { + b = b.substr(1); + } + + return { + type: 'AnPlusB', + loc: this.getLocation(start, this.tokenStart), + a, + b + }; +} + +export function generate(node) { + if (node.a) { + const a = + node.a === '+1' && 'n' || + node.a === '1' && 'n' || + node.a === '-1' && '-n' || + node.a + 'n'; + + if (node.b) { + const b = node.b[0] === '-' || node.b[0] === '+' + ? node.b + : '+' + node.b; + this.tokenize(a + b); + } else { + this.tokenize(a); + } + } else { + this.tokenize(node.b); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Atrule.js b/vanilla/node_modules/css-tree/lib/syntax/node/Atrule.js new file mode 100644 index 0000000..99d1284 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Atrule.js @@ -0,0 +1,100 @@ +import { + AtKeyword, + Semicolon, + LeftCurlyBracket, + RightCurlyBracket +} from '../../tokenizer/index.js'; + +function consumeRaw() { + return this.Raw(this.consumeUntilLeftCurlyBracketOrSemicolon, true); +} + +function isDeclarationBlockAtrule() { + for (let offset = 1, type; type = this.lookupType(offset); offset++) { + if (type === RightCurlyBracket) { + return true; + } + + if (type === LeftCurlyBracket || + type === AtKeyword) { + return false; + } + } + + return false; +} + + +export const name = 'Atrule'; +export const walkContext = 'atrule'; +export const structure = { + name: String, + prelude: ['AtrulePrelude', 'Raw', null], + block: ['Block', null] +}; + +export function parse(isDeclaration = false) { + const start = this.tokenStart; + let name; + let nameLowerCase; + let prelude = null; + let block = null; + + this.eat(AtKeyword); + + name = this.substrToCursor(start + 1); + nameLowerCase = name.toLowerCase(); + this.skipSC(); + + // parse prelude + if (this.eof === false && + this.tokenType !== LeftCurlyBracket && + this.tokenType !== Semicolon) { + if (this.parseAtrulePrelude) { + prelude = this.parseWithFallback(this.AtrulePrelude.bind(this, name, isDeclaration), consumeRaw); + } else { + prelude = consumeRaw.call(this, this.tokenIndex); + } + + this.skipSC(); + } + + switch (this.tokenType) { + case Semicolon: + this.next(); + break; + + case LeftCurlyBracket: + if (hasOwnProperty.call(this.atrule, nameLowerCase) && + typeof this.atrule[nameLowerCase].block === 'function') { + block = this.atrule[nameLowerCase].block.call(this, isDeclaration); + } else { + // TODO: should consume block content as Raw? + block = this.Block(isDeclarationBlockAtrule.call(this)); + } + + break; + } + + return { + type: 'Atrule', + loc: this.getLocation(start, this.tokenStart), + name, + prelude, + block + }; +} + +export function generate(node) { + this.token(AtKeyword, '@' + node.name); + + if (node.prelude !== null) { + this.node(node.prelude); + } + + if (node.block) { + this.node(node.block); + } else { + this.token(Semicolon, ';'); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js b/vanilla/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js new file mode 100644 index 0000000..5b5645a --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/AtrulePrelude.js @@ -0,0 +1,47 @@ +import { + Semicolon, + LeftCurlyBracket +} from '../../tokenizer/index.js'; + +export const name = 'AtrulePrelude'; +export const walkContext = 'atrulePrelude'; +export const structure = { + children: [[]] +}; + +export function parse(name) { + let children = null; + + if (name !== null) { + name = name.toLowerCase(); + } + + this.skipSC(); + + if (hasOwnProperty.call(this.atrule, name) && + typeof this.atrule[name].prelude === 'function') { + // custom consumer + children = this.atrule[name].prelude.call(this); + } else { + // default consumer + children = this.readSequence(this.scope.AtrulePrelude); + } + + this.skipSC(); + + if (this.eof !== true && + this.tokenType !== LeftCurlyBracket && + this.tokenType !== Semicolon) { + this.error('Semicolon or block is expected'); + } + + return { + type: 'AtrulePrelude', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/AttributeSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/AttributeSelector.js new file mode 100644 index 0000000..8578dad --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/AttributeSelector.js @@ -0,0 +1,147 @@ +import { + Ident, + String as StringToken, + Delim, + LeftSquareBracket, + RightSquareBracket +} from '../../tokenizer/index.js'; + +const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($) +const ASTERISK = 0x002A; // U+002A ASTERISK (*) +const EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=) +const CIRCUMFLEXACCENT = 0x005E; // U+005E (^) +const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|) +const TILDE = 0x007E; // U+007E TILDE (~) + +function getAttributeName() { + if (this.eof) { + this.error('Unexpected end of input'); + } + + const start = this.tokenStart; + let expectIdent = false; + + if (this.isDelim(ASTERISK)) { + expectIdent = true; + this.next(); + } else if (!this.isDelim(VERTICALLINE)) { + this.eat(Ident); + } + + if (this.isDelim(VERTICALLINE)) { + if (this.charCodeAt(this.tokenStart + 1) !== EQUALSSIGN) { + this.next(); + this.eat(Ident); + } else if (expectIdent) { + this.error('Identifier is expected', this.tokenEnd); + } + } else if (expectIdent) { + this.error('Vertical line is expected'); + } + + return { + type: 'Identifier', + loc: this.getLocation(start, this.tokenStart), + name: this.substrToCursor(start) + }; +} + +function getOperator() { + const start = this.tokenStart; + const code = this.charCodeAt(start); + + if (code !== EQUALSSIGN && // = + code !== TILDE && // ~= + code !== CIRCUMFLEXACCENT && // ^= + code !== DOLLARSIGN && // $= + code !== ASTERISK && // *= + code !== VERTICALLINE // |= + ) { + this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected'); + } + + this.next(); + + if (code !== EQUALSSIGN) { + if (!this.isDelim(EQUALSSIGN)) { + this.error('Equal sign is expected'); + } + + this.next(); + } + + return this.substrToCursor(start); +} + +// '[' ']' +// '[' [ | ] ? ']' +export const name = 'AttributeSelector'; +export const structure = { + name: 'Identifier', + matcher: [String, null], + value: ['String', 'Identifier', null], + flags: [String, null] +}; + +export function parse() { + const start = this.tokenStart; + let name; + let matcher = null; + let value = null; + let flags = null; + + this.eat(LeftSquareBracket); + this.skipSC(); + + name = getAttributeName.call(this); + this.skipSC(); + + if (this.tokenType !== RightSquareBracket) { + // avoid case `[name i]` + if (this.tokenType !== Ident) { + matcher = getOperator.call(this); + + this.skipSC(); + + value = this.tokenType === StringToken + ? this.String() + : this.Identifier(); + + this.skipSC(); + } + + // attribute flags + if (this.tokenType === Ident) { + flags = this.consume(Ident); + + this.skipSC(); + } + } + + this.eat(RightSquareBracket); + + return { + type: 'AttributeSelector', + loc: this.getLocation(start, this.tokenStart), + name, + matcher, + value, + flags + }; +} + +export function generate(node) { + this.token(Delim, '['); + this.node(node.name); + + if (node.matcher !== null) { + this.tokenize(node.matcher); + this.node(node.value); + } + + if (node.flags !== null) { + this.token(Ident, node.flags); + } + + this.token(Delim, ']'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Block.js b/vanilla/node_modules/css-tree/lib/syntax/node/Block.js new file mode 100644 index 0000000..10bf6fc --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Block.js @@ -0,0 +1,95 @@ +import { + WhiteSpace, + Comment, + Semicolon, + AtKeyword, + LeftCurlyBracket, + RightCurlyBracket +} from '../../tokenizer/index.js'; + +const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&) + +function consumeRaw() { + return this.Raw(null, true); +} +function consumeRule() { + return this.parseWithFallback(this.Rule, consumeRaw); +} +function consumeRawDeclaration() { + return this.Raw(this.consumeUntilSemicolonIncluded, true); +} +function consumeDeclaration() { + if (this.tokenType === Semicolon) { + return consumeRawDeclaration.call(this, this.tokenIndex); + } + + const node = this.parseWithFallback(this.Declaration, consumeRawDeclaration); + + if (this.tokenType === Semicolon) { + this.next(); + } + + return node; +} + +export const name = 'Block'; +export const walkContext = 'block'; +export const structure = { + children: [[ + 'Atrule', + 'Rule', + 'Declaration' + ]] +}; + +export function parse(isStyleBlock) { + const consumer = isStyleBlock ? consumeDeclaration : consumeRule; + const start = this.tokenStart; + let children = this.createList(); + + this.eat(LeftCurlyBracket); + + scan: + while (!this.eof) { + switch (this.tokenType) { + case RightCurlyBracket: + break scan; + + case WhiteSpace: + case Comment: + this.next(); + break; + + case AtKeyword: + children.push(this.parseWithFallback(this.Atrule.bind(this, isStyleBlock), consumeRaw)); + break; + + default: + if (isStyleBlock && this.isDelim(AMPERSAND)) { + children.push(consumeRule.call(this)); + } else { + children.push(consumer.call(this)); + } + } + } + + if (!this.eof) { + this.eat(RightCurlyBracket); + } + + return { + type: 'Block', + loc: this.getLocation(start, this.tokenStart), + children + }; +} + +export function generate(node) { + this.token(LeftCurlyBracket, '{'); + this.children(node, prev => { + if (prev.type === 'Declaration') { + this.token(Semicolon, ';'); + } + }); + this.token(RightCurlyBracket, '}'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Brackets.js b/vanilla/node_modules/css-tree/lib/syntax/node/Brackets.js new file mode 100644 index 0000000..1d97a4c --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Brackets.js @@ -0,0 +1,35 @@ +import { + Delim, + LeftSquareBracket, + RightSquareBracket +} from '../../tokenizer/index.js'; + +export const name = 'Brackets'; +export const structure = { + children: [[]] +}; + +export function parse(readSequence, recognizer) { + const start = this.tokenStart; + let children = null; + + this.eat(LeftSquareBracket); + + children = readSequence.call(this, recognizer); + + if (!this.eof) { + this.eat(RightSquareBracket); + } + + return { + type: 'Brackets', + loc: this.getLocation(start, this.tokenStart), + children + }; +} + +export function generate(node) { + this.token(Delim, '['); + this.children(node); + this.token(Delim, ']'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/CDC.js b/vanilla/node_modules/css-tree/lib/syntax/node/CDC.js new file mode 100644 index 0000000..efed4a6 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/CDC.js @@ -0,0 +1,19 @@ +import { CDC } from '../../tokenizer/index.js'; + +export const name = 'CDC'; +export const structure = []; + +export function parse() { + const start = this.tokenStart; + + this.eat(CDC); // --> + + return { + type: 'CDC', + loc: this.getLocation(start, this.tokenStart) + }; +} + +export function generate() { + this.token(CDC, '-->'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/CDO.js b/vanilla/node_modules/css-tree/lib/syntax/node/CDO.js new file mode 100644 index 0000000..3a9de89 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/CDO.js @@ -0,0 +1,19 @@ +import { CDO } from '../../tokenizer/index.js'; + +export const name = 'CDO'; +export const structure = []; + +export function parse() { + const start = this.tokenStart; + + this.eat(CDO); // + child = this.CDC(); + break; + + // CSS Syntax Module Level 3 + // §2.2 Error handling + // At the "top level" of a stylesheet, an starts an at-rule. + case AtKeyword: + child = this.parseWithFallback(this.Atrule, consumeRaw); + break; + + // Anything else starts a qualified rule ... + default: + child = this.parseWithFallback(this.Rule, consumeRaw); + } + + children.push(child); + } + + return { + type: 'StyleSheet', + loc: this.getLocation(start, this.tokenStart), + children + }; +} + +export function generate(node) { + this.children(node); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/SupportsDeclaration.js b/vanilla/node_modules/css-tree/lib/syntax/node/SupportsDeclaration.js new file mode 100644 index 0000000..ee816e5 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/SupportsDeclaration.js @@ -0,0 +1,34 @@ +import { + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'SupportsDeclaration'; +export const structure = { + declaration: 'Declaration' +}; + +export function parse() { + const start = this.tokenStart; + + this.eat(LeftParenthesis); + this.skipSC(); + + const declaration = this.Declaration(); + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'SupportsDeclaration', + loc: this.getLocation(start, this.tokenStart), + declaration + }; +} + +export function generate(node) { + this.token(LeftParenthesis, '('); + this.node(node.declaration); + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/TypeSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/TypeSelector.js new file mode 100644 index 0000000..272e195 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/TypeSelector.js @@ -0,0 +1,52 @@ +import { Ident } from '../../tokenizer/index.js'; + +const ASTERISK = 0x002A; // U+002A ASTERISK (*) +const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|) + +function eatIdentifierOrAsterisk() { + if (this.tokenType !== Ident && + this.isDelim(ASTERISK) === false) { + this.error('Identifier or asterisk is expected'); + } + + this.next(); +} + +export const name = 'TypeSelector'; +export const structure = { + name: String +}; + +// ident +// ident|ident +// ident|* +// * +// *|ident +// *|* +// |ident +// |* +export function parse() { + const start = this.tokenStart; + + if (this.isDelim(VERTICALLINE)) { + this.next(); + eatIdentifierOrAsterisk.call(this); + } else { + eatIdentifierOrAsterisk.call(this); + + if (this.isDelim(VERTICALLINE)) { + this.next(); + eatIdentifierOrAsterisk.call(this); + } + } + + return { + type: 'TypeSelector', + loc: this.getLocation(start, this.tokenStart), + name: this.substrToCursor(start) + }; +} + +export function generate(node) { + this.tokenize(node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/UnicodeRange.js b/vanilla/node_modules/css-tree/lib/syntax/node/UnicodeRange.js new file mode 100644 index 0000000..95ee8b9 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/UnicodeRange.js @@ -0,0 +1,156 @@ +import { + isHexDigit, + Ident, + Number, + 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 (?) + +function eatHexSequence(offset, allowDash) { + let len = 0; + + for (let pos = this.tokenStart + offset; pos < this.tokenEnd; pos++) { + const code = this.charCodeAt(pos); + + if (code === HYPHENMINUS && allowDash && len !== 0) { + eatHexSequence.call(this, offset + len + 1, false); + return -1; + } + + if (!isHexDigit(code)) { + this.error( + allowDash && len !== 0 + ? 'Hyphen minus' + (len < 6 ? ' or hex digit' : '') + ' is expected' + : (len < 6 ? 'Hex digit is expected' : 'Unexpected input'), + pos + ); + } + + if (++len > 6) { + this.error('Too many hex digits', pos); + }; + } + + this.next(); + return len; +} + +function eatQuestionMarkSequence(max) { + let count = 0; + + while (this.isDelim(QUESTIONMARK)) { + if (++count > max) { + this.error('Too many question marks'); + } + + this.next(); + } +} + +function startsWith(code) { + if (this.charCodeAt(this.tokenStart) !== code) { + this.error((code === PLUSSIGN ? 'Plus sign' : 'Hyphen minus') + ' is expected'); + } +} + +// 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 '+' '?'+ +function scanUnicodeRange() { + let hexLength = 0; + + switch (this.tokenType) { + case Number: + // u '?'* + // u + // u + hexLength = eatHexSequence.call(this, 1, true); + + if (this.isDelim(QUESTIONMARK)) { + eatQuestionMarkSequence.call(this, 6 - hexLength); + break; + } + + if (this.tokenType === Dimension || + this.tokenType === Number) { + startsWith.call(this, HYPHENMINUS); + eatHexSequence.call(this, 1, false); + break; + } + + break; + + case Dimension: + // u '?'* + hexLength = eatHexSequence.call(this, 1, true); + + if (hexLength > 0) { + eatQuestionMarkSequence.call(this, 6 - hexLength); + } + + break; + + default: + // u '+' '?'* + // u '+' '?'+ + this.eatDelim(PLUSSIGN); + + if (this.tokenType === Ident) { + hexLength = eatHexSequence.call(this, 0, true); + if (hexLength > 0) { + eatQuestionMarkSequence.call(this, 6 - hexLength); + } + break; + } + + if (this.isDelim(QUESTIONMARK)) { + this.next(); + eatQuestionMarkSequence.call(this, 5); + break; + } + + this.error('Hex digit or question mark is expected'); + } +} + +export const name = 'UnicodeRange'; +export const structure = { + value: String +}; + +export function parse() { + const start = this.tokenStart; + + // U or u + this.eatIdent('u'); + scanUnicodeRange.call(this); + + return { + type: 'UnicodeRange', + loc: this.getLocation(start, this.tokenStart), + value: this.substrToCursor(start) + }; +} + +export function generate(node) { + this.tokenize(node.value); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Url.js b/vanilla/node_modules/css-tree/lib/syntax/node/Url.js new file mode 100644 index 0000000..ac52c9d --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Url.js @@ -0,0 +1,52 @@ +import * as url from '../../utils/url.js'; +import * as string from '../../utils/string.js'; +import { + Function as FunctionToken, + String as StringToken, + Url, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'Url'; +export const structure = { + value: String +}; + +// | ) +export function parse() { + const start = this.tokenStart; + let value; + + switch (this.tokenType) { + case Url: + value = url.decode(this.consume(Url)); + break; + + case FunctionToken: + if (!this.cmpStr(this.tokenStart, this.tokenEnd, 'url(')) { + this.error('Function name must be `url`'); + } + + this.eat(FunctionToken); + this.skipSC(); + value = string.decode(this.consume(StringToken)); + this.skipSC(); + if (!this.eof) { + this.eat(RightParenthesis); + } + break; + + default: + this.error('Url or Function is expected'); + } + + return { + type: 'Url', + loc: this.getLocation(start, this.tokenStart), + value + }; +} + +export function generate(node) { + this.token(Url, url.encode(node.value)); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Value.js b/vanilla/node_modules/css-tree/lib/syntax/node/Value.js new file mode 100644 index 0000000..ba465bc --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Value.js @@ -0,0 +1,19 @@ +export const name = 'Value'; +export const structure = { + children: [[]] +}; + +export function parse() { + const start = this.tokenStart; + const children = this.readSequence(this.scope.Value); + + return { + type: 'Value', + loc: this.getLocation(start, this.tokenStart), + children + }; +} + +export function generate(node) { + this.children(node); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/WhiteSpace.js b/vanilla/node_modules/css-tree/lib/syntax/node/WhiteSpace.js new file mode 100644 index 0000000..df34e6f --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/WhiteSpace.js @@ -0,0 +1,27 @@ +import { WhiteSpace } from '../../tokenizer/index.js'; + +const SPACE = Object.freeze({ + type: 'WhiteSpace', + loc: null, + value: ' ' +}); + +export const name = 'WhiteSpace'; +export const structure = { + value: String +}; + +export function parse() { + this.eat(WhiteSpace); + return SPACE; + + // return { + // type: 'WhiteSpace', + // loc: this.getLocation(this.tokenStart, this.tokenEnd), + // value: this.consume(WHITESPACE) + // }; +} + +export function generate(node) { + this.token(WhiteSpace, node.value); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/index-generate.js b/vanilla/node_modules/css-tree/lib/syntax/node/index-generate.js new file mode 100644 index 0000000..568736f --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/index-generate.js @@ -0,0 +1,49 @@ +export { generate as AnPlusB } from './AnPlusB.js'; +export { generate as Atrule } from './Atrule.js'; +export { generate as AtrulePrelude } from './AtrulePrelude.js'; +export { generate as AttributeSelector } from './AttributeSelector.js'; +export { generate as Block } from './Block.js'; +export { generate as Brackets } from './Brackets.js'; +export { generate as CDC } from './CDC.js'; +export { generate as CDO } from './CDO.js'; +export { generate as ClassSelector } from './ClassSelector.js'; +export { generate as Combinator } from './Combinator.js'; +export { generate as Comment } from './Comment.js'; +export { generate as Condition } from './Condition.js'; +export { generate as Declaration } from './Declaration.js'; +export { generate as DeclarationList } from './DeclarationList.js'; +export { generate as Dimension } from './Dimension.js'; +export { generate as Feature } from './Feature.js'; +export { generate as FeatureFunction } from './FeatureFunction.js'; +export { generate as FeatureRange } from './FeatureRange.js'; +export { generate as Function } from './Function.js'; +export { generate as GeneralEnclosed } from './GeneralEnclosed.js'; +export { generate as Hash } from './Hash.js'; +export { generate as Identifier } from './Identifier.js'; +export { generate as IdSelector } from './IdSelector.js'; +export { generate as Layer } from './Layer.js'; +export { generate as LayerList } from './LayerList.js'; +export { generate as MediaQuery } from './MediaQuery.js'; +export { generate as MediaQueryList } from './MediaQueryList.js'; +export { generate as NestingSelector } from './NestingSelector.js'; +export { generate as Nth } from './Nth.js'; +export { generate as Number } from './Number.js'; +export { generate as Operator } from './Operator.js'; +export { generate as Parentheses } from './Parentheses.js'; +export { generate as Percentage } from './Percentage.js'; +export { generate as PseudoClassSelector } from './PseudoClassSelector.js'; +export { generate as PseudoElementSelector } from './PseudoElementSelector.js'; +export { generate as Ratio } from './Ratio.js'; +export { generate as Raw } from './Raw.js'; +export { generate as Rule } from './Rule.js'; +export { generate as Scope } from './Scope.js'; +export { generate as Selector } from './Selector.js'; +export { generate as SelectorList } from './SelectorList.js'; +export { generate as String } from './String.js'; +export { generate as StyleSheet } from './StyleSheet.js'; +export { generate as SupportsDeclaration } from './SupportsDeclaration.js'; +export { generate as TypeSelector } from './TypeSelector.js'; +export { generate as UnicodeRange } from './UnicodeRange.js'; +export { generate as Url } from './Url.js'; +export { generate as Value } from './Value.js'; +export { generate as WhiteSpace } from './WhiteSpace.js'; diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/index-parse-selector.js b/vanilla/node_modules/css-tree/lib/syntax/node/index-parse-selector.js new file mode 100644 index 0000000..3a0a2a3 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/index-parse-selector.js @@ -0,0 +1,17 @@ +export { parse as AnPlusB } from './AnPlusB.js'; +export { parse as AttributeSelector } from './AttributeSelector.js'; +export { parse as ClassSelector } from './ClassSelector.js'; +export { parse as Combinator } from './Combinator.js'; +export { parse as Identifier } from './Identifier.js'; +export { parse as IdSelector } from './IdSelector.js'; +export { parse as NestingSelector } from './NestingSelector.js'; +export { parse as Nth } from './Nth.js'; +export { parse as Operator } from './Operator.js'; +export { parse as Percentage } from './Percentage.js'; +export { parse as PseudoClassSelector } from './PseudoClassSelector.js'; +export { parse as PseudoElementSelector } from './PseudoElementSelector.js'; +export { parse as Raw } from './Raw.js'; +export { parse as Selector } from './Selector.js'; +export { parse as SelectorList } from './SelectorList.js'; +export { parse as String } from './String.js'; +export { parse as TypeSelector } from './TypeSelector.js'; diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/index-parse.js b/vanilla/node_modules/css-tree/lib/syntax/node/index-parse.js new file mode 100644 index 0000000..80136d3 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/index-parse.js @@ -0,0 +1,49 @@ +export { parse as AnPlusB } from './AnPlusB.js'; +export { parse as Atrule } from './Atrule.js'; +export { parse as AtrulePrelude } from './AtrulePrelude.js'; +export { parse as AttributeSelector } from './AttributeSelector.js'; +export { parse as Block } from './Block.js'; +export { parse as Brackets } from './Brackets.js'; +export { parse as CDC } from './CDC.js'; +export { parse as CDO } from './CDO.js'; +export { parse as ClassSelector } from './ClassSelector.js'; +export { parse as Combinator } from './Combinator.js'; +export { parse as Comment } from './Comment.js'; +export { parse as Condition } from './Condition.js'; +export { parse as Declaration } from './Declaration.js'; +export { parse as DeclarationList } from './DeclarationList.js'; +export { parse as Dimension } from './Dimension.js'; +export { parse as Feature } from './Feature.js'; +export { parse as FeatureFunction } from './FeatureFunction.js'; +export { parse as FeatureRange } from './FeatureRange.js'; +export { parse as Function } from './Function.js'; +export { parse as GeneralEnclosed } from './GeneralEnclosed.js'; +export { parse as Hash } from './Hash.js'; +export { parse as Identifier } from './Identifier.js'; +export { parse as IdSelector } from './IdSelector.js'; +export { parse as Layer } from './Layer.js'; +export { parse as LayerList } from './LayerList.js'; +export { parse as MediaQuery } from './MediaQuery.js'; +export { parse as MediaQueryList } from './MediaQueryList.js'; +export { parse as NestingSelector } from './NestingSelector.js'; +export { parse as Nth } from './Nth.js'; +export { parse as Number } from './Number.js'; +export { parse as Operator } from './Operator.js'; +export { parse as Parentheses } from './Parentheses.js'; +export { parse as Percentage } from './Percentage.js'; +export { parse as PseudoClassSelector } from './PseudoClassSelector.js'; +export { parse as PseudoElementSelector } from './PseudoElementSelector.js'; +export { parse as Ratio } from './Ratio.js'; +export { parse as Raw } from './Raw.js'; +export { parse as Rule } from './Rule.js'; +export { parse as Scope } from './Scope.js'; +export { parse as Selector } from './Selector.js'; +export { parse as SelectorList } from './SelectorList.js'; +export { parse as String } from './String.js'; +export { parse as StyleSheet } from './StyleSheet.js'; +export { parse as SupportsDeclaration } from './SupportsDeclaration.js'; +export { parse as TypeSelector } from './TypeSelector.js'; +export { parse as UnicodeRange } from './UnicodeRange.js'; +export { parse as Url } from './Url.js'; +export { parse as Value } from './Value.js'; +export { parse as WhiteSpace } from './WhiteSpace.js'; diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/index.js b/vanilla/node_modules/css-tree/lib/syntax/node/index.js new file mode 100644 index 0000000..4aa2dea --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/index.js @@ -0,0 +1,49 @@ +export * as AnPlusB from './AnPlusB.js'; +export * as Atrule from './Atrule.js'; +export * as AtrulePrelude from './AtrulePrelude.js'; +export * as AttributeSelector from './AttributeSelector.js'; +export * as Block from './Block.js'; +export * as Brackets from './Brackets.js'; +export * as CDC from './CDC.js'; +export * as CDO from './CDO.js'; +export * as ClassSelector from './ClassSelector.js'; +export * as Combinator from './Combinator.js'; +export * as Comment from './Comment.js'; +export * as Condition from './Condition.js'; +export * as Declaration from './Declaration.js'; +export * as DeclarationList from './DeclarationList.js'; +export * as Dimension from './Dimension.js'; +export * as Feature from './Feature.js'; +export * as FeatureFunction from './FeatureFunction.js'; +export * as FeatureRange from './FeatureRange.js'; +export * as Function from './Function.js'; +export * as GeneralEnclosed from './GeneralEnclosed.js'; +export * as Hash from './Hash.js'; +export * as Identifier from './Identifier.js'; +export * as IdSelector from './IdSelector.js'; +export * as Layer from './Layer.js'; +export * as LayerList from './LayerList.js'; +export * as MediaQuery from './MediaQuery.js'; +export * as MediaQueryList from './MediaQueryList.js'; +export * as NestingSelector from './NestingSelector.js'; +export * as Nth from './Nth.js'; +export * as Number from './Number.js'; +export * as Operator from './Operator.js'; +export * as Parentheses from './Parentheses.js'; +export * as Percentage from './Percentage.js'; +export * as PseudoClassSelector from './PseudoClassSelector.js'; +export * as PseudoElementSelector from './PseudoElementSelector.js'; +export * as Ratio from './Ratio.js'; +export * as Raw from './Raw.js'; +export * as Rule from './Rule.js'; +export * as Scope from './Scope.js'; +export * as Selector from './Selector.js'; +export * as SelectorList from './SelectorList.js'; +export * as String from './String.js'; +export * as StyleSheet from './StyleSheet.js'; +export * as SupportsDeclaration from './SupportsDeclaration.js'; +export * as TypeSelector from './TypeSelector.js'; +export * as UnicodeRange from './UnicodeRange.js'; +export * as Url from './Url.js'; +export * as Value from './Value.js'; +export * as WhiteSpace from './WhiteSpace.js'; diff --git a/vanilla/node_modules/css-tree/lib/syntax/pseudo/index.js b/vanilla/node_modules/css-tree/lib/syntax/pseudo/index.js new file mode 100644 index 0000000..7d75fe1 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/pseudo/index.js @@ -0,0 +1,56 @@ +import { parseLanguageRangeList } from './lang.js'; + +const selectorList = { + parse() { + return this.createSingleNodeList( + this.SelectorList() + ); + } +}; + +const selector = { + parse() { + return this.createSingleNodeList( + this.Selector() + ); + } +}; + +const identList = { + parse() { + return this.createSingleNodeList( + this.Identifier() + ); + } +}; + +const langList = { + parse: parseLanguageRangeList +}; + +const nth = { + parse() { + return this.createSingleNodeList( + this.Nth() + ); + } +}; + +export default { + 'dir': identList, + 'has': selectorList, + 'lang': langList, + 'matches': selectorList, + 'is': selectorList, + '-moz-any': selectorList, + '-webkit-any': selectorList, + 'where': selectorList, + 'not': selectorList, + 'nth-child': nth, + 'nth-last-child': nth, + 'nth-last-of-type': nth, + 'nth-of-type': nth, + 'slotted': selector, + 'host': selector, + 'host-context': selector +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/pseudo/lang.js b/vanilla/node_modules/css-tree/lib/syntax/pseudo/lang.js new file mode 100644 index 0000000..3adfdb8 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/pseudo/lang.js @@ -0,0 +1,33 @@ +import { Comma, String as StringToken, Ident, RightParenthesis } from '../../tokenizer/index.js'; + +export function parseLanguageRangeList() { + const children = this.createList(); + + this.skipSC(); + + loop: while (!this.eof) { + switch (this.tokenType) { + case Ident: + children.push(this.Identifier()); + break; + + case StringToken: + children.push(this.String()); + break; + + case Comma: + children.push(this.Operator()); + break; + + case RightParenthesis: + break loop; + + default: + this.error('Identifier, string or comma is expected'); + } + + this.skipSC(); + } + + return children; +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js b/vanilla/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js new file mode 100644 index 0000000..c903555 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/scope/atrulePrelude.js @@ -0,0 +1,5 @@ +import getNode from './default.js'; + +export default { + getNode +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/scope/default.js b/vanilla/node_modules/css-tree/lib/syntax/scope/default.js new file mode 100644 index 0000000..8f14035 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/scope/default.js @@ -0,0 +1,85 @@ +import { + Ident, + String as StringToken, + Number as NumberToken, + Function as FunctionToken, + Url, + Hash, + Dimension, + Percentage, + LeftParenthesis, + LeftSquareBracket, + Comma, + Delim +} from '../../tokenizer/index.js'; + +const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#) +const ASTERISK = 0x002A; // U+002A ASTERISK (*) +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) +const U = 0x0075; // U+0075 LATIN SMALL LETTER U (u) + +export default function defaultRecognizer(context) { + switch (this.tokenType) { + case Hash: + return this.Hash(); + + case Comma: + return this.Operator(); + + case LeftParenthesis: + return this.Parentheses(this.readSequence, context.recognizer); + + case LeftSquareBracket: + return this.Brackets(this.readSequence, context.recognizer); + + case StringToken: + return this.String(); + + case Dimension: + return this.Dimension(); + + case Percentage: + return this.Percentage(); + + case NumberToken: + return this.Number(); + + case FunctionToken: + return this.cmpStr(this.tokenStart, this.tokenEnd, 'url(') + ? this.Url() + : this.Function(this.readSequence, context.recognizer); + + case Url: + return this.Url(); + + case Ident: + // check for unicode range, it should start with u+ or U+ + if (this.cmpChar(this.tokenStart, U) && + this.cmpChar(this.tokenStart + 1, PLUSSIGN)) { + return this.UnicodeRange(); + } else { + return this.Identifier(); + } + + case Delim: { + const code = this.charCodeAt(this.tokenStart); + + if (code === SOLIDUS || + code === ASTERISK || + code === PLUSSIGN || + code === HYPHENMINUS) { + return this.Operator(); // TODO: replace with Delim + } + + // TODO: produce a node with Delim node type + + if (code === NUMBERSIGN) { + this.error('Hex or identifier is expected', this.tokenStart + 1); + } + + break; + } + } +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/scope/index.js b/vanilla/node_modules/css-tree/lib/syntax/scope/index.js new file mode 100644 index 0000000..6dabbbe --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/scope/index.js @@ -0,0 +1,3 @@ +export { default as AtrulePrelude } from './atrulePrelude.js'; +export { default as Selector } from './selector.js'; +export { default as Value } from './value.js'; diff --git a/vanilla/node_modules/css-tree/lib/syntax/scope/selector.js b/vanilla/node_modules/css-tree/lib/syntax/scope/selector.js new file mode 100644 index 0000000..a8efcd5 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/scope/selector.js @@ -0,0 +1,94 @@ +import { + Delim, + Ident, + Dimension, + Percentage, + Number as NumberToken, + Hash, + Colon, + LeftSquareBracket +} from '../../tokenizer/index.js'; + +const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#) +const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&) +const ASTERISK = 0x002A; // U+002A ASTERISK (*) +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) +const FULLSTOP = 0x002E; // U+002E FULL STOP (.) +const GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>) +const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|) +const TILDE = 0x007E; // U+007E TILDE (~) + +function onWhiteSpace(next, children) { + if (children.last !== null && children.last.type !== 'Combinator' && + next !== null && next.type !== 'Combinator') { + children.push({ // FIXME: this.Combinator() should be used instead + type: 'Combinator', + loc: null, + name: ' ' + }); + } +} + +function getNode() { + switch (this.tokenType) { + case LeftSquareBracket: + return this.AttributeSelector(); + + case Hash: + return this.IdSelector(); + + case Colon: + if (this.lookupType(1) === Colon) { + return this.PseudoElementSelector(); + } else { + return this.PseudoClassSelector(); + } + + case Ident: + return this.TypeSelector(); + + case NumberToken: + case Percentage: + return this.Percentage(); + + case Dimension: + // throws when .123ident + if (this.charCodeAt(this.tokenStart) === FULLSTOP) { + this.error('Identifier is expected', this.tokenStart + 1); + } + break; + + case Delim: { + const code = this.charCodeAt(this.tokenStart); + + switch (code) { + case PLUSSIGN: + case GREATERTHANSIGN: + case TILDE: + case SOLIDUS: // /deep/ + return this.Combinator(); + + case FULLSTOP: + return this.ClassSelector(); + + case ASTERISK: + case VERTICALLINE: + return this.TypeSelector(); + + case NUMBERSIGN: + return this.IdSelector(); + + case AMPERSAND: + return this.NestingSelector(); + } + + break; + } + } +}; + +export default { + onWhiteSpace, + getNode +}; diff --git a/vanilla/node_modules/css-tree/lib/syntax/scope/value.js b/vanilla/node_modules/css-tree/lib/syntax/scope/value.js new file mode 100644 index 0000000..dc94219 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/scope/value.js @@ -0,0 +1,25 @@ +import getNode from './default.js'; +import expressionFn from '../function/expression.js'; +import varFn from '../function/var.js'; + +function isPlusMinusOperator(node) { + return ( + node !== null && + node.type === 'Operator' && + (node.value[node.value.length - 1] === '-' || node.value[node.value.length - 1] === '+') + ); +} + +export default { + getNode, + onWhiteSpace(next, children) { + if (isPlusMinusOperator(next)) { + next.value = ' ' + next.value; + } + if (isPlusMinusOperator(children.last)) { + children.last.value += ' '; + } + }, + 'expression': expressionFn, + 'var': varFn +}; -- cgit v1.2.3