Skip to content

Commit 9945205

Browse files
fix: append_styles in an effect to make them available on mount (#16509)
Co-authored-by: Rich Harris <[email protected]>
1 parent 1773df9 commit 9945205

File tree

10 files changed

+90
-11
lines changed

10 files changed

+90
-11
lines changed

.changeset/shaggy-comics-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: `append_styles` in an effect to make them available on mount

.changeset/wise-hairs-pay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: always inject styles when compiling as a custom element

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ export function analyze_component(root, source, options) {
451451
}
452452
}
453453

454+
const is_custom_element = !!options.customElementOptions || options.customElement;
455+
454456
// TODO remove all the ?? stuff, we don't need it now that we're validating the config
455457
/** @type {ComponentAnalysis} */
456458
const analysis = {
@@ -500,13 +502,13 @@ export function analyze_component(root, source, options) {
500502
needs_props: false,
501503
event_directive_node: null,
502504
uses_event_attributes: false,
503-
custom_element: options.customElementOptions ?? options.customElement,
504-
inject_styles: options.css === 'injected' || options.customElement,
505-
accessors: options.customElement
506-
? true
507-
: (runes ? false : !!options.accessors) ||
508-
// because $set method needs accessors
509-
options.compatibility?.componentApi === 4,
505+
custom_element: is_custom_element,
506+
inject_styles: options.css === 'injected' || is_custom_element,
507+
accessors:
508+
is_custom_element ||
509+
(runes ? false : !!options.accessors) ||
510+
// because $set method needs accessors
511+
options.compatibility?.componentApi === 4,
510512
reactive_statements: new Map(),
511513
binding_groups: new Map(),
512514
slot_names: new Map(),

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,9 @@ export function client_component(analysis, options) {
621621
);
622622
}
623623

624-
if (analysis.custom_element) {
625-
const ce = analysis.custom_element;
624+
const ce = options.customElementOptions ?? options.customElement;
625+
626+
if (ce) {
626627
const ce_props = typeof ce === 'boolean' ? {} : ce.props || {};
627628

628629
/** @type {ESTree.Property[]} */

packages/svelte/src/internal/client/dom/css.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { DEV } from 'esm-env';
2-
import { queue_micro_task } from './task.js';
32
import { register_style } from '../dev/css.js';
3+
import { effect } from '../reactivity/effects.js';
44

55
/**
66
* @param {Node} anchor
77
* @param {{ hash: string, code: string }} css
88
*/
99
export function append_styles(anchor, css) {
1010
// Use `queue_micro_task` to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results
11-
queue_micro_task(() => {
11+
effect(() => {
1212
var root = anchor.getRootNode();
1313

1414
var target = /** @type {ShadowRoot} */ (root).host
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { test } from '../../assert';
2+
const tick = () => Promise.resolve();
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
target.innerHTML = '<custom-element></custom-element>';
7+
/** @type {any} */
8+
const el = target.querySelector('custom-element');
9+
10+
/** @type {string} */
11+
let html = '';
12+
const handle_evt = (e) => (html = e.detail);
13+
el.addEventListener('html', handle_evt);
14+
15+
await tick();
16+
await tick();
17+
await tick();
18+
19+
assert.ok(html.includes('<style'));
20+
}
21+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<svelte:options css="injected" customElement="custom-element"/>
2+
3+
<script lang="ts">
4+
$effect(() => {
5+
$host().dispatchEvent(new CustomEvent("html", { detail: $host().shadowRoot?.innerHTML }));
6+
})
7+
</script>
8+
9+
<button class="btn">btn</button>
10+
11+
<style >
12+
.btn {
13+
width: 123px;
14+
height: 123px;
15+
}
16+
</style>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<svelte:options customElement={{ tag: 'my-thing' }} />
2+
3+
<h1>hello</h1>
4+
5+
<style>
6+
h1 {
7+
color: red;
8+
}
9+
</style>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
mode: ['client'],
6+
async test({ assert, target }) {
7+
const thing = /** @type HTMLElement & { object: { test: true }; } */ (
8+
target.querySelector('my-thing')
9+
);
10+
11+
await tick();
12+
13+
assert.include(thing.shadowRoot?.innerHTML, 'red');
14+
}
15+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
import './Thing.svelte';
3+
</script>
4+
5+
<my-thing></my-thing>

0 commit comments

Comments
 (0)