chore: more JSDoc (#12588)

pull/12594/head
Rich Harris 1 year ago committed by GitHub
parent abaa4413df
commit 6037b961c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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 {import('zimmerframe').Visitors<SvelteNode, { scope: Scope, analysis: { runes: true } }>} */
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) => {

Loading…
Cancel
Save