@@ -2,7 +2,13 @@ import path from 'node:path'
2
2
import { parse as parseUrl } from 'node:url'
3
3
import fs , { promises as fsp } from 'node:fs'
4
4
import * as mrmime from 'mrmime'
5
- import type { OutputOptions , PluginContext , PreRenderedAsset } from 'rollup'
5
+ import type {
6
+ NormalizedOutputOptions ,
7
+ OutputOptions ,
8
+ PluginContext ,
9
+ PreRenderedAsset ,
10
+ RenderedChunk
11
+ } from 'rollup'
6
12
import MagicString from 'magic-string'
7
13
import { toOutputFilePathInString } from '../build'
8
14
import type { Plugin } from '../plugin'
@@ -36,6 +42,76 @@ export function registerCustomMime(): void {
36
42
mrmime . mimes [ 'eot' ] = 'application/vnd.ms-fontobject'
37
43
}
38
44
45
+ export function renderAssetUrlInJS (
46
+ ctx : PluginContext ,
47
+ config : ResolvedConfig ,
48
+ chunk : RenderedChunk ,
49
+ opts : NormalizedOutputOptions ,
50
+ code : string
51
+ ) : MagicString | undefined {
52
+ let match : RegExpExecArray | null
53
+ let s : MagicString | undefined
54
+
55
+ // Urls added with JS using e.g.
56
+ // imgElement.src = "__VITE_ASSET__5aa0ddc0__" are using quotes
57
+
58
+ // Urls added in CSS that is imported in JS end up like
59
+ // var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
60
+
61
+ // In both cases, the wrapping should already be fine
62
+
63
+ while ( ( match = assetUrlRE . exec ( code ) ) ) {
64
+ s ||= new MagicString ( code )
65
+ const [ full , hash , postfix = '' ] = match
66
+ // some internal plugins may still need to emit chunks (e.g. worker) so
67
+ // fallback to this.getFileName for that. TODO: remove, not needed
68
+ const file = getAssetFilename ( hash , config ) || ctx . getFileName ( hash )
69
+ chunk . viteMetadata . importedAssets . add ( cleanUrl ( file ) )
70
+ const filename = file + postfix
71
+ const replacement = toOutputFilePathInString (
72
+ filename ,
73
+ 'asset' ,
74
+ chunk . fileName ,
75
+ 'js' ,
76
+ config ,
77
+ opts . format
78
+ )
79
+ const replacementString =
80
+ typeof replacement === 'string'
81
+ ? JSON . stringify ( replacement ) . slice ( 1 , - 1 )
82
+ : `"+${ replacement . runtime } +"`
83
+ s . overwrite ( match . index , match . index + full . length , replacementString , {
84
+ contentOnly : true
85
+ } )
86
+ }
87
+
88
+ // Replace __VITE_PUBLIC_ASSET__5aa0ddc0__ with absolute paths
89
+
90
+ const publicAssetUrlMap = publicAssetUrlCache . get ( config ) !
91
+ while ( ( match = publicAssetUrlRE . exec ( code ) ) ) {
92
+ s ||= new MagicString ( code )
93
+ const [ full , hash ] = match
94
+ const publicUrl = publicAssetUrlMap . get ( hash ) ! . slice ( 1 )
95
+ const replacement = toOutputFilePathInString (
96
+ publicUrl ,
97
+ 'public' ,
98
+ chunk . fileName ,
99
+ 'js' ,
100
+ config ,
101
+ opts . format
102
+ )
103
+ const replacementString =
104
+ typeof replacement === 'string'
105
+ ? JSON . stringify ( replacement ) . slice ( 1 , - 1 )
106
+ : `"+${ replacement . runtime } +"`
107
+ s . overwrite ( match . index , match . index + full . length , replacementString , {
108
+ contentOnly : true
109
+ } )
110
+ }
111
+
112
+ return s
113
+ }
114
+
39
115
/**
40
116
* Also supports loading plain strings with import text from './foo.txt?raw'
41
117
*/
@@ -90,66 +166,8 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
90
166
return `export default ${ JSON . stringify ( url ) } `
91
167
} ,
92
168
93
- renderChunk ( code , chunk , outputOptions ) {
94
- let match : RegExpExecArray | null
95
- let s : MagicString | undefined
96
-
97
- // Urls added with JS using e.g.
98
- // imgElement.src = "__VITE_ASSET__5aa0ddc0__" are using quotes
99
-
100
- // Urls added in CSS that is imported in JS end up like
101
- // var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
102
-
103
- // In both cases, the wrapping should already be fine
104
-
105
- while ( ( match = assetUrlRE . exec ( code ) ) ) {
106
- s = s || ( s = new MagicString ( code ) )
107
- const [ full , hash , postfix = '' ] = match
108
- // some internal plugins may still need to emit chunks (e.g. worker) so
109
- // fallback to this.getFileName for that. TODO: remove, not needed
110
- const file = getAssetFilename ( hash , config ) || this . getFileName ( hash )
111
- chunk . viteMetadata . importedAssets . add ( cleanUrl ( file ) )
112
- const filename = file + postfix
113
- const replacement = toOutputFilePathInString (
114
- filename ,
115
- 'asset' ,
116
- chunk . fileName ,
117
- 'js' ,
118
- config ,
119
- outputOptions . format
120
- )
121
- const replacementString =
122
- typeof replacement === 'string'
123
- ? JSON . stringify ( replacement ) . slice ( 1 , - 1 )
124
- : `"+${ replacement . runtime } +"`
125
- s . overwrite ( match . index , match . index + full . length , replacementString , {
126
- contentOnly : true
127
- } )
128
- }
129
-
130
- // Replace __VITE_PUBLIC_ASSET__5aa0ddc0__ with absolute paths
131
-
132
- const publicAssetUrlMap = publicAssetUrlCache . get ( config ) !
133
- while ( ( match = publicAssetUrlRE . exec ( code ) ) ) {
134
- s = s || ( s = new MagicString ( code ) )
135
- const [ full , hash ] = match
136
- const publicUrl = publicAssetUrlMap . get ( hash ) ! . slice ( 1 )
137
- const replacement = toOutputFilePathInString (
138
- publicUrl ,
139
- 'public' ,
140
- chunk . fileName ,
141
- 'js' ,
142
- config ,
143
- outputOptions . format
144
- )
145
- const replacementString =
146
- typeof replacement === 'string'
147
- ? JSON . stringify ( replacement ) . slice ( 1 , - 1 )
148
- : `"+${ replacement . runtime } +"`
149
- s . overwrite ( match . index , match . index + full . length , replacementString , {
150
- contentOnly : true
151
- } )
152
- }
169
+ renderChunk ( code , chunk , opts ) {
170
+ const s = renderAssetUrlInJS ( this , config , chunk , opts , code )
153
171
154
172
if ( s ) {
155
173
return {
0 commit comments