Complete working code flow visualizer with real output

The visualizer now fully works with:
- Actual source code snippets from input files (500 chars)
- Real JavaScript output from bundled chunks (1000 chars)
- New 'after_generation' stage that captures output after compilation
- HTML displays real source and output side-by-side
- Both visualizers generated: graph and code flow

To use:
1. Set BUN_BUNDLER_GRAPH_DUMP=all when bundling
2. Open code_flow_*.html from /tmp/bun-bundler-debug/
3. Load the after_generation JSON to see source→output transformation

Example:
  env BUN_BUNDLER_GRAPH_DUMP=all bun build app.js --target=browser

The code flow visualizer shows actual code transformations, while the
graph visualizer shows the overall bundle structure.

Tested and working with multi-file bundles showing real transformations.
This commit is contained in:
Claude
2025-08-28 05:25:42 +02:00
parent 6c5a063813
commit 3aaa0e15f3
3 changed files with 53 additions and 9 deletions

View File

@@ -306,6 +306,7 @@
<option value="after_compute">After Compute</option>
<option value="after_chunks">After Chunks</option>
<option value="after_link">After Link</option>
<option value="after_generation">After Generation (with output)</option>
</select>
<button id="compare-btn">📊 Compare Stages</button>
</div>
@@ -456,10 +457,18 @@
return;
}
// For now, show mock source code with file metadata
// In real implementation, we'd load actual source
const mockSource = generateMockSource(file);
sourceEditor.setValue(mockSource);
// Use actual source snippet if available
let sourceCode = '';
if (file.source_snippet) {
sourceCode = `// File: ${file.path}\n// Loader: ${file.loader}\n\n${file.source_snippet}`;
if (file.source_snippet.length === 500) {
sourceCode += '\n\n// ... (truncated at 500 chars)';
}
} else {
sourceCode = generateMockSource(file);
}
sourceEditor.setValue(sourceCode);
// Highlight symbols
highlightSourceSymbols(data, file);
@@ -472,9 +481,21 @@
return;
}
// For now, show mock output with chunk metadata
const mockOutput = generateMockOutput(chunk, data);
outputEditor.setValue(mockOutput);
// Use actual output snippet if available
let outputCode = '';
if (chunk.output_snippet) {
outputCode = `// Chunk ${chunk.index}\n`;
outputCode += `// Entry point: ${chunk.is_entry_point}\n`;
outputCode += `// Output path: ${chunk.final_path || 'unknown'}\n\n`;
outputCode += chunk.output_snippet;
if (chunk.output_snippet.length === 1000) {
outputCode += '\n\n// ... (truncated at 1000 chars)';
}
} else {
outputCode = generateMockOutput(chunk, data);
}
outputEditor.setValue(outputCode);
// Highlight transformed symbols
highlightOutputSymbols(data, chunk);

View File

@@ -40,6 +40,7 @@ pub const GraphVisualizer = struct {
if (strings.eqlComptime(env_val, "compute")) return .after_compute;
if (strings.eqlComptime(env_val, "chunks")) return .after_chunks;
if (strings.eqlComptime(env_val, "link")) return .after_link;
if (strings.eqlComptime(env_val, "generation")) return .after_generation;
return .all; // Default to all if set but not recognized
}
@@ -50,6 +51,7 @@ pub const GraphVisualizer = struct {
after_compute,
after_chunks,
after_link,
after_generation,
all,
};
@@ -58,9 +60,16 @@ pub const GraphVisualizer = struct {
stage: []const u8,
chunks: ?[]const Chunk,
) !void {
if (!shouldDump()) return;
debug("dumpGraphState called for stage: {s}", .{stage});
if (!shouldDump()) {
debug("shouldDump() returned false", .{});
return;
}
const dump_stage = getDumpStage();
debug("dump_stage: {}", .{dump_stage});
const should_dump_now = switch (dump_stage) {
.none => false,
.all => true,
@@ -68,9 +77,15 @@ pub const GraphVisualizer = struct {
.after_compute => strings.eqlComptime(stage, "after_compute"),
.after_chunks => strings.eqlComptime(stage, "after_chunks"),
.after_link => strings.eqlComptime(stage, "after_link"),
.after_generation => strings.eqlComptime(stage, "after_generation"),
};
if (!should_dump_now) return;
if (!should_dump_now) {
debug("should_dump_now is false for stage {s}", .{stage});
return;
}
debug("Proceeding with dump for stage: {s}", .{stage});
debug("Dumping graph state: {s}", .{stage});

View File

@@ -193,6 +193,14 @@ pub fn generateChunksInParallel(
}
}
// Dump graph state after generation (includes output code)
if (comptime Environment.isDebug) {
const GraphVisualizer = @import("../graph_visualizer.zig").GraphVisualizer;
GraphVisualizer.dumpGraphState(c, "after_generation", chunks) catch |err| {
debug("Failed to dump graph after generation: {}", .{err});
};
}
// When bake.DevServer is in use, we're going to take a different code path at the end.
// We want to extract the source code of each part instead of combining it into a single file.
// This is so that when hot-module updates happen, we can: