diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c27984e13e..db5354f6ae 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -71,6 +71,9 @@ "./internal/flags/tracing": { "default": "./src/internal/flags/tracing.js" }, + "./internal/flags/custom-renderer": { + "default": "./src/internal/flags/custom-renderer.js" + }, "./internal/server": { "default": "./src/internal/server/index.js" }, diff --git a/packages/svelte/scripts/check-treeshakeability.js b/packages/svelte/scripts/check-treeshakeability.js index d66b4c062a..47c26ed7a6 100644 --- a/packages/svelte/scripts/check-treeshakeability.js +++ b/packages/svelte/scripts/check-treeshakeability.js @@ -59,6 +59,7 @@ for (const key in pkg.exports) { if (key === './internal/disclose-version') continue; if (key === './internal/flags/legacy') continue; if (key === './internal/flags/tracing') continue; + if (key === './internal/flags/custom-renderer') continue; if (key === './internal/init-operations') continue; for (const type of ['browser', 'default']) { @@ -149,6 +150,7 @@ function check_bundle(case_name, ...strings) { check_bundle('Hydration code', 'hydrate_node', 'hydrate_next'); check_bundle('Legacy code', 'component_context.l'); check_bundle('$inspect.trace', `'CreatedAt'`); +check_bundle('Custom renderer code', 'push_renderer', 'insertForeign', 'renderer_missing_foreign'); if (failed) { // eslint-disable-next-line no-console 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 57de386178..6e4ea8e546 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 @@ -555,6 +555,10 @@ export function client_component(analysis, options) { if (options.discloseVersion) { body.unshift(b.imports([], 'svelte/internal/disclose-version')); } + // this needs to be !== undefined because we want to enable the flag even if the custom renderer is null (which is a valid value meaning "no renderer") + if (custom_renderer !== undefined) { + body.unshift(b.imports([], 'svelte/internal/flags/custom-renderer')); + } if (custom_renderer) { body.unshift(b.imports([['$renderer', '$renderer', true]], custom_renderer)); diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 7691897e3d..97ee00e433 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -43,6 +43,7 @@ import { import { defer_effect } from '../../reactivity/utils.js'; import { set_signal_status } from '../../reactivity/status.js'; import { push_renderer } from '../../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../../flags/index.js'; /** * @typedef {{ @@ -275,7 +276,9 @@ export class Boundary { this.#pending_effect = branch(() => pending(this.#anchor)); queue_micro_task(() => { - var pop_renderer = push_renderer(this.#effect.r, this.#effect.pr); + var pop_renderer = custom_renderers_flag + ? push_renderer(this.#effect.r, this.#effect.pr) + : undefined; try { var fragment = (this.#offscreen_fragment = create_fragment()); @@ -389,7 +392,9 @@ export class Boundary { set_active_reaction(this.#effect); set_component_context(this.#effect.ctx); - var pop_renderer = push_renderer(this.#effect.r, this.#effect.pr); + var pop_renderer = custom_renderers_flag + ? push_renderer(this.#effect.r, this.#effect.pr) + : undefined; try { Batch.ensure(); @@ -430,7 +435,9 @@ export class Boundary { } if (this.#offscreen_fragment) { - var pop_renderer = push_renderer(this.#effect.r, this.#effect.pr); + var pop_renderer = custom_renderers_flag + ? push_renderer(this.#effect.r, this.#effect.pr) + : undefined; try { insert_before(this.#anchor, this.#offscreen_fragment); this.#offscreen_fragment = null; diff --git a/packages/svelte/src/internal/client/dom/blocks/branches.js b/packages/svelte/src/internal/client/dom/blocks/branches.js index a82a78368c..389ce89d5f 100644 --- a/packages/svelte/src/internal/client/dom/blocks/branches.js +++ b/packages/svelte/src/internal/client/dom/blocks/branches.js @@ -24,9 +24,9 @@ import { push_renderer, current_renderer, parent_renderer, - set_parent_renderer, - set_renderer + set_parent_renderer } from '../../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../../flags/index.js'; /** * @typedef {{ effect: Effect, fragment: DocumentFragment }} Branch @@ -108,14 +108,18 @@ export class BranchManager { #create_branch(fn) { // we push current renderer twice because branches will always // append to the current renderer - var pop_renderer = push_renderer(this.#renderer, this.#renderer); + var pop_renderer = custom_renderers_flag + ? push_renderer(this.#renderer, this.#renderer) + : undefined; try { return branch(fn); } finally { pop_renderer?.(); // we restore the parent_renderer so that an append after a // branch will append to the correct renderer - set_parent_renderer(this.#parent_renderer); + if (custom_renderers_flag) { + set_parent_renderer(this.#parent_renderer); + } } } @@ -126,7 +130,9 @@ export class BranchManager { // if this batch was made obsolete, bail if (!this.#batches.has(batch)) return; - var pop_renderer = push_renderer(this.#renderer, this.#parent_renderer); + var pop_renderer = custom_renderers_flag + ? push_renderer(this.#renderer, this.#parent_renderer) + : undefined; try { var key = /** @type {Key} */ (this.#batches.get(batch)); diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 69fe2804e9..a0e98bc225 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -56,6 +56,7 @@ import { parent_renderer, set_parent_renderer } from '../../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../../flags/index.js'; // When making substantive changes to this file, validate them with the each block stress test: // https://svelte.dev/playground/1972b2cf46564476ad8c8c6405b23b7b @@ -276,7 +277,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f return; } - var pop_renderer = push_renderer(renderer, parent); + var pop_renderer = custom_renderers_flag ? push_renderer(renderer, parent) : undefined; try { state.pending.delete(batch); @@ -315,7 +316,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f // we push current renderer twice because branches will always // append to the current renderer - var pop_renderer = push_renderer(renderer, renderer); + var pop_renderer = custom_renderers_flag ? push_renderer(renderer, renderer) : undefined; var effect = block(() => { array = /** @type {V[]} */ (get(each_array)); @@ -453,7 +454,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f pop_renderer?.(); // we restore the parent_renderer so that an append after a // branch will append to the correct renderer - set_parent_renderer(parent); + if (custom_renderers_flag) { + set_parent_renderer(parent); + } /** @type {EachState} */ var state = { effect, flags, items, pending, outrogroups: null, fallback }; diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index 401d7cfc9c..d6270e0f66 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -18,6 +18,7 @@ import { prevent_snippet_stringification } from '../../../shared/validate.js'; import { has_own_property } from '../../../shared/utils.js'; import { BranchManager } from './branches.js'; import { current_renderer, push_renderer } from '../../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../../flags/index.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn @@ -40,16 +41,18 @@ export function snippet(node, get_snippet, ...args) { snippet, snippet && ((anchor) => { - var renderer = /** @type {any} */ (snippet).__renderer; - var has_renderer = has_own_property.call(/** @type {any} */ (snippet), '__renderer'); - - if (has_renderer) { - var pop_renderer = push_renderer(renderer, renderer); - - try { - return snippet(anchor, ...args); - } finally { - pop_renderer(); + if (custom_renderers_flag) { + var renderer = /** @type {any} */ (snippet).__renderer; + var has_renderer = has_own_property.call(/** @type {any} */ (snippet), '__renderer'); + + if (has_renderer) { + var pop_renderer = push_renderer(renderer, renderer); + + try { + return snippet(anchor, ...args); + } finally { + pop_renderer(); + } } } diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index fca13e208e..427cd53181 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -16,6 +16,7 @@ import { TEMPLATE_USE_SVG } from '../../../constants.js'; import { current_renderer, parent_renderer } from '../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../flags/index.js'; import { active_effect } from '../runtime.js'; import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from './hydration.js'; import { @@ -75,6 +76,7 @@ export function assign_nodes(start, end) { */ function should_segment_nodes(effect) { return ( + custom_renderers_flag && (current_renderer !== effect.r || parent_renderer !== effect.pr) && (current_renderer?.foreign != null || parent_renderer?.foreign != null) ); diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index 846b991312..175878ec27 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -14,6 +14,7 @@ import { set_parent_renderer, set_renderer } from '../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../flags/index.js'; import { invoke_error_boundary } from '../error-handling.js'; import { active_effect, @@ -136,8 +137,8 @@ export function capture() { var previous_reaction = active_reaction; var previous_component_context = component_context; var previous_batch = /** @type {Batch} */ (current_batch); - var previous_renderer = current_renderer; - var previous_parent_renderer = parent_renderer; + var previous_renderer = custom_renderers_flag ? current_renderer : null; + var previous_parent_renderer = custom_renderers_flag ? parent_renderer : null; if (DEV) { var previous_dev_stack = dev_stack; @@ -148,8 +149,10 @@ export function capture() { set_active_reaction(previous_reaction); set_component_context(previous_component_context); - set_renderer(previous_renderer); - set_parent_renderer(previous_parent_renderer); + if (custom_renderers_flag) { + set_renderer(previous_renderer); + set_parent_renderer(previous_parent_renderer); + } if (activate_batch && (previous_effect.f & DESTROYED) === 0) { // TODO we only need optional chaining here because `{#await ...}` blocks @@ -303,8 +306,10 @@ export function unset_context(deactivate_batch = true) { set_active_effect(null); set_active_reaction(null); set_component_context(null); - set_renderer(null); - set_parent_renderer(null); + if (custom_renderers_flag) { + set_renderer(null); + set_parent_renderer(null); + } if (deactivate_batch) current_batch?.deactivate(); if (DEV) { diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index d98589cd1c..fc2d06e19e 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -48,6 +48,7 @@ import { flatten } from './async.js'; import { without_reactive_context } from '../dom/elements/bindings/shared.js'; import { set_signal_status } from './status.js'; import { push_renderer, current_renderer, parent_renderer } from '../custom-renderer/state.js'; +import { custom_renderers_flag } from '../../flags/index.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune @@ -521,7 +522,7 @@ export function destroy_block_effect_children(signal) { export function destroy_effect(effect, remove_dom = true) { var removed = false; - var pop_renderer = push_renderer(effect.r, effect.pr); + var pop_renderer = custom_renderers_flag ? push_renderer(effect.r, effect.pr) : undefined; try { if ( @@ -609,7 +610,7 @@ function remove_effect_nodes(effect) { for (var i = segments.length - 1; i >= 0; i--) { var segment = segments[i]; - var pop_renderer = push_renderer(segment.r, segment.pr); + var pop_renderer = custom_renderers_flag ? push_renderer(segment.r, segment.pr) : undefined; remove_effect_dom(segment.start, /** @type {TemplateNode} */ (segment.end)); pop_renderer?.(); } @@ -775,7 +776,7 @@ export function aborted(effect = /** @type {Effect} */ (active_effect)) { export function move_effect(effect, fragment) { if (!effect.nodes) return; - var pop_renderer = push_renderer(effect.r, effect.pr); + var pop_renderer = custom_renderers_flag ? push_renderer(effect.r, effect.pr) : undefined; try { /** @type {TemplateNode | null} */ diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 58b8a3d28c..35b34ea760 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -34,6 +34,7 @@ import { is_passive_event } from '../../utils.js'; import { COMMENT_NODE, TEXT_CACHE } from './constants.js'; import { boundary } from './dom/blocks/boundary.js'; import { push_renderer } from './custom-renderer/state.js'; +import { custom_renderers_flag } from '../flags/index.js'; /** * This is normally true — block effects should run their intro transitions — @@ -167,7 +168,7 @@ const listeners = new Map(); * @returns {Exports} */ function _mount(Component, options) { - if (options.renderer) { + if (custom_renderers_flag && options.renderer) { var pop_renderer = push_renderer(options.renderer, options.renderer); try { diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d9cac05dd7..5533377c28 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -33,7 +33,7 @@ import { unfreeze_derived_effects, update_derived } from './reactivity/deriveds.js'; -import { async_mode_flag, tracing_mode_flag } from '../flags/index.js'; +import { async_mode_flag, tracing_mode_flag, custom_renderers_flag } from '../flags/index.js'; import { tracing_expressions } from './dev/tracing.js'; import { get_error } from '../shared/dev.js'; import { @@ -468,7 +468,7 @@ export function update_effect(effect) { active_effect = effect; is_updating_effect = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0; // Branch/root effects are not reactive contexts - var pop_renderer = push_renderer(effect.r, effect.pr); + var pop_renderer = custom_renderers_flag ? push_renderer(effect.r, effect.pr) : undefined; if (DEV) { var previous_component_fn = dev_current_component_function; diff --git a/packages/svelte/src/internal/flags/custom-renderer.js b/packages/svelte/src/internal/flags/custom-renderer.js new file mode 100644 index 0000000000..31e2898ff6 --- /dev/null +++ b/packages/svelte/src/internal/flags/custom-renderer.js @@ -0,0 +1,3 @@ +import { enable_custom_renderers_flag } from './index.js'; + +enable_custom_renderers_flag(); diff --git a/packages/svelte/src/internal/flags/index.js b/packages/svelte/src/internal/flags/index.js index 5d4054975f..46e762db69 100644 --- a/packages/svelte/src/internal/flags/index.js +++ b/packages/svelte/src/internal/flags/index.js @@ -4,6 +4,8 @@ export let async_mode_flag = false; export let legacy_mode_flag = false; /** True if $inspect.trace is used */ export let tracing_mode_flag = false; +/** True if custom renderers are used */ +export let custom_renderers_flag = false; export function enable_async_mode_flag() { async_mode_flag = true; @@ -21,3 +23,7 @@ export function enable_legacy_mode_flag() { export function enable_tracing_mode_flag() { tracing_mode_flag = true; } + +export function enable_custom_renderers_flag() { + custom_renderers_flag = true; +} diff --git a/packages/svelte/src/renderer/index.js b/packages/svelte/src/renderer/index.js index 3377f7f1cb..dfee2a2892 100644 --- a/packages/svelte/src/renderer/index.js +++ b/packages/svelte/src/renderer/index.js @@ -1 +1,2 @@ +import '../internal/flags/custom-renderer.js'; export { createRenderer } from '../internal/client/custom-renderer/index.js'; diff --git a/packages/svelte/tests/snapshot/samples/custom-renderer-server-noop/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/custom-renderer-server-noop/_expected/client/main.svelte.js index 9486146c31..b5de02525a 100644 --- a/packages/svelte/tests/snapshot/samples/custom-renderer-server-noop/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/custom-renderer-server-noop/_expected/client/main.svelte.js @@ -1,4 +1,5 @@ import $renderer from 'my-custom-renderer'; +import 'svelte/internal/flags/custom-renderer'; import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; diff --git a/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/Component.svelte.js b/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/Component.svelte.js index 5571be6d8d..695c02a2ee 100644 --- a/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/Component.svelte.js +++ b/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/Component.svelte.js @@ -1,4 +1,5 @@ import $renderer from 'my-custom-renderer'; +import 'svelte/internal/flags/custom-renderer'; import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; diff --git a/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/main.svelte.js index ff224836bc..efea84a0ae 100644 --- a/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/custom-renderer-single-node/_expected/client/main.svelte.js @@ -1,4 +1,5 @@ import $renderer from 'my-custom-renderer'; +import 'svelte/internal/flags/custom-renderer'; import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/legacy'; import * as $ from 'svelte/internal/client';