Jiti: TypeScript and ESM Runtime for Node.js
Jiti is a robust utility for Node.js that provides seamless runtime support for both TypeScript and ECMAScript Modules (ESM), along with comprehensive interoperability between ESM and CommonJS. The package is currently at version 2.6.1 and maintains a high release cadence with frequent minor version bumps and patch fixes, indicating active and continuous development. Its key differentiators include a 'zero dependency' footprint, significant performance optimizations (e.g., improved startup times from v2.6.0 onwards), and widespread adoption within prominent projects such as Nuxt, TailwindCSS, ESLint, and Storybook. Jiti offers both an asynchronous (`jiti.import`) and a synchronously (now deprecated `jiti()`) API, alongside a global ESM loader for Node.js versions 20 and above. It simplifies the execution of complex JavaScript ecosystems by handling various module formats and language extensions without requiring explicit build steps.
Common errors
-
Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported. Instead change the require of ... to a dynamic import()
cause Attempting to use Node.js `require()` to load an ES Module (with `.mjs` or `type: module` in `package.json`) without Jiti's interop layer or proper Node.js ESM configuration.fixIf using Jiti, ensure it's initialized and use `await jiti.import('./path/to/esm-module.mjs')`. Alternatively, enable Jiti's global loader with `node --import jiti/register`. -
SyntaxError: Cannot use import statement outside a module
cause Running a TypeScript or ES Module file directly with `node` without Jiti's runtime transpilation or a configured Node.js ESM environment.fixExecute the file using `npx jiti ./your-script.ts` or `node --import jiti/register ./your-script.ts`. If programmatic, ensure `createJiti` is correctly initialized. -
TypeError: module.default is not a function/object (or: undefined is not an object)
cause Incorrectly accessing the `default` export of a module. Some modules might not have a `default` export, or the module itself is the default export.fixUse `const mod = await jiti.import(id); console.log(mod?.default ?? mod);` or leverage the shorthand `const modDefault = await jiti.import(id, { default: true });` for direct default export access.
Warnings
- breaking Jiti versions `>=2.1` introduced an enhanced `interopDefault` mechanism using a new Proxy method. This change, while improving compatibility, might cause subtle behavior differences or issues for existing applications. If you migrated from `1.x` or `2.0.0` to `>=2.1` and encounter unexpected behavior, please review module interop logic.
- deprecated The synchronous `jiti()` API (used like `jiti('./path/to/file.ts')`) is considered deprecated. It is recommended to migrate to the asynchronous `jiti.import()` API for new code to better align with modern JavaScript module loading patterns and avoid potential blocking operations.
- gotcha Using `jiti/register` for a global ESM loader hook requires Node.js version 20 or higher for full and stable compatibility.
- gotcha When initializing `createJiti` in a CommonJS context, the documentation marks `createJiti(__filename)` as a 'deprecated' pattern, steering users towards ESM `import { createJiti } from 'jiti'; const jiti = createJiti(import.meta.url);` as the preferred modern approach.
Install
-
npm install jiti -
yarn add jiti -
pnpm add jiti
Imports
- createJiti
const { createJiti } = require('jiti');import { createJiti } from 'jiti'; - jiti/register
require('jiti/register');import 'jiti/register';
- Jiti (type)
import type { Jiti } from 'jiti';
Quickstart
import { createJiti } from "jiti";
import path from "node:path";
import fs from "node:fs/promises";
// Initialize jiti, specifying the current module's URL for resolution context.
// In a CommonJS environment, use `__filename` instead of `import.meta.url`.
const jiti = createJiti(import.meta.url, {
debug: process.env.NODE_ENV === 'development',
// Cache directory to speed up subsequent loads
cacheDir: path.join(process.cwd(), '.jiti-cache'),
});
// Example of importing a TypeScript file with ESM compatibility
// For demonstration, we'll create a dummy TS file first.
async function runJitiExample() {
const dummyTsPath = path.join(__dirname, 'temp-module.ts');
const dummyTsContent = 'export const message = "Hello from Jiti!"; export default { status: "OK" };';
await fs.writeFile(dummyTsPath, dummyTsContent);
try {
// Import the module. Jiti handles TypeScript transpilation.
// Use a generic type to ensure strong typing for the imported module.
const mod = await jiti.import<{ message: string; default: { status: string } }>(dummyTsPath);
console.log("Named export 'message':", mod.message);
// To directly get the default export (common for default-export-only modules)
const modDefault = await jiti.import<{ status: string }>(dummyTsPath, { default: true });
console.log("Default export 'status':", modDefault.status);
// Jiti can also resolve paths using ESM resolution logic.
const resolvedPath = jiti.esmResolve('./temp-module.ts');
console.log("Resolved path for temp-module.ts:", resolvedPath);
} catch (error) {
console.error("Error running Jiti example:", error);
} finally {
// Clean up the temporary file.
await fs.unlink(dummyTsPath);
}
}
runJitiExample();