1
- import { ModulePatcher , TBabelCore , TBabelJest , TTypeScript , TsJestImporter } from '../types'
1
+ import { ModulePatcher , TBabelCore , TBabelJest , TTypeScript } from '../types'
2
2
3
3
import * as hacks from './hacks'
4
4
import { rootLogger } from './logger'
@@ -26,7 +26,7 @@ const passThru = (action: () => void) => (input: any) => {
26
26
/**
27
27
* @internal
28
28
*/
29
- export class Importer implements TsJestImporter {
29
+ export class Importer {
30
30
@Memoize ( )
31
31
static get instance ( ) {
32
32
logger . debug ( 'creating Importer singleton' )
@@ -70,22 +70,51 @@ export class Importer implements TsJestImporter {
70
70
}
71
71
72
72
@Memoize ( ( ...args : string [ ] ) => args . join ( ':' ) )
73
- tryThese ( moduleName : string , ...fallbacks : string [ ] ) : any {
73
+ tryThese ( moduleName : string , ...fallbacks : string [ ] ) {
74
74
let name : string
75
- let loaded : any
75
+ let loaded : RequireResult < true > | undefined
76
76
const tries = [ moduleName , ...fallbacks ]
77
77
// tslint:disable-next-line:no-conditional-assignment
78
78
while ( ( name = tries . shift ( ) as string ) !== undefined ) {
79
- try {
80
- loaded = requireModule ( name )
81
- logger . debug ( 'loaded module' , name )
79
+ const req = requireWrapper ( name )
80
+
81
+ // remove exports from what we're going to log
82
+ const contextReq : any = { ...req }
83
+ delete contextReq . exports
84
+
85
+ if ( req . exists ) {
86
+ // module exists
87
+ loaded = req as RequireResult < true >
88
+ if ( loaded . error ) {
89
+ // require-ing it failed
90
+ logger . error ( { requireResult : contextReq } , `failed loading module '${ name } '` , loaded . error . message )
91
+ } else {
92
+ // it has been loaded, let's patch it
93
+ logger . debug ( { requireResult : contextReq } , 'loaded module' , name )
94
+ loaded . exports = this . _patch ( name , loaded . exports )
95
+ }
82
96
break
83
- } catch ( err ) {
84
- logger . debug ( 'fail loading module' , name )
97
+ } else {
98
+ // module does not exists in the path
99
+ logger . debug ( { requireResult : contextReq } , `module '${ name } ' not found` )
100
+ continue
85
101
}
86
102
}
87
103
88
- return loaded && this . _patch ( name , loaded )
104
+ // return the loaded one, could be one that has been loaded, or one which has failed during load
105
+ // but not one which does not exists
106
+ return loaded
107
+ }
108
+
109
+ tryTheseOr < T > ( moduleNames : [ string , ...string [ ] ] | string , missingResult : T , allowLoadError ?: boolean ) : T
110
+ tryTheseOr < T > ( moduleNames : [ string , ...string [ ] ] | string , missingResult ?: T , allowLoadError ?: boolean ) : T | undefined
111
+ tryTheseOr < T > ( moduleNames : [ string , ...string [ ] ] | string , missingResult ?: T , allowLoadError = false ) : T | undefined {
112
+ const args : [ string , ...string [ ] ] = Array . isArray ( moduleNames ) ? moduleNames : [ moduleNames ]
113
+ const result = this . tryThese ( ...args )
114
+ if ( ! result ) return missingResult
115
+ if ( ! result . error ) return result . exports as T
116
+ if ( allowLoadError ) return missingResult
117
+ throw result . error
89
118
}
90
119
91
120
@Memoize ( name => name )
@@ -105,8 +134,10 @@ export class Importer implements TsJestImporter {
105
134
// try to load any of the alternative after trying main one
106
135
const res = this . tryThese ( moduleName , ...alternatives )
107
136
// if we could load one, return it
108
- if ( res ) {
109
- return res
137
+ if ( res && res . exists ) {
138
+ if ( ! res . error ) return res . exports
139
+ // it could not load because of a failure while importing, but it exists
140
+ throw new Error ( interpolate ( Errors . LoadingModuleFailed , { module : res . given , error : res . error . message } ) )
110
141
}
111
142
112
143
// if it couldn't load, build a nice error message so the user can fix it by himself
@@ -136,11 +167,43 @@ export class Importer implements TsJestImporter {
136
167
*/
137
168
export const importer = Importer . instance
138
169
170
+ /**
171
+ * @internal
172
+ */
173
+ export interface RequireResult < E = boolean > {
174
+ exists : E
175
+ given : string
176
+ path ?: string
177
+ exports ?: any
178
+ error ?: Error
179
+ }
180
+
181
+ function requireWrapper ( moduleName : string ) : RequireResult {
182
+ let path : string
183
+ let exists = false
184
+ try {
185
+ path = resolveModule ( moduleName )
186
+ exists = true
187
+ } catch ( error ) {
188
+ return { error, exists, given : moduleName }
189
+ }
190
+ const result : RequireResult = { exists, path, given : moduleName }
191
+ try {
192
+ result . exports = requireModule ( moduleName )
193
+ } catch ( error ) {
194
+ result . error = error
195
+ }
196
+ return result
197
+ }
198
+
139
199
let requireModule = ( mod : string ) => require ( mod )
200
+ let resolveModule = ( mod : string ) => require . resolve ( mod )
201
+
140
202
/**
141
203
* @internal
142
204
*/
143
205
// so that we can test easier
144
- export function __requireModule ( localRequire : typeof requireModule ) {
206
+ export function __requireModule ( localRequire : typeof requireModule , localResolve : typeof resolveModule ) {
145
207
requireModule = localRequire
208
+ resolveModule = localResolve
146
209
}
0 commit comments