@lwc/style-compiler 8.28.2 → 9.0.0

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