From e343d8a8eb4ae6002706beb9e177548dac535d5f Mon Sep 17 00:00:00 2001 From: wood1986 <5212215+wood1986@users.noreply.github.com> Date: Wed, 13 Oct 2021 00:07:15 -0700 Subject: [PATCH 1/2] fix: plugin uses infrastructureLogger and outputs the report.html to compiler.outputFileSystem --- src/BundleAnalyzerPlugin.js | 17 ++++++--- src/bin/analyzer.js | 7 +++- src/viewer.js | 20 ++++++---- test/dev-server.js | 76 ++++++++++++++++++++++--------------- 4 files changed, 76 insertions(+), 44 deletions(-) diff --git a/src/BundleAnalyzerPlugin.js b/src/BundleAnalyzerPlugin.js index 9e5c2842..f1d26408 100644 --- a/src/BundleAnalyzerPlugin.js +++ b/src/BundleAnalyzerPlugin.js @@ -1,4 +1,3 @@ -const fs = require('fs'); const path = require('path'); const {bold} = require('chalk'); @@ -6,6 +5,7 @@ const Logger = require('./Logger'); const viewer = require('./viewer'); const utils = require('./utils'); const {writeStats} = require('./statsUtils'); +const PLUGIN_NAME = 'webpack-bundle-analyzer'; class BundleAnalyzerPlugin { constructor(opts = {}) { @@ -35,6 +35,8 @@ class BundleAnalyzerPlugin { this.compiler = compiler; const done = (stats, callback) => { + const isWebpack5 = !!compiler.webpack; + this.fs = isWebpack5 ? compiler.outputFileSystem : require('fs'); callback = callback || (() => {}); const actions = []; @@ -72,7 +74,10 @@ class BundleAnalyzerPlugin { }; if (compiler.hooks) { - compiler.hooks.done.tapAsync('webpack-bundle-analyzer', done); + compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { + this.logger = compilation.getLogger(PLUGIN_NAME); + }); + compiler.hooks.done.tapAsync(PLUGIN_NAME, done); } else { compiler.plugin('done', done); } @@ -80,7 +85,7 @@ class BundleAnalyzerPlugin { async generateStatsFile(stats) { const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename); - await fs.promises.mkdir(path.dirname(statsFilepath), {recursive: true}); + await this.fs.promises.mkdir(path.dirname(statsFilepath), {recursive: true}); try { await writeStats(stats, statsFilepath); @@ -117,7 +122,8 @@ class BundleAnalyzerPlugin { reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'), bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, - excludeAssets: this.opts.excludeAssets + excludeAssets: this.opts.excludeAssets, + fs: this.fs }); } @@ -129,7 +135,8 @@ class BundleAnalyzerPlugin { bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, defaultSizes: this.opts.defaultSizes, - excludeAssets: this.opts.excludeAssets + excludeAssets: this.opts.excludeAssets, + fs: this.fs }); } diff --git a/src/bin/analyzer.js b/src/bin/analyzer.js index 3a119732..43138fe1 100755 --- a/src/bin/analyzer.js +++ b/src/bin/analyzer.js @@ -4,6 +4,7 @@ const {resolve, dirname} = require('path'); const commander = require('commander'); const {magenta} = require('chalk'); +const fs = require('fs'); const analyzer = require('../analyzer'); const viewer = require('../viewer'); @@ -138,14 +139,16 @@ if (mode === 'server') { defaultSizes, bundleDir, excludeAssets, - logger: new Logger(logLevel) + logger: new Logger(logLevel), + fs }); } else if (mode === 'json') { viewer.generateJSONReport(bundleStats, { reportFilename: resolve(reportFilename || 'report.json'), bundleDir, excludeAssets, - logger: new Logger(logLevel) + logger: new Logger(logLevel), + fs }); } diff --git a/src/viewer.js b/src/viewer.js index 87304f54..dce34157 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1,5 +1,4 @@ const path = require('path'); -const fs = require('fs'); const http = require('http'); const WebSocket = require('ws'); @@ -22,6 +21,14 @@ function resolveTitle(reportTitle) { } } +function writeToFs(fs, dest, data) { + // older version webpack uses memory-fs whose mkdirSync does not support {recursive: true} + fs.mkdirpSync + ? fs.mkdirpSync(path.dirname(dest)) + : fs.mkdirSync(path.dirname(dest), {recursive: true}); + fs.writeFileSync(dest, data); +} + module.exports = { startServer, generateReport, @@ -129,7 +136,8 @@ async function generateReport(bundleStats, opts) { bundleDir = null, logger = new Logger(), defaultSizes = 'parsed', - excludeAssets = null + excludeAssets = null, + fs } = opts || {}; const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir); @@ -145,8 +153,7 @@ async function generateReport(bundleStats, opts) { }); const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename); - fs.mkdirSync(path.dirname(reportFilepath), {recursive: true}); - fs.writeFileSync(reportFilepath, reportHtml); + writeToFs(fs, reportFilepath, reportHtml); logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`); @@ -156,14 +163,13 @@ async function generateReport(bundleStats, opts) { } async function generateJSONReport(bundleStats, opts) { - const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null} = opts || {}; + const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null, fs} = 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)); + writeToFs(fs, reportFilename, JSON.stringify(chartData)); logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`); } diff --git a/test/dev-server.js b/test/dev-server.js index 6cea244d..ecd11588 100644 --- a/test/dev-server.js +++ b/test/dev-server.js @@ -1,42 +1,58 @@ const fs = require('fs'); -const {spawn} = require('child_process'); - -const del = require('del'); - const ROOT = `${__dirname}/dev-server`; const WEBPACK_CONFIG_PATH = `${ROOT}/webpack.config.js`; const webpackConfig = require(WEBPACK_CONFIG_PATH); +const DevServer = require('webpack-dev-server'); +const webpack = require('webpack'); describe('Webpack Dev Server', function () { - beforeAll(deleteOutputDirectory); - afterEach(deleteOutputDirectory); - - const timeout = 15000; - jest.setTimeout(timeout); - - it('should save report file to the output directory', function (done) { - const startedAt = Date.now(); - - const devServer = spawn(`${__dirname}/../node_modules/.bin/webpack-dev-server`, ['--config', WEBPACK_CONFIG_PATH], { - cwd: ROOT + it('should save report file to memory file system when writeToDisk is empty', async function () { + expect.assertions(2); + const compiler = webpack(webpackConfig); + const devServer = await new Promise((resolve) => { + const devServerOptions = {host: '127.0.0.1', port: 8080}; + const devServer = new DevServer(compiler, devServerOptions); + devServer.listen(devServerOptions.port, devServerOptions.host, () => { + resolve(devServer); + }); + }); + await new Promise((resolve) => { + compiler.hooks.afterDone.tap('webpack-bundle-analyzer', resolve); + }); + const path = `${webpackConfig.output.path}/report.html`; + expect(compiler.outputFileSystem.existsSync(path)).toBeTruthy(); + expect(fs.existsSync(path)).toBeFalsy(); + compiler.outputFileSystem.unlinkSync(path); + await new Promise((resolve) => { + devServer.close(() => { + resolve(); + }); }); - const reportCheckIntervalId = setInterval(() => { - if (fs.existsSync(`${webpackConfig.output.path}/report.html`)) { - finish(); - } else if (Date.now() - startedAt > timeout - 1000) { - finish(`report file wasn't found in "${webpackConfig.output.path}" directory`); - } - }, 300); + }); - function finish(errorMessage) { - clearInterval(reportCheckIntervalId); - devServer.kill(); - done(errorMessage ? new Error(errorMessage) : null); - } + it.skip('should save report file to the output directory when writeToDisk is true', async function () { + expect.assertions(2); + const compiler = webpack(webpackConfig); + const devServer = await new Promise((resolve) => { + const devServerOptions = {host: '127.0.0.1', port: 8080, writeToDisk: true}; + const devServer = new DevServer(compiler, devServerOptions); + devServer.listen(devServerOptions.port, devServerOptions.host, () => { + resolve(devServer); + }); + }); + await new Promise((resolve) => { + compiler.hooks.afterDone.tap('webpack-bundle-analyzer', resolve); + }); + const path = `${webpackConfig.output.path}/report.html`; + expect(compiler.outputFileSystem.existsSync(path)).toBeTruthy(); + expect(fs.existsSync(path)).toBeTruthy(); + compiler.outputFileSystem.unlinkSync(path); + return await new Promise((resolve) => { + devServer.close(() => { + resolve(); + }); + }); }); }); -function deleteOutputDirectory() { - del.sync(webpackConfig.output.path); -} From 8183d691fba39b8488f7166bea846b35267e1bf8 Mon Sep 17 00:00:00 2001 From: wood1986 <5212215+wood1986@users.noreply.github.com> Date: Wed, 13 Oct 2021 00:26:36 -0700 Subject: [PATCH 2/2] fix: generateStatsFile should use utils.mkdirSync --- src/BundleAnalyzerPlugin.js | 2 +- src/utils.js | 8 ++++++++ src/viewer.js | 16 +++++----------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/BundleAnalyzerPlugin.js b/src/BundleAnalyzerPlugin.js index f1d26408..e2df6d29 100644 --- a/src/BundleAnalyzerPlugin.js +++ b/src/BundleAnalyzerPlugin.js @@ -85,7 +85,7 @@ class BundleAnalyzerPlugin { async generateStatsFile(stats) { const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename); - await this.fs.promises.mkdir(path.dirname(statsFilepath), {recursive: true}); + utils.mkdirpSync(this.fs, statsFilepath); try { await writeStats(stats, statsFilepath); diff --git a/src/utils.js b/src/utils.js index be4e40cf..d1bafac4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,7 @@ const {inspect, types} = require('util'); const _ = require('lodash'); const opener = require('opener'); +const path = require('path'); const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; @@ -63,3 +64,10 @@ exports.open = function (uri, logger) { logger.debug(`Opener failed to open "${uri}":\n${err}`); } }; + +exports.mkdirpSync = function mkdirpSync(fs, dest) { + // older version webpack uses memory-fs whose mkdirSync does not support {recursive: true} + fs.mkdirpSync + ? fs.mkdirpSync(path.dirname(dest)) + : fs.mkdirSync(path.dirname(dest), {recursive: true}); +}; diff --git a/src/viewer.js b/src/viewer.js index dce34157..127bfc47 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -8,7 +8,7 @@ const {bold} = require('chalk'); const Logger = require('./Logger'); const analyzer = require('./analyzer'); -const {open} = require('./utils'); +const {open, mkdirpSync} = require('./utils'); const {renderViewer} = require('./template'); const projectRoot = path.resolve(__dirname, '..'); @@ -21,14 +21,6 @@ function resolveTitle(reportTitle) { } } -function writeToFs(fs, dest, data) { - // older version webpack uses memory-fs whose mkdirSync does not support {recursive: true} - fs.mkdirpSync - ? fs.mkdirpSync(path.dirname(dest)) - : fs.mkdirSync(path.dirname(dest), {recursive: true}); - fs.writeFileSync(dest, data); -} - module.exports = { startServer, generateReport, @@ -153,7 +145,8 @@ async function generateReport(bundleStats, opts) { }); const reportFilepath = path.resolve(bundleDir || process.cwd(), reportFilename); - writeToFs(fs, reportFilepath, reportHtml); + mkdirpSync(fs, reportFilepath, reportHtml); + fs.writeFileSync(reportFilepath, reportHtml); logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`); @@ -169,7 +162,8 @@ async function generateJSONReport(bundleStats, opts) { if (!chartData) return; - writeToFs(fs, reportFilename, JSON.stringify(chartData)); + mkdirpSync(fs, reportFilename); + fs.writeFileSync(reportFilename, JSON.stringify(chartData)); logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`); }