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/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 ++++ 53 files changed, 3099 insertions(+) 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 (limited to 'vanilla/node_modules/css-tree/lib/syntax/node') 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'; -- cgit v1.2.3