• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kulshekhar / ts-jest / 13747595123

09 Mar 2025 10:42AM UTC coverage: 93.494% (-1.6%) from 95.133%
13747595123

Pull #4714

github

web-flow
Merge 5c8ad95b3 into a84707fbc
Pull Request #4714: build: update CI config and `lint-staged` script

1063 of 1219 branches covered (87.2%)

Branch coverage included in aggregate %.

4585 of 4822 relevant lines covered (95.09%)

1696.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.77
/src/utils/importer.ts
1
import type { TBabelCore, TBabelJest, TEsBuild, TTypeScript } from '../types'
7✔
2

7✔
3
import { rootLogger } from './logger'
7✔
4
import { Memoize } from './memoize'
7✔
5
import { Errors, Helps, ImportReasons, interpolate } from './messages'
7✔
6

7✔
7
const logger = rootLogger.child({ namespace: 'Importer' })
7✔
8

7✔
9
// When adding an optional dependency which has another reason, add the reason in ImportReasons, and
7✔
10
// create a new method in Importer. Thus uses the importer.yourMethod(ImportReasons.TheReason)
7✔
11
// in the relevant code, so that the user knows why it needs it and how to install it in the
7✔
12
// case it can't import.
7✔
13
interface ImportOptions {
7✔
14
  alternatives?: string[]
7✔
15
  installTip?: string | Array<{ module: string; label: string }>
7✔
16
}
7✔
17

7✔
18
/**
7✔
19
 * @internal
7✔
20
 */
7✔
21
export class Importer {
7✔
22
  @Memoize()
7✔
23
  static get instance(): Importer {
7✔
24
    logger.debug('creating Importer singleton')
21✔
25

21✔
26
    return new Importer()
21✔
27
  }
21✔
28

7✔
29
  babelJest(why: ImportReasons): TBabelJest {
7✔
30
    return this._import(why, 'babel-jest')
70✔
31
  }
70✔
32

7✔
33
  babelCore(why: ImportReasons): TBabelCore {
7✔
34
    return this._import(why, '@babel/core')
14✔
35
  }
14✔
36

7✔
37
  typescript(why: ImportReasons, which: string): TTypeScript {
7✔
38
    return this._import(why, which)
826✔
39
  }
826✔
40

7✔
41
  esBuild(why: ImportReasons): TEsBuild {
7✔
42
    return this._import(why, 'esbuild')
7✔
43
  }
7✔
44

7✔
45
  @Memoize((...args: string[]) => args.join(':'))
7✔
46
  tryThese(moduleName: string, ...fallbacks: string[]): RequireResult<true> | undefined {
7✔
47
    let name: string
56✔
48
    let loaded: RequireResult<true> | undefined
56✔
49
    const tries = [moduleName, ...fallbacks]
56✔
50
    while ((name = tries.shift() as string) !== undefined) {
56✔
51
      const req = requireWrapper(name)
56✔
52

56✔
53
      // remove exports from what we're going to log
56✔
54
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
56✔
55
      const contextReq: any = { ...req }
56✔
56
      delete contextReq.exports
56✔
57

56✔
58
      if (req.exists) {
56✔
59
        // module exists
49✔
60
        loaded = req as RequireResult<true>
49✔
61
        if (req.error) {
49!
62
          logger.error({ requireResult: contextReq }, `failed loading module '${name}'`, req.error.message)
×
63
        } else {
49✔
64
          logger.debug({ requireResult: contextReq }, 'loaded module', name)
49✔
65
        }
49✔
66
        break
49✔
67
      } else {
56✔
68
        // module does not exists in the path
7✔
69
        logger.debug({ requireResult: contextReq }, `module '${name}' not found`)
7✔
70
      }
7✔
71
    }
56✔
72

56✔
73
    // return the loaded one, could be one that has been loaded, or one which has failed during load
56✔
74
    // but not one which does not exists
56✔
75
    return loaded
56✔
76
  }
56✔
77

7✔
78
  tryTheseOr<T>(moduleNames: [string, ...string[]] | string, missingResult: T, allowLoadError?: boolean): T
7✔
79
  // eslint-disable-next-line no-dupe-class-members
7✔
80
  tryTheseOr<T>(moduleNames: [string, ...string[]] | string, missingResult?: T, allowLoadError?: boolean): T | undefined
7✔
81
  // eslint-disable-next-line no-dupe-class-members
7✔
82
  tryTheseOr<T>(moduleNames: [string, ...string[]] | string, missingResult?: T, allowLoadError = false): T | undefined {
7✔
83
    const args: [string, ...string[]] = Array.isArray(moduleNames) ? moduleNames : [moduleNames]
35!
84
    const result = this.tryThese(...args)
35✔
85
    if (!result) return missingResult
35✔
86
    if (!result.error) return result.exports as T
35✔
87
    if (allowLoadError) return missingResult
35✔
88
    throw result.error
7✔
89
  }
7✔
90

7✔
91
  protected _import<T>(
7✔
92
    why: string,
903✔
93
    moduleName: string,
903✔
94
    { alternatives = [], installTip = moduleName }: ImportOptions = {},
903✔
95
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
903✔
96
  ): T {
903✔
97
    // try to load any of the alternative after trying main one
903✔
98
    const res = this.tryThese(moduleName, ...alternatives)
903✔
99
    // if we could load one, return it
903✔
100
    if (res?.exists) {
903✔
101
      if (!res.error) return res.exports
896✔
102
      // it could not load because of a failure while importing, but it exists
×
103
      throw new Error(interpolate(Errors.LoadingModuleFailed, { module: res.given, error: res.error.message }))
×
104
    }
×
105

7✔
106
    // if it couldn't load, build a nice error message so the user can fix it by himself
7✔
107
    const msg = alternatives.length ? Errors.UnableToLoadAnyModule : Errors.UnableToLoadOneModule
903!
108
    const loadModule = [moduleName, ...alternatives].map((m) => `"${m}"`).join(', ')
903✔
109
    if (typeof installTip === 'string') {
903✔
110
      installTip = [{ module: installTip, label: `install "${installTip}"` }]
7✔
111
    }
7✔
112
    const fix = installTip
7✔
113
      .map((tip) => `    ${installTip.length === 1 ? '↳' : '•'} ${interpolate(Helps.FixMissingModule, tip)}`)
7!
114
      .join('\n')
7✔
115

7✔
116
    throw new Error(
7✔
117
      interpolate(msg, {
7✔
118
        module: loadModule,
7✔
119
        reason: why,
7✔
120
        fix,
7✔
121
      }),
7✔
122
    )
7✔
123
  }
7✔
124
}
7✔
125

