Module resolution file extension order for ESM imports
For local ESM imports without an explicit extension, Bun checks for files in this order: .tsx, .jsx, .mts, .ts, .mjs, .js, .cts, .cjs, .json, then the same extensions with /index prefix. If an extension is explicitly included in the import path, Bun checks for that exact file first, then falls back to the extension list appended to the full path.
TypeScript file extension substitution in Bun
If you import from "*.js" or "*.jsx", Bun also checks for a matching *.ts or *.tsx file. Outside node_modules, importing from "*.mjs" also matches "*.mts". This allows source files to reference each other by their compiled output paths. Unlike TypeScript, Bun does not rewrite .cjs to .cts.
require() behavior with ES modules vs CommonJS in Bun
In Bun's JavaScript runtime, both ES modules and CommonJS modules can use require(). 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.
import vs require behavior with different module types
When using import * as with an ES module, it returns the module namespace. When using import * as with a CommonJS module, the default is module.exports and keys of module.exports are named exports. When using require() with an ES module, it returns the module namespace. When using require() with a CommonJS module, it returns module.exports.
Top-level await and require() incompatibility
You cannot require() a file that uses top-level await, since the require() function is inherently synchronous. If using top-level await in your application code, ensure that file is not require()'d from elsewhere. Use import or dynamic import() instead.
Bun package.json exports field resolution order
When resolving the entrypoint of a package using the exports field, Bun checks for the following conditions in order: bun, node-addons (unless --no-addons was passed), node, require (if the importer uses require()), import (if the importer uses import), default. The first matching condition determines the package's entrypoint.
Bun supports package.json subpath exports and imports
Bun respects Node.js-style subpath exports (defined in package.json "exports") and subpath imports (defined in package.json "imports"). Subpath exports and conditional exports work together. When any subpath is specified in the "exports" map, only files explicitly exported are importable; other subpaths become unimportable.
Bun special "bun" export condition for TypeScript libraries
Bun supports a special "bun" export condition in package.json. If your library is written in TypeScript, you can publish un-transpiled TypeScript files to npm directly. Specify your package's *.ts entrypoint in the "bun" condition, and Bun will import and execute your TypeScript source files directly.
Fallback to main and module fields when exports is not defined
If the exports field is not defined in package.json, Bun falls back to legacy top-level entrypoint fields. Bun prefers the "main" field (or an implicit index.* file) when present, and uses "module" otherwise.
NODE_PATH environment variable in Bun
Bun supports NODE_PATH for additional module resolution directories. Use the syntax: NODE_PATH=./packages bun run src/index.js. Multiple paths use the platform's delimiter: colon (:) on Unix/macOS and semicolon (;) on Windows. Example: NODE_PATH=./packages:./lib bun run src/index.js on Unix.
Bun --conditions flag for package resolution
The --conditions flag specifies conditions to use when resolving packages from package.json "exports". Both bun build and Bun's runtime support this flag. Usage: bun build --conditions="react-server" --target=bun ./app/foo/route.js or bun --conditions="react-server" ./app/foo/route.js. Can also be used programmatically with Bun.build({conditions: ["react-server"], ...}).
TypeScript compilerOptions.paths for import remapping in Bun
Bun supports import path re-mapping through TypeScript's compilerOptions.paths in tsconfig.json. Example: {"compilerOptions": {"paths": {"config": ["./config.ts"], "components/*": ["components/*"]}}}. For non-TypeScript projects, use jsconfig.json in the project root for the same behavior.
Node.js-style subpath imports in package.json for Bun
Bun supports Node.js-style subpath imports in package.json, where mapped paths must start with #. Example: {"imports": {"#config": "./config.ts", "#components/*": "./components/*"}}. TypeScript and editors resolve these, and you can use both compilerOptions.paths and package.json imports mechanisms together.
import.meta properties in Bun
Bun implements the following import.meta properties: import.meta.dir (absolute path to directory containing current file, equivalent to __dirname), import.meta.dirname (alias to import.meta.dir for Node.js compatibility), import.meta.env (alias to process.env), import.meta.file (name of current file), import.meta.path (absolute path to current file, equivalent to __filename), import.meta.filename (alias to import.meta.path for Node.js compatibility), import.meta.main (boolean indicating if file is directly executed by bun run), import.meta.resolve (resolves a module specifier to a url, e.g. import.meta.resolve("zod") returns "file:///path/to/project/node_modules/zod/index.js"), import.meta.url (string url to current file).
import.meta.resolve() function in Bun
import.meta.resolve() resolves a module specifier (e.g. "zod" or "./file.tsx") to a url. It is equivalent to import.meta.resolve in browsers. Example: import.meta.resolve("zod") returns "file:///path/to/project/node_modules/zod/index.js".
CommonJS interop in Bun with synthetic module records
When Bun's JavaScript transpiler detects usages of module.exports, it treats the file as CommonJS and wraps the transpiled module in a function: (function (module, exports, require) { // transpiled module })(module, exports, require). Once the CommonJS module is evaluated, Bun creates a Synthetic Module Record with the default ES Module export set to module.exports. If module.exports is an object, Bun also re-exports its keys as named exports.
Importing files with any extension in Bun
You can require() or import any file or package, even .ts, .mjs, or .cjs files. Extensions are optional in import/require statements; you can import { foo } from "./foo" or import { foo } from "./foo.ts".
Using import and require() together in same file with Bun
In Bun, you can use both import and require() in the same file—they both work all the time. For example: import { stuff } from "./my-commonjs.cjs"; import Stuff from "./my-commonjs.cjs"; const myStuff = require("./my-commonjs.cjs");