DashboardPlugin.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // From https://github.com/FormidableLabs/webpack-dashboard/blob/7f99b31c5f00a7818d8129cb8a8fc6eb1b71799c/plugin/index.js
  2. // Modified by Guillaume Chau (Akryum)
  3. /* eslint-disable max-params, max-statements */
  4. 'use strict'
  5. const path = require('path')
  6. const fs = require('fs-extra')
  7. const webpack = require('webpack')
  8. const { IpcMessenger } = require('@vue/cli-shared-utils')
  9. const { analyzeBundle } = require('./analyzeBundle')
  10. const ID = 'vue-cli-dashboard-plugin'
  11. const ONE_SECOND = 1000
  12. const FILENAME_QUERY_REGEXP = /\?.*$/
  13. const ipc = new IpcMessenger()
  14. function getTimeMessage (timer) {
  15. let time = Date.now() - timer
  16. if (time >= ONE_SECOND) {
  17. time /= ONE_SECOND
  18. time = Math.round(time)
  19. time += 's'
  20. } else {
  21. time += 'ms'
  22. }
  23. return ` (${time})`
  24. }
  25. class DashboardPlugin {
  26. constructor (options) {
  27. this.type = options.type
  28. if (this.type === 'build' && options.modernBuild) {
  29. this.type = 'build-modern'
  30. }
  31. this.watching = false
  32. this.autoDisconnect = !options.keepAlive
  33. }
  34. cleanup () {
  35. this.sendData = null
  36. if (this.autoDisconnect) ipc.disconnect()
  37. }
  38. apply (compiler) {
  39. let sendData = this.sendData
  40. let timer
  41. let assetSources = new Map()
  42. if (!sendData) {
  43. sendData = data => ipc.send({
  44. webpackDashboardData: {
  45. type: this.type,
  46. value: data
  47. }
  48. })
  49. }
  50. // Progress status
  51. let progressTime = Date.now()
  52. const progressPlugin = new webpack.ProgressPlugin((percent, msg) => {
  53. // Debouncing
  54. const time = Date.now()
  55. if (time - progressTime > 300) {
  56. progressTime = time
  57. sendData([
  58. {
  59. type: 'status',
  60. value: 'Compiling'
  61. },
  62. {
  63. type: 'progress',
  64. value: percent
  65. },
  66. {
  67. type: 'operations',
  68. value: msg + getTimeMessage(timer)
  69. }
  70. ])
  71. }
  72. })
  73. progressPlugin.apply(compiler)
  74. compiler.hooks.watchRun.tap(ID, c => {
  75. this.watching = true
  76. })
  77. compiler.hooks.run.tap(ID, c => {
  78. this.watching = false
  79. })
  80. compiler.hooks.compile.tap(ID, () => {
  81. timer = Date.now()
  82. sendData([
  83. {
  84. type: 'status',
  85. value: 'Compiling'
  86. },
  87. {
  88. type: 'progress',
  89. value: 0
  90. }
  91. ])
  92. })
  93. compiler.hooks.invalid.tap(ID, () => {
  94. sendData([
  95. {
  96. type: 'status',
  97. value: 'Invalidated'
  98. },
  99. {
  100. type: 'progress',
  101. value: 0
  102. },
  103. {
  104. type: 'operations',
  105. value: 'idle'
  106. }
  107. ])
  108. })
  109. compiler.hooks.failed.tap(ID, () => {
  110. sendData([
  111. {
  112. type: 'status',
  113. value: 'Failed'
  114. },
  115. {
  116. type: 'operations',
  117. value: `idle${getTimeMessage(timer)}`
  118. }
  119. ])
  120. })
  121. compiler.hooks.afterEmit.tap(ID, compilation => {
  122. assetSources = new Map()
  123. for (const name in compilation.assets) {
  124. const asset = compilation.assets[name]
  125. assetSources.set(name.replace(FILENAME_QUERY_REGEXP, ''), asset.source())
  126. }
  127. })
  128. compiler.hooks.done.tap(ID, stats => {
  129. let statsData = stats.toJson()
  130. // Sometimes all the information is located in `children` array
  131. if ((!statsData.assets || !statsData.assets.length) && statsData.children && statsData.children.length) {
  132. statsData = statsData.children[0]
  133. }
  134. const outputPath = compiler.options.output.path
  135. statsData.assets.forEach(asset => {
  136. // Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it)
  137. asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, '')
  138. asset.fullPath = path.join(outputPath, asset.name)
  139. })
  140. // Analyze the assets and update sizes on assets and modules
  141. analyzeBundle(statsData, assetSources)
  142. const hasErrors = stats.hasErrors()
  143. sendData([
  144. {
  145. type: 'status',
  146. value: hasErrors ? 'Failed' : 'Success'
  147. },
  148. {
  149. type: 'progress',
  150. value: 1
  151. },
  152. {
  153. type: 'operations',
  154. value: `idle${getTimeMessage(timer)}`
  155. }
  156. ])
  157. const statsFile = path.resolve(process.cwd(), `./node_modules/.stats-${this.type}.json`)
  158. fs.writeJson(statsFile, {
  159. errors: hasErrors,
  160. warnings: stats.hasWarnings(),
  161. data: statsData
  162. }).then(() => {
  163. sendData([
  164. {
  165. type: 'stats'
  166. }
  167. ])
  168. if (!this.watching) {
  169. this.cleanup()
  170. }
  171. }).catch(error => {
  172. console.error(error)
  173. })
  174. })
  175. }
  176. }
  177. module.exports = DashboardPlugin