UNPKG

@tbela99/css-parser

Version:

CSS parser for node and the browser

227 lines (224 loc) 8.34 kB
import { ValidationLevel, EnumToken } from '../../ast/types.js'; import '../../ast/minify.js'; import '../../ast/walk.js'; import '../../parser/parse.js'; import { mozExtensions, webkitExtensions } from '../../syntax/syntax.js'; import '../../parser/utils/config.js'; import '../../renderer/color/utils/constants.js'; import '../../renderer/sourcemap/lib/encode.js'; import { consumeWhitespace } from '../utils/whitespace.js'; import { getSyntaxConfig } from '../config.js'; function validateCompoundSelector(tokens, root, options) { if (tokens.length == 0) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: root, // @ts-ignore syntax: null, error: 'expected selector', tokens }; } tokens = tokens.slice(); consumeWhitespace(tokens); const config = getSyntaxConfig(); let match = 0; let length = tokens.length; while (tokens.length > 0) { while (tokens.length > 0 && tokens[0].typ == EnumToken.NestingSelectorTokenType) { if (!options?.nestedSelector) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: tokens[0], syntax: null, error: 'nested selector not allowed', tokens }; } match++; tokens.shift(); consumeWhitespace(tokens); } // <type-selector> while (tokens.length > 0 && [ EnumToken.IdenTokenType, EnumToken.NameSpaceAttributeTokenType, EnumToken.ClassSelectorTokenType, EnumToken.HashTokenType, EnumToken.UniversalSelectorTokenType ].includes(tokens[0].typ)) { match++; tokens.shift(); consumeWhitespace(tokens); } while (tokens.length > 0 && tokens[0].typ == EnumToken.PseudoClassFuncTokenType) { if (!mozExtensions.has(tokens[0].val + '()') && !webkitExtensions.has(tokens[0].val + '()') && !((tokens[0].val + '()') in config.selectors)) { if (!options?.lenient || /^(:?)-webkit-/.test(tokens[0].val)) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: tokens[0], syntax: null, error: 'unknown pseudo-class: ' + tokens[0].val + '()', tokens }; } } match++; tokens.shift(); consumeWhitespace(tokens); } while (tokens.length > 0 && (tokens[0].typ == EnumToken.PseudoElementTokenType || tokens[0].typ == EnumToken.PseudoClassTokenType)) { const isPseudoElement = tokens[0].typ == EnumToken.PseudoElementTokenType; if ( // https://developer.mozilla.org/en-US/docs/Web/CSS/WebKit_Extensions#pseudo-elements !(isPseudoElement && tokens[0].val.startsWith('::-webkit-')) && !mozExtensions.has(tokens[0].val) && !webkitExtensions.has(tokens[0].val) && !(tokens[0].val in config.selectors) && !(!isPseudoElement && (':' + tokens[0].val) in config.selectors)) { if (!options?.lenient || /^(:?)-webkit-/.test(tokens[0].val)) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: tokens[0], syntax: null, error: 'unknown pseudo-class: ' + tokens[0].val, tokens }; } } match++; tokens.shift(); consumeWhitespace(tokens); } while (tokens.length > 0 && tokens[0].typ == EnumToken.AttrTokenType) { const children = tokens[0].chi.slice(); consumeWhitespace(children); if (children.length == 0) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], node: tokens[0], syntax: null, error: 'invalid attribute selector', tokens }; } if (![ EnumToken.IdenTokenType, EnumToken.NameSpaceAttributeTokenType, EnumToken.MatchExpressionTokenType ].includes(children[0].typ)) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], node: tokens[0], syntax: null, error: 'invalid attribute selector', tokens }; } if (children[0].typ == EnumToken.MatchExpressionTokenType) { if (children.length != 1) { return { valid: ValidationLevel.Drop, matches: [], node: tokens[0], syntax: null, error: 'invalid <attribute-selector>', tokens }; } if (![ EnumToken.IdenTokenType, EnumToken.NameSpaceAttributeTokenType ].includes(children[0].l.typ) || ![ EnumToken.EqualMatchTokenType, EnumToken.DashMatchTokenType, EnumToken.StartMatchTokenType, EnumToken.ContainMatchTokenType, EnumToken.EndMatchTokenType, EnumToken.IncludeMatchTokenType ].includes(children[0].op.typ) || ![ EnumToken.StringTokenType, EnumToken.IdenTokenType ].includes(children[0].r.typ)) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], node: tokens[0], syntax: null, error: 'invalid attribute selector', tokens }; } if (children[0].attr != null && !['i', 's'].includes(children[0].attr)) { // @ts-ignore return { valid: ValidationLevel.Drop, matches: [], node: tokens[0], syntax: null, error: 'invalid attribute selector', tokens }; } } match++; tokens.shift(); consumeWhitespace(tokens); } if (length == tokens.length) { return { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: tokens[0], // @ts-ignore syntax: null, error: 'expected compound selector', tokens }; } length = tokens.length; } return match == 0 ? { valid: ValidationLevel.Drop, matches: [], // @ts-ignore node: root, // @ts-ignore syntax: null, error: 'expected compound selector', tokens } : // @ts-ignore { valid: ValidationLevel.Valid, matches: [], // @ts-ignore node: root, // @ts-ignore syntax: null, error: null, tokens }; } export { validateCompoundSelector };