Skip to content

Commit 8360d05

Browse files
Br1an67Copilot
andcommitted
fix: read bundle assets via compiler.outputFileSystem
Instead of detecting memfs by constructor name, use the compiler's outputFileSystem directly to read bundle files when it supports readFileSync (e.g. memfs from webpack-dev-server 4+). Falls back to native fs for webpack 4's NodeOutputFileSystem which lacks readFileSync. Fixes #471 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b3f44b0 commit 8360d05

4 files changed

Lines changed: 36 additions & 21 deletions

File tree

src/BundleAnalyzerPlugin.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class BundleAnalyzerPlugin {
181181
reportTitle: this.opts.reportTitle,
182182
compressionAlgorithm: this.opts.compressionAlgorithm,
183183
bundleDir: this.getBundleDirFromCompiler(),
184+
outputFs: this.getOutputFsFromCompiler(),
184185
logger: this.logger,
185186
defaultSizes: this.opts.defaultSizes,
186187
excludeAssets: this.opts.excludeAssets,
@@ -202,6 +203,7 @@ class BundleAnalyzerPlugin {
202203
),
203204
compressionAlgorithm: this.opts.compressionAlgorithm,
204205
bundleDir: this.getBundleDirFromCompiler(),
206+
outputFs: this.getOutputFsFromCompiler(),
205207
logger: this.logger,
206208
excludeAssets: this.opts.excludeAssets,
207209
});
@@ -222,30 +224,34 @@ class BundleAnalyzerPlugin {
222224
reportTitle: this.opts.reportTitle,
223225
compressionAlgorithm: this.opts.compressionAlgorithm,
224226
bundleDir: this.getBundleDirFromCompiler(),
227+
outputFs: this.getOutputFsFromCompiler(),
225228
logger: this.logger,
226229
defaultSizes: this.opts.defaultSizes,
227230
excludeAssets: this.opts.excludeAssets,
228231
});
229232
}
230233

231234
getBundleDirFromCompiler() {
232-
const outputFileSystemConstructor =
233-
/** @type {OutputFileSystem} */
234-
(/** @type {Compiler} */ (this.compiler).outputFileSystem).constructor;
235+
return /** @type {Compiler} */ (this.compiler).outputPath;
236+
}
235237

236-
if (typeof outputFileSystemConstructor === "undefined") {
237-
return /** @type {Compiler} */ (this.compiler).outputPath;
238-
}
239-
switch (outputFileSystemConstructor.name) {
240-
case "MemoryFileSystem":
241-
return null;
242-
// Detect AsyncMFS used by Nuxt 2.5 that replaces webpack's MFS during development
243-
// Related: #274
244-
case "AsyncMFS":
245-
return null;
246-
default:
247-
return /** @type {Compiler} */ (this.compiler).outputPath;
238+
/**
239+
* Returns the compiler's outputFileSystem if it differs from the native fs
240+
* and supports reading files (e.g. memfs used by webpack-dev-server),
241+
* otherwise returns null to fall back to the native fs.
242+
*
243+
* @returns {OutputFileSystem | null}
244+
*/
245+
getOutputFsFromCompiler() {
246+
const outputFs = /** @type {Compiler} */ (this.compiler).outputFileSystem;
247+
if (
248+
outputFs &&
249+
outputFs !== fs &&
250+
typeof /** @type {EXPECTED_ANY} */ (outputFs).readFileSync === "function"
251+
) {
252+
return /** @type {OutputFileSystem} */ (outputFs);
248253
}
254+
return null;
249255
}
250256
}
251257

