@lwc/style-compiler 2.45.2 → 2.45.4

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