diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 169f4a5..3a30eb9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,9 @@ jobs: uses: actions/checkout@v4 - name: Install Pnpm - run: corepack enable + run: | + npm install -g corepack@latest + corepack enable - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..bf03ed3 --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "linter": { + "ignore": [ + "client-src/**/*.js" + ] + } +} \ No newline at end of file diff --git a/client-src/index.js b/client-src/index.js index ba9dcb3..c415d7b 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -1,91 +1,39 @@ // @ts-nocheck -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && - (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), - keys.push.apply(keys, symbols); - } - return keys; -} -function _objectSpread(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 - ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) - : Object.getOwnPropertyDescriptors - ? Object.defineProperties( - target, - Object.getOwnPropertyDescriptors(source) - ) - : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty( - target, - key, - Object.getOwnPropertyDescriptor(source, key) - ); - }); - } - return target; -} -function _defineProperty(obj, key, value) { - key = _toPropertyKey(key); - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - return obj; -} -function _toPropertyKey(arg) { - var key = _toPrimitive(arg, "string"); - return typeof key === "symbol" ? key : String(key); -} -function _toPrimitive(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); -} + /* global __resourceQuery, __webpack_hash__ */ +/* Rspack dev server runtime client */ /// import webpackHotLog from "@rspack/core/hot/log.js"; -import stripAnsi from "webpack-dev-server/client/utils/stripAnsi.js"; -import parseURL from "webpack-dev-server/client/utils/parseURL.js"; -import socket from "webpack-dev-server/client/socket.js"; import { + createOverlay, formatProblem, - createOverlay } from "webpack-dev-server/client/overlay.js"; +import socket from "webpack-dev-server/client/socket.js"; +import createSocketURL from "webpack-dev-server/client/utils/createSocketURL.js"; import { log, logEnabledFeatures, - setLogLevel + setLogLevel, } from "webpack-dev-server/client/utils/log.js"; +import parseURL from "webpack-dev-server/client/utils/parseURL.js"; import sendMessage from "webpack-dev-server/client/utils/sendMessage.js"; +import stripAnsi from "webpack-dev-server/client/utils/stripAnsi.js"; import reloadApp from "./utils/reloadApp.js"; -import createSocketURL from "webpack-dev-server/client/utils/createSocketURL.js"; + +/** + * @typedef {Object} OverlayOptions + * @property {boolean | (error: Error) => boolean} [warnings] + * @property {boolean | (error: Error) => boolean} [errors] + * @property {boolean | (error: Error) => boolean} [runtimeErrors] + * @property {string} [trustedTypesPolicyName] + */ /** * @typedef {Object} Options * @property {boolean} hot * @property {boolean} liveReload * @property {boolean} progress - * @property {boolean | { warnings?: boolean, errors?: boolean, runtimeErrors?: boolean, trustedTypesPolicyName?: string }} overlay + * @property {boolean | OverlayOptions} overlay * @property {string} [logging] * @property {number} [reconnect] */ @@ -98,59 +46,64 @@ import createSocketURL from "webpack-dev-server/client/utils/createSocketURL.js" */ /** - * @type {Status} + * @param {boolean | { warnings?: boolean | string; errors?: boolean | string; runtimeErrors?: boolean | string; }} overlayOptions */ -var 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__ : "" -}; - -var decodeOverlayOptions = function decodeOverlayOptions(overlayOptions) { +const decodeOverlayOptions = (overlayOptions) => { if (typeof overlayOptions === "object") { - ["warnings", "errors", "runtimeErrors"].forEach(function (property) { + ["warnings", "errors", "runtimeErrors"].forEach((property) => { if (typeof overlayOptions[property] === "string") { - var overlayFilterFunctionString = decodeURIComponent( - overlayOptions[property] + const overlayFilterFunctionString = decodeURIComponent( + overlayOptions[property], ); // eslint-disable-next-line no-new-func - var overlayFilterFunction = new Function( + const overlayFilterFunction = new Function( "message", - "var callback = ".concat( - overlayFilterFunctionString, - "\n return callback(message)" - ) + `var callback = ${overlayFilterFunctionString} + return callback(message)`, ); + overlayOptions[property] = overlayFilterFunction; } }); } }; +/** + * @type {Status} + */ +const status = { + isUnloading: false, + // eslint-disable-next-line camelcase + currentHash: __webpack_hash__, +}; + /** @type {Options} */ -var options = { +const options = { hot: false, liveReload: false, progress: false, - overlay: false + overlay: false, }; -var parsedResourceQuery = parseURL(__resourceQuery); -var enabledFeatures = { +const parsedResourceQuery = parseURL(__resourceQuery); + +const enabledFeatures = { "Hot Module Replacement": false, "Live Reloading": false, Progress: false, - Overlay: false + Overlay: false, }; + if (parsedResourceQuery.hot === "true") { options.hot = true; enabledFeatures["Hot Module Replacement"] = true; } + if (parsedResourceQuery["live-reload"] === "true") { options.liveReload = true; enabledFeatures["Live Reloading"] = true; } + if (parsedResourceQuery.progress === "true") { options.progress = true; enabledFeatures.Progress = true; @@ -165,21 +118,22 @@ if (parsedResourceQuery.overlay) { // Fill in default "true" params for partially-specified objects. if (typeof options.overlay === "object") { - options.overlay = _objectSpread( - { - errors: true, - warnings: true, - runtimeErrors: true - }, - options.overlay - ); + options.overlay = { + errors: true, + warnings: true, + runtimeErrors: true, + ...options.overlay, + }; + decodeOverlayOptions(options.overlay); } enabledFeatures.Overlay = true; } + if (parsedResourceQuery.logging) { options.logging = parsedResourceQuery.logging; } + if (typeof parsedResourceQuery.reconnect !== "undefined") { options.reconnect = Number(parsedResourceQuery.reconnect); } @@ -190,66 +144,68 @@ if (typeof parsedResourceQuery.reconnect !== "undefined") { function setAllLogLevel(level) { // This is needed because the HMR logger operate separately from dev server logger webpackHotLog.setLogLevel( - level === "verbose" || level === "log" ? "info" : level + level === "verbose" || level === "log" ? "info" : level, ); setLogLevel(level); } + if (options.logging) { setAllLogLevel(options.logging); } + logEnabledFeatures(enabledFeatures); -self.addEventListener("beforeunload", function () { + +self.addEventListener("beforeunload", () => { status.isUnloading = true; }); -var overlay = + +const overlay = typeof window !== "undefined" ? createOverlay( typeof options.overlay === "object" ? { trustedTypesPolicyName: options.overlay.trustedTypesPolicyName, - catchRuntimeError: options.overlay.runtimeErrors + catchRuntimeError: options.overlay.runtimeErrors, } : { trustedTypesPolicyName: false, - catchRuntimeError: options.overlay - } + catchRuntimeError: options.overlay, + }, ) - : { - send: function send() {} - }; -/* Rspack dev server runtime client */ -var onSocketMessage = { - hot: function hot() { + : { send: () => {} }; + +const onSocketMessage = { + hot() { if (parsedResourceQuery.hot === "false") { return; } + options.hot = true; }, - liveReload: function liveReload() { + liveReload() { if (parsedResourceQuery["live-reload"] === "false") { return; } + options.liveReload = true; }, - invalid: function invalid() { + invalid() { log.info("App updated. Recompiling..."); // Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. if (options.overlay) { - overlay.send({ - type: "DISMISS" - }); + overlay.send({ type: "DISMISS" }); } + sendMessage("Invalid"); }, /** - * @param {string | undefined} _hash + * @param {string | undefined} hash */ hash: function hash(_hash) { if (!_hash) { return; } - status.previousHash = status.currentHash; status.currentHash = _hash; }, @@ -257,26 +213,28 @@ var onSocketMessage = { /** * @param {boolean} value */ - overlay: function overlay(value) { + overlay(value) { if (typeof document === "undefined") { return; } + options.overlay = value; decodeOverlayOptions(options.overlay); }, /** * @param {number} value */ - reconnect: function reconnect(value) { + reconnect(value) { if (parsedResourceQuery.reconnect === "false") { return; } + options.reconnect = value; }, /** * @param {boolean} value */ - progress: function progress(value) { + progress(value) { options.progress = value; }, /** @@ -285,124 +243,123 @@ var onSocketMessage = { "progress-update": function progressUpdate(data) { if (options.progress) { log.info( - "" - .concat(data.pluginName ? "[".concat(data.pluginName, "] ") : "") - .concat(data.percent, "% - ") - .concat(data.msg, ".") + `${data.pluginName ? `[${data.pluginName}] ` : ""}${data.percent}% - ${ + data.msg + }.`, ); } + sendMessage("Progress", data); }, "still-ok": function stillOk() { log.info("Nothing changed."); + if (options.overlay) { - overlay.send({ - type: "DISMISS" - }); + overlay.send({ type: "DISMISS" }); } + sendMessage("StillOk"); }, - ok: function ok() { + ok() { sendMessage("Ok"); + if (options.overlay) { - overlay.send({ - type: "DISMISS" - }); + overlay.send({ type: "DISMISS" }); } + reloadApp(options, status); }, - // TODO: remove in v5 in favor of 'static-changed' - /** - * @param {string} file - */ - "content-changed": function contentChanged(file) { - log.info( - "".concat( - file ? '"'.concat(file, '"') : "Content", - " from static directory was changed. Reloading..." - ) - ); - self.location.reload(); - }, /** * @param {string} file */ "static-changed": function staticChanged(file) { log.info( - "".concat( - file ? '"'.concat(file, '"') : "Content", - " from static directory was changed. Reloading..." - ) + `${ + file ? `"${file}"` : "Content" + } from static directory was changed. Reloading...`, ); + self.location.reload(); }, /** * @param {Error[]} warnings * @param {any} params */ - warnings: function warnings(_warnings, params) { + warnings(warnings, params) { log.warn("Warnings while compiling."); - var printableWarnings = _warnings.map(function (error) { - var _formatProblem = formatProblem("warning", error), - header = _formatProblem.header, - body = _formatProblem.body; - return "".concat(header, "\n").concat(stripAnsi(body)); + + const printableWarnings = warnings.map((error) => { + const { header, body } = formatProblem("warning", error); + + return `${header}\n${stripAnsi(body)}`; }); + sendMessage("Warnings", printableWarnings); - for (var i = 0; i < printableWarnings.length; i++) { + + for (let i = 0; i < printableWarnings.length; i++) { log.warn(printableWarnings[i]); } - var overlayWarningsSetting = + + const overlayWarningsSetting = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.warnings; + if (overlayWarningsSetting) { - var warningsToDisplay = + const warningsToDisplay = typeof overlayWarningsSetting === "function" - ? _warnings.filter(overlayWarningsSetting) - : _warnings; + ? warnings.filter(overlayWarningsSetting) + : warnings; + if (warningsToDisplay.length) { overlay.send({ type: "BUILD_ERROR", level: "warning", - messages: _warnings + messages: warnings, }); } } + if (params && params.preventReloading) { return; } + reloadApp(options, status); }, /** * @param {Error[]} errors */ - errors: function errors(_errors) { + errors(errors) { log.error("Errors while compiling. Reload prevented."); - var printableErrors = _errors.map(function (error) { - var _formatProblem2 = formatProblem("error", error), - header = _formatProblem2.header, - body = _formatProblem2.body; - return "".concat(header, "\n").concat(stripAnsi(body)); + + const printableErrors = errors.map((error) => { + const { header, body } = formatProblem("error", error); + + return `${header}\n${stripAnsi(body)}`; }); + sendMessage("Errors", printableErrors); - for (var i = 0; i < printableErrors.length; i++) { + + for (let i = 0; i < printableErrors.length; i++) { log.error(printableErrors[i]); } - var overlayErrorsSettings = + + const overlayErrorsSettings = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.errors; + if (overlayErrorsSettings) { - var errorsToDisplay = + const errorsToDisplay = typeof overlayErrorsSettings === "function" - ? _errors.filter(overlayErrorsSettings) - : _errors; + ? errors.filter(overlayErrorsSettings) + : errors; + if (errorsToDisplay.length) { overlay.send({ type: "BUILD_ERROR", level: "error", - messages: _errors + messages: errors, }); } } @@ -410,18 +367,20 @@ var onSocketMessage = { /** * @param {Error} error */ - error: function error(_error) { - log.error(_error); + error(error) { + log.error(error); }, - close: function close() { + close() { log.info("Disconnected!"); + if (options.overlay) { - overlay.send({ - type: "DISMISS" - }); + overlay.send({ type: "DISMISS" }); } + sendMessage("Close"); - } + }, }; -var socketURL = createSocketURL(parsedResourceQuery); + +const socketURL = createSocketURL(parsedResourceQuery); + socket(socketURL, onSocketMessage, options.reconnect); diff --git a/client-src/utils/reloadApp.js b/client-src/utils/reloadApp.js index 3bd51f3..204c834 100644 --- a/client-src/utils/reloadApp.js +++ b/client-src/utils/reloadApp.js @@ -10,15 +10,15 @@ import { log } from "webpack-dev-server/client/utils/log.js"; * @param {Options} options * @param {Status} status */ -function reloadApp(_ref, status) { - var hot = _ref.hot, - liveReload = _ref.liveReload; +function reloadApp({ hot, liveReload }, status) { if (status.isUnloading) { return; } - var currentHash = status.currentHash, - previousHash = status.previousHash; - var isInitial = currentHash.indexOf(/** @type {string} */ previousHash) >= 0; + + const { currentHash, previousHash } = status; + const isInitial = + currentHash.indexOf(/** @type {string} */ (previousHash)) >= 0; + if (isInitial) { return; } @@ -29,32 +29,39 @@ function reloadApp(_ref, status) { */ function applyReload(rootWindow, intervalId) { clearInterval(intervalId); + log.info("App updated. Reloading..."); + rootWindow.location.reload(); } - var search = self.location.search.toLowerCase(); - var allowToHot = search.indexOf("webpack-dev-server-hot=false") === -1; - var allowToLiveReload = + + const search = self.location.search.toLowerCase(); + const allowToHot = search.indexOf("webpack-dev-server-hot=false") === -1; + const allowToLiveReload = search.indexOf("webpack-dev-server-live-reload=false") === -1; + if (hot && allowToHot) { log.info("App hot update..."); + hotEmitter.emit("webpackHotUpdate", status.currentHash); + if (typeof self !== "undefined" && self.window) { // broadcast update to window - self.postMessage("webpackHotUpdate".concat(status.currentHash), "*"); + self.postMessage(`webpackHotUpdate${status.currentHash}`, "*"); } } // allow refreshing the page only if liveReload isn't disabled else if (liveReload && allowToLiveReload) { - var rootWindow = self; + let rootWindow = self; // use parent window for reload (in case we're in an iframe with no valid src) - var intervalId = self.setInterval(function () { + const intervalId = self.setInterval(() => { if (rootWindow.location.protocol !== "about:") { // reload immediately if protocol is valid applyReload(rootWindow, intervalId); } else { rootWindow = rootWindow.parent; + if (rootWindow.parent === rootWindow) { // if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways applyReload(rootWindow, intervalId); @@ -63,4 +70,5 @@ function reloadApp(_ref, status) { }); } } + export default reloadApp;