@quasar/app-webpack
Version:
Quasar Framework App CLI with Webpack
289 lines (235 loc) • 6.82 kB
JavaScript
const { ProgressPlugin } = require('webpack')
const throttle = require('lodash/throttle.js')
const { green, gray, bold, trueColor } = require('kolorist')
const {
success,
info,
error,
warning,
clearConsole,
dot
} = require('../utils/logger.js')
const { isMinimalTerminal } = require('../utils/is-minimal-terminal.js')
const {
printWebpackWarnings,
printWebpackErrors
} = require('../utils/print-webpack-issue/index.js')
const { progressLog } = require('../utils/progress-log.js')
let maxLengthName = 0
let isDev = false
const compilations = []
function isCompilationIdle() {
return compilations.every(entry => entry.idle === true)
}
function createState(name) {
const state = {
name,
idle: true,
compiled: false,
warnings: null,
errors: null,
startTime: null,
progress: null,
progressMessage: '',
progressDetails: ''
}
const len = name.length
if (len > maxLengthName) {
maxLengthName = len
}
compilations.push(state)
return state
}
/**
* Progress bar related
*/
const barLength = 20
const barProgressFactor = barLength / 100
const barString = Array.apply(null, { length: barLength }).map((_, index) => {
const p = index / barLength
const colorize =
p <= 0.5
? trueColor(255, Math.round(p * 510), 0)
: trueColor(255 - Math.round(p * 122), 255, 0)
return colorize('█')
})
function printBars() {
if (progressLog.isActive !== true) return
const prefixLen = compilations.length - 1
const lines = compilations.map((state, index) => {
const prefix = index < prefixLen ? '├──' : '└──'
const name = green(state.name.padEnd(maxLengthName))
const barWidth = Math.floor(state.progress * barProgressFactor)
const bar = barString
.map((char, i) => (i <= barWidth ? char : ' '))
.join('')
const details =
state.idle === false
? state.progress +
'% ' +
[
state.progressMessage,
state.progressDetails
? [state.progressDetails[0], state.progressDetails[1]]
.filter(s => s)
.join(' ')
: ''
]
.filter(m => m)
.join(' ')
: 'idle'
return ` ${prefix} ${name} ${bar} ${gray(details)}\n`
})
progressLog(
`\n ${dot} ${green(bold('Compiling with Webpack'))}:\n` + lines.join('')
)
}
const renderBars = throttle(printBars, 200)
/**
* Status related
*/
function printStatus() {
if (isDev === true && isCompilationIdle() === false) return
const entriesWithErrors = compilations.filter(entry => entry.errors !== null)
if (entriesWithErrors.length > 0) {
if (isDev === true) clearConsole()
entriesWithErrors.forEach(entry => {
printWebpackErrors(entry.name, entry.errors)
})
console.log()
error('Please check the log above for details.\n', 'COMPILATION FAILED')
if (isDev === false) {
process.exit(1)
}
return
}
if (isDev !== true && isCompilationIdle() === false) return
const entriesWithWarnings = compilations.filter(
entry => entry.warnings !== null
)
if (entriesWithWarnings.length > 0) {
entriesWithWarnings.forEach(entry => {
printWebpackWarnings(entry.name, entry.warnings)
})
console.log()
warning(
'Compilation succeeded but there are warning(s). Please check the log above.\n'
)
}
}
module.exports.WebpackProgressPlugin = class WebpackProgressPlugin extends (
ProgressPlugin
) {
constructor({ name, quasarConf }) {
const useBars =
isMinimalTerminal !== true &&
quasarConf.build.webpackShowProgress === true
if (useBars === true) {
super({
handler: (percent, msg, ...details) => {
this.updateBars(percent, msg, details)
}
})
} else {
super({ handler: () => {} })
}
this.opts = {
quasarConf,
name,
useBars
}
isDev = quasarConf.ctx.dev === true
}
apply(compiler) {
if (this.opts.useBars) {
super.apply(compiler)
}
compiler.hooks.beforeCompile.tapAsync(
'QuasarProgressPlugin',
(_, callback) => {
progressLog.init().then(() => {
callback()
})
}
)
compiler.hooks.watchClose.tap('QuasarProgressPlugin', () => {
const index = compilations.indexOf(this.state)
compilations.splice(index, 1)
delete this.state
if (this.opts.useBars === true) {
if (compilations.length === 0) {
// ensure progress log is stopped!
progressLog.stop()
}
maxLengthName = compilations.reduce(
(acc, entry) => (entry.name.length > acc ? entry.name.length : acc),
0
)
}
})
compiler.hooks.compile.tap('QuasarProgressPlugin', () => {
if (this.state === void 0) {
this.state = createState(this.opts.name)
} else {
this.resetStats()
}
this.state.idle = false
info(
`Compiling of "${this.state.name}" by Webpack in progress...`,
'WAIT'
)
if (this.opts.useBars === true) {
progressLog.start()
}
this.state.startTime = Number(new Date())
})
compiler.hooks.done.tap('QuasarStatusPlugin', stats => {
this.state.idle = true
this.resetStats()
if (stats.hasErrors()) {
this.state.errors = stats
} else {
this.state.compiled = true
if (stats.hasWarnings()) {
this.state.warnings = stats
}
}
if (this.opts.useBars === true && isCompilationIdle() === true) {
progressLog.stop()
}
const diffTime = Number(new Date()) - this.state.startTime
if (this.state.errors !== null) {
error(
`"${this.state.name}" compiled by Webpack with errors ${dot} ${diffTime}ms`,
'DONE'
)
} else if (this.state.warnings !== null) {
warning(
`"${this.state.name}" compiled by Webpack, but with warnings ${dot} ${diffTime}ms`,
'DONE'
)
} else {
success(
`"${this.state.name}" compiled with success by Webpack ${dot} ${diffTime}ms`,
'DONE'
)
}
printStatus()
})
}
resetStats() {
this.state.errors = null
this.state.warnings = null
}
updateBars(percent, msg, details) {
// it may still be called even after compilation was closed
// due to Webpack's delayed call of handler
if (this.state === void 0) return
const progress = Math.floor(percent * 100)
const running = progress < 100
this.state.progress = progress
this.state.progressMessage = running && msg ? msg : ''
this.state.progressDetails = details
if (this.opts.useBars === true) renderBars()
}
}