fix(compiler): fix corner cases in shadow CSS
`cmp:host {}` and `cmp:host some-other-selector {}` were not handled consistently. Note those should not match anything but are made equivalent to respectively `:host(cmp)` and `:host(cmp) some-other-selector` to avoid breaking legacy apps.
This commit is contained in:
parent
78ba39bfe2
commit
c32f5fd393
|
@ -435,19 +435,34 @@ export class ShadowCss {
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
let res: RegExpExecArray|null;
|
let res: RegExpExecArray|null;
|
||||||
const sep = /( |>|\+|~(?!=))\s*/g;
|
const sep = /( |>|\+|~(?!=))\s*/g;
|
||||||
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
|
|
||||||
|
// If a selector appears before :host it should not be shimmed as it
|
||||||
|
// matches on ancestor elements and not on elements in the host's shadow
|
||||||
|
// `:host-context(div)` is transformed to
|
||||||
|
// `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
|
||||||
|
// the `div` is not part of the component in the 2nd selectors and should not be scoped.
|
||||||
|
// Historically `component-tag:host` was matching the component so we also want to preserve
|
||||||
|
// this behavior to avoid breaking legacy apps (it should not match).
|
||||||
|
// The behavior should be:
|
||||||
|
// - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
|
||||||
|
// - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
|
||||||
|
// `:host-context(tag)`)
|
||||||
|
const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
|
||||||
|
// Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
|
||||||
|
let shouldScope = !hasHost;
|
||||||
|
|
||||||
while ((res = sep.exec(selector)) !== null) {
|
while ((res = sep.exec(selector)) !== null) {
|
||||||
const separator = res[1];
|
const separator = res[1];
|
||||||
const part = selector.slice(startIndex, res.index).trim();
|
const part = selector.slice(startIndex, res.index).trim();
|
||||||
// if a selector appears before :host-context it should not be shimmed as it
|
shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
||||||
// matches on ancestor elements and not on elements in the host's shadow
|
const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
|
||||||
const scopedPart = startIndex >= scopeAfter ? _scopeSelectorPart(part) : part;
|
|
||||||
scopedSelector += `${scopedPart} ${separator} `;
|
scopedSelector += `${scopedPart} ${separator} `;
|
||||||
startIndex = sep.lastIndex;
|
startIndex = sep.lastIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
scopedSelector += _scopeSelectorPart(selector.substring(startIndex));
|
const part = selector.substring(startIndex);
|
||||||
|
shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
||||||
|
scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
|
||||||
|
|
||||||
// replace the placeholders with their original values
|
// replace the placeholders with their original values
|
||||||
return safeContent.restore(scopedSelector);
|
return safeContent.restore(scopedSelector);
|
||||||
|
|
|
@ -150,6 +150,18 @@ export function main() {
|
||||||
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 {}');
|
expect(s(':host(:not(p)):before {}', 'a', 'a-host')).toEqual('[a-host]:not(p):before {}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// see b/63672152
|
||||||
|
it('should handle unexpected selectors in the most reasonable way', () => {
|
||||||
|
expect(s('cmp:host {}', 'a', 'a-host')).toEqual('cmp[a-host] {}');
|
||||||
|
expect(s('cmp:host >>> {}', 'a', 'a-host')).toEqual('cmp[a-host] {}');
|
||||||
|
expect(s('cmp:host child {}', 'a', 'a-host')).toEqual('cmp[a-host] child[a] {}');
|
||||||
|
expect(s('cmp:host >>> child {}', 'a', 'a-host')).toEqual('cmp[a-host] child {}');
|
||||||
|
expect(s('cmp :host {}', 'a', 'a-host')).toEqual('cmp [a-host] {}');
|
||||||
|
expect(s('cmp :host >>> {}', 'a', 'a-host')).toEqual('cmp [a-host] {}');
|
||||||
|
expect(s('cmp :host child {}', 'a', 'a-host')).toEqual('cmp [a-host] child[a] {}');
|
||||||
|
expect(s('cmp :host >>> child {}', 'a', 'a-host')).toEqual('cmp [a-host] child {}');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe((':host-context'), () => {
|
describe((':host-context'), () => {
|
||||||
|
|
Loading…
Reference in New Issue