@lwc/style-compiler 2.45.1 → 2.45.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. package/dist/index.cjs.js +910 -0
  2. package/dist/index.cjs.js.map +1 -0
  3. package/dist/index.js +906 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +28 -20
  6. package/dist/commonjs/css-import/transform.js +0 -41
  7. package/dist/commonjs/css-import/transform.js.map +0 -1
  8. package/dist/commonjs/custom-properties/transform.js +0 -23
  9. package/dist/commonjs/custom-properties/transform.js.map +0 -1
  10. package/dist/commonjs/dir-pseudo-class/transform.js +0 -74
  11. package/dist/commonjs/dir-pseudo-class/transform.js.map +0 -1
  12. package/dist/commonjs/index.js +0 -24
  13. package/dist/commonjs/index.js.map +0 -1
  14. package/dist/commonjs/no-id-selectors/validate.js +0 -13
  15. package/dist/commonjs/no-id-selectors/validate.js.map +0 -1
  16. package/dist/commonjs/postcss-lwc-plugin.js +0 -64
  17. package/dist/commonjs/postcss-lwc-plugin.js.map +0 -1
  18. package/dist/commonjs/scope-at-rules/transform.js +0 -53
  19. package/dist/commonjs/scope-at-rules/transform.js.map +0 -1
  20. package/dist/commonjs/selector-scoping/transform.js +0 -123
  21. package/dist/commonjs/selector-scoping/transform.js.map +0 -1
  22. package/dist/commonjs/selector-scoping/validate.js +0 -50
  23. package/dist/commonjs/selector-scoping/validate.js.map +0 -1
  24. package/dist/commonjs/serialize.js +0 -430
  25. package/dist/commonjs/serialize.js.map +0 -1
  26. package/dist/commonjs/transform.js +0 -26
  27. package/dist/commonjs/transform.js.map +0 -1
  28. package/dist/commonjs/utils/dir-pseudoclass.js +0 -14
  29. package/dist/commonjs/utils/dir-pseudoclass.js.map +0 -1
  30. package/dist/commonjs/utils/message.js +0 -31
  31. package/dist/commonjs/utils/message.js.map +0 -1
  32. package/dist/commonjs/utils/rtl.js +0 -15
  33. package/dist/commonjs/utils/rtl.js.map +0 -1
  34. package/dist/commonjs/utils/selector-parser.js +0 -28
  35. package/dist/commonjs/utils/selector-parser.js.map +0 -1
  36. package/dist/commonjs/utils/selectors-scoping.js +0 -12
  37. package/dist/commonjs/utils/selectors-scoping.js.map +0 -1
  38. /package/dist/{types/css-import → css-import}/transform.d.ts +0 -0
  39. /package/dist/{types/custom-properties → custom-properties}/transform.d.ts +0 -0
  40. /package/dist/{types/dir-pseudo-class → dir-pseudo-class}/transform.d.ts +0 -0
  41. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  42. /package/dist/{types/no-id-selectors → no-id-selectors}/validate.d.ts +0 -0
  43. /package/dist/{types/postcss-lwc-plugin.d.ts → postcss-lwc-plugin.d.ts} +0 -0
  44. /package/dist/{types/scope-at-rules → scope-at-rules}/transform.d.ts +0 -0
  45. /package/dist/{types/selector-scoping → selector-scoping}/transform.d.ts +0 -0
  46. /package/dist/{types/selector-scoping → selector-scoping}/validate.d.ts +0 -0
  47. /package/dist/{types/serialize.d.ts → serialize.d.ts} +0 -0
  48. /package/dist/{types/transform.d.ts → transform.d.ts} +0 -0
  49. /package/dist/{types/utils → utils}/dir-pseudoclass.d.ts +0 -0
  50. /package/dist/{types/utils → utils}/message.d.ts +0 -0
  51. /package/dist/{types/utils → utils}/rtl.d.ts +0 -0
  52. /package/dist/{types/utils → utils}/selector-parser.d.ts +0 -0
  53. /package/dist/{types/utils → utils}/selectors-scoping.d.ts +0 -0
