Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions client-src/clients/SockJSClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
};
}

/**
Expand All @@ -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);
};
}
}
3 changes: 3 additions & 0 deletions client-src/clients/WebSocketClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions client-src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -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<string, string | string[]>): void;
}

export = ansiHtmlCommunity;
}
67 changes: 49 additions & 18 deletions client-src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* global __resourceQuery, __webpack_hash__ */
/// <reference types="webpack/module" />

// @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";
Expand All @@ -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
*/

Expand All @@ -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<RawOverlayOptions, "trustedTypesPolicyName">} */
(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)`,
)
);
}
}
Expand All @@ -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.
Expand All @@ -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<URL> & 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 !== "") {
Expand All @@ -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 <script> this file was called with.
Expand Down Expand Up @@ -137,6 +161,9 @@ const parseURL = (resourceQuery) => {

const parsedResourceQuery = parseURL(__resourceQuery);

/** @typedef {{ ["Hot Module Replacement"]: boolean, ["Live Reloading"]: boolean, Progress: boolean, Overlay: boolean }} Features */

/** @type {Features} */
const enabledFeatures = {
"Hot Module Replacement": false,
"Live Reloading": false,
Expand Down Expand Up @@ -197,7 +224,7 @@ if (typeof parsedResourceQuery.reconnect !== "undefined") {
}

/**
* @param {string} level level
* @param {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} level level
*/
const setAllLogLevel = (level) => {
// This is needed because the HMR logger operate separately from dev server logger
Expand All @@ -211,6 +238,9 @@ if (options.logging) {
setAllLogLevel(options.logging);
}

/**
* @param {Features} features features
*/
const logEnabledFeatures = (features) => {
const listEnabledFeatures = Object.keys(features);
if (!features || listEnabledFeatures.length === 0) {
Expand All @@ -221,7 +251,7 @@ const logEnabledFeatures = (features) => {

// Server started: Hot Module Replacement enabled, Live Reloading enabled, Overlay disabled.
for (let i = 0; i < listEnabledFeatures.length; i++) {
const key = listEnabledFeatures[i];
const key = /** @type {keyof Features} */ (listEnabledFeatures[i]);
logString += ` ${key} ${features[key] ? "enabled" : "disabled"},`;
}
// replace last comma with a period
Expand Down Expand Up @@ -297,6 +327,7 @@ const reloadApp = ({ hot, liveReload }, currentStatus) => {
}
// allow refreshing the page only if liveReload isn't disabled
else if (liveReload && allowToLiveReload) {
/** @type {Window} */
let rootWindow = self;

// use parent window for reload (in case we're in an iframe with no valid src)
Expand Down Expand Up @@ -400,7 +431,7 @@ const onSocketMessage = {
options.progress = value;
},
/**
* @param {{ pluginName?: string, percent: number, msg: string }} data date with progress
* @param {{ pluginName?: string, percent: string, msg: string }} data date with progress
*/
"progress-update": function progressUpdate(data) {
if (options.progress) {
Expand Down Expand Up @@ -625,7 +656,7 @@ const formatURL = (objURL) => {
};

/**
* @param {URL & { fromCurrentScript?: boolean }} parsedURL parsed URL
* @param {ParsedURL} parsedURL parsed URL
* @returns {string} socket URL
*/
const createSocketURL = (parsedURL) => {
Expand Down
1 change: 1 addition & 0 deletions client-src/modules/logger/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// @ts-expect-error
export { default } from "webpack/lib/logging/runtime.js";
Loading
Loading