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
 |