bun
bun copied to clipboard
Sourcemap files sometimes malformed, mostly empty (67 bytes) when using `Bun.build` with `sourcemap: 'external'`
What version of Bun is running?
1.1.9+bb13798d9
What platform is your computer?
Darwin 23.4.0 arm64 arm
What steps can reproduce the bug?
- Bundle app with
Bun.build
and the following configuration object:
{
define: {},
entrypoints: ['./src/index.mts'],
external: [],
format: 'esm',
loader: {},
minify: true,
naming: {
chunk: '[name].[hash].mjs',
entry: '[name].mjs',
},
outdir: './dist',
plugins: [],
publicPath: '',
root: '.',
sourcemap: 'external',
splitting: true,
target: 'bun',
}
- Examine contents of
outdir
(dist/
in this case). Notice how some.map
files are malformed and nearly empty. I'm seeing those files with filesize of 67 bytes.
What is the expected behavior?
Sourcemap files should contain source code
What do you see instead?
Malformed JSON at the start of the file. For example, here are the contents from a sourcemap from a route handler (67 bytes):
",
"debugId": "E5F236DC4A50165064756e2164756e21",
"names": []
}
[!NOTE] All the 67 byte files are malformed the same way and have these keys and shape. They look identical except for the
debugId
value.
Additional information
Here is the full output from a build run so you can see all the different files, types, and sizes (notice multiple 67 byte sourcemap files):
egoldstein@MacCWQXVG6G0V get.apigateway.lambda $ bun run build
$ bun run --silent delete:build-artifacts && bun run --silent build:bundle
Building application artifacts for production...
1) aws4fetch.esm.aac95038a0cfb70e.mjs 7.19 KiB [C]
2) aws4fetch.esm.aac95038a0cfb70e.mjs.map 11.05 KiB [S]
3) error.826f137d89aa8666.mjs 1.95 KiB [C]
4) error.826f137d89aa8666.mjs.map 872 B [S]
5) index.04c119a3beb16a35.mjs 445 B [C]
6) index.04c119a3beb16a35.mjs.map 144 B [S]
7) index.47ac3ed1118934d2.mjs 1.41 KiB [C]
8) index.47ac3ed1118934d2.mjs.map 3.78 KiB [S]
9) index.8b306d478767a477.mjs 331 B [C]
10) index.8b306d478767a477.mjs.map 11.13 KiB [S]
11) index.cc647d1f6d9b8e22.mjs 120.33 KiB [C]
12) index.cc647d1f6d9b8e22.mjs.map 67 B [S]
13) index.mjs 4.19 KiB [E]
14) index.mjs.map 37.18 KiB [S]
15) pageRoute.fbb963aa51542254.mjs 1.2 KiB [C]
16) pageRoute.fbb963aa51542254.mjs.map 67 B [S]
17) precacheRoute.e5f236dc4a501650.mjs 3.03 KiB [C]
18) precacheRoute.e5f236dc4a501650.mjs.map 67 B [S]
8 Chunks 135.87 KiB
1 Entry point 4.19 KiB
9 Sourcemaps 64.32 KiB
TOTAL SIZE 204.38 KiB
Build success [8.62ms]
Rightmost column legend:
[C]
for chunk
[E]
for entry point
[S]
for sourcemap
FYI the 144 byte sourcemap isn't malformed but contains no useful sourcemap info:
{
"version": 3,
"sources": [],
"sourcesContent": [
],
"mappings": "",
"debugId": "04C119A3BEB16A3564756e2164756e21",
"names": []
}
The 872 byte sourcemap contains malformed text:
UAAa,EACpB,wBACA,eACA,cAAc,GACd,eAAe,QACf,mBAAmB,UACnB,OACA,SACA,SACqB,CACrB,MAAM,EAAgC,OAAO,QAAQ,GAAyB,CAAC,CAAC,EAC7E,IAAI,EAAE,EAAU,KAAS,mCAAmC,YAAmB,OAAS,EACxF,KAAK,IAAI,EACN,EAAuB,EAAe,+BAA+B,MAAmB,GACxF,EAAoB,EAAS,gCAAgC,QAAe,GAElF,MAAO;AAAA,gBACO;AAAA;AAAA,eAED;AAAA,0CAC2B;AAAA,iDACO;AAAA,QACzC;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cASM,GAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACvCtB,IAAS,UAAgB,CACvB,EACA,EACA,EAAkB,QAClB,EAAa,IACb,CACA,GAAI,EACF,EAAO,GAAU,EAAc,CAAO,MAEtC,GAAO,GAAU,CAAY,EAE/B,OAAO,EAAsB,EAAc,CAAU,GAG9C,UAAoB,CAC3B,EACA,EACA,EAAkB,QAClB,EAAa,IACb,CACA,GAAI,EACF,EAAO,GAAU,EAAc,CAAO,MAEtC,GAAO,GAAU,CAAY,EAE/B,OAAO,EAAsB,EAAc,CAAE,MAAO,CAAa,CAAC,EAAG,CAAU,GAGxE,UAAkB,CACzB,EACA,EACA,EAAkB,QAClB,CACA,OAAO,EAAiB,EAAc,EAAS,EAAU,GAAG",
"debugId": "826F137D89AA866664756e2164756e21",
"names": []
}
The 11.13 KiB sourcemap contains the same source code twice:
{
"version": 3,
"sources": ["../node_modules/@mangs/bun-utils/src/timeUtils.mts", "../node_modules/@mangs/bun-utils/src/timeUtils.mts"],
"sourcesContent": [
"/**\n * @file Time-related utilities.\n */\n\n// External Imports\nimport { nanoseconds } from 'bun';\n\n// Local Variables\nconst timeUnits = ['ns', 'μs', 'ms', 's'] as const;\n\n// Local Types\ntype TimeUnits = (typeof timeUnits)[number];\ninterface FormatOptions {\n /**\n * Override of the locale used to format and localize the time value.\n */\n localeOverride?: string;\n /**\n * Smallest time unit that can be displayed.\n */\n unitsMinimum?: TimeUnits;\n /**\n * Override of time units to display; supersedes `unitsMinimum`.\n */\n unitsOverride?: TimeUnits;\n}\n\n// Local Functions\n/**\n * Build a `Server-Timing` header to measure a performance metric using the provided values.\n * @param name The name of the performance metric.\n * @param startTime The recorded start time used to compute the metric duration; computed by subtracting the time at which this function is called by the start time. [Milliseconds is the unit recommended by the W3C](https://w3c.github.io/server-timing/#duration-attribute).\n * @param description A description of the metric.\n * @returns A `Server-Timing` header tuple: [`'Server-Timing'`, `string`].\n * @example\n * ```ts\n * import { buildServerTimingHeader } from '@mangs/bun-utils/time';\n *\n * const startTime = performance.now();\n * // sometime later...\n * request.headers.append(...buildServerTimingHeader('metric', startTime, 'It measures everything'));\n * ```\n */\nfunction buildServerTimingHeader(name: string, startTime?: number, description?: string) {\n const durationFormatted =\n typeof startTime === 'number' ? `;dur=${(performance.now() - startTime).toFixed(2)}` : '';\n const descriptionFormatted = description ? `;desc=\"${description}\"` : '';\n return [`Server-Timing`, `${name}${durationFormatted}${descriptionFormatted}`] as const;\n}\n\n/**\n * Get a formatted string representing the time between the provided start time parameter and the\n * time the function is called. An optional options object can be provided to customize formatting.\n * @param startTime Start time calculated by `Bun.nanoseconds()`.\n * @param formatOptions Options object for formatting customization.\n * @returns Localized string showing elapsed time with units.\n */\nfunction getElapsedTimeFormatted(startTime: number, formatOptions?: FormatOptions) {\n const { localeOverride, unitsMinimum = 'ms', unitsOverride } = formatOptions ?? {};\n\n const endTime = nanoseconds();\n let elapsedTime = endTime - startTime;\n let timeIndex = 0;\n\n if (unitsOverride) {\n const exactIndex = timeUnits.indexOf(unitsOverride);\n while (timeIndex < exactIndex) {\n elapsedTime /= 1_000;\n timeIndex += 1;\n }\n } else {\n let isMinimumUnit = false;\n while (elapsedTime > 1) {\n if (timeUnits[timeIndex] === unitsMinimum) {\n isMinimumUnit = true;\n }\n if (isMinimumUnit && elapsedTime <= 1_000) {\n break;\n }\n elapsedTime /= 1_000;\n timeIndex += 1;\n }\n }\n\n const elapsedTimeLocalized = elapsedTime.toLocaleString(localeOverride, {\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n });\n const units = timeUnits[timeIndex];\n return `${elapsedTimeLocalized}${units}`;\n}\n\n/**\n * Measure the execution time of the passed-in function.\n * @param runner Function whose execution duration will be measured.\n * @returns Object containing the elapsed time and the return value of the passed-in function.\n */\nasync function measureElapsedTime<T>(runner: () => T | Promise<T>) {\n const startTime = nanoseconds();\n const returnValue = await runner();\n const elapsedTime = getElapsedTimeFormatted(startTime);\n return { elapsedTime, returnValue };\n}\n\n/**\n * Measure the execution time of the passed-in function, then append to the request object a\n * `Server-Timing` header containing the specified metric name, the measured duration, and\n * optionally the metric description.\n * @param metricName Name of the `Server-Timing` metric being measured.\n * @param request `Request` object to which the `Server-Timing` header will be appended.\n * @param runner Function whose execution duration will be measured.\n * @param metricDescription Optional description of the `Server-Timing` metric being measured.\n * @returns The return value of the passed-in function.\n * @example\n * ```ts\n * import { measureServerTiming } from '@mangs/bun-utils/time';\n *\n * const cmsContent = await measureServerTiming('cmsLoad', request, () =>\n * getCmsContent('article1'),\n * );\n * ```\n */\nasync function measureServerTiming<T>(\n metricName: string,\n request: Request,\n runner: () => T | Promise<T>,\n metricDescription?: string,\n) {\n const startTime = performance.now();\n const value = await runner();\n request.headers.append(...buildServerTimingHeader(metricName, startTime, metricDescription));\n return value;\n}\n\n/**\n * Asynchronous sleep function using promises.\n * @param duration Length of time to sleep.\n * @returns `Promise` that resolves when the specified duration expires.\n */\nfunction sleep(duration: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, duration);\n });\n}\n\n// Module Exports\nexport {\n buildServerTimingHeader,\n getElapsedTimeFormatted,\n measureElapsedTime,\n measureServerTiming,\n sleep,\n};\nexport type { FormatOptions };\n",
"/**\n * @file Time-related utilities.\n */\n\n// External Imports\nimport { nanoseconds } from 'bun';\n\n// Local Variables\nconst timeUnits = ['ns', 'μs', 'ms', 's'] as const;\n\n// Local Types\ntype TimeUnits = (typeof timeUnits)[number];\ninterface FormatOptions {\n /**\n * Override of the locale used to format and localize the time value.\n */\n localeOverride?: string;\n /**\n * Smallest time unit that can be displayed.\n */\n unitsMinimum?: TimeUnits;\n /**\n * Override of time units to display; supersedes `unitsMinimum`.\n */\n unitsOverride?: TimeUnits;\n}\n\n// Local Functions\n/**\n * Build a `Server-Timing` header to measure a performance metric using the provided values.\n * @param name The name of the performance metric.\n * @param startTime The recorded start time used to compute the metric duration; computed by subtracting the time at which this function is called by the start time. [Milliseconds is the unit recommended by the W3C](https://w3c.github.io/server-timing/#duration-attribute).\n * @param description A description of the metric.\n * @returns A `Server-Timing` header tuple: [`'Server-Timing'`, `string`].\n * @example\n * ```ts\n * import { buildServerTimingHeader } from '@mangs/bun-utils/time';\n *\n * const startTime = performance.now();\n * // sometime later...\n * request.headers.append(...buildServerTimingHeader('metric', startTime, 'It measures everything'));\n * ```\n */\nfunction buildServerTimingHeader(name: string, startTime?: number, description?: string) {\n const durationFormatted =\n typeof startTime === 'number' ? `;dur=${(performance.now() - startTime).toFixed(2)}` : '';\n const descriptionFormatted = description ? `;desc=\"${description}\"` : '';\n return [`Server-Timing`, `${name}${durationFormatted}${descriptionFormatted}`] as const;\n}\n\n/**\n * Get a formatted string representing the time between the provided start time parameter and the\n * time the function is called. An optional options object can be provided to customize formatting.\n * @param startTime Start time calculated by `Bun.nanoseconds()`.\n * @param formatOptions Options object for formatting customization.\n * @returns Localized string showing elapsed time with units.\n */\nfunction getElapsedTimeFormatted(startTime: number, formatOptions?: FormatOptions) {\n const { localeOverride, unitsMinimum = 'ms', unitsOverride } = formatOptions ?? {};\n\n const endTime = nanoseconds();\n let elapsedTime = endTime - startTime;\n let timeIndex = 0;\n\n if (unitsOverride) {\n const exactIndex = timeUnits.indexOf(unitsOverride);\n while (timeIndex < exactIndex) {\n elapsedTime /= 1_000;\n timeIndex += 1;\n }\n } else {\n let isMinimumUnit = false;\n while (elapsedTime > 1) {\n if (timeUnits[timeIndex] === unitsMinimum) {\n isMinimumUnit = true;\n }\n if (isMinimumUnit && elapsedTime <= 1_000) {\n break;\n }\n elapsedTime /= 1_000;\n timeIndex += 1;\n }\n }\n\n const elapsedTimeLocalized = elapsedTime.toLocaleString(localeOverride, {\n maximumFractionDigits: 2,\n minimumFractionDigits: 2,\n });\n const units = timeUnits[timeIndex];\n return `${elapsedTimeLocalized}${units}`;\n}\n\n/**\n * Measure the execution time of the passed-in function.\n * @param runner Function whose execution duration will be measured.\n * @returns Object containing the elapsed time and the return value of the passed-in function.\n */\nasync function measureElapsedTime<T>(runner: () => T | Promise<T>) {\n const startTime = nanoseconds();\n const returnValue = await runner();\n const elapsedTime = getElapsedTimeFormatted(startTime);\n return { elapsedTime, returnValue };\n}\n\n/**\n * Measure the execution time of the passed-in function, then append to the request object a\n * `Server-Timing` header containing the specified metric name, the measured duration, and\n * optionally the metric description.\n * @param metricName Name of the `Server-Timing` metric being measured.\n * @param request `Request` object to which the `Server-Timing` header will be appended.\n * @param runner Function whose execution duration will be measured.\n * @param metricDescription Optional description of the `Server-Timing` metric being measured.\n * @returns The return value of the passed-in function.\n * @example\n * ```ts\n * import { measureServerTiming } from '@mangs/bun-utils/time';\n *\n * const cmsContent = await measureServerTiming('cmsLoad', request, () =>\n * getCmsContent('article1'),\n * );\n * ```\n */\nasync function measureServerTiming<T>(\n metricName: string,\n request: Request,\n runner: () => T | Promise<T>,\n metricDescription?: string,\n) {\n const startTime = performance.now();\n const value = await runner();\n request.headers.append(...buildServerTimingHeader(metricName, startTime, metricDescription));\n return value;\n}\n\n/**\n * Asynchronous sleep function using promises.\n * @param duration Length of time to sleep.\n * @returns `Promise` that resolves when the specified duration expires.\n */\nfunction sleep(duration: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, duration);\n });\n}\n\n// Module Exports\nexport {\n buildServerTimingHeader,\n getElapsedTimeFormatted,\n measureElapsedTime,\n measureServerTiming,\n sleep,\n};\nexport type { FormatOptions };\n"
],
"mappings": "AA2CA,IAAS,UAAuB,CAAC,EAAc,EAAoB,EAAsB,CACvF,MAAM,SACG,IAAc,SAAW,SAAS,YAAY,IAAI,EAAI,GAAW,QAAQ,CAAC,IAAM,GACnF,EAAuB,EAAc,UAAU,KAAiB,GACtE,MAAO,CAAC,gBAAiB,GAAG,IAAO,IAAoB,GAAsB",
"debugId": "8B306D478767A47764756e2164756e21",
"names": []
}
[!NOTE] The
aws4fetch
sourcemap appears to be the only one without any issues (1 out of 9).