From abaa4413dfc45805aa83c96e33e203666d737afb Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 24 Jul 2024 19:35:22 +0100 Subject: [PATCH 1/7] fix: tweak element_invalid_self_closing_tag to exclude namespace (#12585) --- .changeset/quick-pumpkins-study.md | 5 +++++ .../svelte/src/compiler/phases/2-analyze/validation.js | 7 +++++-- .../samples/invalid-self-closing-tag/input.svelte | 2 ++ .../samples/invalid-self-closing-tag/warnings.json | 8 ++++---- 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 .changeset/quick-pumpkins-study.md diff --git a/.changeset/quick-pumpkins-study.md b/.changeset/quick-pumpkins-study.md new file mode 100644 index 0000000000..ba887164df --- /dev/null +++ b/.changeset/quick-pumpkins-study.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: tweak element_invalid_self_closing_tag to exclude namespace diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 29758b6b2f..a874d97fd9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -636,11 +636,14 @@ const validation = { } } + // Strip off any namespace from the beginning of the node name. + const node_name = node.name.replace(/[a-zA-Z-]*:/g, ''); + if ( context.state.analysis.source[node.end - 2] === '/' && context.state.options.namespace !== 'foreign' && - !VoidElements.includes(node.name) && - !SVGElements.includes(node.name) + !VoidElements.includes(node_name) && + !SVGElements.includes(node_name) ) { w.element_invalid_self_closing_tag(node, node.name); } diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte index 8b09b6f9a3..376c9f79bd 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte @@ -1,6 +1,8 @@ + +
diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json index 09a47f17a3..40b87ec7c8 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json @@ -3,11 +3,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `
` rather than `
`", "start": { - "line": 6, + "line": 8, "column": 0 }, "end": { - "line": 6, + "line": 8, "column": 7 } }, @@ -15,11 +15,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `` rather than ``", "start": { - "line": 7, + "line": 9, "column": 0 }, "end": { - "line": 7, + "line": 9, "column": 12 } } From 6037b961c043b90b37c192f58c01b06cf05a4ab1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 15:02:12 -0400 Subject: [PATCH 2/7] chore: more JSDoc (#12588) --- .../src/compiler/phases/1-parse/acorn.js | 8 +- .../phases/1-parse/read/expression.js | 8 +- .../compiler/phases/1-parse/state/element.js | 5 +- .../src/compiler/phases/1-parse/state/tag.js | 49 +++--- .../src/compiler/phases/2-analyze/index.js | 141 +++++++++--------- 5 files changed, 109 insertions(+), 102 deletions(-) diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 32be464013..52c4b69895 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -1,3 +1,5 @@ +/** @import { Comment, Program } from 'estree' */ +/** @import { Node } from 'acorn' */ import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; import { tsPlugin } from 'acorn-typescript'; @@ -23,7 +25,7 @@ export function parse(source, typescript) { if (typescript) amend(source, ast); add_comments(ast); - return /** @type {import('estree').Program} */ (ast); + return /** @type {Program} */ (ast); } /** @@ -57,7 +59,7 @@ export function parse_expression_at(source, typescript, index) { */ function get_comment_handlers(source) { /** - * @typedef {import('estree').Comment & { + * @typedef {Comment & { * start: number; * end: number; * }} CommentWithLocation @@ -149,7 +151,7 @@ function get_comment_handlers(source) { /** * Tidy up some stuff left behind by acorn-typescript * @param {string} source - * @param {import('acorn').Node} node + * @param {Node} node */ function amend(source, node) { return walk(node, null, { diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index aa86b85008..4e33c23f28 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -1,10 +1,12 @@ +/** @import { Expression } from 'estree' */ +/** @import { Parser } from '../index.js' */ import { parse_expression_at } from '../acorn.js'; import { regex_whitespace } from '../../patterns.js'; import * as e from '../../../errors.js'; /** - * @param {import('../index.js').Parser} parser - * @returns {import('estree').Expression} + * @param {Parser} parser + * @returns {Expression} */ export default function read_expression(parser) { try { @@ -35,7 +37,7 @@ export default function read_expression(parser) { parser.index = index; - return /** @type {import('estree').Expression} */ (node); + return /** @type {Expression} */ (node); } catch (err) { parser.acorn_error(err); } diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 478b5fc2ad..88dc5eae56 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -1,5 +1,6 @@ -/** @import { Parser } from '../index.js' */ +/** @import { Expression } from 'estree' */ /** @import * as Compiler from '#compiler' */ +/** @import { Parser } from '../index.js' */ import { is_void } from '../../../../constants.js'; import read_expression from '../read/expression.js'; import { read_script } from '../read/script.js'; @@ -589,7 +590,7 @@ function read_attribute(parser) { const first_value = value === true ? undefined : Array.isArray(value) ? value[0] : value; - /** @type {import('estree').Expression | null} */ + /** @type {Expression | null} */ let expression = null; if (first_value) { diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 06a5321c15..4ccfe2cb5f 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -1,3 +1,6 @@ +/** @import { ArrowFunctionExpression, Expression, Identifier } from 'estree' */ +/** @import { AwaitBlock, ConstTag, DebugTag, EachBlock, ExpressionTag, HtmlTag, IfBlock, KeyBlock, RenderTag, SnippetBlock } from '#compiler' */ +/** @import { Parser } from '../index.js' */ import read_pattern from '../read/context.js'; import read_expression from '../read/expression.js'; import * as e from '../../../errors.js'; @@ -7,7 +10,7 @@ import { parse_expression_at } from '../acorn.js'; const regex_whitespace_with_closing_curly_brace = /^\s*}/; -/** @param {import('../index.js').Parser} parser */ +/** @param {Parser} parser */ export default function tag(parser) { const start = parser.index; parser.index += 1; @@ -29,7 +32,7 @@ export default function tag(parser) { parser.allow_whitespace(); parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ parser.append({ type: 'ExpressionTag', start, @@ -42,7 +45,7 @@ export default function tag(parser) { }); } -/** @param {import('../index.js').Parser} parser */ +/** @param {Parser} parser */ function open(parser) { let start = parser.index - 2; while (parser.template[start] !== '{') start -= 1; @@ -50,7 +53,7 @@ function open(parser) { if (parser.eat('if')) { parser.require_whitespace(); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const block = parser.append({ type: 'IfBlock', elseif: false, @@ -76,7 +79,7 @@ function open(parser) { const template = parser.template; let end = parser.template.length; - /** @type {import('estree').Expression | undefined} */ + /** @type {Expression | undefined} */ let expression; // we have to do this loop because `{#each x as { y = z }}` fails to parse — @@ -119,7 +122,7 @@ function open(parser) { expression = walk(expression, null, { // @ts-expect-error TSAsExpression(node, context) { - if (node.end === /** @type {import('estree').Expression} */ (expression).end) { + if (node.end === /** @type {Expression} */ (expression).end) { assertion = node; end = node.expression.end; return node.expression; @@ -171,7 +174,7 @@ function open(parser) { parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const block = parser.append({ type: 'EachBlock', start, @@ -195,7 +198,7 @@ function open(parser) { const expression = read_expression(parser); parser.allow_whitespace(); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const block = parser.append({ type: 'AwaitBlock', start, @@ -249,7 +252,7 @@ function open(parser) { parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const block = parser.append({ type: 'KeyBlock', start, @@ -293,14 +296,14 @@ function open(parser) { const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' '); const params = parser.template.slice(params_start, parser.index); - let function_expression = /** @type {import('estree').ArrowFunctionExpression} */ ( + let function_expression = /** @type {ArrowFunctionExpression} */ ( parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start) ); parser.allow_whitespace(); parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const block = parser.append({ type: 'SnippetBlock', start, @@ -323,7 +326,7 @@ function open(parser) { e.expected_block_type(parser.index); } -/** @param {import('../index.js').Parser} parser */ +/** @param {Parser} parser */ function next(parser) { const start = parser.index - 1; @@ -352,7 +355,7 @@ function next(parser) { let elseif_start = start - 1; while (parser.template[elseif_start] !== '{') elseif_start -= 1; - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ const child = parser.append({ start: elseif_start, end: -1, @@ -434,7 +437,7 @@ function next(parser) { e.block_invalid_continuation_placement(start); } -/** @param {import('../index.js').Parser} parser */ +/** @param {Parser} parser */ function close(parser) { const start = parser.index - 1; @@ -448,7 +451,7 @@ function close(parser) { while (block.elseif) { block.end = parser.index; parser.stack.pop(); - block = /** @type {import('#compiler').IfBlock} */ (parser.current()); + block = /** @type {IfBlock} */ (parser.current()); } block.end = parser.index; parser.pop(); @@ -482,7 +485,7 @@ function close(parser) { parser.pop(); } -/** @param {import('../index.js').Parser} parser */ +/** @param {Parser} parser */ function special(parser) { let start = parser.index; while (parser.template[start] !== '{') start -= 1; @@ -496,7 +499,7 @@ function special(parser) { parser.allow_whitespace(); parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ parser.append({ type: 'HtmlTag', start, @@ -508,7 +511,7 @@ function special(parser) { } if (parser.eat('debug')) { - /** @type {import('estree').Identifier[]} */ + /** @type {Identifier[]} */ let identifiers; // Implies {@debug} which indicates "debug all" @@ -519,8 +522,8 @@ function special(parser) { identifiers = expression.type === 'SequenceExpression' - ? /** @type {import('estree').Identifier[]} */ (expression.expressions) - : [/** @type {import('estree').Identifier} */ (expression)]; + ? /** @type {Identifier[]} */ (expression.expressions) + : [/** @type {Identifier} */ (expression)]; identifiers.forEach( /** @param {any} node */ (node) => { @@ -534,7 +537,7 @@ function special(parser) { parser.eat('}', true); } - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ parser.append({ type: 'DebugTag', start, @@ -567,7 +570,7 @@ function special(parser) { parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ parser.append({ type: 'ConstTag', start, @@ -598,7 +601,7 @@ function special(parser) { parser.allow_whitespace(); parser.eat('}', true); - /** @type {ReturnType>} */ + /** @type {ReturnType>} */ parser.append({ type: 'RenderTag', start, diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index fe1027473c..f130ba18e2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1,3 +1,7 @@ +/** @import { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, LabeledStatement, Literal, Node, Program, Super } from 'estree' */ +/** @import { Attribute, BindDirective, Binding, DelegatedEvent, RegularElement, Root, Script, SvelteNode, Text, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ +/** @import { AnalysisState, Context, LegacyAnalysisState, Visitors } from './types' */ +/** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */ import is_reference from 'is-reference'; import { walk } from 'zimmerframe'; import * as e from '../../errors.js'; @@ -37,14 +41,14 @@ import { ignore_map, ignore_stack, pop_ignore, push_ignore } from '../../state.j import { equal } from '../../utils/assert.js'; /** - * @param {import('#compiler').Script | null} script + * @param {Script | null} script * @param {ScopeRoot} root * @param {boolean} allow_reactive_declarations * @param {Scope | null} parent - * @returns {import('../types.js').Js} + * @returns {Js} */ function js(script, root, allow_reactive_declarations, parent) { - /** @type {import('estree').Program} */ + /** @type {Program} */ const ast = script?.content ?? { type: 'Program', sourceType: 'module', @@ -75,9 +79,9 @@ function get_component_name(filename) { /** * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so * @param {string} event_name - * @param {import('estree').Expression | null} handler - * @param {import('./types').Context} context - * @returns {null | import('#compiler').DelegatedEvent} + * @param {Expression | null} handler + * @param {Context} context + * @returns {null | DelegatedEvent} */ function get_delegated_event(event_name, handler, context) { // Handle delegated event handlers. Bail-out if not a delegated event. @@ -91,9 +95,9 @@ function get_delegated_event(event_name, handler, context) { return null; } - /** @type {import('#compiler').DelegatedEvent} */ + /** @type {DelegatedEvent} */ const non_hoistable = { type: 'non-hoistable' }; - /** @type {import('estree').FunctionExpression | import('estree').FunctionDeclaration | import('estree').ArrowFunctionExpression | null} */ + /** @type {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression | null} */ let target_function = null; let binding = null; @@ -119,20 +123,20 @@ function get_delegated_event(event_name, handler, context) { const grandparent = path.at(-2); - /** @type {import('#compiler').RegularElement | null} */ + /** @type {RegularElement | null} */ let element = null; /** @type {string | null} */ let event_name = null; if (parent.type === 'OnDirective') { - element = /** @type {import('#compiler').RegularElement} */ (grandparent); + element = /** @type {RegularElement} */ (grandparent); event_name = parent.name; } else if ( parent.type === 'ExpressionTag' && grandparent?.type === 'Attribute' && is_event_attribute(grandparent) ) { - element = /** @type {import('#compiler').RegularElement} */ (path.at(-3)); - const attribute = /** @type {import('#compiler').Attribute} */ (grandparent); + element = /** @type {RegularElement} */ (path.at(-3)); + const attribute = /** @type {Attribute} */ (grandparent); event_name = get_attribute_event_name(attribute.name); } @@ -224,9 +228,9 @@ function get_delegated_event(event_name, handler, context) { } /** - * @param {import('estree').Program} ast - * @param {import('#compiler').ValidatedModuleCompileOptions} options - * @returns {import('../types.js').Analysis} + * @param {Program} ast + * @param {ValidatedModuleCompileOptions} options + * @returns {Analysis} */ export function analyze_module(ast, options) { const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null); @@ -239,7 +243,7 @@ export function analyze_module(ast, options) { } walk( - /** @type {import('estree').Node} */ (ast), + /** @type {Node} */ (ast), { scope, analysis: { runes: true } }, // @ts-expect-error TODO clean this mess up merge(set_scope(scopes), validation_runes_js, runes_scope_js_tweaker) @@ -255,10 +259,10 @@ export function analyze_module(ast, options) { } /** - * @param {import('#compiler').Root} root + * @param {Root} root * @param {string} source - * @param {import('#compiler').ValidatedCompileOptions} options - * @returns {import('../types.js').ComponentAnalysis} + * @param {ValidatedCompileOptions} options + * @returns {ComponentAnalysis} */ export function analyze_component(root, source, options) { const scope_root = new ScopeRoot(); @@ -268,7 +272,7 @@ export function analyze_component(root, source, options) { const { scope, scopes } = create_scopes(root.fragment, scope_root, false, instance.scope); - /** @type {import('../types.js').Template} */ + /** @type {Template} */ const template = { ast: root.fragment, scope, scopes }; // create synthetic bindings for store subscriptions @@ -339,7 +343,7 @@ export function analyze_component(root, source, options) { /** @type {number} */ (node.start) > /** @type {number} */ (module.ast.start) && /** @type {number} */ (node.end) < /** @type {number} */ (module.ast.end) && // const state = $state(0) is valid - get_rune(/** @type {import('estree').Node} */ (path.at(-1)), module.scope) === null + get_rune(/** @type {Node} */ (path.at(-1)), module.scope) === null ) { e.store_invalid_subscription(node); } @@ -360,7 +364,7 @@ export function analyze_component(root, source, options) { Array.from(module.scope.references).some(([name]) => Runes.includes(/** @type {any} */ (name))); // TODO remove all the ?? stuff, we don't need it now that we're validating the config - /** @type {import('../types.js').ComponentAnalysis} */ + /** @type {ComponentAnalysis} */ const analysis = { name: module.scope.generate(options.name ?? component_name), root: scope_root, @@ -434,7 +438,7 @@ export function analyze_component(root, source, options) { } for (const { ast, scope, scopes } of [module, instance, template]) { - /** @type {import('./types').AnalysisState} */ + /** @type {AnalysisState} */ const state = { scope, analysis, @@ -450,7 +454,7 @@ export function analyze_component(root, source, options) { }; walk( - /** @type {import('#compiler').SvelteNode} */ (ast), + /** @type {SvelteNode} */ (ast), state, merge(set_scope(scopes), validation_runes, runes_scope_tweaker, common_visitors) ); @@ -474,7 +478,7 @@ export function analyze_component(root, source, options) { // bind:this doesn't need to be a state reference if it will never change if ( type === 'BindDirective' && - /** @type {import('#compiler').BindDirective} */ (path[i]).name === 'this' + /** @type {BindDirective} */ (path[i]).name === 'this' ) { for (let j = i - 1; j >= 0; j -= 1) { const type = path[j].type; @@ -503,7 +507,7 @@ export function analyze_component(root, source, options) { instance.scope.declare(b.id('$$restProps'), 'rest_prop', 'synthetic'); for (const { ast, scope, scopes } of [module, instance, template]) { - /** @type {import('./types').LegacyAnalysisState} */ + /** @type {LegacyAnalysisState} */ const state = { scope, analysis, @@ -522,7 +526,7 @@ export function analyze_component(root, source, options) { }; walk( - /** @type {import('#compiler').SvelteNode} */ (ast), + /** @type {SvelteNode} */ (ast), state, // @ts-expect-error TODO merge(set_scope(scopes), validation_legacy, legacy_scope_tweaker, common_visitors) @@ -583,7 +587,7 @@ export function analyze_component(root, source, options) { // TODO this happens during the analysis phase, which shouldn't know anything about client vs server if (element.type === 'SvelteElement' && options.generate === 'client') continue; - /** @type {import('#compiler').Attribute | undefined} */ + /** @type {Attribute | undefined} */ let class_attribute = undefined; for (const attribute of element.attributes) { @@ -602,7 +606,7 @@ export function analyze_component(root, source, options) { if (is_text_attribute(class_attribute)) { class_attribute.value[0].data += ` ${analysis.css.hash}`; } else { - /** @type {import('#compiler').Text} */ + /** @type {Text} */ const css_text = { type: 'Text', data: ` ${analysis.css.hash}`, @@ -642,19 +646,19 @@ export function analyze_component(root, source, options) { return analysis; } -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const legacy_scope_tweaker = { LabeledStatement(node, { next, path, state }) { if ( state.ast_type !== 'instance' || node.label.name !== '$' || - /** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program' + /** @type {SvelteNode} */ (path.at(-1)).type !== 'Program' ) { return next(); } // Find all dependencies of this `$: {...}` statement - /** @type {import('../types.js').ReactiveStatement} */ + /** @type {ReactiveStatement} */ const reactive_statement = { assignments: new Set(), dependencies: [] @@ -669,14 +673,14 @@ const legacy_scope_tweaker = { if (binding === null) continue; for (const { node, path } of nodes) { - /** @type {import('estree').Expression} */ + /** @type {Expression} */ let left = node; let i = path.length - 1; - let parent = /** @type {import('estree').Expression} */ (path.at(i)); + let parent = /** @type {Expression} */ (path.at(i)); while (parent.type === 'MemberExpression') { left = parent; - parent = /** @type {import('estree').Expression} */ (path.at(--i)); + parent = /** @type {Expression} */ (path.at(--i)); } if ( @@ -757,7 +761,7 @@ const legacy_scope_tweaker = { next(); }, Identifier(node, { state, path }) { - const parent = /** @type {import('estree').Node} */ (path.at(-1)); + const parent = /** @type {Node} */ (path.at(-1)); if (is_reference(node, parent)) { if (node.name === '$$props') { state.analysis.uses_props = true; @@ -834,9 +838,7 @@ const legacy_scope_tweaker = { if (!node.declaration) { for (const specifier of node.specifiers) { - const binding = /** @type {import('#compiler').Binding} */ ( - state.scope.get(specifier.local.name) - ); + const binding = /** @type {Binding} */ (state.scope.get(specifier.local.name)); if ( binding !== null && (binding.kind === 'state' || @@ -863,7 +865,7 @@ const legacy_scope_tweaker = { node.declaration.type === 'ClassDeclaration' ) { state.analysis.exports.push({ - name: /** @type {import('estree').Identifier} */ (node.declaration.id).name, + name: /** @type {Identifier} */ (node.declaration.id).name, alias: null }); return next(); @@ -881,7 +883,7 @@ const legacy_scope_tweaker = { for (const declarator of node.declaration.declarations) { for (const id of extract_identifiers(declarator.id)) { - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); + const binding = /** @type {Binding} */ (state.scope.get(id.name)); binding.kind = 'bindable_prop'; } } @@ -899,7 +901,7 @@ const legacy_scope_tweaker = { } }; -/** @type {import('zimmerframe').Visitors} */ +/** @type {import('zimmerframe').Visitors} */ const runes_scope_js_tweaker = { VariableDeclarator(node, { state }) { if (node.init?.type !== 'CallExpression') return; @@ -919,14 +921,14 @@ const runes_scope_js_tweaker = { for (const path of extract_paths(node.id)) { // @ts-ignore this fails in CI for some insane reason - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(path.node.name)); + const binding = /** @type {Binding} */ (state.scope.get(path.node.name)); binding.kind = rune === '$state' ? 'state' : rune === '$state.frozen' ? 'frozen_state' : 'derived'; } } }; -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const runes_scope_tweaker = { CallExpression(node, { state, next }) { const rune = get_rune(node, state.scope); @@ -956,7 +958,7 @@ const runes_scope_tweaker = { for (const path of extract_paths(node.id)) { // @ts-ignore this fails in CI for some insane reason - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(path.node.name)); + const binding = /** @type {Binding} */ (state.scope.get(path.node.name)); binding.kind = rune === '$state' ? 'state' @@ -973,7 +975,7 @@ const runes_scope_tweaker = { state.analysis.needs_props = true; if (node.id.type === 'Identifier') { - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(node.id.name)); + const binding = /** @type {Binding} */ (state.scope.get(node.id.name)); binding.initial = null; // else would be $props() binding.kind = 'rest_prop'; } else { @@ -984,15 +986,15 @@ const runes_scope_tweaker = { const name = property.value.type === 'AssignmentPattern' - ? /** @type {import('estree').Identifier} */ (property.value.left).name - : /** @type {import('estree').Identifier} */ (property.value).name; + ? /** @type {Identifier} */ (property.value.left).name + : /** @type {Identifier} */ (property.value).name; const alias = property.key.type === 'Identifier' ? property.key.name - : String(/** @type {import('estree').Literal} */ (property.key).value); + : String(/** @type {Literal} */ (property.key).value); let initial = property.value.type === 'AssignmentPattern' ? property.value.right : null; - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(name)); + const binding = /** @type {Binding} */ (state.scope.get(name)); binding.prop_alias = alias; // rewire initial from $props() to the actual initial value, stripping $bindable() if necessary @@ -1001,9 +1003,7 @@ const runes_scope_tweaker = { initial.callee.type === 'Identifier' && initial.callee.name === '$bindable' ) { - binding.initial = /** @type {import('estree').Expression | null} */ ( - initial.arguments[0] ?? null - ); + binding.initial = /** @type {Expression | null} */ (initial.arguments[0] ?? null); binding.kind = 'bindable_prop'; } else { binding.initial = initial; @@ -1033,7 +1033,7 @@ const runes_scope_tweaker = { node.declaration.type === 'ClassDeclaration' ) { state.analysis.exports.push({ - name: /** @type {import('estree').Identifier} */ (node.declaration.id).name, + name: /** @type {Identifier} */ (node.declaration.id).name, alias: null }); return next(); @@ -1050,8 +1050,8 @@ const runes_scope_tweaker = { }; /** - * @param {import('estree').CallExpression} node - * @param {import('./types').Context} context + * @param {CallExpression} node + * @param {Context} context * @returns {boolean} */ function is_known_safe_call(node, context) { @@ -1075,8 +1075,8 @@ function is_known_safe_call(node, context) { } /** - * @param {import('estree').ArrowFunctionExpression | import('estree').FunctionExpression | import('estree').FunctionDeclaration} node - * @param {import('./types').Context} context + * @param {ArrowFunctionExpression | FunctionExpression | FunctionDeclaration} node + * @param {Context} context */ const function_visitor = (node, context) => { // TODO retire this in favour of a more general solution based on bindings @@ -1096,7 +1096,7 @@ const function_visitor = (node, context) => { /** * A 'safe' identifier means that the `foo` in `foo.bar` or `foo()` will not * call functions that require component context to exist - * @param {import('estree').Expression | import('estree').Super} expression + * @param {Expression | Super} expression * @param {Scope} scope */ function is_safe_identifier(expression, scope) { @@ -1120,7 +1120,7 @@ function is_safe_identifier(expression, scope) { ); } -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const common_visitors = { _(node, { state, next, path }) { ignore_map.set(node, structuredClone(ignore_stack)); @@ -1243,7 +1243,7 @@ const common_visitors = { context.next({ ...context.state, expression: node }); }, Identifier(node, context) { - const parent = /** @type {import('estree').Node} */ (context.path.at(-1)); + const parent = /** @type {Node} */ (context.path.at(-1)); if (!is_reference(node, parent)) return; if (node.name === '$$slots') { @@ -1270,8 +1270,7 @@ const common_visitors = { // TODO it would be better to just bail out when we hit the ExportSpecifier node but that's // not currently possibly because of our visitor merging, which I desperately want to nuke const is_export_specifier = - /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1)).type === - 'ExportSpecifier'; + /** @type {SvelteNode} */ (context.path.at(-1)).type === 'ExportSpecifier'; if ( context.state.analysis.runes && @@ -1472,8 +1471,8 @@ const common_visitors = { node.attributes.push( create_attribute( 'value', - /** @type {import('#compiler').Text} */ (node.fragment.nodes.at(0)).start, - /** @type {import('#compiler').Text} */ (node.fragment.nodes.at(-1)).end, + /** @type {Text} */ (node.fragment.nodes.at(0)).start, + /** @type {Text} */ (node.fragment.nodes.at(-1)).end, // @ts-ignore node.fragment.nodes ) @@ -1552,7 +1551,7 @@ const common_visitors = { }; /** - * @param {import('#compiler').RegularElement} node + * @param {RegularElement} node */ function determine_element_spread(node) { let has_spread = false; @@ -1578,10 +1577,10 @@ function get_attribute_event_name(event_name) { } /** - * @param {Map} unsorted_reactive_declarations + * @param {Map} unsorted_reactive_declarations */ function order_reactive_statements(unsorted_reactive_declarations) { - /** @typedef {[import('estree').LabeledStatement, import('../types.js').ReactiveStatement]} Tuple */ + /** @typedef {[LabeledStatement, ReactiveStatement]} Tuple */ /** @type {Map>} */ const lookup = new Map(); @@ -1614,13 +1613,13 @@ function order_reactive_statements(unsorted_reactive_declarations) { } // We use a map and take advantage of the fact that the spec says insertion order is preserved when iterating - /** @type {Map} */ + /** @type {Map} */ const reactive_declarations = new Map(); /** * - * @param {import('estree').LabeledStatement} node - * @param {import('../types.js').ReactiveStatement} declaration + * @param {LabeledStatement} node + * @param {ReactiveStatement} declaration * @returns */ const add_declaration = (node, declaration) => { From 20b879717a6385ec3f00df64f1e39526b142e0e0 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 24 Jul 2024 20:32:26 +0100 Subject: [PATCH 3/7] breaking: avoid flushing queued updates on mount/hydrate (#12587) * breaking: avoid flushing queued updates on mount/hydrat * Fix tests * Update packages/svelte/src/internal/client/render.js Co-authored-by: Rich Harris * tweak * tweak --------- Co-authored-by: Rich Harris --- .changeset/slow-gorillas-yawn.md | 5 ++ packages/svelte/src/internal/client/render.js | 54 +++++++++---------- .../svelte/src/internal/client/runtime.js | 7 +-- packages/svelte/tests/hydration/test.ts | 2 + .../svelte/tests/runtime-browser/driver.js | 3 ++ .../hydrate-modified-input-group/_config.js | 2 + .../samples/hydrate-modified-input/_config.js | 2 + 7 files changed, 40 insertions(+), 35 deletions(-) create mode 100644 .changeset/slow-gorillas-yawn.md diff --git a/.changeset/slow-gorillas-yawn.md b/.changeset/slow-gorillas-yawn.md new file mode 100644 index 0000000000..376b4d041c --- /dev/null +++ b/.changeset/slow-gorillas-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: avoid flushing queued updates on mount/hydrate diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 21cc096325..58bf3a3342 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -79,8 +79,7 @@ export function set_text(text, value) { */ export function mount(component, options) { const anchor = options.anchor ?? options.target.appendChild(empty()); - // Don't flush previous effects to ensure order of outer effects stays consistent - return flush_sync(() => _mount(component, { ...options, anchor }), false); + return _mount(component, { ...options, anchor }); } /** @@ -113,40 +112,35 @@ export function hydrate(component, options) { const previous_hydrate_node = hydrate_node; try { - // Don't flush previous effects to ensure order of outer effects stays consistent - return flush_sync(() => { - var anchor = /** @type {import('#client').TemplateNode} */ (target.firstChild); - while ( - anchor && - (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) - ) { - anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling); - } + var anchor = /** @type {import('#client').TemplateNode} */ (target.firstChild); + while ( + anchor && + (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) + ) { + anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling); + } - if (!anchor) { - throw HYDRATION_ERROR; - } + if (!anchor) { + throw HYDRATION_ERROR; + } - set_hydrating(true); - set_hydrate_node(/** @type {Comment} */ (anchor)); - hydrate_next(); + set_hydrating(true); + set_hydrate_node(/** @type {Comment} */ (anchor)); + hydrate_next(); - const instance = _mount(component, { ...options, anchor }); + const instance = _mount(component, { ...options, anchor }); - if ( - hydrate_node.nodeType !== 8 || - /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END - ) { - w.hydration_mismatch(); - throw HYDRATION_ERROR; - } + if ( + hydrate_node.nodeType !== 8 || + /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END + ) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } - // flush_sync will run this callback and then synchronously run any pending effects, - // which don't belong to the hydration phase anymore - therefore reset it here - set_hydrating(false); + set_hydrating(false); - return instance; - }, false); + return /** @type {Exports} */ (instance); } catch (error) { if (error === HYDRATION_ERROR) { // TODO it's possible for event listeners to have been added and diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 922193f10e..9749002bf7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -670,10 +670,9 @@ function process_effects(effect, collected_effects) { * Internal version of `flushSync` with the option to not flush previous effects. * Returns the result of the passed function, if given. * @param {() => any} [fn] - * @param {boolean} [flush_previous] * @returns {any} */ -export function flush_sync(fn, flush_previous = true) { +export function flush_sync(fn) { var previous_scheduler_mode = current_scheduler_mode; var previous_queued_root_effects = current_queued_root_effects; @@ -687,9 +686,7 @@ export function flush_sync(fn, flush_previous = true) { current_queued_root_effects = root_effects; is_micro_task_queued = false; - if (flush_previous) { - flush_queued_root_effects(previous_queued_root_effects); - } + flush_queued_root_effects(previous_queued_root_effects); var result = fn?.(); diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index fab0e5b308..d592a65de3 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -8,6 +8,7 @@ import { suite, assert_ok, type BaseTest } from '../suite.js'; import { createClassComponent } from 'svelte/legacy'; import { render } from 'svelte/server'; import type { CompileOptions } from '#compiler'; +import { flushSync } from 'svelte'; interface HydrationTest extends BaseTest { load_compiled?: boolean; @@ -114,6 +115,7 @@ const { test, run } = suite(async (config, cwd) => { if (!override) { const expected = read(`${cwd}/_expected.html`) ?? rendered.html; + flushSync(); assert.equal(target.innerHTML.trim(), expected.trim()); } diff --git a/packages/svelte/tests/runtime-browser/driver.js b/packages/svelte/tests/runtime-browser/driver.js index 7a5603e9b8..ef6acd08f6 100644 --- a/packages/svelte/tests/runtime-browser/driver.js +++ b/packages/svelte/tests/runtime-browser/driver.js @@ -5,6 +5,7 @@ import config from '__CONFIG__'; // @ts-expect-error import * as assert from 'assert.js'; import { createClassComponent } from 'svelte/legacy'; +import { flushSync } from 'svelte'; /** @param {HTMLElement} target */ export default async function (target) { @@ -45,6 +46,8 @@ export default async function (target) { } while (new Date().getTime() <= start + ms); }; + flushSync(); + if (config.html) { assert.htmlEqual(target.innerHTML, config.html); } diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js index 7f3bfac707..7596fd97be 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -9,6 +10,7 @@ export default test({ inputs[1].dispatchEvent(new window.Event('change')); // Hydration shouldn't reset the value to 1 hydrate(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js index e5bbe5b0fe..10dac59fae 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -9,6 +10,7 @@ export default test({ input.dispatchEvent(new window.Event('input')); // Hydration shouldn't reset the value to empty hydrate(); + flushSync(); assert.htmlEqual(target.innerHTML, '\nfoo'); } From 7a5c6b588f21ec849d9262a88cb9345e02ea9672 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 15:43:08 -0400 Subject: [PATCH 4/7] chore: more JSDoc imports (#12590) * more * more * more * more * fix --- .../compiler/phases/2-analyze/validation.js | 87 ++++----- .../phases/3-transform/client/utils.js | 170 ++++++++---------- .../3-transform/client/visitors/global.js | 16 +- .../client/visitors/javascript-runes.js | 86 ++++----- 4 files changed, 166 insertions(+), 193 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index a874d97fd9..129b7e280c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -1,3 +1,7 @@ +/** @import { AssignmentExpression, CallExpression, Expression, Identifier, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { Attribute, Component, ElementLike, Fragment, RegularElement, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TransitionDirective } from '#compiler' */ +/** @import { NodeLike } from '../../errors.js' */ +/** @import { AnalysisState, Context, Visitors } from './types.js' */ import is_reference from 'is-reference'; import { disallowed_paragraph_contents, @@ -35,8 +39,8 @@ import { merge } from '../visitors.js'; import { a11y_validators } from './a11y.js'; /** - * @param {import('#compiler').Attribute} attribute - * @param {import('#compiler').ElementLike} parent + * @param {Attribute} attribute + * @param {ElementLike} parent */ function validate_attribute(attribute, parent) { if ( @@ -63,8 +67,8 @@ function validate_attribute(attribute, parent) { } /** - * @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node - * @param {import('zimmerframe').Context} context + * @param {Component | SvelteComponent | SvelteSelf} node + * @param {Context} context */ function validate_component(node, context) { for (const attribute of node.attributes) { @@ -123,16 +127,16 @@ const react_attributes = new Map([ ]); /** - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node - * @param {import('zimmerframe').Context} context + * @param {RegularElement | SvelteElement} node + * @param {Context} context */ function validate_element(node, context) { let has_animate_directive = false; - /** @type {import('#compiler').TransitionDirective | null} */ + /** @type {TransitionDirective | null} */ let in_transition = null; - /** @type {import('#compiler').TransitionDirective | null} */ + /** @type {TransitionDirective | null} */ let out_transition = null; for (const attribute of node.attributes) { @@ -175,7 +179,7 @@ function validate_element(node, context) { } if (attribute.name === 'slot') { - /** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf | undefined} */ + /** @type {RegularElement | SvelteElement | Component | SvelteComponent | SvelteSelf | undefined} */ validate_slot_attribute(context, attribute); } @@ -212,7 +216,7 @@ function validate_element(node, context) { has_animate_directive = true; } } else if (attribute.type === 'TransitionDirective') { - const existing = /** @type {import('#compiler').TransitionDirective | null} */ ( + const existing = /** @type {TransitionDirective | null} */ ( (attribute.intro && in_transition) || (attribute.outro && out_transition) ); @@ -255,7 +259,7 @@ function validate_element(node, context) { } /** - * @param {import('#compiler').Attribute} attribute + * @param {Attribute} attribute */ function validate_attribute_name(attribute) { if ( @@ -269,8 +273,8 @@ function validate_attribute_name(attribute) { } /** - * @param {import('zimmerframe').Context} context - * @param {import('#compiler').Attribute} attribute + * @param {Context} context + * @param {Attribute} attribute * @param {boolean} is_component */ function validate_slot_attribute(context, attribute, is_component = false) { @@ -343,8 +347,8 @@ function validate_slot_attribute(context, attribute, is_component = false) { } /** - * @param {import('#compiler').Fragment | null | undefined} node - * @param {import('zimmerframe').Context} context + * @param {Fragment | null | undefined} node + * @param {Context} context */ function validate_block_not_empty(node, context) { if (!node) return; @@ -356,7 +360,7 @@ function validate_block_not_empty(node, context) { } /** - * @type {import('zimmerframe').Visitors} + * @type {Visitors} */ const validation = { MemberExpression(node, context) { @@ -465,7 +469,7 @@ const validation = { } if (parent.name === 'input' && node.name !== 'this') { - const type = /** @type {import('#compiler').Attribute | undefined} */ ( + const type = /** @type {Attribute | undefined} */ ( parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'type') ); if (type && !is_text_attribute(type)) { @@ -506,7 +510,7 @@ const validation = { } if (ContentEditableBindings.includes(node.name)) { - const contenteditable = /** @type {import('#compiler').Attribute} */ ( + const contenteditable = /** @type {Attribute} */ ( parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable') ); if (!contenteditable) { @@ -846,8 +850,7 @@ export const validation_legacy = merge(validation, a11y_validators, { LabeledStatement(node, { path, state }) { if ( node.label.name === '$' && - (state.ast_type !== 'instance' || - /** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program') + (state.ast_type !== 'instance' || /** @type {SvelteNode} */ (path.at(-1)).type !== 'Program') ) { w.reactive_declaration_invalid_placement(node); } @@ -859,8 +862,8 @@ export const validation_legacy = merge(validation, a11y_validators, { /** * - * @param {import('estree').Node} node - * @param {import('../scope').Scope} scope + * @param {Node} node + * @param {Scope} scope * @param {string} name */ function validate_export(node, scope, name) { @@ -877,16 +880,16 @@ function validate_export(node, scope, name) { } /** - * @param {import('estree').CallExpression} node + * @param {CallExpression} node * @param {Scope} scope - * @param {import('#compiler').SvelteNode[]} path + * @param {SvelteNode[]} path * @returns */ function validate_call_expression(node, scope, path) { const rune = get_rune(node, scope); if (rune === null) return; - const parent = /** @type {import('#compiler').SvelteNode} */ (get_parent(path, -1)); + const parent = /** @type {SvelteNode} */ (get_parent(path, -1)); if (rune === '$props') { if (parent.type === 'VariableDeclarator') return; @@ -965,8 +968,8 @@ function validate_call_expression(node, scope, path) { } /** - * @param {import('estree').VariableDeclarator} node - * @param {import('./types.js').AnalysisState} state + * @param {VariableDeclarator} node + * @param {AnalysisState} state */ function ensure_no_module_import_conflict(node, state) { const ids = extract_identifiers(node.id); @@ -981,7 +984,7 @@ function ensure_no_module_import_conflict(node, state) { } /** - * @type {import('zimmerframe').Visitors} + * @type {Visitors} */ export const validation_runes_js = { ImportDeclaration(node) { @@ -1016,7 +1019,7 @@ export const validation_runes_js = { if (rune === null) return; - const args = /** @type {import('estree').CallExpression} */ (init).arguments; + const args = /** @type {CallExpression} */ (init).arguments; if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { e.rune_invalid_arguments_length(node, rune, 'exactly one argument'); @@ -1073,7 +1076,7 @@ export const validation_runes_js = { }, Identifier(node, { path, state }) { let i = path.length; - let parent = /** @type {import('estree').Expression} */ (path[--i]); + let parent = /** @type {Expression} */ (path[--i]); if ( Runes.includes(/** @type {Runes[number]} */ (node.name)) && @@ -1081,16 +1084,16 @@ export const validation_runes_js = { state.scope.get(node.name) === null && state.scope.get(node.name.slice(1)) === null ) { - /** @type {import('estree').Expression} */ + /** @type {Expression} */ let current = node; let name = node.name; while (parent.type === 'MemberExpression') { if (parent.computed) e.rune_invalid_computed_property(parent); - name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`; + name += `.${/** @type {Identifier} */ (parent.property).name}`; current = parent; - parent = /** @type {import('estree').Expression} */ (path[--i]); + parent = /** @type {Expression} */ (path[--i]); if (!Runes.includes(/** @type {Runes[number]} */ (name))) { if (name === '$effect.active') { @@ -1109,8 +1112,8 @@ export const validation_runes_js = { }; /** - * @param {import('../../errors.js').NodeLike} node - * @param {import('estree').Pattern | import('estree').Expression} argument + * @param {NodeLike} node + * @param {Pattern | Expression} argument * @param {Scope} scope * @param {boolean} is_binding */ @@ -1156,7 +1159,7 @@ function validate_no_const_assignment(node, argument, scope, is_binding) { * Validates that the opening of a control flow block is `{` immediately followed by the expected character. * In legacy mode whitespace is allowed inbetween. TODO remove once legacy mode is gone and move this into parser instead. * @param {{start: number; end: number}} node - * @param {import('./types.js').AnalysisState} state + * @param {AnalysisState} state * @param {string} expected */ function validate_opening_tag(node, state, expected) { @@ -1167,9 +1170,9 @@ function validate_opening_tag(node, state, expected) { } /** - * @param {import('estree').AssignmentExpression | import('estree').UpdateExpression} node - * @param {import('estree').Pattern | import('estree').Expression} argument - * @param {import('./types.js').AnalysisState} state + * @param {AssignmentExpression | UpdateExpression} node + * @param {Pattern | Expression} argument + * @param {AnalysisState} state */ function validate_assignment(node, argument, state) { validate_no_const_assignment(node, argument, state.scope, false); @@ -1192,9 +1195,9 @@ function validate_assignment(node, argument, state) { } } - let object = /** @type {import('estree').Expression | import('estree').Super} */ (argument); + let object = /** @type {Expression | Super} */ (argument); - /** @type {import('estree').Expression | import('estree').PrivateIdentifier | null} */ + /** @type {Expression | PrivateIdentifier | null} */ let property = null; while (object.type === 'MemberExpression') { @@ -1322,7 +1325,7 @@ export const validation_runes = merge(validation, a11y_validators, { if (rune === null) return; - const args = /** @type {import('estree').CallExpression} */ (init).arguments; + const args = /** @type {CallExpression} */ (init).arguments; // TODO some of this is duplicated with above, seems off if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { 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 d7dbe7b057..b68629cd4b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -1,3 +1,7 @@ +/** @import { ArrowFunctionExpression, AssignmentExpression, BinaryOperator, Expression, FunctionDeclaration, FunctionExpression, Identifier, MemberExpression, Node, Pattern, PrivateIdentifier, Statement } from 'estree' */ +/** @import { Binding, SvelteNode } from '#compiler' */ +/** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ +/** @import { Scope } from '../../scope.js' */ import * as b from '../../../utils/builders.js'; import { extract_identifiers, @@ -14,21 +18,21 @@ import { } from '../../../../constants.js'; /** - * @template {import('./types').ClientTransformState} State - * @param {import('estree').AssignmentExpression} node - * @param {import('zimmerframe').Context} context + * @template {ClientTransformState} State + * @param {AssignmentExpression} node + * @param {import('zimmerframe').Context} context * @returns */ export function get_assignment_value(node, { state, visit }) { if (node.left.type === 'Identifier') { const operator = node.operator; return operator === '=' - ? /** @type {import('estree').Expression} */ (visit(node.right)) + ? /** @type {Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 b.binary( - /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), + /** @type {BinaryOperator} */ (operator.slice(0, -1)), serialize_get_binding(node.left, state), - /** @type {import('estree').Expression} */ (visit(node.right)) + /** @type {Expression} */ (visit(node.right)) ); } else if ( node.left.type === 'MemberExpression' && @@ -38,21 +42,21 @@ export function get_assignment_value(node, { state, visit }) { ) { const operator = node.operator; return operator === '=' - ? /** @type {import('estree').Expression} */ (visit(node.right)) + ? /** @type {Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 b.binary( - /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), - /** @type {import('estree').Expression} */ (visit(node.left)), - /** @type {import('estree').Expression} */ (visit(node.right)) + /** @type {BinaryOperator} */ (operator.slice(0, -1)), + /** @type {Expression} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.right)) ); } else { - return /** @type {import('estree').Expression} */ (visit(node.right)); + return /** @type {Expression} */ (visit(node.right)); } } /** - * @param {import('#compiler').Binding} binding - * @param {import('./types').ClientTransformState} state + * @param {Binding} binding + * @param {ClientTransformState} state * @returns {boolean} */ export function is_state_source(binding, state) { @@ -63,9 +67,9 @@ export function is_state_source(binding, state) { } /** - * @param {import('estree').Identifier} node - * @param {import('./types').ClientTransformState} state - * @returns {import('estree').Expression} + * @param {Identifier} node + * @param {ClientTransformState} state + * @returns {Expression} */ export function serialize_get_binding(node, state) { const binding = state.scope.get(node.name); @@ -117,13 +121,13 @@ export function serialize_get_binding(node, state) { } /** - * @template {import('./types').ClientTransformState} State - * @param {import('estree').AssignmentExpression} node - * @param {import('zimmerframe').Context} context + * @template {ClientTransformState} State + * @param {AssignmentExpression} node + * @param {import('zimmerframe').Context} context * @param {() => any} fallback * @param {boolean | null} [prefix] - If the assignment is a transformed update expression, set this. Else `null` * @param {{skip_proxy_and_freeze?: boolean}} [options] - * @returns {import('estree').Expression} + * @returns {Expression} */ export function serialize_set_binding(node, context, fallback, prefix, options) { const { state, visit } = context; @@ -137,10 +141,10 @@ export function serialize_set_binding(node, context, fallback, prefix, options) // Turn assignment into an IIFE, so that `$.set` calls etc don't produce invalid code const tmp_id = context.state.scope.generate('tmp'); - /** @type {import('estree').AssignmentExpression[]} */ + /** @type {AssignmentExpression[]} */ const original_assignments = []; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const assignments = []; const paths = extract_paths(assignee); @@ -159,7 +163,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options) return fallback(); } - const rhs_expression = /** @type {import('estree').Expression} */ (visit(node.right)); + const rhs_expression = /** @type {Expression} */ (visit(node.right)); const iife_is_async = is_expression_async(rhs_expression) || @@ -271,8 +275,8 @@ export function serialize_set_binding(node, context, fallback, prefix, options) '$$_import_' + binding.node.name, b.assignment( node.operator, - /** @type {import('estree').Pattern} */ (visit(node.left)), - /** @type {import('estree').Expression} */ (visit(node.right)) + /** @type {Pattern} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.right)) ) ); } @@ -299,10 +303,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options) if (left === node.left) { const is_initial_proxy = binding.initial !== null && - should_proxy_or_freeze( - /**@type {import("estree").Expression}*/ (binding.initial), - context.state.scope - ); + should_proxy_or_freeze(/**@type {Expression}*/ (binding.initial), context.state.scope); if ((binding.kind === 'prop' || binding.kind === 'bindable_prop') && !is_initial_proxy) { return b.call(left, value); } else if (is_store) { @@ -359,38 +360,31 @@ export function serialize_set_binding(node, context, fallback, prefix, options) // keep consistency with how store $ shorthand reads work in Svelte 4. /** * - * @param {import("estree").Expression | import("estree").Pattern} node - * @returns {import("estree").Expression} + * @param {Expression | Pattern} node + * @returns {Expression} */ function visit_node(node) { if (node.type === 'MemberExpression') { return { ...node, - object: visit_node(/** @type {import("estree").Expression} */ (node.object)), - property: /** @type {import("estree").MemberExpression} */ (visit(node)).property + object: visit_node(/** @type {Expression} */ (node.object)), + property: /** @type {MemberExpression} */ (visit(node)).property }; } if (node.type === 'Identifier') { const binding = state.scope.get(node.name); if (binding !== null && binding.kind === 'store_sub') { - return b.call( - '$.untrack', - b.thunk(/** @type {import('estree').Expression} */ (visit(node))) - ); + return b.call('$.untrack', b.thunk(/** @type {Expression} */ (visit(node)))); } } - return /** @type {import("estree").Expression} */ (visit(node)); + return /** @type {Expression} */ (visit(node)); } return b.call( '$.mutate_store', serialize_get_binding(b.id(left_name), state), - b.assignment( - node.operator, - /** @type {import("estree").Pattern}} */ (visit_node(node.left)), - value - ), + b.assignment(node.operator, /** @type {Pattern}} */ (visit_node(node.left)), value), b.call('$.untrack', b.id('$' + left_name)) ); } else if ( @@ -401,22 +395,14 @@ export function serialize_set_binding(node, context, fallback, prefix, options) if (binding.kind === 'bindable_prop') { return b.call( left, - b.assignment( - node.operator, - /** @type {import('estree').Pattern} */ (visit(node.left)), - value - ), + b.assignment(node.operator, /** @type {Pattern} */ (visit(node.left)), value), b.true ); } else { return b.call( '$.mutate', b.id(left_name), - b.assignment( - node.operator, - /** @type {import('estree').Pattern} */ (visit(node.left)), - value - ) + b.assignment(node.operator, /** @type {Pattern} */ (visit(node.left)), value) ); } } else if ( @@ -426,14 +412,14 @@ export function serialize_set_binding(node, context, fallback, prefix, options) ) { return b.update( node.operator === '+=' ? '++' : '--', - /** @type {import('estree').Expression} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.left)), prefix ); } else { return b.assignment( node.operator, - /** @type {import('estree').Pattern} */ (visit(node.left)), - /** @type {import('estree').Expression} */ (visit(node.right)) + /** @type {Pattern} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.right)) ); } } @@ -447,9 +433,9 @@ export function serialize_set_binding(node, context, fallback, prefix, options) } /** - * @param {import('estree').Expression} value - * @param {import('estree').PrivateIdentifier | string} proxy_reference - * @param {import('./types').ClientTransformState} state + * @param {Expression} value + * @param {PrivateIdentifier | string} proxy_reference + * @param {ClientTransformState} state */ export function serialize_proxy_reassignment(value, proxy_reference, state) { return state.options.dev @@ -465,8 +451,8 @@ export function serialize_proxy_reassignment(value, proxy_reference, state) { } /** - * @param {import('estree').ArrowFunctionExpression | import('estree').FunctionExpression} node - * @param {import('./types').ComponentContext} context + * @param {ArrowFunctionExpression | FunctionExpression} node + * @param {ComponentContext} context */ export const function_visitor = (node, context) => { const metadata = node.metadata; @@ -474,7 +460,7 @@ export const function_visitor = (node, context) => { let state = context.state; if (node.type === 'FunctionExpression') { - const parent = /** @type {import('estree').Node} */ (context.path.at(-1)); + const parent = /** @type {Node} */ (context.path.at(-1)); const in_constructor = parent.type === 'MethodDefinition' && parent.kind === 'constructor'; state = { ...context.state, in_constructor }; @@ -485,7 +471,7 @@ export const function_visitor = (node, context) => { if (metadata?.hoistable === true) { const params = serialize_hoistable_params(node, context); - return /** @type {import('estree').FunctionExpression} */ ({ + return /** @type {FunctionExpression} */ ({ ...node, params, body: context.visit(node.body, state) @@ -496,19 +482,19 @@ export const function_visitor = (node, context) => { }; /** - * @param {import('estree').FunctionDeclaration | import('estree').FunctionExpression | import('estree').ArrowFunctionExpression} node - * @param {import('./types').ComponentContext} context - * @returns {import('estree').Pattern[]} + * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node + * @param {ComponentContext} context + * @returns {Pattern[]} */ function get_hoistable_params(node, context) { const scope = context.state.scope; - /** @type {import('estree').Identifier[]} */ + /** @type {Identifier[]} */ const params = []; /** * We only want to push if it's not already present to avoid name clashing - * @param {import('estree').Identifier} id + * @param {Identifier} id */ function push_unique(id) { if (!params.find((param) => param.name === id.name)) { @@ -523,9 +509,7 @@ function get_hoistable_params(node, context) { if (binding.kind === 'store_sub') { // We need both the subscription for getting the value and the store for updating push_unique(b.id(binding.node.name)); - binding = /** @type {import('#compiler').Binding} */ ( - scope.get(binding.node.name.slice(1)) - ); + binding = /** @type {Binding} */ (scope.get(binding.node.name.slice(1))); } const expression = context.state.getters[reference]; @@ -566,15 +550,15 @@ function get_hoistable_params(node, context) { } /** - * @param {import('estree').FunctionDeclaration | import('estree').FunctionExpression | import('estree').ArrowFunctionExpression} node - * @param {import('./types').ComponentContext} context - * @returns {import('estree').Pattern[]} + * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node + * @param {ComponentContext} context + * @returns {Pattern[]} */ export function serialize_hoistable_params(node, context) { const hoistable_params = get_hoistable_params(node, context); node.metadata.hoistable_params = hoistable_params; - /** @type {import('estree').Pattern[]} */ + /** @type {Pattern[]} */ const params = []; if (node.params.length === 0) { @@ -584,7 +568,7 @@ export function serialize_hoistable_params(node, context) { } } else { for (const param of node.params) { - params.push(/** @type {import('estree').Pattern} */ (context.visit(param))); + params.push(/** @type {Pattern} */ (context.visit(param))); } } @@ -593,14 +577,14 @@ export function serialize_hoistable_params(node, context) { } /** - * @param {import('#compiler').Binding} binding - * @param {import('./types').ComponentClientTransformState} state + * @param {Binding} binding + * @param {ComponentClientTransformState} state * @param {string} name - * @param {import('estree').Expression | null} [initial] + * @param {Expression | null} [initial] * @returns */ export function get_prop_source(binding, state, name, initial) { - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const args = [b.id('$$props'), b.literal(name)]; let flags = 0; @@ -622,7 +606,7 @@ export function get_prop_source(binding, state, name, initial) { flags |= PROPS_IS_UPDATED; } - /** @type {import('estree').Expression | undefined} */ + /** @type {Expression | undefined} */ let arg; if (initial) { @@ -654,8 +638,8 @@ export function get_prop_source(binding, state, name, initial) { /** * - * @param {import('#compiler').Binding} binding - * @param {import('./types').ClientTransformState} state + * @param {Binding} binding + * @param {ClientTransformState} state * @returns */ export function is_prop_source(binding, state) { @@ -672,8 +656,8 @@ export function is_prop_source(binding, state) { } /** - * @param {import('estree').Expression} node - * @param {import("../../scope.js").Scope | null} scope + * @param {Expression} node + * @param {Scope | null} scope */ export function should_proxy_or_freeze(node, scope) { if ( @@ -710,8 +694,8 @@ export function should_proxy_or_freeze(node, scope) { * Port over the location information from the source to the target identifier. * but keep the target as-is (i.e. a new id is created). * This ensures esrap can generate accurate source maps. - * @param {import('estree').Identifier} target - * @param {import('estree').Identifier} source + * @param {Identifier} target + * @param {Identifier} source */ export function with_loc(target, source) { if (source.loc) { @@ -721,16 +705,16 @@ export function with_loc(target, source) { } /** - * @param {import("estree").Pattern} node - * @param {import("zimmerframe").Context} context - * @returns {{ id: import("estree").Pattern, declarations: null | import("estree").Statement[] }} + * @param {Pattern} node + * @param {import('zimmerframe').Context} context + * @returns {{ id: Pattern, declarations: null | Statement[] }} */ export function create_derived_block_argument(node, context) { if (node.type === 'Identifier') { return { id: node, declarations: null }; } - const pattern = /** @type {import('estree').Pattern} */ (context.visit(node)); + const pattern = /** @type {Pattern} */ (context.visit(node)); const identifiers = extract_identifiers(node); const id = b.id('$$source'); @@ -754,8 +738,8 @@ export function create_derived_block_argument(node, context) { /** * Svelte legacy mode should use safe equals in most places, runes mode shouldn't - * @param {import('./types.js').ComponentClientTransformState} state - * @param {import('estree').Expression} arg + * @param {ComponentClientTransformState} state + * @param {Expression} arg */ export function create_derived(state, arg) { return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js index a640fb01b0..8d4698c663 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/global.js @@ -1,11 +1,13 @@ +/** @import { Expression, Node, Pattern, Statement } from 'estree' */ +/** @import { Visitors } from '../types' */ import is_reference from 'is-reference'; import { serialize_get_binding, serialize_set_binding } from '../utils.js'; import * as b from '../../../../utils/builders.js'; -/** @type {import('../types').Visitors} */ +/** @type {Visitors} */ export const global_visitors = { Identifier(node, { path, state }) { - if (is_reference(node, /** @type {import('estree').Node} */ (path.at(-1)))) { + if (is_reference(node, /** @type {Node} */ (path.at(-1)))) { if (node.name === '$$props') { return b.id('$$sanitized_props'); } @@ -74,7 +76,7 @@ export const global_visitors = { binding?.kind === 'bindable_prop' || is_store ) { - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const args = []; let fn = '$.update'; @@ -105,7 +107,7 @@ export const global_visitors = { let fn = '$.update'; if (node.prefix) fn += '_pre'; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const args = [argument]; if (node.operator === '--') { args.push(b.literal(-1)); @@ -116,7 +118,7 @@ export const global_visitors = { // turn it into an IIFEE assignment expression: i++ -> (() => { const $$value = i; i+=1; return $$value; }) const assignment = b.assignment( node.operator === '++' ? '+=' : '-=', - /** @type {import('estree').Pattern} */ (argument), + /** @type {Pattern} */ (argument), b.literal(1) ); const serialized_assignment = serialize_set_binding( @@ -125,14 +127,14 @@ export const global_visitors = { () => assignment, node.prefix ); - const value = /** @type {import('estree').Expression} */ (visit(argument)); + const value = /** @type {Expression} */ (visit(argument)); if (serialized_assignment === assignment) { // No change to output -> nothing to transform -> we can keep the original update expression return next(); } else if (context.state.analysis.runes) { return serialized_assignment; } else { - /** @type {import('estree').Statement[]} */ + /** @type {Statement[]} */ let statements; if (node.prefix) { statements = [b.stmt(serialized_assignment), b.return(value)]; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 71524b539a..f24514a8f9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -1,3 +1,6 @@ +/** @import { CallExpression, Expression, Identifier, Literal, MethodDefinition, PrivateIdentifier, PropertyDefinition, VariableDeclarator } from 'estree' */ +/** @import { Binding } from '#compiler' */ +/** @import { ComponentVisitors, StateField } from '../types.js' */ import { get_rune } from '../../../scope.js'; import { is_hoistable_function, transform_inspect_rune } from '../../utils.js'; import * as b from '../../../../utils/builders.js'; @@ -12,13 +15,13 @@ import { import { extract_paths } from '../../../../utils/ast.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js'; -/** @type {import('../types.js').ComponentVisitors} */ +/** @type {ComponentVisitors} */ export const javascript_visitors_runes = { ClassBody(node, { state, visit }) { - /** @type {Map} */ + /** @type {Map} */ const public_state = new Map(); - /** @type {Map} */ + /** @type {Map} */ const private_state = new Map(); /** @type {string[]} */ @@ -46,7 +49,7 @@ export const javascript_visitors_runes = { rune === '$derived' || rune === '$derived.by' ) { - /** @type {import('../types.js').StateField} */ + /** @type {StateField} */ const field = { kind: rune === '$state' @@ -81,7 +84,7 @@ export const javascript_visitors_runes = { field.id = b.private_id(deconflicted); } - /** @type {Array} */ + /** @type {Array} */ const body = []; const child_state = { ...state, public_state, private_state }; @@ -104,7 +107,7 @@ export const javascript_visitors_runes = { let value = null; if (definition.value.arguments.length > 0) { - const init = /** @type {import('estree').Expression} **/ ( + const init = /** @type {Expression} **/ ( visit(definition.value.arguments[0], child_state) ); @@ -182,7 +185,7 @@ export const javascript_visitors_runes = { } } - body.push(/** @type {import('estree').MethodDefinition} **/ (visit(definition, child_state))); + body.push(/** @type {MethodDefinition} **/ (visit(definition, child_state))); } if (state.options.dev && public_state.size > 0) { @@ -225,15 +228,11 @@ export const javascript_visitors_runes = { if (init != null && is_hoistable_function(init)) { const hoistable_function = visit(init); state.hoisted.push( - b.declaration( - 'const', - declarator.id, - /** @type {import('estree').Expression} */ (hoistable_function) - ) + b.declaration('const', declarator.id, /** @type {Expression} */ (hoistable_function)) ); continue; } - declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); + declarations.push(/** @type {VariableDeclarator} */ (visit(declarator))); continue; } @@ -246,7 +245,7 @@ export const javascript_visitors_runes = { } if (declarator.id.type === 'Identifier') { - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; if (state.options.dev) { @@ -260,9 +259,7 @@ export const javascript_visitors_runes = { for (const property of declarator.id.properties) { if (property.type === 'Property') { - const key = /** @type {import('estree').Identifier | import('estree').Literal} */ ( - property.key - ); + const key = /** @type {Identifier | Literal} */ (property.key); const name = key.type === 'Identifier' ? key.name : /** @type {string} */ (key.value); seen.push(name); @@ -270,10 +267,8 @@ export const javascript_visitors_runes = { let id = property.value.type === 'AssignmentPattern' ? property.value.left : property.value; assert.equal(id.type, 'Identifier'); - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); - let initial = - binding.initial && - /** @type {import('estree').Expression} */ (visit(binding.initial)); + const binding = /** @type {Binding} */ (state.scope.get(id.name)); + let initial = binding.initial && /** @type {Expression} */ (visit(binding.initial)); // We're adding proxy here on demand and not within the prop runtime function so that // people not using proxied state anywhere in their code don't have to pay the additional bundle size cost if ( @@ -289,14 +284,12 @@ export const javascript_visitors_runes = { } } else { // RestElement - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; if (state.options.dev) { // include rest name, so we can provide informative error messages - args.push( - b.literal(/** @type {import('estree').Identifier} */ (property.argument).name) - ); + args.push(b.literal(/** @type {Identifier} */ (property.argument).name)); } declarations.push(b.declarator(property.argument, b.call('$.rest_props', ...args))); @@ -308,19 +301,17 @@ export const javascript_visitors_runes = { continue; } - const args = /** @type {import('estree').CallExpression} */ (init).arguments; + const args = /** @type {CallExpression} */ (init).arguments; const value = - args.length === 0 - ? b.id('undefined') - : /** @type {import('estree').Expression} */ (visit(args[0])); + args.length === 0 ? b.id('undefined') : /** @type {Expression} */ (visit(args[0])); if (rune === '$state' || rune === '$state.frozen') { /** - * @param {import('estree').Identifier} id - * @param {import('estree').Expression} value + * @param {Identifier} id + * @param {Expression} value */ const create_state_declarator = (id, value) => { - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); + const binding = /** @type {Binding} */ (state.scope.get(id.name)); if (should_proxy_or_freeze(value, state.scope)) { value = b.call(rune === '$state' ? '$.proxy' : '$.freeze', value); } @@ -341,9 +332,7 @@ export const javascript_visitors_runes = { b.declarator(b.id(tmp), value), ...paths.map((path) => { const value = path.expression?.(b.id(tmp)); - const binding = state.scope.get( - /** @type {import('estree').Identifier} */ (path.node).name - ); + const binding = state.scope.get(/** @type {Identifier} */ (path.node).name); return b.declarator( path.node, binding?.kind === 'state' || binding?.kind === 'frozen_state' @@ -426,7 +415,7 @@ export const javascript_visitors_runes = { const func = context.visit(node.expression.arguments[0]); return { ...node, - expression: b.call('$.user_effect', /** @type {import('estree').Expression} */ (func)) + expression: b.call('$.user_effect', /** @type {Expression} */ (func)) }; } @@ -441,7 +430,7 @@ export const javascript_visitors_runes = { const func = context.visit(node.expression.arguments[0]); return { ...node, - expression: b.call('$.user_pre_effect', /** @type {import('estree').Expression} */ (func)) + expression: b.call('$.user_pre_effect', /** @type {Expression} */ (func)) }; } } @@ -460,24 +449,19 @@ export const javascript_visitors_runes = { } if (rune === '$state.snapshot') { - return b.call( - '$.snapshot', - /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) - ); + return b.call('$.snapshot', /** @type {Expression} */ (context.visit(node.arguments[0]))); } if (rune === '$state.is') { return b.call( '$.is', - /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])), - /** @type {import('estree').Expression} */ (context.visit(node.arguments[1])) + /** @type {Expression} */ (context.visit(node.arguments[0])), + /** @type {Expression} */ (context.visit(node.arguments[1])) ); } if (rune === '$effect.root') { - const args = /** @type {import('estree').Expression[]} */ ( - node.arguments.map((arg) => context.visit(arg)) - ); + const args = /** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg))); return b.call('$.effect_root', ...args); } @@ -494,8 +478,8 @@ export const javascript_visitors_runes = { if (operator === '===' || operator === '!==') { return b.call( '$.strict_equals', - /** @type {import('estree').Expression} */ (visit(node.left)), - /** @type {import('estree').Expression} */ (visit(node.right)), + /** @type {Expression} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.right)), operator === '!==' && b.literal(false) ); } @@ -503,8 +487,8 @@ export const javascript_visitors_runes = { if (operator === '==' || operator === '!=') { return b.call( '$.equals', - /** @type {import('estree').Expression} */ (visit(node.left)), - /** @type {import('estree').Expression} */ (visit(node.right)), + /** @type {Expression} */ (visit(node.left)), + /** @type {Expression} */ (visit(node.right)), operator === '!=' && b.literal(false) ); } @@ -515,7 +499,7 @@ export const javascript_visitors_runes = { }; /** - * @param {import('estree').Identifier | import('estree').PrivateIdentifier | import('estree').Literal} node + * @param {Identifier | PrivateIdentifier | Literal} node */ function get_name(node) { if (node.type === 'Literal') { From c548932390bd01e30d8faa204c65575ef58691e3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 16:18:45 -0400 Subject: [PATCH 5/7] chore: use JSDoc imports (#12592) * more * more * more * more * more * more * more * regenerate types --- .../src/compiler/phases/2-analyze/index.js | 2 +- .../3-transform/client/visitors/template.js | 171 ++++--- .../3-transform/server/transform-server.js | 422 ++++++++---------- packages/svelte/src/index-client.js | 11 +- packages/svelte/src/index-server.js | 3 +- .../src/internal/client/dom/blocks/each.js | 82 ++-- packages/svelte/src/internal/client/proxy.js | 31 +- packages/svelte/src/internal/client/render.js | 20 +- .../svelte/src/internal/client/runtime.js | 94 ++-- packages/svelte/types/index.d.ts | 4 +- 10 files changed, 400 insertions(+), 440 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index f130ba18e2..aa1e43196f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -901,7 +901,7 @@ const legacy_scope_tweaker = { } }; -/** @type {import('zimmerframe').Visitors} */ +/** @type {Visitors} */ const runes_scope_js_tweaker = { VariableDeclarator(node, { state }) { if (node.init?.type !== 'CallExpression') return; 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 7953431e10..d4cf97042b 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 @@ -1,5 +1,8 @@ /** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */ -/** @import { BindDirective } from '#compiler' */ +/** @import { Attribute, BindDirective, Binding, ClassDirective, Component, DelegatedEvent, EachBlock, ExpressionTag, Namespace, OnDirective, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text } from '#compiler' */ +/** @import { SourceLocation } from '#shared' */ +/** @import { Scope } from '../../../scope.js' */ +/** @import { ComponentClientTransformState, ComponentContext, ComponentVisitors } from '../types.js' */ import { extract_identifiers, extract_paths, @@ -54,9 +57,9 @@ import { locator } from '../../../../state.js'; import is_reference from 'is-reference'; /** - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element - * @param {import('#compiler').Attribute} attribute - * @param {{ state: { metadata: { namespace: import('#compiler').Namespace }}}} context + * @param {RegularElement | SvelteElement} element + * @param {Attribute} attribute + * @param {{ state: { metadata: { namespace: Namespace }}}} context */ function get_attribute_name(element, attribute, context) { let name = attribute.name; @@ -76,9 +79,9 @@ function get_attribute_name(element, attribute, context) { /** * Serializes each style directive into something like `$.set_style(element, style_property, value)` * and adds it either to init or update, depending on whether or not the value or the attributes are dynamic. - * @param {import('#compiler').StyleDirective[]} style_directives + * @param {StyleDirective[]} style_directives * @param {Identifier} element_id - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context * @param {boolean} is_attributes_reactive */ function serialize_style_directives(style_directives, element_id, context, is_attributes_reactive) { @@ -138,9 +141,9 @@ function parse_directive_name(name) { /** * Serializes each class directive into something like `$.class_toogle(element, class_name, value)` * and adds it either to init or update, depending on whether or not the value or the attributes are dynamic. - * @param {import('#compiler').ClassDirective[]} class_directives + * @param {ClassDirective[]} class_directives * @param {Identifier} element_id - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context * @param {boolean} is_attributes_reactive */ function serialize_class_directives(class_directives, element_id, context, is_attributes_reactive) { @@ -161,11 +164,11 @@ function serialize_class_directives(class_directives, element_id, context, is_at } /** - * @param {import('#compiler').Binding[]} references - * @param {import('../types.js').ComponentContext} context + * @param {Binding[]} references + * @param {ComponentContext} context */ function serialize_transitive_dependencies(references, context) { - /** @type {Set} */ + /** @type {Set} */ const dependencies = new Set(); for (const ref of references) { @@ -179,9 +182,9 @@ function serialize_transitive_dependencies(references, context) { } /** - * @param {import('#compiler').Binding} binding - * @param {Set} seen - * @returns {import('#compiler').Binding[]} + * @param {Binding} binding + * @param {Set} seen + * @returns {Binding[]} */ function collect_transitive_dependencies(binding, seen = new Set()) { if (binding.kind !== 'legacy_reactive') return []; @@ -201,8 +204,8 @@ function collect_transitive_dependencies(binding, seen = new Set()) { /** * Special case: if we have a value binding on a select element, we need to set up synchronization * between the value binding and inner signals, for indirect updates - * @param {import('#compiler').BindDirective} value_binding - * @param {import('../types.js').ComponentContext} context + * @param {BindDirective} value_binding + * @param {ComponentContext} context */ function setup_select_synchronization(value_binding, context) { if (context.state.analysis.runes) return; @@ -253,9 +256,9 @@ function setup_select_synchronization(value_binding, context) { } /** - * @param {Array} attributes - * @param {import('../types.js').ComponentContext} context - * @param {import('#compiler').RegularElement} element + * @param {Array} attributes + * @param {ComponentContext} context + * @param {RegularElement} element * @param {Identifier} element_id * @param {boolean} needs_select_handling */ @@ -358,8 +361,8 @@ function serialize_element_spread_attributes( /** * Serializes dynamic element attribute assignments. * Returns the `true` if spread is deemed reactive. - * @param {Array} attributes - * @param {import('../types.js').ComponentContext} context + * @param {Array} attributes + * @param {ComponentContext} context * @param {Identifier} element_id * @returns {boolean} */ @@ -472,10 +475,10 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) { * }); * ``` * Returns true if attribute is deemed reactive, false otherwise. - * @param {import('#compiler').RegularElement} element + * @param {RegularElement} element * @param {Identifier} node_id - * @param {import('#compiler').Attribute} attribute - * @param {import('../types.js').ComponentContext} context + * @param {Attribute} attribute + * @param {ComponentContext} context * @returns {boolean} */ function serialize_element_attribute_update_assignment(element, node_id, attribute, context) { @@ -542,8 +545,8 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu /** * Like `serialize_element_attribute_update_assignment` but without any special attribute treatment. * @param {Identifier} node_id - * @param {import('#compiler').Attribute} attribute - * @param {import('../types.js').ComponentContext} context + * @param {Attribute} attribute + * @param {ComponentContext} context * @returns {boolean} */ function serialize_custom_element_attribute_update_assignment(node_id, attribute, context) { @@ -572,8 +575,8 @@ function serialize_custom_element_attribute_update_assignment(node_id, attribute * Returns true if attribute is deemed reactive, false otherwise. * @param {string} element * @param {Identifier} node_id - * @param {import('#compiler').Attribute} attribute - * @param {import('../types.js').ComponentContext} context + * @param {Attribute} attribute + * @param {ComponentContext} context * @returns {boolean} */ function serialize_element_special_value_attribute(element, node_id, attribute, context) { @@ -632,7 +635,7 @@ function serialize_element_special_value_attribute(element, node_id, attribute, } /** - * @param {import('../types.js').ComponentClientTransformState} state + * @param {ComponentClientTransformState} state * @param {string} id * @param {Expression | undefined} init * @param {Expression} value @@ -646,18 +649,16 @@ function serialize_update_assignment(state, id, init, value, update) { } /** - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context */ function collect_parent_each_blocks(context) { - return /** @type {import('#compiler').EachBlock[]} */ ( - context.path.filter((node) => node.type === 'EachBlock') - ); + return /** @type {EachBlock[]} */ (context.path.filter((node) => node.type === 'EachBlock')); } /** - * @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node + * @param {Component | SvelteComponent | SvelteSelf} node * @param {string} component_name - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context * @param {Expression} anchor * @returns {Statement} */ @@ -668,7 +669,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont /** @type {ExpressionStatement[]} */ const lets = []; - /** @type {Record} */ + /** @type {Record} */ const children = {}; /** @type {Record} */ @@ -681,7 +682,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont let bind_this = null; /** - * @type {import("estree").ExpressionStatement[]} + * @type {ExpressionStatement[]} */ const binding_initializers = []; @@ -844,14 +845,14 @@ function serialize_inline_component(node, component_name, context, anchor = cont let slot_name = 'default'; if (is_element_node(child)) { - const attribute = /** @type {import('#compiler').Attribute | undefined} */ ( + const attribute = /** @type {Attribute | undefined} */ ( child.attributes.find( (attribute) => attribute.type === 'Attribute' && attribute.name === 'slot' ) ); if (attribute !== undefined) { - slot_name = /** @type {import('#compiler').Text[]} */ (attribute.value)[0].data; + slot_name = /** @type {Text[]} */ (attribute.value)[0].data; } } @@ -995,7 +996,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont * Serializes `bind:this` for components and elements. * @param {Identifier | MemberExpression} expression * @param {Expression} value - * @param {import('zimmerframe').Context} context + * @param {import('zimmerframe').Context} context */ function serialize_bind_this(expression, value, { state, visit }) { /** @type {Identifier[]} */ @@ -1059,7 +1060,7 @@ function serialize_bind_this(expression, value, { state, visit }) { } /** - * @param {import('#shared').SourceLocation[]} locations + * @param {SourceLocation[]} locations */ function serialize_locations(locations) { return b.array( @@ -1077,8 +1078,8 @@ function serialize_locations(locations) { /** * - * @param {import('#compiler').Namespace} namespace - * @param {import('../types.js').ComponentClientTransformState} state + * @param {Namespace} namespace + * @param {ComponentClientTransformState} state * @returns */ function get_template_function(namespace, state) { @@ -1116,9 +1117,9 @@ function serialize_render_stmt(update) { /** * Serializes the event handler function of the `on:` directive - * @param {Pick} node + * @param {Pick} node * @param {null | { contains_call_expression: boolean; dynamic: boolean; } | null} metadata - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context */ function serialize_event_handler(node, metadata, { state, visit }) { /** @type {Expression} */ @@ -1225,9 +1226,9 @@ function serialize_event_handler(node, metadata, { state, visit }) { /** * Serializes an event handler function of the `on:` directive or an attribute starting with `on` - * @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: import('#compiler').DelegatedEvent | null;}} node + * @param {{name: string;modifiers: string[];expression: Expression | null;delegated?: DelegatedEvent | null;}} node * @param {null | { contains_call_expression: boolean; dynamic: boolean; }} metadata - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context */ function serialize_event(node, metadata, context) { const state = context.state; @@ -1314,7 +1315,7 @@ function serialize_event(node, metadata, context) { ); } - const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1)); + const parent = /** @type {SvelteNode} */ (context.path.at(-1)); if ( parent.type === 'SvelteDocument' || parent.type === 'SvelteWindow' || @@ -1328,8 +1329,8 @@ function serialize_event(node, metadata, context) { } /** - * @param {import('#compiler').Attribute & { value: import('#compiler').ExpressionTag | [import('#compiler').ExpressionTag] }} node - * @param {import('../types').ComponentContext} context + * @param {Attribute & { value: ExpressionTag | [ExpressionTag] }} node + * @param {ComponentContext} context */ function serialize_event_attribute(node, context) { /** @type {string[]} */ @@ -1357,15 +1358,15 @@ function serialize_event_attribute(node, context) { * Processes an array of template nodes, joining sibling text/expression nodes * (e.g. `{a} b {c}`) into a single update function. Along the way it creates * corresponding template node references these updates are applied to. - * @param {import('#compiler').SvelteNode[]} nodes + * @param {SvelteNode[]} nodes * @param {(is_text: boolean) => Expression} expression * @param {boolean} is_element - * @param {import('../types.js').ComponentContext} context + * @param {ComponentContext} context */ function process_children(nodes, expression, is_element, { visit, state }) { const within_bound_contenteditable = state.metadata.bound_contenteditable; - /** @typedef {Array} Sequence */ + /** @typedef {Array} Sequence */ /** @type {Sequence} */ let sequence = []; @@ -1491,7 +1492,7 @@ function process_children(nodes, expression, is_element, { visit, state }) { /** * @param {Expression} expression - * @param {import('../types.js').ComponentClientTransformState} state + * @param {ComponentClientTransformState} state * @param {string} name */ function get_node_id(expression, state, name) { @@ -1506,8 +1507,8 @@ function get_node_id(expression, state, name) { } /** - * @param {import('#compiler').Attribute['value']} value - * @param {import('../types').ComponentContext} context + * @param {Attribute['value']} value + * @param {ComponentContext} context * @returns {[contains_call_expression: boolean, Expression]} */ function serialize_attribute_value(value, context) { @@ -1532,9 +1533,9 @@ function serialize_attribute_value(value, context) { } /** - * @param {Array} values - * @param {(node: import('#compiler').SvelteNode, state: any) => any} visit - * @param {import("../types.js").ComponentClientTransformState} state + * @param {Array} values + * @param {(node: SvelteNode, state: any) => any} visit + * @param {ComponentClientTransformState} state * @returns {[boolean, TemplateLiteral]} */ function serialize_template_literal(values, visit, state) { @@ -1594,7 +1595,7 @@ function serialize_template_literal(values, visit, state) { return [contains_call_expression, b.template(quasis, expressions)]; } -/** @type {import('../types').ComponentVisitors} */ +/** @type {ComponentVisitors} */ export const template_visitors = { Fragment(node, context) { // Creates a new block which looks roughly like this: @@ -1640,7 +1641,7 @@ export const template_visitors = { /** @type {Statement | undefined} */ let close = undefined; - /** @type {import('../types').ComponentClientTransformState} */ + /** @type {ComponentClientTransformState} */ const state = { ...context.state, before_init: [], @@ -1688,7 +1689,7 @@ export const template_visitors = { }; if (is_single_element) { - const element = /** @type {import('#compiler').RegularElement} */ (trimmed[0]); + const element = /** @type {RegularElement} */ (trimmed[0]); const id = b.id(context.state.scope.generate(element.name)); @@ -1963,7 +1964,7 @@ export const template_visitors = { state.after_update.push(b.stmt(b.call('$.transition', ...args))); }, RegularElement(node, context) { - /** @type {import('#shared').SourceLocation} */ + /** @type {SourceLocation} */ let location = [-1, -1]; if (context.state.options.dev) { @@ -1991,13 +1992,13 @@ export const template_visitors = { context.state.template.push(`<${node.name}`); - /** @type {Array} */ + /** @type {Array} */ const attributes = []; - /** @type {import('#compiler').ClassDirective[]} */ + /** @type {ClassDirective[]} */ const class_directives = []; - /** @type {import('#compiler').StyleDirective[]} */ + /** @type {StyleDirective[]} */ const style_directives = []; /** @type {ExpressionStatement[]} */ @@ -2007,7 +2008,7 @@ export const template_visitors = { let needs_input_reset = false; let needs_content_reset = false; - /** @type {import('#compiler').BindDirective | null} */ + /** @type {BindDirective | null} */ let value_binding = null; /** If true, needs `__value` for inputs */ @@ -2129,7 +2130,7 @@ export const template_visitors = { ); is_attributes_reactive = true; } else { - for (const attribute of /** @type {import('#compiler').Attribute[]} */ (attributes)) { + for (const attribute of /** @type {Attribute[]} */ (attributes)) { if (is_event_attribute(attribute)) { if ( (attribute.name === 'onload' || attribute.name === 'onerror') && @@ -2191,17 +2192,15 @@ export const template_visitors = { context.state.template.push('>'); - /** @type {import('#shared').SourceLocation[]} */ + /** @type {SourceLocation[]} */ const child_locations = []; - /** @type {import('../types').ComponentClientTransformState} */ + /** @type {ComponentClientTransformState} */ const state = { ...context.state, metadata: child_metadata, locations: child_locations, - scope: /** @type {import('../../../scope').Scope} */ ( - context.state.scopes.get(node.fragment) - ), + scope: /** @type {Scope} */ (context.state.scopes.get(node.fragment)), preserve_whitespace: context.state.preserve_whitespace || ((node.name === 'pre' || node.name === 'textarea') && @@ -2284,16 +2283,16 @@ export const template_visitors = { SvelteElement(node, context) { context.state.template.push(``); - /** @type {Array} */ + /** @type {Array} */ const attributes = []; - /** @type {import('#compiler').Attribute['value'] | undefined} */ + /** @type {Attribute['value'] | undefined} */ let dynamic_namespace = undefined; - /** @type {import('#compiler').ClassDirective[]} */ + /** @type {ClassDirective[]} */ const class_directives = []; - /** @type {import('#compiler').StyleDirective[]} */ + /** @type {StyleDirective[]} */ const style_directives = []; /** @type {ExpressionStatement[]} */ @@ -2303,7 +2302,7 @@ export const template_visitors = { // They'll then be added to the function parameter of $.element const element_id = b.id(context.state.scope.generate('$$element')); - /** @type {import('../types').ComponentContext} */ + /** @type {ComponentContext} */ const inner_context = { ...context, state: { @@ -2491,7 +2490,7 @@ export const template_visitors = { /** * @param {Pattern} expression_for_id - * @returns {import('#compiler').Binding['mutation']} + * @returns {Binding['mutation']} */ const create_mutation = (expression_for_id) => { return (assignment, context) => { @@ -2536,8 +2535,8 @@ export const template_visitors = { ? each_node_meta.index : b.id(node.index); const item = each_node_meta.item; - const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(item.name)); - const getter = (/** @type {import("estree").Identifier} */ id) => { + const binding = /** @type {Binding} */ (context.state.scope.get(item.name)); + const getter = (/** @type {Identifier} */ id) => { const item_with_loc = with_loc(item, id); return b.call('$.unwrap', item_with_loc); }; @@ -2567,7 +2566,7 @@ export const template_visitors = { for (const path of paths) { const name = /** @type {Identifier} */ (path.node).name; - const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(name)); + const binding = /** @type {Binding} */ (context.state.scope.get(name)); const needs_derived = path.has_default_value; // to ensure that default value is only called once const fn = b.thunk( /** @type {Expression} */ (context.visit(path.expression?.(unwrapped), child_state)) @@ -2789,7 +2788,7 @@ export const template_visitors = { for (const path of paths) { const name = /** @type {Identifier} */ (path.node).name; - const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(name)); + const binding = /** @type {Binding} */ (context.state.scope.get(name)); const needs_derived = path.has_default_value; // to ensure that default value is only called once const fn = b.thunk( /** @type {Expression} */ ( @@ -3048,7 +3047,7 @@ export const template_visitors = { const parent = path.at(-1); if (parent?.type === 'RegularElement') { const value = /** @type {any[]} */ ( - /** @type {import('#compiler').Attribute} */ ( + /** @type {Attribute} */ ( parent.attributes.find( (a) => a.type === 'Attribute' && @@ -3262,7 +3261,7 @@ export const template_visitors = { b.assignment( '=', b.member(b.id('$.document'), b.id('title')), - b.literal(/** @type {import('#compiler').Text} */ (node.fragment.nodes[0]).data) + b.literal(/** @type {Text} */ (node.fragment.nodes[0]).data) ) ) ); @@ -3301,7 +3300,7 @@ export const template_visitors = { }; /** - * @param {import('../types.js').ComponentClientTransformState} state + * @param {ComponentClientTransformState} state * @param {BindDirective} binding * @param {MemberExpression} expression */ 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 8cb31e3555..913206ab13 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 @@ -1,3 +1,10 @@ +/** @import { AssignmentExpression, AssignmentOperator, BinaryOperator, BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MethodDefinition, Node, Pattern, Program, Property, PropertyDefinition, Statement, TemplateElement, VariableDeclarator } from 'estree' */ +/** @import { Location } from 'locate-character' */ +/** @import { Attribute, Binding, ClassDirective, Comment, Component, ExpressionTag, Namespace, RegularElement, SpreadAttribute, StyleDirective, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TemplateNode, Text, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ +/** @import { ComponentContext, ComponentServerTransformState, ComponentVisitors, ServerTransformState, Visitors } from './types.js' */ +/** @import { Analysis, ComponentAnalysis } from '../../types.js' */ +/** @import { Scope } from '../../scope.js' */ +/** @import { StateField } from '../../3-transform/client/types.js' */ // TODO move this type import { walk } from 'zimmerframe'; import { set_scope, get_rune } from '../../scope.js'; import { @@ -52,27 +59,27 @@ const block_close = b.literal(BLOCK_CLOSE); const empty_comment = b.literal(EMPTY_COMMENT); /** - * @param {import('estree').Node} node - * @returns {node is import('estree').Statement} + * @param {Node} node + * @returns {node is Statement} */ function is_statement(node) { return node.type.endsWith('Statement') || node.type.endsWith('Declaration'); } /** - * @param {Array} template - * @param {import('estree').Identifier} out - * @param {import('estree').AssignmentOperator} operator - * @returns {import('estree').Statement[]} + * @param {Array} template + * @param {Identifier} out + * @param {AssignmentOperator} operator + * @returns {Statement[]} */ function serialize_template(template, out = b.id('$$payload.out'), operator = '+=') { - /** @type {import('estree').TemplateElement[]} */ + /** @type {TemplateElement[]} */ let quasis = []; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ let expressions = []; - /** @type {import('estree').Statement[]} */ + /** @type {Statement[]} */ const statements = []; const flush = () => { @@ -118,18 +125,18 @@ function serialize_template(template, out = b.id('$$payload.out'), operator = '+ /** * Processes an array of template nodes, joining sibling text/expression nodes and * recursing into child nodes. - * @param {Array} nodes - * @param {import('./types').ComponentContext} context + * @param {Array} nodes + * @param {ComponentContext} context */ function process_children(nodes, { visit, state }) { - /** @type {Array} */ + /** @type {Array} */ let sequence = []; function flush() { let quasi = b.quasi('', false); const quasis = [quasi]; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const expressions = []; for (let i = 0; i < sequence.length; i++) { @@ -144,9 +151,7 @@ function process_children(nodes, { visit, state }) { quasi.value.raw += sanitize_template_string(escape_html(node.expression.value + '')); } } else { - expressions.push( - b.call('$.escape', /** @type {import('estree').Expression} */ (visit(node.expression))) - ); + expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression)))); quasi = b.quasi('', i + 1 === sequence.length); quasis.push(quasi); @@ -177,10 +182,10 @@ function process_children(nodes, { visit, state }) { } /** - * @param {import('estree').VariableDeclarator} declarator - * @param {import('../../scope').Scope} scope - * @param {import('estree').Expression} value - * @returns {import('estree').VariableDeclarator[]} + * @param {VariableDeclarator} declarator + * @param {Scope} scope + * @param {Expression} value + * @returns {VariableDeclarator[]} */ function create_state_declarators(declarator, scope, value) { if (declarator.id.type === 'Identifier') { @@ -199,9 +204,9 @@ function create_state_declarators(declarator, scope, value) { } /** - * @param {import('estree').Identifier} node - * @param {import('./types').ServerTransformState} state - * @returns {import('estree').Expression} + * @param {Identifier} node + * @param {ServerTransformState} state + * @returns {Expression} */ function serialize_get_binding(node, state) { const binding = state.scope.get(node.name); @@ -230,23 +235,23 @@ function serialize_get_binding(node, state) { } /** - * @param {import('estree').AssignmentExpression} node - * @param {Pick, 'visit' | 'state'>} context + * @param {AssignmentExpression} node + * @param {Pick, 'visit' | 'state'>} context */ function get_assignment_value(node, { state, visit }) { if (node.left.type === 'Identifier') { const operator = node.operator; return operator === '=' - ? /** @type {import('estree').Expression} */ (visit(node.right)) + ? /** @type {Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 b.binary( - /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), + /** @type {BinaryOperator} */ (operator.slice(0, -1)), serialize_get_binding(node.left, state), - /** @type {import('estree').Expression} */ (visit(node.right)) + /** @type {Expression} */ (visit(node.right)) ); } - return /** @type {import('estree').Expression} */ (visit(node.right)); + return /** @type {Expression} */ (visit(node.right)); } /** @@ -257,10 +262,10 @@ function is_store_name(name) { } /** - * @param {import('estree').AssignmentExpression} node - * @param {import('zimmerframe').Context} context + * @param {AssignmentExpression} node + * @param {import('zimmerframe').Context} context * @param {() => any} fallback - * @returns {import('estree').Expression} + * @returns {Expression} */ function serialize_set_binding(node, context, fallback) { const { state, visit } = context; @@ -273,10 +278,10 @@ function serialize_set_binding(node, context, fallback) { // Turn assignment into an IIFE, so that `$.set` calls etc don't produce invalid code const tmp_id = context.state.scope.generate('tmp'); - /** @type {import('estree').AssignmentExpression[]} */ + /** @type {AssignmentExpression[]} */ const original_assignments = []; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const assignments = []; const paths = extract_paths(node.left); @@ -296,7 +301,7 @@ function serialize_set_binding(node, context, fallback) { return b.call( b.thunk( b.block([ - b.const(tmp_id, /** @type {import('estree').Expression} */ (visit(node.right))), + b.const(tmp_id, /** @type {Expression} */ (visit(node.right))), b.stmt(b.sequence(assignments)), b.return(b.id(tmp_id)) ]) @@ -345,11 +350,7 @@ function serialize_set_binding(node, context, fallback) { const value = get_assignment_value(node, { state, visit }); if (left === node.left) { if (is_store) { - return b.call( - '$.store_set', - b.id(left_name), - /** @type {import('estree').Expression} */ (visit(node.right)) - ); + return b.call('$.store_set', b.id(left_name), /** @type {Expression} */ (visit(node.right))); } return fallback(); } else if (is_store) { @@ -358,16 +359,16 @@ function serialize_set_binding(node, context, fallback) { b.assignment('??=', b.id('$$store_subs'), b.object([])), b.literal(left.name), b.id(left_name), - b.assignment(node.operator, /** @type {import('estree').Pattern} */ (visit(node.left)), value) + b.assignment(node.operator, /** @type {Pattern} */ (visit(node.left)), value) ); } return fallback(); } /** - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element - * @param {import('#compiler').Attribute} attribute - * @param {{ state: { namespace: import('#compiler').Namespace }}} context + * @param {RegularElement | SvelteElement} element + * @param {Attribute} attribute + * @param {{ state: { namespace: Namespace }}} context */ function get_attribute_name(element, attribute, context) { let name = attribute.name; @@ -379,10 +380,10 @@ function get_attribute_name(element, attribute, context) { return name; } -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const global_visitors = { Identifier(node, { path, state }) { - if (is_reference(node, /** @type {import('estree').Node} */ (path.at(-1)))) { + if (is_reference(node, /** @type {Node} */ (path.at(-1)))) { if (node.name === '$$props') { return b.id('$$sanitized_props'); } @@ -425,17 +426,14 @@ const global_visitors = { } if (rune === '$state.snapshot') { - return b.call( - '$.snapshot', - /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) - ); + return b.call('$.snapshot', /** @type {Expression} */ (context.visit(node.arguments[0]))); } if (rune === '$state.is') { return b.call( 'Object.is', - /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])), - /** @type {import('estree').Expression} */ (context.visit(node.arguments[1])) + /** @type {Expression} */ (context.visit(node.arguments[0])), + /** @type {Expression} */ (context.visit(node.arguments[1])) ); } @@ -447,13 +445,13 @@ const global_visitors = { } }; -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const javascript_visitors_runes = { ClassBody(node, { state, visit }) { - /** @type {Map} */ + /** @type {Map} */ const public_derived = new Map(); - /** @type {Map} */ + /** @type {Map} */ const private_derived = new Map(); /** @type {string[]} */ @@ -472,7 +470,7 @@ const javascript_visitors_runes = { if (definition.value?.type === 'CallExpression') { const rune = get_rune(definition.value, state.scope); if (rune === '$derived' || rune === '$derived.by') { - /** @type {import('../../3-transform/client/types.js').StateField} */ + /** @type {StateField} */ const field = { kind: rune === '$derived.by' ? 'derived_call' : 'derived', // @ts-expect-error this is set in the next pass @@ -500,7 +498,7 @@ const javascript_visitors_runes = { field.id = b.private_id(deconflicted); } - /** @type {Array} */ + /** @type {Array} */ const body = []; const child_state = { ...state, private_derived }; @@ -517,7 +515,7 @@ const javascript_visitors_runes = { const field = (is_private ? private_derived : public_derived).get(name); if (definition.value?.type === 'CallExpression' && field !== undefined) { - const init = /** @type {import('estree').Expression} **/ ( + const init = /** @type {Expression} **/ ( visit(definition.value.arguments[0], child_state) ); const value = @@ -551,7 +549,7 @@ const javascript_visitors_runes = { } } - body.push(/** @type {import('estree').MethodDefinition} **/ (visit(definition, child_state))); + body.push(/** @type {MethodDefinition} **/ (visit(definition, child_state))); } return { ...node, body }; @@ -566,7 +564,7 @@ const javascript_visitors_runes = { value: node.value.arguments.length === 0 ? null - : /** @type {import('estree').Expression} */ (visit(node.value.arguments[0])) + : /** @type {Expression} */ (visit(node.value.arguments[0])) }; } if (rune === '$derived.by') { @@ -575,7 +573,7 @@ const javascript_visitors_runes = { value: node.value.arguments.length === 0 ? null - : b.call(/** @type {import('estree').Expression} */ (visit(node.value.arguments[0]))) + : b.call(/** @type {Expression} */ (visit(node.value.arguments[0]))) }; } } @@ -588,7 +586,7 @@ const javascript_visitors_runes = { const init = declarator.init; const rune = get_rune(init, state.scope); if (!rune || rune === '$effect.tracking' || rune === '$inspect' || rune === '$effect.root') { - declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); + declarations.push(/** @type {VariableDeclarator} */ (visit(declarator))); continue; } @@ -601,7 +599,7 @@ const javascript_visitors_runes = { get_rune(node.right, state.scope) === '$bindable' ) { const right = node.right.arguments.length - ? /** @type {import('estree').Expression} */ (visit(node.right.arguments[0])) + ? /** @type {Expression} */ (visit(node.right.arguments[0])) : b.id('undefined'); return b.assignment_pattern(node.left, right); } @@ -611,18 +609,13 @@ const javascript_visitors_runes = { continue; } - const args = /** @type {import('estree').CallExpression} */ (init).arguments; + const args = /** @type {CallExpression} */ (init).arguments; const value = - args.length === 0 - ? b.id('undefined') - : /** @type {import('estree').Expression} */ (visit(args[0])); + args.length === 0 ? b.id('undefined') : /** @type {Expression} */ (visit(args[0])); if (rune === '$derived.by') { declarations.push( - b.declarator( - /** @type {import('estree').Pattern} */ (visit(declarator.id)), - b.call(value) - ) + b.declarator(/** @type {Pattern} */ (visit(declarator.id)), b.call(value)) ); continue; } @@ -633,9 +626,7 @@ const javascript_visitors_runes = { } if (rune === '$derived') { - declarations.push( - b.declarator(/** @type {import('estree').Pattern} */ (visit(declarator.id)), value) - ); + declarations.push(b.declarator(/** @type {Pattern} */ (visit(declarator.id)), value)); continue; } @@ -680,11 +671,11 @@ const javascript_visitors_runes = { /** * - * @param {import('#compiler').Attribute['value']} value - * @param {import('./types').ComponentContext} context + * @param {Attribute['value']} value + * @param {ComponentContext} context * @param {boolean} trim_whitespace * @param {boolean} is_component - * @returns {import('estree').Expression} + * @returns {Expression} */ function serialize_attribute_value(value, context, trim_whitespace = false, is_component = false) { if (value === true) { @@ -702,13 +693,13 @@ function serialize_attribute_value(value, context, trim_whitespace = false, is_c return b.literal(is_component ? data : escape_html(data, true)); } - return /** @type {import('estree').Expression} */ (context.visit(chunk.expression)); + return /** @type {Expression} */ (context.visit(chunk.expression)); } let quasi = b.quasi('', false); const quasis = [quasi]; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const expressions = []; for (let i = 0; i < value.length; i++) { @@ -720,10 +711,7 @@ function serialize_attribute_value(value, context, trim_whitespace = false, is_c : node.data; } else { expressions.push( - b.call( - '$.stringify', - /** @type {import('estree').Expression} */ (context.visit(node.expression)) - ) + b.call('$.stringify', /** @type {Expression} */ (context.visit(node.expression))) ); quasi = b.quasi('', i + 1 === value.length); @@ -736,11 +724,11 @@ function serialize_attribute_value(value, context, trim_whitespace = false, is_c /** * - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element - * @param {Array} attributes - * @param {import('#compiler').StyleDirective[]} style_directives - * @param {import('#compiler').ClassDirective[]} class_directives - * @param {import('./types').ComponentContext} context + * @param {RegularElement | SvelteElement} element + * @param {Array} attributes + * @param {StyleDirective[]} style_directives + * @param {ClassDirective[]} class_directives + * @param {ComponentContext} context */ function serialize_element_spread_attributes( element, @@ -759,7 +747,7 @@ function serialize_element_spread_attributes( directive.name, directive.expression.type === 'Identifier' && directive.expression.name === directive.name ? b.id(directive.name) - : /** @type {import('estree').Expression} */ (context.visit(directive.expression)) + : /** @type {Expression} */ (context.visit(directive.expression)) ) ); @@ -801,7 +789,7 @@ function serialize_element_spread_attributes( return b.prop('init', b.key(name), value); } - return b.spread(/** @type {import('estree').Expression} */ (context.visit(attribute))); + return b.spread(/** @type {Expression} */ (context.visit(attribute))); }) ); @@ -810,21 +798,21 @@ function serialize_element_spread_attributes( } /** - * @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node - * @param {import('estree').Expression} expression - * @param {import('./types').ComponentContext} context + * @param {Component | SvelteComponent | SvelteSelf} node + * @param {Expression} expression + * @param {ComponentContext} context */ function serialize_inline_component(node, expression, context) { - /** @type {Array} */ + /** @type {Array} */ const props_and_spreads = []; - /** @type {import('estree').Property[]} */ + /** @type {Property[]} */ const custom_css_props = []; - /** @type {import('estree').ExpressionStatement[]} */ + /** @type {ExpressionStatement[]} */ const lets = []; - /** @type {Record} */ + /** @type {Record} */ const children = {}; /** @@ -841,7 +829,7 @@ function serialize_inline_component(node, expression, context) { let has_children_prop = false; /** - * @param {import('estree').Property} prop + * @param {Property} prop */ function push_prop(prop) { const current = props_and_spreads.at(-1); @@ -854,9 +842,9 @@ function serialize_inline_component(node, expression, context) { } for (const attribute of node.attributes) { if (attribute.type === 'LetDirective') { - lets.push(/** @type {import('estree').ExpressionStatement} */ (context.visit(attribute))); + lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); } else if (attribute.type === 'SpreadAttribute') { - props_and_spreads.push(/** @type {import('estree').Expression} */ (context.visit(attribute))); + props_and_spreads.push(/** @type {Expression} */ (context.visit(attribute))); } else if (attribute.type === 'Attribute') { if (attribute.name.startsWith('--')) { const value = serialize_attribute_value(attribute.value, context, false, true); @@ -878,13 +866,13 @@ function serialize_inline_component(node, expression, context) { // TODO this needs to turn the whole thing into a while loop because the binding could be mutated eagerly in the child push_prop( b.get(attribute.name, [ - b.return(/** @type {import('estree').Expression} */ (context.visit(attribute.expression))) + b.return(/** @type {Expression} */ (context.visit(attribute.expression))) ]) ); push_prop( b.set(attribute.name, [ b.stmt( - /** @type {import('estree').Expression} */ ( + /** @type {Expression} */ ( context.visit(b.assignment('=', attribute.expression, b.id('$$value'))) ) ), @@ -898,7 +886,7 @@ function serialize_inline_component(node, expression, context) { context.state.init.push(...lets); } - /** @type {import('estree').Statement[]} */ + /** @type {Statement[]} */ const snippet_declarations = []; // Group children by slot @@ -919,13 +907,13 @@ function serialize_inline_component(node, expression, context) { let slot_name = 'default'; if (is_element_node(child)) { - const attribute = /** @type {import('#compiler').Attribute | undefined} */ ( + const attribute = /** @type {Attribute | undefined} */ ( child.attributes.find( (attribute) => attribute.type === 'Attribute' && attribute.name === 'slot' ) ); if (attribute !== undefined) { - slot_name = /** @type {import('#compiler').Text[]} */ (attribute.value)[0].data; + slot_name = /** @type {Text[]} */ (attribute.value)[0].data; } } @@ -934,11 +922,11 @@ function serialize_inline_component(node, expression, context) { } // Serialize each slot - /** @type {import('estree').Property[]} */ + /** @type {Property[]} */ const serialized_slots = []; for (const slot_name of Object.keys(children)) { - const block = /** @type {import('estree').BlockStatement} */ ( + const block = /** @type {BlockStatement} */ ( context.visit( { ...node.fragment, @@ -990,13 +978,13 @@ function serialize_inline_component(node, expression, context) { const props_expression = props_and_spreads.length === 0 || (props_and_spreads.length === 1 && Array.isArray(props_and_spreads[0])) - ? b.object(/** @type {import('estree').Property[]} */ (props_and_spreads[0] || [])) + ? b.object(/** @type {Property[]} */ (props_and_spreads[0] || [])) : b.call( '$.spread_props', b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))) ); - /** @type {import('estree').Statement} */ + /** @type {Statement} */ let statement = b.stmt( (node.type === 'SvelteComponent' ? b.maybe_call : b.call)( expression, @@ -1038,21 +1026,19 @@ function serialize_inline_component(node, expression, context) { } } -/** @type {import('./types').Visitors} */ +/** @type {Visitors} */ const javascript_visitors_legacy = { VariableDeclaration(node, { state, visit }) { - /** @type {import('estree').VariableDeclarator[]} */ + /** @type {VariableDeclarator[]} */ const declarations = []; for (const declarator of node.declarations) { - const bindings = /** @type {import('#compiler').Binding[]} */ ( - state.scope.get_bindings(declarator) - ); + const bindings = /** @type {Binding[]} */ (state.scope.get_bindings(declarator)); const has_state = bindings.some((binding) => binding.kind === 'state'); const has_props = bindings.some((binding) => binding.kind === 'bindable_prop'); if (!has_state && !has_props) { - declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); + declarations.push(/** @type {VariableDeclarator} */ (visit(declarator))); continue; } @@ -1065,15 +1051,13 @@ const javascript_visitors_legacy = { declarations.push( b.declarator( b.id(tmp), - /** @type {import('estree').Expression} */ ( - visit(/** @type {import('estree').Expression} */ (declarator.init)) - ) + /** @type {Expression} */ (visit(/** @type {Expression} */ (declarator.init))) ) ); for (const path of paths) { const value = path.expression?.(b.id(tmp)); - const name = /** @type {import('estree').Identifier} */ (path.node).name; - const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(name)); + const name = /** @type {Identifier} */ (path.node).name; + const binding = /** @type {Binding} */ (state.scope.get(name)); const prop = b.member(b.id('$$props'), b.literal(binding.prop_alias ?? name), true); declarations.push( b.declarator(path.node, b.call('$.value_or_fallback', prop, b.thunk(value))) @@ -1082,19 +1066,17 @@ const javascript_visitors_legacy = { continue; } - const binding = /** @type {import('#compiler').Binding} */ ( - state.scope.get(declarator.id.name) - ); + const binding = /** @type {Binding} */ (state.scope.get(declarator.id.name)); const prop = b.member( b.id('$$props'), b.literal(binding.prop_alias ?? declarator.id.name), true ); - /** @type {import('estree').Expression} */ + /** @type {Expression} */ let init = prop; if (declarator.init) { - const default_value = /** @type {import('estree').Expression} */ (visit(declarator.init)); + const default_value = /** @type {Expression} */ (visit(declarator.init)); init = is_expression_async(default_value) ? b.await(b.call('$.value_or_fallback_async', prop, b.thunk(default_value, true))) : b.call('$.value_or_fallback', prop, b.thunk(default_value)); @@ -1109,7 +1091,7 @@ const javascript_visitors_legacy = { ...create_state_declarators( declarator, state.scope, - /** @type {import('estree').Expression} */ (declarator.init && visit(declarator.init)) + /** @type {Expression} */ (declarator.init && visit(declarator.init)) ) ); } @@ -1129,14 +1111,14 @@ const javascript_visitors_legacy = { context.state.legacy_reactive_statements.set( node, // people could do "break $" inside, so we need to keep the label - b.labeled('$', /** @type {import('estree').ExpressionStatement} */ (context.visit(node.body))) + b.labeled('$', /** @type {ExpressionStatement} */ (context.visit(node.body))) ); return b.empty; } }; -/** @type {import('./types').ComponentVisitors} */ +/** @type {ComponentVisitors} */ const template_visitors = { Fragment(node, context) { const parent = context.path.at(-1) ?? node; @@ -1152,7 +1134,7 @@ const template_visitors = { context.state.options.preserveComments ); - /** @type {import('./types').ComponentServerTransformState} */ + /** @type {ComponentServerTransformState} */ const state = { ...context.state, init: [], @@ -1175,13 +1157,13 @@ const template_visitors = { return b.block([...state.init, ...serialize_template(state.template)]); }, HtmlTag(node, context) { - const expression = /** @type {import('estree').Expression} */ (context.visit(node.expression)); + const expression = /** @type {Expression} */ (context.visit(node.expression)); context.state.template.push(b.call('$.html', expression)); }, ConstTag(node, { state, visit }) { const declaration = node.declaration.declarations[0]; - const pattern = /** @type {import('estree').Pattern} */ (visit(declaration.id)); - const init = /** @type {import('estree').Expression} */ (visit(declaration.init)); + const pattern = /** @type {Pattern} */ (visit(declaration.id)); + const init = /** @type {Expression} */ (visit(declaration.init)); state.init.push(b.declaration('const', pattern, init)); }, DebugTag(node, { state, visit }) { @@ -1191,11 +1173,7 @@ const template_visitors = { 'console.log', b.object( node.identifiers.map((identifier) => - b.prop( - 'init', - identifier, - /** @type {import('estree').Expression} */ (visit(identifier)) - ) + b.prop('init', identifier, /** @type {Expression} */ (visit(identifier))) ) ) ) @@ -1207,10 +1185,10 @@ const template_visitors = { const callee = unwrap_optional(node.expression).callee; const raw_args = unwrap_optional(node.expression).arguments; - const snippet_function = /** @type {import('estree').Expression} */ (context.visit(callee)); + const snippet_function = /** @type {Expression} */ (context.visit(callee)); const snippet_args = raw_args.map((arg) => { - return /** @type {import('estree').Expression} */ (context.visit(arg)); + return /** @type {Expression} */ (context.visit(arg)); }); context.state.template.push( @@ -1236,7 +1214,7 @@ const template_visitors = { RegularElement(node, context) { const namespace = determine_namespace_for_children(node, context.state.namespace); - /** @type {import('./types').ComponentServerTransformState} */ + /** @type {ComponentServerTransformState} */ const state = { ...context.state, getters: { ...context.state.getters }, @@ -1252,7 +1230,7 @@ const template_visitors = { if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) { context.state.template.push( - b.literal(/** @type {import('#compiler').Text} */ (node.fragment.nodes[0]).data), + b.literal(/** @type {Text} */ (node.fragment.nodes[0]).data), b.literal(``) ); @@ -1266,7 +1244,7 @@ const template_visitors = { namespace, { ...state, - scope: /** @type {import('../../scope').Scope} */ (state.scopes.get(node.fragment)) + scope: /** @type {Scope} */ (state.scopes.get(node.fragment)) }, state.preserve_whitespace, state.options.preserveComments @@ -1277,7 +1255,7 @@ const template_visitors = { } if (state.options.dev) { - const location = /** @type {import('locate-character').Location} */ (locator(node.start)); + const location = /** @type {Location} */ (locator(node.start)); state.template.push( b.stmt( b.call( @@ -1325,7 +1303,7 @@ const template_visitors = { } }, SvelteElement(node, context) { - let tag = /** @type {import('estree').Expression} */ (context.visit(node.tag)); + let tag = /** @type {Expression} */ (context.visit(node.tag)); if (tag.type !== 'Identifier') { const tag_id = context.state.scope.generate('$$tag'); context.state.init.push(b.const(tag_id, tag)); @@ -1354,9 +1332,7 @@ const template_visitors = { } const attributes = b.block([...state.init, ...serialize_template(state.template)]); - const children = /** @type {import('estree').BlockStatement} */ ( - context.visit(node.fragment, state) - ); + const children = /** @type {BlockStatement} */ (context.visit(node.fragment, state)); context.state.template.push( b.stmt( @@ -1378,7 +1354,7 @@ const template_visitors = { const state = context.state; const each_node_meta = node.metadata; - const collection = /** @type {import('estree').Expression} */ (context.visit(node.expression)); + const collection = /** @type {Expression} */ (context.visit(node.expression)); const item = each_node_meta.item; const index = each_node_meta.contains_group_binding || !node.index @@ -1388,17 +1364,17 @@ const template_visitors = { const array_id = state.scope.root.unique('each_array'); state.init.push(b.const(array_id, b.call('$.ensure_array_like', collection))); - /** @type {import('estree').Statement[]} */ + /** @type {Statement[]} */ const each = [b.const(item, b.member(array_id, index, true))]; if (node.context.type !== 'Identifier') { - each.push(b.const(/** @type {import('estree').Pattern} */ (node.context), item)); + each.push(b.const(/** @type {Pattern} */ (node.context), item)); } if (index.name !== node.index && node.index != null) { each.push(b.let(node.index, index)); } - each.push(.../** @type {import('estree').BlockStatement} */ (context.visit(node.body)).body); + each.push(.../** @type {BlockStatement} */ (context.visit(node.body)).body); const for_loop = b.for( b.let(index, b.literal(0)), @@ -1410,9 +1386,7 @@ const template_visitors = { if (node.fallback) { const open = b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open)); - const fallback = /** @type {import('estree').BlockStatement} */ ( - context.visit(node.fallback) - ); + const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback)); fallback.body.unshift( b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE))) @@ -1431,14 +1405,12 @@ const template_visitors = { } }, IfBlock(node, context) { - const test = /** @type {import('estree').Expression} */ (context.visit(node.test)); + const test = /** @type {Expression} */ (context.visit(node.test)); - const consequent = /** @type {import('estree').BlockStatement} */ ( - context.visit(node.consequent) - ); + const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent)); const alternate = node.alternate - ? /** @type {import('estree').BlockStatement} */ (context.visit(node.alternate)) + ? /** @type {BlockStatement} */ (context.visit(node.alternate)) : b.block([]); consequent.body.unshift(b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open))); @@ -1455,23 +1427,17 @@ const template_visitors = { b.stmt( b.call( '$.await', - /** @type {import('estree').Expression} */ (context.visit(node.expression)), + /** @type {Expression} */ (context.visit(node.expression)), b.thunk( - node.pending - ? /** @type {import('estree').BlockStatement} */ (context.visit(node.pending)) - : b.block([]) + node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([]) ), b.arrow( - node.value ? [/** @type {import('estree').Pattern} */ (context.visit(node.value))] : [], - node.then - ? /** @type {import('estree').BlockStatement} */ (context.visit(node.then)) - : b.block([]) + node.value ? [/** @type {Pattern} */ (context.visit(node.value))] : [], + node.then ? /** @type {BlockStatement} */ (context.visit(node.then)) : b.block([]) ), b.arrow( - node.error ? [/** @type {import('estree').Pattern} */ (context.visit(node.error))] : [], - node.catch - ? /** @type {import('estree').BlockStatement} */ (context.visit(node.catch)) - : b.block([]) + node.error ? [/** @type {Pattern} */ (context.visit(node.error))] : [], + node.catch ? /** @type {BlockStatement} */ (context.visit(node.catch)) : b.block([]) ) ) ), @@ -1479,14 +1445,14 @@ const template_visitors = { ); }, KeyBlock(node, context) { - const block = /** @type {import('estree').BlockStatement} */ (context.visit(node.fragment)); + const block = /** @type {BlockStatement} */ (context.visit(node.fragment)); context.state.template.push(empty_comment, block, empty_comment); }, SnippetBlock(node, context) { const fn = b.function_declaration( node.expression, [b.id('$$payload'), ...node.parameters], - /** @type {import('estree').BlockStatement} */ (context.visit(node.body)) + /** @type {BlockStatement} */ (context.visit(node.body)) ); // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; @@ -1502,7 +1468,7 @@ const template_visitors = { SvelteComponent(node, context) { serialize_inline_component( node, - /** @type {import('estree').Expression} */ (context.visit(node.expression)), + /** @type {Expression} */ (context.visit(node.expression)), context ); }, @@ -1550,16 +1516,12 @@ const template_visitors = { for (const attribute of node.attributes) { if (attribute.type === 'LetDirective') { context.state.template.push( - /** @type {import('estree').ExpressionStatement} */ ( - context.visit(attribute, child_state) - ) + /** @type {ExpressionStatement} */ (context.visit(attribute, child_state)) ); } } - const block = /** @type {import('estree').BlockStatement} */ ( - context.visit(node.fragment, child_state) - ); + const block = /** @type {BlockStatement} */ (context.visit(node.fragment, child_state)); context.state.template.push(block); }, @@ -1572,21 +1534,21 @@ const template_visitors = { context.state.init.push(...serialize_template(template, b.id('$$payload.title'), '=')); }, SlotElement(node, context) { - /** @type {import('estree').Property[]} */ + /** @type {Property[]} */ const props = []; - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const spreads = []; - /** @type {import('estree').ExpressionStatement[]} */ + /** @type {ExpressionStatement[]} */ const lets = []; - /** @type {import('estree').Expression} */ + /** @type {Expression} */ let expression = b.call('$.default_slot', b.id('$$props')); for (const attribute of node.attributes) { if (attribute.type === 'SpreadAttribute') { - spreads.push(/** @type {import('estree').Expression} */ (context.visit(attribute))); + spreads.push(/** @type {Expression} */ (context.visit(attribute))); } else if (attribute.type === 'Attribute') { const value = serialize_attribute_value(attribute.value, context, false, true); @@ -1600,7 +1562,7 @@ const template_visitors = { } } } else if (attribute.type === 'LetDirective') { - lets.push(/** @type {import('estree').ExpressionStatement} */ (context.visit(attribute))); + lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); } } @@ -1615,14 +1577,14 @@ const template_visitors = { const fallback = node.fragment.nodes.length === 0 ? b.literal(null) - : b.thunk(/** @type {import('estree').BlockStatement} */ (context.visit(node.fragment))); + : b.thunk(/** @type {BlockStatement} */ (context.visit(node.fragment))); const slot = b.call('$.slot', b.id('$$payload'), expression, props_expression, fallback); context.state.template.push(empty_comment, b.stmt(slot), empty_comment); }, SvelteHead(node, context) { - const block = /** @type {import('estree').BlockStatement} */ (context.visit(node.fragment)); + const block = /** @type {BlockStatement} */ (context.visit(node.fragment)); context.state.template.push( b.stmt(b.call('$.head', b.id('$$payload'), b.arrow([b.id('$$payload')], block))) @@ -1633,23 +1595,23 @@ const template_visitors = { /** * Writes the output to the template output. Some elements may have attributes on them that require the * their output to be the child content instead. In this case, an object is returned. - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node - * @param {import('zimmerframe').Context} context + * @param {RegularElement | SvelteElement} node + * @param {import('zimmerframe').Context} context */ function serialize_element_attributes(node, context) { - /** @type {Array} */ + /** @type {Array} */ const attributes = []; - /** @type {import('#compiler').ClassDirective[]} */ + /** @type {ClassDirective[]} */ const class_directives = []; - /** @type {import('#compiler').StyleDirective[]} */ + /** @type {StyleDirective[]} */ const style_directives = []; - /** @type {import('estree').ExpressionStatement[]} */ + /** @type {ExpressionStatement[]} */ const lets = []; - /** @type {import('estree').Expression | null} */ + /** @type {Expression | null} */ let content = null; let has_spread = false; @@ -1716,14 +1678,14 @@ function serialize_element_attributes(node, context) { if (binding?.omit_in_ssr) continue; if (ContentEditableBindings.includes(attribute.name)) { - content = /** @type {import('estree').Expression} */ (context.visit(attribute.expression)); + content = /** @type {Expression} */ (context.visit(attribute.expression)); } else if (attribute.name === 'value' && node.name === 'textarea') { content = b.call( '$.escape', - /** @type {import('estree').Expression} */ (context.visit(attribute.expression)) + /** @type {Expression} */ (context.visit(attribute.expression)) ); } else if (attribute.name === 'group') { - const value_attribute = /** @type {import('#compiler').Attribute | undefined} */ ( + const value_attribute = /** @type {Attribute | undefined} */ ( node.attributes.find((attr) => attr.type === 'Attribute' && attr.name === 'value') ); if (!value_attribute) continue; @@ -1793,7 +1755,7 @@ function serialize_element_attributes(node, context) { } else if (attribute.type === 'StyleDirective') { style_directives.push(attribute); } else if (attribute.type === 'LetDirective') { - lets.push(/** @type {import('estree').ExpressionStatement} */ (context.visit(attribute))); + lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute))); } else { context.visit(attribute); } @@ -1802,7 +1764,7 @@ function serialize_element_attributes(node, context) { if (class_directives.length > 0 && !has_spread) { const class_attribute = serialize_class_directives( class_directives, - /** @type {import('#compiler').Attribute | null} */ (attributes[class_index] ?? null) + /** @type {Attribute | null} */ (attributes[class_index] ?? null) ); if (class_index === -1) { attributes.push(class_attribute); @@ -1812,7 +1774,7 @@ function serialize_element_attributes(node, context) { if (style_directives.length > 0 && !has_spread) { serialize_style_directives( style_directives, - /** @type {import('#compiler').Attribute | null} */ (attributes[style_index] ?? null), + /** @type {Attribute | null} */ (attributes[style_index] ?? null), context ); if (style_index > -1) { @@ -1832,10 +1794,10 @@ function serialize_element_attributes(node, context) { context ); } else { - for (const attribute of /** @type {import('#compiler').Attribute[]} */ (attributes)) { + for (const attribute of /** @type {Attribute[]} */ (attributes)) { if (attribute.value === true || is_text_attribute(attribute)) { const name = get_attribute_name(node, attribute, context); - const literal_value = /** @type {import('estree').Literal} */ ( + const literal_value = /** @type {Literal} */ ( serialize_attribute_value( attribute.value, context, @@ -1881,8 +1843,8 @@ function serialize_element_attributes(node, context) { /** * - * @param {import('#compiler').ClassDirective[]} class_directives - * @param {import('#compiler').Attribute | null} class_attribute + * @param {ClassDirective[]} class_directives + * @param {Attribute | null} class_attribute * @returns */ function serialize_class_directives(class_directives, class_attribute) { @@ -1931,9 +1893,9 @@ function serialize_class_directives(class_directives, class_attribute) { } /** - * @param {import('#compiler').StyleDirective[]} style_directives - * @param {import('#compiler').Attribute | null} style_attribute - * @param {import('./types').ComponentContext} context + * @param {StyleDirective[]} style_directives + * @param {Attribute | null} style_attribute + * @param {ComponentContext} context */ function serialize_style_directives(style_directives, style_attribute, context) { const styles = style_directives.map((directive) => { @@ -1960,12 +1922,12 @@ function serialize_style_directives(style_directives, style_attribute, context) } /** - * @param {import('../../types').ComponentAnalysis} analysis - * @param {import('#compiler').ValidatedCompileOptions} options - * @returns {import('estree').Program} + * @param {ComponentAnalysis} analysis + * @param {ValidatedCompileOptions} options + * @returns {Program} */ export function server_component(analysis, options) { - /** @type {import('./types').ComponentServerTransformState} */ + /** @type {ComponentServerTransformState} */ const state = { analysis, options, @@ -1983,9 +1945,9 @@ export function server_component(analysis, options) { skip_hydration_boundaries: false }; - const module = /** @type {import('estree').Program} */ ( + const module = /** @type {Program} */ ( walk( - /** @type {import('#compiler').SvelteNode} */ (analysis.module.ast), + /** @type {SvelteNode} */ (analysis.module.ast), state, // @ts-expect-error TODO: zimmerframe types { @@ -1996,9 +1958,9 @@ export function server_component(analysis, options) { ) ); - const instance = /** @type {import('estree').Program} */ ( + const instance = /** @type {Program} */ ( walk( - /** @type {import('#compiler').SvelteNode} */ (analysis.instance.ast), + /** @type {SvelteNode} */ (analysis.instance.ast), { ...state, scope: analysis.instance.scope }, // @ts-expect-error TODO: zimmerframe types { @@ -2020,9 +1982,9 @@ export function server_component(analysis, options) { ) ); - const template = /** @type {import('estree').Program} */ ( + const template = /** @type {Program} */ ( walk( - /** @type {import('#compiler').SvelteNode} */ (analysis.template.ast), + /** @type {SvelteNode} */ (analysis.template.ast), { ...state, scope: analysis.template.scope }, // @ts-expect-error TODO: zimmerframe types { @@ -2033,7 +1995,7 @@ export function server_component(analysis, options) { ) ); - /** @type {import('estree').VariableDeclarator[]} */ + /** @type {VariableDeclarator[]} */ const legacy_reactive_declarations = []; for (const [node] of analysis.reactive_statements) { @@ -2088,7 +2050,7 @@ export function server_component(analysis, options) { b.function( b.id('$$render_inner'), [b.id('$$payload')], - b.block(/** @type {import('estree').Statement[]} */ (rest)) + b.block(/** @type {Statement[]} */ (rest)) ) ), b.do_while( @@ -2117,7 +2079,7 @@ export function server_component(analysis, options) { } // Propagate values of bound props upwards if they're undefined in the parent and have a value. // Don't do this as part of the props retrieval because people could eagerly mutate the prop in the instance script. - /** @type {import('estree').Property[]} */ + /** @type {Property[]} */ const props = []; for (const [name, binding] of analysis.instance.scope.declarations) { if (binding.kind === 'bindable_prop' && !name.startsWith('$$')) { @@ -2132,13 +2094,13 @@ export function server_component(analysis, options) { // undefined to a binding that has a default value. template.body.push(b.stmt(b.call('$.bind_props', b.id('$$props'), b.object(props)))); } - /** @type {import('estree').Expression[]} */ + /** @type {Expression[]} */ const push_args = []; if (options.dev) push_args.push(b.id(analysis.name)); const component_block = b.block([ - .../** @type {import('estree').Statement[]} */ (instance.body), - .../** @type {import('estree').Statement[]} */ (template.body) + .../** @type {Statement[]} */ (instance.body), + .../** @type {Statement[]} */ (template.body) ]); let should_inject_context = analysis.needs_context || options.dev; @@ -2275,12 +2237,12 @@ export function server_component(analysis, options) { } /** - * @param {import('../../types').Analysis} analysis - * @param {import('#compiler').ValidatedModuleCompileOptions} options - * @returns {import('estree').Program} + * @param {Analysis} analysis + * @param {ValidatedModuleCompileOptions} options + * @returns {Program} */ export function server_module(analysis, options) { - /** @type {import('./types').ServerTransformState} */ + /** @type {ServerTransformState} */ const state = { analysis, options, @@ -2294,8 +2256,8 @@ export function server_module(analysis, options) { getters: {} }; - const module = /** @type {import('estree').Program} */ ( - walk(/** @type {import('#compiler').SvelteNode} */ (analysis.module.ast), state, { + const module = /** @type {Program} */ ( + walk(/** @type {SvelteNode} */ (analysis.module.ast), state, { ...set_scope(analysis.module.scopes), ...global_visitors, ...javascript_visitors_runes diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 24f8d99a12..843f5cfa86 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -1,3 +1,6 @@ +/** @import { ComponentContext, ComponentContextLegacy } from '#client' */ +/** @import { EventDispatcher } from './index.js' */ +/** @import { NotFunction } from './internal/types.js' */ import { current_component_context, flush_sync, untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; @@ -15,7 +18,7 @@ import { lifecycle_outside_component } from './internal/shared/errors.js'; * * https://svelte.dev/docs/svelte#onmount * @template T - * @param {() => import('./internal/types').NotFunction | Promise> | (() => any)} fn + * @param {() => NotFunction | Promise> | (() => any)} fn * @returns {void} */ export function onMount(fn) { @@ -84,7 +87,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false * https://svelte.dev/docs/svelte#createeventdispatcher * @deprecated Use callback props and/or the `$host()` rune instead — see https://svelte-5-preview.vercel.app/docs/deprecations#createeventdispatcher * @template {Record} [EventMap = any] - * @returns {import('./index.js').EventDispatcher} + * @returns {EventDispatcher} */ export function createEventDispatcher() { const component_context = current_component_context; @@ -164,10 +167,10 @@ export function afterUpdate(fn) { /** * Legacy-mode: Init callbacks object for onMount/beforeUpdate/afterUpdate - * @param {import('#client').ComponentContext} context + * @param {ComponentContext} context */ function init_update_callbacks(context) { - var l = /** @type {import('#client').ComponentContextLegacy} */ (context).l; + var l = /** @type {ComponentContextLegacy} */ (context).l; return (l.u ??= { a: [], b: [], m: [] }); } diff --git a/packages/svelte/src/index-server.js b/packages/svelte/src/index-server.js index e5590fba50..0f1aff8f5a 100644 --- a/packages/svelte/src/index-server.js +++ b/packages/svelte/src/index-server.js @@ -1,10 +1,11 @@ +/** @import { Component } from '#server' */ import { current_component } from './internal/server/context.js'; import { noop } from './internal/shared/utils.js'; import * as e from './internal/server/errors.js'; /** @param {() => void} fn */ export function onDestroy(fn) { - var context = /** @type {import('#server').Component} */ (current_component); + var context = /** @type {Component} */ (current_component); (context.d ??= []).push(fn); } diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index da443b9505..a1566d1f14 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -1,4 +1,4 @@ -/** @import { TemplateNode } from '#client' */ +/** @import { EachItem, EachState, Effect, EffectNodes, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */ import { EACH_INDEX_REACTIVE, EACH_IS_ANIMATED, @@ -36,11 +36,11 @@ import { current_effect } from '../../runtime.js'; /** * The row of a keyed each block that is currently updating. We track this * so that `animate:` directives have something to attach themselves to - * @type {import('#client').EachItem | null} + * @type {EachItem | null} */ export let current_each_item = null; -/** @param {import('#client').EachItem | null} item */ +/** @param {EachItem | null} item */ export function set_current_each_item(item) { current_each_item = item; } @@ -56,13 +56,13 @@ export function index(_, i) { /** * Pause multiple effects simultaneously, and coordinate their * subsequent destruction. Used in each blocks - * @param {import('#client').EachState} state - * @param {import('#client').EachItem[]} items + * @param {EachState} state + * @param {EachItem[]} items * @param {null | Node} controlled_anchor * @param {Map} items_map */ function pause_effects(state, items, controlled_anchor, items_map) { - /** @type {import('#client').TransitionManager[]} */ + /** @type {TransitionManager[]} */ var transitions = []; var length = items.length; @@ -101,14 +101,14 @@ function pause_effects(state, items, controlled_anchor, items_map) { * @param {number} flags * @param {() => V[]} get_collection * @param {(value: V, index: number) => any} get_key - * @param {(anchor: Node, item: import('#client').MaybeSource, index: import('#client').MaybeSource) => void} render_fn + * @param {(anchor: Node, item: MaybeSource, index: MaybeSource) => void} render_fn * @param {null | ((anchor: Node) => void)} fallback_fn * @returns {void} */ export function each(node, flags, get_collection, get_key, render_fn, fallback_fn = null) { var anchor = node; - /** @type {import('#client').EachState} */ + /** @type {EachState} */ var state = { flags, items: new Map(), first: null }; var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0; @@ -125,7 +125,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f hydrate_next(); } - /** @type {import('#client').Effect | null} */ + /** @type {Effect | null} */ var fallback = null; block(() => { @@ -174,10 +174,10 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f // this is separate to the previous block because `hydrating` might change if (hydrating) { - /** @type {import('#client').EachItem | null} */ + /** @type {EachItem | null} */ var prev = null; - /** @type {import('#client').EachItem} */ + /** @type {EachItem} */ var item; for (var i = 0; i < length; i++) { @@ -239,9 +239,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f /** * @template V * @param {Array} array - * @param {import('#client').EachState} state + * @param {EachState} state * @param {Element | Comment | Text} anchor - * @param {(anchor: Node, item: import('#client').MaybeSource, index: number | import('#client').Source) => void} render_fn + * @param {(anchor: Node, item: MaybeSource, index: number | Source) => void} render_fn * @param {number} flags * @param {(value: V, index: number) => any} get_key * @returns {void} @@ -255,19 +255,19 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { var first = state.first; var current = first; - /** @type {Set} */ + /** @type {Set} */ var seen = new Set(); - /** @type {import('#client').EachItem | null} */ + /** @type {EachItem | null} */ var prev = null; - /** @type {Set} */ + /** @type {Set} */ var to_animate = new Set(); - /** @type {import('#client').EachItem[]} */ + /** @type {EachItem[]} */ var matched = []; - /** @type {import('#client').EachItem[]} */ + /** @type {EachItem[]} */ var stashed = []; /** @type {V} */ @@ -276,7 +276,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { /** @type {any} */ var key; - /** @type {import('#client').EachItem | undefined} */ + /** @type {EachItem | undefined} */ var item; /** @type {number} */ @@ -301,9 +301,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { item = items.get(key); if (item === undefined) { - var child_anchor = current - ? /** @type {import('#client').EffectNodes} */ (current.e.nodes).start - : anchor; + var child_anchor = current ? /** @type {EffectNodes} */ (current.e.nodes).start : anchor; prev = create_item( child_anchor, @@ -436,12 +434,12 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) { }); } - /** @type {import('#client').Effect} */ (current_effect).first = state.first && state.first.e; - /** @type {import('#client').Effect} */ (current_effect).last = prev && prev.e; + /** @type {Effect} */ (current_effect).first = state.first && state.first.e; + /** @type {Effect} */ (current_effect).last = prev && prev.e; } /** - * @param {import('#client').EachItem} item + * @param {EachItem} item * @param {any} value * @param {number} index * @param {number} type @@ -453,7 +451,7 @@ function update_item(item, value, index, type) { } if ((type & EACH_INDEX_REACTIVE) !== 0) { - set(/** @type {import('#client').Value} */ (item.i), index); + set(/** @type {Value} */ (item.i), index); } else { item.i = index; } @@ -462,15 +460,15 @@ function update_item(item, value, index, type) { /** * @template V * @param {Node} anchor - * @param {import('#client').EachState} state - * @param {import('#client').EachItem | null} prev - * @param {import('#client').EachItem | null} next + * @param {EachState} state + * @param {EachItem | null} prev + * @param {EachItem | null} next * @param {V} value * @param {unknown} key * @param {number} index - * @param {(anchor: Node, item: V | import('#client').Source, index: number | import('#client').Value) => void} render_fn + * @param {(anchor: Node, item: V | Source, index: number | Value) => void} render_fn * @param {number} flags - * @returns {import('#client').EachItem} + * @returns {EachItem} */ function create_item(anchor, state, prev, next, value, key, index, render_fn, flags) { var previous_each_item = current_each_item; @@ -482,7 +480,7 @@ function create_item(anchor, state, prev, next, value, key, index, render_fn, fl var v = reactive ? (mutable ? mutable_source(value) : source(value)) : value; var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index); - /** @type {import('#client').EachItem} */ + /** @type {EachItem} */ var item = { i, v, @@ -519,29 +517,27 @@ function create_item(anchor, state, prev, next, value, key, index, render_fn, fl } /** - * @param {import('#client').EachItem} item - * @param {import('#client').EachItem | null} next + * @param {EachItem} item + * @param {EachItem | null} next * @param {Text | Element | Comment} anchor */ function move(item, next, anchor) { - var end = item.next - ? /** @type {import('#client').EffectNodes} */ (item.next.e.nodes).start - : anchor; + var end = item.next ? /** @type {EffectNodes} */ (item.next.e.nodes).start : anchor; - var dest = next ? /** @type {import('#client').EffectNodes} */ (next.e.nodes).start : anchor; - var node = /** @type {import('#client').EffectNodes} */ (item.e.nodes).start; + var dest = next ? /** @type {EffectNodes} */ (next.e.nodes).start : anchor; + var node = /** @type {EffectNodes} */ (item.e.nodes).start; while (node !== end) { - var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling); + var next_node = /** @type {TemplateNode} */ (node.nextSibling); dest.before(node); node = next_node; } } /** - * @param {import('#client').EachState} state - * @param {import('#client').EachItem | null} prev - * @param {import('#client').EachItem | null} next + * @param {EachState} state + * @param {EachItem | null} prev + * @param {EachItem | null} next */ function link(state, prev, next) { if (prev === null) { diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index a0c5ca23e0..7b172f0a12 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,3 +1,4 @@ +/** @import { ProxyMetadata, ProxyStateObject, Source } from '#client' */ import { DEV } from 'esm-env'; import { get, current_component_context, untrack, current_effect } from './runtime.js'; import { @@ -18,9 +19,9 @@ import * as e from './errors.js'; /** * @template T * @param {T} value - * @param {import('#client').ProxyMetadata | null} [parent] - * @param {import('#client').Source} [prev] dev mode only - * @returns {import('#client').ProxyStateObject | T} + * @param {ProxyMetadata | null} [parent] + * @param {Source} [prev] dev mode only + * @returns {ProxyStateObject | T} */ export function proxy(value, parent = null, prev) { if ( @@ -31,7 +32,7 @@ export function proxy(value, parent = null, prev) { ) { // If we have an existing proxy, return it... if (STATE_SYMBOL in value) { - const metadata = /** @type {import('#client').ProxyMetadata} */ (value[STATE_SYMBOL]); + const metadata = /** @type {ProxyMetadata} */ (value[STATE_SYMBOL]); // ...unless the proxy belonged to a different object, because // someone copied the state symbol using `Reflect.ownKeys(...)` @@ -53,7 +54,7 @@ export function proxy(value, parent = null, prev) { const proxy = new Proxy(value, state_proxy_handler); define_property(value, STATE_SYMBOL, { - value: /** @type {import('#client').ProxyMetadata} */ ({ + value: /** @type {ProxyMetadata} */ ({ s: new Map(), v: source(0), a: is_array(value), @@ -94,18 +95,18 @@ export function proxy(value, parent = null, prev) { } /** - * @param {import('#client').Source} signal + * @param {Source} signal * @param {1 | -1} [d] */ function update_version(signal, d = 1) { set(signal, signal.v + d); } -/** @type {ProxyHandler>} */ +/** @type {ProxyHandler>} */ const state_proxy_handler = { defineProperty(target, prop, descriptor) { if (descriptor.value) { - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; const s = metadata.s.get(prop); @@ -116,7 +117,7 @@ const state_proxy_handler = { }, deleteProperty(target, prop) { - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; const s = metadata.s.get(prop); const is_array = metadata.a; @@ -149,7 +150,7 @@ const state_proxy_handler = { return Reflect.get(target, STATE_SYMBOL); } - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; let s = metadata.s.get(prop); @@ -170,7 +171,7 @@ const state_proxy_handler = { getOwnPropertyDescriptor(target, prop) { const descriptor = Reflect.getOwnPropertyDescriptor(target, prop); if (descriptor && 'value' in descriptor) { - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; const s = metadata.s.get(prop); @@ -186,7 +187,7 @@ const state_proxy_handler = { if (prop === STATE_SYMBOL) { return true; } - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; const has = Reflect.has(target, prop); @@ -208,7 +209,7 @@ const state_proxy_handler = { }, set(target, prop, value, receiver) { - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; let s = metadata.s.get(prop); // If we haven't yet created a source for this property, we need to ensure @@ -227,7 +228,7 @@ const state_proxy_handler = { const not_has = !(prop in target); if (DEV) { - /** @type {import('#client').ProxyMetadata | undefined} */ + /** @type {ProxyMetadata | undefined} */ const prop_metadata = value?.[STATE_SYMBOL]; if (prop_metadata && prop_metadata?.parent !== metadata) { widen_ownership(metadata, prop_metadata); @@ -271,7 +272,7 @@ const state_proxy_handler = { }, ownKeys(target) { - /** @type {import('#client').ProxyMetadata} */ + /** @type {ProxyMetadata} */ const metadata = target[STATE_SYMBOL]; get(metadata.v); diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 58bf3a3342..7f6b239114 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -1,3 +1,5 @@ +/** @import { ComponentContext, Effect, EffectNodes, TemplateNode } from '#client' */ +/** @import { Component, ComponentType, SvelteComponent } from '../../index.js' */ import { DEV } from 'esm-env'; import { clear_text_content, empty, init_operations } from './dom/operations.js'; import { @@ -59,7 +61,7 @@ export function set_text(text, value) { * * @template {Record} Props * @template {Record} Exports - * @param {import('../../index.js').ComponentType> | import('../../index.js').Component} component + * @param {ComponentType> | Component} component * @param {{} extends Props ? { * target: Document | Element | ShadowRoot; * anchor?: Node; @@ -87,7 +89,7 @@ export function mount(component, options) { * * @template {Record} Props * @template {Record} Exports - * @param {import('../../index.js').ComponentType> | import('../../index.js').Component} component + * @param {ComponentType> | Component} component * @param {{} extends Props ? { * target: Document | Element | ShadowRoot; * props?: Props; @@ -112,12 +114,12 @@ export function hydrate(component, options) { const previous_hydrate_node = hydrate_node; try { - var anchor = /** @type {import('#client').TemplateNode} */ (target.firstChild); + var anchor = /** @type {TemplateNode} */ (target.firstChild); while ( anchor && (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) ) { - anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling); + anchor = /** @type {TemplateNode} */ (anchor.nextSibling); } if (!anchor) { @@ -171,7 +173,7 @@ const document_listeners = new Map(); /** * @template {Record} Exports - * @param {import('../../index.js').ComponentType> | import('../../index.js').Component} Component + * @param {ComponentType> | Component} Component * @param {{ * target: Document | Element | ShadowRoot; * anchor: Node; @@ -226,7 +228,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro branch(() => { if (context) { push({}); - var ctx = /** @type {import('#client').ComponentContext} */ (current_component_context); + var ctx = /** @type {ComponentContext} */ (current_component_context); ctx.c = context; } @@ -236,7 +238,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro } if (hydrating) { - assign_nodes(/** @type {import('#client').TemplateNode} */ (anchor), null); + assign_nodes(/** @type {TemplateNode} */ (anchor), null); } should_intro = intro; @@ -245,9 +247,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro should_intro = true; if (hydrating) { - /** @type {import('#client').Effect & { nodes: import('#client').EffectNodes }} */ ( - current_effect - ).nodes.end = hydrate_node; + /** @type {Effect & { nodes: EffectNodes }} */ (current_effect).nodes.end = hydrate_node; } if (context) { diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 9749002bf7..779548a65c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1,3 +1,4 @@ +/** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */ import { DEV } from 'esm-env'; import { define_property, get_descriptors, get_prototype_of } from '../shared/utils.js'; import { @@ -57,24 +58,24 @@ export function set_is_destroying_effect(value) { // Handle effect queues -/** @type {import('#client').Effect[]} */ +/** @type {Effect[]} */ let current_queued_root_effects = []; let flush_count = 0; // Handle signal reactivity tree dependencies and reactions -/** @type {null | import('#client').Reaction} */ +/** @type {null | Reaction} */ export let current_reaction = null; -/** @param {null | import('#client').Reaction} reaction */ +/** @param {null | Reaction} reaction */ export function set_current_reaction(reaction) { current_reaction = reaction; } -/** @type {null | import('#client').Effect} */ +/** @type {null | Effect} */ export let current_effect = null; -/** @param {null | import('#client').Effect} effect */ +/** @param {null | Effect} effect */ export function set_current_effect(effect) { current_effect = effect; } @@ -83,7 +84,7 @@ export function set_current_effect(effect) { * The dependencies of the reaction that is currently being executed. In many cases, * the dependencies are unchanged between runs, and so this will be `null` unless * and until a new dependency is accessed — we track this via `skipped_deps` - * @type {null | import('#client').Value[]} + * @type {null | Value[]} */ export let new_deps = null; @@ -92,11 +93,11 @@ let skipped_deps = 0; /** * Tracks writes that the effect it's executed in doesn't listen to yet, * so that the dependency can be added to the effect later on if it then reads it - * @type {null | import('#client').Source[]} + * @type {null | Source[]} */ export let current_untracked_writes = null; -/** @param {null | import('#client').Source[]} value */ +/** @param {null | Source[]} value */ export function set_current_untracked_writes(value) { current_untracked_writes = value; } @@ -112,10 +113,10 @@ export let is_signals_recorded = false; let captured_signals = new Set(); // Handling runtime component context -/** @type {import('#client').ComponentContext | null} */ +/** @type {ComponentContext | null} */ export let current_component_context = null; -/** @param {import('#client').ComponentContext | null} context */ +/** @param {ComponentContext | null} context */ export function set_current_component_context(context) { current_component_context = context; } @@ -128,11 +129,11 @@ export function set_current_component_context(context) { * * * ``` - * @type {import('#client').ComponentContext['function']} + * @type {ComponentContext['function']} */ export let dev_current_component_function = null; -/** @param {import('#client').ComponentContext['function']} fn */ +/** @param {ComponentContext['function']} fn */ export function set_dev_current_component_function(fn) { dev_current_component_function = fn; } @@ -149,7 +150,7 @@ export function is_runes() { /** * Determines whether a derived or effect is dirty. * If it is MAYBE_DIRTY, will set the status to CLEAN - * @param {import('#client').Reaction} reaction + * @param {Reaction} reaction * @returns {boolean} */ export function check_dirtiness(reaction) { @@ -177,8 +178,8 @@ export function check_dirtiness(reaction) { for (i = 0; i < dependencies.length; i++) { var dependency = dependencies[i]; - if (check_dirtiness(/** @type {import('#client').Derived} */ (dependency))) { - update_derived(/** @type {import('#client').Derived} */ (dependency)); + if (check_dirtiness(/** @type {Derived} */ (dependency))) { + update_derived(/** @type {Derived} */ (dependency)); } if (dependency.version > reaction.version) { @@ -205,8 +206,8 @@ export function check_dirtiness(reaction) { /** * @param {Error} error - * @param {import("#client").Effect} effect - * @param {import("#client").ComponentContext | null} component_context + * @param {Effect} effect + * @param {ComponentContext | null} component_context */ function handle_error(error, effect, component_context) { // Given we don't yet have error boundaries, we will just always throw. @@ -222,7 +223,7 @@ function handle_error(error, effect, component_context) { component_stack.push(effect_name); } - /** @type {import("#client").ComponentContext | null} */ + /** @type {ComponentContext | null} */ let current_context = component_context; while (current_context !== null) { @@ -266,7 +267,7 @@ function handle_error(error, effect, component_context) { /** * @template V - * @param {import('#client').Reaction} reaction + * @param {Reaction} reaction * @returns {V} */ export function update_reaction(reaction) { @@ -276,7 +277,7 @@ export function update_reaction(reaction) { var previous_reaction = current_reaction; var previous_skip_reaction = current_skip_reaction; - new_deps = /** @type {null | import('#client').Value[]} */ (null); + new_deps = /** @type {null | Value[]} */ (null); skipped_deps = 0; current_untracked_writes = null; current_reaction = (reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; @@ -349,8 +350,8 @@ export function update_reaction(reaction) { /** * @template V - * @param {import('#client').Reaction} signal - * @param {import('#client').Value} dependency + * @param {Reaction} signal + * @param {Value} dependency * @returns {void} */ function remove_reaction(signal, dependency) { @@ -378,12 +379,12 @@ function remove_reaction(signal, dependency) { if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) { dependency.f ^= DISCONNECTED; } - remove_reactions(/** @type {import('#client').Derived} **/ (dependency), 0); + remove_reactions(/** @type {Derived} **/ (dependency), 0); } } /** - * @param {import('#client').Reaction} signal + * @param {Reaction} signal * @param {number} start_index * @returns {void} */ @@ -408,7 +409,7 @@ export function remove_reactions(signal, start_index) { } /** - * @param {import('#client').Reaction} signal + * @param {Reaction} signal * @param {boolean} remove_dom * @returns {void} */ @@ -424,7 +425,7 @@ export function destroy_effect_children(signal, remove_dom = false) { } /** - * @param {import('#client').Effect} effect + * @param {Effect} effect * @returns {void} */ export function update_effect(effect) { @@ -480,7 +481,7 @@ function infinite_loop_guard() { } /** - * @param {Array} root_effects + * @param {Array} root_effects * @returns {void} */ function flush_queued_root_effects(root_effects) { @@ -501,7 +502,7 @@ function flush_queued_root_effects(root_effects) { if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) { flush_queued_effects([effect]); } else { - /** @type {import('#client').Effect[]} */ + /** @type {Effect[]} */ var collected_effects = []; process_effects(effect, collected_effects); @@ -514,7 +515,7 @@ function flush_queued_root_effects(root_effects) { } /** - * @param {Array} effects + * @param {Array} effects * @returns {void} */ function flush_queued_effects(effects) { @@ -559,7 +560,7 @@ function process_deferred() { } /** - * @param {import('#client').Effect} signal + * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { @@ -592,8 +593,8 @@ export function schedule_effect(signal) { * bitwise flag passed in only. The collected effects array will be populated with all the user * effects to be flushed. * - * @param {import('#client').Effect} effect - * @param {import('#client').Effect[]} collected_effects + * @param {Effect} effect + * @param {Effect[]} collected_effects * @returns {void} */ function process_effects(effect, collected_effects) { @@ -679,7 +680,7 @@ export function flush_sync(fn) { try { infinite_loop_guard(); - /** @type {import('#client').Effect[]} */ + /** @type {Effect[]} */ const root_effects = []; current_scheduler_mode = FLUSH_SYNC; @@ -717,7 +718,7 @@ export async function tick() { /** * @template V - * @param {import('#client').Value} signal + * @param {Value} signal * @returns {V} */ export function get(signal) { @@ -765,7 +766,7 @@ export function get(signal) { } if ((flags & DERIVED) !== 0) { - var derived = /** @type {import('#client').Derived} */ (signal); + var derived = /** @type {Derived} */ (signal); if (check_dirtiness(derived)) { update_derived(derived); @@ -801,7 +802,7 @@ export function invalidate_inner_signals(fn) { for (signal of captured) { // Go one level up because derived signals created as part of props in legacy mode if ((signal.f & LEGACY_DERIVED_PROP) !== 0) { - for (const dep of /** @type {import('#client').Derived} */ (signal).deps || []) { + for (const dep of /** @type {Derived} */ (signal).deps || []) { if ((dep.f & DERIVED) === 0) { mutate(dep, null /* doesnt matter */); } @@ -833,7 +834,7 @@ export function untrack(fn) { const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN); /** - * @param {import('#client').Signal} signal + * @param {Signal} signal * @param {number} status * @returns {void} */ @@ -843,14 +844,12 @@ export function set_signal_status(signal, status) { /** * @template V - * @param {V | import('#client').Value} val - * @returns {val is import('#client').Value} + * @param {V | Value} val + * @returns {val is Value} */ export function is_signal(val) { return ( - typeof val === 'object' && - val !== null && - typeof (/** @type {import('#client').Value} */ (val).f) === 'number' + typeof val === 'object' && val !== null && typeof (/** @type {Value} */ (val).f) === 'number' ); } @@ -868,8 +867,7 @@ export function getContext(key) { const result = /** @type {T} */ (context_map.get(key)); if (DEV) { - const fn = /** @type {import('#client').ComponentContext} */ (current_component_context) - .function; + const fn = /** @type {ComponentContext} */ (current_component_context).function; if (fn) { add_owner(result, fn, true); } @@ -949,7 +947,7 @@ function get_or_init_context_map(name) { } /** - * @param {import('#client').ComponentContext} component_context + * @param {ComponentContext} component_context * @returns {Map | null} */ function get_parent_context(component_context) { @@ -965,7 +963,7 @@ function get_parent_context(component_context) { } /** - * @param {import('#client').Value} signal + * @param {Value} signal * @param {1 | -1} [d] * @returns {number} */ @@ -976,7 +974,7 @@ export function update(signal, d = 1) { } /** - * @param {import('#client').Value} signal + * @param {Value} signal * @param {1 | -1} [d] * @returns {number} */ @@ -1156,7 +1154,7 @@ export function deep_read(value, visited = new Set()) { /** * @template V - * @param {V | import('#client').Value} value + * @param {V | Value} value * @returns {V} */ export function unwrap(value) { diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index a99f3dfb2a..b393174e0f 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -373,8 +373,6 @@ declare module 'svelte' { * Synchronously flushes any pending state changes and those that result from it. * */ export function flushSync(fn?: (() => void) | undefined): void; - /** Anything except a function */ - type NotFunction = T extends Function ? never : T; /** * Create a snippet programmatically * */ @@ -382,6 +380,8 @@ declare module 'svelte' { render: () => string; setup?: (element: Element) => void; }): Snippet; + /** Anything except a function */ + type NotFunction = T extends Function ? never : T; /** * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component. * Transitions will play during the initial render unless the `intro` option is set to `false`. From 8d139210b7f69232b1a9f1d2d0a4a4563be707c4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 16:35:48 -0400 Subject: [PATCH 6/7] Revert "breaking: avoid flushing queued updates on mount/hydrate" (#12593) * Revert "breaking: avoid flushing queued updates on mount/hydrate (#12587)" This reverts commit 20b879717a6385ec3f00df64f1e39526b142e0e0. * Update packages/svelte/src/internal/client/render.js --- .changeset/slow-gorillas-yawn.md | 5 -- packages/svelte/src/internal/client/render.js | 52 ++++++++++--------- .../svelte/src/internal/client/runtime.js | 7 ++- packages/svelte/tests/hydration/test.ts | 2 - .../svelte/tests/runtime-browser/driver.js | 3 -- .../hydrate-modified-input-group/_config.js | 2 - .../samples/hydrate-modified-input/_config.js | 2 - 7 files changed, 33 insertions(+), 40 deletions(-) delete mode 100644 .changeset/slow-gorillas-yawn.md diff --git a/.changeset/slow-gorillas-yawn.md b/.changeset/slow-gorillas-yawn.md deleted file mode 100644 index 376b4d041c..0000000000 --- a/.changeset/slow-gorillas-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -breaking: avoid flushing queued updates on mount/hydrate diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 7f6b239114..4bb01a4899 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -81,7 +81,8 @@ export function set_text(text, value) { */ export function mount(component, options) { const anchor = options.anchor ?? options.target.appendChild(empty()); - return _mount(component, { ...options, anchor }); + // Don't flush previous effects to ensure order of outer effects stays consistent + return flush_sync(() => _mount(component, { ...options, anchor }), false); } /** @@ -114,35 +115,38 @@ export function hydrate(component, options) { const previous_hydrate_node = hydrate_node; try { - var anchor = /** @type {TemplateNode} */ (target.firstChild); - while ( - anchor && - (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) - ) { - anchor = /** @type {TemplateNode} */ (anchor.nextSibling); - } + // Don't flush previous effects to ensure order of outer effects stays consistent + return flush_sync(() => { + var anchor = /** @type {TemplateNode} */ (target.firstChild); + while ( + anchor && + (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) + ) { + anchor = /** @type {TemplateNode} */ (anchor.nextSibling); + } - if (!anchor) { - throw HYDRATION_ERROR; - } + if (!anchor) { + throw HYDRATION_ERROR; + } - set_hydrating(true); - set_hydrate_node(/** @type {Comment} */ (anchor)); - hydrate_next(); + set_hydrating(true); + set_hydrate_node(/** @type {Comment} */ (anchor)); + hydrate_next(); - const instance = _mount(component, { ...options, anchor }); + const instance = _mount(component, { ...options, anchor }); - if ( - hydrate_node.nodeType !== 8 || - /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END - ) { - w.hydration_mismatch(); - throw HYDRATION_ERROR; - } + if ( + hydrate_node.nodeType !== 8 || + /** @type {Comment} */ (hydrate_node).data !== HYDRATION_END + ) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } - set_hydrating(false); + set_hydrating(false); - return /** @type {Exports} */ (instance); + return instance; + }, false); } catch (error) { if (error === HYDRATION_ERROR) { // TODO it's possible for event listeners to have been added and diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 779548a65c..16facffabd 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -671,9 +671,10 @@ function process_effects(effect, collected_effects) { * Internal version of `flushSync` with the option to not flush previous effects. * Returns the result of the passed function, if given. * @param {() => any} [fn] + * @param {boolean} [flush_previous] * @returns {any} */ -export function flush_sync(fn) { +export function flush_sync(fn, flush_previous = true) { var previous_scheduler_mode = current_scheduler_mode; var previous_queued_root_effects = current_queued_root_effects; @@ -687,7 +688,9 @@ export function flush_sync(fn) { current_queued_root_effects = root_effects; is_micro_task_queued = false; - flush_queued_root_effects(previous_queued_root_effects); + if (flush_previous) { + flush_queued_root_effects(previous_queued_root_effects); + } var result = fn?.(); diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index d592a65de3..fab0e5b308 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -8,7 +8,6 @@ import { suite, assert_ok, type BaseTest } from '../suite.js'; import { createClassComponent } from 'svelte/legacy'; import { render } from 'svelte/server'; import type { CompileOptions } from '#compiler'; -import { flushSync } from 'svelte'; interface HydrationTest extends BaseTest { load_compiled?: boolean; @@ -115,7 +114,6 @@ const { test, run } = suite(async (config, cwd) => { if (!override) { const expected = read(`${cwd}/_expected.html`) ?? rendered.html; - flushSync(); assert.equal(target.innerHTML.trim(), expected.trim()); } diff --git a/packages/svelte/tests/runtime-browser/driver.js b/packages/svelte/tests/runtime-browser/driver.js index ef6acd08f6..7a5603e9b8 100644 --- a/packages/svelte/tests/runtime-browser/driver.js +++ b/packages/svelte/tests/runtime-browser/driver.js @@ -5,7 +5,6 @@ import config from '__CONFIG__'; // @ts-expect-error import * as assert from 'assert.js'; import { createClassComponent } from 'svelte/legacy'; -import { flushSync } from 'svelte'; /** @param {HTMLElement} target */ export default async function (target) { @@ -46,8 +45,6 @@ export default async function (target) { } while (new Date().getTime() <= start + ms); }; - flushSync(); - if (config.html) { assert.htmlEqual(target.innerHTML, config.html); } diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js index 7596fd97be..7f3bfac707 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input-group/_config.js @@ -1,4 +1,3 @@ -import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +9,6 @@ export default test({ inputs[1].dispatchEvent(new window.Event('change')); // Hydration shouldn't reset the value to 1 hydrate(); - flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js index 10dac59fae..e5bbe5b0fe 100644 --- a/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/hydrate-modified-input/_config.js @@ -1,4 +1,3 @@ -import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +9,6 @@ export default test({ input.dispatchEvent(new window.Event('input')); // Hydration shouldn't reset the value to empty hydrate(); - flushSync(); assert.htmlEqual(target.innerHTML, '\nfoo'); } From c1731409698b944c5ee27568f3ef66fedb24df2f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 24 Jul 2024 16:36:15 -0400 Subject: [PATCH 7/7] chore: use JSDoc import (#12595) * more * more * more --- .../src/internal/client/dom/blocks/each.js | 2 +- .../src/internal/client/dom/blocks/if.js | 10 ++-- .../client/dom/elements/transitions.js | 33 ++++++------ .../internal/client/reactivity/deriveds.js | 15 +++--- .../src/internal/client/reactivity/effects.js | 52 +++++++++---------- .../src/internal/client/reactivity/sources.js | 15 +++--- packages/svelte/src/internal/server/dev.js | 7 +-- packages/svelte/src/internal/server/index.js | 3 +- packages/svelte/src/motion/spring.js | 19 ++++--- packages/svelte/src/reactivity/map.js | 3 +- packages/svelte/src/reactivity/set.js | 3 +- packages/svelte/src/reactivity/utils.js | 3 +- 12 files changed, 88 insertions(+), 77 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index a1566d1f14..f76048637a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -59,7 +59,7 @@ export function index(_, i) { * @param {EachState} state * @param {EachItem[]} items * @param {null | Node} controlled_anchor - * @param {Map} items_map + * @param {Map} items_map */ function pause_effects(state, items, controlled_anchor, items_map) { /** @type {TransitionManager[]} */ diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index dc3ffbd72d..4d8e9412d3 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -1,4 +1,4 @@ -/** @import { TemplateNode } from '#client' */ +/** @import { Effect, TemplateNode } from '#client' */ import { EFFECT_TRANSPARENT } from '../../constants.js'; import { hydrate_next, @@ -14,8 +14,8 @@ import { HYDRATION_START_ELSE } from '../../../../constants.js'; /** * @param {TemplateNode} node * @param {() => boolean} get_condition - * @param {(anchor: Node) => import('#client').Dom} consequent_fn - * @param {null | ((anchor: Node) => import('#client').Dom)} [alternate_fn] + * @param {(anchor: Node) => void} consequent_fn + * @param {null | ((anchor: Node) => void)} [alternate_fn] * @param {boolean} [elseif] True if this is an `{:else if ...}` block rather than an `{#if ...}`, as that affects which transitions are considered 'local' * @returns {void} */ @@ -26,10 +26,10 @@ export function if_block(node, get_condition, consequent_fn, alternate_fn = null var anchor = node; - /** @type {import('#client').Effect | null} */ + /** @type {Effect | null} */ var consequent_effect = null; - /** @type {import('#client').Effect | null} */ + /** @type {Effect | null} */ var alternate_effect = null; /** @type {boolean | null} */ diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 3369e263f3..5d0005b2c1 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -1,3 +1,4 @@ +/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, Task, TransitionFn, TransitionManager } from '#client' */ import { noop, is_function } from '../../../shared/utils.js'; import { effect } from '../../reactivity/effects.js'; import { current_effect, untrack } from '../../runtime.js'; @@ -60,11 +61,11 @@ const linear = (t) => t; * and attaches it to the block, so that moves can be animated following reconciliation. * @template P * @param {Element} element - * @param {() => import('#client').AnimateFn

} get_fn + * @param {() => AnimateFn

} get_fn * @param {(() => P) | null} get_params */ export function animation(element, get_fn, get_params) { - var item = /** @type {import('#client').EachItem} */ (current_each_item); + var item = /** @type {EachItem} */ (current_each_item); /** @type {DOMRect} */ var from; @@ -72,7 +73,7 @@ export function animation(element, get_fn, get_params) { /** @type {DOMRect} */ var to; - /** @type {import('#client').Animation | undefined} */ + /** @type {Animation | undefined} */ var animation; /** @type {null | { position: string, width: string, height: string, transform: string }} */ @@ -167,7 +168,7 @@ export function animation(element, get_fn, get_params) { * @template P * @param {number} flags * @param {HTMLElement} element - * @param {() => import('#client').TransitionFn

} get_fn + * @param {() => TransitionFn

} get_fn * @param {(() => P) | null} get_params * @returns {void} */ @@ -180,15 +181,15 @@ export function transition(flags, element, get_fn, get_params) { /** @type {'in' | 'out' | 'both'} */ var direction = is_both ? 'both' : is_intro ? 'in' : 'out'; - /** @type {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig) | undefined} */ + /** @type {AnimationConfig | ((opts: { direction: 'in' | 'out' }) => AnimationConfig) | undefined} */ var current_options; var inert = element.inert; - /** @type {import('#client').Animation | undefined} */ + /** @type {Animation | undefined} */ var intro; - /** @type {import('#client').Animation | undefined} */ + /** @type {Animation | undefined} */ var outro; /** @type {(() => void) | undefined} */ @@ -201,7 +202,7 @@ export function transition(flags, element, get_fn, get_params) { return (current_options ??= get_fn()(element, get_params?.(), { direction })); } - /** @type {import('#client').TransitionManager} */ + /** @type {TransitionManager} */ var transition = { is_global, in() { @@ -271,7 +272,7 @@ export function transition(flags, element, get_fn, get_params) { } }; - var e = /** @type {import('#client').Effect} */ (current_effect); + var e = /** @type {Effect} */ (current_effect); (e.transitions ??= []).push(transition); @@ -282,7 +283,7 @@ export function transition(flags, element, get_fn, get_params) { let run = is_global; if (!run) { - var block = /** @type {import('#client').Effect | null} */ (e.parent); + var block = /** @type {Effect | null} */ (e.parent); // skip over transparent blocks (e.g. snippets, else-if blocks) while (block && (block.f & EFFECT_TRANSPARENT) !== 0) { @@ -305,12 +306,12 @@ export function transition(flags, element, get_fn, get_params) { /** * Animates an element, according to the provided configuration * @param {Element} element - * @param {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig)} options - * @param {import('#client').Animation | undefined} counterpart The corresponding intro/outro to this outro/intro + * @param {AnimationConfig | ((opts: { direction: 'in' | 'out' }) => AnimationConfig)} options + * @param {Animation | undefined} counterpart The corresponding intro/outro to this outro/intro * @param {number} t2 The target `t` value — `1` for intro, `0` for outro * @param {(() => void) | undefined} on_finish Called after successfully completing the animation * @param {(() => void) | undefined} on_abort Called if the animation is aborted - * @returns {import('#client').Animation} + * @returns {Animation} */ function animate(element, options, counterpart, t2, on_finish, on_abort) { var is_intro = t2 === 1; @@ -319,7 +320,7 @@ function animate(element, options, counterpart, t2, on_finish, on_abort) { // In the case of a deferred transition (such as `crossfade`), `option` will be // a function rather than an `AnimationConfig`. We need to call this function // once DOM has been updated... - /** @type {import('#client').Animation} */ + /** @type {Animation} */ var a; queue_micro_task(() => { @@ -358,10 +359,10 @@ function animate(element, options, counterpart, t2, on_finish, on_abort) { var duration = options.duration * Math.abs(delta); var end = start + duration; - /** @type {Animation} */ + /** @type {globalThis.Animation} */ var animation; - /** @type {import('#client').Task} */ + /** @type {Task} */ var task; if (css) { diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 926c7b13f3..2b9f87dfbf 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -1,3 +1,4 @@ +/** @import { Derived } from '#client' */ import { CLEAN, DERIVED, DESTROYED, DIRTY, MAYBE_DIRTY, UNOWNED } from '../constants.js'; import { current_reaction, @@ -16,14 +17,14 @@ export let updating_derived = false; /** * @template V * @param {() => V} fn - * @returns {import('#client').Derived} + * @returns {Derived} */ /*#__NO_SIDE_EFFECTS__*/ export function derived(fn) { let flags = DERIVED | DIRTY; if (current_effect === null) flags |= UNOWNED; - /** @type {import('#client').Derived} */ + /** @type {Derived} */ const signal = { deps: null, deriveds: null, @@ -38,7 +39,7 @@ export function derived(fn) { }; if (current_reaction !== null && (current_reaction.f & DERIVED) !== 0) { - var current_derived = /** @type {import('#client').Derived} */ (current_reaction); + var current_derived = /** @type {Derived} */ (current_reaction); if (current_derived.deriveds === null) { current_derived.deriveds = [signal]; } else { @@ -52,7 +53,7 @@ export function derived(fn) { /** * @template V * @param {() => V} fn - * @returns {import('#client').Derived} + * @returns {Derived} */ /*#__NO_SIDE_EFFECTS__*/ export function derived_safe_equal(fn) { @@ -62,7 +63,7 @@ export function derived_safe_equal(fn) { } /** - * @param {import('#client').Derived} derived + * @param {Derived} derived * @returns {void} */ function destroy_derived_children(derived) { @@ -79,7 +80,7 @@ function destroy_derived_children(derived) { } /** - * @param {import('#client').Derived} derived + * @param {Derived} derived * @returns {void} */ export function update_derived(derived) { @@ -103,7 +104,7 @@ export function update_derived(derived) { } /** - * @param {import('#client').Derived} signal + * @param {Derived} signal * @returns {void} */ export function destroy_derived(signal) { diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index fa920af207..cf2e7cc11d 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -1,3 +1,4 @@ +/** @import { ComponentContext, ComponentContextLegacy, Effect, Reaction, TemplateNode, TransitionManager } from '#client' */ import { check_dirtiness, current_component_context, @@ -57,8 +58,8 @@ export function validate_effect(rune) { } /** - * @param {import("#client").Effect} effect - * @param {import("#client").Reaction} parent_effect + * @param {Effect} effect + * @param {Reaction} parent_effect */ export function push_effect(effect, parent_effect) { var parent_last = parent_effect.last; @@ -76,12 +77,12 @@ export function push_effect(effect, parent_effect) { * @param {null | (() => void | (() => void))} fn * @param {boolean} sync * @param {boolean} push - * @returns {import('#client').Effect} + * @returns {Effect} */ function create_effect(type, fn, sync, push = true) { var is_root = (type & ROOT_EFFECT) !== 0; - /** @type {import('#client').Effect} */ + /** @type {Effect} */ var effect = { ctx: current_component_context, deps: null, @@ -187,7 +188,7 @@ export function user_effect(fn) { } if (defer) { - var context = /** @type {import('#client').ComponentContext} */ (current_component_context); + var context = /** @type {ComponentContext} */ (current_component_context); (context.e ??= []).push(fn); } else { var signal = effect(fn); @@ -198,7 +199,7 @@ export function user_effect(fn) { /** * Internal representation of `$effect.pre(...)` * @param {() => void | (() => void)} fn - * @returns {import('#client').Effect} + * @returns {Effect} */ export function user_pre_effect(fn) { validate_effect('$effect.pre'); @@ -229,7 +230,7 @@ export function effect_root(fn) { /** * @param {() => void | (() => void)} fn - * @returns {import('#client').Effect} + * @returns {Effect} */ export function effect(fn) { return create_effect(EFFECT, fn, false); @@ -241,9 +242,9 @@ export function effect(fn) { * @param {() => void | (() => void)} fn */ export function legacy_pre_effect(deps, fn) { - var context = /** @type {import('#client').ComponentContextLegacy} */ (current_component_context); + var context = /** @type {ComponentContextLegacy} */ (current_component_context); - /** @type {{ effect: null | import('#client').Effect, ran: boolean }} */ + /** @type {{ effect: null | Effect, ran: boolean }} */ var token = { effect: null, ran: false }; context.l.r1.push(token); @@ -261,7 +262,7 @@ export function legacy_pre_effect(deps, fn) { } export function legacy_pre_effect_reset() { - var context = /** @type {import('#client').ComponentContextLegacy} */ (current_component_context); + var context = /** @type {ComponentContextLegacy} */ (current_component_context); render_effect(() => { if (!get(context.l.r2)) return; @@ -283,7 +284,7 @@ export function legacy_pre_effect_reset() { /** * @param {() => void | (() => void)} fn - * @returns {import('#client').Effect} + * @returns {Effect} */ export function render_effect(fn) { return create_effect(RENDER_EFFECT, fn, true); @@ -291,7 +292,7 @@ export function render_effect(fn) { /** * @param {() => void | (() => void)} fn - * @returns {import('#client').Effect} + * @returns {Effect} */ export function template_effect(fn) { if (DEV) { @@ -319,7 +320,7 @@ export function branch(fn, push = true) { } /** - * @param {import("#client").Effect} effect + * @param {Effect} effect */ export function execute_effect_teardown(effect) { var teardown = effect.teardown; @@ -338,7 +339,7 @@ export function execute_effect_teardown(effect) { } /** - * @param {import('#client').Effect} effect + * @param {Effect} effect * @param {boolean} [remove_dom] * @returns {void} */ @@ -346,14 +347,13 @@ export function destroy_effect(effect, remove_dom = true) { var removed = false; if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes !== null) { - /** @type {import('#client').TemplateNode | null} */ + /** @type {TemplateNode | null} */ var node = effect.nodes.start; var end = effect.nodes.end; while (node !== null) { - /** @type {import('#client').TemplateNode | null} */ - var next = - node === end ? null : /** @type {import('#client').TemplateNode} */ (node.nextSibling); + /** @type {TemplateNode | null} */ + var next = node === end ? null : /** @type {TemplateNode} */ (node.nextSibling); node.remove(); node = next; @@ -396,7 +396,7 @@ export function destroy_effect(effect, remove_dom = true) { /** * Detach an effect from the effect tree, freeing up memory and * reducing the amount of work that happens on subsequent traversals - * @param {import('#client').Effect} effect + * @param {Effect} effect */ export function unlink_effect(effect) { var parent = effect.parent; @@ -418,11 +418,11 @@ export function unlink_effect(effect) { * It stays around (in memory, and in the DOM) until outro transitions have * completed, and if the state change is reversed then we _resume_ it. * A paused effect does not update, and the DOM subtree becomes inert. - * @param {import('#client').Effect} effect + * @param {Effect} effect * @param {() => void} [callback] */ export function pause_effect(effect, callback) { - /** @type {import('#client').TransitionManager[]} */ + /** @type {TransitionManager[]} */ var transitions = []; pause_children(effect, transitions, true); @@ -434,7 +434,7 @@ export function pause_effect(effect, callback) { } /** - * @param {import('#client').TransitionManager[]} transitions + * @param {TransitionManager[]} transitions * @param {() => void} fn */ export function run_out_transitions(transitions, fn) { @@ -450,8 +450,8 @@ export function run_out_transitions(transitions, fn) { } /** - * @param {import('#client').Effect} effect - * @param {import('#client').TransitionManager[]} transitions + * @param {Effect} effect + * @param {TransitionManager[]} transitions * @param {boolean} local */ export function pause_children(effect, transitions, local) { @@ -482,14 +482,14 @@ export function pause_children(effect, transitions, local) { /** * The opposite of `pause_effect`. We call this if (for example) * `x` becomes falsy then truthy: `{#if x}...{/if}` - * @param {import('#client').Effect} effect + * @param {Effect} effect */ export function resume_effect(effect) { resume_children(effect, true); } /** - * @param {import('#client').Effect} effect + * @param {Effect} effect * @param {boolean} local */ function resume_children(effect, local) { diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 8fa229d56f..86427a7509 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -1,3 +1,4 @@ +/** @import { Derived, Effect, Source, Value } from '#client' */ import { DEV } from 'esm-env'; import { current_component_context, @@ -31,7 +32,7 @@ let inspect_effects = new Set(); /** * @template V * @param {V} v - * @returns {import('#client').Source} + * @returns {Source} */ /*#__NO_SIDE_EFFECTS__*/ export function source(v) { @@ -47,7 +48,7 @@ export function source(v) { /** * @template V * @param {V} initial_value - * @returns {import('#client').Source} + * @returns {Source} */ /*#__NO_SIDE_EFFECTS__*/ export function mutable_source(initial_value) { @@ -65,7 +66,7 @@ export function mutable_source(initial_value) { /** * @template V - * @param {import('#client').Value} source + * @param {Value} source * @param {V} value */ export function mutate(source, value) { @@ -78,7 +79,7 @@ export function mutate(source, value) { /** * @template V - * @param {import('#client').Source} source + * @param {Source} source * @param {V} value * @returns {V} */ @@ -129,7 +130,7 @@ export function set(source, value) { } /** - * @param {import('#client').Value} signal + * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY * @returns {void} */ @@ -161,9 +162,9 @@ function mark_reactions(signal, status) { // If the signal a) was previously clean or b) is an unowned derived, then mark it if ((flags & (CLEAN | UNOWNED)) !== 0) { if ((flags & DERIVED) !== 0) { - mark_reactions(/** @type {import('#client').Derived} */ (reaction), MAYBE_DIRTY); + mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); } else { - schedule_effect(/** @type {import('#client').Effect} */ (reaction)); + schedule_effect(/** @type {Effect} */ (reaction)); } } } diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 045dda5948..dfffa7175b 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -1,3 +1,4 @@ +/** @import { Component, Payload } from '#server' */ import { FILENAME, disallowed_paragraph_contents, @@ -33,7 +34,7 @@ function stringify(element) { } /** - * @param {import('#server').Payload} payload + * @param {Payload} payload * @param {Element} parent * @param {Element} child */ @@ -51,13 +52,13 @@ function print_error(payload, parent, child) { } /** - * @param {import('#server').Payload} payload + * @param {Payload} payload * @param {string} tag * @param {number} line * @param {number} column */ export function push_element(payload, tag, line, column) { - var filename = /** @type {import('#server').Component} */ (current_component).function[FILENAME]; + var filename = /** @type {Component} */ (current_component).function[FILENAME]; var child = { tag, parent, filename, line, column }; if (parent !== null && !is_tag_valid_with_parent(tag, parent.tag)) { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 8c2b7de27e..a6d70d21de 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -1,3 +1,4 @@ +/** @import { ComponentType, SvelteComponent } from 'svelte' */ /** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; @@ -103,7 +104,7 @@ export let on_destroy = []; * Only available on the server and when compiling with the `server` option. * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. * @template {Record} Props - * @param {import('svelte').Component | import('svelte').ComponentType>} component + * @param {import('svelte').Component | ComponentType>} component * @param {{ props?: Omit; context?: Map }} [options] * @returns {RenderOutput} */ diff --git a/packages/svelte/src/motion/spring.js b/packages/svelte/src/motion/spring.js index ec56d35410..c3b4177579 100644 --- a/packages/svelte/src/motion/spring.js +++ b/packages/svelte/src/motion/spring.js @@ -1,3 +1,6 @@ +/** @import { Task } from '#client' */ +/** @import { SpringOpts, SpringUpdateOpts, TickContext } from './private.js' */ +/** @import { Spring } from './public.js' */ import { writable } from '../store/index.js'; import { loop } from '../internal/client/loop.js'; import { raf } from '../internal/client/timing.js'; @@ -5,7 +8,7 @@ import { is_date } from './utils.js'; /** * @template T - * @param {import('./private').TickContext} ctx + * @param {TickContext} ctx * @param {T} last_value * @param {T} current_value * @param {T} target_value @@ -53,15 +56,15 @@ function tick_spring(ctx, last_value, current_value, target_value) { * https://svelte.dev/docs/svelte-motion#spring * @template [T=any] * @param {T} [value] - * @param {import('./private').SpringOpts} [opts] - * @returns {import('./public.js').Spring} + * @param {SpringOpts} [opts] + * @returns {Spring} */ export function spring(value, opts = {}) { const store = writable(value); const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts; /** @type {number} */ let last_time; - /** @type {import('../internal/client/types').Task | null} */ + /** @type {Task | null} */ let task; /** @type {object} */ let current_token; @@ -74,7 +77,7 @@ export function spring(value, opts = {}) { let cancel_task = false; /** * @param {T} new_value - * @param {import('./private').SpringUpdateOpts} opts + * @param {SpringUpdateOpts} opts * @returns {Promise} */ function set(new_value, opts = {}) { @@ -101,7 +104,7 @@ export function spring(value, opts = {}) { return false; } inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1); - /** @type {import('./private').TickContext} */ + /** @type {TickContext} */ const ctx = { inv_mass, opts: spring, @@ -120,12 +123,12 @@ export function spring(value, opts = {}) { }); } return new Promise((fulfil) => { - /** @type {import('../internal/client/types').Task} */ (task).promise.then(() => { + /** @type {Task} */ (task).promise.then(() => { if (token === current_token) fulfil(); }); }); } - /** @type {import('./public.js').Spring} */ + /** @type {Spring} */ const spring = { set, update: (fn, opts) => set(fn(/** @type {T} */ (target_value), /** @type {T} */ (value)), opts), diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index 30da04c035..c732806cd0 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -1,3 +1,4 @@ +/** @import { Source } from '#client' */ import { DEV } from 'esm-env'; import { source, set } from '../internal/client/reactivity/sources.js'; import { get } from '../internal/client/runtime.js'; @@ -9,7 +10,7 @@ import { increment } from './utils.js'; * @extends {Map} */ export class SvelteMap extends Map { - /** @type {Map>} */ + /** @type {Map>} */ #sources = new Map(); #version = source(0); #size = source(0); diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 6206ce14ef..38723a0b9e 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -1,3 +1,4 @@ +/** @import { Source } from '#client' */ import { DEV } from 'esm-env'; import { source, set } from '../internal/client/reactivity/sources.js'; import { get } from '../internal/client/runtime.js'; @@ -13,7 +14,7 @@ var inited = false; * @extends {Set} */ export class SvelteSet extends Set { - /** @type {Map>} */ + /** @type {Map>} */ #sources = new Map(); #version = source(0); #size = source(0); diff --git a/packages/svelte/src/reactivity/utils.js b/packages/svelte/src/reactivity/utils.js index 5490dae7e5..355a000e9d 100644 --- a/packages/svelte/src/reactivity/utils.js +++ b/packages/svelte/src/reactivity/utils.js @@ -1,3 +1,4 @@ +/** @import { Source } from '#client' */ import { set } from '../internal/client/reactivity/sources.js'; /** @@ -30,7 +31,7 @@ function get_this() { return this; } -/** @param {import('#client').Source} source */ +/** @param {Source} source */ export function increment(source) { set(source, source.v + 1); }