From b8a27f4050a3b522531a52a720c0e15f986ed878 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 24 Oct 2025 21:52:54 -0400 Subject: [PATCH] WIP --- .../src/compiler/phases/2-analyze/index.js | 92 +++++++++++-------- .../3-transform/client/visitors/Program.js | 4 +- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 12066db550..ac1d3c812d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1,4 +1,4 @@ -/** @import { Expression, Node, Program } from 'estree' */ +/** @import * as ESTree from 'estree' */ /** @import { Binding, AST, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */ /** @import { AnalysisState, Visitors } from './types' */ /** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */ @@ -217,7 +217,7 @@ const visitors = { * @returns {Js} */ function js(script, root, allow_reactive_declarations, parent) { - /** @type {Program} */ + /** @type {ESTree.Program} */ const ast = script?.content ?? { type: 'Program', sourceType: 'module', @@ -300,7 +300,7 @@ export function analyze_module(source, options) { }); walk( - /** @type {Node} */ (ast), + /** @type {ESTree.Node} */ (ast), { scope, scopes, @@ -358,7 +358,7 @@ export function analyze_component(root, source, options) { const store_name = name.slice(1); const declaration = instance.scope.get(store_name); - const init = /** @type {Node | undefined} */ (declaration?.initial); + const init = /** @type {ESTree.Node | undefined} */ (declaration?.initial); // If we're not in legacy mode through the compiler option, assume the user // is referencing a rune and not a global store. @@ -418,7 +418,7 @@ export function analyze_component(root, source, options) { /** @type {number} */ (node.start) > /** @type {number} */ (module.ast.start) && /** @type {number} */ (node.end) < /** @type {number} */ (module.ast.end) && // const state = $state(0) is valid - get_rune(/** @type {Node} */ (path.at(-1)), module.scope) === null + get_rune(/** @type {ESTree.Node} */ (path.at(-1)), module.scope) === null ) { e.store_invalid_subscription(node); } @@ -649,7 +649,7 @@ export function analyze_component(root, source, options) { // @ts-expect-error _: set_scope, Identifier(node, context) { - const parent = /** @type {Expression} */ (context.path.at(-1)); + const parent = /** @type {ESTree.Expression} */ (context.path.at(-1)); if (is_reference(node, parent)) { const binding = context.state.scope.get(node.name); @@ -704,53 +704,67 @@ export function analyze_component(root, source, options) { let awaiting = false; let i = 0; - for (let node of instance.ast.body) { - if (node.type === 'ExportNamedDeclaration' && node.declaration) { - node = node.declaration; - } + /** + * @param {ESTree.Statement | ESTree.VariableDeclarator | ESTree.FunctionDeclaration | ESTree.ClassDeclaration} node + */ + const push = (node) => { + const has_await = has_await_expression(node); + awaiting ||= has_await; - if (node.type === 'VariableDeclaration') { - for (const declarator of node.declarations) { - const has_await = has_await_expression(declarator); - awaiting ||= has_await; + if (!awaiting) return; - if (!awaiting) continue; + const id = b.id(`$$${i++}`); - const id = b.id(`$$${i++}`); + analysis.awaited_statements.set(node, { + id, + has_await, + metadata: create_expression_metadata() + }); - analysis.awaited_statements.set(declarator, { + if (node.type === 'VariableDeclarator') { + for (const identifier of extract_identifiers(node.id)) { + analysis.awaited_declarations.set(identifier.name, { id, has_await, + pattern: node.id, + updated_by: new Set(), metadata: create_expression_metadata() }); - - for (const identifier of extract_identifiers(declarator.id)) { - analysis.awaited_declarations.set(identifier.name, { - id, - has_await, - pattern: declarator.id, - updated_by: new Set(), - metadata: create_expression_metadata() - }); - } } - } else { - const has_await = has_await_expression(node); - awaiting ||= has_await; + } else if (node.type === 'ClassDeclaration' || node.type === 'FunctionDeclaration') { + analysis.awaited_declarations.set(node.id.name, { + id, + has_await, + pattern: node.id, + updated_by: new Set(), + metadata: create_expression_metadata() + }); + } + }; - if (!awaiting) continue; + for (let node of instance.ast.body) { + if ( + node.type === 'ImportDeclaration' || + node.type === 'ExportDefaultDeclaration' || + node.type === 'ExportAllDeclaration' + ) { + continue; + } - if (node.type === 'ClassDeclaration' || node.type === 'FunctionDeclaration') { - throw new Error('TODO handle class/function declaration'); + if (node.type === 'ExportNamedDeclaration') { + if (node.declaration) { + node = node.declaration; } else { - const id = b.id(`$$${i++}`); + continue; + } + } - analysis.awaited_statements.set(node, { - id, - has_await, - metadata: create_expression_metadata() - }); + if (node.type === 'VariableDeclaration') { + for (const declarator of node.declarations) { + push(declarator); } + } else { + push(node); } } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js index 5b0e8322fd..58af1a8a1e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js @@ -187,7 +187,9 @@ function transform_body(program, context) { ? node.init ?? b.block([]) : node.type === 'ExpressionStatement' ? node.expression - : b.block([node]); + : node.type === 'FunctionDeclaration' + ? node + : b.block([node]); out.push( b.var(