fix: `append_styles` in an effect to make them available on mount (#16509)

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/16514/head
Paolo Ricciuti 1 month ago committed by GitHub
parent 1773df9484
commit 99452053af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: `append_styles` in an effect to make them available on mount

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: always inject styles when compiling as a custom element

@ -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(),

@ -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[]} */

@ -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

@ -0,0 +1,21 @@
import { test } from '../../assert';
const tick = () => Promise.resolve();
export default test({
async test({ assert, target }) {
target.innerHTML = '<custom-element></custom-element>';
/** @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('<style'));
}
});

@ -0,0 +1,16 @@
<svelte:options css="injected" customElement="custom-element"/>
<script lang="ts">
$effect(() => {
$host().dispatchEvent(new CustomEvent("html", { detail: $host().shadowRoot?.innerHTML }));
})
</script>
<button class="btn">btn</button>
<style >
.btn {
width: 123px;
height: 123px;
}
</style>

@ -0,0 +1,9 @@
<svelte:options customElement={{ tag: 'my-thing' }} />
<h1>hello</h1>
<style>
h1 {
color: red;
}
</style>

@ -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');
}
});

@ -0,0 +1,5 @@
<script>
import './Thing.svelte';
</script>
<my-thing></my-thing>
Loading…
Cancel
Save