@@ -159,15 +159,27 @@ let lastNativeBindingsLoadErrorCode:
159
159
| 'unsupported_target'
160
160
| string
161
161
| undefined = undefined
162
+ // Used to cache calls to `loadBindings`
163
+ let pendingBindings : Promise < Binding >
164
+ // some things call `loadNative` directly instead of `loadBindings`... Cache calls to that
165
+ // separately.
162
166
let nativeBindings : Binding
167
+ // can allow hacky sync access to bindings for loadBindingsSync
163
168
let wasmBindings : Binding
164
169
let downloadWasmPromise : any
165
- let pendingBindings : any
166
170
let swcTraceFlushGuard : any
167
171
let downloadNativeBindingsPromise : Promise < void > | undefined = undefined
168
172
169
173
export const lockfilePatchPromise : { cur ?: Promise < void > } = { }
170
174
175
+ /**
176
+ * Attempts to load a native or wasm binding.
177
+ *
178
+ * By default, this first tries to use a native binding, falling back to a wasm binding if that
179
+ * fails.
180
+ *
181
+ * This function is `async` as wasm requires an asynchronous import in browsers.
182
+ */
171
183
export async function loadBindings (
172
184
useWasmBinary : boolean = false
173
185
) : Promise < Binding > {
@@ -180,6 +192,10 @@ export async function loadBindings(
180
192
return pendingBindings
181
193
}
182
194
195
+ if ( process . env . NEXT_TEST_WASM ) {
196
+ useWasmBinary = true
197
+ }
198
+
183
199
// rust needs stdout to be blocking, otherwise it will throw an error (on macOS at least) when writing a lot of data (logs) to it
184
200
// see https://github.com/napi-rs/napi-rs/issues/1630
185
201
// and https://github.com/nodejs/node/blob/main/doc/api/process.md#a-note-on-process-io
@@ -291,7 +307,10 @@ async function tryLoadNativeWithFallback(attempts: Array<string>) {
291
307
return undefined
292
308
}
293
309
294
- async function tryLoadWasmWithFallback ( attempts : any [ ] ) {
310
+ // helper for loadBindings
311
+ async function tryLoadWasmWithFallback (
312
+ attempts : any [ ]
313
+ ) : Promise < Binding | undefined > {
295
314
try {
296
315
let bindings = await loadWasm ( '' )
297
316
// @ts -expect-error TODO: this event has a wrong type.
@@ -343,8 +362,8 @@ function loadBindingsSync() {
343
362
attempts = attempts . concat ( a )
344
363
}
345
364
346
- // we can leverage the wasm bindings if they are already
347
- // loaded
365
+ // HACK: we can leverage the wasm bindings if they are already loaded
366
+ // this may introduce race conditions
348
367
if ( wasmBindings ) {
349
368
return wasmBindings
350
369
}
@@ -1059,122 +1078,165 @@ function bindingToApi(
1059
1078
}
1060
1079
1061
1080
async function loadWasm ( importPath = '' ) {
1062
- if ( wasmBindings ) {
1063
- return wasmBindings
1064
- }
1065
-
1066
1081
let attempts = [ ]
1067
- for ( let pkg of [ '@next/swc-wasm-nodejs' , '@next/swc-wasm-web' ] ) {
1068
- try {
1069
- let pkgPath = pkg
1082
+ let rawBindings : RawWasmBindings | null = null
1070
1083
1071
- if ( importPath ) {
1072
- // the import path must be exact when not in node_modules
1073
- pkgPath = path . join ( importPath , pkg , 'wasm.js' )
1074
- }
1075
- let bindings : RawWasmBindings = await import (
1076
- pathToFileURL ( pkgPath ) . toString ( )
1077
- )
1078
- if ( pkg === '@next/swc-wasm-web' ) {
1079
- bindings = await bindings . default ! ( )
1080
- }
1081
- infoLog ( 'next-swc build: wasm build @next/swc-wasm-web' )
1082
-
1083
- // Note wasm binary does not support async intefaces yet, all async
1084
- // interface coereces to sync interfaces.
1085
- wasmBindings = {
1086
- css : {
1087
- lightning : {
1088
- transform : function ( _options : any ) {
1089
- throw new Error (
1090
- '`css.lightning.transform` is not supported by the wasm bindings.'
1091
- )
1092
- } ,
1093
- transformStyleAttr : function ( _options : any ) {
1094
- throw new Error (
1095
- '`css.lightning.transformStyleAttr` is not supported by the wasm bindings.'
1096
- )
1097
- } ,
1098
- } ,
1099
- } ,
1100
- isWasm : true ,
1101
- transform ( src : string , options : any ) {
1102
- // TODO: we can remove fallback to sync interface once new stable version of next-swc gets published (current v12.2)
1103
- return bindings ?. transform
1104
- ? bindings . transform ( src . toString ( ) , options )
1105
- : Promise . resolve ( bindings . transformSync ( src . toString ( ) , options ) )
1106
- } ,
1107
- transformSync ( src : string , options : any ) {
1108
- return bindings . transformSync ( src . toString ( ) , options )
1109
- } ,
1110
- minify ( src : string , options : any ) {
1111
- return bindings ?. minify
1112
- ? bindings . minify ( src . toString ( ) , options )
1113
- : Promise . resolve ( bindings . minifySync ( src . toString ( ) , options ) )
1114
- } ,
1115
- minifySync ( src : string , options : any ) {
1116
- return bindings . minifySync ( src . toString ( ) , options )
1117
- } ,
1118
- parse ( src : string , options : any ) {
1119
- return bindings ?. parse
1120
- ? bindings . parse ( src . toString ( ) , options )
1121
- : Promise . resolve ( bindings . parseSync ( src . toString ( ) , options ) )
1122
- } ,
1123
- getTargetTriple ( ) {
1124
- return undefined
1125
- } ,
1126
- turbo : {
1127
- createProject : function (
1128
- _options : ProjectOptions ,
1129
- _turboEngineOptions ?: TurboEngineOptions | undefined
1130
- ) : Promise < Project > {
1131
- throw new Error (
1132
- '`turbo.createProject` is not supported by the wasm bindings.'
1133
- )
1134
- } ,
1135
- startTurbopackTraceServer : function ( _traceFilePath : string ) : void {
1136
- throw new Error (
1137
- '`turbo.startTurbopackTraceServer` is not supported by the wasm bindings.'
1138
- )
1139
- } ,
1140
- } ,
1141
- mdx : {
1142
- compile ( src : string , options : any ) {
1143
- return bindings . mdxCompile ( src , getMdxOptions ( options ) )
1144
- } ,
1145
- compileSync ( src : string , options : any ) {
1146
- return bindings . mdxCompileSync ( src , getMdxOptions ( options ) )
1147
- } ,
1148
- } ,
1149
- reactCompiler : {
1150
- isReactCompilerRequired ( _filename : string ) {
1151
- return Promise . resolve ( true )
1152
- } ,
1153
- } ,
1154
- }
1155
- return wasmBindings
1156
- } catch ( e : any ) {
1157
- // Only log attempts for loading wasm when loading as fallback
1158
- if ( importPath ) {
1159
- if ( e ?. code === 'ERR_MODULE_NOT_FOUND' ) {
1160
- attempts . push ( `Attempted to load ${ pkg } , but it was not installed` )
1084
+ // Used by `run-tests` to force use of a locally-built wasm binary. This environment variable is
1085
+ // unstable and subject to change.
1086
+ const testWasmDir = process . env . NEXT_TEST_WASM_DIR
1087
+
1088
+ if ( testWasmDir != null ) {
1089
+ // assume these are node.js bindings and don't need a call to `.default()`
1090
+ rawBindings = await import (
1091
+ pathToFileURL ( path . join ( testWasmDir , 'wasm.js' ) ) . toString ( )
1092
+ )
1093
+ infoLog ( `next-swc build: wasm build ${ testWasmDir } ` )
1094
+ } else {
1095
+ for ( let pkg of [ '@next/swc-wasm-nodejs' , '@next/swc-wasm-web' ] ) {
1096
+ try {
1097
+ let pkgPath = pkg
1098
+
1099
+ if ( importPath ) {
1100
+ // the import path must be exact when not in node_modules
1101
+ pkgPath = path . join ( importPath , pkg , 'wasm.js' )
1102
+ }
1103
+ const importedRawBindings = await import (
1104
+ pathToFileURL ( pkgPath ) . toString ( )
1105
+ )
1106
+ if ( pkg === '@next/swc-wasm-web' ) {
1107
+ // https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html
1108
+ // `default` must be called to initialize the module
1109
+ rawBindings = await importedRawBindings . default ! ( )
1161
1110
} else {
1162
- attempts . push (
1163
- `Attempted to load ${ pkg } , but an error occurred: ${ e . message ?? e } `
1164
- )
1111
+ rawBindings = importedRawBindings
1112
+ }
1113
+ infoLog ( `next-swc build: wasm build ${ pkg } ` )
1114
+ } catch ( e : any ) {
1115
+ // Only log attempts for loading wasm when loading as fallback
1116
+ if ( importPath ) {
1117
+ if ( e ?. code === 'ERR_MODULE_NOT_FOUND' ) {
1118
+ attempts . push ( `Attempted to load ${ pkg } , but it was not installed` )
1119
+ } else {
1120
+ attempts . push (
1121
+ `Attempted to load ${ pkg } , but an error occurred: ${ e . message ?? e } `
1122
+ )
1123
+ }
1165
1124
}
1166
1125
}
1167
1126
}
1168
1127
}
1169
1128
1170
- throw attempts
1129
+ if ( rawBindings == null ) {
1130
+ throw attempts
1131
+ }
1132
+
1133
+ function removeUndefined ( obj : any ) : any {
1134
+ // serde-wasm-bindgen expect that `undefined` values map to `()` in rust, but we want to treat
1135
+ // those fields as non-existent, so remove them before passing them to rust.
1136
+ //
1137
+ // The native (non-wasm) bindings use `JSON.stringify`, which strips undefined values.
1138
+ if ( typeof obj !== 'object' ) {
1139
+ return
1140
+ }
1141
+ if ( Array . isArray ( obj ) ) {
1142
+ return obj . map ( removeUndefined )
1143
+ }
1144
+ for ( const [ k , v ] of Object . entries ( obj ) ) {
1145
+ if ( typeof v === 'undefined' ) {
1146
+ delete obj [ k ]
1147
+ } else {
1148
+ obj [ k ] = removeUndefined ( v )
1149
+ }
1150
+ }
1151
+ }
1152
+
1153
+ // Note wasm binary does not support async intefaces yet, all async
1154
+ // interface coereces to sync interfaces.
1155
+ wasmBindings = {
1156
+ css : {
1157
+ lightning : {
1158
+ transform : function ( _options : any ) {
1159
+ throw new Error (
1160
+ '`css.lightning.transform` is not supported by the wasm bindings.'
1161
+ )
1162
+ } ,
1163
+ transformStyleAttr : function ( _options : any ) {
1164
+ throw new Error (
1165
+ '`css.lightning.transformStyleAttr` is not supported by the wasm bindings.'
1166
+ )
1167
+ } ,
1168
+ } ,
1169
+ } ,
1170
+ isWasm : true ,
1171
+ transform ( src : string , options : any ) : Promise < any > {
1172
+ return rawBindings . transform ( src . toString ( ) , removeUndefined ( options ) )
1173
+ } ,
1174
+ transformSync ( src : string , options : any ) {
1175
+ return rawBindings . transformSync ( src . toString ( ) , removeUndefined ( options ) )
1176
+ } ,
1177
+ minify ( src : string , options : any ) : Promise < any > {
1178
+ return rawBindings . minify ( src . toString ( ) , removeUndefined ( options ) )
1179
+ } ,
1180
+ minifySync ( src : string , options : any ) {
1181
+ return rawBindings . minifySync ( src . toString ( ) , removeUndefined ( options ) )
1182
+ } ,
1183
+ parse ( src : string , options : any ) : Promise < any > {
1184
+ return rawBindings . parse ( src . toString ( ) , removeUndefined ( options ) )
1185
+ } ,
1186
+ getTargetTriple ( ) {
1187
+ return undefined
1188
+ } ,
1189
+ turbo : {
1190
+ createProject : function (
1191
+ _options : ProjectOptions ,
1192
+ _turboEngineOptions ?: TurboEngineOptions | undefined
1193
+ ) : Promise < Project > {
1194
+ throw new Error (
1195
+ '`turbo.createProject` is not supported by the wasm bindings.'
1196
+ )
1197
+ } ,
1198
+ startTurbopackTraceServer : function ( _traceFilePath : string ) : void {
1199
+ throw new Error (
1200
+ '`turbo.startTurbopackTraceServer` is not supported by the wasm bindings.'
1201
+ )
1202
+ } ,
1203
+ } ,
1204
+ mdx : {
1205
+ compile ( src : string , options : any ) {
1206
+ return rawBindings . mdxCompile (
1207
+ src ,
1208
+ removeUndefined ( getMdxOptions ( options ) )
1209
+ )
1210
+ } ,
1211
+ compileSync ( src : string , options : any ) {
1212
+ return rawBindings . mdxCompileSync (
1213
+ src ,
1214
+ removeUndefined ( getMdxOptions ( options ) )
1215
+ )
1216
+ } ,
1217
+ } ,
1218
+ reactCompiler : {
1219
+ isReactCompilerRequired ( _filename : string ) {
1220
+ return Promise . resolve ( true )
1221
+ } ,
1222
+ } ,
1223
+ }
1224
+ return wasmBindings
1171
1225
}
1172
1226
1227
+ /**
1228
+ * Loads the native (non-wasm) bindings. Prefer `loadBindings` over this API, as that includes a
1229
+ * wasm fallback.
1230
+ */
1173
1231
function loadNative ( importPath ?: string ) {
1174
1232
if ( nativeBindings ) {
1175
1233
return nativeBindings
1176
1234
}
1177
1235
1236
+ if ( process . env . NEXT_TEST_WASM ) {
1237
+ throw new Error ( 'cannot run loadNative when `NEXT_TEST_WASM` is set' )
1238
+ }
1239
+
1178
1240
const customBindings : RawBindings = ! ! __INTERNAL_CUSTOM_TURBOPACK_BINDINGS
1179
1241
? require ( __INTERNAL_CUSTOM_TURBOPACK_BINDINGS )
1180
1242
: null
0 commit comments