diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/actual.css b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/actual.css new file mode 100644 index 0000000000..18ba4145c5 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/actual.css @@ -0,0 +1 @@ +:host-context(.foo) {} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/config.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/config.json new file mode 100644 index 0000000000..b93f34675c --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/config.json @@ -0,0 +1,4 @@ +{ + "disableSyntheticShadowSupport": true, + "scoped": true +} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/error.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/error.json new file mode 100644 index 0000000000..61ba9a9f3f --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector-scoped/error.json @@ -0,0 +1,6 @@ +{ + "name": "CssSyntaxError", + "reason": "Invalid usage of unsupported selector \":host-context\". This selector is only supported in non-scoped CSS where the `disableSyntheticShadowSupport` flag is set to true.", + "column": 1, + "line": 1 +} \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/actual.css b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/actual.css new file mode 100644 index 0000000000..18ba4145c5 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/actual.css @@ -0,0 +1 @@ +:host-context(.foo) {} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/config.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/config.json new file mode 100644 index 0000000000..0a910de825 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/config.json @@ -0,0 +1,3 @@ +{ + "disableSyntheticShadowSupport": true +} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/expected.js b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/expected.js new file mode 100644 index 0000000000..252029db11 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-host-context-selector/expected.js @@ -0,0 +1,12 @@ +function stylesheet() { + var token; + var useActualHostSelector = true; + var useNativeDirPseudoclass = true; + var shadowSelector = token ? ("[" + token + "]") : ""; + var hostSelector = token ? ("[" + token + "-host]") : ""; + var suffixToken = token ? ("-" + token) : ""; + return ":host-context(.foo)" + shadowSelector + " {}"; + /*LWC compiler vX.X.X*/ +} +stylesheet.$nativeOnly$ = true; +export default [stylesheet]; \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/actual.css b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/actual.css new file mode 100644 index 0000000000..052bf632f1 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/actual.css @@ -0,0 +1 @@ +:root {} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/config.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/config.json new file mode 100644 index 0000000000..b93f34675c --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/config.json @@ -0,0 +1,4 @@ +{ + "disableSyntheticShadowSupport": true, + "scoped": true +} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/error.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/error.json new file mode 100644 index 0000000000..297146009e --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector-scoped/error.json @@ -0,0 +1,6 @@ +{ + "name": "CssSyntaxError", + "reason": "Invalid usage of unsupported selector \":root\". This selector is only supported in non-scoped CSS where the `disableSyntheticShadowSupport` flag is set to true.", + "column": 1, + "line": 1 +} \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/actual.css b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/actual.css new file mode 100644 index 0000000000..052bf632f1 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/actual.css @@ -0,0 +1 @@ +:root {} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/config.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/config.json new file mode 100644 index 0000000000..0a910de825 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/config.json @@ -0,0 +1,3 @@ +{ + "disableSyntheticShadowSupport": true +} diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/expected.js b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/expected.js new file mode 100644 index 0000000000..42d7d5cbc3 --- /dev/null +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/disable-synthetic-shadow-support-root-selector/expected.js @@ -0,0 +1,12 @@ +function stylesheet() { + var token; + var useActualHostSelector = true; + var useNativeDirPseudoclass = true; + var shadowSelector = token ? ("[" + token + "]") : ""; + var hostSelector = token ? ("[" + token + "-host]") : ""; + var suffixToken = token ? ("-" + token) : ""; + return ":root" + shadowSelector + " {}"; + /*LWC compiler vX.X.X*/ +} +stylesheet.$nativeOnly$ = true; +export default [stylesheet]; \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-host-context-selector/error.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-host-context-selector/error.json index f5131d5bdf..61ba9a9f3f 100644 --- a/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-host-context-selector/error.json +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-host-context-selector/error.json @@ -1,6 +1,6 @@ { "name": "CssSyntaxError", - "reason": "Invalid usage of unsupported selector \":host-context\".", + "reason": "Invalid usage of unsupported selector \":host-context\". This selector is only supported in non-scoped CSS where the `disableSyntheticShadowSupport` flag is set to true.", "column": 1, "line": 1 } \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-root-selector/error.json b/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-root-selector/error.json index ba796fd202..297146009e 100644 --- a/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-root-selector/error.json +++ b/packages/@lwc/style-compiler/src/__tests__/fixtures/unsupported-root-selector/error.json @@ -1,6 +1,6 @@ { "name": "CssSyntaxError", - "reason": "Invalid usage of unsupported selector \":root\".", + "reason": "Invalid usage of unsupported selector \":root\". This selector is only supported in non-scoped CSS where the `disableSyntheticShadowSupport` flag is set to true.", "column": 1, "line": 1 } \ No newline at end of file diff --git a/packages/@lwc/style-compiler/src/postcss-lwc-plugin.ts b/packages/@lwc/style-compiler/src/postcss-lwc-plugin.ts index 92171b694a..812110ff7d 100644 --- a/packages/@lwc/style-compiler/src/postcss-lwc-plugin.ts +++ b/packages/@lwc/style-compiler/src/postcss-lwc-plugin.ts @@ -32,15 +32,20 @@ function selectorProcessorFactory(transformConfig: SelectorScopingConfig) { export default function postCssLwcPlugin(options: { scoped: boolean; apiVersion: APIVersion; + disableSyntheticShadowSupport: boolean; }): TransformCallback { // We need 2 types of selectors processors, since transforming the :host selector make the selector // unusable when used in the context of the native shadow and vice-versa. // This distinction also applies to light DOM in scoped (synthetic-like) vs unscoped (native-like) mode. const nativeShadowSelectorProcessor = selectorProcessorFactory({ transformHost: false, + disableSyntheticShadowSupport: options.disableSyntheticShadowSupport, + scoped: options.scoped, }); const syntheticShadowSelectorProcessor = selectorProcessorFactory({ transformHost: true, + disableSyntheticShadowSupport: options.disableSyntheticShadowSupport, + scoped: options.scoped, }); return (root, result) => { diff --git a/packages/@lwc/style-compiler/src/selector-scoping/transform.ts b/packages/@lwc/style-compiler/src/selector-scoping/transform.ts index 25b3a44c5e..3558bece3a 100644 --- a/packages/@lwc/style-compiler/src/selector-scoping/transform.ts +++ b/packages/@lwc/style-compiler/src/selector-scoping/transform.ts @@ -27,6 +27,10 @@ type ChildNode = Exclude; export interface SelectorScopingConfig { /** When set to true, the :host selector gets replace with the the scoping token. */ transformHost: boolean; + /** When set to true, the synthetic shadow support is disabled. */ + disableSyntheticShadowSupport: boolean; + /** When set to true, the selector is scoped. */ + scoped: boolean; } function isHostPseudoClass(node: Node): node is Pseudo { @@ -145,7 +149,10 @@ function transformHost(selector: Selector) { } export default function transformSelector(root: Root, transformConfig: SelectorScopingConfig) { - validateSelectors(root); + validateSelectors( + root, + transformConfig.disableSyntheticShadowSupport && !transformConfig.scoped + ); root.each(scopeSelector); diff --git a/packages/@lwc/style-compiler/src/selector-scoping/validate.ts b/packages/@lwc/style-compiler/src/selector-scoping/validate.ts index 036f2bc1a0..d0f2a9b14f 100644 --- a/packages/@lwc/style-compiler/src/selector-scoping/validate.ts +++ b/packages/@lwc/style-compiler/src/selector-scoping/validate.ts @@ -10,7 +10,7 @@ const DEPRECATED_SELECTORS = new Set(['/deep/', '::shadow', '>>>']); const UNSUPPORTED_SELECTORS = new Set([':root', ':host-context']); const TEMPLATE_DIRECTIVES = [/^key$/, /^lwc:*/, /^if:*/, /^for:*/, /^iterator:*/]; -function validateSelectors(root: Root) { +function validateSelectors(root: Root, native: boolean) { root.walk((node) => { const { value, sourceIndex } = node; @@ -24,11 +24,14 @@ function validateSelectors(root: Root) { } // Ensure the selector doesn't use an unsupported selector. - if (UNSUPPORTED_SELECTORS.has(value)) { - throw root.error(`Invalid usage of unsupported selector "${value}".`, { - index: sourceIndex, - word: value, - }); + if (!native && UNSUPPORTED_SELECTORS.has(value)) { + 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.`, + { + index: sourceIndex, + word: value, + } + ); } } }); @@ -55,7 +58,7 @@ function validateAttribute(root: Root) { }); } -export default function validate(root: Root) { - validateSelectors(root); +export default function validate(root: Root, native: boolean) { + validateSelectors(root, native); validateAttribute(root); } diff --git a/packages/@lwc/style-compiler/src/transform.ts b/packages/@lwc/style-compiler/src/transform.ts index e82020feb1..52f50fe6a2 100644 --- a/packages/@lwc/style-compiler/src/transform.ts +++ b/packages/@lwc/style-compiler/src/transform.ts @@ -53,8 +53,9 @@ export function transform(src: string, id: string, config: Config = {}): { code: const scoped = !!config.scoped; const apiVersion = getAPIVersionFromNumber(config.apiVersion); + const disableSyntheticShadowSupport = !!config.disableSyntheticShadowSupport; - const plugins = [postcssLwc({ scoped, apiVersion })]; + const plugins = [postcssLwc({ scoped, apiVersion, disableSyntheticShadowSupport })]; const result = postcss(plugins).process(src, { from: id }).sync();