1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2024-11-25 08:59:31 -05:00

Refactor from Vue2 to Vue3 (#20044)

Close #19902
This commit is contained in:
André Jaenisch 2022-10-01 16:26:38 +02:00 committed by GitHub
parent 726afe8a9e
commit 04e97b8311
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 384 additions and 606 deletions

763
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@
"@claviska/jquery-minicolors": "2.3.6", "@claviska/jquery-minicolors": "2.3.6",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-2", "@mcaptcha/vanilla-glue": "0.1.0-alpha-2",
"@primer/octicons": "17.5.0", "@primer/octicons": "17.5.0",
"@vue/compiler-sfc": "3.2.37",
"add-asset-webpack-plugin": "2.0.1", "add-asset-webpack-plugin": "2.0.1",
"css-loader": "6.7.1", "css-loader": "6.7.1",
"dropzone": "6.0.0-beta.2", "dropzone": "6.0.0-beta.2",
@ -34,11 +35,10 @@
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"tributejs": "5.1.3", "tributejs": "5.1.3",
"uint8-to-base64": "0.2.0", "uint8-to-base64": "0.2.0",
"vue": "2.6.14", "vue": "3.2.37",
"vue-bar-graph": "1.3.1", "vue-bar-graph": "2.0.0",
"vue-calendar-heatmap": "0.8.4", "vue-loader": "17.0.0",
"vue-loader": "15.9.8", "vue3-calendar-heatmap": "2.0.0",
"vue-template-compiler": "2.6.14",
"webpack": "5.74.0", "webpack": "5.74.0",
"webpack-cli": "4.10.0", "webpack-cli": "4.10.0",
"workbox-routing": "6.5.4", "workbox-routing": "6.5.4",

View file

@ -63,9 +63,9 @@
} }
})(); })();
</script> </script>
<div id="diff-file-list-container"></div> <div id="diff-file-list"></div>
<div id="diff-container"> <div id="diff-container">
<div id="diff-file-tree-container"></div> <div id="diff-file-tree"></div>
<div id="diff-file-boxes" class="sixteen wide column"> <div id="diff-file-boxes" class="sixteen wide column">
{{range $i, $file := .Diff.Files}} {{range $i, $file := .Diff.Files}}
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}} {{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}

View file

@ -19,7 +19,10 @@
{{end}} {{end}}
inline-template inline-template
v-cloak v-cloak
> ></repo-search>
</div>
<template id="dashboard-repo-list-template">
<div> <div>
<div v-if="!isOrganization" class="ui two item tabable menu"> <div v-if="!isOrganization" class="ui two item tabable menu">
<a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{.locale.Tr "repository"}}</a> <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{.locale.Tr "repository"}}</a>
@ -193,5 +196,4 @@
</div> </div>
</div> </div>
</div> </div>
</repo-search> </template>
</div>

View file

@ -15,7 +15,7 @@
</div> </div>
</template> </template>
<script> <script>
import {CalendarHeatmap} from 'vue-calendar-heatmap'; import {CalendarHeatmap} from 'vue3-calendar-heatmap';
export default { export default {
name: 'ActivityHeatmap', name: 'ActivityHeatmap',

View file

@ -1,5 +1,5 @@
<template> <template>
<div> <div ref="root">
<div v-if="loading" class="ui active centered inline loader"/> <div v-if="loading" class="ui active centered inline loader"/>
<div v-if="!loading && issue !== null"> <div v-if="!loading && issue !== null">
<p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p> <p><small>{{ issue.repository.full_name }} on {{ createdAt }}</small></p>
@ -109,15 +109,16 @@ export default {
}, },
mounted() { mounted() {
this.$root.$on('load-context-popup', (data, callback) => { this.$refs.root.addEventListener('us-load-context-popup', (e) => {
const data = e.detail;
if (!this.loading && this.issue === null) { if (!this.loading && this.issue === null) {
this.load(data, callback); this.load(data);
} }
}); });
}, },
methods: { methods: {
load(data, callback) { load(data) {
this.loading = true; this.loading = true;
this.i18nErrorMessage = null; this.i18nErrorMessage = null;
$.get(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`).done((issue) => { $.get(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`).done((issue) => {
@ -130,9 +131,6 @@ export default {
} }
}).always(() => { }).always(() => {
this.loading = false; this.loading = false;
if (callback) {
this.$nextTick(callback);
}
}); });
} }
} }

