224 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // From https://github.com/FormidableLabs/webpack-dashboard/blob/7f99b31c5f00a7818d8129cb8a8fc6eb1b71799c/plugin/index.js
 | |
| // Modified by Guillaume Chau (Akryum)
 | |
| 
 | |
| /* eslint-disable max-params, max-statements */
 | |
| 'use strict'
 | |
| 
 | |
| const path = require('path')
 | |
| const fs = require('fs-extra')
 | |
| const webpack = require('webpack')
 | |
| const { IpcMessenger } = require('@vue/cli-shared-utils')
 | |
| const { analyzeBundle } = require('./analyzeBundle')
 | |
| 
 | |
| const ID = 'vue-cli-dashboard-plugin'
 | |
| const ONE_SECOND = 1000
 | |
| const FILENAME_QUERY_REGEXP = /\?.*$/
 | |
| 
 | |
| const ipc = new IpcMessenger()
 | |
| 
 | |
| function getTimeMessage (timer) {
 | |
|   let time = Date.now() - timer
 | |
| 
 | |
|   if (time >= ONE_SECOND) {
 | |
|     time /= ONE_SECOND
 | |
|     time = Math.round(time)
 | |
|     time += 's'
 | |
|   } else {
 | |
|     time += 'ms'
 | |
|   }
 | |
| 
 | |
|   return ` (${time})`
 | |
| }
 | |
| 
 | |
| class DashboardPlugin {
 | |
|   constructor (options) {
 | |
|     this.type = options.type
 | |
|     if (this.type === 'build' && options.moduleBuild) {
 | |
|       this.type = 'build-modern'
 | |
|     }
 | |
|     this.watching = false
 | |
|     this.autoDisconnect = !options.keepAlive
 | |
|   }
 | |
| 
 | |
|   cleanup () {
 | |
|     this.sendData = null
 | |
|     if (this.autoDisconnect) ipc.disconnect()
 | |
|   }
 | |
| 
 | |
|   apply (compiler) {
 | |
|     let sendData = this.sendData
 | |
|     let timer
 | |
|     let inProgress = false
 | |
| 
 | |
|     let assetSources = new Map()
 | |
| 
 | |
|     if (!sendData) {
 | |
|       sendData = data => ipc.send({
 | |
|         webpackDashboardData: {
 | |
|           type: this.type,
 | |
|           value: data
 | |
|         }
 | |
|       })
 | |
|     }
 | |
| 
 | |
|     // Progress status
 | |
|     let progressTime = Date.now()
 | |
|     const progressPlugin = new webpack.ProgressPlugin((percent, msg) => {
 | |
|       // in webpack 5, progress plugin will continue sending progresses even after the done hook
 | |
|       // for things like caching, causing the progress indicator stuck at 0.99
 | |
|       // so we have to use a flag to stop sending such `compiling` progress data
 | |
|       if (!inProgress) {
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       // Debouncing
 | |
|       const time = Date.now()
 | |
|       if (time - progressTime > 300) {
 | |
|         progressTime = time
 | |
|         sendData([
 | |
|           {
 | |
|             type: 'status',
 | |
|             value: 'Compiling'
 | |
|           },
 | |
|           {
 | |
|             type: 'progress',
 | |
|             value: percent
 | |
|           },
 | |
|           {
 | |
|             type: 'operations',
 | |
|             value: msg + getTimeMessage(timer)
 | |
|           }
 | |
|         ])
 | |
|       }
 | |
|     })
 | |
|     progressPlugin.apply(compiler)
 | |
| 
 | |
|     compiler.hooks.watchRun.tap(ID, c => {
 | |
|       this.watching = true
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.run.tap(ID, c => {
 | |
|       this.watching = false
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.compile.tap(ID, () => {
 | |
|       inProgress = true
 | |
|       timer = Date.now()
 | |
| 
 | |
|       sendData([
 | |
|         {
 | |
|           type: 'status',
 | |
|           value: 'Compiling'
 | |
|         },
 | |
|         {
 | |
|           type: 'progress',
 | |
|           value: 0
 | |
|         }
 | |
|       ])
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.invalid.tap(ID, () => {
 | |
|       sendData([
 | |
|         {
 | |
|           type: 'status',
 | |
|           value: 'Invalidated'
 | |
|         },
 | |
|         {
 | |
|           type: 'progress',
 | |
|           value: 0
 | |
|         },
 | |
|         {
 | |
|           type: 'operations',
 | |
|           value: 'idle'
 | |
|         }
 | |
|       ])
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.failed.tap(ID, () => {
 | |
|       sendData([
 | |
|         {
 | |
|           type: 'status',
 | |
|           value: 'Failed'
 | |
|         },
 | |
|         {
 | |
|           type: 'operations',
 | |
|           value: `idle${getTimeMessage(timer)}`
 | |
|         }
 | |
|       ])
 | |
|       inProgress = false
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.afterEmit.tap(ID, compilation => {
 | |
|       assetSources = new Map()
 | |
|       for (const name in compilation.assets) {
 | |
|         const asset = compilation.assets[name]
 | |
|         const filename = name.replace(FILENAME_QUERY_REGEXP, '')
 | |
|         try {
 | |
|           assetSources.set(filename, asset.source())
 | |
|         } catch (e) {
 | |
|           const webpackFs = compiler.outputFileSystem
 | |
|           const fullPath = (webpackFs.join || path.join)(compiler.options.output.path, filename)
 | |
|           const buf = webpackFs.readFileSync(fullPath)
 | |
|           assetSources.set(filename, buf.toString())
 | |
|         }
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     compiler.hooks.done.tap(ID, stats => {
 | |
|       let statsData = stats.toJson()
 | |
|       // Sometimes all the information is located in `children` array
 | |
|       if ((!statsData.assets || !statsData.assets.length) && statsData.children && statsData.children.length) {
 | |
|         statsData = statsData.children[0]
 | |
|       }
 | |
| 
 | |
|       const outputPath = compiler.options.output.path
 | |
|       statsData.assets.forEach(asset => {
 | |
|         // Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it)
 | |
|         asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, '')
 | |
|         asset.fullPath = path.join(outputPath, asset.name)
 | |
|       })
 | |
|       // Analyze the assets and update sizes on assets and modules
 | |
|       analyzeBundle(statsData, assetSources)
 | |
| 
 | |
|       const hasErrors = stats.hasErrors()
 | |
| 
 | |
|       sendData([
 | |
|         {
 | |
|           type: 'status',
 | |
|           value: hasErrors ? 'Failed' : 'Success'
 | |
|         },
 | |
|         {
 | |
|           type: 'progress',
 | |
|           value: 1
 | |
|         },
 | |
|         {
 | |
|           type: 'operations',
 | |
|           value: `idle${getTimeMessage(timer)}`
 | |
|         }
 | |
|       ])
 | |
|       inProgress = false
 | |
| 
 | |
|       const statsFile = path.resolve(process.cwd(), `./node_modules/.stats-${this.type}.json`)
 | |
|       fs.writeJson(statsFile, {
 | |
|         errors: hasErrors,
 | |
|         warnings: stats.hasWarnings(),
 | |
|         data: statsData
 | |
|       }).then(() => {
 | |
|         sendData([
 | |
|           {
 | |
|             type: 'stats'
 | |
|           }
 | |
|         ])
 | |
| 
 | |
|         if (!this.watching) {
 | |
|           this.cleanup()
 | |
|         }
 | |
|       }).catch(error => {
 | |
|         console.error(error)
 | |
|       })
 | |
|     })
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = DashboardPlugin
 |