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 | getEntrypoints,
|
46 |
|
47 | start: startServer
|
48 | };
|
49 |
|
50 | async function startServer(bundleStats, opts) {
|
51 | const {
|
52 | port = 8888,
|
53 | host = '127.0.0.1',
|
54 | openBrowser = true,
|
55 | bundleDir = null,
|
56 | logger = new Logger(),
|
57 | defaultSizes = 'parsed',
|
58 | excludeAssets = null,
|
59 | reportTitle,
|
60 | analyzerUrl
|
61 | } = opts || {};
|
62 | const analyzerOpts = {
|
63 | logger,
|
64 | excludeAssets
|
65 | };
|
66 | let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
67 | const entrypoints = getEntrypoints(bundleStats);
|
68 | if (!chartData) return;
|
69 | const sirvMiddleware = sirv(`${projectRoot}/public`, {
|
70 |
|
71 | dev: true
|
72 | });
|
73 | const server = http.createServer((req, res) => {
|
74 | if (req.method === 'GET' && req.url === '/') {
|
75 | const html = renderViewer({
|
76 | mode: 'server',
|
77 | title: resolveTitle(reportTitle),
|
78 | chartData,
|
79 | entrypoints,
|
80 | defaultSizes,
|
81 | enableWebSocket: true
|
82 | });
|
83 | res.writeHead(200, {
|
84 | 'Content-Type': 'text/html'
|
85 | });
|
86 | res.end(html);
|
87 | } else {
|
88 | sirvMiddleware(req, res);
|
89 | }
|
90 | });
|
91 | await new Promise(resolve => {
|
92 | server.listen(port, host, () => {
|
93 | resolve();
|
94 | const url = analyzerUrl({
|
95 | listenPort: port,
|
96 | listenHost: host,
|
97 | boundAddress: server.address()
|
98 | });
|
99 | logger.info(`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` + `Use ${bold('Ctrl+C')} to close it`);
|
100 |
|
101 | if (openBrowser) {
|
102 | open(url, logger);
|
103 | }
|
104 | });
|
105 | });
|
106 | const wss = new WebSocket.Server({
|
107 | server
|
108 | });
|
109 | wss.on('connection', ws => {
|
110 | ws.on('error', err => {
|
111 |
|
112 | if (err.errno) return;
|
113 | logger.info(err.message);
|
114 | });
|
115 | });
|
116 | return {
|
117 | ws: wss,
|
118 | http: server,
|
119 | updateChartData
|
120 | };
|
121 |
|
122 | function updateChartData(bundleStats) {
|
123 | const newChartData = getChartData(analyzerOpts, bundleStats, bundleDir);
|
124 | if (!newChartData) return;
|
125 | chartData = newChartData;
|
126 | wss.clients.forEach(client => {
|
127 | if (client.readyState === WebSocket.OPEN) {
|
128 | client.send(JSON.stringify({
|
129 | event: 'chartDataUpdated',
|
130 | data: newChartData
|
131 | }));
|
132 | }
|
133 | });
|
134 | }
|
135 | }
|
136 |
|
137 | async function generateReport(bundleStats, opts) {
|
138 | const {
|
139 | openBrowser = true,
|
140 | reportFilename,
|
141 | reportTitle,
|
142 | bundleDir = null,
|
143 | logger = new Logger(),
|
144 | defaultSizes = 'parsed',
|
145 | excludeAssets = null
|
146 | } = opts || {};
|
147 | const chartData = getChartData({
|
148 | logger,
|
149 | excludeAssets
|
150 | }, bundleStats, bundleDir);
|
151 | const entrypoints = getEntrypoints(bundleStats);
|
152 | if (!chartData) return;
|
153 | const reportHtml = renderViewer({
|
154 | mode: 'static',
|
155 | title: resolveTitle(reportTitle),
|
156 | chartData,
|
157 | entrypoints,
|
158 | defaultSizes,
|
159 | enableWebSocket: false
|
160 | });
|
161 | const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename);
|
162 | fs.mkdirSync(path.dirname(reportFilepath), {
|
163 | recursive: true
|
164 | });
|
165 | fs.writeFileSync(reportFilepath, reportHtml);
|
166 | logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);
|
167 |
|
168 | if (openBrowser) {
|
169 | open(`file://${reportFilepath}`, logger);
|
170 | }
|
171 | }
|
172 |
|
173 | async function generateJSONReport(bundleStats, opts) {
|
174 | const {
|
175 | reportFilename,
|
176 | bundleDir = null,
|
177 | logger = new Logger(),
|
178 | excludeAssets = null
|
179 | } = opts || {};
|
180 | const chartData = getChartData({
|
181 | logger,
|
182 | excludeAssets
|
183 | }, bundleStats, bundleDir);
|
184 | if (!chartData) return;
|
185 | await fs.promises.mkdir(path.dirname(reportFilename), {
|
186 | recursive: true
|
187 | });
|
188 | await fs.promises.writeFile(reportFilename, JSON.stringify(chartData));
|
189 | logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
|
190 | }
|
191 |
|
192 | function getChartData(analyzerOpts, ...args) {
|
193 | let chartData;
|
194 | const {
|
195 | logger
|
196 | } = analyzerOpts;
|
197 |
|
198 | try {
|
199 | chartData = analyzer.getViewerData(...args, analyzerOpts);
|
200 | } catch (err) {
|
201 | logger.error(`Could't analyze webpack bundle:\n${err}`);
|
202 | logger.debug(err.stack);
|
203 | chartData = null;
|
204 | }
|
205 |
|
206 | if (_.isPlainObject(chartData) && _.isEmpty(chartData)) {
|
207 | logger.error("Could't find any javascript bundles in provided stats file");
|
208 | chartData = null;
|
209 | }
|
210 |
|
211 | return chartData;
|
212 | }
|
213 |
|
214 | function getEntrypoints(bundleStats) {
|
215 | if (bundleStats === null || bundleStats === undefined) {
|
216 | return [];
|
217 | }
|
218 |
|
219 | return Object.values(bundleStats.entrypoints || {}).map(entrypoint => entrypoint.name);
|
220 | } |
\ | No newline at end of file |