@ -6,7 +6,12 @@ import Stats from '../Stats';
import { globals , reserved } from '../utils/names' ;
import { globals , reserved } 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 { create_scopes , extract_names , Scope , extract_identifiers } from './utils/scope' ;
import {
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' ;
@ -41,7 +46,13 @@ childKeys.EachBlock = childKeys.IfBlock = ['children', 'else'];
childKeys . Attribute = [ 'value' ] ;
childKeys . Attribute = [ 'value' ] ;
childKeys . ExportNamedDeclaration = [ 'declaration' , 'specifiers' ] ;
childKeys . ExportNamedDeclaration = [ 'declaration' , 'specifiers' ] ;
function remove_node ( code : MagicString , start : number , end : number , body : Node , node : Node ) {
function remove_node (
code : MagicString ,
start : number ,
end : number ,
body : Node ,
node : Node
) {
const i = body . indexOf ( node ) ;
const i = body . indexOf ( node ) ;
if ( i === - 1 ) throw new Error ( 'node not in list' ) ;
if ( i === - 1 ) throw new Error ( 'node not in list' ) ;
@ -101,7 +112,12 @@ export default class Component {
node_for_declaration : Map < string , Node > = new Map ( ) ;
node_for_declaration : Map < string , Node > = new Map ( ) ;
partly_hoisted : string [ ] = [ ] ;
partly_hoisted : string [ ] = [ ] ;
fully_hoisted : string [ ] = [ ] ;
fully_hoisted : string [ ] = [ ] ;
reactive_declarations : Array < { assignees : Set < string > ; dependencies : Set < string > ; node : Node ; declaration : Node } > = [ ] ;
reactive_declarations : Array < {
assignees : Set < string > ;
dependencies : Set < string > ;
node : Node ;
declaration : Node ;
} > = [ ] ;
reactive_declaration_nodes : Set < Node > = new Set ( ) ;
reactive_declaration_nodes : Set < Node > = new Set ( ) ;
has_reactive_assignments = false ;
has_reactive_assignments = false ;
injected_reactive_declaration_vars : Set < string > = new Set ( ) ;
injected_reactive_declaration_vars : Set < string > = new Set ( ) ;
@ -114,7 +130,10 @@ export default class Component {
locate : ( c : number ) = > { line : number ; column : number } ;
locate : ( c : number ) = > { line : number ; column : number } ;
// TODO this does the same as component.locate! remove one or the other
// TODO this does the same as component.locate! remove one or the other
locator : ( search : number , startIndex? : number ) = > {
locator : (
search : number ,
startIndex? : number
) = > {
line : number ;
line : number ;
column : number ;
column : number ;
} ;
} ;
@ -144,27 +163,46 @@ export default class Component {
this . source = source ;
this . source = source ;
this . compile_options = compile_options ;
this . compile_options = compile_options ;
this . file = compile_options . filename && (
this . file =
compile_options . filename &&
// eslint-disable-next-line no-useless-escape
// eslint-disable-next-line no-useless-escape
typeof process !== 'undefined' ? compile_options . filename . replace ( process . cwd ( ) , '' ) . replace ( /^[\/\\]/ , '' ) : compile_options . filename
( typeof process !== 'undefined'
) ;
? compile_options . filename
. replace ( process . cwd ( ) , '' )
. replace ( /^[\/\\]/ , '' )
: compile_options . filename ) ;
this . locate = getLocator ( this . source ) ;
this . locate = getLocator ( this . source ) ;
this . code = new MagicString ( source ) ;
this . code = new MagicString ( source ) ;
// styles
// styles
this . stylesheet = new Stylesheet ( source , ast , compile_options . filename , compile_options . dev ) ;
this . stylesheet = new Stylesheet (
source ,
ast ,
compile_options . filename ,
compile_options . dev
) ;
this . stylesheet . validate ( this ) ;
this . stylesheet . validate ( this ) ;
this . component_options = process_component_options ( this , this . ast . html . children ) ;
this . component_options = process_component_options (
this . namespace = namespaces [ this . component_options . namespace ] || this . component_options . namespace ;
this ,
this . ast . html . children
) ;
this . namespace =
namespaces [ this . component_options . namespace ] ||
this . component_options . namespace ;
if ( compile_options . customElement ) {
if ( compile_options . customElement ) {
if ( this . component_options . tag === undefined && compile_options . tag === undefined ) {
if (
const svelteOptions = ast . html . children . find ( child = > child . name === 'svelte:options' ) || { start : 0 , end : 0 } ;
this . component_options . tag === undefined &&
compile_options . tag === undefined
) {
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}/> ` ,
} ) ;
} ) ;
}
}
this . tag = this . component_options . tag || compile_options . tag ;
this . tag = this . component_options . tag || compile_options . tag ;
@ -199,7 +237,7 @@ export default class Component {
this . add_var ( {
this . add_var ( {
name ,
name ,
injected : true ,
injected : true ,
referenced : true
referenced : true ,
} ) ;
} ) ;
} else if ( name [ 0 ] === '$' ) {
} else if ( name [ 0 ] === '$' ) {
this . add_var ( {
this . add_var ( {
@ -207,7 +245,7 @@ export default class Component {
injected : true ,
injected : true ,
referenced : true ,
referenced : true ,
mutated : true ,
mutated : true ,
writable : true
writable : true ,
} ) ;
} ) ;
const subscribable_name = name . slice ( 1 ) ;
const subscribable_name = name . slice ( 1 ) ;
@ -257,35 +295,52 @@ 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__' } * / ` ;
result = result
result = result
. replace ( /__svelte:self__/g , this . name )
. replace ( /__svelte:self__/g , this . name )
. replace ( compile_options . generate === 'ssr' ? /(@+|#+)(\w*(?:-\w*)?)/g : /(@+)(\w*(?:-\w*)?)/g , ( _match : string , sigil : string , name : string ) = > {
. replace (
if ( sigil === '@' ) {
compile_options . generate === 'ssr'
if ( name [ 0 ] === '_' ) {
? /(@+|#+)(\w*(?:-\w*)?)/g
return this . global ( name . slice ( 1 ) ) ;
: /(@+)(\w*(?:-\w*)?)/g ,
}
( _match : string , sigil : string , name : string ) = > {
if ( sigil === '@' ) {
if ( name [ 0 ] === '_' ) {
return this . global ( name . slice ( 1 ) ) ;
}
if ( ! internal_exports . has ( name ) ) {
if ( ! internal_exports . has ( name ) ) {
throw new Error ( ` compiler error: this shouldn't happen! generated code is trying to use inexistent internal ' ${ name } ' ` ) ;
throw new Error (
}
` compiler error: this shouldn't happen! generated code is trying to use inexistent internal ' ${ name } ' `
) ;
}
if ( compile_options . dev ) {
if ( internal_exports . has ( ` ${ name } _dev ` ) ) name = ` ${ name } _dev ` ;
else if ( internal_exports . has ( ` ${ name } Dev ` ) )
name = ` ${ name } Dev ` ;
}
if ( compile_options . dev && internal_exports . has ( ` ${ name } Dev ` ) ) {
return this . helper ( name ) ;
name = ` ${ name } Dev ` ;
}
}
return this . helper ( name ) ;
return sigil . slice ( 1 ) + name ;
}
}
) ;
return sigil . slice ( 1 ) + name ;
const referenced_globals = Array . from (
} ) ;
this . globals ,
( [ name , alias ] ) = > name !== alias && { name , alias }
const referenced_globals = Array . from ( this . globals , ( [ name , alias ] ) = > name !== alias && ( { name , alias } ) ) . filter ( Boolean ) ;
) . filter ( Boolean ) ;
if ( referenced_globals . length ) {
if ( referenced_globals . length ) {
this . helper ( 'globals' ) ;
this . helper ( 'globals' ) ;
}
}
const imported_helpers = Array . from ( this . helpers , ( [ name , alias ] ) = > ( { name , alias } ) ) ;
const imported_helpers = Array . from ( this . helpers , ( [ name , alias ] ) = > ( {
name ,
alias ,
} ) ) ;
const module = create_module (
const module = create_module (
result ,
result ,
@ -296,10 +351,12 @@ export default class Component {
imported_helpers ,
imported_helpers ,
referenced_globals ,
referenced_globals ,
this . imports ,
this . imports ,
this . vars . filter ( variable = > variable . module && variable . export_name ) . map ( variable = > ( {
this . vars
name : variable.name ,
. filter ( variable = > variable . module && variable . export_name )
as : variable . export_name
. map ( variable = > ( {
} ) ) ,
name : variable.name ,
as : variable . export_name ,
} ) ) ,
this . source
this . source
) ;
) ;
@ -343,16 +400,16 @@ export default class Component {
add_string ( final_chunk ) ;
add_string ( final_chunk ) ;
css = compile_options . customElement ?
css = compile_options . customElement
{ code : null , map : null } :
? { code : null , map : null }
this . stylesheet . render ( compile_options . cssOutputFilename , true ) ;
: this . stylesheet . render ( compile_options . cssOutputFilename , true ) ;
js = {
js = {
code : compiled.toString ( ) ,
code : compiled.toString ( ) ,
map : compiled.generateMap ( {
map : compiled.generateMap ( {
includeContent : true ,
includeContent : true ,
file : compile_options.outputFilename ,
file : compile_options.outputFilename ,
} )
} ) ,
} ;
} ;
}
}
@ -361,17 +418,19 @@ export default class Component {
css ,
css ,
ast : this.ast ,
ast : this.ast ,
warnings : this.warnings ,
warnings : this.warnings ,
vars : this.vars.filter ( v = > ! v . global && ! v . internal ) . map ( v = > ( {
vars : this.vars
name : v.name ,
. filter ( v = > ! v . global && ! v . internal )
export_name : v.export_name || null ,
. map ( v = > ( {
injected : v.injected || false ,
name : v.name ,
module : v.module || false ,
export_name : v.export_name || null ,
mutated : v.mutated || false ,
injected : v.injected || false ,
reassigned : v.reassigned || false ,
module : v.module || false ,
referenced : v.referenced || false ,
mutated : v.mutated || false ,
writable : v.writable || false
reassigned : v.reassigned || false ,
} ) ) ,
referenced : v.referenced || false ,
stats : this.stats.render ( )
writable : v.writable || false ,
} ) ) ,
stats : this.stats.render ( ) ,
} ;
} ;
}
}
@ -406,8 +465,7 @@ export default class Component {
let alias = name ;
let alias = name ;
for (
for (
let i = 1 ;
let i = 1 ;
this . used_names . has ( alias ) ||
this . used_names . has ( alias ) || local_used_names . has ( alias ) ;
local_used_names . has ( alias ) ;
alias = ` ${ name } _ ${ i ++ } `
alias = ` ${ name } _ ${ i ++ } `
) ;
) ;
local_used_names . add ( alias ) ;
local_used_names . add ( alias ) ;
@ -432,7 +490,7 @@ export default class Component {
source : this.source ,
source : this.source ,
start : pos.start ,
start : pos.start ,
end : pos.end ,
end : pos.end ,
filename : this.compile_options.filename
filename : this.compile_options.filename ,
} ) ;
} ) ;
}
}
@ -467,7 +525,8 @@ export default class Component {
end ,
end ,
pos : pos.start ,
pos : pos.start ,
filename : this.compile_options.filename ,
filename : this.compile_options.filename ,
toString : ( ) = > ` ${ warning . message } ( ${ start . line + 1 } : ${ start . column } ) \ n ${ frame } ` ,
toString : ( ) = >
` ${ warning . message } ( ${ start . line + 1 } : ${ start . column } ) \ n ${ frame } ` ,
} ) ;
} ) ;
}
}
@ -490,7 +549,7 @@ export default class Component {
if ( node . type === 'ExportDefaultDeclaration' ) {
if ( node . type === 'ExportDefaultDeclaration' ) {
this . error ( node , {
this . error ( node , {
code : ` default-export ` ,
code : ` default-export ` ,
message : ` A component cannot have a default export `
message : ` A component cannot have a default export ` ,
} ) ;
} ) ;
}
}
@ -498,7 +557,7 @@ export default class Component {
if ( node . source ) {
if ( node . source ) {
this . error ( node , {
this . error ( node , {
code : ` not-implemented ` ,
code : ` not-implemented ` ,
message : ` A component currently cannot have an export ... from `
message : ` A component currently cannot have an export ... from ` ,
} ) ;
} ) ;
}
}
if ( node . declaration ) {
if ( node . declaration ) {
@ -538,7 +597,8 @@ export default class Component {
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 ) return false ;
if ( node . type === 'ExportDeclaration' && node . specifiers . length > 0 )
return false ;
return true ;
return true ;
} ) ;
} ) ;
@ -551,8 +611,11 @@ export default class Component {
let result = '' ;
let result = '' ;
script . content . body . forEach ( ( node ) = > {
script . content . body . forEach ( node = > {
if ( this . hoistable_nodes . has ( node ) || this . reactive_declaration_nodes . has ( node ) ) {
if (
this . hoistable_nodes . has ( node ) ||
this . reactive_declaration_nodes . has ( node )
) {
if ( a !== b ) result += ` [✂ ${ a } - ${ b } ✂] ` ;
if ( a !== b ) result += ` [✂ ${ a } - ${ b } ✂] ` ;
a = node . end ;
a = node . end ;
}
}
@ -580,10 +643,10 @@ export default class Component {
if ( node . type === 'LabeledStatement' && node . label . name === '$' ) {
if ( node . type === 'LabeledStatement' && node . label . name === '$' ) {
component . warn ( node , {
component . warn ( node , {
code : 'module-script-reactive-declaration' ,
code : 'module-script-reactive-declaration' ,
message : '$: has no effect in a module script'
message : '$: has no effect in a module script' ,
} ) ;
} ) ;
}
}
}
} ,
} ) ;
} ) ;
this . add_sourcemap_locations ( script . content ) ;
this . add_sourcemap_locations ( script . content ) ;
@ -595,7 +658,7 @@ export default class Component {
if ( name [ 0 ] === '$' ) {
if ( name [ 0 ] === '$' ) {
this . error ( node , {
this . error ( node , {
code : 'illegal-declaration' ,
code : 'illegal-declaration' ,
message : ` The $ prefix is reserved, and cannot be used for variable and import names `
message : ` The $ prefix is reserved, and cannot be used for variable and import names ` ,
} ) ;
} ) ;
}
}
@ -603,7 +666,7 @@ export default class Component {
name ,
name ,
module : true ,
module : true ,
hoistable : true ,
hoistable : true ,
writable : node.kind === 'var' || node . kind === 'let'
writable : node.kind === 'var' || node . kind === 'let' ,
} ) ;
} ) ;
} ) ;
} ) ;
@ -611,12 +674,12 @@ export default class Component {
if ( name [ 0 ] === '$' ) {
if ( name [ 0 ] === '$' ) {
this . error ( node , {
this . error ( node , {
code : 'illegal-subscription' ,
code : 'illegal-subscription' ,
message : ` Cannot reference store value inside <script context="module"> `
message : ` Cannot reference store value inside <script context="module"> ` ,
} ) ;
} ) ;
} else {
} else {
this . add_var ( {
this . add_var ( {
name ,
name ,
global : true
global : true ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;
@ -648,7 +711,9 @@ export default class Component {
} ) ;
} ) ;
} ) ;
} ) ;
const { scope : instance_scope , map , globals } = create_scopes ( script . content ) ;
const { scope : instance_scope , map , globals } = create_scopes (
script . content
) ;
this . instance_scope = instance_scope ;
this . instance_scope = instance_scope ;
this . instance_scope_map = map ;
this . instance_scope_map = map ;
@ -656,7 +721,7 @@ export default class Component {
if ( name [ 0 ] === '$' ) {
if ( name [ 0 ] === '$' ) {
this . error ( node , {
this . error ( node , {
code : 'illegal-declaration' ,
code : 'illegal-declaration' ,
message : ` The $ prefix is reserved, and cannot be used for variable and import names `
message : ` The $ prefix is reserved, and cannot be used for variable and import names ` ,
} ) ;
} ) ;
}
}
@ -664,7 +729,7 @@ export default class Component {
name ,
name ,
initialised : instance_scope.initialised_declarations.has ( name ) ,
initialised : instance_scope.initialised_declarations.has ( name ) ,
hoistable : /^Import/ . test ( node . type ) ,
hoistable : /^Import/ . test ( node . type ) ,
writable : node.kind === 'var' || node . kind === 'let'
writable : node.kind === 'var' || node . kind === 'let' ,
} ) ;
} ) ;
this . node_for_declaration . set ( name , node ) ;
this . node_for_declaration . set ( name , node ) ;
@ -679,12 +744,12 @@ export default class Component {
injected : true ,
injected : true ,
writable : true ,
writable : true ,
reassigned : true ,
reassigned : true ,
initialised : true
initialised : true ,
} ) ;
} ) ;
} else if ( name === '$$props' ) {
} else if ( name === '$$props' ) {
this . add_var ( {
this . add_var ( {
name ,
name ,
injected : true
injected : true ,
} ) ;
} ) ;
} else if ( name [ 0 ] === '$' ) {
} else if ( name [ 0 ] === '$' ) {
if ( name === '$' || name [ 1 ] === '$' ) {
if ( name === '$' || name [ 1 ] === '$' ) {
@ -698,7 +763,7 @@ export default class Component {
name ,
name ,
injected : true ,
injected : true ,
mutated : true ,
mutated : true ,
writable : true
writable : true ,
} ) ;
} ) ;
this . add_reference ( name . slice ( 1 ) ) ;
this . add_reference ( name . slice ( 1 ) ) ;
@ -708,7 +773,7 @@ export default class Component {
} else {
} else {
this . add_var ( {
this . add_var ( {
name ,
name ,
global : true
global : true ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;
@ -768,7 +833,7 @@ export default class Component {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
}
} ,
} ) ;
} ) ;
}
}
@ -784,10 +849,14 @@ export default class Component {
scope = map . get ( node ) ;
scope = map . get ( node ) ;
}
}
if ( node . type === 'LabeledStatement' && node . label . name === '$' && parent . type !== 'Program' ) {
if (
node . type === 'LabeledStatement' &&
node . label . name === '$' &&
parent . type !== 'Program'
) {
component . warn ( node , {
component . warn ( node , {
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' ,
} ) ;
} ) ;
}
}
@ -805,7 +874,7 @@ export default class Component {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
}
} ,
} ) ;
} ) ;
}
}
@ -820,7 +889,13 @@ export default class Component {
return ` ${ name . slice ( 1 ) } .set( ${ name } ) ` ;
return ` ${ name . slice ( 1 ) } .set( ${ name } ) ` ;
}
}
if ( variable && ! variable . referenced && ! variable . is_reactive_dependency && ! variable . export_name && ! name . startsWith ( '$$' ) ) {
if (
variable &&
! variable . referenced &&
! variable . is_reactive_dependency &&
! variable . export_name &&
! name . startsWith ( '$$' )
) {
return value || name ;
return value || name ;
}
}
@ -832,7 +907,9 @@ export default class Component {
const deps = new Set ( [ name ] ) ;
const deps = new Set ( [ name ] ) ;
deps . forEach ( name = > {
deps . forEach ( name = > {
const reactive_declarations = this . reactive_declarations . filter ( x = > x . assignees . has ( name ) ) ;
const reactive_declarations = this . reactive_declarations . filter ( x = >
x . assignees . has ( name )
) ;
reactive_declarations . forEach ( declaration = > {
reactive_declarations . forEach ( declaration = > {
declaration . dependencies . forEach ( name = > {
declaration . dependencies . forEach ( name = > {
deps . add ( name ) ;
deps . add ( name ) ;
@ -840,7 +917,9 @@ export default class Component {
} ) ;
} ) ;
} ) ;
} ) ;
return Array . from ( deps ) . map ( n = > ` $ $ invalidate(' ${ n } ', ${ n } ) ` ) . join ( ', ' ) ;
return Array . from ( deps )
. map ( n = > ` $ $ invalidate(' ${ n } ', ${ n } ) ` )
. join ( ', ' ) ;
}
}
rewrite_props ( get_insert : ( variable : Var ) = > string ) {
rewrite_props ( get_insert : ( variable : Var ) = > string ) {
@ -876,7 +955,7 @@ export default class Component {
if ( variable . export_name ) {
if ( variable . export_name ) {
component . error ( declarator , {
component . error ( declarator , {
code : 'destructured-prop' ,
code : 'destructured-prop' ,
message : ` Cannot declare props in destructured declaration `
message : ` Cannot declare props in destructured declaration ` ,
} ) ;
} ) ;
}
}
@ -887,7 +966,11 @@ export default class Component {
if ( inserts . length > 0 ) {
if ( inserts . length > 0 ) {
if ( next ) {
if ( next ) {
code . overwrite ( declarator . end , next . start , ` ; ${ inserts . join ( '; ' ) } ; ${ node . kind } ` ) ;
code . overwrite (
declarator . end ,
next . start ,
` ; ${ inserts . join ( '; ' ) } ; ${ node . kind } `
) ;
} else {
} else {
code . appendLeft ( declarator . end , ` ; ${ inserts . join ( '; ' ) } ` ) ;
code . appendLeft ( declarator . end , ` ; ${ inserts . join ( '; ' ) } ` ) ;
}
}
@ -909,7 +992,11 @@ export default class Component {
: null ;
: null ;
if ( ! current_group || ( current_group . insert && insert ) ) {
if ( ! current_group || ( current_group . insert && insert ) ) {
current_group = { kind : node.kind , declarators : [ declarator ] , insert } ;
current_group = {
kind : node.kind ,
declarators : [ declarator ] ,
insert ,
} ;
coalesced_declarations . push ( current_group ) ;
coalesced_declarations . push ( current_group ) ;
} else if ( insert ) {
} else if ( insert ) {
current_group . insert = insert ;
current_group . insert = insert ;
@ -918,17 +1005,28 @@ export default class Component {
current_group . declarators . push ( declarator ) ;
current_group . declarators . push ( declarator ) ;
}
}
if ( variable . writable && variable . name !== variable . export_name ) {
if (
code . prependRight ( declarator . id . start , ` ${ variable . export_name } : ` ) ;
variable . writable &&
variable . name !== variable . export_name
) {
code . prependRight (
declarator . id . start ,
` ${ variable . export_name } : `
) ;
}
}
if ( next ) {
if ( next ) {
const next_variable = component . var_lookup . get ( next . id . name ) ;
const next_variable = component . var_lookup . get ( next . id . name ) ;
const new_declaration = ! next_variable . export_name
const new_declaration =
|| ( current_group . insert && next_variable . subscribable ) ;
! next_variable . export_name ||
( current_group . insert && next_variable . subscribable ) ;
if ( new_declaration ) {
if ( new_declaration ) {
code . overwrite ( declarator . end , next . start , ` ${ node . kind } ` ) ;
code . overwrite (
declarator . end ,
next . start ,
` ${ node . kind } `
) ;
}
}
}
}
} else {
} else {
@ -938,7 +1036,11 @@ export default class Component {
const insert = get_insert ( variable ) ;
const insert = get_insert ( variable ) ;
if ( next ) {
if ( next ) {
code . overwrite ( declarator . end , next . start , ` ; ${ insert } ; ${ node . kind } ` ) ;
code . overwrite (
declarator . end ,
next . start ,
` ; ${ insert } ; ${ node . kind } `
) ;
} else {
} else {
code . appendLeft ( declarator . end , ` ; ${ insert } ` ) ;
code . appendLeft ( declarator . end , ` ; ${ insert } ` ) ;
}
}
@ -957,7 +1059,7 @@ export default class Component {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
}
} ,
} ) ;
} ) ;
coalesced_declarations . forEach ( group = > {
coalesced_declarations . forEach ( group = > {
@ -980,11 +1082,11 @@ export default class Component {
} ) ;
} ) ;
if ( combining ) {
if ( combining ) {
const insert = group . insert
const insert = group . insert ? ` ; ${ group . insert } ` : '' ;
? ` ; ${ group . insert } `
: '' ;
const suffix = ` ${ writable ? ` } = $ $ props ` : ` ` } ${ insert } ` + ( code . original [ c ] === ';' ? ` ` : ` ; ` ) ;
const suffix =
` ${ writable ? ` } = $ $ props ` : ` ` } ${ insert } ` +
( code . original [ c ] === ';' ? ` ` : ` ; ` ) ;
code . appendLeft ( c , suffix ) ;
code . appendLeft ( c , suffix ) ;
}
}
} ) ;
} ) ;
@ -996,7 +1098,11 @@ 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 { hoistable_nodes , var_lookup , injected_reactive_declaration_vars } = this ;
const {
hoistable_nodes ,
var_lookup ,
injected_reactive_declaration_vars ,
} = this ;
const top_level_function_declarations = new Map ( ) ;
const top_level_function_declarations = new Map ( ) ;
@ -1011,7 +1117,12 @@ export default class Component {
if ( v . export_name ) return false ;
if ( v . export_name ) return false ;
if ( this . var_lookup . get ( d . id . name ) . reassigned ) return false ;
if ( this . var_lookup . get ( d . id . name ) . reassigned ) return false ;
if ( this . vars . find ( variable = > variable . name === d . id . name && variable . module ) ) return false ;
if (
this . vars . find (
variable = > variable . name === d . id . name && variable . module
)
)
return false ;
return true ;
return true ;
} ) ;
} ) ;
@ -1027,7 +1138,11 @@ export default class Component {
}
}
}
}
if ( node . type === 'ExportNamedDeclaration' && node . declaration && node . declaration . type === 'FunctionDeclaration' ) {
if (
node . type === 'ExportNamedDeclaration' &&
node . declaration &&
node . declaration . type === 'FunctionDeclaration'
) {
top_level_function_declarations . set ( node . declaration . id . name , node ) ;
top_level_function_declarations . set ( node . declaration . id . name , node ) ;
}
}
@ -1079,18 +1194,21 @@ 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 ( name ) ;
const other_declaration = top_level_function_declarations . get (
name
) ;
if ( walking . has ( other_declaration ) ) {
if ( walking . has ( other_declaration ) ) {
hoistable = false ;
hoistable = false ;
} else if ( other_declaration . type === 'ExportNamedDeclaration' && walking . has ( other_declaration . declaration ) ) {
} else if (
other_declaration . type === 'ExportNamedDeclaration' &&
walking . has ( other_declaration . declaration )
) {
hoistable = false ;
hoistable = false ;
} else if ( ! is_hoistable ( other_declaration ) ) {
} else if ( ! is_hoistable ( other_declaration ) ) {
hoistable = false ;
hoistable = false ;
}
}
}
} else {
else {
hoistable = false ;
hoistable = false ;
}
}
}
}
@ -1103,7 +1221,7 @@ export default class Component {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
}
} ,
} ) ;
} ) ;
checked . add ( fn_declaration ) ;
checked . add ( fn_declaration ) ;
@ -1162,7 +1280,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 = variable && ( variable . writable || variable . mutated ) ;
const is_writable_or_mutated =
variable && ( variable . writable || variable . mutated ) ;
if (
if (
( ! owner || owner === component . instance_scope ) &&
( ! owner || owner === component . instance_scope ) &&
( name [ 0 ] === '$' || is_writable_or_mutated )
( name [ 0 ] === '$' || is_writable_or_mutated )
@ -1179,15 +1298,21 @@ export default class Component {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
}
} ,
} ) ;
} ) ;
add_indentation ( this . code , node . body , 2 ) ;
add_indentation ( this . code , node . body , 2 ) ;
const expression = node . body . expression && unwrap_parens ( node . body . expression ) ;
const expression =
node . body . expression && unwrap_parens ( node . body . expression ) ;
const declaration = expression && expression . left ;
const declaration = expression && expression . left ;
unsorted_reactive_declarations . push ( { assignees , dependencies , node , declaration } ) ;
unsorted_reactive_declarations . push ( {
assignees ,
dependencies ,
node ,
declaration ,
} ) ;
}
}
} ) ;
} ) ;
@ -1236,9 +1361,10 @@ export default class Component {
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 ) earlier_declarations . forEach ( declaration = > {
if ( earlier_declarations )
add_declaration ( declaration ) ;
earlier_declarations . forEach ( declaration = > {
} ) ;
add_declaration ( declaration ) ;
} ) ;
} ) ;
} ) ;
this . reactive_declarations . push ( declaration ) ;
this . reactive_declarations . push ( declaration ) ;
@ -1285,11 +1411,12 @@ export default class Component {
if ( globals . has ( name ) ) return ;
if ( globals . has ( name ) ) return ;
let message = ` ' ${ name } ' is not defined ` ;
let message = ` ' ${ name } ' is not defined ` ;
if ( ! this . ast . instance ) message += ` . Consider adding a <script> block with 'export let ${ name } ' to declare a prop ` ;
if ( ! this . ast . instance )
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' ,
message
message ,
} ) ;
} ) ;
}
}
@ -1308,10 +1435,11 @@ export default class Component {
function process_component_options ( component : Component , nodes ) {
function process_component_options ( component : Component , nodes ) {
const component_options : ComponentOptions = {
const component_options : ComponentOptions = {
immutable : component.compile_options.immutable || false ,
immutable : component.compile_options.immutable || false ,
accessors : 'accessors' in component . compile_options
accessors :
? component . compile_options . accessors
'accessors' in component . compile_options
: ! ! component . compile_options . customElement ,
? component . compile_options . accessors
preserveWhitespace : ! ! component . compile_options . preserveWhitespace
: ! ! component . compile_options . customElement ,
preserveWhitespace : ! ! component . compile_options . preserveWhitespace ,
} ;
} ;
const node = nodes . find ( node = > node . name === 'svelte:options' ) ;
const node = nodes . find ( node = > node . name === 'svelte:options' ) ;
@ -1346,12 +1474,13 @@ 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 ) component . error ( attribute , { code , message } ) ;
if ( typeof tag !== 'string' && tag !== null )
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 , {
code : ` invalid-tag-property ` ,
code : ` invalid-tag-property ` ,
message : ` tag name must be two or more words joined by the '-' character `
message : ` tag name must be two or more words joined by the '-' character ` ,
} ) ;
} ) ;
}
}
@ -1364,19 +1493,20 @@ 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' ) component . error ( attribute , { code , message } ) ;
if ( typeof ns !== 'string' )
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 ) ;
if ( match ) {
if ( match ) {
component . error ( attribute , {
component . error ( attribute , {
code : ` invalid-namespace-property ` ,
code : ` invalid-namespace-property ` ,
message : ` Invalid namespace ' ${ ns } ' (did you mean ' ${ match } '?) `
message : ` Invalid namespace ' ${ ns } ' (did you mean ' ${ match } '?) ` ,
} ) ;
} ) ;
} else {
} else {
component . error ( attribute , {
component . error ( attribute , {
code : ` invalid-namespace-property ` ,
code : ` invalid-namespace-property ` ,
message : ` Invalid namespace ' ${ ns } ' `
message : ` Invalid namespace ' ${ ns } ' ` ,
} ) ;
} ) ;
}
}
}
}
@ -1387,13 +1517,13 @@ function process_component_options(component: Component, nodes) {
case 'accessors' :
case 'accessors' :
case 'immutable' :
case 'immutable' :
case 'preserveWhitespace' :
case 'preserveWhitespace' : {
{
const code = ` invalid- ${ name } -value ` ;
const code = ` invalid- ${ name } -value ` ;
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' ) component . error ( attribute , { code , message } ) ;
if ( typeof value !== 'boolean' )
component . error ( attribute , { code , message } ) ;
component_options [ name ] = value ;
component_options [ name ] = value ;
break ;
break ;
@ -1402,15 +1532,13 @@ function process_component_options(component: Component, nodes) {
default :
default :
component . error ( attribute , {
component . error ( attribute , {
code : ` invalid-options-attribute ` ,
code : ` invalid-options-attribute ` ,
message : ` <svelte:options> unknown attribute `
message : ` <svelte:options> unknown attribute ` ,
} ) ;
} ) ;
}
}
}
} else {
else {
component . error ( attribute , {
component . error ( attribute , {
code : ` invalid-options-attribute ` ,
code : ` invalid-options-attribute ` ,
message : ` <svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes `
message : ` <svelte:options> can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes ` ,
} ) ;
} ) ;
}
}
} ) ;
} ) ;