Skip to main content

Modules

API BackendTooling

Load and resolve custom file types at runtime with a Node.js require replacement or next-generation loaders. Currently supports TypeScript for .ts and .tsx files.

Installation#

yarn add @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.

example.js
module.exports = 123;
const value = require('./example'); // 123const { 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.

example.js
exports.foo = 'abc';exports.bar = 123;
const value = require('./example');value.foo; // abcvalue.bar; // 123
const value = requireModule('./example');value.foo; // abcvalue.bar; // 123value.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; // stringresult.bar; // numberresult.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; // stringresult.bar; // numberresult.default.foo; // stringresult.default.bar; // number

ECMAScript module loaders#

Node.js supports an experimental feature called ESM loaders, 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 loaders, the following requirements must be met.

  • Node.js >= v12.17
  • Must use import() or import/export syntax (no require)
  • 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 --experimental-loader and a supported loader type, or the general loader that supports all file types (demonstrated below).

node --experimental-loader @boost/module/loader.mjs ./path/to/entry-point.mjs

For example, with the loader above you can now import TypeScript files as if they were standard JavaScript!

import defaultValue, { namedValue } from './some/file/written/in.ts';

Supported file types#

TypeScript#

TypeScript files are supported for .ts and .tsx file extensions. The TypeScript source code will be down-leveled according to the currently running Node.js version and its capabilities.

The TypeScript only ESM loader can be referenced with @boost/module/loader/typescript.mjs.