diff --git a/client-src/clients/SockJSClient.js b/client-src/clients/SockJSClient.js index aa0c057fec..cd9d67ad48 100644 --- a/client-src/clients/SockJSClient.js +++ b/client-src/clients/SockJSClient.js @@ -3,6 +3,9 @@ import { log } from "../utils/log.js"; /** @typedef {import("../index").EXPECTED_ANY} EXPECTED_ANY */ +/** + * @implements {CommunicationClient} + */ export default class SockJSClient { /** * @param {string} url url @@ -12,13 +15,9 @@ export default class SockJSClient { this.sock = new SockJS( url.replace(/^ws:/i, "http:").replace(/^wss:/i, "https:"), ); - this.sock.onerror = - /** - * @param {Error} error error - */ - (error) => { - log.error(error); - }; + this.sock.onerror = (error) => { + log.error(error); + }; } /** @@ -40,12 +39,8 @@ export default class SockJSClient { * @param {(...args: EXPECTED_ANY[]) => void} fn function */ onMessage(fn) { - this.sock.onmessage = - /** - * @param {Error & { data: string }} err error - */ - (err) => { - fn(err.data); - }; + this.sock.onmessage = (err) => { + fn(err.data); + }; } } diff --git a/client-src/clients/WebSocketClient.js b/client-src/clients/WebSocketClient.js index 8aa8bb85c4..de7e0ea6a1 100644 --- a/client-src/clients/WebSocketClient.js +++ b/client-src/clients/WebSocketClient.js @@ -2,6 +2,9 @@ import { log } from "../utils/log.js"; /** @typedef {import("../index").EXPECTED_ANY} EXPECTED_ANY */ +/** + * @implements {CommunicationClient} + */ export default class WebSocketClient { /** * @param {string} url url to connect diff --git a/client-src/globals.d.ts b/client-src/globals.d.ts new file mode 100644 index 0000000000..a6aeff9c99 --- /dev/null +++ b/client-src/globals.d.ts @@ -0,0 +1,24 @@ +declare interface CommunicationClient { + onOpen(fn: (...args: any[]) => void): void; + onClose(fn: (...args: any[]) => void): void; + onMessage(fn: (...args: any[]) => void): void; +} + +declare interface CommunicationClientConstructor { + new (url: string): CommunicationClient; // Defines a constructor that takes a string and returns a GreeterInstance +} + +declare const __webpack_dev_server_client__: + | CommunicationClientConstructor + | { default: CommunicationClientConstructor } + | undefined; + +declare module "ansi-html-community" { + function ansiHtmlCommunity(str: string): string; + + namespace ansiHtmlCommunity { + function setColors(colors: Record): void; + } + + export = ansiHtmlCommunity; +} diff --git a/client-src/index.js b/client-src/index.js index 930fc4ba48..8c1ac0baeb 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -1,6 +1,9 @@ /* global __resourceQuery, __webpack_hash__ */ /// + +// @ts-expect-error import hotEmitter from "webpack/hot/emitter.js"; +// @ts-expect-error import webpackHotLog from "webpack/hot/log.js"; import { createOverlay, formatProblem } from "./overlay.js"; import { defineProgressElement, isProgressSupported } from "./progress.js"; @@ -11,21 +14,31 @@ import sendMessage from "./utils/sendMessage.js"; // eslint-disable-next-line jsdoc/no-restricted-syntax /** @typedef {any} EXPECTED_ANY */ +/** + * @typedef {object} RawOverlayOptions + * @property {string=} warnings warnings + * @property {string=} errors errors + * @property {string=} runtimeErrors runtime errors + * @property {string=} trustedTypesPolicyName trusted types policy name + */ + /** * @typedef {object} OverlayOptions - * @property {(boolean | (error: Error) => boolean)=} warnings warnings - * @property {(boolean | (error: Error) => boolean)=} errors errors - * @property {(boolean | (error: Error) => boolean)=} runtimeErrors runtime errors + * @property {(boolean | ((error: Error) => boolean))=} warnings warnings + * @property {(boolean | ((error: Error) => boolean))=} errors errors + * @property {(boolean | ((error: Error) => boolean))=} runtimeErrors runtime errors * @property {string=} trustedTypesPolicyName trusted types policy name */ +/** @typedef {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} LogLevel */ + /** * @typedef {object} Options * @property {boolean} hot true when hot enabled, otherwise false * @property {boolean} liveReload true when live reload enabled, otherwise false * @property {boolean} progress true when need to show progress, otherwise false * @property {boolean | OverlayOptions} overlay overlay options - * @property {string=} logging logging level + * @property {LogLevel=} logging logging level * @property {number=} reconnect count of allowed reconnection */ @@ -37,21 +50,28 @@ import sendMessage from "./utils/sendMessage.js"; */ /** - * @param {boolean | { warnings?: boolean | string, errors?: boolean | string, runtimeErrors?: boolean | string }} overlayOptions overlay options + * @param {boolean | RawOverlayOptions | OverlayOptions} overlayOptions overlay options */ const decodeOverlayOptions = (overlayOptions) => { if (typeof overlayOptions === "object") { - for (const property of ["warnings", "errors", "runtimeErrors"]) { + for (const property_ of ["warnings", "errors", "runtimeErrors"]) { + const property = + /** @type {keyof Omit} */ + (property_); + if (typeof overlayOptions[property] === "string") { const overlayFilterFunctionString = decodeURIComponent( overlayOptions[property], ); - // eslint-disable-next-line no-new-func - overlayOptions[property] = new Function( - "message", - `var callback = ${overlayFilterFunctionString} + /** @type {OverlayOptions} */ + (overlayOptions)[property] = /** @type {(error: Error) => boolean} */ ( + // eslint-disable-next-line no-new-func + new Function( + "message", + `var callback = ${overlayFilterFunctionString} return callback(message)`, + ) ); } } @@ -73,7 +93,7 @@ const getCurrentScriptSource = () => { // `document.currentScript` is the most accurate way to find the current script, // but is not supported in all browsers. if (document.currentScript) { - return document.currentScript.getAttribute("src"); + return /** @type {string} */ (document.currentScript.getAttribute("src")); } // Fallback to getting all scripts running in the document. @@ -94,12 +114,15 @@ const getCurrentScriptSource = () => { throw new Error("[webpack-dev-server] Failed to get current script source."); }; +/** @typedef {{ hot?: string, ["live-reload"]?: string, progress?: string, reconnect?: string, logging?: LogLevel, overlay?: string, fromCurrentScript?: boolean }} AdditionalParsedURL */ +/** @typedef {Partial & AdditionalParsedURL} ParsedURL */ + /** * @param {string} resourceQuery resource query - * @returns {{ [key: string]: string | boolean }} parsed URL + * @returns {ParsedURL} parsed URL */ const parseURL = (resourceQuery) => { - /** @type {{ [key: string]: string }} */ + /** @type {ParsedURL} */ let result = {}; if (typeof resourceQuery === "string" && resourceQuery !== "") { @@ -108,7 +131,8 @@ const parseURL = (resourceQuery) => { for (let i = 0; i < searchParams.length; i++) { const pair = searchParams[i].split("="); - result[pair[0]] = decodeURIComponent(pair[1]); + /** @type {EXPECTED_ANY} */ + (result)[pair[0]] = decodeURIComponent(pair[1]); } } else { // Else, get the url from the