Skip to content

Commit

Permalink
Add ability to filter to only initial JS by entrypoint (#519)
Browse files Browse the repository at this point in the history
Co-authored-by: Peter Goldberg <goldberg447@gmail.com>
Co-authored-by: Vesa Laakso <482561+valscion@users.noreply.github.com>
Co-authored-by: Peter Goldberg <pgoldberg@palantir.com>
  • Loading branch information
4 people committed Oct 26, 2022
1 parent 524dc41 commit 8a3d3f0
Show file tree
Hide file tree
Showing 14 changed files with 5,606 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ _Note: Gaps between patch versions are faulty, broken or test releases._

## UNRELEASED

* **New Feature**
* Add the ability to filter to displaying only initial chunks per entrypoint ([#519](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/519) by [@pas-trop-de-zele](https://github.com/pas-trop-de-zele))

## 4.6.1

* **Bug Fix**
Expand Down
19 changes: 19 additions & 0 deletions client/components/Dropdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.container {
font: var(--main-font);
white-space: nowrap;
}

.select {
border: 1px solid #aaa;
border-radius: 4px;
display: block;
width: 100%;
color: #7f7f7f;
height: 27px;
}

.label {
font-size: 11px;
font-weight: bold;
margin-bottom: 7px;
}
25 changes: 25 additions & 0 deletions client/components/Dropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import PureComponent from '../lib/PureComponent';

import s from './Dropdown.css';
export default class Dropdown extends PureComponent {

render() {
const {label, defaultOption, onSelectionChange, options} = this.props;

return (
<div className={s.container}>
<div className={s.label}>
{label}:
</div>
<div>
<select className={s.select} id={label} name={label} onChange={onSelectionChange}>
<option value={defaultOption}>{defaultOption}</option>
{options.map(option =>
<option key={option} value={option}>{option}</option>
)}
</select>
</div>
</div>
);
}
}
20 changes: 20 additions & 0 deletions client/components/ModulesTreemap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ import s from './ModulesTreemap.css';
import Search from './Search';
import {store} from '../store';
import ModulesList from './ModulesList';
import Dropdown from './Dropdown';

const SIZE_SWITCH_ITEMS = [
{label: 'Stat', prop: 'statSize'},
{label: 'Parsed', prop: 'parsedSize'},
{label: 'Gzipped', prop: 'gzipSize'}
];

const DEFAULT_DROPDOWN_SELECTION = 'Select an entrypoint';

@observer
export default class ModulesTreemap extends Component {
mouseCoords = {
Expand Down Expand Up @@ -78,6 +81,12 @@ export default class ModulesTreemap extends Component {
</div>
}
</div>
<div className={s.sidebarGroup}>
<Dropdown label="Filter to initial chunks"
defaultOption={DEFAULT_DROPDOWN_SELECTION}
options={store.entrypoints}
onSelectionChange={this.handleSelectionChange}/>
</div>
<div className={s.sidebarGroup}>
<Search label="Search modules"
query={store.searchQuery}
Expand Down Expand Up @@ -206,6 +215,17 @@ export default class ModulesTreemap extends Component {
}
}

handleSelectionChange = (event) => {
const selected = event.target.value;

if (selected === DEFAULT_DROPDOWN_SELECTION) {
store.selectedChunks = store.allChunks;
return;
}

store.selectedChunks = store.allChunks.filter(chunk => chunk.isInitialByEntrypoint[selected] ?? false);
}

handleConcatenatedModulesContentToggle = flag => {
store.showConcatenatedModulesContent = flag;
if (flag) {
Expand Down
4 changes: 4 additions & 0 deletions client/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export class Store {
this.selectedChunks = this.allChunks;
}
setEntrypoints(entrypoints) {
this.entrypoints = entrypoints;
}
@computed get hasParsedSizes() {
return this.allChunks.some(isChunkParsed);
}
Expand Down
2 changes: 1 addition & 1 deletion client/viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ try {
window.addEventListener('load', () => {
store.defaultSize = `${window.defaultSizes}Size`;
store.setModules(window.chartData);

store.setEntrypoints(window.entrypoints);
render(
<ModulesTreemap/>,
document.getElementById('app')
Expand Down
18 changes: 17 additions & 1 deletion src/analyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ function getViewerData(bundleStats, bundleDir, opts) {
return result;
}, {});

const chunkToInitialByEntrypoint = getChunkToInitialByEntrypoint(bundleStats);
return Object.entries(assets).map(([filename, asset]) => ({
label: filename,
isAsset: true,
Expand All @@ -157,7 +158,8 @@ function getViewerData(bundleStats, bundleDir, opts) {
statSize: asset.tree.size || asset.size,
parsedSize: asset.parsedSize,
gzipSize: asset.gzipSize,
groups: _.invokeMap(asset.tree.children, 'toChartData')
groups: _.invokeMap(asset.tree.children, 'toChartData'),
isInitialByEntrypoint: chunkToInitialByEntrypoint[filename] ?? {}
}));
}

Expand Down Expand Up @@ -211,3 +213,17 @@ function createModulesTree(modules) {

return root;
}

function getChunkToInitialByEntrypoint(bundleStats) {
if (bundleStats == null) {
return {};
}
const chunkToEntrypointInititalMap = {};
Object.values(bundleStats.entrypoints || {}).forEach((entrypoint) => {
for (const asset of entrypoint.assets) {
chunkToEntrypointInititalMap[asset.name] = chunkToEntrypointInititalMap[asset.name] ?? {};
chunkToEntrypointInititalMap[asset.name][entrypoint.name] = true;
}
});
return chunkToEntrypointInititalMap;
};
3 changes: 2 additions & 1 deletion src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function getScript(filename, mode) {
}
}

function renderViewer({title, enableWebSocket, chartData, defaultSizes, mode} = {}) {
function renderViewer({title, enableWebSocket, chartData, entrypoints, defaultSizes, mode} = {}) {
return html`<!DOCTYPE html>
<html>
<head>
Expand All @@ -58,6 +58,7 @@ function renderViewer({title, enableWebSocket, chartData, defaultSizes, mode} =
<div id="app"></div>
<script>
window.chartData = ${escapeJson(chartData)};
window.entrypoints = ${escapeJson(entrypoints)};
window.defaultSizes = ${escapeJson(defaultSizes)};
</script>
</body>
Expand Down
12 changes: 12 additions & 0 deletions src/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
startServer,
generateReport,
generateJSONReport,
getEntrypoints,
// deprecated
start: startServer
};
Expand All @@ -46,6 +47,7 @@ async function startServer(bundleStats, opts) {
const analyzerOpts = {logger, excludeAssets};

let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
const entrypoints = getEntrypoints(bundleStats);

if (!chartData) return;

Expand All @@ -60,6 +62,7 @@ async function startServer(bundleStats, opts) {
mode: 'server',
title: resolveTitle(reportTitle),
chartData,
entrypoints,
defaultSizes,
enableWebSocket: true
});
Expand Down Expand Up @@ -138,13 +141,15 @@ async function generateReport(bundleStats, opts) {
} = opts || {};

const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);
const entrypoints = getEntrypoints(bundleStats);

if (!chartData) return;

const reportHtml = renderViewer({
mode: 'static',
title: resolveTitle(reportTitle),
chartData,
entrypoints,
defaultSizes,
enableWebSocket: false
});
Expand Down Expand Up @@ -192,3 +197,10 @@ function getChartData(analyzerOpts, ...args) {

return chartData;
}

function getEntrypoints(bundleStats) {
if (bundleStats === null || bundleStats === undefined) {
return [];
}
return Object.values(bundleStats.entrypoints || {}).map(entrypoint => entrypoint.name);
}
16 changes: 15 additions & 1 deletion test/analyzer.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe('Analyzer', function () {
});
});

it('should gracefully process missing chunks', async function () {
it('should gracefully process missing module chunks', async function () {
generateReportFrom('with-missing-module-chunks/stats.json');
const chartData = await getChartData();
const invalidChunk = _.find(chartData, {label: 'invalid-chunk.js'});
Expand Down Expand Up @@ -195,6 +195,20 @@ describe('Analyzer', function () {
await expectValidReport({bundleLabel: 'bundle.js'});
});

it('should map chunks correctly to entrypoints', async function () {
generateReportFrom('with-multiple-entrypoints/stats.json');
const chartData = await getChartData();
expect(chartData).to.containSubset(
require('./stats/with-multiple-entrypoints/expected-chart-data')
);
});

it('should return empty chartData if there are no entrypoints', async function () {
generateReportFrom('with-no-entrypoints/stats.json');
const chartData = await getChartData();
expect(chartData).to.be.empty;
});

describe('options', function () {
describe('title', function () {
it('should take the --title option', async function () {
Expand Down
Loading

0 comments on commit 8a3d3f0

Please sign in to comment.