@@ -72,9 +72,29 @@ const _shadowDOMSelectorsRe = [/::shadow/g, /::content/g];
72
72
73
73
const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$' ;
74
74
const _polyfillHostRe = / - s h a d o w c s s h o s t / gim;
75
- const _colonHostRe = / : h o s t / gim;
76
- const _colonSlottedRe = / : : s l o t t e d / gim;
77
- const _colonHostContextRe = / : h o s t - c o n t e x t / gim;
75
+
76
+ /**
77
+ * Little helper for generating a regex that will match a specified
78
+ * CSS selector when that selector is _not_ a part of a `@supports` rule.
79
+ *
80
+ * The pattern will match the provided `selector` (i.e. ':host', ':host-context', etc.)
81
+ * when that selector is not a part of a `@supports` selector rule _or_ if the selector
82
+ * is a part of the rule's declaration.
83
+ *
84
+ * For instance, if we create the regex with the selector ':host-context':
85
+ * - '@supports selector(:host-context())' will return no matches (starts with '@supports')
86
+ * - '@supports selector(:host-context()) { :host-context() { ... }}' will match the second ':host-context' (part of declaration)
87
+ * - ':host-context() { ... }' will match ':host-context' (selector is not a '@supports' rule)
88
+ * - ':host() { ... }' will return no matches (selector doesn't match selector used to create regex)
89
+ *
90
+ * @param selector The CSS selector we want to match for replacement
91
+ * @returns A look-behind regex containing the selector
92
+ */
93
+ const createSupportsRuleRe = ( selector : string ) =>
94
+ new RegExp ( `((?<!(^@supports(.*)))|(?<=\{.*))(${ selector } \\b)` , 'gim' ) ;
95
+ const _colonSlottedRe = createSupportsRuleRe ( '::slotted' ) ;
96
+ const _colonHostRe = createSupportsRuleRe ( ':host' ) ;
97
+ const _colonHostContextRe = createSupportsRuleRe ( ':host-context' ) ;
78
98
79
99
const _commentRe = / \/ \* \s * [ \s \S ] * ?\* \/ / g;
80
100
@@ -153,12 +173,57 @@ const escapeBlocks = (input: string) => {
153
173
return strEscapedBlocks ;
154
174
} ;
155
175
156
- const insertPolyfillHostInCssText = ( selector : string ) => {
157
- selector = selector
158
- . replace ( _colonHostContextRe , _polyfillHostContext )
159
- . replace ( _colonHostRe , _polyfillHost )
160
- . replace ( _colonSlottedRe , _polyfillSlotted ) ;
161
- return selector ;
176
+ /**
177
+ * Replaces certain strings within the CSS with placeholders
178
+ * that will later be replaced with class selectors appropriate
179
+ * for the level of encapsulation (shadow or scoped).
180
+ *
181
+ * When performing these replacements, we want to ignore selectors that are a
182
+ * part of an `@supports` rule. Replacing these selectors will result in invalid
183
+ * CSS that gets passed to autoprefixer/postcss once the placeholders are replaced.
184
+ * For example, a rule like:
185
+ *
186
+ * ```css
187
+ * @supports selector(:host()) {
188
+ * :host {
189
+ * color: red;
190
+ * }
191
+ * }
192
+ * ```
193
+ *
194
+ * Should be converted to:
195
+ *
196
+ * ```css
197
+ * @supports selector(:host()) {
198
+ * -shadowcsshost {
199
+ * color: red;
200
+ * }
201
+ * }
202
+ * ```
203
+ *
204
+ * The order the regex replacements happen in matters since we match
205
+ * against a whole selector word so we need to match all of `:host-context`
206
+ * before we try to replace `:host`. Otherwise the pattern for `:host` would match
207
+ * `:host-context` resulting in something like `:-shadowcsshost-context`.
208
+ *
209
+ * @param cssText A CSS string for a component
210
+ * @returns The modified CSS string
211
+ */
212
+ const insertPolyfillHostInCssText = ( cssText : string ) => {
213
+ // These replacements use a special syntax with the `$1`. When the replacement
214
+ // occurs, `$1` maps to the content of the string leading up to the selector
215
+ // to be replaced.
216
+ //
217
+ // Otherwise, we will replace all the preceding content in addition to the
218
+ // selector because of the lookbehind in the regex.
219
+ //
220
+ // e.g. `/*!@___0___*/:host {}` => `/*!@___0___*/--shadowcsshost {}`
221
+ cssText = cssText
222
+ . replace ( _colonHostContextRe , `$1${ _polyfillHostContext } ` )
223
+ . replace ( _colonHostRe , `$1${ _polyfillHost } ` )
224
+ . replace ( _colonSlottedRe , `$1${ _polyfillSlotted } ` ) ;
225
+
226
+ return cssText ;
162
227
} ;
163
228
164
229
const convertColonRule = ( cssText : string , regExp : RegExp , partReplacer : Function ) => {
0 commit comments