@ -4,12 +4,7 @@ import Stats from '../Stats';
import { globals , reserved , is_valid } from '../utils/names' ;
import { globals , reserved , is_valid } from '../utils/names' ;
import { namespaces , valid_namespaces } from '../utils/namespaces' ;
import { namespaces , valid_namespaces } from '../utils/namespaces' ;
import create_module from './create_module' ;
import create_module from './create_module' ;
import {
import { create_scopes , extract_names , Scope , extract_identifiers } from './utils/scope' ;
create_scopes ,
extract_names ,
Scope ,
extract_identifiers ,
} from './utils/scope' ;
import Stylesheet from './css/Stylesheet' ;
import Stylesheet from './css/Stylesheet' ;
import { test } from '../config' ;
import { test } from '../config' ;
import Fragment from './nodes/Fragment' ;
import Fragment from './nodes/Fragment' ;
@ -24,7 +19,15 @@ import TemplateScope from './nodes/shared/TemplateScope';
import fuzzymatch from '../utils/fuzzymatch' ;
import fuzzymatch from '../utils/fuzzymatch' ;
import get_object from './utils/get_object' ;
import get_object from './utils/get_object' ;
import Slot from './nodes/Slot' ;
import Slot from './nodes/Slot' ;
import { Node , ImportDeclaration , Identifier , Program , ExpressionStatement , AssignmentExpression , Literal } from 'estree' ;
import {
Node ,
ImportDeclaration ,
Identifier ,
Program ,
ExpressionStatement ,
AssignmentExpression ,
Literal ,
} from 'estree' ;
import add_to_set from './utils/add_to_set' ;
import add_to_set from './utils/add_to_set' ;
import check_graph_for_cycles from './utils/check_graph_for_cycles' ;
import check_graph_for_cycles from './utils/check_graph_for_cycles' ;
import { print , x , b } from 'code-red' ;
import { print , x , b } from 'code-red' ;
@ -66,8 +69,8 @@ export default class Component {
hoistable_nodes : Set < Node > = new Set ( ) ;
hoistable_nodes : Set < Node > = new Set ( ) ;
node_for_declaration : Map < string , Node > = new Map ( ) ;
node_for_declaration : Map < string , Node > = new Map ( ) ;
partly_hoisted : Array < ( Node | Node [ ] ) > = [ ] ;
partly_hoisted : Array < Node | Node [ ] > = [ ] ;
fully_hoisted : Array < ( Node | Node [ ] ) > = [ ] ;
fully_hoisted : Array < Node | Node [ ] > = [ ] ;
reactive_declarations : Array < {
reactive_declarations : Array < {
assignees : Set < string > ;
assignees : Set < string > ;
dependencies : Set < string > ;
dependencies : Set < string > ;
@ -116,43 +119,29 @@ export default class Component {
html : ast.html ,
html : ast.html ,
css : ast.css ,
css : ast.css ,
instance : ast.instance && JSON . parse ( JSON . stringify ( ast . instance ) ) ,
instance : ast.instance && JSON . parse ( JSON . stringify ( ast . instance ) ) ,
module : ast.module
module : ast.module ,
} ;
} ;
this . file =
this . file =
compile_options . filename &&
compile_options . filename &&
( typeof process !== 'undefined'
( typeof process !== 'undefined'
? compile_options . filename
? compile_options . filename . replace ( process . cwd ( ) , '' ) . replace ( /^[/\\]/ , '' )
. replace ( process . cwd ( ) , '' )
. replace ( /^[/\\]/ , '' )
: compile_options . filename ) ;
: compile_options . filename ) ;
this . locate = getLocator ( this . source , { offsetLine : 1 } ) ;
this . locate = getLocator ( this . source , { offsetLine : 1 } ) ;
// styles
// styles
this . stylesheet = new Stylesheet (
this . stylesheet = new Stylesheet ( source , ast , compile_options . filename , compile_options . dev ) ;
source ,
ast ,
compile_options . filename ,
compile_options . dev
) ;
this . stylesheet . validate ( this ) ;
this . stylesheet . validate ( this ) ;
this . component_options = process_component_options (
this . component_options = process_component_options ( this , this . ast . html . children ) ;
this ,
this . namespace = namespaces [ this . component_options . namespace ] || this . component_options . namespace ;
this . ast . html . children
) ;
this . namespace =
namespaces [ this . component_options . namespace ] ||
this . component_options . namespace ;
if ( compile_options . customElement ) {
if ( compile_options . customElement ) {
if (
if ( this . component_options . tag === undefined && compile_options . tag === undefined ) {
this . component_options . tag === undefined &&
const svelteOptions = ast . html . children . find ( ( child ) = > child . name === 'svelte:options' ) || {
compile_options . tag === undefined
start : 0 ,
) {
end : 0 ,
const svelteOptions = ast . html . children . find (
} ;
child = > child . name === 'svelte:options'
) || { start : 0 , end : 0 } ;
this . warn ( svelteOptions , {
this . warn ( svelteOptions , {
code : 'custom-element-no-tag' ,
code : 'custom-element-no-tag' ,
message : ` No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/> ` ,
message : ` No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. <svelte:options tag="my-thing"/>. To hide this warning, use <svelte:options tag={null}/> ` ,
@ -235,7 +224,10 @@ export default class Component {
const { compile_options , name } = this ;
const { compile_options , name } = this ;
const { format = 'esm' } = compile_options ;
const { format = 'esm' } = compile_options ;
const banner = ` ${ this . file ? ` ${ this . file } ` : ` ` } generated by Svelte v ${ '__VERSION__' } ` ;
const banner = ` ${ this . file ? ` ${ this . file } ` : ` ` } generated by Svelte v ${ __VERSION__ } ` ;
const legacy_dev_runtime = compile_options . dev && compile_options . version < 3.22 ;
const dev_runtime = compile_options . dev && compile_options . version >= 3.22 ;
const program : any = { type : 'Program' , body : result.js } ;
const program : any = { type : 'Program' , body : result.js } ;
@ -249,11 +241,11 @@ export default class Component {
} else {
} else {
let name = node . name . slice ( 1 ) ;
let name = node . name . slice ( 1 ) ;
if ( compile_options. dev ) {
if ( legacy_dev_runtime ) {
if ( internal_exports . has ( ` ${ name } _dev `) ) {
if ( internal_exports . has ( ` ${ name } _dev $legacy `) ) {
name += '_dev ';
name += '_dev $legacy ';
} else if ( internal_exports . has ( ` ${ name } Dev `) ) {
} else if ( internal_exports . has ( ` ${ name } Dev $legacy `) ) {
name += 'Dev ';
name += 'Dev $legacy ';
}
}
}
}
@ -261,23 +253,19 @@ export default class Component {
this . helpers . set ( name , alias ) ;
this . helpers . set ( name , alias ) ;
node . name = alias . name ;
node . name = alias . name ;
}
}
}
} else if ( node . name [ 0 ] !== '#' && ! is_valid ( node . name ) ) {
else if ( node . name [ 0 ] !== '#' && ! is_valid ( node . name ) ) {
// this hack allows x`foo.${bar}` where bar could be invalid
// this hack allows x`foo.${bar}` where bar could be invalid
const literal : Literal = { type : 'Literal' , value : node.name } ;
const literal : Literal = { type : 'Literal' , value : node.name } ;
if ( parent . type === 'Property' && key === 'key' ) {
if ( parent . type === 'Property' && key === 'key' ) {
parent . key = literal ;
parent . key = literal ;
}
} else if ( parent . type === 'MemberExpression' && key === 'property' ) {
else if ( parent . type === 'MemberExpression' && key === 'property' ) {
parent . property = literal ;
parent . property = literal ;
parent . computed = true ;
parent . computed = true ;
}
}
}
}
}
}
}
} ,
} ) ;
} ) ;
const referenced_globals = Array . from (
const referenced_globals = Array . from (
@ -297,33 +285,31 @@ export default class Component {
format ,
format ,
name ,
name ,
banner ,
banner ,
compile_options . sveltePath ,
compile_options . sveltePath + ( dev_runtime ? '/dev' : '' ) ,
imported_helpers ,
imported_helpers ,
referenced_globals ,
referenced_globals ,
this . imports ,
this . imports ,
this . vars
this . vars
. filter ( variable = > variable . module && variable . export_name )
. filter ( ( variable ) = > variable . module && variable . export_name )
. map ( variable = > ( {
. map ( ( variable ) = > ( {
name : variable.name ,
name : variable.name ,
as : variable . export_name ,
as : variable . export_name ,
} ) )
} ) )
) ;
) ;
css = compile_options . customElement
css = compile_options . customElement ? { code : null , map : null } : result . css ;
? { code : null , map : null }
: result . css ;
js = print ( program , {
js = print ( program , {
sourceMapSource : compile_options.filename
sourceMapSource : compile_options.filename ,
} ) ;
} ) ;
js . map . sources = [
js . map . sources = [
compile_options . filename ? get_relative_path ( compile_options . outputFilename || '' , compile_options . filename ) : null
compile_options . filename
? get_relative_path ( compile_options . outputFilename || '' , compile_options . filename )
: null ,
] ;
] ;
js . map . sourcesContent = [
js . map . sourcesContent = [ this . source ] ;
this . source
] ;
}
}
return {
return {
@ -332,8 +318,8 @@ export default class Component {
ast : this.original_ast ,
ast : this.original_ast ,
warnings : this.warnings ,
warnings : this.warnings ,
vars : this.vars
vars : this.vars
. filter ( v = > ! v . global && ! v . internal )
. filter ( ( v ) = > ! v . global && ! v . internal )
. map ( v = > ( {
. map ( ( v ) = > ( {
name : v.name ,
name : v.name ,
export_name : v.export_name || null ,
export_name : v.export_name || null ,
injected : v.injected || false ,
injected : v.injected || false ,
@ -378,17 +364,13 @@ export default class Component {
return ( name : string ) : Identifier = > {
return ( name : string ) : Identifier = > {
if ( test ) name = ` ${ name } $ ` ;
if ( test ) name = ` ${ name } $ ` ;
let alias = name ;
let alias = name ;
for (
for ( let i = 1 ; this . used_names . has ( alias ) || local_used_names . has ( alias ) ; alias = ` ${ name } _ ${ i ++ } ` ) ;
let i = 1 ;
this . used_names . has ( alias ) || local_used_names . has ( alias ) ;
alias = ` ${ name } _ ${ i ++ } `
) ;
local_used_names . add ( alias ) ;
local_used_names . add ( alias ) ;
this . globally_used_names . add ( alias ) ;
this . globally_used_names . add ( alias ) ;
return {
return {
type : 'Identifier' ,
type : 'Identifier' ,
name : alias
name : alias ,
} ;
} ;
} ;
} ;
}
}
@ -440,8 +422,7 @@ export default class Component {
end ,
end ,
pos : pos.start ,
pos : pos.start ,
filename : this.compile_options.filename ,
filename : this.compile_options.filename ,
toString : ( ) = >
toString : ( ) = > ` ${ warning . message } ( ${ start . line } : ${ start . column } ) \ n ${ frame } ` ,
` ${ warning . message } ( ${ start . line } : ${ start . column } ) \ n ${ frame } ` ,
} ) ;
} ) ;
}
}
@ -466,14 +447,17 @@ export default class Component {
}
}
if ( node . declaration ) {
if ( node . declaration ) {
if ( node . declaration . type === 'VariableDeclaration' ) {
if ( node . declaration . type === 'VariableDeclaration' ) {
node . declaration . declarations . forEach ( declarator = > {
node . declaration . declarations . forEach ( ( declarator ) = > {
extract_names ( declarator . id ) . forEach ( name = > {
extract_names ( declarator . id ) . forEach ( ( name ) = > {
const variable = this . var_lookup . get ( name ) ;
const variable = this . var_lookup . get ( name ) ;
variable . export_name = name ;
variable . export_name = name ;
if ( variable . writable && ! ( variable . referenced || variable . referenced_from_script || variable . subscribable ) ) {
if (
variable . writable &&
! ( variable . referenced || variable . referenced_from_script || variable . subscribable )
) {
this . warn ( declarator , {
this . warn ( declarator , {
code : ` unused-export-let ` ,
code : ` unused-export-let ` ,
message : ` ${ this . name . name } has unused export property ' ${ name } '. If it is for external reference only, please consider using \` export const ${ name } \` `
message : ` ${ this . name . name } has unused export property ' ${ name } '. If it is for external reference only, please consider using \` export const ${ name } \` ` ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;
@ -487,16 +471,19 @@ export default class Component {
return node . declaration ;
return node . declaration ;
} else {
} else {
node . specifiers . forEach ( specifier = > {
node . specifiers . forEach ( ( specifier ) = > {
const variable = this . var_lookup . get ( specifier . local . name ) ;
const variable = this . var_lookup . get ( specifier . local . name ) ;
if ( variable ) {
if ( variable ) {
variable . export_name = specifier . exported . name ;
variable . export_name = specifier . exported . name ;
if ( variable . writable && ! ( variable . referenced || variable . referenced_from_script || variable . subscribable ) ) {
if (
variable . writable &&
! ( variable . referenced || variable . referenced_from_script || variable . subscribable )
) {
this . warn ( specifier , {
this . warn ( specifier , {
code : ` unused-export-let ` ,
code : ` unused-export-let ` ,
message : ` ${ this . name . name } has unused export property ' ${ specifier . exported . name } '. If it is for external reference only, please consider using \` export const ${ specifier . exported . name } \` `
message : ` ${ this . name . name } has unused export property ' ${ specifier . exported . name } '. If it is for external reference only, please consider using \` export const ${ specifier . exported . name } \` ` ,
} ) ;
} ) ;
}
}
}
}
@ -510,13 +497,12 @@ export default class Component {
extract_javascript ( script ) {
extract_javascript ( script ) {
if ( ! script ) return null ;
if ( ! script ) return null ;
return script . content . body . filter ( node = > {
return script . content . body . filter ( ( node ) = > {
if ( ! node ) return false ;
if ( ! node ) return false ;
if ( this . hoistable_nodes . has ( node ) ) return false ;
if ( this . hoistable_nodes . has ( node ) ) return false ;
if ( this . reactive_declaration_nodes . has ( node ) ) return false ;
if ( this . reactive_declaration_nodes . has ( node ) ) return false ;
if ( node . type === 'ImportDeclaration' ) return false ;
if ( node . type === 'ImportDeclaration' ) return false ;
if ( node . type === 'ExportDeclaration' && node . specifiers . length > 0 )
if ( node . type === 'ExportDeclaration' && node . specifiers . length > 0 ) return false ;
return false ;
return true ;
return true ;
} ) ;
} ) ;
}
}
@ -554,7 +540,7 @@ export default class Component {
name ,
name ,
module : true ,
module : true ,
hoistable : true ,
hoistable : true ,
writable
writable ,
} ) ;
} ) ;
} ) ;
} ) ;
@ -568,7 +554,7 @@ export default class Component {
this . add_var ( {
this . add_var ( {
name ,
name ,
global : true ,
global : true ,
hoistable : true
hoistable : true ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;
@ -598,7 +584,7 @@ export default class Component {
if ( ! script ) return ;
if ( ! script ) return ;
// inject vars for reactive declarations
// inject vars for reactive declarations
script . content . body . forEach ( node = > {
script . content . body . forEach ( ( node ) = > {
if ( node . type !== 'LabeledStatement' ) return ;
if ( node . type !== 'LabeledStatement' ) return ;
if ( node . body . type !== 'ExpressionStatement' ) return ;
if ( node . body . type !== 'ExpressionStatement' ) return ;
@ -606,16 +592,14 @@ export default class Component {
if ( expression . type !== 'AssignmentExpression' ) return ;
if ( expression . type !== 'AssignmentExpression' ) return ;
if ( expression . left . type === 'MemberExpression' ) return ;
if ( expression . left . type === 'MemberExpression' ) return ;
extract_names ( expression . left ) . forEach ( name = > {
extract_names ( expression . left ) . forEach ( ( name ) = > {
if ( ! this . var_lookup . has ( name ) && name [ 0 ] !== '$' ) {
if ( ! this . var_lookup . has ( name ) && name [ 0 ] !== '$' ) {
this . injected_reactive_declaration_vars . add ( name ) ;
this . injected_reactive_declaration_vars . add ( name ) ;
}
}
} ) ;
} ) ;
} ) ;
} ) ;
const { scope : instance_scope , map , globals } = create_scopes (
const { scope : instance_scope , map , globals } = create_scopes ( script . content ) ;
script . content
) ;
this . instance_scope = instance_scope ;
this . instance_scope = instance_scope ;
this . instance_scope_map = map ;
this . instance_scope_map = map ;
@ -632,7 +616,7 @@ export default class Component {
this . add_var ( {
this . add_var ( {
name ,
name ,
initialised : instance_scope.initialised_declarations.has ( name ) ,
initialised : instance_scope.initialised_declarations.has ( name ) ,
writable
writable ,
} ) ;
} ) ;
this . node_for_declaration . set ( name , node ) ;
this . node_for_declaration . set ( name , node ) ;
@ -658,7 +642,7 @@ export default class Component {
if ( name === '$' || name [ 1 ] === '$' ) {
if ( name === '$' || name [ 1 ] === '$' ) {
this . error ( node as any , {
this . error ( node as any , {
code : 'illegal-global' ,
code : 'illegal-global' ,
message : ` ${ name } is an illegal variable name `
message : ` ${ name } is an illegal variable name ` ,
} ) ;
} ) ;
}
}
@ -680,7 +664,7 @@ export default class Component {
this . add_var ( {
this . add_var ( {
name ,
name ,
global : true ,
global : true ,
hoistable : true
hoistable : true ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;
@ -744,7 +728,11 @@ export default class Component {
leave ( node : Node ) {
leave ( node : Node ) {
// do it on leave, to prevent infinite loop
// do it on leave, to prevent infinite loop
if ( component . compile_options . dev && component . compile_options . loopGuardTimeout > 0 ) {
if ( component . compile_options . dev && component . compile_options . loopGuardTimeout > 0 ) {
const to_replace_for_loop_protect = component . loop_protect ( node , scope , component . compile_options . loopGuardTimeout ) ;
const to_replace_for_loop_protect = component . loop_protect (
node ,
scope ,
component . compile_options . loopGuardTimeout
) ;
if ( to_replace_for_loop_protect ) {
if ( to_replace_for_loop_protect ) {
this . replace ( to_replace_for_loop_protect ) ;
this . replace ( to_replace_for_loop_protect ) ;
scope_updated = true ;
scope_updated = true ;
@ -796,13 +784,9 @@ export default class Component {
const deep = assignee . type === 'MemberExpression' ;
const deep = assignee . type === 'MemberExpression' ;
names . forEach ( name = > {
names . forEach ( ( name ) = > {
const scope_owner = scope . find_owner ( name ) ;
const scope_owner = scope . find_owner ( name ) ;
if (
if ( scope_owner !== null ? scope_owner === instance_scope : module_scope && module _scope.has ( name ) ) {
scope_owner !== null
? scope_owner === instance_scope
: module _scope && module _scope.has ( name )
) {
const variable = component . var_lookup . get ( name ) ;
const variable = component . var_lookup . get ( name ) ;
variable [ deep ? 'mutated' : 'reassigned' ] = true ;
variable [ deep ? 'mutated' : 'reassigned' ] = true ;
}
}
@ -827,11 +811,7 @@ export default class Component {
}
}
warn_on_undefined_store_value_references ( node , parent , scope ) {
warn_on_undefined_store_value_references ( node , parent , scope ) {
if (
if ( node . type === 'LabeledStatement' && node . label . name === '$' && parent . type !== 'Program' ) {
node . type === 'LabeledStatement' &&
node . label . name === '$' &&
parent . type !== 'Program'
) {
this . warn ( node as any , {
this . warn ( node as any , {
code : 'non-top-level-reactive-declaration' ,
code : 'non-top-level-reactive-declaration' ,
message : '$: has no effect outside of the top-level' ,
message : '$: has no effect outside of the top-level' ,
@ -849,9 +829,7 @@ export default class Component {
}
}
loop_protect ( node , scope : Scope , timeout : number ) : Node | null {
loop_protect ( node , scope : Scope , timeout : number ) : Node | null {
if ( node . type === 'WhileStatement' ||
if ( node . type === 'WhileStatement' || node . type === 'ForStatement' || node . type === 'DoWhileStatement' ) {
node . type === 'ForStatement' ||
node . type === 'DoWhileStatement' ) {
const guard = this . get_unique_name ( 'guard' , scope ) ;
const guard = this . get_unique_name ( 'guard' , scope ) ;
this . used_names . add ( guard . name ) ;
this . used_names . add ( guard . name ) ;
@ -869,10 +847,7 @@ export default class Component {
return {
return {
type : 'BlockStatement' ,
type : 'BlockStatement' ,
body : [
body : [ before [ 0 ] , node ] ,
before [ 0 ] ,
node ,
] ,
} ;
} ;
}
}
return null ;
return null ;
@ -897,11 +872,11 @@ export default class Component {
if ( node . type === 'VariableDeclaration' ) {
if ( node . type === 'VariableDeclaration' ) {
if ( node . kind === 'var' || scope === instance_scope ) {
if ( node . kind === 'var' || scope === instance_scope ) {
node . declarations . forEach ( declarator = > {
node . declarations . forEach ( ( declarator ) = > {
if ( declarator . id . type !== 'Identifier' ) {
if ( declarator . id . type !== 'Identifier' ) {
const inserts = [ ] ;
const inserts = [ ] ;
extract_names ( declarator . id ) . forEach ( name = > {
extract_names ( declarator . id ) . forEach ( ( name ) = > {
const variable = component . var_lookup . get ( name ) ;
const variable = component . var_lookup . get ( name ) ;
if ( variable . export_name ) {
if ( variable . export_name ) {
@ -928,15 +903,14 @@ export default class Component {
const variable = component . var_lookup . get ( name ) ;
const variable = component . var_lookup . get ( name ) ;
if ( variable . export_name && variable . writable ) {
if ( variable . export_name && variable . writable ) {
const insert = variable . subscribable
const insert = variable . subscribable ? get_insert ( variable ) : null ;
? get_insert ( variable )
: null ;
parent [ key ] . splice ( index + 1 , 0 , insert ) ;
parent [ key ] . splice ( index + 1 , 0 , insert ) ;
declarator . id = {
declarator . id = {
type : 'ObjectPattern' ,
type : 'ObjectPattern' ,
properties : [ {
properties : [
{
type : 'Property' ,
type : 'Property' ,
method : false ,
method : false ,
shorthand : false ,
shorthand : false ,
@ -947,10 +921,11 @@ export default class Component {
? {
? {
type : 'AssignmentPattern' ,
type : 'AssignmentPattern' ,
left : declarator.id ,
left : declarator.id ,
right : declarator.init
right : declarator.init ,
}
}
: declarator . id
: declarator . id ,
} ]
} ,
] ,
} ;
} ;
declarator . init = x ` $ $ props ` ;
declarator . init = x ` $ $ props ` ;
@ -981,12 +956,7 @@ export default class Component {
// reference instance variables other than other
// reference instance variables other than other
// hoistable functions. TODO others?
// hoistable functions. TODO others?
const {
const { hoistable_nodes , var_lookup , injected_reactive_declaration_vars , imports } = this ;
hoistable_nodes ,
var_lookup ,
injected_reactive_declaration_vars ,
imports ,
} = this ;
const top_level_function_declarations = new Map ( ) ;
const top_level_function_declarations = new Map ( ) ;
@ -996,7 +966,7 @@ export default class Component {
const node = body [ i ] ;
const node = body [ i ] ;
if ( node . type === 'VariableDeclaration' ) {
if ( node . type === 'VariableDeclaration' ) {
const all_hoistable = node . declarations . every ( d = > {
const all_hoistable = node . declarations . every ( ( d ) = > {
if ( ! d . init ) return false ;
if ( ! d . init ) return false ;
if ( d . init . type !== 'Literal' ) return false ;
if ( d . init . type !== 'Literal' ) return false ;
@ -1011,18 +981,13 @@ export default class Component {
if ( v . export_name ) return false ;
if ( v . export_name ) return false ;
if ( this . var_lookup . get ( name ) . reassigned ) return false ;
if ( this . var_lookup . get ( name ) . reassigned ) return false ;
if (
if ( this . vars . find ( ( variable ) = > variable . name === name && variable . module ) ) return false ;
this . vars . find (
variable = > variable . name === name && variable . module
)
)
return false ;
return true ;
return true ;
} ) ;
} ) ;
if ( all_hoistable ) {
if ( all_hoistable ) {
node . declarations . forEach ( d = > {
node . declarations . forEach ( ( d ) = > {
const variable = this . var_lookup . get ( ( d . id as Identifier ) . name ) ;
const variable = this . var_lookup . get ( ( d . id as Identifier ) . name ) ;
variable . hoistable = true ;
variable . hoistable = true ;
} ) ;
} ) ;
@ -1050,7 +1015,7 @@ export default class Component {
const checked = new Set ( ) ;
const checked = new Set ( ) ;
const walking = new Set ( ) ;
const walking = new Set ( ) ;
const is_hoistable = fn_declaration = > {
const is_hoistable = ( fn_declaration ) = > {
if ( fn_declaration . type === 'ExportNamedDeclaration' ) {
if ( fn_declaration . type === 'ExportNamedDeclaration' ) {
fn_declaration = fn_declaration . declaration ;
fn_declaration = fn_declaration . declaration ;
}
}
@ -1090,9 +1055,7 @@ export default class Component {
if ( variable . hoistable ) return ;
if ( variable . hoistable ) return ;
if ( top_level_function_declarations . has ( name ) ) {
if ( top_level_function_declarations . has ( name ) ) {
const other_declaration = top_level_function_declarations . get (
const other_declaration = top_level_function_declarations . get ( name ) ;
name
) ;
if ( walking . has ( other_declaration ) ) {
if ( walking . has ( other_declaration ) ) {
hoistable = false ;
hoistable = false ;
@ -1152,7 +1115,7 @@ export default class Component {
const unsorted_reactive_declarations = [ ] ;
const unsorted_reactive_declarations = [ ] ;
this . ast . instance . content . body . forEach ( node = > {
this . ast . instance . content . body . forEach ( ( node ) = > {
if ( node . type === 'LabeledStatement' && node . label . name === '$' ) {
if ( node . type === 'LabeledStatement' && node . label . name === '$' ) {
this . reactive_declaration_nodes . add ( node ) ;
this . reactive_declaration_nodes . add ( node ) ;
@ -1172,7 +1135,7 @@ export default class Component {
if ( node . type === 'AssignmentExpression' ) {
if ( node . type === 'AssignmentExpression' ) {
const left = get_object ( node . left ) ;
const left = get_object ( node . left ) ;
extract_identifiers ( left ) . forEach ( node = > {
extract_identifiers ( left ) . forEach ( ( node ) = > {
assignee_nodes . add ( node ) ;
assignee_nodes . add ( node ) ;
assignees . add ( node . name ) ;
assignees . add ( node . name ) ;
} ) ;
} ) ;
@ -1190,12 +1153,8 @@ export default class Component {
const owner = scope . find_owner ( name ) ;
const owner = scope . find_owner ( name ) ;
const variable = component . var_lookup . get ( name ) ;
const variable = component . var_lookup . get ( name ) ;
if ( variable ) variable . is_reactive_dependency = true ;
if ( variable ) variable . is_reactive_dependency = true ;
const is_writable_or_mutated =
const is_writable_or_mutated = variable && ( variable . writable || variable . mutated ) ;
variable && ( variable . writable || variable . mutated ) ;
if ( ( ! owner || owner === component . instance_scope ) && ( name [ 0 ] === '$' || is_writable_or_mutated ) ) {
if (
( ! owner || owner === component . instance_scope ) &&
( name [ 0 ] === '$' || is_writable_or_mutated )
) {
dependencies . add ( name ) ;
dependencies . add ( name ) ;
}
}
}
}
@ -1225,8 +1184,8 @@ export default class Component {
const lookup = new Map ( ) ;
const lookup = new Map ( ) ;
unsorted_reactive_declarations . forEach ( declaration = > {
unsorted_reactive_declarations . forEach ( ( declaration ) = > {
declaration . assignees . forEach ( name = > {
declaration . assignees . forEach ( ( name ) = > {
if ( ! lookup . has ( name ) ) {
if ( ! lookup . has ( name ) ) {
lookup . set ( name , [ ] ) ;
lookup . set ( name , [ ] ) ;
}
}
@ -1237,34 +1196,35 @@ export default class Component {
} ) ;
} ) ;
} ) ;
} ) ;
const cycle = check_graph_for_cycles ( unsorted_reactive_declarations . reduce ( ( acc , declaration ) = > {
const cycle = check_graph_for_cycles (
declaration . assignees . forEach ( v = > {
unsorted_reactive_declarations . reduce ( ( acc , declaration ) = > {
declaration . dependencies . forEach ( w = > {
declaration . assignees . forEach ( ( v ) = > {
declaration . dependencies . forEach ( ( w ) = > {
if ( ! declaration . assignees . has ( w ) ) {
if ( ! declaration . assignees . has ( w ) ) {
acc . push ( [ v , w ] ) ;
acc . push ( [ v , w ] ) ;
}
}
} ) ;
} ) ;
} ) ;
} ) ;
return acc ;
return acc ;
} , [ ] ) ) ;
} , [ ] )
) ;
if ( cycle && cycle . length ) {
if ( cycle && cycle . length ) {
const declarationList = lookup . get ( cycle [ 0 ] ) ;
const declarationList = lookup . get ( cycle [ 0 ] ) ;
const declaration = declarationList [ 0 ] ;
const declaration = declarationList [ 0 ] ;
this . error ( declaration . node , {
this . error ( declaration . node , {
code : 'cyclical-reactive-declaration' ,
code : 'cyclical-reactive-declaration' ,
message : ` Cyclical dependency detected: ${ cycle . join ( ' → ' ) } `
message : ` Cyclical dependency detected: ${ cycle . join ( ' → ' ) } ` ,
} ) ;
} ) ;
}
}
const add_declaration = declaration = > {
const add_declaration = ( declaration ) = > {
if ( this . reactive_declarations . includes ( declaration ) ) return ;
if ( this . reactive_declarations . includes ( declaration ) ) return ;
declaration . dependencies . forEach ( name = > {
declaration . dependencies . forEach ( ( name ) = > {
if ( declaration . assignees . has ( name ) ) return ;
if ( declaration . assignees . has ( name ) ) return ;
const earlier_declarations = lookup . get ( name ) ;
const earlier_declarations = lookup . get ( name ) ;
if ( earlier_declarations )
if ( earlier_declarations ) earlier_declarations . forEach ( add_declaration ) ;
earlier_declarations . forEach ( add_declaration ) ;
} ) ;
} ) ;
this . reactive_declarations . push ( declaration ) ;
this . reactive_declarations . push ( declaration ) ;
@ -1275,10 +1235,10 @@ export default class Component {
warn_if_undefined ( name : string , node , template_scope : TemplateScope ) {
warn_if_undefined ( name : string , node , template_scope : TemplateScope ) {
if ( name [ 0 ] === '$' ) {
if ( name [ 0 ] === '$' ) {
if ( name === '$' || name [ 1 ] === '$' && ! is_reserved_keyword ( name ) ) {
if ( name === '$' || ( name [ 1 ] === '$' && ! is_reserved_keyword ( name ) ) ) {
this . error ( node , {
this . error ( node , {
code : 'illegal-global' ,
code : 'illegal-global' ,
message : ` ${ name } is an illegal variable name `
message : ` ${ name } is an illegal variable name ` ,
} ) ;
} ) ;
}
}
@ -1294,8 +1254,7 @@ export default class Component {
if ( globals . has ( name ) && node . type !== 'InlineComponent' ) return ;
if ( globals . has ( name ) && node . type !== 'InlineComponent' ) return ;
let message = ` ' ${ name } ' is not defined ` ;
let message = ` ' ${ name } ' is not defined ` ;
if ( ! this . ast . instance )
if ( ! this . ast . instance ) message += ` . Consider adding a <script> block with 'export let ${ name } ' to declare a prop ` ;
message += ` . Consider adding a <script> block with 'export let ${ name } ' to declare a prop ` ;
this . warn ( node , {
this . warn ( node , {
code : 'missing-declaration' ,
code : 'missing-declaration' ,
@ -1325,7 +1284,7 @@ function process_component_options(component: Component, nodes) {
preserveWhitespace : ! ! component . compile_options . preserveWhitespace ,
preserveWhitespace : ! ! component . compile_options . preserveWhitespace ,
} ;
} ;
const node = nodes . find ( node = > node . name === 'svelte:options' ) ;
const node = nodes . find ( ( node ) = > node . name === 'svelte:options' ) ;
function get_value ( attribute , code , message ) {
function get_value ( attribute , code , message ) {
const { value } = attribute ;
const { value } = attribute ;
@ -1347,7 +1306,7 @@ function process_component_options(component: Component, nodes) {
}
}
if ( node ) {
if ( node ) {
node . attributes . forEach ( attribute = > {
node . attributes . forEach ( ( attribute ) = > {
if ( attribute . type === 'Attribute' ) {
if ( attribute . type === 'Attribute' ) {
const { name } = attribute ;
const { name } = attribute ;
@ -1357,8 +1316,7 @@ function process_component_options(component: Component, nodes) {
const message = ` 'tag' must be a string literal ` ;
const message = ` 'tag' must be a string literal ` ;
const tag = get_value ( attribute , code , message ) ;
const tag = get_value ( attribute , code , message ) ;
if ( typeof tag !== 'string' && tag !== null )
if ( typeof tag !== 'string' && tag !== null ) component . error ( attribute , { code , message } ) ;
component . error ( attribute , { code , message } ) ;
if ( tag && ! /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/ . test ( tag ) ) {
if ( tag && ! /^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/ . test ( tag ) ) {
component . error ( attribute , {
component . error ( attribute , {
@ -1370,7 +1328,7 @@ function process_component_options(component: Component, nodes) {
if ( tag && ! component . compile_options . customElement ) {
if ( tag && ! component . compile_options . customElement ) {
component . warn ( attribute , {
component . warn ( attribute , {
code : 'missing-custom-element-compile-options' ,
code : 'missing-custom-element-compile-options' ,
message : ` The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option? `
message : ` The 'tag' option is used when generating a custom element. Did you forget the 'customElement: true' compile option? ` ,
} ) ;
} ) ;
}
}
@ -1383,8 +1341,7 @@ function process_component_options(component: Component, nodes) {
const message = ` The 'namespace' attribute must be a string literal representing a valid namespace ` ;
const message = ` The 'namespace' attribute must be a string literal representing a valid namespace ` ;
const ns = get_value ( attribute , code , message ) ;
const ns = get_value ( attribute , code , message ) ;
if ( typeof ns !== 'string' )
if ( typeof ns !== 'string' ) component . error ( attribute , { code , message } ) ;
component . error ( attribute , { code , message } ) ;
if ( valid_namespaces . indexOf ( ns ) === - 1 ) {
if ( valid_namespaces . indexOf ( ns ) === - 1 ) {
const match = fuzzymatch ( ns , valid_namespaces ) ;
const match = fuzzymatch ( ns , valid_namespaces ) ;
@ -1412,8 +1369,7 @@ function process_component_options(component: Component, nodes) {
const message = ` ${ name } attribute must be true or false ` ;
const message = ` ${ name } attribute must be true or false ` ;
const value = get_value ( attribute , code , message ) ;
const value = get_value ( attribute , code , message ) ;
if ( typeof value !== 'boolean' )
if ( typeof value !== 'boolean' ) component . error ( attribute , { code , message } ) ;
component . error ( attribute , { code , message } ) ;
component_options [ name ] = value ;
component_options [ name ] = value ;
break ;
break ;