Skip to content

Commit

Permalink
Support webpack --watch mode
Browse files Browse the repository at this point in the history
  • Loading branch information
th0r committed May 12, 2017
1 parent bfaf8ef commit 279bb26
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 210 deletions.
16 changes: 13 additions & 3 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
// Babel config for Node
// Compiles sources, gulpfile and tests
{
"presets": [
["es2015"],
"stage-2"
["env", {
"targets": { "node": 4 },
"exclude": [
// Node 4 supports all the generator features that we use so there is no need in Regenerator
"transform-regenerator"
]
}]
],
"plugins": ["transform-react-jsx"]
"plugins": [
["transform-object-rest-spread", { "useBuiltIns": true }],
"transform-class-properties"
]
}
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ client/vendor
# Test fixtures
test/bundles

# Test results
test/output

samples
18 changes: 18 additions & 0 deletions client/components/CheckboxList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ export default class CheckboxList extends Component {
};
}

componentWillReceiveProps(newProps) {
if (newProps.items !== this.props.items) {
if (this.isAllChecked()) {
// Preserving `all checked` state
this.setState({ checkedItems: newProps.items });
this.informAboutChange(newProps.items);
} else {
// Filtering out checked items if new items don't contain them
const checkedItems = this.state.checkedItems.filter(item => newProps.items.includes(item));

if (checkedItems.length !== this.state.checkedItems.length) {
this.setState({ checkedItems });
this.informAboutChange(checkedItems);
}
}
}
}

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

