Skip to content

Comments

Optimize for large reports#154

Open
mlarraz wants to merge 1 commit intosimplecov-ruby:mainfrom
mlarraz:optimize-large-coverage-reports
Open

Optimize for large reports#154
mlarraz wants to merge 1 commit intosimplecov-ruby:mainfrom
mlarraz:optimize-large-coverage-reports

Conversation

@mlarraz
Copy link
Contributor

@mlarraz mlarraz commented Feb 20, 2026

Problem

A real-world coverage report with 1705 source files produces a 41MB
HTML file
(~1.9M lines) that causes significant browser slowdown:

  • 96% of the file is the source files section (~41.3MB)
  • 51% of that section is whitespace from ERB template formatting (~21MB)
  • 142,254 <li> elements rendered into the DOM at page load (hidden)
  • 142K event handlers individually bound to line number elements
  • An unnecessary <div> wrapper around each <li> adds 142K extra DOM nodes

Changes

Phase 1: Template Optimizations

ERB whitespace trimming (lib/simplecov-html.rb, all .erb views)

  • Add trim_mode: '-' to the ERB constructor
  • Add <%- / -%> trim markers to control flow tags in all templates
  • Eliminates ~21MB of blank lines produced by ERB conditionals/loops

Remove <div> wrapper around <li> (views/source_file.erb)

  • These were semantically invalid inside <ol> and unnecessary
  • Saves ~2.8MB and 142K DOM nodes

Conditional data-hits attribute (views/source_file.erb)

  • Only emit data-hits="N" when line.coverage is truthy
  • Previously emitted data-hits="" for ~83K "never" lines with no
    JS/CSS referencing the empty attribute

Compact covered_percent.erb

  • Single-line template avoids multi-line whitespace across ~3,400 calls

Phase 2: Browser Performance

<template> tags for source files (views/layout.erb)

  • Each source file is wrapped in <template id="tmpl-SHA1">
  • <template> content is parsed but NOT rendered into the DOM until
    explicitly activated — removes ~500K DOM nodes from initial page load

Template materialization on demand (assets/javascripts/application.js)

  • New materializeSourceFile() function clones template content into
    the .source_files container when a file is first viewed
  • Syntax highlighting applied on materialization (same deferred approach)
  • All code paths updated: click, colorbox onLoad, popstate, deep links

Event delegation for line number clicks (assets/javascripts/application.js)

  • Replaced direct binding on 142K elements with a single delegated
    event handler on document
  • Required for <template> approach and eliminates 142K event bindings

Results

Tested against a real coverage report with 1705 source files:

Metric Before After Change
File size 41 MB 25 MB -39%
Lines 1,944,847 573,916 -70%
Blank lines 1,090,596 3,578 -99.7%
<div> wrappers 142,255 2 -142,253
data-hits attrs 142,253 59,101 -83,152
DOM nodes at load ~500K+ ~few K deferred via <template>
Event bindings 142K 1 delegated

The remaining 25MB is predominantly the actual source code content
inside <li> elements, which is irreducible.

Files Modified

File Changes
lib/simplecov-html.rb trim_mode: '-' on ERB constructor
views/source_file.erb Trim markers, remove <div>, conditional data-hits
views/layout.erb Trim markers, <template> tags around source files
views/file_list.erb Trim markers on control flow tags
views/covered_percent.erb Single-line template
assets/javascripts/application.js Template materialization, event delegation
public/application.js Re-compiled asset

Test Plan

  • bundle exec rake test passes
  • Generate a coverage report against a test project and compare HTML file size
  • Open in browser: file list loads, clicking a file shows source in modal
  • Line numbers are clickable and scroll to correct position
  • Syntax highlighting works on opened files
  • Back/forward navigation works correctly
  • Deep-linking to a specific file/line via URL hash works

@mlarraz mlarraz changed the title Optimize simplecov-html for large coverage reports Optimize for large reports Feb 20, 2026
## Problem

