@@ -2,7 +2,13 @@ import path from 'node:path'
22import { parse as parseUrl } from 'node:url'
33import fs , { promises as fsp } from 'node:fs'
44import * 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'
612import MagicString from 'magic-string'
713import { toOutputFilePathInString } from '../build'
814import type { Plugin } from '../plugin'
@@ -36,6 +42,76 @@ export function registerCustomMime(): void {
3642 mrmime . mimes [ 'eot' ] = 'application/vnd.ms-fontobject'
3743}
3844
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+
39115/**
40116 * Also supports loading plain strings with import text from './foo.txt?raw'
41117 */
@@ -90,66 +166,8 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
90166 return `export default ${ JSON . stringify ( url ) } `
91167 } ,
92168
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 )
153171
154172 if ( s ) {
155173 return {
0 commit comments