@@ -2,80 +2,89 @@ import { createRequire } from 'node:module';
22
33import { canRead , isLocalModulePath , resolveRelativePath } from './fs' ;
44
5- export async function importModuleItem < T = unknown > (
6- identifier : string ,
7- modulePath : string ,
8- itemName : string = 'default' ,
9- ) : Promise < T > {
10- const module = await importModule ( identifier , modulePath ) ;
11- const item = pickModuleItem ( module , itemName ) as T | undefined ;
12- if ( item === undefined ) {
13- throw new Error ( `Failed to import "${ itemName } " from ${ identifier } at "${ modulePath } ".` ) ;
5+ type ImportModuleItemOptions = {
6+ from : string ;
7+ identifier ?: string ;
8+ item ?: string ;
9+ } ;
10+
11+ export async function importModuleItem < T = unknown > ( options : ImportModuleItemOptions ) : Promise < T > {
12+ const module = await importModule ( options ) ;
13+ const moduleItem = pickModuleItem ( module , options . item ) as T | undefined ;
14+ if ( moduleItem === undefined ) {
15+ const moduleInfo = getModuleInfo ( options ) ;
16+ throw new Error ( `Failed to ${ moduleInfo } .` ) ;
1417 }
15- return item ;
18+ return moduleItem ;
1619}
1720
1821type ModuleDefinition = Partial < Record < string , unknown > > & {
1922 __esModule ?: boolean ;
2023 default ?: Partial < Record < string , unknown > > & { default ?: Partial < Record < string , unknown > > } ;
2124} ;
2225
23- function pickModuleItem ( module : ModuleDefinition , itemName : string ) : unknown {
24- if ( itemName === 'default' ) {
26+ function pickModuleItem ( module : ModuleDefinition , item : string = 'default' ) : unknown {
27+ if ( item === 'default' ) {
2528 return module . default ?. default ?? module . default ?? module ;
2629 }
27- return module [ itemName ] ?? module . default ?. [ itemName ] ?? module . default ?. default ?. [ itemName ] ;
30+ return module [ item ] ?? module . default ?. [ item ] ?? module . default ?. default ?. [ item ] ;
2831}
2932
30- async function importModule < T extends object > ( identifier : string , modulePath : string ) : Promise < T > {
31- if ( isLocalModulePath ( modulePath ) ) {
32- return await importLocalModule ( identifier , modulePath ) ;
33+ async function importModule < T extends object > ( options : ImportModuleItemOptions ) : Promise < T > {
34+ if ( isLocalModulePath ( options . from ) ) {
35+ return await importLocalModule ( options ) ;
3336 }
3437
3538 try {
36- return await importExternalUserModule ( identifier , modulePath ) ;
39+ return await importExternalUserModule ( options ) ;
3740 } catch {
38- return await importExternalModule ( identifier , modulePath ) ;
41+ return await importExternalModule ( options ) ;
3942 }
4043}
4144
42- async function importLocalModule < T extends object > ( identifier : string , modulePath : string ) : Promise < T > {
43- if ( ! ( await canRead ( modulePath ) ) ) {
44- throw new Error ( `Cannot access ${ identifier } at "${ modulePath } "` ) ;
45+ async function importLocalModule < T extends object > ( options : ImportModuleItemOptions ) : Promise < T > {
46+ const { identifier, from } = options ;
47+ if ( ! ( await canRead ( from ) ) ) {
48+ throw new Error ( `Cannot access ${ identifier ?? 'module' } at "${ from } "` ) ;
4549 }
4650
47- const dotIndex = modulePath . lastIndexOf ( '.' ) ;
48- const extension = dotIndex === - 1 ? undefined : modulePath . slice ( dotIndex ) ;
49- const modulePromise = extension === '.json' ? import ( modulePath , { with : { type : 'json' } } ) : import ( modulePath ) ;
50- return await handleImportPromise ( modulePromise , identifier , modulePath ) ;
51+ const dotIndex = from . lastIndexOf ( '.' ) ;
52+ const extension = dotIndex === - 1 ? undefined : from . slice ( dotIndex ) ;
53+ const modulePromise = extension === '.json' ? import ( from , { with : { type : 'json' } } ) : import ( from ) ;
54+ return await handleImportPromise ( modulePromise , options ) ;
5155}
5256
53- async function importExternalModule < T extends object > ( identifier : string , modulePath : string ) : Promise < T > {
54- return await handleImportPromise ( import ( modulePath ) , identifier , modulePath ) ;
57+ async function importExternalModule < T extends object > ( options : ImportModuleItemOptions ) : Promise < T > {
58+ return await handleImportPromise ( import ( options . from ) , options ) ;
5559}
5660
57- async function importExternalUserModule < T extends object > ( identifier : string , modulePath : string ) : Promise < T > {
61+ async function importExternalUserModule < T extends object > ( options : ImportModuleItemOptions ) : Promise < T > {
5862 const userPackageJsonPath = resolveRelativePath ( 'package.json' ) ;
5963 const userRequire = createRequire ( userPackageJsonPath ) ;
60- const userModulePath = userRequire . resolve ( modulePath ) ;
61- return await importExternalModule < T > ( identifier , userModulePath ) ;
64+ const userFrom = userRequire . resolve ( options . from ) ;
65+ return await importExternalModule < T > ( { ... options , from : userFrom } ) ;
6266}
6367
6468async function handleImportPromise < T extends object > (
6569 importPromise : Promise < unknown > ,
66- identifier : string ,
67- modulePath : string ,
70+ options : ImportModuleItemOptions ,
6871) : Promise < T > {
6972 try {
7073 return ( await importPromise ) as T ;
7174 } catch ( error ) {
75+ const moduleInfo = getModuleInfo ( options ) ;
7276 let causeMessage =
7377 ! ! error && typeof error === 'object' && 'message' in error && typeof error . message === 'string'
7478 ? ( error as { message : string } ) . message
7579 : undefined ;
76- causeMessage = causeMessage ? ` (caused by: ${ causeMessage } )` : '' ;
77- throw new Error ( `Failed to import ${ identifier } at "${ modulePath } " as a module${ causeMessage } ` , {
78- cause : error ,
79- } ) ;
80+ causeMessage = causeMessage ? `\n(caused by: ${ causeMessage } )` : '' ;
81+ throw new Error ( `Failed to ${ moduleInfo } .${ causeMessage } ` , { cause : error } ) ;
8082 }
8183}
84+
85+ function getModuleInfo ( options : ImportModuleItemOptions ) : string {
86+ const { identifier, from, item } = options ;
87+ const importStatement = item ? `import { ${ item } } from '${ from } '` : `import default from '${ from } '` ;
88+ if ( ! identifier ) return importStatement ;
89+ return `import ${ identifier } [${ importStatement } ]` ;
90+ }
0 commit comments