fix(core): fix pseudo-selector shimming (#12754)
fixes #12730 fixes #12354
This commit is contained in:
parent
f3793b5953
commit
acbf1d859c
|
@ -378,13 +378,18 @@ export class ShadowCss {
|
||||||
string {
|
string {
|
||||||
// In Android browser, the lastIndex is not reset when the regex is used in String.replace()
|
// In Android browser, the lastIndex is not reset when the regex is used in String.replace()
|
||||||
_polyfillHostRe.lastIndex = 0;
|
_polyfillHostRe.lastIndex = 0;
|
||||||
|
|
||||||
if (_polyfillHostRe.test(selector)) {
|
if (_polyfillHostRe.test(selector)) {
|
||||||
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
||||||
return selector
|
return selector
|
||||||
.replace(
|
.replace(
|
||||||
_polyfillHostNoCombinatorRe,
|
_polyfillHostNoCombinatorRe,
|
||||||
(hnc, selector) => selector[0] === ':' ? replaceBy + selector : selector + replaceBy)
|
(hnc, selector) => {
|
||||||
|
return selector.replace(
|
||||||
|
/([^:]*)(:*)(.*)/,
|
||||||
|
(_: string, before: string, colon: string, after: string) => {
|
||||||
|
return before + replaceBy + colon + after;
|
||||||
|
});
|
||||||
|
})
|
||||||
.replace(_polyfillHostRe, replaceBy + ' ');
|
.replace(_polyfillHostRe, replaceBy + ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,10 +416,10 @@ export class ShadowCss {
|
||||||
scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
|
scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
|
||||||
} else {
|
} else {
|
||||||
// remove :host since it should be unnecessary
|
// remove :host since it should be unnecessary
|
||||||
var t = p.replace(_polyfillHostRe, '');
|
const t = p.replace(_polyfillHostRe, '');
|
||||||
if (t.length > 0) {
|
if (t.length > 0) {
|
||||||
const matches = t.match(/([^:]*)(:*)(.*)/);
|
const matches = t.match(/([^:]*)(:*)(.*)/);
|
||||||
if (matches !== null) {
|
if (matches) {
|
||||||
scopedP = matches[1] + attrName + matches[2] + matches[3];
|
scopedP = matches[1] + attrName + matches[2] + matches[3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,17 +428,8 @@ export class ShadowCss {
|
||||||
return scopedP;
|
return scopedP;
|
||||||
};
|
};
|
||||||
|
|
||||||
let attrSelectorIndex = 0;
|
const safeContent = new SafeSelector(selector);
|
||||||
const attrSelectors: string[] = [];
|
selector = safeContent.content();
|
||||||
|
|
||||||
// replace attribute selectors with placeholders to avoid issue with white space being treated
|
|
||||||
// as separator
|
|
||||||
selector = selector.replace(/\[[^\]]*\]/g, (attrSelector) => {
|
|
||||||
const replaceBy = `__attr_sel_${attrSelectorIndex}__`;
|
|
||||||
attrSelectors.push(attrSelector);
|
|
||||||
attrSelectorIndex++;
|
|
||||||
return replaceBy;
|
|
||||||
});
|
|
||||||
|
|
||||||
let scopedSelector = '';
|
let scopedSelector = '';
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
|
@ -454,7 +450,7 @@ export class ShadowCss {
|
||||||
scopedSelector += _scopeSelectorPart(selector.substring(startIndex));
|
scopedSelector += _scopeSelectorPart(selector.substring(startIndex));
|
||||||
|
|
||||||
// replace the placeholders with their original values
|
// replace the placeholders with their original values
|
||||||
return scopedSelector.replace(/__attr_sel_(\d+)__/g, (ph, index) => attrSelectors[+index]);
|
return safeContent.restore(scopedSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _insertPolyfillHostInCssText(selector: string): string {
|
private _insertPolyfillHostInCssText(selector: string): string {
|
||||||
|
@ -462,6 +458,39 @@ export class ShadowCss {
|
||||||
.replace(_colonHostRe, _polyfillHost);
|
.replace(_colonHostRe, _polyfillHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SafeSelector {
|
||||||
|
private placeholders: string[] = [];
|
||||||
|
private index = 0;
|
||||||
|
private _content: string;
|
||||||
|
|
||||||
|
constructor(selector: string) {
|
||||||
|
// Replaces attribute selectors with placeholders.
|
||||||
|
// The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
|
||||||
|
selector = selector.replace(/(\[[^\]]*\])/g, (_, keep) => {
|
||||||
|
const replaceBy = `__ph-${this.index}__`;
|
||||||
|
this.placeholders.push(keep);
|
||||||
|
this.index++;
|
||||||
|
return replaceBy;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
const replaceBy = `__ph-${this.index}__`;
|
||||||
|
this.placeholders.push(exp);
|
||||||
|
this.index++;
|
||||||
|
return pseudo + replaceBy;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
restore(content: string): string {
|
||||||
|
return content.replace(/__ph-(\d+)__/g, (ph, index) => this.placeholders[+index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
content(): string { return this._content; }
|
||||||
|
}
|
||||||
|
|
||||||
const _cssContentNextSelectorRe =
|
const _cssContentNextSelectorRe =
|
||||||
/polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
/polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
||||||
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
||||||
|
|
|
@ -140,9 +140,15 @@ export function main() {
|
||||||
.toEqual('[a="b"][a-host], [c="d"][a-host] {}');
|
.toEqual('[a="b"][a-host], [c="d"][a-host] {}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle pseudo selector', () => {
|
it('should handle pseudo selectors', () => {
|
||||||
expect(s(':host(:before) {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
expect(s(':host(:before) {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
||||||
expect(s(':host:before {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
expect(s(':host:before {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
||||||
|
expect(s(':host:nth-child(8n+1) {}', 'a', 'a-host')).toEqual('[a-host]:nth-child(8n+1) {}');
|
||||||
|
expect(s(':host:nth-of-type(8n+1) {}', 'a', 'a-host'))
|
||||||
|
.toEqual('[a-host]:nth-of-type(8n+1) {}');
|
||||||
|
expect(s(':host(.class):before {}', 'a', 'a-host')).toEqual('.class[a-host]:before {}');
|
||||||
|
expect(s(':host.class:before {}', 'a', 'a-host')).toEqual('.class[a-host]:before {}');
|
||||||
|
expect(s(':host(:not(p)):before {}', 'a', 'a-host')).toEqual('[a-host]:not(p):before {}');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue