bun-sourcemap
v0.0.2
Published
Language-neutral Bun.build source-map composition for compilers producing JS, JSX, TS, or TSX
Maintainers
Readme
bun-sourcemap
Compose compiler source maps with Bun's bundle source maps.
When a language compiler turns a file into JavaScript, JSX, TypeScript, or TSX before Bun.build() bundles it, Bun's output source map may point to the generated intermediate code instead of your original file. bun-sourcemap chains the compiler's map with Bun's map so debuggers can show the original source and its locations.
source.lang --(your compiler + source map)--> generated.ts
|
Bun.build()
|
bundle.js + Bun map
|
bun-sourcemap
|
bundle.js.map -> source.langFeatures
- Language independent: works with any compiler that returns JS, JSX, TS, or TSX and a Source Map v3.
- Automatic map composition: combines compiler mappings with Bun's output map through the Bun plugin
onEndhook. - Two integration styles: provide compiler transforms directly, or register maps from an existing Bun
onLoadplugin. - Multiple languages per build: associate different file extensions with different compilers.
- Preserves other modules: JavaScript/TypeScript modules without an upstream compiler map retain their Bun mappings.
- Handles common build options: linked or external source maps, minification, and multiple entrypoints.
- No runtime dependencies.
The compiler is responsible for producing the intermediate code and an accurate map back to its original input; this package composes those maps rather than inventing mappings.
Install
bun add -d bun-sourcemapUse with a compiler
Create a compiler adapter using sourceMapChain():
import { sourceMapChain } from 'bun-sourcemap';
import { compile } from 'your-language-compiler'; // replace with your compiler
const plugin = sourceMapChain({
transforms: [
{
filter: /\.lang$/,
loader: 'ts',
async transform({ path, source }) {
const result = await compile(source, {
filename: path,
sourceMap: true,
});
return {
contents: result.code,
map: result.map, // Source Map v3 object or JSON string
intermediatePath: `${path}.ts`,
};
},
},
],
});
const result = await Bun.build({
entrypoints: ['src/main.lang'],
outdir: 'dist',
sourcemap: 'linked', // or 'external'
plugins: [plugin],
});
if (!result.success) throw new Error('Build failed');The output's .js.map now resolves mapped positions to the source files named by your compiler's map, rather than the intermediate TypeScript. For the best debugging experience, ensure the compiler includes sourcesContent with the original source text.
Inline compiler source maps
If the compiler embeds a base64 sourceMappingURL comment in its generated code, return just contents; bun-sourcemap extracts the map:
return {
contents: result.codeWithInlineSourceMap,
};An explicit map takes precedence over an inline map. For output in a different virtual directory, set intermediatePath to the absolute filename used to resolve relative paths in map.sources.
Compiler adapter options
| Option | Description |
| --- | --- |
| filter | Regular expression matching input files (without g or y flags). |
| loader | Bun loader for generated code: 'js', 'jsx', 'ts', or 'tsx'. |
| transform({ path, source, args }) | Returns the generated code and its source map; may be async. |
| namespace | Optional Bun namespace; current map chaining supports filesystem (file) inputs. |
A transform returns { contents, map?, loader?, intermediatePath?, resolveDir? }. contents is required, and a map must be available either explicitly or inline. To handle multiple languages, add more entries to transforms.
Use with an existing Bun plugin
If your compiler already uses build.onLoad(), it can register its source maps without a second loader:
import { createSourceMapChain } from 'bun-sourcemap';
import { compile } from 'your-language-compiler'; // replace with your compiler
const chain = createSourceMapChain();
const languagePlugin = {
name: 'my-language',
setup(build) {
build.onLoad({ filter: /\.lang$/ }, async ({ path }) => {
const source = await Bun.file(path).text();
const result = await compile(source, {
filename: path,
sourceMap: true,
});
chain.register({
path,
map: result.map,
originalSource: source,
intermediatePath: `${path}.ts`,
});
return { contents: result.code, loader: 'ts' };
});
},
};
await Bun.build({
entrypoints: ['src/main.lang'],
outdir: 'dist',
sourcemap: 'linked',
plugins: [languagePlugin, chain.plugin],
});chain.register() accepts { path, map?, contents?, originalSource?, intermediatePath? }. Supply map, or use contents containing an inline map. originalSource lets the package fill missing sourcesContent when the compiler's map identifies the input source file.
This is an explicit opt-in API: Bun does not automatically expose other plugins' compilation maps to bun-sourcemap.
Requirements and limitations
- Works with
Bun.build(), including development servers that rebuild withBun.build(); does not hook intoBun.plugin()runtime loading or Bun's separate native HTML/HMR pipeline. - Use
sourcemap: 'linked'or'external'and write generated outputs to disk (write !== false). - Source inputs must be filesystem-backed modules in Bun's
filenamespace, and generated code must use a supported JS-family loader. - Compiler maps must be flat Source Map v3 objects (or JSON strings), or embedded base64 inline maps. External compiler map URLs and indexed/section maps are not currently supported.
- Each Bun input supports one upstream map. If a compiler has multiple stages, compose those stages first before handing the map to
bun-sourcemap. - Unmapped positions in a compiler or Bun source map remain unmapped; the plugin does not fabricate locations.
- Use a separate chain instance for concurrent builds. A chain instance can be reused for sequential builds.
License
MIT
