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": ` + + +
+ + + + +
+
+`,
+ "/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");
+ },
+ });
});