7✔
126
/**
7✔
127
 * @internal
7✔
128
 */
7✔
129
export const importer = Importer.instance
7✔
130

7✔
131
/**
7✔
132
 * @internal
7✔
133
 */
7✔
134
export interface RequireResult<E = boolean> {
7✔
135
  exists: E
7✔
136
  given: string
7✔
137
  path?: string
7✔
138
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7✔
139
  exports?: any
7✔
140
  error?: Error
7✔
141
}
7✔
142

7✔
143
function requireWrapper(moduleName: string): RequireResult {
56✔
144
  let path: string
56✔
145
  let exists = false
56✔
146
  try {
56✔
147
    path = resolveModule(moduleName)
56✔
148
    exists = true
56✔
149
  } catch (error) {
56✔
150
    return { error: error as Error, exists, given: moduleName }
7✔
151
  }
7✔
152
  const result: RequireResult = { exists, path, given: moduleName }
49✔
153
  try {
49✔
154
    result.exports = requireModule(path)
49✔
155
  } catch {
56!
156
    try {
×
157
      result.exports = requireModule(moduleName)
×
158
    } catch (error) {
×
159
      result.error = error as Error
×
160
    }
×
161
  }
×
162

49✔
163
  return result
49✔
164
}
49✔
165

7✔
166
// eslint-disable-next-line @typescript-eslint/no-require-imports
7✔
167
let requireModule = (mod: string) => require(mod)
7✔
168
let resolveModule = (mod: string) => require.resolve(mod, { paths: [process.cwd(), __dirname] })
7✔
169

7✔
170
/**
7✔
171
 * @internal
7✔
172
 */
7✔
173
// so that we can test easier
7✔
174
export function __requireModule(localRequire: typeof requireModule, localResolve: typeof resolveModule): void {
7✔
175
  requireModule = localRequire
7✔
176
  resolveModule = localResolve
7✔
177
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc