Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast hydration & FIX iframes hydration #4309

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
abd5551
fast hydration
avi Jan 22, 2020
bd42d64
added test that fails on master
avi Jan 23, 2020
f927fc7
Merge branch 'master' into fast-hydration
avi Jan 23, 2020
f0ccaab
removed console.log
avi Jan 23, 2020
a0127ca
Merge branch 'fast-hydration' of github.com:avi/svelte into fast-hydr…
avi Jan 23, 2020
017d0db
skipping two tests that relied on async test in hydration and were se…
avi Jan 23, 2020
595262c
Merge branch 'master' into fast-hydration
avi Jan 29, 2020
4fd40ad
finished merge
avi Jan 29, 2020
7b4d049
Merge remote-tracking branch 'upstream/master' into fast-hydration
avi Feb 4, 2020
0e3e55e
updated typescript
avi Feb 5, 2020
5f1bbd6
Merge remote-tracking branch 'upstream/master' into fast-hydration
avi Feb 9, 2020
f92d463
remove comment
avi Feb 10, 2020
2f75fe9
Merge remote-tracking branch 'upstream/master' into fast-hydration
avi Feb 16, 2020
a4ac609
fast hydration
avi Jan 22, 2020
9dadb50
added test that fails on master
avi Jan 23, 2020
231c3e5
removed console.log
avi Jan 23, 2020
2451774
skipping two tests that relied on async test in hydration and were se…
avi Jan 23, 2020
5bb1876
finished merge
avi Jan 29, 2020
ca46753
updated typescript
avi Feb 5, 2020
a179d6f
remove comment
avi Feb 10, 2020
5e0ac70
Merge branch 'fast-hydration' of github.com:avi/svelte into fast-hydr…
avi Feb 16, 2020
124dcdb
Merge branch 'master' into fast-hydration
avi Mar 25, 2020
d46e1b3
revert typescript bump
avi Mar 25, 2020
9e2b070
forgot to pull before reset package.json and lock file
avi Mar 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/compile/render_dom/wrappers/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export default class EachBlockWrapper extends Wrapper {
block.add_element(
update_anchor_node as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_nodes && x`@claim_text(${parent_nodes}, '')`,
parent_node
);
}
Expand Down Expand Up @@ -385,7 +385,7 @@ export default class EachBlockWrapper extends Wrapper {
this.block.add_element(
this.block.first,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_nodes && x`@claim_text(#nodes, '')`,
null
);
}
Expand Down
10 changes: 3 additions & 7 deletions src/compiler/compile/render_dom/wrappers/Element/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { escape_html, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text';
import TagWrapper from '../shared/Tag';
import fix_attribute_casing from './fix_attribute_casing';
import { b, x, p } from 'code-red';
import { b, x } from 'code-red';
import { namespaces } from '../../../../utils/namespaces';
import AttributeWrapper from './Attribute';
import StyleAttributeWrapper from './StyleAttribute';
Expand Down Expand Up @@ -386,7 +386,7 @@ export default class ElementWrapper extends Wrapper {

if (nodes && this.renderer.options.hydratable && !this.void) {
block.chunks.claim.push(
b`${this.node.children.length > 0 ? nodes : children}.forEach(@detach);`
b`${this.node.children.length > 0 ? nodes : children}.children.forEach(@detach);`
);
}

Expand Down Expand Up @@ -422,17 +422,13 @@ export default class ElementWrapper extends Wrapper {
}

get_claim_statement(nodes: Identifier) {
const attributes = this.node.attributes
.filter((attr) => attr.type === 'Attribute')
.map((attr) => p`${attr.name}: true`);

const name = this.node.namespace
? this.node.name
: this.node.name.toUpperCase();

const svg = this.node.namespace === namespaces.svg ? 1 : null;

return x`@claim_element(${nodes}, "${name}", { ${attributes} }, ${svg})`;
return x`@claim_element(${nodes}, "${name}", null, ${svg})`;
}

add_directives_in_order (block: Block) {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/wrappers/Head.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class HeadWrapper extends Wrapper {

if (nodes && this.renderer.options.hydratable) {
block.chunks.claim.push(
b`${nodes}.forEach(@detach);`
b`${nodes}.children.forEach(@detach);`
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/wrappers/IfBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export default class IfBlockWrapper extends Wrapper {
block.add_element(
anchor as Identifier,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_nodes && x`@claim_text(${parent_nodes}, '')`,
parent_node
);
}
Expand Down
15 changes: 12 additions & 3 deletions src/compiler/compile/render_dom/wrappers/RawMustacheTag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class RawMustacheTagWrapper extends Tag {
this.not_static_content();
}

render(block: Block, parent_node: Identifier, _parent_nodes: Identifier) {
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
const in_head = is_head(parent_node);

const can_use_innerhtml = !in_head && parent_node && !this.prev && !this.next;
Expand Down Expand Up @@ -53,11 +53,20 @@ export default class RawMustacheTagWrapper extends Tag {

const update_anchor = in_head ? 'null' : needs_anchor ? html_anchor : this.next ? this.next.var : 'null';

block.chunks.hydrate.push(b`${html_tag} = new @HtmlTag(${init}, ${update_anchor});`);
block.chunks.hydrate.push(b`${html_tag}.a = ${update_anchor};`);
block.chunks.mount.push(b`${html_tag}.m(${parent_node || '#target'}, ${parent_node ? null : 'anchor'});`);


block.chunks.create.push(b`${html_tag} = new @HtmlTag(${init});`);
if (this.renderer.options.hydratable) {
block.chunks.claim.push(b`${html_tag} = new @HtmlTag(${init});`);
block.chunks.claim.push(
b`${html_tag}.l(${parent_nodes});`
);
}

if (needs_anchor) {
block.add_element(html_anchor, x`@empty()`, x`@empty()`, parent_node);
block.add_element(html_anchor, x`@empty()`, x`@claim_text(${parent_nodes}, '')`, parent_node);
}

if (!parent_node || in_head) {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/compile/render_dom/wrappers/shared/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class Wrapper {
block.add_element(
anchor,
x`@empty()`,
parent_nodes && x`@empty()`,
parent_nodes && x`@claim_text(${parent_nodes}, '')`,
parent_node as Identifier
);
}
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/internal/Component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler';
import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils';
import { children, detach } from './dom';
import { children, detach, update_hydrating } from './dom';
import { transition_in } from './transitions';

interface Fragment {
Expand Down Expand Up @@ -146,6 +146,7 @@ export function init(component, options, instance, create_fragment, not_equal, p

if (options.target) {
if (options.hydrate) {
update_hydrating(true);
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment!.l(nodes);
Expand All @@ -157,6 +158,7 @@ export function init(component, options, instance, create_fragment, not_equal, p

if (options.intro) transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor);
update_hydrating(false);
flush();
}

Expand Down
93 changes: 63 additions & 30 deletions src/runtime/internal/dom.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { has_prop } from "./utils";

let is_hydrating = false;

export function update_hydrating(val: boolean) {
is_hydrating = val;
}

export function append(target: Node, node: Node) {
target.appendChild(node);
if (!is_hydrating || node.parentNode !== target) {
target.appendChild(node);
}
}

export function insert(target: Node, node: Node, anchor?: Node) {
target.insertBefore(node, anchor || null);
if (!is_hydrating || node.parentNode !== target) {
target.insertBefore(node, anchor || null);
}
}

export function detach(node: Node) {
Expand Down Expand Up @@ -144,40 +154,48 @@ export function time_ranges_to_array(ranges) {
return array;
}

export function children(element) {
return Array.from(element.childNodes);
export function children(element: HTMLElement) {
const children = Array.from(element.childNodes);
return {
children,
element,
next: children[0] || null,
last: children.length ? children[children.length - 1].nextSibling : null,
};
}

export function claim_element(nodes, name, attributes, svg) {
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.nodeName === name) {
let j = 0;
while (j < node.attributes.length) {
const attribute = node.attributes[j];
if (attributes[attribute.name]) {
j++;
} else {
node.removeAttribute(attribute.name);
}
export function claim_element(nodes, name, fallback, svg) {
for (let i = 0; i < nodes.children.length; i += 1) {
const node = nodes.children[i];
if (node.nodeType !== 3) {
if (node.nodeName === name) {
nodes.children.splice(0,i + 1);
nodes.next = nodes.children[0];
return node;
} else {
nodes.next = nodes.last;
nodes.children.forEach(detach);
nodes.children.length = 0;
break;
}
return nodes.splice(i, 1)[0];
}
}

return svg ? svg_element(name) : element(name);
const node = fallback || (svg ? svg_element(name) : element(name));
insert(nodes.element, node, nodes.next);
return node;
}

export function claim_text(nodes, data) {
for (let i = 0; i < nodes.length; i += 1) {
const node = nodes[i];
if (node.nodeType === 3) {
node.data = '' + data;
return nodes.splice(i, 1)[0];
}
if (nodes.children.length && nodes.children[0].nodeType === 3) {
const node = nodes.children.shift();
node.data = '' + data;
nodes.next = nodes.children[0];
return node;
} else {
const node = text(data);
insert(nodes.element, node, nodes.next);
return node;
}

return text(data);
}

export function claim_space(nodes) {
Expand Down Expand Up @@ -279,7 +297,13 @@ export function custom_event<T=any>(type: string, detail?: T) {
}

export function query_selector_all(selector: string, parent: HTMLElement = document.body) {
return Array.from(parent.querySelectorAll(selector));
const children = Array.from(parent.querySelectorAll(selector));
return {
children,
element: parent,
next: children[0] || null,
last: children.length ? children[children.length - 1].nextSibling : null
};
}

export class HtmlTag {
Expand All @@ -288,9 +312,8 @@ export class HtmlTag {
t: HTMLElement;
a: HTMLElement;

constructor(html: string, anchor: HTMLElement = null) {
constructor(html: string) {
this.e = element('div');
this.a = anchor;
this.u(html);
}

Expand All @@ -307,6 +330,16 @@ export class HtmlTag {
this.n = Array.from(this.e.childNodes);
}

l(nodes: any) {
this.n = this.n.map(n => {
if (n.nodeType === 3) {
return claim_text(nodes, (n as Text).data);
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like this line and the closing curly brace two lines later both have an extra space

return claim_element(nodes, n.nodeName, n, n.namespaceURI !== 'http://www.w3.org/1999/xhtml');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'http://www.w3.org/1999/xhtml' could be replaced with namespaces.html:

export const html = 'http://www.w3.org/1999/xhtml';

}
});
}

p(html: string) {
this.d();
this.u(html);
Expand Down
4 changes: 2 additions & 2 deletions test/hydration/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('hydration', () => {
throw new Error('Forgot to remove `solo: true` from test');
}

(config.skip ? it.skip : solo ? it.only : it)(dir, () => {
(config.skip ? it.skip : solo ? it.only : it)(dir, async () => {
const cwd = path.resolve(`${__dirname}/samples/${dir}`);

compileOptions = config.compileOptions || {};
Expand Down Expand Up @@ -110,7 +110,7 @@ describe('hydration', () => {
}

if (config.test) {
config.test(assert, target, snapshot, component, window);
await config.test(assert, target, snapshot, component, window);
} else {
component.$destroy();
assert.equal(target.innerHTML, '');
Expand Down
2 changes: 2 additions & 0 deletions test/hydration/samples/binding-input/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export default {
name: 'world'
},

skip: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these tests being skipped?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of tests are skipped - the tests we're written as if their test harness was async but it wasn't and when I wanted to write a test that actually had to be async - the tests failed when I changed the test harness to be async... the change that broke the tests I skipped was to test/hydration/index.js


snapshot(target) {
return {
input: target.querySelector('input'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div></div>
<div class="foo" title="bar"></div>
2 changes: 2 additions & 0 deletions test/hydration/samples/event-handler/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export default {
clicked: false
},

skip: true,

snapshot(target) {
const button = target.querySelector('button');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<title>Some Title</title>
<link href="/" rel="canonical">
<meta content="some description" name="description">
<meta content="some keywords" name="keywords">
<link rel="canonical" href="/" data-svelte="svelte-1s8aodm">
<meta name="description" content="some description" data-svelte="svelte-1s8aodm">
<meta name="keywords" content="some keywords" data-svelte="svelte-1s8aodm">
3 changes: 3 additions & 0 deletions test/hydration/samples/iframe/_after.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
<iframe title="test"></iframe>
</div>
3 changes: 3 additions & 0 deletions test/hydration/samples/iframe/_before.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
<iframe title="test"></iframe>
</div>
Loading