1- import chalk from 'chalk'
21import path from 'path'
32import { parse as parseUrl } from 'url'
43import fs , { promises as fsp } from 'fs'
54import mime from 'mime/lite'
65import { Plugin } from '../plugin'
76import { ResolvedConfig } from '../config'
8- import { createDebugger , cleanUrl } from '../utils'
7+ import { cleanUrl } from '../utils'
98import { FS_PREFIX } from '../constants'
109import { PluginContext } from 'rollup'
1110import MagicString from 'magic-string'
1211
13- const debug = createDebugger ( 'vite:asset' )
14-
1512export const assetUrlRE = / " _ _ V I T E _ A S S E T _ _ ( \w + ) (?: _ _ ( .* ) _ _ ) ? " / g
1613
17- export function isPublicFile ( url : string , root : string ) : string | undefined {
18- // note if the file is in /public, the resolver would have returned it
19- // as-is so it's not going to be a fully resolved path.
20- if ( ! url . startsWith ( '/' ) ) {
21- return
22- }
23- const publicFile = path . posix . join ( root , 'public' , cleanUrl ( url ) )
24- if ( fs . existsSync ( publicFile ) ) {
25- return publicFile
26- } else {
27- return
28- }
29- }
30-
3114/**
3215 * Also supports loading plain strings with import text from './foo.txt?raw'
3316 */
3417export function assetPlugin ( config : ResolvedConfig ) : Plugin {
35- const publicIdMap = new Map < string , string > ( )
36-
3718 return {
3819 name : 'vite:asset' ,
3920
@@ -43,65 +24,28 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
4324 }
4425 // imports to absolute urls pointing to files in /public
4526 // will fail to resolve in the main resolver. handle them here.
46- const publicFile = isPublicFile ( id , config . root )
27+ const publicFile = checkPublicFile ( id , config . root )
4728 if ( publicFile ) {
48- publicIdMap . set ( id , publicFile )
4929 return id
5030 }
5131 } ,
5232
5333 async load ( id ) {
54- let file = cleanUrl ( id )
55- if ( ! config . assetsInclude ( file ) ) {
34+ if ( ! config . assetsInclude ( cleanUrl ( id ) ) ) {
5635 return
5736 }
5837
59- const publicFile = publicIdMap . get ( id )
60- if ( publicFile ) {
61- file = publicFile
62- }
63-
38+ // raw requests, read from disk
6439 if ( / ( \? | & ) r a w \b / . test ( id ) ) {
65- debug ( `[raw] ${ chalk . dim ( file ) } ` )
40+ const file = checkPublicFile ( id , config . root ) || cleanUrl ( id )
6641 // raw query, read file and return as string
6742 return `export default ${ JSON . stringify (
6843 await fsp . readFile ( file , 'utf-8' )
6944 ) } `
7045 }
7146
72- debug ( `[import] ${ chalk . dim ( file ) } ` )
73-
74- // serve
75- if ( config . command === 'serve' ) {
76- let publicPath
77- if ( publicFile ) {
78- // in public dir, keep the url as-is
79- publicPath = id
80- } else if ( id . startsWith ( config . root ) ) {
81- // in project root, infer short public path
82- publicPath = '/' + path . posix . relative ( config . root , id )
83- } else {
84- // outside of project root, use absolute fs path
85- // (this is speical handled by the serve static middleware
86- publicPath = FS_PREFIX + id
87- }
88- return `export default ${ JSON . stringify ( publicPath ) } `
89- }
90-
91- // build
92- if ( publicFile ) {
93- // in public dir, will be copied over to the same url, but need to
94- // account for base config
95- return `export default ${ JSON . stringify (
96- config . build . base + id . slice ( 1 )
97- ) } `
98- } else {
99- return `export default ${ await registerBuildAssetFromFile (
100- id ,
101- config ,
102- this
103- ) } `
104- }
47+ const url = await fileToUrl ( id , config , this )
48+ return `export default ${ JSON . stringify ( url ) } `
10549 } ,
10650
10751 renderChunk ( code ) {
@@ -130,19 +74,44 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
13074 }
13175}
13276
133- export async function registerBuildAsset (
134- url : string ,
135- importer : string ,
77+ export function checkPublicFile ( url : string , root : string ) : string | undefined {
78+ // note if the file is in /public, the resolver would have returned it
79+ // as-is so it's not going to be a fully resolved path.
80+ if ( ! url . startsWith ( '/' ) ) {
81+ return
82+ }
83+ const publicFile = path . posix . join ( root , 'public' , cleanUrl ( url ) )
84+ if ( fs . existsSync ( publicFile ) ) {
85+ return publicFile
86+ } else {
87+ return
88+ }
89+ }
90+
91+ export function fileToUrl (
92+ id : string ,
13693 config : ResolvedConfig ,
137- pluginContext : PluginContext
138- ) : Promise < string > {
139- if ( isPublicFile ( url , config . root ) ) {
140- return config . build . base + url . slice ( 1 )
94+ ctx : PluginContext
95+ ) {
96+ if ( config . command === 'serve' ) {
97+ return fileToDevUrl ( id , config )
98+ } else {
99+ return fileToBuiltUrl ( id , config , ctx )
141100 }
142- const file = url . startsWith ( '/' )
143- ? path . join ( config . root , url )
144- : path . join ( path . dirname ( importer ) , url )
145- return registerBuildAssetFromFile ( file , config , pluginContext )
101+ }
102+
103+ function fileToDevUrl ( id : string , { root } : ResolvedConfig ) {
104+ if ( checkPublicFile ( id , root ) ) {
105+ // in public dir, keep the url as-is
106+ return id
107+ }
108+ if ( id . startsWith ( root ) ) {
109+ // in project root, infer short public path
110+ return '/' + path . posix . relative ( root , id )
111+ }
112+ // outside of project root, use absolute fs path
113+ // (this is speical handled by the serve static middleware
114+ return FS_PREFIX + id
146115}
147116
148117const assetCache = new WeakMap < ResolvedConfig , Map < string , string > > ( )
@@ -151,11 +120,16 @@ const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
151120 * Register an asset to be emitted as part of the bundle (if necessary)
152121 * and returns the resolved public URL
153122 */
154- async function registerBuildAssetFromFile (
123+ async function fileToBuiltUrl (
155124 id : string ,
156125 config : ResolvedConfig ,
157- pluginContext : PluginContext
126+ pluginContext : PluginContext ,
127+ skipPublicCheck = false
158128) : Promise < string > {
129+ if ( ! skipPublicCheck && checkPublicFile ( id , config . root ) ) {
130+ return config . build . base + id . slice ( 1 )
131+ }
132+
159133 let cache = assetCache . get ( config )
160134 if ( ! cache ) {
161135 cache = new Map ( )
@@ -178,9 +152,7 @@ async function registerBuildAssetFromFile(
178152 content . length < Number ( config . build . assetsInlineLimit )
179153 ) {
180154 // base64 inlined as a string
181- url = JSON . stringify (
182- `data:${ mime . getType ( file ) } ;base64,${ content . toString ( 'base64' ) } `
183- )
155+ url = `data:${ mime . getType ( file ) } ;base64,${ content . toString ( 'base64' ) } `
184156 } else {
185157 // emit as asset
186158 // rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
@@ -192,11 +164,30 @@ async function registerBuildAssetFromFile(
192164 type : 'asset' ,
193165 source : content
194166 } )
195- url = JSON . stringify (
196- `__VITE_ASSET__${ fileId } ${ postfix ? `__${ postfix } __` : `` } `
197- )
167+ url = `__VITE_ASSET__${ fileId } ${ postfix ? `__${ postfix } __` : `` } `
198168 }
199169
200170 cache . set ( id , url )
201171 return url
202172}
173+
174+ export async function urlToBuiltUrl (
175+ url : string ,
176+ importer : string ,
177+ config : ResolvedConfig ,
178+ pluginContext : PluginContext
179+ ) : Promise < string > {
180+ if ( checkPublicFile ( url , config . root ) ) {
181+ return config . build . base + url . slice ( 1 )
182+ }
183+ const file = url . startsWith ( '/' )
184+ ? path . join ( config . root , url )
185+ : path . join ( path . dirname ( importer ) , url )
186+ return fileToBuiltUrl (
187+ file ,
188+ config ,
189+ pluginContext ,
190+ // skip public check since we just did it above
191+ true
192+ )
193+ }
0 commit comments