Skip to content

Commit

Permalink
claim static html elements using innerHTML instead of deopt to creati…
Browse files Browse the repository at this point in the history
…ng the nodes
  • Loading branch information
tanhauhau committed Apr 7, 2022
1 parent 5f020cc commit 0f5db18
Show file tree
Hide file tree
Showing 50 changed files with 221 additions and 108 deletions.
10 changes: 10 additions & 0 deletions src/compiler/compile/Component.ts
Expand Up @@ -37,6 +37,7 @@ import compiler_warnings from './compiler_warnings';
import compiler_errors from './compiler_errors';
import { extract_ignores_above_position, extract_svelte_ignore_from_comments } from '../utils/extract_svelte_ignore';
import check_enable_sourcemap from './utils/check_enable_sourcemap';
import Tag from './nodes/shared/Tag';

interface ComponentOptions {
namespace?: string;
Expand Down Expand Up @@ -105,6 +106,8 @@ export default class Component {
slots: Map<string, Slot> = new Map();
slot_outlets: Set<string> = new Set();

tags: Tag[] = []

constructor(
ast: Ast,
source: string,
Expand Down Expand Up @@ -756,6 +759,7 @@ export default class Component {

this.hoist_instance_declarations();
this.extract_reactive_declarations();
this.check_if_tags_content_dynamic();
}

post_template_walk() {
Expand Down Expand Up @@ -1427,6 +1431,12 @@ export default class Component {
unsorted_reactive_declarations.forEach(add_declaration);
}

check_if_tags_content_dynamic() {
this.tags.forEach(tag => {
tag.check_if_content_dynamic();
});
}

warn_if_undefined(name: string, node, template_scope: TemplateScope) {
if (name[0] === '$') {
if (name === '$' || name[1] === '$' && !is_reserved_keyword(name)) {
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/compile/nodes/Attribute.ts
Expand Up @@ -59,6 +59,11 @@ export default class Attribute extends Node {
return expression;
});
}

if (this.dependencies.size > 0) {
parent.cannot_use_innerhtml();
parent.not_static_content();
}
}

get_dependencies() {
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/AwaitBlock.ts
Expand Up @@ -25,6 +25,8 @@ export default class AwaitBlock extends Node {

constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();

this.expression = new Expression(component, this, scope, info.expression);

Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/EachBlock.ts
Expand Up @@ -33,6 +33,8 @@ export default class EachBlock extends AbstractBlock {

constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();

this.expression = new Expression(component, this, scope, info.expression);
this.context = info.context.name || 'each'; // TODO this is used to facilitate binding; currently fails with destructuring
Expand Down
29 changes: 29 additions & 0 deletions src/compiler/compile/nodes/Element.ts
Expand Up @@ -14,6 +14,7 @@ import map_children from './shared/map_children';
import { dimensions } from '../../utils/patterns';
import fuzzymatch from '../../utils/fuzzymatch';
import list from '../../utils/list';
import hash from '../utils/hash';
import Let from './Let';
import TemplateScope from './shared/TemplateScope';
import { INode } from './interfaces';
Expand Down Expand Up @@ -309,6 +310,22 @@ export default class Element extends Node {
this.optimise();

component.apply_stylesheet(this);

if (this.parent) {
if (this.actions.length > 0 ||
this.animation ||
this.bindings.length > 0 ||
this.classes.length > 0 ||
this.intro || this.outro ||
this.handlers.length > 0 ||
this.styles.length > 0 ||
this.name === 'option' ||
component.compile_options.dev
) {
this.parent.cannot_use_innerhtml(); // need to use add_location
this.parent.not_static_content();
}
}
}

validate() {
Expand Down Expand Up @@ -852,6 +869,18 @@ export default class Element extends Node {
}
});
}

get can_use_textcontent() {
return this.is_static_content && this.children.every(node => node.type === 'Text' || node.type === 'MustacheTag');
}

get can_optimise_to_html_string() {
return !this.namespace && (this.can_use_innerhtml || this.can_use_textcontent) && this.children.length > 0;
}

hash() {
return `svelte-${hash(this.component.source.slice(this.start, this.end))}`;
}
}

function should_have_attribute(
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/Head.ts
Expand Up @@ -14,6 +14,8 @@ export default class Head extends Node {
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.can_use_innerhtml = false;

if (info.attributes.length) {
component.error(info.attributes[0], compiler_errors.invalid_attribute_head);
return;
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/IfBlock.ts
Expand Up @@ -14,6 +14,8 @@ export default class IfBlock extends AbstractBlock {

constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();

this.expression = new Expression(component, this, scope, info.expression);
this.children = map_children(component, this, scope, info.children);
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/compile/nodes/InlineComponent.ts
Expand Up @@ -26,6 +26,9 @@ export default class InlineComponent extends Node {
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);

this.cannot_use_innerhtml();
this.not_static_content();

if (info.name !== 'svelte:component' && info.name !== 'svelte:self') {
const name = info.name.split('.')[0]; // accommodate namespaces
component.warn_if_undefined(name, info, scope);
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/compile/nodes/KeyBlock.ts
Expand Up @@ -13,6 +13,8 @@ export default class KeyBlock extends AbstractBlock {

constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
super(component, parent, scope, info);
this.cannot_use_innerhtml();
this.not_static_content();

this.expression = new Expression(component, this, scope, info.expression);

Expand Down
4 changes: 4 additions & 0 deletions src/compiler/compile/nodes/RawMustacheTag.ts
Expand Up @@ -2,4 +2,8 @@ import Tag from './shared/Tag';

export default class RawMustacheTag extends Tag {
type: 'RawMustacheTag'
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
this.not_static_content();
}
}
3 changes: 3 additions & 0 deletions src/compiler/compile/nodes/Slot.ts
Expand Up @@ -60,5 +60,8 @@ export default class Slot extends Element {
}

component.slots.set(this.slot_name, this);

this.cannot_use_innerhtml();
this.not_static_content();
}
}
9 changes: 9 additions & 0 deletions src/compiler/compile/nodes/shared/Node.ts
Expand Up @@ -15,6 +15,7 @@ export default class Node {
next?: INode;

can_use_innerhtml: boolean;
is_static_content: boolean;
var: string;
attributes: Attribute[];

Expand All @@ -33,6 +34,9 @@ export default class Node {
value: parent
}
});

this.can_use_innerhtml = true;
this.is_static_content = true;
}

cannot_use_innerhtml() {
Expand All @@ -42,6 +46,11 @@ export default class Node {
}
}

not_static_content() {
this.is_static_content = false;
if (this.parent) this.parent.not_static_content();
}

find_nearest(selector: RegExp) {
if (selector.test(this.type)) return this;
if (this.parent) return this.parent.find_nearest(selector);
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/compile/nodes/shared/Tag.ts
Expand Up @@ -8,11 +8,22 @@ export default class Tag extends Node {

constructor(component, parent, scope, info) {
super(component, parent, scope, info);
component.tags.push(this);
this.cannot_use_innerhtml();

this.expression = new Expression(component, this, scope, info.expression);

this.should_cache = (
info.expression.type !== 'Identifier' ||
(this.expression.dependencies.size && scope.names.has(info.expression.name))
);
}
is_dependencies_static() {
return this.expression.contextual_dependencies.size === 0 && this.expression.dynamic_dependencies().length === 0;
}
check_if_content_dynamic() {
if (!this.is_dependencies_static()) {
this.not_static_content();
}
}
}
3 changes: 0 additions & 3 deletions src/compiler/compile/render_dom/wrappers/AwaitBlock.ts
Expand Up @@ -133,9 +133,6 @@ export default class AwaitBlockWrapper extends Wrapper {
) {
super(renderer, block, parent, node);

this.cannot_use_innerhtml();
this.not_static_content();

block.add_dependencies(this.node.expression.dependencies);

let is_dynamic = false;
Expand Down
2 changes: 0 additions & 2 deletions src/compiler/compile/render_dom/wrappers/EachBlock.ts
Expand Up @@ -78,8 +78,6 @@ export default class EachBlockWrapper extends Wrapper {
next_sibling: Wrapper
) {
super(renderer, block, parent, node);
this.cannot_use_innerhtml();
this.not_static_content();

const { dependencies } = node.expression;
block.add_dependencies(dependencies);
Expand Down
3 changes: 0 additions & 3 deletions src/compiler/compile/render_dom/wrappers/Element/Attribute.ts
Expand Up @@ -34,9 +34,6 @@ export class BaseAttributeWrapper {
this.parent = parent;

if (node.dependencies.size > 0) {
parent.cannot_use_innerhtml();
parent.not_static_content();

block.add_dependencies(node.dependencies);
}
}
Expand Down

0 comments on commit 0f5db18

Please sign in to comment.