Skip to content

Commit

Permalink
use textContent and innerHTML where appropriate (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Sep 18, 2017
1 parent 3b68d1f commit 66ae0d9
Show file tree
Hide file tree
Showing 12 changed files with 138 additions and 123 deletions.
54 changes: 41 additions & 13 deletions src/generators/dom/preprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ function createDebuggingComment(node: Node, generator: DomGenerator) {
return `${loc} ${source.slice(c, d)}`.replace(/\n/g, ' ');
}

function cannotUseInnerHTML(node: Node) {
while (node && node.canUseInnerHTML) {
node.canUseInnerHTML = false;
node = node.parent;
}
}

// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
Expand All @@ -65,6 +72,7 @@ const preprocessors = {
componentStack: Node[],
stripWhitespace: boolean
) => {
cannotUseInnerHTML(node);
node.var = block.getUniqueName('text');

const dependencies = block.findDependencies(node.expression);
Expand All @@ -80,6 +88,7 @@ const preprocessors = {
componentStack: Node[],
stripWhitespace: boolean
) => {
cannotUseInnerHTML(node);
node.var = block.getUniqueName('raw');

const dependencies = block.findDependencies(node.expression);
Expand Down Expand Up @@ -114,6 +123,8 @@ const preprocessors = {
stripWhitespace: boolean,
nextSibling: Node
) => {
cannotUseInnerHTML(node);

const blocks: Block[] = [];
let dynamic = false;
let hasIntros = false;
Expand Down Expand Up @@ -195,6 +206,7 @@ const preprocessors = {
stripWhitespace: boolean,
nextSibling: Node
) => {
cannotUseInnerHTML(node);
node.var = block.getUniqueName(`each_block`);

const dependencies = block.findDependencies(node.expression);
Expand Down Expand Up @@ -286,10 +298,16 @@ const preprocessors = {
stripWhitespace: boolean,
nextSibling: Node
) => {
if (node.name === 'slot') {
cannotUseInnerHTML(node);
}

node.attributes.forEach((attribute: Node) => {
if (attribute.type === 'Attribute' && attribute.value !== true) {
attribute.value.forEach((chunk: Node) => {
if (chunk.type !== 'Text') {
if (node.parent) cannotUseInnerHTML(node.parent);

const dependencies = block.findDependencies(chunk.expression);
block.addDependencies(dependencies);

Expand All @@ -307,20 +325,24 @@ const preprocessors = {
}
}
});
} else if (attribute.type === 'EventHandler' && attribute.expression) {
attribute.expression.arguments.forEach((arg: Node) => {
const dependencies = block.findDependencies(arg);
} else {
if (node.parent) cannotUseInnerHTML(node.parent);

if (attribute.type === 'EventHandler' && attribute.expression) {
attribute.expression.arguments.forEach((arg: Node) => {
const dependencies = block.findDependencies(arg);
block.addDependencies(dependencies);
});
} else if (attribute.type === 'Binding') {
const dependencies = block.findDependencies(attribute.value);
block.addDependencies(dependencies);
});
} else if (attribute.type === 'Binding') {
const dependencies = block.findDependencies(attribute.value);
block.addDependencies(dependencies);
} else if (attribute.type === 'Transition') {
if (attribute.intro)
generator.hasIntroTransitions = block.hasIntroMethod = true;
if (attribute.outro) {
generator.hasOutroTransitions = block.hasOutroMethod = true;
block.outros += 1;
} else if (attribute.type === 'Transition') {
if (attribute.intro)
generator.hasIntroTransitions = block.hasIntroMethod = true;
if (attribute.outro) {
generator.hasOutroTransitions = block.hasOutroMethod = true;
block.outros += 1;
}
}
}
});
Expand All @@ -336,6 +358,8 @@ const preprocessors = {
// so that if `foo.qux` changes, we know that we need to
// mark `bar` and `baz` as dirty too
if (node.name === 'select') {
cannotUseInnerHTML(node);

const value = node.attributes.find(
(attribute: Node) => attribute.name === 'value'
);
Expand All @@ -355,6 +379,8 @@ const preprocessors = {
generator.components.has(node.name) || node.name === ':Self';

if (isComponent) {
cannotUseInnerHTML(node);

node.var = block.getUniqueName(
(node.name === ':Self' ? generator.name : node.name).toLowerCase()
);
Expand All @@ -365,6 +391,7 @@ const preprocessors = {
} else {
const slot = getStaticAttributeValue(node, 'slot');
if (slot && isChildOfComponent(node, generator)) {
cannotUseInnerHTML(node);
node.slotted = true;
// TODO validate slots — no nesting, no dynamic names...
const component = componentStack[componentStack.length - 1];
Expand Down Expand Up @@ -438,6 +465,7 @@ function preprocessChildren(

cleaned.forEach((child: Node, i: number) => {
child.parent = node;
child.canUseInnerHTML = !generator.hydratable;

const preprocessor = preprocessors[child.type];
if (preprocessor) preprocessor(generator, block, state, child, inEachBlock, elementStack, componentStack, stripWhitespace, cleaned[i + 1] || nextSibling);
Expand Down
51 changes: 47 additions & 4 deletions src/generators/dom/visitors/Element/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import visitBinding from './Binding';
import visitRef from './Ref';
import * as namespaces from '../../../../utils/namespaces';
import getStaticAttributeValue from '../../../../utils/getStaticAttributeValue';
import isVoidElementName from '../../../../utils/isVoidElementName';
import addTransitions from './addTransitions';
import { DomGenerator } from '../../index';
import Block from '../../Block';
Expand Down Expand Up @@ -94,7 +95,6 @@ export default function visitElement(
}

// add CSS encapsulation attribute
// TODO add a helper for this, rather than repeating it
if (node._needsCssAttribute && !generator.customElement) {
generator.needsEncapsulateHelper = true;
block.builders.hydrate.addLine(
Expand Down Expand Up @@ -202,9 +202,21 @@ export default function visitElement(
node.initialUpdate = node.lateUpdate = statement;
}

node.children.forEach((child: Node) => {
visit(generator, block, childState, child, elementStack.concat(node), componentStack);
});
if (!childState.namespace && node.canUseInnerHTML && node.children.length > 0) {
if (node.children.length === 1 && node.children[0].type === 'Text') {
block.builders.create.addLine(
`${name}.textContent = ${JSON.stringify(node.children[0].data)};`
);
} else {
block.builders.create.addLine(
`${name}.innerHTML = ${JSON.stringify(node.children.map(toHTML).join(''))};`
);
}
} else {
node.children.forEach((child: Node) => {
visit(generator, block, childState, child, elementStack.concat(node), componentStack);
});
}

if (node.lateUpdate) {
block.builders.update.addLine(node.lateUpdate);
Expand All @@ -221,6 +233,29 @@ export default function visitElement(
block.builders.claim.addLine(
`${childState.parentNodes}.forEach(@detachNode);`
);

function toHTML(node: Node) {
if (node.type === 'Text') return node.data;

let open = `<${node.name}`;

if (node._needsCssAttribute) {
open += ` ${generator.stylesheet.id}`;
}

if (node._cssRefAttribute) {
open += ` svelte-ref-${node._cssRefAttribute}`;
}

node.attributes.forEach((attr: Node) => {
open += ` ${attr.name}${stringifyAttributeValue(attr.value)}`
});

if (isVoidElementName(node.name)) return open + '>';
if (node.children.length === 0) return open + '/>';

return `${open}>${node.children.map(toHTML).join('')}</${node.name}>`;
}
}

function getRenderStatement(
Expand Down Expand Up @@ -263,3 +298,11 @@ function quoteProp(name: string, legacy: boolean) {
if (/[^a-zA-Z_$0-9]/.test(name) || isLegacyPropName) return `"${name}"`;
return name;
}

function stringifyAttributeValue(value: Node | true) {
if (value === true) return '';
if (value.length === 0) return `=""`;

const data = value[0].data;
return `=${JSON.stringify(data)}`;
}
13 changes: 2 additions & 11 deletions test/js/samples/css-shadow-dom-keyframes/expected-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ function assign(target) {
return target;
}

function appendNode(node, target) {
target.appendChild(node);
}

function insertNode(node, target, anchor) {
target.insertBefore(node, anchor);
}
Expand All @@ -29,10 +25,6 @@ function createElement(name) {
return document.createElement(name);
}

function createText(data) {
return document.createTextNode(data);
}

function blankObject() {
return Object.create(null);
}
Expand Down Expand Up @@ -188,17 +180,16 @@ var proto = {
/* generated by Svelte vX.Y.Z */

function create_main_fragment(state, component) {
var div, text;
var div;

return {
create: function() {
div = createElement("div");
text = createText("fades in");
div.textContent = "fades in";
},

mount: function(target, anchor) {
insertNode(div, target, anchor);
appendNode(text, div);
},

update: noop,
Expand Down
7 changes: 3 additions & 4 deletions test/js/samples/css-shadow-dom-keyframes/expected.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
/* generated by Svelte vX.Y.Z */

import { appendNode, assign, createElement, createText, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";
import { assign, createElement, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";

function create_main_fragment(state, component) {
var div, text;
var div;

return {
create: function() {
div = createElement("div");
text = createText("fades in");
div.textContent = "fades in";
},

mount: function(target, anchor) {
insertNode(div, target, anchor);
appendNode(text, div);
},

update: noop,
Expand Down
13 changes: 2 additions & 11 deletions test/js/samples/event-handlers-custom/expected-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ function assign(target) {
return target;
}

function appendNode(node, target) {
target.appendChild(node);
}

function insertNode(node, target, anchor) {
target.insertBefore(node, anchor);
}
Expand All @@ -29,10 +25,6 @@ function createElement(name) {
return document.createElement(name);
}

function createText(data) {
return document.createTextNode(data);
}

function blankObject() {
return Object.create(null);
}
Expand Down Expand Up @@ -203,12 +195,12 @@ var template = (function() {
}());

function create_main_fragment(state, component) {
var button, foo_handler, text;
var button, foo_handler;

return {
create: function() {
button = createElement("button");
text = createText("foo");
button.textContent = "foo";
this.hydrate();
},

Expand All @@ -221,7 +213,6 @@ function create_main_fragment(state, component) {

mount: function(target, anchor) {
insertNode(button, target, anchor);
appendNode(text, button);
},

update: noop,
Expand Down
7 changes: 3 additions & 4 deletions test/js/samples/event-handlers-custom/expected.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* generated by Svelte vX.Y.Z */

import { appendNode, assign, createElement, createText, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";
import { assign, createElement, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";

var template = (function() {
return {
Expand All @@ -18,12 +18,12 @@ var template = (function() {
}());

function create_main_fragment(state, component) {
var button, foo_handler, text;
var button, foo_handler;

return {
create: function() {
button = createElement("button");
text = createText("foo");
button.textContent = "foo";
this.hydrate();
},

Expand All @@ -36,7 +36,6 @@ function create_main_fragment(state, component) {

mount: function(target, anchor) {
insertNode(button, target, anchor);
appendNode(text, button);
},

update: noop,
Expand Down
Loading

0 comments on commit 66ae0d9

Please sign in to comment.