diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
| commit | 76cb9c2a39d477a64824a985ade40507e3bbade1 (patch) | |
| tree | 41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/css-tree/lib/syntax/node | |
| parent | 819a39a21ac992b1393244a4c283bbb125208c69 (diff) | |
| download | neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2 neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip | |
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/css-tree/lib/syntax/node')
53 files changed, 3099 insertions, 0 deletions
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); + } +} + +// ... <signed-integer> +// ... ['+' | '-'] <signless-integer> +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; + + // <integer> + if (this.tokenType === Number) { + checkTokenIsInteger.call(this, ALLOW_SIGN); + b = this.consume(Number); + } + + // -n + // -n <signed-integer> + // -n ['+' | '-'] <signless-integer> + // -n- <signless-integer> + // <dashndashdigit-ident> + else if (this.tokenType === Ident && this.cmpChar(this.tokenStart, HYPHENMINUS)) { + a = '-1'; + + expectCharCode.call(this, 1, N); + + switch (this.tokenEnd - this.tokenStart) { + // -n + // -n <signed-integer> + // -n ['+' | '-'] <signless-integer> + case 2: + this.next(); + b = consumeB.call(this); + break; + + // -n- <signless-integer> + case 3: + expectCharCode.call(this, 2, HYPHENMINUS); + + this.next(); + this.skipSC(); + + checkTokenIsInteger.call(this, DISALLOW_SIGN); + + b = '-' + this.consume(Number); + break; + + // <dashndashdigit-ident> + default: + expectCharCode.call(this, 2, HYPHENMINUS); + checkInteger.call(this, 3, DISALLOW_SIGN); + this.next(); + + b = this.substrToCursor(start + 2); + } + } + + // '+'? n + // '+'? n <signed-integer> + // '+'? n ['+' | '-'] <signless-integer> + // '+'? n- <signless-integer> + // '+'? <ndashdigit-ident> + 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 <signed-integer> + // '+'? n ['+' | '-'] <signless-integer> + case 1: + this.next(); + b = consumeB.call(this); + break; + + // '+'? n- <signless-integer> + case 2: + expectCharCode.call(this, 1, HYPHENMINUS); + + this.next(); + this.skipSC(); + + checkTokenIsInteger.call(this, DISALLOW_SIGN); + + b = '-' + this.consume(Number); + break; + + // '+'? <ndashdigit-ident> + default: + expectCharCode.call(this, 1, HYPHENMINUS); + checkInteger.call(this, 2, DISALLOW_SIGN); + this.next(); + + b = this.substrToCursor(start + sign + 1); + } + } + + // <ndashdigit-dimension> + // <ndash-dimension> <signless-integer> + // <n-dimension> + // <n-dimension> <signed-integer> + // <n-dimension> ['+' | '-'] <signless-integer> + 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); + + // <n-dimension> + // <n-dimension> <signed-integer> + // <n-dimension> ['+' | '-'] <signless-integer> + if (i + 1 === this.tokenEnd) { + this.next(); + b = consumeB.call(this); + } else { + expectCharCode.call(this, i - this.tokenStart + 1, HYPHENMINUS); + + // <ndash-dimension> <signless-integer> + if (i + 2 === this.tokenEnd) { + this.next(); + this.skipSC(); + checkTokenIsInteger.call(this, DISALLOW_SIGN); + b = '-' + this.consume(Number); + } + // <ndashdigit-dimension> + 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); +} + +// '[' <wq-name> ']' +// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']' +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); // <!-- + + return { + type: 'CDO', + loc: this.getLocation(start, this.tokenStart) + }; +} + +export function generate() { + this.token(CDO, '<!--'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/ClassSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/ClassSelector.js new file mode 100644 index 0000000..febb0d9 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/ClassSelector.js @@ -0,0 +1,24 @@ +import { Delim, Ident } from '../../tokenizer/index.js'; + +const FULLSTOP = 0x002E; // U+002E FULL STOP (.) + +// '.' ident +export const name = 'ClassSelector'; +export const structure = { + name: String +}; + +export function parse() { + this.eatDelim(FULLSTOP); + + return { + type: 'ClassSelector', + loc: this.getLocation(this.tokenStart - 1, this.tokenEnd), + name: this.consume(Ident) + }; +} + +export function generate(node) { + this.token(Delim, '.'); + this.token(Ident, node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Combinator.js b/vanilla/node_modules/css-tree/lib/syntax/node/Combinator.js new file mode 100644 index 0000000..2e5bb1f --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Combinator.js @@ -0,0 +1,54 @@ +import { WhiteSpace, Delim } from '../../tokenizer/index.js'; + +const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) +const GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>) +const TILDE = 0x007E; // U+007E TILDE (~) + +export const name = 'Combinator'; +export const structure = { + name: String +}; + +// + | > | ~ | /deep/ +export function parse() { + const start = this.tokenStart; + let name; + + switch (this.tokenType) { + case WhiteSpace: + name = ' '; + break; + + case Delim: + switch (this.charCodeAt(this.tokenStart)) { + case GREATERTHANSIGN: + case PLUSSIGN: + case TILDE: + this.next(); + break; + + case SOLIDUS: + this.next(); + this.eatIdent('deep'); + this.eatDelim(SOLIDUS); + break; + + default: + this.error('Combinator is expected'); + } + + name = this.substrToCursor(start); + break; + } + + return { + type: 'Combinator', + loc: this.getLocation(start, this.tokenStart), + name + }; +} + +export function generate(node) { + this.tokenize(node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Comment.js b/vanilla/node_modules/css-tree/lib/syntax/node/Comment.js new file mode 100644 index 0000000..8a9d291 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Comment.js @@ -0,0 +1,33 @@ +import { Comment } from '../../tokenizer/index.js'; + +const ASTERISK = 0x002A; // U+002A ASTERISK (*) +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) + + +export const name = 'Comment'; +export const structure = { + value: String +}; + +export function parse() { + const start = this.tokenStart; + let end = this.tokenEnd; + + this.eat(Comment); + + if ((end - start + 2) >= 2 && + this.charCodeAt(end - 2) === ASTERISK && + this.charCodeAt(end - 1) === SOLIDUS) { + end -= 2; + } + + return { + type: 'Comment', + loc: this.getLocation(start, this.tokenStart), + value: this.substring(start + 2, end) + }; +} + +export function generate(node) { + this.token(Comment, '/*' + node.value + '*/'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Condition.js b/vanilla/node_modules/css-tree/lib/syntax/node/Condition.js new file mode 100644 index 0000000..e4b1e0d --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Condition.js @@ -0,0 +1,123 @@ +import { + WhiteSpace, + Comment, + Ident, + LeftParenthesis, + RightParenthesis, + Function as FunctionToken, + Colon, + EOF +} from '../../tokenizer/index.js'; + +const likelyFeatureToken = new Set([Colon, RightParenthesis, EOF]); + +export const name = 'Condition'; +export const structure = { + kind: String, + children: [[ + 'Identifier', + 'Feature', + 'FeatureFunction', + 'FeatureRange', + 'SupportsDeclaration' + ]] +}; + +function featureOrRange(kind) { + if (this.lookupTypeNonSC(1) === Ident && + likelyFeatureToken.has(this.lookupTypeNonSC(2))) { + return this.Feature(kind); + } + + return this.FeatureRange(kind); +} + +const parentheses = { + media: featureOrRange, + container: featureOrRange, + supports() { + return this.SupportsDeclaration(); + } +}; + +export function parse(kind = 'media') { + const children = this.createList(); + + scan: while (!this.eof) { + switch (this.tokenType) { + case Comment: + case WhiteSpace: + this.next(); + continue; + + case Ident: + children.push(this.Identifier()); + break; + + case LeftParenthesis: { + let term = this.parseWithFallback( + () => parentheses[kind].call(this, kind), + () => null + ); + + if (!term) { + term = this.parseWithFallback( + () => { + this.eat(LeftParenthesis); + const res = this.Condition(kind); + this.eat(RightParenthesis); + return res; + }, + () => { + return this.GeneralEnclosed(kind); + } + ); + } + + children.push(term); + break; + } + + case FunctionToken: { + let term = this.parseWithFallback( + () => this.FeatureFunction(kind), + () => null + ); + + if (!term) { + term = this.GeneralEnclosed(kind); + } + + children.push(term); + break; + } + + default: + break scan; + } + } + + if (children.isEmpty) { + this.error('Condition is expected'); + } + + return { + type: 'Condition', + loc: this.getLocationFromList(children), + kind, + children + }; +} + +export function generate(node) { + node.children.forEach(child => { + if (child.type === 'Condition') { + this.token(LeftParenthesis, '('); + this.node(child); + this.token(RightParenthesis, ')'); + } else { + this.node(child); + } + }); +} + diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Declaration.js b/vanilla/node_modules/css-tree/lib/syntax/node/Declaration.js new file mode 100644 index 0000000..af3ea60 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Declaration.js @@ -0,0 +1,165 @@ +import { isCustomProperty } from '../../utils/names.js'; +import { + Ident, + Hash, + Colon, + Semicolon, + Delim, + WhiteSpace +} from '../../tokenizer/index.js'; + +const EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!) +const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#) +const DOLLARSIGN = 0x0024; // U+0024 DOLLAR 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 (/) + +function consumeValueRaw() { + return this.Raw(this.consumeUntilExclamationMarkOrSemicolon, true); +} + +function consumeCustomPropertyRaw() { + return this.Raw(this.consumeUntilExclamationMarkOrSemicolon, false); +} + +function consumeValue() { + const startValueToken = this.tokenIndex; + const value = this.Value(); + + if (value.type !== 'Raw' && + this.eof === false && + this.tokenType !== Semicolon && + this.isDelim(EXCLAMATIONMARK) === false && + this.isBalanceEdge(startValueToken) === false) { + this.error(); + } + + return value; +} + +export const name = 'Declaration'; +export const walkContext = 'declaration'; +export const structure = { + important: [Boolean, String], + property: String, + value: ['Value', 'Raw'] +}; + +export function parse() { + const start = this.tokenStart; + const startToken = this.tokenIndex; + const property = readProperty.call(this); + const customProperty = isCustomProperty(property); + const parseValue = customProperty ? this.parseCustomProperty : this.parseValue; + const consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw; + let important = false; + let value; + + this.skipSC(); + this.eat(Colon); + + const valueStart = this.tokenIndex; + + if (!customProperty) { + this.skipSC(); + } + + if (parseValue) { + value = this.parseWithFallback(consumeValue, consumeRaw); + } else { + value = consumeRaw.call(this, this.tokenIndex); + } + + if (customProperty && value.type === 'Value' && value.children.isEmpty) { + for (let offset = valueStart - this.tokenIndex; offset <= 0; offset++) { + if (this.lookupType(offset) === WhiteSpace) { + value.children.appendData({ + type: 'WhiteSpace', + loc: null, + value: ' ' + }); + break; + } + } + } + + if (this.isDelim(EXCLAMATIONMARK)) { + important = getImportant.call(this); + this.skipSC(); + } + + // Do not include semicolon to range per spec + // https://drafts.csswg.org/css-syntax/#declaration-diagram + + if (this.eof === false && + this.tokenType !== Semicolon && + this.isBalanceEdge(startToken) === false) { + this.error(); + } + + return { + type: 'Declaration', + loc: this.getLocation(start, this.tokenStart), + important, + property, + value + }; +} + +export function generate(node) { + this.token(Ident, node.property); + this.token(Colon, ':'); + this.node(node.value); + + if (node.important) { + this.token(Delim, '!'); + this.token(Ident, node.important === true ? 'important' : node.important); + } +} + +function readProperty() { + const start = this.tokenStart; + + // hacks + if (this.tokenType === Delim) { + switch (this.charCodeAt(this.tokenStart)) { + case ASTERISK: + case DOLLARSIGN: + case PLUSSIGN: + case NUMBERSIGN: + case AMPERSAND: + this.next(); + break; + + // TODO: not sure we should support this hack + case SOLIDUS: + this.next(); + if (this.isDelim(SOLIDUS)) { + this.next(); + } + break; + } + } + + if (this.tokenType === Hash) { + this.eat(Hash); + } else { + this.eat(Ident); + } + + return this.substrToCursor(start); +} + +// ! ws* important +function getImportant() { + this.eat(Delim); + this.skipSC(); + + const important = this.consume(Ident); + + // store original value in case it differ from `important` + // for better original source restoring and hacks like `!ie` support + return important === 'important' ? true : important; +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/DeclarationList.js b/vanilla/node_modules/css-tree/lib/syntax/node/DeclarationList.js new file mode 100644 index 0000000..2b40c99 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/DeclarationList.js @@ -0,0 +1,62 @@ +import { + WhiteSpace, + Comment, + Semicolon, + AtKeyword +} from '../../tokenizer/index.js'; + +const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&) + +function consumeRaw() { + return this.Raw(this.consumeUntilSemicolonIncluded, true); +} + +export const name = 'DeclarationList'; +export const structure = { + children: [[ + 'Declaration', + 'Atrule', + 'Rule' + ]] +}; + +export function parse() { + const children = this.createList(); + + scan: + while (!this.eof) { + switch (this.tokenType) { + case WhiteSpace: + case Comment: + case Semicolon: + this.next(); + break; + + case AtKeyword: + children.push(this.parseWithFallback(this.Atrule.bind(this, true), consumeRaw)); + break; + + default: + if (this.isDelim(AMPERSAND)) { + children.push(this.parseWithFallback(this.Rule, consumeRaw)); + } else { + children.push(this.parseWithFallback(this.Declaration, consumeRaw)); + } + } + } + + return { + type: 'DeclarationList', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node, prev => { + if (prev.type === 'Declaration') { + this.token(Semicolon, ';'); + } + }); +} + diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Dimension.js b/vanilla/node_modules/css-tree/lib/syntax/node/Dimension.js new file mode 100644 index 0000000..4b9bffc --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Dimension.js @@ -0,0 +1,23 @@ +import { Dimension } from '../../tokenizer/index.js'; + +export const name = 'Dimension'; +export const structure = { + value: String, + unit: String +}; + +export function parse() { + const start = this.tokenStart; + const value = this.consumeNumber(Dimension); + + return { + type: 'Dimension', + loc: this.getLocation(start, this.tokenStart), + value, + unit: this.substring(start + value.length, this.tokenStart) + }; +} + +export function generate(node) { + this.token(Dimension, node.value + node.unit); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Feature.js b/vanilla/node_modules/css-tree/lib/syntax/node/Feature.js new file mode 100644 index 0000000..2f2dc3d --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Feature.js @@ -0,0 +1,103 @@ +import { + Ident, + Number, + Dimension, + Function as FunctionToken, + LeftParenthesis, + RightParenthesis, + Colon, + Delim +} from '../../tokenizer/index.js'; + +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) + +export const name = 'Feature'; +export const structure = { + kind: String, + name: String, + value: ['Identifier', 'Number', 'Dimension', 'Ratio', 'Function', null] +}; + +export function parse(kind) { + const start = this.tokenStart; + let name; + let value = null; + + this.eat(LeftParenthesis); + this.skipSC(); + + name = this.consume(Ident); + this.skipSC(); + + if (this.tokenType !== RightParenthesis) { + this.eat(Colon); + this.skipSC(); + + switch (this.tokenType) { + case Number: + if (this.lookupNonWSType(1) === Delim) { + value = this.Ratio(); + } else { + value = this.Number(); + } + + break; + + case Dimension: + value = this.Dimension(); + break; + + case Ident: + value = this.Identifier(); + break; + + case FunctionToken: + value = this.parseWithFallback( + () => { + const res = this.Function(this.readSequence, this.scope.Value); + + this.skipSC(); + + if (this.isDelim(SOLIDUS)) { + this.error(); + } + + return res; + }, + () => { + return this.Ratio(); + } + ); + break; + + default: + this.error('Number, dimension, ratio or identifier is expected'); + } + + this.skipSC(); + } + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'Feature', + loc: this.getLocation(start, this.tokenStart), + kind, + name, + value + }; +} + +export function generate(node) { + this.token(LeftParenthesis, '('); + this.token(Ident, node.name); + + if (node.value !== null) { + this.token(Colon, ':'); + this.node(node.value); + } + + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/FeatureFunction.js b/vanilla/node_modules/css-tree/lib/syntax/node/FeatureFunction.js new file mode 100644 index 0000000..5869479 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/FeatureFunction.js @@ -0,0 +1,63 @@ +import { + Function as FunctionToken, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'FeatureFunction'; +export const structure = { + kind: String, + feature: String, + value: ['Declaration', 'Selector'] +}; + +function getFeatureParser(kind, name) { + const featuresOfKind = this.features[kind] || {}; + const parser = featuresOfKind[name]; + + if (typeof parser !== 'function') { + this.error(`Unknown feature ${name}()`); + } + + return parser; +} + +export function parse(kind = 'unknown') { + const start = this.tokenStart; + const functionName = this.consumeFunctionName(); + const valueParser = getFeatureParser.call(this, kind, functionName.toLowerCase()); + + this.skipSC(); + + const value = this.parseWithFallback( + () => { + const startValueToken = this.tokenIndex; + const value = valueParser.call(this); + + if (this.eof === false && + this.isBalanceEdge(startValueToken) === false) { + this.error(); + } + + return value; + }, + () => this.Raw(null, false) + ); + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'FeatureFunction', + loc: this.getLocation(start, this.tokenStart), + kind, + feature: functionName, + value + }; +} + +export function generate(node) { + this.token(FunctionToken, node.feature + '('); + this.node(node.value); + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/FeatureRange.js b/vanilla/node_modules/css-tree/lib/syntax/node/FeatureRange.js new file mode 100644 index 0000000..a83e479 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/FeatureRange.js @@ -0,0 +1,133 @@ +import { + Ident, + Number, + Dimension, + Function as FunctionToken, + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) +const LESSTHANSIGN = 0x003C; // U+003C LESS-THAN SIGN (<) +const EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=) +const GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>) + +export const name = 'FeatureRange'; +export const structure = { + kind: String, + left: ['Identifier', 'Number', 'Dimension', 'Ratio', 'Function'], + leftComparison: String, + middle: ['Identifier', 'Number', 'Dimension', 'Ratio', 'Function'], + rightComparison: [String, null], + right: ['Identifier', 'Number', 'Dimension', 'Ratio', 'Function', null] +}; + +function readTerm() { + this.skipSC(); + + switch (this.tokenType) { + case Number: + if (this.isDelim(SOLIDUS, this.lookupOffsetNonSC(1))) { + return this.Ratio(); + } else { + return this.Number(); + } + + case Dimension: + return this.Dimension(); + + case Ident: + return this.Identifier(); + + case FunctionToken: + return this.parseWithFallback( + () => { + const res = this.Function(this.readSequence, this.scope.Value); + + this.skipSC(); + + if (this.isDelim(SOLIDUS)) { + this.error(); + } + + return res; + }, + () => { + return this.Ratio(); + } + ); + + default: + this.error('Number, dimension, ratio or identifier is expected'); + } +} + +function readComparison(expectColon) { + this.skipSC(); + + if (this.isDelim(LESSTHANSIGN) || + this.isDelim(GREATERTHANSIGN)) { + const value = this.source[this.tokenStart]; + + this.next(); + + if (this.isDelim(EQUALSSIGN)) { + this.next(); + return value + '='; + } + + return value; + } + + if (this.isDelim(EQUALSSIGN)) { + return '='; + } + + this.error(`Expected ${expectColon ? '":", ' : ''}"<", ">", "=" or ")"`); +} + +export function parse(kind = 'unknown') { + const start = this.tokenStart; + + this.skipSC(); + this.eat(LeftParenthesis); + + const left = readTerm.call(this); + const leftComparison = readComparison.call(this, left.type === 'Identifier'); + const middle = readTerm.call(this); + let rightComparison = null; + let right = null; + + if (this.lookupNonWSType(0) !== RightParenthesis) { + rightComparison = readComparison.call(this); + right = readTerm.call(this); + } + + this.skipSC(); + this.eat(RightParenthesis); + + return { + type: 'FeatureRange', + loc: this.getLocation(start, this.tokenStart), + kind, + left, + leftComparison, + middle, + rightComparison, + right + }; +} + +export function generate(node) { + this.token(LeftParenthesis, '('); + this.node(node.left); + this.tokenize(node.leftComparison); + this.node(node.middle); + + if (node.right) { + this.tokenize(node.rightComparison); + this.node(node.right); + } + + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Function.js b/vanilla/node_modules/css-tree/lib/syntax/node/Function.js new file mode 100644 index 0000000..1fdc414 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Function.js @@ -0,0 +1,41 @@ +import { + Function as FunctionToken, + RightParenthesis +} from '../../tokenizer/index.js'; + + +export const name = 'Function'; +export const walkContext = 'function'; +export const structure = { + name: String, + children: [[]] +}; + +// <function-token> <sequence> ) +export function parse(readSequence, recognizer) { + const start = this.tokenStart; + const name = this.consumeFunctionName(); + const nameLowerCase = name.toLowerCase(); + let children; + + children = recognizer.hasOwnProperty(nameLowerCase) + ? recognizer[nameLowerCase].call(this, recognizer) + : readSequence.call(this, recognizer); + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'Function', + loc: this.getLocation(start, this.tokenStart), + name, + children + }; +} + +export function generate(node) { + this.token(FunctionToken, node.name + '('); + this.children(node); + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/GeneralEnclosed.js b/vanilla/node_modules/css-tree/lib/syntax/node/GeneralEnclosed.js new file mode 100644 index 0000000..8ac8cae --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/GeneralEnclosed.js @@ -0,0 +1,66 @@ +import { + Function as FunctionToken, + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + + +export const name = 'GeneralEnclosed'; +export const structure = { + kind: String, + function: [String, null], + children: [[]] +}; + +// <function-token> <any-value> ) +// ( <any-value> ) +export function parse(kind) { + const start = this.tokenStart; + let functionName = null; + + if (this.tokenType === FunctionToken) { + functionName = this.consumeFunctionName(); + } else { + this.eat(LeftParenthesis); + } + + const children = this.parseWithFallback( + () => { + const startValueToken = this.tokenIndex; + const children = this.readSequence(this.scope.Value); + + if (this.eof === false && + this.isBalanceEdge(startValueToken) === false) { + this.error(); + } + + return children; + }, + () => this.createSingleNodeList( + this.Raw(null, false) + ) + ); + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'GeneralEnclosed', + loc: this.getLocation(start, this.tokenStart), + kind, + function: functionName, + children + }; +} + +export function generate(node) { + if (node.function) { + this.token(FunctionToken, node.function + '('); + } else { + this.token(LeftParenthesis, '('); + } + + this.children(node); + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Hash.js b/vanilla/node_modules/css-tree/lib/syntax/node/Hash.js new file mode 100644 index 0000000..b752752 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Hash.js @@ -0,0 +1,23 @@ +import { Hash } from '../../tokenizer/index.js'; + +// '#' ident +export const xxx = 'XXX'; +export const name = 'Hash'; +export const structure = { + value: String +}; +export function parse() { + const start = this.tokenStart; + + this.eat(Hash); + + return { + type: 'Hash', + loc: this.getLocation(start, this.tokenStart), + value: this.substrToCursor(start + 1) + }; +} +export function generate(node) { + this.token(Hash, '#' + node.value); +} + diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/IdSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/IdSelector.js new file mode 100644 index 0000000..c85c1b2 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/IdSelector.js @@ -0,0 +1,26 @@ +import { Hash, Delim } from '../../tokenizer/index.js'; + +export const name = 'IdSelector'; +export const structure = { + name: String +}; + +export function parse() { + const start = this.tokenStart; + + // TODO: check value is an ident + this.eat(Hash); + + return { + type: 'IdSelector', + loc: this.getLocation(start, this.tokenStart), + name: this.substrToCursor(start + 1) + }; +} + +export function generate(node) { + // Using Delim instead of Hash is a hack to avoid for a whitespace between ident and id-selector + // in safe mode (e.g. "a#id"), because IE11 doesn't allow a sequence <ident-token> <hash-token> + // without a whitespace in values (e.g. "1px solid#000") + this.token(Delim, '#' + node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Identifier.js b/vanilla/node_modules/css-tree/lib/syntax/node/Identifier.js new file mode 100644 index 0000000..067c2d0 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Identifier.js @@ -0,0 +1,18 @@ +import { Ident } from '../../tokenizer/index.js'; + +export const name = 'Identifier'; +export const structure = { + name: String +}; + +export function parse() { + return { + type: 'Identifier', + loc: this.getLocation(this.tokenStart, this.tokenEnd), + name: this.consume(Ident) + }; +} + +export function generate(node) { + this.token(Ident, node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Layer.js b/vanilla/node_modules/css-tree/lib/syntax/node/Layer.js new file mode 100644 index 0000000..d170dcb --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Layer.js @@ -0,0 +1,28 @@ +import { Ident, Delim } from '../../tokenizer/index.js'; + +const FULLSTOP = 0x002E; // U+002E FULL STOP (.) + +export const name = 'Layer'; +export const structure = { + name: String +}; + +export function parse() { + let tokenStart = this.tokenStart; + let name = this.consume(Ident); + + while (this.isDelim(FULLSTOP)) { + this.eat(Delim); + name += '.' + this.consume(Ident); + } + + return { + type: 'Layer', + loc: this.getLocation(tokenStart, this.tokenStart), + name + }; +} + +export function generate(node) { + this.tokenize(node.name); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/LayerList.js b/vanilla/node_modules/css-tree/lib/syntax/node/LayerList.js new file mode 100644 index 0000000..e8a05cd --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/LayerList.js @@ -0,0 +1,36 @@ +import { Comma } from '../../tokenizer/index.js'; + +export const name = 'LayerList'; +export const structure = { + children: [[ + 'Layer' + ]] +}; + +export function parse() { + const children = this.createList(); + + this.skipSC(); + + while (!this.eof) { + children.push(this.Layer()); + + if (this.lookupTypeNonSC(0) !== Comma) { + break; + } + + this.skipSC(); + this.next(); + this.skipSC(); + } + + return { + type: 'LayerList', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node, () => this.token(Comma, ',')); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/MediaQuery.js b/vanilla/node_modules/css-tree/lib/syntax/node/MediaQuery.js new file mode 100644 index 0000000..f569c75 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/MediaQuery.js @@ -0,0 +1,102 @@ +import { + Comma, + EOF, + Ident, + LeftCurlyBracket, + LeftParenthesis, + Function as FunctionToken, + Semicolon +} from '../../tokenizer/index.js'; + +export const name = 'MediaQuery'; +export const structure = { + modifier: [String, null], + mediaType: [String, null], + condition: ['Condition', null] +}; + +export function parse() { + const start = this.tokenStart; + let modifier = null; + let mediaType = null; + let condition = null; + + this.skipSC(); + + if (this.tokenType === Ident && this.lookupTypeNonSC(1) !== LeftParenthesis) { + // [ not | only ]? <media-type> + const ident = this.consume(Ident); + const identLowerCase = ident.toLowerCase(); + + if (identLowerCase === 'not' || identLowerCase === 'only') { + this.skipSC(); + modifier = identLowerCase; + mediaType = this.consume(Ident); + } else { + mediaType = ident; + } + + switch (this.lookupTypeNonSC(0)) { + case Ident: { + // and <media-condition-without-or> + this.skipSC(); + this.eatIdent('and'); + condition = this.Condition('media'); + break; + } + + case LeftCurlyBracket: + case Semicolon: + case Comma: + case EOF: + break; + + default: + this.error('Identifier or parenthesis is expected'); + } + } else { + switch (this.tokenType) { + case Ident: + case LeftParenthesis: + case FunctionToken: { + // <media-condition> + condition = this.Condition('media'); + break; + } + + case LeftCurlyBracket: + case Semicolon: + case EOF: + break; + + default: + this.error('Identifier or parenthesis is expected'); + } + } + + return { + type: 'MediaQuery', + loc: this.getLocation(start, this.tokenStart), + modifier, + mediaType, + condition + }; +} + +export function generate(node) { + if (node.mediaType) { + if (node.modifier) { + this.token(Ident, node.modifier); + } + + this.token(Ident, node.mediaType); + + if (node.condition) { + this.token(Ident, 'and'); + this.node(node.condition); + } + } else if (node.condition) { + this.node(node.condition); + } +} + diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/MediaQueryList.js b/vanilla/node_modules/css-tree/lib/syntax/node/MediaQueryList.js new file mode 100644 index 0000000..1c16bcd --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/MediaQueryList.js @@ -0,0 +1,34 @@ +import { Comma } from '../../tokenizer/index.js'; + +export const name = 'MediaQueryList'; +export const structure = { + children: [[ + 'MediaQuery' + ]] +}; + +export function parse() { + const children = this.createList(); + + this.skipSC(); + + while (!this.eof) { + children.push(this.MediaQuery()); + + if (this.tokenType !== Comma) { + break; + } + + this.next(); + } + + return { + type: 'MediaQueryList', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node, () => this.token(Comma, ',')); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/NestingSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/NestingSelector.js new file mode 100644 index 0000000..33c8b6a --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/NestingSelector.js @@ -0,0 +1,22 @@ +import { Delim } from '../../tokenizer/index.js'; + +const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&) + +export const name = 'NestingSelector'; +export const structure = { +}; + +export function parse() { + const start = this.tokenStart; + + this.eatDelim(AMPERSAND); + + return { + type: 'NestingSelector', + loc: this.getLocation(start, this.tokenStart) + }; +} + +export function generate() { + this.token(Delim, '&'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Nth.js b/vanilla/node_modules/css-tree/lib/syntax/node/Nth.js new file mode 100644 index 0000000..bfd74a1 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Nth.js @@ -0,0 +1,47 @@ +import { Ident } from '../../tokenizer/index.js'; + +export const name = 'Nth'; +export const structure = { + nth: ['AnPlusB', 'Identifier'], + selector: ['SelectorList', null] +}; + +export function parse() { + this.skipSC(); + + const start = this.tokenStart; + let end = start; + let selector = null; + let nth; + + if (this.lookupValue(0, 'odd') || this.lookupValue(0, 'even')) { + nth = this.Identifier(); + } else { + nth = this.AnPlusB(); + } + + end = this.tokenStart; + this.skipSC(); + + if (this.lookupValue(0, 'of')) { + this.next(); + + selector = this.SelectorList(); + end = this.tokenStart; + } + + return { + type: 'Nth', + loc: this.getLocation(start, end), + nth, + selector + }; +} + +export function generate(node) { + this.node(node.nth); + if (node.selector !== null) { + this.token(Ident, 'of'); + this.node(node.selector); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Number.js b/vanilla/node_modules/css-tree/lib/syntax/node/Number.js new file mode 100644 index 0000000..a9d8f0a --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Number.js @@ -0,0 +1,18 @@ +import { Number as NumberToken } from '../../tokenizer/index.js'; + +export const name = 'Number'; +export const structure = { + value: String +}; + +export function parse() { + return { + type: 'Number', + loc: this.getLocation(this.tokenStart, this.tokenEnd), + value: this.consume(NumberToken) + }; +} + +export function generate(node) { + this.token(NumberToken, node.value); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Operator.js b/vanilla/node_modules/css-tree/lib/syntax/node/Operator.js new file mode 100644 index 0000000..4f1238b --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Operator.js @@ -0,0 +1,21 @@ +// '/' | '*' | ',' | ':' | '+' | '-' +export const name = 'Operator'; +export const structure = { + value: String +}; + +export function parse() { + const start = this.tokenStart; + + this.next(); + + return { + type: 'Operator', + 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/Parentheses.js b/vanilla/node_modules/css-tree/lib/syntax/node/Parentheses.js new file mode 100644 index 0000000..8c6cdb5 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Parentheses.js @@ -0,0 +1,34 @@ +import { + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'Parentheses'; +export const structure = { + children: [[]] +}; + +export function parse(readSequence, recognizer) { + const start = this.tokenStart; + let children = null; + + this.eat(LeftParenthesis); + + children = readSequence.call(this, recognizer); + + if (!this.eof) { + this.eat(RightParenthesis); + } + + return { + type: 'Parentheses', + loc: this.getLocation(start, this.tokenStart), + children + }; +} + +export function generate(node) { + this.token(LeftParenthesis, '('); + this.children(node); + this.token(RightParenthesis, ')'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Percentage.js b/vanilla/node_modules/css-tree/lib/syntax/node/Percentage.js new file mode 100644 index 0000000..3c8b628 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Percentage.js @@ -0,0 +1,18 @@ +import { Percentage } from '../../tokenizer/index.js'; + +export const name = 'Percentage'; +export const structure = { + value: String +}; + +export function parse() { + return { + type: 'Percentage', + loc: this.getLocation(this.tokenStart, this.tokenEnd), + value: this.consumeNumber(Percentage) + }; +} + +export function generate(node) { + this.token(Percentage, node.value + '%'); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js new file mode 100644 index 0000000..584546c --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/PseudoClassSelector.js @@ -0,0 +1,65 @@ +import { + Ident, + Function as FunctionToken, + Colon, + RightParenthesis +} from '../../tokenizer/index.js'; + + +export const name = 'PseudoClassSelector'; +export const walkContext = 'function'; +export const structure = { + name: String, + children: [['Raw'], null] +}; + +// : [ <ident> | <function-token> <any-value>? ) ] +export function parse() { + const start = this.tokenStart; + let children = null; + let name; + let nameLowerCase; + + this.eat(Colon); + + if (this.tokenType === FunctionToken) { + name = this.consumeFunctionName(); + nameLowerCase = name.toLowerCase(); + + if (this.lookupNonWSType(0) == RightParenthesis) { + children = this.createList(); + } else if (hasOwnProperty.call(this.pseudo, nameLowerCase)) { + this.skipSC(); + children = this.pseudo[nameLowerCase].call(this); + this.skipSC(); + } else { + children = this.createList(); + children.push( + this.Raw(null, false) + ); + } + + this.eat(RightParenthesis); + } else { + name = this.consume(Ident); + } + + return { + type: 'PseudoClassSelector', + loc: this.getLocation(start, this.tokenStart), + name, + children + }; +} + +export function generate(node) { + this.token(Colon, ':'); + + if (node.children === null) { + this.token(Ident, node.name); + } else { + this.token(FunctionToken, node.name + '('); + this.children(node); + this.token(RightParenthesis, ')'); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js b/vanilla/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js new file mode 100644 index 0000000..b23b9a0 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/PseudoElementSelector.js @@ -0,0 +1,66 @@ +import { + Ident, + Function as FunctionToken, + Colon, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'PseudoElementSelector'; +export const walkContext = 'function'; +export const structure = { + name: String, + children: [['Raw'], null] +}; + +// :: [ <ident> | <function-token> <any-value>? ) ] +export function parse() { + const start = this.tokenStart; + let children = null; + let name; + let nameLowerCase; + + this.eat(Colon); + this.eat(Colon); + + if (this.tokenType === FunctionToken) { + name = this.consumeFunctionName(); + nameLowerCase = name.toLowerCase(); + + if (this.lookupNonWSType(0) == RightParenthesis) { + children = this.createList(); + } else if (hasOwnProperty.call(this.pseudo, nameLowerCase)) { + this.skipSC(); + children = this.pseudo[nameLowerCase].call(this); + this.skipSC(); + } else { + children = this.createList(); + children.push( + this.Raw(null, false) + ); + } + + this.eat(RightParenthesis); + } else { + name = this.consume(Ident); + } + + return { + type: 'PseudoElementSelector', + loc: this.getLocation(start, this.tokenStart), + name, + children + }; +} + +export function generate(node) { + this.token(Colon, ':'); + this.token(Colon, ':'); + + if (node.children === null) { + this.token(Ident, node.name); + } else { + this.token(FunctionToken, node.name + '('); + this.children(node); + this.token(RightParenthesis, ')'); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Ratio.js b/vanilla/node_modules/css-tree/lib/syntax/node/Ratio.js new file mode 100644 index 0000000..c26cf10 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Ratio.js @@ -0,0 +1,68 @@ +import { + Delim, + Number as NumberToken, + Function as FunctionToken +} from '../../tokenizer/index.js'; + +const SOLIDUS = 0x002F; // U+002F SOLIDUS (/) + +// Media Queries Level 3 defines terms of <ratio> as a positive (not zero or negative) +// integers (see https://drafts.csswg.org/mediaqueries-3/#values) +// However, Media Queries Level 4 removes any definition of values +// (see https://drafts.csswg.org/mediaqueries-4/#values) and refers to +// CSS Values and Units for detail. In CSS Values and Units Level 4 a <ratio> +// definition was added (see https://drafts.csswg.org/css-values-4/#ratios) which +// defines ratio as "<number [0,∞]> [ / <number [0,∞]> ]?" and based on it +// any constrains on terms were removed. Parser also doesn't test numbers +// in any way to make possible for linting and fixing them by the tools using CSSTree. +// An additional syntax examination may be applied by a lexer. +function consumeTerm() { + this.skipSC(); + + switch (this.tokenType) { + case NumberToken: + return this.Number(); + + case FunctionToken: + return this.Function(this.readSequence, this.scope.Value); + + default: + this.error('Number of function is expected'); + } +} + +export const name = 'Ratio'; +export const structure = { + left: ['Number', 'Function'], + right: ['Number', 'Function', null] +}; + +// <number [0,∞]> [ / <number [0,∞]> ]? +export function parse() { + const start = this.tokenStart; + const left = consumeTerm.call(this); + let right = null; + + this.skipSC(); + if (this.isDelim(SOLIDUS)) { + this.eatDelim(SOLIDUS); + right = consumeTerm.call(this); + } + + return { + type: 'Ratio', + loc: this.getLocation(start, this.tokenStart), + left, + right + }; +} + +export function generate(node) { + this.node(node.left); + this.token(Delim, '/'); + if (node.right) { + this.node(node.right); + } else { + this.node(NumberToken, 1); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Raw.js b/vanilla/node_modules/css-tree/lib/syntax/node/Raw.js new file mode 100644 index 0000000..0c2ea69 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Raw.js @@ -0,0 +1,41 @@ +import { WhiteSpace } from '../../tokenizer/index.js'; + +function getOffsetExcludeWS() { + if (this.tokenIndex > 0) { + if (this.lookupType(-1) === WhiteSpace) { + return this.tokenIndex > 1 + ? this.getTokenStart(this.tokenIndex - 1) + : this.firstCharOffset; + } + } + + return this.tokenStart; +} + +export const name = 'Raw'; +export const structure = { + value: String +}; + +export function parse(consumeUntil, excludeWhiteSpace) { + const startOffset = this.getTokenStart(this.tokenIndex); + let endOffset; + + this.skipUntilBalanced(this.tokenIndex, consumeUntil || this.consumeUntilBalanceEnd); + + if (excludeWhiteSpace && this.tokenStart > startOffset) { + endOffset = getOffsetExcludeWS.call(this); + } else { + endOffset = this.tokenStart; + } + + return { + type: 'Raw', + loc: this.getLocation(startOffset, endOffset), + value: this.substring(startOffset, endOffset) + }; +} + +export function generate(node) { + this.tokenize(node.value); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Rule.js b/vanilla/node_modules/css-tree/lib/syntax/node/Rule.js new file mode 100644 index 0000000..89745cf --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Rule.js @@ -0,0 +1,51 @@ +import { LeftCurlyBracket } from '../../tokenizer/index.js'; + +function consumeRaw() { + return this.Raw(this.consumeUntilLeftCurlyBracket, true); +} + +function consumePrelude() { + const prelude = this.SelectorList(); + + if (prelude.type !== 'Raw' && + this.eof === false && + this.tokenType !== LeftCurlyBracket) { + this.error(); + } + + return prelude; +} + +export const name = 'Rule'; +export const walkContext = 'rule'; +export const structure = { + prelude: ['SelectorList', 'Raw'], + block: ['Block'] +}; + +export function parse() { + const startToken = this.tokenIndex; + const startOffset = this.tokenStart; + let prelude; + let block; + + if (this.parseRulePrelude) { + prelude = this.parseWithFallback(consumePrelude, consumeRaw); + } else { + prelude = consumeRaw.call(this, startToken); + } + + block = this.Block(true); + + return { + type: 'Rule', + loc: this.getLocation(startOffset, this.tokenStart), + prelude, + block + }; +} +export function generate(node) { + this.node(node.prelude); + this.node(node.block); +} + diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Scope.js b/vanilla/node_modules/css-tree/lib/syntax/node/Scope.js new file mode 100644 index 0000000..650b035 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Scope.js @@ -0,0 +1,66 @@ +import { + Ident, + LeftParenthesis, + RightParenthesis +} from '../../tokenizer/index.js'; + +export const name = 'Scope'; +export const structure = { + root: ['SelectorList', 'Raw', null], + limit: ['SelectorList', 'Raw', null] +}; + +export function parse() { + let root = null; + let limit = null; + + this.skipSC(); + + const startOffset = this.tokenStart; + if (this.tokenType === LeftParenthesis) { + this.next(); + this.skipSC(); + root = this.parseWithFallback( + this.SelectorList, + () => this.Raw(false, true) + ); + this.skipSC(); + this.eat(RightParenthesis); + } + + if (this.lookupNonWSType(0) === Ident) { + this.skipSC(); + this.eatIdent('to'); + this.skipSC(); + this.eat(LeftParenthesis); + this.skipSC(); + limit = this.parseWithFallback( + this.SelectorList, + () => this.Raw(false, true) + ); + this.skipSC(); + this.eat(RightParenthesis); + } + + return { + type: 'Scope', + loc: this.getLocation(startOffset, this.tokenStart), + root, + limit + }; +} + +export function generate(node) { + if (node.root) { + this.token(LeftParenthesis, '('); + this.node(node.root); + this.token(RightParenthesis, ')'); + } + + if (node.limit) { + this.token(Ident, 'to'); + this.token(LeftParenthesis, '('); + this.node(node.limit); + this.token(RightParenthesis, ')'); + } +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/Selector.js b/vanilla/node_modules/css-tree/lib/syntax/node/Selector.js new file mode 100644 index 0000000..36028e0 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/Selector.js @@ -0,0 +1,31 @@ +export const name = 'Selector'; +export const structure = { + children: [[ + 'TypeSelector', + 'IdSelector', + 'ClassSelector', + 'AttributeSelector', + 'PseudoClassSelector', + 'PseudoElementSelector', + 'Combinator' + ]] +}; + +export function parse() { + const children = this.readSequence(this.scope.Selector); + + // nothing were consumed + if (this.getFirstListNode(children) === null) { + this.error('Selector is expected'); + } + + return { + type: 'Selector', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/SelectorList.js b/vanilla/node_modules/css-tree/lib/syntax/node/SelectorList.js new file mode 100644 index 0000000..ebba3cd --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/SelectorList.js @@ -0,0 +1,35 @@ +import { Comma } from '../../tokenizer/index.js'; + +export const name = 'SelectorList'; +export const walkContext = 'selector'; +export const structure = { + children: [[ + 'Selector', + 'Raw' + ]] +}; + +export function parse() { + const children = this.createList(); + + while (!this.eof) { + children.push(this.Selector()); + + if (this.tokenType === Comma) { + this.next(); + continue; + } + + break; + } + + return { + type: 'SelectorList', + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node, () => this.token(Comma, ',')); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/String.js b/vanilla/node_modules/css-tree/lib/syntax/node/String.js new file mode 100644 index 0000000..95e1aaa --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/String.js @@ -0,0 +1,19 @@ +import { String as StringToken } from '../../tokenizer/index.js'; +import { decode, encode } from '../../utils/string.js'; + +export const name = 'String'; +export const structure = { + value: String +}; + +export function parse() { + return { + type: 'String', + loc: this.getLocation(this.tokenStart, this.tokenEnd), + value: decode(this.consume(StringToken)) + }; +} + +export function generate(node) { + this.token(StringToken, encode(node.value)); +} diff --git a/vanilla/node_modules/css-tree/lib/syntax/node/StyleSheet.js b/vanilla/node_modules/css-tree/lib/syntax/node/StyleSheet.js new file mode 100644 index 0000000..3bc5347 --- /dev/null +++ b/vanilla/node_modules/css-tree/lib/syntax/node/StyleSheet.js @@ -0,0 +1,82 @@ +import { + WhiteSpace, + Comment, + AtKeyword, + CDO, + CDC +} from '../../tokenizer/index.js'; + +const EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!) + +function consumeRaw() { + return this.Raw(null, false); +} + +export const name = 'StyleSheet'; +export const walkContext = 'stylesheet'; +export const structure = { + children: [[ + 'Comment', + 'CDO', + 'CDC', + 'Atrule', + 'Rule', + 'Raw' + ]] +}; + +export function parse() { + const start = this.tokenStart; + const children = this.createList(); + let child; + + scan: + while (!this.eof) { + switch (this.tokenType) { + case WhiteSpace: + this.next(); + continue; + + case Comment: + // ignore comments except exclamation comments (i.e. /*! .. */) on top level + if (this.charCodeAt(this.tokenStart + 2) !== EXCLAMATIONMARK) { + this.next(); + continue; + } + + child = this.Comment(); + break; + + case CDO: // <!-- + child = this.CDO(); + break; + + case CDC: // --> + child = this.CDC(); + break; + + // CSS Syntax Module Level 3 + // §2.2 Error handling + // At the "top level" of a stylesheet, an <at-keyword-token> 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 <urange> 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). +// +// <urange> = +// u '+' <ident-token> '?'* | +// u <dimension-token> '?'* | +// u <number-token> '?'* | +// u <number-token> <dimension-token> | +// u <number-token> <number-token> | +// u '+' '?'+ +function scanUnicodeRange() { + let hexLength = 0; + + switch (this.tokenType) { + case Number: + // u <number-token> '?'* + // u <number-token> <dimension-token> + // u <number-token> <number-token> + 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 <dimension-token> '?'* + hexLength = eatHexSequence.call(this, 1, true); + + if (hexLength > 0) { + eatQuestionMarkSequence.call(this, 6 - hexLength); + } + + break; + + default: + // u '+' <ident-token> '?'* + // 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 +}; + +// <url-token> | <function-token> <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'; |
