diff --git a/README.md b/README.md index ac8de9e..ffa4ab1 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,8 @@ It is most useful when multiple instances of React Refresh is running together s Modify the behavior of the error overlay. +Checkout [OverlayOptions](https://github.com/rspack-contrib/rspack-plugin-react-refresh/blob/main/src/options.ts#L4) type signature for more details. + - Enable the error overlay: ```js diff --git a/jest.config.js b/jest.config.ts similarity index 68% rename from jest.config.js rename to jest.config.ts index 01e0c65..c195913 100644 --- a/jest.config.js +++ b/jest.config.ts @@ -1,7 +1,7 @@ -const path = require('node:path'); +import { type JestConfigWithTsJest, createDefaultPreset } from 'ts-jest'; -/** @type {import('jest').Config} */ -const config = { +export default { + ...createDefaultPreset(), watchPathIgnorePatterns: ['/dist', '/tests/dist'], testEnvironment: './scripts/patch-node-env.cjs', testTimeout: process.env.CI ? 60000 : 30000, @@ -10,6 +10,4 @@ const config = { updateSnapshot: process.argv.includes('-u') || process.argv.includes('--updateSnapshot'), }, -}; - -module.exports = config; +} satisfies JestConfigWithTsJest; diff --git a/package.json b/package.json index cdc6e6c..a1f49c2 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,8 @@ "devDependencies": { "@biomejs/biome": "^1.9.4", "@rspack/core": "1.1.4", - "@types/node": "^22.10.1", "@types/jest": "29.5.14", - "react-refresh": "^0.14.2", + "@types/node": "^22.10.1", "cross-env": "^7.0.3", "execa": "9.5.1", "fs-extra": "11.2.0", @@ -49,9 +48,11 @@ "jest-cli": "29.7.0", "jest-environment-node": "29.7.0", "nano-staged": "^0.8.0", + "react-refresh": "^0.14.2", "semver": "7.6.3", "simple-git-hooks": "^2.11.1", "ts-jest": "29.2.5", + "ts-node": "^10.9.2", "typescript": "5.7.2" }, "dependencies": { @@ -59,10 +60,11 @@ "html-entities": "^2.5.2" }, "peerDependencies": { - "react-refresh": ">=0.10.0 <1.0.0" + "react-refresh": ">=0.10.0 <1.0.0", + "webpack-hot-middleware": "2.x" }, "peerDependenciesMeta": { - "react-refresh": { + "webpack-hot-middleware": { "optional": true } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0a4aa5..630ab78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: html-entities: specifier: ^2.5.2 version: 2.5.2 + webpack-hot-middleware: + specifier: 2.x + version: 2.26.1 devDependencies: '@biomejs/biome': specifier: ^1.9.4 @@ -38,10 +41,10 @@ importers: version: 11.2.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.10.1) + version: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-cli: specifier: 29.7.0 - version: 29.7.0(@types/node@22.10.1) + version: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-environment-node: specifier: 29.7.0 version: 29.7.0 @@ -59,7 +62,10 @@ importers: version: 2.11.1 ts-jest: specifier: 29.2.5 - version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.10.1))(typescript@5.7.2) + version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)))(typescript@5.7.2) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.10.1)(typescript@5.7.2) typescript: specifier: 5.7.2 version: 5.7.2 @@ -282,6 +288,10 @@ packages: cpu: [x64] os: [win32] + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -374,6 +384,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@module-federation/runtime-tools@0.5.1': resolution: {integrity: sha512-nfBedkoZ3/SWyO0hnmaxuz0R0iGPSikHZOAZ0N/dVSQaIzlffUo35B5nlC2wgWIc0JdMZfkwkjZRrnuuDIJbzg==} @@ -463,6 +476,18 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -502,10 +527,24 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -526,6 +565,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -654,6 +696,9 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -692,6 +737,10 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} @@ -1394,6 +1443,20 @@ packages: esbuild: optional: true + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -1424,6 +1487,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -1431,6 +1497,9 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + webpack-hot-middleware@2.26.1: + resolution: {integrity: sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1462,6 +1531,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1711,6 +1784,10 @@ snapshots: '@biomejs/cli-win32-x64@1.9.4': optional: true + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -1730,7 +1807,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -1744,7 +1821,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.10.1) + jest-config: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1900,6 +1977,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@module-federation/runtime-tools@0.5.1': dependencies: '@module-federation/runtime': 0.5.1 @@ -1978,6 +2060,14 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.3 @@ -2030,10 +2120,18 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 + ansi-html-community@0.0.8: {} + ansi-regex@5.0.1: {} ansi-styles@3.2.1: @@ -2051,6 +2149,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + arg@4.1.3: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -2192,13 +2292,13 @@ snapshots: convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.10.1): + create-jest@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.1) + jest-config: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -2207,6 +2307,8 @@ snapshots: - supports-color - ts-node + create-require@1.1.1: {} + cross-env@7.0.3: dependencies: cross-spawn: 7.0.3 @@ -2229,6 +2331,8 @@ snapshots: diff-sequences@29.6.3: {} + diff@4.0.2: {} + ejs@3.1.10: dependencies: jake: 10.9.2 @@ -2486,16 +2590,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.10.1): + jest-cli@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.1) + create-jest: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.10.1) + jest-config: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -2505,7 +2609,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.10.1): + jest-config@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -2531,6 +2635,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.10.1 + ts-node: 10.9.2(@types/node@22.10.1)(typescript@5.7.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -2750,12 +2855,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.10.1): + jest@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.10.1) + jest-cli: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3021,12 +3126,12 @@ snapshots: dependencies: is-number: 7.0.0 - ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.10.1))(typescript@5.7.2): + ts-jest@29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.10.1) + jest: 29.7.0(@types/node@22.10.1)(ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -3040,6 +3145,24 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.2) + ts-node@10.9.2(@types/node@22.10.1)(typescript@5.7.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.10.1 + acorn: 8.14.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.7.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + type-detect@4.0.8: {} type-fest@0.21.3: {} @@ -3058,6 +3181,8 @@ snapshots: escalade: 3.1.2 picocolors: 1.0.1 + v8-compile-cache-lib@3.0.1: {} + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -3068,6 +3193,12 @@ snapshots: dependencies: makeerror: 1.0.12 + webpack-hot-middleware@2.26.1: + dependencies: + ansi-html-community: 0.0.8 + html-entities: 2.5.2 + strip-ansi: 6.0.1 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -3101,6 +3232,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yn@3.1.1: {} + yocto-queue@0.1.0: {} yoctocolors@2.1.1: {} diff --git a/src/index.ts b/src/index.ts index b41c734..7a8e4fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,31 @@ import path from 'node:path'; import { normalizeOptions } from './options'; import { reactRefreshPath, refreshUtilsPath, runtimePaths } from './paths'; import { getAdditionalEntries } from './utils/getAdditionalEntries'; -import { getSocketIntegration } from './utils/getSocketIntegration'; +import { + type IntegrationType, + getSocketIntegration, +} from './utils/getSocketIntegration'; import type { Compiler } from '@rspack/core'; import type { NormalizedPluginOptions, PluginOptions } from './options'; +import { getIntegrationEntry } from './utils/getIntegrationEntry'; export type { PluginOptions }; +function addEntry(entry: string, compiler: Compiler) { + new compiler.webpack.EntryPlugin(compiler.context, entry, { + name: undefined, + }).apply(compiler); +} + +function addSocketEntry(sockIntegration: IntegrationType, compiler: Compiler) { + const integrationEntry = getIntegrationEntry(sockIntegration); + + if (integrationEntry) { + addEntry(integrationEntry, compiler); + } +} + class ReactRefreshRspackPlugin { options: NormalizedPluginOptions; @@ -36,15 +54,18 @@ class ReactRefreshRspackPlugin { }); for (const entry of addEntries.prependEntries) { - new compiler.webpack.EntryPlugin(compiler.context, entry, { - name: undefined, - }).apply(compiler); + addEntry(entry, compiler); + } + + if ( + this.options.overlay !== false && + this.options.overlay.sockIntegration + ) { + addSocketEntry(this.options.overlay.sockIntegration, compiler); } for (const entry of addEntries.overlayEntries) { - new compiler.webpack.EntryPlugin(compiler.context, entry, { - name: undefined, - }).apply(compiler); + addEntry(entry, compiler); } new compiler.webpack.ProvidePlugin({ diff --git a/src/options.ts b/src/options.ts index 84a68c9..a8e9304 100644 --- a/src/options.ts +++ b/src/options.ts @@ -4,7 +4,7 @@ import type { IntegrationType } from './utils/getSocketIntegration'; interface OverlayOptions { entry: string; module: string; - sockIntegration: IntegrationType; + sockIntegration: IntegrationType | false; sockHost?: string; sockPath?: string; sockPort?: string; diff --git a/src/sockets/WHMEventSource.ts b/src/sockets/WHMEventSource.ts new file mode 100644 index 0000000..2c268e4 --- /dev/null +++ b/src/sockets/WHMEventSource.ts @@ -0,0 +1,50 @@ +/** + * The following code is modified based on + * https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/f770d3962c388da01eeda7079bff5f40c93992d2/sockets/WHMEventSource.js + * + * MIT Licensed + * Author Michael Mok + * Copyright (c) 2019 Michael Mok + * https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/f770d3962c388da01eeda7079bff5f40c93992d2/LICENSE + */ + +/** + * The hard-coded singleton key for webpack-hot-middleware's client instance. + * + * [Ref](https://github.com/webpack-contrib/webpack-hot-middleware/blob/cb29abb9dde435a1ac8e9b19f82d7d36b1093198/client.js#L152) + */ +const singletonKey = '__webpack_hot_middleware_reporter__'; + +interface WHMClient { + cleanProblemsCache: () => void; + problems: () => boolean; + success: () => void; + useCustomOverlay: (customOverlay: { + showProblems: (type: string, data: string[]) => void; + clear: () => void; + }) => void; +} + +declare global { + interface Window { + [singletonKey]: WHMClient; + } +} + +/** + * Initializes a socket server for HMR for webpack-hot-middleware. + * @param {function(*): void} messageHandler A handler to consume Webpack compilation messages. + * @returns {void} + */ +export function init(messageHandler: (...args: unknown[]) => void) { + const client = window[singletonKey]; + + client.useCustomOverlay({ + showProblems(type, data) { + messageHandler({ type, data }); + }, + clear() { + messageHandler({ type: 'ok' }); + }, + }); +} diff --git a/src/utils/getIntegrationEntry.ts b/src/utils/getIntegrationEntry.ts new file mode 100644 index 0000000..f3fba33 --- /dev/null +++ b/src/utils/getIntegrationEntry.ts @@ -0,0 +1,18 @@ +import type { IntegrationType } from './getSocketIntegration'; + +/** + * Gets entry point of a supported socket integration. + * @param integrationType A valid socket integration type or a path to a module. + * @returns Path to the resolved integration entry point. + */ +export function getIntegrationEntry(integrationType: IntegrationType) { + let resolvedEntry: string | undefined; + switch (integrationType) { + case 'whm': { + resolvedEntry = 'webpack-hot-middleware/client'; + break; + } + } + + return resolvedEntry; +} diff --git a/src/utils/getSocketIntegration.ts b/src/utils/getSocketIntegration.ts index 1c3be6c..ca72011 100644 --- a/src/utils/getSocketIntegration.ts +++ b/src/utils/getSocketIntegration.ts @@ -1,4 +1,4 @@ -export type IntegrationType = 'wds'; +export type IntegrationType = 'wds' | 'whm' | (string & {}); export function getSocketIntegration(integrationType: IntegrationType) { let resolvedSocketIntegration: string; @@ -7,6 +7,10 @@ export function getSocketIntegration(integrationType: IntegrationType) { resolvedSocketIntegration = require.resolve('../sockets/WDSSocket'); break; } + case 'whm': { + resolvedSocketIntegration = require.resolve('../sockets/WHMEventSource'); + break; + } default: { resolvedSocketIntegration = require.resolve(integrationType); break; diff --git a/test/test.spec.ts b/test/test.spec.ts index fe06e8c..3d06075 100644 --- a/test/test.spec.ts +++ b/test/test.spec.ts @@ -1,10 +1,21 @@ -const fs = require("fs"); -const path = require("path"); -const { rspack } = require("@rspack/core"); -const ReactRefreshPlugin = require("@rspack/plugin-react-refresh"); +import assert from "node:assert" +import fs from "node:fs" +import path from "node:path" +import { type Stats, rspack } from "@rspack/core" +import ReactRefreshPlugin, { type PluginOptions} from "@rspack/plugin-react-refresh" + +type Outputs = { + reactRefresh: string, + fixture: string, + runtime: string, + vendor: string +} + +type CompilationCallback = (error: Error | null, stats: Stats | undefined, outputs: Outputs) => void const uniqueName = "ReactRefreshLibrary"; -const compileWithReactRefresh = (fixturePath, refreshOptions, callback) => { + +const compileWithReactRefresh = (fixturePath: string, refreshOptions: PluginOptions, callback:CompilationCallback) => { let dist = path.join(fixturePath, "dist"); rspack( { @@ -43,6 +54,7 @@ const compileWithReactRefresh = (fixturePath, refreshOptions, callback) => { }, (error, stats) => { expect(error).toBeFalsy(); + assert(stats, "stats is not defined"); const statsJson = stats.toJson({ all: true }); expect(statsJson.errors).toHaveLength(0); expect(statsJson.warnings).toHaveLength(0); @@ -84,7 +96,7 @@ describe("react-refresh-rspack-plugin", () => { compileWithReactRefresh( path.join(__dirname, "fixtures/default"), {}, - (_, __, { reactRefresh, fixture, runtime, vendor }) => { + (_, __, { fixture }) => { expect(fixture).toContain("function $RefreshReg$"); done(); } @@ -95,7 +107,7 @@ describe("react-refresh-rspack-plugin", () => { compileWithReactRefresh( path.join(__dirname, "fixtures/default"), {}, - (_, __, { reactRefresh, fixture, runtime, vendor }) => { + (_, __, { reactRefresh }) => { expect(reactRefresh).toContain(uniqueName); done(); } @@ -109,7 +121,7 @@ describe("react-refresh-rspack-plugin", () => { exclude: null, include: path.join(__dirname, "fixtures/node_modules/foo") }, - (_, __, { reactRefresh, fixture, runtime, vendor }) => { + (_, __, { vendor }) => { expect(vendor).toContain("function $RefreshReg$"); done(); } @@ -141,4 +153,20 @@ describe("react-refresh-rspack-plugin", () => { } ); }); + + it("should include entries for webpack-hot-middleware", done => { + compileWithReactRefresh( + path.join(__dirname, "fixtures/custom"), + { + overlay: { + sockIntegration: 'whm' + } + }, + (_, __, { fixture }) => { + expect(fixture).toContain("webpack-hot-middleware/client"); + expect(fixture).toContain("WHMEventSource.js"); + done(); + } + ); + }); });