Skip to content

Commit

Permalink
initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
pas-trop-de-zele committed Aug 17, 2022
1 parent 7d6039e commit a632593
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 6 deletions.
20 changes: 20 additions & 0 deletions client/components/Dropdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.container {
font: var(--main-font);
white-space: nowrap;
}

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

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

import s from './Dropdown.css';
import {store} from '../store';

const DEFAULT_DROPDOWN_SELECTION = 'Select an entrypoint';

export default class Dropdown extends PureComponent {

constructor(props) {
super(props);
this.state = {
selectedOption: DEFAULT_DROPDOWN_SELECTION
};
}

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

return (
<div className={s.container}>
<div className={s.label}>
{label}:
</div>
<div>
<select className={s.select} name={label} id={label} onChange={this.handleSelection}>
<DropdownOption value={DEFAULT_DROPDOWN_SELECTION}/>
{options.map(option =>
<DropdownOption value={option}/>
)}
</select>
</div>
</div>
);
}

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

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

this.setState({selectedOption: selected}, () => {
store.selectedChunks = [];
for (const chunk of store.allChunks) {
if (store.entrypointsToChunksMap[this.state.selectedOption].has(chunk.label)) {
store.selectedChunks.push(chunk);
}
}
});

// this.setState({selectedOption: selected}, () => {
// store.selectedChunks = [];
// for (const chunk of store.allChunks) {
// if (chunk.label in store.entrypointsToChunksMap[this.state.selectedOption]) {
// store.selectedChunks.push(chunk);
// }
// }
// });
}
}
11 changes: 11 additions & 0 deletions client/components/DropdownOption.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Component} from 'preact';

export default class DropdownOption extends Component {
render() {
const {value} = this.props;

return (
<option value={value}>{value}</option>
);
}
}
9 changes: 9 additions & 0 deletions client/components/ModulesTreemap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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'},
Expand Down Expand Up @@ -78,6 +79,10 @@ export default class ModulesTreemap extends Component {
</div>
}
</div>
<div className={s.sidebarGroup}>
<Dropdown label="Filter to initial chunks"
options={this.entrypoints}/>
</div>
<div className={s.sidebarGroup}>
<Search label="Search modules"
query={store.searchQuery}
Expand Down Expand Up @@ -161,6 +166,10 @@ export default class ModulesTreemap extends Component {
];
};

@computed get entrypoints() {
return Object.keys(store.entrypointsToChunksMap);
}

@computed get sizeSwitchItems() {
return store.hasParsedSizes ? SIZE_SWITCH_ITEMS : SIZE_SWITCH_ITEMS.slice(0, 1);
}
Expand Down
12 changes: 12 additions & 0 deletions client/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ export class Store {
this.selectedChunks = this.allChunks;
}
setEntrypoints(_entrypointsToChunksMap) {
// this.entrypointsToChunksMap = {};
// Object.entries(entrypointsToChunksMap).forEach(([entrypoint, chunkList]) => {
// this.entrypointsToChunksMap[entrypoint] = new Set(chunkList);
// });
this.entrypointsToChunksMap = Object.entries(_entrypointsToChunksMap).reduce((entrypointsToChunksMap, [entrypoint, chunks]) => {
entrypointsToChunksMap[entrypoint] = new Set(chunks);
return entrypointsToChunksMap;
}, {});
}
@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.entrypointsToChunksMap);
render(
<ModulesTreemap/>,
document.getElementById('app')
Expand Down
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, entrypointsToChunksMap, 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.entrypointsToChunksMap = ${escapeJson(entrypointsToChunksMap)};
window.defaultSizes = ${escapeJson(defaultSizes)};
</script>
</body>
Expand Down
5 changes: 5 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ exports.open = function (uri, logger) {
logger.debug(`Opener failed to open "${uri}":\n${err}`);
}
};

exports.isJsFile = function (fileName) {
const JS_REGEX = /\.js$/u;
return JS_REGEX.test(fileName);
};
27 changes: 25 additions & 2 deletions src/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const {bold} = require('chalk');

const Logger = require('./Logger');
const analyzer = require('./analyzer');
const {open} = require('./utils');
const {isJsFile, open} = require('./utils');
const {renderViewer} = require('./template');

