@nestjs/common
Version:
Nest - modern, fast, powerful node.js web framework (@common)
98 lines (97 loc) • 3.61 kB
JavaScript
import { createRequire } from 'module';
import { Logger } from '../services/logger.service.js';
const MISSING_REQUIRED_DEPENDENCY = (name, reason) => `The "${name}" package is missing. Please, make sure to install it to use ${reason}.`;
const logger = new Logger('PackageLoader');
/**
* Cache of already-loaded packages keyed by package name.
* Allows subsequent calls (including synchronous ones) to
* return the module without another async import().
*/
const packageCache = new Map();
export async function loadPackage(packageName, context, loaderFn) {
const cached = packageCache.get(packageName);
if (cached) {
return cached;
}
try {
const pkg = loaderFn ? await loaderFn() : await import(packageName);
packageCache.set(packageName, pkg);
return pkg;
}
catch (e) {
logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
Logger.flush();
process.exit(1);
}
}
/**
* Synchronously loads a package using `createRequire` and caches it.
* This is meant for optional dependencies that must be loaded in
* synchronous contexts (e.g. constructors).
*
* @param loaderFn Optional synchronous loader (e.g.
* `() => createRequire(import.meta.url)('pkg')`).
* When provided, bundlers can statically analyse the string literal.
* Falls back to a `createRequire` call resolved from this file.
*/
export function loadPackageSync(packageName, context, loaderFn) {
const cached = packageCache.get(packageName);
if (cached) {
return cached;
}
try {
const pkg = loaderFn
? loaderFn()
: createRequire(import.meta.url)(packageName);
packageCache.set(packageName, pkg);
return pkg;
}
catch (e) {
logger.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
Logger.flush();
process.exit(1);
}
}
/**
* Synchronously returns a package that was previously loaded and cached
* via {@link loadPackage}. Throws if the package has not been loaded yet.
*
* Use this in methods that must remain synchronous (e.g. `connectMicroservice`).
* Ensure that `loadPackage()` has been `await`ed for the same package name
* before calling this function (typically during `init()` or `compile()`).
*/
export function loadPackageCached(packageName, context) {
const cached = packageCache.get(packageName);
if (!cached) {
if (context) {
// The package was not preloaded (typically because it is not
// installed). Fall back to a synchronous load so the user gets the
// actionable "package is missing" message instead of an internal error.
return loadPackageSync(packageName, context);
}
throw new Error(`Package "${packageName}" has not been loaded yet. ` +
`Ensure loadPackage("${packageName}", ...) has been awaited before calling loadPackageCached.`);
}
return cached;
}
/**
* Attempts to load and cache a package. Returns the loaded module on success
* or `null` if the package is not installed.
*
* Unlike {@link loadPackage}, this function does **not** terminate the process
* when the package is missing, making it suitable for optional dependencies.
*/
export async function tryLoadPackage(packageName, loaderFn) {
const cached = packageCache.get(packageName);
if (cached) {
return cached;
}
try {
const pkg = loaderFn ? await loaderFn() : await import(packageName);
packageCache.set(packageName, pkg);
return pkg;
}
catch {
return null;
}
}