Files
bun.sh/test/regression/issue/22003.test.ts
robobun 5fca74a979 fix(sourcemap): escape tab characters in filenames for JSON (#23298)
## What does this PR do?

Fixes #22003 by escaping tab characters in filenames when generating
sourcemap JSON.

When a filename contained a tab character (e.g., `file\ttab.js`), the
sourcemap JSON would contain a **literal tab byte** instead of the
escaped `\t`, producing invalid JSON that caused `error:
InvalidSourceMap`.

The root cause was in `src/bun.js/bindings/highway_strings.cpp` where
the scalar fallback path had:
```cpp
if (char_ >= 127 || (char_ < 0x20 && char_ != 0x09) || ...)
```

This **exempted tab characters** (0x09) from being detected as needing
escape, while the SIMD path correctly detected them. The fix removes the
`&& char_ != 0x09` exemption so both paths consistently escape tabs.

## How did you verify your code works?

Added regression test in `test/regression/issue/22003.test.ts` that:
- Creates a file with a tab character in its filename
- Builds it with sourcemap generation
- Verifies the sourcemap is valid JSON
- Checks that the tab is escaped as `\t` (not a literal byte)

The test **fails on system bun** (produces invalid JSON with literal
tab):
```bash
USE_SYSTEM_BUN=1 bun test test/regression/issue/22003.test.ts
# error: JSON Parse error: Unterminated string
```

The test **passes with the fix** (tab properly escaped):
```bash
bun bd test test/regression/issue/22003.test.ts
# ✓ 1 pass
```

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 19:28:48 -07:00

38 lines
1.2 KiB
TypeScript

import { expect, test } from "bun:test";
import { bunEnv, bunExe, isWindows, tempDir } from "harness";
// https://github.com/oven-sh/bun/issues/22003
test.skipIf(isWindows)("tab character in filename should be escaped in sourcemap JSON", async () => {
using dir = tempDir("22003", {
// Filename with tab character
"file\ttab.js": "module.exports = 42;",
});
await using proc = Bun.spawn({
cmd: [bunExe(), "build", "file\ttab.js", "--outfile=out.js", "--sourcemap"],
env: bunEnv,
cwd: String(dir),
stderr: "pipe",
});
const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]);
expect(exitCode).toBe(0);
expect(stderr).not.toContain("InvalidSourceMap");
const sourcemapContent = await Bun.file(`${dir}/out.js.map`).text();
// Must be valid JSON (system bun would produce invalid JSON with literal tab)
let sourcemap;
expect(() => {
sourcemap = JSON.parse(sourcemapContent);
}).not.toThrow();
// The filename in sources should have the tab properly escaped
expect(sourcemap.sources).toContain("file\ttab.js");
// Verify no literal tab bytes (0x09) in the raw JSON
const hasLiteralTab = sourcemapContent.includes("\t");
expect(hasLiteralTab).toBe(false);
});