diff --git a/.changeset/chilly-dolphins-lick.md b/.changeset/chilly-dolphins-lick.md new file mode 100644 index 0000000000..1941f0a6a0 --- /dev/null +++ b/.changeset/chilly-dolphins-lick.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove selector api diff --git a/.changeset/eight-steaks-shout.md b/.changeset/eight-steaks-shout.md new file mode 100644 index 0000000000..62dbebafaf --- /dev/null +++ b/.changeset/eight-steaks-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correct update_block index type diff --git a/.changeset/khaki-mails-draw.md b/.changeset/khaki-mails-draw.md new file mode 100644 index 0000000000..cebc5770f4 --- /dev/null +++ b/.changeset/khaki-mails-draw.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: tighten up signals implementation diff --git a/.changeset/quiet-camels-mate.md b/.changeset/quiet-camels-mate.md new file mode 100644 index 0000000000..6e723bd4ab --- /dev/null +++ b/.changeset/quiet-camels-mate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve keyblock treeshaking diff --git a/.changeset/small-papayas-laugh.md b/.changeset/small-papayas-laugh.md new file mode 100644 index 0000000000..43475946cb --- /dev/null +++ b/.changeset/small-papayas-laugh.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: remove Component type, keep using SvelteComponent instead diff --git a/.changeset/tall-shrimps-worry.md b/.changeset/tall-shrimps-worry.md new file mode 100644 index 0000000000..7f09311301 --- /dev/null +++ b/.changeset/tall-shrimps-worry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add snippet marker symbol to children prop diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 49d60aecaa..a4819f87ba 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,8 @@ -## Svelte compiler rewrite +## Svelte 5 rewrite -Please note that [the Svelte codebase is currently being rewritten](https://svelte.dev/blog/runes). Thus, it's best to hold off on new features or refactorings for the time being. +Please note that [the Svelte codebase is currently being rewritten for Svelte 5](https://svelte.dev/blog/runes). Changes should target Svelte 5, which lives on the default branch (`main`). + +If your PR concerns Svelte 4 (including updates to [svelte.dev.docs](https://svelte.dev/docs)), please ensure the base branch is `svelte-4` and not `main`. ### Before submitting the PR, please make sure you do the following diff --git a/packages/svelte/scripts/build.js b/packages/svelte/scripts/build.js index 4b49fa76e9..00e0920a3a 100644 --- a/packages/svelte/scripts/build.js +++ b/packages/svelte/scripts/build.js @@ -27,7 +27,7 @@ await createBundle({ [`${pkg.name}/animate`]: `${dir}/src/animate/public.d.ts`, [`${pkg.name}/compiler`]: `${dir}/src/compiler/index.js`, [`${pkg.name}/easing`]: `${dir}/src/easing/index.js`, - [`${pkg.name}/legacy`]: `${dir}/src/legacy/public.d.ts`, + [`${pkg.name}/legacy`]: `${dir}/src/legacy/legacy-client.js`, [`${pkg.name}/motion`]: `${dir}/src/motion/public.d.ts`, [`${pkg.name}/server`]: `${dir}/src/server/index.js`, [`${pkg.name}/store`]: `${dir}/src/store/public.d.ts`, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index eed663846b..1194cffcde 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -251,6 +251,8 @@ export const function_visitor = (node, context) => { const in_constructor = parent.type === 'MethodDefinition' && parent.kind === 'constructor'; state = { ...context.state, in_constructor }; + } else { + state = { ...context.state, in_constructor: false }; } if (metadata?.hoistable === true) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index c06e2d6e75..b830367776 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -819,15 +819,21 @@ function serialize_inline_component(node, component_name, context) { const body = create_block(node, `${node.name}_${slot_name}`, children[slot_name], context); if (body.length === 0) continue; - const fn = b.arrow( + const slot_fn = b.arrow( [b.id('$$anchor'), b.id('$$slotProps')], b.block([...(slot_name === 'default' ? default_lets : []), ...body]) ); if (slot_name === 'default') { - push_prop(b.prop('init', b.id('children'), fn)); + push_prop( + b.prop( + 'init', + b.id('children'), + context.state.options.dev ? b.call('$.add_snippet_symbol', slot_fn) : slot_fn + ) + ); } else { - serialized_slots.push(b.prop('init', b.key(slot_name), fn)); + serialized_slots.push(b.prop('init', b.key(slot_name), slot_fn)); } } @@ -2221,19 +2227,34 @@ export const template_visitors = { declarations.push(b.let(node.index, index)); } - context.state.after_update.push( - b.stmt( - b.call( - '$.each', - context.state.node, - each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection), - b.literal(each_type), - key_function, - b.arrow([b.id('$$anchor'), item, index], b.block(declarations.concat(children))), - else_block + if ((each_type & EACH_KEYED) !== 0) { + context.state.after_update.push( + b.stmt( + b.call( + '$.each_keyed', + context.state.node, + each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection), + b.literal(each_type), + key_function, + b.arrow([b.id('$$anchor'), item, index], b.block(declarations.concat(children))), + else_block + ) ) - ) - ); + ); + } else { + context.state.after_update.push( + b.stmt( + b.call( + '$.each_indexed', + context.state.node, + each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection), + b.literal(each_type), + b.arrow([b.id('$$anchor'), item, index], b.block(declarations.concat(children))), + else_block + ) + ) + ); + } }, IfBlock(node, context) { context.state.template.push(''); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index fe478d0a27..ffba6d09db 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -847,26 +847,21 @@ function serialize_inline_component(node, component_name, context) { const body = create_block(node, children[slot_name], context); if (body.length === 0) continue; + const slot_fn = b.arrow( + [b.id('$$payload'), b.id('$$slotProps')], + b.block([...(slot_name === 'default' ? default_lets : []), ...body]) + ); + if (slot_name === 'default') { push_prop( b.prop( 'init', b.id('children'), - b.arrow( - [b.id('$$payload'), b.id('$$slotProps')], - b.block([...(slot_name === 'default' ? default_lets : []), ...body]) - ) + context.state.options.dev ? b.call('$.add_snippet_symbol', slot_fn) : slot_fn ) ); } else { - const slot = b.prop( - 'init', - b.literal(slot_name), - b.arrow( - [b.id('$$payload'), b.id('$$slotProps')], - b.block([...(slot_name === 'default' ? default_lets : []), ...body]) - ) - ); + const slot = b.prop('init', b.literal(slot_name), slot_fn); serialized_slots.push(slot); } } diff --git a/packages/svelte/src/internal/client/operations.js b/packages/svelte/src/internal/client/operations.js index d3e515286d..d94e20dd3e 100644 --- a/packages/svelte/src/internal/client/operations.js +++ b/packages/svelte/src/internal/client/operations.js @@ -8,21 +8,19 @@ const has_browser_globals = typeof window !== 'undefined'; // than megamorphic. const node_prototype = /** @type {Node} */ (has_browser_globals ? Node.prototype : {}); const element_prototype = /** @type {Element} */ (has_browser_globals ? Element.prototype : {}); -const event_target_prototype = /** @type {EventTarget} */ ( - has_browser_globals ? EventTarget.prototype : {} -); +const text_prototype = /** @type {Text} */ (has_browser_globals ? Text.prototype : {}); const map_prototype = Map.prototype; const append_child_method = node_prototype.appendChild; const clone_node_method = node_prototype.cloneNode; const map_set_method = map_prototype.set; const map_get_method = map_prototype.get; const map_delete_method = map_prototype.delete; -// @ts-expect-error improve perf of expando on DOM nodes for events -event_target_prototype.__click = undefined; -// @ts-expect-error improve perf of expando on DOM textValue updates -event_target_prototype.__nodeValue = ' '; +// @ts-expect-error improve perf of expando on DOM events +element_prototype.__click = undefined; +// @ts-expect-error improve perf of expando on DOM text updates +text_prototype.__nodeValue = ' '; // @ts-expect-error improve perf of expando on DOM className updates -event_target_prototype.__className = ''; +element_prototype.__className = ''; const first_child_get = /** @type {(this: Node) => ChildNode | null} */ ( // @ts-ignore @@ -162,11 +160,10 @@ export function set_class_name(node, class_name) { /** * @template {Node} N * @param {N} node - * @param {string} text * @returns {void} */ -export function text_content(node, text) { - text_content_set.call(node, text); +export function clear_text_content(node) { + text_content_set.call(node, ''); } /** @param {string} name */ diff --git a/packages/svelte/src/internal/client/reconciler.js b/packages/svelte/src/internal/client/reconciler.js index f0b0fce7c2..131c85cbd1 100644 --- a/packages/svelte/src/internal/client/reconciler.js +++ b/packages/svelte/src/internal/client/reconciler.js @@ -1,4 +1,4 @@ -import { append_child, map_get, map_set, text_content } from './operations.js'; +import { append_child, map_get, map_set, clear_text_content } from './operations.js'; import { current_hydration_fragment, get_hydration_fragment, @@ -198,7 +198,7 @@ export function reconcile_indexed_array( b_blocks = []; // Remove old blocks if (is_controlled && a !== 0) { - text_content(dom, ''); + clear_text_content(dom); } while (index < length) { block = a_blocks[index++]; @@ -260,9 +260,9 @@ export function reconcile_indexed_array( * @param {Element | Comment | Text} dom * @param {boolean} is_controlled * @param {(anchor: null, item: V, index: number | import('./types.js').Signal) => void} render_fn - * @param {Array | null} keys * @param {number} flags * @param {boolean} apply_transitions + * @param {Array | null} keys * @returns {void} */ export function reconcile_tracked_array( @@ -271,9 +271,9 @@ export function reconcile_tracked_array( dom, is_controlled, render_fn, - keys, flags, - apply_transitions + apply_transitions, + keys ) { var a_blocks = each_block.items; const is_computed_key = keys !== null; @@ -295,7 +295,7 @@ export function reconcile_tracked_array( b_blocks = []; // Remove old blocks if (is_controlled && a !== 0) { - text_content(dom, ''); + clear_text_content(dom); } while (a > 0) { block = a_blocks[--a]; diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 0925737989..fca29da810 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -68,7 +68,7 @@ import { hydrate_block_anchor, set_current_hydration_fragment } from './hydration.js'; -import { array_from, define_property, get_descriptor, get_descriptors, is_array } from './utils.js'; +import { array_from, define_property, get_descriptor, is_array } from './utils.js'; import { is_promise } from '../common.js'; import { bind_transition } from './transitions.js'; @@ -1270,15 +1270,15 @@ function handle_event_propagation(root_element, event) { } // composedPath contains list of nodes the event has propagated through. - // We check __handled_event_at to skip all nodes below it in case this is a - // parent of the __handled_event_at node, which indicates that there's nested + // We check __root to skip all nodes below it in case this is a + // parent of the __root node, which indicates that there's nested // mounted apps. In this case we don't want to trigger events multiple times. // We're deliberately not skipping if the index is the same or higher, because // someone could create an event programmatically and emit it multiple times, // in which case we want to handle the whole propagation chain properly each time. let path_idx = 0; // @ts-expect-error is added below - const handled_at = event.__handled_event_at; + const handled_at = event.__root; if (handled_at) { const at_idx = path.indexOf(handled_at); if (at_idx < path.indexOf(root_element)) { @@ -1317,7 +1317,7 @@ function handle_event_propagation(root_element, event) { } // @ts-expect-error is used above - event.__handled_event_at = root_element; + event.__root = root_element; } /** @@ -2078,7 +2078,7 @@ function get_first_element(block) { /** * @param {import('./types.js').EachItemBlock} block * @param {any} item - * @param {import('./types.js').MaybeSignal} index + * @param {number} index * @param {number} type * @returns {void} */ @@ -2093,7 +2093,6 @@ export function update_each_item_block(block, item, index, type) { let prev_index = block.index; if (index_is_reactive) { prev_index = /** @type {import('./types.js').Signal} */ (prev_index).value; - index = /** @type {import('./types.js').Signal} */ (index).value; } const items = block.parent.items; if (prev_index !== index && /** @type {number} */ (index) < items.length) { @@ -2263,9 +2262,10 @@ export function each_item_block(item, key, index, render_fn, flags) { * @param {null | ((item: V) => string)} key_fn * @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal) => void} render_fn * @param {null | ((anchor: Node) => void)} fallback_fn + * @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn * @returns {void} */ -export function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) { +function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) { const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0; const block = create_each_block(flags, anchor_node); @@ -2385,20 +2385,7 @@ export function each(anchor_node, collection, flags, key_fn, render_fn, fallback const flags = block.flags; const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0; const anchor_node = block.anchor; - if ((flags & EACH_KEYED) !== 0) { - reconcile_tracked_array( - array, - block, - anchor_node, - is_controlled, - render_fn, - keys, - flags, - true - ); - } else { - reconcile_indexed_array(array, block, anchor_node, is_controlled, render_fn, flags, true); - } + reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, true, keys); }, block, true @@ -2420,12 +2407,39 @@ export function each(anchor_node, collection, flags, key_fn, render_fn, fallback fallback = fallback.prev; } // Clear the array - reconcile_indexed_array([], block, anchor_node, is_controlled, render_fn, flags, false); + reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys); destroy_signal(/** @type {import('./types.js').EffectSignal} */ (render)); }); block.effect = each; } +/** + * @template V + * @param {Element | Comment} anchor_node + * @param {() => V[]} collection + * @param {number} flags + * @param {null | ((item: V) => string)} key_fn + * @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal) => void} render_fn + * @param {null | ((anchor: Node) => void)} fallback_fn + * @returns {void} + */ +export function each_keyed(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) { + each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_tracked_array); +} + +/** + * @template V + * @param {Element | Comment} anchor_node + * @param {() => V[]} collection + * @param {number} flags + * @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal) => void} render_fn + * @param {null | ((anchor: Node) => void)} fallback_fn + * @returns {void} + */ +export function each_indexed(anchor_node, collection, flags, render_fn, fallback_fn) { + each(anchor_node, collection, flags, null, render_fn, fallback_fn, reconcile_indexed_array); +} + /** * @param {Element | Text | Comment} anchor * @param {boolean} is_html @@ -3020,7 +3034,7 @@ export function unwrap(value) { * @template {Record} Props * @template {Record | undefined} Exports * @template {Record} Events - * @param {import('../../main/public.js').Component} component + * @param {import('../../main/public.js').SvelteComponent} component * @param {{ * target: Node; * props?: Props; @@ -3139,7 +3153,7 @@ export function createRoot(component, options) { * @template {Record} Props * @template {Record | undefined} Exports * @template {Record} Events - * @param {import('../../main/public.js').Component} component + * @param {import('../../main/public.js').SvelteComponent} component * @param {{ * target: Node; * props?: Props; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index b5fddc4489..a67f300b21 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -343,7 +343,13 @@ function destroy_references(signal) { if (references !== null) { let i; for (i = 0; i < references.length; i++) { - destroy_signal(references[i]); + const reference = references[i]; + if ((reference.flags & IS_EFFECT) !== 0) { + destroy_signal(reference); + } else { + remove_consumer(reference, 0, true); + reference.dependencies = null; + } } } } @@ -710,7 +716,7 @@ export function exposable(fn) { export function get(signal) { const flags = signal.flags; if ((flags & DESTROYED) !== 0) { - return /** @type {V} */ (UNINITIALIZED); + return signal.value; } if (is_signal_exposed && current_should_capture_signal) { @@ -1156,6 +1162,11 @@ export function managed_pre_effect(init, sync) { * @returns {import('./types.js').EffectSignal} */ export function pre_effect(init) { + if (current_effect === null) { + throw new Error( + 'The Svelte $effect.pre rune can only be used during component initialisation.' + ); + } const sync = current_effect !== null && (current_effect.flags & RENDER_EFFECT) !== 0; return internal_create_effect( PRE_EFFECT, @@ -1243,105 +1254,6 @@ export function set_signal_status(signal, status) { } } -/** @template V */ -class Selector { - /** @type {Map>} */ - #consumers_map = new Map(); - - /** @type {import('./types.js').Signal} */ - #active_key; - - /** @param {V | null} [key] */ - constructor(key) { - this.#active_key = source(key || null); - } - - get current() { - return get(this.#active_key); - } - - /** - * @param {V | null} key - * @returns {void} - */ - set(key) { - const active_key = this.#active_key; - const previous_key = active_key.value; - if (previous_key === key) { - return; - } - - set_signal_value(active_key, key); - - const consumers_map = this.#consumers_map; - let consumers = map_get(consumers_map, /** @type {V} */ (previous_key)); - if (consumers !== undefined) { - this.#update_consumers(consumers); - } - - consumers = map_get(consumers_map, /** @type {V} */ (key)); - if (consumers !== undefined) { - this.#update_consumers(consumers); - } - } - - /** - * @param {Set} consumers - * @returns {void} - */ - #update_consumers(consumers) { - let consumer; - for (consumer of consumers) { - set_signal_status(consumer, DIRTY); - if ((consumer.flags & IS_EFFECT) !== 0) { - schedule_effect(/** @type {import('./types.js').EffectSignal} */ (consumer), false); - } else { - mark_signal_consumers(consumer, DIRTY, true); - } - } - } - - /** - * @param {V} key - * @returns {boolean} - */ - is(key) { - const consumers_map = this.#consumers_map; - let consumers = map_get(consumers_map, key); - if (consumers === undefined) { - consumers = new Set(); - map_set(consumers_map, key, consumers); - } - - const consumer = current_consumer; - const effect = current_effect; - if (effect !== null && consumer !== null && !consumers.has(consumer)) { - consumers.add(consumer); - push_destroy_fn(effect, () => { - const consumers_set = /** @type {Set} */ (consumers); - consumers_set.delete(effect); - if (consumers_set.size === 0) { - map_delete(consumers_map, key); - } - }); - } - return this.#active_key.value === key; - } -} - -/** - * `selector` allows you to track the currently selected item in a list in a performance optimized manner - * that runs in constant time (O(1)) - this is only noticable for very large lists. - * - * https://svelte-5-preview.vercel.app/docs/functions#selector - * @template Key - * @param {Key | null} [key] - * @returns {Selector} - */ -export function selector(key) { - return new Selector(key); -} - /** * @template V * @param {V | import('./types.js').Signal} val diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index b33f95635f..5c0378920e 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -119,7 +119,7 @@ export function add_snippet_symbol(fn) { export function validate_snippet(snippet_fn) { if (snippet_fn[symbol] !== true) { throw new Error( - 'The argument to `{@html ...}` must be a snippet function, not a component or some other kind of function. ' + + 'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' + 'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.' ); } diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index ab24dad496..a0796cd37d 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -13,7 +13,6 @@ export { user_effect, render_effect, pre_effect, - selector, flushSync, bubble_event, safe_equal, diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index ec6560cba1..8c63213208 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -11,12 +11,12 @@ import * as $ from '../internal/index.js'; * @template {Record} Events * @template {Record} Slots * - * @param {import('./public.js').ComponentConstructorOptions & { - * component: import('../main/public.js').Component; + * @param {import('../main/public.js').ComponentConstructorOptions & { + * component: import('../main/public.js').SvelteComponent; * immutable?: boolean; * recover?: false; * }} options - * @returns {import('./public.js').SvelteComponent} + * @returns {import('../main/public.js').SvelteComponent & Exports} */ export function createClassComponent(options) { // @ts-expect-error $$prop_def etc are not actually defined @@ -33,8 +33,8 @@ export function createClassComponent(options) { * @template {Record} Events * @template {Record} Slots * - * @param {import('../main/public.js').Component} component - * @returns {typeof import('./public.js').SvelteComponent} + * @param {import('../main/public.js').SvelteComponent} component + * @returns {typeof import('../main/public.js').SvelteComponent & Exports} */ export function asClassComponent(component) { // @ts-expect-error $$prop_def etc are not actually defined @@ -57,7 +57,7 @@ class Svelte4Component { #instance; /** - * @param {import('./public.js').ComponentConstructorOptions & { + * @param {import('../main/public.js').ComponentConstructorOptions & { * component: any; * immutable?: boolean; * recover?: false; diff --git a/packages/svelte/src/legacy/legacy-server.js b/packages/svelte/src/legacy/legacy-server.js index d11bbd5873..aeba694ea6 100644 --- a/packages/svelte/src/legacy/legacy-server.js +++ b/packages/svelte/src/legacy/legacy-server.js @@ -6,7 +6,7 @@ import { render } from '../internal/server/index.js'; export { createClassComponent }; /** - * Takes the component function and returns a Svelte 4 compatible component constructor. + * Takes a Svelte 5 component and returns a Svelte 4 compatible component constructor. * * @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5. * @@ -15,8 +15,8 @@ export { createClassComponent }; * @template {Record} Events * @template {Record} Slots * - * @param {import('../main/public.js').Component} component - * @returns {typeof import('./public.js').SvelteComponent} + * @param {import('../main/public.js').SvelteComponent} component + * @returns {typeof import('../main/public.js').SvelteComponent & Exports} */ export function asClassComponent(component) { const component_constructor = as_class_component(component); @@ -30,8 +30,9 @@ export function asClassComponent(component) { html: result.html }; }; - // @ts-expect-error this is present for SSR + // this is present for SSR component_constructor.render = _render; + // @ts-ignore return component_constructor; } diff --git a/packages/svelte/src/legacy/public.d.ts b/packages/svelte/src/legacy/public.d.ts deleted file mode 100644 index dd6eb9725e..0000000000 --- a/packages/svelte/src/legacy/public.d.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @deprecated Use `Component` instead. See TODO for more information. - */ -export interface ComponentConstructorOptions< - Props extends Record = Record -> { - target: Element | Document | ShadowRoot; - anchor?: Element; - props?: Props; - context?: Map; - hydrate?: boolean; - intro?: boolean; - $$inline?: boolean; -} - -/** - * @deprecated use `Component` instead. See TODO for more information. - * - * Base class for Svelte components in Svelte 4. Svelte 5+ components implement - * the `Component` interface instead. This class is only provided for backwards - * compatibility with Svelte 4 typings and doesn't have any runtime equivalent. - * - * Can be used to create strongly typed Svelte components. - * - * #### Example: - * - * You have component library on npm called `component-library`, from which - * you export a component called `MyComponent`. For Svelte+TypeScript users, - * you want to provide typings. Therefore you create a `index.d.ts`: - * ```ts - * import { SvelteComponent } from "svelte"; - * export class MyComponent extends SvelteComponent<{foo: string}> {} - * ``` - * Typing this makes it possible for IDEs like VS Code with the Svelte extension - * to provide intellisense and to use the component like this in a Svelte file - * with TypeScript: - * ```svelte - * - * - * ``` - */ -export class SvelteComponent< - Props extends Record = any, - Events extends Record = any, - Slots extends Record = any -> { - [prop: string]: any; - - constructor(options: ComponentConstructorOptions); - /** - * For type checking capabilities only. - * Does not exist at runtime. - * ### DO NOT USE! - * - * */ - $$prop_def: Props; - /** - * For type checking capabilities only. - * Does not exist at runtime. - * ### DO NOT USE! - * - * */ - $$events_def: Events; - /** - * For type checking capabilities only. - * Does not exist at runtime. - * ### DO NOT USE! - * - * */ - $$slot_def: Slots; - - $destroy(): void; - - $on>( - type: K, - callback: (e: Events[K]) => void - ): () => void; - - $set(props: Partial): void; -} - -/** - * @deprecated Use `Component` instead. See TODO for more information. - */ -export class SvelteComponentTyped< - Props extends Record = any, - Events extends Record = any, - Slots extends Record = any -> extends SvelteComponent {} - -export * from './legacy-client.js'; diff --git a/packages/svelte/src/main/ambient.d.ts b/packages/svelte/src/main/ambient.d.ts index a48fc73e58..d80e8e4323 100644 --- a/packages/svelte/src/main/ambient.d.ts +++ b/packages/svelte/src/main/ambient.d.ts @@ -1,5 +1,5 @@ declare module '*.svelte' { - export { Component as default } from 'svelte'; + export { SvelteComponent as default } from 'svelte'; } /** diff --git a/packages/svelte/src/main/main-client.js b/packages/svelte/src/main/main-client.js index c6a14f3408..0971ebdc14 100644 --- a/packages/svelte/src/main/main-client.js +++ b/packages/svelte/src/main/main-client.js @@ -255,12 +255,4 @@ export function afterUpdate(fn) { // TODO bring implementations in here // (except probably untrack — do we want to expose that, if there's also a rune?) -export { - flushSync, - createRoot, - mount, - tick, - untrack, - onDestroy, - selector -} from '../internal/index.js'; +export { flushSync, createRoot, mount, tick, untrack, onDestroy } from '../internal/index.js'; diff --git a/packages/svelte/src/main/main-server.js b/packages/svelte/src/main/main-server.js index 3ff8962cf4..b9f81c5253 100644 --- a/packages/svelte/src/main/main-server.js +++ b/packages/svelte/src/main/main-server.js @@ -7,7 +7,6 @@ export { hasContext, mount, onDestroy, - selector, setContext, tick, untrack diff --git a/packages/svelte/src/main/public.d.ts b/packages/svelte/src/main/public.d.ts index b01e3bad11..f6d1ab7571 100644 --- a/packages/svelte/src/main/public.d.ts +++ b/packages/svelte/src/main/public.d.ts @@ -1,18 +1,24 @@ // This should contain all the public interfaces (not all of them are actually importable, check current Svelte for which ones are). -// Once we convert to JSDoc make it a d.ts file. -import type { - ComponentConstructorOptions, - SvelteComponent, - SvelteComponentTyped -} from '../legacy/public.js'; - -// For Svelte 6 we can think about only exporting these from svelte/legacy -export { SvelteComponent, SvelteComponentTyped, ComponentConstructorOptions }; +/** + * @deprecated Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. + * Use `mount` or `createRoot` instead to instantiate components. + * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * for more info. + */ +export interface ComponentConstructorOptions< + Props extends Record = Record +> { + target: Element | Document | ShadowRoot; + anchor?: Element; + props?: Props; + context?: Map; + hydrate?: boolean; + intro?: boolean; + $$inline?: boolean; +} /** - * Base interface for Svelte components. - * * Can be used to create strongly typed Svelte components. * * #### Example: @@ -21,8 +27,8 @@ export { SvelteComponent, SvelteComponentTyped, ComponentConstructorOptions }; * you export a component called `MyComponent`. For Svelte+TypeScript users, * you want to provide typings. Therefore you create a `index.d.ts`: * ```ts - * import type { Component } from "svelte"; - * export type MyComponent = Component<{foo: string}> + * import { SvelteComponent } from "svelte"; + * export class MyComponent extends SvelteComponent<{foo: string}> {} * ``` * Typing this makes it possible for IDEs like VS Code with the Svelte extension * to provide intellisense and to use the component like this in a Svelte file @@ -33,27 +39,87 @@ export { SvelteComponent, SvelteComponentTyped, ComponentConstructorOptions }; * * * ``` + * + * This was the base class for Svelte components in Svelte 4. Svelte 5+ components + * are completely different under the hood. You should only use this type for typing, + * not actually instantiate components with `new` - use `mount` or `createRoot` instead. + * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) + * for more info. */ -export interface Component< - Props extends Record = {}, - Exports extends Record | undefined = undefined, - Events extends Record = {}, - Slots extends Record = {} +export class SvelteComponent< + Props extends Record = any, + Events extends Record = any, + Slots extends Record = any > { - /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ - element?: typeof HTMLElement; + [prop: string]: any; + + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + */ + constructor(props: Props); + /** + * @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which + * is a stop-gap solution. Migrate towards using `mount` or `createRoot` instead. See + * https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info. + */ + constructor(options: ComponentConstructorOptions); + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * */ + $$prop_def: Props; + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * + * */ + $$events_def: Events; + /** + * For type checking capabilities only. + * Does not exist at runtime. + * ### DO NOT USE! + * + * */ + $$slot_def: Slots; + + /** + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. + */ + $destroy(): void; + + /** + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. + */ + $on>( + type: K, + callback: (e: Events[K]) => void + ): () => void; /** - * ## DO NOT USE THIS - * This only exists for typing purposes and has no runtime value. + * @deprecated This method only exists when using one of the legacy compatibility helpers, which + * is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes + * for more info. */ - z_$$( - props: Props, - events: Events, - slots: Slots - ): Exports extends undefined ? Props | undefined : Exports & Partial; + $set(props: Partial): void; } +/** + * @deprecated Use `SvelteComponent` instead. See TODO for more information. + */ +export class SvelteComponentTyped< + Props extends Record = any, + Events extends Record = any, + Slots extends Record = any +> extends SvelteComponent {} + /** * Convenience type to get the events the given component expects. Example: * ```html @@ -69,12 +135,12 @@ export interface Component< * * ``` */ -export type ComponentEvents | SvelteComponent> = - Comp extends SvelteComponent - ? Events - : Comp extends Component - ? Events - : never; +export type ComponentEvents = Comp extends SvelteComponent< + any, + infer Events +> + ? Events + : never; /** * Convenience type to get the props the given component expects. Example: @@ -87,16 +153,12 @@ export type ComponentEvents | SvelteC * * ``` */ -export type ComponentProps | SvelteComponent> = - Comp extends SvelteComponent - ? Props - : Comp extends Component - ? Props - : never; +export type ComponentProps = Comp extends SvelteComponent + ? Props + : never; /** - * Convenience type to get the type of a Svelte component. Not necessary when using the `Component` type, - * but useful when using the deprecated `SvelteComponent` type and for example in combination with + * Convenience type to get the type of a Svelte component. Useful for example in combination with * dynamic components using ``. * * Example: @@ -114,17 +176,14 @@ export type ComponentProps | SvelteCo * * ``` */ -export type ComponentType | SvelteComponent> = - Comp extends SvelteComponent - ? (new ( - options: ComponentConstructorOptions< - Comp extends SvelteComponent ? Props : Record - > - ) => Comp) & { - /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ - element?: typeof HTMLElement; - } - : Comp; +export type ComponentType = (new ( + options: ComponentConstructorOptions< + Comp extends SvelteComponent ? Props : Record + > +) => Comp) & { + /** The custom element version of the component. Only present if compiled with the `customElement` compiler option */ + element?: typeof HTMLElement; +}; interface DispatchOptions { cancelable?: boolean; diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index 7b7485d31a..3b28b16f9d 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -7,6 +7,48 @@ function cubic_out(t) { return f * f * f + 1.0; } +/** + * https://svelte.dev/docs/svelte-easing + * @param {number} t + * @returns {number} + */ +export function cubic_in_out(t) { + return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0; +} + +/** @param {number | string} value + * @returns {[number, string]} + */ +export function split_css_unit(value) { + const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); + return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px']; +} + +/** + * Animates a `blur` filter alongside an element's opacity. + * + * https://svelte.dev/docs/svelte-transition#blur + * @param {Element} node + * @param {import('./public').BlurParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function blur( + node, + { delay = 0, duration = 400, easing = cubic_in_out, amount = 5, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const f = style.filter === 'none' ? '' : style.filter; + const od = target_opacity * (1 - opacity); + const [value, unit] = split_css_unit(amount); + return { + delay, + duration, + easing, + css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * value}${unit});` + }; +} + /** * Animates the opacity of an element from 0 to the current opacity for `in` transitions and from the current opacity to 0 for `out` transitions. * @@ -25,6 +67,34 @@ export function fade(node, { delay = 0, duration = 400, easing = linear } = {}) }; } +/** + * Animates the x and y positions and the opacity of an element. `in` transitions animate from the provided values, passed as parameters to the element's default values. `out` transitions animate from the element's default values to the provided values. + * + * https://svelte.dev/docs/svelte-transition#fly + * @param {Element} node + * @param {import('./public').FlyParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function fly( + node, + { delay = 0, duration = 400, easing = cubic_out, x = 0, y = 0, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const transform = style.transform === 'none' ? '' : style.transform; + const od = target_opacity * (1 - opacity); + const [x_value, x_unit] = split_css_unit(x); + const [y_value, y_unit] = split_css_unit(y); + return { + delay, + duration, + easing, + css: (t, u) => ` + transform: ${transform} translate(${(1 - t) * x_value}${x_unit}, ${(1 - t) * y_value}${y_unit}); + opacity: ${target_opacity - od * u}` + }; +} + /** * Slides an element in and out. * @@ -69,6 +139,68 @@ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axi }; } +/** + * Animates the opacity and scale of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values. + * + * https://svelte.dev/docs/svelte-transition#scale + * @param {Element} node + * @param {import('./public').ScaleParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function scale( + node, + { delay = 0, duration = 400, easing = cubic_out, start = 0, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const transform = style.transform === 'none' ? '' : style.transform; + const sd = 1 - start; + const od = target_opacity * (1 - opacity); + return { + delay, + duration, + easing, + css: (_t, u) => ` + transform: ${transform} scale(${1 - sd * u}); + opacity: ${target_opacity - od * u} + ` + }; +} + +/** + * Animates the stroke of an SVG element, like a snake in a tube. `in` transitions begin with the path invisible and draw the path to the screen over time. `out` transitions start in a visible state and gradually erase the path. `draw` only works with elements that have a `getTotalLength` method, like `` and ``. + * + * https://svelte.dev/docs/svelte-transition#draw + * @param {SVGElement & { getTotalLength(): number }} node + * @param {import('./public').DrawParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function draw(node, { delay = 0, speed, duration, easing = cubic_in_out } = {}) { + let len = node.getTotalLength(); + const style = getComputedStyle(node); + if (style.strokeLinecap !== 'butt') { + len += parseInt(style.strokeWidth); + } + if (duration === undefined) { + if (speed === undefined) { + duration = 800; + } else { + duration = len / speed; + } + } else if (typeof duration === 'function') { + duration = duration(len); + } + return { + delay, + duration, + easing, + css: (_, u) => ` + stroke-dasharray: ${len}; + stroke-dashoffset: ${u * len}; + ` + }; +} + /** * @template T * @template S diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/_config.js new file mode 100644 index 0000000000..dd847ce2f2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + ssrHtml: ``, + + async test({ assert, target }) { + flushSync(); + + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/main.svelte new file mode 100644 index 0000000000..88b0398943 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private/main.svelte @@ -0,0 +1,14 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/_config.js new file mode 100644 index 0000000000..dd847ce2f2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + ssrHtml: ``, + + async test({ assert, target }) { + flushSync(); + + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/main.svelte new file mode 100644 index 0000000000..fe7fcf1aa3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure/main.svelte @@ -0,0 +1,22 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js new file mode 100644 index 0000000000..ee690418d6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + get props() { + return { log: [] }; + }, + + async test({ assert, target, component }) { + const [b1] = target.querySelectorAll('button'); + flushSync(() => { + b1.click(); + }); + flushSync(() => { + b1.click(); + }); + assert.deepEqual(component.log, ['init 0', 'cleanup 2', 'init 2', 'cleanup 4', 'init 4']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte new file mode 100644 index 0000000000..7c84aeddff --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/main.svelte @@ -0,0 +1,17 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/selector/_config.js b/packages/svelte/tests/runtime-runes/samples/selector/_config.js deleted file mode 100644 index f6c8c1620d..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/selector/_config.js +++ /dev/null @@ -1,47 +0,0 @@ -import { test } from '../../test'; - -export default test({ - html: ` - - - -