View file

@ -1,12 +1,12 @@
import Vue from 'vue'; import {createApp, nextTick} from 'vue';
import $ from 'jquery'; import $ from 'jquery';
import {initVueSvg, vueDelimiters} from './VueComponentLoader.js'; import {initVueSvg, vueDelimiters} from './VueComponentLoader.js';
import {initTooltip} from '../modules/tippy.js'; import {initTooltip} from '../modules/tippy.js';
const {appSubUrl, assetUrlPrefix, pageData} = window.config; const {appSubUrl, assetUrlPrefix, pageData} = window.config;
function initVueComponents() { function initVueComponents(app) {
Vue.component('repo-search', { app.component('repo-search', {
delimiters: vueDelimiters, delimiters: vueDelimiters,
props: { props: {
searchLimit: { searchLimit: {
@ -138,13 +138,14 @@ function initVueComponents() {
}, },
mounted() { mounted() {
const el = document.getElementById('dashboard-repo-list');
this.changeReposFilter(this.reposFilter); this.changeReposFilter(this.reposFilter);
for (const el of this.$el.querySelectorAll('.tooltip')) { for (const elTooltip of el.querySelectorAll('.tooltip')) {
initTooltip(el); initTooltip(elTooltip);
} }
$(this.$el).find('.dropdown').dropdown(); $(el).find('.dropdown').dropdown();
this.setCheckboxes(); this.setCheckboxes();
Vue.nextTick(() => { nextTick(() => {
this.$refs.search.focus(); this.$refs.search.focus();
}); });
}, },
@ -192,7 +193,7 @@ function initVueComponents() {
this.reposFilter = filter; this.reposFilter = filter;
this.repos = []; this.repos = [];
this.page = 1; this.page = 1;
Vue.set(this.counts, `${filter}:${this.archivedFilter}:${this.privateFilter}`, 0); this.counts[`${filter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos(); this.searchRepos();
}, },
@ -261,7 +262,7 @@ function initVueComponents() {
this.page = 1; this.page = 1;
this.repos = []; this.repos = [];
this.setCheckboxes(); this.setCheckboxes();
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos(); this.searchRepos();
}, },
@ -283,7 +284,7 @@ function initVueComponents() {
this.page = 1; this.page = 1;
this.repos = []; this.repos = [];
this.setCheckboxes(); this.setCheckboxes();
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos(); this.searchRepos();
}, },
@ -297,7 +298,7 @@ function initVueComponents() {
this.page = 1; this.page = 1;
} }
this.repos = []; this.repos = [];
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, 0); this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = 0;
this.searchRepos(); this.searchRepos();
}, },
@ -331,7 +332,7 @@ function initVueComponents() {
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') { if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count; this.reposTotalCount = count;
} }
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count); this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`] = count;
this.finalPage = Math.ceil(count / this.searchLimit); this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory(); this.updateHistory();
this.isLoading = false; this.isLoading = false;
@ -352,22 +353,20 @@ function initVueComponents() {
} }
return 'octicon-repo'; return 'octicon-repo';
} }
} },
template: document.getElementById('dashboard-repo-list-template'),
}); });
} }
export function initDashboardRepoList() { export function initDashboardRepoList() {
const el = document.getElementById('dashboard-repo-list'); const el = document.getElementById('dashboard-repo-list');
const dashboardRepoListData = pageData.dashboardRepoList || null; const dashboardRepoListData = pageData.dashboardRepoList || null;
if (!el || !dashboardRepoListData) return; if (!el || !dashboardRepoListData) return;
initVueSvg(); const app = createApp({
initVueComponents();
new Vue({
el,
delimiters: vueDelimiters, delimiters: vueDelimiters,
data: () => { data() {
return { return {
searchLimit: dashboardRepoListData.searchLimit || 0, searchLimit: dashboardRepoListData.searchLimit || 0,
subUrl: appSubUrl, subUrl: appSubUrl,
@ -375,4 +374,7 @@ export function initDashboardRepoList() {
}; };
}, },
}); });
initVueSvg(app);
initVueComponents(app);
app.mount(el);
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<ol class="diff-detail-box diff-stats m-0" id="diff-files" v-if="fileListIsVisible"> <ol class="diff-detail-box diff-stats m-0" ref="root" v-if="fileListIsVisible">
<li v-for="file in files" :key="file.NameHash"> <li v-for="file in files" :key="file.NameHash">
<div class="bold df ac pull-right"> <div class="bold df ac pull-right">
<span v-if="file.IsBin" class="ml-1 mr-3">{{ binaryFileMessage }}</span> <span v-if="file.IsBin" class="ml-1 mr-3">{{ binaryFileMessage }}</span>
@ -37,7 +37,7 @@ export default {
fileListIsVisible(newValue) { fileListIsVisible(newValue) {
if (newValue === true) { if (newValue === true) {
this.$nextTick(() => { this.$nextTick(() => {
for (const el of this.$el.querySelectorAll('.tooltip')) { for (const el of this.$refs.root.querySelectorAll('.tooltip')) {
initTooltip(el); initTooltip(el);
} }
}); });

View file

@ -1,11 +1,10 @@
<template> <template>
<div <div
v-show="fileTreeIsVisible" v-if="fileTreeIsVisible"
id="diff-file-tree"
class="mr-3 mt-3 diff-detail-box" class="mr-3 mt-3 diff-detail-box"
> >
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often --> <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
<div class="ui list" v-if="fileTreeIsVisible"> <div class="ui list">
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item" /> <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item" />
</div> </div>
<div v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2"> <div v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2">
@ -117,6 +116,9 @@ export default {
const [toShow, toHide] = document.querySelectorAll('.diff-toggle-file-tree-button .icon'); const [toShow, toHide] = document.querySelectorAll('.diff-toggle-file-tree-button .icon');
toShow.classList.toggle('hide', visible); // hide the toShow icon if the tree is visible toShow.classList.toggle('hide', visible); // hide the toShow icon if the tree is visible
toHide.classList.toggle('hide', !visible); // similarly toHide.classList.toggle('hide', !visible); // similarly
const diffTree = document.getElementById('diff-file-tree');
diffTree.classList.toggle('hide', !visible);
}, },
loadMoreData() { loadMoreData() {
this.isLoadingNewData = true; this.isLoadingNewData = true;

View file

@ -1,4 +1,4 @@
import Vue from 'vue'; import {createApp, nextTick} from 'vue';
import $ from 'jquery'; import $ from 'jquery';
import {vueDelimiters} from './VueComponentLoader.js'; import {vueDelimiters} from './VueComponentLoader.js';
@ -37,10 +37,14 @@ export function initRepoBranchTagDropdown(selector) {
}); });
}); });
$data.remove(); $data.remove();
new Vue({
el: this, // eslint-disable-next-line unicorn/no-this-assignment
const elRoot = this;
const view = createApp({
delimiters: vueDelimiters, delimiters: vueDelimiters,
data, data() {
return data;
},
computed: { computed: {
filteredItems() { filteredItems() {
const items = this.items.filter((item) => { const items = this.items.filter((item) => {
@ -73,10 +77,10 @@ export function initRepoBranchTagDropdown(selector) {
}, },
beforeMount() { beforeMount() {
this.noResults = this.$el.getAttribute('data-no-results'); this.noResults = elRoot.getAttribute('data-no-results');
this.canCreateBranch = this.$el.getAttribute('data-can-create-branch') === 'true'; this.canCreateBranch = elRoot.getAttribute('data-can-create-branch') === 'true';
this.branchForm = this.$el.getAttribute('data-branch-form'); this.branchForm = elRoot.getAttribute('data-branch-form');
switch (this.$el.getAttribute('data-view-type')) { switch (elRoot.getAttribute('data-view-type')) {
case 'tree': case 'tree':
this.isViewTree = true; this.isViewTree = true;
break; break;
@ -87,19 +91,19 @@ export function initRepoBranchTagDropdown(selector) {
this.isViewBranch = true; this.isViewBranch = true;
break; break;
} }
this.refName = this.$el.getAttribute('data-ref-name'); this.refName = elRoot.getAttribute('data-ref-name');
this.branchURLPrefix = this.$el.getAttribute('data-branch-url-prefix'); this.branchURLPrefix = elRoot.getAttribute('data-branch-url-prefix');
this.branchURLSuffix = this.$el.getAttribute('data-branch-url-suffix'); this.branchURLSuffix = elRoot.getAttribute('data-branch-url-suffix');
this.tagURLPrefix = this.$el.getAttribute('data-tag-url-prefix'); this.tagURLPrefix = elRoot.getAttribute('data-tag-url-prefix');
this.tagURLSuffix = this.$el.getAttribute('data-tag-url-suffix'); this.tagURLSuffix = elRoot.getAttribute('data-tag-url-suffix');
this.setAction = this.$el.getAttribute('data-set-action') === 'true'; this.setAction = elRoot.getAttribute('data-set-action') === 'true';
this.submitForm = this.$el.getAttribute('data-submit-form') === 'true'; this.submitForm = elRoot.getAttribute('data-submit-form') === 'true';
document.body.addEventListener('click', (event) => { document.body.addEventListener('click', (event) => {
if (this.$el.contains(event.target)) return; if (elRoot.contains(event.target)) return;
if (this.menuVisible) { if (this.menuVisible) {
Vue.set(this, 'menuVisible', false); this.menuVisible = false;
} }
}); });
}, },
@ -135,7 +139,7 @@ export function initRepoBranchTagDropdown(selector) {
if (this.submitForm) { if (this.submitForm) {
$(`#${this.branchForm}`).trigger('submit'); $(`#${this.branchForm}`).trigger('submit');
} }
Vue.set(this, 'menuVisible', false); this.menuVisible = false;
} }
}, },
createNewBranch() { createNewBranch() {
@ -143,7 +147,7 @@ export function initRepoBranchTagDropdown(selector) {
$(this.$refs.newBranchForm).trigger('submit'); $(this.$refs.newBranchForm).trigger('submit');
}, },
focusSearchField() { focusSearchField() {
Vue.nextTick(() => { nextTick(() => {
this.$refs.searchField.focus(); this.$refs.searchField.focus();
}); });
}, },
@ -213,5 +217,6 @@ export function initRepoBranchTagDropdown(selector) {
} }
} }
}); });
view.mount(this);
}); });
} }

