@@ -43,6 +43,7 @@ import {
4343 untrack ,
4444 effect ,
4545 flushSync ,
46+ flush_sync ,
4647 safe_not_equal ,
4748 current_block ,
4849 managed_effect ,
@@ -64,12 +65,11 @@ import {
6465 get_descriptors ,
6566 is_array ,
6667 is_function ,
67- object_assign ,
68- object_keys
68+ object_assign
6969} from './utils.js' ;
7070import { is_promise } from '../common.js' ;
7171import { bind_transition , trigger_transitions } from './transitions.js' ;
72- import { STATE_SYMBOL , proxy } from './proxy.js' ;
72+ import { STATE_SYMBOL } from './proxy.js' ;
7373
7474/** @type {Set<string> } */
7575const all_registerd_events = new Set ( ) ;
@@ -2825,14 +2825,21 @@ export function spread_props(...props) {
28252825 return new Proxy ( { props } , spread_props_handler ) ;
28262826}
28272827
2828+ // TODO 5.0 remove this
28282829/**
2829- * Mounts the given component to the given target and returns a handle to the component's public accessors
2830- * as well as a `$set` and `$destroy` method to update the props of the component or destroy it.
2831- *
2832- * If you don't need to interact with the component after mounting, use `mount` instead to save some bytes.
2830+ * @deprecated Use `mount` or `hydrate` instead
2831+ */
2832+ export function createRoot ( ) {
2833+ throw new Error (
2834+ '`createRoot` has been removed. Use `mount` or `hydrate` instead. See the updated docs for more info: https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes'
2835+ ) ;
2836+ }
2837+
2838+ /**
2839+ * Mounts a component to the given target and returns the exports and potentially the accessors (if compiled with `accessors: true`) of the component
28332840 *
28342841 * @template {Record<string, any>} Props
2835- * @template {Record<string, any> | undefined } Exports
2842+ * @template {Record<string, any>} Exports
28362843 * @template {Record<string, any>} Events
28372844 * @param {import('../../main/public.js').ComponentType<import('../../main/public.js').SvelteComponent<Props, Events>> } component
28382845 * @param {{
@@ -2841,48 +2848,22 @@ export function spread_props(...props) {
28412848 * events?: Events;
28422849 * context?: Map<any, any>;
28432850 * intro?: boolean;
2844- * recover?: false;
28452851 * }} options
2846- * @returns {Exports & { $destroy: () => void; $set: (props: Partial<Props>) => void; } }
2852+ * @returns {Exports }
28472853 */
2848- export function createRoot ( component , options ) {
2849- const props = proxy ( /** @type {any } */ ( options . props ) || { } , false ) ;
2850-
2851- let [ accessors , $destroy ] = hydrate ( component , { ...options , props } ) ;
2852-
2853- const result =
2854- /** @type {Exports & { $destroy: () => void; $set: (props: Partial<Props>) => void; } } */ ( {
2855- $set : ( next ) => {
2856- object_assign ( props , next ) ;
2857- } ,
2858- $destroy
2859- } ) ;
2860-
2861- for ( const key of object_keys ( accessors || { } ) ) {
2862- define_property ( result , key , {
2863- get ( ) {
2864- // @ts -expect-error TS doesn't know key exists on accessor
2865- return accessors [ key ] ;
2866- } ,
2867- /** @param {any } value */
2868- set ( value ) {
2869- // @ts -expect-error TS doesn't know key exists on accessor
2870- flushSync ( ( ) => ( accessors [ key ] = value ) ) ;
2871- } ,
2872- enumerable : true
2873- } ) ;
2874- }
2875-
2876- return result ;
2854+ export function mount ( component , options ) {
2855+ init_operations ( ) ;
2856+ const anchor = empty ( ) ;
2857+ options . target . appendChild ( anchor ) ;
2858+ // Don't flush previous effects to ensure order of outer effects stays consistent
2859+ return flush_sync ( ( ) => _mount ( component , { ...options , anchor } ) , false ) ;
28772860}
28782861
28792862/**
2880- * Mounts the given component to the given target and returns the accessors of the component and a function to destroy it.
2881- *
2882- * If you need to interact with the component after mounting, use `createRoot` instead.
2863+ * Hydrates a component on the given target and returns the exports and potentially the accessors (if compiled with `accessors: true`) of the component
28832864 *
28842865 * @template {Record<string, any>} Props
2885- * @template {Record<string, any> | undefined } Exports
2866+ * @template {Record<string, any>} Exports
28862867 * @template {Record<string, any>} Events
28872868 * @param {import('../../main/public.js').ComponentType<import('../../main/public.js').SvelteComponent<Props, Events>> } component
28882869 * @param {{
@@ -2891,19 +2872,65 @@ export function createRoot(component, options) {
28912872 * events?: Events;
28922873 * context?: Map<any, any>;
28932874 * intro?: boolean;
2875+ * recover?: false;
28942876 * }} options
2895- * @returns {[ Exports, () => void] }
2877+ * @returns {Exports }
28962878 */
2897- export function mount ( component , options ) {
2879+ export function hydrate ( component , options ) {
28982880 init_operations ( ) ;
2899- const anchor = empty ( ) ;
2900- options . target . appendChild ( anchor ) ;
2901- return _mount ( component , { ...options , anchor } ) ;
2881+ const container = options . target ;
2882+ const first_child = /** @type {ChildNode } */ ( container . firstChild ) ;
2883+ // Call with insert_text == true to prevent empty {expressions} resulting in an empty
2884+ // fragment array, resulting in a hydration error down the line
2885+ const hydration_fragment = get_hydration_fragment ( first_child , true ) ;
2886+ const previous_hydration_fragment = current_hydration_fragment ;
2887+ set_current_hydration_fragment ( hydration_fragment ) ;
2888+
2889+ /** @type {null | Text } */
2890+ let anchor = null ;
2891+ if ( hydration_fragment === null ) {
2892+ anchor = empty ( ) ;
2893+ container . appendChild ( anchor ) ;
2894+ }
2895+
2896+ let finished_hydrating = false ;
2897+
2898+ try {
2899+ // Don't flush previous effects to ensure order of outer effects stays consistent
2900+ return flush_sync ( ( ) => {
2901+ const instance = _mount ( component , { ...options , anchor } ) ;
2902+ // flush_sync will run this callback and then synchronously run any pending effects,
2903+ // which don't belong to the hydration phase anymore - therefore reset it here
2904+ set_current_hydration_fragment ( null ) ;
2905+ finished_hydrating = true ;
2906+ return instance ;
2907+ } , false ) ;
2908+ } catch ( error ) {
2909+ if ( ! finished_hydrating && options . recover !== false && hydration_fragment !== null ) {
2910+ // eslint-disable-next-line no-console
2911+ console . error (
2912+ 'ERR_SVELTE_HYDRATION_MISMATCH' +
2913+ ( DEV
2914+ ? ': Hydration failed because the initial UI does not match what was rendered on the server.'
2915+ : '' ) ,
2916+ error
2917+ ) ;
2918+ remove ( hydration_fragment ) ;
2919+ first_child . remove ( ) ;
2920+ hydration_fragment . at ( - 1 ) ?. nextSibling ?. remove ( ) ;
2921+ set_current_hydration_fragment ( null ) ;
2922+ return mount ( component , options ) ;
2923+ } else {
2924+ throw error ;
2925+ }
2926+ } finally {
2927+ set_current_hydration_fragment ( previous_hydration_fragment ) ;
2928+ }
29022929}
29032930
29042931/**
29052932 * @template {Record<string, any>} Props
2906- * @template {Record<string, any> | undefined } Exports
2933+ * @template {Record<string, any>} Exports
29072934 * @template {Record<string, any>} Events
29082935 * @param {import('../../main/public.js').ComponentType<import('../../main/public.js').SvelteComponent<Props, Events>> } component
29092936 * @param {{
@@ -2915,7 +2942,7 @@ export function mount(component, options) {
29152942 * intro?: boolean;
29162943 * recover?: false;
29172944 * }} options
2918- * @returns {[ Exports, () => void] }
2945+ * @returns {Exports }
29192946 */
29202947function _mount ( component , options ) {
29212948 const registered_events = new Set ( ) ;
@@ -2934,7 +2961,7 @@ function _mount(component, options) {
29342961 options . context ;
29352962 }
29362963 // @ts -expect-error the public typings are not what the actual function looks like
2937- accessors = component ( options . anchor , options . props || { } ) ;
2964+ accessors = component ( options . anchor , options . props || { } ) || { } ;
29382965 if ( options . context ) {
29392966 pop ( ) ;
29402967 }
@@ -2981,80 +3008,38 @@ function _mount(component, options) {
29813008 event_handle ( array_from ( all_registerd_events ) ) ;
29823009 root_event_handles . add ( event_handle ) ;
29833010
2984- return [
2985- accessors ,
2986- ( ) => {
2987- for ( const event_name of registered_events ) {
2988- container . removeEventListener ( event_name , bound_event_listener ) ;
2989- }
2990- root_event_handles . delete ( event_handle ) ;
2991- const dom = block . d ;
2992- if ( dom !== null ) {
2993- remove ( dom ) ;
2994- }
2995- destroy_signal ( /** @type {import('./types.js').EffectSignal } */ ( block . e ) ) ;
3011+ mounted_components . set ( accessors , ( ) => {
3012+ for ( const event_name of registered_events ) {
3013+ container . removeEventListener ( event_name , bound_event_listener ) ;
3014+ }
3015+ root_event_handles . delete ( event_handle ) ;
3016+ const dom = block . d ;
3017+ if ( dom !== null ) {
3018+ remove ( dom ) ;
29963019 }
2997- ] ;
3020+ destroy_signal ( /** @type {import('./types.js').EffectSignal } */ ( block . e ) ) ;
3021+ } ) ;
3022+
3023+ return accessors ;
29983024}
29993025
30003026/**
3001- * Hydrates the given component to the given target and returns the accessors of the component and a function to destroy it.
3002- *
3003- * If you need to interact with the component after hydrating, use `createRoot` instead.
3004- *
3005- * @template {Record<string, any>} Props
3006- * @template {Record<string, any> | undefined} Exports
3007- * @template {Record<string, any>} Events
3008- * @param {import('../../main/public.js').ComponentType<import('../../main/public.js').SvelteComponent<Props, Events>> } component
3009- * @param {{
3010- * target: Node;
3011- * props?: Props;
3012- * events?: Events;
3013- * context?: Map<any, any>;
3014- * intro?: boolean;
3015- * recover?: false;
3016- * }} options
3017- * @returns {[Exports, () => void] }
3027+ * References of the accessors of all components that were `mount`ed or `hydrate`d.
3028+ * Uses a `WeakMap` to avoid memory leaks.
30183029 */
3019- export function hydrate ( component , options ) {
3020- init_operations ( ) ;
3021- const container = options . target ;
3022- const first_child = /** @type {ChildNode } */ ( container . firstChild ) ;
3023- // Call with insert_text == true to prevent empty {expressions} resulting in an empty
3024- // fragment array, resulting in a hydration error down the line
3025- const hydration_fragment = get_hydration_fragment ( first_child , true ) ;
3026- const previous_hydration_fragment = current_hydration_fragment ;
3030+ let mounted_components = new WeakMap ( ) ;
30273031
3028- try {
3029- /** @type {null | Text } */
3030- let anchor = null ;
3031- if ( hydration_fragment === null ) {
3032- anchor = empty ( ) ;
3033- container . appendChild ( anchor ) ;
3034- }
3035- set_current_hydration_fragment ( hydration_fragment ) ;
3036- return _mount ( component , { ...options , anchor } ) ;
3037- } catch ( error ) {
3038- if ( options . recover !== false && hydration_fragment !== null ) {
3039- // eslint-disable-next-line no-console
3040- console . error (
3041- 'ERR_SVELTE_HYDRATION_MISMATCH' +
3042- ( DEV
3043- ? ': Hydration failed because the initial UI does not match what was rendered on the server.'
3044- : '' ) ,
3045- error
3046- ) ;
3047- remove ( hydration_fragment ) ;
3048- first_child . remove ( ) ;
3049- hydration_fragment . at ( - 1 ) ?. nextSibling ?. remove ( ) ;
3050- set_current_hydration_fragment ( null ) ;
3051- return mount ( component , options ) ;
3052- } else {
3053- throw error ;
3054- }
3055- } finally {
3056- set_current_hydration_fragment ( previous_hydration_fragment ) ;
3032+ /**
3033+ * Unmounts a component that was previously mounted using `mount` or `hydrate`.
3034+ * @param {Record<string, any> } component
3035+ */
3036+ export function unmount ( component ) {
3037+ const destroy = mounted_components . get ( component ) ;
3038+ if ( DEV && ! destroy ) {
3039+ // eslint-disable-next-line no-console
3040+ console . warn ( 'Tried to unmount a component that was not mounted.' ) ;
30573041 }
3042+ destroy ?. ( ) ;
30583043}
30593044
30603045/**
0 commit comments