diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index 35d3f87a25..8e229ae298 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -110,6 +110,8 @@ export default class Block { const dupes = new Set(); this.wrappers.forEach(wrapper => { + if (wrapper.parent && wrapper.parent.canUseInnerHTML) return; + if (seen.has(wrapper.var)) { dupes.add(wrapper.var); } @@ -121,7 +123,7 @@ export default class Block { this.wrappers.forEach(wrapper => { if (dupes.has(wrapper.var)) { - const i = counts.get(wrapper.var); + const i = counts.get(wrapper.var) || 0; wrapper.var = this.getUniqueName(wrapper.var + i); counts.set(wrapper.var, i + 1); } else { diff --git a/src/compile/render-dom/wrappers/Element/Attribute.ts b/src/compile/render-dom/wrappers/Element/Attribute.ts index 4f3b0eb144..1a2549ce4e 100644 --- a/src/compile/render-dom/wrappers/Element/Attribute.ts +++ b/src/compile/render-dom/wrappers/Element/Attribute.ts @@ -10,21 +10,25 @@ export default class AttributeWrapper { constructor(node: Attribute, parent: ElementWrapper) { this.node = node; this.parent = parent; + + if (node.dependencies.size > 0) { + parent.cannotUseInnerHTML(); + } } render(block: Block) { const element = this.parent; const name = fixAttributeCasing(this.node.name); - let metadata = element.namespace ? null : attributeLookup[name]; + let metadata = element.node.namespace ? null : attributeLookup[name]; if (metadata && metadata.appliesTo && !~metadata.appliesTo.indexOf(element.node.name)) metadata = null; const isIndirectlyBoundValue = name === 'value' && - (element.name === 'option' || // TODO check it's actually bound - (element.name === 'input' && - element.bindings.find( + (element.node.name === 'option' || // TODO check it's actually bound + (element.node.name === 'input' && + element.node.bindings.find( (binding: Binding) => /checked|group/.test(binding.name) ))); @@ -36,7 +40,7 @@ export default class AttributeWrapper { // xlink is a special case... we could maybe extend this to generic // namespaced attributes but I'm not sure that's applicable in // HTML5? - const method = /-/.test(element.name) + const method = /-/.test(element.node.name) ? '@setCustomElementData' : name.slice(0, 6) === 'xlink:' ? '@setXlinkAttribute' @@ -44,7 +48,7 @@ export default class AttributeWrapper { const isLegacyInputType = element.renderer.component.options.legacy && name === 'type' && this.parent.name === 'input'; - const isDataSet = /^data-/.test(name) && !element.renderer.component.options.legacy && !element.namespace; + const isDataSet = /^data-/.test(name) && !element.renderer.component.options.legacy && !element.node.namespace; const camelCaseName = isDataSet ? name.replace('data-', '').replace(/(-\w)/g, function (m) { return m[1].toUpperCase(); }) : name; @@ -75,7 +79,7 @@ export default class AttributeWrapper { } const isSelectValueAttribute = - name === 'value' && element.name === 'select'; + name === 'value' && element.node.name === 'select'; const shouldCache = this.shouldCache || isSelectValueAttribute; @@ -183,6 +187,19 @@ export default class AttributeWrapper { if (this.node.isDynamic) block.builders.update.addLine(updateValue); } } + + stringify() { + const value = this.node.chunks; + + if (value === true) return ''; + if (value.length === 0) return `=""`; + + return `="${value.map(chunk => { + return chunk.type === 'Text' + ? chunk.data.replace(/"/g, '\\"') + : `\${${chunk.snippet}}` + })}"`; + } } // source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes diff --git a/src/compile/render-dom/wrappers/Element/StyleAttribute.ts b/src/compile/render-dom/wrappers/Element/StyleAttribute.ts index 8f22df279a..89bdd41c82 100644 --- a/src/compile/render-dom/wrappers/Element/StyleAttribute.ts +++ b/src/compile/render-dom/wrappers/Element/StyleAttribute.ts @@ -14,10 +14,6 @@ export default class StyleAttributeWrapper extends AttributeWrapper { node: Attribute; parent: ElementWrapper; - constructor(node: Attribute, parent: ElementWrapper) { - super(node, parent); - } - render(block: Block) { const styleProps = optimizeStyle(this.node.chunks); if (!styleProps) return super.render(block); diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 331efaba92..4e2f2211ed 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -7,7 +7,7 @@ import { CompileOptions } from '../../../../interfaces'; import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../../utils/quoteIfNecessary'; import isVoidElementName from '../../../../utils/isVoidElementName'; import FragmentWrapper from '../Fragment'; -import { stringify, escapeHTML } from '../../../../utils/stringify'; +import { stringify, escapeHTML, escape } from '../../../../utils/stringify'; import TextWrapper from '../Text'; import fixAttributeCasing from '../../../../utils/fixAttributeCasing'; import deindent from '../../../../utils/deindent'; @@ -108,7 +108,7 @@ export default class ElementWrapper extends Wrapper { ); } else { block.builders.create.addLine( - `${node}.innerHTML = ${stringify(this.fragment.nodes.map(toHTML).join(''))};` + `${node}.innerHTML = \`${escape(this.fragment.nodes.map(toHTML).join(''))}\`;` ); } } else { @@ -193,8 +193,8 @@ export default class ElementWrapper extends Wrapper { let open = `<${wrapper.node.name}`; - (wrapper).node.attributes.forEach((attr: Node) => { - open += ` ${fixAttributeCasing(attr.name)}${stringifyAttributeValue(attr.chunks)}` + (wrapper).attributes.forEach((attr: AttributeWrapper) => { + open += ` ${fixAttributeCasing(attr.node.name)}${attr.stringify()}` }); if (isVoidElementName(wrapper.node.name)) return open + '>'; @@ -748,12 +748,4 @@ export default class ElementWrapper extends Wrapper { ); } } -} - -function stringifyAttributeValue(value: Node[] | true) { - if (value === true) return ''; - if (value.length === 0) return `=""`; - - const data = value[0].data; - return `=${JSON.stringify(data)}`; } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/Text.ts b/src/compile/render-dom/wrappers/Text.ts index 726f5cd74d..dbb9096b71 100644 --- a/src/compile/render-dom/wrappers/Text.ts +++ b/src/compile/render-dom/wrappers/Text.ts @@ -32,6 +32,7 @@ function shouldSkip(node: Text) { export default class TextWrapper extends Wrapper { node: Text; + skip: boolean; var: string; constructor( @@ -41,10 +42,14 @@ export default class TextWrapper extends Wrapper { node: Text ) { super(renderer, block, parent, node); - this.var = 'text'; + + this.skip = shouldSkip(this.node); + this.var = this.skip ? null : 'text'; } render(block: Block, parentNode: string, parentNodes: string) { + if (this.skip) return; + block.addElement( this.var, `@createText(${stringify(this.node.data)})`, diff --git a/test/runtime/index.js b/test/runtime/index.js index c59aa72d54..559a57c33c 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -25,7 +25,7 @@ function getName(filename) { return base[0].toUpperCase() + base.slice(1); } -describe("runtime", () => { +describe.only("runtime", () => { before(() => { svelte = loadSvelte(false); svelte$ = loadSvelte(true);