@@ -2,12 +2,15 @@ import { transform } from 'esbuild'
2
2
import { TraceMap , decodedMap , encodedMap } from '@jridgewell/trace-mapping'
3
3
import type { ResolvedConfig } from '../config'
4
4
import type { Plugin } from '../plugin'
5
- import { escapeRegex , getHash } from '../utils'
5
+ import { escapeRegex } from '../utils'
6
6
import { isCSSRequest } from './css'
7
7
import { isHTMLRequest } from './html'
8
8
9
9
const nonJsRe = / \. j s o n (?: $ | \? ) /
10
10
const isNonJsRequest = ( request : string ) : boolean => nonJsRe . test ( request )
11
+ const importMetaEnvMarker = '__vite_import_meta_env__'
12
+ const bareImportMetaEnvRe = new RegExp ( `${ importMetaEnvMarker } (?!\\.)\\b` )
13
+ const importMetaEnvKeyRe = new RegExp ( `${ importMetaEnvMarker } \\..+?\\b` , 'g' )
11
14
12
15
export function definePlugin ( config : ResolvedConfig ) : Plugin {
13
16
const isBuild = config . command === 'build'
@@ -69,13 +72,16 @@ export function definePlugin(config: ResolvedConfig): Plugin {
69
72
define [ 'import.meta.env.SSR' ] = ssr + ''
70
73
}
71
74
if ( 'import.meta.env' in define ) {
72
- define [ 'import.meta.env' ] = serializeDefine ( {
73
- ...importMetaEnvKeys ,
74
- SSR : ssr + '' ,
75
- ...userDefineEnv ,
76
- } )
75
+ define [ 'import.meta.env' ] = importMetaEnvMarker
77
76
}
78
77
78
+ const importMetaEnvVal = serializeDefine ( {
79
+ ...importMetaEnvKeys ,
80
+ SSR : ssr + '' ,
81
+ ...userDefineEnv ,
82
+ } )
83
+ const banner = `const ${ importMetaEnvMarker } = ${ importMetaEnvVal } ;\n`
84
+
79
85
// Create regex pattern as a fast check before running esbuild
80
86
const patternKeys = Object . keys ( userDefine )
81
87
if ( replaceProcessEnv && Object . keys ( processEnv ) . length ) {
@@ -88,7 +94,7 @@ export function definePlugin(config: ResolvedConfig): Plugin {
88
94
? new RegExp ( patternKeys . map ( escapeRegex ) . join ( '|' ) )
89
95
: null
90
96
91
- return [ define , pattern ] as const
97
+ return [ define , pattern , banner ] as const
92
98
}
93
99
94
100
const defaultPattern = generatePattern ( false )
@@ -116,14 +122,32 @@ export function definePlugin(config: ResolvedConfig): Plugin {
116
122
return
117
123
}
118
124
119
- const [ define , pattern ] = ssr ? ssrPattern : defaultPattern
125
+ const [ define , pattern , banner ] = ssr ? ssrPattern : defaultPattern
120
126
if ( ! pattern ) return
121
127
122
128
// Check if our code needs any replacements before running esbuild
123
129
pattern . lastIndex = 0
124
130
if ( ! pattern . test ( code ) ) return
125
131
126
- return await replaceDefine ( code , id , define , config )
132
+ const result = await replaceDefine ( code , id , define , config )
133
+
134
+ // Replace `import.meta.env.*` with undefined
135
+ result . code = result . code . replaceAll ( importMetaEnvKeyRe , ( m ) =>
136
+ 'undefined' . padEnd ( m . length ) ,
137
+ )
138
+
139
+ // If there's bare `import.meta.env` references, prepend the banner
140
+ if ( bareImportMetaEnvRe . test ( result . code ) ) {
141
+ result . code = banner + result . code
142
+
143
+ if ( result . map ) {
144
+ const map = JSON . parse ( result . map )
145
+ map . mappings = ';' + map . mappings
146
+ result . map = map
147
+ }
148
+ }
149
+
150
+ return result
127
151
} ,
128
152
}
129
153
}
@@ -134,19 +158,6 @@ export async function replaceDefine(
134
158
define : Record < string , string > ,
135
159
config : ResolvedConfig ,
136
160
) : Promise < { code : string ; map : string | null } > {
137
- // Because esbuild only allows JSON-serializable values, and `import.meta.env`
138
- // may contain values with raw identifiers, making it non-JSON-serializable,
139
- // we replace it with a temporary marker and then replace it back after to
140
- // workaround it. This means that esbuild is unable to optimize the `import.meta.env`
141
- // access, but that's a tradeoff for now.
142
- const replacementMarkers : Record < string , string > = { }
143
- const env = define [ 'import.meta.env' ]
144
- if ( env && ! canJsonParse ( env ) ) {
145
- const marker = `_${ getHash ( env , env . length - 2 ) } _`
146
- replacementMarkers [ marker ] = env
147
- define = { ...define , 'import.meta.env' : marker }
148
- }
149
-
150
161
const esbuildOptions = config . esbuild || { }
151
162
152
163
const result = await transform ( code , {
@@ -178,10 +189,6 @@ export async function replaceDefine(
178
189
}
179
190
}
180
191
181
- for ( const marker in replacementMarkers ) {
182
- result . code = result . code . replaceAll ( marker , replacementMarkers [ marker ] )
183
- }
184
-
185
192
return {
186
193
code : result . code ,
187
194
map : result . map || null ,
@@ -212,12 +219,3 @@ function handleDefineValue(value: any): string {
212
219
if ( typeof value === 'string' ) return value
213
220
return JSON . stringify ( value )
214
221
}
215
-
216
- function canJsonParse ( value : any ) : boolean {
217
- try {
218
- JSON . parse ( value )
219
- return true
220
- } catch {
221
- return false
222
- }
223
- }
0 commit comments