-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
helpers.ts
109 lines (101 loc) · 3.52 KB
/
helpers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* eslint-disable unicorn/no-await-expression-member */
import { createRequire } from 'node:module'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
export const arrayify = <T, R = T extends Array<infer S> ? S : T>(
...args: T[]
) =>
args.reduce<R[]>((arr, curr) => {
arr.push(...(Array.isArray(curr) ? curr : curr == null ? [] : [curr]))
return arr
}, [])
/* istanbul ignore next */
export const loadEsmModule = <T>(modulePath: URL | string): Promise<T> =>
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
new Function('modulePath', `return import(modulePath);`)(
modulePath,
) as Promise<T>
const cjsRequire =
typeof require === 'undefined' ? createRequire(import.meta.url) : require
/**
* ! copied from https://github.com/just-jeb/angular-builders/blob/master/packages/custom-webpack/src/utils.ts#L53-L67
*
* This uses a dynamic import to load a module which may be ESM.
* CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
* will currently, unconditionally downlevel dynamic import into a require call.
* require calls cannot load ESM code and will result in a runtime error. To workaround
* this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
* Once TypeScript provides support for keeping the dynamic import this workaround can
* be dropped.
*
* @param modulePath The path of the module to load.
* @returns A Promise that resolves to the dynamically imported module.
*/
/**
* Loads CJS and ESM modules based on extension
* @param modulePath path to the module
* @returns
*/
export const loadModule = async <T>(modulePath: string): Promise<T> => {
const esModulePath = path.isAbsolute(modulePath)
? pathToFileURL(modulePath)
: modulePath
switch (path.extname(modulePath)) {
/* istanbul ignore next */
case '.mjs': {
return (await loadEsmModule<{ default: T }>(esModulePath)).default
}
/* istanbul ignore next */
case '.cjs': {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return cjsRequire(modulePath)
}
default: {
// The file could be either CommonJS or ESM.
// CommonJS is tried first then ESM if loading fails.
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return cjsRequire(modulePath)
} catch (err) {
/* istanbul ignore if */
if ((err as { code: string }).code === 'ERR_REQUIRE_ESM') {
// Load the ESM configuration file using the TypeScript dynamic import workaround.
// Once TypeScript provides support for keeping the dynamic import this workaround can be
// changed to a direct dynamic import.
return (await loadEsmModule<{ default: T }>(esModulePath)).default
}
throw err
}
}
}
}
export const requirePkg = async <T>(
plugin: string,
prefix: string,
filePath?: string,
): Promise<T> => {
let packages: string[]
if (filePath && /^\.\.?(?:[/\\]|$)/.test(plugin)) {
packages = [path.resolve(path.dirname(filePath), plugin)]
} else {
prefix = prefix.endsWith('-') ? prefix : prefix + '-'
packages = [
plugin,
/* istanbul ignore next */
plugin.startsWith('@')
? plugin.replace('/', '/' + prefix)
: prefix + plugin,
]
}
let error: Error | undefined
for (const pkg of packages) {
try {
return await loadModule<T>(pkg)
} catch (err) {
if (!error) {
error = err as Error
}
}
}
throw error
}