@@ -14,6 +14,7 @@ import { createRollupError } from './utils/error'
1414import { resolveScript } from './script'
1515import { transformTemplateInMain } from './template'
1616import { isOnlyTemplateChanged } from './handleHotUpdate'
17+ import { RawSourceMap , SourceMapConsumer , SourceMapGenerator } from 'source-map'
1718
1819export async function genSfcFacade (
1920 code : string ,
@@ -30,7 +31,6 @@ export async function genSfcFacade(
3031 const { descriptor, errors } = parse ( code , {
3132 sourceMap : true ,
3233 filename,
33- sourceRoot,
3434 } )
3535 setDescriptor ( filename , descriptor )
3636
@@ -68,9 +68,17 @@ export async function genSfcFacade(
6868 ! ( descriptor . template && descriptor . template . src )
6969 const hasTemplateImport = descriptor . template && ! useInlineTemplate
7070
71- const templateCode = hasTemplateImport
72- ? genTemplateCode ( descriptor , scopeId , options , isServer , pluginContext )
73- : ''
71+ let templateCode = ''
72+ let templateMap
73+ if ( hasTemplateImport ) {
74+ ; ( { code : templateCode , map : templateMap } = genTemplateCode (
75+ descriptor ,
76+ scopeId ,
77+ options ,
78+ isServer ,
79+ pluginContext
80+ ) )
81+ }
7482
7583 const renderReplace = hasTemplateImport
7684 ? isServer
@@ -128,9 +136,34 @@ export async function genSfcFacade(
128136 )
129137 }
130138
139+ // if the template is inlined into the main module (indicated by the presence
140+ // of templateMap, we need to concatenate the two source maps.
141+ let resolvedMap = map
142+ if ( map && templateMap ) {
143+ const generator = SourceMapGenerator . fromSourceMap (
144+ new SourceMapConsumer ( map )
145+ )
146+ const offset = scriptCode . match ( / \r ? \n / g) ?. length || 1
147+ const templateMapConsumer = new SourceMapConsumer ( templateMap )
148+ templateMapConsumer . eachMapping ( ( m ) => {
149+ generator . addMapping ( {
150+ source : m . source ,
151+ original : { line : m . originalLine , column : m . originalColumn } ,
152+ generated : {
153+ line : m . generatedLine + offset ,
154+ column : m . generatedColumn ,
155+ } ,
156+ } )
157+ } )
158+ resolvedMap = ( generator as any ) . toJSON ( )
159+ // if this is a template only update, we will be reusing a cached version
160+ // of the main module compile result, which has outdated sourcesContent.
161+ resolvedMap . sourcesContent = templateMap . sourcesContent
162+ }
163+
131164 return {
132165 code : output . join ( '\n' ) ,
133- map : map || {
166+ map : resolvedMap || {
134167 mappings : '' ,
135168 } ,
136169 }
@@ -146,6 +179,9 @@ function genTemplateCode(
146179 const renderFnName = isServer ? 'ssrRender' : 'render'
147180 const template = descriptor . template !
148181
182+ // If the template is not using pre-processor AND is not using external src,
183+ // compile and inline it directly in the main module. When served in vite this
184+ // saves an extra request per SFC which can improve load performance.
149185 if ( ! template . lang && ! template . src ) {
150186 return transformTemplateInMain (
151187 template . content ,
@@ -160,9 +196,12 @@ function genTemplateCode(
160196 const srcQuery = template . src ? `&src` : ``
161197 const attrsQuery = attrsToQuery ( template . attrs , 'js' , true )
162198 const query = `?vue&type=template${ idQuery } ${ srcQuery } ${ attrsQuery } `
163- return `import { ${ renderFnName } as _sfc_${ renderFnName } } from ${ JSON . stringify (
164- src + query
165- ) } `
199+ return {
200+ code : `import { ${ renderFnName } as _sfc_${ renderFnName } } from ${ JSON . stringify (
201+ src + query
202+ ) } `,
203+ map : undefined ,
204+ }
166205 }
167206}
168207
@@ -173,7 +212,10 @@ async function genScriptCode(
173212 isServer : boolean ,
174213 options : Options ,
175214 pluginContext : TransformPluginContext
176- ) {
215+ ) : Promise < {
216+ code : string
217+ map : RawSourceMap
218+ } > {
177219 let scriptCode = `const _sfc_main = {}`
178220 let map
179221 const script = resolveScript (
@@ -185,7 +227,8 @@ async function genScriptCode(
185227 pluginContext
186228 )
187229 if ( script ) {
188- // js or ts can be directly placed in the main module
230+ // If the script is js/ts and has no external src, it can be directly placed
231+ // in the main module.
189232 if (
190233 ( ! script . lang ||
191234 ( script . lang === 'ts' && ( pluginContext as any ) . server ) ) &&
0 commit comments