src/analyzer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ function isEntryModule(statsModule) {
180180
* @property {Logger} logger logger
181181
* @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
182182
* @property {ExcludeAssets} excludeAssets exclude assets
183+
* @property {import("webpack").OutputFileSystem | null=} outputFs non-default filesystem for reading bundle files
183184
*/
184185

185186
/** @typedef {import("./tree/Module").ModuleChartData} ModuleChartData */
@@ -216,6 +217,7 @@ function getViewerData(bundleStats, bundleDir, opts) {
216217
logger = new Logger(),
217218
compressionAlgorithm = "gzip",
218219
excludeAssets = null,
220+
outputFs = null,
219221
} = opts || {};
220222

221223
const isAssetIncluded = createAssetsFilter(excludeAssets);
@@ -286,6 +288,7 @@ function getViewerData(bundleStats, bundleDir, opts) {
286288
try {
287289
bundleInfo = parseBundle(assetFile, {
288290
sourceType: statAsset.info.javascriptModule ? "module" : "script",
291+
...(outputFs && { fs: outputFs }),
289292
});
290293
} catch (err) {
291294
const msg =

src/parseUtils.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,13 @@ function isAsyncWebWorkerChunkExpression(node) {
323323

324324
/**
325325
* @param {string} bundlePath bundle path
326-
* @param {{ sourceType: "script" | "module" }} opts options
326+
* @param {{ sourceType: "script" | "module", fs?: import("webpack").OutputFileSystem }} opts options
327327
* @returns {{ modules: Modules, src: string, runtimeSrc: string }} parsed result
328328
*/
329329
module.exports.parseBundle = function parseBundle(bundlePath, opts) {
330-
const { sourceType = "script" } = opts || {};
330+
const { sourceType = "script", fs: bundleFs = fs } = opts || {};
331331

332-
const content = fs.readFileSync(bundlePath, "utf8");
332+
const content = /** @type {string} */ (bundleFs.readFileSync(bundlePath, "utf8"));
333333
const ast = acorn.parse(content, {
334334
sourceType,
335335
ecmaVersion: "latest",

src/viewer.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ function getChartData(analyzerOpts, bundleStats, bundleDir) {
107107
* @property {string} host host
108108
* @property {boolean} openBrowser true when need to open browser, otherwise false
109109
* @property {string | null} bundleDir bundle dir
110+
* @property {import("webpack").OutputFileSystem | null=} outputFs filesystem for reading bundle files
110111
* @property {Logger} logger logger
111112
* @property {Sizes} defaultSizes default sizes
112113
* @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
@@ -128,6 +129,7 @@ async function startServer(bundleStats, opts) {
128129
host = "127.0.0.1",
129130
openBrowser = true,
130131
bundleDir = null,
132+
outputFs,
131133
logger = new Logger(),
132134
defaultSizes = "parsed",
133135
compressionAlgorithm,
@@ -136,7 +138,7 @@ async function startServer(bundleStats, opts) {
136138
analyzerUrl,
137139
} = opts || {};
138140

139-
const analyzerOpts = { logger, excludeAssets, compressionAlgorithm };
141+
const analyzerOpts = { logger, excludeAssets, compressionAlgorithm, outputFs };
140142

141143
let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
142144

@@ -240,6 +242,7 @@ async function startServer(bundleStats, opts) {
240242
* @property {string} reportFilename report filename
241243
* @property {ReportTitle} reportTitle report title
242244
* @property {string | null} bundleDir bundle dir
245+
* @property {import("webpack").OutputFileSystem | null=} outputFs filesystem for reading bundle files
243246
* @property {Logger} logger logger
244247
* @property {Sizes} defaultSizes default sizes
245248
* @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
@@ -257,14 +260,15 @@ async function generateReport(bundleStats, opts) {
257260
reportFilename,
258261
reportTitle,
259262
bundleDir = null,
263+
outputFs,
260264
logger = new Logger(),
261265
defaultSizes = "parsed",
262266
compressionAlgorithm,
263267
excludeAssets = null,
264268
} = opts || {};
265269

266270
const chartData = getChartData(
267-
{ logger, excludeAssets, compressionAlgorithm },
271+
{ logger, excludeAssets, compressionAlgorithm, outputFs },
268272
bundleStats,
269273
bundleDir,
270274
);
@@ -302,6 +306,7 @@ async function generateReport(bundleStats, opts) {
302306
* @typedef {object} GenerateJSONReportOptions
303307
* @property {string} reportFilename report filename
304308
* @property {string | null} bundleDir bundle dir
309+
* @property {import("webpack").OutputFileSystem | null=} outputFs filesystem for reading bundle files
305310
* @property {Logger} logger logger
306311
* @property {ExcludeAssets} excludeAssets exclude assets
307312
* @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
@@ -316,13 +321,14 @@ async function generateJSONReport(bundleStats, opts) {
316321
const {
317322
reportFilename,
318323
bundleDir = null,
324+
outputFs,
319325
logger = new Logger(),
320326
excludeAssets = null,
321327
compressionAlgorithm,
322328
} = opts || {};
323329

324330
const chartData = getChartData(
325-
{ logger, excludeAssets, compressionAlgorithm },
331+
{ logger, excludeAssets, compressionAlgorithm, outputFs },
326332
bundleStats,
327333
bundleDir,
328334
);

0 commit comments

Comments
 (0)