@@ -18,6 +18,7 @@ import {
18
18
SFCTemplateCompileOptions ,
19
19
SFCTemplateCompileResults ,
20
20
SFCAsyncStyleCompileOptions ,
21
+ generateCssVars ,
21
22
} from '@vue/compiler-sfc'
22
23
import fs from 'fs'
23
24
import createDebugger from 'debug'
@@ -117,7 +118,7 @@ export default function PluginVue(userOptions: Partial<Options> = {}): Plugin {
117
118
query . type === 'template'
118
119
?descriptor . template !
119
120
:query . type === 'script'
120
- ?descriptor . script !
121
+ ?descriptor . scriptCompiled || descriptor . script
121
122
:query . type === 'style'
122
123
?descriptor . styles [ query . index ]
123
124
:typeof query . index === 'number'
@@ -142,36 +143,16 @@ export default function PluginVue(userOptions: Partial<Options> = {}): Plugin {
142
143
if ( ! query . src && ! filter ( query . filename ) ) return null
143
144
144
145
const descriptor = getDescriptor ( query . filename )
145
- const hasScoped = descriptor . styles . some ( ( s ) => s . scoped )
146
146
if ( query . src ) {
147
147
this . addWatchFile ( query . filename )
148
148
}
149
149
150
150
if ( query . type === 'template' ) {
151
151
debug ( `transform(${ id } )` )
152
- const block = descriptor . template !
153
- const preprocessLang = block . lang
154
- const preprocessOptions =
155
- preprocessLang &&
156
- options . templatePreprocessOptions &&
157
- options . templatePreprocessOptions [ preprocessLang ]
158
152
const result = compileTemplate ( {
159
- filename : query . filename ,
153
+ ... getTemplateCompilerOptions ( options , descriptor , query . id ) ,
160
154
source :code ,
161
- inMap :query . src ?undefined :block . map ,
162
- preprocessLang,
163
- preprocessOptions,
164
- preprocessCustomRequire :options . preprocessCustomRequire ,
165
- compiler :options . compiler ,
166
- ssr :isServer ,
167
- compilerOptions :{
168
- ...options . compilerOptions ,
169
- scopeId :hasScoped ?`data-v-${ query . id } ` :undefined ,
170
- bindingMetadata :descriptor . script
171
- ?descriptor . script . bindings
172
- :undefined ,
173
- } ,
174
- transformAssetUrls :options . transformAssetUrls ,
155
+ filename :query . filename ,
175
156
} )
176
157
177
158
if ( result . errors . length ) {
@@ -232,10 +213,10 @@ export default function PluginVue(userOptions: Partial<Options> = {}): Plugin {
232
213
233
214
const result = await compileStyleAsync ( {
234
215
filename :query . filename ,
235
- id :`data-v-${ query . id ! } ` ,
216
+ id :`data-v-${ query . id } ` ,
217
+ isProd :isProduction ,
236
218
source :code ,
237
219
scoped :block . scoped ,
238
- vars :! ! block . vars ,
239
220
modules :! ! block . module ,
240
221
postcssOptions :options . postcssOptions ,
241
222
postcssPlugins :options . postcssPlugins ,
@@ -300,6 +281,47 @@ export default function PluginVue(userOptions: Partial<Options> = {}): Plugin {
300
281
}
301
282
}
302
283
284
+ function getTemplateCompilerOptions (
285
+ options :Options ,
286
+ descriptor :SFCDescriptor ,
287
+ scopeId :string
288
+ ) :Omit < SFCTemplateCompileOptions , 'source' > | undefined {
289
+ const block = descriptor . template
290
+ if ( ! block ) {
291
+ return
292
+ }
293
+
294
+ const isServer = options . target === 'node'
295
+ const isProduction =
296
+ process . env . NODE_ENV === 'production' || process . env . BUILD === 'production'
297
+ const hasScoped = descriptor . styles . some ( ( s ) => s . scoped )
298
+ const preprocessLang = block . lang
299
+ const preprocessOptions =
300
+ preprocessLang &&
301
+ options . templatePreprocessOptions &&
302
+ options . templatePreprocessOptions [ preprocessLang ]
303
+ return {
304
+ filename :descriptor . filename ,
305
+ inMap :block . src ?undefined :block . map ,
306
+ preprocessLang,
307
+ preprocessOptions,
308
+ preprocessCustomRequire :options . preprocessCustomRequire ,
309
+ compiler :options . compiler ,
310
+ ssr :isServer ,
311
+ compilerOptions :{
312
+ ...options . compilerOptions ,
313
+ scopeId :hasScoped ?`data-v-${ scopeId } ` :undefined ,
314
+ bindingMetadata :descriptor . scriptCompiled
315
+ ?descriptor . scriptCompiled . bindings
316
+ :undefined ,
317
+ ssrCssVars :isServer
318
+ ?generateCssVars ( descriptor , scopeId , isProduction )
319
+ :undefined ,
320
+ } ,
321
+ transformAssetUrls :options . transformAssetUrls ,
322
+ }
323
+ }
324
+
303
325
function createCustomBlockFilter (
304
326
queries ?:string [ ]
305
327
) :( type :string ) => boolean {
@@ -336,15 +358,15 @@ type Query =
336
358
filename :string
337
359
vue :true
338
360
type :'template'
339
- id ? :string
361
+ id :string
340
362
src ?:true
341
363
}
342
364
| {
343
365
filename :string
344
366
vue :true
345
367
type :'style'
346
368
index :number
347
- id ? :string
369
+ id :string
348
370
scoped ?:boolean
349
371
module ?:string | boolean
350
372
src ?:true
@@ -418,25 +440,39 @@ function transformVueSFC(
418
440
const shortFilePath = relative ( rootContext , resourcePath )
419
441
. replace ( / ^ ( \. \. [ \/ \\ ] ) + / , '' )
420
442
. replace ( / \\ / g, '/' )
421
- const id = hash ( isProduction ?shortFilePath + '\n' + code :shortFilePath )
443
+ const scopeId = hash (
444
+ isProduction ?shortFilePath + '\n' + code :shortFilePath
445
+ )
422
446
// feature information
423
447
const hasScoped = descriptor . styles . some ( ( s ) => s . scoped )
424
448
425
- const templateImport = ! descriptor . template
426
- ?''
427
- :getTemplateCode ( descriptor , resourcePath , id , hasScoped , isServer )
449
+ const hasTemplateImport =
450
+ descriptor . template &&
451
+ // script setup compiles template inline, do not import again
452
+ ( isServer || ! descriptor . scriptSetup )
453
+
454
+ const templateImport = hasTemplateImport
455
+ ?getTemplateCode ( descriptor , resourcePath , scopeId , isServer )
456
+ :''
428
457
429
- const renderReplace = ! descriptor . template
430
- ?''
431
- : isServer
432
- ? `script.ssrRender =ssrRender `
433
- :`script.render = render`
458
+ const renderReplace = hasTemplateImport
459
+ ?isServer
460
+ ? `script.ssrRender = ssrRender`
461
+ : `script.render =render `
462
+ :''
434
463
435
- const scriptImport = getScriptCode ( descriptor , resourcePath )
464
+ const scriptImport = getScriptCode (
465
+ descriptor ,
466
+ resourcePath ,
467
+ scopeId ,
468
+ isProduction ,
469
+ isServer ,
470
+ getTemplateCompilerOptions ( options , descriptor , scopeId )
471
+ )
436
472
const stylesCode = getStyleCode (
437
473
descriptor ,
438
474
resourcePath ,
439
- id ,
475
+ scopeId ,
440
476
options . preprocessStyles
441
477
)
442
478
const customBlocksCode = getCustomBlock (
@@ -452,7 +488,7 @@ function transformVueSFC(
452
488
renderReplace ,
453
489
]
454
490
if ( hasScoped ) {
455
- output . push ( `script.__scopeId =${ _ ( `data-v-${ id } ` ) } ` )
491
+ output . push ( `script.__scopeId =${ _ ( `data-v-${ scopeId } ` ) } ` )
456
492
}
457
493
if ( ! isProduction ) {
458
494
output . push ( `script.__file =${ _ ( shortFilePath ) } ` )
@@ -467,7 +503,6 @@ function getTemplateCode(
467
503
descriptor :SFCDescriptor ,
468
504
resourcePath :string ,
469
505
id :string ,
470
- hasScoped :boolean ,
471
506
isServer :boolean
472
507
) {
473
508
const renderFnName = isServer ?'ssrRender' :'render'
@@ -476,27 +511,39 @@ function getTemplateCode(
476
511
if ( descriptor . template ) {
477
512
const src = descriptor . template . src || resourcePath
478
513
const idQuery = `&id=${ id } `
479
- const scopedQuery = hasScoped ?`&scoped=true` :``
480
514
const srcQuery = descriptor . template . src ?`&src` :``
481
515
const attrsQuery = attrsToQuery ( descriptor . template . attrs , 'js' , true )
482
- const query = `?vue&type=template${ idQuery } ${ srcQuery } ${ scopedQuery } ${ attrsQuery } `
516
+ const query = `?vue&type=template${ idQuery } ${ srcQuery } ${ attrsQuery } `
483
517
templateRequest = _ ( src + query )
484
518
templateImport = `import {${ renderFnName } } from${ templateRequest } `
485
519
}
486
520
487
521
return templateImport
488
522
}
489
523
490
- function getScriptCode ( descriptor :SFCDescriptor , resourcePath :string ) {
524
+ function getScriptCode (
525
+ descriptor :SFCDescriptor ,
526
+ resourcePath :string ,
527
+ id :string ,
528
+ isProd :boolean ,
529
+ isServer :boolean ,
530
+ templateOptions ?:Partial < SFCTemplateCompileOptions >
531
+ ) {
491
532
let scriptImport = `const script = {}`
492
533
if ( descriptor . script || descriptor . scriptSetup ) {
493
534
if ( compileScript ) {
494
- descriptor . script = compileScript ( descriptor )
535
+ descriptor . scriptCompiled = compileScript ( descriptor , {
536
+ id,
537
+ isProd,
538
+ inlineTemplate :! isServer ,
539
+ templateOptions,
540
+ } )
495
541
}
496
- if ( descriptor . script ) {
497
- const src = descriptor . script . src || resourcePath
498
- const attrsQuery = attrsToQuery ( descriptor . script . attrs , 'js' )
499
- const srcQuery = descriptor . script . src ?`&src` :``
542
+ const script = descriptor . scriptCompiled || descriptor . script
543
+ if ( script ) {
544
+ const src = script . src || resourcePath
545
+ const attrsQuery = attrsToQuery ( script . attrs , 'js' )
546
+ const srcQuery = script . src ?`&src` :``
500
547
const query = `?vue&type=script${ srcQuery } ${ attrsQuery } `
501
548
const scriptRequest = _ ( src + query )
502
549
scriptImport =
@@ -527,7 +574,7 @@ function getStyleCode(
527
574
)
528
575
// make sure to only pass id when necessary so that we don't inject
529
576
// duplicate tags when multiple components import the same css file
530
- const idQuery = style . scoped ? `&id=${ id } ` : ` `
577
+ const idQuery = `&id=${ id } `
531
578
const srcQuery = style . src ?`&src` :``
532
579
const query = `?vue&type=style&index=${ i } ${ srcQuery } ${ idQuery } `
533
580
const styleRequest = src + query + attrsQuery