progress before vue 3

This commit is contained in:
Max Leiter 2022-05-21 17:27:51 -07:00
parent 4c98b81e35
commit aace97056b
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
49 changed files with 183 additions and 303 deletions

View file

@ -84,6 +84,8 @@ const vueRules = defineConfig({
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/v-slot-style": ["error", "longform"],
// Should be fixable in Vue 3 / when components use Vue.extend()
"@typescript-eslint/unbound-method": "off",
},
}).rules;
@ -113,6 +115,9 @@ const tsRulesTemp = defineConfig({
module.exports = defineConfig({
root: true,
parserOptions: {
ecmaVersion: 2022,
},
overrides: [
{
files: [
@ -132,7 +137,11 @@ module.exports = defineConfig({
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
rules: {...baseRules, ...tsRules, ...tsRulesTemp},
rules: {
...baseRules,
...tsRules,
...tsRulesTemp,
},
},
// TODO: verify
{

View file

@ -31,15 +31,6 @@ import ConfirmDialog from "./ConfirmDialog.vue";
import Mentions from "./Mentions.vue";
import VueApp from "vue";
// This stops Vue from complaining about adding objects to the component context
declare module "vue/types/vue" {
interface Vue {
debouncedResize: () => void;
// TODO; type as Timeout
dayChangeTimeout: any;
}
}
export default VueApp.extend({
name: "App",
components: {

View file

@ -27,30 +27,32 @@
</ChannelWrapper>
</template>
<script>
<script lang="ts">
import Vue, {PropType} from "vue";
import roundBadgeNumber from "../js/helpers/roundBadgeNumber";
import {ClientChan, ClientNetwork} from "../js/types";
import ChannelWrapper from "./ChannelWrapper.vue";
export default {
export default Vue.extend({
name: "Channel",
components: {
ChannelWrapper,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
active: Boolean,
isFiltering: Boolean,
},
computed: {
unreadCount() {
unreadCount(): string {
return roundBadgeNumber(this.channel.unread);
},
},
methods: {
close() {
close(): void {
this.$root.closeChannel(this.channel);
},
},
};
});
</script>

View file

@ -32,15 +32,17 @@
</div>
</template>
<script>
<script lang="ts">
import Vue, {PropType} from "vue";
import eventbus from "../js/eventbus";
import isChannelCollapsed from "../js/helpers/isChannelCollapsed";
import {ClientNetwork, ClientChan} from "../js/types";
export default {
export default Vue.extend({
name: "ChannelWrapper",
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
active: Boolean,
isFiltering: Boolean,
},
@ -53,7 +55,7 @@ export default {
},
},
methods: {
getAriaLabel() {
getAriaLabel(): string {
const extra = [];
const type = this.channel.type;
@ -75,14 +77,14 @@ export default {
return `${type}: ${this.channel.name} ${extra.length ? `(${extra.join(", ")})` : ""}`;
},
click() {
click(): void {
if (this.isFiltering) {
return;
}
this.$root.switchToChannel(this.channel);
},
openContextMenu(event) {
openContextMenu(event): void {
eventbus.emit("contextmenu:channel", {
event: event,
channel: this.channel,
@ -90,5 +92,5 @@ export default {
});
},
},
};
});
</script>

View file

@ -145,8 +145,8 @@ export default {
MessageSearchForm,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
focused: String,
},
computed: {

View file

@ -91,8 +91,8 @@ let autocompletionRef = null;
export default {
name: "ChatInput",
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
watch: {
"channel.id"() {

View file

@ -74,7 +74,7 @@ export default {
Username,
},
props: {
channel: Object,
channel: Object as PropType<ClientChan>,
},
data() {
return {

View file

@ -48,8 +48,8 @@ export default {
},
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
data() {
return {

View file

@ -138,7 +138,7 @@ export default {
props: {
link: Object,
keepScrollPosition: Function,
channel: Object,
channel: Object as PropType<ClientChan>,
},
data() {
return {

View file

@ -113,8 +113,8 @@ export default {
components: MessageTypes,
props: {
message: Object,
channel: Object,
network: Object,
channel: Object as PropType<ClientChan>,
network: Object as PropType<ClientNetwork>,
keepScrollPosition: Function,
isPreviousSource: Boolean,
focused: Boolean,

View file

@ -27,7 +27,7 @@ export default {
Message,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
messages: Array,
keepScrollPosition: Function,
focused: Boolean,

View file

@ -84,8 +84,8 @@ form.message-search.opened .input-wrapper {
export default {
name: "MessageSearchForm",
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
data() {
return {

View file

@ -20,7 +20,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -19,7 +19,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -23,7 +23,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -16,7 +16,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -17,7 +17,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -13,7 +13,7 @@ export default {
ParsedMessage,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
computed: {

View file

@ -19,7 +19,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -23,7 +23,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -20,7 +20,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -17,7 +17,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -8,7 +8,7 @@
export default {
name: "MessageChannelMode",
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -8,7 +8,7 @@
export default {
name: "MessageChannelMode",
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -13,7 +13,7 @@ export default {
ParsedMessage,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
computed: {

View file

@ -15,7 +15,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -20,7 +20,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -20,7 +20,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -6,7 +6,7 @@
export default {
name: "MessageTypeRaw",
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -21,7 +21,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
};

View file

@ -16,7 +16,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
computed: {

View file

@ -123,7 +123,7 @@ export default {
Username,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
message: Object,
},
methods: {

View file

@ -209,7 +209,7 @@ import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
import distance from "../js/helpers/distance";
import eventbus from "../js/eventbus";
export default Vue.extend({
export default {
name: "NetworkList",
components: {
JoinChannel,
@ -481,5 +481,5 @@ export default Vue.extend({
});
},
},
});
};
</script>

View file

@ -56,7 +56,7 @@ export default {
ChannelWrapper,
},
props: {
network: Object,
network: Object as PropType<ClientNetwork>,
isJoinChannelShown: Boolean,
active: Boolean,
isFiltering: Boolean,

View file

@ -7,7 +7,7 @@ export default {
props: {
text: String,
message: Object,
network: Object,
network: Object as PropType<ClientNetwork>,
},
render(createElement, context) {
return parse(

View file

@ -27,8 +27,8 @@ export default {
ParsedMessage,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
methods: {
localetime(date) {

View file

@ -27,8 +27,8 @@ export default {
ParsedMessage,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
};
</script>

View file

@ -25,8 +25,8 @@ export default {
ParsedMessage,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
methods: {
localetime(date) {

View file

@ -29,8 +29,8 @@ export default {
ParsedMessage,
},
props: {
network: Object,
channel: Object,
network: Object as PropType<ClientNetwork>,
channel: Object as PropType<ClientChan>,
},
methods: {
localetime(date) {

View file

@ -20,8 +20,8 @@ export default {
user: Object,
active: Boolean,
onHover: Function,
channel: Object,
network: Object,
channel: Object as PropType<ClientChan>,
network: Object as PropType<ClientNetwork>,
},
computed: {
mode() {

View file

@ -4,6 +4,7 @@ import storage from "../localStorage";
import {router, switchToChannel, navigate} from "../router";
import store from "../store";
import parseIrcUri from "../helpers/parseIrcUri";
import {ClientChan, ClientNetwork, InitClientChan} from "../types";
socket.on("init", function (data) {
store.commit("networks", mergeNetworkData(data.networks));
@ -47,8 +48,9 @@ socket.on("init", function (data) {
}
});
function mergeNetworkData(newNetworks) {
const collapsedNetworks = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
function mergeNetworkData(newNetworks: ClientNetwork[]) {
const stored = storage.get("thelounge.networks.collapsed");
const collapsedNetworks = stored ? new Set(JSON.parse(stored)) : new Set();
for (let n = 0; n < newNetworks.length; n++) {
const network = newNetworks[n];
@ -74,7 +76,7 @@ function mergeNetworkData(newNetworks) {
if (key === "channels") {
currentNetwork.channels = mergeChannelData(
currentNetwork.channels,
network.channels
network.channels as InitClientChan[]
);
} else {
currentNetwork[key] = network[key];
@ -87,7 +89,7 @@ function mergeNetworkData(newNetworks) {
return newNetworks;
}
function mergeChannelData(oldChannels, newChannels) {
function mergeChannelData(oldChannels: InitClientChan[], newChannels: InitClientChan[]) {
for (let c = 0; c < newChannels.length; c++) {
const channel = newChannels[c];
const currentChannel = oldChannels.find((chan) => chan.id === channel.id);
@ -131,7 +133,7 @@ function mergeChannelData(oldChannels, newChannels) {
// on the client, and decide whether theres more messages to load from server
if (key === "totalMessages") {
currentChannel.moreHistoryAvailable =
channel.totalMessages > currentChannel.messages.length;
channel.totalMessages! > currentChannel.messages.length;
continue;
}
@ -167,10 +169,12 @@ function handleQueryParams() {
if (params.has("uri")) {
// Set default connection settings from IRC protocol links
const uri = params.get("uri");
const queryParams = parseIrcUri(uri);
const queryParams = parseIrcUri(uri as string);
cleanParams();
router.push({name: "Connect", query: queryParams});
router.push({name: "Connect", query: queryParams}).catch(() => {
// Ignore errors
});
return true;
} else if (document.body.classList.contains("public") && document.location.search) {
@ -178,7 +182,9 @@ function handleQueryParams() {
const queryParams = Object.fromEntries(params.entries());
cleanParams();
router.push({name: "Connect", query: queryParams});
router.push({name: "Connect", query: queryParams}).catch(() => {
// Ignore errors
});
return true;
}

View file

@ -1,8 +1,8 @@
import Vue from "vue";
import Vuex from "vuex";
import Vuex, {GetterTree, Store} from "vuex";
import {createSettingsStore} from "./store-settings";
import storage from "./localStorage";
import {ClientChan, ClientNetwork} from "./types";
import type {ClientChan, ClientNetwork, InitClientChan} from "./types";
const appName = document.title;
@ -20,7 +20,7 @@ function detectDesktopNotificationState() {
return "blocked";
}
export type State = {
export interface State {
appLoaded: boolean;
activeChannel: {
network: ClientNetwork;
@ -54,13 +54,15 @@ export type State = {
} | null;
messageSearchInProgress: boolean;
searchEnabled: boolean;
};
}
export type SettingsState = {};
const store = new Vuex.Store<Omit<State, "settings">>({
const store = new Store<State>({
state: {
appLoaded: false,
activeChannel: null,
activeChannel: {
network: {} as ClientNetwork,
channel: {} as ClientChan,
},
currentUserVisibleError: null,
desktopNotificationState: detectDesktopNotificationState(),
isAutoCompleting: false,
@ -162,14 +164,16 @@ const store = new Vuex.Store<Omit<State, "settings">>({
state.messageSearchResults = value;
},
addMessageSearchResults(state, value) {
if (state.messageSearchResults!.results) {
// Append the search results and add networks and channels to new messages
value.results = [...state.messageSearchResults!.results, ...value.results];
} else {
value.results = value.results;
// Append the search results and add networks and channels to new messages
if (!state.messageSearchResults) {
state.messageSearchResults = {results: []};
}
state.messageSearchResults = value;
const results = [...state.messageSearchResults.results, ...value.results];
state.messageSearchResults = {
results,
};
},
},
actions: {
@ -179,11 +183,11 @@ const store = new Vuex.Store<Omit<State, "settings">>({
},
},
getters: {
findChannelOnCurrentNetwork: (state) => (name) => {
findChannelOnCurrentNetwork: (state) => (name: string) => {
name = name.toLowerCase();
return state.activeChannel?.network.channels.find((c) => c.name.toLowerCase() === name);
},
findChannelOnNetwork: (state) => (networkUuid, channelName) => {
findChannelOnNetwork: (state) => (networkUuid: string, channelName: string) => {
for (const network of state.networks) {
if (network.uuid !== networkUuid) {
continue;
@ -198,7 +202,7 @@ const store = new Vuex.Store<Omit<State, "settings">>({
return null;
},
findChannel: (state) => (id) => {
findChannel: (state) => (id: number) => {
for (const network of state.networks) {
for (const channel of network.channels) {
if (channel.id === id) {
@ -209,7 +213,7 @@ const store = new Vuex.Store<Omit<State, "settings">>({
return null;
},
findNetwork: (state) => (uuid) => {
findNetwork: (state) => (uuid: string) => {
for (const network of state.networks) {
if (network.uuid === uuid) {
return network;
@ -233,14 +237,16 @@ const store = new Vuex.Store<Omit<State, "settings">>({
return highlightCount;
},
// TODO: type
title(state, getters) {
const alertEventCount = getters.highlightCount ? `(${getters.highlightCount}) ` : "";
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
const alertEventCount = getters?.highlightCount ? `(${getters.highlightCount}) ` : "";
const channelname = state.activeChannel ? `${state.activeChannel.channel.name}` : "";
return alertEventCount + channelname + appName;
},
initChannel: () => (channel) => {
initChannel: () => (channel: InitClientChan) => {
// TODO: This should be a mutation
channel.pendingMessage = "";
channel.inputHistoryPosition = 0;
@ -250,20 +256,20 @@ const store = new Vuex.Store<Omit<State, "settings">>({
.filter((m) => m.self && m.text && m.type === "message")
.map((m) => m.text)
.reverse()
.slice(null, 99)
.slice(0, 99)
);
channel.historyLoading = false;
channel.scrolledToBottom = true;
channel.editTopic = false;
channel.moreHistoryAvailable = channel.totalMessages > channel.messages.length;
channel.moreHistoryAvailable = channel.totalMessages! > channel.messages.length;
delete channel.totalMessages;
if (channel.type === "channel") {
channel.usersOutdated = true;
}
return channel;
return channel as ClientChan;
},
},
});

15
client/js/types.d.ts vendored
View file

@ -5,6 +5,7 @@ declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
interface LoungeWindow extends Window {
g_TheLoungeRemoveLoading?: () => void;
}
@ -12,8 +13,22 @@ interface LoungeWindow extends Window {
type ClientChan = Chan & {
moreHistoryAvailable: boolean;
editTopic: boolean;
// these are added in store/initChannel
pendingMessage: string;
inputHistoryPosition: number;
inputHistory: string[];
historyLoading: boolean;
scrolledToBottom: boolean;
usersOutdated: boolean;
};
type InitClientChan = ClientChan & {
// total messages is deleted after its use when init event is sent/handled
totalMessages?: number;
};
type ClientNetwork = Network & {
isJoinChannelShown: boolean;
isCollapsed: boolean;
};

View file

@ -2,7 +2,7 @@ import constants from "./constants";
import "../css/style.css";
import Vue from "vue";
import store from "./store";
import store, {State} from "./store";
import App from "../components/App.vue";
import storage from "./localStorage";
import {router, navigate} from "./router";
@ -13,20 +13,26 @@ import "./socket-events";
import "./webpush";
import "./keybinds";
import {ClientChan} from "./types";
import {Store} from "vuex";
const favicon = document.getElementById("favicon");
const faviconNormal = favicon?.getAttribute("href") || "";
const faviconAlerted = favicon?.dataset.other || "";
type Data = {};
export type Methods = {
switchToChannel: (channel: ClientChan) => void;
closeChannel: (channel: ClientChan) => void;
};
type Computed = {};
type Props = {};
declare module "vue/types/vue" {
interface Vue {
debouncedResize: () => void;
// TODO; type as Timeout
dayChangeTimeout: any;
new Vue<Data, Methods, Computed, Props>({
switchToChannel: (channel: ClientChan) => void;
closeChannel: (channel: ClientChan) => void;
$store: Store<State>;
}
}
new Vue({
el: "#viewport",
router,
mounted() {

View file

@ -111,6 +111,7 @@
"@typescript-eslint/parser": "5.22.0",
"@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
"@vue/babel-preset-jsx": "1.2.4",
"@vue/runtime-core": "3.2.35",
"@vue/runtime-dom": "3.2.33",
"@vue/server-test-utils": "1.3.0",
"@vue/test-utils": "1.3.0",

View file

@ -4,11 +4,22 @@ import express from "express";
import log from "../log";
import webpack from "webpack";
import config from "../../webpack.config";
export default (app: express.Application) => {
log.debug("Starting server in development mode");
const webpack = require("webpack");
const webpackConfig = require("../../webpack.config.js")(undefined, { mode: "production" });
const webpackConfig = config(undefined, {mode: "production"});
if (
!webpackConfig ||
!webpackConfig.plugins?.length ||
!webpackConfig.entry ||
!webpackConfig.entry["js/bundle.js"]
) {
throw new Error("No valid production webpack config found");
}
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
webpackConfig.entry["js/bundle.js"].push(

View file

@ -15,7 +15,7 @@ export default {
props: {
text: String,
message: Object,
network: Object,
network: Object as PropType<ClientNetwork>,
},
};
</script>

View file

@ -1,189 +0,0 @@
"use strict";
const webpack = require("webpack");
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const Helper = require("./src/helper.js");
const babelConfig = require("./babel.config.cjs");
const isProduction = process.env.NODE_ENV === "production";
const config = {
mode: isProduction ? "production" : "development",
entry: {
"js/bundle.js": [path.resolve(__dirname, "client/js/vue.js")],
},
devtool: "source-map",
output: {
clean: true, // Clean the output directory before emit.
path: path.resolve(__dirname, "public"),
filename: "[name]",
publicPath: "/",
},
performance: {
hints: false,
},
module: {
rules: [
{
test: /\.vue$/,
use: {
loader: "vue-loader",
options: {
compilerOptions: {
preserveWhitespace: false,
},
},
},
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false,
},
},
{
loader: "css-loader",
options: {
url: false,
importLoaders: 1,
sourceMap: true,
},
},
{
loader: "postcss-loader",
options: {
sourceMap: true,
},
},
],
},
{
test: /\.js$/,
include: [path.resolve(__dirname, "client")],
use: {
loader: "babel-loader",
options: babelConfig,
},
},
],
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "js/bundle.vendor.js",
chunks: "all",
},
},
},
},
externals: {
json3: "JSON", // socket.io uses json3.js, but we do not target any browsers that need it
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "css/style.css",
}),
new CopyPlugin({
patterns: [
{
from: "./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*",
to: "fonts/[name][ext]",
},
{
from: "./client/js/loading-error-handlers.js",
to: "js/[name][ext]",
},
{
from: "./client/*",
to: "[name][ext]",
globOptions: {
ignore: ["**/index.html.tpl", "**/service-worker.js"],
},
},
{
from: "./client/service-worker.js",
to: "[name][ext]",
transform(content) {
return content
.toString()
.replace(
"__HASH__",
isProduction ? Helper.getVersionCacheBust() : "dev"
);
},
},
{
from: "./client/audio/*",
to: "audio/[name][ext]",
},
{
from: "./client/img/*",
to: "img/[name][ext]",
},
{
from: "./client/themes/*",
to: "themes/[name][ext]",
},
],
}),
// socket.io uses debug, we don't need it
new webpack.NormalModuleReplacementPlugin(
/debug/,
path.resolve(__dirname, "scripts/noop.js")
),
],
};
module.exports = (env, argv) => {
if (argv.mode === "development") {
config.target = "node";
config.devtool = "eval";
config.stats = "errors-only";
config.output.path = path.resolve(__dirname, "test/public");
config.entry = {
"testclient.js": [path.resolve(__dirname, "test/client/index.js")],
};
// Add the istanbul plugin to babel-loader options
for (const rule of config.module.rules) {
if (rule.use.loader === "babel-loader") {
rule.use.options.plugins = ["istanbul"];
}
}
// `optimization.splitChunks` is incompatible with a `target` of `node`. See:
// - https://github.com/zinserjan/mocha-webpack/issues/84
// - https://github.com/webpack/webpack/issues/6727#issuecomment-372589122
config.optimization.splitChunks = false;
// Disable plugins like copy files, it is not required
config.plugins = [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "css/style.css",
}),
// Client tests that require Vue may end up requireing socket.io
new webpack.NormalModuleReplacementPlugin(
/js(\/|\\)socket\.js/,
path.resolve(__dirname, "scripts/noop.js")
),
// "Fixes" Critical dependency: the request of a dependency is an expression
new webpack.ContextReplacementPlugin(/vue-server-renderer$/),
];
}
if (argv.mode === "production") {
// ...
}
return config;
};

View file

@ -1874,6 +1874,13 @@
dependencies:
"@vue/shared" "3.2.33"
"@vue/reactivity@3.2.35":
version "3.2.35"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.35.tgz#c66af289f3beda6aba63c264db9c6acd607d1c73"
integrity sha512-6j9N9R1SwHVcJas4YqAzwdRS/cgmj3Z9aUert5Mv1jk5B9H9ivN/zot/fgMUbseWXigkkmX60OsfRbz49o8kCw==
dependencies:
"@vue/shared" "3.2.35"
"@vue/runtime-core@3.2.33":
version "3.2.33"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.33.tgz#2df8907c85c37c3419fbd1bdf1a2df097fa40df2"
@ -1882,6 +1889,14 @@
"@vue/reactivity" "3.2.33"
"@vue/shared" "3.2.33"
"@vue/runtime-core@3.2.35":
version "3.2.35"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.35.tgz#a87bd5214ff31f9dc6542f5c498d4f3543c6ea8f"
integrity sha512-P8AeGPRGyIiYdOdvLc/7KR8VSdbUGG8Jxdx6Xlj5okEjyV9IYxeHRIQIoye85K0lZXBH4zuh1syD1mX+oZ0KqQ==
dependencies:
"@vue/reactivity" "3.2.35"
"@vue/shared" "3.2.35"
"@vue/runtime-dom@3.2.33":
version "3.2.33"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz#123b8969247029ea0d9c1983676d4706a962d848"
@ -1904,6 +1919,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.33.tgz#69a8c99ceb37c1b031d5cc4aec2ff1dc77e1161e"
integrity sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==
"@vue/shared@3.2.35":
version "3.2.35"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.35.tgz#fb60530fa009dc21473386a7639eed833877cb0f"
integrity sha512-/sxDqMcy0MsfQ3LQixKYDxIinDYNy1dXTsF2Am0pv0toImWabymFQ8cFmPJnPt+gh5ElKwwn7KzQcDbLHar60A==
"@vue/test-utils@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.3.0.tgz#d563decdcd9c68a7bca151d4179a2bfd6d5c3e15"