From 4e5d8eae654ef382697722c6406dbc96207594aa Mon Sep 17 00:00:00 2001
From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com>
Date: Wed, 22 Dec 2021 19:01:43 +0300
Subject: [PATCH] fix: droped `url` package (#4132)
---
.eslintrc.js | 15 +++
client-src/clients/SockJSClient.js | 10 +-
client-src/index.js | 45 +++++++-
client-src/overlay.js | 36 +++++-
client-src/socket.js | 25 ++++-
client-src/utils/createSocketURL.js | 80 ++++++++++++-
client-src/utils/getCurrentScriptSource.js | 3 +
client-src/utils/parseURL.js | 39 ++++---
client-src/utils/reloadApp.js | 16 ++-
client-src/utils/sendMessage.js | 2 +-
client-src/webpack.config.js | 4 +
package-lock.json | 106 ++++++------------
package.json | 2 +-
setupTest.js | 2 +-
.../multi-compiler.test.js.snap.webpack5 | 12 +-
test/e2e/api.test.js | 4 +-
16 files changed, 276 insertions(+), 125 deletions(-)
diff --git a/.eslintrc.js b/.eslintrc.js
index 654f4758f1..24127cd2b2 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -21,6 +21,21 @@ module.exports = {
"prefer-rest-params": "off",
strict: ["error", "safe"],
"global-require": "off",
+ "spaced-comment": [
+ "error",
+ "always",
+ {
+ line: {
+ exceptions: ["-", "+"],
+ markers: ["=", "!", "/"],
+ },
+ block: {
+ exceptions: ["-", "+"],
+ markers: ["=", "!"],
+ balanced: false,
+ },
+ },
+ ],
},
overrides: [
{
diff --git a/client-src/clients/SockJSClient.js b/client-src/clients/SockJSClient.js
index 0f90bb6da1..2958d44eef 100644
--- a/client-src/clients/SockJSClient.js
+++ b/client-src/clients/SockJSClient.js
@@ -38,8 +38,12 @@ export default class SockJSClient {
* @param {(...args: any[]) => void} f
*/
onMessage(f) {
- this.sock.onmessage = (e) => {
- f(e.data);
- };
+ this.sock.onmessage =
+ /**
+ * @param {Error & { data: string }} e
+ */
+ (e) => {
+ f(e.data);
+ };
}
}
diff --git a/client-src/index.js b/client-src/index.js
index d8f832c987..dc85fdcff0 100644
--- a/client-src/index.js
+++ b/client-src/index.js
@@ -1,5 +1,5 @@
/* global __resourceQuery, __webpack_hash__ */
-
+///
import webpackHotLog from "webpack/hot/log.js";
import stripAnsi from "./modules/strip-ansi/index.js";
import parseURL from "./utils/parseURL.js";
@@ -10,6 +10,26 @@ import sendMessage from "./utils/sendMessage.js";
import reloadApp from "./utils/reloadApp.js";
import createSocketURL from "./utils/createSocketURL.js";
+/**
+ * @typedef {Object} Options
+ * @property {boolean} hot
+ * @property {boolean} liveReload
+ * @property {boolean} progress
+ * @property {boolean | { warnings?: boolean, errors?: boolean }} overlay
+ * @property {string} [logging]
+ * @property {number} [reconnect]
+ */
+
+/**
+ * @typedef {Object} Status
+ * @property {boolean} isUnloading
+ * @property {string} currentHash
+ * @property {string} [previousHash]
+ */
+
+/**
+ * @type {Status}
+ */
const status = {
isUnloading: false,
// TODO Workaround for webpack v4, `__webpack_hash__` is not replaced without HotModuleReplacement
@@ -17,6 +37,7 @@ const status = {
currentHash: typeof __webpack_hash__ !== "undefined" ? __webpack_hash__ : "",
};
+/** @type {Options} */
const options = {
hot: false,
liveReload: false,
@@ -101,6 +122,9 @@ const onSocketMessage = {
status.currentHash = hash;
},
logging: setAllLogLevel,
+ /**
+ * @param {boolean} value
+ */
overlay(value) {
if (typeof document === "undefined") {
return;
@@ -108,6 +132,9 @@ const onSocketMessage = {
options.overlay = value;
},
+ /**
+ * @param {number} value
+ */
reconnect(value) {
if (parsedResourceQuery.reconnect === "false") {
return;
@@ -115,9 +142,15 @@ const onSocketMessage = {
options.reconnect = value;
},
- progress(progress) {
- options.progress = progress;
+ /**
+ * @param {boolean} value
+ */
+ progress(value) {
+ options.progress = value;
},
+ /**
+ * @param {{ pluginName?: string, percent: number, msg: string }} data
+ */
"progress-update": function progressUpdate(data) {
if (options.progress) {
log.info(
@@ -148,6 +181,9 @@ const onSocketMessage = {
reloadApp(options, status);
},
// TODO: remove in v5 in favor of 'static-changed'
+ /**
+ * @param {string} file
+ */
"content-changed": function contentChanged(file) {
log.info(
`${
@@ -157,6 +193,9 @@ const onSocketMessage = {
self.location.reload();
},
+ /**
+ * @param {string} file
+ */
"static-changed": function staticChanged(file) {
log.info(
`${
diff --git a/client-src/overlay.js b/client-src/overlay.js
index 819597ed15..5c074702ff 100644
--- a/client-src/overlay.js
+++ b/client-src/overlay.js
@@ -17,8 +17,11 @@ const colors = {
darkgrey: "6D7891",
};
+/** @type {HTMLIFrameElement | null | undefined} */
let iframeContainerElement;
+/** @type {HTMLDivElement | null | undefined} */
let containerElement;
+/** @type {Array<(element: HTMLDivElement) => void>} */
let onLoadQueue = [];
ansiHTML.setColors(colors);
@@ -38,7 +41,11 @@ function createContainer() {
iframeContainerElement.style.zIndex = 9999999999;
iframeContainerElement.onload = () => {
containerElement =
- iframeContainerElement.contentDocument.createElement("div");
+ /** @type {Document} */
+ (
+ /** @type {HTMLIFrameElement} */
+ (iframeContainerElement).contentDocument
+ ).createElement("div");
containerElement.id = "webpack-dev-server-client-overlay-div";
containerElement.style.position = "fixed";
containerElement.style.boxSizing = "border-box";
@@ -71,6 +78,7 @@ function createContainer() {
closeButtonElement.style.color = "white";
closeButtonElement.style.cursor = "pointer";
closeButtonElement.style.cssFloat = "right";
+ // @ts-ignore
closeButtonElement.style.styleFloat = "right";
closeButtonElement.addEventListener("click", () => {
hide();
@@ -81,19 +89,27 @@ function createContainer() {
containerElement.appendChild(document.createElement("br"));
containerElement.appendChild(document.createElement("br"));
- iframeContainerElement.contentDocument.body.appendChild(containerElement);
+ /** @type {Document} */
+ (
+ /** @type {HTMLIFrameElement} */
+ (iframeContainerElement).contentDocument
+ ).body.appendChild(containerElement);
onLoadQueue.forEach((onLoad) => {
- onLoad(containerElement);
+ onLoad(/** @type {HTMLDivElement} */ (containerElement));
});
onLoadQueue = [];
- iframeContainerElement.onload = null;
+ /** @type {HTMLIFrameElement} */
+ (iframeContainerElement).onload = null;
};
document.body.appendChild(iframeContainerElement);
}
+/**
+ * @param {(element: HTMLDivElement) => void} callback
+ */
function ensureOverlayExists(callback) {
if (containerElement) {
// Everything is ready, call the callback right away.
@@ -124,6 +140,11 @@ function hide() {
containerElement = null;
}
+/**
+ * @param {string} type
+ * @param {string | { file?: string, moduleName?: string, loc?: string, message?: string }} item
+ * @returns {{ header: string, body: string }}
+ */
function formatProblem(type, item) {
let header = type === "warning" ? "WARNING" : "ERROR";
let body = "";
@@ -154,6 +175,10 @@ function formatProblem(type, item) {
}
// Compilation with errors (e.g. syntax error or missing modules).
+/**
+ * @param {string} type
+ * @param {Array} messages
+ */
function show(type, messages) {
ensureOverlayExists(() => {
messages.forEach((message) => {
@@ -177,7 +202,8 @@ function show(type, messages) {
entryElement.appendChild(document.createElement("br"));
entryElement.appendChild(document.createElement("br"));
- containerElement.appendChild(entryElement);
+ /** @type {HTMLDivElement} */
+ (containerElement).appendChild(entryElement);
});
});
}
diff --git a/client-src/socket.js b/client-src/socket.js
index dc76cd295b..4b913e4c69 100644
--- a/client-src/socket.js
+++ b/client-src/socket.js
@@ -18,12 +18,20 @@ let retries = 0;
let maxRetries = 10;
let client = null;
+/**
+ * @param {string} url
+ * @param {{ [handler: string]: (data?: any, params?: any) => any }} handlers
+ * @param {number} [reconnect]
+ */
const socket = function initSocket(url, handlers, reconnect) {
client = new Client(url);
client.onOpen(() => {
retries = 0;
- maxRetries = reconnect;
+
+ if (typeof reconnect !== "undefined") {
+ maxRetries = reconnect;
+ }
});
client.onClose(() => {
@@ -51,13 +59,18 @@ const socket = function initSocket(url, handlers, reconnect) {
}
});
- client.onMessage((data) => {
- const message = JSON.parse(data);
+ client.onMessage(
+ /**
+ * @param {any} data
+ */
+ (data) => {
+ const message = JSON.parse(data);
- if (handlers[message.type]) {
- handlers[message.type](message.data, message.params);
+ if (handlers[message.type]) {
+ handlers[message.type](message.data, message.params);
+ }
}
- });
+ );
};
export default socket;
diff --git a/client-src/utils/createSocketURL.js b/client-src/utils/createSocketURL.js
index 168c3b6c9e..5d8a28ddbd 100644
--- a/client-src/utils/createSocketURL.js
+++ b/client-src/utils/createSocketURL.js
@@ -1,12 +1,82 @@
-import url from "url";
+/**
+ * @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL
+ * @returns {string}
+ */
+function format(objURL) {
+ let protocol = objURL.protocol || "";
+
+ if (protocol && protocol.substr(-1) !== ":") {
+ protocol += ":";
+ }
+
+ let auth = objURL.auth || "";
+
+ if (auth) {
+ auth = encodeURIComponent(auth);
+ auth = auth.replace(/%3A/i, ":");
+ auth += "@";
+ }
+
+ let host = "";
+
+ if (objURL.hostname) {
+ host =
+ auth +
+ (objURL.hostname.indexOf(":") === -1
+ ? objURL.hostname
+ : `[${objURL.hostname}]`);
+
+ if (objURL.port) {
+ host += `:${objURL.port}`;
+ }
+ }
+
+ let pathname = objURL.pathname || "";
+
+ if (objURL.slashes) {
+ host = `//${host || ""}`;
+
+ if (pathname && pathname.charAt(0) !== "/") {
+ pathname = `/${pathname}`;
+ }
+ } else if (!host) {
+ host = "";
+ }
+
+ let search = objURL.search || "";
+
+ if (search && search.charAt(0) !== "?") {
+ search = `?${search}`;
+ }
+
+ let hash = objURL.hash || "";
+
+ if (hash && hash.charAt(0) !== "#") {
+ hash = `#${hash}`;
+ }
+
+ pathname = pathname.replace(
+ /[?#]/g,
+ /**
+ * @param {string} match
+ * @returns {string}
+ */
+ (match) => encodeURIComponent(match)
+ );
+ search = search.replace("#", "%23");
+
+ return `${protocol}${host}${pathname}${search}${hash}`;
+}
-// We handle legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers
-// Please look at https://nodejs.org/api/url.html#url_url_strings_and_url_objects
+/**
+ * @param {URL & { fromCurrentScript?: boolean }} parsedURL
+ * @returns {string}
+ */
function createSocketURL(parsedURL) {
let { hostname } = parsedURL;
// Node.js module parses it as `::`
- // `new URL(urlString, [baseURLstring])` parses it as '[::]'
+ // `new URL(urlString, [baseURLString])` parses it as '[::]'
const isInAddrAny =
hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]";
@@ -80,7 +150,7 @@ function createSocketURL(parsedURL) {
socketURLPathname = parsedURL.pathname;
}
- return url.format({
+ return format({
protocol: socketURLProtocol,
auth: socketURLAuth,
hostname: socketURLHostname,
diff --git a/client-src/utils/getCurrentScriptSource.js b/client-src/utils/getCurrentScriptSource.js
index 01b05cb66d..2c16610389 100644
--- a/client-src/utils/getCurrentScriptSource.js
+++ b/client-src/utils/getCurrentScriptSource.js
@@ -1,3 +1,6 @@
+/**
+ * @returns {string}
+ */
function getCurrentScriptSource() {
// `document.currentScript` is the most accurate way to find the current script,
// but is not supported in all browsers.
diff --git a/client-src/utils/parseURL.js b/client-src/utils/parseURL.js
index 9be4077d25..3a4f2f3fda 100644
--- a/client-src/utils/parseURL.js
+++ b/client-src/utils/parseURL.js
@@ -1,7 +1,11 @@
-import url from "url";
import getCurrentScriptSource from "./getCurrentScriptSource.js";
+/**
+ * @param {string} resourceQuery
+ * @returns {{ [key: string]: string | boolean }}
+ */
function parseURL(resourceQuery) {
+ /** @type {{ [key: string]: string }} */
let options = {};
if (typeof resourceQuery === "string" && resourceQuery !== "") {
@@ -16,25 +20,20 @@ function parseURL(resourceQuery) {
// Else, get the url from the