Skip to content

Commit 07d6d17

Browse files
authored
[fix] style manager transition regression (#7831)
1 parent ed078e3 commit 07d6d17

File tree

9 files changed

+97
-39
lines changed

9 files changed

+97
-39
lines changed

src/runtime/internal/dom.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,13 @@ export function get_root_for_style(node: Node): ShadowRoot | Document {
154154
return node.ownerDocument;
155155
}
156156

157-
export function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
157+
export function append_empty_stylesheet(node: Node) {
158+
const style_element = element('style') as HTMLStyleElement;
159+
append_stylesheet(get_root_for_style(node), style_element);
160+
return style_element.sheet as CSSStyleSheet;
161+
}
162+
163+
function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
158164
append((node as Document).head || node, style);
159165
return style.sheet as CSSStyleSheet;
160166
}

src/runtime/internal/style_manager.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { append_stylesheet, detach, element, get_root_for_style } from './dom';
1+
import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
22
import { raf } from './environment';
33

44
interface StyleInformation {
5-
style_element: HTMLStyleElement;
5+
stylesheet: CSSStyleSheet;
66
rules: Record<string, true>;
77
}
88

@@ -20,8 +20,8 @@ function hash(str: string) {
2020
return hash >>> 0;
2121
}
2222

23-
function create_style_information(doc: Document | ShadowRoot) {
24-
const info = { style_element: element('style'), rules: {} };
23+
function create_style_information(doc: Document | ShadowRoot, node: Element & ElementCSSInlineStyle) {
24+
const info = { stylesheet: append_empty_stylesheet(node), rules: {} };
2525
managed_styles.set(doc, info);
2626
return info;
2727
}
@@ -39,10 +39,9 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b:
3939
const name = `__svelte_${hash(rule)}_${uid}`;
4040
const doc = get_root_for_style(node);
4141

42-
const { style_element, rules } = managed_styles.get(doc) || create_style_information(doc);
42+
const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node);
4343

4444
if (!rules[name]) {
45-
const stylesheet = append_stylesheet(doc, style_element);
4645
rules[name] = true;
4746
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
4847
}
@@ -72,8 +71,9 @@ export function clear_rules() {
7271
raf(() => {
7372
if (active) return;
7473
managed_styles.forEach(info => {
75-
const { style_element } = info;
76-
detach(style_element);
74+
const { ownerNode } = info.stylesheet;
75+
// there is no ownerNode if it runs on jsdom.
76+
if (ownerNode) detach(ownerNode);
7777
});
7878
managed_styles.clear();
7979
});

test/runtime-puppeteer/index.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@ describe('runtime (puppeteer)', function() {
117117
load(id) {
118118
if (id === 'main') {
119119
return `
120-
import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))};
121-
import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))};
120+
import SvelteComponent from ${JSON.stringify(
121+
path.join(__dirname, 'samples', dir, 'main.svelte')
122+
)};
123+
import config from ${JSON.stringify(
124+
path.join(__dirname, 'samples', dir, '_config.js')
125+
)};
122126
import * as assert from 'assert';
123127
124128
export default async function (target) {
@@ -140,6 +144,14 @@ describe('runtime (puppeteer)', function() {
140144
141145
const component = new SvelteComponent(options);
142146
147+
const waitUntil = async (fn, ms = 500) => {
148+
const start = new Date().getTime();
149+
do {
150+
if (fn()) return;
151+
await new Promise(resolve => window.setTimeout(resolve, 1));
152+
} while (new Date().getTime() <= start + ms);
153+
};
154+
143155
if (config.html) {
144156
assert.htmlEqual(target.innerHTML, config.html);
145157
}
@@ -150,6 +162,7 @@ describe('runtime (puppeteer)', function() {
150162
component,
151163
target,
152164
window,
165+
waitUntil,
153166
});
154167
155168
component.$destroy();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default {
2+
skip_if_ssr: true,
3+
skip_if_hydrate: true,
4+
skip_if_hydrate_from_ssr: true,
5+
test: async ({ component, assert, window, waitUntil }) => {
6+
assert.htmlEqual(window.document.head.innerHTML, '');
7+
component.visible = true;
8+
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
9+
await waitUntil(() => window.document.head.innerHTML === '');
10+
assert.htmlEqual(window.document.head.innerHTML, '');
11+
12+
component.visible = false;
13+
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
14+
await waitUntil(() => window.document.head.innerHTML === '');
15+
assert.htmlEqual(window.document.head.innerHTML, '');
16+
}
17+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script>
2+
export let visible;
3+
4+
function foo() {
5+
return {
6+
duration: 10,
7+
css: t => {
8+
return `opacity: ${t}`;
9+
}
10+
};
11+
}
12+
</script>
13+
14+
{#if visible}
15+
<div transition:foo></div>
16+
{/if}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default {
2+
test: async ({ assert, component, window, waitUntil }) => {
3+
component.visible = true;
4+
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
5+
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
6+
await waitUntil(() => window.document.head.querySelector('style') === null);
7+
assert.equal(window.document.head.querySelector('style'), null);
8+
component.visible = false;
9+
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
10+
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
11+
await waitUntil(() => window.document.head.querySelector('style') === null);
12+
assert.equal(window.document.head.querySelector('style'), null);
13+
}
14+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
export let visible;
3+
4+
function foo() {
5+
return {
6+
duration: 10,
7+
css: t => {
8+
return `opacity: ${t}`;
9+
}
10+
};
11+
}
12+
</script>
13+
14+
{#if visible}
15+
<div transition:foo></div>
16+
{/if}
17+
18+
{#if !visible}
19+
<div transition:foo></div>
20+
{/if}

test/runtime/samples/style_manager-cleanup/_config.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

test/runtime/samples/style_manager-cleanup/main.svelte

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)