166 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| 
 | |
| const fs = require('fs');
 | |
| 
 | |
| const path = require('path');
 | |
| 
 | |
| const {
 | |
|   bold
 | |
| } = require('picocolors');
 | |
| 
 | |
| const Logger = require('./Logger');
 | |
| 
 | |
| const viewer = require('./viewer');
 | |
| 
 | |
| const utils = require('./utils');
 | |
| 
 | |
| const {
 | |
|   writeStats
 | |
| } = require('./statsUtils');
 | |
| 
 | |
| class BundleAnalyzerPlugin {
 | |
|   constructor(opts = {}) {
 | |
|     this.opts = {
 | |
|       analyzerMode: 'server',
 | |
|       analyzerHost: '127.0.0.1',
 | |
|       reportFilename: null,
 | |
|       reportTitle: utils.defaultTitle,
 | |
|       defaultSizes: 'parsed',
 | |
|       openAnalyzer: true,
 | |
|       generateStatsFile: false,
 | |
|       statsFilename: 'stats.json',
 | |
|       statsOptions: null,
 | |
|       excludeAssets: null,
 | |
|       logLevel: 'info',
 | |
|       // deprecated
 | |
|       startAnalyzer: true,
 | |
|       analyzerUrl: utils.defaultAnalyzerUrl,
 | |
|       ...opts,
 | |
|       analyzerPort: 'analyzerPort' in opts ? opts.analyzerPort === 'auto' ? 0 : opts.analyzerPort : 8888
 | |
|     };
 | |
|     this.server = null;
 | |
|     this.logger = new Logger(this.opts.logLevel);
 | |
|   }
 | |
| 
 | |
|   apply(compiler) {
 | |
|     this.compiler = compiler;
 | |
| 
 | |
|     const done = (stats, callback) => {
 | |
|       callback = callback || (() => {});
 | |
| 
 | |
|       const actions = [];
 | |
| 
 | |
|       if (this.opts.generateStatsFile) {
 | |
|         actions.push(() => this.generateStatsFile(stats.toJson(this.opts.statsOptions)));
 | |
|       } // Handling deprecated `startAnalyzer` flag
 | |
| 
 | |
| 
 | |
|       if (this.opts.analyzerMode === 'server' && !this.opts.startAnalyzer) {
 | |
|         this.opts.analyzerMode = 'disabled';
 | |
|       }
 | |
| 
 | |
|       if (this.opts.analyzerMode === 'server') {
 | |
|         actions.push(() => this.startAnalyzerServer(stats.toJson()));
 | |
|       } else if (this.opts.analyzerMode === 'static') {
 | |
|         actions.push(() => this.generateStaticReport(stats.toJson()));
 | |
|       } else if (this.opts.analyzerMode === 'json') {
 | |
|         actions.push(() => this.generateJSONReport(stats.toJson()));
 | |
|       }
 | |
| 
 | |
|       if (actions.length) {
 | |
|         // Making analyzer logs to be after all webpack logs in the console
 | |
|         setImmediate(async () => {
 | |
|           try {
 | |
|             await Promise.all(actions.map(action => action()));
 | |
|             callback();
 | |
|           } catch (e) {
 | |
|             callback(e);
 | |
|           }
 | |
|         });
 | |
|       } else {
 | |
|         callback();
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     if (compiler.hooks) {
 | |
|       compiler.hooks.done.tapAsync('webpack-bundle-analyzer', done);
 | |
|     } else {
 | |
|       compiler.plugin('done', done);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async generateStatsFile(stats) {
 | |
|     const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
 | |
|     await fs.promises.mkdir(path.dirname(statsFilepath), {
 | |
|       recursive: true
 | |
|     });
 | |
| 
 | |
|     try {
 | |
|       await writeStats(stats, statsFilepath);
 | |
|       this.logger.info(`${bold('Webpack Bundle Analyzer')} saved stats file to ${bold(statsFilepath)}`);
 | |
|     } catch (error) {
 | |
|       this.logger.error(`${bold('Webpack Bundle Analyzer')} error saving stats file to ${bold(statsFilepath)}: ${error}`);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async startAnalyzerServer(stats) {
 | |
|     if (this.server) {
 | |
|       (await this.server).updateChartData(stats);
 | |
|     } else {
 | |
|       this.server = viewer.startServer(stats, {
 | |
|         openBrowser: this.opts.openAnalyzer,
 | |
|         host: this.opts.analyzerHost,
 | |
|         port: this.opts.analyzerPort,
 | |
|         reportTitle: this.opts.reportTitle,
 | |
|         bundleDir: this.getBundleDirFromCompiler(),
 | |
|         logger: this.logger,
 | |
|         defaultSizes: this.opts.defaultSizes,
 | |
|         excludeAssets: this.opts.excludeAssets,
 | |
|         analyzerUrl: this.opts.analyzerUrl
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async generateJSONReport(stats) {
 | |
|     await viewer.generateJSONReport(stats, {
 | |
|       reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
 | |
|       bundleDir: this.getBundleDirFromCompiler(),
 | |
|       logger: this.logger,
 | |
|       excludeAssets: this.opts.excludeAssets
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   async generateStaticReport(stats) {
 | |
|     await viewer.generateReport(stats, {
 | |
|       openBrowser: this.opts.openAnalyzer,
 | |
|       reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
 | |
|       reportTitle: this.opts.reportTitle,
 | |
|       bundleDir: this.getBundleDirFromCompiler(),
 | |
|       logger: this.logger,
 | |
|       defaultSizes: this.opts.defaultSizes,
 | |
|       excludeAssets: this.opts.excludeAssets
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   getBundleDirFromCompiler() {
 | |
|     if (typeof this.compiler.outputFileSystem.constructor === 'undefined') {
 | |
|       return this.compiler.outputPath;
 | |
|     }
 | |
| 
 | |
|     switch (this.compiler.outputFileSystem.constructor.name) {
 | |
|       case 'MemoryFileSystem':
 | |
|         return null;
 | |
|       // Detect AsyncMFS used by Nuxt 2.5 that replaces webpack's MFS during development
 | |
|       // Related: #274
 | |
| 
 | |
|       case 'AsyncMFS':
 | |
|         return null;
 | |
| 
 | |
|       default:
 | |
|         return this.compiler.outputPath;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| module.exports = BundleAnalyzerPlugin; |