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