Modules
Load and resolve custom file types at runtime with a Node.js require
replacement or
next-generation hooks. Currently supports
TypeScript for .ts
and .tsx
files.
Installation
- Yarn
- pnpm
- npm
yarn add @boost/module
pnpm add @boost/module
npm install @boost/module
CommonJS requires
Node.js's native require()
has historically only supported .js
and .json
files (and now .cjs
too). But what if we were able to require non-JavaScript files at runtime also? Like TypeScript?
This package does just that through a new function called requireModule()
.
This function operates by patching the list of allowed file types/extensions in Node.js's module resolution. Begin by importing the function and importing a supported file type!
import { requireModule } from '@boost/module';
const result = requireModule('./some/non-js/file');
This function should only be used to import module-like files, like JavaScript and TypeScript. It should not be used for other file types, like JSON or YAML.
If you'd prefer to not use requireModule()
and still use native require()
, but also support
custom file types, you may do so by calling
registerExtensions()
at the top of your script or
application entry point.
Module interoperability
Unlike require()
which returns imported values as-is, requireModule()
changes
the shape of the import to align with ECMAScript modules. We do this for interoperability and
consistency sake, so that the developer experience is the same for both systems.
So what does this mean exactly? The biggest change is that CommonJS default exports
(module.exports
) are now returned under a default
property, like so.
module.exports = 123;
const value = require('./example'); // 123
const { default: value } = requireModule('./example'); // 123
Another change is that CommonJS named exports (exports.<name>
) are returned as properties in the
imported object, as well as properties in an object on the default
property. This pattern exists
to match Node.js >= v12.20 functionality.
exports.foo = 'abc';
exports.bar = 123;
const value = require('./example');
value.foo; // abc
value.bar; // 123
const value = requireModule('./example');
value.foo; // abc
value.bar; // 123
value.foo === value.default.foo; // true
Generic types
In TypeScript, the native require()
is typed to return any
, which isn't that ideal. However,
requireModule()
can type both the default and named exports of a module via
generics.
The 1st generic slot types the default export (module.exports
for CJS and export default
for
ESM) under the returned default
property.
const result = requireModule<string>('./example');
result.default; // string
While the 2nd generic slot types named exports (exports.<name>
for CJS and export <name>
for
ESM) under their respective property names.
const result = requireModule<string, { foo: string; bar: number }>('./example');
result.foo; // string
result.bar; // number
result.default; // string
For backwards compatibility with CommonJS (can't mix module.exports
and exports.<name>
), named
exports are also encapsulated under the default
property. To type this correctly, pass void
for
the default generic, which passes the type through accordingly.
const result = requireModule<void, { foo: string; bar: number }>('./example');
result.foo; // string
result.bar; // number
result.default.foo; // string
result.default.bar; // number
Node.js hooks
Node.js supports an experimental feature called
hooks, where non-JavaScript files can be
loaded, parsed, and evaluated at runtime through Node.js's module system when using
import
/export
.
To make use of hooks, the following requirements must be met.
- Node.js >= v16.20
- Must use
import()
orimport
/export
syntax (norequire
) - Source files must be modules (
.mjs
,.ts
, etc) - Imported files must have trailing file extensions
If you meet all of these requirements, then you may run your Node.js script with one of the following patterns.
# v20+
node --import @boost/module/register ./path/to/entry-point.mjs
# v18+
node --loader @boost/module/hook-typescript ./path/to/entry-point.mjs
# v16+
node --experimental-loader @boost/module/hook-typescript ./path/to/entry-point.mjs
For example, with the above you can now import TypeScript files as if they were standard JavaScript!
// entry-point.mjs
import defaultValue, { namedValue } from './some/file/written/in.ts';
Supported file types
TypeScript
TypeScript files are supported for .ts
, .tsx
, .cts
, and
.mts
file extensions. The TypeScript source code will be down-leveled according to the currently
running Node.js version and its capabilities.
The TypeScript only hook/loader can be referenced with @boost/module/hook-typescript
.