# Compatibility

> What works as it does under bun run, what differs on purpose, and what isn't available in an executable built with bun compile

<Warning>Ahead-of-time compilation is experimental.</Warning>

An executable built with [`bun compile`](/compiler/aot) runs the same language and the same APIs as `bun run`. This page lists what differs.

A value that doesn't match its declared type isn't on the list. It [falls back to the slow path](/compiler/types#when-a-value-doesnt-match-its-type), and the program behaves as it does under `bun run`.

If an error message sent you here:

| The message                                                                                   | See                                                                    |
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `Cannot assign to ... because built-in objects are read-only`                                 | [Built-in objects are read-only](#built-in-objects-are-read-only)      |
| `Cannot delete ... because built-in objects are read-only`                                    | [Built-in objects are read-only](#built-in-objects-are-read-only)      |
| `Cannot change the prototype of ... because built-in objects are read-only`                   | [Built-in objects are read-only](#built-in-objects-are-read-only)      |
| `Cannot declare 'Math': compiled code treats the global 'Math' as a constant ...`             | [Built-in objects are read-only](#built-in-objects-are-read-only)      |
| `Cannot load '...': it isn't in this executable`                                              | [Code that isn't in the executable](#code-that-isnt-in-the-executable) |
| `Cannot load '...': this executable can't load a module from a dynamic path`                  | [Code from strings](#code-from-strings)                                |
| `Code generation from strings disallowed for this context: the compiler couldn't tell ...`    | [Code from strings](#code-from-strings)                                |
| `... was not compiled into this executable because your code never uses it`                   | [Code from strings](#code-from-strings)                                |
| `Cannot replace a loader in Module._extensions: imports in this executable were resolved ...` | [Loader hooks](#loader-hooks)                                          |
| `... can't be used with this executable`                                                      | [Options that load code](#options-that-load-code)                      |
| `... was compiled ahead of time and can't be imported inside a ShadowRealm`                   | [What isn't available](#what-isnt-available)                           |
| `super() cannot be called from direct eval`                                                   | [What isn't available](#what-isnt-available)                           |
| Anything else                                                                                 | [Troubleshooting](/compiler/troubleshooting)                           |

## What works the same

- npm packages, with or without types, except for [a few](#libraries-that-are-known-not-to-work)
- ES modules, CommonJS, `require()`, `import()` of a string literal, top-level `await`
- every `node:` and `bun:` module
- [Workers](#workers) and [native addons](#native-addons)
- [`eval` and `new Function`](#code-from-strings), in the interpreter
- [macros](/bundler/macros)
- [plugins of `Bun.build`](#plugins-and-build-options), `files`, `define`, `loader` and the other build options
- proxies, getters and setters, generators, symbols, `Reflect`, private members, `delete`, BigInt, regular expressions

Some of these take a slow path: a `Proxy` used as an array, and an array with holes, with getters for elements or with inherited elements. They behave as under `bun run`.

## What differs on purpose

| Area                                                                                      | `bun run`                                                        | Compiled                                                                                                                                                                                                                                |
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Type errors](/compiler/aot#type-errors-stop-the-build)                                   | `bun build` ignores them                                         | They stop the build                                                                                                                                                                                                                     |
| [The version of TypeScript](#type-errors-are-typescript-7s)                               | Types are never checked                                          | TypeScript 7's errors, whichever version you have installed                                                                                                                                                                             |
| [A `.ts` file without `import` or `export`](#files-without-import-or-export)              | Its variables aren't global. Another file that reads one throws  | The other file is a type error, also where `tsc` takes the file for a script                                                                                                                                                            |
| [Built-in objects](#built-in-objects-are-read-only)                                       | Can be changed                                                   | Can be extended. Their existing members can't be replaced                                                                                                                                                                               |
| [Seven Bun classes](#request-response-and-five-other-classes)                             | Their prototypes can be changed                                  | Their prototypes are read-only. Subclasses work                                                                                                                                                                                         |
| [Code from strings](#code-from-strings)                                                   | The JIT compiles it                                              | Bun interprets it                                                                                                                                                                                                                       |
| [WebAssembly](#webassembly)                                                               | The JIT compiles it                                              | A `.wasm` file that you import is compiled. Bun interprets any other module                                                                                                                                                             |
| [Dynamic imports](#dynamic-imports)                                                       | Loaded from disk                                                 | Entry points are found in the executable. Other code throws. Data files load                                                                                                                                                            |
| Exports of compiled modules                                                               | `spyOn(namespace, "f")` from `bun:test` replaces the export      | Throws a `TypeError`. An export of a module loaded from disk at run time can still be spied on                                                                                                                                          |
| [The module cache](#the-module-cache)                                                     | Lists every module that has run. `delete` makes one run again    | Doesn't list the modules of the executable. Each runs at most once                                                                                                                                                                      |
| [A CommonJS module that threw](#the-module-cache)                                         | The next `require()` runs it again                               | The next `require()` throws the same error                                                                                                                                                                                              |
| [Loader hooks](#loader-hooks)                                                             | `Module.prototype.require = f` sees every `require()`            | It sees a `require()` of a dynamic path. A constant path was resolved at build time                                                                                                                                                     |
| [`require.main`, `module.children`, `module.parent`](#module-objects)                     | Lead from any module object to every other                       | The same if some file of your program names one of them. Otherwise `undefined` for everybody                                                                                                                                            |
| [`export *` of a CommonJS module](#re-exports-of-commonjs-modules)                        | The namespace object follows the specification                   | It differs in one rare case                                                                                                                                                                                                             |
| The namespace object of an ES module that a CommonJS module has required                  | `Object.isExtensible()` says `true`, `Object.isSealed()` `false` | `false` and `true`, as for every other namespace object. In both, nothing can be added to it                                                                                                                                            |
| [Some error messages](#error-messages)                                                    | Quote your source: `(evaluating 'o.a.c')`                        | Quote what you called, the name of the property, `(evaluating 'null.c')`, or nothing                                                                                                                                                    |
| [The lines of code under an error](#error-messages)                                       | Printed above the message, with a caret                          | Not printed. The message and the stack trace are the same                                                                                                                                                                               |
| The `line`, `column` and `stack` of a `DOMException`. Where a module disposes a `using`   | Places in the text that runs                                     | Places in the source file                                                                                                                                                                                                               |
| The stack of an error that the parameters of an async function with an `await` throw      | Doesn't list the function                                        | Lists the function                                                                                                                                                                                                                      |
| The stack of an error from the setup of an `enum`, a `namespace` or a decorated class     | Has an `<anonymous>` frame for the declaration                   | Has no such frame. The function around the declaration is listed at the place that ran                                                                                                                                                  |
| The stack of an error from a field of a class with a decorated field and no constructor   | Lists `new` and the name of the class                            | Doesn't, as for a class without decorators                                                                                                                                                                                              |
| The stack of a `RangeError` from `new Array(-1)`, where `Array` is a variable of your own | Often doesn't list `Array`, if the variable holds the built-in   | Lists `Array`, like any other function that a variable holds                                                                                                                                                                            |
| The frames printed for an error that leaves a generator and that nobody catches           | Don't list the generator                                         | List the generator                                                                                                                                                                                                                      |
| A closure that runs while another closure of the same function gets a `displayName`       | Stack traces list it under its own name                          | They list it under the `displayName` until it returns, and a callback of `map()` or `forEach()` until that call ends. Closures that are called later are listed under their own names                                                   |
| Closures of one function that `new Function()` code or a `Proxy` trap names one by one    | Stack traces list each closure under its own name                | Where the build knows which function a call runs, they list each of the closures under the name that was set last                                                                                                                       |
| A closure that is named after it threw, before `stack` is read, where the build can't see | The stack lists it under the new name                            | The first time in a program, the stack lists it under the name that it had when it threw. Later ones are listed under the new name                                                                                                      |
| [The name of a class under a computed key](#class-names-from-computed-keys)               | Set after the static fields ran, and skipped if they read `name` | Set before the static fields run                                                                                                                                                                                                        |
| [The name of a function in a local variable](#function-names-from-local-variables)        | Empty, if the variable is a `let` or `const` that is used once   | The name of the variable                                                                                                                                                                                                                |
| The name of a function or a class behind `export default` that has no name of its own     | The file's name and `_default`. A class: `starDefault` in traces | `default`, in `name` and in stack traces                                                                                                                                                                                                |
| [Raw strings of tagged templates](#raw-strings-of-tagged-templates)                       | Characters outside ASCII come back as escapes                    | The text as you wrote it                                                                                                                                                                                                                |
| [`Function.prototype.toString()`](#functionprototypetostring)                             | What the transpiler printed                                      | The text of your file, without the types                                                                                                                                                                                                |
| Code nested more than 2,000 levels deep: calls in calls, an `else if` behind 2,000 others | Runs, as deep as the stack allows                                | The build stops with `TS100301` at the code, or with `TS100060` for a file that is too deep to read. A sum or an `&&` of any number of operands isn't nested                                                                            |
| `JSON.stringify()` of objects nested more than 40,000 deep                                | Throws a `RangeError`                                            | Writes the text where the types name the objects, as deep as the stack allows                                                                                                                                                           |
| `JSON.stringify()` of an object that holds a string of 358 million characters or more     | Writes the text, or throws a `RangeError` for some objects       | Writes the text for more of them, as Node.js does: for an object that also holds a `Date`, and for an instance of a class declared in a function, where `bun run` throws once the function has run a few times                          |
| `process.default`, `Bun.default`                                                          | `undefined`                                                      | The object itself                                                                                                                                                                                                                       |
| The own keys of `Object`, copied and then listed, behind a `for (key in Bun)` loop        | `setPrototypeOf`, `defineProperties` stand ahead of the others   | In the place that they have without the loop, unless the program names `Bun.$`. The loop asks for `$`, `bun run` makes `Bun.$` for that, and the code that makes it reads the two. A member of `Bun` that nothing names isn't made      |
| [Sloppy-mode code](#sloppy-mode-code)                                                     | `f.caller`, `f.arguments`                                        | Both are `null`                                                                                                                                                                                                                         |
| [Sloppy-mode code](#sloppy-mode-code)                                                     | `getFunction()` of a call site answers the function              | `undefined`, as for a strict function, and as in an executable built without `--aot`                                                                                                                                                    |
| [A `.cjs` file loaded with `import`](#sloppy-mode-code)                                   | Strict, if nothing in its text shows that it's CommonJS          | Sloppy, as in Node.js                                                                                                                                                                                                                   |
| A `.mjs` file that calls `require()` and has no `import` or `export`, behind an `import`  | Runs ahead of the files that are imported before it              | Runs in the order of the imports, as in an executable built without `--aot`                                                                                                                                                             |
| `require()` of modules that an `import()` is loading, while the loader fetches others     | The others run a few promise jobs behind the `require()`         | In some turns they run inside the `require()`, as under `bun run` once nothing else is left to fetch. Each module runs once, behind what it imports. The language leaves open when a load ends                                          |
| `require()` of such modules where one of them awaits, so that `require()` throws          | The first of them begins, then `require()` throws                | In some turns `require()` throws first and they begin behind the error. Each module runs once, behind what it imports                                                                                                                   |
| `import()` of a module that another `import()` is still loading                           | Settles in a turn that changes from run to run                   | Settles in the same turn in every run. Each module runs once, behind what it imports. The language leaves open when a load ends                                                                                                         |
| `require.resolve("./x")` of a file that isn't in the executable                           | Looks in the folder of the file that asks                        | Looks in that folder below the working directory if it's there, and else in the working directory, where an executable built without `--aot` looks                                                                                      |
| `__esModule` of a CommonJS file, stored by a file that is imported behind it              | An `import` goes by what it was when the file had run            | An `import` goes by what it is when every CommonJS file that the module imports has run, as in `bun build --compile` and Node.js                                                                                                        |
| `import { x }`, where a CommonJS file has a getter for `x` that isn't enumerable          | The import fails: the file has no export named `x`               | Finds `x` if the getter only returns a member of another file, as in `return other.x`, and the text of the file shows all of its exports, as in an executable built without `--aot`. Otherwise the import fails                         |
| A CommonJS file that is imported back while a `require()` of an ES module runs it         | Some importers keep its exports of that moment, later ones don't | All importers read the same exports: those of that moment while the file runs, and what it has exported at its end from then on. `import()` gives the namespace object that `import * as` gave                                          |
| `process.nextTick()` in a file that an `import` loads, with more files still to run       | When the tick runs hangs on when the other files were read       | Each file runs once, in the order of the imports, and ticks keep their order. A tick may run behind a file that it runs ahead of under `bun run`, or ahead of the importing module where it runs behind it under `bun run`              |
| `import` of a package whose package.json has both `"module"` and `"main"`                 | Loads the file that `"main"` names                               | Loads the file that `"module"` names, as in an executable built without `--aot`. If the program also loads the package with `require()`, both load the file that `"main"` names                                                         |
| `undefined[key] = value`, and the same on `null`                                          | Converts the key, which can run your code, then throws           | Throws first, as the specification says. The key's `toString()` never runs                                                                                                                                                              |
| `arguments` in a string that `eval` runs in a generator or an async function              | A `ReferenceError`, or those of a function further out           | Those of the generator or async function. The same under `bun run` if its own code names `arguments`, or if an async function has no `await`                                                                                            |
| `eval(text)` in a computed key or the `extends` clause of a class, in sloppy-mode code    | Runs the text as sloppy-mode code                                | Runs it as strict code, like the rest of the class, as Node.js and an executable built without `--aot` do                                                                                                                               |
| `function (a, b = eval("var x = 1")) {}` in sloppy-mode code where `x` exists further out | The text stores to the outer `x`                                 | The text makes an `x` for this call, as in Node.js. The body reads it, `delete x` removes it, and the outer `x` keeps its value                                                                                                         |
| `function (a, ...b) { eval("var a = 4"); }`, or a pattern for `...b`, in sloppy-mode code | The body keeps reading the parameter `a`                         | The body reads the `a` of the string from then on, as in Node.js: `4` here, `undefined` after `eval("var a")`                                                                                                                           |
| `function* (a, f = () => a) { var a = 2; }`, and the same in an async function            | `f` reads and stores to the `a` of the body                      | `f` reads and stores to the parameter, which is another variable than the `var`, as in Node.js and as in a function that is neither                                                                                                     |
| `x = f() + ""` in a `try`, where `f()` returns a symbol, so that the `+` throws           | `x` holds the symbol in the `catch` and after it                 | `x` keeps the value that it had, as in Node.js                                                                                                                                                                                          |
| `await import("bun:main")` at the top level of the entry point, or of a file it imports   | Stays pending, and the process waits                             | Loads a module without exports, and the file continues                                                                                                                                                                                  |
| `import("./a.js", options)` with `{ with: { type: "text" } }` held in a variable          | The text of the file                                             | The module, as in an executable built without `--aot`. With the options written in the call, the text                                                                                                                                   |
| `import("node:path", { with: { type: "json" } })`: a built-in module with a type          | Another namespace object, with the same members                  | The namespace object that `import("node:path")` gives. A built-in module is one module, whatever the attributes say                                                                                                                     |
| `require()` by `const { platform } = process` where `process.platform` was assigned       | Loads the file of the assigned name                              | Throws `MODULE_NOT_FOUND`. The executable contains the file for the platform that it's built for. Pass the other file as another entry point                                                                                            |
| `this` at the top level of a file with an `import`, an `export` or a top-level `await`    | `null`                                                           | `undefined`, as the specification says                                                                                                                                                                                                  |
| `this` at the top of a CommonJS file, in `eval("this")` or behind `// @bun @bun-cjs`      | The `module` object                                              | `module.exports`, which is what Node.js passes to a CommonJS file, and what a `this` that is written in the file is under `bun run` too                                                                                                 |
| An unused read of a browser global such as `closed`, `name` or `document`                 | Removed. Nothing is thrown, though the global doesn't exist      | Throws a `ReferenceError`, as the specification says                                                                                                                                                                                    |
| An object literal that nothing uses, with a symbol as a computed key                      | Throws a `TypeError`                                             | Nothing is thrown, as the specification says                                                                                                                                                                                            |
| `Object.keys({ get a() {}, b: 1, get a() {} })`: a getter that a literal has twice        | `b`, then `a`                                                    | `a`, then `b`: a key stays where it first stood, as the specification says and as in Node.js                                                                                                                                            |
| `concat()` of a list with a hole, into a filled list that `Symbol.species` answers        | The index is empty afterwards                                    | What the list held there stays, as the specification says and as in Node.js                                                                                                                                                             |
| `delete "text".length`, `delete "text"[1]`: the length or a character of a string literal | `true`                                                           | `false`, and a `TypeError` in strict code, as the specification says                                                                                                                                                                    |
| The name of a class with a class decorator, read before the decorators have run           | `undefined`                                                      | Throws a `ReferenceError`, as the specification says                                                                                                                                                                                    |
| The computed key of a member with a decorator, with `experimentalDecorators`              | Runs the key expression twice                                    | Runs it once, as TypeScript's compiler emits it                                                                                                                                                                                         |
| `design:paramtypes` of a decorated class that has fields and no constructor               | An empty list, with `useDefineForClassFields: false`             | Not set, as in what TypeScript's compiler emits                                                                                                                                                                                         |
| An imported interface that only the type of a decorated member names                      | Throws a `SyntaxError`, with `emitDecoratorMetadata`             | Runs. `design:type` is `Object`, as in what TypeScript's compiler emits                                                                                                                                                                 |
| `export { T } from "./file"` where `T` is only a type                                     | Throws a `SyntaxError`: the export is not found                  | Runs the file, as an executable built without `--aot` does                                                                                                                                                                              |
| `namespace A.B {}` or `namespace A { /*! note */ }` with nothing but types inside         | `A` is an object, and an export of the file if it is exported    | There is no `A`, as in what TypeScript's compiler emits. Reading `A` as a value is a type error                                                                                                                                         |
| The values of `accessor` fields in a class with no class above                            | Some run behind the defaults of the constructor's parameters     | All run ahead of them, in the order of the members, as the values of fields do                                                                                                                                                          |
| `accessor a = 1;` in a subclass whose constructor has `super()` inside a statement        | Throws a `ReferenceError`                                        | Works, with or without a field beside it, as what TypeScript's compiler emits does                                                                                                                                                      |
| The name of `f` behind `const f = (0, () => {})` or `const f = null ?? (() => {})`        | `"f"`                                                            | Empty, as in Node.js                                                                                                                                                                                                                    |
| The name of `f` behind `const f = { held: () => {} }.held` or `const f = [() => {}][0]`   | `"f"`                                                            | `"held"`, from the key in the literal, and empty for the function out of the list, as in Node.js                                                                                                                                        |
| `f.bind().name`, where `f` has its name from a computed key, as in `{ [key]() {} }`       | `"bound "`, without the key                                      | `"bound "` and the key, as in Node.js                                                                                                                                                                                                   |
| An async `f` with such a name in a stack trace, behind its `await`. `spyOn(o, key).name`  | `<anonymous>`. An empty name                                     | The key. Node.js lists the function under it too                                                                                                                                                                                        |
| `for (const { a = a } of list)`, from the second turn on                                  | Reads what `a` held on the turn before                           | Throws a `ReferenceError`, as on the first turn and as Node.js does                                                                                                                                                                     |
| The message for `[a] = [1]; let a;`: a pattern stores to a variable ahead of its `let`    | Quotes the pattern, `'[a]'`, or nothing, `''`                    | Names the variable, as Node.js does: `Cannot access 'a' before initialization.`                                                                                                                                                         |
| `{ [key]: f() }`, where `key` is an object                                                | Calls `f` ahead of `key.toString()`                              | Calls `key.toString()` first, as Node.js does                                                                                                                                                                                           |
| The keys of `{ set a(v) {}, b: 1, set a(v) {} }`                                          | `b`, `a`                                                         | `a`, `b`, as in Node.js: a key keeps its first place                                                                                                                                                                                    |
| `0 / 0` in the scope of a variable of yours named `NaN`                                   | What the variable holds                                          | `NaN`, as in Node.js and in an executable built without `--aot`                                                                                                                                                                         |
| A function that a later clause of a `switch` declares, read in an earlier clause          | Throws a `ReferenceError`                                        | Is there, as in Node.js: the clauses are one block                                                                                                                                                                                      |
| `label: function f() {}` in sloppy-mode code                                              | Nothing around the label sees `f`                                | The function around it sees `f`, as in Node.js                                                                                                                                                                                          |
| `super.x` in the constructor of a class that extends a `Proxy`                            | Throws a `TypeError`                                             | Works, as in a method and as in Node.js                                                                                                                                                                                                 |
| `[f, g][1]()`: a function called straight out of an array literal, by a constant index    | `this` is `undefined`, or the global object in sloppy-mode code  | `this` is the array, as in Node.js and in an executable built without `--aot`                                                                                                                                                           |
| `` (0, object.method)`x` ``: the tag of a template in parentheses, behind a comma         | `this` is `object`                                               | `this` is `undefined`, or the global object in sloppy-mode code, as in Node.js and as in the call `(0, object.method)()`                                                                                                                |
| `arguments.length` behind `Object.freeze(arguments)`, where a closure reads a parameter   | The number of parameters                                         | The number of arguments, as in Node.js                                                                                                                                                                                                  |
| `Object.isExtensible(ns)`, where `ns` is what `require()` returns for an ES module        | `true`, and `Object.isSealed(ns)` is `false`                     | `false`, and `Object.isSealed(ns)` is `true`, as in Node.js and as for `import * as ns`                                                                                                                                                 |
| An unused read of a `let`, `const` or local class before its declaration has run          | Removed. Nothing is thrown                                       | Throws a `ReferenceError`                                                                                                                                                                                                               |
| `export const B = class extends Proxy {}`, where no file imports `B`                      | Throws a `TypeError` where the class is made                     | The class is removed with the export that nothing uses. Nothing is thrown, as in an executable built without `--aot`                                                                                                                    |
| `const a = /* @__PURE__ */ f()`, where nothing reads `a`                                  | Calls `f`                                                        | The call is removed with the variable, as in an executable built without `--aot`                                                                                                                                                        |
| Joining strings into one that is too long, where nothing uses the result                  | Throws a `RangeError`, until the JIT has optimized the function  | Throws a `RangeError` every time                                                                                                                                                                                                        |
| A regular expression whose groups begin a hundred million turns before it finds its match | Gives the match up and answers that there is none                | Finds the match where it needs fewer turns: it begins none of which it can tell beforehand that it fails. Otherwise it gives the match up too                                                                                           |
| The sign and payload of a `NaN` that is read from a float view or a `DataView`            | Dropped at the read, until the JIT has optimized the function    | Dropped where the number leaves arithmetic and float views, as in a function that the JIT has optimized. The sign that `-x` gives it can be dropped there too, and `to[i] = from[i]` can copy the bytes                                 |
| `"a" + symbol + f()`: a symbol in a sum of three or more operands with a string           | Calls `f()`, then throws the `TypeError`                         | Throws the `TypeError` first. `f()` never runs                                                                                                                                                                                          |
| `{ [key]: value }`, where converting `key` to a string runs your code                     | Evaluates `value`, then converts `key`                           | Converts `key`, then evaluates `value`                                                                                                                                                                                                  |
| An object literal or a class that defines the same getter or setter twice                 | Its key is listed where it was defined last                      | Its key is listed where it was defined first                                                                                                                                                                                            |
| `static { a: { break a; } }`: a `break` out of a labeled block in a `static` block        | A `SyntaxError` when the file loads                              | Leaves the block                                                                                                                                                                                                                        |
| `(5).#a`: a private name read off a whole number in parentheses                           | A `SyntaxError` when the file loads                              | The file loads. The read throws a `TypeError` when it runs                                                                                                                                                                              |
| `Level.Low ** n`, where the enum member is negative and `n` isn't a constant              | A `SyntaxError` when the file loads                              | Computes the power                                                                                                                                                                                                                      |
| In sloppy-mode code: `function f(a, a) {}`, `arguments = 1`, `eval++` on a local `eval`   | An error when the file loads                                     | Runs, as in Node.js                                                                                                                                                                                                                     |
| `"use\x20strict"` or `("use strict")` at the top of a function that has a `var public`    | An error when the file loads                                     | Runs, as in Node.js. Only the exact text is the directive                                                                                                                                                                               |
| `a: b: while (x) { continue a; }`: `continue` to the outer of two labels on one loop      | An error when the file loads                                     | Continues the loop, as in Node.js                                                                                                                                                                                                       |
| `enum E { static }` below `"use strict"`: a member named by a reserved word               | An error when the file loads                                     | Runs, as in what TypeScript's compiler emits                                                                                                                                                                                            |
| `if (x) const enum A {}`: an `enum` as the one statement of an `if` or a loop             | An error when the file loads                                     | Runs, as in what TypeScript's compiler emits                                                                                                                                                                                            |
| `declare class C { static prototype: number }`                                            | An error when the file loads                                     | Runs. The declaration is erased                                                                                                                                                                                                         |
| `accessor a = 1;` in a class, with `experimentalDecorators`                               | An error when the file loads                                     | Runs, as in what TypeScript's compiler emits                                                                                                                                                                                            |
| `@d(x => x.#a) m() {}`: a private name in a decorator, with `experimentalDecorators`      | A `SyntaxError` when the file loads                              | Runs, as in what TypeScript's compiler emits                                                                                                                                                                                            |
| A class with a decorator in a file with `this` at the top or `export =`                   | A `SyntaxError` when the file loads                              | Runs                                                                                                                                                                                                                                    |
| `import require = require("./file")` in an ES module                                      | Throws a `ReferenceError`                                        | Imports the file under that name                                                                                                                                                                                                        |
| `import { x } from "./m"; const x = 1;` below `// @ts-ignore`, where that `x` is a value  | Runs. `x` is the constant                                        | The build reports `TS2440`, which no comment turns off: both are there when the program runs, so the name is declared twice in one scope                                                                                                |
| `import { a as x }` and `import { b as x }` in a file with `// @ts-nocheck`               | Runs. `x` is the later import                                    | The build reports `TS2300`, which no comment turns off. In a JavaScript file `bun run` reports an error too                                                                                                                             |
| `for (using of list)`, `for await (async of list)`: loops that store to such a variable   | An error when the file loads                                     | Runs, as in Node.js                                                                                                                                                                                                                     |
| ``(a?.b)`x` ``, `new (a?.b)()`: an optional chain in parentheses as a tag or behind `new` | A `SyntaxError` when the file loads                              | Runs, as in Node.js                                                                                                                                                                                                                     |
| `x <!--y` in a TypeScript file                                                            | An error when the file loads                                     | Compares `x` with `!--y`, as in what TypeScript's compiler emits                                                                                                                                                                        |
| `function f(a, this) {}` in a file that is not type-checked                               | Runs without the parameter: `f.length` is 1                      | The build reports `TS2680`, as TypeScript does in a file that it checks. Node.js reports a syntax error                                                                                                                                 |
| `` `\8` ``, `((a)) => 1`, `[(a = 1)] = []`, `(function await() {})` in a module           | Runs                                                             | The build stops with an error that names the place. Node.js throws a `SyntaxError` when it loads the file                                                                                                                               |
| A JSON, TOML or YAML file, or a `data:` URL imported as one, that doesn't parse           | The `import` or `require()` that loads it throws                 | The build stops with an error that names the place, as in an executable built without `--aot`                                                                                                                                           |
| An `enum` member set to the `length` of a string literal: `Length = "text".length`        | Every `Size.Length` is `4`, whatever the program stores there    | `Size.Length` reads the object, as with `bun build --compile`. It returns what the program stores there, and throws before the `enum` has run                                                                                           |
| An `enum` member set from a path with brackets: `Sum = Outer["Inner"].A + 30`             | Every `Totals.Sum` is `31`, whatever the program stores there    | `Totals.Sum` reads the object, as in what TypeScript's compiler emits. It returns what the program stores there                                                                                                                         |
| `(Level.Low as any)()`, `` (Level.Low as any)`x` ``: an `enum` member as callee or as tag | Calls the value of the member, so it throws a `TypeError`        | Reads the member from the object, as in what TypeScript's compiler emits. It calls a function that the program has stored there, and throws a `TypeError` otherwise                                                                     |
| A `const enum` in a namespace of another file, or behind `import Alias = Outer.Fixed`     | Reads the object, so it returns what the program stores there    | Every `Outer.Fixed.A` is its value, whatever the program stores there, as when TypeScript's compiler compiles the files together                                                                                                        |
| [A function declared in a block](#sloppy-mode-code) of sloppy-mode code                   | Only the block sees it                                           | The code around the block sees it too, once the block has run                                                                                                                                                                           |
| `/(?:\b){5000000}/`: a group that takes no characters, repeated millions of times         | `null`                                                           | Matches, as the specification says                                                                                                                                                                                                      |
| `await f(text)` in an async function whose parameter `f` is `RegExp.prototype.test`       | Prints another output                                            | Throws a `TypeError` before it converts `text` to a string, as `f.call(undefined, text)` does. `Array.fromAsync(list, RegExp.prototype.test)` does the same                                                                             |
| `f.apply(receiver, list)`, where `f` inherits `apply` and isn't a function                | Reads `list` first, and throws if it isn't a list                | Throws the `TypeError` about `f` first, as the specification says. `list` is never read, so its getters don't run                                                                                                                       |
| `new Readable()`, `stream.toArray()`: internal properties of a stream, such as `_events`  | Each is read and stored as often as in Node.js                   | While a stream is made or awaited, some are read once, or before the others are stored. Only a getter, a setter or a `Proxy` above the stream can tell                                                                                  |
| `req`, `res`, `req.socket` of a `node:http` server: internal properties such as `_events` | Each is read and stored wherever the server uses it              | During an exchange, some are read less often, or never. Only a getter or a setter that the program defines for one of them can tell                                                                                                     |
| A function that the program stores to `process.nextTick`, and built-in modules            | Sees the calls that the modules make under `bun run`             | Sees the calls that the compiled modules make. How many there are, their order and which internal functions they hand over may differ: a `node:http` server asks for fewer ticks for a request                                          |
| `for...in` over `{ 0: x, 1: y }`, with a typed array as its prototype                     | Lists an index that both have twice                              | Lists it once                                                                                                                                                                                                                           |
| `o[9] = x`, where the prototype of `o` is a typed array without the index `9`             | Adds the element to `o`. Throws a `TypeError` if `o` is sealed   | The same, but nothing happens if `o` is sealed and has elements, or once the `prototype` of a function that was never constructed has a `Proxy` above it, an accessor or a read-only element under an index, or is frozen with elements |
| `TZ` in a `.env` file, or on Windows in the environment                                   | Dates use that time zone from the start                          | The same. An executable built without `--aot` starts in the time zone of the system                                                                                                                                                     |
| [Five flags on `process`](#flags-on-process)                                              | A value that `process` inherits counts                           | Only own properties of `process` count                                                                                                                                                                                                  |
| [A `FinalizationRegistry` that only a local variable holds](#memory)                      | Lives while the function waits at an `await`                     | Can be collected after the variable's last use. Its callback then never runs. What the parameters and `this` of an async function or a generator hold lives until it ends, as under `bun run`                                           |
| Objects per thread                                                                        | Limited by memory                                                | 32 GB, or no limit with `--aot-huge-heap`                                                                                                                                                                                               |

## What isn't available

| Feature                                                           | What you see                                                                                             | Instead                                                                                                         |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `--target` for another platform                                   | `error: bun compile can't cross-compile. It only builds for the platform it runs on`                     | Run the build on the target platform, or pass `--no-aot`                                                        |
| More than 256 `JSCallback`s from `bun:ffi` open at a time         | `RangeError: bun:ffi: a compiled executable can have 256 JSCallbacks open at a time`                     | `close()` the ones you no longer use. The rest of `bun:ffi` works                                               |
| `cc()` from `bun:ffi`, on Linux 6.7 or later                      | `Error: Cannot compile C code because this process can't create executable memory (PR_SET_MDWE)`         | Compile the C file when you build, and load the library with `dlopen()`                                         |
| A native addon that generates machine code, on Linux 6.7 or later | Whatever the addon does when `mprotect` or `mmap` fails                                                  | Run that code in a child process. Child processes aren't restricted                                             |
| Breakpoints, stepping, `debugger` statements                      | A breakpoint never binds                                                                                 | `bun --inspect-brk ./app.ts`, from your source files                                                            |
| `import()`, `require()` or `import * as` of an HTML file          | An error from the build                                                                                  | `import page from "./page.html"`, as in a [full-stack executable](/compiler/executables#full-stack-executables) |
| A query string in an import path: `import "./a.ts?raw"`           | `bun compile does not support a query string in an import path`                                          | Import the path without it                                                                                      |
| `importValue()` of one of your modules in a `ShadowRealm`         | `SyntaxError: The module ... was compiled ahead of time and can't be imported inside a ShadowRealm`      | `evaluate()`, with the code as a string                                                                         |
| `super()` in the string of a direct `eval`, in a few classes      | `TypeError: super() cannot be called from direct eval in a compiled executable`                          | Call it outside of the string. Only with private methods under `useDefineForClassFields: false`                 |
| `process.stdout` and others, used only in a string you `eval`     | `Error: ... was not compiled into this executable because your code never uses it`, when the string runs | [Use it in your own code too](#code-from-strings)                                                               |
| `bun --aot` with `-e` or `-p`                                     | An error                                                                                                 | [Run under the compiler's rules](/compiler/debugging#run-under-the-compilers-rules) has the list                |
| `--inspect` in `BUN_OPTIONS`, `BUN_INSPECT`                       | An error when the executable starts                                                                      | Build with [`--compile-inspector`](#options-that-load-code)                                                     |
| `--preload` in `BUN_OPTIONS`                                      | An error when the executable starts                                                                      | [Import the file from your entry point](#options-that-load-code)                                                |
| `banner` or `footer` with code                                    | An error from the build                                                                                  | [Plugins and build options](#plugins-and-build-options)                                                         |

## Libraries that are known not to work

These packages work under `bun run` and don't work in an executable. Each does something that `bun compile` rules out on purpose. The table says what that is, with the line of the package that does it, and links to the rule. The versions are the ones that were tried.

Most of them end in the same `TypeError`. It names the object and the property, and its stack trace points to the line in the package. Where the table cuts a `TypeError` short with `...`, it ends like this one:

```txt
TypeError: Cannot assign to Function.prototype.call$0 because built-in objects are read-only in a compiled executable (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
```

| Package                                                                                                           | What it does                                                                                                                                                                                      | What you see                                                                                                                                   | Why                                                                                            | Instead                                                                                                    |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `sass` 1.85.0, 1.106.1                                                                                            | The Dart runtime inside it adds `call$0`, `call$1` and so on to every function: `Function.prototype.call$0 = function() { return this(); };`                                                      | On import: `TypeError: Cannot assign to Function.prototype.call$0 ...`                                                                         | [`Function.prototype` can't be changed](#built-in-objects-are-read-only)                       | Compile your stylesheets before you build the executable                                                   |
| `zone.js` 0.16.3, and Angular through it                                                                          | Keeps the `then` of promises under a second name, then replaces it: `proto[symbolThen] = originalThen;`                                                                                           | On import: `TypeError: Cannot assign to Promise.prototype.__zone_symbol__then ...`                                                             | [`Promise.prototype` can't be changed](#built-in-objects-are-read-only)                        | Angular without zones: `provideZonelessChangeDetection()`                                                  |
| `private` 0.1.8. Through it `recast` before 0.20.4, `regenerator-transform` before 0.14.5 and `babel-core` 6.26.3 | Copies `call` and `apply` onto the built-in functions that it uses: `defProp(fun, "call", fun.call);`                                                                                             | On import: `TypeError: Cannot assign to 'call' of a built-in function ...`                                                                     | [A built-in function can't have a `call` of its own](#built-in-objects-are-read-only)          | `recast` 0.20.4, `regenerator-transform` 0.14.5 and Babel 7 do without it                                  |
| `asciidoctor` 2.2.8, 3.0.4, and `opal-runtime` 3.0.0                                                              | Opal, the Ruby runtime inside it, puts Ruby's classes between the built-in prototypes and `Object.prototype`: `$set_proto(native_klass.prototype, (klass.$$super \|\| Opal.Object).$$prototype);` | On import: `TypeError: Cannot change the prototype of Boolean.prototype ...`                                                                   | [The prototype of a built-in object can't be changed](#built-in-objects-are-read-only)         | `asciidoctor` 4.1.1, which has no Ruby runtime                                                             |
| `ses` 2.3.0                                                                                                       | Redefines the members of `ArrayBuffer.prototype` when it loads: `defineProperties(arrayBufferPrototype, getOwnPropertyDescriptors(immutableArrayBufferLibProperties));`                           | On import: `TypeError: Cannot assign to ArrayBuffer.prototype.byteLength ...`                                                                  | [Existing members can't be redefined](#built-in-objects-are-read-only)                         | To guard against prototype pollution, freeze the objects yourself: `Object.freeze(Object.prototype)` works |
| `require-in-the-middle` 8.0.1, and `@opentelemetry/instrumentation` 0.300.0 through it                            | Replaces `require` to patch modules as they load: `this._require = Module.prototype.require = function (id) {`                                                                                    | No error. The hook never runs, so nothing is instrumented                                                                                      | [A `require()` of a constant path calls no hook](#loader-hooks)                                | Build with `--no-aot`. The hook then runs for built-in modules                                             |
| `pirates` 4.0.7. Through it `@babel/register` 7.25.9 and `sucrase/register` 3.35.0                                | Replaces the loader of `.js` files to transpile them as they load: `loaders[ext] = Module._extensions[ext] = function newLoader(mod, filename) {`                                                 | When the hook is added: `error: Cannot replace a loader in Module._extensions: ...`                                                            | [The loader of a compiled file can't be replaced](#loader-hooks)                               | Remove it. The build compiles TypeScript and JSX                                                           |
| `module-alias` 2.2.3, `tsconfig-paths` 4.2.0                                                                      | Replaces the resolver, so that `require("@lib/other.js")` finds a file: `Module._resolveFilename = function (request, parentModule, isMain, options) {`                                           | From the build: `warn: TS100150: Could not resolve: "@lib/other.js"`. At the `require()`: `error: Cannot find module '@lib/other.js' from ...` | [Constant paths are resolved at build time](#loader-hooks)                                     | `paths` in `tsconfig.json`, or `imports` in `package.json`                                                 |
| `async-listener` 0.6.10, and `continuation-local-storage` 3.2.1 through it                                        | Wraps `setTimeout` and other globals in a helper that takes the object and the name as arguments: `massWrap(global, asynchronizers, activatorFirst);`                                             | On import: `TypeError: Cannot assign to setTimeout ...`                                                                                        | [The compiler can't tell which object the assignment goes to](#built-in-objects-are-read-only) | `AsyncLocalStorage` from `node:async_hooks`, or `cls-hooked` 4.2.2                                         |
| `core-js` 2.6.12                                                                                                  | `require("core-js")` also loads extensions of its own, one of them for functions: `$export($export.P + $export.F, 'Function', { part: require('./_partial') });`                                  | On import: `TypeError: Cannot assign to Function.prototype.part ...`                                                                           | [`Function.prototype` can't be changed](#built-in-objects-are-read-only)                       | `core-js/shim`, `babel-polyfill` 6.26.0, `@babel/polyfill` 7.12.1 or `core-js` 3                           |
| `es5-shim` 4.6.7, and `airbnb-js-shims` 2.2.1 through it                                                          | Replaces `forEach` and other methods of arrays: `defineProperties(ArrayPrototype, { forEach: function forEach(callbackfn/*, thisArg*/) {`                                                         | On import: `TypeError: Cannot assign to Array.prototype.forEach ...`                                                                           | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | Remove the import. Bun implements what it fills in                                                         |
| `es6-shim` 0.35.8                                                                                                 | Replaces `toString` of regular expressions: `defineProperty(RegExp.prototype, 'toString', function toString() {`                                                                                  | On import: `TypeError: Cannot assign to RegExp.prototype.toString ...`                                                                         | [`RegExp.prototype` can't be changed](#built-in-objects-are-read-only)                         | Remove the import. Bun implements what it fills in                                                         |
| `array.prototype.map` 1.0.8, and `array.from`, `array.prototype.every`, `filter`, `foreach`, `reduce` and `some`  | `require("array.prototype.map/auto")` and `shim()` replace the method: `define(Array.prototype, { map: polyfill }, { map: function () { return Array.prototype.map !== polyfill; } });`           | At that call: `TypeError: Cannot assign to Array.prototype.map ...`                                                                            | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | Call what the package exports: `require("array.prototype.map")(list, f)`                                   |
| `set.prototype.union` 1.1.3, and `difference`, `intersection` and `issubsetof`                                    | `/auto` and `shim()` give the iterators of sets a `Symbol.iterator` of their own: `addIterator(Object.getPrototypeOf(new OrigSet().values()));`                                                   | At that call: `TypeError: Cannot assign to %SetIterator%.prototype[Symbol(Symbol.iterator)] ...`                                               | [A well-known symbol can't be added](#built-in-objects-are-read-only)                          | Call what the package exports: `require("set.prototype.union")(a, b)`                                      |
| `string.prototype.matchall` 4.1.0                                                                                 | `/auto` and `shim()` define a property of `Symbol` whose key is `Symbol.matchAll`: `defineP(Symbol, symbol, {`                                                                                    | At that call: `TypeError: Cannot assign to Symbol[Symbol(Symbol.matchAll)] ...`                                                                | [A well-known symbol can't be added](#built-in-objects-are-read-only)                          | Call what the package exports: `require("string.prototype.matchall")(text, pattern)`                       |
| `url-polyfill` 1.1.13                                                                                             | Adds a helper to the built-in class: `Object.defineProperty(proto, '_fromString', {`                                                                                                              | On import: `TypeError: Cannot assign to URLSearchParams.prototype._fromString ...`                                                             | [`URLSearchParams.prototype` is read-only](#request-response-and-five-other-classes)           | Remove the import. `URL` and `URLSearchParams` are built in                                                |
| `promise` 8.3.0                                                                                                   | `require("promise/polyfill")` adds `done` to the built-in promises: `Promise.prototype.done = function (onFulfilled, onRejected) {`                                                               | On import: `TypeError: Cannot assign to Promise.prototype.done ...`                                                                            | [`Promise.prototype` can't be changed](#built-in-objects-are-read-only)                        | Remove the import, and use `then`                                                                          |
| `harmony-reflect` 1.6.2                                                                                           | Replaces functions of `Object`: `Object.preventExtensions = function(subject) {`                                                                                                                  | On import: `TypeError: Cannot assign to Object.preventExtensions ...`                                                                          | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | Remove the import. `Reflect` and `Proxy` are built in                                                      |
| `sugar` 2.0.6, 1.5.0                                                                                              | `Sugar.extend()` copies its functions onto the built-in objects, over the ones that exist: `setProperty(target, name, method);`                                                                   | When `extend()` is called: `TypeError: Cannot assign to Object.values ...`. In 1.5.0 on import, for `Array.prototype.at`                       | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | The functions on `Sugar` itself: `Sugar.Array.sum(list)`. `Sugar.Date.extend()` works                      |
| `collections` 5.1.13                                                                                              | Replaces functions of `Object`: `Object.values = function (object) {`                                                                                                                             | On import: `TypeError: Cannot assign to Object.values ...`                                                                                     | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | None                                                                                                       |
| `prototype` 0.0.5                                                                                                 | Copies its functions onto `Object`, over the ones that exist: `destination[property] = source[property];`                                                                                         | On import: `TypeError: Cannot assign to Object.values ...`                                                                                     | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | None                                                                                                       |
| `chai` 4.5.0, 6.3.0                                                                                               | `chai.should()` and `chai/register-should` define a getter for every object: `Object.defineProperty(Object.prototype, "should", {`                                                                | At that call: `TypeError: Cannot assign to Object.prototype.should ...`                                                                        | [Nothing can be added to `Object.prototype`](#built-in-objects-are-read-only)                  | `expect` and `assert`                                                                                      |
| `should` 13.2.3                                                                                                   | Defines the same getter, and ignores the error: `var prevShould = should$1.extend(defaultProperty, defaultProto);`                                                                                | Nothing on import. At the first `.should`: `TypeError: undefined is not an object (evaluating 'undefined.be')`                                 | [Nothing can be added to `Object.prototype`](#built-in-objects-are-read-only)                  | `require("should/as-function")`                                                                            |
| `must` 0.13.4                                                                                                     | Defines a getter for every object: `Object.defineProperty(Object.prototype, "must", {`                                                                                                            | On import: `TypeError: Cannot assign to Object.prototype.must ...`                                                                             | [Nothing can be added to `Object.prototype`](#built-in-objects-are-read-only)                  | `require("must/must")`                                                                                     |
| `timezone-mock` 1.3.6                                                                                             | `register()` replaces a method of every object: `Object.prototype.toString = mockDateObjectToString;`                                                                                             | When `register()` is called: `TypeError: Cannot assign to Object.prototype.toString ...`                                                       | [`Object.prototype` can't be changed](#built-in-objects-are-read-only)                         | Set the time zone itself: `process.env.TZ = "US/Pacific"`                                                  |
| `clear-module` 4.1.2                                                                                              | Takes a module out of the cache, so that the next `require()` runs it again: `delete require.cache[filePath];`                                                                                    | No error. The next `require()` returns the same exports                                                                                        | [A module of the executable runs at most once](#the-module-cache)                              | None                                                                                                       |
| `@js-temporal/polyfill` 0.5.1                                                                                     | Its README has you replace a built-in method: `Date.prototype.toTemporalInstant = toTemporalInstant;`                                                                                             | From the build: `error: TS100014: this assignment always throws: built-in objects are read-only in a compiled executable`                      | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | Leave the line out. The rest of the polyfill works, and `Temporal` is built in                             |
| `next` 16.4.2                                                                                                     | Puts a `Date` of its own in the place of the built-in one, and catches the error: `Object.defineProperty(originalConstructor.prototype, 'constructor', {`                                         | No error. Each time it starts, it logs a line that begins with `Failed to install`, and keeps the built-in `Date`                              | [Existing members can't be replaced](#built-in-objects-are-read-only)                          | None. Only `cacheComponents` uses it                                                                       |

The rows about built-in objects apply to [`bun run --aot` and `bun test --aot`](/compiler/debugging#run-under-the-compilers-rules) too, with the same messages. The row of `async-listener` and the rows about the loader and the module cache don't: there your program runs from its source files.

The lines of `sass` and `asciidoctor` belong to a language runtime, not to the package's own code. Other packages that are compiled from Dart with `dart2js`, or from Ruby with Opal, can stop at the same place.

Two old benchmarks add a method to `Object.prototype`. `deltablue` of Octane defines `Object.prototype.inheritsFrom` at the top level of its file, so the build fails with `TS100014`. `string-tagcloud` of SunSpider assigns `Object.prototype.toJSONString` and throws the `TypeError` when it starts.

These change built-in objects too, in ways that are allowed. They load, and change the same members as under `bun run`: `core-js` 3.51.0 (all of it, `core-js/stable` and `core-js/actual`), `@babel/polyfill`, `babel-polyfill`, `react-app-polyfill`, `regenerator-runtime`, `es7-shim`, `reflect-metadata`, `symbol-observable`, `zen-observable`, `colors`, `date-utils`, `temporal-polyfill`, `mockdate`, `timekeeper`, `jest-date-mock`, and the other packages of `es-shims`. So does `sinon`, fake timers included. A `sinon.stub()` of a read-only member, such as `Array.prototype.push`, throws like an assignment.

## Type errors are TypeScript 7's

The build reports the errors that `tsc` from TypeScript 7 reports, like [`bun check`](/runtime/check), whichever version of `typescript` your project has installed. A project that passes TypeScript 5 or 6 can still have errors. [Coming from TypeScript 5](/runtime/check#coming-from-typescript-5) lists the options and defaults that changed.

TypeScript 7 also stopped reading five older patterns in JavaScript files. They still run. But when a TypeScript file imports one of your own `.js` files that uses them, with `import type` too, it no longer sees what TypeScript 6 saw, and the build stops at the TypeScript file.

| In the `.js` file                                                             | The error in the TypeScript file                                                                                                           | Write instead                                           |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
| `function f() { return arguments.length }`, called with arguments             | `TS2554: Expected 0 arguments, but got 3.`                                                                                                 | A rest parameter: `function f(...values) {}`            |
| A constructor function: `function Old(v) { this.value = v }` and `new Old(3)` | `TS7009: 'new' expression, whose target lacks a construct signature`                                                                       | A `class` with a `constructor`                          |
| The same function named as a type: `(old: Old)`, `InstanceType<typeof Old>`   | `TS2749: 'Old' refers to a value, but is being used as a type here.` and `TS2344: Type '(v: any) => void' does not satisfy the constraint` | A `class` with a `constructor`                          |
| A method added later: `Grown.prototype.extra = function () {}`                | `TS2339: Property 'extra' does not exist on type 'Grown'.`                                                                                 | A method in the body of the class                       |
| `/** @enum {number} */ const Level = { Low: 1 }`, with `Level` used as a type | `TS2749: 'Level' refers to a value, but is being used as a type here.`                                                                     | `/** @typedef {number} Level */` above the object       |
| `module.exports = Main` followed by `module.exports.helper = ...`             | `TS2339: Property 'helper' does not exist on type 'typeof Main'.`                                                                          | `Main.helper = ...` first, then `module.exports = Main` |

The build doesn't report type errors inside a `.js` file, so only the TypeScript files that use it are affected.

### Files without `import` or `export`

For TypeScript, a `.ts` file that has no `import` and no `export` is a script, and everything at its top level is global. `"moduleDetection": "force"` turns that off. `bun init` writes it, and the build assumes it if there's no `tsconfig.json`.

Bun runs every file as a module, whatever `moduleDetection` says. So a variable, a function or a class of such a file exists only in that file. Its types follow `tsc`, and its values follow what runs:

| At the top level of the file                                                                                                                          | `tsc` and `bun check`                                                                                       | The build                                                                                                |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| An `interface`, a `type`, anything with `declare`                                                                                                     | Global. It adds to a global declaration of the same name: `interface Array<T> { last(): T }`                | The same                                                                                                 |
| One of those that can't add to the global declaration: `type Node` beside the `Node` of the DOM types, `declare var self: Worker` beside their `self` | `TS2300: Duplicate identifier 'Node'.`, `TS2403: Subsequent variable declarations must have the same type.` | No error. In that file the name is the file's own                                                        |
| `var`, `let`, `const`, `function`, `class`, `enum`, a `namespace` with code in it                                                                     | Global. Another file can use it without an import                                                           | Another file gets `TS2304: Cannot find name`. Under `bun run` it throws a `ReferenceError`               |
| A `class`, an `enum` or a `namespace` with code in it, named like a global declaration of another file                                                | The two merge                                                                                               | They don't. It has the members that it has at run time, so the others are `TS2339`, `TS2345` or `TS2694` |

A name that is both, like `interface Point` beside `const Point`, counts as code. To use it in another file, `export` it and `import` it there.

An `interface Request` or `interface Node` of your own in such a file adds to the one of the DOM types or of `@types/bun`, so `tsc`, `bun check` and the build all report what is missing from your objects. An `export {}` at the end of the file, or `"moduleDetection": "force"`, makes the name the file's own.

Every script that your `tsconfig.json` includes counts, as for `tsc`, also one that the entry point never imports. Two scripts of one project that each declare `interface Options` share one `Options`.

## Built-in objects are read-only

The compiler turns `array.push(x)` into a few instructions without first checking whether somebody has replaced `push`. So nobody can. Replacing an existing member of a built-in object isn't supported. Adding a new one is fine, with the exceptions in the table.

```ts builtins.ts icon="/icons/typescript.svg"
function attempt(change: () => unknown): void {
  try {
    console.log(change());
  } catch (error) {
    console.log((error as Error).name);
  }
}

attempt(() => ((Array.prototype as any).last = 1));
attempt(() => ((Array.prototype as any).push = 1));
attempt(() => delete (String.prototype as any).slice);
attempt(() => Reflect.set(Array.prototype, "push", 1));
attempt(() => ((Object.prototype as any).extra = 1));
attempt(() => ((Date as any).tomorrow = 1));
```

```bash terminal icon="terminal"
bun compile ./builtins.ts && ./builtins
```

```txt
1
TypeError
TypeError
false
TypeError
1
```

| Object                                                                                                                                                                                                                                                                   | Throws a `TypeError`                                                                                                                                                                                                                              | Works                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Object.prototype`, `RegExp.prototype`, `Promise.prototype`, the prototypes of generators                                                                                                                                                                                | Every change                                                                                                                                                                                                                                      | The same on your own object or subclass                                                                                                                                   |
| `Function.prototype`                                                                                                                                                                                                                                                     | Every change but the ones on the right                                                                                                                                                                                                            | Replacing `toString`. Adding `demethodize` or `unThis`, as `core-js` does. Adding a property under a symbol that you made                                                 |
| The prototypes of `Array`, `String`, `Number`, `Boolean`, `BigInt`, `Symbol`, `Map`, `Set`, `WeakMap`, `WeakSet`, `Iterator`, the built-in iterators, `ArrayBuffer`, `SharedArrayBuffer`, `DataView`, `Date`, the typed arrays and `Buffer`                              | Replacing, redefining or deleting a member. Adding a numeric key, a well-known symbol, a name that the object inherits, or [a name that the language looks up by itself](#new-properties-on-built-in-prototypes). Changing the object's prototype | Adding a property under a new name: `Array.prototype.last`, `"text".red`, `BigInt.prototype.toJSON`                                                                       |
| `Math`, `JSON`, `Reflect`, `Atomics`. The constructors `Array`, `Object`, `String`, `Number`, `Boolean`, `BigInt`, `Symbol`, `Map`, `Set`, `WeakMap`, `WeakSet`, `Promise`, `Proxy`, `RegExp`, `Date`, `ArrayBuffer`, `SharedArrayBuffer`, the typed arrays and `Buffer` | Replacing, redefining or deleting a member. Adding a well-known symbol or a name that the object inherits, such as `call` or `toString`. Changing the object's prototype                                                                          | Adding a property under a new name, a number or a symbol that you made: `Math.tau`, `Object.myHelper`, `Array[1]`, `Symbol.observable`. Changing it and deleting it again |
| The built-in functions themselves, constructors too: `Math.max`, `Array.prototype.map`, `Object.prototype.hasOwnProperty`, `parseInt`, `console.log`, `Error`                                                                                                            | Giving the function a `call`, `apply`, `bind` or `Symbol.hasInstance` of its own: `Math.max.call = f`. Changing its prototype                                                                                                                     | Adding a property under any other name. All of it on your own functions, classes and bound functions                                                                      |
| `createRequire` and `Module` of `node:module`                                                                                                                                                                                                                            | Replacing, redefining or deleting it                                                                                                                                                                                                              | Calling it, and keeping it in a variable. The other members of `node:module` follow the rule of the last group                                                            |
| Everything else: `Error` and the other error constructors, `Function`, `Intl`, `Temporal`, `WebAssembly`, `console`, `process`, `fetch`                                                                                                                                  | An assignment from [code in a string](#code-from-strings) or a [module loaded at run time](#dynamic-imports) to a member that your compiled code never assigns                                                                                    | Every assignment in the code that you compile                                                                                                                             |

A few members of the third and fourth groups can be replaced, because well-known packages do: `String.prototype.at`, `trim`, `trimStart` and `trimEnd` of strings, the HTML methods of strings such as `bold` and `link`, the methods of `Set` that take another set, such as `union`, `Math.random`, `Math.sign`, `Math.sinh`, `Math.sumPrecise`, `JSON.parse`, `JSON.stringify`, `Date.now`, `Date.parse`, `Date.prototype.toString`, `toLocaleString`, `toLocaleDateString` and `toLocaleTimeString` of dates, `Number.parseFloat`, `Number.parseInt`, `Map.groupBy`, `Promise.try`, `RegExp.escape`, `Uint8Array.fromBase64`, `Uint8Array.fromHex` and `Buffer.poolSize`. They follow the rule of the last group. `toJSON` can be added to `Map.prototype` and `Set.prototype`, and `toLocaleString` to a typed array constructor. The `Reflect` functions return `false` where an assignment would throw. `Object.defineProperty()` throws like an assignment. An assignment or a `delete` throws in sloppy-mode code too, which is what most CommonJS packages are. A package always finds out that it didn't replace the member.

In the last group, an assignment counts when the compiler can tell which object it goes to: `console.log = f`, `const con = console; con[name] = f`. If your code gets hold of `console` in a way that the compiler can't follow, such as out of a `JSON.parse` reviver, an assignment through that value to a member that nothing else assigns throws the same `TypeError`, with the member's name. Assign through the global name instead. `delete` and `Object.defineProperty` follow the same rule as an assignment.

The same goes for the global object as `this`. A constructor function that starts with `if (!(this instanceof F)) return new F(...)` can be called without `new`, and behind that test the compiler takes `this` for an instance of `F`. The global object passes the test only if you set `F.prototype = Object.prototype`. Then an assignment such as `this[key] = value` in that function goes to the global object in a way that the compiler didn't follow: it throws the same `TypeError`.

`Object.freeze()`, `Object.seal()` and `Object.preventExtensions()` work on all of these objects as they do under `bun run`, so a server can freeze `Object.prototype` when it starts, to guard against prototype pollution. So does an `Object.defineProperty()` that repeats what a member already is, or that only makes it read-only or non-configurable.

The build reports each such assignment that it can see as `TS100014`. It's an error if the assignment always runs, and a warning if it only might. The built-in objects of a `node:vm` context or a `ShadowRealm` that your program creates can be changed: each has its own.

A global that can't be replaced can't be shadowed either. A script that you run with `vm.runInThisContext()` or `new vm.Script()` shares its top-level `let`, `const` and `class` declarations with the whole program, so `let Math = ...` at the top of such a script throws a `SyntaxError`. Use another name, move the declaration into a function or a block, or run the script with `vm.runInNewContext()`.

### Bun and process

An executable always runs on Bun, on the platform that you built it for. A test that asks which runtime this is has one answer, so the compiler answers it at build time. The code for the other runtimes isn't compiled, and what only that code imports isn't in the executable and doesn't have to be installed.

```ts database.ts icon="/icons/typescript.svg"
const isBun = typeof process !== "undefined" && "bun" in process.versions;

export function open(path: string) {
  if (isBun) return new (require("bun:sqlite").Database)(path);
  return new (require("better-sqlite3"))(path);
}
```

| Answered at build time                                                                                  | The answer                                       |
| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `typeof Bun`, `typeof process`, `globalThis.Bun`, `globalThis.process`, `"Bun" in globalThis`           | Two objects                                      |
| `process.isBun`                                                                                         | `true`                                           |
| `process.versions.bun`, `process.versions.node`, `"bun" in process.versions`, `typeof process.versions` | The versions of the Bun that built the program   |
| `process.release.name`                                                                                  | `"node"`                                         |
| `process.platform`, `process.arch`                                                                      | Those of the `--target`                          |
| `typeof require`, `typeof module`                                                                       | Depends on whether the file is a CommonJS module |

It makes no difference whether there is a `?.` on the way, as in `process?.versions?.bun`. A `const`, or a `let` or `var` that is assigned once, keeps the answer for the functions that read it.

Only the globals count, and the `process` of `import process from "node:process"`, which is the same object. A parameter, a variable or an import that is called `Bun` or `process` is read when the program runs, like any other.

So that the answers stay true, an executable can't replace what gives them. Assigning to, redefining or deleting the global `process`, or `process.isBun`, `process.versions` and `process.release`, throws the `TypeError` above. `process.versions.bun`, `process.versions.node` and `process.release.name` are read-only, so a program that overwrites `process.versions.node` to pass for another version of Node.js can't do that in an executable. The rest of the two objects works as it does under `bun run`: a loader can still add `process.versions.pnp`. The global `Bun` is read-only under `bun run` too. Every other property of `process` and `Bun` follows the rule of the last group.

### New properties on built-in prototypes

You can add a new property to the objects of the third group and to `Error.prototype`. It doesn't matter who adds it: your compiled code, [code in a string](#code-from-strings) or an `--external` package. Compiled code finds the property as it does under `bun run`. `list.last` finds an `Array.prototype.last`, and `this.node = node` in a class that extends `Error` runs a setter called `node` on `Error.prototype`.

The exceptions are the names that the language looks up by itself. A prototype of the third group that doesn't have such a name never gets it:

| Name                      | Looked up by                                                                                      | Can't be added to                                                                                                        |
| ------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `then`                    | `await`, and a promise that is resolved with the object                                           | The prototypes whose instances are objects: the third group without `String`, `Number`, `Boolean`, `BigInt` and `Symbol` |
| `toJSON`                  | `JSON.stringify`                                                                                  | The same prototypes, except `Map.prototype` and `Set.prototype`                                                          |
| `next`, `return`, `throw` | `for...of`, spread, destructuring and `yield*`, on the object that `[Symbol.iterator]()` returned | Every prototype of the third group, but `next` on `Date.prototype`                                                       |

```txt
TypeError: Cannot assign to Array.prototype.next because built-in objects are read-only in a compiled executable (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
```

Put the method on your own object or class instead. A class that extends `Array` can have a `then`, a `toJSON` or a `next`.

### Request, Response and five other classes

The prototypes of `Request`, `Response`, `Headers`, `Blob`, `URL` and `URLSearchParams` are read-only too. So is the prototype of the request objects that `Bun.serve` passes to your `routes` handlers. `File.prototype` is the same object as `Blob.prototype`.

Use a subclass. It has a prototype of its own.

```ts response.ts icon="/icons/typescript.svg"
try {
  (Response.prototype as any).text = async () => "patched";
} catch (error) {
  console.log(String(error));
}

class Upper extends Response {
  override async text(): Promise<string> {
    return (await super.text()).toUpperCase();
  }
}

console.log(await new Upper("hello").text());
```

```bash terminal icon="terminal"
bun compile ./response.ts && ./response
```

```txt
TypeError: Cannot assign to Response.prototype.text because built-in objects are read-only in a compiled executable (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
HELLO
```

A subclass can override methods and getters such as `text()` and `ok`, add fields and call `super`.

These also work as usual:

- a property on one instance: `Object.defineProperty(response, "url", { value })`
- replacing a static function: `URL.createObjectURL = stub`
- replacing the global: `globalThis.Response = Upper`
- a polyfill that checks first: `Headers.prototype.getSetCookie ??= ...` assigns nothing, so nothing throws

### Packages that change built-in objects

A few packages change built-ins, mostly to support old browsers or to offer a shorter syntax in tests. [Libraries that are known not to work](#libraries-that-are-known-not-to-work) lists the ones that stop, what you see, and what to use instead. It also lists well-known ones that load.

## Code from strings

`eval`, `new Function` and `node:vm` work, and so does [a Worker made from a string](#workers). The build can't see their code, and the executable has no JIT, so Bun interprets it.

```ts strings.ts icon="/icons/typescript.svg"
const add = new Function("a", "b", "return a + b");
const local = 5;
console.log(add(2, 3), eval("local * 2"), (0, eval)("typeof Bun"));
```

```bash terminal icon="terminal"
bun compile ./strings.ts && ./strings
```

```txt
5 10 object
```

That's fine for a template or a configuration file. It matters when a library generates its hot code that way: zod 4, ajv, fast-json-stringify and Elysia do. If the library has a mode without generated code, measure both: `z.config({ jitless: true })`, called before you create a schema, makes `parse` in zod 4 cheaper, while `new Elysia({ aot: false })` makes a request slower. For Fastify, see [below](#fastify).

If your program never calls `eval` or `new Function`, the compiler assumes it has seen all the code that will ever run (a closed-world assumption, like GraalVM Native Image) and optimizes accordingly.

It finds these calls by tracking the functions as values, so `const run = eval`, `Reflect.construct(Function, [code])`, `(() => {}).constructor(code)` and `new Proxy(Function, {})(code)` all count.

A direct `eval` in strict code doesn't count if its string is known at build time and is only the name of a variable, as in `eval("require")`, or `eval("quire".replace(/^/, "re"))` in the `@protobufjs/inquire` package. It's compiled as a read of that variable.

Only a call counts. Naming `eval` or `Function`, storing one in an object or passing one to another function of your program costs nothing by itself. Neither does a call to a function that the compiler knows nothing about: it doesn't assume that such a call might be `eval`. The exception is a program that hands `eval` or `Function` to something that the compiler can't follow, such as the `resolve` function of `Promise.withResolvers()`, a proxy or the `detail` of an event. There it counts as called.

What it doesn't track is an `eval` or a `Function` that your program reads under a computed key, as in `object[name](code)` or `globalThis[name](code)`. That holds for your own objects too. A table such as `{ "%eval%": eval, "%parse%": parse }`, like the one in the `get-intrinsic` package, costs nothing however it's read, and `table["%eval%"](code)` counts as a call while `table[name](code)` doesn't. It doesn't track the third argument of `Reflect.get(target, key, receiver)` into the getter either, so `this.constructor(code)` in a getter that runs on a function that way doesn't count. In a program with no other such call, that one throws:

```txt
EvalError: Code generation from strings disallowed for this context: the compiler couldn't tell at build time that this call reaches eval() or the Function constructor. Call eval() or new Function() directly.
```

A few other ways to load code follow the same rule. They work where the compiler finds the call, and throw where it doesn't.

- `Bun.plugin`
- `require` and `process.getBuiltinModule` used as values, in a program where every import path is a constant

Code in a string can use any API, with one exception. Some of `process`, `console` and `Bun` is written in JavaScript, and the executable only contains the APIs that your program uses. If a string is the only thing that uses one of the others, that use throws:

```ts stream.ts icon="/icons/typescript.svg"
const text = process.argv[2] ?? "process.stdout.write('hi\\n')";
try {
  new Function(text)();
} catch (error) {
  console.log(String(error));
}
```

```txt
Error: None of console._stderr, console._stdout, process.stderr or process.stdout was compiled into this executable because your code never uses them. To keep them, use one of them somewhere in your code. (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
```

The error is catchable. Using `process.stdout` anywhere in your own code fixes it.

This affects `process.stdout`, `stderr`, `stdin`, `openStdin()`, `finalization`, `allowedNodeEnvironmentFlags` and `loadEnvFile()`, `console.Console` and `console.write()`, and `Bun.$`, `Bun.sql`, `Bun.SQL` and `Bun.peek()`.

A function of a `node:` module that your program imports and never calls isn't compiled either. A string rarely finds one, but it can: on an object such as `process` that inherits from `EventEmitter`, or by loading the module itself, as in `process.getBuiltinModule("node:http").request(url)` in a program that only has `import { createServer } from "node:http"`. The call then throws `'...' was not compiled into this executable because your code never calls it`, with the function's name.

`process.mainModule` is `undefined` in a string unless [a file of your program names it](#module-objects), as it is in every executable built without `--aot`. Besides `import()` of an entry point, it's the only path from a string to your modules, so without it the compiler knows every caller of what the other files export. `require.cache` is no such path: [it lists none of them](#the-module-cache).

A string can `import()` an entry point of the build by its path. A JSON file or another data file that's compiled into the executable is different: a string finds it only if your program has a dynamic `import()` or `require()` of its own. Otherwise the import rejects with `ERR_MODULE_NOT_FOUND`, as in an executable built without `--aot`.

Everything that belongs to the language always works in a string: `Array`, `Map`, `Promise`, `JSON`, regular expressions, generators. So does everything that Bun implements natively, such as `fetch`, `Bun.file` and `Bun.serve`.

It's always the call that throws. Importing `node:vm`, `node:worker_threads`, `node:repl` or `bun:ffi` costs nothing and never fails. `inspector.open()` from `node:inspector` starts no debugger and doesn't throw, unless you build with [`--compile-inspector`](#options-that-load-code).

If your program needs none of it, [`--disallow-code-generation-from-strings`](/runtime/code-generation-from-strings) turns it off in the executable, like the Node.js flag of the same name.

```bash terminal icon="terminal"
bun compile --disallow-code-generation-from-strings ./strings.ts && ./strings
```

```txt
EvalError: Code generation from strings disallowed for this context
      at /$bunfs/root/strings.ts:1:17
```

With the flag, a call to `eval` or `new Function` can only throw, so the compiler treats your program as if the call ran nothing. It knows every caller of your functions again, which means less code to compile and more that it can optimize, and [`--trim`](/compiler/size#turn-off-code-from-strings) can remove the interpreter. A library that tries `new Function` inside a `try`, such as zod 4, falls back to plain code. A library that can't work without generated code, such as Elysia, throws the `EvalError` when it starts.

That fallback is faster here. zod 4 generates a parser for each object schema, and an executable has to interpret it. The plain code it falls back to is compiled. A `POST` route that checks an order with ten items (600 bytes of JSON) with zod, in requests per second on one core:

| Server      | Without the flag | With the flag |       |
| ----------- | ---------------: | ------------: | ----: |
| `Bun.serve` |           47,081 |        50,364 | +7.0% |
| Hono        |           42,906 |        45,375 | +5.8% |
| `node:http` |           37,501 |        38,649 | +3.1% |
| Express     |           23,639 |        25,278 | +6.9% |

`z.config({ jitless: true })` does the same for zod alone, without the flag.

**With `=strict`, code from the disk fails with the flag's error too.** `require()` and `import()` of a file that isn't in the executable throw `EvalError: Code generation from strings disallowed for this context`. `--preload`, `-r`, `--require`, `--import` and `BUN_INSPECT_PRELOAD` end the process with that error and exit code `1`. An executable built without `--aot` does the same. Without the flag, you get [the messages that say what to do](#code-that-isnt-in-the-executable).

**Pass the flag when you build.** `BUN_OPTIONS` can turn it on too, but that's too late for a string that's a constant. The compiler has compiled `(0, eval)("1 + 1")` and `new Function("return 2")` like the rest of your program, so they still run. A string that's put together at run time throws.

### Fastify

Fastify generates code from strings in three places:

- **The router.** find-my-way generates a few short functions for each route when the server starts, and a request calls two or three of them. That's 2 to 7% of a request. You can't turn it off.
- **Response schemas.** fast-json-stringify generates a serializer for each `schema.response`.
- **Request schemas.** ajv generates a validator for each `schema.body`, `schema.querystring`, `schema.params` and `schema.headers`.

The serializers and validators run on every request, and interpreting them can cost more than the rest of the request. A route that returns 3.5 KB of JSON runs six times as many instructions with a response schema as without one. You can keep the schemas and avoid both.

**Serialize with `JSON.stringify`.**

```ts server.ts icon="/icons/typescript.svg"
const app = Fastify();
app.setSerializerCompiler(() => JSON.stringify);
```

In Bun, `JSON.stringify` is faster than the generated serializer even with the JIT. The compiled server answers that route more than twice as often per second. There's one difference: fast-json-stringify leaves out properties that aren't in the schema, and `JSON.stringify` writes every property of the object you return.

**Write the validators to files before you compile.** `@fastify/ajv-compiler` can give you the source code of a validator instead of calling `new Function`. Save it to a file, and Bun compiles it with the rest of your program.

```ts server.ts icon="/icons/typescript.svg"
import { StandaloneValidator } from "@fastify/ajv-compiler";
import Fastify from "fastify";
import { writeFileSync } from "node:fs";

const writes = process.argv[2] === "write";
const buildValidator = writes
  ? StandaloneValidator({
      readMode: false,
      storeFunction: (route, code) => writeFileSync("./order-validator.js", code),
    })
  : StandaloneValidator({
      readMode: true,
      restoreFunction: route => require("./order-validator.js"),
    });

const app = Fastify({ schemaController: { compilersFactory: { buildValidator } } });

app.post("/orders", { schema: { body: orderSchema } }, placeOrder);

await app.ready();
if (writes) process.exit(0);
await app.listen({ port: 3000 });
```

```bash terminal icon="terminal"
bun add @fastify/ajv-compiler ajv
bun ./server.ts write
bun compile ./server.ts
```

`route` has the `method`, the `url` and the `httpPart` (`"body"`, `"querystring"`, ...) that the validator is for. If you have more than one schema, name each file after them. Give each `require()` a constant path, because [a module behind a computed path isn't compiled](#dynamic-imports). The validators import helpers from `ajv`, so it has to be installed.

With both changes, a route that validates an order of ten items answers about 1.6 times as many requests per second, the same as without schemas.

## WebAssembly

A `.wasm` file that your program imports is compiled at build time.

```ts
import path from "./add.wasm";

const bytes = await Bun.file(path).arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
```

The executable recognizes the module by its bytes. So it makes no difference how your program reads the file, or which function of `WebAssembly` it passes the bytes to.

Bun interprets every other module: bytes that your program downloads, decodes from a string or assembles itself. The executable has no JIT, so a tight loop runs about 20 times slower there.

On some machines, and with some memories, the executable can't use the compiled code of a module. [The run-time log says so](/compiler/debugging#webassembly-that-falls-back-to-the-interpreter).

## Dynamic imports

`bun compile` compiles every module that the entry point reaches through an `import` or `require`. When an import path isn't a constant, the compiler uses the parts of it that are.

```ts app.ts icon="/icons/typescript.svg"
const wanted = process.argv[2] ?? "csv";
const plugin = await import("./plugins/" + wanted + ".ts");
console.log(plugin.name);
```

```ts plugins/csv.ts icon="/icons/typescript.svg"
export const name: string = "csv";
```

```ts plugins/json.ts icon="/icons/typescript.svg"
export const name: string = "json";
```

Every path here starts with `./plugins/` and ends with `.ts`, so the compiler includes every such file below `plugins/`. The executable contains both plugins and runs without your source files.

```bash terminal icon="terminal"
bun compile ./app.ts --outfile elsewhere/app && cd elsewhere && ./app json
```

```txt
json
```

The `require` doesn't have to be called under its own name. The compiler follows it into a variable that's never assigned again, and into a parameter of a function that's called where it's written. That covers the wrapper that TypeScript writes for `"module": "umd"`, which an executable built without `--aot` doesn't follow.

```js lib.js icon="/icons/javascript.svg"
(function (factory) {
  if (typeof module === "object" && typeof module.exports === "object") factory(require, exports);
  else if (typeof define === "function" && define.amd) define(["require", "exports", "./part"], factory);
})(function (require, exports) {
  exports.part = require("./part");
});
```

A path that's one of a few, like `require(fast ? "./fast.ts" : "./small.ts")`, adds those files and no others. [Native addons](#native-addons) lists the forms that the compiler follows.

A path with no constant start, like `require(name)`, adds nothing to the executable. Of your own files it finds the entry points, by the path the file had at build time, as in an executable built without `--aot`. A file that's only in the executable because another file imports it isn't found, even after it has run.

```txt
error: Cannot load '/$bunfs/root/inside.ts': this executable can't load a module from a dynamic path. Pass the file to bun compile as another entry point.
```

To load a file by a dynamic path, pass it as an additional entry point. An entry point can be a glob pattern.

```bash terminal icon="terminal"
bun compile ./app.ts './extra/*.ts' --outfile app
```

A package that's loaded by a dynamic name, like the database driver of an ORM, works the same way. Pass the file that the package's `main` or `exports` points to. `require(name)` and `import(name)` then find it by the name of the package, from every file that sees the package in its `node_modules`, and the executable doesn't need `node_modules` on disk.

```bash terminal icon="terminal"
bun compile ./app.ts ./node_modules/pg/lib/index.js --outfile app
```

A package with `exports` is found by the names it exports, patterns like `"./features/*"` included. A package without `exports` is also found by the paths of its files, like `pg/lib/client`.

### Paths that start at the file's folder

The compiler knows that `__dirname`, `import.meta.dir` and `import.meta.dirname` name the folder of the file, and it follows `path.join` and `path.resolve` when their first argument is one of them. `require(path.join(__dirname, "..", "package.json"))` is compiled like `require("../package.json")`.

Two things turn such a path into a dynamic path:

- **The file changes `__dirname`, `__filename` or `import.meta`.** That includes handing `import.meta` to a function. Files that only read them aren't affected.
- **The program replaces `path.join` or `path.resolve`.** The executable compares the path that the call returned with the path that Bun's own function returns. If they differ, `require` throws and `import()` rejects. `bun run` would load whatever file the replacement named.

```txt
error: Cannot load './plugin.js': path.join or path.resolve was replaced and returned a different path. This executable can't load a module from a dynamic path.
```

### Built-in modules

The same rule applies to `node:` and `bun:` modules. The executable contains the ones that your program, a package or another built-in module imports by a constant name.

A name doesn't have to stand in the call. If your program or a package loads a module by a name that the compiler can't work out, or hands `require`, `module`, `process` or `import.meta` to code that the compiler can't follow, the executable also contains every built-in module whose name is written as a string anywhere in the program, with or without `node:`.

| Code                                                                                      | In the executable       |
| ----------------------------------------------------------------------------------------- | ----------------------- |
| `function tryRequire(name) { try { return require(name) } catch {} }; tryRequire("zlib")` | `node:zlib`             |
| `function load(mod, name) { return mod.require(name) }; load(module, "perf_hooks")`       | `node:perf_hooks`       |
| `for (const name of ["node:vm", "node:v8"]) process.getBuiltinModule(name)`               | `node:vm` and `node:v8` |
| `function ask(p) { return p.getBuiltinModule("node:dgram") }; ask(process)`               | `node:dgram`            |

A string doesn't count if it's only compared (`name === "fs"`, `case "fs":`), used as a property key (`table["fs"]`), or kept in an array or a `Set` that the program only searches (`names.includes(name)`, `names.has(name)`). No string counts in a program where every import has a constant name. A dynamic path that can't be the name of a built-in module, like `require("./locales/" + name)`, doesn't make strings count either.

If a function passes its parameter to `require()`, `import()` or `process.getBuiltinModule()` and the program only ever calls that function directly, as with `tryRequire` above, the compiler takes the names from the calls. Such a function doesn't make other strings count.
A `require(name)` doesn't add the rest, so one such call in a package doesn't put `node:repl` and `node:dgram` into your server. A name that comes from outside of the program, or that's put together from parts, only finds a module that's in the executable for another reason.

```ts load.ts icon="/icons/typescript.svg"
import "node:path";

const name = process.argv[2] ?? "node:path";
console.log(Object.keys(require(name)).length > 0);
```

```bash terminal icon="terminal"
bun compile ./load.ts && ./load node:path && ./load node:dgram
```

```txt
true
error: Cannot load 'node:dgram': it isn't in this executable. Import it by name somewhere in your program to keep it. (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
```

A built-in module that a dynamic name finds works like one that you import, even if it's only in the executable because another built-in module imports it.

To keep a built-in module whose name isn't written anywhere in your program, import it once by its name. The import costs nothing until the module is used.

```ts load.ts icon="/icons/typescript.svg"
import "node:dgram";
```

The same goes for `node:cluster`. `server.listen()` in a cluster worker asks the primary for its socket, and that code is in the executable only if your program imports `node:cluster`, itself or through a package. A program that forks itself does. If another program starts your executable as a worker, with `cluster.setupPrimary({ exec })`, and your program never imports `node:cluster`, `listen()` fails with `Cannot load 'node:cluster': it isn't in this executable` instead of listening on its own. `import "node:cluster"` fixes it.

Some properties of `process`, `Bun` and `console` are written in JavaScript too: `process.stdout` needs `node:tty`, and `Bun.sql` needs `bun:sql`. The executable contains the ones your program can reach. Reading `process[name]` with a dynamic name, or storing `process` where the compiler can't follow it, keeps all of them.

The compiler follows a built-in module through variables, parameters, return values and properties of objects that your own code creates, so `fs[name]()` with a dynamic name keeps every function of `node:fs` wherever `fs` got there that way. It can't follow a module through an object that came from outside your code: after `const box = event.detail; box.url = require("node:url")`, `box.url[name]()` throws `'...' was not compiled into this executable because your code never calls it`, unless your program also calls that function by name or reads `url[name]` somewhere the compiler can follow. The same goes for a method of a class of a built-in module: after `box.handle = await open(path)`, `box.handle.readLines()` works, and `box.handle[name]()` throws that error unless your program also calls `readLines()` by name or reads `handle[name]` on a handle the compiler can follow.

The code behind the IPC channel is kept for `process.on("message", ...)` only when the compiler can see that the call is on `process`: called on `process` itself, or on a variable or parameter that holds it. If `process` got there through a value the compiler can't follow, like `error.cause`, a message that carries a `Buffer` or a handle throws an error that names what's missing. Using `process.send` anywhere in your code keeps it.

### Code that isn't in the executable

A dynamic `import()` or `require()` can only load code that's in the executable. Anything else throws: JavaScript, TypeScript, WebAssembly or a native addon. So does everything that Bun would read as JavaScript or turn into it: a file with no extension, a file with an extension that Bun doesn't know, a Markdown file, and a `data:` or `blob:` URL.

```txt
error: Cannot load '/home/me/plugins/late.ts': it isn't in this executable, which only runs code that was compiled into it. Pass the file to bun compile as another entry point, or build with --aot-allow-code-from-disk.
```

The error has the code `MODULE_NOT_FOUND` from `require()` and `ERR_MODULE_NOT_FOUND` from `import()`, like a file that doesn't exist. The build warns at each import where this can happen.

```txt
warn: TS100064: this import path isn't a constant. If it resolves to code that isn't in the executable, the import throws at run time (data files still load). Add the file as an entry point, or build with --aot-allow-code-from-disk
```

| You want                     | Do this                                                                                                                           |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| The file compiled            | Pass it as an additional entry point                                                                                              |
| One package loaded from disk | `--external <package>`. Bun interprets it, and everything it loads                                                                |
| Any file loaded from disk    | [`--aot-allow-code-from-disk`](#load-code-from-disk)                                                                              |
| Data                         | Nothing. JSON, JSON5, TOML, YAML, XML, text and CSS files run no code, so they load from disk as usual                            |
| A database or an HTML page   | Import it by a constant path. `import(name, { with: { type: "sqlite" } })` and `require(name)` of an `.html` file throw like code |

An `--external` package has `require`, `module`, `require.cache` and every built-in module, whole: the compiler can't see what the package calls, so a build with `--external` compiles every function of the built-in modules in the executable, not only the ones that your program calls. That makes the executable a few megabytes larger. The package doesn't see the modules of your program, as in an executable built without `--aot`: its `module.parent` has the path of the file that required it and empty `exports`, and `require.cache` lists the files that were loaded from disk. If a file of your program [names a way between module objects](#module-objects), the package finds your modules through `process.mainModule` too. [Built-in objects are read-only](#built-in-objects-are-read-only) for it too: it can add a global or a property, and it can't replace one that your compiled code uses. `globalThis.fetch = polyfill` in such a package throws a `TypeError` if your program calls `fetch`.

`createRequire()`, `import.meta.require()` and `module.require()` follow the same rules. The compiler follows `createRequire` from `node:module` to the files that its `require` names, so a program [can't replace](#built-in-objects-are-read-only) `createRequire`.

`new Bun.Transpiler()` works and costs nothing: it turns text into text. A [macro](/bundler/macros) in that text is code that the build never saw, so it follows the rule for [code from strings](#code-from-strings). Making a transpiler doesn't count as a call to `eval`, and in a program with no such call, `transform()`, `transformSync()` and `scan()` throw for a text that calls a macro:

```txt
error: Macros cannot be used in this executable, which only runs code that was compiled into it
```

### Load code from disk

A plugin, a config file, a tool that your user wrote: the build never sees them, because their paths are only known when the program runs. With `--aot-allow-code-from-disk`, a dynamic `import()` or `require()` loads such a file from disk, as in an executable built without `--aot`. It isn't compiled. Bun transpiles it and interprets it, without a JIT, like [code from a string](#code-from-strings).

```ts host.ts icon="/icons/typescript.svg"
const plugin = await import(Bun.pathToFileURL(process.argv[2]).href);
console.log(plugin.default(20));
```

```ts plugin.ts icon="/icons/typescript.svg"
import { cpus } from "node:os";

export default (n: number) => n * 2 + Math.sign(cpus().length);
```

```bash terminal icon="terminal"
bun compile --aot-allow-code-from-disk ./host.ts && ./host ./plugin.ts
```

```txt
41
```

- **Every kind of file loads** that loads in an executable built without `--aot`: JavaScript, TypeScript, JSX, JSON and the other data files, WebAssembly, a native addon, a `data:` or `blob:` URL. The path can be absolute, relative to the working directory, or a `file:` URL.
- **The file imports what it likes**: files beside it, packages in a `node_modules` on disk, and every `node:` and `bun:` module. It gets the same `node:fs` and the same `EventEmitter` as your program. A built-in module that isn't in the executable is interpreted too. A `package.json` on disk is only read if you build with `--compile-autoload-package-json`, as in [any executable](/compiler/executables).
- **The two sides are one program.** The file calls your compiled functions and they call its functions. Objects, classes, promises and exceptions cross in both directions. A type of yours says nothing about what such a file returns or stores, and nothing throws because of that.
- **[`Bun.plugin`](/runtime/plugins) applies to it.** `onResolve`, `onLoad` and `module()` run for what the file imports, so a host can hand its own modules to a plugin: `build.module("my-sdk", () => ({ exports: sdk, loader: "object" }))`.
- **It doesn't get the files of your program by path.** An entry point is found, and it's the compiled module, which runs once. Any other file of the executable isn't, as described [above](#dynamic-imports).
- **[Built-in objects are read-only](#built-in-objects-are-read-only)** for it too.

The option has a price, and only a program that can reach such an import pays it:

- **The executable is larger.** It keeps Bun's transpiler and the source code of Bun's built-in modules. It also holds every function of the built-in modules that are compiled into it, not only the ones that your program calls. That's a few megabytes, and [`--trim`](/compiler/size) can't remove the transpiler or the interpreter.
- **The program can run slower.** The compiler can't see what the file does with what it's handed, so it proves less about the objects and functions that the file can get hold of. Those include everything that the built-in modules export. A server on `node:http` can need up to a third more CPU time for a request.

A program without a dynamic `import()` or `require()` is the same executable with and without the option.

With the option, a name that comes from outside of the program finds every [built-in module](#built-in-modules). One that isn't in the executable isn't compiled: Bun interprets it, like a file from disk.

`process.getBuiltinModule(name)` is the exception. It never loads a file, so it doesn't make the executable ready for code that wasn't compiled. In a program that has no other dynamic import, it only finds a module that's in the executable, with the option too.

```txt
error: Cannot load 'node:dgram': it isn't in this executable. Import it by name somewhere in your program to keep it. (bun compile differs from bun run here: https://bun.com/docs/compiler/compatibility)
```

In `Bun.build`, the option is `compile: { allowCodeFromDisk: true }`.

The compiler also follows `require` under another name, as long as it can see that the name holds `require`. Where it can't, every path is a dynamic path, a string literal too.

| Call                                                                        | Compiled like                              |
| --------------------------------------------------------------------------- | ------------------------------------------ |
| `const load = require; load("./inside.js")`                                 | `require("./inside.js")`                   |
| `require.call(this, "./inside.js")`, `require.apply(this, ["./inside.js"])` | `require("./inside.js")`                   |
| `const load = require; load("./plugins/" + wanted + ".js")`                 | `require("./plugins/" + wanted + ".js")`   |
| `(r => r("./inside.js"))(require)`                                          | `require(name)`: it finds the entry points |
| `({ load: require }).load("./inside.js")`                                   | `require(name)`: it finds the entry points |
| `require.bind(null)("./inside.js")`                                         | `require(name)`: it finds the entry points |
| `let load = require; load = other; load("./inside.js")`                     | `require(name)`: it finds the entry points |

A function that's called where it's written and returns `require` counts as another name too. That's the `__require` helper that esbuild puts into ES module bundles.

| Call                                                                                               | Compiled like            |
| -------------------------------------------------------------------------------------------------- | ------------------------ |
| `var __require = (x => typeof require !== "undefined" ? require : x)(f); __require("./inside.js")` | `require("./inside.js")` |
| `const load = (() => require)(); load("./inside.js")`                                              | `require("./inside.js")` |

With code splitting, esbuild and `bun build` export the helper from a shared chunk. The compiler follows `require`, `import.meta.require` and `createRequire()` from an `export` to an `import` for built-in modules, whose names mean the same in every file. Any other path is still a dynamic path there.

| Call                                                               | Compiled like                              |
| ------------------------------------------------------------------ | ------------------------------------------ |
| `import { __require } from "./chunk.js"; __require("node:zlib")`   | `require("node:zlib")`                     |
| `import { __require } from "./chunk.js"; __require("./inside.js")` | `require(name)`: it finds the entry points |

`process.getBuiltinModule("node:fs")` with a string literal is compiled like `require("node:fs")`, also on a variable that holds `process` or `globalThis` and is never assigned again. If your program assigns a function of its own to `process.getBuiltinModule`, as `require-in-the-middle` does, every call runs that function. The compiler still follows `process.getBuiltinModule("node:module").createRequire(import.meta.url)` to the files that its `require` names, so the executable compares the `createRequire` that it's about to call with Bun's own. If the replacement returned an object with a different one, the call throws `Cannot call createRequire: process.getBuiltinModule was replaced and returned a different createRequire`. Under another name, or on a `process` that came through a parameter or a property, the function loads [the built-in modules that your program names](#built-in-modules). It can't see the function behind a computed key (`process[key](name)`): such a call throws `Cannot load '...': this executable can't load a module from a dynamic path`.

`require()` can't wait. If it reaches an ES module outside of the executable that has a top-level `await`, it throws a `TypeError` that tells you to use `await import()`.

### Frameworks that load your pages by path

Next.js and Angular load your application's pages and routes from paths that they build while the server runs, so the compiler doesn't find them. Pass the pages as additional entry points, and they are compiled. Pass the rest of the build output with [`--asset`](/compiler/executables#embed-directories), and the server reads it out of the executable.

```bash terminal icon="terminal"
# Next.js with `output: "standalone"`, in .next/standalone
bun compile ./server.js ".next/server/**/*.js" --asset .next --asset public --asset node_modules/@img --outfile server
```

The result is one file that needs nothing else on the machine that runs it. Bun expands the quoted pattern itself. `node_modules/@img` holds `sharp`, which resizes the images of `next/image`; without it Next.js serves them at their original size. Next.js can't write its page cache into the executable, so it keeps the cache in memory. It can't store the images it has resized either, and logs `Failed to write image to cache` for each of them.

`--compile-in-place` builds an executable that only runs on the machine that built it: modules keep their paths on disk, and whatever wasn't compiled or embedded is read from there.

```bash terminal icon="terminal"
bun compile --compile-in-place ./server.js $(find .next/server -name '*.js') --outfile server
```

For Angular the files are `$(find dist/site/server -name '*.mjs' -not -name server.mjs)`. Nuxt, SvelteKit, Astro and React Router need no additional entry points. Pass what the server reads from disk with `--asset`:

```bash terminal icon="terminal"
bun compile ./.output/server/index.mjs --asset .output/public --outfile server # Nuxt
bun compile ./dist/server/entry.mjs --asset dist/client --outfile server # Astro
bun compile ./build/index.js --asset build/client --asset build/prerendered --outfile server # SvelteKit
bun compile ./server.ts --asset build/client --outfile server # React Router, with a server of your own
```

Without the entry points, the first request fails with the error above.

## Options that load code

`--preload` and a few other options load code that the compiler never saw. What the compiler proved about your program only holds while nothing else runs, so an executable rejects them. The exception is an executable that's built with [`--aot-allow-code-from-disk`](#load-code-from-disk) and whose program has a dynamic `import()` or `require()`: it's ready for such code, so it takes `--preload`, `-r` and `--import`.

```ts greeting.ts icon="/icons/typescript.svg"
console.log("hello");
```

```ts before.ts icon="/icons/typescript.svg"
console.log("before");
```

```bash terminal icon="terminal"
bun compile ./greeting.ts && BUN_OPTIONS="--preload ./before.ts" ./greeting
```

```txt
error: --preload can't be used with this executable because it only runs code that was compiled into it. Rebuild it with --compile-inspector to load other code
```

The executable exits with code `1`, before any of your code runs. To run `before.ts` first, import it at the top of `greeting.ts`.

| A default build | Options                                                                                                                                                          |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Rejects         | `--preload`, `-r`, `--require`, `--import`, `--eval`, `-e`, `--print`, `-p`, `--hot` and `--watch` in `BUN_OPTIONS`, and `BUN_INSPECT_PRELOAD`                   |
| Rejects         | `BUN_INSPECT`, and `--inspect`, `--inspect-wait` and `--inspect-brk` in `BUN_OPTIONS`                                                                            |
| Ignores         | `--trace-exit`, `--trace-env`, `--trace-env-js-stack` and the three `--trace-event` options in `BUN_OPTIONS` and in the `execArgv` of a `Worker`, with a warning |
| Ignores         | `preload` and `[serve.static] plugins` in a `bunfig.toml`                                                                                                        |

An executable reads the `bunfig.toml` of the directory it's started in. That file may be there for `bun run`, so what it would load is ignored and isn't an error. The build has bundled your pages, so no server needs the plugins.

A module of Bun's own library carries out the `--trace` options of the third row, and an executable has only the modules that your program uses. They print or write something for you to look at, and your program does the same without them. So the executable prints one line for each and runs your program. `--stack-trace-limit` works like in `bun run`.

```txt
warn: --trace-exit is ignored by this executable because it only runs code that was compiled into it. Rebuild it with --compile-inspector to load other code
```

In `--compile-exec-argv`, the options of the first and the third row fail the build.

Three build flags change that.

| Flag                           | In `Bun.build`                         | The executable                                                                                  |
| ------------------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `--compile-inspector`          | `compile: { inspector: true }`         | Accepts all of the above, so that a debugger can attach. Bun interprets the code it loads       |
| `--aot-allow-code-from-disk`   | `compile: { allowCodeFromDisk: true }` | Accepts `--preload`, `-r` and `--import` if its program has a dynamic `import()` or `require()` |
| `--no-compile-function-source` | `compile: { functionSource: false }`   | Rejects the options in the first row, with `--compile-inspector` too                            |

- **`--compile-inspector` has a price.** A debugger can run any code in any scope, so the compiler can prove less about the program. The executable is larger and slower, and [`--trim`](/compiler/size) removes no features.
- **An option in `--compile-exec-argv` is part of the build.** `--inspect` there needs no `--compile-inspector`.
- **Editors set `BUN_INSPECT_CONNECT_TO`** for every process that you start from their terminals. An executable built without `--compile-inspector` ignores it and runs.

## The module cache

A module of the executable runs at most once. `require.cache` and `Module._cache` don't list it, and `delete require.cache[path]` does nothing to it. That goes for CommonJS modules, ES modules and JSON files. An executable built without `--aot` does the same.

```js counter.cjs icon="/icons/javascript.svg"
globalThis.runs = (globalThis.runs ?? 0) + 1;
exports.runs = globalThis.runs;
```

```js again.cjs icon="/icons/javascript.svg"
const path = require.resolve("./counter.cjs");
console.log(require("./counter.cjs").runs, path in require.cache);
delete require.cache[path];
console.log(require("./counter.cjs").runs);
```

```bash terminal icon="terminal"
bun run ./again.cjs && bun compile ./again.cjs && ./again
```

```txt
1 true
2
1 false
1
```

**Circular requires work as under `bun run`.** A module that is required again while it's still running doesn't start over. The second `require()` returns its `module.exports` as it is at that moment, with the names that have been assigned so far. The rest show up as the module keeps running.

One case differs. A callback that you handed to other code may call `require()` on an ES module while that module is still running, without any `import` or `require()` leading from the module back to the file of the callback. The call then returns the module's namespace object, and reading an export that isn't initialized yet throws a `ReferenceError`. Under `bun run` that's what happens if an `import` started the module; if a `require()` started it, the call returns an empty object.

**A CommonJS module that throws isn't tried again.** The `require()` that started it throws. Every later `require()` of that module throws the same error, as it does for an ES module. Under `bun run` the CommonJS module would run again, so a program that repeats a `require()` until something outside of it has changed keeps getting the first error.

In one case nobody gets the error, under `bun run` and in an executable alike. The CommonJS module is the entry point or was started by an `import`, it calls `require()` on an ES module that imports it back, and then it throws. The ES module has already taken the names that were exported by then, so the import counts as done. Every later `require()` of the CommonJS module returns the namespace object of that import: its `default` is the module's `module.exports`, next to the names it had at that moment.

**A file that your program loads from disk at run time isn't part of the executable**: an `--external` package, or a config file that a package reads with `require()`. It's in `require.cache`, `delete` takes it out, and it runs again at the next `require()`, as under `bun run`.

**A module of the executable can't be loaded a second time in another form.** `new Bun.ModuleGraph().import(url)` of one rejects with `ERR_MODULE_NOT_FOUND`: a second graph would have to run the module again from its file. `require(path, { type: "text" })` with a path that is put together at run time throws as well, because the file isn't there to be read. Use `import(path, { with: { type: "text" } })` with a constant path: the text is then included at build time.

```txt
error: Cannot load '/$bunfs/root/app.ts': it's compiled into this executable, and a compiled module can't be loaded a second time (for example into a Bun.ModuleGraph).
```

## Loader hooks

The compiler resolves every `import` and every `require()` of a constant path at build time, and compiles it to a call of the module. Nothing in `node:module` is asked on the way, so a hook doesn't see those.

| Hook                                                                                     | The assignment    | Bun calls it for                                                                                                    |
| ---------------------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `Module._resolveFilename`, `Module.prototype.require`                                    | Works             | A `require()` or a `require.resolve()` of a [dynamic path](#dynamic-imports), for a name that the build didn't find |
| `Module._load`, `Module.wrap`                                                            | Works             | Nothing. `bun run` doesn't call `Module._load` either                                                               |
| `Module._extensions[".js"]`, and every other extension that a file in the executable has | Throws an `Error` |                                                                                                                     |

Frameworks such as Next.js install the first two when they are loaded, to point a few names at their own copies of a package. In an executable the names that are constants already point where the build found them.

A loader for an extension that no file in the executable has is stored as under `bun run`. Packages such as `handlebars` register one for their own kind of file (`require.extensions[".hbs"]`) when they are required, and it changes nothing that was compiled.

```txt
Cannot replace a loader in Module._extensions: imports in this executable were resolved at build time, so the module loader can't be patched.
```

Packages that patch the loader to transpile, trace or mock what a program requires don't see the modules of an executable. Run such a program with `bun run`, or build it with `--no-aot`.

## Module objects

`module`, `require.main` and `process.mainModule` work as under `bun run`, and so do `children` and `parent` of the objects they give you.

```js
console.log(require.main === module, module.parent?.filename);
for (const child of module.children) console.log(child.id, Object.keys(child.exports));
```

`require.main`, `process.mainModule`, `module.children` and `module.parent` are the only ways from the object of one module to the object of another. The compiler looks for them in the text of your program: `module.children`, `const m = module; m.parent`, `show(require.main)`. What it can answer at build time doesn't count: `require.main === module`, `require.main.filename`, and `!module.parent` in most files.

- **If any file names one of the four, all four work everywhere**, also in a [string that you `eval`](#code-from-strings) and in an `--external` package. It doesn't matter whether the code that names it ever runs.
- **If no file does, all four are `undefined` for everybody**, as in an executable built without `--aot`. That only shows where your code gets to them in a roundabout way: `module[key]` with a computed key, or a `module` that came out of an array or back from native code. The rest of such an object works: `module.exports`, `module.id`, `module.filename`, `require.resolve()`, and `require(path)`, which [finds the entry points](#dynamic-imports).

On an object that the compiler didn't expect anybody to reach, `children` is `undefined` if it would have to list a module for which the compiler keeps no exports object, and `parent` likewise.

`import.meta.require`, `require` in an ES module and a function that `createRequire()` makes for the file that calls it count as the `require` of that file. A file that they load has the module object of the calling file as its `parent` for as long as the program runs, and a CommonJS file lists it in its own `module.children`. Under `bun run` each of these functions has a module object of its own: that object is the `parent` and lists the children, and once nothing else holds it, it is collected and `parent` reads `undefined`.

If the four always worked, one `require` that a package stores in a table would make the compiler keep an exports object for every CommonJS module of your program. Most need none.

## Re-exports of CommonJS modules

The names that a CommonJS module exports are only known once it has run. The same goes for a JSON file, a built-in module and a module outside of the executable. When an ES module re-exports one of these with `export *`, one rare case differs from `bun run`. It is about the namespace object that `import * as ns` gives you. Named imports work the same.

A file has `export *` for two ES modules that both export `x`. That makes `x` ambiguous, so the namespace object of the file has no `x`. If a third `export *` of that file re-exports a CommonJS module that exports `x` as well, the compiled namespace object has the CommonJS value.

## Workers

The compiler finds the file of a worker whose path your code writes out, and compiles it, with everything it imports, as one more entry point. Any other worker you pass as an additional entry point, like in [any executable](/compiler/executables#worker).

A Worker has its own global object, so the compiler treats each entry point as a separate program when your code starts Workers. What a worker does to a class, a prototype or a shared module doesn't slow down the main thread's code, and the other way around. A module that both import is compiled once for each, so when your entry points share most of their code, they are compiled as one program.

```ts worker.ts icon="/icons/typescript.svg"
declare var self: Worker;
self.onmessage = event => self.postMessage(event.data * 2);
```

```ts spawn.ts icon="/icons/typescript.svg"
const worker = new Worker("./worker.ts");
worker.onmessage = event => {
  console.log(event.data);
  worker.terminate();
};
worker.postMessage(21);
```

```bash terminal icon="terminal"
bun compile ./spawn.ts --outfile spawn && rm worker.ts && ./spawn
```

```txt
42
```

These all name a file that the compiler finds:

| You write                                                                                                         | The file is looked for                                      |
| ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `new Worker("./worker.ts")`, `"../worker.ts"`                                                                     | From the folder of your entry point, as the executable does |
| `new Worker(new URL("./worker.ts", import.meta.url))`, also with `.href`                                          | From the file that says so                                  |
| `import.meta.resolve("./worker.ts")`, `require.resolve("./worker.js")`                                            | As an import of that file is                                |
| `path.join(__dirname, "worker.js")`, `path.resolve(import.meta.dir, "worker.ts")`, `` `${__dirname}/worker.js` `` | From the file that says so                                  |
| `import.meta.url`, `import.meta.path`, `__filename`                                                               | The file itself                                             |
| `fileURLToPath()` and `pathToFileURL()` of `node:url` around one of these                                         |                                                             |

The path can pass through `const` variables, parameters of functions in the same file and exports on its way to `new Worker()`, of the global `Worker` or of `node:worker_threads`.

`bun run` looks for a relative path like `"./worker.ts"` from the working directory. An executable looks in itself first, from the folder of its entry point, or from the folder that all of them share. So does the compiler.

A name without `./` in front, like `"some-package/worker.js"` or `"worker.ts"`, leads to a package, else to a file, like under `bun run`, and from that same folder. A file above that folder is found too, like `new URL("../workers/job.ts", import.meta.url)` in `src/main.ts`. Only a path that `new URL()` or `path.join()` leads two or more folders above it isn't, because nothing is left of the executable's own folder in such a path: write `"../../workers/job.ts"`, or pass the file as an additional entry point. The compiler only follows the paths in your own files: a worker that a package in `node_modules` starts from one of its files isn't compiled, because most programs that import such a package never start it. A package of your workspace that is only linked into `node_modules` counts as your own files. Pass such a file as an additional entry point. A path that names no file changes nothing about the build, and the Worker fails to load.

You don't have to list a worker that you embed with the `file` loader and start by the path of the copy. The compiler follows the import to `new Worker()` and compiles the file, with everything it imports, as one more entry point:

```ts spawn.ts icon="/icons/typescript.svg"
import path from "./worker.ts" with { type: "file" };

const worker = new Worker(path);
```

The path doesn't have to go straight from the import to `new Worker()`. It can pass through variables, objects, arrays, functions and promises.

A file that you embed for another use, like a script that a server sends to browsers, stays a file. The compiler goes by what your program does with the path. A path that only ever reaches `new Worker()` is a worker. If the same path can also reach something like `Bun.file()` or `fs.readFile()`, it could be either. The compiler tries up to four files like that, and none of them if there are more. A Worker that gets the path of a file that wasn't compiled fails to load, so the build warns where that can happen:

```txt
warn: TS100071: This Worker may start from an embedded file that isn't compiled, and then it fails to load: "/home/me/app/site/index.js", "/home/me/app/site/login.js", "/home/me/app/site/settings.js" and 97 more. Add the file as an entry point, or pass its import straight to new Worker()
```

You don't have to list a worker that starts from a `data:` URL either, if the whole URL is known at build time. The compiler decodes it and compiles what it holds as one more entry point:

```ts spawn.ts icon="/icons/typescript.svg"
const code = `postMessage("from the worker")`;
const worker = new Worker("data:text/javascript," + encodeURIComponent(code));
```

The URL can be a string, a template or a `+` of constants, with `encodeURIComponent()` or `btoa()` around a part of it, percent-encoded or in base64, and wrapped in `new URL()`, which `node:worker_threads` asks for. It can pass through `const` variables, parameters of functions in the same file and exports on its way to `new Worker()`. The code is read as TypeScript with JSX whatever type the URL names, like under `bun run`, and its imports are resolved from the directory you build in. A dynamic `import()` in a `data:` URL only finds a built-in module or an absolute path, like under `bun run` and in Node.js.

The same goes for the code that you pass to `new Worker(code, { eval: true })` of `node:worker_threads`, and for an object URL, if the code and the type of the `Blob` are known at build time:

```ts spawn.ts icon="/icons/typescript.svg"
const code = `postMessage("from the worker")`;
const worker = new Worker(URL.createObjectURL(new Blob([code], { type: "text/javascript" })));
```

The options and the parts of the `Blob` have to be written as literals, `{ eval: true }` and `[code]`. The `Blob` and the URL can be kept in `const` variables. The executable knows the code by what it says, so a `Blob` that your program makes elsewhere with the same text and the same type starts the compiled code too. In such a Worker `import.meta.url`, `Bun.main` and stack traces show a `blob:` URL that is the same in every run.

If the code in the URL, or in a file that the compiler found by its path, has a syntax error or an import that can't be resolved, the build leaves it out. It prints what's wrong as warnings, and the Worker fails as it does under `bun run`:

```txt
warn: TS100072: The code that this Worker starts from can't be compiled, so the Worker fails to load
```

Any other error in that code stops the build, as it does in an entry point that you list: a type error in `worker.ts` is the same error as in `main.ts`.

A Worker that you create from a `data:` URL, an object URL or `eval` code that your program puts together at run time works too. Its code is [code from a string](#code-from-strings), so Bun interprets it, and the built-in modules it imports too. It shares nothing with your compiled code: it can use any API, [built-in objects](#built-in-objects-are-read-only) aren't read-only in it, and it makes the rest of your program neither larger nor slower.

With [`--trim`](/compiler/size) such a Worker fails to load, like a Worker from a file on disk. So it does with `--disallow-code-generation-from-strings=strict`, where `new Worker()` throws the flag's `EvalError`.

A Worker that you create from a path that your program puts together at run time runs [code that isn't in the executable](#code-that-isnt-in-the-executable), so it fails to load: the Worker gets an `error` event. Pass the file as an entry point. With [`--aot-allow-code-from-disk`](#load-code-from-disk) it loads, in a program that also has a dynamic `import()` or `require()`.

A file can start itself as a Worker. To tell the two threads apart, use `Bun.isMainThread`, not `import.meta.main`. In an executable with a single entry point, `import.meta.main` is decided at build time, like in any executable: it's `true` in that file on every thread. Under `bun run` it's `false` in the Worker. With more than one entry point it's `false` in a Worker here too.

Everything is one program when entry points can meet in one global object: when your code calls `import()` or `require()` with a [dynamic path](#dynamic-imports), which finds any of them, or passes `preload` to `new Worker()`. The program of that global object has to include all of them, so separate programs would only repeat it.

## Native addons

`require("./addon.node")` embeds the addon in the executable, like in [any executable](/compiler/executables#embed-n-api-addons). `process.dlopen(module, path)` loads one from disk, and its path can be computed. Calls into either work the same. A `require()` with a computed path doesn't load an addon, because it [only finds code that's in the executable](#code-that-isnt-in-the-executable).

A package usually ships one addon per platform and makes the name of the file out of `process.platform` and `process.arch`. The compiler knows both, so such a name is a constant to it, and the executable contains the one addon that it loads.

```js index.js icon="/icons/javascript.svg"
module.exports = require(`./my-app.${process.platform}-${process.arch}.node`);
```

The same goes for `"./my-app." + process.platform + ".node"`, for `path.join(__dirname, name)`, for a `require` from `createRequire()`, and for the loaders that [NAPI-RS](https://napi.rs) generates: a `switch` or a chain of `if`s over the platform and the architecture, where each arm requires the file beside the loader or else the package for that platform, like `@my/app-linux-x64-gnu`. Such a loader first asks `existsSync(join(__dirname, "my-app.linux-x64-gnu.node"))`. An embedded addon keeps its name, so the answer is `true`. In an executable built without `--aot` the name gets a hash, `my-app.linux-x64-gnu-2ecze33x.node`, which is also what `Bun.embeddedFiles` lists there.

Where only the start of a package's name is a constant, as in ``require(`my-app-${parts.join("-")}`)``, the executable contains every installed package whose name starts with `my-app-`.

A name can also be one of a few. The compiler follows each of them, and the executable contains every one that's installed:

- an entry of a table, as in `names[process.platform][process.arch]`
- an element of a list that a loop tries, as in `for (const path of paths) require(path)`, or that `forEach`, `map`, `filter` or `find` hands to a function written in the call
- a value or a key of a table, as in `for (const name of Object.values(names))`
- what a function in the same file returns, and what's passed for a parameter of a function that's only ever called
- a variable that's assigned in more than one place, or added to outside of a loop, as in `name += "-musl"`
- a list that's joined, as in `parts.join("-")`, with what `parts.push()` adds outside of a loop
- what `require.resolve()` or `import.meta.resolve()` returns for any such name or for a constant, handed on as it is, as in `require(require.resolve(name))`

The table or the list has to be written in the file and stay as it's written: the file only reads it, and doesn't export it or pass it to a function. In such a name the compiler also takes `const { platform, arch } = process`, what `require("node:process")` returns, `os.platform()` and `os.arch()` for the platform and the architecture that the executable is built for.

The compiler still warns that such a path isn't a constant. If it can't follow a name, pass the addon as another entry point, and the loader finds it by the name that it makes.

```bash terminal icon="terminal"
bun compile ./app.ts ./node_modules/my-app-linux-x64-gnu/my-app.linux-x64-gnu.node --outfile app
```

An addon is native code. It can keep, call and change whatever your program hands it, right away or later, and it can read, call and change globals by name. [Built-in objects are read-only](#built-in-objects-are-read-only) for it too. `napi_run_script` runs [code from a string](#code-from-strings), so it throws the same `EvalError` as `eval` unless the compiler found a call to `eval` or `Function` in your program.

On Linux, compiled code raises `SIGSEGV` and `SIGBUS` itself and handles them before anything else does. An addon or a library can still set a handler for either one with `sigaction()` or `signal()`. The handler is called for every other `SIGSEGV` and `SIGBUS`, the same as without `--aot`. Blocking them with `pthread_sigmask()` or `sigprocmask()` does nothing. All of this works on calls to the C library's functions by name. A library that makes the `rt_sigaction` system call itself, or that gets the function from `dlsym()` with `RTLD_NEXT` or a handle to the C library, takes the executable's place. It has to call the handler that was there before for any signal it didn't raise.

## Plugins and build options

[Plugins](/bundler/plugins) of `Bun.build` work with `compile: { aot: true }`. The compiler is handed what your plugins resolve and load, never what's on disk. Which names a CommonJS file exports and in which order modules run come from the text that `onLoad` returns.

```ts build.ts icon="/icons/typescript.svg"
await Bun.build({
  entrypoints: ["./app.ts"],
  compile: { outfile: "./app", aot: true },
  plugins: [
    {
      name: "settings",
      setup(build) {
        build.onResolve({ filter: /^settings:/ }, args => ({ path: args.path.slice(9), namespace: "settings" }));
        build.onLoad({ filter: /.*/, namespace: "settings" }, async args => ({
          contents: `export default ${JSON.stringify(await readSettings(args.path))};`,
          loader: "js",
        }));
      },
    },
  ],
});
```

All of this behaves as in a build without `aot`, and each hook gets the same arguments:

- `onStart`, `onResolve`, `onLoad`, `onEnd`, `defer()`, and `onBeforeParse` with a native plugin
- several plugins, which are asked in order until one answers
- filters, namespaces, modules that are in no file, and what such a module imports
- `external: true`
- every loader, `contents` as a string or as bytes, and answers that come late
- a plugin that throws or rejects: the build fails with its message
- answers for the entry point, for a package in `node_modules`, for a CommonJS file, for a file of a page
- `files`, macros, `define`, `loader`, `env`, `conditions`, `external`, `packages`, `drop`, `features`, `jsx`, `tsconfig`, `root`, `minify` and `sourcemap`
- which file of a package ends up in the executable: `"exports"` and `conditions` first, then `"module"` for an `import` and `"main"` for a `require()`. If anything requires the package, its imports use `"main"` too, so there's one copy. `bun run` always takes `"main"`
- which loader a file gets: `with { type: "file" }` and the `loader` option come before the extension, and a file with an extension that Bun doesn't know is embedded as it is, for `import` and for `require()`

This differs:

|                                                                               | `bun build --compile`                  | `bun compile`                                                        |
| ----------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------- |
| Files that a [computed path](#dynamic-imports) or `import.meta.resolve` names | Not in the executable, so no hook runs | In the executable, so the hooks run for them                         |
| One import that a file has twice                                              | `onResolve` runs twice                 | `onResolve` runs once                                                |
| The `"development"` condition of `"exports"`                                  | Holds, except in a production build    | Holds only with `--conditions=development`, as under `bun run`       |
| One path in two namespaces                                                    | One module                             | Two modules                                                          |
| `minify`                                                                      | Renames functions and classes          | Shortens the same names in `name`, stack traces and messages         |
| `sourcemap`                                                                   | A map is one of the outputs            | Only `external` [writes a map](/compiler/debugging#use-with-sentry)  |
| `metafile`                                                                    | One output for each entry point        | [One output, the executable](#metafile)                              |
| `format`, `bytecode`, `--bytecode-depth`, `--bytecode-order`                  | Change what's in the executable        | No effect. Nothing is bundled, and the executable has no bytecode    |
| `banner` or `footer` with code                                                | Work                                   | The build fails. A `banner` or `footer` that's only comments is fine |

The text that `onLoad` returns is type checked like a file, and its types are what the files that import it see.

Plugins for Solid, Vue or Svelte return JavaScript (`loader: "js"` or `"jsx"`) for a TypeScript file, so the types are gone from what they return. In that case the JavaScript is compiled, and the files that import it get their types from the file on disk, like in your editor. The build doesn't report type errors inside of that file. Your editor and `bun check` do. `import type { Props } from "./view.tsx"` keeps working.

`onResolve` doesn't change what the type checker reads: it resolves an import as TypeScript does. So a module that's in no file needs a declaration, like in your editor.

```ts settings.d.ts icon="/icons/typescript.svg"
declare module "settings:*" {
  const settings: { port: number };
  export default settings;
}
```

If a module doesn't match its declaration, or the JavaScript from a plugin doesn't match the file it was made from, the program [behaves as under `bun run`](/compiler/types#when-a-value-doesnt-match-its-type).

### Metafile

`--metafile`, `--metafile-md` and `metafile` in `Bun.build` write [the same format](/bundler#metafile) as without `--aot`. `inputs` lists every file of yours and of your packages that's in the executable, with its imports. Nothing is bundled, so a few things differ:

- `outputs` has one entry, the executable, whatever the number of entry points. Its `entryPoint` is the first one.
- `bytes` of the output is the size of your compiled program inside of the executable. That includes the parts of Bun's own modules, like `node:fs`, that are compiled with it. It doesn't include the Bun runtime.
- `bytesInOutput` is the machine code of the functions in that file. A function that's inlined counts for the file that calls it.
- Files that a [computed path](#dynamic-imports) or `import.meta.resolve` names are inputs, and imports of the file that names them.
- An import that's only types, or that nothing uses, isn't listed. When a file that only re-exports is left out of the executable, its importer lists the files behind it instead, without `original`.

The executable is the same with and without a metafile.

`bun build` on the command line has no plugins, with or without `--aot`. [`Bun.plugin`](/runtime/plugins) in your program is about [code that isn't in the executable](#code-from-strings). As in every executable from `bun build --compile`, a plugin is never asked for a module that's in the executable, whatever its filters match.

## `Function.prototype.toString()`

The executable includes the source code of your functions, with the types removed, and `toString()` returns it. It's the text of your file. `bun run` returns what its transpiler printed, so line breaks, indentation and quotes can differ.

TypeScript's parameter properties go with the types. The text of `class Point { constructor(public id: number) {} }` is `class Point { constructor(id) {} }`, where `bun run` shows the field and the assignment that it compiles the property to, `id;` and `this.id = id;`. `new Point(1).id` is `1` in both. A class that you get by evaluating the executable's text again doesn't set `id`.

`--no-compile-function-source` removes the text. The executable is smaller and doesn't contain your source code. It still runs [code from strings](#code-from-strings). In `Bun.build`, the option is `compile: { functionSource: false }`.

```ts text.ts icon="/icons/typescript.svg"
function add(a: number, b: number): number {
  return a + b;
}
console.log(String(add), add.name, add.length);
```

```bash terminal icon="terminal"
bun compile --no-compile-function-source ./text.ts && ./text
```

```txt
function add() { [native code] } add 2
```

Code that reads parameter names out of that string, as some dependency injection libraries do, finds none.

A library that reads the text of your functions can also decide differently on the text of your file than on what the transpiler printed. Elysia reads a handler to find out whether it needs the request body, and it doesn't follow a variable that holds the context: after `const c = context; return handle(c)`, `context.body` is `undefined` in an executable. `bun run` prints that handler as `return handle(context)`, which Elysia does follow. Pass `context` itself.

## Error messages

The class of an error, its line and its stack are the same as under `bun run`. Where `bun run` quotes your source in a message, an executable quotes less of it.

```ts messages.ts icon="/icons/typescript.svg"
const o: any = { a: null };
try {
  o.b();
} catch (error) {
  console.log(String(error));
}
try {
  o.b(1, 2);
} catch (error) {
  console.log(String(error));
}
try {
  new o.b();
} catch (error) {
  console.log(String(error));
}
try {
  o.a.c;
} catch (error) {
  console.log(String(error));
}
try {
  o.a.c = 1;
} catch (error) {
  console.log(String(error));
}
```

```bash terminal icon="terminal"
bun messages.ts
```

```txt
TypeError: o.b is not a function. (In 'o.b()', 'o.b' is undefined)
TypeError: o.b is not a function. (In 'o.b(1, 2)', 'o.b' is undefined)
TypeError: undefined is not a constructor (evaluating 'new o.b')
TypeError: null is not an object (evaluating 'o.a.c')
TypeError: null is not an object (evaluating 'o.a.c = 1')
```

```bash terminal icon="terminal"
bun compile ./messages.ts && ./messages
```

```txt
TypeError: o.b is not a function. (In 'o.b()', 'o.b' is undefined)
TypeError: o.b is not a function. (In 'o.b()', 'o.b' is undefined)
TypeError: undefined is not a constructor (evaluating 'new o.b')
TypeError: null is not an object (evaluating 'null.c')
TypeError: null is not an object (evaluating 'null.c')
```

| The message                            | What an executable quotes                                                                                                                                     |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `… is not a function`                  | What you called, as you wrote it: `o.b`, `this.client.send`, `handlers[name]`. Not the arguments                                                              |
| `… is not a constructor`               | The same, behind `new`                                                                                                                                        |
| `… is not a function`, of `instanceof` | The whole expression, where both sides consist of names as above: `(evaluating 'value instanceof o')`. Otherwise nothing: `Object  is not a function`         |
| `… is not an object`                   | The value that was `null` or `undefined`, then the name of the property: `null.c`, `undefined["key"]`, `null[0]`. Not the expression that gave you the `null` |
| `… is not an Object.`, of `in`         | Nothing: `5 is not an Object.`, where `bun run` adds `(evaluating '"x" in five')`                                                                             |

An executable doesn't hold the text of each expression. It keeps what you called where that consists of names, `this`, `super` and keys in brackets, up to 80 characters. A callee that's more than that has no name in the message: `f()()` says `5 is not a function`, and so does a call with spread arguments.

A method that you call on more than that has its own name alone: `make().get()`, `new Late(1).get()` and `[1].get()` all say `get is not a function. (In 'get()', 'get' is undefined)`, where `bun run` says `make().get is not a function. (In 'make().get()', 'make().get' is undefined)`. So does a method that many places call on different objects: `stream.destroy()` can say `destroy is not a function`.

A tagged template is worded as a call. ``tag`x` `` says `tag is not a function. (In 'tag()', 'tag' is undefined)`, where `bun run` says ``undefined is not a function (near '...tag`x`...')``.

One more message differs in an executable built with [`--sourcemap=external`](/compiler/debugging#minified-executables-and-external-source-maps), although `bun run` quotes nothing in it. There, an update through a computed key, `o[k] += 1`, `o[k]++`, `--o[k]` or `o[k] &&= 1`, throws `undefined is not an object` when `o` is `undefined` and `null is not an object` when it's `null`, unless `k` is an object. `bun run` and every other executable say `Cannot access property of undefined or null` for both.

The class of the error is the same, so test `error instanceof TypeError` or `error.name`, not `error.message`. A service that groups errors by message, such as Sentry, sees new groups.

When `bun run` prints an error, it prints the lines of your file around it first, with a caret under the place. An executable prints the message and the stack trace without those lines. It doesn't hold the text of your files, and it never opens a file to show you one: what lies on the disk under that name may not be what was compiled. The stack trace has the lines and columns of your sources, and the `originalLine` and `originalColumn` of an error are its `line` and `column`. [`bun run --aot`](/compiler/debugging) runs your files where they are, so it prints the lines.

## Class names from computed keys

A class without a name takes the name of the property that it's assigned to. With a computed key, `bun run` sets that name after the static fields of the class have run, and not at all if one of them has read `name`. An executable sets it first, as the ECMAScript specification says.

```ts names.ts icon="/icons/typescript.svg"
function create(key: string) {
  return {
    [key]: class {
      static label = this.name;
    },
  }[key]!;
}

const Shape = create("Shape");
console.log([Shape.label, Shape.name]);
```

```bash terminal icon="terminal"
bun compile ./names.ts && ./names
```

```txt
[ "Shape", "Shape" ]
```

`bun names.ts` prints `[ "", "" ]`.

## Function names from local variables

A function or a class without a name takes the name of the variable that it's assigned to. Inside a function or a block, Bun's transpiler replaces a `let` or `const` that is used once, in the next statement, with its value. Under `bun run` the function then never reaches a variable, so it has no name. An executable gives it the name, as the ECMAScript specification says. The same holds for a function that is declared in a block, such as the body of an `if` or of a loop, and used once in the first statement of that block.

```ts locals.ts icon="/icons/typescript.svg"
function create() {
  const double = (x: number) => x * 2;
  return double;
}

console.log([create().name]);
```

```bash terminal icon="terminal"
bun compile ./locals.ts && ./locals
```

```txt
[ "double" ]
```

`bun locals.ts` prints `[ "" ]`. A `var`, and a variable at the top level of a file, give the name in both.

## Raw strings of tagged templates

`String.raw`, and `strings.raw` in a tag function, give you the text of a template as you wrote it.

```ts raw.ts icon="/icons/typescript.svg"
console.log(String.raw`é`, String.raw`é`.length);
```

```bash terminal icon="terminal"
bun compile ./raw.ts && ./raw
```

```txt
é 1
```

That's what the ECMAScript specification says. `bun raw.ts` prints `é 6`, because Bun's transpiler rewrites every character above U+007F in a tagged template to an escape before the file runs. The cooked strings, `strings[0]` in a tag function, are the same in both.

## Sloppy-mode code

This section applies to CommonJS files without `"use strict"` at the top. ES modules and TypeScript files with `import` or `export` are always strict.

| You write                                  | Compiled                                                                                                                                                  |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `f.caller`, `f.arguments`                  | `null`. The build warns where it can see that `f` is a function                                                                                           |
| `getFunction()` of a call site             | `undefined`, as for a strict function and in an executable built without `--aot`. Also in a text that `eval` or `Function` gets as a constant             |
| A function declared in a block             | The code around the block, branch, loop, `case` or `try` can read it too, once that has run. Under `bun run` only the block sees it                       |
| `"use strict"` at the top of a function    | The function runs in sloppy mode, as under `bun run`: Bun's transpiler removes the directive. A function declared in a block inside it stays in its block |
| A decorator, with `experimentalDecorators` | The decorator expression runs in strict mode, like the class body. Under `bun run` it runs after the class, in sloppy mode                                |
| A `.cjs` file loaded with `import`         | Sloppy mode, as in Node.js. `bun run` has it strict if it never names `require`, `module` or `exports` and no `package.json` says `"type": "commonjs"`    |
| `function (a, b = 2) { eval("var a"); }`   | `var a` makes a new variable, `undefined` at first, and the body reads it from then on, as in Node.js. Under `bun run` the body still reads the parameter |

`with` statements work, and so does a direct `eval` that declares variables.

## Flags on process

Bun itself looks at five properties of `process`: `noDeprecation`, `throwDeprecation`, `traceDeprecation`, `traceProcessWarnings` and `domain`. In a compiled executable, Bun only reads them as own properties of `process`. `process.noDeprecation = true` and `BUN_OPTIONS="--no-deprecation"` work as before. `EventEmitter.prototype.noDeprecation = true` doesn't: Bun ignores it, and still warns. `util.inspect` reads its `styles` the same way: only the object's own keys count.

## Memory

- **Each thread can have 32 GB of objects.** References between objects take 32 bits. Objects and small arrays count toward the limit. The characters of strings, large arrays, `ArrayBuffer`s and typed arrays don't. Each `Worker` has a limit of its own. Where the address space of the process is limited (`ulimit -v`), objects take address space as they need it and share the limit with everything else in the process. Address space that objects have taken stays reserved for objects until the process exits. A thread that goes over it gets a `RangeError` whose message starts with "Out of memory" and names the limit and the option that removes it. A thread that stays just under the limit gets the same error once four garbage collections in a row have each freed less than 1/64 of the heap. Otherwise it would spend nearly all of its time collecting garbage. Your code can catch it. It is thrown where it allocates, in `JSON.parse` and `structuredClone`, or at the next function call that checks the stack. One case is left: a loop that allocates inside a `try` of its own function, or that the compiler has inlined into one, and that calls no function gets no error there, and ends the process with an out-of-memory error once memory is used up, or after 16 such collections in a row. In a `Worker` it ends that Worker alone: its parent gets an `error` event with the `RangeError`, and the exit code is 1. Move the loop into a function of its own. `bun compile --aot-huge-heap` compiles with 64-bit references, which removes the limit. The executable then uses more memory and runs slower.
- **Where the address space is limited, `new Worker()` throws a `TypeError` when there is no room for another thread.** If another `Worker` that the same thread started is ending at that moment, the constructor returns, and the new `Worker` starts once that one has ended. One new `Worker` can wait for each one that is ending. If there is still no room then, it gets an `error` event with the same `TypeError` and closes with code 1.
- **A local variable stops keeping its value alive after its last use,** even if the function is still waiting at an `await`. In an async function or a generator, parameters are the exception: whatever the names in the parameter list hold, and `this`, stay alive until the function ends, as under `bun run`. In any other function, a parameter is like a local variable. Under `bun run`, a local variable that a closure captures lives as long as the function's other variables. So if a `FinalizationRegistry` is in a local variable that you never touch again, it can be collected while the function waits, and its callback never runs. Keep the registry at module level.
- **A variable or property that nothing reads doesn't keep its value alive.** The compiler removes it along with the stores into it. If you park an object in a module-level variable only so that a `WeakRef` to it keeps answering, or so that its `FinalizationRegistry` callback doesn't run yet, read that variable somewhere.

## Platforms

`bun compile` compiles for arm64 and x64, on the platform that runs the build.

| Platform                                        | State                            |
| ----------------------------------------------- | -------------------------------- |
| Linux x64, glibc                                | Tested                           |
| Linux arm64, Linux musl, macOS arm64, macOS x64 | Tested. Some tests still fail    |
| Windows x64, Windows arm64                      | Tested. Many tests still fail    |
| FreeBSD, Android                                | Bun builds there. Not tested yet |
