diff --git a/src/HTMLScanner.zig b/src/HTMLScanner.zig index c6462882f9..dd69942977 100644 --- a/src/HTMLScanner.zig +++ b/src/HTMLScanner.zig @@ -28,12 +28,20 @@ pub fn deinit(this: *HTMLScanner) void { this.import_records.deinitWithAllocator(this.allocator); } -fn createImportRecord(this: *HTMLScanner, path: []const u8, kind: ImportKind) !void { +fn createImportRecord(this: *HTMLScanner, input_path: []const u8, kind: ImportKind) !void { + // In HTML, sometimes people do /src/index.js + // In that case, we don't want to use the absolute filesystem path, we want to use the path relative to the project root + const path_to_use = if (input_path.len > 1 and input_path[0] == '/') + bun.path.joinAbsString(bun.fs.FileSystem.instance.top_level_dir, &[_][]const u8{input_path[1..]}, .auto) + else + input_path; + const record = ImportRecord{ - .path = fs.Path.init(try this.allocator.dupe(u8, path)), + .path = fs.Path.init(try this.allocator.dupeZ(u8, path_to_use)), .kind = kind, .range = logger.Range.None, }; + try this.import_records.push(this.allocator, record); } diff --git a/test/bundler/bundler_html.test.ts b/test/bundler/bundler_html.test.ts index 29ac02c90f..9778ee55ea 100644 --- a/test/bundler/bundler_html.test.ts +++ b/test/bundler/bundler_html.test.ts @@ -721,4 +721,60 @@ body { expect(cssBundle).toContain("box-sizing: border-box"); }, }); + + // Test absolute paths in HTML + itBundled("html/absolute-paths", { + outdir: "out/", + files: { + "/index.html": ` + + + + + + + +

Absolute Paths

+ + +`, + "/styles/main.css": "body { margin: 0; }", + "/scripts/app.js": "console.log('App loaded')", + "/images/logo.png": "fake image content", + }, + experimentalHtml: true, + experimentalCss: true, + entryPoints: ["/index.html"], + onAfterBundle(api) { + // Check that absolute paths are handled correctly + const htmlBundle = api.readFile("out/index.html"); + + // CSS should be bundled and hashed + api.expectFile("out/index.html").not.toContain("/styles/main.css"); + api.expectFile("out/index.html").toMatch(/href=".*\.css"/); + + // JS should be bundled and hashed + api.expectFile("out/index.html").not.toContain("/scripts/app.js"); + api.expectFile("out/index.html").toMatch(/src=".*\.js"/); + + // Image should be hashed + api.expectFile("out/index.html").not.toContain("/images/logo.png"); + api.expectFile("out/index.html").toMatch(/src=".*\.png"/); + + // Get the bundled files and verify their contents + const cssMatch = htmlBundle.match(/href="(.*\.css)"/); + const jsMatch = htmlBundle.match(/src="(.*\.js)"/); + const imgMatch = htmlBundle.match(/src="(.*\.png)"/); + + expect(cssMatch).not.toBeNull(); + expect(jsMatch).not.toBeNull(); + expect(imgMatch).not.toBeNull(); + + const cssBundle = api.readFile("out/" + cssMatch![1]); + const jsBundle = api.readFile("out/" + jsMatch![1]); + + expect(cssBundle).toContain("margin: 0"); + expect(jsBundle).toContain("App loaded"); + }, + }); });