@@ -38,6 +38,7 @@ import {
38
38
isDataUrl ,
39
39
isExternalUrl ,
40
40
isObject ,
41
+ isRelativeBase ,
41
42
normalizePath ,
42
43
parseRequest ,
43
44
processSrcSet
@@ -48,7 +49,10 @@ import {
48
49
assetUrlRE ,
49
50
checkPublicFile ,
50
51
fileToUrl ,
51
- getAssetFilename
52
+ getAssetFilename ,
53
+ publicAssetUrlCache ,
54
+ publicAssetUrlRE ,
55
+ publicFileToBuiltUrl
52
56
} from './asset'
53
57
54
58
// const debug = createDebugger('vite:css')
@@ -106,6 +110,8 @@ const inlineCSSRE = /(\?|&)inline-css\b/
106
110
const usedRE = / ( \? | & ) u s e d \b /
107
111
const varRE = / ^ v a r \( / i
108
112
113
+ const cssBundleName = 'style.css'
114
+
109
115
const enum PreprocessLang {
110
116
less = 'less' ,
111
117
sass = 'sass' ,
@@ -183,7 +189,11 @@ export function cssPlugin(config: ResolvedConfig): Plugin {
183
189
184
190
const urlReplacer : CssUrlReplacer = async ( url , importer ) => {
185
191
if ( checkPublicFile ( url , config ) ) {
186
- return config . base + url . slice ( 1 )
192
+ if ( isRelativeBase ( config . base ) ) {
193
+ return publicFileToBuiltUrl ( url , config )
194
+ } else {
195
+ return config . base + url . slice ( 1 )
196
+ }
187
197
}
188
198
const resolved = await resolveUrl ( url , importer )
189
199
if ( resolved ) {
@@ -283,6 +293,30 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
283
293
let outputToExtractedCSSMap : Map < NormalizedOutputOptions , string >
284
294
let hasEmitted = false
285
295
296
+ const relativeBase = isRelativeBase ( config . base )
297
+
298
+ const rollupOptionsOutput = config . build . rollupOptions . output
299
+ const assetFileNames = (
300
+ Array . isArray ( rollupOptionsOutput )
301
+ ? rollupOptionsOutput [ 0 ]
302
+ : rollupOptionsOutput
303
+ ) ?. assetFileNames
304
+ const getCssAssetDirname = ( cssAssetName : string ) => {
305
+ if ( ! assetFileNames ) {
306
+ return config . build . assetsDir
307
+ } else if ( typeof assetFileNames === 'string' ) {
308
+ return path . dirname ( assetFileNames )
309
+ } else {
310
+ return path . dirname (
311
+ assetFileNames ( {
312
+ name : cssAssetName ,
313
+ type : 'asset' ,
314
+ source : '/* vite internal call, ignore */'
315
+ } )
316
+ )
317
+ }
318
+ }
319
+
286
320
return {
287
321
name : 'vite:css-post' ,
288
322
@@ -415,35 +449,42 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
415
449
return null
416
450
}
417
451
418
- // resolve asset URL placeholders to their built file URLs and perform
419
- // minification if necessary
420
- const processChunkCSS = async (
421
- css : string ,
422
- {
423
- inlined,
424
- minify
425
- } : {
426
- inlined : boolean
427
- minify : boolean
428
- }
429
- ) => {
452
+ const publicAssetUrlMap = publicAssetUrlCache . get ( config ) !
453
+
454
+ // resolve asset URL placeholders to their built file URLs
455
+ function resolveAssetUrlsInCss ( chunkCSS : string , cssAssetName : string ) {
456
+ const cssAssetDirname = relativeBase
457
+ ? getCssAssetDirname ( cssAssetName )
458
+ : undefined
459
+
430
460
// replace asset url references with resolved url.
431
- const isRelativeBase = config . base === '' || config . base . startsWith ( '.' )
432
- css = css . replace ( assetUrlRE , ( _ , fileHash , postfix = '' ) => {
461
+ chunkCSS = chunkCSS . replace ( assetUrlRE , ( _ , fileHash , postfix = '' ) => {
433
462
const filename = getAssetFilename ( fileHash , config ) + postfix
434
463
chunk . viteMetadata . importedAssets . add ( cleanUrl ( filename ) )
435
- if ( ! isRelativeBase || inlined ) {
436
- // absolute base or relative base but inlined (injected as style tag into
437
- // index.html) use the base as-is
438
- return config . base + filename
464
+ if ( relativeBase ) {
465
+ // relative base + extracted CSS
466
+ const relativePath = path . posix . relative ( cssAssetDirname ! , filename )
467
+ return relativePath . startsWith ( '.' )
468
+ ? relativePath
469
+ : './' + relativePath
439
470
} else {
440
- // relative base + extracted CSS - asset file will be in the same dir
441
- return `./ ${ path . posix . basename ( filename ) } `
471
+ // absolute base
472
+ return config . base + filename
442
473
}
443
474
} )
444
- // only external @imports and @charset should exist at this point
445
- css = await finalizeCss ( css , minify , config )
446
- return css
475
+ // resolve public URL from CSS paths
476
+ if ( relativeBase ) {
477
+ const relativePathToPublicFromCSS = path . posix . relative (
478
+ cssAssetDirname ! ,
479
+ ''
480
+ )
481
+ chunkCSS = chunkCSS . replace (
482
+ publicAssetUrlRE ,
483
+ ( _ , hash ) =>
484
+ relativePathToPublicFromCSS + publicAssetUrlMap . get ( hash ) !
485
+ )
486
+ }
487
+ return chunkCSS
447
488
}
448
489
449
490
if ( config . build . cssCodeSplit ) {
@@ -456,23 +497,25 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
456
497
opts . format === 'cjs' ||
457
498
opts . format === 'system'
458
499
) {
459
- chunkCSS = await processChunkCSS ( chunkCSS , {
460
- inlined : false ,
461
- minify : true
462
- } )
500
+ const cssAssetName = chunk . name + '.css'
501
+
502
+ chunkCSS = resolveAssetUrlsInCss ( chunkCSS , cssAssetName )
503
+ chunkCSS = await finalizeCss ( chunkCSS , true , config )
504
+
463
505
// emit corresponding css file
464
506
const fileHandle = this . emitFile ( {
465
- name : chunk . name + '.css' ,
507
+ name : cssAssetName ,
466
508
type : 'asset' ,
467
509
source : chunkCSS
468
510
} )
469
511
chunk . viteMetadata . importedCss . add ( this . getFileName ( fileHandle ) )
470
512
} else if ( ! config . build . ssr ) {
471
- // legacy build, inline css
472
- chunkCSS = await processChunkCSS ( chunkCSS , {
473
- inlined : true ,
474
- minify : true
475
- } )
513
+ // legacy build and inline css
514
+
515
+ // __VITE_ASSET__ and __VITE_PUBLIC_ASSET__ urls are processed by
516
+ // the vite:asset plugin, don't call resolveAssetUrlsInCss here
517
+ chunkCSS = await finalizeCss ( chunkCSS , true , config )
518
+
476
519
const style = `__vite_style__`
477
520
const injectCode =
478
521
`var ${ style } = document.createElement('style');` +
@@ -481,6 +524,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
481
524
if ( config . build . sourcemap ) {
482
525
const s = new MagicString ( code )
483
526
s . prepend ( injectCode )
527
+ // resolve public URL from CSS paths, we need to use absolute paths
484
528
return {
485
529
code : s . toString ( ) ,
486
530
map : s . generateMap ( { hires : true } )
@@ -490,11 +534,9 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
490
534
}
491
535
}
492
536
} else {
493
- // non-split extracted CSS will be minified together
494
- chunkCSS = await processChunkCSS ( chunkCSS , {
495
- inlined : false ,
496
- minify : false
497
- } )
537
+ chunkCSS = resolveAssetUrlsInCss ( chunkCSS , cssBundleName )
538
+ // finalizeCss is called for the aggregated chunk in generateBundle
539
+
498
540
outputToExtractedCSSMap . set (
499
541
opts ,
500
542
( outputToExtractedCSSMap . get ( opts ) || '' ) + chunkCSS
@@ -558,7 +600,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
558
600
hasEmitted = true
559
601
extractedCss = await finalizeCss ( extractedCss , true , config )
560
602
this . emitFile ( {
561
- name : 'style.css' ,
603
+ name : cssBundleName ,
562
604
type : 'asset' ,
563
605
source : extractedCss
564
606
} )
0 commit comments