From 55367b387a86c18db21e99f49cf082035bf07cb6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:02:24 -0700 Subject: [PATCH 1/5] Update for TS7 I am testing Typescript 7's JS support, which I've largely rewritten during the switch to Go. That means Javascript code will need to change much more than Typescript code. Fortunately, most of the changes are for the better: Javascript semantics are now nearly identical to Typescript semantics. It's much stricter and no longer has some persistent bugs that arose from shady JS handling I wrote years ago. This PR changes Svelte so that it compiles both with TS5.* and TS7, which means that occasionally there are duplicative or non-obvious changes. I'll annotate the interesting changes to explain why I made them. Because TS7 is quite a way off, I don't know whether you'll want to take this PR. Most of the changes are for the better, because they're due to stricter TS-aligned checking. But some are neutral and there is the previously mentioned duplication in a few places. --- packages/svelte/scripts/generate-types.js | 2 +- packages/svelte/src/compiler/index.js | 4 ++- packages/svelte/src/compiler/legacy.js | 2 +- .../phases/2-analyze/css/css-prune.js | 4 +-- .../src/compiler/phases/2-analyze/index.js | 2 +- .../2-analyze/utils/check_graph_for_cycles.js | 1 + .../2-analyze/visitors/shared/a11y/index.js | 2 +- .../2-analyze/visitors/shared/component.js | 1 + packages/svelte/src/compiler/phases/scope.js | 2 +- packages/svelte/src/compiler/state.js | 2 +- packages/svelte/src/index-client.js | 6 ++++- .../svelte/src/internal/client/dev/hmr.js | 2 +- .../src/internal/client/dom/blocks/await.js | 5 ++-- .../src/internal/client/dom/blocks/each.js | 2 +- .../src/internal/client/dom/blocks/if.js | 2 +- .../client/dom/elements/bindings/input.js | 1 + .../src/internal/client/dom/template.js | 2 +- .../internal/client/reactivity/equality.js | 4 +-- .../src/internal/client/reactivity/props.js | 14 +++++----- .../src/internal/client/reactivity/sources.js | 1 + packages/svelte/src/internal/client/render.js | 1 + .../svelte/src/internal/client/runtime.js | 3 ++- .../svelte/src/internal/server/payload.js | 3 ++- .../svelte/src/internal/shared/validate.js | 2 +- packages/svelte/src/utils.js | 6 ++--- packages/svelte/tests/animation-helpers.js | 26 ++++++++++++++----- packages/svelte/tests/helpers.js | 1 + .../svelte/tests/runtime-browser/assert.js | 11 ++++---- 28 files changed, 71 insertions(+), 43 deletions(-) diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js index c558a2bbf78a..f41be07e7e1c 100644 --- a/packages/svelte/scripts/generate-types.js +++ b/packages/svelte/scripts/generate-types.js @@ -26,7 +26,7 @@ await createBundle({ // so that types/properties with `@internal` (and its dependencies) are removed from the output stripInternal: true, paths: Object.fromEntries( - Object.entries(pkg.imports).map(([key, value]) => { + Object.entries(pkg.imports).map(/** @param {[string,any]} import */([key, value]) => { return [key, [value.types ?? value.default ?? value]]; }) ) diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js index a378af34eedc..fb7b7b2548f8 100644 --- a/packages/svelte/src/compiler/index.js +++ b/packages/svelte/src/compiler/index.js @@ -82,6 +82,7 @@ export function compileModule(source, options) { * @returns {AST.Root} */ +// TODO 6.0 remove unused `filename` /** * The parse function parses a component, returning only its abstract syntax tree. * @@ -94,7 +95,6 @@ export function compileModule(source, options) { * @returns {Record} */ -// TODO 6.0 remove unused `filename` /** * The parse function parses a component, returning only its abstract syntax tree. * @@ -123,6 +123,8 @@ export function parse(source, { modern, loose } = {}) { * @param {boolean | undefined} modern */ function to_public_ast(source, ast, modern) { + /** @type {AST.Root} */ + const rrrrr = parse('hi', { modern: true }); // ensure that the modern AST is available if (modern) { const clean = (/** @type {any} */ node) => { delete node.metadata; diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index 85345bca4a22..96bfbd9f785d 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -55,7 +55,7 @@ export function convert(source, ast) { // Insert svelte:options back into the root nodes if (/** @type {any} */ (options)?.__raw__) { - let idx = node.fragment.nodes.findIndex((node) => options.end <= node.start); + let idx = node.fragment.nodes.findIndex((node) => /** @type {any} */ (options).end <= node.start); if (idx === -1) { idx = node.fragment.nodes.length; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 79e8fbb02c08..8a4d8cb35056 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -9,8 +9,8 @@ import { import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../patterns.js'; import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; -/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */ -/** @typedef {FORWARD | BACKWARD} Direction */ +/** @typedef {typeof NODE_PROBABLY_EXISTS | typeof NODE_DEFINITELY_EXISTS} NodeExistsValue */ +/** @typedef {typeof FORWARD | typeof BACKWARD} Direction */ const NODE_PROBABLY_EXISTS = 0; const NODE_DEFINITELY_EXISTS = 1; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index d407b4455639..f10a8b4a5e74 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -295,7 +295,7 @@ export function analyze_module(source, options) { // TODO the following are not needed for modules, but we have to pass them in order to avoid type error, // and reducing the type would result in a lot of tedious type casts elsewhere - find a good solution one day ast_type: /** @type {any} */ (null), - component_slots: new Set(), + component_slots: /** @type {Set} */ (new Set()), expression: null, function_depth: 0, has_props_rune: false, diff --git a/packages/svelte/src/compiler/phases/2-analyze/utils/check_graph_for_cycles.js b/packages/svelte/src/compiler/phases/2-analyze/utils/check_graph_for_cycles.js index 67201d482507..83959248fec2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/utils/check_graph_for_cycles.js +++ b/packages/svelte/src/compiler/phases/2-analyze/utils/check_graph_for_cycles.js @@ -14,6 +14,7 @@ export default function check_graph_for_cycles(edges) { }, new Map()); const visited = new Set(); + /** @type {Set} */ const on_stack = new Set(); /** @type {Array>} */ const cycles = []; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js index f45a6c9a80d6..e2f84290e567 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js @@ -599,7 +599,7 @@ function has_disabled_attribute(attribute_map) { /** * @param {string} tag_name * @param {Map} attribute_map - * @returns {ElementInteractivity[keyof ElementInteractivity]} + * @returns {typeof ElementInteractivity[keyof typeof ElementInteractivity]} */ function element_interactivity(tag_name, attribute_map) { if ( diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js index aca87fab811c..6d09398fb7eb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js @@ -145,6 +145,7 @@ export function visit_component(node, context) { if (slot_name !== 'default') comments = []; } + /** @type {Set} */ const component_slots = new Set(); for (const slot_name in nodes) { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 700e098e4511..7fd2b6539907 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -22,7 +22,7 @@ const NUMBER = Symbol('number'); const STRING = Symbol('string'); const FUNCTION = Symbol('string'); -/** @type {Record} */ +/** @type {Record} */ const globals = { BigInt: [NUMBER], 'Math.min': [NUMBER, Math.min], diff --git a/packages/svelte/src/compiler/state.js b/packages/svelte/src/compiler/state.js index 725d03b802de..6d9873eb301e 100644 --- a/packages/svelte/src/compiler/state.js +++ b/packages/svelte/src/compiler/state.js @@ -87,7 +87,7 @@ export function pop_ignore() { /** * @param {AST.SvelteNode | NodeLike} node - * @param {import('../constants.js').IGNORABLE_RUNTIME_WARNINGS[number]} code + * @param {typeof import('../constants.js').IGNORABLE_RUNTIME_WARNINGS[number]} code * @returns */ export function is_ignored(node, code) { diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 397a81c31978..85eeab7de989 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -160,10 +160,14 @@ export function createEventDispatcher() { e.lifecycle_outside_component('createEventDispatcher'); } + /** + * @param [detail] + * @param [options] + */ return (type, detail, options) => { const events = /** @type {Record} */ ( active_component_context.s.$$events - )?.[/** @type {any} */ (type)]; + )?.[/** @type {string} */ (type)]; if (events) { const callbacks = is_array(events) ? events.slice() : [events]; diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index 27e2643d1674..709a1b272220 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -64,7 +64,7 @@ export function hmr(original, get_source) { // @ts-expect-error wrapper[FILENAME] = original[FILENAME]; - // @ts-expect-error + // @ts-ignore wrapper[HMR] = { // When we accept an update, we set the original source to the new component original, diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 4f68db57b1bb..42df41041ecd 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -28,6 +28,8 @@ const PENDING = 0; const THEN = 1; const CATCH = 2; +/** @typedef {typeof PENDING | typeof THEN | typeof CATCH} AwaitState */ + /** * @template V * @param {TemplateNode} node @@ -67,9 +69,8 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { : mutable_source(/** @type {V} */ (undefined), false, false); var error_source = runes ? source(undefined) : mutable_source(undefined, false, false); var resolved = false; - /** - * @param {PENDING | THEN | CATCH} state + * @param {AwaitState} state * @param {boolean} restore */ function update(state, restore) { diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 43c75e2a3769..006bf09257d1 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -191,7 +191,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f // store a reference to the effect so that we can update the start/end nodes in reconciliation each_effect ??= /** @type {Effect} */ (active_effect); - array = get(each_array); + array = /** @type {V[]} */ (get(each_array)); var length = array.length; if (was_empty && length === 0) { diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 6ba9ad4936f1..f418d465388e 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -36,7 +36,7 @@ export function if_block(node, fn, elseif = false) { /** @type {Effect | null} */ var alternate_effect = null; - /** @type {UNINITIALIZED | boolean | null} */ + /** @type {typeof UNINITIALIZED | boolean | null} */ var condition = UNINITIALIZED; var flags = elseif ? EFFECT_TRANSPARENT : 0; diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index 7c1fccea0fbc..29ba0e4d4bd3 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -240,6 +240,7 @@ export function bind_checked(input, get, set = get) { * @returns {V[]} */ function get_binding_group_value(group, __value, checked) { + /** @type {Set} */ var value = new Set(); for (var i = 0; i < group.length; i += 1) { diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index ebbf0039b269..265a52262f4b 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -156,7 +156,7 @@ export function from_mathml(content, flags) { /** * @param {TemplateStructure[]} structure - * @param {NAMESPACE_SVG | NAMESPACE_MATHML | undefined} [ns] + * @param {typeof NAMESPACE_SVG | typeof NAMESPACE_MATHML | undefined} [ns] */ function fragment_from_tree(structure, ns) { var fragment = create_fragment(); diff --git a/packages/svelte/src/internal/client/reactivity/equality.js b/packages/svelte/src/internal/client/reactivity/equality.js index 104123857366..113781b4cd3e 100644 --- a/packages/svelte/src/internal/client/reactivity/equality.js +++ b/packages/svelte/src/internal/client/reactivity/equality.js @@ -1,7 +1,7 @@ /** @import { Equals } from '#client' */ /** @type {Equals} */ -export function equals(value) { +export const equals = function(value) { return value === this.v; } @@ -26,6 +26,6 @@ export function not_equal(a, b) { } /** @type {Equals} */ -export function safe_equals(value) { +export const safe_equals = function(value) { return !safe_not_equal(value, this.v); } diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 05b747a1c4a5..cb5033749226 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -184,8 +184,7 @@ export function legacy_rest_props(props, exclude) { * The proxy handler for spread props. Handles the incoming array of props * that looks like `() => { dynamic: props }, { static: prop }, ..` and wraps * them so that the whole thing is passed to the component as the `$$props` argument. - * @template {Record} T - * @type {ProxyHandler<{ props: Array T)> }>}} + * @type {ProxyHandler<{ props: Array | (() => Record)> }>}} */ const spread_props_handler = { get(target, key) { @@ -362,8 +361,7 @@ export function prop(props, key, flags, fallback) { // means we can just call `$$props.foo = value` directly if (setter) { var legacy_parent = props.$$legacy; - - return function (/** @type {any} */ value, /** @type {boolean} */ mutation) { + return /** @type {() => V} */ (function (/** @type {V} */ value, /** @type {boolean} */ mutation) { if (arguments.length > 0) { // We don't want to notify if the value was mutated and the parent is in runes mode. // In that case the state proxy (if it exists) should take care of the notification. @@ -377,7 +375,7 @@ export function prop(props, key, flags, fallback) { } return getter(); - }; + }); } // Either prop is written to, but there's no binding, which means we @@ -399,8 +397,8 @@ export function prop(props, key, flags, fallback) { if (bindable) get(d); var parent_effect = /** @type {Effect} */ (active_effect); - - return function (/** @type {any} */ value, /** @type {boolean} */ mutation) { + + return /** @type {() => V} */(function (/** @type {any} */ value, /** @type {boolean} */ mutation) { if (arguments.length > 0) { const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value; @@ -424,5 +422,5 @@ export function prop(props, key, flags, fallback) { } return get(d); - }; + }); } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index f6b14f3360de..9bb0ec368f90 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -37,6 +37,7 @@ import { Batch, schedule_effect } from './batch.js'; import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js'; +/** @type {Set} */ export let inspect_effects = new Set(); /** @type {Map} */ diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index ff6844453dcc..8cb2b72c81eb 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -169,6 +169,7 @@ const document_listeners = new Map(); function _mount(Component, { target, anchor, props = {}, events, context, intro = true }) { init_operations(); + /** @type {Set} */ var registered_events = new Set(); /** @param {Array} events */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index e86866af2a14..3d760e4b9ef7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -284,7 +284,8 @@ export function update_reaction(reaction) { try { reaction.f |= REACTION_IS_UPDATING; - var result = /** @type {Function} */ (0, reaction.fn)(); + var fn = /** @type {Function} */ (reaction.fn); + var result = fn(); var deps = reaction.deps; if (new_deps !== null) { diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index 195488e06127..341039d46f07 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -6,7 +6,8 @@ export class HeadPayload { uid = () => ''; title = ''; - constructor(css = new Set(), /** @type {string[]} */ out = [], title = '', uid = () => '') { + constructor(/** @type {Set<{ hash: string; code: string }>} */ css = new Set(), /** @type {string[]} */ out = [], title = '', uid = () => '') { + this.css = css; this.out = out; this.title = title; diff --git a/packages/svelte/src/internal/shared/validate.js b/packages/svelte/src/internal/shared/validate.js index 8f3e2807e714..48e76f09589d 100644 --- a/packages/svelte/src/internal/shared/validate.js +++ b/packages/svelte/src/internal/shared/validate.js @@ -35,7 +35,7 @@ export function validate_store(store, name) { } /** - * @template {() => unknown} T + * @template {(...args: any[]) => unknown} T * @param {T} fn */ export function prevent_snippet_stringification(fn) { diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index cd79cfc27467..f8c39253aca1 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -452,7 +452,7 @@ const RUNES = /** @type {const} */ ([ '$host' ]); -/** @typedef {RUNES[number]} RuneName */ +/** @typedef {typeof RUNES[number]} RuneName */ /** * @param {string} name @@ -462,7 +462,7 @@ export function is_rune(name) { return RUNES.includes(/** @type {RuneName} */ (name)); } -/** @typedef {STATE_CREATION_RUNES[number]} StateCreationRuneName */ +/** @typedef {typeof STATE_CREATION_RUNES[number]} StateCreationRuneName */ /** * @param {string} name @@ -477,7 +477,7 @@ const RAW_TEXT_ELEMENTS = /** @type {const} */ (['textarea', 'script', 'style', /** @param {string} name */ export function is_raw_text_element(name) { - return RAW_TEXT_ELEMENTS.includes(/** @type {RAW_TEXT_ELEMENTS[number]} */ (name)); + return RAW_TEXT_ELEMENTS.includes(/** @type {typeof RAW_TEXT_ELEMENTS[number]} */ (name)); } /** diff --git a/packages/svelte/tests/animation-helpers.js b/packages/svelte/tests/animation-helpers.js index dcbb06292305..0452a0d64346 100644 --- a/packages/svelte/tests/animation-helpers.js +++ b/packages/svelte/tests/animation-helpers.js @@ -3,7 +3,9 @@ import { raf as svelte_raf } from 'svelte/internal/client'; import { queue_micro_task } from '../src/internal/client/dom/task.js'; export const raf = { + /** @type {Set} */ animations: new Set(), + /** @type {Set<(n: number) => void>} */ ticks: new Set(), tick, time: 0, @@ -54,14 +56,24 @@ class Animation { /** * @param {HTMLElement} target - * @param {Keyframe[]} keyframes - * @param {{ duration: number, delay: number }} options + * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes + * @param {number | KeyframeAnimationOptions | undefined} options */ - constructor(target, keyframes, { duration, delay }) { + constructor(target, keyframes, options) { this.target = target; - this.#keyframes = keyframes; - this.#duration = Math.round(duration); - this.#delay = delay ?? 0; + this.#keyframes = Array.isArray(keyframes) ? keyframes : []; + if (typeof options === 'number') { + this.#duration = options; + this.#delay = 0; + } else { + const { duration = 0, delay = 0 } = options ?? {}; + if (typeof duration === 'object') { + this.#duration = 0; + } else { + this.#duration = Math.round(+duration); + } + this.#delay = delay; + } this._update(); } @@ -189,6 +201,7 @@ function interpolate(a, b, p) { * @param {{duration: number, delay: number}} options * @returns {globalThis.Animation} */ +// @ts-ignore HTMLElement.prototype.animate = function (keyframes, options) { const animation = new Animation(this, keyframes, options); raf.animations.add(animation); @@ -196,6 +209,7 @@ HTMLElement.prototype.animate = function (keyframes, options) { return animation; }; +// @ts-ignore HTMLElement.prototype.getAnimations = function () { return Array.from(raf.animations).filter((animation) => animation.target === this); }; diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 410838829e3a..7a9640636c40 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -43,6 +43,7 @@ export function create_deferred() { /** @param {any} [reason] */ let reject = (reason) => {}; + /** @type {Promise} */ const promise = new Promise((f, r) => { resolve = f; reject = r; diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index 48bde014105c..e0d0b027f2b7 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -1,6 +1,5 @@ /** @import { assert } from 'vitest' */ -/** @import { CompileOptions, Warning } from '#compiler' */ - +import { parse } from 'svelte/compiler'; import { ELEMENT_NODE } from '#client/constants'; /** @@ -20,6 +19,8 @@ export function deepEqual(a, b, message) { * @returns {boolean} */ function is_equal(a, b) { + const input = '\uFEFF
'; + const actual = parse(input, { modern: true }); if (a && typeof a === 'object') { const is_array = Array.isArray(a); if (Array.isArray(b) !== is_array) return false; @@ -87,15 +88,15 @@ function normalize_html(window, html) { /** @param {any} node */ function normalize_children(node) { // sort attributes - const attributes = Array.from(node.attributes).sort((a, b) => { + const attributes = Array.from(node.attributes).sort((/** @type {any} */ a,/** @type {any} */ b) => { return a.name < b.name ? -1 : 1; }); - attributes.forEach((attr) => { + attributes.forEach((/** @type{any} */ attr) => { node.removeAttribute(attr.name); }); - attributes.forEach((attr) => { + attributes.forEach((/** @type{any} */ attr) => { node.setAttribute(attr.name, attr.value); }); From 3de210e27f835bb206fe7e811dfbad381c8f7ecd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:20:27 -0700 Subject: [PATCH 2/5] add changeset --- .changeset/gorgeous-gorillas-fix.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/gorgeous-gorillas-fix.md diff --git a/.changeset/gorgeous-gorillas-fix.md b/.changeset/gorgeous-gorillas-fix.md new file mode 100644 index 000000000000..65e08e10d5cb --- /dev/null +++ b/.changeset/gorgeous-gorillas-fix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +Update for TS7 From f8bf2826ab8b1c443363fb90d22613713501b396 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:28:01 -0700 Subject: [PATCH 3/5] revert scribbles mistakenly added --- packages/svelte/src/compiler/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js index fb7b7b2548f8..a378af34eedc 100644 --- a/packages/svelte/src/compiler/index.js +++ b/packages/svelte/src/compiler/index.js @@ -82,7 +82,6 @@ export function compileModule(source, options) { * @returns {AST.Root} */ -// TODO 6.0 remove unused `filename` /** * The parse function parses a component, returning only its abstract syntax tree. * @@ -95,6 +94,7 @@ export function compileModule(source, options) { * @returns {Record} */ +// TODO 6.0 remove unused `filename` /** * The parse function parses a component, returning only its abstract syntax tree. * @@ -123,8 +123,6 @@ export function parse(source, { modern, loose } = {}) { * @param {boolean | undefined} modern */ function to_public_ast(source, ast, modern) { - /** @type {AST.Root} */ - const rrrrr = parse('hi', { modern: true }); // ensure that the modern AST is available if (modern) { const clean = (/** @type {any} */ node) => { delete node.metadata; From 84b50a64682938f1138d1e439719f8f2ef33a9cd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:49:23 -0700 Subject: [PATCH 4/5] pnpm run format --- packages/svelte/scripts/generate-types.js | 8 ++- packages/svelte/src/compiler/legacy.js | 4 +- .../internal/client/reactivity/equality.js | 8 +-- .../src/internal/client/reactivity/props.js | 68 ++++++++++--------- .../svelte/src/internal/server/payload.js | 8 ++- .../svelte/tests/runtime-browser/assert.js | 8 ++- 6 files changed, 59 insertions(+), 45 deletions(-) diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js index f41be07e7e1c..0ee6004d4a2c 100644 --- a/packages/svelte/scripts/generate-types.js +++ b/packages/svelte/scripts/generate-types.js @@ -26,9 +26,11 @@ await createBundle({ // so that types/properties with `@internal` (and its dependencies) are removed from the output stripInternal: true, paths: Object.fromEntries( - Object.entries(pkg.imports).map(/** @param {[string,any]} import */([key, value]) => { - return [key, [value.types ?? value.default ?? value]]; - }) + Object.entries(pkg.imports).map( + /** @param {[string,any]} import */ ([key, value]) => { + return [key, [value.types ?? value.default ?? value]]; + } + ) ) }, modules: { diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index 96bfbd9f785d..b1bbcfcf741a 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -55,7 +55,9 @@ export function convert(source, ast) { // Insert svelte:options back into the root nodes if (/** @type {any} */ (options)?.__raw__) { - let idx = node.fragment.nodes.findIndex((node) => /** @type {any} */ (options).end <= node.start); + let idx = node.fragment.nodes.findIndex( + (node) => /** @type {any} */ (options).end <= node.start + ); if (idx === -1) { idx = node.fragment.nodes.length; } diff --git a/packages/svelte/src/internal/client/reactivity/equality.js b/packages/svelte/src/internal/client/reactivity/equality.js index 113781b4cd3e..76a4f8a34cd2 100644 --- a/packages/svelte/src/internal/client/reactivity/equality.js +++ b/packages/svelte/src/internal/client/reactivity/equality.js @@ -1,9 +1,9 @@ /** @import { Equals } from '#client' */ /** @type {Equals} */ -export const equals = function(value) { +export const equals = function (value) { return value === this.v; -} +}; /** * @param {unknown} a @@ -26,6 +26,6 @@ export function not_equal(a, b) { } /** @type {Equals} */ -export const safe_equals = function(value) { +export const safe_equals = function (value) { return !safe_not_equal(value, this.v); -} +}; diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index cb5033749226..8353eb39e2f5 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -361,21 +361,23 @@ export function prop(props, key, flags, fallback) { // means we can just call `$$props.foo = value` directly if (setter) { var legacy_parent = props.$$legacy; - return /** @type {() => V} */ (function (/** @type {V} */ value, /** @type {boolean} */ mutation) { - if (arguments.length > 0) { - // We don't want to notify if the value was mutated and the parent is in runes mode. - // In that case the state proxy (if it exists) should take care of the notification. - // If the parent is not in runes mode, we need to notify on mutation, too, that the prop - // has changed because the parent will not be able to detect the change otherwise. - if (!runes || !mutation || legacy_parent || is_store_sub) { - /** @type {Function} */ (setter)(mutation ? getter() : value); + return /** @type {() => V} */ ( + function (/** @type {V} */ value, /** @type {boolean} */ mutation) { + if (arguments.length > 0) { + // We don't want to notify if the value was mutated and the parent is in runes mode. + // In that case the state proxy (if it exists) should take care of the notification. + // If the parent is not in runes mode, we need to notify on mutation, too, that the prop + // has changed because the parent will not be able to detect the change otherwise. + if (!runes || !mutation || legacy_parent || is_store_sub) { + /** @type {Function} */ (setter)(mutation ? getter() : value); + } + + return value; } - return value; + return getter(); } - - return getter(); - }); + ); } // Either prop is written to, but there's no binding, which means we @@ -397,30 +399,32 @@ export function prop(props, key, flags, fallback) { if (bindable) get(d); var parent_effect = /** @type {Effect} */ (active_effect); - - return /** @type {() => V} */(function (/** @type {any} */ value, /** @type {boolean} */ mutation) { - if (arguments.length > 0) { - const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value; - set(d, new_value); - overridden = true; + return /** @type {() => V} */ ( + function (/** @type {any} */ value, /** @type {boolean} */ mutation) { + if (arguments.length > 0) { + const new_value = mutation ? get(d) : runes && bindable ? proxy(value) : value; + + set(d, new_value); + overridden = true; + + if (fallback_value !== undefined) { + fallback_value = new_value; + } - if (fallback_value !== undefined) { - fallback_value = new_value; + return value; } - return value; - } + // special case — avoid recalculating the derived if we're in a + // teardown function and the prop was overridden locally, or the + // component was already destroyed (this latter part is necessary + // because `bind:this` can read props after the component has + // been destroyed. TODO simplify `bind:this` + if ((is_destroying_effect && overridden) || (parent_effect.f & DESTROYED) !== 0) { + return d.v; + } - // special case — avoid recalculating the derived if we're in a - // teardown function and the prop was overridden locally, or the - // component was already destroyed (this latter part is necessary - // because `bind:this` can read props after the component has - // been destroyed. TODO simplify `bind:this` - if ((is_destroying_effect && overridden) || (parent_effect.f & DESTROYED) !== 0) { - return d.v; + return get(d); } - - return get(d); - }); + ); } diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index 341039d46f07..a7e40ad1db36 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -6,8 +6,12 @@ export class HeadPayload { uid = () => ''; title = ''; - constructor(/** @type {Set<{ hash: string; code: string }>} */ css = new Set(), /** @type {string[]} */ out = [], title = '', uid = () => '') { - + constructor( + /** @type {Set<{ hash: string; code: string }>} */ css = new Set(), + /** @type {string[]} */ out = [], + title = '', + uid = () => '' + ) { this.css = css; this.out = out; this.title = title; diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index e0d0b027f2b7..df0cdeffa83e 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -88,9 +88,11 @@ function normalize_html(window, html) { /** @param {any} node */ function normalize_children(node) { // sort attributes - const attributes = Array.from(node.attributes).sort((/** @type {any} */ a,/** @type {any} */ b) => { - return a.name < b.name ? -1 : 1; - }); + const attributes = Array.from(node.attributes).sort( + (/** @type {any} */ a, /** @type {any} */ b) => { + return a.name < b.name ? -1 : 1; + } + ); attributes.forEach((/** @type{any} */ attr) => { node.removeAttribute(attr.name); From c2f5e05ee4b5979d9768f2f52fd2bdebcb1e9247 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:59:17 -0700 Subject: [PATCH 5/5] revert mistaken edit --- packages/svelte/tests/runtime-browser/assert.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index df0cdeffa83e..9a294a48c7f0 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -1,5 +1,6 @@ /** @import { assert } from 'vitest' */ -import { parse } from 'svelte/compiler'; +/** @import { CompileOptions, Warning } from '#compiler' */ + import { ELEMENT_NODE } from '#client/constants'; /** @@ -19,8 +20,6 @@ export function deepEqual(a, b, message) { * @returns {boolean} */ function is_equal(a, b) { - const input = '\uFEFF
'; - const actual = parse(input, { modern: true }); if (a && typeof a === 'object') { const is_array = Array.isArray(a); if (Array.isArray(b) !== is_array) return false;