Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 44 additions & 17 deletions docs/bundler/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1568,34 +1568,50 @@ bun build ./src/index.ts --outdir ./dist --metafile=./dist/meta.json --metafile-

#### `metafile` option formats

In the JavaScript API, `metafile` accepts several forms:
In the JavaScript API, `metafile` accepts several forms. Each form includes the metafile in the result object as `result.metafile`:

```ts title="build.ts" icon="/icons/typescript.svg"
// Boolean — include metafile in the result object
// Boolean: include the metafile in the result object
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
metafile: true,
});

// String — write JSON metafile to a specific path
// String: also write the JSON metafile to ./dist/meta.json
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
metafile: "./dist/meta.json",
metafile: "meta.json",
});

// Object — specify separate paths for JSON and markdown output
// Object: also write the JSON metafile, the markdown metafile, or both
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
metafile: {
json: "./dist/meta.json",
markdown: "./dist/meta.md",
json: "meta.json", // ./dist/meta.json
markdown: "meta.md", // ./dist/meta.md
},
});
```

A relative path is relative to `outdir`. This differs from the CLI flags, whose paths are relative to the working directory.

Each path also adds an artifact to `result.outputs`, with a `kind` of `"metafile-json"` or `"metafile-markdown"`. A metafile is written only when there is an `outdir`. Without an `outdir`, read it from the artifact:

```ts title="build.ts" icon="/icons/typescript.svg"
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
metafile: { markdown: "meta.md" },
});

const report = result.outputs.find(output => output.kind === "metafile-markdown");
console.log(await report?.text());
```

A build that compiles an executable adds no metafile artifact. With an `outdir` it still writes the metafiles. Without one, read `result.metafile`: the markdown metafile is not available.

The metafile structure contains:

```ts
Expand Down Expand Up @@ -1637,11 +1653,11 @@ interface BuildOutput {
outputs: BuildArtifact[];
success: boolean;
logs: Array<object>; // see docs for details
metafile?: BuildMetafile; // only when metafile: true
metafile?: BuildMetafile; // only when the metafile option is set
}

interface BuildArtifact extends Blob {
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode";
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode" | "metafile-json" | "metafile-markdown";
path: string;
loader: Loader;
hash: string | null;
Expand All @@ -1663,13 +1679,13 @@ for (const output of build.outputs) {

Each artifact also contains the following properties:

| Property | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind` | What kind of build output this file is. A build generates bundled entrypoints, code-split "chunks", sourcemaps, bytecode, and copied assets (like images). |
| `path` | Absolute path to the file on disk |
| `loader` | The loader used to interpret the file. See [loaders](/bundler/loaders) for how Bun maps file extensions to built-in loaders. |
| `hash` | The hash of the file contents. Always defined for assets. |
| `sourcemap` | The sourcemap file corresponding to this file, if generated. Only defined for entrypoints and chunks. |
| Property | Description |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind` | What kind of build output this file is. A build generates bundled entrypoints, code-split "chunks", sourcemaps, bytecode, copied assets (like images), and the metafiles of a `metafile` path. |
| `path` | Absolute path to the file on disk |
| `loader` | The loader used to interpret the file. See [loaders](/bundler/loaders) for how Bun maps file extensions to built-in loaders. |
| `hash` | The hash of the file contents. Always defined for assets. |
| `sourcemap` | The sourcemap file corresponding to this file, if generated. Only defined for entrypoints and chunks. |

Similar to `BunFile`, `BuildArtifact` objects can be passed directly into `new Response()`.

Expand Down Expand Up @@ -2010,20 +2026,30 @@ interface BuildConfig {
*/
tsconfig?: string;

/**
* Generate a metafile that describes the build. `true` returns it as
* `BuildOutput.metafile`. A string or an object also gives the path of the
* JSON metafile and of the markdown metafile, relative to `outdir`.
*
* @default false
*/
metafile?: boolean | string | { json?: string; markdown?: string };

outdir?: string;
}

interface BuildOutput {
outputs: BuildArtifact[];
success: boolean;
logs: Array<BuildMessage | ResolveMessage>;
metafile?: BuildMetafile; // only when the metafile option is set
}

interface BuildArtifact extends Blob {
path: string;
loader: Loader;
hash: string | null;
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode";
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode" | "metafile-json" | "metafile-markdown";
sourcemap: BuildArtifact | null;
}

Expand All @@ -2034,6 +2060,7 @@ interface BuildOutput {
outputs: BuildArtifact[];
success: boolean;
logs: Array<BuildMessage | ResolveMessage>;
metafile?: BuildMetafile; // only when the metafile option is set
}

declare class ResolveMessage {
Expand Down
63 changes: 52 additions & 11 deletions packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3785,13 +3785,26 @@ declare module "bun" {
files?: Record<string, string | Blob | NodeJS.TypedArray | ArrayBufferLike>;

/**
* Generate a JSON file containing metadata about the build.
* Generate metadata about the build.
*
* The metafile contains information about inputs, outputs, imports, and exports
* which can be used for bundle analysis, visualization, or integration with
* other tools.
*
* When `true`, the metafile JSON string is included in the {@link BuildOutput.metafile} property.
* - `true`: the metafile is returned as the {@link BuildOutput.metafile} object
* - a string: also the path of the JSON metafile
* - an object: also the path of the JSON metafile, of a markdown report of
* the module graph, or of both
*
* A relative path is relative to {@link BuildConfig.outdir}. The build writes
* the file only when there is an `outdir`. Each path also adds a
* {@link BuildArtifact} of kind `"metafile-json"` or `"metafile-markdown"` to
* {@link BuildOutput.outputs} (except when `compile` makes an executable).
* Without an `outdir`, nothing is written and the artifact holds the content.
* An empty path is the same as no path.
*
* The `bun build --metafile=<path>` and `--metafile-md=<path>` CLI flags are
* different: their paths are relative to the working directory.
*
* @default false
*
Expand All @@ -3803,18 +3816,42 @@ declare module "bun" {
* metafile: true,
* });
*
* // Write metafile to disk for analysis
* if (result.metafile) {
* await Bun.write('./dist/meta.json', result.metafile);
* console.log('Input files:', Object.keys(result.metafile.inputs));
* console.log('Output files:', Object.keys(result.metafile.outputs));
* }
* ```
*
* @example
* ```ts
* // Writes ./dist/meta.json and ./dist/meta.md
* await Bun.build({
* entrypoints: ['./src/index.ts'],
* outdir: './dist',
* metafile: { json: 'meta.json', markdown: 'meta.md' },
* });
* ```
*
* // Parse and analyze the metafile
* const meta = JSON.parse(result.metafile!);
* console.log('Input files:', Object.keys(meta.inputs));
* console.log('Output files:', Object.keys(meta.outputs));
* @example
* ```ts
* // No outdir: the build writes nothing, the report is an artifact
* const result = await Bun.build({
* entrypoints: ['./src/index.ts'],
* metafile: { markdown: 'meta.md' },
* });
* const report = result.outputs.find(output => output.kind === 'metafile-markdown');
* console.log(await report?.text());
* ```
*/
metafile?: boolean;
metafile?:
| boolean
| string
Comment thread
claude[bot] marked this conversation as resolved.
Resolved
| {
/** Path of the JSON metafile. */
json?: string;
/** Path of a markdown report of the module graph. */
markdown?: string;
};

