1 | "use strict";
|
2 |
|
3 | const path = require('path');
|
4 |
|
5 | const fs = require('fs');
|
6 |
|
7 | const http = require('http');
|
8 |
|
9 | const WebSocket = require('ws');
|
10 |
|
11 | const sirv = require('sirv');
|
12 |
|
13 | const _ = require('lodash');
|
14 |
|
15 | const {
|
16 | bold
|
17 | } = require('chalk');
|
18 |
|
19 | const Logger = require('./Logger');
|
20 |
|
21 | const analyzer = require('./analyzer');
|
22 |
|
23 | const {
|
24 | open
|
25 | } = require('./utils');
|
26 |
|
27 | const {
|
28 | renderViewer
|
29 | } = require('./template');
|
30 |
|
31 | const projectRoot = path.resolve(__dirname, '..');
|
32 |
|
33 | function resolveTitle(reportTitle) {
|
34 | if (typeof reportTitle === 'function') {
|
35 | return reportTitle();
|
36 | } else {
|
37 | return reportTitle;
|
38 | }
|
39 | }
|
40 |
|
41 | module.exports = {
|
42 | startServer,
|
43 | generateReport,
|
44 | generateJSONReport,
|
45 |
|
46 | start: startServer
|
47 | };
|
48 |
|
49 | async function startServer(bundleStats, opts) {
|
50 | const {
|
51 | port = 8888,
|
52 | host = '127.0.0.1',
|
53 | openBrowser = true,
|
54 | bundleDir = null,
|
55 | logger = new Logger(),
|
56 | defaultSizes = 'parsed',
|
57 | excludeAssets = null,
|
58 | reportTitle
|
59 | } = opts || {};
|
60 | const analyzerOpts = {
|
61 | logger,
|
62 | excludeAssets
|
63 | };
|
64 | let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
65 | if (!chartData) return;
|
66 | const sirvMiddleware = sirv(`${projectRoot}/public`, {
|
67 |
|
68 | dev: true
|
69 | });
|
70 | const server = http.createServer((req, res) => {
|
71 | if (req.method === 'GET' && req.url === '/') {
|
72 | const html = renderViewer({
|
73 | mode: 'server',
|
74 | title: resolveTitle(reportTitle),
|
75 | chartData,
|
76 | defaultSizes,
|
77 | enableWebSocket: true
|
78 | });
|
79 | res.writeHead(200, {
|
80 | 'Content-Type': 'text/html'
|
81 | });
|
82 | res.end(html);
|
83 | } else {
|
84 | sirvMiddleware(req, res);
|
85 | }
|
86 | });
|
87 | await new Promise(resolve => {
|
88 | server.listen(port, host, () => {
|
89 | resolve();
|
90 | const url = `http://${host}:${server.address().port}`;
|
91 | logger.info(`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` + `Use ${bold('Ctrl+C')} to close it`);
|
92 |
|
93 | if (openBrowser) {
|
94 | open(url, logger);
|
95 | }
|
96 | });
|
97 | });
|
98 | const wss = new WebSocket.Server({
|
99 | server
|
100 | });
|
101 | wss.on('connection', ws => {
|
102 | ws.on('error', err => {
|
103 |
|
104 | if (err.errno) return;
|
105 | logger.info(err.message);
|
106 | });
|
107 | });
|
108 | return {
|
109 | ws: wss,
|
110 | http: server,
|
111 | updateChartData
|
112 | };
|
113 |
|
114 | function updateChartData(bundleStats) {
|
115 | const newChartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
116 | if (!newChartData) return;
|
117 | chartData = newChartData;
|
118 | wss.clients.forEach(client => {
|
119 | if (client.readyState === WebSocket.OPEN) {
|
120 | client.send(JSON.stringify({
|
121 | event: 'chartDataUpdated',
|
122 | data: newChartData
|
123 | }));
|
124 | }
|
125 | });
|
126 | }
|
127 | }
|
128 |
|
129 | async function generateReport(bundleStats, opts) {
|
130 | const {
|
131 | openBrowser = true,
|
132 | reportFilename,
|
133 | reportTitle,
|
134 | bundleDir = null,
|
135 | logger = new Logger(),
|
136 | defaultSizes = 'parsed',
|
137 | excludeAssets = null
|
138 | } = opts || {};
|
139 | const chartData = getChartData({
|
140 | logger,
|
141 | excludeAssets
|
142 | }, bundleStats, bundleDir);
|
143 | if (!chartData) return;
|
144 | const reportHtml = renderViewer({
|
145 | mode: 'static',
|
146 | title: resolveTitle(reportTitle),
|
147 | chartData,
|
148 | defaultSizes,
|
149 | enableWebSocket: false
|
150 | });
|
151 | const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);
|
152 | fs.mkdirSync(path.dirname(reportFilepath), {
|
153 | recursive: true
|
154 | });
|
155 | fs.writeFileSync(reportFilepath, reportHtml);
|
156 | logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);
|
157 |
|
158 | if (openBrowser) {
|
159 | open(`file://${reportFilepath}`, logger);
|
160 | }
|
161 | }
|
162 |
|
163 | async function generateJSONReport(bundleStats, opts) {
|
164 | const {
|
165 | reportFilename,
|
166 | bundleDir = null,
|
167 | logger = new Logger(),
|
168 | excludeAssets = null
|
169 | } = opts || {};
|
170 | const chartData = getChartData({
|
171 | logger,
|
172 | excludeAssets
|
173 | }, bundleStats, bundleDir);
|
174 | if (!chartData) return;
|
175 | await fs.promises.mkdir(path.dirname(reportFilename), {
|
176 | recursive: true
|
177 | });
|
178 | await fs.promises.writeFile(reportFilename, JSON.stringify(chartData));
|
179 | logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
|
180 | }
|
181 |
|
182 | function getChartData(analyzerOpts, ...args) {
|
183 | let chartData;
|
184 | const {
|
185 | logger
|
186 | } = analyzerOpts;
|
187 |
|
188 | try {
|
189 | chartData = analyzer.getViewerData(...args, analyzerOpts);
|
190 | } catch (err) {
|
191 | logger.error(`Could't analyze webpack bundle:\n${err}`);
|
192 | logger.debug(err.stack);
|
193 | chartData = null;
|
194 | }
|
195 |
|
196 | if (_.isPlainObject(chartData) && _.isEmpty(chartData)) {
|
197 | logger.error("Could't find any javascript bundles in provided stats file");
|
198 | chartData = null;
|
199 | }
|
200 |
|
201 | return chartData;
|
202 | } |
\ | No newline at end of file |