|
10 | 10 | * https://github.com/angular/angular/blob/master/packages/compiler/src/shadow_css.ts |
11 | 11 | */ |
12 | 12 |
|
| 13 | +import { escapeRegExpSpecialCharacters } from './regular-expression'; |
| 14 | + |
13 | 15 | const safeSelector = (selector: string) => { |
14 | 16 | const placeholders: string[] = []; |
15 | 17 | let index = 0; |
@@ -279,9 +281,9 @@ const convertColonSlotted = (cssText: string, slotScopeId: string) => { |
279 | 281 | prefixSelector = char + prefixSelector; |
280 | 282 | } |
281 | 283 |
|
282 | | - const orgSelector = prefixSelector + slottedSelector; |
283 | | - const addedSelector = `${prefixSelector.trimRight()}${slottedSelector.trim()}`; |
284 | | - if (orgSelector.trim() !== addedSelector.trim()) { |
| 284 | + const orgSelector = (prefixSelector + slottedSelector).trim(); |
| 285 | + const addedSelector = `${prefixSelector.trimEnd()}${slottedSelector.trim()}`.trim(); |
| 286 | + if (orgSelector !== addedSelector) { |
285 | 287 | const updatedSelector = `${addedSelector}, ${orgSelector}`; |
286 | 288 | selectors.push({ |
287 | 289 | orgSelector, |
@@ -471,15 +473,32 @@ const scopeCssText = ( |
471 | 473 | cssText = scopeSelectors(cssText, scopeId, hostScopeId, slotScopeId, commentOriginalSelector); |
472 | 474 | } |
473 | 475 |
|
474 | | - cssText = cssText.replace(/-shadowcsshost-no-combinator/g, `.${hostScopeId}`); |
| 476 | + cssText = replaceShadowCssHost(cssText, hostScopeId); |
475 | 477 | cssText = cssText.replace(/>\s*\*\s+([^{, ]+)/gm, ' $1 '); |
476 | 478 |
|
477 | 479 | return { |
478 | 480 | cssText: cssText.trim(), |
479 | | - slottedSelectors: slotted.selectors, |
| 481 | + // We need to replace the shadow CSS host string in each of these selectors since we created |
| 482 | + // them prior to the replacement happening in the components CSS text. |
| 483 | + slottedSelectors: slotted.selectors.map((ref) => ({ |
| 484 | + orgSelector: replaceShadowCssHost(ref.orgSelector, hostScopeId), |
| 485 | + updatedSelector: replaceShadowCssHost(ref.updatedSelector, hostScopeId), |
| 486 | + })), |
480 | 487 | }; |
481 | 488 | }; |
482 | 489 |
|
| 490 | +/** |
| 491 | + * Helper function that replaces the interim string representing a `:host` selector with |
| 492 | + * the host scope selector class for the element. |
| 493 | + * |
| 494 | + * @param cssText The CSS string to make the replacement in |
| 495 | + * @param hostScopeId The scope ID that will be used as the class representing the host element |
| 496 | + * @returns CSS with the selector replaced |
| 497 | + */ |
| 498 | +const replaceShadowCssHost = (cssText: string, hostScopeId: string) => { |
| 499 | + return cssText.replace(/-shadowcsshost-no-combinator/g, `.${hostScopeId}`); |
| 500 | +}; |
| 501 | + |
483 | 502 | export const scopeCss = (cssText: string, scopeId: string, commentOriginalSelector: boolean) => { |
484 | 503 | const hostScopeId = scopeId + '-h'; |
485 | 504 | const slotScopeId = scopeId + '-s'; |
@@ -528,7 +547,8 @@ export const scopeCss = (cssText: string, scopeId: string, commentOriginalSelect |
528 | 547 | } |
529 | 548 |
|
530 | 549 | scoped.slottedSelectors.forEach((slottedSelector) => { |
531 | | - cssText = cssText.replace(slottedSelector.orgSelector, slottedSelector.updatedSelector); |
| 550 | + const regex = new RegExp(escapeRegExpSpecialCharacters(slottedSelector.orgSelector), 'g'); |
| 551 | + cssText = cssText.replace(regex, slottedSelector.updatedSelector); |
532 | 552 | }); |
533 | 553 |
|
534 | 554 | return cssText; |
|
0 commit comments