This commit is contained in:
agra
2026-03-04 17:12:56 +02:00
parent 67e02a20a5
commit 22bc2439ce
7 changed files with 217 additions and 18 deletions

View File

@@ -270,6 +270,74 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
if (result.exited != 0) return error.LinkError;
}
/// After emcc produces HTML output, inject cache-busting hashes into the
/// generated <script> tag and add Module.locateFile for .wasm/.data files.
pub fn postProcessWasmHtml(allocator: std.mem.Allocator, io: std.Io, html_path: []const u8) void {
const base = if (std.mem.endsWith(u8, html_path, ".html"))
html_path[0 .. html_path.len - 5]
else
return;
// Hash build output contents (.js + .wasm + optional .data)
var hash: u64 = 0;
const exts = [_][]const u8{ ".js", ".wasm", ".data" };
for (exts) |ext| {
const path = std.fmt.allocPrint(allocator, "{s}{s}", .{ base, ext }) catch continue;
if (std.Io.Dir.readFileAlloc(.cwd(), io, path, allocator, .limited(64 * 1024 * 1024))) |data| {
hash = std.hash.Wyhash.hash(hash, data);
} else |_| {}
}
const hash_hex = std.fmt.allocPrint(allocator, "{x:0>8}", .{@as(u32, @truncate(hash))}) catch return;
// Read the final HTML produced by emcc
const html = std.Io.Dir.readFileAlloc(.cwd(), io, html_path, allocator, .limited(10 * 1024 * 1024)) catch return;
var out = std.ArrayList(u8).empty;
var pos: usize = 0;
var injected_locateFile = false;
// Find emcc's generated script tag: <script ...src="*.js"></script>
// Inject ?v=HASH into the src and prepend a Module.locateFile script.
while (std.mem.indexOfPos(u8, html, pos, "src=\"")) |src_start| {
const val_start = src_start + 5; // past src="
const val_end = std.mem.indexOfPos(u8, html, val_start, "\"") orelse break;
const src_val = html[val_start..val_end];
if (!std.mem.endsWith(u8, src_val, ".js")) {
// Not a .js src — skip past this attribute and keep searching
pos = val_end + 1;
continue;
}
// Find the opening < of this tag to inject locateFile before it
const tag_start = if (std.mem.lastIndexOf(u8, html[pos..src_start], "<")) |off| pos + off else src_start;
// Copy everything up to the tag start
out.appendSlice(allocator, html[pos..tag_start]) catch return;
// Inject Module.locateFile once, before the first .js script tag
if (!injected_locateFile) {
out.appendSlice(allocator, "<script>Module.locateFile=function(p){return p+'?v=") catch return;
out.appendSlice(allocator, hash_hex) catch return;
out.appendSlice(allocator, "'}</script>\n") catch return;
injected_locateFile = true;
}
// Copy tag up to the closing quote of src, inserting ?v=HASH
out.appendSlice(allocator, html[tag_start..val_end]) catch return;
out.appendSlice(allocator, "?v=") catch return;
out.appendSlice(allocator, hash_hex) catch return;
pos = val_end;
}
// Copy remaining HTML
out.appendSlice(allocator, html[pos..]) catch return;
const final = out.toOwnedSlice(allocator) catch return;
std.Io.Dir.writeFile(.cwd(), io, .{ .sub_path = html_path, .data = final }) catch {};
}
/// Common library paths for the host OS, computed at comptime.
pub const host_lib_paths = blk: {
const builtin = @import("builtin");