UNPKG

5.2 kBJavaScriptView Raw
1"use strict";
2
3const path = require('path');
4
5const fs = require('fs');
6
7const http = require('http');
8
9const WebSocket = require('ws');
10
11const sirv = require('sirv');
12
13const _ = require('lodash');
14
15const {
16 bold
17} = require('chalk');
18
19const Logger = require('./Logger');
20
21const analyzer = require('./analyzer');
22
23const {
24 open
25} = require('./utils');
26
27const {
28 renderViewer
29} = require('./template');
30
31const projectRoot = path.resolve(__dirname, '..');
32
33function resolveTitle(reportTitle) {
34 if (typeof reportTitle === 'function') {
35 return reportTitle();
36 } else {
37 return reportTitle;
38 }
39}
40
41module.exports = {
42 startServer,
43 generateReport,
44 generateJSONReport,
45 getEntrypoints,
46 // deprecated
47 start: startServer
48};
49
50async 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 // disables caching and traverse the file system on every request
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 // Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
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
137async 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
173async 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
192function 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
214function 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