diff --git a/.changeset/old-dots-sin.md b/.changeset/old-dots-sin.md new file mode 100644 index 0000000000..f7e2933897 --- /dev/null +++ b/.changeset/old-dots-sin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: Switch `payload.out` to an array diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js index ac6c9891a7..ee0086de2e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js @@ -44,12 +44,12 @@ export function EachBlock(node, context) { ); if (node.fallback) { - const open = b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open)); + const open = b.stmt(b.call(b.member(b.id('$$payload.out'), b.id('push')), block_open)); const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback)); fallback.body.unshift( - b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE))) + b.stmt(b.call(b.member(b.id('$$payload.out'), b.id('push')), b.literal(BLOCK_OPEN_ELSE))) ); state.template.push( diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js index 508dcc0fdd..8c082f38d5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js @@ -17,10 +17,12 @@ export function IfBlock(node, context) { ? /** @type {BlockStatement} */ (context.visit(node.alternate)) : b.block([]); - consequent.body.unshift(b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open))); + consequent.body.unshift( + b.stmt(b.call(b.member(b.id('$$payload.out'), b.id('push')), block_open)) + ); alternate.body.unshift( - b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE))) + b.stmt(b.call(b.member(b.id('$$payload.out'), b.id('push')), b.literal(BLOCK_OPEN_ELSE))) ); context.state.template.push(b.if(test, consequent, alternate), block_close); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 8fcf8efa68..8a8633dd1a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -96,10 +96,10 @@ function is_statement(node) { /** * @param {Array} template * @param {Identifier} out - * @param {AssignmentOperator} operator + * @param {AssignmentOperator | 'push'} operator * @returns {Statement[]} */ -export function build_template(template, out = b.id('$$payload.out'), operator = '+=') { +export function build_template(template, out = b.id('$$payload.out'), operator = 'push') { /** @type {string[]} */ let strings = []; @@ -110,18 +110,32 @@ export function build_template(template, out = b.id('$$payload.out'), operator = const statements = []; const flush = () => { - statements.push( - b.stmt( - b.assignment( - operator, - out, - b.template( - strings.map((cooked, i) => b.quasi(cooked, i === strings.length - 1)), - expressions + if (operator === 'push') { + statements.push( + b.stmt( + b.call( + b.member(out, b.id('push')), + b.template( + strings.map((cooked, i) => b.quasi(cooked, i === strings.length - 1)), + expressions + ) ) ) - ) - ); + ); + } else { + statements.push( + b.stmt( + b.assignment( + operator, + out, + b.template( + strings.map((cooked, i) => b.quasi(cooked, i === strings.length - 1)), + expressions + ) + ) + ) + ); + } strings = []; expressions = []; }; diff --git a/packages/svelte/src/internal/server/blocks/snippet.js b/packages/svelte/src/internal/server/blocks/snippet.js index 9e96ae3430..bb82ca97d0 100644 --- a/packages/svelte/src/internal/server/blocks/snippet.js +++ b/packages/svelte/src/internal/server/blocks/snippet.js @@ -15,8 +15,10 @@ export function createRawSnippet(fn) { // @ts-expect-error the types are a lie return (/** @type {Payload} */ payload, /** @type {Params} */ ...args) => { var getters = /** @type {Getters} */ (args.map((value) => () => value)); - payload.out += fn(...getters) - .render() - .trim(); + payload.out.push( + fn(...getters) + .render() + .trim() + ); }; } diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 3c320f9698..2a0cb057a3 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -40,7 +40,7 @@ function print_error(payload, message) { // eslint-disable-next-line no-console console.error(message); - payload.head.out += ``; + payload.head.out.push(``); } export function reset_elements() { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 9942882d26..62ee22d6fc 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -33,23 +33,23 @@ const INVALID_ATTR_NAME_CHAR_REGEX = * @returns {void} */ export function element(payload, tag, attributes_fn = noop, children_fn = noop) { - payload.out += ''; + payload.out.push(''); if (tag) { - payload.out += `<${tag}`; + payload.out.push(`<${tag}`); attributes_fn(); - payload.out += `>`; + payload.out.push(`>`); if (!is_void(tag)) { children_fn(); if (!is_raw_text_element(tag)) { - payload.out += EMPTY_COMMENT; + payload.out.push(EMPTY_COMMENT); } - payload.out += ``; + payload.out.push(``); } } - payload.out += ''; + payload.out.push(''); } /** @@ -72,7 +72,7 @@ export function render(component, options = {}) { const prev_on_destroy = on_destroy; on_destroy = []; - payload.out += BLOCK_OPEN; + payload.out.push(BLOCK_OPEN); let reset_reset_element; @@ -97,20 +97,22 @@ export function render(component, options = {}) { reset_reset_element(); } - payload.out += BLOCK_CLOSE; + payload.out.push(BLOCK_CLOSE); for (const cleanup of on_destroy) cleanup(); on_destroy = prev_on_destroy; - let head = payload.head.out + payload.head.title; + let head = payload.head.out.join('') + payload.head.title; for (const { hash, code } of payload.css) { head += ``; } + const body = payload.out.join(''); + return { head, - html: payload.out, - body: payload.out + html: body, + body: body }; } finally { abort(); @@ -124,9 +126,9 @@ export function render(component, options = {}) { */ export function head(payload, fn) { const head_payload = payload.head; - head_payload.out += BLOCK_OPEN; + head_payload.out.push(BLOCK_OPEN); fn(head_payload); - head_payload.out += BLOCK_CLOSE; + head_payload.out.push(BLOCK_CLOSE); } /** @@ -141,21 +143,21 @@ export function css_props(payload, is_html, props, component, dynamic = false) { const styles = style_object_to_string(props); if (is_html) { - payload.out += ``; + payload.out.push(``); } else { - payload.out += ``; + payload.out.push(``); } if (dynamic) { - payload.out += ''; + payload.out.push(''); } component(); if (is_html) { - payload.out += ``; + payload.out.push(``); } else { - payload.out += ``; + payload.out.push(``); } } @@ -440,13 +442,13 @@ export function bind_props(props_parent, props_now) { */ function await_block(payload, promise, pending_fn, then_fn) { if (is_promise(promise)) { - payload.out += BLOCK_OPEN; + payload.out.push(BLOCK_OPEN); promise.then(null, noop); if (pending_fn !== null) { pending_fn(); } } else if (then_fn !== null) { - payload.out += BLOCK_OPEN_ELSE; + payload.out.push(BLOCK_OPEN_ELSE); then_fn(promise); } } @@ -493,7 +495,7 @@ export function once(get_value) { */ export function props_id(payload) { const uid = payload.uid(); - payload.out += ''; + payload.out.push(''); return uid; } @@ -562,10 +564,13 @@ export function valueless_option(payload, children) { children(); - var body = payload.out.slice(i); + var body = payload.out.slice(i).join(''); if (body.replace(//g, '') === payload.select_value) { // replace '>' with ' selected>' (closing tag will be added later) - payload.out = payload.out.slice(0, i - 1) + ' selected>' + body; + var last_item = payload.out[i - 1]; + payload.out[i - 1] = last_item.slice(0, -1) + ' selected>'; + // Remove the old items after position i and add the body as a single item + payload.out.splice(i, payload.out.length - i, body); } } diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index a31120ae16..195488e061 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -1,11 +1,12 @@ export class HeadPayload { /** @type {Set<{ hash: string; code: string }>} */ css = new Set(); - out = ''; + /** @type {string[]} */ + out = []; uid = () => ''; title = ''; - constructor(css = new Set(), out = '', title = '', uid = () => '') { + constructor(css = new Set(), /** @type {string[]} */ out = [], title = '', uid = () => '') { this.css = css; this.out = out; this.title = title; @@ -16,7 +17,8 @@ export class HeadPayload { export class Payload { /** @type {Set<{ hash: string; code: string }>} */ css = new Set(); - out = ''; + /** @type {string[]} */ + out = []; uid = () => ''; select_value = undefined; @@ -36,12 +38,12 @@ export class Payload { export function copy_payload({ out, css, head, uid }) { const payload = new Payload(); - payload.out = out; + payload.out = [...out]; payload.css = new Set(css); payload.uid = uid; payload.head = new HeadPayload(); - payload.head.out = head.out; + payload.head.out = [...head.out]; payload.head.css = new Set(head.css); payload.head.title = head.title; payload.head.uid = head.uid; @@ -56,7 +58,7 @@ export function copy_payload({ out, css, head, uid }) { * @returns {void} */ export function assign_payload(p1, p2) { - p1.out = p2.out; + p1.out = [...p2.out]; p1.css = p2.css; p1.head = p2.head; p1.uid = p2.uid; diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js index 4b6e32d58e..cc2628c852 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js @@ -8,7 +8,7 @@ export default function Await_block_scope($$payload) { counter.count += 1; } - $$payload.out += ` `; + $$payload.out.push(` `); $.await($$payload, promise, () => {}, (counter) => {}); - $$payload.out += ` ${$.escape(counter.count)}`; + $$payload.out.push(` ${$.escape(counter.count)}`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js index e2c0ee29a5..c0db7d2fd5 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js @@ -2,7 +2,7 @@ import * as $ from 'svelte/internal/server'; import TextInput from './Child.svelte'; function snippet($$payload) { - $$payload.out += `Something`; + $$payload.out.push(`Something`); } export default function Bind_component_snippet($$payload) { @@ -23,7 +23,7 @@ export default function Bind_component_snippet($$payload) { } }); - $$payload.out += ` value: ${$.escape(value)}`; + $$payload.out.push(` value: ${$.escape(value)}`); } do { diff --git a/packages/svelte/tests/snapshot/samples/delegated-locally-declared-shadowed/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/delegated-locally-declared-shadowed/_expected/server/index.svelte.js index e465af6f8b..ac3dfcd2cb 100644 --- a/packages/svelte/tests/snapshot/samples/delegated-locally-declared-shadowed/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/delegated-locally-declared-shadowed/_expected/server/index.svelte.js @@ -3,11 +3,11 @@ import * as $ from 'svelte/internal/server'; export default function Delegated_locally_declared_shadowed($$payload) { const each_array = $.ensure_array_like({ length: 1 }); - $$payload.out += ``; + $$payload.out.push(``); for (let index = 0, $$length = each_array.length; index < $$length; index++) { - $$payload.out += ``; + $$payload.out.push(``); } - $$payload.out += ``; + $$payload.out.push(``); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js index cf731d8187..9c837d4e1d 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js @@ -6,5 +6,5 @@ export default function Main($$payload) { let y = () => 'test'; - $$payload.out += ` `; + $$payload.out.push(` `); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js index 3431e36833..8fa0c5f28c 100644 --- a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js @@ -3,11 +3,11 @@ import * as $ from 'svelte/internal/server'; export default function Each_index_non_null($$payload) { const each_array = $.ensure_array_like(Array(10)); - $$payload.out += ``; + $$payload.out.push(``); for (let i = 0, $$length = each_array.length; i < $$length; i++) { - $$payload.out += `

index: ${$.escape(i)}

`; + $$payload.out.push(`

index: ${$.escape(i)}

`); } - $$payload.out += ``; + $$payload.out.push(``); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js index 4386c22ebe..6dbe8130da 100644 --- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js @@ -3,13 +3,13 @@ import * as $ from 'svelte/internal/server'; export default function Each_string_template($$payload) { const each_array = $.ensure_array_like(['foo', 'bar', 'baz']); - $$payload.out += ``; + $$payload.out.push(``); for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { let thing = each_array[$$index]; - $$payload.out += `${$.escape(thing)}, `; + $$payload.out.push(`${$.escape(thing)}, `); } - $$payload.out += ``; + $$payload.out.push(``); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js index 7d37abd97b..ce4f09ed1d 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js @@ -15,7 +15,7 @@ export default function Function_prop_no_getter($$payload) { onmouseenter: () => count = plusOne(count), children: ($$payload) => { - $$payload.out += `clicks: ${$.escape(count)}`; + $$payload.out.push(`clicks: ${$.escape(count)}`); }, $$slots: { default: true } diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js index dc49c0c213..b1a0a5f9e6 100644 --- a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js @@ -1,5 +1,5 @@ import * as $ from 'svelte/internal/server'; export default function Functional_templating($$payload) { - $$payload.out += `

hello

child element

another child element

`; + $$payload.out.push(`

hello

child element

another child element

`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js index 8766fb1300..30f6d6b74a 100644 --- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js @@ -1,5 +1,5 @@ import * as $ from 'svelte/internal/server'; export default function Hello_world($$payload) { - $$payload.out += `

hello world

`; + $$payload.out.push(`

hello world

`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js index 959e0a403e..ea1d12c83b 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js @@ -1,5 +1,5 @@ import * as $ from 'svelte/internal/server'; export default function Hmr($$payload) { - $$payload.out += `

hello world

`; + $$payload.out.push(`

hello world

`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js index 3b23befcd4..18e01b4f72 100644 --- a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js @@ -4,5 +4,5 @@ export default function Nullish_coallescence_omittance($$payload) { let name = 'world'; let count = 0; - $$payload.out += `

Hello, world!

123

Hello, world

`; + $$payload.out.push(`

Hello, world!

123

Hello, world

`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js index 9457378c0d..29b0d0d594 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js @@ -1,7 +1,7 @@ import * as $ from 'svelte/internal/server'; export default function Purity($$payload) { - $$payload.out += `

0

${$.escape(location.href)}

`; + $$payload.out.push(`

0

${$.escape(location.href)}

`); Child($$payload, { prop: encodeURIComponent('hello') }); - $$payload.out += ``; + $$payload.out.push(``); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js index 0532ec5aa9..bad475ec86 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js @@ -3,5 +3,5 @@ import * as $ from 'svelte/internal/server'; export default function Skip_static_subtree($$payload, $$props) { let { title, content } = $$props; - $$payload.out += `

${$.escape(title)}

we don't need to traverse these nodes

or

these

ones

${$.html(content)}

these

trailing

nodes

can

be

completely

ignored

`; + $$payload.out.push(`

${$.escape(title)}

we don't need to traverse these nodes

or

these

ones

${$.html(content)}

these

trailing

nodes

can

be

completely

ignored

`); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js index f814dd4f84..c2736b0f43 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js @@ -11,5 +11,5 @@ export default function State_proxy_literal($$payload) { tpl = ``; } - $$payload.out += ` `; + $$payload.out.push(` `); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js index 6f019647f5..f7dc586026 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js @@ -12,5 +12,5 @@ export default function Text_nodes_deriveds($$payload) { return count2; } - $$payload.out += `

${$.escape(text1())}${$.escape(text2())}

`; + $$payload.out.push(`

${$.escape(text1())}${$.escape(text2())}

`); } \ No newline at end of file