@@ -126,51 +126,54 @@ async function ssrTransformScript(
126
126
) {
127
127
const source = importNode . source . value as string
128
128
deps . add ( source )
129
- const importId = `__vite_ssr_import_${ uid ++ } __`
130
129
131
130
// Reduce metadata to undefined if it's all default values
132
- if (
133
- metadata &&
134
- ( metadata . importedNames == null || metadata . importedNames . length === 0 )
135
- ) {
136
- metadata = undefined
137
- }
138
- const metadataStr = metadata ? `, ${ JSON . stringify ( metadata ) } ` : ''
131
+ const metadataArg =
132
+ ( metadata ?. importedNames ?. length ?? 0 ) > 0
133
+ ? `, ${ JSON . stringify ( metadata ) } `
134
+ : ''
139
135
140
- // Track how many lines the original import statement spans, so we can preserve the line offset.
141
- let linesSpanned = 1
142
- for ( let i = importNode . start ; i < importNode . end ; i ++ ) {
143
- if ( code [ i ] === '\n' ) {
144
- linesSpanned ++
145
- }
146
- }
136
+ const importId = `__vite_ssr_import_${ uid ++ } __`
137
+ const transformedImport = `const ${ importId } = await ${ ssrImportKey } (${ JSON . stringify (
138
+ source ,
139
+ ) } ${ metadataArg } );`
147
140
148
- s . update (
149
- importNode . start ,
150
- importNode . end ,
151
- `const ${ importId } = await ${ ssrImportKey } (${ JSON . stringify (
152
- source ,
153
- ) } ${ metadataStr } );${ '\n' . repeat ( linesSpanned - 1 ) } `,
154
- )
141
+ s . update ( importNode . start , importNode . end , transformedImport )
155
142
156
143
// Check for non-whitespace characters between the last import and the
157
- // current one.
144
+ // current one, to determine if hoisting is needed.
145
+ // TODO: Account for comments between imports.
158
146
const nonWhitespaceRegex = / \S / g
159
147
nonWhitespaceRegex . lastIndex = index
160
148
nonWhitespaceRegex . exec ( code )
161
-
162
- // TODO: Account for comments between imports.
163
149
if ( importNode . start > nonWhitespaceRegex . lastIndex ) {
164
- // By moving the import to the top of the module, we ensure that it's
165
- // imported before it's used.
150
+ // Imports are moved to the top of the file (AKA “hoisting”) to ensure any
151
+ // non-import statements before them are executed after the import. This
152
+ // aligns SSR imports with native ESM import behavior.
166
153
s . move ( importNode . start , importNode . end , index )
167
154
} else {
168
- // Only update hoistIndex when not hoisting the current import. This
155
+ // Only update hoistIndex when * not* hoisting the current import. This
169
156
// ensures that once any import in this module has been hoisted, all
170
- // remaining imports will also be hoisted.
157
+ // remaining imports will also be hoisted. This is inherently true because
158
+ // we work from the top of the file downward.
171
159
hoistIndex = importNode . end
172
160
}
173
161
162
+ // Track how many lines the original import statement spans, so we can
163
+ // preserve the line offset.
164
+ let linesSpanned = 1
165
+ for ( let i = importNode . start ; i < importNode . end ; i ++ ) {
166
+ if ( code [ i ] === '\n' ) {
167
+ linesSpanned ++
168
+ }
169
+ }
170
+ if ( linesSpanned > 1 ) {
171
+ // This leaves behind any extra newlines that were removed during
172
+ // transformation, in the position of the original import statement
173
+ // (before any hoisting).
174
+ s . prependRight ( importNode . end , '\n' . repeat ( linesSpanned - 1 ) )
175
+ }
176
+
174
177
return importId
175
178
}
176
179
0 commit comments