@lwc/style-compiler 2.45.2 → 2.45.4

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 +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