You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
4.6 KiB
202 lines
4.6 KiB
"use strict"; |
|
|
|
const path = require('path'); |
|
|
|
const fs = require('fs'); |
|
|
|
const http = require('http'); |
|
|
|
const WebSocket = require('ws'); |
|
|
|
const sirv = require('sirv'); |
|
|
|
const _ = require('lodash'); |
|
|
|
const { |
|
bold |
|
} = require('chalk'); |
|
|
|
const Logger = require('./Logger'); |
|
|
|
const analyzer = require('./analyzer'); |
|
|
|
const { |
|
open |
|
} = require('./utils'); |
|
|
|
const { |
|
renderViewer |
|
} = require('./template'); |
|
|
|
const projectRoot = path.resolve(__dirname, '..'); |
|
|
|
function resolveTitle(reportTitle) { |
|
if (typeof reportTitle === 'function') { |
|
return reportTitle(); |
|
} else { |
|
return reportTitle; |
|
} |
|
} |
|
|
|
module.exports = { |
|
startServer, |
|
generateReport, |
|
generateJSONReport, |
|
// deprecated |
|
start: startServer |
|
}; |
|
|
|
async function startServer(bundleStats, opts) { |
|
const { |
|
port = 8888, |
|
host = '127.0.0.1', |
|
openBrowser = true, |
|
bundleDir = null, |
|
logger = new Logger(), |
|
defaultSizes = 'parsed', |
|
excludeAssets = null, |
|
reportTitle |
|
} = opts || {}; |
|
const analyzerOpts = { |
|
logger, |
|
excludeAssets |
|
}; |
|
let chartData = getChartData(analyzerOpts, bundleStats, bundleDir); |
|
if (!chartData) return; |
|
const sirvMiddleware = sirv(`${projectRoot}/public`, { |
|
// disables caching and traverse the file system on every request |
|
dev: true |
|
}); |
|
const server = http.createServer((req, res) => { |
|
if (req.method === 'GET' && req.url === '/') { |
|
const html = renderViewer({ |
|
mode: 'server', |
|
title: resolveTitle(reportTitle), |
|
chartData, |
|
defaultSizes, |
|
enableWebSocket: true |
|
}); |
|
res.writeHead(200, { |
|
'Content-Type': 'text/html' |
|
}); |
|
res.end(html); |
|
} else { |
|
sirvMiddleware(req, res); |
|
} |
|
}); |
|
await new Promise(resolve => { |
|
server.listen(port, host, () => { |
|
resolve(); |
|
const url = `http://${host}:${server.address().port}`; |
|
logger.info(`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` + `Use ${bold('Ctrl+C')} to close it`); |
|
|
|
if (openBrowser) { |
|
open(url, logger); |
|
} |
|
}); |
|
}); |
|
const wss = new WebSocket.Server({ |
|
server |
|
}); |
|
wss.on('connection', ws => { |
|
ws.on('error', err => { |
|
// Ignore network errors like `ECONNRESET`, `EPIPE`, etc. |
|
if (err.errno) return; |
|
logger.info(err.message); |
|
}); |
|
}); |
|
return { |
|
ws: wss, |
|
http: server, |
|
updateChartData |
|
}; |
|
|
|
function updateChartData(bundleStats) { |
|
const newChartData = getChartData(analyzerOpts, bundleStats, bundleDir); |
|
if (!newChartData) return; |
|
chartData = newChartData; |
|
wss.clients.forEach(client => { |
|
if (client.readyState === WebSocket.OPEN) { |
|
client.send(JSON.stringify({ |
|
event: 'chartDataUpdated', |
|
data: newChartData |
|
})); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
async function generateReport(bundleStats, opts) { |
|
const { |
|
openBrowser = true, |
|
reportFilename, |
|
reportTitle, |
|
bundleDir = null, |
|
logger = new Logger(), |
|
defaultSizes = 'parsed', |
|
excludeAssets = null |
|
} = opts || {}; |
|
const chartData = getChartData({ |
|
logger, |
|
excludeAssets |
|
}, bundleStats, bundleDir); |
|
if (!chartData) return; |
|
const reportHtml = renderViewer({ |
|
mode: 'static', |
|
title: resolveTitle(reportTitle), |
|
chartData, |
|
defaultSizes, |
|
enableWebSocket: false |
|
}); |
|
const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename); |
|
fs.mkdirSync(path.dirname(reportFilepath), { |
|
recursive: true |
|
}); |
|
fs.writeFileSync(reportFilepath, reportHtml); |
|
logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`); |
|
|
|
if (openBrowser) { |
|
open(`file://${reportFilepath}`, logger); |
|
} |
|
} |
|
|
|
async function generateJSONReport(bundleStats, opts) { |
|
const { |
|
reportFilename, |
|
bundleDir = null, |
|
logger = new Logger(), |
|
excludeAssets = null |
|
} = opts || {}; |
|
const chartData = getChartData({ |
|
logger, |
|
excludeAssets |
|
}, bundleStats, bundleDir); |
|
if (!chartData) return; |
|
await fs.promises.mkdir(path.dirname(reportFilename), { |
|
recursive: true |
|
}); |
|
await fs.promises.writeFile(reportFilename, JSON.stringify(chartData)); |
|
logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`); |
|
} |
|
|
|
function getChartData(analyzerOpts, ...args) { |
|
let chartData; |
|
const { |
|
logger |
|
} = analyzerOpts; |
|
|
|
try { |
|
chartData = analyzer.getViewerData(...args, analyzerOpts); |
|
} catch (err) { |
|
logger.error(`Could't analyze webpack bundle:\n${err}`); |
|
logger.debug(err.stack); |
|
chartData = null; |
|
} |
|
|
|
if (_.isPlainObject(chartData) && _.isEmpty(chartData)) { |
|
logger.error("Could't find any javascript bundles in provided stats file"); |
|
chartData = null; |
|
} |
|
|
|
return chartData; |
|
} |