@ -1,13 +1,13 @@
/** @import { Node, Program } from 'estree' */
/** @import { Root, Script, SvelteNode, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
/** @import { Expression, Node, Program } from 'estree' */
/** @import { Binding, Root, Script, SvelteNode, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
/** @import { AnalysisState, Visitors } from './types' */
/** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */
import { walk } from 'zimmerframe' ;
import * as e from '../../errors.js' ;
import * as w from '../../warnings.js' ;
import { is_text _attribute } from '../../utils/ast.js' ;
import { extract_identifiers , is_text _attribute } from '../../utils/ast.js' ;
import * as b from '../../utils/builders.js' ;
import { Scope , ScopeRoot , create _scopes , get _rune } from '../scope.js' ;
import { Scope , ScopeRoot , create _scopes , get _rune , set _scope } from '../scope.js' ;
import check _graph _for _cycles from './utils/check_graph_for_cycles.js' ;
import { create _attribute } from '../nodes.js' ;
import { analyze _css } from './css/css-analyze.js' ;
@ -62,6 +62,7 @@ import { Text } from './visitors/Text.js';
import { TitleElement } from './visitors/TitleElement.js' ;
import { UpdateExpression } from './visitors/UpdateExpression.js' ;
import { VariableDeclarator } from './visitors/VariableDeclarator.js' ;
import is _reference from 'is-reference' ;
/ * *
* @ type { Visitors }
@ -397,6 +398,112 @@ export function analyze_component(root, source, options) {
source
} ;
if ( ! runes ) {
// every exported `let` or `var` declaration becomes a prop, everything else becomes an export
for ( const node of instance . ast . body ) {
if ( node . type !== 'ExportNamedDeclaration' ) continue ;
analysis . needs _props = true ;
if ( node . declaration ) {
if (
node . declaration . type === 'FunctionDeclaration' ||
node . declaration . type === 'ClassDeclaration'
) {
analysis . exports . push ( {
name : /** @type {import('estree').Identifier} */ ( node . declaration . id ) . name ,
alias : null
} ) ;
} else if ( node . declaration . type === 'VariableDeclaration' ) {
if ( node . declaration . kind === 'const' ) {
for ( const declarator of node . declaration . declarations ) {
for ( const node of extract _identifiers ( declarator . id ) ) {
analysis . exports . push ( { name : node . name , alias : null } ) ;
}
}
} else {
for ( const declarator of node . declaration . declarations ) {
for ( const id of extract _identifiers ( declarator . id ) ) {
const binding = /** @type {Binding} */ ( instance . scope . get ( id . name ) ) ;
binding . kind = 'bindable_prop' ;
}
}
}
}
} else {
for ( const specifier of node . specifiers ) {
const binding = instance . scope . get ( specifier . local . name ) ;
if (
binding &&
( binding . declaration _kind === 'var' || binding . declaration _kind === 'let' )
) {
binding . kind = 'bindable_prop' ;
if ( specifier . exported . name !== specifier . local . name ) {
binding . prop _alias = specifier . exported . name ;
}
} else {
analysis . exports . push ( { name : specifier . local . name , alias : specifier . exported . name } ) ;
}
}
}
}
// if reassigned/mutated bindings are referenced in `$:` blocks
// or the template, turn them into state
for ( const binding of instance . scope . declarations . values ( ) ) {
if ( binding . kind !== 'normal' ) continue ;
for ( const { node , path } of binding . references ) {
if ( node === binding . node ) continue ;
if ( binding . updated ) {
if (
path [ path . length - 1 ] . type === 'StyleDirective' ||
path . some ( ( node ) => node . type === 'Fragment' ) ||
( path [ 1 ] . type === 'LabeledStatement' && path [ 1 ] . label . name === '$' )
) {
binding . kind = 'state' ;
}
}
}
}
// more legacy nonsense: if an `each` binding is reassigned/mutated,
// treat the expression as being mutated as well
walk ( /** @type {SvelteNode} */ ( template . ast ) , null , {
EachBlock ( node ) {
const scope = /** @type {Scope} */ ( template . scopes . get ( node ) ) ;
for ( const binding of scope . declarations . values ( ) ) {
if ( binding . updated ) {
const state = { scope : /** @type {Scope} */ ( scope . parent ) , scopes : template . scopes } ;
walk ( node . expression , state , {
// @ts-expect-error
_ : set _scope ,
Identifier ( node , context ) {
const parent = /** @type {Expression} */ ( context . path . at ( - 1 ) ) ;
if ( is _reference ( node , parent ) ) {
const binding = context . state . scope . get ( node . name ) ;
if ( binding && binding . kind === 'normal' ) {
binding . kind = 'state' ;
binding . mutated = binding . updated = true ;
}
}
}
} ) ;
break ;
}
}
}
} ) ;
}
if ( root . options ) {
for ( const attribute of root . options . attributes ) {
if ( attribute . name === 'accessors' ) {