diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index bdf70408f1..0481621ce1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -167,7 +167,7 @@ export function client_component(analysis, options) { in_constructor: false, instance_level_snippets: [], module_level_snippets: [], - prevent_template_cloning: options.preventTemplateCloning, + is_functional_template_mode: options.templatingMode === 'functional', // these are set inside the `Fragment` visitor, and cannot be used until then init: /** @type {any} */ (null), diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js index 98b830a907..f28e2cf78e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js @@ -27,7 +27,7 @@ function get_template_function(namespace, state) { : contains_script_tag ? '$.template_with_script' : '$.template' - ).concat(state.prevent_template_cloning ? '_fn' : ''); + ).concat(state.is_functional_template_mode ? '_fn' : ''); } /** @@ -75,7 +75,7 @@ export function transform_template(state, context, namespace, template_name, fla /** @type {Expression[]} */ const args = [ - state.prevent_template_cloning + state.is_functional_template_mode ? template_to_functions(state.template, namespace) : b.template([b.quasi(template_to_string(state.template), true)], []) ]; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index a73e0e0e83..fb20ebe79f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -94,7 +94,7 @@ export interface ComponentClientTransformState extends ClientTransformState { }; }; readonly preserve_whitespace: boolean; - readonly prevent_template_cloning?: boolean; + readonly is_functional_template_mode?: boolean; /** The anchor node for the current context */ readonly node: Identifier; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 98f8b0c2e9..12abc8e9e0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -37,7 +37,7 @@ export function Fragment(node, context) { context.state, context.state.preserve_whitespace, context.state.options.preserveComments, - context.state.prevent_template_cloning + context.state.is_functional_template_mode ); if (hoisted.length === 0 && trimmed.length === 0) { @@ -133,7 +133,7 @@ export function Fragment(node, context) { ...context, state }, - context.state.prevent_template_cloning + context.state.is_functional_template_mode ); body.push(b.var(id, b.call('$.text'))); @@ -146,7 +146,7 @@ export function Fragment(node, context) { () => b.id('$$anchor'), false, { ...context, state }, - context.state.prevent_template_cloning + context.state.is_functional_template_mode ); } else { /** @type {(is_text: boolean) => Expression} */ @@ -157,7 +157,7 @@ export function Fragment(node, context) { expression, false, { ...context, state }, - context.state.prevent_template_cloning + context.state.is_functional_template_mode ); let flags = TEMPLATE_FRAGMENT; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index fb2bb1da20..fd8f27bc4a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -124,7 +124,7 @@ export function RegularElement(node, context) { kind: 'set_prop', args: [ 'is', - context.state.prevent_template_cloning + context.state.is_functional_template_mode ? value.value : escape_html(value.value, true) ] @@ -312,7 +312,7 @@ export function RegularElement(node, context) { : [ value === true ? '' - : context.state.prevent_template_cloning + : context.state.is_functional_template_mode ? value : escape_html(value, true) ] @@ -386,7 +386,7 @@ export function RegularElement(node, context) { state, node.name === 'script' || state.preserve_whitespace, state.options.preserveComments, - state.prevent_template_cloning + state.is_functional_template_mode ); /** @type {typeof state} */ @@ -438,7 +438,7 @@ export function RegularElement(node, context) { ...context, state: child_state }, - context.state.prevent_template_cloning + context.state.is_functional_template_mode ); if (needs_reset) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 6ec6b5322c..fd6f6853d0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -15,14 +15,14 @@ import { build_template_chunk } from './utils.js'; * @param {(is_text: boolean) => Expression} initial * @param {boolean} is_element * @param {ComponentContext} context - * @param {boolean} [prevent_template_cloning] + * @param {boolean} [is_functional_template_mode] */ export function process_children( nodes, initial, is_element, { visit, state }, - prevent_template_cloning + is_functional_template_mode ) { const within_bound_contenteditable = state.metadata.bound_contenteditable; let prev = initial; @@ -73,7 +73,9 @@ export function process_children( skipped += 1; state.template.push({ kind: 'create_text', - args: [sequence.map((node) => (prevent_template_cloning ? node.data : node.raw)).join('')] + args: [ + sequence.map((node) => (is_functional_template_mode ? node.data : node.raw)).join('') + ] }); return; } diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index e1ef6ef4c9..93e4c34479 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -141,7 +141,7 @@ function sort_const_tags(nodes, state) { * @param {TransformState & { options: ValidatedCompileOptions }} state * @param {boolean} preserve_whitespace * @param {boolean} preserve_comments - * @param {boolean} [prevent_template_cloning] + * @param {boolean} [is_functional_template_mode] */ export function clean_nodes( parent, @@ -154,7 +154,7 @@ export function clean_nodes( // rather than from `ClientTransformState` and `ServerTransformState` preserve_whitespace, preserve_comments, - prevent_template_cloning + is_functional_template_mode ) { if (!state.analysis.runes) { nodes = sort_const_tags(nodes, state); @@ -276,13 +276,13 @@ export function clean_nodes( // initial newline inside a `
` is disregarded, if not followed by another newline
 	if (
 		parent.type === 'RegularElement' &&
-		(parent.name === 'pre' || (prevent_template_cloning && parent.name === 'textarea')) &&
+		(parent.name === 'pre' || (is_functional_template_mode && parent.name === 'textarea')) &&
 		first?.type === 'Text'
 	) {
 		const text = first.data.replace(regex_starts_with_newline, '');
 		if (text !== first.data) {
 			const tmp = text.replace(regex_starts_with_newline, '');
-			if (text === tmp || prevent_template_cloning) {
+			if (text === tmp || is_functional_template_mode) {
 				first.data = text;
 				first.raw = first.raw.replace(regex_starts_with_newline, '');
 				if (first.data === '') {
diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts
index 4161c71206..4aa244ad51 100644
--- a/packages/svelte/src/compiler/types/index.d.ts
+++ b/packages/svelte/src/compiler/types/index.d.ts
@@ -114,11 +114,11 @@ export interface CompileOptions extends ModuleCompileOptions {
 	 */
 	preserveWhitespace?: boolean;
 	/**
-	 *  If `true`, the template will get compiled to a series of `document.createElement` calls instead of using `template.innerHTML`.
+	 *  If `functional`, the template will get compiled to a series of `document.createElement` calls, if `string` it will render the template tp a string and use `template.innerHTML`.
 	 *
-	 * @default false
+	 * @default 'string'
 	 */
-	preventTemplateCloning?: boolean;
+	templatingMode?: 'string' | 'functional';
 	/**
 	 * Set to `true` to force the compiler into runes mode, even if there are no indications of runes usage.
 	 * Set to `false` to force the compiler into ignoring runes, even if there are indications of runes usage.
diff --git a/packages/svelte/src/compiler/validate-options.js b/packages/svelte/src/compiler/validate-options.js
index 5e6eaf5ef6..1d67951fd8 100644
--- a/packages/svelte/src/compiler/validate-options.js
+++ b/packages/svelte/src/compiler/validate-options.js
@@ -110,7 +110,7 @@ export const validate_component_options =
 
 			preserveComments: boolean(false),
 
-			preventTemplateCloning: boolean(false),
+			templatingMode: list(['string', 'functional']),
 
 			preserveWhitespace: boolean(false),
 
diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js
index 164d855e89..684bde3875 100644
--- a/packages/svelte/tests/helpers.js
+++ b/packages/svelte/tests/helpers.js
@@ -80,7 +80,7 @@ export async function compile_directory(
 			filename: path.join(cwd, file),
 			...compileOptions,
 			generate,
-			preventTemplateCloning: templating_mode === 'functional'
+			templatingMode: templating_mode
 		};
 
 		if (file.endsWith('.js')) {
diff --git a/packages/svelte/tests/runtime-browser/test.ts b/packages/svelte/tests/runtime-browser/test.ts
index 0f7b259715..02823ad87b 100644
--- a/packages/svelte/tests/runtime-browser/test.ts
+++ b/packages/svelte/tests/runtime-browser/test.ts
@@ -106,7 +106,7 @@ async function run_test(
 							immutable: config.immutable,
 							customElement: test_dir.includes('custom-elements-samples'),
 							accessors: 'accessors' in config ? config.accessors : true,
-							preventTemplateCloning: templating_mode === 'functional'
+							templatingMode: templating_mode
 						});
 
 						write(
@@ -171,7 +171,7 @@ async function run_test(
 								immutable: config.immutable,
 								customElement: test_dir.includes('custom-elements-samples'),
 								accessors: 'accessors' in config ? config.accessors : true,
-								preventTemplateCloning: templating_mode === 'functional'
+								templatingMode: templating_mode
 							});
 
 							return {
diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts
index 8acac668f9..4630a4924c 100644
--- a/packages/svelte/tests/runtime-legacy/shared.ts
+++ b/packages/svelte/tests/runtime-legacy/shared.ts
@@ -168,7 +168,7 @@ async function common_setup(
 		immutable: config.immutable,
 		accessors: 'accessors' in config ? config.accessors : true,
 		runes,
-		preventTemplateCloning: templating_mode === 'functional'
+		templatingMode: templating_mode
 	};
 
 	// load_compiled can be used for debugging a test. It means the compiler will not run on the input
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index ef0179f3f1..19bb6e9279 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -845,11 +845,11 @@ declare module 'svelte/compiler' {
 		 */
 		preserveWhitespace?: boolean;
 		/**
-		 *  If `true`, the template will get compiled to a series of `document.createElement` calls instead of using `template.innerHTML`.
+		 *  If `functional`, the template will get compiled to a series of `document.createElement` calls, if `string` it will render the template tp a string and use `template.innerHTML`.
 		 *
-		 * @default false
+		 * @default 'string'
 		 */
-		preventTemplateCloning?: boolean;
+		templatingMode?: 'string' | 'functional';
 		/**
 		 * Set to `true` to force the compiler into runes mode, even if there are no indications of runes usage.
 		 * Set to `false` to force the compiler into ignoring runes, even if there are indications of runes usage.
@@ -2561,11 +2561,11 @@ declare module 'svelte/types/compiler/interfaces' {
 		 */
 		preserveWhitespace?: boolean;
 		/**
-		 *  If `true`, the template will get compiled to a series of `document.createElement` calls instead of using `template.innerHTML`.
+		 *  If `functional`, the template will get compiled to a series of `document.createElement` calls, if `string` it will render the template tp a string and use `template.innerHTML`.
 		 *
-		 * @default false
+		 * @default 'string'
 		 */
-		preventTemplateCloning?: boolean;
+		templatingMode?: 'string' | 'functional';
 		/**
 		 * Set to `true` to force the compiler into runes mode, even if there are no indications of runes usage.
 		 * Set to `false` to force the compiler into ignoring runes, even if there are indications of runes usage.