Expand Down
45 changes: 28 additions & 17 deletions client/components/ModulesTreemap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,13 @@ export default class ModulesTreemap extends Component {

constructor(props) {
super(props);
this.treemap = null;
this.hasParsedSizes = (typeof props.data[0].parsedSize === 'number');
this.sizeSwitchItems = this.hasParsedSizes ? SIZE_SWITCH_ITEMS : SIZE_SWITCH_ITEMS.slice(0, 1);
let activeSizeItem = this.sizeSwitchItems.find(item => item.prop === `${props.defaultSizes}Size`);
if (!activeSizeItem) activeSizeItem = this.sizeSwitchItems[0];

this.chunkItems = [...props.data]
.sort((chunk1, chunk2) => compareStrings(chunk1.label, chunk2.label))
.map(chunk => ({ label: chunk.label }));
this.setData(props.data, true);
}

this.state = {
data: props.data,
showTooltip: false,
tooltipContent: null,
activeSizeItem
};
componentWillReceiveProps(newProps) {
if (newProps.data !== this.props.data) {
this.setData(newProps.data);
}
}

render() {
Expand All @@ -52,10 +43,10 @@ export default class ModulesTreemap extends Component {
activeItem={activeSizeItem}
onSwitch={this.handleSizeSwitch}/>
</div>
{this.chunkItems.length > 1 &&
{this.state.chunkItems.length > 1 &&
<div className={s.sidebarGroup}>
<CheckboxList label="Show chunks"
items={this.chunkItems}
items={this.state.chunkItems}
onChange={this.handleVisibleChunksChange}/>
</div>
}
Expand Down Expand Up @@ -115,6 +106,26 @@ export default class ModulesTreemap extends Component {
}
};

setData(data, initial) {
const hasParsedSizes = (typeof data[0].parsedSize === 'number');
this.sizeSwitchItems = hasParsedSizes ? SIZE_SWITCH_ITEMS : SIZE_SWITCH_ITEMS.slice(0, 1);
const activeSizeItemProp = initial ? `${this.props.defaultSizes}Size` : this.state.activeSizeItem.prop;
let activeSizeItem = this.sizeSwitchItems.find(item => item.prop === activeSizeItemProp);
if (!activeSizeItem) activeSizeItem = this.sizeSwitchItems[0];

const chunkItems = [...data]
.sort((chunk1, chunk2) => compareStrings(chunk1.label, chunk2.label))
.map(chunk => ({ label: chunk.label }));

this.setState({
data,
showTooltip: false,
tooltipContent: null,
activeSizeItem,
chunkItems
});
}

getTooltipContent(module) {
if (!module) return null;

Expand Down
34 changes: 30 additions & 4 deletions client/viewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,35 @@ import ModulesTreemap from './components/ModulesTreemap';
/* eslint no-unused-vars: "off" */
import styles from './viewer.css';

window.addEventListener('load', () => {
render(
<ModulesTreemap data={window.chartData} defaultSizes={window.defaultSizes}/>,
document.getElementById('app')
// Initializing WebSocket for live treemap updates
let ws;
try {
ws = new WebSocket(`ws://${location.host}`);
} catch (err) {
console.warn(
"Your browser doesn't support WebSockets so you'll have to reload page manually to see updates in the treemap"
);
}

window.addEventListener('load', () => {
renderApp(window.chartData);

if (ws) {
ws.addEventListener('message', event => {
const msg = JSON.parse(event.data);

if (msg.event === 'chartDataUpdated') {
renderApp(msg.data);
}
});
}
}, false);

let app;
function renderApp(chartData, initialRender) {
app = render(
<ModulesTreemap data={chartData} defaultSizes={window.defaultSizes}/>,
document.getElementById('app'),
app
);
}
2 changes: 0 additions & 2 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require('babel-polyfill');

const { resolve, relative } = require('path');

const gulp = require('gulp');
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"build": "gulp build",
"npm-publish": "npm run lint && npm run build && npm test && npm publish",
"lint": "eslint --ext js,jsx .",
"test": "mocha --compilers js:babel-core/register -r babel-polyfill",
"test-dev": "mocha --watch --compilers js:babel-core/register -r babel-polyfill"
"test": "mocha --compilers js:babel-core/register",
"test-dev": "mocha --watch --compilers js:babel-core/register"
},
"files": [
"public",
Expand All @@ -42,17 +42,17 @@
"gzip-size": "^3.0.0",
"lodash": "^4.17.4",
"mkdirp": "^0.5.1",
"opener": "^1.4.3"
"opener": "^1.4.3",
"ws": "^2.3.1"
},
"devDependencies": {
"babel-core": "6.24.1",
"babel-eslint": "7.2.3",
"babel-loader": "7.0.0",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-plugin-transform-object-rest-spread": "6.23.0",
"babel-plugin-transform-react-jsx": "6.24.1",
"babel-plugin-transform-runtime": "6.23.0",
"babel-polyfill": "6.23.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-2": "6.24.1",
"babel-preset-env": "1.4.0",
"chai": "3.5.0",
"chai-subset": "1.5.0",
"classnames": "2.2.5",
Expand Down
23 changes: 14 additions & 9 deletions src/BundleAnalyzerPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class BundleAnalyzerPlugin {
...opts
};

this.server = null;
this.logger = new Logger(this.opts.logLevel);
}

Expand Down Expand Up @@ -79,15 +80,19 @@ class BundleAnalyzerPlugin {
);
}

startAnalyzerServer(stats) {
viewer.startServer(stats, {
openBrowser: this.opts.openAnalyzer,
host: this.opts.analyzerHost,
port: this.opts.analyzerPort,
bundleDir: this.compiler.outputPath,
logger: this.logger,
defaultSizes: this.opts.defaultSizes
});
async startAnalyzerServer(stats) {
if (this.server) {
(await this.server).updateChartData(stats);
} else {
this.server = viewer.startServer(stats, {
openBrowser: this.opts.openAnalyzer,
host: this.opts.analyzerHost,
port: this.opts.analyzerPort,
bundleDir: this.compiler.outputPath,
logger: this.logger,
defaultSizes: this.opts.defaultSizes
});
}
}

generateStaticReport(stats) {
Expand Down
57 changes: 45 additions & 12 deletions src/viewer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const path = require('path');
const fs = require('fs');
const http = require('http');

const WebSocket = require('ws');
const _ = require('lodash');
const express = require('express');
const ejs = require('ejs');
Expand All @@ -20,7 +22,7 @@ module.exports = {
start: startServer
};

function startServer(bundleStats, opts) {
async function startServer(bundleStats, opts) {
const {
port = 8888,
host = '127.0.0.1',
Expand All @@ -30,7 +32,7 @@ function startServer(bundleStats, opts) {
defaultSizes = 'parsed'
} = opts || {};

const chartData = getChartData(logger, bundleStats, bundleDir);
let chartData = getChartData(logger, bundleStats, bundleDir);

if (!chartData) return;

Expand All @@ -46,23 +48,54 @@ function startServer(bundleStats, opts) {
app.use('/', (req, res) => {
res.render('viewer', {
mode: 'server',
chartData: JSON.stringify(chartData),
get chartData() { return JSON.stringify(chartData) },
defaultSizes: JSON.stringify(defaultSizes)
});
});

return app.listen(port, host, () => {
const url = `http://${host}:${port}`;
const server = http.createServer(app);

logger.info(
`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` +
`Use ${bold('Ctrl+C')} to close it`
);
await new Promise(resolve => {
server.listen(port, host, () => {
resolve();

if (openBrowser) {
opener(url);
}
const url = `http://${host}:${port}`;

logger.info(
`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` +
`Use ${bold('Ctrl+C')} to close it`
);

if (openBrowser) {
opener(url);
}
});
});

const wss = new WebSocket.Server({ server });

return {
ws: wss,
http: server,
updateChartData
};

function updateChartData(bundleStats) {
const newChartData = getChartData(logger, bundleStats, bundleDir);

if (!newChartData) return;

chartData = newChartData;

wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
event: 'chartDataUpdated',
data: newChartData
}));
}
});
}
}

function generateReport(bundleStats, opts) {
Expand Down
3 changes: 3 additions & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
output

# Sandbox config
webpack.config.js
23 changes: 16 additions & 7 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ const webpack = require('webpack');
const BundleAnalyzePlugin = require('./lib/BundleAnalyzerPlugin');

module.exports = opts => {
opts = {
opts = Object.assign({
env: 'dev',
analyze: false,
...opts
};
analyze: false
}, opts);

return {
context: __dirname,
Expand All @@ -29,15 +28,25 @@ module.exports = opts => {
watch: (opts.env === 'dev'),

module: {
loaders: [
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules|client\/vendor)/,
loader: 'babel-loader'
loader: 'babel-loader',
options: {
presets: [
['env', { targets: { uglify: true } }]
],
plugins: [
'transform-class-properties',
'transform-react-jsx',
['transform-object-rest-spread', { useBuiltIns: true }]
]
}
},
{
test: /\.css$/,
loaders: [
use: [
'style-loader',
{
loader: 'css-loader',
Expand Down
Loading

0 comments on commit 279bb26

Please sign in to comment.