From b4cf3d0a71f4506cbb565708f429d264a3fc11e3 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Tue, 31 Mar 2026 08:48:51 +0200 Subject: [PATCH] chore: move option under experimental --- packages/svelte/src/compiler/index.js | 12 ++++++++++-- .../phases/2-analyze/visitors/Attribute.js | 2 +- .../phases/2-analyze/visitors/RegularElement.js | 2 +- .../3-transform/client/transform-client.js | 15 +++++++-------- .../client/transform-template/index.js | 2 +- .../client/visitors/RegularElement.js | 16 ++++++++++------ .../client/visitors/shared/fragment.js | 2 +- .../server/visitors/RegularElement.js | 12 +++++++++--- packages/svelte/src/compiler/types/index.d.ts | 8 ++++---- packages/svelte/src/compiler/validate-options.js | 5 ++--- packages/svelte/tests/custom-renderers/shared.ts | 7 +++++-- packages/svelte/types/index.d.ts | 16 ++++++++-------- 12 files changed, 59 insertions(+), 40 deletions(-) diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js index 1d822514b9..f1a15f2ebf 100644 --- a/packages/svelte/src/compiler/index.js +++ b/packages/svelte/src/compiler/index.js @@ -28,7 +28,11 @@ export function compile(source, options) { let parsed = _parse(source); - const { customElement: customElementOptions, ...parsed_options } = parsed.options || {}; + const { + customElement: customElementOptions, + customRenderer, + ...parsed_options + } = parsed.options || {}; /** @type {ValidatedCompileOptions} */ const combined_options = { @@ -36,7 +40,11 @@ export function compile(source, options) { ...parsed_options, customElementOptions, css: 'css' in parsed_options ? () => parsed_options.css ?? 'external' : validated.css, - runes: 'runes' in parsed_options ? () => parsed_options.runes : validated.runes + runes: 'runes' in parsed_options ? () => parsed_options.runes : validated.runes, + experimental: { + ...validated.experimental, + ...(customRenderer !== undefined ? { customRenderer } : {}) + } }; if (parsed.metadata.ts) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 838cbf0413..320760235f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -60,7 +60,7 @@ export function Attribute(node, context) { } // we can't delegate event handlers in a non dom environment - if (!context.state.options.customRenderer) { + if (!context.state.options.experimental.customRenderer) { node.metadata.delegated = parent?.type === 'RegularElement' && can_delegate_event(node.name.slice(2)); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index 448013cbb6..e447507fbe 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -112,7 +112,7 @@ export function RegularElement(node, context) { // We mark the subtree as dynamic so parent elements properly include the child init code if ( (is_customizable_select_element(node) || node.name === 'selectedcontent') && - !context.state.options.customRenderer + !context.state.options.experimental.customRenderer ) { // Mark the element's own fragment as dynamic so it's not treated as static node.fragment.metadata.dynamic = true; 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 162b979e4c..fb467403be 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 @@ -139,13 +139,11 @@ const visitors = { }; /** - * @param {string} custom_renderer_module + * @param {string | undefined} custom_renderer_module */ function custom_renderer_imports(custom_renderer_module) { - const imports = [ - b.import_all('$', 'svelte/internal/client'), - ] - if(custom_renderer_module){ + const imports = [b.import_all('$', 'svelte/internal/client')]; + if (custom_renderer_module) { imports.push(b.imports([['$renderer', '$renderer', true]], custom_renderer_module)); } return imports; @@ -165,7 +163,8 @@ export function client_component(analysis, options) { scopes: analysis.module.scopes, is_instance: false, hoisted: [ - ...custom_renderer_imports(options.customRenderer), ...analysis.instance_body.hoisted + ...custom_renderer_imports(options.experimental.customRenderer), + ...analysis.instance_body.hoisted ], node: /** @type {any} */ (null), // populated by the root node legacy_reactive_imports: [], @@ -407,7 +406,7 @@ export function client_component(analysis, options) { ); } - if (options.customRenderer) { + if (options.experimental.customRenderer) { component_block.body.unshift( b.var('$$pop_renderer', b.call('$.push_renderer', b.id('$renderer'))) ); @@ -586,7 +585,7 @@ export function client_component(analysis, options) { // disclose version attach the svelte version to `window` which is not guaranteed // to be a thing in custom renderers environments - if (options.discloseVersion && !options.customRenderer) { + if (options.discloseVersion && !options.experimental.customRenderer) { body.unshift(b.imports([], 'svelte/internal/disclose-version')); } 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 e34bfc0123..cae9c94f4d 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 @@ -36,7 +36,7 @@ function build_locations(nodes) { */ export function transform_template(state, namespace, flags = 0) { // custom renderers needs a tree to work because there's no template element we can use - const tree = state.options.fragments === 'tree' || !!state.options.customRenderer; + const tree = state.options.fragments === 'tree' || !!state.options.experimental.customRenderer; const expression = tree ? state.template.as_tree() : state.template.as_html(); 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 a649d87726..76152b28ed 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 @@ -41,7 +41,7 @@ export function RegularElement(node, context) { const is_html = context.state.metadata.namespace === 'html' && node.name !== 'svg' && - !context.state.options.customRenderer; + !context.state.options.experimental.customRenderer; const name = is_html ? node.name.toLowerCase() : node.name; context.state.template.push_element(name, node.start, is_html); @@ -51,7 +51,8 @@ export function RegularElement(node, context) { } // we never treat elements as custom element in custom renderers, since we don't want to apply special handling to them (e.g. class merging) - const is_custom_element = is_custom_element_node(node) && !context.state.options.customRenderer; + const is_custom_element = + is_custom_element_node(node) && !context.state.options.experimental.customRenderer; // cloneNode is faster, but it does not instantiate the underlying class of the // custom element until the template is connected to the dom, which would @@ -257,7 +258,7 @@ export function RegularElement(node, context) { if (name !== 'class' || value) { context.state.template.set_prop(attribute.name, value === true ? '' : value); } - } else if (name === 'autofocus' && !context.state.options.customRenderer) { + } else if (name === 'autofocus' && !context.state.options.experimental.customRenderer) { let { value } = build_attribute_value(attribute.value, context); context.state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); } else if (name === 'class') { @@ -279,7 +280,7 @@ export function RegularElement(node, context) { name, value, attributes, - !!context.state.options.customRenderer + !!context.state.options.experimental.customRenderer ); (has_state ? context.state.update : context.state.init).push(b.stmt(update)); @@ -356,7 +357,7 @@ export function RegularElement(node, context) { const empty_string = value.type === 'Literal' && value.value === ''; if (!empty_string) { - if (context.state.options.customRenderer) { + if (context.state.options.experimental.customRenderer) { // custom renderers need to use the method to invoke the renderer context.state.template.push_text([ { @@ -376,7 +377,10 @@ export function RegularElement(node, context) { ); } } - } else if (is_customizable_select_element(node) && !context.state.options.customRenderer) { + } else if ( + is_customizable_select_element(node) && + !context.state.options.experimental.customRenderer + ) { // For , or