mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-04-22 04:18:33 -05:00
feat(ui): modern build mode
This commit is contained in:
@@ -48,7 +48,8 @@ module.exports = (api, options) => {
|
||||
process.env.VUE_CLI_MODERN_MODE = true
|
||||
delete process.env.VUE_CLI_MODERN_BUILD
|
||||
await build(Object.assign({}, args, {
|
||||
modernBuild: false
|
||||
modernBuild: false,
|
||||
keepAlive: true
|
||||
}), api, options)
|
||||
|
||||
process.env.VUE_CLI_MODERN_BUILD = true
|
||||
@@ -158,7 +159,9 @@ async function build (args, api, options) {
|
||||
const DashboardPlugin = require('../../webpack/DashboardPlugin')
|
||||
modifyConfig(webpackConfig, config => {
|
||||
config.plugins.push(new DashboardPlugin({
|
||||
type: 'build'
|
||||
type: 'build',
|
||||
modernBuild: args.modernBuild,
|
||||
keepAlive: args.keepAlive
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'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')
|
||||
@@ -32,12 +33,16 @@ function getTimeMessage (timer) {
|
||||
class DashboardPlugin {
|
||||
constructor (options) {
|
||||
this.type = options.type
|
||||
if (this.type === 'build' && options.modernBuild) {
|
||||
this.type = 'build-modern'
|
||||
}
|
||||
this.watching = false
|
||||
this.autoDisconnect = !options.keepAlive
|
||||
}
|
||||
|
||||
cleanup () {
|
||||
this.sendData = null
|
||||
ipc.disconnect()
|
||||
if (this.autoDisconnect) ipc.disconnect()
|
||||
}
|
||||
|
||||
apply (compiler) {
|
||||
@@ -61,7 +66,7 @@ class DashboardPlugin {
|
||||
const progressPlugin = new webpack.ProgressPlugin((percent, msg) => {
|
||||
// Debouncing
|
||||
const time = Date.now()
|
||||
if (time - progressTime > 100) {
|
||||
if (time - progressTime > 300) {
|
||||
progressTime = time
|
||||
sendData([
|
||||
{
|
||||
@@ -168,20 +173,27 @@ class DashboardPlugin {
|
||||
{
|
||||
type: 'operations',
|
||||
value: `idle${getTimeMessage(timer)}`
|
||||
},
|
||||
{
|
||||
type: 'stats',
|
||||
value: {
|
||||
errors: hasErrors,
|
||||
warnings: stats.hasWarnings(),
|
||||
data: statsData
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
if (!this.watching) {
|
||||
this.cleanup()
|
||||
}
|
||||
const statsFile = path.resolve(process.cwd(), `.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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
<VueIcon icon="donut_large"/>
|
||||
<div class="title">{{ $t('vue-webpack.analyzer.title') }}</div>
|
||||
|
||||
<VueSwitch
|
||||
v-if="modernMode"
|
||||
v-model="showModernBuild"
|
||||
>
|
||||
{{ $t('vue-webpack.modern-mode') }}
|
||||
</VueSwitch>
|
||||
|
||||
<template v-if="currentTree">
|
||||
<VueButton
|
||||
icon-left="arrow_upward"
|
||||
@@ -22,6 +29,7 @@
|
||||
class="separator"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VueSelect
|
||||
v-model="selectedChunk"
|
||||
:disabled="Object.keys(modulesTrees).length === 0"
|
||||
@@ -33,11 +41,13 @@
|
||||
:label="`${$t('vue-webpack.analyzer.chunk')} ${getChunkName(key)}`"
|
||||
/>
|
||||
</VueSelect>
|
||||
|
||||
<VueSelect v-model="sizeField">
|
||||
<VueSelectButton value="stats" :label="`${$t('vue-webpack.sizes.stats')}`"/>
|
||||
<VueSelectButton value="parsed" :label="`${$t('vue-webpack.sizes.parsed')}`"/>
|
||||
<VueSelectButton value="gzip" :label="`${$t('vue-webpack.sizes.gzip')}`"/>
|
||||
</VueSelect>
|
||||
|
||||
<VueButton
|
||||
class="icon-button"
|
||||
icon-left="help"
|
||||
|
||||
@@ -19,11 +19,20 @@
|
||||
class="separator"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<VueSwitch
|
||||
v-if="modernMode"
|
||||
v-model="showModernBuild"
|
||||
>
|
||||
{{ $t('vue-webpack.modern-mode') }}
|
||||
</VueSwitch>
|
||||
|
||||
<VueSelect v-model="sizeField">
|
||||
<VueSelectButton value="stats" :label="`${$t('vue-webpack.sizes.stats')}`"/>
|
||||
<VueSelectButton value="parsed" :label="`${$t('vue-webpack.sizes.parsed')}`"/>
|
||||
<VueSelectButton value="gzip" :label="`${$t('vue-webpack.sizes.gzip')}`"/>
|
||||
</VueSelect>
|
||||
|
||||
<VueButton
|
||||
class="icon-button"
|
||||
icon-left="help"
|
||||
@@ -67,12 +76,6 @@ export default {
|
||||
SpeedStats,
|
||||
AssetList,
|
||||
ModuleList
|
||||
},
|
||||
|
||||
sharedData () {
|
||||
return {
|
||||
serveUrl: `webpack-dashboard-serve-url`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,21 +14,51 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
sharedData () {
|
||||
return {
|
||||
serveUrl: `webpack-dashboard-serve-url`,
|
||||
modernMode: `webpack-dashboard-modern-mode`
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
sizeField: {
|
||||
get () { return this.$store.getters.sizeField },
|
||||
set (value) { this.$store.commit('sizeField', value) }
|
||||
},
|
||||
|
||||
showModernBuild: {
|
||||
get () { return this.$store.state.showModernBuild },
|
||||
set (value) { this.$store.commit('showModernBuild', value) }
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
modernMode: {
|
||||
handler (value) {
|
||||
this.showModernBuild = value
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
const mode = this.mode = this.TaskDetails.task.command.indexOf('vue-cli-service serve') !== -1 ? 'serve' : 'build'
|
||||
this.$store.commit('mode', mode)
|
||||
this.$watchSharedData(`webpack-dashboard-${mode}-stats`, value => {
|
||||
this.$store.commit('stats', {
|
||||
mode,
|
||||
value
|
||||
if (mode === 'build') {
|
||||
this.syncMode('build-modern')
|
||||
}
|
||||
this.syncMode(mode)
|
||||
},
|
||||
|
||||
methods: {
|
||||
syncMode (mode) {
|
||||
this.$watchSharedData(`webpack-dashboard-${mode}-stats`, value => {
|
||||
this.$store.commit('stats', {
|
||||
mode,
|
||||
value
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,28 @@ const store = new Vuex.Store({
|
||||
return {
|
||||
sizeField: localStorage.getItem('vue-webpack.sizeField') || 'parsed',
|
||||
mode: 'serve',
|
||||
showModernBuild: true,
|
||||
serve: {
|
||||
stats: null
|
||||
},
|
||||
build: {
|
||||
stats: null
|
||||
},
|
||||
'build-modern': {
|
||||
stats: null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
sizeField: state => state.sizeField,
|
||||
mode: state => state.mode,
|
||||
stats: state => state[state.mode].stats,
|
||||
mode: state => {
|
||||
if (state.mode === 'build' && state.showModernBuild) {
|
||||
return 'build-modern'
|
||||
}
|
||||
return state.mode
|
||||
},
|
||||
stats: (state, getters) => state[getters.mode].stats,
|
||||
errors: (state, getters) => (getters.stats && getters.stats.data.errors) || [],
|
||||
warnings: (state, getters) => (getters.stats && getters.stats.data.warnings) || [],
|
||||
assets: (state, getters) => (getters.stats && getters.stats.data.assets) || [],
|
||||
@@ -47,6 +56,10 @@ const store = new Vuex.Store({
|
||||
state.mode = value
|
||||
},
|
||||
|
||||
showModernBuild (state, value) {
|
||||
state.showModernBuild = value
|
||||
},
|
||||
|
||||
stats (state, { mode, value }) {
|
||||
state[mode].stats = value
|
||||
}
|
||||
|
||||
@@ -410,6 +410,7 @@
|
||||
"gzip": "Gzip",
|
||||
"help": "<b>Stats:</b> size from webpack stats data.<br><b>Parsed:</b> size from extracted source (after minification plugins). More accurate.<br><b>Gzip:</b> size of gzipped extracted source."
|
||||
},
|
||||
"modern-mode": "Show modern build",
|
||||
"tasks": {
|
||||
"serve": {
|
||||
"description": "Compiles and hot-reloads for development",
|
||||
@@ -431,7 +432,11 @@
|
||||
"wc-async": "Async web component"
|
||||
},
|
||||
"name": "Name for library or web-component mode (default: 'name' in package.json or entry filename)",
|
||||
"watch": "Watch for changes"
|
||||
"watch": "Watch for changes",
|
||||
"modern": {
|
||||
"label": "Modern mode",
|
||||
"description": "Build app targeting modern browsers with auto fallback"
|
||||
}
|
||||
},
|
||||
"inspect": {
|
||||
"description": "Inspect the resolved webpack config",
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
|
||||
module.exports = api => {
|
||||
const { setSharedData, removeSharedData } = api.namespace('webpack-dashboard-')
|
||||
|
||||
let firstRun = true
|
||||
let hadFailed = false
|
||||
let modernMode = false
|
||||
|
||||
function resetSharedData (key) {
|
||||
setSharedData(`${key}-status`, null)
|
||||
@@ -13,35 +17,52 @@ module.exports = api => {
|
||||
setSharedData(`${key}-problems`, null)
|
||||
}
|
||||
|
||||
function onWebpackMessage ({ data: message }) {
|
||||
async function onWebpackMessage ({ data: message }) {
|
||||
if (message.webpackDashboardData) {
|
||||
const type = message.webpackDashboardData.type
|
||||
for (const data of message.webpackDashboardData.value) {
|
||||
setSharedData(`${type}-${data.type}`, data.value)
|
||||
if (data.type === 'stats') {
|
||||
// Stats are read from a file
|
||||
const statsFile = path.resolve(process.cwd(), `.stats-${type}.json`)
|
||||
const value = await fs.readJson(statsFile)
|
||||
setSharedData(`${type}-${data.type}`, value)
|
||||
await fs.remove(statsFile)
|
||||
} else if (type.indexOf('build') !== -1 && modernMode && data.type === 'progress') {
|
||||
// Progress is shared between 'build' and 'build-modern'
|
||||
// 'build' first and then 'build-modern'
|
||||
const value = type === 'build' ? data.value / 2 : (data.value + 1) / 2
|
||||
// We display the same progress bar for both
|
||||
for (const t of ['build', 'build-modern']) {
|
||||
setSharedData(`${t}-${data.type}`, value)
|
||||
}
|
||||
} else {
|
||||
setSharedData(`${type}-${data.type}`, data.value)
|
||||
|
||||
if (type === 'serve' && data.type === 'status') {
|
||||
if (data.value === 'Failed') {
|
||||
api.notify({
|
||||
title: 'Build failed',
|
||||
message: 'The build has errors.',
|
||||
icon: 'error'
|
||||
})
|
||||
hadFailed = true
|
||||
} else if (data.value === 'Success') {
|
||||
if (hadFailed) {
|
||||
// Notifications
|
||||
if (type === 'serve' && data.type === 'status') {
|
||||
if (data.value === 'Failed') {
|
||||
api.notify({
|
||||
title: 'Build fixed',
|
||||
message: 'The build succeeded.',
|
||||
icon: 'done'
|
||||
title: 'Build failed',
|
||||
message: 'The build has errors.',
|
||||
icon: 'error'
|
||||
})
|
||||
hadFailed = false
|
||||
} else if (firstRun) {
|
||||
api.notify({
|
||||
title: 'App ready',
|
||||
message: 'The build succeeded.',
|
||||
icon: 'done'
|
||||
})
|
||||
firstRun = false
|
||||
hadFailed = true
|
||||
} else if (data.value === 'Success') {
|
||||
if (hadFailed) {
|
||||
api.notify({
|
||||
title: 'Build fixed',
|
||||
message: 'The build succeeded.',
|
||||
icon: 'done'
|
||||
})
|
||||
hadFailed = false
|
||||
} else if (firstRun) {
|
||||
api.notify({
|
||||
title: 'App ready',
|
||||
message: 'The build succeeded.',
|
||||
icon: 'done'
|
||||
})
|
||||
firstRun = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +72,7 @@ module.exports = api => {
|
||||
|
||||
// Init data
|
||||
api.onProjectOpen(() => {
|
||||
for (const key of ['serve', 'build']) {
|
||||
for (const key of ['serve', 'build', 'build-modern']) {
|
||||
resetSharedData(key)
|
||||
}
|
||||
})
|
||||
@@ -155,6 +176,14 @@ module.exports = api => {
|
||||
link: 'https://cli.vuejs.org/guide/cli-service.html#vue-cli-service-build',
|
||||
icon: '/public/webpack-logo.png',
|
||||
prompts: [
|
||||
{
|
||||
name: 'modern',
|
||||
type: 'confirm',
|
||||
default: false,
|
||||
message: 'vue-webpack.tasks.build.modern.label',
|
||||
description: 'vue-webpack.tasks.build.modern.description',
|
||||
link: 'https://cli.vuejs.org/guide/browser-compatibility.html#modern-mode'
|
||||
},
|
||||
{
|
||||
name: 'mode',
|
||||
type: 'list',
|
||||
@@ -225,10 +254,13 @@ module.exports = api => {
|
||||
if (answers.target) args.push('--target', answers.target)
|
||||
if (answers.name) args.push('--port', answers.name)
|
||||
if (answers.watch) args.push('--watch')
|
||||
if (answers.modern) args.push('--modern')
|
||||
setSharedData('modern-mode', modernMode = !!answers.modern)
|
||||
args.push('--dashboard')
|
||||
|
||||
// Data
|
||||
resetSharedData('build')
|
||||
resetSharedData('build-modern')
|
||||
},
|
||||
onRun: () => {
|
||||
api.ipcOn(onWebpackMessage)
|
||||
|
||||
Reference in New Issue
Block a user