Merge branch 'main' into fix-ordering

pull/12591/head
Rich Harris 2 years ago committed by GitHub
commit 2712ab9151
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: tweak element_invalid_self_closing_tag to exclude namespace

@ -1,3 +1,5 @@
/** @import { Comment, Program } from 'estree' */
/** @import { Node } from 'acorn' */
import * as acorn from 'acorn'; import * as acorn from 'acorn';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import { tsPlugin } from 'acorn-typescript'; import { tsPlugin } from 'acorn-typescript';
@ -23,7 +25,7 @@ export function parse(source, typescript) {
if (typescript) amend(source, ast); if (typescript) amend(source, ast);
add_comments(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) { function get_comment_handlers(source) {
/** /**
* @typedef {import('estree').Comment & { * @typedef {Comment & {
* start: number; * start: number;
* end: number; * end: number;
* }} CommentWithLocation * }} CommentWithLocation
@ -149,7 +151,7 @@ function get_comment_handlers(source) {
/** /**
* Tidy up some stuff left behind by acorn-typescript * Tidy up some stuff left behind by acorn-typescript
* @param {string} source * @param {string} source
* @param {import('acorn').Node} node * @param {Node} node
*/ */
function amend(source, node) { function amend(source, node) {
return walk(node, null, { return walk(node, null, {

@ -1,10 +1,12 @@
/** @import { Expression } from 'estree' */
/** @import { Parser } from '../index.js' */
import { parse_expression_at } from '../acorn.js'; import { parse_expression_at } from '../acorn.js';
import { regex_whitespace } from '../../patterns.js'; import { regex_whitespace } from '../../patterns.js';
import * as e from '../../../errors.js'; import * as e from '../../../errors.js';
/** /**
* @param {import('../index.js').Parser} parser * @param {Parser} parser
* @returns {import('estree').Expression} * @returns {Expression}
*/ */
export default function read_expression(parser) { export default function read_expression(parser) {
try { try {
@ -35,7 +37,7 @@ export default function read_expression(parser) {
parser.index = index; parser.index = index;
return /** @type {import('estree').Expression} */ (node); return /** @type {Expression} */ (node);
} catch (err) { } catch (err) {
parser.acorn_error(err); parser.acorn_error(err);
} }

@ -1,5 +1,6 @@
/** @import { Parser } from '../index.js' */ /** @import { Expression } from 'estree' */
/** @import * as Compiler from '#compiler' */ /** @import * as Compiler from '#compiler' */
/** @import { Parser } from '../index.js' */
import { is_void } from '../../../../constants.js'; import { is_void } from '../../../../constants.js';
import read_expression from '../read/expression.js'; import read_expression from '../read/expression.js';
import { read_script } from '../read/script.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; const first_value = value === true ? undefined : Array.isArray(value) ? value[0] : value;
/** @type {import('estree').Expression | null} */ /** @type {Expression | null} */
let expression = null; let expression = null;
if (first_value) { if (first_value) {

@ -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_pattern from '../read/context.js';
import read_expression from '../read/expression.js'; import read_expression from '../read/expression.js';
import * as e from '../../../errors.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*}/; const regex_whitespace_with_closing_curly_brace = /^\s*}/;
/** @param {import('../index.js').Parser} parser */ /** @param {Parser} parser */
export default function tag(parser) { export default function tag(parser) {
const start = parser.index; const start = parser.index;
parser.index += 1; parser.index += 1;
@ -29,7 +32,7 @@ export default function tag(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').ExpressionTag>>} */ /** @type {ReturnType<typeof parser.append<ExpressionTag>>} */
parser.append({ parser.append({
type: 'ExpressionTag', type: 'ExpressionTag',
start, start,
@ -42,7 +45,7 @@ export default function tag(parser) {
}); });
} }
/** @param {import('../index.js').Parser} parser */ /** @param {Parser} parser */
function open(parser) { function open(parser) {
let start = parser.index - 2; let start = parser.index - 2;
while (parser.template[start] !== '{') start -= 1; while (parser.template[start] !== '{') start -= 1;
@ -50,7 +53,7 @@ function open(parser) {
if (parser.eat('if')) { if (parser.eat('if')) {
parser.require_whitespace(); parser.require_whitespace();
/** @type {ReturnType<typeof parser.append<import('#compiler').IfBlock>>} */ /** @type {ReturnType<typeof parser.append<IfBlock>>} */
const block = parser.append({ const block = parser.append({
type: 'IfBlock', type: 'IfBlock',
elseif: false, elseif: false,
@ -76,7 +79,7 @@ function open(parser) {
const template = parser.template; const template = parser.template;
let end = parser.template.length; let end = parser.template.length;
/** @type {import('estree').Expression | undefined} */ /** @type {Expression | undefined} */
let expression; let expression;
// we have to do this loop because `{#each x as { y = z }}` fails to parse — // 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, { expression = walk(expression, null, {
// @ts-expect-error // @ts-expect-error
TSAsExpression(node, context) { TSAsExpression(node, context) {
if (node.end === /** @type {import('estree').Expression} */ (expression).end) { if (node.end === /** @type {Expression} */ (expression).end) {
assertion = node; assertion = node;
end = node.expression.end; end = node.expression.end;
return node.expression; return node.expression;
@ -171,7 +174,7 @@ function open(parser) {
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').EachBlock>>} */ /** @type {ReturnType<typeof parser.append<EachBlock>>} */
const block = parser.append({ const block = parser.append({
type: 'EachBlock', type: 'EachBlock',
start, start,
@ -195,7 +198,7 @@ function open(parser) {
const expression = read_expression(parser); const expression = read_expression(parser);
parser.allow_whitespace(); parser.allow_whitespace();
/** @type {ReturnType<typeof parser.append<import('#compiler').AwaitBlock>>} */ /** @type {ReturnType<typeof parser.append<AwaitBlock>>} */
const block = parser.append({ const block = parser.append({
type: 'AwaitBlock', type: 'AwaitBlock',
start, start,
@ -249,7 +252,7 @@ function open(parser) {
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').KeyBlock>>} */ /** @type {ReturnType<typeof parser.append<KeyBlock>>} */
const block = parser.append({ const block = parser.append({
type: 'KeyBlock', type: 'KeyBlock',
start, start,
@ -293,14 +296,14 @@ function open(parser) {
const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' '); const prelude = parser.template.slice(0, params_start).replace(/\S/g, ' ');
const params = parser.template.slice(params_start, parser.index); 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) parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start)
); );
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').SnippetBlock>>} */ /** @type {ReturnType<typeof parser.append<SnippetBlock>>} */
const block = parser.append({ const block = parser.append({
type: 'SnippetBlock', type: 'SnippetBlock',
start, start,
@ -323,7 +326,7 @@ function open(parser) {
e.expected_block_type(parser.index); e.expected_block_type(parser.index);
} }
/** @param {import('../index.js').Parser} parser */ /** @param {Parser} parser */
function next(parser) { function next(parser) {
const start = parser.index - 1; const start = parser.index - 1;
@ -352,7 +355,7 @@ function next(parser) {
let elseif_start = start - 1; let elseif_start = start - 1;
while (parser.template[elseif_start] !== '{') elseif_start -= 1; while (parser.template[elseif_start] !== '{') elseif_start -= 1;
/** @type {ReturnType<typeof parser.append<import('#compiler').IfBlock>>} */ /** @type {ReturnType<typeof parser.append<IfBlock>>} */
const child = parser.append({ const child = parser.append({
start: elseif_start, start: elseif_start,
end: -1, end: -1,
@ -434,7 +437,7 @@ function next(parser) {
e.block_invalid_continuation_placement(start); e.block_invalid_continuation_placement(start);
} }
/** @param {import('../index.js').Parser} parser */ /** @param {Parser} parser */
function close(parser) { function close(parser) {
const start = parser.index - 1; const start = parser.index - 1;
@ -448,7 +451,7 @@ function close(parser) {
while (block.elseif) { while (block.elseif) {
block.end = parser.index; block.end = parser.index;
parser.stack.pop(); parser.stack.pop();
block = /** @type {import('#compiler').IfBlock} */ (parser.current()); block = /** @type {IfBlock} */ (parser.current());
} }
block.end = parser.index; block.end = parser.index;
parser.pop(); parser.pop();
@ -482,7 +485,7 @@ function close(parser) {
parser.pop(); parser.pop();
} }
/** @param {import('../index.js').Parser} parser */ /** @param {Parser} parser */
function special(parser) { function special(parser) {
let start = parser.index; let start = parser.index;
while (parser.template[start] !== '{') start -= 1; while (parser.template[start] !== '{') start -= 1;
@ -496,7 +499,7 @@ function special(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').HtmlTag>>} */ /** @type {ReturnType<typeof parser.append<HtmlTag>>} */
parser.append({ parser.append({
type: 'HtmlTag', type: 'HtmlTag',
start, start,
@ -508,7 +511,7 @@ function special(parser) {
} }
if (parser.eat('debug')) { if (parser.eat('debug')) {
/** @type {import('estree').Identifier[]} */ /** @type {Identifier[]} */
let identifiers; let identifiers;
// Implies {@debug} which indicates "debug all" // Implies {@debug} which indicates "debug all"
@ -519,8 +522,8 @@ function special(parser) {
identifiers = identifiers =
expression.type === 'SequenceExpression' expression.type === 'SequenceExpression'
? /** @type {import('estree').Identifier[]} */ (expression.expressions) ? /** @type {Identifier[]} */ (expression.expressions)
: [/** @type {import('estree').Identifier} */ (expression)]; : [/** @type {Identifier} */ (expression)];
identifiers.forEach( identifiers.forEach(
/** @param {any} node */ (node) => { /** @param {any} node */ (node) => {
@ -534,7 +537,7 @@ function special(parser) {
parser.eat('}', true); parser.eat('}', true);
} }
/** @type {ReturnType<typeof parser.append<import('#compiler').DebugTag>>} */ /** @type {ReturnType<typeof parser.append<DebugTag>>} */
parser.append({ parser.append({
type: 'DebugTag', type: 'DebugTag',
start, start,
@ -567,7 +570,7 @@ function special(parser) {
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').ConstTag>>} */ /** @type {ReturnType<typeof parser.append<ConstTag>>} */
parser.append({ parser.append({
type: 'ConstTag', type: 'ConstTag',
start, start,
@ -598,7 +601,7 @@ function special(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
parser.eat('}', true); parser.eat('}', true);
/** @type {ReturnType<typeof parser.append<import('#compiler').RenderTag>>} */ /** @type {ReturnType<typeof parser.append<RenderTag>>} */
parser.append({ parser.append({
type: 'RenderTag', type: 'RenderTag',
start, start,

@ -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 is_reference from 'is-reference';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import * as e from '../../errors.js'; 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'; import { equal } from '../../utils/assert.js';
/** /**
* @param {import('#compiler').Script | null} script * @param {Script | null} script
* @param {ScopeRoot} root * @param {ScopeRoot} root
* @param {boolean} allow_reactive_declarations * @param {boolean} allow_reactive_declarations
* @param {Scope | null} parent * @param {Scope | null} parent
* @returns {import('../types.js').Js} * @returns {Js}
*/ */
function js(script, root, allow_reactive_declarations, parent) { function js(script, root, allow_reactive_declarations, parent) {
/** @type {import('estree').Program} */ /** @type {Program} */
const ast = script?.content ?? { const ast = script?.content ?? {
type: 'Program', type: 'Program',
sourceType: 'module', 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 * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so
* @param {string} event_name * @param {string} event_name
* @param {import('estree').Expression | null} handler * @param {Expression | null} handler
* @param {import('./types').Context} context * @param {Context} context
* @returns {null | import('#compiler').DelegatedEvent} * @returns {null | DelegatedEvent}
*/ */
function get_delegated_event(event_name, handler, context) { function get_delegated_event(event_name, handler, context) {
// Handle delegated event handlers. Bail-out if not a delegated event. // 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; return null;
} }
/** @type {import('#compiler').DelegatedEvent} */ /** @type {DelegatedEvent} */
const non_hoistable = { type: 'non-hoistable' }; 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 target_function = null;
let binding = null; let binding = null;
@ -119,20 +123,20 @@ function get_delegated_event(event_name, handler, context) {
const grandparent = path.at(-2); const grandparent = path.at(-2);
/** @type {import('#compiler').RegularElement | null} */ /** @type {RegularElement | null} */
let element = null; let element = null;
/** @type {string | null} */ /** @type {string | null} */
let event_name = null; let event_name = null;
if (parent.type === 'OnDirective') { if (parent.type === 'OnDirective') {
element = /** @type {import('#compiler').RegularElement} */ (grandparent); element = /** @type {RegularElement} */ (grandparent);
event_name = parent.name; event_name = parent.name;
} else if ( } else if (
parent.type === 'ExpressionTag' && parent.type === 'ExpressionTag' &&
grandparent?.type === 'Attribute' && grandparent?.type === 'Attribute' &&
is_event_attribute(grandparent) is_event_attribute(grandparent)
) { ) {
element = /** @type {import('#compiler').RegularElement} */ (path.at(-3)); element = /** @type {RegularElement} */ (path.at(-3));
const attribute = /** @type {import('#compiler').Attribute} */ (grandparent); const attribute = /** @type {Attribute} */ (grandparent);
event_name = get_attribute_event_name(attribute.name); 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 {Program} ast
* @param {import('#compiler').ValidatedModuleCompileOptions} options * @param {ValidatedModuleCompileOptions} options
* @returns {import('../types.js').Analysis} * @returns {Analysis}
*/ */
export function analyze_module(ast, options) { export function analyze_module(ast, options) {
const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null); const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null);
@ -239,7 +243,7 @@ export function analyze_module(ast, options) {
} }
walk( walk(
/** @type {import('estree').Node} */ (ast), /** @type {Node} */ (ast),
{ scope, analysis: { runes: true } }, { scope, analysis: { runes: true } },
// @ts-expect-error TODO clean this mess up // @ts-expect-error TODO clean this mess up
merge(set_scope(scopes), validation_runes_js, runes_scope_js_tweaker) 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 {string} source
* @param {import('#compiler').ValidatedCompileOptions} options * @param {ValidatedCompileOptions} options
* @returns {import('../types.js').ComponentAnalysis} * @returns {ComponentAnalysis}
*/ */
export function analyze_component(root, source, options) { export function analyze_component(root, source, options) {
const scope_root = new ScopeRoot(); 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); 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 }; const template = { ast: root.fragment, scope, scopes };
// create synthetic bindings for store subscriptions // 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.start) > /** @type {number} */ (module.ast.start) &&
/** @type {number} */ (node.end) < /** @type {number} */ (module.ast.end) && /** @type {number} */ (node.end) < /** @type {number} */ (module.ast.end) &&
// const state = $state(0) is valid // 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); 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))); 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 // 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 = { const analysis = {
name: module.scope.generate(options.name ?? component_name), name: module.scope.generate(options.name ?? component_name),
root: scope_root, root: scope_root,
@ -434,7 +438,7 @@ export function analyze_component(root, source, options) {
} }
for (const { ast, scope, scopes } of [module, instance, template]) { for (const { ast, scope, scopes } of [module, instance, template]) {
/** @type {import('./types').AnalysisState} */ /** @type {AnalysisState} */
const state = { const state = {
scope, scope,
analysis, analysis,
@ -450,7 +454,7 @@ export function analyze_component(root, source, options) {
}; };
walk( walk(
/** @type {import('#compiler').SvelteNode} */ (ast), /** @type {SvelteNode} */ (ast),
state, state,
merge(set_scope(scopes), validation_runes, runes_scope_tweaker, common_visitors) 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 // bind:this doesn't need to be a state reference if it will never change
if ( if (
type === 'BindDirective' && 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) { for (let j = i - 1; j >= 0; j -= 1) {
const type = path[j].type; 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'); instance.scope.declare(b.id('$$restProps'), 'rest_prop', 'synthetic');
for (const { ast, scope, scopes } of [module, instance, template]) { for (const { ast, scope, scopes } of [module, instance, template]) {
/** @type {import('./types').LegacyAnalysisState} */ /** @type {LegacyAnalysisState} */
const state = { const state = {
scope, scope,
analysis, analysis,
@ -522,7 +526,7 @@ export function analyze_component(root, source, options) {
}; };
walk( walk(
/** @type {import('#compiler').SvelteNode} */ (ast), /** @type {SvelteNode} */ (ast),
state, state,
// @ts-expect-error TODO // @ts-expect-error TODO
merge(set_scope(scopes), validation_legacy, legacy_scope_tweaker, common_visitors) 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 // TODO this happens during the analysis phase, which shouldn't know anything about client vs server
if (element.type === 'SvelteElement' && options.generate === 'client') continue; if (element.type === 'SvelteElement' && options.generate === 'client') continue;
/** @type {import('#compiler').Attribute | undefined} */ /** @type {Attribute | undefined} */
let class_attribute = undefined; let class_attribute = undefined;
for (const attribute of element.attributes) { for (const attribute of element.attributes) {
@ -602,7 +606,7 @@ export function analyze_component(root, source, options) {
if (is_text_attribute(class_attribute)) { if (is_text_attribute(class_attribute)) {
class_attribute.value[0].data += ` ${analysis.css.hash}`; class_attribute.value[0].data += ` ${analysis.css.hash}`;
} else { } else {
/** @type {import('#compiler').Text} */ /** @type {Text} */
const css_text = { const css_text = {
type: 'Text', type: 'Text',
data: ` ${analysis.css.hash}`, data: ` ${analysis.css.hash}`,
@ -642,19 +646,19 @@ export function analyze_component(root, source, options) {
return analysis; return analysis;
} }
/** @type {import('./types').Visitors<import('./types').LegacyAnalysisState>} */ /** @type {Visitors<LegacyAnalysisState>} */
const legacy_scope_tweaker = { const legacy_scope_tweaker = {
LabeledStatement(node, { next, path, state }) { LabeledStatement(node, { next, path, state }) {
if ( if (
state.ast_type !== 'instance' || state.ast_type !== 'instance' ||
node.label.name !== '$' || node.label.name !== '$' ||
/** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program' /** @type {SvelteNode} */ (path.at(-1)).type !== 'Program'
) { ) {
return next(); return next();
} }
// Find all dependencies of this `$: {...}` statement // Find all dependencies of this `$: {...}` statement
/** @type {import('../types.js').ReactiveStatement} */ /** @type {ReactiveStatement} */
const reactive_statement = { const reactive_statement = {
assignments: new Set(), assignments: new Set(),
dependencies: [] dependencies: []
@ -669,14 +673,14 @@ const legacy_scope_tweaker = {
if (binding === null) continue; if (binding === null) continue;
for (const { node, path } of nodes) { for (const { node, path } of nodes) {
/** @type {import('estree').Expression} */ /** @type {Expression} */
let left = node; let left = node;
let i = path.length - 1; 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') { while (parent.type === 'MemberExpression') {
left = parent; left = parent;
parent = /** @type {import('estree').Expression} */ (path.at(--i)); parent = /** @type {Expression} */ (path.at(--i));
} }
if ( if (
@ -757,7 +761,7 @@ const legacy_scope_tweaker = {
next(); next();
}, },
Identifier(node, { state, path }) { 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 (is_reference(node, parent)) {
if (node.name === '$$props') { if (node.name === '$$props') {
state.analysis.uses_props = true; state.analysis.uses_props = true;
@ -834,9 +838,7 @@ const legacy_scope_tweaker = {
if (!node.declaration) { if (!node.declaration) {
for (const specifier of node.specifiers) { for (const specifier of node.specifiers) {
const binding = /** @type {import('#compiler').Binding} */ ( const binding = /** @type {Binding} */ (state.scope.get(specifier.local.name));
state.scope.get(specifier.local.name)
);
if ( if (
binding !== null && binding !== null &&
(binding.kind === 'state' || (binding.kind === 'state' ||
@ -863,7 +865,7 @@ const legacy_scope_tweaker = {
node.declaration.type === 'ClassDeclaration' node.declaration.type === 'ClassDeclaration'
) { ) {
state.analysis.exports.push({ state.analysis.exports.push({
name: /** @type {import('estree').Identifier} */ (node.declaration.id).name, name: /** @type {Identifier} */ (node.declaration.id).name,
alias: null alias: null
}); });
return next(); return next();
@ -881,7 +883,7 @@ const legacy_scope_tweaker = {
for (const declarator of node.declaration.declarations) { for (const declarator of node.declaration.declarations) {
for (const id of extract_identifiers(declarator.id)) { 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'; binding.kind = 'bindable_prop';
} }
} }
@ -899,7 +901,7 @@ const legacy_scope_tweaker = {
} }
}; };
/** @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, { scope: Scope, analysis: { runes: true } }>} */ /** @type {Visitors} */
const runes_scope_js_tweaker = { const runes_scope_js_tweaker = {
VariableDeclarator(node, { state }) { VariableDeclarator(node, { state }) {
if (node.init?.type !== 'CallExpression') return; if (node.init?.type !== 'CallExpression') return;
@ -919,14 +921,14 @@ const runes_scope_js_tweaker = {
for (const path of extract_paths(node.id)) { for (const path of extract_paths(node.id)) {
// @ts-ignore this fails in CI for some insane reason // @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 = binding.kind =
rune === '$state' ? 'state' : rune === '$state.frozen' ? 'frozen_state' : 'derived'; rune === '$state' ? 'state' : rune === '$state.frozen' ? 'frozen_state' : 'derived';
} }
} }
}; };
/** @type {import('./types').Visitors} */ /** @type {Visitors} */
const runes_scope_tweaker = { const runes_scope_tweaker = {
CallExpression(node, { state, next }) { CallExpression(node, { state, next }) {
const rune = get_rune(node, state.scope); const rune = get_rune(node, state.scope);
@ -956,7 +958,7 @@ const runes_scope_tweaker = {
for (const path of extract_paths(node.id)) { for (const path of extract_paths(node.id)) {
// @ts-ignore this fails in CI for some insane reason // @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 = binding.kind =
rune === '$state' rune === '$state'
? 'state' ? 'state'
@ -973,7 +975,7 @@ const runes_scope_tweaker = {
state.analysis.needs_props = true; state.analysis.needs_props = true;
if (node.id.type === 'Identifier') { 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.initial = null; // else would be $props()
binding.kind = 'rest_prop'; binding.kind = 'rest_prop';
} else { } else {
@ -984,15 +986,15 @@ const runes_scope_tweaker = {
const name = const name =
property.value.type === 'AssignmentPattern' property.value.type === 'AssignmentPattern'
? /** @type {import('estree').Identifier} */ (property.value.left).name ? /** @type {Identifier} */ (property.value.left).name
: /** @type {import('estree').Identifier} */ (property.value).name; : /** @type {Identifier} */ (property.value).name;
const alias = const alias =
property.key.type === 'Identifier' property.key.type === 'Identifier'
? property.key.name ? 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; 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; binding.prop_alias = alias;
// rewire initial from $props() to the actual initial value, stripping $bindable() if necessary // 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.type === 'Identifier' &&
initial.callee.name === '$bindable' initial.callee.name === '$bindable'
) { ) {
binding.initial = /** @type {import('estree').Expression | null} */ ( binding.initial = /** @type {Expression | null} */ (initial.arguments[0] ?? null);
initial.arguments[0] ?? null
);
binding.kind = 'bindable_prop'; binding.kind = 'bindable_prop';
} else { } else {
binding.initial = initial; binding.initial = initial;
@ -1033,7 +1033,7 @@ const runes_scope_tweaker = {
node.declaration.type === 'ClassDeclaration' node.declaration.type === 'ClassDeclaration'
) { ) {
state.analysis.exports.push({ state.analysis.exports.push({
name: /** @type {import('estree').Identifier} */ (node.declaration.id).name, name: /** @type {Identifier} */ (node.declaration.id).name,
alias: null alias: null
}); });
return next(); return next();
@ -1050,8 +1050,8 @@ const runes_scope_tweaker = {
}; };
/** /**
* @param {import('estree').CallExpression} node * @param {CallExpression} node
* @param {import('./types').Context} context * @param {Context} context
* @returns {boolean} * @returns {boolean}
*/ */
function is_known_safe_call(node, context) { 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 {ArrowFunctionExpression | FunctionExpression | FunctionDeclaration} node
* @param {import('./types').Context} context * @param {Context} context
*/ */
const function_visitor = (node, context) => { const function_visitor = (node, context) => {
// TODO retire this in favour of a more general solution based on bindings // 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 * A 'safe' identifier means that the `foo` in `foo.bar` or `foo()` will not
* call functions that require component context to exist * call functions that require component context to exist
* @param {import('estree').Expression | import('estree').Super} expression * @param {Expression | Super} expression
* @param {Scope} scope * @param {Scope} scope
*/ */
function is_safe_identifier(expression, 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 = { const common_visitors = {
_(node, { state, next, path }) { _(node, { state, next, path }) {
ignore_map.set(node, structuredClone(ignore_stack)); ignore_map.set(node, structuredClone(ignore_stack));
@ -1243,7 +1243,7 @@ const common_visitors = {
context.next({ ...context.state, expression: node }); context.next({ ...context.state, expression: node });
}, },
Identifier(node, context) { 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 (!is_reference(node, parent)) return;
if (node.name === '$$slots') { 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 // 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 // not currently possibly because of our visitor merging, which I desperately want to nuke
const is_export_specifier = const is_export_specifier =
/** @type {import('#compiler').SvelteNode} */ (context.path.at(-1)).type === /** @type {SvelteNode} */ (context.path.at(-1)).type === 'ExportSpecifier';
'ExportSpecifier';
if ( if (
context.state.analysis.runes && context.state.analysis.runes &&
@ -1472,8 +1471,8 @@ const common_visitors = {
node.attributes.push( node.attributes.push(
create_attribute( create_attribute(
'value', 'value',
/** @type {import('#compiler').Text} */ (node.fragment.nodes.at(0)).start, /** @type {Text} */ (node.fragment.nodes.at(0)).start,
/** @type {import('#compiler').Text} */ (node.fragment.nodes.at(-1)).end, /** @type {Text} */ (node.fragment.nodes.at(-1)).end,
// @ts-ignore // @ts-ignore
node.fragment.nodes node.fragment.nodes
) )
@ -1552,7 +1551,7 @@ const common_visitors = {
}; };
/** /**
* @param {import('#compiler').RegularElement} node * @param {RegularElement} node
*/ */
function determine_element_spread(node) { function determine_element_spread(node) {
let has_spread = false; let has_spread = false;
@ -1578,10 +1577,10 @@ function get_attribute_event_name(event_name) {
} }
/** /**
* @param {Map<import('estree').LabeledStatement, import('../types.js').ReactiveStatement>} unsorted_reactive_declarations * @param {Map<LabeledStatement, ReactiveStatement>} unsorted_reactive_declarations
*/ */
function order_reactive_statements(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<string, Array<Tuple>>} */ /** @type {Map<string, Array<Tuple>>} */
const lookup = new 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 // We use a map and take advantage of the fact that the spec says insertion order is preserved when iterating
/** @type {Map<import('estree').LabeledStatement, import('../types.js').ReactiveStatement>} */ /** @type {Map<LabeledStatement, ReactiveStatement>} */
const reactive_declarations = new Map(); const reactive_declarations = new Map();
/** /**
* *
* @param {import('estree').LabeledStatement} node * @param {LabeledStatement} node
* @param {import('../types.js').ReactiveStatement} declaration * @param {ReactiveStatement} declaration
* @returns * @returns
*/ */
const add_declaration = (node, declaration) => { const add_declaration = (node, declaration) => {

@ -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 is_reference from 'is-reference';
import { import {
disallowed_paragraph_contents, disallowed_paragraph_contents,
@ -35,8 +39,8 @@ import { merge } from '../visitors.js';
import { a11y_validators } from './a11y.js'; import { a11y_validators } from './a11y.js';
/** /**
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {import('#compiler').ElementLike} parent * @param {ElementLike} parent
*/ */
function validate_attribute(attribute, parent) { function validate_attribute(attribute, parent) {
if ( if (
@ -63,8 +67,8 @@ function validate_attribute(attribute, parent) {
} }
/** /**
* @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node * @param {Component | SvelteComponent | SvelteSelf} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context * @param {Context} context
*/ */
function validate_component(node, context) { function validate_component(node, context) {
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
@ -123,16 +127,16 @@ const react_attributes = new Map([
]); ]);
/** /**
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node * @param {RegularElement | SvelteElement} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context * @param {Context} context
*/ */
function validate_element(node, context) { function validate_element(node, context) {
let has_animate_directive = false; let has_animate_directive = false;
/** @type {import('#compiler').TransitionDirective | null} */ /** @type {TransitionDirective | null} */
let in_transition = null; let in_transition = null;
/** @type {import('#compiler').TransitionDirective | null} */ /** @type {TransitionDirective | null} */
let out_transition = null; let out_transition = null;
for (const attribute of node.attributes) { for (const attribute of node.attributes) {
@ -175,7 +179,7 @@ function validate_element(node, context) {
} }
if (attribute.name === 'slot') { 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); validate_slot_attribute(context, attribute);
} }
@ -212,7 +216,7 @@ function validate_element(node, context) {
has_animate_directive = true; has_animate_directive = true;
} }
} else if (attribute.type === 'TransitionDirective') { } 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) (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) { function validate_attribute_name(attribute) {
if ( if (
@ -269,8 +273,8 @@ function validate_attribute_name(attribute) {
} }
/** /**
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context * @param {Context} context
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {boolean} is_component * @param {boolean} is_component
*/ */
function validate_slot_attribute(context, attribute, is_component = false) { 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 {Fragment | null | undefined} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context * @param {Context} context
*/ */
function validate_block_not_empty(node, context) { function validate_block_not_empty(node, context) {
if (!node) return; if (!node) return;
@ -356,7 +360,7 @@ function validate_block_not_empty(node, context) {
} }
/** /**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} * @type {Visitors}
*/ */
const validation = { const validation = {
MemberExpression(node, context) { MemberExpression(node, context) {
@ -465,7 +469,7 @@ const validation = {
} }
if (parent.name === 'input' && node.name !== 'this') { 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') parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'type')
); );
if (type && !is_text_attribute(type)) { if (type && !is_text_attribute(type)) {
@ -506,7 +510,7 @@ const validation = {
} }
if (ContentEditableBindings.includes(node.name)) { 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') parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable')
); );
if (!contenteditable) { if (!contenteditable) {
@ -636,11 +640,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 ( if (
context.state.analysis.source[node.end - 2] === '/' && context.state.analysis.source[node.end - 2] === '/' &&
context.state.options.namespace !== 'foreign' && context.state.options.namespace !== 'foreign' &&
!VoidElements.includes(node.name) && !VoidElements.includes(node_name) &&
!SVGElements.includes(node.name) !SVGElements.includes(node_name)
) { ) {
w.element_invalid_self_closing_tag(node, node.name); w.element_invalid_self_closing_tag(node, node.name);
} }
@ -843,8 +850,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
LabeledStatement(node, { path, state }) { LabeledStatement(node, { path, state }) {
if ( if (
node.label.name === '$' && node.label.name === '$' &&
(state.ast_type !== 'instance' || (state.ast_type !== 'instance' || /** @type {SvelteNode} */ (path.at(-1)).type !== 'Program')
/** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program')
) { ) {
w.reactive_declaration_invalid_placement(node); w.reactive_declaration_invalid_placement(node);
} }
@ -856,8 +862,8 @@ export const validation_legacy = merge(validation, a11y_validators, {
/** /**
* *
* @param {import('estree').Node} node * @param {Node} node
* @param {import('../scope').Scope} scope * @param {Scope} scope
* @param {string} name * @param {string} name
*/ */
function validate_export(node, scope, name) { function validate_export(node, scope, name) {
@ -874,16 +880,16 @@ function validate_export(node, scope, name) {
} }
/** /**
* @param {import('estree').CallExpression} node * @param {CallExpression} node
* @param {Scope} scope * @param {Scope} scope
* @param {import('#compiler').SvelteNode[]} path * @param {SvelteNode[]} path
* @returns * @returns
*/ */
function validate_call_expression(node, scope, path) { function validate_call_expression(node, scope, path) {
const rune = get_rune(node, scope); const rune = get_rune(node, scope);
if (rune === null) return; 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 (rune === '$props') {
if (parent.type === 'VariableDeclarator') return; if (parent.type === 'VariableDeclarator') return;
@ -962,8 +968,8 @@ function validate_call_expression(node, scope, path) {
} }
/** /**
* @param {import('estree').VariableDeclarator} node * @param {VariableDeclarator} node
* @param {import('./types.js').AnalysisState} state * @param {AnalysisState} state
*/ */
function ensure_no_module_import_conflict(node, state) { function ensure_no_module_import_conflict(node, state) {
const ids = extract_identifiers(node.id); const ids = extract_identifiers(node.id);
@ -978,7 +984,7 @@ function ensure_no_module_import_conflict(node, state) {
} }
/** /**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} * @type {Visitors}
*/ */
export const validation_runes_js = { export const validation_runes_js = {
ImportDeclaration(node) { ImportDeclaration(node) {
@ -1013,7 +1019,7 @@ export const validation_runes_js = {
if (rune === null) return; 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) { if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {
e.rune_invalid_arguments_length(node, rune, 'exactly one argument'); e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
@ -1070,7 +1076,7 @@ export const validation_runes_js = {
}, },
Identifier(node, { path, state }) { Identifier(node, { path, state }) {
let i = path.length; let i = path.length;
let parent = /** @type {import('estree').Expression} */ (path[--i]); let parent = /** @type {Expression} */ (path[--i]);
if ( if (
Runes.includes(/** @type {Runes[number]} */ (node.name)) && Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
@ -1078,16 +1084,16 @@ export const validation_runes_js = {
state.scope.get(node.name) === null && state.scope.get(node.name) === null &&
state.scope.get(node.name.slice(1)) === null state.scope.get(node.name.slice(1)) === null
) { ) {
/** @type {import('estree').Expression} */ /** @type {Expression} */
let current = node; let current = node;
let name = node.name; let name = node.name;
while (parent.type === 'MemberExpression') { while (parent.type === 'MemberExpression') {
if (parent.computed) e.rune_invalid_computed_property(parent); 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; current = parent;
parent = /** @type {import('estree').Expression} */ (path[--i]); parent = /** @type {Expression} */ (path[--i]);
if (!Runes.includes(/** @type {Runes[number]} */ (name))) { if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
if (name === '$effect.active') { if (name === '$effect.active') {
@ -1106,8 +1112,8 @@ export const validation_runes_js = {
}; };
/** /**
* @param {import('../../errors.js').NodeLike} node * @param {NodeLike} node
* @param {import('estree').Pattern | import('estree').Expression} argument * @param {Pattern | Expression} argument
* @param {Scope} scope * @param {Scope} scope
* @param {boolean} is_binding * @param {boolean} is_binding
*/ */
@ -1153,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. * 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. * 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 {{start: number; end: number}} node
* @param {import('./types.js').AnalysisState} state * @param {AnalysisState} state
* @param {string} expected * @param {string} expected
*/ */
function validate_opening_tag(node, state, expected) { function validate_opening_tag(node, state, expected) {
@ -1164,9 +1170,9 @@ function validate_opening_tag(node, state, expected) {
} }
/** /**
* @param {import('estree').AssignmentExpression | import('estree').UpdateExpression} node * @param {AssignmentExpression | UpdateExpression} node
* @param {import('estree').Pattern | import('estree').Expression} argument * @param {Pattern | Expression} argument
* @param {import('./types.js').AnalysisState} state * @param {AnalysisState} state
*/ */
function validate_assignment(node, argument, state) { function validate_assignment(node, argument, state) {
validate_no_const_assignment(node, argument, state.scope, false); validate_no_const_assignment(node, argument, state.scope, false);
@ -1189,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; let property = null;
while (object.type === 'MemberExpression') { while (object.type === 'MemberExpression') {
@ -1319,7 +1325,7 @@ export const validation_runes = merge(validation, a11y_validators, {
if (rune === null) return; 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 // TODO some of this is duplicated with above, seems off
if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) { if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {

@ -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 * as b from '../../../utils/builders.js';
import { import {
extract_identifiers, extract_identifiers,
@ -14,21 +18,21 @@ import {
} from '../../../../constants.js'; } from '../../../../constants.js';
/** /**
* @template {import('./types').ClientTransformState} State * @template {ClientTransformState} State
* @param {import('estree').AssignmentExpression} node * @param {AssignmentExpression} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, State>} context * @param {import('zimmerframe').Context<SvelteNode, State>} context
* @returns * @returns
*/ */
export function get_assignment_value(node, { state, visit }) { export function get_assignment_value(node, { state, visit }) {
if (node.left.type === 'Identifier') { if (node.left.type === 'Identifier') {
const operator = node.operator; const operator = node.operator;
return operator === '=' return operator === '='
? /** @type {import('estree').Expression} */ (visit(node.right)) ? /** @type {Expression} */ (visit(node.right))
: // turn something like x += 1 into x = x + 1 : // turn something like x += 1 into x = x + 1
b.binary( b.binary(
/** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {BinaryOperator} */ (operator.slice(0, -1)),
serialize_get_binding(node.left, state), serialize_get_binding(node.left, state),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {Expression} */ (visit(node.right))
); );
} else if ( } else if (
node.left.type === 'MemberExpression' && node.left.type === 'MemberExpression' &&
@ -38,21 +42,21 @@ export function get_assignment_value(node, { state, visit }) {
) { ) {
const operator = node.operator; const operator = node.operator;
return operator === '=' return operator === '='
? /** @type {import('estree').Expression} */ (visit(node.right)) ? /** @type {Expression} */ (visit(node.right))
: // turn something like x += 1 into x = x + 1 : // turn something like x += 1 into x = x + 1
b.binary( b.binary(
/** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {BinaryOperator} */ (operator.slice(0, -1)),
/** @type {import('estree').Expression} */ (visit(node.left)), /** @type {Expression} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {Expression} */ (visit(node.right))
); );
} else { } else {
return /** @type {import('estree').Expression} */ (visit(node.right)); return /** @type {Expression} */ (visit(node.right));
} }
} }
/** /**
* @param {import('#compiler').Binding} binding * @param {Binding} binding
* @param {import('./types').ClientTransformState} state * @param {ClientTransformState} state
* @returns {boolean} * @returns {boolean}
*/ */
export function is_state_source(binding, state) { export function is_state_source(binding, state) {
@ -63,9 +67,9 @@ export function is_state_source(binding, state) {
} }
/** /**
* @param {import('estree').Identifier} node * @param {Identifier} node
* @param {import('./types').ClientTransformState} state * @param {ClientTransformState} state
* @returns {import('estree').Expression} * @returns {Expression}
*/ */
export function serialize_get_binding(node, state) { export function serialize_get_binding(node, state) {
const binding = state.scope.get(node.name); const binding = state.scope.get(node.name);
@ -117,13 +121,13 @@ export function serialize_get_binding(node, state) {
} }
/** /**
* @template {import('./types').ClientTransformState} State * @template {ClientTransformState} State
* @param {import('estree').AssignmentExpression} node * @param {AssignmentExpression} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, State>} context * @param {import('zimmerframe').Context<SvelteNode, State>} context
* @param {() => any} fallback * @param {() => any} fallback
* @param {boolean | null} [prefix] - If the assignment is a transformed update expression, set this. Else `null` * @param {boolean | null} [prefix] - If the assignment is a transformed update expression, set this. Else `null`
* @param {{skip_proxy_and_freeze?: boolean}} [options] * @param {{skip_proxy_and_freeze?: boolean}} [options]
* @returns {import('estree').Expression} * @returns {Expression}
*/ */
export function serialize_set_binding(node, context, fallback, prefix, options) { export function serialize_set_binding(node, context, fallback, prefix, options) {
const { state, visit } = context; 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 // Turn assignment into an IIFE, so that `$.set` calls etc don't produce invalid code
const tmp_id = context.state.scope.generate('tmp'); const tmp_id = context.state.scope.generate('tmp');
/** @type {import('estree').AssignmentExpression[]} */ /** @type {AssignmentExpression[]} */
const original_assignments = []; const original_assignments = [];
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const assignments = []; const assignments = [];
const paths = extract_paths(assignee); const paths = extract_paths(assignee);
@ -159,7 +163,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
return fallback(); return fallback();
} }
const rhs_expression = /** @type {import('estree').Expression} */ (visit(node.right)); const rhs_expression = /** @type {Expression} */ (visit(node.right));
const iife_is_async = const iife_is_async =
is_expression_async(rhs_expression) || is_expression_async(rhs_expression) ||
@ -271,8 +275,8 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
'$$_import_' + binding.node.name, '$$_import_' + binding.node.name,
b.assignment( b.assignment(
node.operator, node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)), /** @type {Pattern} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @type {Expression} */ (visit(node.right))
) )
); );
} }
@ -299,10 +303,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
if (left === node.left) { if (left === node.left) {
const is_initial_proxy = const is_initial_proxy =
binding.initial !== null && binding.initial !== null &&
should_proxy_or_freeze( should_proxy_or_freeze(/**@type {Expression}*/ (binding.initial), context.state.scope);
/**@type {import("estree").Expression}*/ (binding.initial),
context.state.scope
);
if ((binding.kind === 'prop' || binding.kind === 'bindable_prop') && !is_initial_proxy) { if ((binding.kind === 'prop' || binding.kind === 'bindable_prop') && !is_initial_proxy) {
return b.call(left, value); return b.call(left, value);
} else if (is_store) { } 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. // keep consistency with how store $ shorthand reads work in Svelte 4.
/** /**
* *
* @param {import("estree").Expression | import("estree").Pattern} node * @param {Expression | Pattern} node
* @returns {import("estree").Expression} * @returns {Expression}
*/ */
function visit_node(node) { function visit_node(node) {
if (node.type === 'MemberExpression') { if (node.type === 'MemberExpression') {
return { return {
...node, ...node,
object: visit_node(/** @type {import("estree").Expression} */ (node.object)), object: visit_node(/** @type {Expression} */ (node.object)),
property: /** @type {import("estree").MemberExpression} */ (visit(node)).property property: /** @type {MemberExpression} */ (visit(node)).property
}; };
} }
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
const binding = state.scope.get(node.name); const binding = state.scope.get(node.name);
if (binding !== null && binding.kind === 'store_sub') { if (binding !== null && binding.kind === 'store_sub') {
return b.call( return b.call('$.untrack', b.thunk(/** @type {Expression} */ (visit(node))));
'$.untrack',
b.thunk(/** @type {import('estree').Expression} */ (visit(node)))
);
} }
} }
return /** @type {import("estree").Expression} */ (visit(node)); return /** @type {Expression} */ (visit(node));
} }
return b.call( return b.call(
'$.mutate_store', '$.mutate_store',
serialize_get_binding(b.id(left_name), state), serialize_get_binding(b.id(left_name), state),
b.assignment( b.assignment(node.operator, /** @type {Pattern}} */ (visit_node(node.left)), value),
node.operator,
/** @type {import("estree").Pattern}} */ (visit_node(node.left)),
value
),
b.call('$.untrack', b.id('$' + left_name)) b.call('$.untrack', b.id('$' + left_name))
); );
} else if ( } else if (
@ -401,22 +395,14 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
if (binding.kind === 'bindable_prop') { if (binding.kind === 'bindable_prop') {
return b.call( return b.call(
left, left,
b.assignment( b.assignment(node.operator, /** @type {Pattern} */ (visit(node.left)), value),
node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)),
value
),
b.true b.true
); );
} else { } else {
return b.call( return b.call(
'$.mutate', '$.mutate',
b.id(left_name), b.id(left_name),
b.assignment( b.assignment(node.operator, /** @type {Pattern} */ (visit(node.left)), value)
node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)),
value
)
); );
} }
} else if ( } else if (
@ -426,14 +412,14 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
) { ) {
return b.update( return b.update(
node.operator === '+=' ? '++' : '--', node.operator === '+=' ? '++' : '--',
/** @type {import('estree').Expression} */ (visit(node.left)), /** @type {Expression} */ (visit(node.left)),
prefix prefix
); );
} else { } else {
return b.assignment( return b.assignment(
node.operator, node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)), /** @type {Pattern} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)) /** @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 {Expression} value
* @param {import('estree').PrivateIdentifier | string} proxy_reference * @param {PrivateIdentifier | string} proxy_reference
* @param {import('./types').ClientTransformState} state * @param {ClientTransformState} state
*/ */
export function serialize_proxy_reassignment(value, proxy_reference, state) { export function serialize_proxy_reassignment(value, proxy_reference, state) {
return state.options.dev 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 {ArrowFunctionExpression | FunctionExpression} node
* @param {import('./types').ComponentContext} context * @param {ComponentContext} context
*/ */
export const function_visitor = (node, context) => { export const function_visitor = (node, context) => {
const metadata = node.metadata; const metadata = node.metadata;
@ -474,7 +460,7 @@ export const function_visitor = (node, context) => {
let state = context.state; let state = context.state;
if (node.type === 'FunctionExpression') { 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'; const in_constructor = parent.type === 'MethodDefinition' && parent.kind === 'constructor';
state = { ...context.state, in_constructor }; state = { ...context.state, in_constructor };
@ -485,7 +471,7 @@ export const function_visitor = (node, context) => {
if (metadata?.hoistable === true) { if (metadata?.hoistable === true) {
const params = serialize_hoistable_params(node, context); const params = serialize_hoistable_params(node, context);
return /** @type {import('estree').FunctionExpression} */ ({ return /** @type {FunctionExpression} */ ({
...node, ...node,
params, params,
body: context.visit(node.body, state) 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 {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
* @param {import('./types').ComponentContext} context * @param {ComponentContext} context
* @returns {import('estree').Pattern[]} * @returns {Pattern[]}
*/ */
function get_hoistable_params(node, context) { function get_hoistable_params(node, context) {
const scope = context.state.scope; const scope = context.state.scope;
/** @type {import('estree').Identifier[]} */ /** @type {Identifier[]} */
const params = []; const params = [];
/** /**
* We only want to push if it's not already present to avoid name clashing * 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) { function push_unique(id) {
if (!params.find((param) => param.name === id.name)) { if (!params.find((param) => param.name === id.name)) {
@ -523,9 +509,7 @@ function get_hoistable_params(node, context) {
if (binding.kind === 'store_sub') { if (binding.kind === 'store_sub') {
// We need both the subscription for getting the value and the store for updating // We need both the subscription for getting the value and the store for updating
push_unique(b.id(binding.node.name)); push_unique(b.id(binding.node.name));
binding = /** @type {import('#compiler').Binding} */ ( binding = /** @type {Binding} */ (scope.get(binding.node.name.slice(1)));
scope.get(binding.node.name.slice(1))
);
} }
const expression = context.state.getters[reference]; 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 {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node
* @param {import('./types').ComponentContext} context * @param {ComponentContext} context
* @returns {import('estree').Pattern[]} * @returns {Pattern[]}
*/ */
export function serialize_hoistable_params(node, context) { export function serialize_hoistable_params(node, context) {
const hoistable_params = get_hoistable_params(node, context); const hoistable_params = get_hoistable_params(node, context);
node.metadata.hoistable_params = hoistable_params; node.metadata.hoistable_params = hoistable_params;
/** @type {import('estree').Pattern[]} */ /** @type {Pattern[]} */
const params = []; const params = [];
if (node.params.length === 0) { if (node.params.length === 0) {
@ -584,7 +568,7 @@ export function serialize_hoistable_params(node, context) {
} }
} else { } else {
for (const param of node.params) { 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 {Binding} binding
* @param {import('./types').ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @param {string} name * @param {string} name
* @param {import('estree').Expression | null} [initial] * @param {Expression | null} [initial]
* @returns * @returns
*/ */
export function get_prop_source(binding, state, name, initial) { export function get_prop_source(binding, state, name, initial) {
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const args = [b.id('$$props'), b.literal(name)]; const args = [b.id('$$props'), b.literal(name)];
let flags = 0; let flags = 0;
@ -622,7 +606,7 @@ export function get_prop_source(binding, state, name, initial) {
flags |= PROPS_IS_UPDATED; flags |= PROPS_IS_UPDATED;
} }
/** @type {import('estree').Expression | undefined} */ /** @type {Expression | undefined} */
let arg; let arg;
if (initial) { if (initial) {
@ -654,8 +638,8 @@ export function get_prop_source(binding, state, name, initial) {
/** /**
* *
* @param {import('#compiler').Binding} binding * @param {Binding} binding
* @param {import('./types').ClientTransformState} state * @param {ClientTransformState} state
* @returns * @returns
*/ */
export function is_prop_source(binding, state) { export function is_prop_source(binding, state) {
@ -672,8 +656,8 @@ export function is_prop_source(binding, state) {
} }
/** /**
* @param {import('estree').Expression} node * @param {Expression} node
* @param {import("../../scope.js").Scope | null} scope * @param {Scope | null} scope
*/ */
export function should_proxy_or_freeze(node, scope) { export function should_proxy_or_freeze(node, scope) {
if ( 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. * 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). * but keep the target as-is (i.e. a new id is created).
* This ensures esrap can generate accurate source maps. * This ensures esrap can generate accurate source maps.
* @param {import('estree').Identifier} target * @param {Identifier} target
* @param {import('estree').Identifier} source * @param {Identifier} source
*/ */
export function with_loc(target, source) { export function with_loc(target, source) {
if (source.loc) { if (source.loc) {
@ -721,16 +705,16 @@ export function with_loc(target, source) {
} }
/** /**
* @param {import("estree").Pattern} node * @param {Pattern} node
* @param {import("zimmerframe").Context<import("#compiler").SvelteNode, import("./types").ComponentClientTransformState>} context * @param {import('zimmerframe').Context<SvelteNode, ComponentClientTransformState>} context
* @returns {{ id: import("estree").Pattern, declarations: null | import("estree").Statement[] }} * @returns {{ id: Pattern, declarations: null | Statement[] }}
*/ */
export function create_derived_block_argument(node, context) { export function create_derived_block_argument(node, context) {
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
return { id: node, declarations: null }; 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 identifiers = extract_identifiers(node);
const id = b.id('$$source'); 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 * Svelte legacy mode should use safe equals in most places, runes mode shouldn't
* @param {import('./types.js').ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @param {import('estree').Expression} arg * @param {Expression} arg
*/ */
export function create_derived(state, arg) { export function create_derived(state, arg) {
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg); return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg);

@ -1,11 +1,13 @@
/** @import { Expression, Node, Pattern, Statement } from 'estree' */
/** @import { Visitors } from '../types' */
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import { serialize_get_binding, serialize_set_binding } from '../utils.js'; import { serialize_get_binding, serialize_set_binding } from '../utils.js';
import * as b from '../../../../utils/builders.js'; import * as b from '../../../../utils/builders.js';
/** @type {import('../types').Visitors} */ /** @type {Visitors} */
export const global_visitors = { export const global_visitors = {
Identifier(node, { path, state }) { 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') { if (node.name === '$$props') {
return b.id('$$sanitized_props'); return b.id('$$sanitized_props');
} }
@ -74,7 +76,7 @@ export const global_visitors = {
binding?.kind === 'bindable_prop' || binding?.kind === 'bindable_prop' ||
is_store is_store
) { ) {
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const args = []; const args = [];
let fn = '$.update'; let fn = '$.update';
@ -105,7 +107,7 @@ export const global_visitors = {
let fn = '$.update'; let fn = '$.update';
if (node.prefix) fn += '_pre'; if (node.prefix) fn += '_pre';
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const args = [argument]; const args = [argument];
if (node.operator === '--') { if (node.operator === '--') {
args.push(b.literal(-1)); 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; }) // turn it into an IIFEE assignment expression: i++ -> (() => { const $$value = i; i+=1; return $$value; })
const assignment = b.assignment( const assignment = b.assignment(
node.operator === '++' ? '+=' : '-=', node.operator === '++' ? '+=' : '-=',
/** @type {import('estree').Pattern} */ (argument), /** @type {Pattern} */ (argument),
b.literal(1) b.literal(1)
); );
const serialized_assignment = serialize_set_binding( const serialized_assignment = serialize_set_binding(
@ -125,14 +127,14 @@ export const global_visitors = {
() => assignment, () => assignment,
node.prefix node.prefix
); );
const value = /** @type {import('estree').Expression} */ (visit(argument)); const value = /** @type {Expression} */ (visit(argument));
if (serialized_assignment === assignment) { if (serialized_assignment === assignment) {
// No change to output -> nothing to transform -> we can keep the original update expression // No change to output -> nothing to transform -> we can keep the original update expression
return next(); return next();
} else if (context.state.analysis.runes) { } else if (context.state.analysis.runes) {
return serialized_assignment; return serialized_assignment;
} else { } else {
/** @type {import('estree').Statement[]} */ /** @type {Statement[]} */
let statements; let statements;
if (node.prefix) { if (node.prefix) {
statements = [b.stmt(serialized_assignment), b.return(value)]; statements = [b.stmt(serialized_assignment), b.return(value)];

@ -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 { get_rune } from '../../../scope.js';
import { is_hoistable_function, transform_inspect_rune } from '../../utils.js'; import { is_hoistable_function, transform_inspect_rune } from '../../utils.js';
import * as b from '../../../../utils/builders.js'; import * as b from '../../../../utils/builders.js';
@ -12,13 +15,13 @@ import {
import { extract_paths } from '../../../../utils/ast.js'; import { extract_paths } from '../../../../utils/ast.js';
import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js';
/** @type {import('../types.js').ComponentVisitors} */ /** @type {ComponentVisitors} */
export const javascript_visitors_runes = { export const javascript_visitors_runes = {
ClassBody(node, { state, visit }) { ClassBody(node, { state, visit }) {
/** @type {Map<string, import('../types.js').StateField>} */ /** @type {Map<string, StateField>} */
const public_state = new Map(); const public_state = new Map();
/** @type {Map<string, import('../types.js').StateField>} */ /** @type {Map<string, StateField>} */
const private_state = new Map(); const private_state = new Map();
/** @type {string[]} */ /** @type {string[]} */
@ -46,7 +49,7 @@ export const javascript_visitors_runes = {
rune === '$derived' || rune === '$derived' ||
rune === '$derived.by' rune === '$derived.by'
) { ) {
/** @type {import('../types.js').StateField} */ /** @type {StateField} */
const field = { const field = {
kind: kind:
rune === '$state' rune === '$state'
@ -81,7 +84,7 @@ export const javascript_visitors_runes = {
field.id = b.private_id(deconflicted); field.id = b.private_id(deconflicted);
} }
/** @type {Array<import('estree').MethodDefinition | import('estree').PropertyDefinition>} */ /** @type {Array<MethodDefinition | PropertyDefinition>} */
const body = []; const body = [];
const child_state = { ...state, public_state, private_state }; const child_state = { ...state, public_state, private_state };
@ -104,7 +107,7 @@ export const javascript_visitors_runes = {
let value = null; let value = null;
if (definition.value.arguments.length > 0) { if (definition.value.arguments.length > 0) {
const init = /** @type {import('estree').Expression} **/ ( const init = /** @type {Expression} **/ (
visit(definition.value.arguments[0], child_state) 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) { if (state.options.dev && public_state.size > 0) {
@ -225,15 +228,11 @@ export const javascript_visitors_runes = {
if (init != null && is_hoistable_function(init)) { if (init != null && is_hoistable_function(init)) {
const hoistable_function = visit(init); const hoistable_function = visit(init);
state.hoisted.push( state.hoisted.push(
b.declaration( b.declaration('const', declarator.id, /** @type {Expression} */ (hoistable_function))
'const',
declarator.id,
/** @type {import('estree').Expression} */ (hoistable_function)
)
); );
continue; continue;
} }
declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); declarations.push(/** @type {VariableDeclarator} */ (visit(declarator)));
continue; continue;
} }
@ -246,7 +245,7 @@ export const javascript_visitors_runes = {
} }
if (declarator.id.type === 'Identifier') { if (declarator.id.type === 'Identifier') {
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))];
if (state.options.dev) { if (state.options.dev) {
@ -260,9 +259,7 @@ export const javascript_visitors_runes = {
for (const property of declarator.id.properties) { for (const property of declarator.id.properties) {
if (property.type === 'Property') { if (property.type === 'Property') {
const key = /** @type {import('estree').Identifier | import('estree').Literal} */ ( const key = /** @type {Identifier | Literal} */ (property.key);
property.key
);
const name = key.type === 'Identifier' ? key.name : /** @type {string} */ (key.value); const name = key.type === 'Identifier' ? key.name : /** @type {string} */ (key.value);
seen.push(name); seen.push(name);
@ -270,10 +267,8 @@ export const javascript_visitors_runes = {
let id = let id =
property.value.type === 'AssignmentPattern' ? property.value.left : property.value; property.value.type === 'AssignmentPattern' ? property.value.left : property.value;
assert.equal(id.type, 'Identifier'); assert.equal(id.type, 'Identifier');
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); const binding = /** @type {Binding} */ (state.scope.get(id.name));
let initial = let initial = binding.initial && /** @type {Expression} */ (visit(binding.initial));
binding.initial &&
/** @type {import('estree').Expression} */ (visit(binding.initial));
// We're adding proxy here on demand and not within the prop runtime function so that // 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 // people not using proxied state anywhere in their code don't have to pay the additional bundle size cost
if ( if (
@ -289,14 +284,12 @@ export const javascript_visitors_runes = {
} }
} else { } else {
// RestElement // RestElement
/** @type {import('estree').Expression[]} */ /** @type {Expression[]} */
const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))];
if (state.options.dev) { if (state.options.dev) {
// include rest name, so we can provide informative error messages // include rest name, so we can provide informative error messages
args.push( args.push(b.literal(/** @type {Identifier} */ (property.argument).name));
b.literal(/** @type {import('estree').Identifier} */ (property.argument).name)
);
} }
declarations.push(b.declarator(property.argument, b.call('$.rest_props', ...args))); declarations.push(b.declarator(property.argument, b.call('$.rest_props', ...args)));
@ -308,19 +301,17 @@ export const javascript_visitors_runes = {
continue; continue;
} }
const args = /** @type {import('estree').CallExpression} */ (init).arguments; const args = /** @type {CallExpression} */ (init).arguments;
const value = const value =
args.length === 0 args.length === 0 ? b.id('undefined') : /** @type {Expression} */ (visit(args[0]));
? b.id('undefined')
: /** @type {import('estree').Expression} */ (visit(args[0]));
if (rune === '$state' || rune === '$state.frozen') { if (rune === '$state' || rune === '$state.frozen') {
/** /**
* @param {import('estree').Identifier} id * @param {Identifier} id
* @param {import('estree').Expression} value * @param {Expression} value
*/ */
const create_state_declarator = (id, 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)) { if (should_proxy_or_freeze(value, state.scope)) {
value = b.call(rune === '$state' ? '$.proxy' : '$.freeze', value); value = b.call(rune === '$state' ? '$.proxy' : '$.freeze', value);
} }
@ -341,9 +332,7 @@ export const javascript_visitors_runes = {
b.declarator(b.id(tmp), value), b.declarator(b.id(tmp), value),
...paths.map((path) => { ...paths.map((path) => {
const value = path.expression?.(b.id(tmp)); const value = path.expression?.(b.id(tmp));
const binding = state.scope.get( const binding = state.scope.get(/** @type {Identifier} */ (path.node).name);
/** @type {import('estree').Identifier} */ (path.node).name
);
return b.declarator( return b.declarator(
path.node, path.node,
binding?.kind === 'state' || binding?.kind === 'frozen_state' binding?.kind === 'state' || binding?.kind === 'frozen_state'
@ -426,7 +415,7 @@ export const javascript_visitors_runes = {
const func = context.visit(node.expression.arguments[0]); const func = context.visit(node.expression.arguments[0]);
return { return {
...node, ...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]); const func = context.visit(node.expression.arguments[0]);
return { return {
...node, ...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') { if (rune === '$state.snapshot') {
return b.call( return b.call('$.snapshot', /** @type {Expression} */ (context.visit(node.arguments[0])));
'$.snapshot',
/** @type {import('estree').Expression} */ (context.visit(node.arguments[0]))
);
} }
if (rune === '$state.is') { if (rune === '$state.is') {
return b.call( return b.call(
'$.is', '$.is',
/** @type {import('estree').Expression} */ (context.visit(node.arguments[0])), /** @type {Expression} */ (context.visit(node.arguments[0])),
/** @type {import('estree').Expression} */ (context.visit(node.arguments[1])) /** @type {Expression} */ (context.visit(node.arguments[1]))
); );
} }
if (rune === '$effect.root') { if (rune === '$effect.root') {
const args = /** @type {import('estree').Expression[]} */ ( const args = /** @type {Expression[]} */ (node.arguments.map((arg) => context.visit(arg)));
node.arguments.map((arg) => context.visit(arg))
);
return b.call('$.effect_root', ...args); return b.call('$.effect_root', ...args);
} }
@ -494,8 +478,8 @@ export const javascript_visitors_runes = {
if (operator === '===' || operator === '!==') { if (operator === '===' || operator === '!==') {
return b.call( return b.call(
'$.strict_equals', '$.strict_equals',
/** @type {import('estree').Expression} */ (visit(node.left)), /** @type {Expression} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)), /** @type {Expression} */ (visit(node.right)),
operator === '!==' && b.literal(false) operator === '!==' && b.literal(false)
); );
} }
@ -503,8 +487,8 @@ export const javascript_visitors_runes = {
if (operator === '==' || operator === '!=') { if (operator === '==' || operator === '!=') {
return b.call( return b.call(
'$.equals', '$.equals',
/** @type {import('estree').Expression} */ (visit(node.left)), /** @type {Expression} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right)), /** @type {Expression} */ (visit(node.right)),
operator === '!=' && b.literal(false) 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) { function get_name(node) {
if (node.type === 'Literal') { if (node.type === 'Literal') {

@ -1,5 +1,8 @@
/** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */ /** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */
/** @import { BindDirective, RegularElement } 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 { import {
extract_identifiers, extract_identifiers,
extract_paths, extract_paths,
@ -54,9 +57,9 @@ import { locator } from '../../../../state.js';
import is_reference from 'is-reference'; import is_reference from 'is-reference';
/** /**
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} element * @param {RegularElement | SvelteElement} element
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {{ state: { metadata: { namespace: import('#compiler').Namespace }}}} context * @param {{ state: { metadata: { namespace: Namespace }}}} context
*/ */
function get_attribute_name(element, attribute, context) { function get_attribute_name(element, attribute, context) {
let name = attribute.name; 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)` * 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. * 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 {Identifier} element_id
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @param {boolean} is_attributes_reactive * @param {boolean} is_attributes_reactive
*/ */
function serialize_style_directives(style_directives, element_id, context, 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)` * 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. * 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 {Identifier} element_id
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @param {boolean} is_attributes_reactive * @param {boolean} is_attributes_reactive
*/ */
function serialize_class_directives(class_directives, element_id, context, 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 {Binding[]} references
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
*/ */
function serialize_transitive_dependencies(references, context) { function serialize_transitive_dependencies(references, context) {
/** @type {Set<import('#compiler').Binding>} */ /** @type {Set<Binding>} */
const dependencies = new Set(); const dependencies = new Set();
for (const ref of references) { for (const ref of references) {
@ -179,9 +182,9 @@ function serialize_transitive_dependencies(references, context) {
} }
/** /**
* @param {import('#compiler').Binding} binding * @param {Binding} binding
* @param {Set<import('#compiler').Binding>} seen * @param {Set<Binding>} seen
* @returns {import('#compiler').Binding[]} * @returns {Binding[]}
*/ */
function collect_transitive_dependencies(binding, seen = new Set()) { function collect_transitive_dependencies(binding, seen = new Set()) {
if (binding.kind !== 'legacy_reactive') return []; 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 * 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 * between the value binding and inner signals, for indirect updates
* @param {import('#compiler').BindDirective} value_binding * @param {BindDirective} value_binding
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
*/ */
function setup_select_synchronization(value_binding, context) { function setup_select_synchronization(value_binding, context) {
if (context.state.analysis.runes) return; if (context.state.analysis.runes) return;
@ -253,9 +256,9 @@ function setup_select_synchronization(value_binding, context) {
} }
/** /**
* @param {Array<import('#compiler').Attribute | import('#compiler').SpreadAttribute>} attributes * @param {Array<Attribute | SpreadAttribute>} attributes
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @param {import('#compiler').RegularElement} element * @param {RegularElement} element
* @param {Identifier} element_id * @param {Identifier} element_id
* @param {boolean} needs_select_handling * @param {boolean} needs_select_handling
*/ */
@ -358,8 +361,8 @@ function serialize_element_spread_attributes(
/** /**
* Serializes dynamic element attribute assignments. * Serializes dynamic element attribute assignments.
* Returns the `true` if spread is deemed reactive. * Returns the `true` if spread is deemed reactive.
* @param {Array<import('#compiler').Attribute | import('#compiler').SpreadAttribute>} attributes * @param {Array<Attribute | SpreadAttribute>} attributes
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @param {Identifier} element_id * @param {Identifier} element_id
* @returns {boolean} * @returns {boolean}
*/ */
@ -472,10 +475,10 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) {
* }); * });
* ``` * ```
* Returns true if attribute is deemed reactive, false otherwise. * Returns true if attribute is deemed reactive, false otherwise.
* @param {import('#compiler').RegularElement} element * @param {RegularElement} element
* @param {Identifier} node_id * @param {Identifier} node_id
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @returns {boolean} * @returns {boolean}
*/ */
function serialize_element_attribute_update_assignment(element, node_id, attribute, context) { 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. * Like `serialize_element_attribute_update_assignment` but without any special attribute treatment.
* @param {Identifier} node_id * @param {Identifier} node_id
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @returns {boolean} * @returns {boolean}
*/ */
function serialize_custom_element_attribute_update_assignment(node_id, attribute, context) { 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. * Returns true if attribute is deemed reactive, false otherwise.
* @param {string} element * @param {string} element
* @param {Identifier} node_id * @param {Identifier} node_id
* @param {import('#compiler').Attribute} attribute * @param {Attribute} attribute
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @returns {boolean} * @returns {boolean}
*/ */
function serialize_element_special_value_attribute(element, node_id, attribute, context) { 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 {string} id
* @param {Expression | undefined} init * @param {Expression | undefined} init
* @param {Expression} value * @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) { function collect_parent_each_blocks(context) {
return /** @type {import('#compiler').EachBlock[]} */ ( return /** @type {EachBlock[]} */ (context.path.filter((node) => node.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 {string} component_name
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
* @param {Expression} anchor * @param {Expression} anchor
* @returns {Statement} * @returns {Statement}
*/ */
@ -668,7 +669,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont
/** @type {ExpressionStatement[]} */ /** @type {ExpressionStatement[]} */
const lets = []; const lets = [];
/** @type {Record<string, import('#compiler').TemplateNode[]>} */ /** @type {Record<string, TemplateNode[]>} */
const children = {}; const children = {};
/** @type {Record<string, Expression[]>} */ /** @type {Record<string, Expression[]>} */
@ -681,7 +682,7 @@ function serialize_inline_component(node, component_name, context, anchor = cont
let bind_this = null; let bind_this = null;
/** /**
* @type {import("estree").ExpressionStatement[]} * @type {ExpressionStatement[]}
*/ */
const binding_initializers = []; const binding_initializers = [];
@ -844,14 +845,14 @@ function serialize_inline_component(node, component_name, context, anchor = cont
let slot_name = 'default'; let slot_name = 'default';
if (is_element_node(child)) { if (is_element_node(child)) {
const attribute = /** @type {import('#compiler').Attribute | undefined} */ ( const attribute = /** @type {Attribute | undefined} */ (
child.attributes.find( child.attributes.find(
(attribute) => attribute.type === 'Attribute' && attribute.name === 'slot' (attribute) => attribute.type === 'Attribute' && attribute.name === 'slot'
) )
); );
if (attribute !== undefined) { 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. * Serializes `bind:this` for components and elements.
* @param {Identifier | MemberExpression} expression * @param {Identifier | MemberExpression} expression
* @param {Expression} value * @param {Expression} value
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('../types.js').ComponentClientTransformState>} context * @param {import('zimmerframe').Context<SvelteNode, ComponentClientTransformState>} context
*/ */
function serialize_bind_this(expression, value, { state, visit }) { function serialize_bind_this(expression, value, { state, visit }) {
/** @type {Identifier[]} */ /** @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) { function serialize_locations(locations) {
return b.array( return b.array(
@ -1077,8 +1078,8 @@ function serialize_locations(locations) {
/** /**
* *
* @param {import('#compiler').Namespace} namespace * @param {Namespace} namespace
* @param {import('../types.js').ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @returns * @returns
*/ */
function get_template_function(namespace, state) { function get_template_function(namespace, state) {
@ -1116,9 +1117,9 @@ function serialize_render_stmt(update) {
/** /**
* Serializes the event handler function of the `on:` directive * Serializes the event handler function of the `on:` directive
* @param {Pick<import('#compiler').OnDirective, 'name' | 'modifiers' | 'expression'>} node * @param {Pick<OnDirective, 'name' | 'modifiers' | 'expression'>} node
* @param {null | { contains_call_expression: boolean; dynamic: boolean; } | null} metadata * @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 }) { function serialize_event_handler(node, metadata, { state, visit }) {
/** @type {Expression} */ /** @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` * 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 {null | { contains_call_expression: boolean; dynamic: boolean; }} metadata
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
*/ */
function serialize_event(node, metadata, context) { function serialize_event(node, metadata, context) {
const state = context.state; const state = context.state;
@ -1312,7 +1313,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));
const has_action_directive = const has_action_directive =
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective'); parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
const statement = b.stmt( const statement = b.stmt(
@ -1332,8 +1333,8 @@ function serialize_event(node, metadata, context) {
} }
/** /**
* @param {import('#compiler').Attribute & { value: import('#compiler').ExpressionTag | [import('#compiler').ExpressionTag] }} node * @param {Attribute & { value: ExpressionTag | [ExpressionTag] }} node
* @param {import('../types').ComponentContext} context * @param {ComponentContext} context
*/ */
function serialize_event_attribute(node, context) { function serialize_event_attribute(node, context) {
/** @type {string[]} */ /** @type {string[]} */
@ -1361,15 +1362,15 @@ function serialize_event_attribute(node, context) {
* Processes an array of template nodes, joining sibling text/expression nodes * 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 * (e.g. `{a} b {c}`) into a single update function. Along the way it creates
* corresponding template node references these updates are applied to. * corresponding template node references these updates are applied to.
* @param {import('#compiler').SvelteNode[]} nodes * @param {SvelteNode[]} nodes
* @param {(is_text: boolean) => Expression} expression * @param {(is_text: boolean) => Expression} expression
* @param {boolean} is_element * @param {boolean} is_element
* @param {import('../types.js').ComponentContext} context * @param {ComponentContext} context
*/ */
function process_children(nodes, expression, is_element, { visit, state }) { function process_children(nodes, expression, is_element, { visit, state }) {
const within_bound_contenteditable = state.metadata.bound_contenteditable; const within_bound_contenteditable = state.metadata.bound_contenteditable;
/** @typedef {Array<import('#compiler').Text | import('#compiler').ExpressionTag>} Sequence */ /** @typedef {Array<Text | ExpressionTag>} Sequence */
/** @type {Sequence} */ /** @type {Sequence} */
let sequence = []; let sequence = [];
@ -1495,7 +1496,7 @@ function process_children(nodes, expression, is_element, { visit, state }) {
/** /**
* @param {Expression} expression * @param {Expression} expression
* @param {import('../types.js').ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @param {string} name * @param {string} name
*/ */
function get_node_id(expression, state, name) { function get_node_id(expression, state, name) {
@ -1510,8 +1511,8 @@ function get_node_id(expression, state, name) {
} }
/** /**
* @param {import('#compiler').Attribute['value']} value * @param {Attribute['value']} value
* @param {import('../types').ComponentContext} context * @param {ComponentContext} context
* @returns {[contains_call_expression: boolean, Expression]} * @returns {[contains_call_expression: boolean, Expression]}
*/ */
function serialize_attribute_value(value, context) { function serialize_attribute_value(value, context) {
@ -1536,9 +1537,9 @@ function serialize_attribute_value(value, context) {
} }
/** /**
* @param {Array<import('#compiler').Text | import('#compiler').ExpressionTag>} values * @param {Array<Text | ExpressionTag>} values
* @param {(node: import('#compiler').SvelteNode, state: any) => any} visit * @param {(node: SvelteNode, state: any) => any} visit
* @param {import("../types.js").ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @returns {[boolean, TemplateLiteral]} * @returns {[boolean, TemplateLiteral]}
*/ */
function serialize_template_literal(values, visit, state) { function serialize_template_literal(values, visit, state) {
@ -1598,7 +1599,7 @@ function serialize_template_literal(values, visit, state) {
return [contains_call_expression, b.template(quasis, expressions)]; return [contains_call_expression, b.template(quasis, expressions)];
} }
/** @type {import('../types').ComponentVisitors} */ /** @type {ComponentVisitors} */
export const template_visitors = { export const template_visitors = {
Fragment(node, context) { Fragment(node, context) {
// Creates a new block which looks roughly like this: // Creates a new block which looks roughly like this:
@ -1644,7 +1645,7 @@ export const template_visitors = {
/** @type {Statement | undefined} */ /** @type {Statement | undefined} */
let close = undefined; let close = undefined;
/** @type {import('../types').ComponentClientTransformState} */ /** @type {ComponentClientTransformState} */
const state = { const state = {
...context.state, ...context.state,
before_init: [], before_init: [],
@ -1692,7 +1693,7 @@ export const template_visitors = {
}; };
if (is_single_element) { 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)); const id = b.id(context.state.scope.generate(element.name));
@ -1967,7 +1968,7 @@ export const template_visitors = {
state.after_update.push(b.stmt(b.call('$.transition', ...args))); state.after_update.push(b.stmt(b.call('$.transition', ...args)));
}, },
RegularElement(node, context) { RegularElement(node, context) {
/** @type {import('#shared').SourceLocation} */ /** @type {SourceLocation} */
let location = [-1, -1]; let location = [-1, -1];
if (context.state.options.dev) { if (context.state.options.dev) {
@ -1995,13 +1996,13 @@ export const template_visitors = {
context.state.template.push(`<${node.name}`); context.state.template.push(`<${node.name}`);
/** @type {Array<import('#compiler').Attribute | import('#compiler').SpreadAttribute>} */ /** @type {Array<Attribute | SpreadAttribute>} */
const attributes = []; const attributes = [];
/** @type {import('#compiler').ClassDirective[]} */ /** @type {ClassDirective[]} */
const class_directives = []; const class_directives = [];
/** @type {import('#compiler').StyleDirective[]} */ /** @type {StyleDirective[]} */
const style_directives = []; const style_directives = [];
/** @type {ExpressionStatement[]} */ /** @type {ExpressionStatement[]} */
@ -2011,7 +2012,7 @@ export const template_visitors = {
let needs_input_reset = false; let needs_input_reset = false;
let needs_content_reset = false; let needs_content_reset = false;
/** @type {import('#compiler').BindDirective | null} */ /** @type {BindDirective | null} */
let value_binding = null; let value_binding = null;
/** If true, needs `__value` for inputs */ /** If true, needs `__value` for inputs */
@ -2133,7 +2134,7 @@ export const template_visitors = {
); );
is_attributes_reactive = true; is_attributes_reactive = true;
} else { } else {
for (const attribute of /** @type {import('#compiler').Attribute[]} */ (attributes)) { for (const attribute of /** @type {Attribute[]} */ (attributes)) {
if (is_event_attribute(attribute)) { if (is_event_attribute(attribute)) {
if ( if (
(attribute.name === 'onload' || attribute.name === 'onerror') && (attribute.name === 'onload' || attribute.name === 'onerror') &&
@ -2195,17 +2196,15 @@ export const template_visitors = {
context.state.template.push('>'); context.state.template.push('>');
/** @type {import('#shared').SourceLocation[]} */ /** @type {SourceLocation[]} */
const child_locations = []; const child_locations = [];
/** @type {import('../types').ComponentClientTransformState} */ /** @type {ComponentClientTransformState} */
const state = { const state = {
...context.state, ...context.state,
metadata: child_metadata, metadata: child_metadata,
locations: child_locations, locations: child_locations,
scope: /** @type {import('../../../scope').Scope} */ ( scope: /** @type {Scope} */ (context.state.scopes.get(node.fragment)),
context.state.scopes.get(node.fragment)
),
preserve_whitespace: preserve_whitespace:
context.state.preserve_whitespace || context.state.preserve_whitespace ||
((node.name === 'pre' || node.name === 'textarea') && ((node.name === 'pre' || node.name === 'textarea') &&
@ -2288,16 +2287,16 @@ export const template_visitors = {
SvelteElement(node, context) { SvelteElement(node, context) {
context.state.template.push(`<!>`); context.state.template.push(`<!>`);
/** @type {Array<import('#compiler').Attribute | import('#compiler').SpreadAttribute>} */ /** @type {Array<Attribute | SpreadAttribute>} */
const attributes = []; const attributes = [];
/** @type {import('#compiler').Attribute['value'] | undefined} */ /** @type {Attribute['value'] | undefined} */
let dynamic_namespace = undefined; let dynamic_namespace = undefined;
/** @type {import('#compiler').ClassDirective[]} */ /** @type {ClassDirective[]} */
const class_directives = []; const class_directives = [];
/** @type {import('#compiler').StyleDirective[]} */ /** @type {StyleDirective[]} */
const style_directives = []; const style_directives = [];
/** @type {ExpressionStatement[]} */ /** @type {ExpressionStatement[]} */
@ -2307,7 +2306,7 @@ export const template_visitors = {
// They'll then be added to the function parameter of $.element // They'll then be added to the function parameter of $.element
const element_id = b.id(context.state.scope.generate('$$element')); const element_id = b.id(context.state.scope.generate('$$element'));
/** @type {import('../types').ComponentContext} */ /** @type {ComponentContext} */
const inner_context = { const inner_context = {
...context, ...context,
state: { state: {
@ -2495,7 +2494,7 @@ export const template_visitors = {
/** /**
* @param {Pattern} expression_for_id * @param {Pattern} expression_for_id
* @returns {import('#compiler').Binding['mutation']} * @returns {Binding['mutation']}
*/ */
const create_mutation = (expression_for_id) => { const create_mutation = (expression_for_id) => {
return (assignment, context) => { return (assignment, context) => {
@ -2540,8 +2539,8 @@ export const template_visitors = {
? each_node_meta.index ? each_node_meta.index
: b.id(node.index); : b.id(node.index);
const item = each_node_meta.item; const item = each_node_meta.item;
const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(item.name)); const binding = /** @type {Binding} */ (context.state.scope.get(item.name));
const getter = (/** @type {import("estree").Identifier} */ id) => { const getter = (/** @type {Identifier} */ id) => {
const item_with_loc = with_loc(item, id); const item_with_loc = with_loc(item, id);
return b.call('$.unwrap', item_with_loc); return b.call('$.unwrap', item_with_loc);
}; };
@ -2571,7 +2570,7 @@ export const template_visitors = {
for (const path of paths) { for (const path of paths) {
const name = /** @type {Identifier} */ (path.node).name; 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 needs_derived = path.has_default_value; // to ensure that default value is only called once
const fn = b.thunk( const fn = b.thunk(
/** @type {Expression} */ (context.visit(path.expression?.(unwrapped), child_state)) /** @type {Expression} */ (context.visit(path.expression?.(unwrapped), child_state))
@ -2793,7 +2792,7 @@ export const template_visitors = {
for (const path of paths) { for (const path of paths) {
const name = /** @type {Identifier} */ (path.node).name; 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 needs_derived = path.has_default_value; // to ensure that default value is only called once
const fn = b.thunk( const fn = b.thunk(
/** @type {Expression} */ ( /** @type {Expression} */ (
@ -3052,7 +3051,7 @@ export const template_visitors = {
const parent = path.at(-1); const parent = path.at(-1);
if (parent?.type === 'RegularElement') { if (parent?.type === 'RegularElement') {
const value = /** @type {any[]} */ ( const value = /** @type {any[]} */ (
/** @type {import('#compiler').Attribute} */ ( /** @type {Attribute} */ (
parent.attributes.find( parent.attributes.find(
(a) => (a) =>
a.type === 'Attribute' && a.type === 'Attribute' &&
@ -3274,7 +3273,7 @@ export const template_visitors = {
b.assignment( b.assignment(
'=', '=',
b.member(b.id('$.document'), b.id('title')), 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)
) )
) )
); );
@ -3313,7 +3312,7 @@ export const template_visitors = {
}; };
/** /**
* @param {import('../types.js').ComponentClientTransformState} state * @param {ComponentClientTransformState} state
* @param {BindDirective} binding * @param {BindDirective} binding
* @param {MemberExpression} expression * @param {MemberExpression} expression
*/ */

@ -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 { current_component_context, flush_sync, untrack } from './internal/client/runtime.js';
import { is_array } from './internal/shared/utils.js'; import { is_array } from './internal/shared/utils.js';
import { user_effect } from './internal/client/index.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 * https://svelte.dev/docs/svelte#onmount
* @template T * @template T
* @param {() => import('./internal/types').NotFunction<T> | Promise<import('./internal/types').NotFunction<T>> | (() => any)} fn * @param {() => NotFunction<T> | Promise<NotFunction<T>> | (() => any)} fn
* @returns {void} * @returns {void}
*/ */
export function onMount(fn) { export function onMount(fn) {
@ -84,7 +87,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false
* https://svelte.dev/docs/svelte#createeventdispatcher * 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 * @deprecated Use callback props and/or the `$host()` rune instead see https://svelte-5-preview.vercel.app/docs/deprecations#createeventdispatcher
* @template {Record<string, any>} [EventMap = any] * @template {Record<string, any>} [EventMap = any]
* @returns {import('./index.js').EventDispatcher<EventMap>} * @returns {EventDispatcher<EventMap>}
*/ */
export function createEventDispatcher() { export function createEventDispatcher() {
const component_context = current_component_context; const component_context = current_component_context;
@ -164,10 +167,10 @@ export function afterUpdate(fn) {
/** /**
* Legacy-mode: Init callbacks object for onMount/beforeUpdate/afterUpdate * Legacy-mode: Init callbacks object for onMount/beforeUpdate/afterUpdate
* @param {import('#client').ComponentContext} context * @param {ComponentContext} context
*/ */
function init_update_callbacks(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: [] }); return (l.u ??= { a: [], b: [], m: [] });
} }

@ -1,10 +1,11 @@
/** @import { Component } from '#server' */
import { current_component } from './internal/server/context.js'; import { current_component } from './internal/server/context.js';
import { noop } from './internal/shared/utils.js'; import { noop } from './internal/shared/utils.js';
import * as e from './internal/server/errors.js'; import * as e from './internal/server/errors.js';
/** @param {() => void} fn */ /** @param {() => void} fn */
export function onDestroy(fn) { export function onDestroy(fn) {
var context = /** @type {import('#server').Component} */ (current_component); var context = /** @type {Component} */ (current_component);
(context.d ??= []).push(fn); (context.d ??= []).push(fn);
} }

@ -1,4 +1,4 @@
/** @import { TemplateNode } from '#client' */ /** @import { EachItem, EachState, Effect, EffectNodes, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
import { import {
EACH_INDEX_REACTIVE, EACH_INDEX_REACTIVE,
EACH_IS_ANIMATED, 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 * The row of a keyed each block that is currently updating. We track this
* so that `animate:` directives have something to attach themselves to * so that `animate:` directives have something to attach themselves to
* @type {import('#client').EachItem | null} * @type {EachItem | null}
*/ */
export let current_each_item = null; export let current_each_item = null;
/** @param {import('#client').EachItem | null} item */ /** @param {EachItem | null} item */
export function set_current_each_item(item) { export function set_current_each_item(item) {
current_each_item = item; current_each_item = item;
} }
@ -56,13 +56,13 @@ export function index(_, i) {
/** /**
* Pause multiple effects simultaneously, and coordinate their * Pause multiple effects simultaneously, and coordinate their
* subsequent destruction. Used in each blocks * subsequent destruction. Used in each blocks
* @param {import('#client').EachState} state * @param {EachState} state
* @param {import('#client').EachItem[]} items * @param {EachItem[]} items
* @param {null | Node} controlled_anchor * @param {null | Node} controlled_anchor
* @param {Map<any, import("#client").EachItem>} items_map * @param {Map<any, EachItem>} items_map
*/ */
function pause_effects(state, items, controlled_anchor, items_map) { function pause_effects(state, items, controlled_anchor, items_map) {
/** @type {import('#client').TransitionManager[]} */ /** @type {TransitionManager[]} */
var transitions = []; var transitions = [];
var length = items.length; var length = items.length;
@ -101,14 +101,14 @@ function pause_effects(state, items, controlled_anchor, items_map) {
* @param {number} flags * @param {number} flags
* @param {() => V[]} get_collection * @param {() => V[]} get_collection
* @param {(value: V, index: number) => any} get_key * @param {(value: V, index: number) => any} get_key
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn * @param {(anchor: Node, item: MaybeSource<V>, index: MaybeSource<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn * @param {null | ((anchor: Node) => void)} fallback_fn
* @returns {void} * @returns {void}
*/ */
export function each(node, flags, get_collection, get_key, render_fn, fallback_fn = null) { export function each(node, flags, get_collection, get_key, render_fn, fallback_fn = null) {
var anchor = node; var anchor = node;
/** @type {import('#client').EachState} */ /** @type {EachState} */
var state = { flags, items: new Map(), first: null }; var state = { flags, items: new Map(), first: null };
var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0; 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(); hydrate_next();
} }
/** @type {import('#client').Effect | null} */ /** @type {Effect | null} */
var fallback = null; var fallback = null;
block(() => { 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 // this is separate to the previous block because `hydrating` might change
if (hydrating) { if (hydrating) {
/** @type {import('#client').EachItem | null} */ /** @type {EachItem | null} */
var prev = null; var prev = null;
/** @type {import('#client').EachItem} */ /** @type {EachItem} */
var item; var item;
for (var i = 0; i < length; i++) { 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 * @template V
* @param {Array<V>} array * @param {Array<V>} array
* @param {import('#client').EachState} state * @param {EachState} state
* @param {Element | Comment | Text} anchor * @param {Element | Comment | Text} anchor
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: number | import('#client').Source<number>) => void} render_fn * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>) => void} render_fn
* @param {number} flags * @param {number} flags
* @param {(value: V, index: number) => any} get_key * @param {(value: V, index: number) => any} get_key
* @returns {void} * @returns {void}
@ -255,19 +255,19 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
var first = state.first; var first = state.first;
var current = first; var current = first;
/** @type {Set<import('#client').EachItem>} */ /** @type {Set<EachItem>} */
var seen = new Set(); var seen = new Set();
/** @type {import('#client').EachItem | null} */ /** @type {EachItem | null} */
var prev = null; var prev = null;
/** @type {Set<import('#client').EachItem>} */ /** @type {Set<EachItem>} */
var to_animate = new Set(); var to_animate = new Set();
/** @type {import('#client').EachItem[]} */ /** @type {EachItem[]} */
var matched = []; var matched = [];
/** @type {import('#client').EachItem[]} */ /** @type {EachItem[]} */
var stashed = []; var stashed = [];
/** @type {V} */ /** @type {V} */
@ -276,7 +276,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
/** @type {any} */ /** @type {any} */
var key; var key;
/** @type {import('#client').EachItem | undefined} */ /** @type {EachItem | undefined} */
var item; var item;
/** @type {number} */ /** @type {number} */
@ -301,9 +301,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
item = items.get(key); item = items.get(key);
if (item === undefined) { if (item === undefined) {
var child_anchor = current var child_anchor = current ? /** @type {EffectNodes} */ (current.e.nodes).start : anchor;
? /** @type {import('#client').EffectNodes} */ (current.e.nodes).start
: anchor;
prev = create_item( prev = create_item(
child_anchor, 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 {Effect} */ (current_effect).first = state.first && state.first.e;
/** @type {import('#client').Effect} */ (current_effect).last = prev && prev.e; /** @type {Effect} */ (current_effect).last = prev && prev.e;
} }
/** /**
* @param {import('#client').EachItem} item * @param {EachItem} item
* @param {any} value * @param {any} value
* @param {number} index * @param {number} index
* @param {number} type * @param {number} type
@ -453,7 +451,7 @@ function update_item(item, value, index, type) {
} }
if ((type & EACH_INDEX_REACTIVE) !== 0) { if ((type & EACH_INDEX_REACTIVE) !== 0) {
set(/** @type {import('#client').Value<number>} */ (item.i), index); set(/** @type {Value<number>} */ (item.i), index);
} else { } else {
item.i = index; item.i = index;
} }
@ -462,15 +460,15 @@ function update_item(item, value, index, type) {
/** /**
* @template V * @template V
* @param {Node} anchor * @param {Node} anchor
* @param {import('#client').EachState} state * @param {EachState} state
* @param {import('#client').EachItem | null} prev * @param {EachItem | null} prev
* @param {import('#client').EachItem | null} next * @param {EachItem | null} next
* @param {V} value * @param {V} value
* @param {unknown} key * @param {unknown} key
* @param {number} index * @param {number} index
* @param {(anchor: Node, item: V | import('#client').Source<V>, index: number | import('#client').Value<number>) => void} render_fn * @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>) => void} render_fn
* @param {number} flags * @param {number} flags
* @returns {import('#client').EachItem} * @returns {EachItem}
*/ */
function create_item(anchor, state, prev, next, value, key, index, render_fn, flags) { function create_item(anchor, state, prev, next, value, key, index, render_fn, flags) {
var previous_each_item = current_each_item; 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 v = reactive ? (mutable ? mutable_source(value) : source(value)) : value;
var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index); var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
/** @type {import('#client').EachItem} */ /** @type {EachItem} */
var item = { var item = {
i, i,
v, v,
@ -519,29 +517,27 @@ function create_item(anchor, state, prev, next, value, key, index, render_fn, fl
} }
/** /**
* @param {import('#client').EachItem} item * @param {EachItem} item
* @param {import('#client').EachItem | null} next * @param {EachItem | null} next
* @param {Text | Element | Comment} anchor * @param {Text | Element | Comment} anchor
*/ */
function move(item, next, anchor) { function move(item, next, anchor) {
var end = item.next var end = item.next ? /** @type {EffectNodes} */ (item.next.e.nodes).start : anchor;
? /** @type {import('#client').EffectNodes} */ (item.next.e.nodes).start
: anchor;
var dest = next ? /** @type {import('#client').EffectNodes} */ (next.e.nodes).start : anchor; var dest = next ? /** @type {EffectNodes} */ (next.e.nodes).start : anchor;
var node = /** @type {import('#client').EffectNodes} */ (item.e.nodes).start; var node = /** @type {EffectNodes} */ (item.e.nodes).start;
while (node !== end) { while (node !== end) {
var next_node = /** @type {import('#client').TemplateNode} */ (node.nextSibling); var next_node = /** @type {TemplateNode} */ (node.nextSibling);
dest.before(node); dest.before(node);
node = next_node; node = next_node;
} }
} }
/** /**
* @param {import('#client').EachState} state * @param {EachState} state
* @param {import('#client').EachItem | null} prev * @param {EachItem | null} prev
* @param {import('#client').EachItem | null} next * @param {EachItem | null} next
*/ */
function link(state, prev, next) { function link(state, prev, next) {
if (prev === null) { if (prev === null) {

@ -1,4 +1,4 @@
/** @import { TemplateNode } from '#client' */ /** @import { Effect, TemplateNode } from '#client' */
import { EFFECT_TRANSPARENT } from '../../constants.js'; import { EFFECT_TRANSPARENT } from '../../constants.js';
import { import {
hydrate_next, hydrate_next,
@ -14,8 +14,8 @@ import { HYDRATION_START_ELSE } from '../../../../constants.js';
/** /**
* @param {TemplateNode} node * @param {TemplateNode} node
* @param {() => boolean} get_condition * @param {() => boolean} get_condition
* @param {(anchor: Node) => import('#client').Dom} consequent_fn * @param {(anchor: Node) => void} consequent_fn
* @param {null | ((anchor: Node) => import('#client').Dom)} [alternate_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' * @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} * @returns {void}
*/ */
@ -26,10 +26,10 @@ export function if_block(node, get_condition, consequent_fn, alternate_fn = null
var anchor = node; var anchor = node;
/** @type {import('#client').Effect | null} */ /** @type {Effect | null} */
var consequent_effect = null; var consequent_effect = null;
/** @type {import('#client').Effect | null} */ /** @type {Effect | null} */
var alternate_effect = null; var alternate_effect = null;
/** @type {boolean | null} */ /** @type {boolean | null} */

@ -1,3 +1,4 @@
/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, Task, TransitionFn, TransitionManager } from '#client' */
import { noop, is_function } from '../../../shared/utils.js'; import { noop, is_function } from '../../../shared/utils.js';
import { effect } from '../../reactivity/effects.js'; import { effect } from '../../reactivity/effects.js';
import { current_effect, untrack } from '../../runtime.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. * and attaches it to the block, so that moves can be animated following reconciliation.
* @template P * @template P
* @param {Element} element * @param {Element} element
* @param {() => import('#client').AnimateFn<P | undefined>} get_fn * @param {() => AnimateFn<P | undefined>} get_fn
* @param {(() => P) | null} get_params * @param {(() => P) | null} get_params
*/ */
export function animation(element, get_fn, 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} */ /** @type {DOMRect} */
var from; var from;
@ -72,7 +73,7 @@ export function animation(element, get_fn, get_params) {
/** @type {DOMRect} */ /** @type {DOMRect} */
var to; var to;
/** @type {import('#client').Animation | undefined} */ /** @type {Animation | undefined} */
var animation; var animation;
/** @type {null | { position: string, width: string, height: string, transform: string }} */ /** @type {null | { position: string, width: string, height: string, transform: string }} */
@ -167,7 +168,7 @@ export function animation(element, get_fn, get_params) {
* @template P * @template P
* @param {number} flags * @param {number} flags
* @param {HTMLElement} element * @param {HTMLElement} element
* @param {() => import('#client').TransitionFn<P | undefined>} get_fn * @param {() => TransitionFn<P | undefined>} get_fn
* @param {(() => P) | null} get_params * @param {(() => P) | null} get_params
* @returns {void} * @returns {void}
*/ */
@ -180,15 +181,15 @@ export function transition(flags, element, get_fn, get_params) {
/** @type {'in' | 'out' | 'both'} */ /** @type {'in' | 'out' | 'both'} */
var direction = is_both ? 'both' : is_intro ? 'in' : 'out'; 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 current_options;
var inert = element.inert; var inert = element.inert;
/** @type {import('#client').Animation | undefined} */ /** @type {Animation | undefined} */
var intro; var intro;
/** @type {import('#client').Animation | undefined} */ /** @type {Animation | undefined} */
var outro; var outro;
/** @type {(() => void) | undefined} */ /** @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 })); return (current_options ??= get_fn()(element, get_params?.(), { direction }));
} }
/** @type {import('#client').TransitionManager} */ /** @type {TransitionManager} */
var transition = { var transition = {
is_global, is_global,
in() { 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); (e.transitions ??= []).push(transition);
@ -282,7 +283,7 @@ export function transition(flags, element, get_fn, get_params) {
let run = is_global; let run = is_global;
if (!run) { 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) // skip over transparent blocks (e.g. snippets, else-if blocks)
while (block && (block.f & EFFECT_TRANSPARENT) !== 0) { 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 * Animates an element, according to the provided configuration
* @param {Element} element * @param {Element} element
* @param {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig)} options * @param {AnimationConfig | ((opts: { direction: 'in' | 'out' }) => AnimationConfig)} options
* @param {import('#client').Animation | undefined} counterpart The corresponding intro/outro to this outro/intro * @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 {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_finish Called after successfully completing the animation
* @param {(() => void) | undefined} on_abort Called if the animation is aborted * @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) { function animate(element, options, counterpart, t2, on_finish, on_abort) {
var is_intro = t2 === 1; 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 // 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 // a function rather than an `AnimationConfig`. We need to call this function
// once DOM has been updated... // once DOM has been updated...
/** @type {import('#client').Animation} */ /** @type {Animation} */
var a; var a;
queue_micro_task(() => { 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 duration = options.duration * Math.abs(delta);
var end = start + duration; var end = start + duration;
/** @type {Animation} */ /** @type {globalThis.Animation} */
var animation; var animation;
/** @type {import('#client').Task} */ /** @type {Task} */
var task; var task;
if (css) { if (css) {

@ -1,3 +1,4 @@
/** @import { ProxyMetadata, ProxyStateObject, Source } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { get, current_component_context, untrack, current_effect } from './runtime.js'; import { get, current_component_context, untrack, current_effect } from './runtime.js';
import { import {
@ -18,9 +19,9 @@ import * as e from './errors.js';
/** /**
* @template T * @template T
* @param {T} value * @param {T} value
* @param {import('#client').ProxyMetadata | null} [parent] * @param {ProxyMetadata | null} [parent]
* @param {import('#client').Source<T>} [prev] dev mode only * @param {Source<T>} [prev] dev mode only
* @returns {import('#client').ProxyStateObject<T> | T} * @returns {ProxyStateObject<T> | T}
*/ */
export function proxy(value, parent = null, prev) { export function proxy(value, parent = null, prev) {
if ( if (
@ -31,7 +32,7 @@ export function proxy(value, parent = null, prev) {
) { ) {
// If we have an existing proxy, return it... // If we have an existing proxy, return it...
if (STATE_SYMBOL in value) { if (STATE_SYMBOL in value) {
const metadata = /** @type {import('#client').ProxyMetadata<T>} */ (value[STATE_SYMBOL]); const metadata = /** @type {ProxyMetadata<T>} */ (value[STATE_SYMBOL]);
// ...unless the proxy belonged to a different object, because // ...unless the proxy belonged to a different object, because
// someone copied the state symbol using `Reflect.ownKeys(...)` // 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); const proxy = new Proxy(value, state_proxy_handler);
define_property(value, STATE_SYMBOL, { define_property(value, STATE_SYMBOL, {
value: /** @type {import('#client').ProxyMetadata} */ ({ value: /** @type {ProxyMetadata} */ ({
s: new Map(), s: new Map(),
v: source(0), v: source(0),
a: is_array(value), a: is_array(value),
@ -94,18 +95,18 @@ export function proxy(value, parent = null, prev) {
} }
/** /**
* @param {import('#client').Source<number>} signal * @param {Source<number>} signal
* @param {1 | -1} [d] * @param {1 | -1} [d]
*/ */
function update_version(signal, d = 1) { function update_version(signal, d = 1) {
set(signal, signal.v + d); set(signal, signal.v + d);
} }
/** @type {ProxyHandler<import('#client').ProxyStateObject<any>>} */ /** @type {ProxyHandler<ProxyStateObject<any>>} */
const state_proxy_handler = { const state_proxy_handler = {
defineProperty(target, prop, descriptor) { defineProperty(target, prop, descriptor) {
if (descriptor.value) { if (descriptor.value) {
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop); const s = metadata.s.get(prop);
@ -116,7 +117,7 @@ const state_proxy_handler = {
}, },
deleteProperty(target, prop) { deleteProperty(target, prop) {
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop); const s = metadata.s.get(prop);
const is_array = metadata.a; const is_array = metadata.a;
@ -149,7 +150,7 @@ const state_proxy_handler = {
return Reflect.get(target, STATE_SYMBOL); return Reflect.get(target, STATE_SYMBOL);
} }
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
let s = metadata.s.get(prop); let s = metadata.s.get(prop);
@ -170,7 +171,7 @@ const state_proxy_handler = {
getOwnPropertyDescriptor(target, prop) { getOwnPropertyDescriptor(target, prop) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, prop); const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
if (descriptor && 'value' in descriptor) { if (descriptor && 'value' in descriptor) {
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop); const s = metadata.s.get(prop);
@ -186,7 +187,7 @@ const state_proxy_handler = {
if (prop === STATE_SYMBOL) { if (prop === STATE_SYMBOL) {
return true; return true;
} }
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
const has = Reflect.has(target, prop); const has = Reflect.has(target, prop);
@ -208,7 +209,7 @@ const state_proxy_handler = {
}, },
set(target, prop, value, receiver) { set(target, prop, value, receiver) {
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
let s = metadata.s.get(prop); let s = metadata.s.get(prop);
// If we haven't yet created a source for this property, we need to ensure // 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); const not_has = !(prop in target);
if (DEV) { if (DEV) {
/** @type {import('#client').ProxyMetadata | undefined} */ /** @type {ProxyMetadata | undefined} */
const prop_metadata = value?.[STATE_SYMBOL]; const prop_metadata = value?.[STATE_SYMBOL];
if (prop_metadata && prop_metadata?.parent !== metadata) { if (prop_metadata && prop_metadata?.parent !== metadata) {
widen_ownership(metadata, prop_metadata); widen_ownership(metadata, prop_metadata);
@ -271,7 +272,7 @@ const state_proxy_handler = {
}, },
ownKeys(target) { ownKeys(target) {
/** @type {import('#client').ProxyMetadata} */ /** @type {ProxyMetadata} */
const metadata = target[STATE_SYMBOL]; const metadata = target[STATE_SYMBOL];
get(metadata.v); get(metadata.v);

@ -1,3 +1,4 @@
/** @import { Derived } from '#client' */
import { CLEAN, DERIVED, DESTROYED, DIRTY, MAYBE_DIRTY, UNOWNED } from '../constants.js'; import { CLEAN, DERIVED, DESTROYED, DIRTY, MAYBE_DIRTY, UNOWNED } from '../constants.js';
import { import {
current_reaction, current_reaction,
@ -16,14 +17,14 @@ export let updating_derived = false;
/** /**
* @template V * @template V
* @param {() => V} fn * @param {() => V} fn
* @returns {import('#client').Derived<V>} * @returns {Derived<V>}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function derived(fn) { export function derived(fn) {
let flags = DERIVED | DIRTY; let flags = DERIVED | DIRTY;
if (current_effect === null) flags |= UNOWNED; if (current_effect === null) flags |= UNOWNED;
/** @type {import('#client').Derived<V>} */ /** @type {Derived<V>} */
const signal = { const signal = {
deps: null, deps: null,
deriveds: null, deriveds: null,
@ -38,7 +39,7 @@ export function derived(fn) {
}; };
if (current_reaction !== null && (current_reaction.f & DERIVED) !== 0) { 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) { if (current_derived.deriveds === null) {
current_derived.deriveds = [signal]; current_derived.deriveds = [signal];
} else { } else {
@ -52,7 +53,7 @@ export function derived(fn) {
/** /**
* @template V * @template V
* @param {() => V} fn * @param {() => V} fn
* @returns {import('#client').Derived<V>} * @returns {Derived<V>}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function derived_safe_equal(fn) { 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} * @returns {void}
*/ */
function destroy_derived_children(derived) { function destroy_derived_children(derived) {
@ -79,7 +80,7 @@ function destroy_derived_children(derived) {
} }
/** /**
* @param {import('#client').Derived} derived * @param {Derived} derived
* @returns {void} * @returns {void}
*/ */
export function update_derived(derived) { export function update_derived(derived) {
@ -103,7 +104,7 @@ export function update_derived(derived) {
} }
/** /**
* @param {import('#client').Derived} signal * @param {Derived} signal
* @returns {void} * @returns {void}
*/ */
export function destroy_derived(signal) { export function destroy_derived(signal) {

@ -1,3 +1,4 @@
/** @import { ComponentContext, ComponentContextLegacy, Effect, Reaction, TemplateNode, TransitionManager } from '#client' */
import { import {
check_dirtiness, check_dirtiness,
current_component_context, current_component_context,
@ -57,8 +58,8 @@ export function validate_effect(rune) {
} }
/** /**
* @param {import("#client").Effect} effect * @param {Effect} effect
* @param {import("#client").Reaction} parent_effect * @param {Reaction} parent_effect
*/ */
export function push_effect(effect, parent_effect) { export function push_effect(effect, parent_effect) {
var parent_last = parent_effect.last; var parent_last = parent_effect.last;
@ -76,12 +77,12 @@ export function push_effect(effect, parent_effect) {
* @param {null | (() => void | (() => void))} fn * @param {null | (() => void | (() => void))} fn
* @param {boolean} sync * @param {boolean} sync
* @param {boolean} push * @param {boolean} push
* @returns {import('#client').Effect} * @returns {Effect}
*/ */
function create_effect(type, fn, sync, push = true) { function create_effect(type, fn, sync, push = true) {
var is_root = (type & ROOT_EFFECT) !== 0; var is_root = (type & ROOT_EFFECT) !== 0;
/** @type {import('#client').Effect} */ /** @type {Effect} */
var effect = { var effect = {
ctx: current_component_context, ctx: current_component_context,
deps: null, deps: null,
@ -187,7 +188,7 @@ export function user_effect(fn) {
} }
if (defer) { if (defer) {
var context = /** @type {import('#client').ComponentContext} */ (current_component_context); var context = /** @type {ComponentContext} */ (current_component_context);
(context.e ??= []).push(fn); (context.e ??= []).push(fn);
} else { } else {
var signal = effect(fn); var signal = effect(fn);
@ -198,7 +199,7 @@ export function user_effect(fn) {
/** /**
* Internal representation of `$effect.pre(...)` * Internal representation of `$effect.pre(...)`
* @param {() => void | (() => void)} fn * @param {() => void | (() => void)} fn
* @returns {import('#client').Effect} * @returns {Effect}
*/ */
export function user_pre_effect(fn) { export function user_pre_effect(fn) {
validate_effect('$effect.pre'); validate_effect('$effect.pre');
@ -229,7 +230,7 @@ export function effect_root(fn) {
/** /**
* @param {() => void | (() => void)} fn * @param {() => void | (() => void)} fn
* @returns {import('#client').Effect} * @returns {Effect}
*/ */
export function effect(fn) { export function effect(fn) {
return create_effect(EFFECT, fn, false); return create_effect(EFFECT, fn, false);
@ -241,9 +242,9 @@ export function effect(fn) {
* @param {() => void | (() => void)} fn * @param {() => void | (() => void)} fn
*/ */
export function legacy_pre_effect(deps, 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 }; var token = { effect: null, ran: false };
context.l.r1.push(token); context.l.r1.push(token);
@ -261,7 +262,7 @@ export function legacy_pre_effect(deps, fn) {
} }
export function legacy_pre_effect_reset() { export function legacy_pre_effect_reset() {
var context = /** @type {import('#client').ComponentContextLegacy} */ (current_component_context); var context = /** @type {ComponentContextLegacy} */ (current_component_context);
render_effect(() => { render_effect(() => {
if (!get(context.l.r2)) return; if (!get(context.l.r2)) return;
@ -283,7 +284,7 @@ export function legacy_pre_effect_reset() {
/** /**
* @param {() => void | (() => void)} fn * @param {() => void | (() => void)} fn
* @returns {import('#client').Effect} * @returns {Effect}
*/ */
export function render_effect(fn) { export function render_effect(fn) {
return create_effect(RENDER_EFFECT, fn, true); return create_effect(RENDER_EFFECT, fn, true);
@ -291,7 +292,7 @@ export function render_effect(fn) {
/** /**
* @param {() => void | (() => void)} fn * @param {() => void | (() => void)} fn
* @returns {import('#client').Effect} * @returns {Effect}
*/ */
export function template_effect(fn) { export function template_effect(fn) {
if (DEV) { 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) { export function execute_effect_teardown(effect) {
var teardown = effect.teardown; 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] * @param {boolean} [remove_dom]
* @returns {void} * @returns {void}
*/ */
@ -346,14 +347,13 @@ export function destroy_effect(effect, remove_dom = true) {
var removed = false; var removed = false;
if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes !== null) { 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 node = effect.nodes.start;
var end = effect.nodes.end; var end = effect.nodes.end;
while (node !== null) { while (node !== null) {
/** @type {import('#client').TemplateNode | null} */ /** @type {TemplateNode | null} */
var next = var next = node === end ? null : /** @type {TemplateNode} */ (node.nextSibling);
node === end ? null : /** @type {import('#client').TemplateNode} */ (node.nextSibling);
node.remove(); node.remove();
node = next; 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 * Detach an effect from the effect tree, freeing up memory and
* reducing the amount of work that happens on subsequent traversals * reducing the amount of work that happens on subsequent traversals
* @param {import('#client').Effect} effect * @param {Effect} effect
*/ */
export function unlink_effect(effect) { export function unlink_effect(effect) {
var parent = effect.parent; 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 * 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. * completed, and if the state change is reversed then we _resume_ it.
* A paused effect does not update, and the DOM subtree becomes inert. * A paused effect does not update, and the DOM subtree becomes inert.
* @param {import('#client').Effect} effect * @param {Effect} effect
* @param {() => void} [callback] * @param {() => void} [callback]
*/ */
export function pause_effect(effect, callback) { export function pause_effect(effect, callback) {
/** @type {import('#client').TransitionManager[]} */ /** @type {TransitionManager[]} */
var transitions = []; var transitions = [];
pause_children(effect, transitions, true); 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 * @param {() => void} fn
*/ */
export function run_out_transitions(transitions, 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 {Effect} effect
* @param {import('#client').TransitionManager[]} transitions * @param {TransitionManager[]} transitions
* @param {boolean} local * @param {boolean} local
*/ */
export function pause_children(effect, transitions, 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) * The opposite of `pause_effect`. We call this if (for example)
* `x` becomes falsy then truthy: `{#if x}...{/if}` * `x` becomes falsy then truthy: `{#if x}...{/if}`
* @param {import('#client').Effect} effect * @param {Effect} effect
*/ */
export function resume_effect(effect) { export function resume_effect(effect) {
resume_children(effect, true); resume_children(effect, true);
} }
/** /**
* @param {import('#client').Effect} effect * @param {Effect} effect
* @param {boolean} local * @param {boolean} local
*/ */
function resume_children(effect, local) { function resume_children(effect, local) {

@ -1,3 +1,4 @@
/** @import { Derived, Effect, Source, Value } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { import {
current_component_context, current_component_context,
@ -31,7 +32,7 @@ let inspect_effects = new Set();
/** /**
* @template V * @template V
* @param {V} v * @param {V} v
* @returns {import('#client').Source<V>} * @returns {Source<V>}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function source(v) { export function source(v) {
@ -47,7 +48,7 @@ export function source(v) {
/** /**
* @template V * @template V
* @param {V} initial_value * @param {V} initial_value
* @returns {import('#client').Source<V>} * @returns {Source<V>}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function mutable_source(initial_value) { export function mutable_source(initial_value) {
@ -65,7 +66,7 @@ export function mutable_source(initial_value) {
/** /**
* @template V * @template V
* @param {import('#client').Value<V>} source * @param {Value<V>} source
* @param {V} value * @param {V} value
*/ */
export function mutate(source, value) { export function mutate(source, value) {
@ -78,7 +79,7 @@ export function mutate(source, value) {
/** /**
* @template V * @template V
* @param {import('#client').Source<V>} source * @param {Source<V>} source
* @param {V} value * @param {V} value
* @returns {V} * @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 * @param {number} status should be DIRTY or MAYBE_DIRTY
* @returns {void} * @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 the signal a) was previously clean or b) is an unowned derived, then mark it
if ((flags & (CLEAN | UNOWNED)) !== 0) { if ((flags & (CLEAN | UNOWNED)) !== 0) {
if ((flags & DERIVED) !== 0) { if ((flags & DERIVED) !== 0) {
mark_reactions(/** @type {import('#client').Derived} */ (reaction), MAYBE_DIRTY); mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY);
} else { } else {
schedule_effect(/** @type {import('#client').Effect} */ (reaction)); schedule_effect(/** @type {Effect} */ (reaction));
} }
} }
} }

@ -1,3 +1,5 @@
/** @import { ComponentContext, Effect, EffectNodes, TemplateNode } from '#client' */
/** @import { Component, ComponentType, SvelteComponent } from '../../index.js' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { clear_text_content, empty, init_operations } from './dom/operations.js'; import { clear_text_content, empty, init_operations } from './dom/operations.js';
import { import {
@ -59,7 +61,7 @@ export function set_text(text, value) {
* *
* @template {Record<string, any>} Props * @template {Record<string, any>} Props
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component * @param {ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>} component
* @param {{} extends Props ? { * @param {{} extends Props ? {
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* anchor?: Node; * anchor?: Node;
@ -88,7 +90,7 @@ export function mount(component, options) {
* *
* @template {Record<string, any>} Props * @template {Record<string, any>} Props
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component * @param {ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>} component
* @param {{} extends Props ? { * @param {{} extends Props ? {
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* props?: Props; * props?: Props;
@ -115,12 +117,12 @@ export function hydrate(component, options) {
try { try {
// Don't flush previous effects to ensure order of outer effects stays consistent // Don't flush previous effects to ensure order of outer effects stays consistent
return flush_sync(() => { return flush_sync(() => {
var anchor = /** @type {import('#client').TemplateNode} */ (target.firstChild); var anchor = /** @type {TemplateNode} */ (target.firstChild);
while ( while (
anchor && anchor &&
(anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START) (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== HYDRATION_START)
) { ) {
anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling); anchor = /** @type {TemplateNode} */ (anchor.nextSibling);
} }
if (!anchor) { if (!anchor) {
@ -141,8 +143,6 @@ export function hydrate(component, options) {
throw HYDRATION_ERROR; 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; return instance;
@ -177,7 +177,7 @@ const document_listeners = new Map();
/** /**
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<any>> | import('../../index.js').Component<any>} Component * @param {ComponentType<SvelteComponent<any>> | Component<any>} Component
* @param {{ * @param {{
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* anchor: Node; * anchor: Node;
@ -232,7 +232,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
branch(() => { branch(() => {
if (context) { if (context) {
push({}); push({});
var ctx = /** @type {import('#client').ComponentContext} */ (current_component_context); var ctx = /** @type {ComponentContext} */ (current_component_context);
ctx.c = context; ctx.c = context;
} }
@ -242,7 +242,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
} }
if (hydrating) { if (hydrating) {
assign_nodes(/** @type {import('#client').TemplateNode} */ (anchor), null); assign_nodes(/** @type {TemplateNode} */ (anchor), null);
} }
should_intro = intro; should_intro = intro;
@ -251,9 +251,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
should_intro = true; should_intro = true;
if (hydrating) { if (hydrating) {
/** @type {import('#client').Effect & { nodes: import('#client').EffectNodes }} */ ( /** @type {Effect & { nodes: EffectNodes }} */ (current_effect).nodes.end = hydrate_node;
current_effect
).nodes.end = hydrate_node;
} }
if (context) { if (context) {

@ -1,3 +1,4 @@
/** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { define_property, get_descriptors, get_prototype_of } from '../shared/utils.js'; import { define_property, get_descriptors, get_prototype_of } from '../shared/utils.js';
import { import {
@ -57,24 +58,24 @@ export function set_is_destroying_effect(value) {
// Handle effect queues // Handle effect queues
/** @type {import('#client').Effect[]} */ /** @type {Effect[]} */
let current_queued_root_effects = []; let current_queued_root_effects = [];
let flush_count = 0; let flush_count = 0;
// Handle signal reactivity tree dependencies and reactions // Handle signal reactivity tree dependencies and reactions
/** @type {null | import('#client').Reaction} */ /** @type {null | Reaction} */
export let current_reaction = null; export let current_reaction = null;
/** @param {null | import('#client').Reaction} reaction */ /** @param {null | Reaction} reaction */
export function set_current_reaction(reaction) { export function set_current_reaction(reaction) {
current_reaction = reaction; current_reaction = reaction;
} }
/** @type {null | import('#client').Effect} */ /** @type {null | Effect} */
export let current_effect = null; export let current_effect = null;
/** @param {null | import('#client').Effect} effect */ /** @param {null | Effect} effect */
export function set_current_effect(effect) { export function set_current_effect(effect) {
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 of the reaction that is currently being executed. In many cases,
* the dependencies are unchanged between runs, and so this will be `null` unless * 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` * 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; 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, * 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 * 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; export let current_untracked_writes = null;
/** @param {null | import('#client').Source[]} value */ /** @param {null | Source[]} value */
export function set_current_untracked_writes(value) { export function set_current_untracked_writes(value) {
current_untracked_writes = value; current_untracked_writes = value;
} }
@ -112,10 +113,10 @@ export let is_signals_recorded = false;
let captured_signals = new Set(); let captured_signals = new Set();
// Handling runtime component context // Handling runtime component context
/** @type {import('#client').ComponentContext | null} */ /** @type {ComponentContext | null} */
export let current_component_context = null; export let current_component_context = null;
/** @param {import('#client').ComponentContext | null} context */ /** @param {ComponentContext | null} context */
export function set_current_component_context(context) { export function set_current_component_context(context) {
current_component_context = context; current_component_context = context;
} }
@ -128,11 +129,11 @@ export function set_current_component_context(context) {
* <Bar /> <!-- context == Foo.svelte, function == App.svelte --> * <Bar /> <!-- context == Foo.svelte, function == App.svelte -->
* </Foo> * </Foo>
* ``` * ```
* @type {import('#client').ComponentContext['function']} * @type {ComponentContext['function']}
*/ */
export let dev_current_component_function = null; 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) { export function set_dev_current_component_function(fn) {
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. * Determines whether a derived or effect is dirty.
* If it is MAYBE_DIRTY, will set the status to CLEAN * If it is MAYBE_DIRTY, will set the status to CLEAN
* @param {import('#client').Reaction} reaction * @param {Reaction} reaction
* @returns {boolean} * @returns {boolean}
*/ */
export function check_dirtiness(reaction) { export function check_dirtiness(reaction) {
@ -177,8 +178,8 @@ export function check_dirtiness(reaction) {
for (i = 0; i < dependencies.length; i++) { for (i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i]; var dependency = dependencies[i];
if (check_dirtiness(/** @type {import('#client').Derived} */ (dependency))) { if (check_dirtiness(/** @type {Derived} */ (dependency))) {
update_derived(/** @type {import('#client').Derived} */ (dependency)); update_derived(/** @type {Derived} */ (dependency));
} }
if (dependency.version > reaction.version) { if (dependency.version > reaction.version) {
@ -205,8 +206,8 @@ export function check_dirtiness(reaction) {
/** /**
* @param {Error} error * @param {Error} error
* @param {import("#client").Effect} effect * @param {Effect} effect
* @param {import("#client").ComponentContext | null} component_context * @param {ComponentContext | null} component_context
*/ */
function handle_error(error, effect, component_context) { function handle_error(error, effect, component_context) {
// Given we don't yet have error boundaries, we will just always throw. // 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); component_stack.push(effect_name);
} }
/** @type {import("#client").ComponentContext | null} */ /** @type {ComponentContext | null} */
let current_context = component_context; let current_context = component_context;
while (current_context !== null) { while (current_context !== null) {
@ -266,7 +267,7 @@ function handle_error(error, effect, component_context) {
/** /**
* @template V * @template V
* @param {import('#client').Reaction} reaction * @param {Reaction} reaction
* @returns {V} * @returns {V}
*/ */
export function update_reaction(reaction) { export function update_reaction(reaction) {
@ -276,7 +277,7 @@ export function update_reaction(reaction) {
var previous_reaction = current_reaction; var previous_reaction = current_reaction;
var previous_skip_reaction = current_skip_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; skipped_deps = 0;
current_untracked_writes = null; current_untracked_writes = null;
current_reaction = (reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; current_reaction = (reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
@ -349,8 +350,8 @@ export function update_reaction(reaction) {
/** /**
* @template V * @template V
* @param {import('#client').Reaction} signal * @param {Reaction} signal
* @param {import('#client').Value<V>} dependency * @param {Value<V>} dependency
* @returns {void} * @returns {void}
*/ */
function remove_reaction(signal, dependency) { function remove_reaction(signal, dependency) {
@ -378,12 +379,12 @@ function remove_reaction(signal, dependency) {
if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) { if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) {
dependency.f ^= DISCONNECTED; 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 * @param {number} start_index
* @returns {void} * @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 * @param {boolean} remove_dom
* @returns {void} * @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} * @returns {void}
*/ */
export function update_effect(effect) { export function update_effect(effect) {
@ -480,7 +481,7 @@ function infinite_loop_guard() {
} }
/** /**
* @param {Array<import('#client').Effect>} root_effects * @param {Array<Effect>} root_effects
* @returns {void} * @returns {void}
*/ */
function flush_queued_root_effects(root_effects) { 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) { if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) {
flush_queued_effects([effect]); flush_queued_effects([effect]);
} else { } else {
/** @type {import('#client').Effect[]} */ /** @type {Effect[]} */
var collected_effects = []; var collected_effects = [];
process_effects(effect, collected_effects); process_effects(effect, collected_effects);
@ -514,7 +515,7 @@ function flush_queued_root_effects(root_effects) {
} }
/** /**
* @param {Array<import('#client').Effect>} effects * @param {Array<Effect>} effects
* @returns {void} * @returns {void}
*/ */
function flush_queued_effects(effects) { function flush_queued_effects(effects) {
@ -559,7 +560,7 @@ function process_deferred() {
} }
/** /**
* @param {import('#client').Effect} signal * @param {Effect} signal
* @returns {void} * @returns {void}
*/ */
export function schedule_effect(signal) { 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 * bitwise flag passed in only. The collected effects array will be populated with all the user
* effects to be flushed. * effects to be flushed.
* *
* @param {import('#client').Effect} effect * @param {Effect} effect
* @param {import('#client').Effect[]} collected_effects * @param {Effect[]} collected_effects
* @returns {void} * @returns {void}
*/ */
function process_effects(effect, collected_effects) { function process_effects(effect, collected_effects) {
@ -680,7 +681,7 @@ export function flush_sync(fn, flush_previous = true) {
try { try {
infinite_loop_guard(); infinite_loop_guard();
/** @type {import('#client').Effect[]} */ /** @type {Effect[]} */
const root_effects = []; const root_effects = [];
current_scheduler_mode = FLUSH_SYNC; current_scheduler_mode = FLUSH_SYNC;
@ -720,7 +721,7 @@ export async function tick() {
/** /**
* @template V * @template V
* @param {import('#client').Value<V>} signal * @param {Value<V>} signal
* @returns {V} * @returns {V}
*/ */
export function get(signal) { export function get(signal) {
@ -768,7 +769,7 @@ export function get(signal) {
} }
if ((flags & DERIVED) !== 0) { if ((flags & DERIVED) !== 0) {
var derived = /** @type {import('#client').Derived} */ (signal); var derived = /** @type {Derived} */ (signal);
if (check_dirtiness(derived)) { if (check_dirtiness(derived)) {
update_derived(derived); update_derived(derived);
@ -804,7 +805,7 @@ export function invalidate_inner_signals(fn) {
for (signal of captured) { for (signal of captured) {
// Go one level up because derived signals created as part of props in legacy mode // Go one level up because derived signals created as part of props in legacy mode
if ((signal.f & LEGACY_DERIVED_PROP) !== 0) { 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) { if ((dep.f & DERIVED) === 0) {
mutate(dep, null /* doesnt matter */); mutate(dep, null /* doesnt matter */);
} }
@ -836,7 +837,7 @@ export function untrack(fn) {
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN); const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
/** /**
* @param {import('#client').Signal} signal * @param {Signal} signal
* @param {number} status * @param {number} status
* @returns {void} * @returns {void}
*/ */
@ -846,14 +847,12 @@ export function set_signal_status(signal, status) {
/** /**
* @template V * @template V
* @param {V | import('#client').Value<V>} val * @param {V | Value<V>} val
* @returns {val is import('#client').Value<V>} * @returns {val is Value<V>}
*/ */
export function is_signal(val) { export function is_signal(val) {
return ( return (
typeof val === 'object' && typeof val === 'object' && val !== null && typeof (/** @type {Value<V>} */ (val).f) === 'number'
val !== null &&
typeof (/** @type {import('#client').Value<V>} */ (val).f) === 'number'
); );
} }
@ -871,8 +870,7 @@ export function getContext(key) {
const result = /** @type {T} */ (context_map.get(key)); const result = /** @type {T} */ (context_map.get(key));
if (DEV) { if (DEV) {
const fn = /** @type {import('#client').ComponentContext} */ (current_component_context) const fn = /** @type {ComponentContext} */ (current_component_context).function;
.function;
if (fn) { if (fn) {
add_owner(result, fn, true); add_owner(result, fn, true);
} }
@ -952,7 +950,7 @@ function get_or_init_context_map(name) {
} }
/** /**
* @param {import('#client').ComponentContext} component_context * @param {ComponentContext} component_context
* @returns {Map<unknown, unknown> | null} * @returns {Map<unknown, unknown> | null}
*/ */
function get_parent_context(component_context) { function get_parent_context(component_context) {
@ -968,7 +966,7 @@ function get_parent_context(component_context) {
} }
/** /**
* @param {import('#client').Value<number>} signal * @param {Value<number>} signal
* @param {1 | -1} [d] * @param {1 | -1} [d]
* @returns {number} * @returns {number}
*/ */
@ -979,7 +977,7 @@ export function update(signal, d = 1) {
} }
/** /**
* @param {import('#client').Value<number>} signal * @param {Value<number>} signal
* @param {1 | -1} [d] * @param {1 | -1} [d]
* @returns {number} * @returns {number}
*/ */
@ -1159,7 +1157,7 @@ export function deep_read(value, visited = new Set()) {
/** /**
* @template V * @template V
* @param {V | import('#client').Value<V>} value * @param {V | Value<V>} value
* @returns {V} * @returns {V}
*/ */
export function unwrap(value) { export function unwrap(value) {

@ -1,3 +1,4 @@
/** @import { Component, Payload } from '#server' */
import { import {
FILENAME, FILENAME,
disallowed_paragraph_contents, disallowed_paragraph_contents,
@ -33,7 +34,7 @@ function stringify(element) {
} }
/** /**
* @param {import('#server').Payload} payload * @param {Payload} payload
* @param {Element} parent * @param {Element} parent
* @param {Element} child * @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 {string} tag
* @param {number} line * @param {number} line
* @param {number} column * @param {number} column
*/ */
export function push_element(payload, tag, line, 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 }; var child = { tag, parent, filename, line, column };
if (parent !== null && !is_tag_valid_with_parent(tag, parent.tag)) { if (parent !== null && !is_tag_valid_with_parent(tag, parent.tag)) {

@ -1,3 +1,4 @@
/** @import { ComponentType, SvelteComponent } from 'svelte' */
/** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Component, Payload, RenderOutput } from '#server' */
/** @import { Store } from '#shared' */ /** @import { Store } from '#shared' */
export { FILENAME, HMR } from '../../constants.js'; 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. * 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. * 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<string, any>} Props * @template {Record<string, any>} Props
* @param {import('svelte').Component<Props> | import('svelte').ComponentType<import('svelte').SvelteComponent<Props>>} component * @param {import('svelte').Component<Props> | ComponentType<SvelteComponent<Props>>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options] * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options]
* @returns {RenderOutput} * @returns {RenderOutput}
*/ */

@ -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 { writable } from '../store/index.js';
import { loop } from '../internal/client/loop.js'; import { loop } from '../internal/client/loop.js';
import { raf } from '../internal/client/timing.js'; import { raf } from '../internal/client/timing.js';
@ -5,7 +8,7 @@ import { is_date } from './utils.js';
/** /**
* @template T * @template T
* @param {import('./private').TickContext<T>} ctx * @param {TickContext<T>} ctx
* @param {T} last_value * @param {T} last_value
* @param {T} current_value * @param {T} current_value
* @param {T} target_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 * https://svelte.dev/docs/svelte-motion#spring
* @template [T=any] * @template [T=any]
* @param {T} [value] * @param {T} [value]
* @param {import('./private').SpringOpts} [opts] * @param {SpringOpts} [opts]
* @returns {import('./public.js').Spring<T>} * @returns {Spring<T>}
*/ */
export function spring(value, opts = {}) { export function spring(value, opts = {}) {
const store = writable(value); const store = writable(value);
const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts; const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = opts;
/** @type {number} */ /** @type {number} */
let last_time; let last_time;
/** @type {import('../internal/client/types').Task | null} */ /** @type {Task | null} */
let task; let task;
/** @type {object} */ /** @type {object} */
let current_token; let current_token;
@ -74,7 +77,7 @@ export function spring(value, opts = {}) {
let cancel_task = false; let cancel_task = false;
/** /**
* @param {T} new_value * @param {T} new_value
* @param {import('./private').SpringUpdateOpts} opts * @param {SpringUpdateOpts} opts
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
function set(new_value, opts = {}) { function set(new_value, opts = {}) {
@ -101,7 +104,7 @@ export function spring(value, opts = {}) {
return false; return false;
} }
inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1); inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1);
/** @type {import('./private').TickContext<T>} */ /** @type {TickContext<T>} */
const ctx = { const ctx = {
inv_mass, inv_mass,
opts: spring, opts: spring,
@ -120,12 +123,12 @@ export function spring(value, opts = {}) {
}); });
} }
return new Promise((fulfil) => { return new Promise((fulfil) => {
/** @type {import('../internal/client/types').Task} */ (task).promise.then(() => { /** @type {Task} */ (task).promise.then(() => {
if (token === current_token) fulfil(); if (token === current_token) fulfil();
}); });
}); });
} }
/** @type {import('./public.js').Spring<T>} */ /** @type {Spring<T>} */
const spring = { const spring = {
set, set,
update: (fn, opts) => set(fn(/** @type {T} */ (target_value), /** @type {T} */ (value)), opts), update: (fn, opts) => set(fn(/** @type {T} */ (target_value), /** @type {T} */ (value)), opts),

@ -1,3 +1,4 @@
/** @import { Source } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { source, set } from '../internal/client/reactivity/sources.js'; import { source, set } from '../internal/client/reactivity/sources.js';
import { get } from '../internal/client/runtime.js'; import { get } from '../internal/client/runtime.js';
@ -9,7 +10,7 @@ import { increment } from './utils.js';
* @extends {Map<K, V>} * @extends {Map<K, V>}
*/ */
export class SvelteMap extends Map { export class SvelteMap extends Map {
/** @type {Map<K, import('#client').Source<number>>} */ /** @type {Map<K, Source<number>>} */
#sources = new Map(); #sources = new Map();
#version = source(0); #version = source(0);
#size = source(0); #size = source(0);

@ -1,3 +1,4 @@
/** @import { Source } from '#client' */
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { source, set } from '../internal/client/reactivity/sources.js'; import { source, set } from '../internal/client/reactivity/sources.js';
import { get } from '../internal/client/runtime.js'; import { get } from '../internal/client/runtime.js';
@ -13,7 +14,7 @@ var inited = false;
* @extends {Set<T>} * @extends {Set<T>}
*/ */
export class SvelteSet extends Set { export class SvelteSet extends Set {
/** @type {Map<T, import('#client').Source<boolean>>} */ /** @type {Map<T, Source<boolean>>} */
#sources = new Map(); #sources = new Map();
#version = source(0); #version = source(0);
#size = source(0); #size = source(0);

@ -1,3 +1,4 @@
/** @import { Source } from '#client' */
import { set } from '../internal/client/reactivity/sources.js'; import { set } from '../internal/client/reactivity/sources.js';
/** /**
@ -30,7 +31,7 @@ function get_this() {
return this; return this;
} }
/** @param {import('#client').Source<number>} source */ /** @param {Source<number>} source */
export function increment(source) { export function increment(source) {
set(source, source.v + 1); set(source, source.v + 1);
} }

@ -1,6 +1,8 @@
<!-- valid --> <!-- valid -->
<link /> <link />
<svg><g /></svg> <svg><g /></svg>
<enhanced:img />
<!-- invalid --> <!-- invalid -->
<div /> <div />

@ -3,11 +3,11 @@
"code": "element_invalid_self_closing_tag", "code": "element_invalid_self_closing_tag",
"message": "Self-closing HTML tags for non-void elements are ambiguous — use `<div ...></div>` rather than `<div ... />`", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `<div ...></div>` rather than `<div ... />`",
"start": { "start": {
"line": 6, "line": 8,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 6, "line": 8,
"column": 7 "column": 7
} }
}, },
@ -15,11 +15,11 @@
"code": "element_invalid_self_closing_tag", "code": "element_invalid_self_closing_tag",
"message": "Self-closing HTML tags for non-void elements are ambiguous — use `<my-thing ...></my-thing>` rather than `<my-thing ... />`", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `<my-thing ...></my-thing>` rather than `<my-thing ... />`",
"start": { "start": {
"line": 7, "line": 9,
"column": 0 "column": 0
}, },
"end": { "end": {
"line": 7, "line": 9,
"column": 12 "column": 12
} }
} }

@ -373,8 +373,6 @@ declare module 'svelte' {
* Synchronously flushes any pending state changes and those that result from it. * Synchronously flushes any pending state changes and those that result from it.
* */ * */
export function flushSync(fn?: (() => void) | undefined): void; export function flushSync(fn?: (() => void) | undefined): void;
/** Anything except a function */
type NotFunction<T> = T extends Function ? never : T;
/** /**
* Create a snippet programmatically * Create a snippet programmatically
* */ * */
@ -382,6 +380,8 @@ declare module 'svelte' {
render: () => string; render: () => string;
setup?: (element: Element) => void; setup?: (element: Element) => void;
}): Snippet<Params>; }): Snippet<Params>;
/** Anything except a function */
type NotFunction<T> = 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. * 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`. * Transitions will play during the initial render unless the `intro` option is set to `false`.

Loading…
Cancel
Save