import { store , File } from './store' ; import { SFCDescriptor , BindingMetadata } from '@vue/compiler-sfc' ; import * as defaultCompiler from '@vue/compiler-sfc' ; import { ref } from 'vue' ; export const MAIN_FILE = 'App.vue' ; export const COMP_IDENTIFIER = `__sfc__` ; /**
 * The default SFC compiler we are using is built from each commit
 * but we may swap it out with a version fetched from CDNs
 */ let SFCCompiler = defaultCompiler ; const defaultVueUrl = import . meta . env . PROD ? `${ location . origin }/vue.runtime.esm-browser.js` : `${ location . origin }/src/vue-dev-proxy` ; export const vueRuntimeUrl = ref ( defaultVueUrl ) ; export async function setVersion ( version ) { const compilerUrl = `https://unpkg.com/@vue/compiler-sfc@${ version }/dist/compiler-sfc.esm-browser.js` ; const runtimeUrl = `https://unpkg.com/@vue/runtime-dom@${ version }/dist/runtime-dom.esm-browser.js` ; const [ compiler ] = await Promise . all ( [ import ( compilerUrl ) , import ( runtimeUrl ) ] ) ; SFCCompiler = compiler ; vueRuntimeUrl . value = runtimeUrl ; console . info ( `Now using Vue version: ${ version }` ) ; } export function resetVersion ( ) { SFCCompiler = defaultCompiler ; vueRuntimeUrl . value = defaultVueUrl ; } export async function compileFile ( { filename , code , compiled } ) { if ( ! code . trim ( ) ) { store . errors = [ ] ; return ; } if ( ! filename . endsWith ( '.vue' ) ) { compiled . js = compiled . ssr = code ; store . errors = [ ] ; return ; } const id = await hashId ( filename ) ; const { errors , descriptor } = SFCCompiler . parse ( code , { filename , sourceMap : true } ) ; if ( errors . length ) { store . errors = errors ; return ; } if ( descriptor . styles . some ( s => s . lang ) || ( descriptor . template && descriptor . template . lang ) ) { store . errors = [ `lang="x" pre-processors for <template> or <style> are currently not ` + `supported.` ] ; return ; } const scriptLang = ( descriptor . script && descriptor . script . lang ) || ( descriptor . scriptSetup && descriptor . scriptSetup . lang ) ; if ( scriptLang && scriptLang !== 'ts' ) { store . errors = [ `Only lang="ts" is supported for <script> blocks.` ] ; return ; } const hasScoped = descriptor . styles . some ( s => s . scoped ) ; let clientCode = '' ; let ssrCode = '' ; const appendSharedCode = ( code ) => { clientCode += code ; ssrCode += code ; } ; const clientScriptResult = await doCompileScript ( descriptor , id , false ) ; if ( ! clientScriptResult ) { return ; } const [ clientScript , bindings ] = clientScriptResult ; clientCode += clientScript ; if ( descriptor . scriptSetup ) { const ssrScriptResult = await doCompileScript ( descriptor , id , true ) ; if ( ssrScriptResult ) { ssrCode += ssrScriptResult [ 0 ] ; } else { ssrCode = `/* SSR compile error: ${ store . errors [ 0 ] } */` ; } } else { ssrCode += clientScript ; } if ( descriptor . template && ! descriptor . scriptSetup ) { const clientTemplateResult = doCompileTemplate ( descriptor , id , bindings , false ) ; if ( ! clientTemplateResult ) { return ; } clientCode += clientTemplateResult ; const ssrTemplateResult = doCompileTemplate ( descriptor , id , bindings , true ) ; if ( ssrTemplateResult ) { ssrCode += ssrTemplateResult ; } else { ssrCode = `/* SSR compile error: ${ store . errors [ 0 ] } */` ; } } if ( hasScoped ) { appendSharedCode ( `\n${ COMP_IDENTIFIER }.__scopeId = ${ JSON . stringify ( `data-v-${ id }` ) }` ) ; } if ( clientCode || ssrCode ) { appendSharedCode ( `\n${ COMP_IDENTIFIER }.__file = ${ JSON . stringify ( filename ) }` + `\nexport default ${ COMP_IDENTIFIER }` ) ; compiled . js = clientCode . trimStart ( ) ; compiled . ssr = ssrCode . trimStart ( ) ; } let css = '' ; for ( const style of descriptor . styles ) { if ( style . module ) { store . errors = [ `<style module> is not supported in the playground.` ] ; return ; } const styleResult = await SFCCompiler . compileStyleAsync ( { source : style . content , filename , id , scoped : style . scoped , modules : ! ! style . module } ) ; if ( styleResult . errors . length ) { if ( ! styleResult . errors [ 0 ] . message . includes ( 'pathToFileURL' ) ) { store . errors = styleResult . errors ; } } else { css += styleResult . code + '\n' ; } } if ( css ) { compiled . css = css . trim ( ) ; } else { compiled . css = '/* No <style> tags present */' ; } store . errors = [ ] ; } async function doCompileScript ( descriptor , id , ssr ) { if ( descriptor . script || descriptor . scriptSetup ) { try { const compiledScript = SFCCompiler . compileScript ( descriptor , { id , refSugar : true , inlineTemplate : true , templateOptions : { ssr , ssrCssVars : descriptor . cssVars } } ) ; let code = '' ; if ( compiledScript . bindings ) { code += `\n/* Analyzed bindings: ${ JSON . stringify ( compiledScript . bindings , null , 2 ) } */` ; } code += `\n` + SFCCompiler . rewriteDefault ( compiledScript . content , COMP_IDENTIFIER ) ; if ( ( descriptor . script || descriptor . scriptSetup ) ! . lang === 'ts' ) { code = ( await import ( 'sucrase' ) ) . transform ( code , { transforms : [ 'typescript' ] } ) . code ; } return [ code , compiledScript . bindings ] ; } catch ( e ) { store . errors = [ e . stack . split ( '\n' ) . slice ( 0 , 12 ) . join ( '\n' ) ] ; return ; } } else { return [ `\nconst ${ COMP_IDENTIFIER } = {}` , undefined ] ; } } function doCompileTemplate ( descriptor , id , bindingMetadata , ssr ) { const templateResult = SFCCompiler . compileTemplate ( { source : descriptor . template ! . content , filename : descriptor . filename , id , scoped : descriptor . styles . some ( s => s . scoped ) , slotted : descriptor . slotted , ssr , ssrCssVars : descriptor . cssVars , isProd : false , compilerOptions : { bindingMetadata } } ) ; if ( templateResult . errors . length ) { store . errors = templateResult . errors ; return ; } const fnName = ssr ? `ssrRender` : `render` ; return ( `\n${ templateResult . code . replace ( /\nexport (function|const) (render|ssrRender)/ , `$1 ${ fnName }` ) }` + `\n${ COMP_IDENTIFIER }.${ fnName } = ${ fnName }` ) ; } async function hashId ( filename ) { const msgUint8 = new TextEncoder ( ) . encode ( filename ) ; const hashBuffer = await crypto . subtle . digest ( 'SHA-256' , msgUint8 ) ; const hashArray = Array . from ( new Uint8Array ( hashBuffer ) ) ; const hashHex = hashArray . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ; return hashHex . slice ( 0 , 8 ) ; }