Module resolution in JavaScript is a complex topic.
The ecosystem is currently in the midst of a years-long transition from CommonJS modules to native ES modules. TypeScript enforces its own set of rules around import extensions that aren't compatible with ESM. Different build tools support path re-mapping via disparate non-compatible mechanisms.
Bun aims to provide a consistent and predictable module resolution system that just works. Unfortunately it's still quite complex.
Syntax
Consider the following files.
import { hello } from "./hello";
hello();
export function hello() {
console.log("Hello world!");
When we run
index.ts
, it prints "Hello world!".
bun index.ts
Hello world!
In this case, we are importing from
./hello
, a relative path with no extension.
Extensioned imports are optional but supported.
To resolve this import, Bun will check for the following files in order:
-
./hello.tsx
-
./hello.jsx
-
./hello.ts
-
./hello.mjs
-
./hello.js
-
./hello.cjs
-
./hello.json
-
./hello/index.tsx
-
./hello/index.jsx
-
./hello/index.ts
-
./hello/index.mjs
-
./hello/index.js
-
./hello/index.cjs
-
./hello/index.json
Import paths are case-insensitive, meaning these are all valid imports:
import { hello } from "./hello";
import { hello } from "./HELLO";
import { hello } from "./hElLo";
Import paths can optionally include extensions. If an extension is present, Bun will only check for a file with that exact extension.
import { hello } from "./hello";
import { hello } from "./hello.ts"; // this works
If you import
from "*.js{x}"
, Bun will additionally check for a matching
*.ts{x}
file, to be compatible with TypeScript's
ES module support
.
import { hello } from "./hello";
import { hello } from "./hello.ts"; // this works
import { hello } from "./hello.js"; // this also works
Bun supports both ES modules (
import
/
export
syntax) and CommonJS modules (
require()
/
module.exports
). The following CommonJS version would also work in Bun.
const { hello } = require("./hello");
hello();
function hello() {
console.log("Hello world!");
exports.hello = hello;
That said, using CommonJS is discouraged in new projects.
Module systems
Bun has native support for CommonJS and ES modules. ES Modules are the recommended module format for new projects, but CommonJS modules are still widely used in the Node.js ecosystem.
In Bun's JavaScript runtime,
require
can be used by both ES Modules and CommonJS modules. If the target module is an ES Module,
require
returns the module namespace object (equivalent to
import * as
). If the target module is a CommonJS module,
require
returns the
module.exports
object (as in Node.js).
Module Type |
require()
|
import * as
|
---|---|---|
ES Module | Module Namespace | Module Namespace |
CommonJS | module.exports |
default
is
module.exports
, keys of module.exports are named exports
|
Using
require()
You can
require()
any file or package, even
.ts
or
.mjs
files.
const { foo } = require("./foo"); // extensions are optional
const { bar } = require("./bar.mjs");
const { baz } = require("./baz.tsx");
What is a CommonJS module?