2020-07-02 13:02:35 +00:00
|
|
|
import { join } from 'path'
|
2021-04-02 11:47:01 +00:00
|
|
|
import jiti from 'jiti'
|
2020-07-02 13:02:35 +00:00
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
// TODO: use create-require for jest environment
|
|
|
|
const _require = jiti(process.cwd())
|
|
|
|
|
|
|
|
export interface ResolveModuleOptions {
|
|
|
|
paths?: string[]
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface RequireModuleOptions extends ResolveModuleOptions {
|
2021-04-15 18:49:29 +00:00
|
|
|
// TODO: use create-require for jest environment
|
|
|
|
// native?: boolean
|
|
|
|
/** Clear the require cache (force fresh require) but only if not within `node_modules` */
|
2021-04-02 11:47:01 +00:00
|
|
|
clearCache?: boolean
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Automatically de-default the result of requiring the module. */
|
2021-04-02 11:47:01 +00:00
|
|
|
interopDefault?: boolean
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isNodeModules (id: string) {
|
|
|
|
// TODO: Follow symlinks
|
2020-07-02 13:02:35 +00:00
|
|
|
return /[/\\]node_modules[/\\]/.test(id)
|
|
|
|
}
|
|
|
|
|
2020-07-30 23:40:16 +00:00
|
|
|
export function clearRequireCache (id: string) {
|
2021-04-02 11:47:01 +00:00
|
|
|
if (isNodeModules(id)) {
|
2020-07-02 13:02:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const entry = getRequireCacheItem(id)
|
|
|
|
|
|
|
|
if (!entry) {
|
2021-04-02 11:47:01 +00:00
|
|
|
delete _require.cache[id]
|
2020-07-02 13:02:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.parent) {
|
|
|
|
entry.parent.children = entry.parent.children.filter(e => e.id !== id)
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const child of entry.children) {
|
|
|
|
clearRequireCache(child.id)
|
|
|
|
}
|
|
|
|
|
2021-04-02 11:47:01 +00:00
|
|
|
delete _require.cache[id]
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 23:40:16 +00:00
|
|
|
export function scanRequireTree (id: string, files = new Set<string>()) {
|
2021-04-02 11:47:01 +00:00
|
|
|
if (isNodeModules(id) || files.has(id)) {
|
2020-07-02 13:02:35 +00:00
|
|
|
return files
|
|
|
|
}
|
|
|
|
|
|
|
|
const entry = getRequireCacheItem(id)
|
|
|
|
|
|
|
|
if (!entry) {
|
|
|
|
files.add(id)
|
|
|
|
return files
|
|
|
|
}
|
|
|
|
|
|
|
|
files.add(entry.id)
|
|
|
|
|
|
|
|
for (const child of entry.children) {
|
|
|
|
scanRequireTree(child.id, files)
|
|
|
|
}
|
|
|
|
|
|
|
|
return files
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Access the require cache by module id. */
|
2020-07-30 23:40:16 +00:00
|
|
|
export function getRequireCacheItem (id: string) {
|
2020-07-02 13:02:35 +00:00
|
|
|
try {
|
2021-04-02 11:47:01 +00:00
|
|
|
return _require.cache[id]
|
2020-07-02 13:02:35 +00:00
|
|
|
} catch (e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Resolve the `package.json` file for a given module. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function requireModulePkg (id: string, opts: RequireModuleOptions = {}) {
|
|
|
|
return requireModule(join(id, 'package.json'), opts)
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Resolve the path of a module. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
|
|
|
|
return _require.resolve(id, {
|
|
|
|
paths: opts.paths
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Try to resolve the path of a module, but don't emit an error if it can't be found. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function tryResolveModule (path: string, opts: ResolveModuleOptions = {}) {
|
2020-07-02 13:02:35 +00:00
|
|
|
try {
|
2021-04-02 11:47:01 +00:00
|
|
|
return resolveModule(path, opts)
|
|
|
|
} catch (error) {
|
|
|
|
if (error.code !== 'MODULE_NOT_FOUND') {
|
|
|
|
throw error
|
|
|
|
}
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Require a module and return it. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function requireModule (id: string, opts: RequireModuleOptions = {}) {
|
|
|
|
// Resolve id
|
|
|
|
const resolvedPath = resolveModule(id, opts)
|
|
|
|
|
|
|
|
// Clear require cache if necessary
|
|
|
|
if (opts.clearCache && !isNodeModules(id)) {
|
|
|
|
clearRequireCache(resolvedPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to require
|
|
|
|
let requiredModule = _require(resolvedPath)
|
|
|
|
|
|
|
|
// Interop default
|
|
|
|
if (opts.interopDefault !== false && requiredModule && requiredModule.default) {
|
|
|
|
requiredModule = requiredModule.default
|
|
|
|
}
|
|
|
|
|
|
|
|
return requiredModule
|
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Try to require a module, but don't emit an error if the module can't be required. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function tryRequireModule (id: string, opts: RequireModuleOptions = {}) {
|
|
|
|
try {
|
|
|
|
return requireModule(id, opts)
|
|
|
|
} catch (e) {
|
|
|
|
}
|
2020-07-02 13:02:35 +00:00
|
|
|
}
|