@boost/module
Version:
Module resolving and loading utilities with TypeScript support.
194 lines (180 loc) • 7.25 kB
JavaScript
// Bundled with Packemon: https://packemon.dev
// Platform: node, Support: stable, Format: cjs
;
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const fs = require('node:fs');
const _interopDefault = e => e && e.__esModule ? e : {
default: e
};
const fs__default = /*#__PURE__*/_interopDefault(fs);
/* eslint-disable @typescript-eslint/consistent-type-assertions */
/**
* Formats the shape of an imported module to align with the
* ES module specification.
*
* For ES or ES-like modules, returns the shape as-is.
*
* For CommonJS modules, returns an object with the following:
* - `module.exports` under the `default` property.
* - `exports.<name>` under properties of the same name,
* and also under a `default` object.
*/
function interopModule(result) {
if (typeof result !== 'object' || result === null) {
return {
default: result
};
}
// Already a module, so return early
if ('__esModule' in result || 'default' in result) {
return result;
}
return _objectSpread(_objectSpread({}, result), {}, {
default: result
});
}
/* eslint-disable no-magic-numbers */
const COMPILER_OPTIONS = {
allowJs: true,
allowSyntheticDefaultImports: true,
esModuleInterop: true,
noEmit: true
};
const NODE_VERSION = Number.parseFloat(process.version.slice(1));
function isNodeNext(path) {
return path.endsWith('.cts') || path.endsWith('.mts');
}
function isTypeScript(path) {
return path.endsWith('.ts') || path.endsWith('.tsx') || isNodeNext(path);
}
function getTargetFromNodeVersion(ts) {
if (NODE_VERSION >= 20) {
return ts.ScriptTarget.ESNext;
}
if (NODE_VERSION >= 18) {
return ts.ScriptTarget.ES2022;
}
if (NODE_VERSION >= 17) {
return ts.ScriptTarget.ES2021;
}
return ts.ScriptTarget.ES2020;
}
/* eslint-disable no-underscore-dangle, node/no-deprecated-api */
let tsInstance = null;
function loadTypeScript() {
if (!tsInstance) {
try {
tsInstance = require('typescript');
} catch {
// Ignore and check at runtime
}
}
return tsInstance;
}
function transform(contents, filePath) {
const ts = loadTypeScript();
if (!ts) {
throw new Error(`\`typescript\` package required for transforming file "${filePath}".`);
}
return ts.transpileModule(contents, {
compilerOptions: _objectSpread(_objectSpread({}, COMPILER_OPTIONS), {}, {
module: ts.ModuleKind.CommonJS,
resolveJsonModule: true,
target: getTargetFromNodeVersion(ts)
}),
fileName: filePath
}).outputText;
}
function transformHandler(mod, filePath) {
mod._compile(transform(fs__default.default.readFileSync(filePath, 'utf8'), filePath), filePath);
}
/**
* Register `.ts` and `.tsx` file extensions into Node.js's resolution algorithm.
*/
function registerExtensions() {
require.extensions['.ts'] = transformHandler;
require.extensions['.tsx'] = transformHandler;
require.extensions['.cts'] = transformHandler;
require.extensions['.mts'] = transformHandler;
}
/**
* Unregister `.ts` and `.tsx` file extensions.
*/
function unregisterExtensions() {
delete require.extensions['.ts'];
delete require.extensions['.tsx'];
delete require.extensions['.cts'];
delete require.extensions['.mts'];
}
/**
* Like `requireModule` but for loading TypeScript files ending in `ts` or `tsx`.
* When imported, will transform the file using the `typescript` package,
* evaluate the code in the current module context, and apply the same process
* to all child imports.
*
* ```ts
* import { requireTSModule } from '@boost/module';
*
* const result = requireTSModule('../../some/module.ts');
* ```
*
* > This helper rarely needs to be used directly as `requireModule` will
* > call it under the hood based on the file extension.
*/
function requireTSModule(path, requirer = require) {
const filePath = String(path);
if (!isTypeScript(filePath)) {
throw new Error(`Unable to import non-TypeScript file "${filePath}", use \`requireModule\` instead.`);
}
registerExtensions();
const result = interopModule(requirer(filePath));
unregisterExtensions();
return result;
}
/**
* Works in a similar fashion to the native NodeJS `require()`, but can also
* import custom file types like TypeScript, and also returns a module shape
* that aligns with the ESM loader specification.
*
* When loading custom file types, the extension in the file path is optional,
* as NodeJS will iterate through each extension until a file is found.
*
* ```ts
* import { requireModule } from '@boost/module';
*
* const result = requireModule('../../some/module');
* ```
*
* Caveats and differences:
*
* - CommonJS files that utilize `module.exports` (default export) will have this
* value returned under a `default` property, instead of being returned directly.
* - CommonJS files that utilize multiple `exports.<name>` (named exports) will
* have these values returned as properties on the result object, and will also
* be found on the `default` property.
* - "ES module like" files will be returned as-is. These are files that are built
* with Babel or TypeScript and export an `__esModule` internal property.
*
* These changes align with `import()` and the ES module system. We made this
* decision for consistency and reliability.
*/
function requireModule(path, requirer = require) {
const filePath = String(path);
if (filePath.endsWith('.mjs') || filePath.endsWith('.mts')) {
throw new Error(`Unable to require non-CommonJS file "${filePath}", use ESM imports instead.`);
}
if (isTypeScript(filePath)) {
return requireTSModule(filePath, requirer);
}
return interopModule(requirer(filePath));
}
exports.interopModule = interopModule;
exports.registerExtensions = registerExtensions;
exports.requireModule = requireModule;
exports.requireTSModule = requireTSModule;
exports.unregisterExtensions = unregisterExtensions;
//# sourceMappingURL=index.cjs.map