package/dist/index.js ADDED
@@ -0,0 +1,906 @@
1
+ /**
2
+ * Copyright (C) 2023 salesforce.com, inc.
3
+ */
4
+ import postcss from 'postcss';
5
+ import postcssValueParser from 'postcss-value-parser';
6
+ import matchAll from 'string.prototype.matchall';
7
+ import { LWC_VERSION_COMMENT, KEY__SCOPED_CSS } from '@lwc/shared';
8
+ import postCssSelector, { isPseudoClass, isCombinator, isPseudoElement, attribute, combinator } from 'postcss-selector-parser';
9
+
10
+ const PLUGIN_NAME = '@lwc/style-compiler';
11
+ const IMPORT_TYPE = 'import';
12
+ const VAR_FUNCTION_TYPE = 'var-function';
13
+ function importMessage(id) {
14
+ return {
15
+ plugin: PLUGIN_NAME,
16
+ type: IMPORT_TYPE,
17
+ id,
18
+ };
19
+ }
20
+ function isImportMessage(message) {
21
+ return message.type === IMPORT_TYPE && message.id;
22
+ }
23
+ function varFunctionMessage(original) {
24
+ return {
25
+ plugin: PLUGIN_NAME,
26
+ type: VAR_FUNCTION_TYPE,
27
+ original,
28
+ };
29
+ }
30
+ function isVarFunctionMessage(message) {
31
+ return message.type === VAR_FUNCTION_TYPE && message.original;
32
+ }
33
+
34
+ /*
35
+ * Copyright (c) 2018, salesforce.com, inc.
36
+ * All rights reserved.
37
+ * SPDX-License-Identifier: MIT
38
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
39
+ */
40
+ const HOST_ATTRIBUTE = '__hostAttribute__';
41
+ const SHADOW_ATTRIBUTE = '__shadowAttribute__';
42
+
43
+ /*
44
+ * Copyright (c) 2018, salesforce.com, inc.
45
+ * All rights reserved.
46
+ * SPDX-License-Identifier: MIT
47
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
48
+ */
49
+ const DIR_ATTRIBUTE_NATIVE_LTR = `__dirAttributeNativeLtr__`;
50
+ const DIR_ATTRIBUTE_NATIVE_RTL = `__dirAttributeNativeRtl__`;
51
+ const DIR_ATTRIBUTE_SYNTHETIC_LTR = `__dirAttributeSyntheticLtr__`;
52
+ const DIR_ATTRIBUTE_SYNTHETIC_RTL = `__dirAttributeSyntheticRtl__`;
53
+
54
+ /*
55
+ * Copyright (c) 2018, salesforce.com, inc.
56
+ * All rights reserved.
57
+ * SPDX-License-Identifier: MIT
58
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
59
+ */
60
+ var TokenType;
61
+ (function (TokenType) {
62
+ TokenType["text"] = "text";
63
+ TokenType["expression"] = "expression";
64
+ TokenType["identifier"] = "identifier";
65
+ TokenType["divider"] = "divider";
66
+ })(TokenType || (TokenType = {}));
67
+ // "1400 binary expressions are enough to reach Node.js maximum call stack size"
68
+ // https://github.com/salesforce/lwc/issues/1726
69
+ // The vast majority of stylesheet functions are much less than this, so we can set the limit lower
70
+ // to play it safe.
71
+ const BINARY_EXPRESSION_LIMIT = 100;
72
+ // Javascript identifiers used for the generation of the style module
73
+ const HOST_SELECTOR_IDENTIFIER = 'hostSelector';
74
+ const SHADOW_SELECTOR_IDENTIFIER = 'shadowSelector';
75
+ const SUFFIX_TOKEN_IDENTIFIER = 'suffixToken';
76
+ const USE_ACTUAL_HOST_SELECTOR = 'useActualHostSelector';
77
+ const USE_NATIVE_DIR_PSEUDOCLASS = 'useNativeDirPseudoclass';
78
+ const TOKEN = 'token';
79
+ const STYLESHEET_IDENTIFIER = 'stylesheet';
80
+ const VAR_RESOLVER_IDENTIFIER = 'varResolver';
81
+ function serialize(result, config) {
82
+ const { messages } = result;
83
+ const collectVarFunctions = Boolean(config.customProperties && config.customProperties.resolverModule);
84
+ const useVarResolver = messages.some(isVarFunctionMessage);
85
+ const importedStylesheets = messages.filter(isImportMessage).map((message) => message.id);
86
+ const disableSyntheticShadow = Boolean(config.disableSyntheticShadowSupport);
87
+ const scoped = Boolean(config.scoped);
88
+ let buffer = '';
89
+ if (collectVarFunctions && useVarResolver) {
90
+ buffer += `import ${VAR_RESOLVER_IDENTIFIER} from "${config.customProperties
91
+ .resolverModule}";\n`;
92
+ }
93
+ for (let i = 0; i < importedStylesheets.length; i++) {
94
+ buffer += `import ${STYLESHEET_IDENTIFIER + i} from "${importedStylesheets[i]}";\n`;
95
+ }
96
+ if (importedStylesheets.length) {
97
+ buffer += '\n';
98
+ }
99
+ const stylesheetList = importedStylesheets.map((_str, i) => `${STYLESHEET_IDENTIFIER + i}`);
100
+ const serializedStyle = serializeCss(result, collectVarFunctions).trim();
101
+ if (serializedStyle) {
102
+ // inline function
103
+ if (disableSyntheticShadow && !scoped) {
104
+ // If synthetic shadow DOM support is disabled and this is not a scoped stylesheet, then the
105
+ // function signature will always be:
106
+ // stylesheet(token = undefined, useActualHostSelector = true, useNativeDirPseudoclass = true)
107
+ // This means that we can just have a function that takes no arguments and returns a string,
108
+ // reducing the bundle size when minified.
109
+ buffer += `function ${STYLESHEET_IDENTIFIER}() {\n`;
110
+ buffer += ` var ${TOKEN};\n`; // undefined
111
+ buffer += ` var ${USE_ACTUAL_HOST_SELECTOR} = true;\n`;
112
+ buffer += ` var ${USE_NATIVE_DIR_PSEUDOCLASS} = true;\n`;
113
+ }
114
+ else {
115
+ buffer += `function ${STYLESHEET_IDENTIFIER}(${TOKEN}, ${USE_ACTUAL_HOST_SELECTOR}, ${USE_NATIVE_DIR_PSEUDOCLASS}) {\n`;
116
+ }
117
+ // For scoped stylesheets, we use classes, but for synthetic shadow DOM, we use attributes
118
+ if (scoped) {
119
+ buffer += ` var ${SHADOW_SELECTOR_IDENTIFIER} = ${TOKEN} ? ("." + ${TOKEN}) : "";\n`;
120
+ buffer += ` var ${HOST_SELECTOR_IDENTIFIER} = ${TOKEN} ? ("." + ${TOKEN} + "-host") : "";\n`;
121
+ }
122
+ else {
123
+ buffer += ` var ${SHADOW_SELECTOR_IDENTIFIER} = ${TOKEN} ? ("[" + ${TOKEN} + "]") : "";\n`;
124
+ buffer += ` var ${HOST_SELECTOR_IDENTIFIER} = ${TOKEN} ? ("[" + ${TOKEN} + "-host]") : "";\n`;
125
+ }
126
+ // Used for keyframes
127
+ buffer += ` var ${SUFFIX_TOKEN_IDENTIFIER} = ${TOKEN} ? ("-" + ${TOKEN}) : "";\n`;
128
+ buffer += ` return ${serializedStyle};\n`;
129
+ buffer += ` /*${LWC_VERSION_COMMENT}*/\n`;
130
+ buffer += `}\n`;
131
+ if (scoped) {
132
+ // Mark the stylesheet as scoped so that we can distinguish it later at runtime
133
+ buffer += `${STYLESHEET_IDENTIFIER}.${KEY__SCOPED_CSS} = true;\n`;
134
+ }
135
+ // add import at the end
136
+ stylesheetList.push(STYLESHEET_IDENTIFIER);
137
+ }
138
+ // exports
139
+ if (stylesheetList.length) {
140
+ buffer += `export default [${stylesheetList.join(', ')}];`;
141
+ }
142
+ else {
143
+ buffer += `export default undefined;`;
144
+ }
145
+ return buffer;
146
+ }
147
+ function reduceTokens(tokens) {
148
+ return [{ type: TokenType.text, value: '' }, ...tokens, { type: TokenType.text, value: '' }]
149
+ .reduce((acc, token) => {
150
+ const prev = acc[acc.length - 1];
151
+ if (token.type === TokenType.text && prev && prev.type === TokenType.text) {
152
+ // clone the previous token to avoid mutating it in-place
153
+ acc[acc.length - 1] = {
154
+ type: prev.type,
155
+ value: prev.value + token.value,
156
+ };
157
+ return acc;
158
+ }
159
+ else {
160
+ return [...acc, token];
161
+ }
162
+ }, [])
163
+ .filter((t) => t.value !== '');
164
+ }
165
+ function normalizeString(str) {
166
+ return str.replace(/(\r\n\t|\n|\r\t)/gm, '').trim();
167
+ }
168
+ function generateExpressionFromTokens(tokens) {
169
+ const serializedTokens = reduceTokens(tokens).map(({ type, value }) => {
170
+ switch (type) {
171
+ // Note that we don't expect to get a TokenType.divider here. It should be converted into an
172
+ // expression elsewhere.
173
+ case TokenType.text:
174
+ return JSON.stringify(value);
175
+ // Expressions may be concatenated with " + ", in which case we must remove ambiguity
176
+ case TokenType.expression:
177
+ return `(${value})`;
178
+ default:
179
+ return value;
180
+ }
181
+ });
182
+ if (serializedTokens.length === 0) {
183
+ return '';
184
+ }
185
+ else if (serializedTokens.length === 1) {
186
+ return serializedTokens[0];
187
+ }
188
+ else if (serializedTokens.length < BINARY_EXPRESSION_LIMIT) {
189
+ return serializedTokens.join(' + ');
190
+ }
191
+ else {
192
+ // #1726 Using Array.prototype.join() instead of a standard "+" operator to concatenate the
193
+ // string to avoid running into a maximum call stack error when the stylesheet is parsed
194
+ // again by the bundler.
195
+ return `[${serializedTokens.join(', ')}].join('')`;
196
+ }
197
+ }
198
+ function areTokensEqual(left, right) {
199
+ return left.type === right.type && left.value === right.value;
200
+ }
201
+ function calculateNumDuplicatedTokens(left, right) {
202
+ // Walk backwards until we find a token that is different between left and right
203
+ let i = 0;
204
+ for (; i < left.length && i < right.length; i++) {
205
+ const currentLeft = left[left.length - 1 - i];
206
+ const currentRight = right[right.length - 1 - i];
207
+ if (!areTokensEqual(currentLeft, currentRight)) {
208
+ break;
209
+ }
210
+ }
211
+ return i;
212
+ }
213
+ // For `:host` selectors, the token lists for native vs synthetic will be identical at the end of
214
+ // each list. So as an optimization, we can de-dup these tokens.
215
+ // See: https://github.com/salesforce/lwc/issues/3224#issuecomment-1353520052
216
+ function deduplicateHostTokens(nativeHostTokens, syntheticHostTokens) {
217
+ const numDuplicatedTokens = calculateNumDuplicatedTokens(nativeHostTokens, syntheticHostTokens);
218
+ const numUniqueNativeTokens = nativeHostTokens.length - numDuplicatedTokens;
219
+ const numUniqueSyntheticTokens = syntheticHostTokens.length - numDuplicatedTokens;
220
+ const uniqueNativeTokens = nativeHostTokens.slice(0, numUniqueNativeTokens);
221
+ const uniqueSyntheticTokens = syntheticHostTokens.slice(0, numUniqueSyntheticTokens);
222
+ const nativeExpression = generateExpressionFromTokens(uniqueNativeTokens);
223
+ const syntheticExpression = generateExpressionFromTokens(uniqueSyntheticTokens);
224
+ // Generate a conditional ternary to switch between native vs synthetic for the unique tokens
225
+ const conditionalToken = {
226
+ type: TokenType.expression,
227
+ value: `(${USE_ACTUAL_HOST_SELECTOR} ? ${nativeExpression} : ${syntheticExpression})`,
228
+ };
229
+ return [
230
+ conditionalToken,
231
+ // The remaining tokens are the same between native and synthetic
232
+ ...syntheticHostTokens.slice(numUniqueSyntheticTokens),
233
+ ];
234
+ }
235
+ function serializeCss(result, collectVarFunctions) {
236
+ const tokens = [];
237
+ let currentRuleTokens = [];
238
+ let nativeHostTokens;
239
+ // Walk though all nodes in the CSS...
240
+ postcss.stringify(result.root, (part, node, nodePosition) => {
241
+ // When consuming the beginning of a rule, first we tokenize the selector
242
+ if (node && node.type === 'rule' && nodePosition === 'start') {
243
+ currentRuleTokens.push(...tokenizeCss(normalizeString(part)));
244
+ // When consuming the end of a rule we normalize it and produce a new one
245
+ }
246
+ else if (node && node.type === 'rule' && nodePosition === 'end') {
247
+ currentRuleTokens.push({ type: TokenType.text, value: part });
248
+ // If we are in synthetic shadow or scoped light DOM, we don't want to have native :host selectors
249
+ // Note that postcss-lwc-plugin should ensure that _isNativeHost appears before _isSyntheticHost
250
+ if (node._isNativeHost) {
251
+ // Save native tokens so in the next rule we can apply a conditional ternary
252
+ nativeHostTokens = [...currentRuleTokens];
253
+ }
254
+ else if (node._isSyntheticHost) {
255
+ /* istanbul ignore if */
256
+ if (!nativeHostTokens) {
257
+ throw new Error('Unexpected host rules ordering');
258
+ }
259
+ const hostTokens = deduplicateHostTokens(nativeHostTokens, currentRuleTokens);
260
+ tokens.push(...hostTokens);
261
+ nativeHostTokens = undefined;
262
+ }
263
+ else {
264
+ /* istanbul ignore if */
265
+ if (nativeHostTokens) {
266
+ throw new Error('Unexpected host rules ordering');
267
+ }
268
+ tokens.push(...currentRuleTokens);
269
+ }
270
+ // Reset rule
271
+ currentRuleTokens = [];
272
+ // When inside a declaration, tokenize it and push it to the current token list
273
+ }
274
+ else if (node && node.type === 'decl') {
275
+ if (collectVarFunctions) {
276
+ const declTokens = tokenizeCssDeclaration(node);
277
+ currentRuleTokens.push(...declTokens);
278
+ currentRuleTokens.push({ type: TokenType.text, value: ';' });
279
+ }
280
+ else {
281
+ currentRuleTokens.push(...tokenizeCss(part));
282
+ }
283
+ }
284
+ else if (node && node.type === 'atrule') {
285
+ // Certain atrules have declaration associated with for example @font-face. We need to add the rules tokens
286
+ // when it's the case.
287
+ if (currentRuleTokens.length) {
288
+ tokens.push(...currentRuleTokens);
289
+ currentRuleTokens = [];
290
+ }
291
+ tokens.push(...tokenizeCss(normalizeString(part)));
292
+ }
293
+ else {
294
+ // When inside anything else but a comment just push it
295
+ if (!node || node.type !== 'comment') {
296
+ currentRuleTokens.push({ type: TokenType.text, value: normalizeString(part) });
297
+ }
298
+ }
299
+ });
300
+ return generateExpressionFromTokens(tokens);
301
+ }
302
+ // Given any CSS string, replace the scope tokens from the CSS with code to properly
303
+ // replace it in the stylesheet function.
304
+ function tokenizeCss(data) {
305
+ data = data.replace(/( {2,})/gm, ' '); // remove when there are more than two spaces
306
+ const tokens = [];
307
+ const attributes = [
308
+ SHADOW_ATTRIBUTE,
309
+ HOST_ATTRIBUTE,
310
+ DIR_ATTRIBUTE_NATIVE_LTR,
311
+ DIR_ATTRIBUTE_NATIVE_RTL,
312
+ DIR_ATTRIBUTE_SYNTHETIC_LTR,
313
+ DIR_ATTRIBUTE_SYNTHETIC_RTL,
314
+ ];
315
+ const regex = new RegExp(`[[-](${attributes.join('|')})]?`, 'g');
316
+ let lastIndex = 0;
317
+ for (const match of matchAll(data, regex)) {
318
+ const index = match.index;
319
+ const [matchString, substring] = match;
320
+ if (index > lastIndex) {
321
+ tokens.push({ type: TokenType.text, value: data.substring(lastIndex, index) });
322
+ }
323
+ const identifier = substring === SHADOW_ATTRIBUTE ? SHADOW_SELECTOR_IDENTIFIER : HOST_SELECTOR_IDENTIFIER;
324
+ if (matchString.startsWith('[')) {
325
+ if (substring === SHADOW_ATTRIBUTE || substring === HOST_ATTRIBUTE) {
326
+ // attribute in a selector, e.g. `[__shadowAttribute__]` or `[__hostAttribute__]`
327
+ tokens.push({
328
+ type: TokenType.identifier,
329
+ value: identifier,
330
+ });
331
+ }
332
+ else {
333
+ // :dir pseudoclass placeholder, e.g. `[__dirAttributeNativeLtr__]` or `[__dirAttributeSyntheticRtl__]`
334
+ const native = substring === DIR_ATTRIBUTE_NATIVE_LTR ||
335
+ substring === DIR_ATTRIBUTE_NATIVE_RTL;
336
+ const dirValue = substring === DIR_ATTRIBUTE_NATIVE_LTR ||
337
+ substring === DIR_ATTRIBUTE_SYNTHETIC_LTR
338
+ ? 'ltr'
339
+ : 'rtl';
340
+ tokens.push({
341
+ type: TokenType.expression,
342
+ // use the native :dir() pseudoclass for native shadow, the [dir] attribute otherwise
343
+ value: native
344
+ ? `${USE_NATIVE_DIR_PSEUDOCLASS} ? ':dir(${dirValue})' : ''`
345
+ : `${USE_NATIVE_DIR_PSEUDOCLASS} ? '' : '[dir="${dirValue}"]'`,
346
+ });
347
+ }
348
+ }
349
+ else {
350
+ // suffix for an at-rule, e.g. `@keyframes spin-__shadowAttribute__`
351
+ tokens.push({
352
+ type: TokenType.identifier,
353
+ // Suffix the keyframe (i.e. "-" plus the token)
354
+ value: SUFFIX_TOKEN_IDENTIFIER,
355
+ });
356
+ }
357
+ lastIndex = index + matchString.length;
358
+ }
359
+ if (lastIndex < data.length) {
360
+ tokens.push({ type: TokenType.text, value: data.substring(lastIndex, data.length) });
361
+ }
362
+ return tokens;
363
+ }
364
+ function isTextOrExpression(token) {
365
+ return token.type === TokenType.text || token.type == TokenType.expression;
366
+ }
367
+ /*
368
+ * This method takes a tokenized CSS property value `1px solid var(--foo , bar)`
369
+ * and transforms its custom variables in function calls
370
+ */
371
+ function recursiveValueParse(node, inVarExpression = false) {
372
+ const { type, nodes, value } = node;
373
+ // If it has not type it means is the root, process its children
374
+ if (!type && nodes) {
375
+ return nodes.reduce((acc, n) => {
376
+ acc.push(...recursiveValueParse(n, inVarExpression));
377
+ return acc;
378
+ }, []);
379
+ }
380
+ if (type === 'comment') {
381
+ return [];
382
+ }
383
+ if (type === 'div') {
384
+ return [
385
+ {
386
+ type: inVarExpression ? TokenType.divider : TokenType.text,
387
+ value,
388
+ },
389
+ ];
390
+ }
391
+ if (type === 'string') {
392
+ const { quote } = node;
393
+ return [
394
+ {
395
+ type: TokenType.text,
396
+ value: quote ? quote + value + quote : value,
397
+ },
398
+ ];
399
+ }
400
+ // If we are inside a var() expression use need to stringify since we are converting it into a function
401
+ if (type === 'word') {
402
+ const convertIdentifier = value.startsWith('--');
403
+ if (convertIdentifier) {
404
+ return [{ type: TokenType.identifier, value: `"${value}"` }];
405
+ }
406
+ // For animation properties, the shadow/host attributes may be in this text
407
+ return tokenizeCss(value);
408
+ }
409
+ // If we inside a var() function we need to prepend and append to generate an expression
410
+ if (type === 'function') {
411
+ if (value === 'var') {
412
+ // `tokens` may include tokens of type `divider`, `expression`, `identifier`,
413
+ // and `text`. However, an identifier will only ever be adjacent to a divider,
414
+ // whereas text and expression tokens may be adjacent to one another. This is
415
+ // important below when inserting concatenetors.
416
+ //
417
+ // For fuller context, see the following conversation:
418
+ // https://github.com/salesforce/lwc/pull/2902#discussion_r904626421
419
+ let tokens = recursiveValueParse({ nodes }, true);
420
+ tokens = reduceTokens(tokens);
421
+ const exprToken = tokens.reduce((buffer, token, index) => {
422
+ const isTextToken = token.type === TokenType.text;
423
+ const normalizedToken = isTextToken ? JSON.stringify(token.value) : token.value;
424
+ const nextToken = tokens[index + 1];
425
+ // If we have a token sequence of text + expression or expression + text,
426
+ // we need to add the concatenation operator. Examples:
427
+ //
428
+ // Input: var(--x, 0 0 2px var(--y, #fff))
429
+ // Output: varResolver("--x", "0 0 2px " + varResolver("--y", "#fff"))
430
+ //
431
+ // Input: var(--x, var(--y, #fff) 0 0 2px)
432
+ // Output: varResolver("--x", varResolver("--y", "#fff") + " 0 0 2px"))
433
+ //
434
+ // Since identifier tokens will never be adjacent to text or expression
435
+ // tokens (see above comment), a concatenator will never be required if
436
+ // `token` or `nextToken` is an identifier.
437
+ const shouldAddConcatenator = isTextOrExpression(token) && nextToken && isTextOrExpression(nextToken);
438
+ const concatOperator = shouldAddConcatenator ? ' + ' : '';
439
+ return buffer + normalizedToken + concatOperator;
440
+ }, '');
441
+ // Generate the function call for runtime evaluation
442
+ return [
443
+ {
444
+ type: TokenType.expression,
445
+ value: `${VAR_RESOLVER_IDENTIFIER}(${exprToken})`,
446
+ },
447
+ ];
448
+ // for any other function just do the equivalent string concatenation (no need for expressions)
449
+ }
450
+ else {
451
+ const tokens = nodes.reduce((acc, n) => {
452
+ acc.push(...recursiveValueParse(n, false));
453
+ return acc;
454
+ }, []);
455
+ return [
456
+ { type: TokenType.text, value: `${value}(` },
457
+ ...reduceTokens(tokens),
458
+ { type: TokenType.text, value: ')' },
459
+ ];
460
+ }
461
+ }
462
+ // For any other token types we just need to return text
463
+ return [{ type: TokenType.text, value }];
464
+ }
465
+ function tokenizeCssDeclaration(node) {
466
+ const valueRoot = postcssValueParser(node.value);
467
+ const parsed = recursiveValueParse(valueRoot);
468
+ return [{ type: TokenType.text, value: `${node.prop.trim()}: ` }, ...parsed];
469
+ }
470
+
471
+ function validateIdSelectors (root) {
472
+ root.walkIds((node) => {
473
+ const message = `Invalid usage of id selector '#${node.value}'. Try using a class selector or some other selector.`;
474
+ throw root.error(message, {
475
+ index: node.sourceIndex,
476
+ word: node.value,
477
+ });
478
+ });
479
+ }
480
+
481
+ function process$2(root, result, isScoped) {
482
+ root.walkAtRules('import', (node) => {
483
+ if (isScoped) {
484
+ throw node.error(`Invalid import statement, imports are not allowed in *.scoped.css files.`);
485
+ }
486
+ // Ensure @import are at the top of the file
487
+ let prev = node.prev();
488
+ while (prev) {
489
+ if (prev.type === 'comment' || (prev.type === 'atrule' && prev.name === 'import')) {
490
+ prev = prev.prev();
491
+ }
492
+ else {
493
+ throw prev.error('@import must precede all other statements');
494
+ }
495
+ }
496
+ const { nodes: params } = postcssValueParser(node.params);
497
+ // Ensure import match the following syntax:
498
+ // @import "foo";
499
+ // @import "./foo.css";
500
+ if (!params.length || params[0].type !== 'string' || !params[0].value) {
501
+ throw node.error(`Invalid import statement, unable to find imported module.`);
502
+ }
503
+ if (params.length > 1) {
504
+ throw node.error(`Invalid import statement, import statement only support a single parameter.`);
505
+ }
506
+ // Add the imported to results messages
507
+ const message = importMessage(params[0].value);
508
+ result.messages.push(message);
509
+ // Remove the import from the generated css
510
+ node.remove();
511
+ });
512
+ }
513
+
514
+ /*
515
+ * Copyright (c) 2018, salesforce.com, inc.
516
+ * All rights reserved.
517
+ * SPDX-License-Identifier: MIT
518
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
519
+ */
520
+ function isDirPseudoClass(node) {
521
+ return isPseudoClass(node) && node.value === ':dir';
522
+ }
523
+
524
+ function findNode(container, predicate) {
525
+ return container && container.nodes && container.nodes.find(predicate);
526
+ }
527
+ function replaceNodeWith(oldNode, ...newNodes) {
528
+ if (newNodes.length) {
529
+ const { parent } = oldNode;
530
+ if (!parent) {
531
+ throw new Error(`Impossible to replace root node.`);
532
+ }
533
+ newNodes.forEach((node) => {
534
+ parent.insertBefore(oldNode, node);
535
+ });
536
+ oldNode.remove();
537
+ }
538
+ }
539
+ function trimNodeWhitespaces(node) {
540
+ if (node && node.spaces) {
541
+ node.spaces.before = '';
542
+ node.spaces.after = '';
543
+ }
544
+ }
545
+
546
+ const DEPRECATED_SELECTORS = new Set(['/deep/', '::shadow', '>>>']);
547
+ const UNSUPPORTED_SELECTORS = new Set([':root', ':host-context']);
548
+ const TEMPLATE_DIRECTIVES = [/^key$/, /^lwc:*/, /^if:*/, /^for:*/, /^iterator:*/];
549
+ function validateSelectors(root) {
550
+ root.walk((node) => {
551
+ const { value, sourceIndex } = node;
552
+ if (value) {
553
+ // Ensure the selector doesn't use a deprecated CSS selector.
554
+ if (DEPRECATED_SELECTORS.has(value)) {
555
+ throw root.error(`Invalid usage of deprecated selector "${value}".`, {
556
+ index: sourceIndex,
557
+ word: value,
558
+ });
559
+ }
560
+ // Ensure the selector doesn't use an unsupported selector.
561
+ if (UNSUPPORTED_SELECTORS.has(value)) {
562
+ throw root.error(`Invalid usage of unsupported selector "${value}".`, {
563
+ index: sourceIndex,
564
+ word: value,
565
+ });
566
+ }
567
+ }
568
+ });
569
+ }
570
+ function validateAttribute(root) {
571
+ root.walkAttributes((node) => {
572
+ const { attribute: attributeName, sourceIndex } = node;
573
+ const isTemplateDirective = TEMPLATE_DIRECTIVES.some((directive) => {
574
+ return directive.test(attributeName);
575
+ });
576
+ if (isTemplateDirective) {
577
+ const message = [
578
+ `Invalid usage of attribute selector "${attributeName}". `,
579
+ `"${attributeName}" is a template directive and therefore not supported in css rules.`,
580
+ ];
581
+ throw root.error(message.join(''), {
582
+ index: sourceIndex,
583
+ word: attributeName,
584
+ });
585
+ }
586
+ });
587
+ }
588
+ function validate(root) {
589
+ validateSelectors(root);
590
+ validateAttribute(root);
591
+ }
592
+
593
+ /*
594
+ * Copyright (c) 2018, salesforce.com, inc.
595
+ * All rights reserved.
596
+ * SPDX-License-Identifier: MIT
597
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
598
+ */
599
+ function isHostPseudoClass(node) {
600
+ return isPseudoClass(node) && node.value === ':host';
601
+ }
602
+ /**
603
+ * Add scoping attributes to all the matching selectors:
604
+ * h1 -> h1[x-foo_tmpl]
605
+ * p a -> p[x-foo_tmpl] a[x-foo_tmpl]
606
+ */
607
+ function scopeSelector(selector) {
608
+ const compoundSelectors = [[]];
609
+ // Split the selector per compound selector. Compound selectors are interleaved with combinator nodes.
610
+ // https://drafts.csswg.org/selectors-4/#typedef-complex-selector
611
+ selector.each((node) => {
612
+ if (isCombinator(node)) {
613
+ compoundSelectors.push([]);
614
+ }
615
+ else {
616
+ const current = compoundSelectors[compoundSelectors.length - 1];
617
+ current.push(node);
618
+ }
619
+ });
620
+ for (const compoundSelector of compoundSelectors) {
621
+ // Compound selectors with only a single :dir pseudo class should be scoped, the dir pseudo
622
+ // class transform will take care of transforming it properly.
623
+ const containsSingleDirSelector = compoundSelector.length === 1 && isDirPseudoClass(compoundSelector[0]);
624
+ // Compound selectors containing :host have a special treatment and should not be scoped
625
+ // like the rest of the complex selectors.
626
+ const containsHost = compoundSelector.some(isHostPseudoClass);
627
+ if (!containsSingleDirSelector && !containsHost) {
628
+ let nodeToScope;
629
+ // In each compound selector we need to locate the last selector to scope.
630
+ for (const node of compoundSelector) {
631
+ if (!isPseudoElement(node)) {
632
+ nodeToScope = node;
633
+ }
634
+ }
635
+ const shadowAttribute = attribute({
636
+ attribute: SHADOW_ATTRIBUTE,
637
+ value: undefined,
638
+ raws: {},
639
+ });
640
+ if (nodeToScope) {
641
+ // Add the scoping attribute right after the node scope
642
+ selector.insertAfter(nodeToScope, shadowAttribute);
643
+ }
644
+ else {
645
+ // Add the scoping token in the first position of the compound selector as a fallback
646
+ // when there is no node to scope. For example: ::after {}
647
+ const [firstSelector] = compoundSelector;
648
+ selector.insertBefore(firstSelector, shadowAttribute);
649
+ // Move any whitespace before the selector (e.g. " ::after") to before the shadow attribute,
650
+ // so that the resulting selector is correct (e.g. " [attr]::after", not "[attr] ::after")
651
+ if (firstSelector && firstSelector.spaces.before) {
652
+ shadowAttribute.spaces.before = firstSelector.spaces.before;
653
+ const clonedFirstSelector = firstSelector.clone({});
654
+ clonedFirstSelector.spaces.before = '';
655
+ firstSelector.replaceWith(clonedFirstSelector);
656
+ }
657
+ }
658
+ }
659
+ }
660
+ }
661
+ /**
662
+ * Mark the :host selector with a placeholder. If the selector has a list of
663
+ * contextual selector it will generate a rule for each of them.
664
+ * :host -> [x-foo_tmpl-host]
665
+ * :host(.foo, .bar) -> [x-foo_tmpl-host].foo, [x-foo_tmpl-host].bar
666
+ */
667
+ function transformHost(selector) {
668
+ // Locate the first :host pseudo-class
669
+ const hostNode = findNode(selector, isHostPseudoClass);
670
+ if (hostNode) {
671
+ // Store the original location of the :host in the selector
672
+ const hostIndex = selector.index(hostNode);
673
+ // Swap the :host pseudo-class with the host scoping token
674
+ const hostAttribute = attribute({
675
+ attribute: HOST_ATTRIBUTE,
676
+ value: undefined,
677
+ raws: {},
678
+ });
679
+ hostNode.replaceWith(hostAttribute);
680
+ // Generate a unique contextualized version of the selector for each selector pass as argument
681
+ // to the :host
682
+ const contextualSelectors = hostNode.nodes.map((contextSelectors) => {
683
+ const clonedSelector = selector.clone({});
684
+ const clonedHostNode = clonedSelector.at(hostIndex);
685
+ // Add to the compound selector previously containing the :host pseudo class
686
+ // the contextual selectors.
687
+ contextSelectors.each((node) => {
688
+ trimNodeWhitespaces(node);
689
+ clonedSelector.insertAfter(clonedHostNode, node);
690
+ });
691
+ return clonedSelector;
692
+ });
693
+ // Replace the current selector with the different variants
694
+ replaceNodeWith(selector, ...contextualSelectors);
695
+ }
696
+ }
697
+ function transformSelector(root, transformConfig) {
698
+ validate(root);
699
+ root.each(scopeSelector);
700
+ if (transformConfig.transformHost) {
701
+ root.each(transformHost);
702
+ }
703
+ }
704
+
705
+ function process$1(root, result) {
706
+ root.walkDecls((decl) => {
707
+ const valueRoot = postcssValueParser(decl.value);
708
+ let varFound = false;
709
+ valueRoot.walk(({ type, value }) => {
710
+ if (!varFound && type === 'function' && value === 'var') {
711
+ // Add the imported to results messages
712
+ const message = varFunctionMessage(value);
713
+ result.messages.push(message);
714
+ varFound = true;
715
+ }
716
+ });
717
+ });
718
+ }
719
+
720
+ /*
721
+ * Copyright (c) 2018, salesforce.com, inc.
722
+ * All rights reserved.
723
+ * SPDX-License-Identifier: MIT
724
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
725
+ */
726
+ function isValidDirValue(value) {
727
+ return value === 'ltr' || value === 'rtl';
728
+ }
729
+ function transformDirPseudoClass (root) {
730
+ root.nodes.forEach((selector) => {
731
+ selector.nodes.forEach((node) => {
732
+ if (!isDirPseudoClass(node)) {
733
+ return;
734
+ }
735
+ const value = node.nodes.toString().trim();
736
+ if (!isValidDirValue(value)) {
737
+ throw root.error(`:dir() pseudo class expects "ltr" or "rtl" for value, but received "${value}".`, {
738
+ index: node.sourceIndex,
739
+ word: node.value,
740
+ });
741
+ }
742
+ // Set placeholders for `:dir()` so we can keep it for native shadow and
743
+ // replace it with a polyfill for synthetic shadow.
744
+ //
745
+ // Native: `:dir(ltr)`
746
+ // Synthetic: `[dir="ltr"]`
747
+ //
748
+ // The placeholders look like this: `[__dirAttributeNativeLtr__]`
749
+ // The attribute has no value because it's simpler during serialization, and there
750
+ // are only two valid values: "ltr" and "rtl".
751
+ //
752
+ // Now consider a more complex selector: `.foo:dir(ltr):not(.bar)`.
753
+ // For native shadow, we need to leave it as-is. Whereas for synthetic shadow, we need
754
+ // to convert it to: `[dir="ltr"] .foo:not(.bar)`.
755
+ // I.e. we need to use a descendant selector (' ' combinator) relying on a `dir`
756
+ // attribute added to the host element. So we need two placeholders:
757
+ // `<synthetic_placeholder> .foo<native_placeholder>:not(.bar)`
758
+ const nativeAttribute = attribute({
759
+ attribute: value === 'ltr' ? DIR_ATTRIBUTE_NATIVE_LTR : DIR_ATTRIBUTE_NATIVE_RTL,
760
+ value: undefined,
761
+ raws: {},
762
+ });
763
+ const syntheticAttribute = attribute({
764
+ attribute: value === 'ltr' ? DIR_ATTRIBUTE_SYNTHETIC_LTR : DIR_ATTRIBUTE_SYNTHETIC_RTL,
765
+ value: undefined,
766
+ raws: {},
767
+ });
768
+ node.replaceWith(nativeAttribute);
769
+ // If the selector is not empty and if the first node in the selector is not already a
770
+ // " " combinator, we need to use the descendant selector format
771
+ const shouldAddDescendantCombinator = selector.first && !isCombinator(selector.first) && selector.first.value !== ' ';
772
+ if (shouldAddDescendantCombinator) {
773
+ selector.insertBefore(selector.first, combinator({
774
+ value: ' ',
775
+ }));
776
+ // Add the [dir] attribute in front of the " " combinator, i.e. as an ancestor
777
+ selector.insertBefore(selector.first, syntheticAttribute);
778
+ }
779
+ else {
780
+ // Otherwise there's no need for the descendant selector, so we can skip adding the
781
+ // space combinator and just put the synthetic placeholder next to the native one
782
+ selector.insertBefore(nativeAttribute, syntheticAttribute);
783
+ }
784
+ });
785
+ });
786
+ }
787
+
788
+ // Subset of prefixes for animation-related names that we expect people might be using.
789
+ // The most important is -webkit, which is actually part of the spec now. All -webkit prefixes
790
+ // are listed here: https://developer.mozilla.org/en-US/docs/Web/CSS/Webkit_Extensions
791
+ // -moz is also still supported as of Firefox 95.
792
+ // We could probably get away with just doing -webkit and -moz (since -ms never seems
793
+ // to have existed for keyframes/animations, and Opera has used Blink since 2013), but
794
+ // covering all the popular ones will at least make the compiled code more consistent
795
+ // for developers who are using all the variants.
796
+ // List based on a subset from https://github.com/wooorm/vendors/blob/2f489ad/index.js
797
+ const VENDOR_PREFIXES = ['moz', 'ms', 'o', 'webkit'];
798
+ // create a list like ['animation', '-webkit-animation', ...]
799
+ function getAllNames(name) {
800
+ return new Set([name, ...VENDOR_PREFIXES.map((prefix) => `-${prefix}-${name}`)]);
801
+ }
802
+ const ANIMATION = getAllNames('animation');
803
+ const ANIMATION_NAME = getAllNames('animation-name');
804
+ function process(root) {
805
+ const knownNames = new Set();
806
+ root.walkAtRules((atRule) => {
807
+ // Note that @-webkit-keyframes, @-moz-keyframes, etc. are not actually a thing supported
808
+ // in any browser, even though you'll see it on some StackOverflow answers.
809
+ if (atRule.name === 'keyframes') {
810
+ const { params } = atRule;
811
+ knownNames.add(params);
812
+ atRule.params = `${params}-${SHADOW_ATTRIBUTE}`;
813
+ }
814
+ });
815
+ root.walkRules((rule) => {
816
+ rule.walkDecls((decl) => {
817
+ if (ANIMATION.has(decl.prop)) {
818
+ // Use a simple heuristic of breaking up the tokens by whitespace. We could use
819
+ // a dedicated animation prop parser (e.g.
820
+ // https://github.com/hookhookun/parse-animation-shorthand) but it's
821
+ // probably overkill.
822
+ const tokens = decl.value
823
+ .trim()
824
+ .split(/\s+/g)
825
+ .map((token) => knownNames.has(token) ? `${token}-${SHADOW_ATTRIBUTE}` : token);
826
+ decl.value = tokens.join(' ');
827
+ }
828
+ else if (ANIMATION_NAME.has(decl.prop)) {
829
+ if (knownNames.has(decl.value)) {
830
+ decl.value = `${decl.value}-${SHADOW_ATTRIBUTE}`;
831
+ }
832
+ }
833
+ });
834
+ });
835
+ }
836
+
837
+ function shouldTransformSelector(rule) {
838
+ var _a;
839
+ // @keyframe at-rules are special, rules inside are not standard selectors and should not be
840
+ // scoped like any other rules.
841
+ return ((_a = rule.parent) === null || _a === void 0 ? void 0 : _a.type) !== 'atrule' || rule.parent.name !== 'keyframes';
842
+ }
843
+ function selectorProcessorFactory(transformConfig) {
844
+ return postCssSelector((root) => {
845
+ validateIdSelectors(root);
846
+ transformSelector(root, transformConfig);
847
+ transformDirPseudoClass(root);
848
+ });
849
+ }
850
+ function postCssLwcPlugin(options) {
851
+ // We need 2 types of selectors processors, since transforming the :host selector make the selector
852
+ // unusable when used in the context of the native shadow and vice-versa.
853
+ // This distinction also applies to light DOM in scoped (synthetic-like) vs unscoped (native-like) mode.
854
+ const nativeShadowSelectorProcessor = selectorProcessorFactory({
855
+ transformHost: false,
856
+ });
857
+ const syntheticShadowSelectorProcessor = selectorProcessorFactory({
858
+ transformHost: true,
859
+ });
860
+ return (root, result) => {
861
+ process$2(root, result, options.scoped);
862
+ process$1(root, result);
863
+ process(root);
864
+ root.walkRules((rule) => {
865
+ if (!shouldTransformSelector(rule)) {
866
+ return;
867
+ }
868
+ // Let transform the selector with the 2 processors.
869
+ const syntheticSelector = syntheticShadowSelectorProcessor.processSync(rule);
870
+ const nativeSelector = nativeShadowSelectorProcessor.processSync(rule);
871
+ rule.selector = syntheticSelector;
872
+ // If the resulting selector are different it means that the selector use the :host selector. In
873
+ // this case we need to duplicate the CSS rule and assign the other selector.
874
+ if (syntheticSelector !== nativeSelector) {
875
+ // The cloned selector is inserted before the currently processed selector to avoid processing
876
+ // again the cloned selector.
877
+ const currentRule = rule;
878
+ const clonedRule = rule.cloneBefore();
879
+ clonedRule.selector = nativeSelector;
880
+ // Safe a reference to each other
881
+ clonedRule._isNativeHost = true;
882
+ currentRule._isSyntheticHost = true;
883
+ }
884
+ });
885
+ };
886
+ }
887
+
888
+ /*
889
+ * Copyright (c) 2018, salesforce.com, inc.
890
+ * All rights reserved.
891
+ * SPDX-License-Identifier: MIT
892
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
893
+ */
894
+ function transform(src, id, config = {}) {
895
+ if (src === '') {
896
+ return { code: 'export default undefined' };
897
+ }
898
+ const scoped = !!config.scoped;
899
+ const plugins = [postCssLwcPlugin({ scoped })];
900
+ const result = postcss(plugins).process(src, { from: id }).sync();
901
+ return { code: serialize(result, config) };
902
+ }
903
+
904
+ export { transform };
905
+ /** version: 2.45.3 */
906
+ //# sourceMappingURL=index.js.map