mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(compiler): support complex selectors in :nth-child()
:nth-child() (and its siblings) support complex expressions, e.g. `:nth-child(2n of :is(.foo, .bar))`. Previously we'd choke because of the `:is()`. Now, we reuse the `_parenSuffix` subexpression to match nested parentheses the same way we do for :host() and :host-context(). Note that we only support 3 levels of nesting, so a selector like `:nth-child(n of :is(:has(:not(.foo))))` will still break. I'll say yet again that we really should add a proper parser so we stop getting bug reports like this :) Fixes #64913
This commit is contained in:
parent
4ed8781301
commit
24cfd5a0ed
3 changed files with 20 additions and 2 deletions
|
|
@ -1029,9 +1029,9 @@ class SafeSelector {
|
|||
|
||||
// Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
|
||||
// WS and "+" would otherwise be interpreted as selector separators.
|
||||
this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
|
||||
this._content = selector.replace(nthRegex, (_, pseudo, exp) => {
|
||||
const replaceBy = `__ph-${this.index}__`;
|
||||
this.placeholders.push(exp);
|
||||
this.placeholders.push(`(${exp})`);
|
||||
this.index++;
|
||||
return pseudo + replaceBy;
|
||||
});
|
||||
|
|
@ -1076,6 +1076,7 @@ const _level1Parens = String.raw`(?:\(${_noParens}\)|${_noParens})+?`;
|
|||
// Matches content with at most TWO levels of nesting, e.g., "a(b(c)d)e"
|
||||
const _level2Parens = String.raw`(?:\(${_level1Parens}\)|${_noParens})+?`;
|
||||
const _parenSuffix = String.raw`(?:\((${_level2Parens})\))`;
|
||||
const nthRegex = new RegExp(String.raw`(:nth-[-\w]+)` + _parenSuffix, 'g');
|
||||
const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix + '?([^,{]*)', 'gim');
|
||||
// note: :host-context patterns are terminated with `{`, as opposed to :host which
|
||||
// is both `{` and `,` because :host-context handles top-level commas differently.
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ describe('ShadowCss, :host and :host-context', () => {
|
|||
expect(shim(':host:nth-child(8n+1) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'[a-host]:nth-child(8n+1) {}',
|
||||
);
|
||||
expect(shim(':host(:nth-child(3n of :not(p, a))) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'[a-host]:nth-child(3n of :not(p, a)) {}',
|
||||
);
|
||||
expect(shim(':host:nth-of-type(8n+1) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'[a-host]:nth-of-type(8n+1) {}',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -214,6 +214,20 @@ describe('ShadowCss', () => {
|
|||
'table[contenta] [contenta]:where(td, th):hover { color:lime;}',
|
||||
);
|
||||
|
||||
// :nth
|
||||
expect(shim(':nth-child(3n of :not(p, a), :is(.foo)) {}', 'contenta', 'hosta')).toEqualCss(
|
||||
'[contenta]:nth-child(3n of :not(p, a), :is(.foo)) {}',
|
||||
);
|
||||
expect(shim('li:nth-last-child(-n + 3) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'li[contenta]:nth-last-child(-n + 3) {}',
|
||||
);
|
||||
expect(shim('dd:nth-last-of-type(3n) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'dd[contenta]:nth-last-of-type(3n) {}',
|
||||
);
|
||||
expect(shim('dd:nth-of-type(even) {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'dd[contenta]:nth-of-type(even) {}',
|
||||
);
|
||||
|
||||
// complex selectors
|
||||
expect(shim(':host:is([foo],[foo-2])>div.example-2 {}', 'contenta', 'a-host')).toEqualCss(
|
||||
'[a-host]:is([foo],[foo-2]) > div.example-2[contenta] {}',
|
||||
|
|
|
|||
Loading…
Reference in a new issue