Skip to content

Commit 87dafd5

Browse files
committed
nested selector validation #43
1 parent 5d70a9a commit 87dafd5

File tree

12 files changed

+783
-271
lines changed

12 files changed

+783
-271
lines changed

dist/index-umd-web.js

Lines changed: 168 additions & 63 deletions
Large diffs are not rendered by default.

dist/index.cjs

Lines changed: 168 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3395,6 +3395,10 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
33953395
return '<';
33963396
case exports.EnumToken.LteTokenType:
33973397
return '<=';
3398+
case exports.EnumToken.SubsequentSiblingCombinatorTokenType:
3399+
return '~';
3400+
case exports.EnumToken.NextSiblingCombinatorTokenType:
3401+
return '+';
33983402
case exports.EnumToken.GtTokenType:
33993403
case exports.EnumToken.ChildCombinatorTokenType:
34003404
return '>';
@@ -3408,6 +3412,7 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
34083412
return '[';
34093413
case exports.EnumToken.AttrEndTokenType:
34103414
return ']';
3415+
case exports.EnumToken.DescendantCombinatorTokenType:
34113416
case exports.EnumToken.WhitespaceTokenType:
34123417
return ' ';
34133418
case exports.EnumToken.ColonTokenType:
@@ -58998,58 +59003,86 @@ const expressions = [
5899859003
exports.EnumToken.DelimTokenType, exports.EnumToken.IncludeMatchTokenType, exports.EnumToken.DashMatchTokenType,
5899959004
exports.EnumToken.StartMatchTokenType, exports.EnumToken.EndMatchTokenType, exports.EnumToken.ContainMatchTokenType
5900059005
];
59006+
const selectorTokens = [
59007+
exports.EnumToken.IdenTokenType, exports.EnumToken.ClassSelectorTokenType, exports.EnumToken.AttrTokenType,
59008+
exports.EnumToken.PseudoClassTokenType, exports.EnumToken.PseudoClassFuncTokenType, exports.EnumToken.HashTokenType,
59009+
exports.EnumToken.UniversalSelectorTokenType
59010+
];
59011+
const combinatorTokens = [
59012+
exports.EnumToken.ChildCombinatorTokenType, exports.EnumToken.NextSiblingCombinatorTokenType,
59013+
exports.EnumToken.SubsequentSiblingCombinatorTokenType
59014+
];
5900159015
function validateSelector(selector, root) {
5900259016
return selector.length > 0 && doValidateSelector(selector, root) != null;
5900359017
}
5900459018
function doValidateSelector(selector, root) {
5900559019
let result = null;
59020+
if (selector.length == 0) {
59021+
return selector;
59022+
}
59023+
if (combinatorTokens.includes(selector[0].typ)) {
59024+
if (root == null || root.typ == exports.EnumToken.StyleSheetNodeType) {
59025+
return null;
59026+
}
59027+
selector = selector.slice(1);
59028+
}
5900659029
while ((selector?.length ?? 0) > 0) {
5900759030
result = validateSimpleSelector(selector, root);
59008-
// console.error([0, result], selector);
59031+
// console.error(0, {result, selector});
5900959032
if (result == null) {
5901059033
return null;
5901159034
}
59012-
selector = consumeWhitespace(result);
59013-
if (selector.length == 0) {
59014-
break;
59035+
if (result.length == 0) {
59036+
return result;
5901559037
}
59038+
selector = result;
5901659039
result = validateCombinator(selector);
59017-
// console.error([1, result], selector);
59040+
// console.error(1, {result, selector});
5901859041
if (result == null) {
59019-
if (selector[0].typ == exports.EnumToken.CommaTokenType) {
59020-
result = validateSimpleSelector(selector.slice(1), root);
59021-
// console.error([2, result], selector);
59022-
if (result == null || result.length == selector.length) {
59023-
// console.error({result, selector});
59024-
// console.error('unexpected token: ', JSON.stringify(selector[0], null, 1));
59025-
// console.error(new Error('unexpected token:\n' + JSON.stringify(selector[0], null, 1)));
59026-
return null;
59042+
while (result == null && selector.length > 0) {
59043+
if (selector[0].typ == exports.EnumToken.CommaTokenType) {
59044+
selector = selector.slice(1);
59045+
// console.error(2, {result, selector});
59046+
if (selector.length > 0 && combinatorTokens.includes(selector[0].typ)) {
59047+
if (root == null || root.typ == exports.EnumToken.StyleSheetNodeType) {
59048+
return null;
59049+
}
59050+
selector = selector.slice(1);
59051+
// console.error(3, {result, selector});
59052+
}
59053+
result = validateSimpleSelector(selector, root);
59054+
// console.error(4, {result, selector});
59055+
if (result == null) {
59056+
// console.error({result, selector});
59057+
// console.error('unexpected token: ', JSON.stringify(selector[0], null, 1));
59058+
// console.error(new Error('unexpected token:\n' + JSON.stringify(selector[0], null, 1)));
59059+
return null;
59060+
}
59061+
selector = result;
59062+
result = validateCombinator(selector);
59063+
if (result != null) {
59064+
if (result.length == 0) {
59065+
return result;
59066+
}
59067+
selector = result;
59068+
}
59069+
}
59070+
else {
59071+
break;
5902759072
}
59028-
selector = result;
59029-
continue;
5903059073
}
5903159074
}
5903259075
else {
5903359076
selector = result;
5903459077
}
59035-
result = validateSimpleSelector(consumeWhitespace(selector), root);
59036-
// console.error([3, result], selector);
59037-
if (result == null) {
59038-
return null;
59039-
}
59040-
if (result.length > 0 && result.length == selector.length) {
59041-
// console.error([4, result], selector);
59042-
// console.error('unexpected token: ', JSON.stringify(result[0], null, 1));
59043-
// console.error(new Error('unexpected token:\n' + JSON.stringify(result[0], null, 1)));
59044-
return null;
59045-
}
59046-
selector = result;
5904759078
}
5904859079
return selector;
5904959080
}
5905059081
function consumeWhitespace(selector) {
5905159082
let i = 0;
59052-
while (i < selector.length && (selector[i].typ == exports.EnumToken.WhitespaceTokenType || selector[i].typ == exports.EnumToken.CommentTokenType)) {
59083+
while (i < selector.length &&
59084+
(selector[i].typ == exports.EnumToken.WhitespaceTokenType ||
59085+
selector[i].typ == exports.EnumToken.CommentTokenType)) {
5905359086
i++;
5905459087
}
5905559088
return selector.slice(i);
@@ -59079,36 +59112,31 @@ function validateSimpleSelector(selector, root) {
5907959112
}
5908059113
return selector.slice(i);
5908159114
}
59082-
if (selector[i].typ == exports.EnumToken.IdenTokenType) {
59115+
while (i < selector.length && [exports.EnumToken.WhitespaceTokenType, exports.EnumToken.CommentTokenType].includes(selector[i].typ)) {
5908359116
i++;
5908459117
}
59085-
while (i < selector.length) {
59086-
if ([
59087-
exports.EnumToken.HashTokenType,
59088-
exports.EnumToken.AttrTokenType,
59089-
exports.EnumToken.LiteralTokenType,
59090-
exports.EnumToken.CommentTokenType,
59091-
exports.EnumToken.PseudoClassTokenType,
59092-
exports.EnumToken.ClassSelectorTokenType,
59093-
exports.EnumToken.PseudoClassFuncTokenType,
59094-
exports.EnumToken.UniversalSelectorTokenType
59095-
].includes(selector[i].typ) || (selector[i].typ == exports.EnumToken.NestingSelectorTokenType && root != null)) {
59096-
if (selector[i].typ == exports.EnumToken.AttrTokenType && !validateAttributeSelector(selector[i])) {
59097-
return null;
59098-
}
59099-
if (selector[i].typ == exports.EnumToken.PseudoClassTokenType && !validatePseudoClass(selector[i])) {
59100-
return null;
59101-
}
59102-
if (selector[i].typ == exports.EnumToken.PseudoClassFuncTokenType && !validatePseudoClassFunction(selector[i])) {
59103-
return null;
59104-
}
59105-
i++;
59118+
if (i >= selector.length) {
59119+
return selector.slice(i);
59120+
}
59121+
if (selectorTokens.includes(selector[i].typ)) {
59122+
if (selector[i].typ == exports.EnumToken.PseudoClassTokenType && !validatePseudoClass(selector[i])) {
59123+
return null;
5910659124
}
59107-
else {
59108-
break;
59125+
if (selector[i].typ == exports.EnumToken.PseudoClassFuncTokenType && !validatePseudoClassFunction(selector[i])) {
59126+
return null;
5910959127
}
59128+
if (selector[i].typ == exports.EnumToken.AttrTokenType && !validateAttributeSelector(selector[i])) {
59129+
return null;
59130+
}
59131+
return selector.slice(i + 1);
5911059132
}
59111-
return selector.slice(i);
59133+
if (selector[i].typ == exports.EnumToken.LiteralTokenType && selector[i].val.startsWith('\\')) {
59134+
return selector.slice(i + 1);
59135+
}
59136+
if (selector[i].typ == exports.EnumToken.NestingSelectorTokenType && root != null && root.typ != exports.EnumToken.StyleSheetNodeType) {
59137+
return selector.slice(i + 1);
59138+
}
59139+
return null;
5911259140
}
5911359141
function validatePseudoClass(selector) {
5911459142
const name = selector.val.slice(selector.val[1] == ':' ? 2 : 1);
@@ -59274,7 +59302,44 @@ const enumTokenHints = new Set([
5927459302
const webkitPseudoAliasMap = {
5927559303
'-webkit-autofill': 'autofill',
5927659304
'-webkit-any': 'is',
59277-
'-moz-any': 'is'
59305+
'-moz-any': 'is',
59306+
'-webkit-border-after': 'border-block-end',
59307+
'-webkit-border-after-color': 'border-block-end-color',
59308+
'-webkit-border-after-style': 'border-block-end-style',
59309+
'-webkit-border-after-width': 'border-block-end-width',
59310+
'-webkit-border-before': 'border-block-start',
59311+
'-webkit-border-before-color': 'border-block-start-color',
59312+
'-webkit-border-before-style': 'border-block-start-style',
59313+
'-webkit-border-before-width': 'border-block-start-width',
59314+
'-webkit-border-end': 'border-inline-end',
59315+
'-webkit-border-end-color': 'border-inline-end-color',
59316+
'-webkit-border-end-style': 'border-inline-end-style',
59317+
'-webkit-border-end-width': 'border-inline-end-width',
59318+
'-webkit-border-start': 'border-inline-start',
59319+
'-webkit-border-start-color': 'border-inline-start-color',
59320+
'-webkit-border-start-style': 'border-inline-start-style',
59321+
'-webkit-border-start-width': 'border-inline-start-width',
59322+
'-webkit-box-align': 'align-items',
59323+
'-webkit-box-direction': 'flex-direction',
59324+
'-webkit-box-flex': 'flex-grow',
59325+
'-webkit-box-lines': 'flex-flow',
59326+
'-webkit-box-ordinal-group': 'order',
59327+
'-webkit-box-orient': 'flex-direction',
59328+
'-webkit-box-pack': 'justify-content',
59329+
'-webkit-column-break-after': 'break-after',
59330+
'-webkit-column-break-before': 'break-before',
59331+
'-webkit-column-break-inside': 'break-inside',
59332+
'-webkit-font-feature-settings': 'font-feature-settings',
59333+
'-webkit-hyphenate-character': 'hyphenate-character',
59334+
'-webkit-initial-letter': 'initial-letter',
59335+
'-webkit-margin-end': 'margin-block-end',
59336+
'-webkit-margin-start': 'margin-block-start',
59337+
'-webkit-padding-after': 'padding-block-end',
59338+
'-webkit-padding-before': 'padding-block-start',
59339+
'-webkit-padding-end': 'padding-inline-end',
59340+
'-webkit-padding-start': 'padding-inline-start',
59341+
'-webkit-min-device-pixel-ratio': 'min-resolution',
59342+
'-webkit-max-device-pixel-ratio': 'max-resolution'
5927859343
};
5927959344
function reject(reason) {
5928059345
throw new Error(reason ?? 'Parsing aborted');
@@ -59715,24 +59780,28 @@ async function parseNode(results, context, stats, options, errors, src, map) {
5971559780
return acc;
5971659781
}, uniq);
5971759782
const ruleType = context.typ == exports.EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? exports.EnumToken.KeyFrameRuleNodeType : exports.EnumToken.RuleNodeType;
59718-
const node = {
59719-
typ: ruleType,
59720-
// @ts-ignore
59721-
sel: [...uniq.keys()].join(','),
59722-
chi: []
59723-
};
5972459783
if (ruleType == exports.EnumToken.RuleNodeType) {
5972559784
parseSelector(tokens);
5972659785
const valid = validateSelector(tokens, context);
5972759786
if (!valid) {
59787+
const node = {
59788+
typ: exports.EnumToken.InvalidRuleTokenType,
59789+
// @ts-ignore
59790+
sel: tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), ''),
59791+
chi: []
59792+
};
5972859793
errors.push({ action: 'drop', message: 'invalid selector', location: { src, ...position } });
59729-
node.typ = exports.EnumToken.InvalidRuleTokenType;
59730-
node.sel = tokens;
5973159794
// @ts-ignore
5973259795
context.chi.push(node);
5973359796
return node;
5973459797
}
5973559798
}
59799+
const node = {
59800+
typ: ruleType,
59801+
// @ts-ignore
59802+
sel: [...uniq.keys()].join(','),
59803+
chi: []
59804+
};
5973659805
let raw = [...uniq.values()];
5973759806
Object.defineProperty(node, 'raw', {
5973859807
enumerable: false,
@@ -59829,7 +59898,23 @@ function parseSelector(tokens) {
5982959898
// @ts-ignore
5983059899
else if (value.typ == exports.EnumToken.WhitespaceTokenType) {
5983159900
if (nextValue != null && nextValue.typ == exports.EnumToken.LiteralTokenType) {
59832-
if (['>', '+', '~'].includes(value.val)) {
59901+
if (['>', '+', '~'].includes(nextValue.val)) {
59902+
switch (value.val) {
59903+
case '>':
59904+
// @ts-ignore
59905+
nextValue.typ = exports.EnumToken.ChildCombinatorTokenType;
59906+
break;
59907+
case '+':
59908+
// @ts-ignore
59909+
nextValue.typ = exports.EnumToken.NextSiblingCombinatorTokenType;
59910+
break;
59911+
case '~':
59912+
// @ts-ignore
59913+
nextValue.typ = exports.EnumToken.SubsequentSiblingCombinatorTokenType;
59914+
break;
59915+
}
59916+
// @ts-ignore
59917+
delete nextValue.val;
5983359918
continue;
5983459919
}
5983559920
}
@@ -59912,6 +59997,26 @@ function parseSelector(tokens) {
5991259997
}
5991359998
}
5991459999
}
60000+
let i = 0;
60001+
for (; i < tokens.length; i++) {
60002+
if ([
60003+
exports.EnumToken.ChildCombinatorTokenType,
60004+
exports.EnumToken.NextSiblingCombinatorTokenType,
60005+
exports.EnumToken.SubsequentSiblingCombinatorTokenType
60006+
].includes(tokens[i].typ)) {
60007+
if (i + 1 < tokens.length && [exports.EnumToken.WhitespaceTokenType, exports.EnumToken.DescendantCombinatorTokenType].includes(tokens[i + 1].typ)) {
60008+
tokens.splice(i + 1, 1);
60009+
}
60010+
if (i > 0 && [exports.EnumToken.WhitespaceTokenType, exports.EnumToken.DescendantCombinatorTokenType].includes(tokens[i - 1].typ)) {
60011+
tokens.splice(i - 1, 1);
60012+
i--;
60013+
continue;
60014+
}
60015+
}
60016+
if (tokens[i].typ == exports.EnumToken.WhitespaceTokenType) {
60017+
tokens[i].typ = exports.EnumToken.DescendantCombinatorTokenType;
60018+
}
60019+
}
5991560020
return tokens;
5991660021
}
5991760022
function parseString(src, options = { location: false }) {

dist/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ declare enum EnumToken {
7575
ClassSelectorTokenType = 73,
7676
UniversalSelectorTokenType = 74,
7777
ChildCombinatorTokenType = 75,
78-
DescendantCombinatorTokenType = 76,
78+
DescendantCombinatorTokenType = 76,// whitespace
7979
NextSiblingCombinatorTokenType = 77,
8080
SubsequentSiblingCombinatorTokenType = 78,
8181
NestingSelectorTokenType = 79,

0 commit comments

Comments
 (0)