feat(bundler): add files option for in-memory bundling (#25852)

## Summary

Add support for in-memory entrypoints and files in `Bun.build` via the
`files` option:

```ts
await Bun.build({
  entrypoints: ["/app/index.ts"],
  files: {
    "/app/index.ts": `
      import { greet } from "./greet.ts";
      console.log(greet("World"));
    `,
    "/app/greet.ts": `
      export function greet(name: string) {
        return "Hello, " + name + "!";
      }
    `,
  },
});
```

### Features

- **Bundle entirely from memory**: No files on disk needed
- **Override files on disk**: In-memory files take priority over disk
files
- **Mix disk and virtual files**: Real files can import virtual files
and vice versa
- **Multiple content types**: Supports `string`, `Blob`, `TypedArray`,
and `ArrayBuffer`

### Use Cases

- Code generation at build time
- Injecting build-time constants
- Testing with mock modules
- Bundling dynamically generated code
- Overriding configuration files for different environments

### Implementation Details

- Added `FileMap` struct in `JSBundler.zig` with `resolve`, `get`,
`contains`, `fromJS`, and `deinit` methods
- Uses `"memory"` namespace to avoid `pathWithPrettyInitialized`
allocation issues during linking phase
- FileMap checks added in:
  - `runResolver` (entry point resolution)
  - `runResolutionForParseTask` (import resolution)
  - `enqueueEntryPoints` (entry point handling)
  - `getCodeForParseTaskWithoutPlugins` (file content reading)
- Root directory defaults to cwd when all entrypoints are in the FileMap
- Added TypeScript types with JSDoc documentation
- Added bundler documentation with examples

## Test plan

- [x] Basic in-memory file bundling
- [x] In-memory files with absolute imports
- [x] In-memory files with relative imports (same dir, subdirs, parent
dirs)
- [x] Nested/chained imports between in-memory files
- [x] TypeScript and JSX support
- [x] Blob, Uint8Array, and ArrayBuffer content types
- [x] Re-exports and default exports
- [x] In-memory file overrides real file on disk
- [x] Real file on disk imports in-memory file via relative path
- [x] Mixed disk and memory files with complex import graphs

Run tests with: `bun bd test test/bundler/bundler_files.test.ts`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
This commit is contained in:
robobun
2026-01-08 15:05:41 -08:00
committed by GitHub
parent dda9a9b0fd
commit 24b97994e3
8 changed files with 1077 additions and 4 deletions

View File

@@ -1979,6 +1979,65 @@ declare module "bun" {
*/
reactFastRefresh?: boolean;
/**
* A map of file paths to their contents for in-memory bundling.
*
* This allows you to bundle virtual files that don't exist on disk, or override
* the contents of files that do exist on disk. The keys are file paths (which should
* match how they're imported) and the values are the file contents.
*
* File contents can be provided as:
* - `string` - The source code as a string
* - `Blob` - A Blob containing the source code
* - `NodeJS.TypedArray` - A typed array (e.g., `Uint8Array`) containing the source code
* - `ArrayBufferLike` - An ArrayBuffer containing the source code
*
* @example
* ```ts
* // Bundle entirely from memory (no files on disk needed)
* await Bun.build({
* entrypoints: ["/app/index.ts"],
* files: {
* "/app/index.ts": `
* import { helper } from "./helper.ts";
* console.log(helper());
* `,
* "/app/helper.ts": `
* export function helper() {
* return "Hello from memory!";
* }
* `,
* },
* });
* ```
*
* @example
* ```ts
* // Override a file on disk with in-memory contents
* await Bun.build({
* entrypoints: ["./src/index.ts"],
* files: {
* // This will be used instead of the actual ./src/config.ts file
* "./src/config.ts": `export const API_URL = "https://production.api.com";`,
* },
* });
* ```
*
* @example
* ```ts
* // Mix disk files with in-memory files
* // Entry point is on disk, but imports a virtual file
* await Bun.build({
* entrypoints: ["./src/index.ts"], // Real file on disk
* files: {
* // Virtual file that ./src/index.ts can import via "./generated.ts"
* "./src/generated.ts": `export const BUILD_TIME = ${Date.now()};`,
* },
* });
* ```
*/
files?: Record<string, string | Blob | NodeJS.TypedArray | ArrayBufferLike>;
/**
* Generate a JSON file containing metadata about the build.
*