@@ -419,3 +419,131 @@ test("getModuleInfo: determineDependencyInfo for ESM js resource", async (t) =>
419419 ) ;
420420} ) ;
421421
422+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using import.meta" , async ( t ) => {
423+ const { ResourcePool, verboseLogStub, errorLogStub} = t . context ;
424+
425+ const resourcePool = new ResourcePool ( ) ;
426+ const esmCode = `const url = import.meta.url;` ;
427+ const inputJsResource = { name : "thirdparty/esm-meta-module.js" , buffer : async ( ) => esmCode } ;
428+ resourcePool . addResource ( inputJsResource ) ;
429+
430+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-meta-module.js" ) ;
431+
432+ t . is ( info . name , "thirdparty/esm-meta-module.js" , "name should be set" ) ;
433+ t . is ( info . size , esmCode . length , "size should be set" ) ;
434+ t . is ( info . format , "esm" , "format should be set to ESM" ) ;
435+ t . deepEqual ( info . dependencies , [ ] , "no dependencies should be found since parsing failed" ) ;
436+
437+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called for ESM parse failure" ) ;
438+ t . true ( verboseLogStub . callCount >= 1 , "log.verbose should be called" ) ;
439+ t . true (
440+ verboseLogStub . getCalls ( ) . some ( ( call ) =>
441+ call . args [ 0 ] . includes ( "Failed to parse thirdparty/esm-meta-module.js" ) &&
442+ call . args [ 0 ] . includes ( "Cannot use 'import.meta' outside a module" )
443+ ) ,
444+ "log.verbose should contain the import.meta parse error message"
445+ ) ;
446+ } ) ;
447+
448+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using side-effect import" , async ( t ) => {
449+ const { ResourcePool, errorLogStub} = t . context ;
450+
451+ const resourcePool = new ResourcePool ( ) ;
452+ const esmCode = `import "./side-effect.js";` ;
453+ const inputJsResource = { name : "thirdparty/esm-side-effect.js" , buffer : async ( ) => esmCode } ;
454+ resourcePool . addResource ( inputJsResource ) ;
455+
456+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-side-effect.js" ) ;
457+
458+ t . is ( info . format , "esm" , "format should be set to ESM for side-effect import" ) ;
459+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
460+ } ) ;
461+
462+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using re-export" , async ( t ) => {
463+ const { ResourcePool, errorLogStub} = t . context ;
464+
465+ const resourcePool = new ResourcePool ( ) ;
466+ const esmCode = `export { foo } from "./foo.js";` ;
467+ const inputJsResource = { name : "thirdparty/esm-reexport.js" , buffer : async ( ) => esmCode } ;
468+ resourcePool . addResource ( inputJsResource ) ;
469+
470+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-reexport.js" ) ;
471+
472+ t . is ( info . format , "esm" , "format should be set to ESM for re-export" ) ;
473+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
474+ } ) ;
475+
476+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using export *" , async ( t ) => {
477+ const { ResourcePool, errorLogStub} = t . context ;
478+
479+ const resourcePool = new ResourcePool ( ) ;
480+ const esmCode = `export * from "./foo.js";` ;
481+ const inputJsResource = { name : "thirdparty/esm-export-star.js" , buffer : async ( ) => esmCode } ;
482+ resourcePool . addResource ( inputJsResource ) ;
483+
484+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-export-star.js" ) ;
485+
486+ t . is ( info . format , "esm" , "format should be set to ESM for export *" ) ;
487+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
488+ } ) ;
489+
490+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using named export" , async ( t ) => {
491+ const { ResourcePool, errorLogStub} = t . context ;
492+
493+ const resourcePool = new ResourcePool ( ) ;
494+ const esmCode = `export const myVar = 42;` ;
495+ const inputJsResource = { name : "thirdparty/esm-named-export.js" , buffer : async ( ) => esmCode } ;
496+ resourcePool . addResource ( inputJsResource ) ;
497+
498+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-named-export.js" ) ;
499+
500+ t . is ( info . format , "esm" , "format should be set to ESM for named export" ) ;
501+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
502+ } ) ;
503+
504+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using import.meta.resolve" , async ( t ) => {
505+ const { ResourcePool, errorLogStub} = t . context ;
506+
507+ const resourcePool = new ResourcePool ( ) ;
508+ const esmCode = `const resolved = import.meta.resolve("./foo.js");` ;
509+ const inputJsResource = { name : "thirdparty/esm-meta-resolve.js" , buffer : async ( ) => esmCode } ;
510+ resourcePool . addResource ( inputJsResource ) ;
511+
512+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-meta-resolve.js" ) ;
513+
514+ t . is ( info . format , "esm" , "format should be set to ESM for import.meta.resolve" ) ;
515+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
516+ } ) ;
517+
518+ test ( "getModuleInfo: determineDependencyInfo for ESM js resource using top-level await" , async ( t ) => {
519+ const { ResourcePool} = t . context ;
520+
521+ // Top-level await is ESM-only syntax, but the parser produces a generic "Unexpected token"
522+ // error that cannot be reliably distinguished from actual syntax errors.
523+ // Therefore, the module is NOT detected as ESM.
524+ const resourcePool = new ResourcePool ( ) ;
525+ const esmCode = `const x = await Promise.resolve(1);` ;
526+ const inputJsResource = { name : "thirdparty/esm-top-level-await.js" , buffer : async ( ) => esmCode } ;
527+ resourcePool . addResource ( inputJsResource ) ;
528+
529+ const info = await resourcePool . getModuleInfo ( "thirdparty/esm-top-level-await.js" ) ;
530+
531+ t . is ( info . format , undefined , "format should not be set for top-level await (not detectable)" ) ;
532+ } ) ;
533+
534+ test ( "getModuleInfo: determineDependencyInfo for js resource using dynamic import()" , async ( t ) => {
535+ const { ResourcePool, errorLogStub} = t . context ;
536+
537+ // Dynamic import() is valid in both scripts and modules, so it parses successfully
538+ // and should NOT be flagged as ESM.
539+ const resourcePool = new ResourcePool ( ) ;
540+ const code = `const m = import("./foo.js");` ;
541+ const inputJsResource = { name : "thirdparty/dynamic-import.js" , buffer : async ( ) => code } ;
542+ resourcePool . addResource ( inputJsResource ) ;
543+
544+ const info = await resourcePool . getModuleInfo ( "thirdparty/dynamic-import.js" ) ;
545+
546+ t . not ( info . format , "esm" , "format should not be ESM for dynamic import()" ) ;
547+ t . is ( errorLogStub . callCount , 0 , "log.error should not be called" ) ;
548+ } ) ;
549+
0 commit comments