import { Node } from 'estree-walker' ; import MagicString from 'magic-string' ; import { convertHtmlxToJsx } from '../htmlxtojsx' ; import { parseHtmlx } from '../utils/htmlxparser' ; import { ComponentDocumentation } from './nodes/ComponentDocumentation' ; import { ComponentEvents } from './nodes/ComponentEvents' ; import { EventHandler } from './nodes/event-handler' ; import { ExportedNames } from './nodes/ExportedNames' ; import { handleScopeAndResolveForSlot , handleScopeAndResolveLetVarForSlot } from './nodes/handleScopeAndResolveForSlot' ; import { ImplicitStoreValues } from './nodes/ImplicitStoreValues' ; import { Scripts } from './nodes/Scripts' ; import { SlotHandler } from './nodes/slot' ; import { Stores } from './nodes/Stores' ; import TemplateScope from './nodes/TemplateScope' ; import { processInstanceScriptContent } from './processInstanceScriptContent' ; import { processModuleScriptTag } from './processModuleScriptTag' ; import { ScopeStack } from './utils/Scope' ; import { Generics } from './nodes/Generics' ; import { addComponentExport } from './addComponentExport' ; import { createRenderFunction } from './createRenderFunction' ; type TemplateProcessResult = { uses$$props : boolean ; uses$$restProps : boolean ; uses$$slots : boolean ; slots : Map < string , Map < string , string > > ; scriptTag : Node ; moduleScriptTag : Node ; /** To be added later as a comment on the default class export */ componentDocumentation : ComponentDocumentation ; events : ComponentEvents ; resolvedStores : string [ ] ; usesAccessors : boolean ; } ; function processSvelteTemplate ( str , options ? ) { const { htmlxAst , tags } = parseHtmlx ( str . original , options ) ; let uses$$props = false ; let uses$$restProps = false ; let uses$$slots = false ; let usesAccessors = ! ! options . accessors ; const componentDocumentation = new ComponentDocumentation ( ) ; const isDeclaration = { value : false } ; const scopeStack = new ScopeStack ( ) ; const stores = new Stores ( scopeStack , str , isDeclaration ) ; const scripts = new Scripts ( htmlxAst ) ; const handleSvelteOptions = ( node ) => { for ( let i = 0 ; i < node . attributes . length ; i ++ ) { const optionName = node . attributes [ i ] . name ; const optionValue = node . attributes [ i ] . value ; switch ( optionName ) { case 'accessors' : if ( Array . isArray ( optionValue ) ) { if ( optionValue [ 0 ] . type === 'MustacheTag' ) { usesAccessors = optionValue [ 0 ] . expression . value ; } } else { usesAccessors = true ; } break ; } } } ; const handleIdentifier = ( node ) => { if ( node . name === '$$props' ) { uses$$props = true ; return ; } if ( node . name === '$$restProps' ) { uses$$restProps = true ; return ; } if ( node . name === '$$slots' ) { uses$$slots = true ; return ; } } ; const handleStyleTag = ( node ) => { str . remove ( node . start , node . end ) ; } ; const slotHandler = new SlotHandler ( str . original ) ; let templateScope = new TemplateScope ( ) ; const handleEach = ( node ) => { templateScope = templateScope . child ( ) ; if ( node . context ) { handleScopeAndResolveForSlotInner ( node . context , node . expression , node ) ; } } ; const handleAwait = ( node ) => { templateScope = templateScope . child ( ) ; if ( node . value ) { handleScopeAndResolveForSlotInner ( node . value , node . expression , node . then ) ; } if ( node . error ) { handleScopeAndResolveForSlotInner ( node . error , node . expression , node . catch ) ; } } ; const handleComponentLet = ( component ) => { templateScope = templateScope . child ( ) ; const lets = slotHandler . getSlotConsumerOfComponent ( component ) ; for ( const { letNode , slotName } of lets ) { handleScopeAndResolveLetVarForSlot ( { letNode , slotName , slotHandler , templateScope , component } ) ; } } ; const handleScopeAndResolveForSlotInner = ( identifierDef , initExpression , owner ) => { handleScopeAndResolveForSlot ( { identifierDef , initExpression , slotHandler , templateScope , owner } ) ; } ; const eventHandler = new EventHandler ( ) ; const onHtmlxWalk = ( node , parent , prop ) => { if ( prop == 'params' && ( parent . type == 'FunctionDeclaration' || parent . type == 'ArrowFunctionExpression' ) ) { isDeclaration . value = true ; } if ( prop == 'id' && parent . type == 'VariableDeclarator' ) { isDeclaration . value = true ; } switch ( node . type ) { case 'Comment' : componentDocumentation . handleComment ( node ) ; break ; case 'Options' : handleSvelteOptions ( node ) ; break ; case 'Identifier' : handleIdentifier ( node ) ; stores . handleIdentifier ( node , parent , prop ) ; eventHandler . handleIdentifier ( node , parent , prop ) ; break ; case 'Transition' : case 'Action' : case 'Animation' : stores . handleDirective ( node , str ) ; break ; case 'Slot' : slotHandler . handleSlot ( node , templateScope ) ; break ; case 'Style' : handleStyleTag ( node ) ; break ; case 'Element' : scripts . handleScriptTag ( node , parent ) ; break ; case 'BlockStatement' : scopeStack . push ( ) ; break ; case 'FunctionDeclaration' : scopeStack . push ( ) ; break ; case 'ArrowFunctionExpression' : scopeStack . push ( ) ; break ; case 'EventHandler' : eventHandler . handleEventHandler ( node , parent ) ; break ; case 'VariableDeclarator' : isDeclaration . value = true ; break ; case 'EachBlock' : handleEach ( node ) ; break ; case 'AwaitBlock' : handleAwait ( node ) ; break ; case 'InlineComponent' : handleComponentLet ( node ) ; break ; } } ; const onHtmlxLeave = ( node , parent , prop , _index ) => { if ( prop == 'params' && ( parent . type == 'FunctionDeclaration' || parent . type == 'ArrowFunctionExpression' ) ) { isDeclaration . value = false ; } if ( prop == 'id' && parent . type == 'VariableDeclarator' ) { isDeclaration . value = false ; } const onTemplateScopeLeave = ( ) => { templateScope = templateScope . parent ; } ; switch ( node . type ) { case 'BlockStatement' : scopeStack . pop ( ) ; break ; case 'FunctionDeclaration' : scopeStack . pop ( ) ; break ; case 'ArrowFunctionExpression' : scopeStack . pop ( ) ; break ; case 'EachBlock' : onTemplateScopeLeave ( ) ; break ; case 'AwaitBlock' : onTemplateScopeLeave ( ) ; break ; case 'InlineComponent' : onTemplateScopeLeave ( ) ; break ; } } ; convertHtmlxToJsx ( str , htmlxAst , onHtmlxWalk , onHtmlxLeave , { preserveAttributeCase : options ?. namespace == 'foreign' } ) ; const { scriptTag , moduleScriptTag } = scripts . getTopLevelScriptTags ( ) ; scripts . blankOtherScriptTags ( str ) ; const resolvedStores = stores . resolveStores ( ) ; return { moduleScriptTag , scriptTag , slots : slotHandler . getSlotDef ( ) , events : new ComponentEvents ( eventHandler , tags . some ( ( tag ) => tag . attributes ?. some ( ( a ) => a . name === 'strictEvents' ) ) , str ) , uses$$props , uses$$restProps , uses$$slots , componentDocumentation , resolvedStores , usesAccessors } ; } export function svelte2tsx ( svelte , options = { } ) { const str = new MagicString ( svelte ) ; let { moduleScriptTag , scriptTag , slots , uses$$props , uses$$slots , uses$$restProps , events , componentDocumentation , resolvedStores , usesAccessors } = processSvelteTemplate ( str , options ) ; let instanceScriptTarget = 0 ; if ( moduleScriptTag ) { if ( moduleScriptTag . start != 0 ) { str . move ( moduleScriptTag . start , moduleScriptTag . end , 0 ) ; } else { instanceScriptTarget = moduleScriptTag . end ; } } const renderFunctionStart = scriptTag ? str . original . lastIndexOf ( '>' , scriptTag . content . start ) + 1 : instanceScriptTarget ; const implicitStoreValues = new ImplicitStoreValues ( resolvedStores , renderFunctionStart ) ; let exportedNames = new ExportedNames ( str , 0 ) ; let generics = new Generics ( str , 0 ) ; let uses$$SlotsInterface = false ; if ( scriptTag ) { if ( scriptTag . start != instanceScriptTarget ) { str . move ( scriptTag . start , scriptTag . end , instanceScriptTarget ) ; } const res = processInstanceScriptContent ( str , scriptTag , events , implicitStoreValues , options . mode ) ; uses$$props = uses$$props || res . uses$$props ; uses$$restProps = uses$$restProps || res . uses$$restProps ; uses$$slots = uses$$slots || res . uses$$slots ; ( { exportedNames , events , generics , uses$$SlotsInterface } = res ) ; } createRenderFunction ( { str , scriptTag , scriptDestination : instanceScriptTarget , slots , events , exportedNames , isTsFile : options ?. isTsFile , uses$$props , uses$$restProps , uses$$slots , uses$$SlotsInterface , generics , mode : options . mode } ) ; if ( moduleScriptTag ) { processModuleScriptTag ( str , moduleScriptTag , new ImplicitStoreValues ( implicitStoreValues . getAccessedStores ( ) , renderFunctionStart , scriptTag ? undefined : ( input ) => `</>;${ input }<>` ) ) ; } addComponentExport ( { str , canHaveAnyProp : ! exportedNames . uses$$Props && ( uses$$props || uses$$restProps ) , strictEvents : events . hasStrictEvents ( ) , isTsFile : options ?. isTsFile , exportedNames , usesAccessors , fileName : options ?. filename , componentDocumentation , mode : options . mode , generics } ) ; if ( options . mode === 'dts' ) { str . prepend ( 'import { SvelteComponentTyped } from "svelte"\n' + ( options ?. isTsFile ? '' : `
declare function __sveltets_1_createSvelteComponentTyped<Props, Events, Slots>(
    render: {props: Props, events: Events, slots: Slots }
): SvelteComponentConstructor<SvelteComponentTyped<Props, Events, Slots>,Svelte2TsxComponentConstructorParameters<Props>>;
` ) + '\n' ) ; let code = str . toString ( ) ; code = code . replace ( '<></>;' , '' ) . replace ( '<></>;' , '' ) . replace ( /<>.*<\/>/s , '' ) . replace ( '\n() => ();' , '' ) ; return { code } ; } else { str . prepend ( '///<reference types="svelte" />\n' ) ; return { code : str . toString ( ) , map : str . generateMap ( { hires : true , source : options ?. filename } ) , exportedNames : exportedNames . getExportsMap ( ) , events : events . createAPI ( ) } ; } }