perf(vite): chunk webcams, locales and large libraries (#1578)

This commit is contained in:
Pieter Willekens 2023-10-07 23:00:35 +02:00 committed by GitHub
parent 817efba0d8
commit 32122348b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 875 additions and 784 deletions

1284
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,8 +34,6 @@
"@jaames/iro": "^5.5.2",
"@lezer/highlight": "^1.0.0",
"@sindarius/gcodeviewer": "^3.2.2",
"@types/node": "^18.0.0",
"@types/overlayscrollbars": "^1.12.1",
"@uiw/codemirror-theme-vscode": "^4.19.11",
"axios": "^0.27.0",
"codemirror": "^6.0.1",
@ -45,12 +43,12 @@
"echarts-gl": "^2.0.8",
"hls.js": "^1.3.3",
"jmuxer": "^2.0.5",
"js-sha256": "^0.9.0",
"js-sha256": "^0.10.0",
"lodash.kebabcase": "^4.1.1",
"lodash.throttle": "^4.1.1",
"overlayscrollbars": "^1.13.1",
"overlayscrollbars-vue": "^0.2.2",
"regenerator-runtime": "^0.13.9",
"regenerator-runtime": "^0.14.0",
"resize-observer-polyfill": "^1.5.1",
"semver": "^7.5.2",
"typed_janus_js": "^1.0.14",
@ -76,8 +74,10 @@
"@types/jmuxer": "^2.0.3",
"@types/lodash.kebabcase": "^4.1.6",
"@types/lodash.throttle": "^4.1.6",
"@types/node": "^20.0.0",
"@types/overlayscrollbars": "^1.12.1",
"@types/semver": "^7.3.8",
"@types/uuid": "^8.3.1",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
@ -87,13 +87,15 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-jsonc": "^2.2.1",
"eslint-plugin-vue": "^9.0.0",
"postcss": "^8.4.31",
"postcss-nesting": "^12.0.1",
"prettier": "^3.0.0",
"sass": "~1.32",
"start-server-and-test": "^1.14.0",
"start-server-and-test": "^2.0.0",
"typescript": "^4.5.5",
"unplugin-vue-components": "^0.22.12",
"vite": "^3.2.7",
"vite-plugin-checker": "^0.5.0",
"vite": "^4.4.9",
"vite-plugin-checker": "^0.6.0",
"vite-plugin-package-version": "^1.0.2",
"vite-plugin-pwa": "^0.16.4",
"vite-plugin-vue2": "^2.0.1",

View File

@ -40,6 +40,7 @@ import TheUploadSnackbar from '@/components/TheUploadSnackbar.vue'
import TheManualProbeDialog from '@/components/dialogs/TheManualProbeDialog.vue'
import TheBedScrewsDialog from '@/components/dialogs/TheBedScrewsDialog.vue'
import TheScrewsTiltAdjustDialog from '@/components/dialogs/TheScrewsTiltAdjustDialog.vue'
import { setAndLoadLocale } from './plugins/i18n'
Component.registerHooks(['metaInfo'])
@ -162,8 +163,8 @@ export default class App extends Mixins(BaseMixin) {
}
@Watch('language')
languageChanged(newVal: string): void {
this.$i18n.locale = newVal
async languageChanged(newVal: string): Promise<void> {
await setAndLoadLocale(newVal)
}
@Watch('customStylesheet')
@ -310,10 +311,10 @@ export default class App extends Mixins(BaseMixin) {
<style>
@import './assets/styles/fonts.css';
@import './assets/styles/toastr.css';
@import './assets/styles/page.scss';
@import './assets/styles/sidebar.scss';
@import './assets/styles/utils.scss';
@import './assets/styles/updateManager.scss';
@import './assets/styles/page.css';
@import './assets/styles/sidebar.css';
@import './assets/styles/utils.css';
@import './assets/styles/updateManager.css';
:root {
--app-height: 100%;

View File

@ -1,12 +1,3 @@
@mixin no-select {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.user-select-none {
-webkit-touch-callout: none;
-webkit-user-select: none;

View File

@ -124,7 +124,7 @@ export default class CommandHelpModal extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.command-help-content {
overflow-x: hidden;

View File

@ -341,7 +341,7 @@ export default class TheEditor extends Mixins(BaseMixin) {
}
}
</script>
<style lang="scss" scoped>
<style scoped>
::v-deep .ͼ1 .cm-panel.cm-search *:focus:not(.focus-visible) {
outline: none;
}

View File

@ -1,4 +1,4 @@
<style scoped lang="scss"></style>
<style scoped></style>
<template>
<div :class="'consoleTable ' + (isMini ? 'mini' : '')">

View File

@ -1,4 +1,4 @@
<style scoped lang="scss">
<style scoped>
.consoleTableRow {
font-family: 'Roboto Mono', monospace;
font-size: 0.95em;

View File

@ -173,4 +173,4 @@ export default class TheBedScrewsDialog extends Mixins(BaseMixin, ControlMixin)
}
</script>
<style lang="scss" scoped></style>
<style scoped></style>

View File

@ -181,7 +181,7 @@ export default class TheManualProbeDialog extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.v-btn-toggle {
width: 100%;
}

View File

@ -274,7 +274,7 @@ export default class ExtruderControlPanel extends Mixins(BaseMixin, ExtruderMixi
}
</script>
<style lang="scss" scoped>
<style scoped>
._btn-group {
border-radius: 4px;
display: inline-flex;

View File

@ -49,14 +49,13 @@ export default class ExtruderControlPanel extends Mixins(BaseMixin, ControlMixin
}
</script>
<style lang="scss" scoped>
<style scoped>
._btn-group {
border-radius: 4px;
display: inline-flex;
flex-wrap: nowrap;
max-width: 100%;
min-width: 100%;
width: 100%;
.v-btn {
border-radius: 0;

View File

@ -116,7 +116,7 @@ export default class GitCommitsList extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.groupedCommits {
padding-top: 0;
}

View File

@ -99,7 +99,7 @@ export default class GitCommitsListDayCommit extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
li.commit {
border-color: rgb(48, 54, 61);
border-style: solid;

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<panel

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<div v-if="['printing', 'paused'].includes(printer_state) && printing_objects.length">

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<div>

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<div>

View File

@ -1,6 +1,5 @@
<style lang="scss" scoped>
<style scoped>
svg {
//background: rgba(200, 200, 200);
border: 2px solid #888;
}

View File

@ -156,7 +156,7 @@ export default class StatusPanelJobqueue extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.filesJobqueue {
position: relative;
}

View File

@ -275,7 +275,7 @@ export default class StatusPanelJobqueueEntry extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.filesJobqueue {
position: relative;
}

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<div>

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<div>

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<v-card-text class="pa-0">

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped></style>
<style scoped></style>
<template>
<v-card-text class="pa-0">

View File

@ -223,7 +223,7 @@ export default class StatusPanelPrintstatusThumbnail extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.statusPanel-big-thumbnail {
transition: height 0.25s ease-out;
}

View File

@ -397,7 +397,7 @@ export default class StatusPanel extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
._border-radius {
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;

View File

@ -155,7 +155,7 @@ export default class TemperaturePanelList extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.temperature-panel-table th,
.temperature-panel-table ::v-deep td {
padding-top: 5px !important;

View File

@ -273,7 +273,7 @@ export default class TemperaturePanelListItem extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
::v-deep .v-icon._no-focus-style:focus::after {
opacity: 0 !important;
}

View File

@ -99,7 +99,7 @@ export default class TemperaturePanelListItemNevermore extends Mixins(BaseMixin)
}
</script>
<style lang="scss" scoped>
<style scoped>
::v-deep .v-icon._no-focus-style:focus::after {
opacity: 0 !important;
}

View File

@ -107,7 +107,7 @@ export default class TemperaturePanelPresets extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
._preset-title {
font-size: 0.8125rem;
font-weight: 500;

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped>
<style scoped>
.btnHomeAxis {
width: 36px;
min-width: 36px !important;

View File

@ -706,7 +706,7 @@ export default class CircleControl extends Mixins(BaseMixin, ControlMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
svg {
max-height: 350px;
min-height: 275px;

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped>
<style scoped>
.btnMinWidthAuto {
min-width: auto !important;
}

View File

@ -267,7 +267,7 @@ export default class ZoffsetControl extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.v-btn-toggle {
width: 100%;
}

View File

@ -82,6 +82,25 @@ export default class SettingsGeneralTab extends Mixins(BaseMixin, SettingsGenera
mdiHelpCircle = mdiHelpCircle
mdiCloseThick = mdiCloseThick
availableLanguages: { text: string; value: string }[] = []
async created() {
const locales = import.meta.glob<string>('../../locales/*.json', { import: 'title' })
const languages: { text: string; value: string }[] = []
for (const file in locales) {
const langKey = file.slice(file.lastIndexOf('.') - 2, file.lastIndexOf('.'))
const title = await locales[file]()
languages.push({
text: title,
value: langKey,
})
}
this.availableLanguages = languages
}
get printerName() {
return this.$store.state.gui.general.printername
}
@ -98,22 +117,6 @@ export default class SettingsGeneralTab extends Mixins(BaseMixin, SettingsGenera
this.$store.dispatch('gui/saveSetting', { name: 'general.language', value: newVal })
}
get availableLanguages() {
const locales = import.meta.glob('../../locales/*.json', { eager: true }) as { [key: string]: any }
const languages: { text: string; value: string }[] = []
Object.keys(locales).map((file: string) => {
const langKey = file.slice(file.lastIndexOf('.') - 2, file.lastIndexOf('.'))
languages.push({
text: locales[file].title,
value: langKey,
})
})
return languages
}
get dateFormat() {
return this.$store.state.gui.general.dateFormat
}

View File

@ -78,4 +78,4 @@ export default class SettingsMiscellaneousTab extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped></style>
<style scoped></style>

View File

@ -233,4 +233,4 @@ export default class SettingsMiscellaneousTabLightGroups extends Mixins(BaseMixi
}
</script>
<style lang="scss" scoped></style>
<style scoped></style>

View File

@ -464,4 +464,4 @@ export default class SettingsMiscellaneousTabLightPresets extends Mixins(BaseMix
}
</script>
<style lang="scss" scoped></style>
<style scoped></style>

View File

@ -322,7 +322,7 @@ export default class WebcamForm extends Mixins(BaseMixin, WebcamMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
::v-deep ._transition svg {
transition: transform 500ms;
}

View File

@ -70,4 +70,4 @@ export default class WebcamListEntry extends Mixins(BaseMixin, WebcamMixin) {
}
</script>
<style lang="scss" scoped></style>
<style scoped></style>

View File

@ -83,7 +83,7 @@ export default class Panel extends Mixins(BaseMixin) {
}
</script>
<style lang="scss" scoped>
<style scoped>
.expanded header.v-toolbar {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
@ -107,7 +107,7 @@ export default class Panel extends Mixins(BaseMixin) {
}
</style>
<style lang="scss">
<style>
.v-card.panel .v-toolbar__content {
padding-right: 0;
}

View File

@ -1,4 +1,4 @@
<style lang="scss" scoped>
<style scoped>
.btn-collapsible > * {
will-change: transform;
transition: transform 500ms;

View File

@ -1,31 +1,31 @@
<template>
<div>
<template v-if="service === 'mjpegstreamer'">
<mjpegstreamer :cam-settings="webcam" :show-fps="showFps" :printer-url="printerUrl" />
<mjpegstreamer-async :cam-settings="webcam" :show-fps="showFps" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'mjpegstreamer-adaptive'">
<mjpegstreamer-adaptive :cam-settings="webcam" :show-fps="showFps" :printer-url="printerUrl" />
<mjpegstreamer-adaptive-async :cam-settings="webcam" :show-fps="showFps" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'uv4l-mjpeg'">
<uv4l-mjpeg :cam-settings="webcam" :printer-url="printerUrl" />
<uv4l-mjpeg-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'ipstream'">
<ipstreamer :cam-settings="webcam" :printer-url="printerUrl" />
<ipstreamer-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'hlsstream'">
<hlsstreamer :cam-settings="webcam" :printer-url="printerUrl" />
<hlsstreamer-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'jmuxer-stream'">
<j-muxer-stream :cam-settings="webcam" :printer-url="printerUrl" />
<j-muxer-stream-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'webrtc-camerastreamer'">
<webrtc-camera-streamer :cam-settings="webcam" :printer-url="printerUrl" />
<webrtc-camera-streamer-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'webrtc-janus'">
<janus-streamer :cam-settings="webcam" :printer-url="printerUrl" />
<janus-streamer-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else-if="service === 'webrtc-mediamtx'">
<webrtc-media-m-t-x :cam-settings="webcam" :printer-url="printerUrl" />
<webrtc-media-m-t-x-async :cam-settings="webcam" :printer-url="printerUrl" />
</template>
<template v-else>
<p class="text-center py-3 font-italic">{{ $t('Panels.WebcamPanel.UnknownWebcamService') }}</p>
@ -38,18 +38,19 @@ import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
import { DynamicCamLoader } from '@/components/webcams/streamers/DynamicCamLoader'
@Component({
components: {
Hlsstreamer: () => import('@/components/webcams/Hlsstreamer.vue'),
Ipstreamer: () => import('@/components/webcams/Ipstreamer.vue'),
JanusStreamer: () => import('@/components/webcams/JanusStreamer.vue'),
JMuxerStream: () => import('@/components/webcams/JMuxerStream.vue'),
Mjpegstreamer: () => import('@/components/webcams/Mjpegstreamer.vue'),
MjpegstreamerAdaptive: () => import('@/components/webcams/MjpegstreamerAdaptive.vue'),
Uv4lMjpeg: () => import('@/components/webcams/Uv4lMjpeg.vue'),
WebrtcCameraStreamer: () => import('@/components/webcams/WebrtcCameraStreamer.vue'),
WebrtcMediaMTX: () => import('@/components/webcams/WebrtcMediaMTX.vue'),
HlsstreamerAsync: DynamicCamLoader('Hlsstreamer'),
IpstreamerAsync: DynamicCamLoader('Ipstreamer'),
JanusStreamerAsync: DynamicCamLoader('JanusStreamer'),
JMuxerStreamAsync: DynamicCamLoader('JMuxerStream'),
MjpegstreamerAsync: DynamicCamLoader('Mjpegstreamer'),
MjpegstreamerAdaptiveAsync: DynamicCamLoader('MjpegstreamerAdaptive'),
Uv4lMjpegAsync: DynamicCamLoader('Uv4lMjpeg'),
WebrtcCameraStreamerAsync: DynamicCamLoader('WebrtcCameraStreamer'),
WebrtcMediaMTXAsync: DynamicCamLoader('WebrtcMediaMTX'),
},
})
export default class WebcamWrapperItem extends Mixins(BaseMixin) {

View File

@ -0,0 +1,39 @@
import Vue from 'vue'
type StreamerTypes =
| 'Hlsstreamer'
| 'Ipstreamer'
| 'JanusStreamer'
| 'JMuxerStream'
| 'Mjpegstreamer'
| 'MjpegstreamerAdaptive'
| 'Uv4lMjpeg'
| 'WebrtcCameraStreamer'
| 'WebrtcMediaMTX'
function getDynamicCamImport(componentName: StreamerTypes) {
// split each webcam streamer into its own chunk
switch (componentName) {
case 'Hlsstreamer':
return () => import('@/components/webcams/streamers/Hlsstreamer.vue')
case 'Ipstreamer':
return () => import('@/components/webcams/streamers/Ipstreamer.vue')
case 'JanusStreamer':
return () => import('@/components/webcams/streamers/JanusStreamer.vue')
case 'JMuxerStream':
return () => import('@/components/webcams/streamers/JMuxerStream.vue')
case 'Mjpegstreamer':
return () => import('@/components/webcams/streamers/Mjpegstreamer.vue')
case 'MjpegstreamerAdaptive':
return () => import('@/components/webcams/streamers/MjpegstreamerAdaptive.vue')
case 'Uv4lMjpeg':
return () => import('@/components/webcams/streamers/Uv4lMjpeg.vue')
case 'WebrtcCameraStreamer':
return () => import('@/components/webcams/streamers/WebrtcCameraStreamer.vue')
case 'WebrtcMediaMTX':
return () => import('@/components/webcams/streamers/WebrtcMediaMTX.vue')
}
}
export const DynamicCamLoader = (componentName: StreamerTypes) =>
Vue.component(componentName, getDynamicCamImport(componentName))

View File

@ -1,41 +1,53 @@
import 'core-js/stable' // polyfills for older browsers
import 'regenerator-runtime' // async polyfill used by the gcodeviewer
import 'resize-observer-polyfill' // polyfill needed by the responsive class detection
import Vue from 'vue'
import App from '@/App.vue'
import vuetify from '@/plugins/vuetify'
import i18n from '@/plugins/i18n'
import i18n, { setAndLoadLocale } from '@/plugins/i18n'
import store from '@/store'
import router from '@/plugins/router'
import { WebSocketPlugin } from '@/plugins/webSocketClient'
Vue.config.productionTip = false
// vue-observe-visibility
import { ObserveVisibility } from 'vue-observe-visibility'
Vue.directive('observe-visibility', ObserveVisibility)
//vue-meta
import VueMeta from 'vue-meta'
Vue.use(VueMeta)
//vue-load-image
import VueLoadImage from 'vue-load-image'
Vue.component('VueLoadImage', VueLoadImage)
//vue-toast-notifications
import VueToast from 'vue-toast-notification'
import 'vue-toast-notification/dist/theme-sugar.css'
//overlayerscrollbars-vue
import { OverlayScrollbarsPlugin } from 'overlayscrollbars-vue'
import 'overlayscrollbars/css/OverlayScrollbars.css'
// Directives
import './directives/longpress'
import './directives/responsive-class'
// Echarts
import ECharts from 'vue-echarts'
import { use } from 'echarts/core'
// import ECharts modules manually to reduce bundle size
import { SVGRenderer } from 'echarts/renderers'
import { BarChart, LineChart, PieChart } from 'echarts/charts'
import { DatasetComponent, GridComponent, LegendComponent, TooltipComponent } from 'echarts/components'
// vue-resize
import 'vue-resize/dist/vue-resize.css'
// @ts-ignore
import VueResize from 'vue-resize'
Vue.config.productionTip = false
Vue.directive('observe-visibility', ObserveVisibility)
Vue.use(VueMeta)
Vue.component('VueLoadImage', VueLoadImage)
Vue.use(VueToast, {
duration: 3000,
})
//overlayerscrollbars-vue
import { OverlayScrollbarsPlugin } from 'overlayscrollbars-vue'
import 'overlayscrollbars/css/OverlayScrollbars.css'
const isSafari = navigator.userAgent.includes('Safari') && navigator.userAgent.search('Chrome') === -1
const isTouch = 'ontouchstart' in window || (navigator.maxTouchPoints > 0 && navigator.maxTouchPoints !== 256)
Vue.use(OverlayScrollbarsPlugin, {
@ -46,41 +58,27 @@ Vue.use(OverlayScrollbarsPlugin, {
},
})
// Directives
import './directives/longpress'
import './directives/responsive-class'
// Echarts
import ECharts from 'vue-echarts'
import { use } from 'echarts/core'
// import ECharts modules manually to reduce bundle size
import { SVGRenderer } from 'echarts/renderers'
import { LineChart, BarChart, PieChart } from 'echarts/charts'
import { GridComponent, LegendComponent, TooltipComponent, DatasetComponent } from 'echarts/components'
use([SVGRenderer, LineChart, BarChart, LegendComponent, PieChart, DatasetComponent, GridComponent, TooltipComponent])
Vue.component('EChart', ECharts)
// vue-resize
import 'vue-resize/dist/vue-resize.css'
// @ts-ignore
import VueResize from 'vue-resize'
Vue.use(VueResize)
const initLoad = async () => {
//load config.json
await fetch('/config.json')
.then((res) => res.json())
.then(async (file) => {
window.console.debug('Loaded config.json')
try {
//load config.json
const res = await fetch('/config.json')
const file = (await res.json()) as Record<string, unknown>
await store.dispatch('importConfigJson', file)
if ('defaultLocale' in file) i18n.locale = file.defaultLocale
})
.catch(() => {
window.console.error('config.json not found or cannot be decoded!')
})
window.console.debug('Loaded config.json')
await store.dispatch('importConfigJson', file)
if ('defaultLocale' in file) {
await setAndLoadLocale(file.defaultLocale as string)
}
} catch (e) {
window.console.error('Failed to load config.json')
window.console.error(e)
}
const url = store.getters['socket/getWebsocketUrl']
Vue.use(WebSocketPlugin, { url, store })

View File

@ -2,23 +2,21 @@ import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
function loadLocaleMessages() {
const locales = import.meta.globEager('../locales/*.json')
const messages: any = {}
import defaultLocale from '../locales/en.json'
for (const file in locales) {
const langKey = file.slice(file.lastIndexOf('.') - 2, file.lastIndexOf('.'))
const locale = (import.meta.env.VUE_APP_I18N_LOCALE as string) || 'en'
if (langKey && langKey.length > 1) {
messages[langKey] = JSON.parse(JSON.stringify(locales[file]))
}
}
return messages
}
export default new VueI18n({
locale: (import.meta.env.VUE_APP_I18N_LOCALE as string) || 'en',
const i18n = new VueI18n({
locale,
fallbackLocale: (import.meta.env.VUE_APP_I18N_FALLBACK_LOCALE as string) || 'en',
messages: loadLocaleMessages(),
messages: { en: defaultLocale },
})
export default i18n
export async function setAndLoadLocale(lang: string) {
const locales = await import(`../locales/${lang}.json`)
i18n.setLocaleMessage(lang, locales)
i18n.locale = lang
return locales
}

View File

@ -76,17 +76,35 @@ export default defineConfig({
resolvers: [VuetifyResolver()],
}),
],
css: {
preprocessorOptions: {
css: { charset: false },
scss: {
quietDeps: true,
},
postcss: {
plugins: [require('postcss-nesting')()],
},
},
build: {
target: 'safari12',
rollupOptions: {
output: {
manualChunks: (id: string) => {
if (id.includes('node_modules')) {
// split codemirror into its own chunk
if (id.includes('/codemirror/') || id.includes('/@codemirror/')) {
return 'codemirror'
}
// split these libs into their own chunks
const chunkedLibs = ['vuetify', 'echarts', 'overlayscrollbars']
for (const lib of chunkedLibs) {
if (id.includes(`/node_modules/${lib}/`)) {
return lib.replace('.js', '')
}
}
}
},
},
},
},
envPrefix: 'VUE_',