UNPKG

4.55 kBJavaScriptView Raw
1"use strict";
2
3const fs = require('fs');
4
5const path = require('path');
6
7const {
8 bold
9} = require('chalk');
10
11const Logger = require('./Logger');
12
13const viewer = require('./viewer');
14
15const utils = require('./utils');
16
17const {
18 writeStats
19} = require('./statsUtils');
20
21class BundleAnalyzerPlugin {
22 constructor(opts = {}) {
23 this.opts = {
24 analyzerMode: 'server',
25 analyzerHost: '127.0.0.1',
26 reportFilename: null,
27 reportTitle: utils.defaultTitle,
28 defaultSizes: 'parsed',
29 openAnalyzer: true,
30 generateStatsFile: false,
31 statsFilename: 'stats.json',
32 statsOptions: null,
33 excludeAssets: null,
34 logLevel: 'info',
35 // deprecated
36 startAnalyzer: true,
37 ...opts,
38 analyzerPort: 'analyzerPort' in opts ? opts.analyzerPort === 'auto' ? 0 : opts.analyzerPort : 8888
39 };
40 this.server = null;
41 this.logger = new Logger(this.opts.logLevel);
42 }
43
44 apply(compiler) {
45 this.compiler = compiler;
46
47 const done = (stats, callback) => {
48 callback = callback || (() => {});
49
50 const actions = [];
51
52 if (this.opts.generateStatsFile) {
53 actions.push(() => this.generateStatsFile(stats.toJson(this.opts.statsOptions)));
54 } // Handling deprecated `startAnalyzer` flag
55
56
57 if (this.opts.analyzerMode === 'server' && !this.opts.startAnalyzer) {
58 this.opts.analyzerMode = 'disabled';
59 }
60
61 if (this.opts.analyzerMode === 'server') {
62 actions.push(() => this.startAnalyzerServer(stats.toJson()));
63 } else if (this.opts.analyzerMode === 'static') {
64 actions.push(() => this.generateStaticReport(stats.toJson()));
65 } else if (this.opts.analyzerMode === 'json') {
66 actions.push(() => this.generateJSONReport(stats.toJson()));
67 }
68
69 if (actions.length) {
70 // Making analyzer logs to be after all webpack logs in the console
71 setImmediate(async () => {
72 try {
73 await Promise.all(actions.map(action => action()));
74 callback();
75 } catch (e) {
76 callback(e);
77 }
78 });
79 } else {
80 callback();
81 }
82 };
83
84 if (compiler.hooks) {
85 compiler.hooks.done.tapAsync('webpack-bundle-analyzer', done);
86 } else {
87 compiler.plugin('done', done);
88 }
89 }
90
91 async generateStatsFile(stats) {
92 const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
93 await fs.promises.mkdir(path.dirname(statsFilepath), {
94 recursive: true
95 });
96
97 try {
98 await writeStats(stats, statsFilepath);
99 this.logger.info(`${bold('Webpack Bundle Analyzer')} saved stats file to ${bold(statsFilepath)}`);
100 } catch (error) {
101 this.logger.error(`${bold('Webpack Bundle Analyzer')} error saving stats file to ${bold(statsFilepath)}: ${error}`);
102 }
103 }
104
105 async startAnalyzerServer(stats) {
106 if (this.server) {
107 (await this.server).updateChartData(stats);
108 } else {
109 this.server = viewer.startServer(stats, {
110 openBrowser: this.opts.openAnalyzer,
111 host: this.opts.analyzerHost,
112 port: this.opts.analyzerPort,
113 reportTitle: this.opts.reportTitle,
114 bundleDir: this.getBundleDirFromCompiler(),
115 logger: this.logger,
116 defaultSizes: this.opts.defaultSizes,
117 excludeAssets: this.opts.excludeAssets
118 });
119 }
120 }
121
122 async generateJSONReport(stats) {
123 await viewer.generateJSONReport(stats, {
124 reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
125 bundleDir: this.getBundleDirFromCompiler(),
126 logger: this.logger,
127 excludeAssets: this.opts.excludeAssets
128 });
129 }
130
131 async generateStaticReport(stats) {
132 await viewer.generateReport(stats, {
133 openBrowser: this.opts.openAnalyzer,
134 reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
135 reportTitle: this.opts.reportTitle,
136 bundleDir: this.getBundleDirFromCompiler(),
137 logger: this.logger,
138 defaultSizes: this.opts.defaultSizes,
139 excludeAssets: this.opts.excludeAssets
140 });
141 }
142
143 getBundleDirFromCompiler() {
144 switch (this.compiler.outputFileSystem.constructor.name) {
145 case 'MemoryFileSystem':
146 return null;
147 // Detect AsyncMFS used by Nuxt 2.5 that replaces webpack's MFS during development
148 // Related: #274
149
150 case 'AsyncMFS':
151 return null;
152
153 default:
154 return this.compiler.outputPath;
155 }
156 }
157
158}
159
160module.exports = BundleAnalyzerPlugin;
\No newline at end of file