Skip to content

Commit

Permalink
[fix] style manager transition regression (#7831)
Browse files Browse the repository at this point in the history
  • Loading branch information
baseballyama committed Sep 8, 2022
1 parent ed078e3 commit 07d6d17
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 39 deletions.
8 changes: 7 additions & 1 deletion src/runtime/internal/dom.ts
Expand Up @@ -154,7 +154,13 @@ export function get_root_for_style(node: Node): ShadowRoot | Document {
return node.ownerDocument;
}

export function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
export function append_empty_stylesheet(node: Node) {
const style_element = element('style') as HTMLStyleElement;
append_stylesheet(get_root_for_style(node), style_element);
return style_element.sheet as CSSStyleSheet;
}

function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) {
append((node as Document).head || node, style);
return style.sheet as CSSStyleSheet;
}
Expand Down
16 changes: 8 additions & 8 deletions src/runtime/internal/style_manager.ts
@@ -1,8 +1,8 @@
import { append_stylesheet, detach, element, get_root_for_style } from './dom';
import { append_empty_stylesheet, detach, get_root_for_style } from './dom';
import { raf } from './environment';

interface StyleInformation {
style_element: HTMLStyleElement;
stylesheet: CSSStyleSheet;
rules: Record<string, true>;
}

Expand All @@ -20,8 +20,8 @@ function hash(str: string) {
return hash >>> 0;
}

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

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

if (!rules[name]) {
const stylesheet = append_stylesheet(doc, style_element);
rules[name] = true;
stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
}
Expand Down Expand Up @@ -72,8 +71,9 @@ export function clear_rules() {
raf(() => {
if (active) return;
managed_styles.forEach(info => {
const { style_element } = info;
detach(style_element);
const { ownerNode } = info.stylesheet;
// there is no ownerNode if it runs on jsdom.
if (ownerNode) detach(ownerNode);
});
managed_styles.clear();
});
Expand Down
17 changes: 15 additions & 2 deletions test/runtime-puppeteer/index.ts
Expand Up @@ -117,8 +117,12 @@ describe('runtime (puppeteer)', function() {
load(id) {
if (id === 'main') {
return `
import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))};
import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))};
import SvelteComponent from ${JSON.stringify(
path.join(__dirname, 'samples', dir, 'main.svelte')
)};
import config from ${JSON.stringify(
path.join(__dirname, 'samples', dir, '_config.js')
)};
import * as assert from 'assert';
export default async function (target) {
Expand All @@ -140,6 +144,14 @@ describe('runtime (puppeteer)', function() {
const component = new SvelteComponent(options);
const waitUntil = async (fn, ms = 500) => {
const start = new Date().getTime();
do {
if (fn()) return;
await new Promise(resolve => window.setTimeout(resolve, 1));
} while (new Date().getTime() <= start + ms);
};
if (config.html) {
assert.htmlEqual(target.innerHTML, config.html);
}
Expand All @@ -150,6 +162,7 @@ describe('runtime (puppeteer)', function() {
component,
target,
window,
waitUntil,
});
component.$destroy();
Expand Down
17 changes: 17 additions & 0 deletions test/runtime-puppeteer/samples/style_manager-cleanup/_config.js
@@ -0,0 +1,17 @@
export default {
skip_if_ssr: true,
skip_if_hydrate: true,
skip_if_hydrate_from_ssr: true,
test: async ({ component, assert, window, waitUntil }) => {
assert.htmlEqual(window.document.head.innerHTML, '');
component.visible = true;
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
await waitUntil(() => window.document.head.innerHTML === '');
assert.htmlEqual(window.document.head.innerHTML, '');

component.visible = false;
assert.htmlEqual(window.document.head.innerHTML, '<style></style>');
await waitUntil(() => window.document.head.innerHTML === '');
assert.htmlEqual(window.document.head.innerHTML, '');
}
};
16 changes: 16 additions & 0 deletions test/runtime-puppeteer/samples/style_manager-cleanup/main.svelte
@@ -0,0 +1,16 @@
<script>
export let visible;
function foo() {
return {
duration: 10,
css: t => {
return `opacity: ${t}`;
}
};
}
</script>

{#if visible}
<div transition:foo></div>
{/if}
14 changes: 14 additions & 0 deletions test/runtime-puppeteer/samples/transition-css-out-in/_config.js
@@ -0,0 +1,14 @@
export default {
test: async ({ assert, component, window, waitUntil }) => {
component.visible = true;
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
await waitUntil(() => window.document.head.querySelector('style') === null);
assert.equal(window.document.head.querySelector('style'), null);
component.visible = false;
await waitUntil(() => window.document.head.querySelector('style').sheet.rules.length === 2);
assert.equal(window.document.head.querySelector('style').sheet.rules.length, 2);
await waitUntil(() => window.document.head.querySelector('style') === null);
assert.equal(window.document.head.querySelector('style'), null);
}
};
20 changes: 20 additions & 0 deletions test/runtime-puppeteer/samples/transition-css-out-in/main.svelte
@@ -0,0 +1,20 @@
<script>
export let visible;
function foo() {
return {
duration: 10,
css: t => {
return `opacity: ${t}`;
}
};
}
</script>

{#if visible}
<div transition:foo></div>
{/if}

{#if !visible}
<div transition:foo></div>
{/if}
14 changes: 0 additions & 14 deletions test/runtime/samples/style_manager-cleanup/_config.js

This file was deleted.

14 changes: 0 additions & 14 deletions test/runtime/samples/style_manager-cleanup/main.svelte

This file was deleted.

0 comments on commit 07d6d17

Please sign in to comment.