diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 253ab107d8..ac7e80c299 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -6,6 +6,7 @@ import { is_void, sanitize } from '../../../../utils/names'; import FragmentWrapper from '../Fragment'; 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 { namespaces } from '../../../../utils/namespaces'; @@ -849,7 +850,7 @@ export default class ElementWrapper extends Wrapper { } } -function to_html(wrappers: Array, block: Block, literal: any, state: any) { +function to_html(wrappers: Array, block: Block, literal: any, state: any) { wrappers.forEach(wrapper => { if (wrapper.node.type === 'Text') { if ((wrapper as TextWrapper).use_space()) state.quasi.value.raw += ' '; @@ -867,6 +868,15 @@ function to_html(wrappers: Array, block: Block, li .replace(/\$/g, '\\$'); } + else if (wrapper.node.type === 'MustacheTag' || wrapper.node.type === 'RawMustacheTag' ) { + literal.quasis.push(state.quasi); + literal.expressions.push(wrapper.node.expression.manipulate(block)); + state.quasi = { + type: 'TemplateElement', + value: { raw: '' } + }; + } + else if (wrapper.node.name === 'noscript') { // do nothing } diff --git a/src/compiler/compile/render_dom/wrappers/MustacheTag.ts b/src/compiler/compile/render_dom/wrappers/MustacheTag.ts index 27364f5efb..b5ac6505f8 100644 --- a/src/compiler/compile/render_dom/wrappers/MustacheTag.ts +++ b/src/compiler/compile/render_dom/wrappers/MustacheTag.ts @@ -12,7 +12,6 @@ export default class MustacheTagWrapper extends Tag { constructor(renderer: Renderer, block: Block, parent: Wrapper, node: MustacheTag | RawMustacheTag) { super(renderer, block, parent, node); - this.cannot_use_innerhtml(); } render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { diff --git a/src/compiler/compile/render_dom/wrappers/shared/Tag.ts b/src/compiler/compile/render_dom/wrappers/shared/Tag.ts index 6738dd8c99..01cba3918a 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/Tag.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/Tag.ts @@ -11,11 +11,17 @@ export default class Tag extends Wrapper { constructor(renderer: Renderer, block: Block, parent: Wrapper, node: MustacheTag | RawMustacheTag) { super(renderer, block, parent, node); - this.cannot_use_innerhtml(); + if (!this.is_dependencies_static()) { + this.cannot_use_innerhtml(); + } block.add_dependencies(node.expression.dependencies); } + is_dependencies_static() { + return this.node.expression.contextual_dependencies.size === 0 && this.node.expression.dynamic_dependencies().length === 0; + } + rename_this_method( block: Block, update: ((value: Node) => (Node | Node[])) diff --git a/test/js/samples/hoisted-const/expected.js b/test/js/samples/hoisted-const/expected.js index af4d360d35..997b6635e6 100644 --- a/test/js/samples/hoisted-const/expected.js +++ b/test/js/samples/hoisted-const/expected.js @@ -1,28 +1,23 @@ import { SvelteComponent, - append, detach, element, init, insert, noop, - safe_not_equal, - text + safe_not_equal } from "svelte/internal"; function create_fragment(ctx) { let b; - let t_value = get_answer() + ""; - let t; return { c() { b = element("b"); - t = text(t_value); + b.innerHTML = `${get_answer()}`; }, m(target, anchor) { insert(target, b, anchor); - append(b, t); }, p: noop, i: noop, diff --git a/test/js/samples/hoisted-let/expected.js b/test/js/samples/hoisted-let/expected.js index 5392a5018c..4489e2f1af 100644 --- a/test/js/samples/hoisted-let/expected.js +++ b/test/js/samples/hoisted-let/expected.js @@ -1,28 +1,23 @@ import { SvelteComponent, - append, detach, element, init, insert, noop, - safe_not_equal, - text + safe_not_equal } from "svelte/internal"; function create_fragment(ctx) { let b; - let t_value = get_answer() + ""; - let t; return { c() { b = element("b"); - t = text(t_value); + b.innerHTML = `${get_answer()}`; }, m(target, anchor) { insert(target, b, anchor); - append(b, t); }, p: noop, i: noop, diff --git a/test/js/samples/non-mutable-reference/expected.js b/test/js/samples/non-mutable-reference/expected.js index 246850aaf4..c385ad9fe5 100644 --- a/test/js/samples/non-mutable-reference/expected.js +++ b/test/js/samples/non-mutable-reference/expected.js @@ -1,33 +1,23 @@ import { SvelteComponent, - append, detach, element, init, insert, noop, - safe_not_equal, - text + safe_not_equal } from "svelte/internal"; function create_fragment(ctx) { let h1; - let t0; - let t1; - let t2; return { c() { h1 = element("h1"); - t0 = text("Hello "); - t1 = text(name); - t2 = text("!"); + h1.innerHTML = `Hello ${name}!`; }, m(target, anchor) { insert(target, h1, anchor); - append(h1, t0); - append(h1, t1); - append(h1, t2); }, p: noop, i: noop, diff --git a/test/js/samples/unchanged-expression/expected.js b/test/js/samples/unchanged-expression/expected.js new file mode 100644 index 0000000000..ec16490f85 --- /dev/null +++ b/test/js/samples/unchanged-expression/expected.js @@ -0,0 +1,78 @@ +import { + SvelteComponent, + append, + detach, + element, + init, + insert, + noop, + safe_not_equal, + set_data, + space, + text +} from "svelte/internal"; + +function create_fragment(ctx) { + let div0; + let t7; + let div1; + let p3; + let t8; + let t9; + + return { + c() { + div0 = element("div"); + + div0.innerHTML = `

Hello world

+

Hello ${world1}

+

Hello ${world2}

`; + + t7 = space(); + div1 = element("div"); + p3 = element("p"); + t8 = text("Hello "); + t9 = text(ctx.world3); + }, + m(target, anchor) { + insert(target, div0, anchor); + insert(target, t7, anchor); + insert(target, div1, anchor); + append(div1, p3); + append(p3, t8); + append(p3, t9); + }, + p(changed, ctx) { + if (changed.world3) set_data(t9, ctx.world3); + }, + i: noop, + o: noop, + d(detaching) { + if (detaching) detach(div0); + if (detaching) detach(t7); + if (detaching) detach(div1); + } + }; +} + +let world1 = "world"; +let world2 = "world"; + +function instance($$self, $$props, $$invalidate) { + const world3 = "world"; + + function foo() { + $$invalidate("world3", world3 = "svelte"); + } + + return { world3 }; +} + +class Component extends SvelteComponent { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal, []); + } +} + +export default Component; \ No newline at end of file diff --git a/test/js/samples/unchanged-expression/input.svelte b/test/js/samples/unchanged-expression/input.svelte new file mode 100644 index 0000000000..057201358c --- /dev/null +++ b/test/js/samples/unchanged-expression/input.svelte @@ -0,0 +1,17 @@ + +
+

Hello world

+

Hello {world1}

+

Hello {world2}

+
+
+

Hello {world3}

+
diff --git a/test/runtime/index.js b/test/runtime/index.js index 397cfef172..5955b06c0b 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -166,7 +166,8 @@ describe("runtime", () => { mod, target, window, - raf + raf, + compileOptions })).then(() => { component.$destroy(); diff --git a/test/runtime/samples/lifecycle-render-order-for-children/_config.js b/test/runtime/samples/lifecycle-render-order-for-children/_config.js index a1d35d7aa8..033b593aea 100644 --- a/test/runtime/samples/lifecycle-render-order-for-children/_config.js +++ b/test/runtime/samples/lifecycle-render-order-for-children/_config.js @@ -3,26 +3,47 @@ import order from './order.js'; export default { skip_if_ssr: true, - test({ assert, component, target }) { - assert.deepEqual(order, [ - '0: beforeUpdate', - '0: render', - '1: beforeUpdate', - '1: render', - '2: beforeUpdate', - '2: render', - '3: beforeUpdate', - '3: render', - '1: onMount', - '1: afterUpdate', - '2: onMount', - '2: afterUpdate', - '3: onMount', - '3: afterUpdate', - '0: onMount', - '0: afterUpdate' - ]); + test({ assert, component, target, compileOptions }) { + if (compileOptions.hydratable) { + assert.deepEqual(order, [ + '0: beforeUpdate', + '0: render', + '1: beforeUpdate', + '1: render', + '2: beforeUpdate', + '2: render', + '3: beforeUpdate', + '3: render', + '1: onMount', + '1: afterUpdate', + '2: onMount', + '2: afterUpdate', + '3: onMount', + '3: afterUpdate', + '0: onMount', + '0: afterUpdate', + ]); + } else { + assert.deepEqual(order, [ + '0: beforeUpdate', + '0: render', + '1: beforeUpdate', + '2: beforeUpdate', + '3: beforeUpdate', + '1: render', + '2: render', + '3: render', + '1: onMount', + '1: afterUpdate', + '2: onMount', + '2: afterUpdate', + '3: onMount', + '3: afterUpdate', + '0: onMount', + '0: afterUpdate', + ]); + } order.length = 0; - } + }, };