A real-world coverage report with 1705 source files produces a **41MB
HTML file** (~1.9M lines) that causes significant browser slowdown:

- 96% of the file is the source files section (~41.3MB)
- 51% of that section is whitespace from ERB template formatting (~21MB)
- 142,254 `<li>` elements rendered into the DOM at page load (hidden)
- 142K event handlers individually bound to line number elements
- An unnecessary `<div>` wrapper around each `<li>` adds 142K extra DOM nodes

## Changes

### Phase 1: Template Optimizations

**ERB whitespace trimming** (`lib/simplecov-html.rb`, all `.erb` views)
- Add `trim_mode: '-'` to the ERB constructor
- Add `<%-` / `-%>` trim markers to control flow tags in all templates
- Eliminates ~21MB of blank lines produced by ERB conditionals/loops

**Remove `<div>` wrapper around `<li>`** (`views/source_file.erb`)
- These were semantically invalid inside `<ol>` and unnecessary
- Saves ~2.8MB and 142K DOM nodes

**Conditional `data-hits` attribute** (`views/source_file.erb`)
- Only emit `data-hits="N"` when `line.coverage` is truthy
- Previously emitted `data-hits=""` for ~83K "never" lines with no
  JS/CSS referencing the empty attribute

**Compact `covered_percent.erb`**
- Single-line template avoids multi-line whitespace across ~3,400 calls

### Phase 2: Browser Performance

**`<template>` tags for source files** (`views/layout.erb`)
- Each source file is wrapped in `<template id="tmpl-SHA1">`
- `<template>` content is parsed but NOT rendered into the DOM until
  explicitly activated — removes ~500K DOM nodes from initial page load

**Template materialization on demand** (`assets/javascripts/application.js`)
- New `materializeSourceFile()` function clones template content into
  the `.source_files` container when a file is first viewed
- Syntax highlighting applied on materialization (same deferred approach)
- All code paths updated: click, colorbox onLoad, popstate, deep links

**Event delegation for line number clicks** (`assets/javascripts/application.js`)
- Replaced direct binding on 142K elements with a single delegated
  event handler on `document`
- Required for `<template>` approach and eliminates 142K event bindings

## Results

Tested against a real coverage report with 1705 source files:

| Metric              | Before      | After     | Change       |
|---------------------|-------------|-----------|--------------|
| File size           | 41 MB       | 25 MB     | **-39%**     |
| Lines               | 1,944,847   | 573,916   | **-70%**     |
| Blank lines         | 1,090,596   | 3,578     | **-99.7%**   |
| `<div>` wrappers    | 142,255     | 2         | **-142,253** |
| `data-hits` attrs   | 142,253     | 59,101    | **-83,152**  |
| DOM nodes at load   | ~500K+      | ~few K    | deferred via `<template>` |
| Event bindings      | 142K        | 1         | delegated    |

The remaining 25MB is predominantly the actual source code content
inside `<li>` elements, which is irreducible.

## Files Modified

| File | Changes |
|---|---|
| `lib/simplecov-html.rb` | `trim_mode: '-'` on ERB constructor |
| `views/source_file.erb` | Trim markers, remove `<div>`, conditional `data-hits` |
| `views/layout.erb` | Trim markers, `<template>` tags around source files |
| `views/file_list.erb` | Trim markers on control flow tags |
| `views/covered_percent.erb` | Single-line template |
| `assets/javascripts/application.js` | Template materialization, event delegation |
| `public/application.js` | Re-compiled asset |

## Test Plan

- [x] `bundle exec rake test` passes
- [x] Generate a coverage report against a test project and compare HTML file size
- [x] Open in browser: file list loads, clicking a file shows source in modal
- [x] Line numbers are clickable and scroll to correct position
- [x] Syntax highlighting works on opened files
- [x] Back/forward navigation works correctly
- [x] Deep-linking to a specific file/line via URL hash works

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mlarraz mlarraz force-pushed the optimize-large-coverage-reports branch from c541a35 to 22250d2 Compare February 20, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant