@quasar/app-webpack
Version:
Quasar Framework App CLI with Webpack
254 lines (200 loc) • 6.58 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, index) => (index <= 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) {
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 = +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 = +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
this.opts.useBars === true && renderBars()
}
}