# Publish a library for bun compile

> A package points bun compile at its TypeScript with the aot condition of its exports. Everyone else sees an ordinary package

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

[`bun compile`](/compiler/aot) generates better code when it knows the types. Most packages on npm ship JavaScript and `.d.ts` files, so the types inside their functions are gone. A package gives them back with one line in its `package.json`: the `"aot"` condition.

```json title="package.json" icon="file-json"
{
  "name": "greeter",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": {
      "aot": "./dist/index.aot.ts",
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  }
}
```

## How it works

`"aot"` is an ordinary [condition of `exports`](https://nodejs.org/api/packages.html#conditional-exports), like `"node"` or `"browser"`.

| Command                                                                        | `import "greeter"` loads |
| ------------------------------------------------------------------------------ | ------------------------ |
| `bun compile`, `bun build --compile --aot`, `bun run --aot`, `bun test --aot`  | `dist/index.aot.ts`      |
| `bun check --aot`                                                              | `dist/index.aot.ts`      |
| `bun run`, `bun test`, `bun build`, Node.js, `tsc`, editors and other bundlers | `dist/index.js`          |

- **The file is the module.** Bun compiles it like any TypeScript file of a dependency. Nothing compares it with `index.js`, so keeping the two in step is up to you. Generating both from the same sources does that.
- **A wrong type never changes what a program does.** A type is a prediction that a guard checks. A value of another type takes a slower path.
- **The type checker reads the same file.** Under `--aot`, `"types"` isn't read for an export that has an `"aot"` condition, whatever the order of the keys, so every class has one declaration. What the file exports is what importers see: a class written as `export var Box = (() => { class Box {} return Box; })()` is a value there and can't be used as a type.
- **Put `"aot"` first.** As with every condition, the first one that matches wins: after `"default"`, or after an `"import"` that names a file, `"aot"` is never reached.
- **It works wherever conditions work:** nested under `"import"` or `"require"`, on subpaths, on patterns such as `"./features/*"`, and in `"imports"`.
- **Relative imports are resolved as always.** Only imports that go through `exports` or `imports` see the condition, so a typed file imports the typed files beside it by name: `import { parse } from "./parse.aot.ts"`. As in TypeScript, `"./parse.js"` also finds `parse.ts`, `"./parse.mjs"` finds `parse.mts`, and `"./parse.cjs"` finds `parse.cts`.
- **The name of the file is up to you.** It can be your source: `"aot": "./src/index.ts"`, if you publish `src`.

### Compiler options

Bun type checks and compiles the TypeScript of a package with an `"aot"` condition under the `"compilerOptions"` of that package's `package.json`, or as `strict` if it has none. The `tsconfig.json` and the flags of the program that installs the package don't apply, so the package behaves the same in every program.

```json title="package.json" icon="file-json"
{
  "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true }
}
```

State the options that change what your code does, if your build sets them: `useDefineForClassFields`, `experimentalDecorators`, `emitDecoratorMetadata`, `jsx`, `jsxFactory`, `jsxFragmentFactory` and `jsxImportSource`. As everywhere in Bun, `target` and `verbatimModuleSyntax` don't change the code.

In the package you're working on, the one you run Bun in, your `tsconfig.json` applies as always.

### Type errors

If the files have type errors in the program that installs the package, the build continues and compiles them without their types:

```txt
warn: TS100009: 'greeter' has 1 type error (the first at node_modules/greeter/dist/index.aot.ts:3:7), so 1 of its 1 files are compiled without their types. They still work, but run slower.
```

### Syntax errors and missing files

A file that doesn't parse stops the build, and so does an `"aot"` condition that names a file the package doesn't have. The file is the module, so Bun doesn't fall back to `index.js`, just as Node.js doesn't try the next condition when a target is missing. With a pattern such as `"./features/*"`, every subpath that a program imports needs its own file.

## Let Bun write the files

`bun compile --library` builds a package that's ready for npm, `"aot"` condition included.

```ts src/index.ts icon="/icons/typescript.svg"
export interface Options {
  name: string;
  times: number;
}

export function greet(options: Options): string {
  return ("hello " + options.name + " ").repeat(options.times).trim();
}
```

```json package.json icon="file-json"
{
  "name": "greeter",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": { "test": "bun test" },
  "devDependencies": { "@types/bun": "latest" }
}
```

```bash terminal icon="terminal"
bun compile --library
```

```txt
  index.js      135 bytes  (entry point)
  index.d.ts    123 bytes  (declaration)
  index.aot.ts  232 bytes  (aot source)
  package.json  450 bytes  (asset)
```

| File           | Holds                                              | Read by                     |
| -------------- | -------------------------------------------------- | --------------------------- |
| `index.js`     | The bundle, as ES modules                          | Every runtime and bundler   |
| `index.d.ts`   | Types for everything `index.js` exports            | Editors, `tsc`, `bun check` |
| `index.aot.ts` | `index.js` with the types of your sources put back | `bun compile`               |
| `package.json` | Your `package.json`, rewritten for the files above | npm and whoever installs it |

Bun type checks the package first, and a type error fails the build. It keeps the fields of your `package.json` that describe the package, such as `name`, `version`, `license` and `dependencies`. It leaves out `scripts`, `devDependencies`, `private` and `workspaces`, and writes its own `type`, `main`, `module`, `types`, `exports`, `files` and `compilerOptions`. It also copies your `README` and `LICENSE` files. Publish the folder with `bun publish` from `dist`.

### Keep your own package.json

[`bun build --dts`](/bundler#dts) writes the same `.js`, `.d.ts` and `.aot.ts` files to `--outdir` and leaves your `package.json` alone. Add the condition yourself, as at the top of this page.

```bash terminal icon="terminal"
bun build ./src/index.ts --outdir dist --target node --dts
```

### What has no .aot.ts file yet

With `--minify-identifiers` (which `--minify` includes), `--no-bundle`, `--format=cjs` or `--sourcemap=inline`, Bun writes the JavaScript and the `.d.ts` files and warns that it left out the rest:

```txt
warn: the .aot.ts files that --aot compiles a package from are left out: they do not support --minify-identifiers yet
```

Bun also type checks each `.aot.ts` file before it writes it, the way a program that installs your package does. If a file has a type error, the build warns you and leaves the files out.

## Test before you publish

```bash terminal icon="terminal"
cd dist && bun pm pack            # greeter-1.0.0.tgz
cd ../../app && bun add ../greeter/dist/greeter-1.0.0.tgz
bun check --aot ./main.ts         # type checks what bun compile compiles, your package included
bun compile ./main.ts --explain   # shows where types are missing
```

`bun run --aot ./main.ts` and `bun test --aot` run your `.aot.ts` files without compiling them, so your package's own tests can cover them.
