From 3f9ebc085c620dffe1fe55d6b67549a5b3446879 Mon Sep 17 00:00:00 2001 From: Bramus Date: Mon, 12 Dec 2022 19:40:21 +0100 Subject: [PATCH] ::slotted() should only accept a --- .../selector/functional-pseudo/slotted.json | 5 +++ lib/__tests/common.js | 2 +- lib/__tests/walk.js | 2 +- lib/syntax/config/parser.js | 1 + lib/syntax/node/CompoundSelector.js | 39 +++++++++++++++++++ lib/syntax/node/index-generate.js | 1 + lib/syntax/node/index-parse-selector.js | 1 + lib/syntax/node/index-parse.js | 1 + lib/syntax/node/index.js | 1 + lib/syntax/pseudo/index.js | 10 ++++- 10 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 lib/syntax/node/CompoundSelector.js diff --git a/fixtures/ast/selector/functional-pseudo/slotted.json b/fixtures/ast/selector/functional-pseudo/slotted.json index ce0521c0..419aa0a8 100644 --- a/fixtures/ast/selector/functional-pseudo/slotted.json +++ b/fixtures/ast/selector/functional-pseudo/slotted.json @@ -59,6 +59,11 @@ } }, "error": [ + { + "source": "::slotted(.a .b)", + "offset": " ^", + "error": "CompoundSelector is expected" + }, { "source": "::slotted(.a{)", "offset": " ^", diff --git a/lib/__tests/common.js b/lib/__tests/common.js index 326c8cb1..5da9c497 100644 --- a/lib/__tests/common.js +++ b/lib/__tests/common.js @@ -39,7 +39,7 @@ describe('Common', () => { assert.deepStrictEqual( [...foundTypes].sort(), - types.sort().filter(type => type !== 'WhiteSpace') // FIXME: temporary filter white space + types.sort().filter(type => !['WhiteSpace', 'CompoundSelector'].includes(type)) // FIXME: temporary filter white space ); }); diff --git a/lib/__tests/walk.js b/lib/__tests/walk.js index a5598e6f..752a8bee 100644 --- a/lib/__tests/walk.js +++ b/lib/__tests/walk.js @@ -561,7 +561,7 @@ describe('AST traversal', () => { it('should throws when visit has wrong value', () => { assert.throws( () => walk(ast, { visit: 'Foo' }), - /Bad value `Foo` for `visit` option \(should be: AnPlusB, Atrule, AtrulePrelude, AttributeSelector, Block, Brackets, CDC, CDO, ClassSelector, Combinator, Comment, Declaration, DeclarationList, Dimension, Function, Hash, IdSelector, Identifier, MediaFeature, MediaQuery, MediaQueryList, NestingSelector, Nth, Number, Operator, Parentheses, Percentage, PseudoClassSelector, PseudoElementSelector, Ratio, Raw, Rule, Selector, SelectorList, String, StyleSheet, TypeSelector, UnicodeRange, Url, Value, WhiteSpace\)/ + /Bad value `Foo` for `visit` option \(should be: AnPlusB, Atrule, AtrulePrelude, AttributeSelector, Block, Brackets, CDC, CDO, ClassSelector, Combinator, Comment, CompoundSelector, Declaration, DeclarationList, Dimension, Function, Hash, IdSelector, Identifier, MediaFeature, MediaQuery, MediaQueryList, NestingSelector, Nth, Number, Operator, Parentheses, Percentage, PseudoClassSelector, PseudoElementSelector, Ratio, Raw, Rule, Selector, SelectorList, String, StyleSheet, TypeSelector, UnicodeRange, Url, Value, WhiteSpace\)/ ); }); }); diff --git a/lib/syntax/config/parser.js b/lib/syntax/config/parser.js index fe2cae23..21e937ba 100644 --- a/lib/syntax/config/parser.js +++ b/lib/syntax/config/parser.js @@ -16,6 +16,7 @@ export default { rule: 'Rule', selectorList: 'SelectorList', selector: 'Selector', + compoundSelector: 'CompoundSelector', block() { return this.Block(true); }, diff --git a/lib/syntax/node/CompoundSelector.js b/lib/syntax/node/CompoundSelector.js new file mode 100644 index 00000000..0ce4b2f9 --- /dev/null +++ b/lib/syntax/node/CompoundSelector.js @@ -0,0 +1,39 @@ +export const name = 'CompoundSelector'; +export const structure = { + children: [[ + 'TypeSelector', + 'IdSelector', + 'ClassSelector', + 'AttributeSelector', + 'PseudoClassSelector', + 'PseudoElementSelector', + 'Combinator', + 'WhiteSpace' + ]] +}; + +export function parse() { + const children = this.readSequence(this.scope.Selector); + + // nothing were consumed + if (this.getFirstListNode(children) === null) { + this.error('Selector is expected'); + } + + // Selector Contains Combinator + children.forEach((entry) => { + if (entry.type === 'Combinator') { + this.error('CompoundSelector is expected'); + } + }); + + return { + type: 'Selector', // Report as Selector + loc: this.getLocationFromList(children), + children + }; +} + +export function generate(node) { + this.children(node); +} diff --git a/lib/syntax/node/index-generate.js b/lib/syntax/node/index-generate.js index 2aae5684..eb6d03fe 100644 --- a/lib/syntax/node/index-generate.js +++ b/lib/syntax/node/index-generate.js @@ -29,6 +29,7 @@ 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 CompoundSelector } from './CompoundSelector.js'; export { generate as Selector } from './Selector.js'; export { generate as SelectorList } from './SelectorList.js'; export { generate as String } from './String.js'; diff --git a/lib/syntax/node/index-parse-selector.js b/lib/syntax/node/index-parse-selector.js index 1eb775f8..898e6eb1 100644 --- a/lib/syntax/node/index-parse-selector.js +++ b/lib/syntax/node/index-parse-selector.js @@ -9,6 +9,7 @@ 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 CompoundSelector } from './CompoundSelector.js'; export { parse as Selector } from './Selector.js'; export { parse as SelectorList } from './SelectorList.js'; export { parse as String } from './String.js'; diff --git a/lib/syntax/node/index-parse.js b/lib/syntax/node/index-parse.js index 22c372f1..53afc7b0 100644 --- a/lib/syntax/node/index-parse.js +++ b/lib/syntax/node/index-parse.js @@ -29,6 +29,7 @@ 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 CompoundSelector } from './CompoundSelector.js'; export { parse as Selector } from './Selector.js'; export { parse as SelectorList } from './SelectorList.js'; export { parse as String } from './String.js'; diff --git a/lib/syntax/node/index.js b/lib/syntax/node/index.js index 61f2f46a..9c2ea382 100644 --- a/lib/syntax/node/index.js +++ b/lib/syntax/node/index.js @@ -30,6 +30,7 @@ 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 CompoundSelector from './CompoundSelector.js'; export * as Selector from './Selector.js'; export * as SelectorList from './SelectorList.js'; export * as String from './String.js'; diff --git a/lib/syntax/pseudo/index.js b/lib/syntax/pseudo/index.js index 7a336917..0b9cb678 100644 --- a/lib/syntax/pseudo/index.js +++ b/lib/syntax/pseudo/index.js @@ -30,6 +30,14 @@ const nth = { } }; +const compoundSelector = { + parse() { + return this.createSingleNodeList( + this.CompoundSelector() + ); + } +}; + export default { 'dir': identList, 'has': selectorList, @@ -44,5 +52,5 @@ export default { 'nth-last-child': nth, 'nth-last-of-type': nth, 'nth-of-type': nth, - 'slotted': selector + 'slotted': compoundSelector };