Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: drop url package #4132

Merged
merged 5 commits into from Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions .eslintrc.js
Expand Up @@ -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: [
{
Expand Down
10 changes: 7 additions & 3 deletions client-src/clients/SockJSClient.js
Expand Up @@ -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);
};
}
}
45 changes: 42 additions & 3 deletions client-src/index.js
@@ -1,5 +1,5 @@
/* global __resourceQuery, __webpack_hash__ */

/// <reference types="webpack/module" />
import webpackHotLog from "webpack/hot/log.js";
import stripAnsi from "./modules/strip-ansi/index.js";
import parseURL from "./utils/parseURL.js";
Expand All @@ -10,13 +10,34 @@ 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
// eslint-disable-next-line camelcase
currentHash: typeof __webpack_hash__ !== "undefined" ? __webpack_hash__ : "",
};

/** @type {Options} */
const options = {
hot: false,
liveReload: false,
Expand Down Expand Up @@ -101,23 +122,35 @@ const onSocketMessage = {
status.currentHash = hash;
},
logging: setAllLogLevel,
/**
* @param {boolean} value
*/
overlay(value) {
if (typeof document === "undefined") {
return;
}

options.overlay = value;
},
/**
* @param {number} value
*/
reconnect(value) {
if (parsedResourceQuery.reconnect === "false") {
return;
}

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(
Expand Down Expand Up @@ -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(
`${
Expand All @@ -157,6 +193,9 @@ const onSocketMessage = {

self.location.reload();
},
/**
* @param {string} file
*/
"static-changed": function staticChanged(file) {
log.info(
`${
Expand Down
36 changes: 31 additions & 5 deletions client-src/overlay.js
Expand Up @@ -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);
Expand All @@ -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";
Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand Down Expand Up @@ -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 = "";
Expand Down Expand Up @@ -154,6 +175,10 @@ function formatProblem(type, item) {
}

// Compilation with errors (e.g. syntax error or missing modules).
/**
* @param {string} type
* @param {Array<string | { file?: string, moduleName?: string, loc?: string, message?: string }>} messages
*/
function show(type, messages) {
ensureOverlayExists(() => {
messages.forEach((message) => {
Expand All @@ -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);
});
});
}
Expand Down
25 changes: 19 additions & 6 deletions client-src/socket.js
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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;
80 changes: 75 additions & 5 deletions 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 === "[::]";

Expand Down Expand Up @@ -80,7 +150,7 @@ function createSocketURL(parsedURL) {
socketURLPathname = parsedURL.pathname;
}

return url.format({
return format({
protocol: socketURLProtocol,
auth: socketURLAuth,
hostname: socketURLHostname,
Expand Down
3 changes: 3 additions & 0 deletions 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.
Expand Down