outdir?: string;

Expand Down Expand Up @@ -4672,7 +4709,11 @@ declare module "bun" {
path: string;
loader: Loader;
hash: string | null;
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode";
/**
* `"metafile-json"` and `"metafile-markdown"` are the files of a
* {@link BuildConfig.metafile} path.
*/
kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode" | "metafile-json" | "metafile-markdown";
sourcemap: BuildArtifact | null;
}

Expand All @@ -4691,7 +4732,7 @@ declare module "bun" {
* - **outputs**: every generated file with its byte size, the inputs that
* contributed to it, imports between chunks, and exports
*
* Only present when {@link BuildConfig.metafile} is `true`.
* Only present when {@link BuildConfig.metafile} is `true`, a string, or an object.
*
* Use it for bundle size analysis, inspecting the dependency graph, or as
* input to bundle analyzer tools.
Expand Down
6 changes: 5 additions & 1 deletion src/bundler/aot/metafile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ pub fn write_metafile_files(json: &[u8], files: &MetafileFiles<'_>) -> Option<Bo
}
},
};
let directory = files.output_directory;
// Without an output directory no file is written, and the outputs below would hold copies that nothing reads.
if directory.is_empty() {
return markdown;
}
// An executable is the only artifact of its build, as without `--aot`.
let mut artifacts = Vec::new();
let directory = files.output_directory;
if !files.json_path.is_empty() {
let kind = OutputKind::MetafileJson;
let _ = write_metafile_output(&mut artifacts, directory, files.json_path, json, kind);
Expand Down
9 changes: 8 additions & 1 deletion src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5677,7 +5677,14 @@ pub mod bv2_impl {
b"metafile.md".as_slice()
}),
output_path: Box::<[u8]>::from(file_path),
data: crate::output_file::OptionsData::Saved(content.len()),
// With no outdir the build writes nothing, so the artifact holds the bytes.
data: if outdir.is_empty() {
crate::output_file::OptionsData::Buffer {
data: Box::<[u8]>::from(content),
}
} else {
crate::output_file::OptionsData::Saved(content.len())
},
Comment thread
robobun marked this conversation as resolved.
Resolved
output_kind,
is_executable: false,
side: None,
Expand Down
Loading
Loading