From 59a379596c2e61327007b7e428e3f7838f685344 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Sun, 27 Jul 2025 11:28:07 +0200 Subject: [PATCH 1/2] fix: `append_styles` in an effect to make them available on mount --- .changeset/shaggy-comics-fail.md | 5 +++++ .../svelte/src/internal/client/dom/css.js | 4 ++-- .../host-rune-access-injected-css/_config.js | 21 +++++++++++++++++++ .../host-rune-access-injected-css/main.svelte | 16 ++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 .changeset/shaggy-comics-fail.md create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/host-rune-access-injected-css/_config.js create mode 100644 packages/svelte/tests/runtime-browser/custom-elements-samples/host-rune-access-injected-css/main.svelte diff --git a/.changeset/shaggy-comics-fail.md b/.changeset/shaggy-comics-fail.md new file mode 100644 index 000000000000..981a25c978d6 --- /dev/null +++ b/.changeset/shaggy-comics-fail.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: `append_styles` in an effect to make them available on mount diff --git a/packages/svelte/src/internal/client/dom/css.js b/packages/svelte/src/internal/client/dom/css.js index 52be36aa1f46..8e6faa0e32dc 100644 --- a/packages/svelte/src/internal/client/dom/css.js +++ b/packages/svelte/src/internal/client/dom/css.js @@ -1,6 +1,6 @@ import { DEV } from 'esm-env'; -import { queue_micro_task } from './task.js'; import { register_style } from '../dev/css.js'; +import { effect } from '../reactivity/effects.js'; /** * @param {Node} anchor @@ -8,7 +8,7 @@ import { register_style } from '../dev/css.js'; */ export function append_styles(anchor, css) { // Use `queue_micro_task` to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results - queue_micro_task(() => { + effect(() => { var root = anchor.getRootNode(); var target = /** @type {ShadowRoot} */ (root).host diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/host-rune-access-injected-css/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/host-rune-access-injected-css/_config.js new file mode 100644 index 000000000000..99a223492b8b --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/host-rune-access-injected-css/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../assert'; +const tick = () => Promise.resolve(); + +export default test({ + async test({ assert, target }) { + target.innerHTML = ''; + /** @type {any} */ + const el = target.querySelector('custom-element'); + + /** @type {string} */ + let html = ''; + const handle_evt = (e) => (html = e.detail); + el.addEventListener('html', handle_evt); + + await tick(); + await tick(); + await tick(); + + assert.ok(html.includes(' + + + + + + \ No newline at end of file From e006ac5d3fa15e30c1add9352bb7c4011223f212 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 27 Jul 2025 10:31:14 -0400 Subject: [PATCH 2/2] always inject styles when compiling as a custom element --- .changeset/wise-hairs-pay.md | 5 +++++ .../src/compiler/phases/2-analyze/index.js | 16 +++++++++------- .../3-transform/client/transform-client.js | 5 +++-- .../custom-element-injected-styles/Thing.svelte | 9 +++++++++ .../custom-element-injected-styles/_config.js | 15 +++++++++++++++ .../custom-element-injected-styles/main.svelte | 5 +++++ 6 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 .changeset/wise-hairs-pay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/Thing.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/main.svelte diff --git a/.changeset/wise-hairs-pay.md b/.changeset/wise-hairs-pay.md new file mode 100644 index 000000000000..7d96c1daab15 --- /dev/null +++ b/.changeset/wise-hairs-pay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: always inject styles when compiling as a custom element diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index d407b4455639..cd44fd998aed 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -451,6 +451,8 @@ export function analyze_component(root, source, options) { } } + const is_custom_element = !!options.customElementOptions || options.customElement; + // TODO remove all the ?? stuff, we don't need it now that we're validating the config /** @type {ComponentAnalysis} */ const analysis = { @@ -500,13 +502,13 @@ export function analyze_component(root, source, options) { needs_props: false, event_directive_node: null, uses_event_attributes: false, - custom_element: options.customElementOptions ?? options.customElement, - inject_styles: options.css === 'injected' || options.customElement, - accessors: options.customElement - ? true - : (runes ? false : !!options.accessors) || - // because $set method needs accessors - options.compatibility?.componentApi === 4, + custom_element: is_custom_element, + inject_styles: options.css === 'injected' || is_custom_element, + accessors: + is_custom_element || + (runes ? false : !!options.accessors) || + // because $set method needs accessors + options.compatibility?.componentApi === 4, reactive_statements: new Map(), binding_groups: new Map(), slot_names: new Map(), diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 124438a9dab0..a56aca9c5f0b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -621,8 +621,9 @@ export function client_component(analysis, options) { ); } - if (analysis.custom_element) { - const ce = analysis.custom_element; + const ce = options.customElementOptions ?? options.customElement; + + if (ce) { const ce_props = typeof ce === 'boolean' ? {} : ce.props || {}; /** @type {ESTree.Property[]} */ diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/Thing.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/Thing.svelte new file mode 100644 index 000000000000..0a2b139274d6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/Thing.svelte @@ -0,0 +1,9 @@ + + +

hello

+ + diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/_config.js new file mode 100644 index 000000000000..74597504bd92 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/_config.js @@ -0,0 +1,15 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + async test({ assert, target }) { + const thing = /** @type HTMLElement & { object: { test: true }; } */ ( + target.querySelector('my-thing') + ); + + await tick(); + + assert.include(thing.shadowRoot?.innerHTML, 'red'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/main.svelte new file mode 100644 index 000000000000..ba5b788da9b3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-injected-styles/main.svelte @@ -0,0 +1,5 @@ + + +