Skip to content

Commit 3af0d23

Browse files
robobunClaude Botclaudeautofix-ci[bot]
authored
docs: expand single-file executable file embedding documentation (#25408)
## Summary - Expanded documentation for embedding files in single-file executables with `with { type: "file" }` - Added clear explanation of how the import attribute works and path transformation at build time - Added examples for reading embedded files with both `Bun.file()` and Node.js `fs` APIs - Added practical examples: JSON configs, HTTP static assets, templates, binary files (WASM, fonts) - Improved `Bun.embeddedFiles` section with a dynamic asset server example ## Test plan - [x] Verified all code examples compile and run correctly with `bun build --compile` - [x] Tested `Bun.file()` reads embedded files correctly - [x] Tested `node:fs` APIs (`readFileSync`, `promises.readFile`, `stat`) work with embedded files - [x] Tested `Bun.embeddedFiles` returns correct blob array - [x] Tested `--asset-naming` flag removes content hashes 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 9c96937 commit 3af0d23

File tree

1 file changed

+157
-15
lines changed

1 file changed

+157
-15
lines changed

docs/bundler/executables.mdx

Lines changed: 157 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -413,34 +413,141 @@ cd /home/me/Desktop
413413

414414
## Embed assets & files
415415

416-
Standalone executables support embedding files.
416+
Standalone executables support embedding files directly into the binary. This lets you ship a single executable that contains images, JSON configs, templates, or any other assets your application needs.
417417

418-
To embed files into an executable with `bun build --compile`, import the file in your code.
418+
### How it works
419+
420+
Use the `with { type: "file" }` [import attribute](https://github.com/tc39/proposal-import-attributes) to embed a file:
421+
422+
```ts index.ts icon="/icons/typescript.svg"
423+
import icon from "./icon.png" with { type: "file" };
424+
425+
console.log(icon);
426+
// During development: "./icon.png"
427+
// After compilation: "$bunfs/icon-a1b2c3d4.png" (internal path)
428+
```
429+
430+
The import returns a **path string** that points to the embedded file. At build time, Bun:
431+
432+
1. Reads the file contents
433+
2. Embeds the data into the executable
434+
3. Replaces the import with an internal path (prefixed with `$bunfs/`)
435+
436+
You can then read this embedded file using `Bun.file()` or Node.js `fs` APIs.
437+
438+
### Reading embedded files with Bun.file()
439+
440+
`Bun.file()` is the recommended way to read embedded files:
419441

420442
```ts index.ts icon="/icons/typescript.svg"
421-
// this becomes an internal file path
422443
import icon from "./icon.png" with { type: "file" };
423444
import { file } from "bun";
424445

446+
// Get file contents as different types
447+
const bytes = await file(icon).arrayBuffer(); // ArrayBuffer
448+
const text = await file(icon).text(); // string (for text files)
449+
const blob = file(icon); // Blob
450+
451+
// Stream the file in a response
425452
export default {
426453
fetch(req) {
427-
// Embedded files can be streamed from Response objects
428-
return new Response(file(icon));
454+
return new Response(file(icon), {
455+
headers: { "Content-Type": "image/png" },
456+
});
429457
},
430458
};
431459
```
432460

433-
Embedded files can be read using `Bun.file`'s functions or the Node.js `fs.readFile` function (in `"node:fs"`).
461+
### Reading embedded files with Node.js fs
434462

435-
For example, to read the contents of the embedded file:
463+
Embedded files work seamlessly with Node.js file system APIs:
436464

437465
```ts index.ts icon="/icons/typescript.svg"
438466
import icon from "./icon.png" with { type: "file" };
467+
import config from "./config.json" with { type: "file" };
468+
import { readFileSync, promises as fs } from "node:fs";
469+
470+
// Synchronous read
471+
const iconBuffer = readFileSync(icon);
472+
473+
// Async read
474+
const configData = await fs.readFile(config, "utf-8");
475+
const parsed = JSON.parse(configData);
476+
477+
// Check file stats
478+
const stats = await fs.stat(icon);
479+
console.log(`Icon size: ${stats.size} bytes`);
480+
```
481+
482+
### Practical examples
483+
484+
#### Embedding a JSON config file
485+
486+
```ts index.ts icon="/icons/typescript.svg"
487+
import configPath from "./default-config.json" with { type: "file" };
488+
import { file } from "bun";
489+
490+
// Load the embedded default configuration
491+
const defaultConfig = await file(configPath).json();
492+
493+
// Merge with user config if it exists
494+
const userConfig = await file("./user-config.json")
495+
.json()
496+
.catch(() => ({}));
497+
const config = { ...defaultConfig, ...userConfig };
498+
```
499+
500+
#### Serving static assets in an HTTP server
501+
502+
Use `static` routes in `Bun.serve()` for efficient static file serving:
503+
504+
```ts server.ts icon="/icons/typescript.svg"
505+
import favicon from "./favicon.ico" with { type: "file" };
506+
import logo from "./logo.png" with { type: "file" };
507+
import styles from "./styles.css" with { type: "file" };
508+
import { file, serve } from "bun";
509+
510+
serve({
511+
static: {
512+
"/favicon.ico": file(favicon),
513+
"/logo.png": file(logo),
514+
"/styles.css": file(styles),
515+
},
516+
fetch(req) {
517+
return new Response("Not found", { status: 404 });
518+
},
519+
});
520+
```
521+
522+
Bun automatically handles Content-Type headers and caching for static routes.
523+
524+
#### Embedding templates
525+
526+
```ts index.ts icon="/icons/typescript.svg"
527+
import templatePath from "./email-template.html" with { type: "file" };
528+
import { file } from "bun";
529+
530+
async function sendWelcomeEmail(user: { name: string; email: string }) {
531+
const template = await file(templatePath).text();
532+
const html = template.replace("{{name}}", user.name).replace("{{email}}", user.email);
533+
534+
// Send email with the rendered template...
535+
}
536+
```
537+
538+
#### Embedding binary files
539+
540+
```ts index.ts icon="/icons/typescript.svg"
541+
import wasmPath from "./processor.wasm" with { type: "file" };
542+
import fontPath from "./font.ttf" with { type: "file" };
439543
import { file } from "bun";
440544

441-
const bytes = await file(icon).arrayBuffer();
442-
// await fs.promises.readFile(icon)
443-
// fs.readFileSync(icon)
545+
// Load a WebAssembly module
546+
const wasmBytes = await file(wasmPath).arrayBuffer();
547+
const wasmModule = await WebAssembly.instantiate(wasmBytes);
548+
549+
// Read binary font data
550+
const fontData = await file(fontPath).bytes();
444551
```
445552

446553
### Embed SQLite databases
@@ -507,22 +614,57 @@ This is honestly a workaround, and we expect to improve this in the future with
507614

508615
### Listing embedded files
509616

510-
To get a list of all embedded files, use `Bun.embeddedFiles`:
617+
`Bun.embeddedFiles` gives you access to all embedded files as `Blob` objects:
511618

512619
```ts index.ts icon="/icons/typescript.svg"
513620
import "./icon.png" with { type: "file" };
621+
import "./data.json" with { type: "file" };
622+
import "./template.html" with { type: "file" };
514623
import { embeddedFiles } from "bun";
515624

516-
console.log(embeddedFiles[0].name); // `icon-${hash}.png`
625+
// List all embedded files
626+
for (const blob of embeddedFiles) {
627+
console.log(`${blob.name} - ${blob.size} bytes`);
628+
}
629+
// Output:
630+
// icon-a1b2c3d4.png - 4096 bytes
631+
// data-e5f6g7h8.json - 256 bytes
632+
// template-i9j0k1l2.html - 1024 bytes
517633
```
518634

519-
`Bun.embeddedFiles` returns an array of `Blob` objects which you can use to get the size, contents, and other properties of the files.
635+
Each item in `Bun.embeddedFiles` is a `Blob` with a `name` property:
520636

521637
```ts
522-
embeddedFiles: Blob[]
638+
embeddedFiles: ReadonlyArray<Blob>;
523639
```
524640

525-
The list of embedded files excludes bundled source code like `.ts` and `.js` files.
641+
This is useful for dynamically serving all embedded assets using `static` routes:
642+
643+
```ts server.ts icon="/icons/typescript.svg"
644+
import "./public/favicon.ico" with { type: "file" };
645+
import "./public/logo.png" with { type: "file" };
646+
import "./public/styles.css" with { type: "file" };
647+
import { embeddedFiles, serve } from "bun";
648+
649+
// Build static routes from all embedded files
650+
const staticRoutes: Record<string, Blob> = {};
651+
for (const blob of embeddedFiles) {
652+
// Remove hash from filename: "icon-a1b2c3d4.png" -> "icon.png"
653+
const name = blob.name.replace(/-[a-f0-9]+\./, ".");
654+
staticRoutes[`/${name}`] = blob;
655+
}
656+
657+
serve({
658+
static: staticRoutes,
659+
fetch(req) {
660+
return new Response("Not found", { status: 404 });
661+
},
662+
});
663+
```
664+
665+
<Note>
666+
`Bun.embeddedFiles` excludes bundled source code (`.ts`, `.js`, etc.) to help protect your application's source.
667+
</Note>
526668

527669
#### Content hash
528670

0 commit comments

Comments
 (0)