@@ -256,14 +256,31 @@ impl EsmBindgen {
256256 js_string. contains ( "\" " ) || js_string. contains ( "'" ) || js_string. contains ( "`" )
257257 }
258258
259+ /// Render a block of imports
260+ ///
261+ /// This is normally right before the instantiation code in the generated output, e.g.:
262+ ///
263+ /// ```
264+ /// const { someFn } = imports['ns:pkg/iface'];
265+ /// const { asyncOtherFn } = imports['ns:pkg/iface2'];
266+ /// const { someFn, asyncSomeFn } = imports['ns:pkg/iface3'];
267+ /// const { getDirectories } = imports['wasi:filesystem/preopens'];
268+ /// const { Descriptor, filesystemErrorCode } = imports['wasi:filesystem/types'];
269+ /// const { Error: Error$1 } = imports['wasi:io/error'];
270+ /// const { InputStream, OutputStream } = imports['wasi:io/streams'];
271+ /// let gen = (function* _initGenerator () {
272+ /// ```
273+ ///
259274 pub fn render_imports (
260275 & mut self ,
261276 output : & mut Source ,
262277 imports_object : Option < & str > ,
263278 local_names : & mut LocalNames ,
264279 ) {
265280 let mut iface_imports = Vec :: new ( ) ;
281+
266282 for ( specifier, binding) in & self . imports {
283+ // Build IDL binding if the specifier uses specifal WebIDL support
267284 let idl_binding = if specifier. starts_with ( "webidl:" ) {
268285 let iface_idx = specifier. find ( '/' ) . unwrap ( ) + 1 ;
269286 let iface_name = if let Some ( version_idx) = specifier. find ( '@' ) {
@@ -275,13 +292,19 @@ impl EsmBindgen {
275292 } else {
276293 None
277294 } ;
295+
278296 if imports_object. is_some ( ) || idl_binding. is_some ( ) {
279297 uwrite ! ( output, "const " ) ;
280298 } else {
281299 uwrite ! ( output, "import " ) ;
282300 }
301+
283302 match binding {
303+ // For interfaces we import the entire object as one
284304 ImportBinding :: Interface ( bindings) => {
305+ // If we there is no import object and it's not an IDL binding and there's only
306+ // *one* binding, then we can directly import rather than attempting to extract
307+ // individual imports
285308 if imports_object. is_none ( ) && idl_binding. is_none ( ) && bindings. len ( ) == 1 {
286309 let ( import_name, import) = bindings. iter ( ) . next ( ) . unwrap ( ) ;
287310 if import_name == "default" {
@@ -305,8 +328,13 @@ impl EsmBindgen {
305328 continue ;
306329 }
307330 }
331+
308332 uwrite ! ( output, "{{" ) ;
333+
309334 let mut first = true ;
335+ let mut bound_external_names = Vec :: new ( ) ;
336+ // Generate individual imports for all the bindings that were provided,
337+ // to generate the lhs of the destructured assignment
310338 for ( external_name, import) in bindings {
311339 match import {
312340 ImportBinding :: Interface ( iface) => {
@@ -328,7 +356,12 @@ impl EsmBindgen {
328356 } else {
329357 uwrite ! ( output, "{external_name} as {iface_local_name}" ) ;
330358 }
359+ bound_external_names. push ( (
360+ external_name. to_string ( ) ,
361+ iface_local_name. to_string ( ) ,
362+ ) ) ;
331363 }
364+
332365 ImportBinding :: Local ( local_names) => {
333366 for local_name in local_names {
334367 if first {
@@ -344,19 +377,34 @@ impl EsmBindgen {
344377 } else {
345378 uwrite ! ( output, "{external_name} as {local_name}" ) ;
346379 }
380+ bound_external_names
381+ . push ( ( external_name. to_string ( ) , local_name. to_string ( ) ) ) ;
347382 }
348383 }
349384 } ;
350385 }
386+
351387 if !first {
352388 output. push_str ( " " ) ;
353389 }
390+
391+ // End the destructured assignment
354392 if let Some ( imports_object) = imports_object {
355393 uwriteln ! (
356394 output,
357395 "}} = {imports_object}{};" ,
358396 maybe_quote_member( specifier)
359397 ) ;
398+ for ( external_name, local_name) in bound_external_names {
399+ uwriteln ! (
400+ output,
401+ r#"
402+ if ({local_name} === undefined) {{
403+ throw new Error("unexpectedly undefined instance import '{local_name}', was '{external_name}' available at instantiation?");
404+ }}
405+ "# ,
406+ ) ;
407+ }
360408 } else if let Some ( idl_binding) = idl_binding {
361409 uwrite ! (
362410 output,
@@ -373,6 +421,8 @@ impl EsmBindgen {
373421 uwriteln ! ( output, "}} from '{specifier}';" ) ;
374422 }
375423 }
424+
425+ // For local bindings we can use a simpler direct assignment
376426 ImportBinding :: Local ( binding_local_names) => {
377427 let local_name = & binding_local_names[ 0 ] ;
378428 if let Some ( imports_object) = imports_object {
@@ -391,10 +441,12 @@ impl EsmBindgen {
391441 }
392442 }
393443
394- // render interface import member getters
444+ // Render interface import member getters
395445 for ( iface_local_name, iface_imports) in iface_imports {
396446 uwrite ! ( output, "const {{" ) ;
397447 let mut first = true ;
448+ let mut generated_member_names = Vec :: new ( ) ;
449+
398450 for ( member_name, binding) in iface_imports {
399451 let ImportBinding :: Local ( binding_local_names) = binding else {
400452 continue ;
@@ -411,12 +463,26 @@ impl EsmBindgen {
411463 } else {
412464 uwrite ! ( output, "{member_name}: {local_name}" ) ;
413465 }
466+ generated_member_names. push ( ( member_name, local_name) ) ;
414467 }
415468 }
416469 if !first {
417470 output. push_str ( " " ) ;
418471 }
419472 uwriteln ! ( output, "}} = {iface_local_name};" ) ;
473+
474+ // Ensure that the imports we destructured were defined
475+ // (if they were not, the user is likely missing an import @ instantiation time)
476+ for ( member_name, local_name) in generated_member_names {
477+ uwriteln ! (
478+ output,
479+ r#"
480+ if ({local_name} === undefined) {{
481+ throw new Error("unexpectedly undefined local import '{local_name}', was '{member_name}' available at instantiation?");
482+ }}
483+ "# ,
484+ ) ;
485+ }
420486 }
421487 }
422488}
0 commit comments