const projectRoot = path.resolve(__dirname, '..');
Expand All @@ -26,6 +26,7 @@ module.exports = {
startServer,
generateReport,
generateJSONReport,
getEntrypointsToChunksMap,
// deprecated
start: startServer
};
Expand All @@ -45,8 +46,9 @@ async function startServer(bundleStats, opts) {
const analyzerOpts = {logger, excludeAssets};

let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
const entrypointsToChunksMap = getEntrypointsToChunksMap(bundleStats);

if (!chartData) return;
if (!chartData || !entrypointsToChunksMap) return;

const sirvMiddleware = sirv(`${projectRoot}/public`, {
// disables caching and traverse the file system on every request
Expand All @@ -59,6 +61,7 @@ async function startServer(bundleStats, opts) {
mode: 'server',
title: resolveTitle(reportTitle),
chartData,
entrypointsToChunksMap,
defaultSizes,
enableWebSocket: true
});
Expand Down Expand Up @@ -133,13 +136,15 @@ async function generateReport(bundleStats, opts) {
} = opts || {};

const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir);
const entrypointsToChunksMap = getEntrypointsToChunksMap(bundleStats);

if (!chartData) return;

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

return chartData;
}

function getEntrypointsToChunksMap(bundleStats) {
if (bundleStats === null || bundleStats === undefined) {
return {};
}

return Object.values(bundleStats.entrypoints || {}).reduce((entrypointsToChunksMap, entrypoint) => {
if (!(entrypoint.name in entrypointsToChunksMap)) {
entrypointsToChunksMap[entrypoint.name] = [];
}
entrypoint.assets.forEach(asset => {
if (isJsFile(asset.name)) {
entrypointsToChunksMap[entrypoint.name].push(asset.name);
}
});
return entrypointsToChunksMap;
}, {});
}
15 changes: 14 additions & 1 deletion test/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const chai = require('chai');
chai.use(require('chai-subset'));
const {expect} = chai;
const {createAssetsFilter} = require('../lib/utils');
const {createAssetsFilter, isJsFile} = require('../lib/utils');

describe('createAssetsFilter', function () {

Expand Down Expand Up @@ -57,3 +57,16 @@ describe('createAssetsFilter', function () {
});

});

describe('isJsFile', function () {
it('should recognize .js files', function () {
expect(isJsFile('file.js')).to.equal(true);
expect(isJsFile('file.ts')).to.equal(false);
expect(isJsFile('file.css')).to.equal(false);
});

it('should filter out .js files', function () {
const DUMMY_FILES = ['file.js', 'file.css', 'file.ts'];
expect(DUMMY_FILES.filter(isJsFile).join()).to.equal(['file.js'].join());
});
});
36 changes: 35 additions & 1 deletion test/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const crypto = require('crypto');
const net = require('net');

const Logger = require('../lib/Logger');
const {startServer} = require('../lib/viewer.js');
const {getEntrypointsToChunksMap, startServer} = require('../lib/viewer.js');

describe('WebSocket server', function () {
it('should not crash when an error is emitted on the websocket', function (done) {
Expand Down Expand Up @@ -69,3 +69,37 @@ describe('WebSocket server', function () {
.catch(done);
});
});

describe('getEntrypointsToChunksMap', function () {
it('should map entrypoints correctly to chuks', function () {
const bundleStats = {
entrypoints: {
'entrypoint': {
name: 'entrypoint',
assets: [
{
name: 'chunk.js'
},
{
name: 'chunk.css'
}
]
}
}
};
expect(JSON.stringify(getEntrypointsToChunksMap(bundleStats))).to.equal(JSON.stringify({
'entrypoint': ['chunk.js']
}));
});

it('should handle when bundle stats does not have entrypoints', function () {
const bundleStatsWithoutEntryPoints = {};
expect(JSON.stringify(getEntrypointsToChunksMap(bundleStatsWithoutEntryPoints))).to.equal(JSON.stringify({}));
});

it('should handle when entrypoints is empty', function () {
const bundleStatsEmptyEntryPoint = {entrypoints: {}};
expect(JSON.stringify(getEntrypointsToChunksMap(bundleStatsEmptyEntryPoint))).to.equal(JSON.stringify({}));
});

});

0 comments on commit a632593

Please sign in to comment.