[{"data":1,"prerenderedAt":1069},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope":1061},{"id":4,"title":5,"body":6,"description":1040,"extension":1041,"meta":1042,"navigation":1054,"path":1055,"seo":1056,"stem":1059,"__hash__":1060},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope\u002Findex.md","Comparing Two Builds with Statoscope",{"type":7,"value":8,"toc":1024},"minimark",[9,14,29,32,180,185,214,218,224,230,236,242,318,322,327,441,448,452,508,511,515,677,702,705,709,716,831,835,838,842,860,864,893,897,903,909,915,921,925,937,946,955,964,973,982,986,1009,1014,1017,1020],[10,11,13],"h1",{"id":12},"how-to-compare-two-webpack-builds-with-statoscope","How to Compare Two webpack Builds with Statoscope",[15,16,17,18,23,24,28],"p",{},"This guide extends ",[19,20,22],"a",{"href":21},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002F","Webpack Bundle Analysis Techniques"," within ",[19,25,27],{"href":26},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". Most bundle tools answer \"what is in this build?\". The more useful question during development is \"what changed between these two builds, and why?\". Statoscope is a toolkit built around webpack's stats JSON that answers that directly: it diffs two stats files to show added and removed modules, changed chunk compositions, package version changes and duplicate packages, and it can validate builds against rules in CI.",[15,30,31],{},"That makes it the natural tool for investigating a failed size budget (\"the landing chunk grew 40KB — from what?\"), reviewing a dependency upgrade (\"did upgrading the date library change our bundle?\") and enforcing structural rules that size checks cannot express (\"no package may appear in more than one version\").",[15,33,34],{},[35,36,42,43,42,50,42,54,42,57,42,75,42,83,42,89,42,97,42,104,42,109,42,112,42,116,42,119,42,125,42,129,42,132,42,136,42,139,42,143,42,146,42,149,42,153,42,156,42,159,42,168,42,172,42,176,42],"svg",{"viewBox":37,"width":38,"role":39,"ariaLabel":40,"style":41},"0 0 760 149","100%","img","Steps from generating webpack stats for two builds to a diff report and CI validation.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[44,45],"rect",{"className":46,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],"svg-canvas","0","#ffffff",[51,52,53],"title",{},"Statoscope diff workflow",[55,56,40],"desc",{},[58,59,60],"defs",{},[61,62,69],"marker",{"id":63,"viewBox":64,"refX":65,"refY":66,"markerWidth":67,"markerHeight":67,"orient":68},"fa814032e6","0 0 10 10","9","5","7","auto-start-reverse",[70,71],"path",{"d":72,"fill":73,"style":74},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[44,76],{"x":77,"y":77,"width":78,"height":79,"rx":80,"fill":81,"stroke":73,"style":82},"1","758","147","10","none","stroke-opacity:0.18",[84,85,53],"text",{"x":86,"y":87,"fill":73,"style":88},"28.0","34.0","font-size:16px;font-weight:700",[44,90],{"x":86,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[84,98,103],{"x":99,"y":100,"fill":73,"style":101,"textAnchor":102},"82.4","78.0","font-size:13px;font-weight:700","middle","Base stats.json",[84,105,108],{"x":99,"y":106,"fill":73,"style":107,"textAnchor":102},"96.0","font-size:12px","main branch build",[44,110],{"x":111,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"176.8",[84,113,115],{"x":114,"y":100,"fill":73,"style":101,"textAnchor":102},"231.2","Head stats.json",[84,117,118],{"x":114,"y":106,"fill":73,"style":107,"textAnchor":102},"PR build",[44,120],{"x":121,"y":91,"width":92,"height":93,"rx":94,"fill":122,"stroke":123,"style":124},"325.6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[84,126,128],{"x":127,"y":100,"fill":73,"style":101,"textAnchor":102},"380.0","Diff",[84,130,131],{"x":127,"y":106,"fill":73,"style":107,"textAnchor":102},"modules, chunks,",[84,133,135],{"x":127,"y":134,"fill":73,"style":107,"textAnchor":102},"112.0","packages",[44,137],{"x":138,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"474.4",[84,140,142],{"x":141,"y":100,"fill":73,"style":101,"textAnchor":102},"528.8","Validate",[84,144,145],{"x":141,"y":106,"fill":73,"style":107,"textAnchor":102},"rules for CI",[44,147],{"x":148,"y":91,"width":92,"height":93,"rx":94,"fill":95,"stroke":95,"style":96},"623.2",[84,150,152],{"x":151,"y":100,"fill":73,"style":101,"textAnchor":102},"677.6","Report",[84,154,155],{"x":151,"y":106,"fill":73,"style":107,"textAnchor":102},"HTML + PR",[84,157,158],{"x":151,"y":134,"fill":73,"style":107,"textAnchor":102},"comment",[160,161],"line",{"x1":162,"y1":163,"x2":164,"y2":163,"stroke":73,"strokeWidth":165,"style":166,"markerEnd":167},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa814032e6)",[160,169],{"x1":170,"y1":163,"x2":171,"y2":163,"stroke":73,"strokeWidth":165,"style":166,"markerEnd":167},"285.6","323.6",[160,173],{"x1":174,"y1":163,"x2":175,"y2":163,"stroke":73,"strokeWidth":165,"style":166,"markerEnd":167},"434.4","472.4",[160,177],{"x1":178,"y1":163,"x2":179,"y2":163,"stroke":73,"strokeWidth":165,"style":166,"markerEnd":167},"583.2","621.2",[181,182,184],"h2",{"id":183},"rapid-diagnosis","Rapid Diagnosis",[186,187,188,196,202,208],"ul",{},[189,190,191,195],"li",{},[192,193,194],"strong",{},"Do you produce stats JSON in CI?"," Without it, build comparisons are guesswork.",[189,197,198,201],{},[192,199,200],{},"Is the stats output complete?"," Statoscope needs module, chunk and reason information; minimal stats presets omit it.",[189,203,204,207],{},[192,205,206],{},"Do you know what changed in the last size regression?"," If answering takes more than a few minutes, a diff tool pays for itself.",[189,209,210,213],{},[192,211,212],{},"Do you have structural rules?"," Rules like \"no duplicate React\" or \"entry chunk under N modules\" need a validator, not a byte budget.",[181,215,217],{"id":216},"root-cause-analysis-why-regressions-go-unexplained","Root Cause Analysis: Why Regressions Go Unexplained",[15,219,220,223],{},[192,221,222],{},"1. Size tools report totals, not causes."," A byte count rising does not say which module or dependency caused it.",[15,225,226,229],{},[192,227,228],{},"2. Dependency changes hide in lockfiles."," A minor upgrade of a transitive dependency can pull in new modules; nobody reviews lockfile diffs in detail.",[15,231,232,235],{},[192,233,234],{},"3. Chunk composition shifts silently."," A module moving from a lazy chunk to the entry looks like a size increase in one place and a decrease in another; totals hide it.",[15,237,238,241],{},[192,239,240],{},"4. Duplicates creep in."," Two versions of a package resolve after an upgrade, doubling its contribution.",[15,243,244],{},[35,245,42,248,42,251,42,254,42,256,42,259,42,261,42,268,42,275,42,280,42,284,42,288,42,292,42,296,42,301,42,305,42,309,42,314,42],{"viewBox":246,"width":38,"role":39,"ariaLabel":247,"style":41},"0 0 760 194","Bar chart attributing a 43 kilobyte entry chunk increase to its causes as shown by a build diff.",[44,249],{"className":250,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,252,253],{},"What a Statoscope diff found in a +43KB regression",[55,255,247],{},[44,257],{"x":77,"y":77,"width":78,"height":258,"rx":80,"fill":81,"stroke":73,"style":82},"192",[84,260,253],{"x":86,"y":87,"fill":73,"style":88},[84,262,267],{"x":263,"y":264,"fill":73,"style":265,"textAnchor":266},"203.2","70.0","font-size:13px","end","date-fns duplicated (v2 + v3)",[44,269],{"x":270,"y":91,"width":271,"height":272,"rx":273,"fill":122,"stroke":123,"style":274},"215.2","452.8","19","3","fill-opacity:0.7;stroke-opacity:0.9",[84,276,279],{"x":277,"y":264,"fill":73,"style":278},"674.0","font-size:12px;font-weight:600","21KB",[84,281,283],{"x":263,"y":282,"fill":73,"style":265,"textAnchor":266},"101.0","chart module moved into entry",[44,285],{"x":270,"y":286,"width":287,"height":272,"rx":273,"fill":122,"stroke":123,"style":274},"87.0","388.1",[84,289,291],{"x":290,"y":282,"fill":73,"style":278},"609.3","18KB",[84,293,295],{"x":263,"y":294,"fill":73,"style":265,"textAnchor":266},"132.0","New feature code",[44,297],{"x":270,"y":298,"width":299,"height":272,"rx":273,"fill":95,"stroke":95,"style":300},"118.0","129.4","fill-opacity:0.55;stroke-opacity:0.9",[84,302,304],{"x":303,"y":294,"fill":73,"style":278},"350.6","6KB",[84,306,308],{"x":263,"y":307,"fill":73,"style":265,"textAnchor":266},"163.0","Removed dead code",[44,310],{"x":270,"y":311,"width":312,"height":272,"rx":273,"fill":73,"stroke":73,"style":313},"149.0","2.0","fill-opacity:0.7;stroke-opacity:0.4",[84,315,317],{"x":316,"y":307,"fill":73,"style":278},"223.2","-2KB",[181,319,321],{"id":320},"step-by-step-workflow","Step-by-Step Workflow",[323,324,326],"h3",{"id":325},"_1-emit-full-stats-from-both-builds","1. Emit full stats from both builds",[328,329,334],"pre",{"className":330,"code":331,"language":332,"meta":333,"style":333},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F webpack.config.js — with @statoscope\u002Fwebpack-plugin\nconst StatoscopeWebpackPlugin = require('@statoscope\u002Fwebpack-plugin').default;\nmodule.exports = {\n  plugins: [new StatoscopeWebpackPlugin({ saveStatsTo: 'dist\u002Fstats.json', saveReportTo: 'dist\u002Fstatoscope.html', open: false })],\n};\n\u002F\u002F trade-off: full stats files can be tens of megabytes for large apps and add\n\u002F\u002F build time. Generate them in CI, not in every local build.\n","javascript","",[335,336,337,345,374,391,423,429,435],"code",{"__ignoreMap":333},[338,339,341],"span",{"class":160,"line":340},1,[338,342,344],{"class":343},"sjfSM","\u002F\u002F webpack.config.js — with @statoscope\u002Fwebpack-plugin\n",[338,346,348,352,356,359,363,367,371],{"class":160,"line":347},2,[338,349,351],{"class":350},"sPARh","const",[338,353,355],{"class":354},"sPXB4"," StatoscopeWebpackPlugin",[338,357,358],{"class":350}," =",[338,360,362],{"class":361},"smZ65"," require",[338,364,366],{"class":365},"saISM","(",[338,368,370],{"class":369},"sZ8jY","'@statoscope\u002Fwebpack-plugin'",[338,372,373],{"class":365},").default;\n",[338,375,377,380,383,386,388],{"class":160,"line":376},3,[338,378,379],{"class":354},"module",[338,381,382],{"class":365},".",[338,384,385],{"class":354},"exports",[338,387,358],{"class":350},[338,389,390],{"class":365}," {\n",[338,392,394,397,400,402,405,408,411,414,417,420],{"class":160,"line":393},4,[338,395,396],{"class":365},"  plugins: [",[338,398,399],{"class":350},"new",[338,401,355],{"class":361},[338,403,404],{"class":365},"({ saveStatsTo: ",[338,406,407],{"class":369},"'dist\u002Fstats.json'",[338,409,410],{"class":365},", saveReportTo: ",[338,412,413],{"class":369},"'dist\u002Fstatoscope.html'",[338,415,416],{"class":365},", open: ",[338,418,419],{"class":354},"false",[338,421,422],{"class":365}," })],\n",[338,424,426],{"class":160,"line":425},5,[338,427,428],{"class":365},"};\n",[338,430,432],{"class":160,"line":431},6,[338,433,434],{"class":343},"\u002F\u002F trade-off: full stats files can be tens of megabytes for large apps and add\n",[338,436,438],{"class":160,"line":437},7,[338,439,440],{"class":343},"\u002F\u002F build time. Generate them in CI, not in every local build.\n",[15,442,443,444,447],{},"Expected outcome: a ",[335,445,446],{},"stats.json"," and an HTML report for each build.",[323,449,451],{"id":450},"_2-generate-a-diff-report","2. Generate a diff report",[328,453,457],{"className":454,"code":455,"language":456,"meta":333,"style":333},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","npx @statoscope\u002Fcli generate -i base\u002Fstats.json -i head\u002Fstats.json -o diff.html\n# Open diff.html and choose the \"Diff\" view: added\u002Fremoved modules, changed\n# chunks, package version changes and duplicates are listed separately.\n# trade-off: comparing builds with different webpack configs (e.g. dev vs prod)\n# produces noise. Always diff like with like.\n","bash",[335,458,459,488,493,498,503],{"__ignoreMap":333},[338,460,461,465,468,471,474,477,479,482,485],{"class":160,"line":340},[338,462,464],{"class":463},"sQw3B","npx",[338,466,467],{"class":369}," @statoscope\u002Fcli",[338,469,470],{"class":369}," generate",[338,472,473],{"class":354}," -i",[338,475,476],{"class":369}," base\u002Fstats.json",[338,478,473],{"class":354},[338,480,481],{"class":369}," head\u002Fstats.json",[338,483,484],{"class":354}," -o",[338,486,487],{"class":369}," diff.html\n",[338,489,490],{"class":160,"line":347},[338,491,492],{"class":343},"# Open diff.html and choose the \"Diff\" view: added\u002Fremoved modules, changed\n",[338,494,495],{"class":160,"line":376},[338,496,497],{"class":343},"# chunks, package version changes and duplicates are listed separately.\n",[338,499,500],{"class":160,"line":393},[338,501,502],{"class":343},"# trade-off: comparing builds with different webpack configs (e.g. dev vs prod)\n",[338,504,505],{"class":160,"line":425},[338,506,507],{"class":343},"# produces noise. Always diff like with like.\n",[15,509,510],{},"Expected outcome: a concrete list of what changed, with sizes.",[323,512,514],{"id":513},"_3-encode-rules-with-the-validator","3. Encode rules with the validator",[328,516,518],{"className":330,"code":517,"language":332,"meta":333,"style":333},"\u002F\u002F statoscope.config.js\nmodule.exports = {\n  validate: {\n    plugins: ['@statoscope\u002Fwebpack'],\n    rules: {\n      '@statoscope\u002Fwebpack\u002Fno-packages-dups': ['error', { exclude: ['tslib'] }],\n      '@statoscope\u002Fwebpack\u002Frestricted-packages': ['error', ['moment', 'lodash']],\n      '@statoscope\u002Fwebpack\u002Fentry-download-size-limits': ['error', { global: { maxSize: 180 * 1024 } }],\n      '@statoscope\u002Fwebpack\u002Fdiff-entry-download-size-limits': ['error', { global: { maxSizeDiff: 10 * 1024 } }],\n    },\n  },\n};\n\u002F\u002F trade-off: rule names and options evolve between Statoscope versions — pin\n\u002F\u002F the version and check the rule docs when upgrading.\n",[335,519,520,525,537,542,553,558,578,602,627,648,654,660,665,671],{"__ignoreMap":333},[338,521,522],{"class":160,"line":340},[338,523,524],{"class":343},"\u002F\u002F statoscope.config.js\n",[338,526,527,529,531,533,535],{"class":160,"line":347},[338,528,379],{"class":354},[338,530,382],{"class":365},[338,532,385],{"class":354},[338,534,358],{"class":350},[338,536,390],{"class":365},[338,538,539],{"class":160,"line":376},[338,540,541],{"class":365},"  validate: {\n",[338,543,544,547,550],{"class":160,"line":393},[338,545,546],{"class":365},"    plugins: [",[338,548,549],{"class":369},"'@statoscope\u002Fwebpack'",[338,551,552],{"class":365},"],\n",[338,554,555],{"class":160,"line":425},[338,556,557],{"class":365},"    rules: {\n",[338,559,560,563,566,569,572,575],{"class":160,"line":431},[338,561,562],{"class":369},"      '@statoscope\u002Fwebpack\u002Fno-packages-dups'",[338,564,565],{"class":365},": [",[338,567,568],{"class":369},"'error'",[338,570,571],{"class":365},", { exclude: [",[338,573,574],{"class":369},"'tslib'",[338,576,577],{"class":365},"] }],\n",[338,579,580,583,585,587,590,593,596,599],{"class":160,"line":437},[338,581,582],{"class":369},"      '@statoscope\u002Fwebpack\u002Frestricted-packages'",[338,584,565],{"class":365},[338,586,568],{"class":369},[338,588,589],{"class":365},", [",[338,591,592],{"class":369},"'moment'",[338,594,595],{"class":365},", ",[338,597,598],{"class":369},"'lodash'",[338,600,601],{"class":365},"]],\n",[338,603,605,608,610,612,615,618,621,624],{"class":160,"line":604},8,[338,606,607],{"class":369},"      '@statoscope\u002Fwebpack\u002Fentry-download-size-limits'",[338,609,565],{"class":365},[338,611,568],{"class":369},[338,613,614],{"class":365},", { global: { maxSize: ",[338,616,617],{"class":354},"180",[338,619,620],{"class":350}," *",[338,622,623],{"class":354}," 1024",[338,625,626],{"class":365}," } }],\n",[338,628,630,633,635,637,640,642,644,646],{"class":160,"line":629},9,[338,631,632],{"class":369},"      '@statoscope\u002Fwebpack\u002Fdiff-entry-download-size-limits'",[338,634,565],{"class":365},[338,636,568],{"class":369},[338,638,639],{"class":365},", { global: { maxSizeDiff: ",[338,641,80],{"class":354},[338,643,620],{"class":350},[338,645,623],{"class":354},[338,647,626],{"class":365},[338,649,651],{"class":160,"line":650},10,[338,652,653],{"class":365},"    },\n",[338,655,657],{"class":160,"line":656},11,[338,658,659],{"class":365},"  },\n",[338,661,663],{"class":160,"line":662},12,[338,664,428],{"class":365},[338,666,668],{"class":160,"line":667},13,[338,669,670],{"class":343},"\u002F\u002F trade-off: rule names and options evolve between Statoscope versions — pin\n",[338,672,674],{"class":160,"line":673},14,[338,675,676],{"class":343},"\u002F\u002F the version and check the rule docs when upgrading.\n",[328,678,680],{"className":454,"code":679,"language":456,"meta":333,"style":333},"npx @statoscope\u002Fcli validate --input head\u002Fstats.json --reference base\u002Fstats.json\n",[335,681,682],{"__ignoreMap":333},[338,683,684,686,688,691,694,696,699],{"class":160,"line":340},[338,685,464],{"class":463},[338,687,467],{"class":369},[338,689,690],{"class":369}," validate",[338,692,693],{"class":354}," --input",[338,695,481],{"class":369},[338,697,698],{"class":354}," --reference",[338,700,701],{"class":369}," base\u002Fstats.json\n",[15,703,704],{},"Expected outcome: CI fails on duplicates, banned packages, oversized entries or large entry growth, with messages that name the cause.",[323,706,708],{"id":707},"_4-add-the-diff-to-pull-requests","4. Add the diff to pull requests",[15,710,711,712,715],{},"Upload ",[335,713,714],{},"diff.html"," as a CI artefact and link it from the size comment, so reviewers can drill into causes when a number looks wrong.",[15,717,718],{},[35,719,42,722,42,725,42,728,42,730,42,733,42,735,42,740,42,747,42,751,42,755,42,758,42,762,42,766,42,770,42,772,42,775,42,777,42,780,42,783,42,787,42,789,42,792,42,794,42,797,42,800,42,804,42,806,42,809,42,811,42,814,42,817,42,821,42,823,42,826,42,828,42],{"viewBox":720,"width":38,"role":39,"ariaLabel":721,"style":41},"0 0 760 228","Examples of Statoscope validation rules and the regressions they catch that a byte budget would miss.",[44,723],{"className":724,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,726,727],{},"Structural rules size budgets cannot express",[55,729,721],{},[44,731],{"x":77,"y":77,"width":78,"height":732,"rx":80,"fill":81,"stroke":73,"style":82},"226",[84,734,727],{"x":86,"y":87,"fill":73,"style":88},[44,736],{"x":86,"y":91,"width":737,"height":738,"rx":48,"fill":73,"stroke":73,"style":739},"195.4","30.0","fill-opacity:0.06;stroke-opacity:0.4",[84,741,746],{"x":742,"y":743,"fill":73,"style":744,"textAnchor":745},"38.0","75.5","font-size:12.5px;font-weight:700","start","Rule",[44,748],{"x":749,"y":91,"width":750,"height":738,"rx":48,"fill":73,"stroke":73,"style":739},"223.4","254.3",[84,752,754],{"x":753,"y":743,"fill":73,"style":744,"textAnchor":102},"350.5","Catches",[44,756],{"x":757,"y":91,"width":750,"height":738,"rx":48,"fill":73,"stroke":73,"style":739},"477.7",[84,759,761],{"x":760,"y":743,"fill":73,"style":744,"textAnchor":102},"604.8","Budget alone?",[44,763],{"x":86,"y":764,"width":737,"height":738,"rx":48,"fill":81,"stroke":73,"style":765},"86.0","stroke-opacity:0.35",[84,767,769],{"x":742,"y":768,"fill":73,"style":744,"textAnchor":745},"105.5","no-packages-dups",[44,771],{"x":749,"y":764,"width":750,"height":738,"rx":48,"fill":95,"stroke":95,"style":96},[84,773,774],{"x":753,"y":768,"fill":73,"style":107,"textAnchor":102},"two versions of a library",[44,776],{"x":757,"y":764,"width":750,"height":738,"rx":48,"fill":122,"stroke":123,"style":124},[84,778,779],{"x":760,"y":768,"fill":73,"style":107,"textAnchor":102},"only if total grows enough",[44,781],{"x":86,"y":782,"width":737,"height":738,"rx":48,"fill":81,"stroke":73,"style":765},"116.0",[84,784,786],{"x":742,"y":785,"fill":73,"style":744,"textAnchor":745},"135.5","restricted-packages",[44,788],{"x":749,"y":782,"width":750,"height":738,"rx":48,"fill":95,"stroke":95,"style":96},[84,790,791],{"x":753,"y":785,"fill":73,"style":107,"textAnchor":102},"banned heavy libs reappearing",[44,793],{"x":757,"y":782,"width":750,"height":738,"rx":48,"fill":122,"stroke":123,"style":124},[84,795,796],{"x":760,"y":785,"fill":73,"style":107,"textAnchor":102},"no",[44,798],{"x":86,"y":799,"width":737,"height":738,"rx":48,"fill":81,"stroke":73,"style":765},"146.0",[84,801,803],{"x":742,"y":802,"fill":73,"style":744,"textAnchor":745},"165.5","diff-entry-download-size-limits",[44,805],{"x":749,"y":799,"width":750,"height":738,"rx":48,"fill":95,"stroke":95,"style":96},[84,807,808],{"x":753,"y":802,"fill":73,"style":107,"textAnchor":102},"growth per PR",[44,810],{"x":757,"y":799,"width":750,"height":738,"rx":48,"fill":73,"stroke":73,"style":739},[84,812,813],{"x":760,"y":802,"fill":73,"style":107,"textAnchor":102},"partly",[44,815],{"x":86,"y":816,"width":737,"height":738,"rx":48,"fill":81,"stroke":73,"style":765},"176.0",[84,818,820],{"x":742,"y":819,"fill":73,"style":744,"textAnchor":745},"195.5","entry-download-size-limits",[44,822],{"x":749,"y":816,"width":750,"height":738,"rx":48,"fill":95,"stroke":95,"style":96},[84,824,825],{"x":753,"y":819,"fill":73,"style":107,"textAnchor":102},"absolute entry size",[44,827],{"x":757,"y":816,"width":750,"height":738,"rx":48,"fill":95,"stroke":95,"style":96},[84,829,830],{"x":760,"y":819,"fill":73,"style":107,"textAnchor":102},"yes",[181,832,834],{"id":833},"verification","Verification",[15,836,837],{},"Introduce a known change on a branch — add a dependency to the entry — and confirm the diff names it with the right size and that the validator fails if the rule is set. Then verify against a no-op change that the diff is empty; if not, your builds are non-deterministic (unstable IDs or timestamps), which is worth fixing for caching reasons too.",[181,839,841],{"id":840},"worked-example-a-silent-duplicate","Worked Example: A Silent Duplicate",[15,843,844,845,848,849,852,853,856,857,859],{},"After a routine ",[335,846,847],{},"npm update",", a team's entry chunk grew by 21KB without any code change. Size checks flagged the growth but not the cause. The Statoscope diff showed a new package entry, ",[335,850,851],{},"date-fns@2.30.0",", alongside the existing ",[335,854,855],{},"date-fns@3.6.0",": a component library dependency had pinned v2. The fix was a package-manager override forcing v3 (after checking compatibility), and the team added ",[335,858,769],{}," to CI so the next duplicate would fail with a message naming the package instead of an unexplained byte count.",[181,861,863],{"id":862},"common-mistakes","Common Mistakes",[186,865,866,875,881,887],{},[189,867,868,874],{},[192,869,870,871,382],{},"Using ",[335,872,873],{},"stats: 'minimal'"," Statoscope needs reasons, modules and chunk data; use the plugin or a verbose stats preset.",[189,876,877,880],{},[192,878,879],{},"Diffing builds from different environments."," Development and production stats differ wildly.",[189,882,883,886],{},[192,884,885],{},"Treating every diff entry as a problem."," New feature code is expected; focus on duplicates, moved modules and dependency changes.",[189,888,889,892],{},[192,890,891],{},"Not pinning tool versions."," Validator rules change between releases.",[181,894,896],{"id":895},"edge-cases-when-diffing-builds","Edge Cases When Diffing Builds",[15,898,899,902],{},[192,900,901],{},"Non-deterministic builds."," If a no-op change produces a non-empty diff, your build embeds timestamps, random IDs or environment values. Fix that first; otherwise every diff is noise and caching suffers too.",[15,904,905,908],{},[192,906,907],{},"Different dependency trees."," Comparing builds produced from different lockfiles shows every transitive change. That is useful for reviewing dependency upgrades, but for code reviews make sure both builds use the same lockfile except for intentional changes.",[15,910,911,914],{},[192,912,913],{},"Large monorepos."," Stats for monorepos with many entry points can be very large. Diff per application rather than for the whole repository, and generate stats only for applications affected by a pull request.",[15,916,917,920],{},[192,918,919],{},"Module concatenation."," Concatenated modules appear as a group in stats; changes inside them are attributed to the group root. Disable concatenation in an analysis-only build if you need module-level precision.",[181,922,924],{"id":923},"faq","FAQ",[926,927,930,934],"details",{"className":928},[929],"faq-item",[931,932,933],"summary",{},"Does Statoscope work with Rspack or Vite?",[15,935,936],{},"Rspack produces webpack-compatible stats, so Statoscope works with it. Vite\u002FRollup do not produce webpack stats; use rollup-plugin-visualizer's raw output or the build manifest for diffs instead.",[926,938,940,943],{"className":939},[929],[931,941,942],{},"How is this different from webpack-bundle-analyzer?",[15,944,945],{},"webpack-bundle-analyzer visualises one build's composition. Statoscope can do that too, but its strengths are comparing builds, explaining why modules are included, finding duplicates and validating rules — the tasks that matter when investigating changes.",[926,947,949,952],{"className":948},[929],[931,950,951],{},"Should the HTML report be public?",[15,953,954],{},"No. It reveals your dependency list and file structure. Keep it as a CI artefact accessible to your team.",[926,956,958,961],{"className":957},[929],[931,959,960],{},"Can Statoscope explain why a module is in a chunk?",[15,962,963],{},"Yes. Its module view shows the reasons — which modules import it and through which chunk — which is the fastest way to find the import that moved a heavy dependency into the entry chunk. Follow the reasons upward until you reach your own source.",[926,965,967,970],{"className":966},[929],[931,968,969],{},"Is it worth running on every pull request?",[15,971,972],{},"Validation, yes — it is fast and deterministic. Full HTML diff reports are heavier; generate them on every pull request if CI time allows, or only when the size check reports a change above a threshold.",[926,974,976,979],{"className":975},[929],[931,977,978],{},"What is the quickest win once Statoscope is set up?",[15,980,981],{},"Turn on the duplicate-packages rule. Duplicates are common after dependency updates, invisible in code review, and almost always fixable with a resolution override or a dependency upgrade — pure byte savings with no product change.",[181,983,985],{"id":984},"related","Related",[186,987,988,995,1002],{},[189,989,990,994],{},[19,991,993],{"href":992},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Ftracking-bundle-size-per-pull-request\u002F","Tracking bundle size per pull request"," — the reporting layer that links to the diff.",[189,996,997,1001],{},[19,998,1000],{"href":999},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F","Finding duplicate dependencies in a monorepo bundle"," — resolving the duplicates the validator finds.",[189,1003,1004,1008],{},[19,1005,1007],{"href":1006},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-bundle-bloat-with-source-map-explorer\u002F","Finding bundle bloat with source-map-explorer"," — byte-accurate attribution of the final output.",[1010,1011,1013],"script",{"type":1012},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Compare Two webpack Builds with Statoscope\",\n  \"description\": \"How to use Statoscope's diff and validation features to understand exactly what changed between two webpack builds and stop regressions.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Emit full stats from both builds\",\n      \"text\": \"Expected outcome: a stats.json and an HTML report for each build.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Generate a diff report\",\n      \"text\": \"Expected outcome: a concrete list of what changed, with sizes.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Encode rules with the validator\",\n      \"text\": \"Expected outcome: CI fails on duplicates, banned packages, oversized entries or large entry growth, with messages that name the cause.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Add the diff to pull requests\",\n      \"text\": \"Upload diff.html as a CI artefact and link it from the size comment, so reviewers can drill into causes when a number looks wrong.\"\n    }\n  ]\n}\n",[1010,1015,1016],{"type":1012},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Compare Two webpack Builds with Statoscope\",\n  \"description\": \"How to use Statoscope's diff and validation features to understand exactly what changed between two webpack builds and stop regressions.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope\u002F\"\n  }\n}\n",[1010,1018,1019],{"type":1012},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"JavaScript Bundle Optimization & Code Splitting\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Webpack Bundle Analysis Techniques\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Comparing Two Builds with Statoscope\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope\u002F\"\n    }\n  ]\n}\n",[1021,1022,1023],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}",{"title":333,"searchDepth":347,"depth":347,"links":1025},[1026,1027,1028,1034,1035,1036,1037,1038,1039],{"id":183,"depth":347,"text":184},{"id":216,"depth":347,"text":217},{"id":320,"depth":347,"text":321,"children":1029},[1030,1031,1032,1033],{"id":325,"depth":376,"text":326},{"id":450,"depth":376,"text":451},{"id":513,"depth":376,"text":514},{"id":707,"depth":376,"text":708},{"id":833,"depth":347,"text":834},{"id":840,"depth":347,"text":841},{"id":862,"depth":347,"text":863},{"id":895,"depth":347,"text":896},{"id":923,"depth":347,"text":924},{"id":984,"depth":347,"text":985},"How to use Statoscope’s diff and validation features to understand exactly what changed between two webpack builds and stop regressions.","md",{"slug":1043,"type":1044,"breadcrumb":1045,"datePublished":1053,"dateModified":1053},"comparing-two-builds-with-statoscope","article",[1046,1049,1050,1051],{"name":1047,"url":1048},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1052},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope\u002F","2026-10-06",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope",{"title":1057,"description":1058},"Comparing Two webpack Builds with Statoscope","Use Statoscope to diff webpack stats between builds, see added and removed modules, duplicate packages and chunk changes, and enforce rules with validators in CI.","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fcomparing-two-builds-with-statoscope\u002Findex","m3xst2UTHKTq7xZ-Rl0bIFQsaohkEokOU7JbVumN0JA",[1062,1065],{"title":22,"path":1063,"stem":1064,"children":-1},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Findex",{"title":1066,"path":1067,"stem":1068,"children":-1},"Finding Bundle Bloat with source-map-explorer","\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-bundle-bloat-with-source-map-explorer","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-bundle-bloat-with-source-map-explorer\u002Findex",1791308076129]