- `, - - async test({ assert, target }) { - const [b1, b2, b3] = target.querySelectorAll('button'); - - await b1.click(); - assert.htmlEqual( - target.innerHTML, - ` - - - -

1

- ` - ); - - await b3.click(); - assert.htmlEqual( - target.innerHTML, - ` - - - -

3

- ` - ); - - await b2.click(); - assert.htmlEqual( - target.innerHTML, - ` - - - -

2

- ` - ); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/selector/main.svelte b/packages/svelte/tests/runtime-runes/samples/selector/main.svelte deleted file mode 100644 index b48c63ff0f..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/selector/main.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -{#each array as item} - -{/each} - -

{selected.current}

diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-validation-error/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-validation-error/_config.js index 4b38ceb3ec..01c2773b32 100644 --- a/packages/svelte/tests/runtime-runes/samples/snippet-validation-error/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/snippet-validation-error/_config.js @@ -5,6 +5,6 @@ export default test({ dev: true }, error: - 'The argument to `{@html ...}` must be a snippet function, not a component or some other kind of function. ' + + 'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' + 'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.' }); diff --git a/packages/svelte/tests/types/component.ts b/packages/svelte/tests/types/component.ts index e75d433992..749c648e2f 100644 --- a/packages/svelte/tests/types/component.ts +++ b/packages/svelte/tests/types/component.ts @@ -1,7 +1,7 @@ -import { SvelteComponent, asClassComponent, createClassComponent } from 'svelte/legacy'; +import { asClassComponent, createClassComponent } from 'svelte/legacy'; import { createRoot, - type Component, + SvelteComponent, type ComponentEvents, type ComponentProps, type ComponentType @@ -15,11 +15,11 @@ class LegacyComponent extends SvelteComponent< { slot: { slotProps: boolean } } > {} +// @ts-expect-error const legacyComponent = new LegacyComponent({ target: null as any as Document | Element | ShadowRoot, props: { prop: 'foo', - // @ts-expect-error x: '' } }); @@ -48,35 +48,34 @@ const legacyComponentEvents2: ComponentEvents = { // --------------------------------------------------------------------------- new: functions -type NewComponent = Component< +class NewComponent extends SvelteComponent< { prop: string }, - { anExport: number }, { event: MouseEvent }, { slot: { slotProps: boolean } } ->; - -const newComponent: NewComponent = { - z_$$: (props, events, slots) => { - props.prop; - // @ts-expect-error - props.x; +> { + anExport: string = ''; +} - events.event; - // @ts-expect-error - events.x; - - slots.slot; - // @ts-expect-error - slots.x; +// @ts-expect-error +new NewComponent({ + prop: 'foo', + x: '' +}); - return { - anExport: 1, - prop: props.prop - }; - } -}; +const newComponent: NewComponent = new NewComponent({ + prop: 'foo' +}); +newComponent.$$events_def.event; +// @ts-expect-error +newComponent.$$events_def.x; +newComponent.$$slot_def.slot; +// @ts-expect-error +newComponent.$$slot_def.x; +newComponent.anExport === ''; +// @ts-expect-error +newComponent.anExport === 1; -const newComponentType: ComponentType = newComponent; +const newComponentType: ComponentType = NewComponent; const newComponentProps1: ComponentProps = { prop: '', @@ -124,20 +123,28 @@ instance.anExport === 1; // --------------------------------------------------------------------------- interop const AsLegacyComponent = asClassComponent(newComponent); -const asLegacyComponent = new AsLegacyComponent({ +// @ts-expect-error +new AsLegacyComponent({ target: null as any, props: { prop: '', - // @ts-expect-error x: '' } }); +const asLegacyComponent = new AsLegacyComponent({ + target: null as any, + props: { + prop: '' + } +}); asLegacyComponent.$on('event', (e) => e.clientX); // @ts-expect-error asLegacyComponent.$on('event', (e) => e.foo); // @ts-expect-error asLegacyComponent.$on('bar', (e) => e); asLegacyComponent.$$prop_def.prop = ''; +asLegacyComponent.anExport = ''; +// @ts-expect-error asLegacyComponent.$$prop_def.anExport = 1; // @ts-expect-error asLegacyComponent.$$prop_def.prop = 1; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 06104c2525..2f90cc693e 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -19,7 +19,7 @@ "svelte/action": ["./src/action/public.d.ts"], "svelte/compiler": ["./src/compiler/public.d.ts"], "svelte/internal": ["./src/internal/index.js"], - "svelte/legacy": ["./src/legacy/public.d.ts"], + "svelte/legacy": ["./src/legacy/legacy-client.js"], "svelte/server": ["./src/server/index.js"], "svelte/store": ["./src/store/public.d.ts"], "#compiler": ["./src/compiler/types/index.d.ts"] diff --git a/sites/svelte-5-preview/src/lib/Output/CompilerOptions.svelte b/sites/svelte-5-preview/src/lib/Output/CompilerOptions.svelte index 5f244a07fc..60504c5052 100644 --- a/sites/svelte-5-preview/src/lib/Output/CompilerOptions.svelte +++ b/sites/svelte-5-preview/src/lib/Output/CompilerOptions.svelte @@ -1,6 +1,7 @@ @@ -16,6 +17,12 @@ + + }); diff --git a/sites/svelte-5-preview/src/lib/Repl.svelte b/sites/svelte-5-preview/src/lib/Repl.svelte index 5922a63ea8..ff144d5d32 100644 --- a/sites/svelte-5-preview/src/lib/Repl.svelte +++ b/sites/svelte-5-preview/src/lib/Repl.svelte @@ -76,7 +76,8 @@ /** @type {import('svelte/compiler').CompileOptions} */ const DEFAULT_COMPILE_OPTIONS = { - generate: 'client' + generate: 'client', + dev: false }; /** @type {Map} */ diff --git a/sites/svelte-5-preview/src/lib/workers/bundler/index.js b/sites/svelte-5-preview/src/lib/workers/bundler/index.js index 1e5c6cc02c..15c43529cf 100644 --- a/sites/svelte-5-preview/src/lib/workers/bundler/index.js +++ b/sites/svelte-5-preview/src/lib/workers/bundler/index.js @@ -387,7 +387,8 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { } else if (id.endsWith('.svelte')) { result = svelte.compile(code, { filename: name + '.svelte', - generate: 'client' + generate: 'client', + dev: true }); if (result.css) { @@ -400,7 +401,11 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { `.replace(/\t/g, ''); } } else if (id.endsWith('.svelte.js')) { - result = svelte.compileModule(code, { filename: name + '.js', generate: 'client' }); + result = svelte.compileModule(code, { + filename: name + '.js', + generate: 'client', + dev: true + }); if (!result) { return null; } @@ -420,7 +425,7 @@ async function get_bundle(uid, mode, cache, local_files_lookup) { warnings.push(warning); }); - /** @type {import('rollup').TransformResult} */ + /** @type {import('@rollup/browser').TransformResult} */ const transform_result = { code: result.js.code, map: result.js.map diff --git a/sites/svelte-5-preview/src/lib/workers/compiler/index.js b/sites/svelte-5-preview/src/lib/workers/compiler/index.js index 0563cdaffd..56ff247161 100644 --- a/sites/svelte-5-preview/src/lib/workers/compiler/index.js +++ b/sites/svelte-5-preview/src/lib/workers/compiler/index.js @@ -59,7 +59,8 @@ function compile({ id, source, options, return_ast }) { if (options.filename.endsWith('.svelte')) { const compiled = svelte.compile(source, { filename: options.filename, - generate: options.generate + generate: options.generate, + dev: options.dev }); const { js, css, warnings, metadata } = compiled; @@ -77,7 +78,8 @@ function compile({ id, source, options, return_ast }) { } else if (options.filename.endsWith('.svelte.js')) { const compiled = svelte.compileModule(source, { filename: options.filename, - generate: options.generate + generate: options.generate, + dev: options.dev }); if (compiled) { diff --git a/sites/svelte-5-preview/src/lib/workers/jsconfig.json b/sites/svelte-5-preview/src/lib/workers/jsconfig.json new file mode 100644 index 0000000000..60351b7548 --- /dev/null +++ b/sites/svelte-5-preview/src/lib/workers/jsconfig.json @@ -0,0 +1,9 @@ +{ + "include": ["./**/*"], + "compilerOptions": { + "paths": { + "svelte": ["../../../static/svelte/main"], + "svelte/*": ["../../../static/svelte/*"] + } + } +} diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/03-functions.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/03-functions.md index 6385c53a14..b529d05cfb 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/03-functions.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/03-functions.md @@ -22,22 +22,3 @@ To prevent something from being treated as an `$effect`/`$derived` dependency, u }); ``` - -## `selector` - -`selector` allows you to track the currently selected item in a list in a performance optimized manner that runs in constant time (`O(1)`). With `selector`, you can immediately determine if an item is selected: - -```svelte - - -{#each array as item} - -{/each} - -

{selected.current}

-```