Skip to content

Commit d1eeaa0

Browse files
dylan-conwayclaude
andcommitted
fix(bundler): escape backslashes in metafile paths and memoize getter
Two fixes for the metafile feature: 1. Properly JSON-escape paths in MetafileBuilder to handle Windows backslashes (e.g., C:\Users\... becomes C:\Users\...). Without this, JSON.parse fails with "Invalid escape character U". 2. Memoize the lazy metafile getter by replacing it with the parsed value after first access. This prevents assertion failures when the metafile property is accessed multiple times. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 9aa9d21 commit d1eeaa0

File tree

2 files changed

+12
-10
lines changed

2 files changed

+12
-10
lines changed

src/bun.js/bindings/BundlerMetafile.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ JSC_DEFINE_CUSTOM_GETTER(bundlerMetafileLazyGetter, (JSGlobalObject * globalObje
3939
JSValue parsedValue = JSC::JSONParseWithException(globalObject, view);
4040
RETURN_IF_EXCEPTION(scope, {});
4141

42+
// Replace the lazy getter with the parsed value (memoize for subsequent accesses)
43+
thisObject->putDirect(vm, property, parsedValue, 0);
44+
4245
// Clear the raw JSON string so it can be GC'd
4346
thisObject->putDirect(vm, privateName, jsUndefined(), 0);
4447

src/bundler/linker_context/MetafileBuilder.zig

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ pub fn generate(
214214
}
215215
first_input = false;
216216

217-
j.pushStatic("\n \"");
218-
j.pushStatic(path);
219-
j.push(std.fmt.allocPrint(allocator, "\": {{\n \"bytes\": {d}", .{source.contents.len}) catch return error.OutOfMemory, allocator);
217+
j.pushStatic("\n ");
218+
j.push(try std.fmt.allocPrint(allocator, "{f}", .{bun.fmt.formatJSONStringUTF8(path, .{})}), allocator);
219+
j.push(try std.fmt.allocPrint(allocator, ": {{\n \"bytes\": {d}", .{source.contents.len}), allocator);
220220

221221
// Write imports
222222
j.pushStatic(",\n \"imports\": [");
@@ -231,19 +231,18 @@ pub fn generate(
231231
}
232232
first_import = false;
233233

234-
j.pushStatic("\n {\n \"path\": \"");
235-
// Write path as-is - chunk references (unique_keys) will be resolved
234+
j.pushStatic("\n {\n \"path\": ");
235+
// Write path with JSON escaping - chunk references (unique_keys) will be resolved
236236
// by breakOutputIntoPieces and code() below
237-
j.pushStatic(record.path.text);
238-
j.pushStatic("\",\n \"kind\": \"");
237+
j.push(try std.fmt.allocPrint(allocator, "{f}", .{bun.fmt.formatJSONStringUTF8(record.path.text, .{})}), allocator);
238+
j.pushStatic(",\n \"kind\": \"");
239239
j.pushStatic(record.kind.label());
240240
j.pushStatic("\"");
241241

242242
// Add "original" field if different from path
243243
if (record.original_path.len > 0 and !std.mem.eql(u8, record.original_path, record.path.text)) {
244-
j.pushStatic(",\n \"original\": \"");
245-
j.pushStatic(record.original_path);
246-
j.pushStatic("\"");
244+
j.pushStatic(",\n \"original\": ");
245+
j.push(try std.fmt.allocPrint(allocator, "{f}", .{bun.fmt.formatJSONStringUTF8(record.original_path, .{})}), allocator);
247246
}
248247

249248
// Add "external": true for external imports

0 commit comments

Comments
 (0)