fix yarn build and start

This commit is contained in:
Max Leiter 2022-05-04 21:10:12 -07:00
parent a607924a2b
commit 1d2dc40985
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
20 changed files with 173 additions and 90 deletions

View file

@ -1,15 +1,32 @@
module.exports = {
root: true,
overrides: [
{
files: ["**/*.ts"],
parser: "@typescript-eslint/parser",
parserOptions: {
tsconfigRootDir: __dirname,
parser: "@typescript-eslint/parser",
project: ["./tsconfig.json", "./client/tsconfig.json", "./src/tsconfig.json"],
},
plugins: ["@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
},
],
parserOptions: {
ecmaVersion: 2022,
parser: "@typescript-eslint/parser",
sourceType: "module",
// sourceType: "module",
// project: ["./eslint.tsconfig.json"],
// extraFileExtensions: [".vue", ".cjs"],
},
// TODO: this should just be for client?
parser: "vue-eslint-parser",
plugins: ["vue", "@typescript-eslint"],
plugins: ["vue"],
env: {
es6: true,
browser: true,

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ package-lock.json
coverage/
public/
client/dist
src/dist

View file

@ -1,8 +1,7 @@
{
"extends": "../tsconfig.base.json",
"include": ["./**/*", "../src/models/*"],
"include": ["./**/*"],
"files": [
"../src/types/models/channel.d.ts",
"js/helpers/simplemap.json",
"js/helpers/fullnamemap.json",
"../package.json",

View file

@ -1,4 +0,0 @@
{
"extends": "./tsconfig.base.json",
"include": ["src/**/*.ts", "client/**/*.ts", "test/**/*.ts", "scripts/**/*.{ts,js}"]
}

View file

@ -13,15 +13,16 @@
"homepage": "https://thelounge.chat/",
"scripts": {
"build:client": "webpack",
"build:server": "tsc -p src/tsconfig.json src/index.ts",
"build:server": "tsc -p src/tsconfig.json",
"build": "run-p --aggregate-output build:client build:server",
"coverage": "run-s test:* && nyc --nycrc-path=test/.nycrc-report.json report",
"dev": "ts-node --project src/tsconfig.json src/index.ts start --dev",
"dev": "NODE_ENV=development ts-node --project src/tsconfig.json src/index.ts start --dev",
"format:prettier": "prettier --write \"**/*.*\"",
"lint:check-eslint": "eslint-config-prettier .eslintrc.cjs",
"lint:eslint": "eslint . --ext .js,.vue --report-unused-disable-directives --color",
"lint:prettier": "prettier --list-different \"**/*.*\"",
"lint:stylelint": "stylelint --color \"client/**/*.css\"",
"start": "ts-node src/index start",
"start": "node src/dist/src/index start",
"test": "run-p --aggregate-output --continue-on-error lint:* test:*",
"test:mocha": "webpack --mode=development && nyc --nycrc-path=test/.nycrc-mocha.json mocha --colors --config=test/.mocharc.yml",
"watch": "webpack --watch"
@ -84,6 +85,7 @@
"@textcomplete/textarea": "0.1.10",
"@types/bcryptjs": "2.4.2",
"@types/chai": "4.3.1",
"@types/cheerio": "0.22.31",
"@types/content-disposition": "0.5.4",
"@types/express": "4.17.13",
"@types/is-utf8": "0.2.0",
@ -102,6 +104,7 @@
"@types/uuid": "8.3.4",
"@types/web-push": "3.3.2",
"@types/webpack-env": "1.16.4",
"@types/webpack-hot-middleware": "2.25.6",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.22.0",
"@typescript-eslint/parser": "5.22.0",

View file

@ -8,7 +8,6 @@ import Helper from "../helper";
import Config from "../config";
import path from "path";
import {spawn} from "child_process";
let home: string;
class Utils {
@ -28,46 +27,58 @@ class Utils {
return home;
}
const distConfig = path.resolve(path.join(__dirname, "..", "..", ".thelounge_home"));
const distConfig = Utils.getFileFromRelativeToRoot(".thelounge_home");
home = fs.readFileSync(distConfig, "utf-8").trim();
return home;
}
// TODO: handle this more elegantly?
static getFileFromRelativeToRoot(...fileName: string[]) {
if (process.env.NODE_ENV === "test" || process.env.NODE_ENV === "development") {
return path.resolve(path.join(__dirname, "..", "..", ...fileName));
} else {
return path.resolve(path.join(__dirname, "..", "..", "..", "..", ...fileName));
}
}
// Parses CLI options such as `-c public=true`, `-c debug.raw=true`, etc.
static parseConfigOptions(val, memo) {
static parseConfigOptions(val: string, memo) {
// Invalid option that is not of format `key=value`, do nothing
if (!val.includes("=")) {
return memo;
}
const parseValue = (value) => {
if (value === "true") {
return true;
} else if (value === "false") {
return false;
} else if (value === "undefined") {
return undefined;
} else if (value === "null") {
return null;
} else if (/^-?[0-9]+$/.test(value)) {
// Numbers like port
value = parseInt(value, 10);
} else if (/^\[.*\]$/.test(value)) {
// Arrays
// Supporting arrays `[a,b]` and `[a, b]`
const array = value.slice(1, -1).split(/,\s*/);
const parseValue = (value: string) => {
switch (value) {
case "true":
return true;
case "false":
return false;
case "undefined":
return undefined;
case "null":
return null;
default:
if (/^-?[0-9]+$/.test(value)) {
// Numbers like port
return parseInt(value, 10);
} else if (/^\[.*\]$/.test(value)) {
// Arrays
// Supporting arrays `[a,b]` and `[a, b]`
const array = value.slice(1, -1).split(/,\s*/);
// If [] is given, it will be parsed as `[ "" ]`, so treat this as empty
if (array.length === 1 && array[0] === "") {
return [];
}
// If [] is given, it will be parsed as `[ "" ]`, so treat this as empty
if (array.length === 1 && array[0] === "") {
return [];
}
return array.map(parseValue); // Re-parses all values of the array
return array.map(parseValue); // Re-parses all values of the array
}
return value;
}
return value;
};
// First time the option is parsed, memo is not set

View file

@ -5,8 +5,10 @@ import fs, {Stats} from "fs";
import os from "os";
import _ from "lodash";
import colors from "chalk";
import log from "./log";
import Helper from "./helper";
import Utils from "./command-line/utils";
// TODO: Type this
export type WebIRC = {
@ -224,9 +226,7 @@ class Config {
}
}
const manifestPath = path.resolve(
path.join(__dirname, "..", "public", "thelounge.webmanifest")
);
const manifestPath = Utils.getFileFromRelativeToRoot("public", "thelounge.webmanifest");
// Check if manifest exists, if not, the app most likely was not built
if (!fs.existsSync(manifestPath)) {

View file

@ -78,7 +78,7 @@ class Msg {
raw_modes!: any;
when!: Date;
whois!: any;
users!: UserInMessage[];
users!: UserInMessage[] | string[];
statusmsgGroup!: string;
params!: string[];
@ -104,7 +104,7 @@ class Msg {
self: false,
});
if (this.time?.getTime() > 0) {
if (this.time) {
this.time = new Date(this.time);
} else {
this.time = new Date();

View file

@ -282,7 +282,9 @@ class Network {
// Exponential backoff maxes out at 300 seconds after 9 reconnects,
// it will keep trying for well over an hour (plus the timeouts)
auto_reconnect_max_retries: 30,
});
// TODO: this type should be set after setIrcFrameworkOptions
}) as NetworkWithIrcFramework["irc"];
this.setIrcFrameworkOptions(client);
@ -473,7 +475,7 @@ class Network {
}
// TODO: setNick is called in validate() before irc exists. Is that a problem?
if (this.irc) {
if (this.irc?.options) {
this.irc.options.nick = nick;
}
}

View file

@ -1,8 +1,11 @@
"use strict";
import log from "../log";
import webpackDevMiddleware from "webpack-dev-middleware";
import webpackHotMiddleware from "webpack-hot-middleware";
import express from "express";
export default (app) => {
import log from "../log";
export default (app: express.Application) => {
log.debug("Starting server in development mode");
const webpack = require("webpack");
@ -23,8 +26,9 @@ export default (app) => {
publicPath: webpackConfig.output?.publicPath,
})
).use(
webpackDevMiddleware(compiler, {
publicPath: "/storage/__webpack_hmr",
// TODO: Fix compiler type
webpackHotMiddleware(compiler as any, {
path: "/storage/__webpack_hmr",
})
);
};

View file

@ -11,11 +11,10 @@ import {findLinksWithSchema} from "../../../client/js/helpers/ircmessageparser/f
import storage from "../storage";
import Client, {IrcEventHandler} from "../../client";
import Chan from "../../models/chan";
import Msg from "../../models/msg";
import {Cheerio} from "cheerio";
import Msg, {MessagePreview} from "../../models/msg";
type FetchRequest = {
data: Record<string, any>;
data: Buffer;
type: string;
size: number;
};
@ -23,7 +22,7 @@ const currentFetchPromises = new Map<string, Promise<FetchRequest>>();
const imageTypeRegex = /^image\/.+/;
const mediaTypeRegex = /^(audio|video)\/.+/;
type Preview = {
type LinkPreview = {
type: string;
head: string;
body: string;
@ -40,11 +39,11 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
return;
}
msg.previews = findLinksWithSchema(cleanText).reduce((cleanLinks: Preview[], link) => {
msg.previews = findLinksWithSchema(cleanText).reduce((cleanLinks: LinkPreview[], link) => {
const url = normalizeURL(link.link);
// If the URL is invalid and cannot be normalized, don't fetch it
if (url === null) {
if (!url) {
return cleanLinks;
}
@ -58,7 +57,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
return cleanLinks;
}
const preview: Preview = {
const preview: LinkPreview = {
type: "loading",
head: "",
body: "",
@ -74,7 +73,7 @@ export default function (client: Client, chan: Chan, msg: Msg, cleanText: string
fetch(url, {
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
language: client.config.browser?.language,
language: client.config.browser?.language || "",
})
.then((res) => {
parse(msg, chan, preview, res, client);
@ -133,7 +132,7 @@ function parseHtml(preview, res, client: Client) {
// Verify that thumbnail pic exists and is under allowed size
if (thumb.length) {
fetch(thumb, {language: client.config.browser?.language})
fetch(thumb, {language: client.config.browser?.language || ""})
.then((resThumb) => {
if (
resThumb !== null &&
@ -153,7 +152,8 @@ function parseHtml(preview, res, client: Client) {
});
}
function parseHtmlMedia($: Cheerio, preview, client: Client) {
// TODO: type $
function parseHtmlMedia($: any, preview, client: Client) {
return new Promise((resolve, reject) => {
if (Config.values.disableMediaPreview) {
reject();
@ -180,7 +180,7 @@ function parseHtmlMedia($: Cheerio, preview, client: Client) {
return;
}
$(`meta[property="og:${type}:type"]`).each(function (i) {
$(`meta[property="og:${type}:type"]`).each(function (this: cheerio.Element, i: number) {
const mimeType = $(this).attr("content");
if (!mimeType) {
@ -210,7 +210,7 @@ function parseHtmlMedia($: Cheerio, preview, client: Client) {
type === "video"
? "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5"
: "audio/webm, audio/ogg, audio/wav, audio/*;q=0.9, application/ogg;q=0.7, video/*;q=0.6; */*;q=0.5",
language: client.config.browser?.language,
language: client.config.browser?.language || "",
})
.then((resMedia) => {
if (resMedia === null || !mediaTypeRegex.test(resMedia.type)) {
@ -361,7 +361,7 @@ function handlePreview(client, chan, msg, preview, res) {
});
}
function emitPreview(client, chan, msg, preview) {
function emitPreview(client: Client, chan: Chan, msg: Msg, preview: LinkPreview) {
// If there is no title but there is preview or description, set title
// otherwise bail out and show no preview
if (!preview.head.length && preview.type === "link") {
@ -389,7 +389,7 @@ function removePreview(msg, preview) {
}
}
function getRequestHeaders(headers) {
function getRequestHeaders(headers: Record<string, string>) {
const formattedHeaders = {
// Certain websites like Amazon only add <meta> tags to known bots,
// lets pretend to be them to get the metadata
@ -407,7 +407,7 @@ function getRequestHeaders(headers) {
return formattedHeaders;
}
function fetch(uri, headers) {
function fetch(uri: string, headers: Record<string, string>) {
// Stringify the object otherwise the objects won't compute to the same value
const cacheKey = JSON.stringify([uri, headers]);
let promise = currentFetchPromises.get(cacheKey);
@ -476,13 +476,13 @@ function fetch(uri, headers) {
})
.on("end", () => gotStream.destroy())
.on("close", () => {
let type = "";
let type: string = "";
// If we downloaded more data then specified in Content-Length, use real data size
const size = contentLength > buffer.length ? contentLength : buffer.length;
if (contentType) {
type = contentType.split(/ *; */).shift();
type = contentType.split(/ *; */).shift() || "";
}
resolve({data: buffer, type, size});

View file

@ -145,10 +145,11 @@ export default <IrcEventHandler>function (irc, network) {
msg.statusmsgGroup = data.group;
}
let match;
let match: RegExpExecArray | null;
while ((match = nickRegExp.exec(data.message))) {
if (chan.findUser(match[1])) {
// @ts-ignore TODO: fix this
msg.users.push(match[1]);
}
}

View file

@ -75,7 +75,7 @@ export default <IrcEventHandler>function (irc, network) {
self: data.nick === irc.user.nick,
});
const users: User[] = [];
const users: string[] = [];
for (const param of data.raw_params) {
if (targetChan.findUser(param)) {

View file

@ -5,6 +5,7 @@ import path from "path";
import _ from "lodash";
import Config from "../../config";
import Utils from "../../command-line/utils";
type Module = {
type: string;
@ -33,9 +34,7 @@ export default {
};
function loadLocalThemes() {
const builtInThemes = fs.readdirSync(
path.join(__dirname, "..", "..", "..", "public", "themes")
);
const builtInThemes = fs.readdirSync(Utils.getFileFromRelativeToRoot("public", "themes"));
builtInThemes
.filter((theme) => theme.endsWith(".css"))

View file

@ -28,6 +28,7 @@ themes.loadLocalThemes();
import packages from "./plugins/packages/index";
import {NetworkWithIrcFramework} from "./models/network";
import {ChanType} from "./models/chan";
import Utils from "./command-line/utils";
type ServerOptions = {
dev: boolean;
@ -96,7 +97,7 @@ export default async function (
.get("/service-worker.js", forceNoCacheRequest)
.get("/js/bundle.js.map", forceNoCacheRequest)
.get("/css/style.css.map", forceNoCacheRequest)
.use(express.static(path.join(__dirname, "..", "public"), staticOptions))
.use(express.static(Utils.getFileFromRelativeToRoot("public"), staticOptions))
.use("/storage/", express.static(Config.getStoragePath(), staticOptions));
if (Config.values.fileUpload.enable) {
@ -382,7 +383,7 @@ function indexRequest(req, res) {
res.setHeader("Content-Type", "text/html");
return fs.readFile(
path.join(__dirname, "..", "client", "index.html.tpl"),
Utils.getFileFromRelativeToRoot("client/index.html.tpl"),
"utf-8",
(err, file) => {
if (err) {

View file

@ -2,6 +2,9 @@
"extends": "../tsconfig.base.json",
"files": [
"../package.json",
"../webpack.config.ts",
"../babel.config.cjs",
"../defaults/config.js",
"../client/js/constants.ts",
"../client/js/helpers/ircmessageparser/findLinks.ts",
"../client/js/helpers/ircmessageparser/cleanIrcMessage.ts"
@ -11,13 +14,15 @@
},
// TODO: these should be imported from the base config,
// but ts-node doesn't seem to care.
// "include": ["**/*"],
"include": ["**/*"],
// "typeRoots": ["./types/"],
"compilerOptions": {
"types": ["node"],
"baseUrl": ".",
"noImplicitAny": false
"noImplicitAny": false,
"outDir": "./dist",
"noEmit": false
}
// "references": [{"path": "../"}]
}

View file

@ -7,18 +7,16 @@
"rootDir": ".",
"baseUrl": ".",
"allowJs": true,
"noImplicitAny": false,
"checkJs": true,
"resolveJsonModule": true,
"composite": true,
"strict": true /* Enable all strict type-checking options. */,
"typeRoots": ["./src/types/"],
// "typeRoots": ["./src/types/"],
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"files": ["./package.json"],
"include": ["./src/types/*"]
"files": ["./package.json"]
}

View file

@ -1,5 +1,9 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
// TODO: enable
"noImplicitAny": false
},
"files": ["./webpack.config.ts", "./babel.config.cjs", "./src/helper.ts"],
"references": [
{

View file

@ -104,22 +104,25 @@ const config: webpack.Configuration = {
new CopyPlugin({
patterns: [
{
from: "./node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff*",
from: path.resolve(
__dirname,
"node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff"
),
to: "fonts/[name][ext]",
},
{
from: "./client/js/loading-error-handlers.js",
from: path.resolve(__dirname, "./client/js/loading-error-handlers.js"),
to: "js/[name][ext]",
},
{
from: "./client/*",
from: path.resolve(__dirname, "./client/*"),
to: "[name][ext]",
globOptions: {
ignore: ["**/index.html.tpl", "**/service-worker.js"],
},
},
{
from: "./client/service-worker.js",
from: path.resolve(__dirname, "./client/service-worker.js"),
to: "[name][ext]",
transform(content) {
return content
@ -131,15 +134,15 @@ const config: webpack.Configuration = {
},
},
{
from: "./client/audio/*",
from: path.resolve(__dirname, "./client/audio/*"),
to: "audio/[name][ext]",
},
{
from: "./client/img/*",
from: path.resolve(__dirname, "./client/img/*"),
to: "img/[name][ext]",
},
{
from: "./client/themes/*",
from: path.resolve(__dirname, "./client/themes/*"),
to: "themes/[name][ext]",
},
],

View file

@ -1414,7 +1414,7 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04"
integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==
"@types/cheerio@^0.22.10":
"@types/cheerio@0.22.31", "@types/cheerio@^0.22.10":
version "0.22.31"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.31.tgz#b8538100653d6bb1b08a1e46dec75b4f2a5d5eb6"
integrity sha512-Kt7Cdjjdi2XWSfrZ53v4Of0wG3ZcmaegFXjMmz9tfNrZSkzzo36G0AL1YqSdcIA78Etjt6E609pt5h1xnQkPUw==
@ -1448,7 +1448,7 @@
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080"
integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
"@types/eslint-scope@^3.7.0":
"@types/eslint-scope@^3.7.0", "@types/eslint-scope@^3.7.3":
version "3.7.3"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
@ -1464,7 +1464,7 @@
"@types/estree" "*"
"@types/json-schema" "*"
"@types/estree@*":
"@types/estree@*", "@types/estree@^0.0.51":
version "0.0.51"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
@ -1670,6 +1670,15 @@
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.4.tgz#1f4969042bf76d7ef7b5914f59b3b60073f4e1f4"
integrity sha512-llS8qveOUX3wxHnSykP5hlYFFuMfJ9p5JvIyCiBgp7WTfl6K5ZcyHj8r8JsN/J6QODkAsRRCLIcTuOCu8etkUw==
"@types/webpack-hot-middleware@2.25.6":
version "2.25.6"
resolved "https://registry.yarnpkg.com/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.6.tgz#4336abd668dc73284a777907cfd00147e794354a"
integrity sha512-1Q9ClNvZR30HIsEAHYQL3bXJK1K7IsrqjGMTfIureFjphsGOZ3TkbeoCupbCmi26pSLjVTPHp+pFrJNpOkBSVg==
dependencies:
"@types/connect" "*"
tapable "^2.2.0"
webpack "^5"
"@types/ws@8.5.3":
version "8.5.3"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
@ -3604,7 +3613,7 @@ engine.io@~6.1.0:
engine.io-parser "~5.0.3"
ws "~8.2.3"
enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0, enhanced-resolve@^5.8.3:
enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0, enhanced-resolve@^5.8.3, enhanced-resolve@^5.9.2:
version "5.9.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
@ -9163,6 +9172,36 @@ webpack@5.68.0:
watchpack "^2.3.1"
webpack-sources "^3.2.3"
webpack@^5:
version "5.72.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.0.tgz#f8bc40d9c6bb489a4b7a8a685101d6022b8b6e28"
integrity sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^0.0.51"
"@webassemblyjs/ast" "1.11.1"
"@webassemblyjs/wasm-edit" "1.11.1"
"@webassemblyjs/wasm-parser" "1.11.1"
acorn "^8.4.1"
acorn-import-assertions "^1.7.6"
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.9.2"
es-module-lexer "^0.9.0"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.9"
json-parse-better-errors "^1.0.2"
loader-runner "^4.2.0"
mime-types "^2.1.27"
neo-async "^2.6.2"
schema-utils "^3.1.0"
tapable "^2.1.1"
terser-webpack-plugin "^5.1.3"
watchpack "^2.3.1"
webpack-sources "^3.2.3"
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"