View file

@ -1,4 +1,4 @@
import Vue from 'vue'; import {createApp} from 'vue';
import {svgs} from '../svg.js'; import {svgs} from '../svg.js';
export const vueDelimiters = ['${', '}']; export const vueDelimiters = ['${', '}'];
@ -8,13 +8,14 @@ export function initVueEnv() {
if (vueEnvInited) return; if (vueEnvInited) return;
vueEnvInited = true; vueEnvInited = true;
const isProd = window.config.runModeIsProd; // As far as I could tell, this is no longer possible.
Vue.config.productionTip = false; // But there seem not to be a guide what to do instead.
Vue.config.devtools = !isProd; // const isProd = window.config.runModeIsProd;
// Vue.config.devtools = !isProd;
} }
let vueSvgInited = false; let vueSvgInited = false;
export function initVueSvg() { export function initVueSvg(app) {
if (vueSvgInited) return; if (vueSvgInited) return;
vueSvgInited = true; vueSvgInited = true;
@ -24,7 +25,7 @@ export function initVueSvg() {
.replace(/height="[0-9]+"/, 'v-bind:height="size"') .replace(/height="[0-9]+"/, 'v-bind:height="size"')
.replace(/width="[0-9]+"/, 'v-bind:width="size"'); .replace(/width="[0-9]+"/, 'v-bind:width="size"');
Vue.component(name, { app.component(name, {
props: { props: {
size: { size: {
type: String, type: String,
@ -42,8 +43,7 @@ export function initVueApp(el, opts = {}) {
} }
if (!el) return null; if (!el) return null;
return new Vue(Object.assign({ return createApp(
el, Object.assign({delimiters: vueDelimiters}, opts)
delimiters: vueDelimiters, ).mount(el);
}, opts));
} }

View file

@ -1,5 +1,5 @@
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue'; import {createApp} from 'vue';
import ContextPopup from '../components/ContextPopup.vue'; import ContextPopup from '../components/ContextPopup.vue';
import {parseIssueHref} from '../utils.js'; import {parseIssueHref} from '../utils.js';
import {createTippy} from '../modules/tippy.js'; import {createTippy} from '../modules/tippy.js';
@ -17,17 +17,12 @@ export default function initContextPopups() {
if (!owner) return; if (!owner) return;
const el = document.createElement('div'); const el = document.createElement('div');
el.innerHTML = '<div></div>';
this.parentNode.insertBefore(el, this.nextSibling); this.parentNode.insertBefore(el, this.nextSibling);
const View = Vue.extend({ const view = createApp(ContextPopup);
render: (createElement) => createElement(ContextPopup),
});
const view = new View();
try { try {
view.$mount(el.firstChild); view.mount(el);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
el.textContent = 'ContextPopup failed to load'; el.textContent = 'ContextPopup failed to load';
@ -37,7 +32,7 @@ export default function initContextPopups() {
content: el, content: el,
interactive: true, interactive: true,
onShow: () => { onShow: () => {
view.$emit('load-context-popup', {owner, repo, index}); el.firstChild.dispatchEvent(new CustomEvent('us-load-context-popup', {detail: {owner, repo, index}}));
} }
}); });
}); });

View file

@ -1,4 +1,4 @@
import Vue from 'vue'; import {createApp} from 'vue';
import ActivityHeatmap from '../components/ActivityHeatmap.vue'; import ActivityHeatmap from '../components/ActivityHeatmap.vue';
export default function initHeatmap() { export default function initHeatmap() {
@ -17,11 +17,9 @@ export default function initHeatmap() {
return {date: new Date(v), count: heatmap[v]}; return {date: new Date(v), count: heatmap[v]};
}); });
const View = Vue.extend({ const View = createApp(ActivityHeatmap, {values});
render: (createElement) => createElement(ActivityHeatmap, {props: {values}}),
});
new View().$mount(el); View.mount(el);
} catch (err) { } catch (err) {
console.error('Heatmap failed to load', err); console.error('Heatmap failed to load', err);
el.textContent = 'Heatmap failed to load'; el.textContent = 'Heatmap failed to load';

View file

@ -1,21 +1,17 @@
import Vue from 'vue'; import {createApp} from 'vue';
import DiffFileTree from '../components/DiffFileTree.vue'; import DiffFileTree from '../components/DiffFileTree.vue';
import DiffFileList from '../components/DiffFileList.vue'; import DiffFileList from '../components/DiffFileList.vue';
export default function initDiffFileTree() { export default function initDiffFileTree() {
const el = document.getElementById('diff-file-tree-container'); const el = document.getElementById('diff-file-tree');
if (!el) return; if (!el) return;
const View = Vue.extend({ const fileTreeView = createApp(DiffFileTree);
render: (createElement) => createElement(DiffFileTree), fileTreeView.mount(el);
});
new View().$mount(el);
const fileListElement = document.getElementById('diff-file-list-container'); const fileListElement = document.getElementById('diff-file-list');
if (!fileListElement) return; if (!fileListElement) return;
const fileListView = Vue.extend({ const fileListView = createApp(DiffFileList);
render: (createElement) => createElement(DiffFileList), fileListView.mount(fileListElement);
});
new fileListView().$mount(fileListElement);
} }

View file

@ -1,12 +1,10 @@
import Vue from 'vue'; import {createApp} from 'vue';
import PullRequestMergeForm from '../components/PullRequestMergeForm.vue'; import PullRequestMergeForm from '../components/PullRequestMergeForm.vue';
export default function initPullRequestMergeForm() { export default function initPullRequestMergeForm() {
const el = document.getElementById('pull-request-merge-form'); const el = document.getElementById('pull-request-merge-form');
if (!el) return; if (!el) return;
const View = Vue.extend({ const view = createApp(PullRequestMergeForm);
render: (createElement) => createElement(PullRequestMergeForm), view.mount(el);
});
new View().$mount(el);
} }

View file

@ -26,8 +26,6 @@ import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.sv
import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg'; import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg';
import Vue from 'vue';
export const svgs = { export const svgs = {
'octicon-chevron-down': octiconChevronDown, 'octicon-chevron-down': octiconChevronDown,
'octicon-chevron-right': octiconChevronRight, 'octicon-chevron-right': octiconChevronRight,
@ -74,7 +72,8 @@ export function svg(name, size = 16, className = '') {
return serializer.serializeToString(svgNode); return serializer.serializeToString(svgNode);
} }
export const SvgIcon = Vue.component('SvgIcon', { export const SvgIcon = {
name: 'SvgIcon',
props: { props: {
name: {type: String, required: true}, name: {type: String, required: true},
size: {type: Number, default: 16}, size: {type: Number, default: 16},
@ -88,4 +87,4 @@ export const SvgIcon = Vue.component('SvgIcon', {
}, },
template: `<span v-html="svg" />` template: `<span v-html="svg" />`
}); };

View file

@ -3,10 +3,16 @@
text-align: center; text-align: center;
position: relative; position: relative;
min-height: 125px; min-height: 125px;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
// for the "Less" and "More" legend
.vch__legend .vch__legend div:first-child,
.vch__legend .vch__legend div:last-child {
display: inline-block;
padding: 0 5px;
}
> svg { > svg {
width: 100%; width: 100%;
} }

View file

@ -4,14 +4,13 @@ import AddAssetPlugin from 'add-asset-webpack-plugin';
import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin'; import LicenseCheckerWebpackPlugin from 'license-checker-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
import VueLoader from 'vue-loader'; import {VueLoaderPlugin} from 'vue-loader';
import EsBuildLoader from 'esbuild-loader'; import EsBuildLoader from 'esbuild-loader';
import {parse, dirname} from 'path'; import {parse, dirname} from 'path';
import webpack from 'webpack'; import webpack from 'webpack';
import {fileURLToPath} from 'url'; import {fileURLToPath} from 'url';
import {readFileSync} from 'fs'; import {readFileSync} from 'fs';
const {VueLoaderPlugin} = VueLoader;
const {ESBuildMinifyPlugin} = EsBuildLoader; const {ESBuildMinifyPlugin} = EsBuildLoader;
const {SourceMapDevToolPlugin} = webpack; const {SourceMapDevToolPlugin} = webpack;
const formatLicenseText = (licenseText) => wrapAnsi(licenseText || '', 80).trim(); const formatLicenseText = (licenseText) => wrapAnsi(licenseText || '', 80).trim();
@ -242,9 +241,6 @@ export default {
}, },
resolve: { resolve: {
symlinks: false, symlinks: false,
alias: {
vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only
},
}, },
watchOptions: { watchOptions: {
ignored: [ ignored: [