UNPKG

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