chore: more JSDoc imports (#12590)

* more

* more

* more

* more

* fix
pull/12594/head
Rich Harris 2 months ago committed by GitHub
parent 20b879717a
commit 7a5c6b588f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,3 +1,7 @@
/** @import { AssignmentExpression, CallExpression, Expression, Identifier, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { Attribute, Component, ElementLike, Fragment, RegularElement, SvelteComponent, SvelteElement, SvelteNode, SvelteSelf, TransitionDirective } from '#compiler' */
/** @import { NodeLike } from '../../errors.js' */
/** @import { AnalysisState, Context, Visitors } from './types.js' */
import is_reference from 'is-reference';
import {
disallowed_paragraph_contents,
@ -35,8 +39,8 @@ import { merge } from '../visitors.js';
import { a11y_validators } from './a11y.js';
/**
* @param {import('#compiler').Attribute} attribute
* @param {import('#compiler').ElementLike} parent
* @param {Attribute} attribute
* @param {ElementLike} parent
*/
function validate_attribute(attribute, parent) {
if (
@ -63,8 +67,8 @@ function validate_attribute(attribute, parent) {
}
/**
* @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
* @param {Component | SvelteComponent | SvelteSelf} node
* @param {Context} context
*/
function validate_component(node, context) {
for (const attribute of node.attributes) {
@ -123,16 +127,16 @@ const react_attributes = new Map([
]);
/**
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
* @param {RegularElement | SvelteElement} node
* @param {Context} context
*/
function validate_element(node, context) {
let has_animate_directive = false;
/** @type {import('#compiler').TransitionDirective | null} */
/** @type {TransitionDirective | null} */
let in_transition = null;
/** @type {import('#compiler').TransitionDirective | null} */
/** @type {TransitionDirective | null} */
let out_transition = null;
for (const attribute of node.attributes) {
@ -175,7 +179,7 @@ function validate_element(node, context) {
}
if (attribute.name === 'slot') {
/** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf | undefined} */
/** @type {RegularElement | SvelteElement | Component | SvelteComponent | SvelteSelf | undefined} */
validate_slot_attribute(context, attribute);
}
@ -212,7 +216,7 @@ function validate_element(node, context) {
has_animate_directive = true;
}
} else if (attribute.type === 'TransitionDirective') {
const existing = /** @type {import('#compiler').TransitionDirective | null} */ (
const existing = /** @type {TransitionDirective | null} */ (
(attribute.intro && in_transition) || (attribute.outro && out_transition)
);
@ -255,7 +259,7 @@ function validate_element(node, context) {
}
/**
* @param {import('#compiler').Attribute} attribute
* @param {Attribute} attribute
*/
function validate_attribute_name(attribute) {
if (
@ -269,8 +273,8 @@ function validate_attribute_name(attribute) {
}
/**
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
* @param {import('#compiler').Attribute} attribute
* @param {Context} context
* @param {Attribute} attribute
* @param {boolean} is_component
*/
function validate_slot_attribute(context, attribute, is_component = false) {
@ -343,8 +347,8 @@ function validate_slot_attribute(context, attribute, is_component = false) {
}
/**
* @param {import('#compiler').Fragment | null | undefined} node
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types.js').AnalysisState>} context
* @param {Fragment | null | undefined} node
* @param {Context} context
*/
function validate_block_not_empty(node, context) {
if (!node) return;
@ -356,7 +360,7 @@ function validate_block_not_empty(node, context) {
}
/**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>}
* @type {Visitors}
*/
const validation = {
MemberExpression(node, context) {
@ -465,7 +469,7 @@ const validation = {
}
if (parent.name === 'input' && node.name !== 'this') {
const type = /** @type {import('#compiler').Attribute | undefined} */ (
const type = /** @type {Attribute | undefined} */ (
parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'type')
);
if (type && !is_text_attribute(type)) {
@ -506,7 +510,7 @@ const validation = {
}
if (ContentEditableBindings.includes(node.name)) {
const contenteditable = /** @type {import('#compiler').Attribute} */ (
const contenteditable = /** @type {Attribute} */ (
parent.attributes.find((a) => a.type === 'Attribute' && a.name === 'contenteditable')
);
if (!contenteditable) {
@ -846,8 +850,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
LabeledStatement(node, { path, state }) {
if (
node.label.name === '$' &&
(state.ast_type !== 'instance' ||
/** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program')
(state.ast_type !== 'instance' || /** @type {SvelteNode} */ (path.at(-1)).type !== 'Program')
) {
w.reactive_declaration_invalid_placement(node);
}
@ -859,8 +862,8 @@ export const validation_legacy = merge(validation, a11y_validators, {
/**
*
* @param {import('estree').Node} node
* @param {import('../scope').Scope} scope
* @param {Node} node
* @param {Scope} scope
* @param {string} name
*/
function validate_export(node, scope, name) {
@ -877,16 +880,16 @@ function validate_export(node, scope, name) {
}
/**
* @param {import('estree').CallExpression} node
* @param {CallExpression} node
* @param {Scope} scope
* @param {import('#compiler').SvelteNode[]} path
* @param {SvelteNode[]} path
* @returns
*/
function validate_call_expression(node, scope, path) {
const rune = get_rune(node, scope);
if (rune === null) return;
const parent = /** @type {import('#compiler').SvelteNode} */ (get_parent(path, -1));
const parent = /** @type {SvelteNode} */ (get_parent(path, -1));
if (rune === '$props') {
if (parent.type === 'VariableDeclarator') return;
@ -965,8 +968,8 @@ function validate_call_expression(node, scope, path) {
}
/**
* @param {import('estree').VariableDeclarator} node
* @param {import('./types.js').AnalysisState} state
* @param {VariableDeclarator} node
* @param {AnalysisState} state
*/
function ensure_no_module_import_conflict(node, state) {
const ids = extract_identifiers(node.id);
@ -981,7 +984,7 @@ function ensure_no_module_import_conflict(node, state) {
}
/**
* @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, import('./types.js').AnalysisState>}
* @type {Visitors}
*/
export const validation_runes_js = {
ImportDeclaration(node) {
@ -1016,7 +1019,7 @@ export const validation_runes_js = {
if (rune === null) return;
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
const args = /** @type {CallExpression} */ (init).arguments;
if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {
e.rune_invalid_arguments_length(node, rune, 'exactly one argument');
@ -1073,7 +1076,7 @@ export const validation_runes_js = {
},
Identifier(node, { path, state }) {
let i = path.length;
let parent = /** @type {import('estree').Expression} */ (path[--i]);
let parent = /** @type {Expression} */ (path[--i]);
if (
Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
@ -1081,16 +1084,16 @@ export const validation_runes_js = {
state.scope.get(node.name) === null &&
state.scope.get(node.name.slice(1)) === null
) {
/** @type {import('estree').Expression} */
/** @type {Expression} */
let current = node;
let name = node.name;
while (parent.type === 'MemberExpression') {
if (parent.computed) e.rune_invalid_computed_property(parent);
name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`;
name += `.${/** @type {Identifier} */ (parent.property).name}`;
current = parent;
parent = /** @type {import('estree').Expression} */ (path[--i]);
parent = /** @type {Expression} */ (path[--i]);
if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
if (name === '$effect.active') {
@ -1109,8 +1112,8 @@ export const validation_runes_js = {
};
/**
* @param {import('../../errors.js').NodeLike} node
* @param {import('estree').Pattern | import('estree').Expression} argument
* @param {NodeLike} node
* @param {Pattern | Expression} argument
* @param {Scope} scope
* @param {boolean} is_binding
*/
@ -1156,7 +1159,7 @@ function validate_no_const_assignment(node, argument, scope, is_binding) {
* Validates that the opening of a control flow block is `{` immediately followed by the expected character.
* In legacy mode whitespace is allowed inbetween. TODO remove once legacy mode is gone and move this into parser instead.
* @param {{start: number; end: number}} node
* @param {import('./types.js').AnalysisState} state
* @param {AnalysisState} state
* @param {string} expected
*/
function validate_opening_tag(node, state, expected) {
@ -1167,9 +1170,9 @@ function validate_opening_tag(node, state, expected) {
}
/**
* @param {import('estree').AssignmentExpression | import('estree').UpdateExpression} node
* @param {import('estree').Pattern | import('estree').Expression} argument
* @param {import('./types.js').AnalysisState} state
* @param {AssignmentExpression | UpdateExpression} node
* @param {Pattern | Expression} argument
* @param {AnalysisState} state
*/
function validate_assignment(node, argument, state) {
validate_no_const_assignment(node, argument, state.scope, false);
@ -1192,9 +1195,9 @@ function validate_assignment(node, argument, state) {
}
}
let object = /** @type {import('estree').Expression | import('estree').Super} */ (argument);
let object = /** @type {Expression | Super} */ (argument);
/** @type {import('estree').Expression | import('estree').PrivateIdentifier | null} */
/** @type {Expression | PrivateIdentifier | null} */
let property = null;
while (object.type === 'MemberExpression') {
@ -1322,7 +1325,7 @@ export const validation_runes = merge(validation, a11y_validators, {
if (rune === null) return;
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
const args = /** @type {CallExpression} */ (init).arguments;
// TODO some of this is duplicated with above, seems off
if ((rune === '$derived' || rune === '$derived.by') && args.length !== 1) {

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

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

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

Loading…
Cancel
Save