diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 39f485a9f7..3ec9c4622a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -122,6 +122,13 @@ const any_selector = { */ const seen = new Set(); +/** + * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node + */ +function is_inside_svelte_head(node) { + return node.metadata.path.some((ancestor) => ancestor.type === 'SvelteHead'); +} + /** * * @param {Compiler.AST.CSS.StyleSheet} stylesheet @@ -248,6 +255,8 @@ function apply_selector( from = 0, to = relative_selectors.length ) { + if (is_inside_svelte_head(element)) return false; + if (from >= to) return false; const selector_index = direction === FORWARD ? from : to - 1; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index ef20049697..daa5b77231 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -207,6 +207,13 @@ const visitors = { VariableDeclarator }; +/** + * @param {AST.RegularElement | AST.SvelteElement} node + */ +function is_inside_svelte_head(node) { + return node.metadata.path.some((ancestor) => ancestor.type === 'SvelteHead'); +} + /** * @param {AST.Script | null} script * @param {ScopeRoot} root @@ -881,6 +888,12 @@ export function analyze_component(root, source, options) { } for (const node of analysis.elements) { + // Elements rendered through are not style-scopable. + // Prevent css hash injection (class="s-...") on tags like , , \ No newline at end of file diff --git a/packages/svelte/tests/hydration/samples/head-css-no-class/main.svelte b/packages/svelte/tests/hydration/samples/head-css-no-class/main.svelte new file mode 100644 index 0000000000..17889a34a4 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/head-css-no-class/main.svelte @@ -0,0 +1,10 @@ + + + + + + +
dummy
+ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/_config.js b/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/_config.js new file mode 100644 index 0000000000..e9c6e7520f --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../assert'; + +export default test({ + test({ assert, window }) { + const head = window.document.head; + + const meta = head.querySelector('meta[name="author"]'); + const link = head.querySelector('link[rel="author"]'); + const script = head.querySelector('script[type="application/ld+json"]'); + + assert.ok(meta); + assert.ok(link); + assert.ok(script); + + if (!meta || !link || !script) return; + + assert.equal(meta.getAttribute('class'), null); + assert.equal(link.getAttribute('class'), null); + assert.equal(script.getAttribute('class'), null); + } +}); diff --git a/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/main.svelte b/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/main.svelte new file mode 100644 index 0000000000..378ad3b802 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/head-elements-not-scoped/main.svelte @@ -0,0 +1,19 @@ + + + + + + +
+ Site by + + {author} + +
+ + + +