@ -1,220 +1,171 @@
import type { RawSourceMap , DecodedSourceMap } from '@ampproject/remapping' ;
import { b , x , p } from 'code-red' ;
import { b , x , p } from 'code-red' ;
import Component from '../Component' ;
import Renderer from './Renderer.js' ;
import Renderer from './Renderer' ;
import { CompileOptions , CssResult } from '../../interfaces' ;
import { walk } from 'estree-walker' ;
import { walk } from 'estree-walker' ;
import { extract_names , Scope } from 'periscopic' ;
import { extract_names } from 'periscopic' ;
import { invalidate } from './invalidate' ;
import { invalidate } from './invalidate.js' ;
import Block from './Block' ;
import { apply_preprocessor_sourcemap } from '../../utils/mapped_code.js' ;
import {
import { flatten } from '../../utils/flatten.js' ;
ImportDeclaration ,
import check_enable_sourcemap from '../utils/check_enable_sourcemap.js' ;
ClassDeclaration ,
import { push_array } from '../../utils/push_array.js' ;
Node ,
Statement ,
/ * *
ObjectExpression ,
* @param { import ( '../Component.js' ) . default } component
Expression
* @param { import ( '../../interfaces.js' ) . CompileOptions } options
} from 'estree' ;
* @returns { { js : import ( "C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index" ) . import ( 'estree' ) . Node [ ] ; css : import ( '../../interfaces.js' ) . CssResult ; } }
import { apply_preprocessor_sourcemap } from '../../utils/mapped_code' ;
* /
import { flatten } from '../../utils/flatten' ;
export default function dom ( component , options ) {
import check_enable_sourcemap from '../utils/check_enable_sourcemap' ;
import { push_array } from '../../utils/push_array' ;
export default function dom (
component : Component ,
options : CompileOptions
) : { js : Node [ ] ; css : CssResult } {
const { name } = component ;
const { name } = component ;
const renderer = new Renderer ( component , options ) ;
const renderer = new Renderer ( component , options ) ;
const { block } = renderer ;
const { block } = renderer ;
block . has_outro_method = true ;
block . has_outro_method = true ;
const body = [ ] ;
const body = [ ] ;
if ( renderer . file_var ) {
if ( renderer . file_var ) {
const file = component . file ? x ` " ${ component . file } " ` : x ` undefined ` ;
const file = component . file ? x ` " ${ component . file } " ` : x ` undefined ` ;
body . push ( b ` const ${ renderer . file_var } = ${ file } ; ` ) ;
body . push ( b ` const ${ renderer . file_var } = ${ file } ; ` ) ;
}
}
const css = component . stylesheet . render ( options . filename ) ;
const css = component . stylesheet . render ( options . filename ) ;
const css_sourcemap_enabled = check_enable_sourcemap ( options . enableSourcemap , 'css' ) ;
const css_sourcemap_enabled = check_enable_sourcemap ( options . enableSourcemap , 'css' ) ;
if ( css_sourcemap_enabled ) {
if ( css_sourcemap_enabled ) {
css . map = apply_preprocessor_sourcemap (
css . map = apply_preprocessor_sourcemap ( options . filename , css . map ,
options . filename ,
/** @type {string | import('@ampproject/remapping').RawSourceMap | import('@ampproject/remapping').DecodedSourceMap} */ ( options . sourcemap ) ) ;
css . map ,
}
options . sourcemap as string | RawSourceMap | DecodedSourceMap
else {
) ;
} else {
css . map = null ;
css . map = null ;
}
}
const styles = css_sourcemap_enabled && component . stylesheet . has_styles && options . dev
const styles =
css_sourcemap_enabled && component . stylesheet . has_styles && options . dev
? ` ${ css . code } \ n/*# sourceMappingURL= ${ css . map . toUrl ( ) } */ `
? ` ${ css . code } \ n/*# sourceMappingURL= ${ css . map . toUrl ( ) } */ `
: css . code ;
: css . code ;
const add_css = component . get_unique_name ( 'add_css' ) ;
const add_css = component . get_unique_name ( 'add_css' ) ;
const should_add_css = ! ! styles && ( options . customElement || options . css === 'injected' ) ;
const should_add_css = ! ! styles && ( options . customElement || options . css === 'injected' ) ;
if ( should_add_css ) {
if ( should_add_css ) {
body . push ( b `
body . push ( b `
function $ { add_css } ( target ) {
function $ { add_css } ( target ) {
@append_styles ( target , "${component.stylesheet.id}" , "${styles}" ) ;
@append_styles ( target , "${component.stylesheet.id}" , "${styles}" ) ;
}
}
` );
` );
}
}
// fix order
// fix order
// TODO the deconflicted names of blocks are reversed... should set them here
// TODO the deconflicted names of blocks are reversed... should set them here
const blocks = renderer . blocks . slice ( ) . reverse ( ) ;
const blocks = renderer . blocks . slice ( ) . reverse ( ) ;
push_array ( body , blocks . map ( ( block ) = > {
push_array (
body ,
blocks . map ( ( block ) = > {
// TODO this is a horrible mess — renderer.blocks
// TODO this is a horrible mess — renderer.blocks
// contains a mixture of Blocks and Nodes
// contains a mixture of Blocks and Nodes
if ( ( block as Block ) . render ) return ( block as Block ) . render ( ) ;
if ( ( /** @type {import('./Block.js').default} */ ( block ) ) . render )
return ( /** @type {import('./Block.js').default} */ ( block ) ) . render ( ) ;
return block ;
return block ;
} )
} ) ) ;
) ;
if ( options . dev && ! options . hydratable ) {
if ( options . dev && ! options . hydratable ) {
block . chunks . claim . push (
block . chunks . claim . push ( b ` throw new @_Error("options.hydrate only works if the component was compiled with the \` hydratable: true \` option"); ` ) ;
b ` throw new @_Error("options.hydrate only works if the component was compiled with the \` hydratable: true \` option"); `
) ;
}
}
const uses_slots = component . var_lookup . has ( '$$slots' ) ;
const uses_slots = component . var_lookup . has ( '$$slots' ) ;
let compute_slots : Node [ ] | undefined ;
/** @type {import('estree').Node[] | undefined} */
let compute_slots ;
if ( uses_slots ) {
if ( uses_slots ) {
compute_slots = b `
compute_slots = b `
const $ $slots = @compute_slots ( # slots ) ;
const $ $slots = @compute_slots ( # slots ) ;
` ;
` ;
}
}
const uses_props = component . var_lookup . has ( '$$props' ) ;
const uses_props = component . var_lookup . has ( '$$props' ) ;
const uses_rest = component . var_lookup . has ( '$$restProps' ) ;
const uses_rest = component . var_lookup . has ( '$$restProps' ) ;
const $ $props = uses_props || uses_rest ? '$$new_props' : '$$props' ;
const $ $props = uses_props || uses_rest ? '$$new_props' : '$$props' ;
const props = component . vars . filter ( ( variable ) = > ! variable . module && variable . export_name ) ;
const props = component . vars . filter ( ( variable ) = > ! variable . module && variable . export_name ) ;
const writable_props = props . filter ( ( variable ) = > variable . writable ) ;
const writable_props = props . filter ( ( variable ) = > variable . writable ) ;
const omit_props_names = component . get_unique_name ( 'omit_props_names' ) ;
const omit_props_names = component . get_unique_name ( 'omit_props_names' ) ;
const compute_rest = x ` @compute_rest_props( $ $ props, ${ omit_props_names . name } ) ` ;
const compute_rest = x ` @compute_rest_props( $ $ props, ${ omit_props_names . name } ) ` ;
const rest = uses_rest
const rest = uses_rest
? b `
? b `
const $ { omit_props_names . name } = [ $ { props . map ( ( prop ) = > ` " ${ prop . export_name } " ` ) . join ( ',' ) } ] ;
const $ { omit_props_names . name } = [ $ { props . map ( ( prop ) = > ` " ${ prop . export_name } " ` ) . join ( ',' ) } ] ;
let $ $restProps = $ { compute_rest } ;
let $ $restProps = $ { compute_rest } ;
`
`
: null ;
: null ;
const set = uses_props || uses_rest || writable_props . length > 0 || component . slots . size > 0
const set =
? x `
uses_props || uses_rest || writable_props . length > 0 || component . slots . size > 0
? x `
$ { $ $props } = > {
$ { $ $props } = > {
$ {
$ { uses_props &&
uses_props &&
renderer . invalidate ( '$$props' , x ` $ $ props = @assign(@assign({}, $ $ props), @exclude_internal_props( $ $ new_props)) ` ) }
renderer . invalidate (
$ { uses_rest &&
'$$props' ,
x ` $ $ props = @assign(@assign({}, $ $ props), @exclude_internal_props( $ $ new_props)) `
)
}
$ {
uses_rest &&
! uses_props &&
! uses_props &&
x ` $ $ props = @assign(@assign({}, $ $ props), @exclude_internal_props( $ $ new_props)) `
x ` $ $ props = @assign(@assign({}, $ $ props), @exclude_internal_props( $ $ new_props)) ` }
}
$ { uses_rest && renderer . invalidate ( '$$restProps' , x ` $ $ restProps = ${ compute_rest } ` ) }
$ { uses_rest && renderer . invalidate ( '$$restProps' , x ` $ $ restProps = ${ compute_rest } ` ) }
$ { writable_props . map ( ( prop ) = > b ` if (' ${ prop . export_name } ' in ${ $ $props } ) ${ renderer . invalidate ( prop . name , x ` ${ prop . name } = ${ $ $props } . ${ prop . export_name } ` ) } ; ` ) }
$ { writable_props . map (
$ { component . slots . size > 0 &&
( prop ) = >
b ` if (' $ $ scope' in ${ $ $props } ) ${ renderer . invalidate ( '$$scope' , x ` $ $ scope = ${ $ $props } . $ $ scope ` ) } ; ` }
b ` if (' ${ prop . export_name } ' in ${ $ $props } ) ${ renderer . invalidate (
prop . name ,
x ` ${ prop . name } = ${ $ $props } . ${ prop . export_name } `
) } ; `
) }
$ {
component . slots . size > 0 &&
b ` if (' $ $ scope' in ${ $ $props } ) ${ renderer . invalidate (
'$$scope' ,
x ` $ $ scope = ${ $ $props } . $ $ scope `
) } ; `
}
}
}
`
`
: null ;
: null ;
const accessors = [ ] ;
const accessors = [ ] ;
const not_equal = component . component_options . immutable ? x ` @not_equal ` : x ` @safe_not_equal ` ;
const not_equal = component . component_options . immutable ? x ` @not_equal ` : x ` @safe_not_equal ` ;
/** @type {import('estree').Node[] | import('estree').Node} */
let missing_props_check : Node [ ] | Node ;
let missing_props_check ;
let inject_state : Expression ;
let capture_state : Expression ;
let props_inject : Node [ ] | Node ;
/** @type {import('estree').Expression} */
let inject_state ;
/** @type {import('estree').Expression} */
let capture_state ;
/** @type {import('estree').Node[] | import('estree').Node} */
let props_inject ;
props . forEach ( ( prop ) = > {
props . forEach ( ( prop ) = > {
const variable = component . var_lookup . get ( prop . name ) ;
const variable = component . var_lookup . get ( prop . name ) ;
if ( ! variable . writable || component . component_options . accessors ) {
if ( ! variable . writable || component . component_options . accessors ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'get' ,
kind : 'get' ,
key : { type : 'Identifier' , name : prop.export_name } ,
key : { type : 'Identifier' , name : prop.export_name } ,
value : x ` function() {
value : x ` function() {
return $ {
return $ { prop . hoistable
prop . hoistable
? prop . name
? prop . name
: x ` this. $ $ .ctx[ ${ renderer . context_lookup . get ( prop . name ) . index } ] `
: x ` this. $ $ .ctx[ ${ renderer . context_lookup . get ( prop . name ) . index } ] ` }
}
} `
} `
} ) ;
} ) ;
} else if ( component . compile_options . dev ) {
}
else if ( component . compile_options . dev ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'get' ,
kind : 'get' ,
key : { type : 'Identifier' , name : prop.export_name } ,
key : { type : 'Identifier' , name : prop.export_name } ,
value : x ` function() {
value : x ` function() {
throw new @_Error ( "<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
throw new @_Error ( "<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
} `
} `
} ) ;
} ) ;
}
}
if ( component . component_options . accessors ) {
if ( component . component_options . accessors ) {
if ( variable . writable && ! renderer . readonly . has ( prop . name ) ) {
if ( variable . writable && ! renderer . readonly . has ( prop . name ) ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'set' ,
kind : 'set' ,
key : { type : 'Identifier' , name : prop.export_name } ,
key : { type : 'Identifier' , name : prop.export_name } ,
value : x ` function( ${ prop . name } ) {
value : x ` function( ${ prop . name } ) {
this . $ $set ( { $ { prop . export_name } : $ { prop . name } } ) ;
this . $ $set ( { $ { prop . export_name } : $ { prop . name } } ) ;
@flush ( ) ;
@flush ( ) ;
} `
} `
} ) ;
} ) ;
} else if ( component . compile_options . dev ) {
}
else if ( component . compile_options . dev ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'set' ,
kind : 'set' ,
key : { type : 'Identifier' , name : prop.export_name } ,
key : { type : 'Identifier' , name : prop.export_name } ,
value : x ` function(value) {
value : x ` function(value) {
throw new @_Error ( "<${component.tag}>: Cannot set read-only property '${prop.export_name}'" ) ;
throw new @_Error ( "<${component.tag}>: Cannot set read-only property '${prop.export_name}'" ) ;
} `
} `
} ) ;
} ) ;
}
}
} else if ( component . compile_options . dev ) {
}
else if ( component . compile_options . dev ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'set' ,
kind : 'set' ,
key : { type : 'Identifier' , name : prop.export_name } ,
key : { type : 'Identifier' , name : prop.export_name } ,
value : x ` function(value) {
value : x ` function(value) {
throw new @_Error ( "<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
throw new @_Error ( "<${component.tag}>: Props cannot be set directly on the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
} `
} `
} ) ;
} ) ;
}
}
} ) ;
} ) ;
component . instance_exports_from . forEach ( ( exports_from ) = > {
component . instance_exports_from . forEach ( ( exports_from ) = > {
const import_declaration = {
const import_declaration = {
. . . exports_from ,
. . . exports_from ,
@ -222,8 +173,7 @@ export default function dom(
specifiers : [ ] ,
specifiers : [ ] ,
source : exports_from.source
source : exports_from.source
} ;
} ;
component . imports . push ( import_declaration as ImportDeclaration ) ;
component . imports . push ( /** @type {import('estree').ImportDeclaration} */ ( import_declaration ) ) ;
exports_from . specifiers . forEach ( ( specifier ) = > {
exports_from . specifiers . forEach ( ( specifier ) = > {
if ( component . component_options . accessors ) {
if ( component . component_options . accessors ) {
const name = component . get_unique_name ( specifier . exported . name ) ;
const name = component . get_unique_name ( specifier . exported . name ) ;
@ -233,189 +183,147 @@ export default function dom(
imported : specifier.local ,
imported : specifier.local ,
local : name
local : name
} ) ;
} ) ;
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'get' ,
kind : 'get' ,
key : { type : 'Identifier' , name : specifier.exported.name } ,
key : { type : 'Identifier' , name : specifier.exported.name } ,
value : x ` function() {
value : x ` function() {
return $ { name }
return $ { name }
} `
} `
} ) ;
} ) ;
} else if ( component . compile_options . dev ) {
}
else if ( component . compile_options . dev ) {
accessors . push ( {
accessors . push ( {
type : 'MethodDefinition' ,
type : 'MethodDefinition' ,
kind : 'get' ,
kind : 'get' ,
key : { type : 'Identifier' , name : specifier.exported.name } ,
key : { type : 'Identifier' , name : specifier.exported.name } ,
value : x ` function() {
value : x ` function() {
throw new @_Error ( "<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
throw new @_Error ( "<${component.tag}>: Props cannot be read directly from the component instance unless compiling with 'accessors: true' or '<svelte:options accessors/>'" ) ;
} `
} `
} ) ;
} ) ;
}
}
} ) ;
} ) ;
} ) ;
} ) ;
if ( component . compile_options . dev ) {
if ( component . compile_options . dev ) {
// checking that expected ones were passed
// checking that expected ones were passed
const expected = props . filter ( ( prop ) = > prop . writable && ! prop . initialised ) ;
const expected = props . filter ( ( prop ) = > prop . writable && ! prop . initialised ) ;
if ( expected . length ) {
if ( expected . length ) {
missing_props_check = b `
missing_props_check = b `
$ $self . $ $ . on_mount . push ( function ( ) {
$ $self . $ $ . on_mount . push ( function ( ) {
$ { expected . map (
$ { expected . map ( ( prop ) = > b `
( prop ) = > b `
if ( $ { prop . name } === undefined && ! ( ( '${prop.export_name}' in $ $props ) || $ $self . $ $ . bound [ $ $self . $ $ . props [ '${prop.export_name}' ] ] ) ) {
if ( $ { prop . name } === undefined && ! ( ( '${prop.export_name}' in $ $props ) || $ $self . $ $ . bound [ $ $self . $ $ . props [ '${prop.export_name}' ] ] ) ) {
@_console . warn ( "<${component.tag}> was created without expected prop '${prop.export_name}'" ) ;
@_console . warn ( "<${component.tag}> was created without expected prop '${prop.export_name}'" ) ;
} `
} ` )}
) }
} ) ;
} ) ;
` ;
` ;
}
}
const capturable_vars = component . vars . filter ( ( v ) = > ! v . internal && ! v . global && ! v . name . startsWith ( '$$' ) ) ;
const capturable_vars = component . vars . filter (
( v ) = > ! v . internal && ! v . global && ! v . name . startsWith ( '$$' )
) ;
if ( capturable_vars . length > 0 ) {
if ( capturable_vars . length > 0 ) {
capture_state = x ` () => ({ ${ capturable_vars . map ( ( prop ) = > p ` ${ prop . name } ` ) } }) ` ;
capture_state = x ` () => ({ ${ capturable_vars . map ( ( prop ) = > p ` ${ prop . name } ` ) } }) ` ;
}
}
const injectable_vars = capturable_vars . filter ( ( v ) = > ! v . module && v . writable && v . name [ 0 ] !== '$' ) ;
const injectable_vars = capturable_vars . filter (
( v ) = > ! v . module && v . writable && v . name [ 0 ] !== '$'
) ;
if ( uses_props || injectable_vars . length > 0 ) {
if ( uses_props || injectable_vars . length > 0 ) {
inject_state = x `
inject_state = x `
$ { $ $props } = > {
$ { $ $props } = > {
$ {
$ { uses_props &&
uses_props &&
renderer . invalidate ( '$$props' , x ` $ $ props = @assign(@assign({}, $ $ props), $ $ new_props) ` ) }
renderer . invalidate ( '$$props' , x ` $ $ props = @assign(@assign({}, $ $ props), $ $ new_props) ` )
$ { injectable_vars . map ( ( v ) = > b ` if (' ${ v . name } ' in $ $ props) ${ renderer . invalidate ( v . name , x ` ${ v . name } = ${ $ $props } . ${ v . name } ` ) } ; ` ) }
}
$ { injectable_vars . map (
( v ) = >
b ` if (' ${ v . name } ' in $ $ props) ${ renderer . invalidate (
v . name ,
x ` ${ v . name } = ${ $ $props } . ${ v . name } `
) } ; `
) }
}
}
` ;
` ;
props_inject = b `
props_inject = b `
if ( $ $props && "$$inject" in $ $props ) {
if ( $ $props && "$$inject" in $ $props ) {
$ $self . $inject_state ( $ $props . $ $inject ) ;
$ $self . $inject_state ( $ $props . $ $inject ) ;
}
}
` ;
` ;
}
}
}
}
// instrument assignments
// instrument assignments
if ( component . ast . instance ) {
if ( component . ast . instance ) {
let scope = component . instance_scope ;
let scope = component . instance_scope ;
const map = component . instance_scope_map ;
const map = component . instance_scope_map ;
let execution_context : Node | null = null ;
/** @type {import('estree').Node | null} */
let execution_context = null ;
walk ( component . ast . instance . content , {
walk ( component . ast . instance . content , {
enter ( node : Node ) {
enter ( n ode) {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = map . get ( node ) as Scope ;
scope = /** @type {import('periscopic').Scope} */ ( map . get ( node ) ) ;
if ( ! execution_context && ! scope . block ) {
if ( ! execution_context && ! scope . block ) {
execution_context = node ;
execution_context = node ;
}
}
} else if (
}
! execution_context &&
else if ( ! execution_context &&
node . type === 'LabeledStatement' &&
node . type === 'LabeledStatement' &&
node . label . name === '$'
node . label . name === '$' ) {
) {
execution_context = node ;
execution_context = node ;
}
}
} ,
} ,
leave ( node ) {
leave ( node : Node ) {
if ( map . has ( node ) ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
scope = scope . parent ;
}
}
if ( execution_context === node ) {
if ( execution_context === node ) {
execution_context = null ;
execution_context = null ;
}
}
if ( node . type === 'AssignmentExpression' || node . type === 'UpdateExpression' ) {
if ( node . type === 'AssignmentExpression' || node . type === 'UpdateExpression' ) {
const assignee = node . type === 'AssignmentExpression' ? node.left : node.argument ;
const assignee = node . type === 'AssignmentExpression' ? node.left : node.argument ;
// normally (`a = 1`, `b.c = 2`), there'll be a single name
// normally (`a = 1`, `b.c = 2`), there'll be a single name
// (a or b). In destructuring cases (`[d, e] = [e, d]`) there
// (a or b). In destructuring cases (`[d, e] = [e, d]`) there
// may be more, in which case we need to tack the extra ones
// may be more, in which case we need to tack the extra ones
// onto the initial function call
// onto the initial function call
const names = new Set ( extract_names ( assignee as Node ) ) ;
const names = new Set ( extract_names ( /** @type {import('estree').Node} */ ( assignee ) ) ) ;
this . replace ( invalidate ( renderer , scope , node , names , execution_context === null ) ) ;
this . replace ( invalidate ( renderer , scope , node , names , execution_context === null ) ) ;
}
}
}
}
} ) ;
} ) ;
component . rewrite_props ( ( { name , reassigned , export_name } ) = > {
component . rewrite_props ( ( { name , reassigned , export_name } ) = > {
const value = ` $ ${ name } ` ;
const value = ` $ ${ name } ` ;
const i = renderer . context_lookup . get ( ` $ ${ name } ` ) . index ;
const i = renderer . context_lookup . get ( ` $ ${ name } ` ) . index ;
const insert = reassigned || export_name
const insert =
? b ` ${ ` $ $ subscribe_ ${ name } ` } () `
reassigned || export_name
: b ` @component_subscribe( $ $ self, ${ name } , #value => $ $ invalidate( ${ i } , ${ value } = #value)) ` ;
? b ` ${ ` $ $ subscribe_ ${ name } ` } () `
: b ` @component_subscribe( $ $ self, ${ name } , #value => $ $ invalidate( ${ i } , ${ value } = #value)) ` ;
if ( component . compile_options . dev ) {
if ( component . compile_options . dev ) {
return b ` @validate_store( ${ name } , ' ${ name } '); ${ insert } ` ;
return b ` @validate_store( ${ name } , ' ${ name } '); ${ insert } ` ;
}
}
return insert ;
return insert ;
} ) ;
} ) ;
}
}
const args = [ x ` $ $ self ` ] ;
const args = [ x ` $ $ self ` ] ;
const has_invalidate = props . length > 0 ||
const has_invalidate =
props . length > 0 ||
component . has_reactive_assignments ||
component . has_reactive_assignments ||
component . slots . size > 0 ||
component . slots . size > 0 ||
capture_state ||
capture_state ||
inject_state ;
inject_state ;
if ( has_invalidate ) {
if ( has_invalidate ) {
args . push ( x ` $ $ props ` , x ` $ $ invalidate ` ) ;
args . push ( x ` $ $ props ` , x ` $ $ invalidate ` ) ;
} else if ( component . compile_options . dev ) {
}
else if ( component . compile_options . dev ) {
// $$props arg is still needed for unknown prop check
// $$props arg is still needed for unknown prop check
args . push ( x ` $ $ props ` ) ;
args . push ( x ` $ $ props ` ) ;
}
}
// has_create_fragment is intentionally to be true in dev mode.
// has_create_fragment is intentionally to be true in dev mode.
const has_create_fragment = component . compile_options . dev || block . has_content ( ) ;
const has_create_fragment = component . compile_options . dev || block . has_content ( ) ;
if ( has_create_fragment ) {
if ( has_create_fragment ) {
body . push ( b `
body . push ( b `
function create_fragment ( # ctx ) {
function create_fragment ( # ctx ) {
$ { block . get_contents ( ) }
$ { block . get_contents ( ) }
}
}
` );
` );
}
}
body . push ( b `
body . push ( b `
$ { component . extract_javascript ( component . ast . module ) }
$ { component . extract_javascript ( component . ast . module ) }
$ { component . fully_hoisted }
$ { component . fully_hoisted }
` );
` );
const filtered_props = props . filter ( ( prop ) = > {
const filtered_props = props . filter ( ( prop ) = > {
const variable = component . var_lookup . get ( prop . name ) ;
const variable = component . var_lookup . get ( prop . name ) ;
if ( variable . hoistable )
if ( variable . hoistable ) return false ;
return false ;
return prop . name [ 0 ] !== '$' ;
return prop . name [ 0 ] !== '$' ;
} ) ;
} ) ;
const reactive_stores = component . vars . filter ( ( variable ) = > variable . name [ 0 ] === '$' && variable . name [ 1 ] !== '$' ) ;
const reactive_stores = component . vars . filter (
( variable ) = > variable . name [ 0 ] === '$' && variable . name [ 1 ] !== '$'
) ;
const instance_javascript = component . extract_javascript ( component . ast . instance ) ;
const instance_javascript = component . extract_javascript ( component . ast . instance ) ;
const has_definition = component . compile_options . dev ||
const has_definition =
component . compile_options . dev ||
( instance_javascript && instance_javascript . length > 0 ) ||
( instance_javascript && instance_javascript . length > 0 ) ||
filtered_props . length > 0 ||
filtered_props . length > 0 ||
uses_props ||
uses_props ||
@ -424,106 +332,86 @@ export default function dom(
component . reactive_declarations . length > 0 ||
component . reactive_declarations . length > 0 ||
capture_state ||
capture_state ||
inject_state ;
inject_state ;
const definition = has_definition
const definition = has_definition
? component . alias ( 'instance' )
? component . alias ( 'instance' )
: { type : 'Literal' , value : null } ;
: { type : 'Literal' , value : null } ;
const reactive_store_subscriptions = reactive_stores
const reactive_store_subscriptions = reactive_stores
. filter ( ( store ) = > {
. filter ( ( store ) = > {
const variable = component . var_lookup . get ( store . name . slice ( 1 ) ) ;
const variable = component . var_lookup . get ( store . name . slice ( 1 ) ) ;
return ! variable || variable . hoistable ;
return ! variable || variable . hoistable ;
} )
} )
. map (
. map ( ( { name } ) = > b `
( { name } ) = > b `
$ { component . compile_options . dev && b ` @validate_store( ${ name . slice ( 1 ) } , ' ${ name . slice ( 1 ) } '); ` }
$ { component . compile_options . dev && b ` @validate_store( ${ name . slice ( 1 ) } , ' ${ name . slice ( 1 ) } '); ` }
@component_subscribe ( $ $self , $ { name . slice ( 1 ) } , $ $value = > $ $invalidate ( $ { renderer . context_lookup . get ( name ) . index } , $ { name } = $ $value ) ) ;
@component_subscribe ( $ $self , $ { name . slice ( 1 ) } , $ $value = > $ $invalidate ( $ {
` );
renderer . context_lookup . get ( name ) . index
} , $ { name } = $ $value ) ) ;
`
) ;
const resubscribable_reactive_store_unsubscribers = reactive_stores
const resubscribable_reactive_store_unsubscribers = reactive_stores
. filter ( ( store ) = > {
. filter ( ( store ) = > {
const variable = component . var_lookup . get ( store . name . slice ( 1 ) ) ;
const variable = component . var_lookup . get ( store . name . slice ( 1 ) ) ;
return variable && ( variable . reassigned || variable . export_name ) ;
return variable && ( variable . reassigned || variable . export_name ) ;
} )
} )
. map ( ( { name } ) = > b ` $ $ self. $ $ .on_destroy.push(() => ${ ` $ $ unsubscribe_ ${ name . slice ( 1 ) } ` } ()); ` ) ;
. map ( ( { name } ) = > b ` $ $ self. $ $ .on_destroy.push(() => ${ ` $ $ unsubscribe_ ${ name . slice ( 1 ) } ` } ()); ` ) ;
if ( has_definition ) {
if ( has_definition ) {
const reactive_declarations : Node | Node [ ] = [ ] ;
const fixed_reactive_declarations : Node [ ] = [ ] ; // not really 'reactive' but whatever
/** @type {import('estree').Node | import('estree').Node[]} */
const reactive_declarations = [ ] ;
/** @type {import('estree').Node[]} */
const fixed_reactive_declarations = [ ] ; // not really 'reactive' but whatever
component . reactive_declarations . forEach ( ( d ) = > {
component . reactive_declarations . forEach ( ( d ) = > {
const dependencies = Array . from ( d . dependencies ) ;
const dependencies = Array . from ( d . dependencies ) ;
const uses_rest_or_props = ! ! dependencies . find ( ( n ) = > n === '$$props' || n === '$$restProps' ) ;
const uses_rest_or_props = ! ! dependencies . find ( ( n ) = > n === '$$props' || n === '$$restProps' ) ;
const writable = dependencies . filter ( ( n ) = > {
const writable = dependencies . filter ( ( n ) = > {
const variable = component . var_lookup . get ( n ) ;
const variable = component . var_lookup . get ( n ) ;
return variable && ( variable . export_name || variable . mutated || variable . reassigned ) ;
return variable && ( variable . export_name || variable . mutated || variable . reassigned ) ;
} ) ;
} ) ;
const condition = ! uses_rest_or_props && writable . length > 0 && renderer . dirty ( writable , true ) ;
const condition =
! uses_rest_or_props && writable . length > 0 && renderer . dirty ( writable , true ) ;
let statement = d . node ; // TODO remove label (use d.node.body) if it's not referenced
let statement = d . node ; // TODO remove label (use d.node.body) if it's not referenced
if ( condition )
if ( condition ) statement = b ` if ( ${ condition } ) { ${ statement } } ` [ 0 ] as Statement ;
statement = /** @type {import('estree').Statement} */ ( b ` if ( ${ condition } ) { ${ statement } } ` [ 0 ] ) ;
if ( condition || uses_rest_or_props ) {
if ( condition || uses_rest_or_props ) {
reactive_declarations . push ( statement ) ;
reactive_declarations . push ( statement ) ;
} else {
}
else {
fixed_reactive_declarations . push ( statement ) ;
fixed_reactive_declarations . push ( statement ) ;
}
}
} ) ;
} ) ;
const injected = Array . from ( component . injected_reactive_declaration_vars ) . filter ( ( name ) = > {
const injected = Array . from ( component . injected_reactive_declaration_vars ) . filter ( ( name ) = > {
const variable = component . var_lookup . get ( name ) ;
const variable = component . var_lookup . get ( name ) ;
return variable . injected && variable . name [ 0 ] !== '$' ;
return variable . injected && variable . name [ 0 ] !== '$' ;
} ) ;
} ) ;
const reactive_store_declarations = reactive_stores . map ( ( variable ) = > {
const reactive_store_declarations = reactive_stores . map ( ( variable ) = > {
const $name = variable . name ;
const $name = variable . name ;
const name = $name . slice ( 1 ) ;
const name = $name . slice ( 1 ) ;
const store = component . var_lookup . get ( name ) ;
const store = component . var_lookup . get ( name ) ;
if ( store && ( store . reassigned || store . export_name ) ) {
if ( store && ( store . reassigned || store . export_name ) ) {
const unsubscribe = ` $ $ unsubscribe_ ${ name } ` ;
const unsubscribe = ` $ $ unsubscribe_ ${ name } ` ;
const subscribe = ` $ $ subscribe_ ${ name } ` ;
const subscribe = ` $ $ subscribe_ ${ name } ` ;
const i = renderer . context_lookup . get ( $name ) . index ;
const i = renderer . context_lookup . get ( $name ) . index ;
return b ` let ${ $name } , ${ unsubscribe } = @noop, ${ subscribe } = () => ( ${ unsubscribe } (), ${ unsubscribe } = @subscribe( ${ name } , $ $ value => $ $ invalidate( ${ i } , ${ $name } = $ $ value)), ${ name } ) ` ;
return b ` let ${ $name } , ${ unsubscribe } = @noop, ${ subscribe } = () => ( ${ unsubscribe } (), ${ unsubscribe } = @subscribe( ${ name } , $ $ value => $ $ invalidate( ${ i } , ${ $name } = $ $ value)), ${ name } ) ` ;
}
}
return b ` let ${ $name } ; ` ;
return b ` let ${ $name } ; ` ;
} ) ;
} ) ;
let unknown_props_check : Node [ ] | undefined ;
/** @type {import('estree').Node[] | undefined} */
let unknown_props_check ;
if ( component . compile_options . dev && ! ( uses_props || uses_rest ) ) {
if ( component . compile_options . dev && ! ( uses_props || uses_rest ) ) {
unknown_props_check = b `
unknown_props_check = b `
const writable_props = [ $ { writable_props . map ( ( prop ) = > x ` ' ${ prop . export_name } ' ` ) } ] ;
const writable_props = [ $ { writable_props . map ( ( prop ) = > x ` ' ${ prop . export_name } ' ` ) } ] ;
@_Object . keys ( $ $props ) . forEach ( key = > {
@_Object . keys ( $ $props ) . forEach ( key = > {
if ( ! ~ writable_props . indexOf ( key ) && key . slice ( 0 , 2 ) !== '$$' && key !== 'slot' ) @_console . warn ( \ ` < ${
if ( ! ~ writable_props . indexOf ( key ) && key . slice ( 0 , 2 ) !== '$$' && key !== 'slot' ) @_console . warn ( \ ` < ${ component . tag } > was created with unknown prop ' \ ${ key } ' \` );
component . tag
} > was created with unknown prop '\${key}' \ ` );
} ) ;
} ) ;
` ;
` ;
}
}
const return_value = {
const return_value = {
type : 'ArrayExpression' ,
type : 'ArrayExpression' ,
elements : renderer.initial_context.map (
elements : renderer.initial_context.map ( ( member ) = > ( /** @type {import('estree').Expression} */ ( {
( member ) = >
( {
type : 'Identifier' ,
type : 'Identifier' ,
name : member.name
name : member.name
} as Expression )
} ) ) )
)
} ;
} ;
body . push ( b `
body . push ( b `
function $ { definition } ( $ { args } ) {
function $ { definition } ( $ { args } ) {
$ { injected . map ( ( name ) = > b ` let ${ name } ; ` ) }
$ { injected . map ( ( name ) = > b ` let ${ name } ; ` ) }
$ { rest }
$ { rest }
@ -533,17 +421,13 @@ export default function dom(
$ { resubscribable_reactive_store_unsubscribers }
$ { resubscribable_reactive_store_unsubscribers }
$ {
$ { component . slots . size || component . compile_options . dev || uses_slots
component . slots . size || component . compile_options . dev || uses_slots
? b ` let { $ $ slots: #slots = {}, $ $ scope } = $ $ props; `
? b ` let { $ $ slots: #slots = {}, $ $ scope } = $ $ props; `
: null }
: null
$ { component . compile_options . dev &&
}
b ` @validate_slots(' ${ component . tag } ', #slots, [ ${ [ . . . component . slots . keys ( ) ]
$ {
component . compile_options . dev &&
b ` @validate_slots(' ${ component . tag } ', #slots, [ ${ [ . . . component . slots . keys ( ) ]
. map ( ( key ) = > ` ' ${ key } ' ` )
. map ( ( key ) = > ` ' ${ key } ' ` )
. join ( ',' ) } ] ) ; `
. join ( ',' ) } ] ) ; ` }
}
$ { compute_slots }
$ { compute_slots }
$ { instance_javascript }
$ { instance_javascript }
@ -551,86 +435,72 @@ export default function dom(
$ { missing_props_check }
$ { missing_props_check }
$ { unknown_props_check }
$ { unknown_props_check }
$ {
$ { renderer . binding_groups . size > 0 &&
renderer . binding_groups . size > 0 &&
b ` const $ $ binding_groups = [ ${ [ . . . renderer . binding_groups . keys ( ) ] . map ( ( _ ) = > x ` [] ` ) } ]; ` }
b ` const $ $ binding_groups = [ ${ [ . . . renderer . binding_groups . keys ( ) ] . map ( ( _ ) = > x ` [] ` ) } ]; `
}
$ { component . partly_hoisted }
$ { component . partly_hoisted }
$ { set && b ` $ $ self. $ $ set = ${ set } ; ` }
$ { set && b ` $ $ self. $ $ set = ${ set } ; ` }
$ { capture_state && b ` $ $ self. $ capture_state = ${ capture_state } ; ` }
$ { capture_state && b ` $ $ self. $ capture_state = ${ capture_state } ; ` }
$ { inject_state && b ` $ $ self. $ inject_state = ${ inject_state } ; ` }
$ { inject_state && b ` $ $ self. $ inject_state = ${ inject_state } ; ` }
$ { /* before reactive declarations */ props_inject }
$ { /* before reactive declarations */ props_inject }
$ {
$ { reactive_declarations . length > 0 &&
reactive_declarations . length > 0 &&
b `
b `
$ $self . $ $ . update = ( ) = > {
$ $self . $ $ . update = ( ) = > {
$ { reactive_declarations }
$ { reactive_declarations }
} ;
} ;
`
` }
}
$ { fixed_reactive_declarations }
$ { fixed_reactive_declarations }
$ { uses_props && b ` $ $ props = @exclude_internal_props( $ $ props); ` }
$ { uses_props && b ` $ $ props = @exclude_internal_props( $ $ props); ` }
return $ { return_value } ;
return $ { return_value } ;
}
}
` );
` );
}
}
const prop_indexes = /** @type {import('estree').ObjectExpression} */ ( x ` {
const prop_indexes = x ` {
$ { props
$ { props
. filter ( ( v ) = > v . export_name && ! v . module )
. filter ( ( v ) = > v . export_name && ! v . module )
. map ( ( v ) = > p ` ${ v . export_name } : ${ renderer . context_lookup . get ( v . name ) . index } ` ) }
. map ( ( v ) = > p ` ${ v . export_name } : ${ renderer . context_lookup . get ( v . name ) . index } ` ) }
} ` as ObjectExpression;
} ` );
let dirty ;
let dirty ;
if ( renderer . context_overflow ) {
if ( renderer . context_overflow ) {
dirty = x ` [] ` ;
dirty = x ` [] ` ;
for ( let i = 0 ; i < renderer . context . length ; i += 31 ) {
for ( let i = 0 ; i < renderer . context . length ; i += 31 ) {
dirty . elements . push ( x ` -1 ` ) ;
dirty . elements . push ( x ` -1 ` ) ;
}
}
}
}
const superclass = {
const superclass = {
type : 'Identifier' ,
type : 'Identifier' ,
name : options.dev ? '@SvelteComponentDev' : '@SvelteComponent'
name : options.dev ? '@SvelteComponentDev' : '@SvelteComponent'
} ;
} ;
const optional_parameters = [ ] ;
const optional_parameters = [ ] ;
if ( should_add_css ) {
if ( should_add_css ) {
optional_parameters . push ( add_css ) ;
optional_parameters . push ( add_css ) ;
} else if ( dirty ) {
}
optional_parameters . push ( x ` null ` ) ;
else if ( dirty ) {
optional_parameters . push ( x ` null ` ) ;
}
}
if ( dirty ) {
if ( dirty ) {
optional_parameters . push ( dirty ) ;
optional_parameters . push ( dirty ) ;
}
}
const declaration = /** @type {import('estree').ClassDeclaration} */ ( b `
const declaration = b `
class $ { name } extends $ { superclass } {
class $ { name } extends $ { superclass } {
constructor ( options ) {
constructor ( options ) {
super ( $ { options . dev && 'options' } ) ;
super ( $ { options . dev && 'options' } ) ;
@init ( this , options , $ { definition } , $ {
@init ( this , options , $ { definition } , $ { has_create_fragment ? 'create_fragment' : 'null' } , $ { not_equal } , $ { prop_indexes } , $ { optional_parameters } ) ;
has_create_fragment ? 'create_fragment' : 'null'
$ { options . dev &&
} , $ { not_equal } , $ { prop_indexes } , $ { optional_parameters } ) ;
b ` @dispatch_dev("SvelteRegisterComponent", { component: this, tagName: " ${ name . name } ", options, id: create_fragment.name }); ` }
$ {
options . dev &&
b ` @dispatch_dev("SvelteRegisterComponent", { component: this, tagName: " ${ name . name } ", options, id: create_fragment.name }); `
}
}
}
}
}
` [0] as ClassDeclaration;
` [0]);
push_array ( declaration . body . body , accessors ) ;
push_array ( declaration . body . body , accessors ) ;
body . push ( declaration ) ;
body . push ( declaration ) ;
if ( options . customElement ) {
if ( options . customElement ) {
const props_str = writable_props . reduce ( ( def , prop ) = > {
const props_str = writable_props . reduce ( ( def , prop ) = > {
def [ prop . export_name ] =
def [ prop . export_name ] =
@ -645,25 +515,17 @@ export default function dom(
. filter ( ( accessor ) = > ! writable_props . some ( ( prop ) = > prop . export_name === accessor . key . name ) )
. filter ( ( accessor ) = > ! writable_props . some ( ( prop ) = > prop . export_name === accessor . key . name ) )
. map ( ( accessor ) = > ` " ${ accessor . key . name } " ` )
. map ( ( accessor ) = > ` " ${ accessor . key . name } " ` )
. join ( ',' ) ;
. join ( ',' ) ;
const use_shadow_dom =
const use_shadow_dom = component . component_options . customElement ? . shadow !== 'none' ? 'true' : 'false' ;
component . component_options . customElement ? . shadow !== 'none' ? 'true' : 'false' ;
if ( component . component_options . customElement ? . tag ) {
if ( component . component_options . customElement ? . tag ) {
body . push (
body . push ( b ` @_customElements.define(" ${ component . component_options . customElement . tag } ", @create_custom_element( ${ name } , ${ JSON . stringify ( props_str ) } , [ ${ slots_str } ], [ ${ accessors_str } ], ${ use_shadow_dom } )); ` ) ;
b ` @_customElements.define(" ${
}
component . component_options . customElement . tag
else {
} " , @create_custom_element ( $ { name } , $ { JSON . stringify (
body . push ( b ` @create_custom_element( ${ name } , ${ JSON . stringify ( props_str ) } , [ ${ slots_str } ], [ ${ accessors_str } ], ${ use_shadow_dom } ); ` ) ;
props_str
) } , [ $ { slots_str } ] , [ $ { accessors_str } ] , $ { use_shadow_dom } ) ) ; `
) ;
} else {
body . push (
b ` @create_custom_element( ${ name } , ${ JSON . stringify (
props_str
) } , [ $ { slots_str } ] , [ $ { accessors_str } ] , $ { use_shadow_dom } ) ; `
) ;
}
}
}
}
return { js : flatten ( body ) , css } ;
return { js : flatten ( body ) , css } ;
}
}