diff --git a/jest.config.ts b/jest.config.mjs similarity index 90% rename from jest.config.ts rename to jest.config.mjs index 16b09195..67861cea 100644 --- a/jest.config.ts +++ b/jest.config.mjs @@ -1,7 +1,8 @@ -import type {Config} from "jest"; +// @ts-check import {availableParallelism} from "node:os"; -const shared: Config = { +/** @type {import("jest").Config} */ +const shared = { rootDir: import.meta.dirname, testEnvironment: "node", // This function is serialized into browser/VM realms; Istanbul closures cannot cross that boundary. @@ -9,12 +10,13 @@ const shared: Config = { globals: {ADNBN_TEST_ROOT: import.meta.dirname}, setupFilesAfterEnv: ["/tests/jest.setup.ts"], - transformIgnorePatterns: ["/node_modules/(?!(@addon-core/storage|nanoid)/)"], modulePathIgnorePatterns: ["/.cache/"], resolver: "/tests/raw-module-resolver.cjs", moduleNameMapper: { + // Rspack loads this module through native Node, outside Jest's TypeScript transform. + // Build tests use the published ESM loader, including on Node 22.12 without type stripping. "^\\.\\./\\.\\./bundler/loaders/resolve-style-urls$": - "/tests/integration/build/style/resolve-style-urls-loader.cjs", + "/dist/cli/bundler/loaders/resolve-style-urls.js", "^#adnbn/(.*)$": "/src/virtual/$1.ts", "^@cli/(.*)$": "/src/cli/$1", "^@entry/(.*)$": "/src/entry/$1", @@ -68,10 +70,12 @@ const domTests = [ "/src/locale/adapters/**/*.test.ts", "/src/message/adapters/**/*.test.ts", ]; -const exclude = (patterns: string[]) => +/** @param {string[]} patterns */ +const exclude = patterns => patterns.map(pattern => `!${pattern.replace("", import.meta.dirname.replaceAll("\\", "/"))}`); -const config: Config = { +/** @type {import("jest").Config} */ +const config = { verbose: false, reporters: ["default", "/tests/BrowserStartupReporter.cjs"], maxWorkers: Math.max(1, Math.min(8, availableParallelism() - 1)), diff --git a/package-lock.json b/package-lock.json index 8e47c5a3..a66eec9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "@addon-core/browser": "^0.12.0", "@addon-core/inject-script": "^0.5.1", "@addon-core/storage": "^0.8.3", - "@rsdoctor/rspack-plugin": "^1.5.16", - "@rspack/core": "^1.7.5", + "@rsdoctor/core": "^2.0.1", + "@rspack/core": "~2.2.9", "@rspack/lite-tapable": "^1.1.0", "@rspackjs/plugin-html-tags": "^1.0.0", "@svgr/webpack": "^8.1.0", @@ -51,6 +51,7 @@ "devDependencies": { "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", + "@jest/globals": "^30.3.0", "@microsoft/api-extractor": "^7.53.1", "@release-it/conventional-changelog": "^10.0.4", "@swc/core": "^1.11.7", @@ -79,14 +80,13 @@ "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "jest": "^30.2.0", + "jest": "^30.3.0", "jest-environment-jsdom": "^30.0.0", "memfs": "4.57.2", "prettier": "^3.6.2", "react": "^19.2.8", "react-dom": "^19.2.8", "release-it": "^19.2.3", - "ts-node": "10.9.2", "tsc-alias": "^1.8.17", "tsup": "^8.5.0", "tsx": "^4.19.2", @@ -2378,30 +2378,6 @@ } } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@csstools/color-helpers": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", @@ -2543,20 +2519,20 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "license": "MIT", "optional": true, "dependencies": { @@ -2564,9 +2540,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", "license": "MIT", "optional": true, "dependencies": { @@ -5081,69 +5057,22 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@module-federation/error-codes": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.22.0.tgz", - "integrity": "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==", - "license": "MIT" - }, - "node_modules/@module-federation/runtime": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.22.0.tgz", - "integrity": "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==", - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.22.0", - "@module-federation/runtime-core": "0.22.0", - "@module-federation/sdk": "0.22.0" - } - }, - "node_modules/@module-federation/runtime-core": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.22.0.tgz", - "integrity": "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==", - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.22.0", - "@module-federation/sdk": "0.22.0" - } - }, - "node_modules/@module-federation/runtime-tools": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.22.0.tgz", - "integrity": "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==", - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.22.0", - "@module-federation/webpack-bundler-runtime": "0.22.0" - } - }, - "node_modules/@module-federation/sdk": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.22.0.tgz", - "integrity": "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==", - "license": "MIT" - }, - "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.22.0.tgz", - "integrity": "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==", - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.22.0", - "@module-federation/sdk": "0.22.0" - } - }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", - "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@nodelib/fs.scandir": { @@ -5697,6 +5626,12 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "license": "MIT" + }, "node_modules/@release-it/conventional-changelog": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/@release-it/conventional-changelog/-/conventional-changelog-10.0.6.tgz", @@ -6069,93 +6004,23 @@ "win32" ] }, - "node_modules/@rsbuild/plugin-check-syntax": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@rsbuild/plugin-check-syntax/-/plugin-check-syntax-1.6.1.tgz", - "integrity": "sha512-26xtEYN0QjZYoyt0lWnvIztBWjEZJvcfw7MN4f5B4SpNggmnF7F7aNPrgkY3EccXVFx1VGQBhnCkBV//OoS07Q==", + "node_modules/@rsdoctor/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rsdoctor/core/-/core-2.0.1.tgz", + "integrity": "sha512-87C/flgxSz7EsuO+JF6ARfDVq7vFgbL/eC/FMxLxU2wT08geT/7TeHGNyTsRLSi9MQ1I2/yqKd7M/IcFdg7wVA==", "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "browserslist-to-es-version": "^1.2.0", - "htmlparser2": "10.0.0", - "picocolors": "^1.1.1", - "source-map": "^0.7.6" - }, - "peerDependencies": { - "@rsbuild/core": "^1.0.0 || ^2.0.0-0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - } - } - }, - "node_modules/@rsdoctor/client": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/client/-/client-1.6.5.tgz", - "integrity": "sha512-k/k1X8Y+/7JbAiE7ELdSfTbRP51fKY0stHc1y1N2s0uF7RRBSozN5ENyD+5yJSIDgNP5N5lRnZF+1Pp4BOhhjA==", - "license": "MIT" - }, - "node_modules/@rsdoctor/core": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/core/-/core-1.6.5.tgz", - "integrity": "sha512-yiY3Og8I05QNkDVY3Jgj2VPMVqZpWLn0wyhUXej2BQXJC+iifPSTEHAcB4sVofyx1v8MJgTBelepjDr0vbNeGA==", - "license": "MIT", - "dependencies": { - "@rsbuild/plugin-check-syntax": "^1.6.1", - "@rsdoctor/graph": "1.6.5", - "@rsdoctor/sdk": "1.6.5", - "@rsdoctor/types": "1.6.5", - "@rsdoctor/utils": "1.6.5", - "@rspack/resolver": "^0.2.8", - "browserslist-load-config": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-toolkit": "^1.49.0", - "filesize": "^11.0.17", - "fs-extra": "^11.1.1", - "semver": "^7.8.5", - "source-map": "^0.7.6" - } - }, - "node_modules/@rsdoctor/core/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "@rsdoctor/client": "2.0.1", + "@rsdoctor/shared": "2.0.1", + "caniuse-lite": "^1.0.30001815", + "sirv": "3.0.2", + "source-map": "^0.8.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@rsdoctor/graph": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/graph/-/graph-1.6.5.tgz", - "integrity": "sha512-0XnUkPLG2HO4L8IC0o5NSYhoKUKsjERSc5sgdv5hsPGWDMa5K0lvJpXp9C6mHfEjZtHu7308Cm7/LGQsGPJffA==", - "license": "MIT", - "dependencies": { - "@rsdoctor/types": "1.6.5", - "@rsdoctor/utils": "1.6.5", - "es-toolkit": "^1.49.0", - "path-browserify": "1.0.1", - "source-map": "^0.7.6" - } - }, - "node_modules/@rsdoctor/rspack-plugin": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/rspack-plugin/-/rspack-plugin-1.6.5.tgz", - "integrity": "sha512-61U0NqOH6DKrkv7MJC0LwxG4NAYCP3xI5y3TXCs/95f0zm73PYVP80XTRQ1ffNC/yiFmU6uJYY6DXiKdQUpYZw==", - "license": "MIT", - "dependencies": { - "@rsdoctor/core": "1.6.5", - "@rsdoctor/graph": "1.6.5", - "@rsdoctor/sdk": "1.6.5", - "@rsdoctor/types": "1.6.5", - "@rsdoctor/utils": "1.6.5" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "@rspack/core": "*" + "@rspack/core": "^2.0.0" }, "peerDependenciesMeta": { "@rspack/core": { @@ -6163,132 +6028,85 @@ } } }, - "node_modules/@rsdoctor/sdk": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/sdk/-/sdk-1.6.5.tgz", - "integrity": "sha512-TTXZ2o8qY1nTK8QN8M4kwrBRgRZ1rwRgwGKyM/v0f7VLjQ9BdLjekHapP7PyMVgw+nR5y9C/UR+eDQD1GqW93Q==", + "node_modules/@rsdoctor/core/node_modules/@rsdoctor/client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rsdoctor/client/-/client-2.0.1.tgz", + "integrity": "sha512-v/KZwX9j1s2Vsni8KNVNCcxGDx7/UZeK+U+DkqgGT1akmTHq/Hr1bNAkzUknIne5Fy+UVVmJBx61KyLcA8he8w==", "license": "MIT", - "dependencies": { - "@rsdoctor/client": "1.6.5", - "@rsdoctor/graph": "1.6.5", - "@rsdoctor/types": "1.6.5", - "@rsdoctor/utils": "1.6.5", - "launch-editor": "^2.13.2", - "safer-buffer": "2.1.2", - "socket.io": "4.8.1", - "tapable": "2.3.3" + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rsdoctor/types": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/types/-/types-1.6.5.tgz", - "integrity": "sha512-qcmJXkkohhGM+7/OKLWBDZ88CCDstc7YFTQNBVNO2bQ4xDyLjjWYg1MiS4Lsy3dC0BC7Sqy35eunIvmlnkMabw==", + "node_modules/@rsdoctor/core/node_modules/source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz", + "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@rsdoctor/shared": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rsdoctor/shared/-/shared-2.0.1.tgz", + "integrity": "sha512-aTCgBg2cST1YErx+IM2GfKgCV3ej2VN5S/fAU2M8wnI7f86X6ZvPT32H+dN4AmdPD/uLkb2URB0N+lQ76xiHYw==", "license": "MIT", "dependencies": { - "@types/connect": "3.4.38", + "@rspack/lite-tapable": "^1.3.0", "@types/estree": "1.0.5", - "@types/tapable": "2.3.0", - "source-map": "^0.7.6" + "buffer": "6.0.3", + "connect-next": "5.0.0", + "path-browserify": "1.0.1", + "source-map": "^0.8.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "@rspack/core": "*", - "webpack": "5.x" + "@rspack/core": "^2.0.0", + "@types/node": ">=20" }, "peerDependenciesMeta": { "@rspack/core": { "optional": true - }, - "webpack": { - "optional": true } } }, - "node_modules/@rsdoctor/utils": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/@rsdoctor/utils/-/utils-1.6.5.tgz", - "integrity": "sha512-/Ybsk9krXapFwn0LBS7iKqjVBreRbv2siySAKxEhMJiAiHmaWP8vFsgfyaQHK+TnjqQwiNlN/JeOnh8NF7+tyQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "7.26.2", - "@rsdoctor/types": "1.6.5", - "@types/estree": "1.0.5", - "acorn": "^8.10.0", - "acorn-import-attributes": "^1.9.5", - "acorn-walk": "8.3.5", - "deep-eql": "4.1.4", - "envinfo": "7.21.0", - "fs-extra": "^11.1.1", - "get-port": "5.1.1", - "json-stream-stringify": "3.0.1", - "lines-and-columns": "2.0.4", - "picocolors": "^1.1.1", - "rslog": "^2.1.2", - "strip-ansi": "^7.2.0" - } - }, - "node_modules/@rsdoctor/utils/node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@rsdoctor/utils/node_modules/ansi-regex": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", - "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@rsdoctor/utils/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, + "node_modules/@rsdoctor/shared/node_modules/source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz", + "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==", + "license": "BSD-3-Clause", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">= 12" } }, "node_modules/@rspack/binding": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.11.tgz", - "integrity": "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-2.2.9.tgz", + "integrity": "sha512-Y8wDJoRo8n0KXDGq6J3KGVekkUQcOcUHSppMHoJv8JLbNBEsyaaKiDx4ljNTGEDtTON0CRbMEWbSiLRe+bJxOQ==", "license": "MIT", "optionalDependencies": { - "@rspack/binding-darwin-arm64": "1.7.11", - "@rspack/binding-darwin-x64": "1.7.11", - "@rspack/binding-linux-arm64-gnu": "1.7.11", - "@rspack/binding-linux-arm64-musl": "1.7.11", - "@rspack/binding-linux-x64-gnu": "1.7.11", - "@rspack/binding-linux-x64-musl": "1.7.11", - "@rspack/binding-wasm32-wasi": "1.7.11", - "@rspack/binding-win32-arm64-msvc": "1.7.11", - "@rspack/binding-win32-ia32-msvc": "1.7.11", - "@rspack/binding-win32-x64-msvc": "1.7.11" + "@rspack/binding-darwin-arm64": "2.2.9", + "@rspack/binding-darwin-x64": "2.2.9", + "@rspack/binding-linux-arm64-gnu": "2.2.9", + "@rspack/binding-linux-arm64-musl": "2.2.9", + "@rspack/binding-linux-ppc64-gnu": "2.2.9", + "@rspack/binding-linux-riscv64-gnu": "2.2.9", + "@rspack/binding-linux-riscv64-musl": "2.2.9", + "@rspack/binding-linux-s390x-gnu": "2.2.9", + "@rspack/binding-linux-x64-gnu": "2.2.9", + "@rspack/binding-linux-x64-musl": "2.2.9", + "@rspack/binding-wasm32-wasi": "2.2.9", + "@rspack/binding-win32-arm64-msvc": "2.2.9", + "@rspack/binding-win32-ia32-msvc": "2.2.9", + "@rspack/binding-win32-x64-msvc": "2.2.9" } }, "node_modules/@rspack/binding-darwin-arm64": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.7.11.tgz", - "integrity": "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-2.2.9.tgz", + "integrity": "sha512-aIHkk/wngpwgWkwVQFz2AIkmG9WpWtWlkqy4BlYhs5a7l9z0BrB0kXnUb6VSbqZ7Tc3cRepKw7ipkGjKa8KKXw==", "cpu": [ "arm64" ], @@ -6299,9 +6117,9 @@ ] }, "node_modules/@rspack/binding-darwin-x64": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-1.7.11.tgz", - "integrity": "sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-darwin-x64/-/binding-darwin-x64-2.2.9.tgz", + "integrity": "sha512-QtUS7lyOE5oxdMm7RH/Fw176jqwdHiTW2qEsmbdCYzPw3t7QkmI5ohoyiuxOp3czJfaayXP6bVyvm2FqqnWPAw==", "cpu": [ "x64" ], @@ -6312,12 +6130,15 @@ ] }, "node_modules/@rspack/binding-linux-arm64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.7.11.tgz", - "integrity": "sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-2.2.9.tgz", + "integrity": "sha512-GMKxtBJAMoqBPqXZVi0QanDkneWwgiKRpTgHW/pbKPY/2XMfZJ98thm4YlATO18XZJ2KET+GfsNfaf1o5Y+aaA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -6325,37 +6146,14 @@ ] }, "node_modules/@rspack/binding-linux-arm64-musl": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.7.11.tgz", - "integrity": "sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-musl/-/binding-linux-arm64-musl-2.2.9.tgz", + "integrity": "sha512-aW3fcM64R5mY2+ym6Q8okAo/pcVIjAliqxFOc9lf9hCyS7hHaabWo30smeMX3neEjsqw+iyNbzWMuH0G5AugTw==", "cpu": [ "arm64" ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rspack/binding-linux-x64-gnu": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.7.11.tgz", - "integrity": "sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rspack/binding-linux-x64-musl": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-1.7.11.tgz", - "integrity": "sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg==", - "cpu": [ - "x64" + "libc": [ + "musl" ], "license": "MIT", "optional": true, @@ -6363,136 +6161,47 @@ "linux" ] }, - "node_modules/@rspack/binding-wasm32-wasi": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-1.7.11.tgz", - "integrity": "sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "1.0.7" - } - }, - "node_modules/@rspack/binding-win32-arm64-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.7.11.tgz", - "integrity": "sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA==", + "node_modules/@rspack/binding-linux-ppc64-gnu": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-2.2.9.tgz", + "integrity": "sha512-dVEiyZdANq+g/qugI64vKnxO+PCVicidu/GQ6sO0QEWGVzdSUbxHztHviSnMwdvRs6OLrN7Lwhaw536hJ/cU8w==", "cpu": [ - "arm64" + "ppc64" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rspack/binding-win32-ia32-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.7.11.tgz", - "integrity": "sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw==", - "cpu": [ - "ia32" + "libc": [ + "glibc" ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@rspack/binding-win32-x64-msvc": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.7.11.tgz", - "integrity": "sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q==", + "node_modules/@rspack/binding-linux-riscv64-gnu": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-2.2.9.tgz", + "integrity": "sha512-uBHWmfMm502145le0EUOsBoV6bJ9XA5s3NQLL4smSMoSEuMbNrRhqDJ2+B1u8aXVfjfhxCPyNlMDmgbsdVaO0Q==", "cpu": [ - "x64" + "riscv64" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rspack/core": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.7.11.tgz", - "integrity": "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew==", - "license": "MIT", - "dependencies": { - "@module-federation/runtime-tools": "0.22.0", - "@rspack/binding": "1.7.11", - "@rspack/lite-tapable": "1.1.0" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.1" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@rspack/lite-tapable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz", - "integrity": "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==", - "license": "MIT" - }, - "node_modules/@rspack/resolver": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver/-/resolver-0.2.8.tgz", - "integrity": "sha512-FBWqdHhzS8mcf/WN4Ktzr7EaeaN+hsxbN98EweegX3924beZuY6H70CSFWCv1fIHAieCUv/9XCjKggHvhCsLwA==", - "license": "MIT", - "optionalDependencies": { - "@rspack/resolver-binding-darwin-arm64": "0.2.8", - "@rspack/resolver-binding-darwin-x64": "0.2.8", - "@rspack/resolver-binding-linux-arm64-gnu": "0.2.8", - "@rspack/resolver-binding-linux-arm64-musl": "0.2.8", - "@rspack/resolver-binding-linux-x64-gnu": "0.2.8", - "@rspack/resolver-binding-linux-x64-musl": "0.2.8", - "@rspack/resolver-binding-wasm32-wasi": "0.2.8", - "@rspack/resolver-binding-win32-arm64-msvc": "0.2.8", - "@rspack/resolver-binding-win32-ia32-msvc": "0.2.8", - "@rspack/resolver-binding-win32-x64-msvc": "0.2.8" - } - }, - "node_modules/@rspack/resolver-binding-darwin-arm64": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-0.2.8.tgz", - "integrity": "sha512-nTnK17kmxXEvR+WpOIZPSIzUFYeWCHoffgU9tvOLOwuTBH41kWnSQXXWu+AiMVwvJ6wdRO6Vo30hPhlXEG7Pyw==", - "cpu": [ - "arm64" + "libc": [ + "glibc" ], "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ] }, - "node_modules/@rspack/resolver-binding-darwin-x64": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-0.2.8.tgz", - "integrity": "sha512-Aqr4TK2rA6XVYUOmM5YCtYyCMZhOIR53P4cOGgGARg99A7OuMBMzUL4r1n0M0Fx35v6/sSx1OBe+odHmPxksEg==", + "node_modules/@rspack/binding-linux-riscv64-musl": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-2.2.9.tgz", + "integrity": "sha512-MR7e6PP7KcG3jHYWznk17npBBvjUXQg7qQbS+yrAu0R3p/27wLp+aP5m/zk4ev1Iq93ccIBIAGVRGA3v1fSKew==", "cpu": [ - "x64" + "riscv64" ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rspack/resolver-binding-linux-arm64-gnu": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-0.2.8.tgz", - "integrity": "sha512-wGvkxm2G4mNTztslaOzLzx5JuySQSy5DcOWEZxHcjJJzp5L3ODbYLK18HtUc6cvmaVOmjaGrrYPrqJJ0hHTVFg==", - "cpu": [ - "arm64" + "libc": [ + "musl" ], "license": "MIT", "optional": true, @@ -6500,12 +6209,15 @@ "linux" ] }, - "node_modules/@rspack/resolver-binding-linux-arm64-musl": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-0.2.8.tgz", - "integrity": "sha512-EqRJ9zLQsLAvyDKJKVZ45BSqRIMS12f5HtJdy3KkAHU14ZmsGv8e5IKkwUZN5CNBRad8xVlOMMx3dOfF4whJzg==", + "node_modules/@rspack/binding-linux-s390x-gnu": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-2.2.9.tgz", + "integrity": "sha512-Tc9Nuv7XvkrAG79Ps/6F7vT4bxZqiaiQ3Rd3BidKUjVYiwTPJogk1W3gPXp7Lw+AMoN/dY63W22bMJ7Oh0FeZw==", "cpu": [ - "arm64" + "s390x" + ], + "libc": [ + "glibc" ], "license": "MIT", "optional": true, @@ -6513,73 +6225,57 @@ "linux" ] }, - "node_modules/@rspack/resolver-binding-linux-x64-gnu": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-0.2.8.tgz", - "integrity": "sha512-eXbeotNCTntL4/+mxJRVCxK63YeWzTfp0F3POeHJFSs6Nt0f2J/mZNFlasJmd6xm7zvE80h/HWOwbwjRBLcElA==", + "node_modules/@rspack/binding-linux-x64-gnu": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-2.2.9.tgz", + "integrity": "sha512-xQYhQwC8q3mSCzHV+yi76Gt2Lt9BhnmeJnQgXGjuhyvjZWBB9jFNp9Uj6umun7tlMliMRhoqTesqvK4Vo/kkcQ==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rspack/resolver-binding-linux-x64-musl": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-0.2.8.tgz", - "integrity": "sha512-KWFHlOWGkT+eMngoUgPGXrDi+rU04VCh9jyk0U6Ot2RTWvhGxwKykjmLS+CWZI/EBrzr9A6g2U3jzKTMNz9oCw==", + "node_modules/@rspack/binding-linux-x64-musl": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-musl/-/binding-linux-x64-musl-2.2.9.tgz", + "integrity": "sha512-FfEYdFhOd1nZL+U/up8nlbcRTTyWX4ohXUQcApQMYKIZoR1EJ0g/oBhS915+TSray0DchA+NT6yVL1sqWJOFaQ==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rspack/resolver-binding-wasm32-wasi": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-0.2.8.tgz", - "integrity": "sha512-I6GIhgICFViE88jejIV74oiiWHnpLpQ5ogaZM1ozM9KDnfqcHoX0IVEyrIh5KqA8iLDyhuoFSW+Hf0qN7VTBBQ==", + "node_modules/@rspack/binding-wasm32-wasi": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-wasm32-wasi/-/binding-wasm32-wasi-2.2.9.tgz", + "integrity": "sha512-+29JEBiUi2TMDOGYmT3Tm3JGk3f19+CbjxNEt1bVreR8axuZZj+wOSwB/sx85lKQkL5fuNPh0BhOZ7cdkDxQpA==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rspack/resolver-binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.6.tgz", - "integrity": "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", - "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" + "@emnapi/core": "1.11.3", + "@emnapi/runtime": "1.11.3", + "@napi-rs/wasm-runtime": "1.1.6" } }, - "node_modules/@rspack/resolver-binding-win32-arm64-msvc": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-0.2.8.tgz", - "integrity": "sha512-ZXCt3qUfDAEbtc2sHpvxM7lNFZM+DxfblgXUIl3Jy6BuEZbHe1i6z+t9c34ayHoGTVbVSNCtaYuG/MaWdSnPHw==", + "node_modules/@rspack/binding-win32-arm64-msvc": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-2.2.9.tgz", + "integrity": "sha512-x/BDAwQCkR+ANWAAWV+15PFWuae2z9DzL+QcVeb+5taOri+tHa/iQdG8e3GJ60d0amsR+u9BJ2CBtJLK1fQkxw==", "cpu": [ "arm64" ], @@ -6589,10 +6285,10 @@ "win32" ] }, - "node_modules/@rspack/resolver-binding-win32-ia32-msvc": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-0.2.8.tgz", - "integrity": "sha512-2LRymjDK8MpUERD8CL0PPae5y2crU5TAg4T4EzpeL5jLARVq6izsEruiWzB6Y+D15vUYlvmgs2370GXVSB861w==", + "node_modules/@rspack/binding-win32-ia32-msvc": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-2.2.9.tgz", + "integrity": "sha512-i622t0Mom3STiDi8VHLaX62LKwhbT2kg37fKjzv3wQT+9bJvQ/+St6QQzFZg9C7fV53LXqAw8jFi5yIPIjg0Qw==", "cpu": [ "ia32" ], @@ -6602,10 +6298,10 @@ "win32" ] }, - "node_modules/@rspack/resolver-binding-win32-x64-msvc": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@rspack/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-0.2.8.tgz", - "integrity": "sha512-hzRpfbtvv4M4EVrKKIAaHDs5wT8lVcbSUjtwPs5u4IeLEix45nQPQ6ZQjmE4lIH0GP/3L3XQhZroYmTcH/xdsQ==", + "node_modules/@rspack/binding-win32-x64-msvc": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/binding-win32-x64-msvc/-/binding-win32-x64-msvc-2.2.9.tgz", + "integrity": "sha512-K98FPCAv0VpDPAz5ZqZ0XI1CmdJu9Kw1FmUDl4rN/zQIt9iqYn+Hgxq6Nk6Zx7g938jiG7ABLwITXSIKGbs6aw==", "cpu": [ "x64" ], @@ -6615,6 +6311,36 @@ "win32" ] }, + "node_modules/@rspack/core": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@rspack/core/-/core-2.2.9.tgz", + "integrity": "sha512-Tp6/ZdM5UrGB3BeLfvh69l1iX03PZ46IgXjKqubVEuJVAhnOjpshooH/YAxcxR0AymcBLKA997Z4v+LV2aXzCw==", + "license": "MIT", + "dependencies": { + "@rspack/binding": "2.2.9" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", + "@swc/helpers": "^0.5.23" + }, + "peerDependenciesMeta": { + "@module-federation/runtime-tools": { + "optional": true + }, + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@rspack/lite-tapable": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.3.0.tgz", + "integrity": "sha512-YQLaGRltBp5wzh9ee7PMmh1pErbx0E3zDlfO4K3iQpsEcJcBcgj3+9gq6/wqB/zbXbluoWoRCvL+odkft/Qt3A==", + "license": "MIT" + }, "node_modules/@rspackjs/plugin-html-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rspackjs/plugin-html-tags/-/plugin-html-tags-1.0.0.tgz", @@ -6824,12 +6550,6 @@ "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -7520,34 +7240,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, "node_modules/@tybys/wasm-util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", @@ -7628,24 +7320,6 @@ "@types/har-format": "*" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -7884,15 +7558,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-oMnbAXeVo+KUnje3hzdORXUbfnzTfqD0H92mLl19NE5hFqH9Q4ktq+xehNSxcNeeLm1COopYwa0zeP6Iz+oIXg==", - "license": "MIT", - "dependencies": { - "tapable": "^2.3.0" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -7900,15 +7565,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -8390,40 +8046,6 @@ "license": "Apache-2.0", "peer": true }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -8436,15 +8058,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-import-phases": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", @@ -8458,18 +8071,6 @@ "acorn": "^8.14.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", - "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/adjust-sourcemap-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", @@ -8630,13 +8231,6 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -8863,14 +8457,25 @@ "node": "18 || 20 || >=22" } }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, "node_modules/baseline-browser-mapping": { "version": "2.11.33", @@ -8997,24 +8602,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/browserslist-load-config": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/browserslist-load-config/-/browserslist-load-config-1.0.4.tgz", - "integrity": "sha512-5Naou4DgM5JyU3lubTHD+UWIvwpjxVAAxaGvh5DV4rDNEEwzAt26Z0XfrVwXOdQSJPs8OPwiDwZ9Sq/oew7U6w==", - "license": "MIT" - }, - "node_modules/browserslist-to-es-version": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/browserslist-to-es-version/-/browserslist-to-es-version-1.4.2.tgz", - "integrity": "sha512-3NV13pCv0wmPxxZZcekHAG6vt8rQ94w2c4/UBe3ZU3NDUm5TP+QFK3rjS6XeKWSHWpnPYNfQzlhnljka0BrEOA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2" - }, - "bin": { - "browserslist-to-es-version": "dist/cli.js" - } - }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -9025,6 +8612,30 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9538,6 +9149,15 @@ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "license": "MIT" }, + "node_modules/connect-next": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/connect-next/-/connect-next-5.0.0.tgz", + "integrity": "sha512-f8s8Vjzr46shiGeR6KIQxDy8i/PRw+cFpzDei9SHEv8C46WVpzfmKIIvYA6z9lkE7Qqiq+XcKlfdsU4MqcUl0w==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", @@ -9680,15 +9300,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", @@ -9699,24 +9310,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://opencollective.com/core-js" } }, "node_modules/cosmiconfig": { @@ -9764,13 +9358,6 @@ "typescript": ">=5" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -10040,18 +9627,6 @@ } } }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -10337,61 +9912,12 @@ "node": ">= 4" } }, - "node_modules/engine.io": { - "version": "6.6.11", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.11.tgz", - "integrity": "sha512-z34mGpLoY/82ZbkX1LqGMIaf3wjVMPL6gVC681QDqnmzJ26synI8qAE32P+a7UazmpJZ8UGrFIeAavgRfXzYrg==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "@types/ws": "^8.5.12", - "accepts": "~1.3.4", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.21.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/enhanced-resolve": { "version": "5.21.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" @@ -10422,18 +9948,6 @@ "node": ">=6" } }, - "node_modules/envinfo": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", - "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -10463,6 +9977,7 @@ "version": "1.53.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.53.0.tgz", "integrity": "sha512-3+AYXp9fmJ/UhHBMYj2/U+RaUouMD90SzoItyDOB2Za7W6dB7fQApFGnZPl2MF6K8/Tv1koh1JyirSx/kju6Pw==", + "dev": true, "license": "MIT", "workspaces": [ "docs", @@ -10860,15 +10375,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/filesize": { - "version": "11.0.29", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-11.0.29.tgz", - "integrity": "sha512-+Mkmp8keoklLzAnvP/RQGDUEWEpjGmhvm4d4U739C1ZnkqxqoSqdLHnjv9/4QX6lltKSRk6qzQI7+94JVDtuCA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 10.8.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -10997,18 +10503,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -11293,37 +10787,6 @@ "dev": true, "license": "MIT" }, - "node_modules/htmlparser2": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", - "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.1", - "entities": "^6.0.0" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -11417,6 +10880,26 @@ "postcss": "^8.1.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -13834,12 +13317,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/json-stream-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.0.1.tgz", - "integrity": "sha512-vuxs3G1ocFDiAQ/SX0okcZbtqXwgj1g71qE9+vrjJ2EkjKQlEFDAcUNRxRU8O+GekV4v5cM2qXP0Wyt/EMDBiQ==", - "license": "MIT" - }, "node_modules/json-stringify-deterministic": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/json-stringify-deterministic/-/json-stringify-deterministic-1.0.13.tgz", @@ -13896,16 +13373,6 @@ "node": ">=0.10.0" } }, - "node_modules/launch-editor": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.2.tgz", - "integrity": "sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.10.0" - } - }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -13940,15 +13407,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/lines-and-columns": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", - "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -14139,13 +13597,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -14423,6 +13874,15 @@ "pathe": "^2.0.1" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -14506,15 +13966,6 @@ "dev": true, "license": "MIT" }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -14679,6 +14130,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16005,15 +15457,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rslog": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/rslog/-/rslog-2.3.0.tgz", - "integrity": "sha512-g2wW/ermwzGTLlzGdJBDRc4YKz2/yPBjf57w9g5CvhtpZ91Xq95OaWku0oNHYi+XsuV6v8QrCo2oJJR/pCUR8g==", - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/rspack-plugin-virtual-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rspack-plugin-virtual-module/-/rspack-plugin-virtual-module-1.0.1.tgz", @@ -16105,6 +15548,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, "node_modules/sass": { @@ -16319,18 +15763,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", - "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -16338,6 +15770,20 @@ "dev": true, "license": "ISC" }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -16372,85 +15818,6 @@ "tslib": "^2.0.3" } }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", - "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", - "license": "MIT", - "dependencies": { - "debug": "~4.4.1", - "ws": "~8.21.0" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", - "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/socks": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.8.tgz", @@ -16485,6 +15852,7 @@ "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">= 12" @@ -17155,6 +16523,15 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", @@ -17215,60 +16592,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "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 - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/tsc-alias": { "version": "1.8.17", "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.17.tgz", @@ -18448,15 +17771,6 @@ "@esbuild/win32-x64": "0.28.2" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -18693,13 +18007,6 @@ "node": ">= 4" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -18726,15 +18033,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -19301,16 +18599,6 @@ "node": ">=8" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 30842686..9165422d 100644 --- a/package.json +++ b/package.json @@ -136,8 +136,8 @@ "@addon-core/browser": "^0.12.0", "@addon-core/inject-script": "^0.5.1", "@addon-core/storage": "^0.8.3", - "@rsdoctor/rspack-plugin": "^1.5.16", - "@rspack/core": "^1.7.5", + "@rsdoctor/core": "^2.0.1", + "@rspack/core": "~2.2.9", "@rspack/lite-tapable": "^1.1.0", "@rspackjs/plugin-html-tags": "^1.0.0", "@svgr/webpack": "^8.1.0", @@ -171,6 +171,7 @@ "devDependencies": { "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", + "@jest/globals": "^30.3.0", "@microsoft/api-extractor": "^7.53.1", "@release-it/conventional-changelog": "^10.0.4", "@swc/core": "^1.11.7", @@ -199,14 +200,13 @@ "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "jest": "^30.2.0", + "jest": "^30.3.0", "jest-environment-jsdom": "^30.0.0", "memfs": "4.57.2", "prettier": "^3.6.2", "react": "^19.2.8", "react-dom": "^19.2.8", "release-it": "^19.2.3", - "ts-node": "10.9.2", "tsc-alias": "^1.8.17", "tsup": "^8.5.0", "tsx": "^4.19.2", diff --git a/scripts/test-run.mjs b/scripts/test-run.mjs index f417862b..216afbea 100644 --- a/scripts/test-run.mjs +++ b/scripts/test-run.mjs @@ -3,8 +3,13 @@ import {constants} from "node:os"; import path from "node:path"; const root = path.resolve(import.meta.dirname, ".."); +const flags = ["--experimental-vm-modules"]; + // Keep the workaround scoped to the environment where this project reproduces nodejs/node#62393. -const flags = process.platform === "darwin" && process.arch === "arm64" ? ["--no-sparkplug"] : []; +if (process.platform === "darwin" && process.arch === "arm64") { + flags.push("--no-sparkplug"); +} + const child = spawn( process.execPath, [...flags, path.join(root, "node_modules/jest/bin/jest.js"), ...process.argv.slice(2)], diff --git a/src/cli/builders/app/build.test.ts b/src/cli/builders/app/build.test.ts index c16a8a6e..c1b04b28 100644 --- a/src/cli/builders/app/build.test.ts +++ b/src/cli/builders/app/build.test.ts @@ -5,7 +5,7 @@ import path from "path"; import {BuildError, build} from "./build"; -const fixtures = path.join(__dirname, "tests", "fixtures"); +const fixtures = path.join(import.meta.dirname, "tests", "fixtures"); let output: string; beforeEach(async () => { diff --git a/src/cli/builders/locale/LocaleBuilder.test.ts b/src/cli/builders/locale/LocaleBuilder.test.ts index 5f8e662c..45d4ef57 100644 --- a/src/cli/builders/locale/LocaleBuilder.test.ts +++ b/src/cli/builders/locale/LocaleBuilder.test.ts @@ -1,11 +1,14 @@ +import type {LocaleValidator} from "@typing/locale"; +import {jest} from "@jest/globals"; + import LocaleBuilder from "./LocaleBuilder"; import {Language, LocaleCustomKeyForLanguage, LocaleValuesSeparator} from "@typing/locale"; import {Browser} from "@typing/browser"; const makeValidator = () => ({ - isValid: jest.fn(), - validate: jest.fn(), + isValid: jest.fn<() => boolean>(), + validate: jest.fn<(locale: LocaleBuilder) => LocaleValidator>(), }); describe("LocaleBuilder", () => { diff --git a/src/cli/builders/locale/LocaleStructureValidator.test.ts b/src/cli/builders/locale/LocaleStructureValidator.test.ts index 80076f7a..90ab4f6e 100644 --- a/src/cli/builders/locale/LocaleStructureValidator.test.ts +++ b/src/cli/builders/locale/LocaleStructureValidator.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import LocaleBuilder from "./LocaleBuilder"; import LocaleStructureValidator from "./LocaleStructureValidator"; @@ -13,10 +15,10 @@ const makeBuilders = (items: Array<[Language, LocaleData]>): LocaleBuilders => { }; describe("LocaleStructureValidator", () => { - let consoleWarnSpy: jest.SpyInstance; + let consoleWarnSpy: jest.Spied; beforeEach(() => { - consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(); + consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => { diff --git a/src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts b/src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts index 5f2f470d..a82ae180 100644 --- a/src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts +++ b/src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts @@ -1,5 +1,7 @@ /** @jest-environment node */ +import {jest} from "@jest/globals"; + import crypto from "crypto"; import fs from "fs"; import {readFile, writeFile} from "fs/promises"; @@ -35,8 +37,8 @@ const UnrelatedFile = "public/unrelated.txt"; const FullMapPlaceholder = "__ADNBN_BUILD_ASSETS_FULL_MAP_PLACEHOLDER__"; const ChunkLoadingGlobal = "buildAssetsFixtureChunks"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); -const projectRoot = path.resolve(__dirname, "../../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); +const projectRoot = path.resolve(import.meta.dirname, "../../../../.."); interface BuildResult { readonly assets: EntrypointAssetsMap; diff --git a/src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts b/src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts index 6a6aeafb..8bb87192 100644 --- a/src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts +++ b/src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts @@ -8,7 +8,7 @@ import {type Compiler, rspack, type Stats} from "@rspack/core"; import ChunkLoaderPlugin from "./ChunkLoaderPlugin"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); const closeCompiler = (compiler: Compiler): Promise => { return new Promise((resolve, reject) => compiler.close(error => (error ? reject(error) : resolve()))); diff --git a/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts b/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts index 115d38c2..a9bce96f 100644 --- a/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts +++ b/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts @@ -1,5 +1,7 @@ /** @jest-environment node */ +import {jest} from "@jest/globals"; + import path from "path"; import type {Compiler, EntryNormalized} from "@rspack/core"; diff --git a/src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts b/src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts index d8d5c5f6..552337c9 100644 --- a/src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts +++ b/src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import path from "path"; import {rspack, Compiler, type Stats} from "@rspack/core"; import {createFsFromVolume, Volume} from "memfs"; import GenerateJsonPlugin from "./GenerateJsonPlugin"; describe("GenerateJsonPlugin watch updates", () => { - const output = path.resolve(__dirname, "dist"); + const output = path.resolve(import.meta.dirname, "dist"); let compiler: Compiler; const run = () => diff --git a/src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts b/src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts index 778a29ec..a8ddcd0d 100644 --- a/src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts +++ b/src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts @@ -7,7 +7,7 @@ import vm from "vm"; import {rspack, type Compiler, type NormalModule, type Stats} from "@rspack/core"; import {GenerateModulePlugin} from "./index"; -const fixtures = path.join(__dirname, "tests/fixtures"); +const fixtures = path.join(import.meta.dirname, "tests/fixtures"); const modules = { "virtual/value": "export default 3;", "virtual/tools": "export function double(value) { return value * 2; }", diff --git a/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts b/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts index 123a1b4e..ebd93c7e 100644 --- a/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts +++ b/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts @@ -1,5 +1,7 @@ /** @jest-environment node */ +import {jest} from "@jest/globals"; + import fs from "fs"; import os from "os"; import path from "path"; @@ -14,8 +16,8 @@ import IsolatedStylesPlugin, { type IsolatedStylesPluginFiles, } from "./IsolatedStylesPlugin"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); -const projectRoot = path.resolve(__dirname, "../../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); +const projectRoot = path.resolve(import.meta.dirname, "../../../../.."); const RuntimeProperty = "fixtureStyles"; interface BuildResult { @@ -193,6 +195,30 @@ const compile = async (options: CompileOptions = {}): Promise => { } }; +test("rejects an unsupported CSS loader runtime before emitting a broken entrypoint", async () => { + const outputPath = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-isolated-styles-seam-")); + const compiler = createCompiler(outputPath); + + compiler.hooks.thisCompilation.tap("ChangeCssLoaderRuntime", compilation => { + compilation.hooks.runtimeModule.tap({name: "ChangeCssLoaderRuntime", stage: -1}, runtimeModule => { + const source = runtimeModule.source; + + if (source && typeof source.source === "string") { + source.source = source.source.replaceAll("extractCssLoadStylesheet", "changedStylesheet"); + } + }); + }); + + try { + await expect(runCompiler(compiler)).rejects.toThrow( + 'CSS loading runtime seam for isolated entrypoint "shadow" is unavailable' + ); + } finally { + await closeCompiler(compiler); + fs.rmSync(outputPath, {force: true, recursive: true}); + } +}); + test.each(["./shadow.js", "./shadow-async.js"])( "reports initial and async CSS using the entry delivery policy (%s)", async shadowEntry => { @@ -789,7 +815,7 @@ test.each(["contenthash", "chunkhash", "fullhash", "callback"])( }); try { const harness = executeShadowEntrypoint(result); - const resolveAgain = jest.fn(); + const resolveAgain = jest.fn<(file: string) => string>(); harness.runtime.initialize(resolveAgain); expect(resolveAgain).not.toHaveBeenCalled(); const root = createRoot(link => queueMicrotask(() => link.onload?.())); diff --git a/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.ts b/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.ts index 51e82631..808a6c38 100644 --- a/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.ts +++ b/src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.ts @@ -12,6 +12,7 @@ const PluginName = "IsolatedStylesPlugin"; const CssLoadingRuntimeIdentifier = "webpack/runtime/css loading"; const CssRuntimeBoundary = "// object to store loaded CSS chunks"; const CssFilenameExpression = /var href = (.+?)\(chunkId\);/; +const CssStylesheetLoaderDeclaration = /\bvar\s+(extractCssLoadStylesheet)\s*=\s*function\b/; export interface IsolatedStylesPluginOptions extends RuntimePropertyOptions { cssFilename: Filename; @@ -195,8 +196,10 @@ export default class IsolatedStylesPlugin { const boundary = originalSource.indexOf(CssRuntimeBoundary); const filenameExpression = originalSource.match(CssFilenameExpression)?.[1]; + const originalLoader = originalSource.slice(0, boundary); + const stylesheetLoader = originalLoader.match(CssStylesheetLoaderDeclaration)?.[1]; - if (boundary < 0 || !filenameExpression) { + if (boundary < 0 || !filenameExpression || !stylesheetLoader) { throw new Error(`CSS loading runtime seam for isolated entrypoint "${chunk.name}" is unavailable`); } @@ -206,7 +209,8 @@ export default class IsolatedStylesPlugin { .filter(candidate => isIsolatedStylesChunk(compilation, candidate)) .map(candidate => [candidate.id, true]) ), - originalLoader: originalSource.slice(0, boundary), + originalLoader, + stylesheetLoader, cssFilenameExpression: filenameExpression, originalRuntime: originalSource.slice(boundary), publicPath: RuntimeGlobals.publicPath, diff --git a/src/cli/bundler/plugins/isolated-styles/templates.ts b/src/cli/bundler/plugins/isolated-styles/templates.ts index 9371db74..a285e6c1 100644 --- a/src/cli/bundler/plugins/isolated-styles/templates.ts +++ b/src/cli/bundler/plugins/isolated-styles/templates.ts @@ -14,6 +14,7 @@ interface IsolatedStylesRuntimeTemplateOptions extends RuntimePropertyOptions { interface IsolatedStylesCssLoaderTemplateOptions extends RuntimePropertyOptions { readonly isolatedChunks: Readonly>; readonly originalLoader: string; + readonly stylesheetLoader: string; readonly cssFilenameExpression: string; readonly originalRuntime: string; readonly publicPath: string; @@ -34,6 +35,7 @@ export const renderIsolatedStylesCssLoader = (options: IsolatedStylesCssLoaderTe return renderRuntimeTemplate(cssLoaderTemplate, { __ADNBN_ISOLATED_CSS_CHUNKS__: JSON.stringify(options.isolatedChunks), __ADNBN_ORIGINAL_CSS_LOADER__: options.originalLoader, + __ADNBN_CSS_STYLESHEET_LOADER__: options.stylesheetLoader, __ADNBN_CSS_FILENAME_EXPRESSION__: options.cssFilenameExpression, __ADNBN_ORIGINAL_CSS_RUNTIME__: options.originalRuntime, __ADNBN_PUBLIC_PATH__: options.publicPath, diff --git a/src/cli/bundler/plugins/isolated-styles/templates/css-loader.template.js b/src/cli/bundler/plugins/isolated-styles/templates/css-loader.template.js index 4435b57d..7b7d4f20 100644 --- a/src/cli/bundler/plugins/isolated-styles/templates/css-loader.template.js +++ b/src/cli/bundler/plugins/isolated-styles/templates/css-loader.template.js @@ -1,8 +1,11 @@ __ADNBN_ORIGINAL_CSS_LOADER__; -var loadDocumentStylesheet = loadStylesheet; -loadStylesheet = function (chunkId) { - if (!__ADNBN_ISOLATED_CSS_CHUNKS__[chunkId]) return loadDocumentStylesheet(chunkId); +var loadDocumentStylesheet = __ADNBN_CSS_STYLESHEET_LOADER__; +__ADNBN_CSS_STYLESHEET_LOADER__ = function (chunkId, fetchPriority) { + if (!__ADNBN_ISOLATED_CSS_CHUNKS__[chunkId]) { + return loadDocumentStylesheet(chunkId, fetchPriority); + } + var href = __ADNBN_CSS_FILENAME_EXPRESSION__(chunkId); var fullhref = __ADNBN_PUBLIC_PATH__ + href; diff --git a/src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts b/src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts index cda73f72..77d570e3 100644 --- a/src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts +++ b/src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts @@ -120,7 +120,7 @@ describe("ManifestPlugin compilation hooks", () => { .raw({web_accessible_resources: [{resources: ["pages/*"], matches: ["https://example.com/*"]}]}) ); return rspack({ - context: path.join(__dirname, "tests/fixtures"), + context: path.join(import.meta.dirname, "tests/fixtures"), entry: {background: "./entry.js", content: "./entry.js"}, mode: "production", devtool: false, diff --git a/src/cli/bundler/plugins/replace/ReplacePlugin.ts b/src/cli/bundler/plugins/replace/ReplacePlugin.ts index f878f16a..0fd9998c 100644 --- a/src/cli/bundler/plugins/replace/ReplacePlugin.ts +++ b/src/cli/bundler/plugins/replace/ReplacePlugin.ts @@ -1,4 +1,4 @@ -import {type Compiler} from "@rspack/core"; +import {type Compiler, sources} from "@rspack/core"; interface ReplaceOptions { values: Record; @@ -21,22 +21,7 @@ export default class ReplacePlugin { } if (hasChanges) { - const sourceMap = { - version: 3, - sources: [], - names: [], - mappings: "", - file: filename, - }; - - compilation.assets[filename] = { - source: () => content, - size: () => content.length, - buffer: () => Buffer.from(content), - map: () => sourceMap, - sourceAndMap: () => ({source: content, map: sourceMap}), - updateHash: () => {}, - }; + compilation.updateAsset(filename, new sources.RawSource(content)); } } }); diff --git a/src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts b/src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts index 438872cb..8d1ec187 100644 --- a/src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts +++ b/src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts @@ -39,7 +39,7 @@ describe("ResourceAccessPlugin", () => { .raw({web_accessible_resources: [{resources: ["client.js"], matches: ["https://*.example.com/*"]}]}); return rspack({ - context: path.join(__dirname, "tests/fixtures"), + context: path.join(import.meta.dirname, "tests/fixtures"), entry: {background: "./client.js", client: "./client.js"}, mode: "production", devtool: false, diff --git a/src/cli/bundler/styles/prepare-style-sources.test.ts b/src/cli/bundler/styles/prepare-style-sources.test.ts index 265b2826..ad378ef8 100644 --- a/src/cli/bundler/styles/prepare-style-sources.test.ts +++ b/src/cli/bundler/styles/prepare-style-sources.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import fs from "fs"; import path from "path"; import sass from "sass"; @@ -6,7 +8,7 @@ import {pathToFileURL} from "url"; import {prepareStyleSources} from "./prepare-style-sources"; import type {StyleSource} from "./types"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); const readSource = (...parts: string[]): StyleSource => { const filename = path.join(fixtures, ...parts); diff --git a/src/cli/bundler/styles/style-paths.test.ts b/src/cli/bundler/styles/style-paths.test.ts index 3c76a38e..038ab3be 100644 --- a/src/cli/bundler/styles/style-paths.test.ts +++ b/src/cli/bundler/styles/style-paths.test.ts @@ -7,7 +7,7 @@ const source = path.resolve("src"); const from = path.join(source, "apps", "alpha", "content", "content.scss"); const directory = path.dirname(from).split(path.sep).join("/"); const moduleUrl = (request: string): string => pathToFileURL(path.resolve(path.dirname(from), request)).href; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); describe("prepareModuleRequests", () => { test.each(["tokens", "./tokens"])("reports every candidate for %s even after finding a stylesheet", request => { diff --git a/src/cli/bundler/utils/app-filename.test.ts b/src/cli/bundler/utils/app-filename.test.ts index 5aa639a7..a38f67f8 100644 --- a/src/cli/bundler/utils/app-filename.test.ts +++ b/src/cli/bundler/utils/app-filename.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import type {AssetInfo, PathData} from "@rspack/core"; import {appFilenameResolver} from "./app-filename"; diff --git a/src/cli/entrypoint/file/ExpressionFile.test.ts b/src/cli/entrypoint/file/ExpressionFile.test.ts index fddafaef..6be3948f 100644 --- a/src/cli/entrypoint/file/ExpressionFile.test.ts +++ b/src/cli/entrypoint/file/ExpressionFile.test.ts @@ -2,7 +2,7 @@ import path from "path"; import ExpressionFile from "./ExpressionFile"; import {ImportResolver, TsResolver} from "./resolvers"; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "expression"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "expression"); describe("ExpressionFile", () => { describe("Default export descriptions", () => { diff --git a/src/cli/entrypoint/file/OptionFile.test.ts b/src/cli/entrypoint/file/OptionFile.test.ts index 95af349b..73021eb5 100644 --- a/src/cli/entrypoint/file/OptionFile.test.ts +++ b/src/cli/entrypoint/file/OptionFile.test.ts @@ -1,13 +1,15 @@ +import {jest} from "@jest/globals"; + import path from "path"; import OptionFile from "./OptionFile"; -const fixtures = path.resolve(__dirname, "tests/fixtures/static-options"); +const fixtures = path.resolve(import.meta.dirname, "tests/fixtures/static-options"); const read = (name: string, properties: string[]) => OptionFile.make(path.join(fixtures, name + ".ts")) .setDefinition("defineContentScript") .setProperties(properties); -const shapes = path.resolve(__dirname, "tests/fixtures/export-shapes"); +const shapes = path.resolve(import.meta.dirname, "tests/fixtures/export-shapes"); const shape = (name: string, properties: string[], definition: string | string[] = "defineBackground") => OptionFile.make(path.join(shapes, name + ".ts")) .setDefinition(definition) diff --git a/src/cli/entrypoint/finder/AbstractEntrypointFinder.test.ts b/src/cli/entrypoint/finder/AbstractEntrypointFinder.test.ts index 29d9599c..ab5d49b8 100644 --- a/src/cli/entrypoint/finder/AbstractEntrypointFinder.test.ts +++ b/src/cli/entrypoint/finder/AbstractEntrypointFinder.test.ts @@ -63,7 +63,7 @@ const relativeFiles = (root: string, files: Set): string[] => { return Array.from(files, ({file}) => toPosix(path.relative(root, file))).sort(); }; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "discovery"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "discovery"); describe("AbstractEntrypointFinder", () => { test.each([ diff --git a/src/cli/entrypoint/finder/AbstractOverrideFinder.test.ts b/src/cli/entrypoint/finder/AbstractOverrideFinder.test.ts index 81f07008..0ed814c1 100644 --- a/src/cli/entrypoint/finder/AbstractOverrideFinder.test.ts +++ b/src/cli/entrypoint/finder/AbstractOverrideFinder.test.ts @@ -9,7 +9,7 @@ import {toPosix} from "@cli/utils/path"; import type {ReadonlyConfig} from "@typing/config"; import type {EntrypointFile} from "@typing/entrypoint"; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "override"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "override"); const workspace = path.join(fixtures, "workspace"); const makeConfig = (overrides: Partial = {}): ReadonlyConfig => { diff --git a/src/cli/entrypoint/finder/AbstractViewFinder.test.ts b/src/cli/entrypoint/finder/AbstractViewFinder.test.ts index d8c9e2ef..1ea3eacb 100644 --- a/src/cli/entrypoint/finder/AbstractViewFinder.test.ts +++ b/src/cli/entrypoint/finder/AbstractViewFinder.test.ts @@ -4,7 +4,7 @@ import PopupFinder from "./PopupFinder"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "tests", "fixtures", "selection"); +const rootDir = path.resolve(import.meta.dirname, "tests", "fixtures", "selection"); const makeFinder = (multiplePopup: boolean): PopupFinder => { const config = { diff --git a/src/cli/entrypoint/finder/CommandFinder.test.ts b/src/cli/entrypoint/finder/CommandFinder.test.ts index e3533b30..c930a8d3 100644 --- a/src/cli/entrypoint/finder/CommandFinder.test.ts +++ b/src/cli/entrypoint/finder/CommandFinder.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import path from "path"; import CommandFinder from "./CommandFinder"; @@ -7,7 +9,7 @@ import {Browser} from "@typing/browser"; import type {ReadonlyConfig} from "@typing/config"; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "command"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "command"); const makeFinder = (scenario: string, overrides: Partial = {}): CommandFinder => { const config = { diff --git a/src/cli/entrypoint/finder/LocaleFinder.test.ts b/src/cli/entrypoint/finder/LocaleFinder.test.ts index 431ed9aa..da95fbfd 100644 --- a/src/cli/entrypoint/finder/LocaleFinder.test.ts +++ b/src/cli/entrypoint/finder/LocaleFinder.test.ts @@ -7,7 +7,7 @@ import {Command, Mode} from "@typing/app"; import {Browser} from "@typing/browser"; import {Language} from "@typing/locale"; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "locale"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "locale"); class TestLocaleFinder extends LocaleFinder { public languageFromFilename(filename: string): Language { diff --git a/src/cli/entrypoint/finder/OptionsFinder.test.ts b/src/cli/entrypoint/finder/OptionsFinder.test.ts index a6b77a75..0fe915eb 100644 --- a/src/cli/entrypoint/finder/OptionsFinder.test.ts +++ b/src/cli/entrypoint/finder/OptionsFinder.test.ts @@ -59,7 +59,7 @@ const file = (filename: string): EntrypointFile => ({ describe("OptionsFinder", () => { test("discovers options files and index directories without treating options as a separate plural group", () => { - const fixtures = path.resolve(__dirname, "tests", "fixtures", "options", "discovery"); + const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "options", "discovery"); const finder = new TestOptionsFinder(config, new Map()); const files = [...finder.scan(fixtures)].map(({file}) => toPosix(path.relative(fixtures, file))).sort(); @@ -68,7 +68,7 @@ describe("OptionsFinder", () => { }); test("ignores nested candidates when there is no root entrypoint", async () => { - const rootDir = path.resolve(__dirname, "tests", "fixtures", "discovery", "nested"); + const rootDir = path.resolve(import.meta.dirname, "tests", "fixtures", "discovery", "nested"); const finder = new OptionsFinder(makeConfig({rootDir, sharedDir: "."})); await expect(finder.files()).resolves.toEqual(new Set()); diff --git a/src/cli/entrypoint/finder/PluginFinder.test.ts b/src/cli/entrypoint/finder/PluginFinder.test.ts index 33821fa6..d775d397 100644 --- a/src/cli/entrypoint/finder/PluginFinder.test.ts +++ b/src/cli/entrypoint/finder/PluginFinder.test.ts @@ -5,7 +5,7 @@ import PopupFinder from "./PopupFinder"; import {ReadonlyConfig} from "@typing/config"; import {EntrypointFile} from "@typing/entrypoint"; -const fixtures = path.resolve(__dirname, "tests", "fixtures", "precedence"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "precedence"); const pluginFile = (name: string): EntrypointFile => ({ file: path.join(fixtures, "plugins", name, "popup.ts"), diff --git a/src/cli/entrypoint/finder/PopupFinder.test.ts b/src/cli/entrypoint/finder/PopupFinder.test.ts index 23edb6a7..0b4e1388 100644 --- a/src/cli/entrypoint/finder/PopupFinder.test.ts +++ b/src/cli/entrypoint/finder/PopupFinder.test.ts @@ -56,7 +56,7 @@ const createPlugin = ( describe("PopupFinder", () => { test("keeps grouped directory priority when multiple popups are disabled", async () => { - const rootDir = path.resolve(__dirname, "tests", "fixtures", "discovery", "grouped"); + const rootDir = path.resolve(import.meta.dirname, "tests", "fixtures", "discovery", "grouped"); const finder = new PopupFinder({...config, rootDir, sharedDir: ".", multiplePopup: false}); const files = Array.from(await finder.files(), ({file}) => toPosix(path.relative(rootDir, file))); diff --git a/src/cli/entrypoint/finder/ViewPermissionsFinder.test.ts b/src/cli/entrypoint/finder/ViewPermissionsFinder.test.ts index 01c3b654..ed65c5cd 100644 --- a/src/cli/entrypoint/finder/ViewPermissionsFinder.test.ts +++ b/src/cli/entrypoint/finder/ViewPermissionsFinder.test.ts @@ -6,7 +6,7 @@ import PopupFinder from "./PopupFinder"; import type {ReadonlyConfig} from "@typing/config"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); const makeFinder = (rootDir: string, overrides: Partial = {}): PopupFinder => { const config = { diff --git a/src/cli/entrypoint/parser/AbstractParser.test.ts b/src/cli/entrypoint/parser/AbstractParser.test.ts index ebad7c79..b8cc3b63 100644 --- a/src/cli/entrypoint/parser/AbstractParser.test.ts +++ b/src/cli/entrypoint/parser/AbstractParser.test.ts @@ -6,8 +6,8 @@ import AbstractParser from "./AbstractParser"; import type {ReadonlyConfig} from "@typing/config"; import type {EntrypointFile, EntrypointOptions} from "@typing/entrypoint"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "common"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "common"); const config = {rootDir} as ReadonlyConfig; diff --git a/src/cli/entrypoint/parser/BackgroundParser.test.ts b/src/cli/entrypoint/parser/BackgroundParser.test.ts index ffb5f85a..4c2939cd 100644 --- a/src/cli/entrypoint/parser/BackgroundParser.test.ts +++ b/src/cli/entrypoint/parser/BackgroundParser.test.ts @@ -4,8 +4,8 @@ import BackgroundParser from "./BackgroundParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "background"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "background"); const parser = new BackgroundParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/CommandParser.test.ts b/src/cli/entrypoint/parser/CommandParser.test.ts index 1e003fa6..616ab109 100644 --- a/src/cli/entrypoint/parser/CommandParser.test.ts +++ b/src/cli/entrypoint/parser/CommandParser.test.ts @@ -4,8 +4,8 @@ import CommandParser from "./CommandParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "command"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "command"); const parser = new CommandParser({rootDir} as ReadonlyConfig); const parseOptions = (...parts: string[]) => { diff --git a/src/cli/entrypoint/parser/ContentParser.test.ts b/src/cli/entrypoint/parser/ContentParser.test.ts index a22755f7..f7cbbca3 100644 --- a/src/cli/entrypoint/parser/ContentParser.test.ts +++ b/src/cli/entrypoint/parser/ContentParser.test.ts @@ -5,8 +5,8 @@ import ContentParser from "./ContentParser"; import type {ReadonlyConfig} from "@typing/config"; import {ContentScriptShadowMode, ContentScriptWorld} from "@typing/content"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "content"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "content"); const parser = new ContentParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/OffscreenParser.test.ts b/src/cli/entrypoint/parser/OffscreenParser.test.ts index 2d490e13..6198974f 100644 --- a/src/cli/entrypoint/parser/OffscreenParser.test.ts +++ b/src/cli/entrypoint/parser/OffscreenParser.test.ts @@ -4,8 +4,8 @@ import OffscreenParser from "./OffscreenParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "offscreen"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "offscreen"); const parser = new OffscreenParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/OptionsParser.test.ts b/src/cli/entrypoint/parser/OptionsParser.test.ts index 8151505e..90803c96 100644 --- a/src/cli/entrypoint/parser/OptionsParser.test.ts +++ b/src/cli/entrypoint/parser/OptionsParser.test.ts @@ -4,8 +4,8 @@ import OptionsParser from "./OptionsParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "options"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "options"); const parser = new OptionsParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/PageParser.test.ts b/src/cli/entrypoint/parser/PageParser.test.ts index 3642a1ae..69a19c0b 100644 --- a/src/cli/entrypoint/parser/PageParser.test.ts +++ b/src/cli/entrypoint/parser/PageParser.test.ts @@ -4,8 +4,8 @@ import PageParser from "./PageParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "page"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "page"); const parser = new PageParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/PopupParser.test.ts b/src/cli/entrypoint/parser/PopupParser.test.ts index 38166296..8267774b 100644 --- a/src/cli/entrypoint/parser/PopupParser.test.ts +++ b/src/cli/entrypoint/parser/PopupParser.test.ts @@ -4,8 +4,8 @@ import PopupParser from "./PopupParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "popup"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "popup"); const parser = new PopupParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/RelayParser.test.ts b/src/cli/entrypoint/parser/RelayParser.test.ts index fcf9b22b..ff51dd75 100644 --- a/src/cli/entrypoint/parser/RelayParser.test.ts +++ b/src/cli/entrypoint/parser/RelayParser.test.ts @@ -4,8 +4,8 @@ import RelayParser from "./RelayParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "relay"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "relay"); const parser = new RelayParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/SandboxParser.test.ts b/src/cli/entrypoint/parser/SandboxParser.test.ts index d6b80813..05c95123 100644 --- a/src/cli/entrypoint/parser/SandboxParser.test.ts +++ b/src/cli/entrypoint/parser/SandboxParser.test.ts @@ -4,8 +4,8 @@ import SandboxParser from "./SandboxParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "sandbox"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "sandbox"); const parser = new SandboxParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/ServiceParser.test.ts b/src/cli/entrypoint/parser/ServiceParser.test.ts index 69236de5..d72369d0 100644 --- a/src/cli/entrypoint/parser/ServiceParser.test.ts +++ b/src/cli/entrypoint/parser/ServiceParser.test.ts @@ -4,8 +4,8 @@ import ServiceParser from "./ServiceParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "service"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "service"); const parser = new ServiceParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/SidebarParser.test.ts b/src/cli/entrypoint/parser/SidebarParser.test.ts index 05888c2a..0937a8e1 100644 --- a/src/cli/entrypoint/parser/SidebarParser.test.ts +++ b/src/cli/entrypoint/parser/SidebarParser.test.ts @@ -4,8 +4,8 @@ import SidebarParser from "./SidebarParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures", "sidebar"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures", "sidebar"); const parser = new SidebarParser({rootDir} as ReadonlyConfig); diff --git a/src/cli/entrypoint/parser/ViewParser.test.ts b/src/cli/entrypoint/parser/ViewParser.test.ts index 46c72e08..15de6359 100644 --- a/src/cli/entrypoint/parser/ViewParser.test.ts +++ b/src/cli/entrypoint/parser/ViewParser.test.ts @@ -11,8 +11,8 @@ class TestViewParser extends ViewParser { } } -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.join(__dirname, "tests", "fixtures", "view"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.join(import.meta.dirname, "tests", "fixtures", "view"); const parser = new TestViewParser({rootDir} as ReadonlyConfig); const parseOptions = (...parts: string[]) => { const file = path.join(fixtures, ...parts); diff --git a/src/cli/entrypoint/parser/override-parsers.test.ts b/src/cli/entrypoint/parser/override-parsers.test.ts index ca8e21b9..a653e74a 100644 --- a/src/cli/entrypoint/parser/override-parsers.test.ts +++ b/src/cli/entrypoint/parser/override-parsers.test.ts @@ -6,8 +6,8 @@ import NewtabParser from "./NewtabParser"; import type {ReadonlyConfig} from "@typing/config"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); const config = {rootDir} as ReadonlyConfig; diff --git a/src/cli/plugins/background/BackgroundEntry.test.ts b/src/cli/plugins/background/BackgroundEntry.test.ts index 2d735ff0..6a7161c3 100644 --- a/src/cli/plugins/background/BackgroundEntry.test.ts +++ b/src/cli/plugins/background/BackgroundEntry.test.ts @@ -1,7 +1,31 @@ +import path from "node:path"; + import BackgroundEntry from "./BackgroundEntry"; -import TestBackgroundFinder from "./tests/TestBackgroundFinder"; +import {BackgroundFinder} from "@cli/entrypoint"; import type {BackgroundEntrypointOptions} from "@typing/background"; +import type {ReadonlyConfig} from "@typing/config"; +import type {EntrypointFile, EntrypointOptionsFinder} from "@typing/entrypoint"; + +class TestBackgroundFinder extends BackgroundFinder { + public constructor(private readonly entries: BackgroundEntrypointOptions[]) { + super({rootDir: path.resolve(import.meta.dirname)} as ReadonlyConfig); + } + + public plugin(): EntrypointOptionsFinder { + return this; + } + + public async options(): Promise> { + return new Map( + this.entries.map((options, index) => { + const file = path.resolve(import.meta.dirname, `${index}.background.ts`); + + return [{file, import: file}, options]; + }) + ); + } +} interface PersistenceScenario { name: string; diff --git a/src/cli/plugins/background/BackgroundManifest.test.ts b/src/cli/plugins/background/BackgroundManifest.test.ts index 7815479d..1f9a2136 100644 --- a/src/cli/plugins/background/BackgroundManifest.test.ts +++ b/src/cli/plugins/background/BackgroundManifest.test.ts @@ -1,8 +1,32 @@ +import path from "node:path"; + import BackgroundEntry from "./BackgroundEntry"; import BackgroundManifest from "./BackgroundManifest"; -import TestBackgroundFinder from "./tests/TestBackgroundFinder"; +import {BackgroundFinder} from "@cli/entrypoint"; import type {BackgroundEntrypointOptions} from "@typing/background"; +import type {ReadonlyConfig} from "@typing/config"; +import type {EntrypointFile, EntrypointOptionsFinder} from "@typing/entrypoint"; + +class TestBackgroundFinder extends BackgroundFinder { + public constructor(private readonly entries: BackgroundEntrypointOptions[]) { + super({rootDir: path.resolve(import.meta.dirname)} as ReadonlyConfig); + } + + public plugin(): EntrypointOptionsFinder { + return this; + } + + public async options(): Promise> { + return new Map( + this.entries.map((options, index) => { + const file = path.resolve(import.meta.dirname, `${index}.background.ts`); + + return [{file, import: file}, options]; + }) + ); + } +} interface PersistenceScenario { name: string; diff --git a/src/cli/plugins/background/tests/TestBackgroundFinder.ts b/src/cli/plugins/background/tests/TestBackgroundFinder.ts deleted file mode 100644 index c42b4d34..00000000 --- a/src/cli/plugins/background/tests/TestBackgroundFinder.ts +++ /dev/null @@ -1,27 +0,0 @@ -import path from "path"; - -import {BackgroundFinder} from "@cli/entrypoint"; - -import type {BackgroundEntrypointOptions} from "@typing/background"; -import type {ReadonlyConfig} from "@typing/config"; -import type {EntrypointFile, EntrypointOptionsFinder} from "@typing/entrypoint"; - -export default class TestBackgroundFinder extends BackgroundFinder { - public constructor(private readonly entries: BackgroundEntrypointOptions[]) { - super({rootDir: path.resolve(__dirname)} as ReadonlyConfig); - } - - public plugin(): EntrypointOptionsFinder { - return this; - } - - public async options(): Promise> { - return new Map( - this.entries.map((options, index) => { - const file = path.resolve(__dirname, `${index}.background.ts`); - - return [{file, import: file}, options]; - }) - ); - } -} diff --git a/src/cli/plugins/content/ContentManager.test.ts b/src/cli/plugins/content/ContentManager.test.ts index 034c8c9c..b070aa1a 100644 --- a/src/cli/plugins/content/ContentManager.test.ts +++ b/src/cli/plugins/content/ContentManager.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import ContentManager from "./ContentManager"; import type {ContentDriver, ContentItems, ContentProvider} from "./types"; diff --git a/src/cli/plugins/content/RelayDeclaration.test.ts b/src/cli/plugins/content/RelayDeclaration.test.ts index 5e9c035e..796acd9b 100644 --- a/src/cli/plugins/content/RelayDeclaration.test.ts +++ b/src/cli/plugins/content/RelayDeclaration.test.ts @@ -8,8 +8,8 @@ import RelayDeclaration from "./RelayDeclaration"; import type {ReadonlyConfig} from "@typing/config"; describe("Relay declarations", () => { - const projectDir = path.resolve(__dirname, "../../../.."); - const fixtureDir = path.join(__dirname, "tests/fixtures/relay-types"); + const projectDir = path.resolve(import.meta.dirname, "../../../.."); + const fixtureDir = path.join(import.meta.dirname, "tests/fixtures/relay-types"); const consumerFile = path.join(fixtureDir, "consumer.ts"); const generatedFile = path.join(fixtureDir, "generated.d.ts"); let rootDir: string; diff --git a/src/cli/plugins/locale/Locale.test.ts b/src/cli/plugins/locale/Locale.test.ts index 37d0a268..d0f354f6 100644 --- a/src/cli/plugins/locale/Locale.test.ts +++ b/src/cli/plugins/locale/Locale.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import fs from "fs"; import os from "os"; import path from "path"; @@ -10,7 +12,7 @@ import {Language, LocaleMessages} from "@typing/locale"; import {GenerateJsonPluginData} from "@cli/bundler"; import {flattenLocaleMessages, getLocaleFilename} from "@shared/locale"; -const fixtures = path.resolve(__dirname, "tests/fixtures/completion"); +const fixtures = path.resolve(import.meta.dirname, "tests/fixtures/completion"); const makeLocale = (fixture: string, overrides: Partial = {}): Locale => { const plugins = [...(overrides.plugins ?? [])]; @@ -51,11 +53,11 @@ const messages = (json: GenerateJsonPluginData, lang: Language): LocaleMessages json[getLocaleFilename(lang)] as LocaleMessages; describe("locale JSON completion", () => { - let consoleWarnSpy: jest.SpyInstance; + let consoleWarnSpy: jest.Spied; const temporaryDirectories: string[] = []; beforeEach(() => { - consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(); + consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => { diff --git a/src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts b/src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts index 801c826a..fc7e8e0b 100644 --- a/src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts +++ b/src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts @@ -7,7 +7,7 @@ import LocaleDeclaration from "./LocaleDeclaration"; import type {ReadonlyConfig} from "@typing/config"; import type {LocaleStructure} from "@typing/locale"; -const fixtureDirectory = path.join(__dirname, "tests/fixtures"); +const fixtureDirectory = path.join(import.meta.dirname, "tests/fixtures"); const typecheck = (files: string[], declaration?: string, target: "source" | "package" = "package"): string[] => { const config = ts.readConfigFile(path.join(ADNBN_TEST_ROOT, "tsconfig.json"), ts.sys.readFile); diff --git a/src/cli/plugins/locale/index.test.ts b/src/cli/plugins/locale/index.test.ts index f5d2dcd0..17ec7d66 100644 --- a/src/cli/plugins/locale/index.test.ts +++ b/src/cli/plugins/locale/index.test.ts @@ -9,7 +9,7 @@ import {Browser} from "@typing/browser"; import {Language} from "@typing/locale"; import {ReadonlyConfig} from "@typing/config"; -const fixtures = path.resolve(__dirname, "tests/fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests/fixtures"); const localeFixture = (name: string): Partial => { const root = path.join(fixtures, name); diff --git a/src/cli/plugins/meta/Author.test.ts b/src/cli/plugins/meta/Author.test.ts index 5edf81de..82f40c60 100644 --- a/src/cli/plugins/meta/Author.test.ts +++ b/src/cli/plugins/meta/Author.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import Author from "./Author"; describe("Author meta", () => { diff --git a/src/cli/plugins/meta/Homepage.test.ts b/src/cli/plugins/meta/Homepage.test.ts index 4f56fead..0b43a869 100644 --- a/src/cli/plugins/meta/Homepage.test.ts +++ b/src/cli/plugins/meta/Homepage.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import Homepage from "./Homepage"; describe("Homepage meta", () => { diff --git a/src/cli/plugins/meta/Incognito.test.ts b/src/cli/plugins/meta/Incognito.test.ts index 9926b575..88ba5db3 100644 --- a/src/cli/plugins/meta/Incognito.test.ts +++ b/src/cli/plugins/meta/Incognito.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import Incognito from "./Incognito"; import {ManifestIncognito} from "@typing/manifest"; diff --git a/src/cli/plugins/meta/SpecificSettings.test.ts b/src/cli/plugins/meta/SpecificSettings.test.ts index f87a8831..096cf98f 100644 --- a/src/cli/plugins/meta/SpecificSettings.test.ts +++ b/src/cli/plugins/meta/SpecificSettings.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {DataCollectionPermission} from "@typing/browser"; import SpecificSettings from "./SpecificSettings"; diff --git a/src/cli/plugins/override/Override.test.ts b/src/cli/plugins/override/Override.test.ts index 65704be9..2d0e33cf 100644 --- a/src/cli/plugins/override/Override.test.ts +++ b/src/cli/plugins/override/Override.test.ts @@ -8,7 +8,7 @@ import {Browser} from "@typing/browser"; import {EntrypointType} from "@typing/entrypoint"; import type {ReadonlyConfig} from "@typing/config"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); /** Wires the manager the way the plugin does: every override handler serves the workspace files. */ const makeOverride = (fixture: string, config: Partial = {}): Override => { diff --git a/src/cli/plugins/popup/Popup.test.ts b/src/cli/plugins/popup/Popup.test.ts index d7375bd9..3c8732ad 100644 --- a/src/cli/plugins/popup/Popup.test.ts +++ b/src/cli/plugins/popup/Popup.test.ts @@ -5,7 +5,7 @@ import type {ViewItems} from "@cli/entrypoint/finder/AbstractViewFinder"; import type {ReadonlyConfig} from "@typing/config"; import type {PopupEntrypointOptions} from "@typing/popup"; -const rootDir = path.resolve(__dirname); +const rootDir = path.resolve(import.meta.dirname); class TestPopup extends Popup { public constructor(private readonly entries: ViewItems) { diff --git a/src/cli/plugins/typescript/TypescriptConfig.test.ts b/src/cli/plugins/typescript/TypescriptConfig.test.ts index 2cee832c..8fff2224 100644 --- a/src/cli/plugins/typescript/TypescriptConfig.test.ts +++ b/src/cli/plugins/typescript/TypescriptConfig.test.ts @@ -5,7 +5,7 @@ import TypescriptConfig from "./TypescriptConfig"; import type {ReadonlyConfig} from "@typing/config"; describe("TypescriptConfig", () => { - const rootDir = path.resolve(__dirname, "tests", "project"); + const rootDir = path.resolve(import.meta.dirname, "tests", "project"); test.each([ {name: "default directories", srcDir: "src", sharedDir: ".", alias: "src", source: "src", shared: "src"}, diff --git a/src/cli/plugins/typescript/index.ts b/src/cli/plugins/typescript/index.ts index a4504735..f3d8c4d2 100644 --- a/src/cli/plugins/typescript/index.ts +++ b/src/cli/plugins/typescript/index.ts @@ -26,11 +26,19 @@ export default definePlugin(() => { alias: typescript.aliases(), }, module: { + parser: { + javascript: { + typeReexportsPresence: "tolerant", + }, + }, rules: [ { test: /\.tsx?$/, loader: "builtin:swc-loader", options: { + collectTypeScriptInfo: { + typeExports: true, + }, jsc: { parser: { syntax: "typescript", diff --git a/src/cli/plugins/view/View.test.ts b/src/cli/plugins/view/View.test.ts index e4c8d4e9..56621abc 100644 --- a/src/cli/plugins/view/View.test.ts +++ b/src/cli/plugins/view/View.test.ts @@ -8,7 +8,7 @@ import {Mode} from "@typing/app"; import {Browser} from "@typing/browser"; import type {ReadonlyConfig} from "@typing/config"; -const fixtures = path.resolve(__dirname, "tests", "fixtures"); +const fixtures = path.resolve(import.meta.dirname, "tests", "fixtures"); const makeView = (fixture: string) => { const config = { diff --git a/src/cli/resolvers/bundler.ts b/src/cli/resolvers/bundler.ts index 09be6689..ad4d22c2 100644 --- a/src/cli/resolvers/bundler.ts +++ b/src/cli/resolvers/bundler.ts @@ -1,5 +1,5 @@ import {Configuration as RspackConfig, RspackPluginInstance} from "@rspack/core"; -import {RsdoctorRspackPlugin} from "@rsdoctor/rspack-plugin"; +import {RsdoctorRspackPlugin} from "@rsdoctor/core"; import {merge as mergeConfig} from "webpack-merge"; import path from "node:path"; @@ -12,7 +12,7 @@ import ManifestPlugin from "@cli/bundler/plugins/manifest"; import WatchPlugin from "@cli/bundler/plugins/watch"; import {ReadonlyConfig} from "@typing/config"; -import {Command} from "@typing/app"; +import {Command, Mode} from "@typing/app"; const getConfigFromPlugins = async (rspack: RspackConfig, config: ReadonlyConfig): Promise => { let mergedConfig: RspackConfig = {}; @@ -55,15 +55,28 @@ const getConfigForManifest = async (config: ReadonlyConfig): Promise => { + const root = resolveRootPath(config); + let rspack: RspackConfig = { - context: resolveRootPath(config), + context: root, entry: {}, mode: config.mode, + // Extension CSP disallows eval-based source maps. + devtool: config.mode === Mode.Development ? "cheap-module-source-map" : false, cache: false, + module: { + parser: { + javascript: { + exportsPresence: "error", + }, + }, + }, resolve: { + // Preserve project-relative CSS URLs such as url(/images/icon.svg). + roots: [root], // Virtual entry modules may live outside the application. Keep issuer-local // lookup first, then resolve the consumer's packages from its project root. - modules: ["node_modules", path.join(resolveRootPath(config), "node_modules")], + modules: ["node_modules", path.join(root, "node_modules")], }, }; @@ -94,9 +107,7 @@ export default async (config: ReadonlyConfig): Promise => { plugins: [ new RsdoctorRspackPlugin({ supports: { - banner: true, parseBundle: true, - generateTileGraph: true, }, }), ], diff --git a/src/cli/resolvers/config.test.ts b/src/cli/resolvers/config.test.ts index dd4eb1f5..3c3f13e8 100644 --- a/src/cli/resolvers/config.test.ts +++ b/src/cli/resolvers/config.test.ts @@ -1,4 +1,9 @@ -jest.mock("../plugins", () => { +import {Language} from "@typing/locale"; +import {Browser} from "@typing/browser"; +import {Command, Mode, Workspace} from "@typing/app"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("../plugins", () => { const plugin = (name: string) => () => ({name}); return { @@ -31,18 +36,14 @@ jest.mock("../plugins", () => { }; }); -jest.mock("c12", () => ({ +jest.unstable_mockModule("c12", () => ({ loadConfig: jest.fn(), })); -import {loadConfig} from "c12"; +const {loadConfig} = await import("c12"); +const {default: resolveConfig} = await import("./config"); -import resolveConfig from "./config"; - -import {Command, Mode, Workspace} from "@typing/app"; -import {Browser} from "@typing/browser"; import type {OptionalConfig, UserConfig} from "@typing/config"; -import {Language} from "@typing/locale"; const mockedLoadConfig = jest.mocked(loadConfig); diff --git a/src/cli/virtual/virtual.test.ts b/src/cli/virtual/virtual.test.ts index 97627625..5cdea308 100644 --- a/src/cli/virtual/virtual.test.ts +++ b/src/cli/virtual/virtual.test.ts @@ -8,7 +8,7 @@ import {build} from "esbuild"; type Generator = keyof typeof import("./index"); describe("Built virtual modules", () => { - const projectDir = path.resolve(__dirname, "../../.."); + const projectDir = path.resolve(import.meta.dirname, "../../.."); const cases: {generator: Generator; imports: string[]}[] = [ { generator: "virtualBackgroundModule", diff --git a/src/entry/background/resolvers/definition.test.ts b/src/entry/background/resolvers/definition.test.ts index 3dcefee0..7e11add5 100644 --- a/src/entry/background/resolvers/definition.test.ts +++ b/src/entry/background/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {resolveDefinition} from "./definition"; describe("Background definitions", () => { diff --git a/src/entry/command/Builder.test.ts b/src/entry/command/Builder.test.ts index 79d64153..8511a8f9 100644 --- a/src/entry/command/Builder.test.ts +++ b/src/entry/command/Builder.test.ts @@ -1,3 +1,6 @@ +import type {CommandExecute} from "@typing/command"; +import {jest} from "@jest/globals"; + import Builder from "./Builder"; import {defineExecuteActionCommand} from "@main/command"; @@ -12,7 +15,7 @@ describe.each([ harness.runtime.setManifest({name: "Command Addon", version: "1.0.0", manifest_version: manifestVersion}); - const execute = jest.fn(); + const execute = jest.fn(); const definition = defineExecuteActionCommand({defaultKey: "Ctrl+Shift+Y", execute}); const builder = new Builder(definition); const tab = {id: 1} as chrome.tabs.Tab; diff --git a/src/entry/command/resolvers/definition.test.ts b/src/entry/command/resolvers/definition.test.ts index 60b0bb08..d2764747 100644 --- a/src/entry/command/resolvers/definition.test.ts +++ b/src/entry/command/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {resolveDefinition} from "./definition"; describe("Command definitions", () => { diff --git a/src/entry/content/adapters/react/Builder.test.ts b/src/entry/content/adapters/react/Builder.test.ts index 07db2f0a..04934996 100644 --- a/src/entry/content/adapters/react/Builder.test.ts +++ b/src/entry/content/adapters/react/Builder.test.ts @@ -1,18 +1,19 @@ -import {act} from "@testing-library/react"; -import {createElement, Fragment, useEffect, useState} from "react"; -import {createPortal} from "react-dom"; - -import Builder from "./Builder"; -import {resolveDefinition} from "./definition"; -import {createMutationObserverStrategy} from "../../resolvers/watch"; import {ContentScriptEvent, type ContentScriptContext, type ContentScriptProps} from "@typing/content"; +import {jest} from "@jest/globals"; -// Extend the shared random-ID mock for the builders' generated marker attribute. -jest.mock("nanoid", () => ({ +// Keep the builders' generated marker attributes deterministic. +jest.unstable_mockModule("nanoid", () => ({ nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "contentmarker", })); +const {act} = await import("@testing-library/react"); +const {createElement, Fragment, useEffect, useState} = await import("react"); +const {createPortal} = await import("react-dom"); +const {default: Builder} = await import("./Builder"); +const {resolveDefinition} = await import("./definition"); +const {createMutationObserverStrategy} = await import("../../resolvers/watch"); + describe("React Builder", () => { afterEach(() => document.body.replaceChildren()); diff --git a/src/entry/content/adapters/react/Node.test.ts b/src/entry/content/adapters/react/Node.test.ts index c31f8890..9435fbdf 100644 --- a/src/entry/content/adapters/react/Node.test.ts +++ b/src/entry/content/adapters/react/Node.test.ts @@ -1,17 +1,17 @@ -import {createElement} from "react"; -import {createRoot} from "react-dom/client"; +import {ContentScriptShadowMode} from "@typing/content"; +import {jest} from "@jest/globals"; -import ReactNode from "./Node"; +jest.unstable_mockModule("react-dom/client", () => ({ + createRoot: jest.fn(), +})); -import {ContentScriptShadowMode} from "@typing/content"; +const {createElement} = await import("react"); +const {createRoot} = await import("react-dom/client"); +const {default: ReactNode} = await import("./Node"); const render = jest.fn(); const unmount = jest.fn(); -jest.mock("react-dom/client", () => ({ - createRoot: jest.fn(), -})); - describe("ReactNode", () => { beforeEach(() => { jest.clearAllMocks(); @@ -22,7 +22,7 @@ describe("ReactNode", () => { const anchor = document.createElement("article"); const target = document.createElement("section"); document.body.append(anchor); - const node = {anchor, target, mount: jest.fn(), unmount: jest.fn()}; + const node = {anchor, target, mount: jest.fn<() => void>(), unmount: jest.fn<() => void>()}; const renderer = new ReactNode( node, () => createElement("span"), diff --git a/src/entry/content/adapters/react/definition.test.ts b/src/entry/content/adapters/react/definition.test.ts index 0635057f..28afbd4b 100644 --- a/src/entry/content/adapters/react/definition.test.ts +++ b/src/entry/content/adapters/react/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {createElement} from "react"; import {createPortal} from "react-dom"; diff --git a/src/entry/content/adapters/vanilla/Builder.test.ts b/src/entry/content/adapters/vanilla/Builder.test.ts index d62bf7d8..4f98873f 100644 --- a/src/entry/content/adapters/vanilla/Builder.test.ts +++ b/src/entry/content/adapters/vanilla/Builder.test.ts @@ -1,7 +1,8 @@ -import Builder from "./Builder"; -import {waitFor} from "@testing-library/react"; -import {resolveDefinition} from "./definition"; -import {defineContentScript, defineContentScriptAppend} from "@main/content"; +import type { + ContentScriptContainerFactory, + ContentScriptPrepareHandler, + ContentScriptRenderHandler, +} from "@typing/content"; import { ContentScriptAppend, ContentScriptEvent, @@ -10,17 +11,23 @@ import { type ContentScriptTargetProps, type ContentScriptMainFunction, } from "@typing/content"; +import {jest} from "@jest/globals"; -// Extend the shared random-ID mock for the builders' generated marker attribute. -jest.mock("nanoid", () => ({ +// Keep the builders' generated marker attributes deterministic. +jest.unstable_mockModule("nanoid", () => ({ nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "contentmarker", })); -jest.mock("../../lifecycle/nodes/isolated-styles", () => ({ +jest.unstable_mockModule("../../lifecycle/nodes/isolated-styles", () => ({ getContentScriptStylesRuntime: () => ({add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn()}), })); +const {default: Builder} = await import("./Builder"); +const {waitFor} = await import("@testing-library/react"); +const {resolveDefinition} = await import("./definition"); +const {defineContentScript, defineContentScriptAppend} = await import("@main/content"); + describe("Vanilla Builder", () => { afterEach(() => document.body.replaceChildren()); @@ -279,7 +286,7 @@ describe("Vanilla preparation and render props", () => { const calls: ContentScriptProps<{title: string}>[] = []; const events: ContentScriptEvent[] = []; - const container = jest.fn(({data}) => { + const container = jest.fn>(({data}) => { const element = document.createElement("section"); element.title = data.title; @@ -349,7 +356,7 @@ describe("Vanilla preparation and render props", () => { const rendered: ContentScriptProps[] = []; const boundaries: (ShadowRoot | HTMLIFrameElement | undefined)[] = []; - const main = jest.fn, Parameters>(context => { + const main = jest.fn(context => { context.watch((_event, node) => boundaries.push(node.boundary)); }); @@ -444,9 +451,11 @@ describe("Vanilla preparation and render props", () => { const allowed = createAnchor(); const denied = createAnchor(); allowed.className = denied.className = "product"; - const prepare = jest.fn(async ({anchor}) => (anchor === allowed ? {title: "Allowed"} : false)); + const prepare = jest.fn>(async ({anchor}) => + anchor === allowed ? {title: "Allowed"} : false + ); const container = jest.fn(() => document.createElement("section")); - const render = jest.fn(({data}) => data.title); + const render = jest.fn>(({data}) => data.title); const update = Promise.withResolvers<() => void>(); const builder = new Builder( @@ -487,7 +496,7 @@ describe("Vanilla preparation and render props", () => { const response = Promise.withResolvers(); let calls = 0; const container = jest.fn(() => document.createElement("section")); - const render = jest.fn(({data}) => data); + const render = jest.fn>(({data}) => data); const watch = jest.fn(() => () => {}); const builder = new Builder( @@ -736,7 +745,7 @@ test("does not mount a container whose async factory finishes after its anchor w const started = Promise.withResolvers(); const response = Promise.withResolvers(); const render = jest.fn(() => "UI"); - const mount = jest.fn(); + const mount = jest.fn<() => void>(); const builder = new Builder( defineContentScript({ diff --git a/src/entry/content/adapters/vanilla/Node.test.ts b/src/entry/content/adapters/vanilla/Node.test.ts index 7d531c62..428f3b84 100644 --- a/src/entry/content/adapters/vanilla/Node.test.ts +++ b/src/entry/content/adapters/vanilla/Node.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import VanillaNode from "./Node"; import {ContentScriptShadowMode} from "@typing/content"; diff --git a/src/entry/content/adapters/vanilla/definition.test.ts b/src/entry/content/adapters/vanilla/definition.test.ts index a1611701..3a8d03e4 100644 --- a/src/entry/content/adapters/vanilla/definition.test.ts +++ b/src/entry/content/adapters/vanilla/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {resolveDefinition} from "./definition"; describe("Vanilla content definitions", () => { diff --git a/src/entry/content/lifecycle/IsolationSetup.test.ts b/src/entry/content/lifecycle/IsolationSetup.test.ts index 4a7a2e02..aa8d4010 100644 --- a/src/entry/content/lifecycle/IsolationSetup.test.ts +++ b/src/entry/content/lifecycle/IsolationSetup.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import type {ContentScriptBoundaryHandler, ContentScriptTargetProps} from "@typing/content"; import IsolationSetup from "./IsolationSetup"; @@ -8,7 +10,7 @@ test("IsolationSetup reads current props and the replacement container after rem const anchor = document.createElement("article"); const node = new Node(anchor, document.createElement("section")); let data = {title: "First"}; - const boundary = jest.fn(); + const boundary = jest.fn<() => void>(); const target = jest.fn(({document, data}: ContentScriptTargetProps<{title: string}>) => { const element = document.createElement("span"); @@ -74,7 +76,7 @@ test("IsolationSetup uses each supplied document without repeating boundary setu }); test.each(["boundary", "target"] as const)("IsolationSetup rejects a missing container during %s", stage => { - const boundary = jest.fn(); + const boundary = jest.fn<() => void>(); const target = jest.fn(({document}: ContentScriptTargetProps) => document.createElement("div")); const setup = new IsolationSetup({ diff --git a/src/entry/content/lifecycle/MountBuilder.test.ts b/src/entry/content/lifecycle/MountBuilder.test.ts index 669e5a4d..706a200e 100644 --- a/src/entry/content/lifecycle/MountBuilder.test.ts +++ b/src/entry/content/lifecycle/MountBuilder.test.ts @@ -1,19 +1,21 @@ -import MountBuilder from "./MountBuilder"; -import {resolveDefinition} from "../resolvers/definition"; -import {defineContentScript} from "@main/content"; import { ContentScriptEvent, ContentScriptIsolation, ContentScriptMarker, type ContentScriptDefinition, } from "@typing/content"; +import {jest} from "@jest/globals"; -// Extend the shared random-ID mock for the builders' generated marker attribute. -jest.mock("nanoid", () => ({ +// Keep the builders' generated marker attributes deterministic. +jest.unstable_mockModule("nanoid", () => ({ nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "contentmarker", })); +const {default: MountBuilder} = await import("./MountBuilder"); +const {resolveDefinition} = await import("../resolvers/definition"); +const {defineContentScript} = await import("@main/content"); + describe("MountBuilder", () => { afterEach(() => document.body.replaceChildren()); @@ -240,7 +242,7 @@ describe("MountBuilder", () => { }); test("Common runtime runs main without resolving a marker, creating nodes or starting a DOM watcher", async () => { - const main = jest.fn(); + const main = jest.fn<() => void>(); const marker = jest.fn(() => ContentScriptMarker.Weak); const anchor = jest.fn(() => document.body); const container = jest.fn(() => document.createElement("section")); @@ -374,7 +376,7 @@ describe("MountBuilder", () => { test("rejects marker initialization after main has run without starting anchor processing", async () => { const error = new Error("Marker initialization failed"); - const main = jest.fn(); + const main = jest.fn<() => void>(); const anchor = jest.fn(() => document.body); const watch = jest.fn(() => () => {}); @@ -474,8 +476,8 @@ describe("MountBuilder", () => { document.body.append(anchor); const prepare = jest.fn(async () => false as const); const container = jest.fn(() => document.createElement("section")); - const mount = jest.fn(); - const boundary = jest.fn(); + const mount = jest.fn<() => void>(); + const boundary = jest.fn<() => void>(); const builder = new MountBuilder( defineContentScript({ @@ -569,7 +571,7 @@ test.each(["none", "shadow", "iframe"] as const)( ); test.each([undefined, "none", {type: "none"}])("rejects boundary setup without isolation %p", isolation => { - const boundary = jest.fn(); + const boundary = jest.fn<() => void>(); expect(() => new MountBuilder(resolveDefinition({default: {isolation, boundary}}))).toThrow( "boundary requires Shadow DOM or an iframe" @@ -608,7 +610,7 @@ test("rejects asynchronous boundary setup and removes a partially created iframe }); test.each(["shadow", "iframe"] as const)("headless %s never invokes boundary setup", async isolation => { - const boundary = jest.fn(); + const boundary = jest.fn<() => void>(); const builder = isolation === "shadow" ? new MountBuilder( diff --git a/src/entry/content/lifecycle/context/ContainerRegistry.test.ts b/src/entry/content/lifecycle/context/ContainerRegistry.test.ts index 83d3d8cd..e0fd5893 100644 --- a/src/entry/content/lifecycle/context/ContainerRegistry.test.ts +++ b/src/entry/content/lifecycle/context/ContainerRegistry.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import ContainerRegistry from "./ContainerRegistry"; test("owns registered containers and their descendants until released", () => { diff --git a/src/entry/content/lifecycle/context/Context.test.ts b/src/entry/content/lifecycle/context/Context.test.ts index 47499adb..a51cbaec 100644 --- a/src/entry/content/lifecycle/context/Context.test.ts +++ b/src/entry/content/lifecycle/context/Context.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {ManagedContext, EventEmitter} from "./index"; import {MountNode, Node} from "../nodes"; import {ContentScriptEvent} from "@typing/content"; diff --git a/src/entry/content/lifecycle/markers/marker.test.ts b/src/entry/content/lifecycle/markers/marker.test.ts index 2364730f..4c996b40 100644 --- a/src/entry/content/lifecycle/markers/marker.test.ts +++ b/src/entry/content/lifecycle/markers/marker.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {AttributeMarker, WeakMarker} from "./index"; import {ContentScriptMarkerContract, ContentScriptMarkerValue} from "@typing/content"; diff --git a/src/entry/content/lifecycle/nodes/FrameNode.test.ts b/src/entry/content/lifecycle/nodes/FrameNode.test.ts index abd098b0..fa61af6b 100644 --- a/src/entry/content/lifecycle/nodes/FrameNode.test.ts +++ b/src/entry/content/lifecycle/nodes/FrameNode.test.ts @@ -1,3 +1,16 @@ +import type {EventNode as EventNodeType} from "./index"; +import {ContentScriptEvent} from "@typing/content"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/page", () => ({aliases: {panel: "panel.html"}})); + +jest.unstable_mockModule("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); + +const {FrameNode, Node, MountNode, EventNode} = await import("./index"); +const {ContainerRegistry, EventEmitter} = await import("../context"); +const {default: IsolationSetup} = await import("../IsolationSetup"); +const {readContentStyles} = await import("#adnbn/runtime"); + import type { ContentScriptBoundaryHandler, ContentScriptBoundaryProps, @@ -5,22 +18,13 @@ import type { ContentScriptIsolationFrameOptions, } from "@typing/content"; -import {ContentScriptEvent} from "@typing/content"; -import {FrameNode, Node, MountNode, EventNode} from "./index"; -import {ContainerRegistry, EventEmitter} from "../context"; -import IsolationSetup from "../IsolationSetup"; -import {readContentStyles} from "#adnbn/runtime"; - -jest.mock("#adnbn/page", () => ({aliases: {panel: "panel.html"}})); -jest.mock("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); - test("FrameNode explains a disconnected host and allows mounting again once the mounter connects it", () => { const runtime = { initialize: jest.fn(), add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); @@ -104,7 +108,7 @@ test.each([ const emitter = new EventEmitter(); emitter.on(event => events.push(event)); let cancel = true; - let node: EventNode; + let node: EventNodeType; const mounted = new MountNode( new Node(anchor, document.createElement("div")), @@ -155,7 +159,7 @@ test("FrameNode replaces lost targets, unregisters old styles and releases its l add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); @@ -243,7 +247,7 @@ test.each(["throw", "unmount"])("FrameNode cleans up when target creation trigge add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); @@ -303,7 +307,7 @@ const createBoundaryNode = ( add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; jest.mocked(readContentStyles).mockReturnValue(runtime); diff --git a/src/entry/content/lifecycle/nodes/MountNode.test.ts b/src/entry/content/lifecycle/nodes/MountNode.test.ts index 66ee9acc..b495dfd3 100644 --- a/src/entry/content/lifecycle/nodes/MountNode.test.ts +++ b/src/entry/content/lifecycle/nodes/MountNode.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {ContainerRegistry} from "../context"; import {MountNode, Node} from "./index"; diff --git a/src/entry/content/lifecycle/nodes/RenderNode.test.ts b/src/entry/content/lifecycle/nodes/RenderNode.test.ts index 8e20a3bd..a27f08c0 100644 --- a/src/entry/content/lifecycle/nodes/RenderNode.test.ts +++ b/src/entry/content/lifecycle/nodes/RenderNode.test.ts @@ -1,15 +1,18 @@ -import {readContentStyles} from "#adnbn/runtime"; -import {renderIsolatedStylesRuntime} from "@cli/bundler/plugins/isolated-styles/templates"; +import type {EventNode as EventNodeType} from "./index"; import {ContentScriptEvent, type ContentScriptStylesRuntime} from "@typing/content"; -import VanillaNode from "../../adapters/vanilla/Node"; -import ReactNode from "../../adapters/react/Node"; -import {act, waitFor} from "@testing-library/react"; -import {createElement, useEffect, useLayoutEffect} from "react"; -import IsolationSetup from "../IsolationSetup"; -import {ContainerRegistry, EventEmitter} from "../context"; -import {EventNode, FrameNode, MountNode, Node, ShadowNode} from "./index"; +import {jest} from "@jest/globals"; -jest.mock("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); +jest.unstable_mockModule("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); + +const {readContentStyles} = await import("#adnbn/runtime"); +const {renderIsolatedStylesRuntime} = await import("@cli/bundler/plugins/isolated-styles/templates"); +const {default: VanillaNode} = await import("../../adapters/vanilla/Node"); +const {default: ReactNode} = await import("../../adapters/react/Node"); +const {act, waitFor} = await import("@testing-library/react"); +const {createElement, useEffect, useLayoutEffect} = await import("react"); +const {default: IsolationSetup} = await import("../IsolationSetup"); +const {ContainerRegistry, EventEmitter} = await import("../context"); +const {EventNode, FrameNode, MountNode, Node, ShadowNode} = await import("./index"); const createRuntime = (files = ["first.css", "second.css"]): ContentScriptStylesRuntime => { const registry: Record = {}; @@ -97,7 +100,7 @@ const createNode = (mode: "shadow" | "iframe" = "shadow") => { }; }; -const links = (node: EventNode): HTMLLinkElement[] => { +const links = (node: EventNodeType): HTMLLinkElement[] => { const root = node.target!.getRootNode() as ShadowRoot | Document; return Array.from(root.querySelectorAll('link[rel="stylesheet"]')); @@ -281,9 +284,9 @@ test("iframe recovery invalidates pending CSS and waits for the new target witho test("React component, layout effects and effects start only after CSS is ready", async () => { createRuntime(["initial.css"]); const {isolated, emitter, events, props, onError} = createNode(); - const layout = jest.fn(); - const effect = jest.fn(); - const cleanup = jest.fn(); + const layout = jest.fn<() => void>(); + const effect = jest.fn<() => void>(); + const cleanup = jest.fn<() => void>(); const Panel = jest.fn(() => { useLayoutEffect(layout, []); diff --git a/src/entry/content/lifecycle/nodes/ShadowNode.test.ts b/src/entry/content/lifecycle/nodes/ShadowNode.test.ts index 36602de3..cb6e490c 100644 --- a/src/entry/content/lifecycle/nodes/ShadowNode.test.ts +++ b/src/entry/content/lifecycle/nodes/ShadowNode.test.ts @@ -1,8 +1,3 @@ -import {ContainerRegistry} from "../context"; -import {MountNode, Node, ShadowNode} from "./index"; -import IsolationSetup from "../IsolationSetup"; -import {getContentScriptStylesRuntime} from "./isolated-styles"; - import { ContentScriptShadowMode, type ContentScriptShadowOptions, @@ -11,11 +6,18 @@ import { type ContentScriptBoundaryProps, type ContentScriptTargetProps, } from "@typing/content"; +import {jest} from "@jest/globals"; -jest.mock("./isolated-styles", () => ({ +jest.unstable_mockModule("./isolated-styles", () => ({ getContentScriptStylesRuntime: jest.fn(), })); +const {getContentScriptStylesRuntime} = await import("./isolated-styles"); + +const {ContainerRegistry} = await import("../context"); +const {MountNode, Node, ShadowNode} = await import("./index"); +const {default: IsolationSetup} = await import("../IsolationSetup"); + const createNode = ( runtime: ContentScriptStylesRuntime, host = document.createElement("section"), @@ -123,7 +125,7 @@ describe("ShadowNode", () => { add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; const factory = jest.fn(({boundary, document}: ContentScriptTargetProps) => { @@ -167,7 +169,7 @@ describe("ShadowNode", () => { add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; const factory = jest.fn(({document}: ContentScriptTargetProps) => @@ -200,7 +202,7 @@ describe("ShadowNode", () => { add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; const {node} = createNode(runtime, undefined, undefined, ({document}) => { @@ -283,7 +285,7 @@ test("ShadowNode sets up each closed boundary once and cleans subscriptions befo add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; const calls: string[] = []; const cleanup = jest.fn(); @@ -331,7 +333,7 @@ test.each(["setup", "target", "unmount"])("ShadowNode releases boundary resource add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; const cleanup = jest.fn(); diff --git a/src/entry/content/lifecycle/nodes/isolated-styles.test.ts b/src/entry/content/lifecycle/nodes/isolated-styles.test.ts index dbe92b47..7d4132ec 100644 --- a/src/entry/content/lifecycle/nodes/isolated-styles.test.ts +++ b/src/entry/content/lifecycle/nodes/isolated-styles.test.ts @@ -1,11 +1,12 @@ -import {getUrl} from "@addon-core/browser"; -import {readContentStyles} from "#adnbn/runtime"; +import {jest} from "@jest/globals"; -import type {ContentScriptStylesRuntime} from "@typing/content"; +jest.unstable_mockModule("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); -import {getContentScriptStylesRuntime} from "./isolated-styles"; +const {getUrl} = await import("@addon-core/browser"); +const {readContentStyles} = await import("#adnbn/runtime"); +const {getContentScriptStylesRuntime} = await import("./isolated-styles"); -jest.mock("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); +import type {ContentScriptStylesRuntime} from "@typing/content"; beforeEach(() => { jest.resetAllMocks(); @@ -23,7 +24,7 @@ test("initializes and returns the styles runtime supplied by the facade", () => add: jest.fn(), ready: jest.fn(async () => undefined), delete: jest.fn(), - load: jest.fn(), + load: jest.fn<(url: string) => Promise>(), }; jest.mocked(readContentStyles).mockReturnValue(runtime); diff --git a/src/entry/content/resolvers/definition.test.ts b/src/entry/content/resolvers/definition.test.ts index 5545739e..d21ca391 100644 --- a/src/entry/content/resolvers/definition.test.ts +++ b/src/entry/content/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {resolveDefinition} from "./definition"; describe("Common content definitions", () => { diff --git a/src/entry/content/resolvers/target.test.ts b/src/entry/content/resolvers/target.test.ts index 05997974..d085b12f 100644 --- a/src/entry/content/resolvers/target.test.ts +++ b/src/entry/content/resolvers/target.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {createTargetResolver} from "./target"; import type {ContentScriptTarget, ContentScriptTargetProps} from "@typing/content"; diff --git a/src/entry/content/resolvers/watch.test.ts b/src/entry/content/resolvers/watch.test.ts index ef9daa3a..59232281 100644 --- a/src/entry/content/resolvers/watch.test.ts +++ b/src/entry/content/resolvers/watch.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {createAwaitFirstStrategy, createMutationObserverStrategy} from "../../../content"; import {EventEmitter, ManagedContext} from "../lifecycle/context"; import Node from "../lifecycle/nodes/Node"; @@ -17,7 +19,10 @@ describe("content watch strategies", () => { body.remove(); try { - const unwatch = createMutationObserverStrategy()(jest.fn(), new ManagedContext(new EventEmitter())); + const unwatch = createMutationObserverStrategy()( + jest.fn<() => void>(), + new ManagedContext(new EventEmitter()) + ); expect(observe).toHaveBeenCalledWith( document.documentElement, @@ -35,7 +40,7 @@ describe("content watch strategies", () => { jest.useFakeTimers(); const element = document.createElement("span"); document.body.append(element); - const update = jest.fn(); + const update = jest.fn<() => void>(); const context = new ManagedContext(new EventEmitter()); const unwatch = createMutationObserverStrategy({ @@ -73,7 +78,7 @@ describe("content watch strategies", () => { const element = document.createElement("span"); element.textContent = "initial"; document.body.append(element); - const update = jest.fn(); + const update = jest.fn<() => void>(); const context = new ManagedContext(new EventEmitter()); const unwatch = createMutationObserverStrategy()(update, context); @@ -93,7 +98,7 @@ describe("content watch strategies", () => { test("processes continuous mutations without waiting for a quiet period", async () => { jest.useFakeTimers(); - const update = jest.fn(); + const update = jest.fn<() => void>(); const context = new ManagedContext(new EventEmitter()); const unwatch = createMutationObserverStrategy()(update, context); @@ -143,7 +148,7 @@ describe("content watch strategies", () => { const node = new MountNode(new Node(anchor, container), context.containers, mounter); context.add(node); context.mount(); - const update = jest.fn(); + const update = jest.fn<() => void>(); const unwatch = createMutationObserverStrategy({attributes: true, characterData: true})(update, context); try { @@ -198,7 +203,7 @@ describe("content watch strategies", () => { unwatch: () => managedContext.unwatch(), }; - const update = jest.fn(); + const update = jest.fn<() => void>(); const unwatch = createMutationObserverStrategy()(update, context); try { @@ -226,7 +231,7 @@ describe("content watch strategies", () => { ); context.add(node); context.mount(); - const update = jest.fn(); + const update = jest.fn<() => void>(); const unwatch = createMutationObserverStrategy()(update, context); try { diff --git a/src/entry/offscreen/index.test.ts b/src/entry/offscreen/index.test.ts index 09217b23..c3d6c787 100644 --- a/src/entry/offscreen/index.test.ts +++ b/src/entry/offscreen/index.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {waitFor} from "@testing-library/react"; import offscreen, {Builder, resolveDefinition} from "./index"; diff --git a/src/entry/relay/Builder.test.ts b/src/entry/relay/Builder.test.ts index 0cf53fb5..32c1306e 100644 --- a/src/entry/relay/Builder.test.ts +++ b/src/entry/relay/Builder.test.ts @@ -1,12 +1,20 @@ -import Builder from "./Builder"; -import VanillaBuilder from "../content/adapters/vanilla/Builder"; -import MountBuilder from "../content/lifecycle/MountBuilder"; -import RelayManager from "@relay/RelayManager"; +import type {RelayMainHandler} from "@typing/relay"; +import type {TransportType} from "@typing/transport"; import {RelayAllFrames, RelayMethod, type RelayOptions} from "@typing/relay"; -import type {ContentScriptDefinition} from "@typing/content"; +import {jest} from "@jest/globals"; // Keep content marker IDs deterministic for lifecycle assertions. -jest.mock("nanoid", () => ({nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "relaymarker"})); +jest.unstable_mockModule("nanoid", () => ({ + nanoid: jest.fn(() => "mocked-id"), + customAlphabet: () => () => "relaymarker", +})); + +const {default: Builder} = await import("./Builder"); +const {default: VanillaBuilder} = await import("../content/adapters/vanilla/Builder"); +const {default: MountBuilder} = await import("../content/lifecycle/MountBuilder"); +const {default: RelayManager} = await import("@relay/RelayManager"); + +import type {ContentScriptDefinition} from "@typing/content"; describe("Relay Builder", () => { let manager: ReturnType; @@ -34,11 +42,11 @@ describe("Relay Builder", () => { const instance = {ready: true}; const init = jest.fn((_options: RelayOptions) => instance); - const main = jest.fn(); + const main = jest.fn>(); const render = true as const; const watch = () => () => {}; const target = jest.fn(() => "span" as const); - const boundary = jest.fn(); + const boundary = jest.fn<() => void>(); const builder = new Builder( { @@ -134,7 +142,7 @@ describe("Relay Builder", () => { const instance = {ready: true}; - const main = jest.fn(async (relay, context) => { + const main = jest.fn>(async (relay, context) => { events.push("main"); expect(relay).toBe(instance); expect(context.nodes.size).toBe(1); @@ -207,7 +215,7 @@ describe("Relay Builder", () => { const error = new Error("Preparation failed"); const report = jest.spyOn(console, "error").mockImplementation(() => {}); const instance = {ready: true}; - const main = jest.fn(); + const main = jest.fn>(); const watch = jest.fn(() => () => {}); const builder = new Builder( @@ -259,7 +267,7 @@ describe("Relay Builder", () => { expect(manager.has("rebuild")).toBe(false); }); - const main = jest.fn(); + const main = jest.fn>(); const builder = new Builder( { @@ -313,7 +321,7 @@ describe("Relay Builder", () => { ); test("uses the common content builder for iframe navigation", async () => { - const main = jest.fn(); + const main = jest.fn>(); const builder = new Builder( { @@ -347,7 +355,7 @@ describe("Relay Builder", () => { test("Relay destruction invalidates pending preparation and prevents late main", async () => { const prepareStarted = Promise.withResolvers(); const response = Promise.withResolvers(); - const main = jest.fn(); + const main = jest.fn>(); const render = jest.fn(() => "UI"); const init = jest.fn((_options: RelayOptions) => ({ready: true})); diff --git a/src/entry/relay/index.test.ts b/src/entry/relay/index.test.ts index c9198647..25c1ff05 100644 --- a/src/entry/relay/index.test.ts +++ b/src/entry/relay/index.test.ts @@ -1,14 +1,21 @@ -import relay, {resolveDefinition} from "./index"; -import VanillaBuilder from "../content/adapters/vanilla/Builder"; -import RelayManager from "@relay/RelayManager"; +import type {default as VanillaBuilderType} from "../content/adapters/vanilla/Builder"; import {RelayMethod} from "@typing/relay"; -import type {ContentScriptDefinition} from "@typing/content"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("nanoid", () => ({ + nanoid: jest.fn(() => "mocked-id"), + customAlphabet: () => () => "relaymarker", +})); -jest.mock("nanoid", () => ({nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "relaymarker"})); +const {default: relay, resolveDefinition} = await import("./index"); +const {default: VanillaBuilder} = await import("../content/adapters/vanilla/Builder"); +const {default: RelayManager} = await import("@relay/RelayManager"); + +import type {ContentScriptDefinition} from "@typing/content"; describe("Relay startup", () => { let manager: ReturnType; - let content: VanillaBuilder | undefined; + let content: VanillaBuilderType | undefined; class ContentBuilder extends VanillaBuilder { constructor(definition: ContentScriptDefinition) { diff --git a/src/entry/relay/resolvers/definition.test.ts b/src/entry/relay/resolvers/definition.test.ts index 06581d71..8d7de3e8 100644 --- a/src/entry/relay/resolvers/definition.test.ts +++ b/src/entry/relay/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {resolveDefinition} from "./definition"; describe("Relay definitions", () => { diff --git a/src/entry/sandbox/index.test.ts b/src/entry/sandbox/index.test.ts index 345e2111..c09873b3 100644 --- a/src/entry/sandbox/index.test.ts +++ b/src/entry/sandbox/index.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {waitFor} from "@testing-library/react"; import sandbox, {Builder, resolveDefinition} from "./index"; diff --git a/src/entry/transport/resolvers/definition.test.ts b/src/entry/transport/resolvers/definition.test.ts index f7db9792..b497cdd3 100644 --- a/src/entry/transport/resolvers/definition.test.ts +++ b/src/entry/transport/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {mergeDefinition} from "./definition"; describe("Transport definitions", () => { diff --git a/src/entry/view/adapters/react/definition.test.ts b/src/entry/view/adapters/react/definition.test.ts index e1d144be..1925a98f 100644 --- a/src/entry/view/adapters/react/definition.test.ts +++ b/src/entry/view/adapters/react/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {createElement} from "react"; import {createPortal} from "react-dom"; diff --git a/src/entry/view/adapters/vanilla/Builder.test.ts b/src/entry/view/adapters/vanilla/Builder.test.ts index 93ea19f7..121d7960 100644 --- a/src/entry/view/adapters/vanilla/Builder.test.ts +++ b/src/entry/view/adapters/vanilla/Builder.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import Builder from "./Builder"; import type {ViewConfig, ViewRenderHandler} from "@typing/view"; diff --git a/src/entry/view/adapters/vanilla/definition.test.ts b/src/entry/view/adapters/vanilla/definition.test.ts index 7044ec53..bbaa9314 100644 --- a/src/entry/view/adapters/vanilla/definition.test.ts +++ b/src/entry/view/adapters/vanilla/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {createElement} from "react"; import {resolveDefinition} from "./definition"; diff --git a/src/entry/view/resolvers/definition.test.ts b/src/entry/view/resolvers/definition.test.ts index a1eeffd9..c202778c 100644 --- a/src/entry/view/resolvers/definition.test.ts +++ b/src/entry/view/resolvers/definition.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {mergeDefinition} from "./definition"; describe("View definitions", () => { diff --git a/src/frame/ReadyFrame.test.ts b/src/frame/ReadyFrame.test.ts index 62a78b1b..d4a6f59a 100644 --- a/src/frame/ReadyFrame.test.ts +++ b/src/frame/ReadyFrame.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import ReadyFrame, {ReadyFrameParams} from "./ReadyFrame"; const wait = () => new Promise(resolve => setTimeout(resolve)); diff --git a/src/locale/adapters/react/hooks/use-locale-attributes.test.ts b/src/locale/adapters/react/hooks/use-locale-attributes.test.ts index 3914a0db..03a86f88 100644 --- a/src/locale/adapters/react/hooks/use-locale-attributes.test.ts +++ b/src/locale/adapters/react/hooks/use-locale-attributes.test.ts @@ -1,13 +1,16 @@ -import {getBrowserTest} from "@tests/browser-harness/session"; +import {Language, LocaleDir} from "@typing/locale"; +import {jest} from "@jest/globals"; -jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic-locale")); +import type {LocaleReactContract} from "../index"; -import {createElement, StrictMode} from "react"; -import {renderToString} from "react-dom/server"; -import {act, cleanup, renderHook} from "@testing-library/react"; -import {useLocale, useLocaleAttributes, useNativeLocale, type LocaleReactContract} from "../index"; -import {MemoryLocaleStorage} from "@locale/tests/fixtures"; -import {Language, LocaleDir} from "@typing/locale"; +jest.unstable_mockModule("#adnbn/locale", () => import("./tests/fixtures/dynamic-locale")); + +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {createElement, StrictMode} = await import("react"); +const {renderToString} = await import("react-dom/server.node"); +const {act, cleanup, renderHook} = await import("@testing-library/react"); +const {useLocale, useLocaleAttributes, useNativeLocale} = await import("../index"); +const {MemoryLocaleStorage} = await import("@locale/tests/fixtures"); const english: Pick = { lang: Language.English, diff --git a/src/locale/adapters/react/hooks/use-locale.test.ts b/src/locale/adapters/react/hooks/use-locale.test.ts index e2fd8cd4..d5d0a377 100644 --- a/src/locale/adapters/react/hooks/use-locale.test.ts +++ b/src/locale/adapters/react/hooks/use-locale.test.ts @@ -1,14 +1,17 @@ -import {getBrowserTest} from "@tests/browser-harness/session"; +import {Language, LocaleDir, type LocaleStorageDriver} from "@typing/locale"; +import {jest} from "@jest/globals"; -jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic-locale")); +import type {LocaleReactDynamicContract} from "../index"; -import {createElement, StrictMode, useState} from "react"; -import {renderToString} from "react-dom/server"; -import {act, cleanup, fireEvent, render, renderHook, waitFor} from "@testing-library/react"; -import {useLocale, useNativeLocale, type LocaleReactDynamicContract} from "../index"; -import {ObservableLocale} from "@locale/observers"; -import {MemoryLocaleStorage} from "@locale/tests/fixtures"; -import {Language, LocaleDir, type LocaleStorageDriver} from "@typing/locale"; +jest.unstable_mockModule("#adnbn/locale", () => import("./tests/fixtures/dynamic-locale")); + +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {createElement, StrictMode, useState} = await import("react"); +const {renderToString} = await import("react-dom/server.node"); +const {act, cleanup, fireEvent, render, renderHook, waitFor} = await import("@testing-library/react"); +const {useLocale, useNativeLocale} = await import("../index"); +const {ObservableLocale} = await import("@locale/observers"); +const {MemoryLocaleStorage} = await import("@locale/tests/fixtures"); import type {Structure} from "./tests/fixtures/dynamic-locale"; diff --git a/src/locale/adapters/react/hooks/use-native-locale.test.ts b/src/locale/adapters/react/hooks/use-native-locale.test.ts index f80acfb2..7a4fe63a 100644 --- a/src/locale/adapters/react/hooks/use-native-locale.test.ts +++ b/src/locale/adapters/react/hooks/use-native-locale.test.ts @@ -1,17 +1,24 @@ -import {getBrowserTest} from "@tests/browser-harness/session"; +import {Language, LocaleDir} from "@typing/locale"; +import {jest} from "@jest/globals"; + +import type {LocaleReactContract} from "../index"; -jest.mock("#adnbn/locale", () => ({ - keys: Object.keys(require("./tests/fixtures/native-messages.json")).map(key => key.replaceAll("_", ".")), +jest.unstable_mockModule("#adnbn/locale", async () => ({ + default: {}, + lang: "en", + keys: Object.keys((await import("./tests/fixtures/native-messages.json")).default).map(key => + key.replaceAll("_", ".") + ), languages: ["en", "fr"], })); -import {createElement, createRef, StrictMode, useState} from "react"; -import {createPortal} from "react-dom"; -import {cleanup, fireEvent, render, renderHook} from "@testing-library/react"; -import {useNativeLocale, type LocaleReactContract} from "../index"; -import {NativeLocale} from "@locale/providers"; -import {Language, LocaleDir} from "@typing/locale"; -import messages from "./tests/fixtures/native-messages.json"; +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {createElement, createRef, StrictMode, useState} = await import("react"); +const {createPortal} = await import("react-dom"); +const {cleanup, fireEvent, render, renderHook} = await import("@testing-library/react"); +const {useNativeLocale} = await import("../index"); +const {NativeLocale} = await import("@locale/providers"); +const {default: messages} = await import("./tests/fixtures/native-messages.json"); interface Structure { "app.title": {plural: false; substitutions: []}; @@ -74,7 +81,7 @@ describe("useNativeLocale", () => { }); test("renders repeated JSX placeholders without adding DOM wrappers", () => { - const warn = jest.spyOn(console, "error").mockImplementation(); + const warn = jest.spyOn(console, "error").mockImplementation(() => {}); const Greeting = () => { const {t} = useFixtureLocale(); return createElement("p", null, t("app.greeting", {name: createElement("strong", null, "Ada")})); @@ -163,7 +170,7 @@ describe("useNativeLocale", () => { }); test("preserves empty messages and the existing missing-message and substitution diagnostics", () => { - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); const {result} = renderHook(useFixtureLocale); expect(result.current.t("app.empty")).toBe(""); expect(warn).not.toHaveBeenCalled(); diff --git a/src/locale/helpers.test.ts b/src/locale/helpers.test.ts index 4ef06b59..2160de6f 100644 --- a/src/locale/helpers.test.ts +++ b/src/locale/helpers.test.ts @@ -1,7 +1,14 @@ -import {getBrowserTest} from "@tests/browser-harness/session"; -import {resolve} from "./index"; +import {jest} from "@jest/globals"; -jest.mock("#adnbn/locale", () => ({keys: ["locale", "app.title", "app.greeting"], languages: ["en"]})); +jest.unstable_mockModule("#adnbn/locale", () => ({ + default: {}, + lang: "en", + keys: ["locale", "app.title", "app.greeting"], + languages: ["en"], +})); + +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {resolve} = await import("./index"); describe("locale resolve", () => { beforeEach(() => { @@ -29,7 +36,7 @@ describe("locale resolve", () => { }); test("keeps the native missing-key warning and fallback", () => { - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); expect(resolve("@missing")).toBe("missing"); expect(warn).toHaveBeenCalledWith('Locale key "missing" not found in "en" language.'); diff --git a/src/locale/observers/ObservableLocale.test.ts b/src/locale/observers/ObservableLocale.test.ts index 625f51f9..7fedca19 100644 --- a/src/locale/observers/ObservableLocale.test.ts +++ b/src/locale/observers/ObservableLocale.test.ts @@ -1,11 +1,13 @@ -jest.mock("#adnbn/locale", () => require("@locale/providers/tests/fixtures/dynamic")); - -import {Storage} from "@addon-core/storage"; -import {PackageName} from "@typing/app"; -import {getBrowserTest} from "@tests/browser-harness/session"; -import {ObservableLocale} from "./index"; -import {MemoryLocaleStorage} from "../tests/fixtures"; import {Language, type LocaleDynamicProvider, type LocaleStorageDriver} from "@typing/locale"; +import {PackageName} from "@typing/app"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/locale", () => import("@locale/providers/tests/fixtures/dynamic")); + +const {Storage} = await import("@addon-core/storage"); +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {ObservableLocale} = await import("./index"); +const {MemoryLocaleStorage} = await import("../tests/fixtures"); import type {Structure} from "@locale/providers/tests/fixtures/dynamic"; @@ -261,7 +263,7 @@ test("validates selections and saved languages without publishing an invalid sna const storage = new MemoryLocaleStorage(Language.German); const locale = new ObservableLocale(storage); const snapshot = locale.snapshot(); - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); await locale.sync(); expect(warn).toHaveBeenCalledWith('Incorrect language code in storage - "de"'); await expect(locale.change(Language.German)).rejects.toThrow('Language "de" is not available'); @@ -288,7 +290,7 @@ test("an invalid choice neither writes nor invalidates a pending storage read", test("snapshots treat missing prototype keys as missing messages", () => { const locale = new ObservableLocale>(false); - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); expect(locale.snapshot().get("toString")).toBe("toString"); expect(warn).toHaveBeenCalledWith('Locale key "toString" not found in "en" language.'); expect(locale.snapshot().get("__proto__")).toBe("Ordinary message"); @@ -298,7 +300,7 @@ test("reports background synchronization errors and still releases the listener" const storage = new MemoryLocaleStorage(); const failure = new Error("read failed"); jest.spyOn(storage, "get").mockRejectedValue(failure); - const report = jest.spyOn(console, "error").mockImplementation(); + const report = jest.spyOn(console, "error").mockImplementation(() => {}); const locale = new ObservableLocale(storage); const stop = locale.subscribe(jest.fn()); await Promise.resolve(); diff --git a/src/locale/providers/CustomLocale.test.ts b/src/locale/providers/CustomLocale.test.ts index 237731c2..0f5bcf23 100644 --- a/src/locale/providers/CustomLocale.test.ts +++ b/src/locale/providers/CustomLocale.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import CustomLocale from "./CustomLocale"; import {Language, LocaleValuesSeparator} from "@typing/locale"; @@ -11,11 +13,11 @@ describe("CustomLocale", () => { }; const locale = new CustomLocale(Language.English, mockMessages); - let consoleWarnSpy: jest.SpyInstance; + let consoleWarnSpy: jest.Spied; beforeEach(() => { locale.setLang(Language.English).setData(mockMessages); - consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(); + consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => { diff --git a/src/locale/providers/DynamicLocale.test.ts b/src/locale/providers/DynamicLocale.test.ts index caa4839e..35c11f92 100644 --- a/src/locale/providers/DynamicLocale.test.ts +++ b/src/locale/providers/DynamicLocale.test.ts @@ -1,9 +1,11 @@ -jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic")); - -import {getBrowserTest} from "@tests/browser-harness/session"; -import DynamicLocale from "./DynamicLocale"; -import {MemoryLocaleStorage} from "../tests/fixtures"; import {Language} from "@typing/locale"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/locale", () => import("./tests/fixtures/dynamic")); + +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {default: DynamicLocale} = await import("./DynamicLocale"); +const {MemoryLocaleStorage} = await import("../tests/fixtures"); import type {Structure} from "./tests/fixtures/dynamic"; @@ -184,7 +186,7 @@ test("rejects write errors without rolling back a newer selection", async () => test("ignores prototype properties and rejects languages absent from the application catalogue", async () => { const locale = new DynamicLocale>(false); - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); expect(locale.trans("toString")).toBe("toString"); expect(warn).toHaveBeenCalledWith('Locale key "toString" not found in "en" language.'); await expect(locale.change(Language.German)).rejects.toThrow('Language "de" is not available'); @@ -205,7 +207,7 @@ test("restores the saved language only through explicit sync, without writing it test.each([undefined, "de"])("keeps the current language for invalid or absent storage: %s", async value => { await seed(value); - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); const locale = new DynamicLocale(); await expect(locale.sync()).resolves.toBe(Language.English); expect(storageHarness().local.set.calls).toHaveLength(0); @@ -261,7 +263,7 @@ test("retains the single-listener contract and permits subscribing again after u test("reports invalid external changes without changing state or notifying the handler", async () => { const locale = new DynamicLocale(); - const error = jest.spyOn(console, "error").mockImplementation(); + const error = jest.spyOn(console, "error").mockImplementation(() => {}); const handler = jest.fn(); const stop = locale.watch(handler); diff --git a/src/locale/providers/NativeLocale.test.ts b/src/locale/providers/NativeLocale.test.ts index 4eec796f..43cf0a7a 100644 --- a/src/locale/providers/NativeLocale.test.ts +++ b/src/locale/providers/NativeLocale.test.ts @@ -1,7 +1,15 @@ -jest.mock("#adnbn/locale", () => ({keys: ["demo.empty", "demo.title"], languages: ["en", "fr", "ru"]})); +import type {default as NativeLocaleType} from "./NativeLocale"; +import {jest} from "@jest/globals"; -import {getBrowserTest} from "@tests/browser-harness/session"; -import NativeLocale from "./NativeLocale"; +jest.unstable_mockModule("#adnbn/locale", () => ({ + default: {}, + lang: "en", + keys: ["demo.empty", "demo.title"], + languages: ["en", "fr", "ru"], +})); + +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {default: NativeLocale} = await import("./NativeLocale"); interface Structure { "demo.empty": {plural: false; substitutions: []}; @@ -9,17 +17,17 @@ interface Structure { } describe("NativeLocale", () => { - let locale: NativeLocale; - let warn: jest.SpyInstance; - let error: jest.SpyInstance; + let locale: NativeLocaleType; + let warn: jest.Spied; + let error: jest.Spied; beforeEach(() => { getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(key => key === "locale" ? "ru" : "" ); - warn = jest.spyOn(console, "warn").mockImplementation(); - error = jest.spyOn(console, "error").mockImplementation(); + warn = jest.spyOn(console, "warn").mockImplementation(() => {}); + error = jest.spyOn(console, "error").mockImplementation(() => {}); locale = new NativeLocale(); }); diff --git a/src/locale/storage/LocaleStorage.test.ts b/src/locale/storage/LocaleStorage.test.ts index 5f27c20d..63a5e8ef 100644 --- a/src/locale/storage/LocaleStorage.test.ts +++ b/src/locale/storage/LocaleStorage.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {getBrowserTest} from "@tests/browser-harness/session"; import {LocaleStorage} from "./index"; import {Language} from "@typing/locale"; @@ -38,7 +40,7 @@ test.each([Language.English, Language.EnglishGreatBritain, Language.German])( test.each([null, "", "unknown", "__proto__", "en-GB", 1, {}, ["en"]])( "ignores invalid stored values in reads and notifications: %p", async invalid => { - const warn = jest.spyOn(console, "warn").mockImplementation(); + const warn = jest.spyOn(console, "warn").mockImplementation(() => {}); const storage = new LocaleStorage(); const handler = jest.fn(); const stop = storage.watch(handler); diff --git a/src/main/popup.test.ts b/src/main/popup.test.ts index b8955aa0..65ee1026 100644 --- a/src/main/popup.test.ts +++ b/src/main/popup.test.ts @@ -1,4 +1,6 @@ -jest.mock("#adnbn/popup", () => ({ +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/popup", () => ({ aliases: { plain: {path: "plain.html", tooltip: "Open account", icon: "account"}, localized: {path: "account.html", tooltip: "@popup.account"}, @@ -10,11 +12,11 @@ jest.mock("#adnbn/popup", () => ({ }, })); -jest.mock("#adnbn/icon", () => ({groups: {account: {16: "account-16.png"}}})); +jest.unstable_mockModule("#adnbn/icon", () => ({groups: {account: {16: "account-16.png"}}})); -import {createTabFixture} from "@addon-core/browser/testing"; -import {getBrowserTest} from "@tests/browser-harness/session"; -import {changePopup, getPopups} from "./popup"; +const {createTabFixture} = await import("@addon-core/browser/testing"); +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {changePopup, getPopups} = await import("./popup"); describe("changePopup", () => { beforeEach(() => { diff --git a/src/main/relay.test.ts b/src/main/relay.test.ts index 48f7c11c..2bcd7299 100644 --- a/src/main/relay.test.ts +++ b/src/main/relay.test.ts @@ -1,9 +1,11 @@ -jest.mock("#adnbn/relay", () => ({options: {}})); - -import {getRelay} from "./relay"; -import {options} from "#adnbn/relay"; import {RelayMethod} from "@typing/relay"; -import {RelayPermissionGlobalKey} from "@relay/RelayPermission"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/relay", () => ({options: {}})); + +const {getRelay} = await import("./relay"); +const {options} = await import("#adnbn/relay"); +const {RelayPermissionGlobalKey} = await import("@relay/RelayPermission"); // Generated RelayRegistry typing is covered by RelayDeclaration tests; exercise arbitrary runtime maps here. const getFixtureRelay = getRelay as (name: string, tabId: number) => unknown; diff --git a/src/main/sidebar.test.ts b/src/main/sidebar.test.ts index 35d8f250..63750653 100644 --- a/src/main/sidebar.test.ts +++ b/src/main/sidebar.test.ts @@ -1,4 +1,6 @@ -jest.mock("#adnbn/sidebar", () => ({ +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/sidebar", () => ({ aliases: { plain: {path: "plain.html", label: "Open account", icon: "account"}, localized: {path: "account.html", label: "@sidebar.account", icon: "account"}, @@ -10,11 +12,11 @@ jest.mock("#adnbn/sidebar", () => ({ }, })); -jest.mock("#adnbn/icon", () => ({groups: {account: {16: "account-16.png"}}})); +jest.unstable_mockModule("#adnbn/icon", () => ({groups: {account: {16: "account-16.png"}}})); -import {createTabFixture} from "@addon-core/browser/testing"; -import {getBrowserTest} from "@tests/browser-harness/session"; -import {changeSidebar, getSidebars} from "./sidebar"; +const {createTabFixture} = await import("@addon-core/browser/testing"); +const {getBrowserTest} = await import("@tests/browser-harness/session"); +const {changeSidebar, getSidebars} = await import("./sidebar"); describe("changeSidebar in Firefox", () => { beforeEach(() => { diff --git a/src/message/adapters/react/use-message-handler.test.ts b/src/message/adapters/react/use-message-handler.test.ts index 0b1efda2..a5ce8880 100644 --- a/src/message/adapters/react/use-message-handler.test.ts +++ b/src/message/adapters/react/use-message-handler.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import React from "react"; import {act, cleanup, render, renderHook} from "@testing-library/react"; import type {BrowserContextMessaging} from "@addon-core/browser/testing"; @@ -18,8 +20,8 @@ type SuspendedHandlerProps = {value: string}; type BooleanMessages = {isEnabled: () => boolean}; let caller: BrowserContextMessaging; -let addListener: jest.SpyInstance; -let removeListener: jest.SpyInstance; +let addListener: jest.Spied; +let removeListener: jest.Spied; beforeEach(() => { const session = getBrowserTest(); diff --git a/src/offscreen/OffscreenBridge.test.ts b/src/offscreen/OffscreenBridge.test.ts index ee184795..4fd8f8a4 100644 --- a/src/offscreen/OffscreenBridge.test.ts +++ b/src/offscreen/OffscreenBridge.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {getBrowserTest} from "@tests/browser-harness/session"; import {OffscreenBackground, ProxyOffscreen, RegisterOffscreen} from "./index"; import type {RpcAsyncProxy} from "@typing/rpc"; diff --git a/src/offscreen/providers/ProxyOffscreen.test.ts b/src/offscreen/providers/ProxyOffscreen.test.ts index 0e175864..addb3d0a 100644 --- a/src/offscreen/providers/ProxyOffscreen.test.ts +++ b/src/offscreen/providers/ProxyOffscreen.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {closeOffscreen} from "@addon-core/browser"; import {getBrowserTest} from "@tests/browser-harness/session"; import MockLockManager from "@tests/offscreen/MockLockManager"; diff --git a/src/relay/tests/runtime.ts b/src/relay/tests/runtime.ts index ffca838a..4b777c43 100644 --- a/src/relay/tests/runtime.ts +++ b/src/relay/tests/runtime.ts @@ -5,8 +5,8 @@ import {createTabFixture} from "@addon-core/browser/testing"; import {getBrowserTest} from "@tests/browser-harness/session"; const source = buildSync({ - entryPoints: [path.resolve(__dirname, "fixtures/runtime.ts")], - tsconfig: path.resolve(__dirname, "../../../tsconfig.json"), + entryPoints: [path.resolve(import.meta.dirname, "fixtures/runtime.ts")], + tsconfig: path.resolve(import.meta.dirname, "../../../tsconfig.json"), bundle: true, write: false, format: "iife", diff --git a/src/sandbox/SandboxMessage.test.ts b/src/sandbox/SandboxMessage.test.ts index 424a912c..e6538d6c 100644 --- a/src/sandbox/SandboxMessage.test.ts +++ b/src/sandbox/SandboxMessage.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {getBrowserTest} from "@tests/browser-harness/session"; import SandboxMessage from "./SandboxMessage"; diff --git a/src/sandbox/providers/Sandbox.test.ts b/src/sandbox/providers/Sandbox.test.ts index 4072b208..8cbbdb76 100644 --- a/src/sandbox/providers/Sandbox.test.ts +++ b/src/sandbox/providers/Sandbox.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import SandboxFrame from "../SandboxFrame"; import SandboxMessage from "../SandboxMessage"; import RegisterSandbox from "./RegisterSandbox"; @@ -55,7 +57,7 @@ describe("Sandbox host runtime", () => { const send = message.send({path: "parse", args: ["

Hi

"]}); const frame = document.querySelector("iframe") as HTMLIFrameElement; - const postMessage = jest.fn(); + const postMessage = jest.fn(); Object.defineProperty(frame.contentWindow, "postMessage", {configurable: true, value: postMessage}); ready("parser", frame); diff --git a/src/service/providers/ProxyService.test.ts b/src/service/providers/ProxyService.test.ts index 51fb3943..fcddedff 100644 --- a/src/service/providers/ProxyService.test.ts +++ b/src/service/providers/ProxyService.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import { createManifestFixture, createTabFixture, diff --git a/tests/README.md b/tests/README.md index 7e5d40e2..7df52d1f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,10 @@ # Running tests -Run commands from the repository root. Node.js 22 and 24 are supported. +Run commands from the repository root. Node.js 22.12 or newer in the 22 line, and Node.js 24, are supported. -Use the npm test commands below, or `npm run test:run --` to pass arguments directly to Jest. All Jest invocations from these commands, pre-push and inventory checks use `scripts/test-run.mjs`. On macOS arm64 this wrapper launches Jest with `--no-sparkplug`; Linux and Windows keep Node's default JIT settings. +Use the npm test commands below, or `npm run test:run --` to pass arguments directly to Jest. All Jest invocations from these commands, pre-push and inventory checks use `scripts/test-run.mjs`. The wrapper enables native ESM with `--experimental-vm-modules`. On macOS arm64 it also adds `--no-sparkplug`; Linux and Windows keep Node's default JIT settings. + +Tests import `jest` from `@jest/globals` and use `import.meta.dirname` or `import.meta.filename` for filesystem paths. Register ESM fixtures with `jest.unstable_mockModule` before dynamically importing their consumers; static imports execute before mock registration. Raw templates are transformed into ESM default exports. Compiler tests use the real Rspack package and loaders. The style build tests resolve the framework's private URL loader to its built ESM file in `dist`: Rspack loads it through native Node, so these tests do not depend on Node's optional TypeScript stripping. Run the package build before these tests, as required for the `build` project. The observed Node 24 crashes contain `Builtins_BaselineOutOfLinePrologue` and `ClearStaleLeftTrimmedPointerVisitor`, matching the Sparkplug report in [nodejs/node#62393](https://github.com/nodejs/node/issues/62393). The earlier `--no-maglev` workaround did not prevent the crash. Successful repetitions measure observed stability; they do not prove a rare native fault impossible. Keep the workaround scoped to the reproduced environment and reassess it when the upstream fix is verified. Jest workers inherit the flag through `execArgv`; CLI fixture child processes and real browsers keep their normal settings. Tests are never automatically retried or skipped to hide a native crash. @@ -48,7 +50,7 @@ The [override scheduling follow-up](benchmarks/2026-09-25-override.md) compares ## Groups and workers -`jest.config.ts` defines six projects: `unit-node`, `unit-dom`, `build`, `types`, `chrome` and `firefox`. Put DOM tests in the `unit-dom` patterns. Tests that execute the built CLI, consume package declarations or compile imports from the built package belong to `build` or `types`. Keep unit tests runnable with no `dist` directory. Each browser file contains scenarios for one browser only. +`jest.config.mjs` defines six projects: `unit-node`, `unit-dom`, `build`, `types`, `chrome` and `firefox`. The configuration is native JavaScript with JSDoc types so loading it does not require Node's TypeScript support. Put DOM tests in the `unit-dom` patterns. Tests that execute the built CLI, consume package declarations or compile imports from the built package belong to `build` or `types`. Keep unit tests runnable with no `dist` directory. Each browser file contains scenarios for one browser only. Relay declaration tests keep the source/package × POSIX/Windows matrix for the generated registry, selectors and exports. Constructor, prepared-data and bootstrap contracts live in `integration/types/relay.integration.test.ts`, checked against diff --git a/tests/browser-harness-dom.test.ts b/tests/browser-harness-dom.test.ts index b5aa93e6..85e8e93e 100644 --- a/tests/browser-harness-dom.test.ts +++ b/tests/browser-harness-dom.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {getManifest} from "@addon-core/browser"; import {getBrowserTest} from "./browser-harness/session"; diff --git a/tests/browser-harness.test.ts b/tests/browser-harness.test.ts index f9c4d491..52d1033e 100644 --- a/tests/browser-harness.test.ts +++ b/tests/browser-harness.test.ts @@ -1,24 +1,24 @@ -jest.mock("#adnbn/locale", () => require("../src/locale/providers/tests/fixtures/dynamic")); - -import {executeScript, getBrowserInfo, getManifest, sendMessage, sendTabMessage} from "@addon-core/browser"; -import {createTabFixture} from "@addon-core/browser/testing"; -import {Storage} from "@addon-core/storage"; - -import NativeLocale from "@locale/providers/NativeLocale"; -import {ObservableLocale} from "@locale/observers"; -import {MemoryLocaleStorage} from "@locale/tests/fixtures"; -import OffscreenBridge from "@offscreen/OffscreenBridge"; -import SandboxMessage from "@sandbox/SandboxMessage"; -import {getApp, getBrowser, getManifestVersion, isBrowser} from "@main/env"; -import {Browser} from "@typing/browser"; -import MessageManager from "@message/MessageManager"; -import Message from "@message/providers/Message"; -import RelayManager from "@relay/RelayManager"; -import {MessageGlobalKey} from "@typing/message"; import {RelayGlobalKey} from "@typing/relay"; - -import BrowserTestSession from "./browser-harness/BrowserTestSession"; -import {getBrowserTest, stopBrowserTest} from "./browser-harness/session"; +import {MessageGlobalKey} from "@typing/message"; +import {Browser} from "@typing/browser"; +import {jest} from "@jest/globals"; + +jest.unstable_mockModule("#adnbn/locale", () => import("../src/locale/providers/tests/fixtures/dynamic")); + +const {executeScript, getBrowserInfo, getManifest, sendMessage, sendTabMessage} = await import("@addon-core/browser"); +const {createTabFixture} = await import("@addon-core/browser/testing"); +const {Storage} = await import("@addon-core/storage"); +const {default: NativeLocale} = await import("@locale/providers/NativeLocale"); +const {ObservableLocale} = await import("@locale/observers"); +const {MemoryLocaleStorage} = await import("@locale/tests/fixtures"); +const {default: OffscreenBridge} = await import("@offscreen/OffscreenBridge"); +const {default: SandboxMessage} = await import("@sandbox/SandboxMessage"); +const {getApp, getBrowser, getManifestVersion, isBrowser} = await import("@main/env"); +const {default: MessageManager} = await import("@message/MessageManager"); +const {default: Message} = await import("@message/providers/Message"); +const {default: RelayManager} = await import("@relay/RelayManager"); +const {default: BrowserTestSession} = await import("./browser-harness/BrowserTestSession"); +const {getBrowserTest, stopBrowserTest} = await import("./browser-harness/session"); const hostSetTimeout = setTimeout; const initialBrowser = process.env.BROWSER; @@ -119,7 +119,7 @@ test("disposes pending guest execution, listeners, framework caches and restores test("continues teardown after a consumer cleanup fails and rejects pending message channels", async () => { const session = getBrowserTest(); const receiver = session.harness.contexts.create({kind: "background"}); - const cleaned = jest.fn(); + const cleaned = jest.fn<() => void>(); receiver.onMessage.on(() => true); diff --git a/tests/browser-harness/framework-state.ts b/tests/browser-harness/framework-state.ts index 3b9c0380..9f3eee14 100644 --- a/tests/browser-harness/framework-state.ts +++ b/tests/browser-harness/framework-state.ts @@ -29,37 +29,30 @@ export const frameworkStateResetters = { // Browser event subscriptions are removed by harness.reset(). Reflect.deleteProperty(globalThis, RelayPermissionGlobalKey); }, - NativeLocale: () => { - const {default: NativeLocale} = jest.requireActual( - "@locale/providers/NativeLocale" - ); + NativeLocale: async () => { + const {default: NativeLocale} = await import("@locale/providers/NativeLocale"); Reflect.deleteProperty(NativeLocale, "instance"); }, - Message: () => { - const {default: Message} = - jest.requireActual("@message/providers/Message"); + Message: async () => { + const {default: Message} = await import("@message/providers/Message"); Reflect.deleteProperty(Message, "instance"); }, - ObservableLocale: () => { - const {default: ObservableLocale} = jest.requireActual( - "@locale/observers/ObservableLocale" - ); + ObservableLocale: async () => { + const {default: ObservableLocale} = await import("@locale/observers/ObservableLocale"); Reflect.deleteProperty(ObservableLocale, "defaultInstance"); Reflect.deleteProperty(ObservableLocale, "memoryInstance"); Reflect.set(ObservableLocale, "drivers", new WeakMap()); }, - OffscreenBridge: () => { - const {default: OffscreenBridge} = - jest.requireActual("@offscreen/OffscreenBridge"); + OffscreenBridge: async () => { + const {default: OffscreenBridge} = await import("@offscreen/OffscreenBridge"); Reflect.deleteProperty(OffscreenBridge, "instance"); }, - SandboxMessage: () => { - const {default: SandboxMessage} = - jest.requireActual("@sandbox/SandboxMessage"); + SandboxMessage: async () => { + const {default: SandboxMessage} = await import("@sandbox/SandboxMessage"); const hosts = Reflect.get(SandboxMessage, "hosts") as Map>; const errors: unknown[] = []; @@ -79,12 +72,12 @@ export const frameworkStateResetters = { }, }; -export function resetFrameworkState(): void { +export async function resetFrameworkState(): Promise { const errors: unknown[] = []; for (const reset of Object.values(frameworkStateResetters)) { try { - reset(); + await reset(); } catch (error) { errors.push(error); } diff --git a/tests/integration/browser/content/css-fonts-utils.ts b/tests/integration/browser/content/css-fonts-utils.ts index 9918ece7..5b5d0096 100644 --- a/tests/integration/browser/content/css-fonts-utils.ts +++ b/tests/integration/browser/content/css-fonts-utils.ts @@ -15,8 +15,8 @@ interface CssFontsProbeState { } export const runCssFontsProbe = async (browser: "chrome" | "firefox", manifestVersion: 2 | 3) => { - const root = path.resolve(__dirname, "../../../.."); - const fixture = path.join(__dirname, "css-fonts"); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = path.join(import.meta.dirname, "css-fonts"); const cache = path.join(root, ".cache/integration"); await mkdir(cache, {recursive: true}); const extension = await mkdtemp(path.join(cache, "css-fonts-")); diff --git a/tests/integration/browser/content/css-fonts.firefox.integration.test.ts b/tests/integration/browser/content/css-fonts.firefox.integration.test.ts index efe15c4a..4ad74b6f 100644 --- a/tests/integration/browser/content/css-fonts.firefox.integration.test.ts +++ b/tests/integration/browser/content/css-fonts.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runCssFontsProbe} from "./css-fonts-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/css-fonts.integration.test.ts b/tests/integration/browser/content/css-fonts.integration.test.ts index 031cee8e..34a8297a 100644 --- a/tests/integration/browser/content/css-fonts.integration.test.ts +++ b/tests/integration/browser/content/css-fonts.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runCssFontsProbe} from "./css-fonts-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts b/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts index 407a79a1..848c8f08 100644 --- a/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts +++ b/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile, stat} from "fs/promises"; import path from "path"; @@ -8,8 +10,8 @@ import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; import {documentStateExpression, expectLoadedProbe, type DocumentState} from "./utils"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "entrypoint-assets"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "entrypoint-assets"); interface FrameState { top: DocumentState; diff --git a/tests/integration/browser/content/entrypoint-assets.integration.test.ts b/tests/integration/browser/content/entrypoint-assets.integration.test.ts index 125cd11d..2f57f3fa 100644 --- a/tests/integration/browser/content/entrypoint-assets.integration.test.ts +++ b/tests/integration/browser/content/entrypoint-assets.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile, stat} from "fs/promises"; import path from "path"; @@ -10,8 +12,8 @@ import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fix import {documentStateExpression, expectLoadedProbe, type DocumentState} from "./utils"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "entrypoint-assets"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "entrypoint-assets"); const evaluate = async (browser: CdpClient, sessionId: string, expression: string): Promise => { const result = await browser.send( diff --git a/tests/integration/browser/content/frame-pages-utils.ts b/tests/integration/browser/content/frame-pages-utils.ts index ab8bd915..4c5614ca 100644 --- a/tests/integration/browser/content/frame-pages-utils.ts +++ b/tests/integration/browser/content/frame-pages-utils.ts @@ -6,8 +6,8 @@ import {startIntegrationSite} from "../utils/site"; import {waitFor} from "../utils/browser"; export const runFramePagesIntegration = async (browser: "chrome" | "firefox", manifestVersion: 2 | 3) => { - const root = path.resolve(__dirname, "../../../.."); - const fixture = await createIntegrationFixture(root, path.join(__dirname, "frame-pages")); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(import.meta.dirname, "frame-pages")); const site = await startIntegrationSite(path.join(fixture.directory, "site"), null); let session: Awaited> | undefined; try { diff --git a/tests/integration/browser/content/frame-pages.firefox.integration.test.ts b/tests/integration/browser/content/frame-pages.firefox.integration.test.ts index ee6e1c19..b8d524f6 100644 --- a/tests/integration/browser/content/frame-pages.firefox.integration.test.ts +++ b/tests/integration/browser/content/frame-pages.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runFramePagesIntegration} from "./frame-pages-utils"; jest.setTimeout(90_000); test.each([2, 3] as const)( diff --git a/tests/integration/browser/content/frame-pages.integration.test.ts b/tests/integration/browser/content/frame-pages.integration.test.ts index 2b1876bc..e7a04583 100644 --- a/tests/integration/browser/content/frame-pages.integration.test.ts +++ b/tests/integration/browser/content/frame-pages.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runFramePagesIntegration} from "./frame-pages-utils"; jest.setTimeout(90_000); test("Chrome MV3 embeds an extension page and MAIN external source with production frame nodes", async () => { diff --git a/tests/integration/browser/content/isolated-styles-utils.ts b/tests/integration/browser/content/isolated-styles-utils.ts index c27c1a46..6fef6b47 100644 --- a/tests/integration/browser/content/isolated-styles-utils.ts +++ b/tests/integration/browser/content/isolated-styles-utils.ts @@ -138,7 +138,7 @@ export const runIsolatedStylesIntegration = async ( manifestVersion: 2 | 3, fixtureName = "isolation-shadow" ): Promise => { - const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); + const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); let session: BrowserSession | undefined; let chrome: CdpClient | undefined; let firefox: BidiClient | undefined; @@ -147,7 +147,7 @@ export const runIsolatedStylesIntegration = async ( let lastState: unknown; try { - fixture = await createIntegrationFixture(rootDir, path.join(__dirname, fixtureName)); + fixture = await createIntegrationFixture(rootDir, path.join(import.meta.dirname, fixtureName)); const extensionDir = await fixture.build({browser: name, manifestVersion}); const manifest = JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); const scripts = manifest.content_scripts as Array<{css?: string[]; js: string[]}>; diff --git a/tests/integration/browser/content/isolation-iframe.firefox.integration.test.ts b/tests/integration/browser/content/isolation-iframe.firefox.integration.test.ts index d6fedd21..d51134e3 100644 --- a/tests/integration/browser/content/isolation-iframe.firefox.integration.test.ts +++ b/tests/integration/browser/content/isolation-iframe.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolatedStylesIntegration} from "./isolated-styles-utils"; jest.setTimeout(90_000); test.each([2, 3] as const)( diff --git a/tests/integration/browser/content/isolation-iframe.integration.test.ts b/tests/integration/browser/content/isolation-iframe.integration.test.ts index 2638a5ad..5f165a84 100644 --- a/tests/integration/browser/content/isolation-iframe.integration.test.ts +++ b/tests/integration/browser/content/isolation-iframe.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolatedStylesIntegration} from "./isolated-styles-utils"; jest.setTimeout(90_000); test("Chrome MV3 renders production iframe UI, CSS and fonts under strict CSP", async () => { diff --git a/tests/integration/browser/content/isolation-shadow-mv2.firefox.integration.test.ts b/tests/integration/browser/content/isolation-shadow-mv2.firefox.integration.test.ts index 94dfad36..4fc7b0ea 100644 --- a/tests/integration/browser/content/isolation-shadow-mv2.firefox.integration.test.ts +++ b/tests/integration/browser/content/isolation-shadow-mv2.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolatedStylesIntegration} from "./isolated-styles-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/isolation-shadow-mv3.firefox.integration.test.ts b/tests/integration/browser/content/isolation-shadow-mv3.firefox.integration.test.ts index 65abceda..0a93b18e 100644 --- a/tests/integration/browser/content/isolation-shadow-mv3.firefox.integration.test.ts +++ b/tests/integration/browser/content/isolation-shadow-mv3.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolatedStylesIntegration} from "./isolated-styles-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/isolation-shadow.integration.test.ts b/tests/integration/browser/content/isolation-shadow.integration.test.ts index c614d85e..071cd2b8 100644 --- a/tests/integration/browser/content/isolation-shadow.integration.test.ts +++ b/tests/integration/browser/content/isolation-shadow.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolatedStylesIntegration} from "./isolated-styles-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/isolation-spike-utils.ts b/tests/integration/browser/content/isolation-spike-utils.ts index a12d44bd..79a32184 100644 --- a/tests/integration/browser/content/isolation-spike-utils.ts +++ b/tests/integration/browser/content/isolation-spike-utils.ts @@ -10,8 +10,8 @@ const stateExpression = `Array.from(document.querySelectorAll('[data-probe]'), h links: Array.from(host.querySelector('iframe').contentDocument.querySelectorAll('link'), link => link.href)}))`; export const runIsolationSpike = async (browser: "chrome" | "firefox", manifestVersion: 2 | 3) => { - const root = path.resolve(__dirname, "../../../.."); - const fixture = path.join(__dirname, "isolation-spike"); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = path.join(import.meta.dirname, "isolation-spike"); const cache = path.join(root, ".cache/integration"); await mkdir(cache, {recursive: true}); const extension = await mkdtemp(path.join(cache, "isolation-spike-")); @@ -39,7 +39,7 @@ export const runIsolationSpike = async (browser: "chrome" | "firefox", manifestV for (const file of ["initial.css", "lazy.css", "panel.html", "panel.js"]) await cp(path.join(fixture, "src", file), path.join(extension, file)); await cp( - path.join(__dirname, "isolation-shadow/src/probe.content/probe.woff2"), + path.join(import.meta.dirname, "isolation-shadow/src/probe.content/probe.woff2"), path.join(extension, "probe.woff2") ); const resources = ["initial.css", "lazy.css", "probe.woff2", "panel.html"]; diff --git a/tests/integration/browser/content/isolation-spike.firefox.integration.test.ts b/tests/integration/browser/content/isolation-spike.firefox.integration.test.ts index edc42131..ffd0f335 100644 --- a/tests/integration/browser/content/isolation-spike.firefox.integration.test.ts +++ b/tests/integration/browser/content/isolation-spike.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolationSpike} from "./isolation-spike-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/isolation-spike.integration.test.ts b/tests/integration/browser/content/isolation-spike.integration.test.ts index 7f43816b..07806fa8 100644 --- a/tests/integration/browser/content/isolation-spike.integration.test.ts +++ b/tests/integration/browser/content/isolation-spike.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runIsolationSpike} from "./isolation-spike-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/relay-styles-utils.ts b/tests/integration/browser/content/relay-styles-utils.ts index d24860e2..0d86021c 100644 --- a/tests/integration/browser/content/relay-styles-utils.ts +++ b/tests/integration/browser/content/relay-styles-utils.ts @@ -6,8 +6,8 @@ import {startIntegrationSite} from "../utils/site"; import {waitFor} from "../utils/browser"; export const expectRelayStyles = async (browser: "chrome" | "firefox", manifestVersion: 2 | 3): Promise => { - const root = path.resolve(__dirname, "../../../.."); - const fixture = await createIntegrationFixture(root, path.join(__dirname, "relay-styles")); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(import.meta.dirname, "relay-styles")); const site = await startIntegrationSite(path.join(fixture.directory, "site"), null); let session: Awaited> | undefined; try { diff --git a/tests/integration/browser/content/relay-styles.firefox.integration.test.ts b/tests/integration/browser/content/relay-styles.firefox.integration.test.ts index 140c871c..cc9bd45d 100644 --- a/tests/integration/browser/content/relay-styles.firefox.integration.test.ts +++ b/tests/integration/browser/content/relay-styles.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRelayStyles} from "./relay-styles-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/relay-styles.integration.test.ts b/tests/integration/browser/content/relay-styles.integration.test.ts index f7a71443..8e7a7018 100644 --- a/tests/integration/browser/content/relay-styles.integration.test.ts +++ b/tests/integration/browser/content/relay-styles.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRelayStyles} from "./relay-styles-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/render-props-utils.ts b/tests/integration/browser/content/render-props-utils.ts index 5c4a5ba3..3d673797 100644 --- a/tests/integration/browser/content/render-props-utils.ts +++ b/tests/integration/browser/content/render-props-utils.ts @@ -6,8 +6,8 @@ import {startIntegrationSite} from "../utils/site"; import {waitFor} from "../utils/browser"; export async function expectRenderProps(browser: "chrome" | "firefox") { - const root = path.resolve(__dirname, "../../../.."); - const fixture = await createIntegrationFixture(root, path.join(__dirname, "render-props")); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(import.meta.dirname, "render-props")); const site = await startIntegrationSite(path.join(fixture.directory, "site"), null); let session: Awaited> | undefined; diff --git a/tests/integration/browser/content/render-props.firefox.integration.test.ts b/tests/integration/browser/content/render-props.firefox.integration.test.ts index 648b913c..3501e0f6 100644 --- a/tests/integration/browser/content/render-props.firefox.integration.test.ts +++ b/tests/integration/browser/content/render-props.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRenderProps} from "./render-props-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/render-props.integration.test.ts b/tests/integration/browser/content/render-props.integration.test.ts index c66d3a77..4ebb6e05 100644 --- a/tests/integration/browser/content/render-props.integration.test.ts +++ b/tests/integration/browser/content/render-props.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRenderProps} from "./render-props-utils"; jest.setTimeout(90_000); test("chrome prepares content and Relay before mounting UI with current DOM props", () => expectRenderProps("chrome")); diff --git a/tests/integration/browser/content/selective-split-utils.ts b/tests/integration/browser/content/selective-split-utils.ts index 53152c44..94bd7c2f 100644 --- a/tests/integration/browser/content/selective-split-utils.ts +++ b/tests/integration/browser/content/selective-split-utils.ts @@ -90,7 +90,7 @@ export const runSelectiveSplitProbe = async (browser: "chrome" | "firefox", mani ); session = await startBrowserSession(browser, project, extension); - const sourceSite = path.join(__dirname, "../../build/style-routing/selective-split/site"); + const sourceSite = path.join(import.meta.dirname, "../../build/style-routing/selective-split/site"); const siteDirectory = path.join(extension, "site"); await mkdir(siteDirectory); await copyFile(path.join(sourceSite, "top.html"), path.join(siteDirectory, "top.html")); diff --git a/tests/integration/browser/content/selective-split.firefox.integration.test.ts b/tests/integration/browser/content/selective-split.firefox.integration.test.ts index eb613308..d14a3253 100644 --- a/tests/integration/browser/content/selective-split.firefox.integration.test.ts +++ b/tests/integration/browser/content/selective-split.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runSelectiveSplitProbe} from "./selective-split-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/content/selective-split.integration.test.ts b/tests/integration/browser/content/selective-split.integration.test.ts index f7bd3bff..651fcb27 100644 --- a/tests/integration/browser/content/selective-split.integration.test.ts +++ b/tests/integration/browser/content/selective-split.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {runSelectiveSplitProbe} from "./selective-split-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/locale/chunks.firefox.integration.test.ts b/tests/integration/browser/locale/chunks.firefox.integration.test.ts index beb9e79e..3678277a 100644 --- a/tests/integration/browser/locale/chunks.firefox.integration.test.ts +++ b/tests/integration/browser/locale/chunks.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; diff --git a/tests/integration/browser/locale/chunks.integration.test.ts b/tests/integration/browser/locale/chunks.integration.test.ts index 959ba86f..ec50f970 100644 --- a/tests/integration/browser/locale/chunks.integration.test.ts +++ b/tests/integration/browser/locale/chunks.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; diff --git a/tests/integration/browser/locale/dollars.firefox.integration.test.ts b/tests/integration/browser/locale/dollars.firefox.integration.test.ts index c2ebab9f..913e865f 100644 --- a/tests/integration/browser/locale/dollars.firefox.integration.test.ts +++ b/tests/integration/browser/locale/dollars.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifyLocaleDollars} from "./dollars"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/locale/dollars.integration.test.ts b/tests/integration/browser/locale/dollars.integration.test.ts index 911abc1e..2ef4a296 100644 --- a/tests/integration/browser/locale/dollars.integration.test.ts +++ b/tests/integration/browser/locale/dollars.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifyLocaleDollars} from "./dollars"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/locale/dynamic.firefox.integration.test.ts b/tests/integration/browser/locale/dynamic.firefox.integration.test.ts index adda23e5..7d549704 100644 --- a/tests/integration/browser/locale/dynamic.firefox.integration.test.ts +++ b/tests/integration/browser/locale/dynamic.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; import BidiClient from "../utils/BidiClient"; diff --git a/tests/integration/browser/locale/dynamic.integration.test.ts b/tests/integration/browser/locale/dynamic.integration.test.ts index 75cf4162..1cfb473c 100644 --- a/tests/integration/browser/locale/dynamic.integration.test.ts +++ b/tests/integration/browser/locale/dynamic.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; diff --git a/tests/integration/browser/offscreen/service.integration.test.ts b/tests/integration/browser/offscreen/service.integration.test.ts index 7703b922..2497fa71 100644 --- a/tests/integration/browser/offscreen/service.integration.test.ts +++ b/tests/integration/browser/offscreen/service.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import path from "path"; import {type CdpTarget, targets} from "../utils/chrome"; @@ -6,8 +8,8 @@ import CdpClient from "../utils/CdpClient"; import {waitFor} from "../utils/browser"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "service"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "service"); jest.setTimeout(60_000); diff --git a/tests/integration/browser/options/options.integration.test.ts b/tests/integration/browser/options/options.integration.test.ts index 150ad11b..11941837 100644 --- a/tests/integration/browser/options/options.integration.test.ts +++ b/tests/integration/browser/options/options.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; @@ -7,7 +9,7 @@ import CdpClient from "../utils/CdpClient"; import {waitFor} from "../utils/browser"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); jest.setTimeout(90_000); @@ -15,7 +17,7 @@ test.each([ {adapter: "vanilla", page: "options.html", title: "Vanilla Options", help: "help.html"}, {adapter: "react", page: "ui/preferences.options.html", title: "React Options", help: "ui/help.html"}, ])("Chrome MV3 opens and renders $adapter options from the background", async ({adapter, page, title, help}) => { - const fixtureDir = path.join(__dirname, adapter); + const fixtureDir = path.join(import.meta.dirname, adapter); let session: BrowserSession | undefined; let browser: CdpClient | undefined; let fixture: IntegrationFixture | undefined; diff --git a/tests/integration/browser/override/override.integration.test.ts b/tests/integration/browser/override/override.integration.test.ts index 40de93d6..1ccf1e19 100644 --- a/tests/integration/browser/override/override.integration.test.ts +++ b/tests/integration/browser/override/override.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; @@ -5,7 +7,7 @@ import {waitFor} from "../utils/browser"; import {startBrowserSession} from "../utils/session"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); jest.setTimeout(90_000); @@ -21,7 +23,7 @@ test.each([ let session: Awaited> | undefined; try { - fixture = await createIntegrationFixture(rootDir, path.join(__dirname, page)); + fixture = await createIntegrationFixture(rootDir, path.join(import.meta.dirname, page)); const extensionDir = await fixture.build(); const manifest = JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); diff --git a/tests/integration/browser/popup/popup.firefox.integration.test.ts b/tests/integration/browser/popup/popup.firefox.integration.test.ts index b95bfc64..91495ff6 100644 --- a/tests/integration/browser/popup/popup.firefox.integration.test.ts +++ b/tests/integration/browser/popup/popup.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifyPopup} from "./popup"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/popup/popup.integration.test.ts b/tests/integration/browser/popup/popup.integration.test.ts index 9ec2321e..e6a924e2 100644 --- a/tests/integration/browser/popup/popup.integration.test.ts +++ b/tests/integration/browser/popup/popup.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifyPopup} from "./popup"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/relay/scripting-utils.ts b/tests/integration/browser/relay/scripting-utils.ts index 21a0e8c0..ec85b4f0 100644 --- a/tests/integration/browser/relay/scripting-utils.ts +++ b/tests/integration/browser/relay/scripting-utils.ts @@ -7,8 +7,8 @@ import {waitFor} from "@tests/integration/browser/utils/browser"; type BrowserSession = Awaited>; export async function expectRelayScripting(browser: "chrome" | "firefox", manifestVersion: 2 | 3): Promise { - const root = path.resolve(__dirname, "../../../.."); - const fixture = await createIntegrationFixture(root, path.join(__dirname, "scripting")); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(import.meta.dirname, "scripting")); const site = await startIntegrationSite(path.join(fixture.directory, "site"), null); let session: BrowserSession | undefined; diff --git a/tests/integration/browser/relay/scripting.firefox.integration.test.ts b/tests/integration/browser/relay/scripting.firefox.integration.test.ts index 4533bca7..2fde698d 100644 --- a/tests/integration/browser/relay/scripting.firefox.integration.test.ts +++ b/tests/integration/browser/relay/scripting.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRelayScripting} from "./scripting-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/relay/scripting.integration.test.ts b/tests/integration/browser/relay/scripting.integration.test.ts index 811ab25a..e83d8ebe 100644 --- a/tests/integration/browser/relay/scripting.integration.test.ts +++ b/tests/integration/browser/relay/scripting.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {expectRelayScripting} from "./scripting-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/sidebar/sidebar.firefox.integration.test.ts b/tests/integration/browser/sidebar/sidebar.firefox.integration.test.ts index 59fc8c83..35fff424 100644 --- a/tests/integration/browser/sidebar/sidebar.firefox.integration.test.ts +++ b/tests/integration/browser/sidebar/sidebar.firefox.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifySidebar} from "./sidebar"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/sidebar/sidebar.integration.test.ts b/tests/integration/browser/sidebar/sidebar.integration.test.ts index 5bf97366..148b91dc 100644 --- a/tests/integration/browser/sidebar/sidebar.integration.test.ts +++ b/tests/integration/browser/sidebar/sidebar.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {verifySidebar} from "./sidebar"; jest.setTimeout(90_000); diff --git a/tests/integration/browser/utils/site.test.ts b/tests/integration/browser/utils/site.test.ts index 571f055b..32f39af3 100644 --- a/tests/integration/browser/utils/site.test.ts +++ b/tests/integration/browser/utils/site.test.ts @@ -5,7 +5,7 @@ import path from "path"; import {startIntegrationSite} from "./site"; test("closes speculative browser connections that have not sent an HTTP request", async () => { - const site = await startIntegrationSite(path.resolve(__dirname, "../content/isolation-shadow/site")); + const site = await startIntegrationSite(path.resolve(import.meta.dirname, "../content/isolation-shadow/site")); const socket = createConnection({host: "127.0.0.1", port: Number(new URL(site.origin).port)}); let timeout: ReturnType | undefined; let closing: Promise | undefined; diff --git a/tests/integration/browser/utils/startup.test.ts b/tests/integration/browser/utils/startup.test.ts index 3382a81e..aef9b9a1 100644 --- a/tests/integration/browser/utils/startup.test.ts +++ b/tests/integration/browser/utils/startup.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {waitForBrowserStartup} from "./startup"; afterEach(() => { diff --git a/tests/integration/browser/view/jsx.integration.test.ts b/tests/integration/browser/view/jsx.integration.test.ts index c53d4659..5b77a037 100644 --- a/tests/integration/browser/view/jsx.integration.test.ts +++ b/tests/integration/browser/view/jsx.integration.test.ts @@ -1,11 +1,13 @@ +import {jest} from "@jest/globals"; + import path from "path"; import {startBrowserSession, type BrowserSession} from "../utils/session"; import {waitFor} from "../utils/browser"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.resolve(__dirname, "..", "..", "build", "jsx", "fixture"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.resolve(import.meta.dirname, "..", "..", "build", "jsx", "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/browser/view/render.integration.test.ts b/tests/integration/browser/view/render.integration.test.ts index d126f3d0..8289ca2c 100644 --- a/tests/integration/browser/view/render.integration.test.ts +++ b/tests/integration/browser/view/render.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; @@ -7,7 +9,7 @@ import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); jest.setTimeout(90_000); @@ -18,7 +20,7 @@ test("Chrome MV3 renders offscreen, sandbox, page and content views through the let site: IntegrationSite | undefined; try { - fixture = await createIntegrationFixture(rootDir, path.join(__dirname, "render")); + fixture = await createIntegrationFixture(rootDir, path.join(import.meta.dirname, "render")); site = await startIntegrationSite(path.join(fixture.directory, "site"), null); const extensionDir = await fixture.build(); diff --git a/tests/integration/build/action/action.integration.test.ts b/tests/integration/build/action/action.integration.test.ts index 68dc79b3..d5791bac 100644 --- a/tests/integration/build/action/action.integration.test.ts +++ b/tests/integration/build/action/action.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {copyFile, readFile, stat} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const projectRoot = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "fixture"); +const projectRoot = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/build/background/persistent.integration.test.ts b/tests/integration/build/background/persistent.integration.test.ts index b60e2afb..964ef08e 100644 --- a/tests/integration/build/background/persistent.integration.test.ts +++ b/tests/integration/build/background/persistent.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtureDir = path.join(__dirname, "fixture"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/build/cli/BuildSession.integration.test.ts b/tests/integration/build/cli/BuildSession.integration.test.ts index e5d9926e..2a9175c5 100644 --- a/tests/integration/build/cli/BuildSession.integration.test.ts +++ b/tests/integration/build/cli/BuildSession.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {fork} from "node:child_process"; import {cp, mkdir, mkdtemp, readFile, rm, symlink, access} from "node:fs/promises"; import os from "node:os"; @@ -5,8 +7,8 @@ import path from "node:path"; import BuildSession, {BuildSessionError} from "../../utils/BuildSession"; -const projectRoot = path.resolve(__dirname, "../../../.."); -const fixtures = path.join(__dirname, "fixtures"); +const projectRoot = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.join(import.meta.dirname, "fixtures"); let directory: string; let root: string; let session: BuildSession; diff --git a/tests/integration/build/cli/build-api.integration.test.ts b/tests/integration/build/cli/build-api.integration.test.ts index e22c5a3d..be48bcda 100644 --- a/tests/integration/build/cli/build-api.integration.test.ts +++ b/tests/integration/build/cli/build-api.integration.test.ts @@ -4,8 +4,8 @@ import os from "node:os"; import path from "node:path"; import {stripVTControlCharacters} from "node:util"; -const scripts = path.join(__dirname, "scripts"); -const appModule = path.resolve(__dirname, "../../../../dist/cli/builders/app/index.js"); +const scripts = path.join(import.meta.dirname, "scripts"); +const appModule = path.resolve(import.meta.dirname, "../../../../dist/cli/builders/app/index.js"); test.each([ "sequence", @@ -51,7 +51,7 @@ test("the internal app build returns Stats after shutdown without printing them" const artifact = path.join(root, "dist/exit-code-chrome-mv3"); try { - cpSync(path.join(__dirname, "fixtures/exit-code/lifecycle"), root, {recursive: true}); + cpSync(path.join(import.meta.dirname, "fixtures/exit-code/lifecycle"), root, {recursive: true}); const result = spawnSync(process.execPath, [path.join(scripts, "build-api.mjs"), appModule, root, artifact], { cwd: root, encoding: "utf8", diff --git a/tests/integration/build/cli/cli.integration.test.ts b/tests/integration/build/cli/cli.integration.test.ts index 018f3723..b7dc3d49 100644 --- a/tests/integration/build/cli/cli.integration.test.ts +++ b/tests/integration/build/cli/cli.integration.test.ts @@ -4,8 +4,8 @@ import os from "os"; import path from "path"; import {stripVTControlCharacters} from "util"; -const cli = path.resolve(__dirname, "../../../../bin/adnbn.js"); -const fixtures = path.join(__dirname, "fixtures/exit-code"); +const cli = path.resolve(import.meta.dirname, "../../../../bin/adnbn.js"); +const fixtures = path.join(import.meta.dirname, "fixtures/exit-code"); describe("CLI exit codes", () => { let root: string; diff --git a/tests/integration/build/cli/config.integration.test.ts b/tests/integration/build/cli/config.integration.test.ts index e66968ba..6a33075f 100644 --- a/tests/integration/build/cli/config.integration.test.ts +++ b/tests/integration/build/cli/config.integration.test.ts @@ -7,7 +7,7 @@ import type {OptionalConfig} from "@typing/config"; import {Browser} from "@typing/browser"; import {Mode} from "@typing/app"; -const projectRoot = path.resolve(__dirname, "../../../.."); +const projectRoot = path.resolve(import.meta.dirname, "../../../.."); const resolver = path.join(projectRoot, "dist", "cli", "resolvers", "config.js"); const cli = path.join(projectRoot, "bin", "adnbn.js"); @@ -33,7 +33,7 @@ describe("user configuration through real c12", () => { let fixture: IntegrationFixture; beforeEach(async () => { - fixture = await createIntegrationFixture(projectRoot, path.join(__dirname, "fixtures", "config")); + fixture = await createIntegrationFixture(projectRoot, path.join(import.meta.dirname, "fixtures", "config")); }); afterEach(async () => { @@ -57,7 +57,7 @@ describe("user configuration through real c12", () => { const inspect = (options: OptionalConfig = {}, configFile = "adnbn.config.ts") => { return run([ - path.join(__dirname, "scripts", "inspect-config.mjs"), + path.join(import.meta.dirname, "scripts", "inspect-config.mjs"), resolver, fixture.directory, configFile, @@ -224,6 +224,14 @@ describe("user configuration through real c12", () => { test.each([ {kind: "object", app: "reader", browser: "safari", name: "reader", readable: true, manifestVersion: 2}, + { + kind: "disabled-source-maps", + app: "reader", + browser: "safari", + name: "reader", + readable: true, + manifestVersion: 2, + }, {kind: "debug", app: "reader", browser: "chrome", name: "reader", readable: true, manifestVersion: 3}, {kind: "explicit", app: "reader", browser: "safari", name: "addon", readable: false, manifestVersion: 3}, {kind: "production", app: "addon", browser: "chrome", name: "addon", readable: false, manifestVersion: 3}, @@ -252,8 +260,23 @@ describe("user configuration through real c12", () => { const assets = await readdir(path.join(output, "assets")); expect(manifest).toMatchObject({name, version: "1.2.3", manifest_version: manifestVersion}); - expect(js).toEqual([readable ? "probe.page.js" : expect.stringMatching(/^[a-f0-9]{5}\.js$/)]); - expect(css).toEqual([readable ? "probe.page.css" : expect.stringMatching(/^[a-f0-9]{5}\.css$/)]); + + for (const [extension, files] of [ + ["js", js], + ["css", css], + ] as const) { + const bundles = files.filter(file => file.endsWith(`.${extension}`)); + const maps = files.filter(file => !file.endsWith(`.${extension}`)); + + expect(bundles).toEqual([ + readable + ? `probe.page.${extension}` + : expect.stringMatching(new RegExp(`^[a-f0-9]{5}\\.${extension}$`)), + ]); + // Framework development defaults emit external maps; plugins can explicitly disable them. + expect(maps).toEqual(kind === "object" || kind === "explicit" ? [`${bundles[0]}.map`] : []); + } + expect(assets).toEqual([ expect.stringMatching(readable ? /^marker-[a-f0-9]{4}\.svg$/ : /^[a-f0-9]{4}\.svg$/), ]); diff --git a/tests/integration/build/cli/dotenv.integration.test.ts b/tests/integration/build/cli/dotenv.integration.test.ts index 18a0373b..0aa87b3a 100644 --- a/tests/integration/build/cli/dotenv.integration.test.ts +++ b/tests/integration/build/cli/dotenv.integration.test.ts @@ -3,8 +3,8 @@ import {cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync} from "node:fs"; import os from "node:os"; import path from "node:path"; -const fixtures = path.join(__dirname, "fixtures/dotenv"); -const resolver = path.resolve(__dirname, "../../../../dist/cli/resolvers/config.js"); +const fixtures = path.join(import.meta.dirname, "fixtures/dotenv"); +const resolver = path.resolve(import.meta.dirname, "../../../../dist/cli/resolvers/config.js"); const filenames = [ ".env.development.chrome.local", ".env.development.chrome", @@ -39,7 +39,7 @@ describe("dotenv resolution through the built config loader", () => { ); const result = spawnSync( process.execPath, - [path.join(__dirname, "scripts/inspect-dotenv.mjs"), resolver, root, configFile], + [path.join(import.meta.dirname, "scripts/inspect-dotenv.mjs"), resolver, root, configFile], { cwd, env: {...inherited, ...env}, diff --git a/tests/integration/build/cli/fixtures/config/defaults/disabled-source-maps.config.ts b/tests/integration/build/cli/fixtures/config/defaults/disabled-source-maps.config.ts new file mode 100644 index 00000000..3ad694cf --- /dev/null +++ b/tests/integration/build/cli/fixtures/config/defaults/disabled-source-maps.config.ts @@ -0,0 +1,12 @@ +import {defineConfig} from "adnbn"; +import {settings} from "./settings"; + +export default defineConfig({ + ...settings, + plugins: [ + { + name: "disable-source-maps", + bundler: () => ({devtool: false}), + }, + ], +}); diff --git a/tests/integration/build/cli/root-dir.integration.test.ts b/tests/integration/build/cli/root-dir.integration.test.ts index 89314222..845c9a2d 100644 --- a/tests/integration/build/cli/root-dir.integration.test.ts +++ b/tests/integration/build/cli/root-dir.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {spawn, spawnSync, type ChildProcess} from "node:child_process"; import {mkdtempSync, rmSync} from "node:fs"; import {cp, mkdir, readFile, readdir, symlink, writeFile} from "node:fs/promises"; @@ -8,7 +10,7 @@ import {stop, waitFor} from "../../browser/utils/browser"; jest.setTimeout(90_000); -const projectRoot = path.resolve(__dirname, "../../../.."); +const projectRoot = path.resolve(import.meta.dirname, "../../../.."); const relativeForms = ["bare", "dot", "parent"]; const buildCases = ["api", "cli"].flatMap(mode => ["absolute", ...relativeForms].flatMap(form => ["vanilla", "react"].map(renderer => ({mode, form, renderer}))) @@ -22,7 +24,15 @@ test.each(buildCases)( try { const result = spawnSync( process.execPath, - [path.join(__dirname, "scripts/verify-root-dir.mjs"), projectRoot, directory, mode, "", form, renderer], + [ + path.join(import.meta.dirname, "scripts/verify-root-dir.mjs"), + projectRoot, + directory, + mode, + "", + form, + renderer, + ], {encoding: "utf8", timeout: 60_000, maxBuffer: 4 * 1024 * 1024} ); @@ -54,8 +64,11 @@ test.each(["absolute", ...relativeForms])( let output = ""; try { - await cp(path.join(__dirname, "fixtures/root-dir"), root, {recursive: true}); - await cp(path.join(__dirname, "fixtures/root-dir-watch.config.ts"), path.join(root, "adnbn.config.ts")); + await cp(path.join(import.meta.dirname, "fixtures/root-dir"), root, {recursive: true}); + await cp( + path.join(import.meta.dirname, "fixtures/root-dir-watch.config.ts"), + path.join(root, "adnbn.config.ts") + ); // Keep resolution inside the disposable project instead of watching missing packages up to the temp root. await writeFile(path.join(root, "package.json"), '{"private":true,"type":"module"}\n'); await mkdir(cwd, {recursive: true}); @@ -117,7 +130,7 @@ test.each(["absolute", ...relativeForms])( await writeFile( path.join(root, source), - await readFile(path.join(__dirname, "fixtures/root-dir-watch-values.ts")) + await readFile(path.join(import.meta.dirname, "fixtures/root-dir-watch-values.ts")) ); const updated = await waitFor( diff --git a/tests/integration/build/command/command.integration.test.ts b/tests/integration/build/command/command.integration.test.ts index 54a84944..e4716dcd 100644 --- a/tests/integration/build/command/command.integration.test.ts +++ b/tests/integration/build/command/command.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const projectRoot = path.resolve(__dirname, "../../../.."); -const fixtureDir = path.join(__dirname, "fixture"); +const projectRoot = path.resolve(import.meta.dirname, "../../../.."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/build/content/content-api.integration.test.ts b/tests/integration/build/content/content-api.integration.test.ts index e8ac8a29..31f55bb7 100644 --- a/tests/integration/build/content/content-api.integration.test.ts +++ b/tests/integration/build/content/content-api.integration.test.ts @@ -2,7 +2,7 @@ import path from "path"; import {build} from "esbuild"; describe("published content API", () => { - const projectDir = path.resolve(__dirname, "../../../.."); + const projectDir = path.resolve(import.meta.dirname, "../../../.."); test.each([ { diff --git a/tests/integration/build/entrypoint-discovery/entrypoint-discovery.integration.test.ts b/tests/integration/build/entrypoint-discovery/entrypoint-discovery.integration.test.ts index 53b41930..9f48e9a4 100644 --- a/tests/integration/build/entrypoint-discovery/entrypoint-discovery.integration.test.ts +++ b/tests/integration/build/entrypoint-discovery/entrypoint-discovery.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile, readdir} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "fixture"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/build/html/html.integration.test.ts b/tests/integration/build/html/html.integration.test.ts index 888cf530..63077bcc 100644 --- a/tests/integration/build/html/html.integration.test.ts +++ b/tests/integration/build/html/html.integration.test.ts @@ -1,11 +1,13 @@ +import {jest} from "@jest/globals"; + import {readFile, writeFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "../../../.."); -const fixtureDir = path.join(__dirname, "fixture"); -const scenarios = path.join(__dirname, "scenarios"); +const rootDir = path.resolve(import.meta.dirname, "../../../.."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); +const scenarios = path.join(import.meta.dirname, "scenarios"); const forms = ["object", "array", "function-object", "function-array"]; jest.setTimeout(90_000); diff --git a/tests/integration/build/isolation/isolation-watch.integration.test.ts b/tests/integration/build/isolation/isolation-watch.integration.test.ts index cfdef26c..2cb51d3d 100644 --- a/tests/integration/build/isolation/isolation-watch.integration.test.ts +++ b/tests/integration/build/isolation/isolation-watch.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {spawn, type ChildProcess} from "child_process"; import {cp, copyFile, readFile, rm, writeFile} from "fs/promises"; import path from "path"; @@ -8,7 +10,7 @@ import {waitFor, stop} from "../../browser/utils/browser"; jest.setTimeout(90_000); test("real CLI watch changes isolation and page aliases and removes outdated WAR matches", async () => { - const root = path.resolve(__dirname, "../../../.."); + const root = path.resolve(import.meta.dirname, "../../../.."); const fixture = await createIntegrationFixture( root, @@ -19,7 +21,10 @@ test("real CLI watch changes isolation and page aliases and removes outdated WAR let output = ""; try { - await copyFile(path.join(__dirname, "states/background.ts"), path.join(fixture.directory, "src/background.ts")); + await copyFile( + path.join(import.meta.dirname, "states/background.ts"), + path.join(fixture.directory, "src/background.ts") + ); await rm(path.join(fixture.directory, "src/source.content.ts")); await copyFile( @@ -28,18 +33,25 @@ test("real CLI watch changes isolation and page aliases and removes outdated WAR ); await writeFile(path.join(fixture.directory, "src/watch.css"), ".watch-ui { color: red; }\n"); - await copyFile(path.join(__dirname, "states/fonts.css"), path.join(fixture.directory, "src/watch-fonts.css")); - await cp(path.join(__dirname, "states/shared-lazy"), path.join(fixture.directory, "src/shared-lazy"), { - recursive: true, - }); await copyFile( - path.join(__dirname, "states/normal-shared.ts"), + path.join(import.meta.dirname, "states/fonts.css"), + path.join(fixture.directory, "src/watch-fonts.css") + ); + await cp( + path.join(import.meta.dirname, "states/shared-lazy"), + path.join(fixture.directory, "src/shared-lazy"), + { + recursive: true, + } + ); + await copyFile( + path.join(import.meta.dirname, "states/normal-shared.ts"), path.join(fixture.directory, "src/normal.content.ts") ); const entry = path.join(fixture.directory, "src/page.content.ts"); // Save like an editor: Windows copyFile preserves the fixture's old mtime, so watch can miss the edit. const setState = async (state: string) => - writeFile(entry, await readFile(path.join(__dirname, "states", state + ".ts"))); + writeFile(entry, await readFile(path.join(import.meta.dirname, "states", state + ".ts"))); await setState("none"); const directory = await fixture.build({browser: "chrome"}); diff --git a/tests/integration/build/jsx/jsx.integration.test.ts b/tests/integration/build/jsx/jsx.integration.test.ts index 264abd0f..eaca83d4 100644 --- a/tests/integration/build/jsx/jsx.integration.test.ts +++ b/tests/integration/build/jsx/jsx.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "fixture"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "fixture"); jest.setTimeout(90_000); diff --git a/tests/integration/build/locale/chunks-disabled.integration.test.ts b/tests/integration/build/locale/chunks-disabled.integration.test.ts index 93525c1d..27ea9250 100644 --- a/tests/integration/build/locale/chunks-disabled.integration.test.ts +++ b/tests/integration/build/locale/chunks-disabled.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {fixtureEntries, expectLocaleChunks, type Scenario} from "./chunks-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/locale/chunks-threshold.integration.test.ts b/tests/integration/build/locale/chunks-threshold.integration.test.ts index 477a4ece..443c1aff 100644 --- a/tests/integration/build/locale/chunks-threshold.integration.test.ts +++ b/tests/integration/build/locale/chunks-threshold.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {fixtureEntries, expectLocaleChunks, type Scenario} from "./chunks-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/locale/chunks-utils.ts b/tests/integration/build/locale/chunks-utils.ts index 6424fcff..4cbbe79b 100644 --- a/tests/integration/build/locale/chunks-utils.ts +++ b/tests/integration/build/locale/chunks-utils.ts @@ -24,7 +24,7 @@ export interface Scenario { } export const expectLocaleChunks = async ({browser, entries, bytes, enabled = true}: Scenario): Promise => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "chunks-fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "chunks-fixture")); try { for (const entry of fixtureEntries) { if (!entries.includes(entry)) await rm(path.join(fixture.directory, "src", `${entry}.ts`)); diff --git a/tests/integration/build/locale/chunks.integration.test.ts b/tests/integration/build/locale/chunks.integration.test.ts index 56e03cbc..1ef2150e 100644 --- a/tests/integration/build/locale/chunks.integration.test.ts +++ b/tests/integration/build/locale/chunks.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {fixtureEntries, expectLocaleChunks, type Scenario} from "./chunks-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/locale/dynamic.integration.test.ts b/tests/integration/build/locale/dynamic.integration.test.ts index aa13dcfc..a59d1267 100644 --- a/tests/integration/build/locale/dynamic.integration.test.ts +++ b/tests/integration/build/locale/dynamic.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {copyFile, readFile, readdir, rm} from "fs/promises"; import path from "path"; import vm from "vm"; @@ -6,10 +8,10 @@ import {run} from "../../utils/process"; jest.setTimeout(90_000); -const fixtureDirectory = path.join(__dirname, "dynamic-fixture"); +const fixtureDirectory = path.join(import.meta.dirname, "dynamic-fixture"); test("the published package imports without bundler aliases or extension APIs", async () => { - await run(process.execPath, [path.join(__dirname, "states/package-import.mjs")], ADNBN_TEST_ROOT); + await run(process.execPath, [path.join(import.meta.dirname, "states/package-import.mjs")], ADNBN_TEST_ROOT); }); test.each(["chrome", "firefox"])( @@ -59,7 +61,7 @@ test("native-only consumers do not include the catalogue despite importing the p for (const file of ["popup.ts", "options.ts", "locale.content.ts", "main.content.ts"]) await rm(path.join(fixture.directory, "src", file)); await copyFile( - path.join(__dirname, "states/native-background.ts"), + path.join(import.meta.dirname, "states/native-background.ts"), path.join(fixture.directory, "src/background.ts") ); const directory = await fixture.build(); @@ -106,7 +108,10 @@ test("the native React hook builds through the public adapter without bundling t try { for (const file of ["background.ts", "options.ts", "locale.content.ts", "main.content.ts"]) await rm(path.join(fixture.directory, "src", file)); - await copyFile(path.join(__dirname, "states/native-popup.ts"), path.join(fixture.directory, "src/popup.ts")); + await copyFile( + path.join(import.meta.dirname, "states/native-popup.ts"), + path.join(fixture.directory, "src/popup.ts") + ); const directory = await fixture.build(); const manifest = JSON.parse(await readFile(path.join(directory, "manifest.json"), "utf8")); diff --git a/tests/integration/build/locale/locale.integration.test.ts b/tests/integration/build/locale/locale.integration.test.ts index f8b3d446..19f39b6c 100644 --- a/tests/integration/build/locale/locale.integration.test.ts +++ b/tests/integration/build/locale/locale.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {spawn, type ChildProcess} from "child_process"; import {copyFile, readFile, readdir, rm, writeFile} from "fs/promises"; import path from "path"; @@ -44,7 +46,7 @@ const inspect = async (directory: string) => { }; test.each(["chrome", "firefox"])("%s build imports the catalogue and preserves the native JSON data", async browser => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { const {catalogue, keys} = await inspect(await fixture.build({browser})); expect([...keys].sort()).toEqual([ @@ -72,7 +74,7 @@ test.each(["chrome", "firefox"])("%s build imports the catalogue and preserves t }); test("exports the configured default language even when it is not the first catalogue language", async () => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { // Give the new default the complete message contract, including own __proto__ keys. await copyFile( @@ -93,7 +95,7 @@ test("exports the configured default language even when it is not the first cata }); test("CLI watch discovers, edits and removes locales and recovers from invalid translations", async () => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); let compilations = 0; const update = async (change: () => Promise) => { const previous = compilations; @@ -109,7 +111,7 @@ test("CLI watch discovers, edits and removes locales and recovers from invalid t update(async () => writeFile( path.join(fixture.directory, "src/locales", `${language}.json`), - await readFile(path.join(__dirname, "states", state)) + await readFile(path.join(import.meta.dirname, "states", state)) ) ); let watcher: ChildProcess | undefined; diff --git a/tests/integration/build/options/options.integration.test.ts b/tests/integration/build/options/options.integration.test.ts index 3c0c152d..cb66b017 100644 --- a/tests/integration/build/options/options.integration.test.ts +++ b/tests/integration/build/options/options.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile, writeFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "embedded"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "embedded"); jest.setTimeout(90_000); diff --git a/tests/integration/build/override/bookmarks.integration.test.ts b/tests/integration/build/override/bookmarks.integration.test.ts index 091c0ebd..bc215842 100644 --- a/tests/integration/build/override/bookmarks.integration.test.ts +++ b/tests/integration/build/override/bookmarks.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {testOverridePage} from "./override-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/override/competing.integration.test.ts b/tests/integration/build/override/competing.integration.test.ts index 1f01dab3..cce179d3 100644 --- a/tests/integration/build/override/competing.integration.test.ts +++ b/tests/integration/build/override/competing.integration.test.ts @@ -1,21 +1,23 @@ +import {jest} from "@jest/globals"; + import {readdir, readFile, writeFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; import {readOverrideManifest} from "./override-utils"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); jest.setTimeout(90_000); /** Adds the History entrypoint to a copy of the New Tab application, so two overrides compete. */ const createCompetingFixture = async (): Promise => { - const fixture = await createIntegrationFixture(rootDir, path.join(__dirname, "newtab")); + const fixture = await createIntegrationFixture(rootDir, path.join(import.meta.dirname, "newtab")); try { await writeFile( path.join(fixture.directory, "src", "history.ts"), - await readFile(path.join(__dirname, "history", "src", "history.ts")) + await readFile(path.join(import.meta.dirname, "history", "src", "history.ts")) ); } catch (error) { await fixture.dispose(); diff --git a/tests/integration/build/override/history.integration.test.ts b/tests/integration/build/override/history.integration.test.ts index 5497f82f..0f162b34 100644 --- a/tests/integration/build/override/history.integration.test.ts +++ b/tests/integration/build/override/history.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {testOverridePage} from "./override-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/override/newtab.integration.test.ts b/tests/integration/build/override/newtab.integration.test.ts index 386c9e14..4be49b8d 100644 --- a/tests/integration/build/override/newtab.integration.test.ts +++ b/tests/integration/build/override/newtab.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {testOverridePage} from "./override-utils"; jest.setTimeout(90_000); diff --git a/tests/integration/build/override/override-utils.ts b/tests/integration/build/override/override-utils.ts index a01a5185..1b0d0aac 100644 --- a/tests/integration/build/override/override-utils.ts +++ b/tests/integration/build/override/override-utils.ts @@ -18,7 +18,7 @@ if (buildMode !== "cli" && buildMode !== "session") { throw new Error(`Unknown override build mode: ${buildMode}`); } -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); export const readOverrideManifest = async (extensionDir: string) => { return JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); @@ -26,7 +26,7 @@ export const readOverrideManifest = async (extensionDir: string) => { export const testOverridePage = ({page, permission, supported, unsupported}: OverridePageScenario): void => { describe(`${page} override`, () => { - const fixtureDir = path.join(__dirname, page); + const fixtureDir = path.join(import.meta.dirname, page); const source = `https://${page}.example.com`; let session: BuildSession | undefined; diff --git a/tests/integration/build/permissions/permissions.integration.test.ts b/tests/integration/build/permissions/permissions.integration.test.ts index 55be195b..a6f1d436 100644 --- a/tests/integration/build/permissions/permissions.integration.test.ts +++ b/tests/integration/build/permissions/permissions.integration.test.ts @@ -1,10 +1,12 @@ +import {jest} from "@jest/globals"; + import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const fixtureDir = path.join(__dirname, "views"); +const rootDir = path.resolve(import.meta.dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(import.meta.dirname, "views"); jest.setTimeout(90_000); diff --git a/tests/integration/build/permissions/view-watch.integration.test.ts b/tests/integration/build/permissions/view-watch.integration.test.ts index 34355cbe..d5d87fd8 100644 --- a/tests/integration/build/permissions/view-watch.integration.test.ts +++ b/tests/integration/build/permissions/view-watch.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {spawn, type ChildProcess} from "child_process"; import {readFile, rm, writeFile} from "fs/promises"; import path from "path"; @@ -8,8 +10,8 @@ import {stop, waitFor} from "../../browser/utils/browser"; jest.setTimeout(90_000); test("CLI watch replaces page and offscreen permissions and a fresh build drops removed views", async () => { - const root = path.resolve(__dirname, "../../../.."); - const fixture = await createIntegrationFixture(root, path.join(__dirname, "views")); + const root = path.resolve(import.meta.dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(import.meta.dirname, "views")); let watcher: ChildProcess | undefined; let output = ""; @@ -78,7 +80,7 @@ test("CLI watch replaces page and offscreen permissions and a fresh build drops ); const beforeEdit = completedBuilds(); - await writeFile(entry, await readFile(path.join(__dirname, "states", "reports.ts"))); + await writeFile(entry, await readFile(path.join(import.meta.dirname, "states", "reports.ts"))); const changed = await observe( "page edit", @@ -94,7 +96,7 @@ test("CLI watch replaces page and offscreen permissions and a fresh build drops expect(changed.content_security_policy.extension_pages).not.toContain("https://api.example.net"); const beforeOffscreenEdit = completedBuilds(); - await writeFile(offscreen, await readFile(path.join(__dirname, "states", "processor.ts"))); + await writeFile(offscreen, await readFile(path.join(import.meta.dirname, "states", "processor.ts"))); const changedOffscreen = await observe( "offscreen edit", diff --git a/tests/integration/build/popup/popup.integration.test.ts b/tests/integration/build/popup/popup.integration.test.ts index ca7d7464..3928376e 100644 --- a/tests/integration/build/popup/popup.integration.test.ts +++ b/tests/integration/build/popup/popup.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {copyFile, readFile} from "fs/promises"; import path from "path"; @@ -6,7 +8,7 @@ import {createIntegrationFixture} from "../../utils/fixture"; jest.setTimeout(90_000); test.each([2, 3] as const)("builds independent popup titles and tooltips in MV%i", async manifestVersion => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { const directory = await fixture.build({manifestVersion}); @@ -31,7 +33,7 @@ test.each([2, 3] as const)("builds independent popup titles and tooltips in MV%i }); test("builds an apply: false popup with a missing runtime translation without changing the initial tooltip", async () => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { await copyFile( diff --git a/tests/integration/build/relay-runtime/relay-runtime.integration.test.ts b/tests/integration/build/relay-runtime/relay-runtime.integration.test.ts index c5f0f8e7..79d7d0a3 100644 --- a/tests/integration/build/relay-runtime/relay-runtime.integration.test.ts +++ b/tests/integration/build/relay-runtime/relay-runtime.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {spawn, type ChildProcess} from "child_process"; import {copyFile, readFile, rm, writeFile} from "fs/promises"; import path from "path"; @@ -17,17 +19,17 @@ test("generated page and Relay data follow their consumers through the public ba try { for (const file of ["control.relay.ts", "iframe.relay.ts"]) await rm(path.join(fixture.directory, "src", file)); await copyFile( - path.join(__dirname, "states/messaging.ts"), + path.join(import.meta.dirname, "states/messaging.ts"), path.join(fixture.directory, "src/shadow.relay.ts") ); await copyFile( - path.join(__dirname, "states/page.ts"), + path.join(import.meta.dirname, "states/page.ts"), path.join(fixture.directory, "src/control.page/index.ts") ); for (const state of ["unused-background", "page-background", "background"]) { await copyFile( - path.join(__dirname, "states", `${state}.ts`), + path.join(import.meta.dirname, "states", `${state}.ts`), path.join(fixture.directory, "src/background.ts") ); const directory = await fixture.build(); @@ -52,7 +54,7 @@ test("generated page and Relay data follow their consumers through the public ba }); test("content plugin delivers Relay options and refreshes names, methods and optional values in CLI watch", async () => { - const root = path.resolve(__dirname, "../../../.."); + const root = path.resolve(import.meta.dirname, "../../../.."); // Reuse the browser application: the same production configuration also exercises real Relay RPC. const fixture = await createIntegrationFixture( root, @@ -84,11 +86,14 @@ test("content plugin delivers Relay options and refreshes names, methods and opt try { await rm(path.join(fixture.directory, "src/control.relay.ts")); await rm(path.join(fixture.directory, "src/iframe.relay.ts")); - await copyFile(path.join(__dirname, "states/background.ts"), path.join(fixture.directory, "src/background.ts")); + await copyFile( + path.join(import.meta.dirname, "states/background.ts"), + path.join(fixture.directory, "src/background.ts") + ); const entry = path.join(fixture.directory, "src/shadow.relay.ts"); // Save like an editor: Windows copyFile preserves the fixture's old mtime, so watch can miss the edit. const setState = async (state: string) => - writeFile(entry, await readFile(path.join(__dirname, "states", `${state}.ts`))); + writeFile(entry, await readFile(path.join(import.meta.dirname, "states", `${state}.ts`))); await setState("messaging"); const directory = await fixture.build({browser: "chrome"}); const inspect = async () => { diff --git a/tests/integration/build/sidebar/sidebar.integration.test.ts b/tests/integration/build/sidebar/sidebar.integration.test.ts index 631bc6fb..e2531a93 100644 --- a/tests/integration/build/sidebar/sidebar.integration.test.ts +++ b/tests/integration/build/sidebar/sidebar.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {copyFile, readFile, stat} from "fs/promises"; import path from "path"; @@ -12,7 +14,7 @@ test.each([ {browser: "opera", manifestVersion: 2}, {browser: "opera", manifestVersion: 3}, ] as const)("builds independent sidebar documents and labels for $browser MV$manifestVersion", async options => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { const directory = await fixture.build(options); @@ -59,7 +61,7 @@ test.each([ {variant: "without-label", expected: "Sidebar contract test"}, {variant: "empty-label", expected: ""}, ])("keeps document titles out of the initial UI label: $variant", async ({variant, expected}) => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { await copyFile( diff --git a/tests/integration/build/style-routing/content-chunks.integration.test.ts b/tests/integration/build/style-routing/content-chunks.integration.test.ts index efe5f34a..5a4e4c9a 100644 --- a/tests/integration/build/style-routing/content-chunks.integration.test.ts +++ b/tests/integration/build/style-routing/content-chunks.integration.test.ts @@ -15,7 +15,7 @@ import IsolatedStylesPlugin from "@cli/bundler/plugins/isolated-styles"; import {ContentScriptStylesRuntimeProperty, ContentScriptWorld} from "@typing/content"; import type {ReadonlyConfig} from "@typing/config"; -const root = path.resolve(__dirname, "../../../.."); +const root = path.resolve(import.meta.dirname, "../../../.."); const require = createRequire(import.meta.url); test.each([true, false])( @@ -23,7 +23,7 @@ test.each([true, false])( async commonChunks => { const output = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-content-chunks-")); const config = { - rootDir: __dirname, + rootDir: import.meta.dirname, srcDir: "src", sharedDir: ".", appsDir: "apps", @@ -49,7 +49,7 @@ test.each([true, false])( const mainLayer = getContentLayer(ContentScriptWorld.Main); const compiler = rspack( merge(merge(settings), { - context: path.join(__dirname, "src"), + context: path.join(import.meta.dirname, "src"), mode: "production", devtool: false, entry: { @@ -67,7 +67,7 @@ test.each([true, false])( adnbn$: path.join(root, "dist/index.js"), "adnbn/entry/content/vanilla$": path.join(root, "dist/entry/content/adapters/vanilla/index.js"), }, - modules: [path.join(__dirname, "src/vendor"), "node_modules"], + modules: [path.join(import.meta.dirname, "src/vendor"), "node_modules"], }, resolveLoader: {modules: [path.join(root, "node_modules")]}, optimization: {minimize: false, concatenateModules: false}, @@ -112,8 +112,10 @@ test.each([true, false])( } // User components and CSS-bearing libraries share JavaScript identity within the world too. - expect(layers(path.join(__dirname, "src/shared.js")).sort()).toEqual([worldLayer, mainLayer, null].sort()); - expect(layers(path.join(__dirname, "src/vendor/fixture-styles/index.js")).sort()).toEqual( + expect(layers(path.join(import.meta.dirname, "src/shared.js")).sort()).toEqual( + [worldLayer, mainLayer, null].sort() + ); + expect(layers(path.join(import.meta.dirname, "src/vendor/fixture-styles/index.js")).sort()).toEqual( [worldLayer, mainLayer, null].sort() ); diff --git a/tests/integration/build/style-routing/delivery-spike.integration.test.ts b/tests/integration/build/style-routing/delivery-spike.integration.test.ts index f1c3f0ac..72cbe782 100644 --- a/tests/integration/build/style-routing/delivery-spike.integration.test.ts +++ b/tests/integration/build/style-routing/delivery-spike.integration.test.ts @@ -239,7 +239,7 @@ test.each<{name: string; filename: Filename; cssFilename: Filename; commonChunks test("watch switches document/shadow/iframe delivery without changing JavaScript module identities", async () => { const output = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-delivery-watch-")); const selectionFile = path.join(output, "selection.json"); - const states = path.join(__dirname, "delivery-spike/states"); + const states = path.join(import.meta.dirname, "delivery-spike/states"); fs.copyFileSync(path.join(states, "none.json"), selectionFile); const compiler = await createCompiler(path.join(output, "dist"), {selectionFile}); let completion: ((error: Error | null, stats?: Stats) => void) | undefined; diff --git a/tests/integration/build/style-routing/delivery-spike/compiler.ts b/tests/integration/build/style-routing/delivery-spike/compiler.ts index cd4f9b8c..972a0a83 100644 --- a/tests/integration/build/style-routing/delivery-spike/compiler.ts +++ b/tests/integration/build/style-routing/delivery-spike/compiler.ts @@ -14,8 +14,8 @@ import ManifestV3 from "@cli/builders/manifest/ManifestV3"; import {Browser} from "@typing/browser"; import {ContentScriptStylesRuntimeProperty, ContentScriptWorld} from "@typing/content"; -export const project = path.resolve(__dirname, "../../../../.."); -export const fixtures = path.join(__dirname, "src"); +export const project = path.resolve(import.meta.dirname, "../../../../.."); +export const fixtures = path.join(import.meta.dirname, "src"); export const origin = "https://extension.test/"; export const contentEntries = ["normal", "shadow", "iframe", "relay", "switch"]; export const worldLayer = getContentLayer(ContentScriptWorld.Isolated); @@ -35,7 +35,7 @@ export const createCompiler = async (output: string, options: CompilerOptions = const cssFilename = options.cssFilename ?? "css/[name].[contenthash:8].css"; let selection = "none"; const config = { - rootDir: __dirname, + rootDir: import.meta.dirname, srcDir: "src", sharedDir: ".", appsDir: "apps", @@ -69,7 +69,7 @@ export const createCompiler = async (output: string, options: CompilerOptions = }, resolve: { alias: {"adnbn/entry/content/vanilla$": path.join(project, "dist/entry/content/adapters/vanilla/index.js")}, - modules: [path.resolve(__dirname, "../src/vendor"), path.join(project, "node_modules")], + modules: [path.resolve(import.meta.dirname, "../src/vendor"), path.join(project, "node_modules")], extensions: [".js"], }, resolveLoader: {modules: [path.join(project, "node_modules")]}, diff --git a/tests/integration/build/style-routing/routing-spike.integration.test.ts b/tests/integration/build/style-routing/routing-spike.integration.test.ts index bf4fc5cd..2dabb902 100644 --- a/tests/integration/build/style-routing/routing-spike.integration.test.ts +++ b/tests/integration/build/style-routing/routing-spike.integration.test.ts @@ -4,8 +4,8 @@ import path from "path"; import vm from "vm"; import {rspack, CssExtractRspackPlugin, type Compiler, type Filename, type Stats} from "@rspack/core"; -const project = path.resolve(__dirname, "../../../.."); -const fixtures = path.join(__dirname, "src"); +const project = path.resolve(import.meta.dirname, "../../../.."); +const fixtures = path.join(import.meta.dirname, "src"); const layer = "adnbn:css:isolation"; const boundary = "// object to store loaded CSS chunks"; @@ -116,11 +116,11 @@ const createCompiler = (output: string, filename: Filename, enabled: () => boole source.source = source.source.slice(0, position) + - `var loadPageStylesheet = loadStylesheet; - loadStylesheet = function(id) { + `var loadPageStylesheet = extractCssLoadStylesheet; + extractCssLoadStylesheet = function(id, fetchPriority) { return ${JSON.stringify(ids)}[id] ? globalThis.loadIsolatedCss(__webpack_require__.p + __webpack_require__.miniCssF(id)) - : loadPageStylesheet(id); + : loadPageStylesheet(id, fetchPriority); };\n` + source.source.slice(position); }); diff --git a/tests/integration/build/style-routing/selective-split.integration.test.ts b/tests/integration/build/style-routing/selective-split.integration.test.ts index 4d5e01aa..b7a3d857 100644 --- a/tests/integration/build/style-routing/selective-split.integration.test.ts +++ b/tests/integration/build/style-routing/selective-split.integration.test.ts @@ -1,4 +1,4 @@ -import {mkdtemp, readFile, rm, writeFile} from "fs/promises"; +import {mkdtemp, readFile, rename, rm, writeFile} from "fs/promises"; import os from "os"; import path from "path"; import type {NormalModule, Stats} from "@rspack/core"; @@ -134,8 +134,18 @@ test("ordinary initial CSS stays named and mixed in import order beside isolated test("watch adds and removes the shared lazy split and restores the ordinary CSS runtime", async () => { const output = await mkdtemp(path.join(os.tmpdir(), "adnbn-selective-watch-")); const selectionFile = path.join(output, "selection.json"); - const setState = async (state: string) => - writeFile(selectionFile, await readFile(path.join(__dirname, "delivery-spike/states", `${state}.json`))); + const setState = async (state: string) => { + const pendingFile = path.join(output, "selection-next.json"); + + await writeFile( + pendingFile, + await readFile(path.join(import.meta.dirname, "delivery-spike/states", `${state}.json`)) + ); + + // Publish complete JSON so watchRun cannot read during writeFile's truncate/write interval. + await rename(pendingFile, selectionFile); + }; + await setState("none"); const compiler = await createCompiler(output, false, {initial: true, selectionFile}); let queuedState: string | undefined; diff --git a/tests/integration/build/style-routing/selective-split/compiler.ts b/tests/integration/build/style-routing/selective-split/compiler.ts index 433a3a2b..fc743b48 100644 --- a/tests/integration/build/style-routing/selective-split/compiler.ts +++ b/tests/integration/build/style-routing/selective-split/compiler.ts @@ -16,8 +16,8 @@ import stylePlugin from "@cli/plugins/style"; import {ContentScriptStylesRuntimeProperty, ContentScriptWorld} from "@typing/content"; import type {ReadonlyConfig} from "@typing/config"; -export const project = path.resolve(__dirname, "../../../../.."); -export const fixture = path.join(__dirname, "src"); +export const project = path.resolve(import.meta.dirname, "../../../../.."); +export const fixture = path.join(import.meta.dirname, "src"); export const orders = ["document-first", "document-last"] as const; export const watchSelections = new WeakMap(); @@ -35,7 +35,7 @@ export const createCompiler = async ( const cssFilename = options.cssFilename ?? "css/[name].[contenthash:8].css"; const cssChunkFilename = cssFilename; const config = { - rootDir: __dirname, + rootDir: import.meta.dirname, srcDir: "src", sharedDir: ".", appsDir: "apps", diff --git a/tests/integration/build/style-routing/style-routing.integration.test.ts b/tests/integration/build/style-routing/style-routing.integration.test.ts index b97c4039..6a47f30e 100644 --- a/tests/integration/build/style-routing/style-routing.integration.test.ts +++ b/tests/integration/build/style-routing/style-routing.integration.test.ts @@ -74,11 +74,11 @@ test.each<{ withoutMap, cssFilename = "css/[name].[contenthash:8].css", }) => { - const root = path.resolve(__dirname, "../../../.."); + const root = path.resolve(import.meta.dirname, "../../../.."); const output = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-css-routing-")); const config = { - rootDir: __dirname, + rootDir: import.meta.dirname, srcDir: "src", sharedDir: ".", appsDir: "apps", @@ -114,7 +114,7 @@ test.each<{ ) as Configuration; const compiler = rspack({ - context: path.join(__dirname, "src"), + context: path.join(import.meta.dirname, "src"), mode: "production", devtool: false, entry: { @@ -131,7 +131,7 @@ test.each<{ resolveLoader: {modules: [path.join(root, "node_modules")]}, resolve: { alias: {adnbn$: path.join(root, "dist/index.js")}, - modules: [path.join(__dirname, "src/vendor"), "node_modules"], + modules: [path.join(import.meta.dirname, "src/vendor"), "node_modules"], }, module: {rules: [...styles.module!.rules!, ...assets.module!.rules!]}, optimization: { diff --git a/tests/integration/build/style/compiler.ts b/tests/integration/build/style/compiler.ts index e73e8c3f..6064a3c9 100644 --- a/tests/integration/build/style/compiler.ts +++ b/tests/integration/build/style/compiler.ts @@ -8,8 +8,8 @@ import assetPlugin from "@cli/plugins/asset"; import {Browser} from "@typing/browser"; import type {ReadonlyConfig} from "@typing/config"; -const project = path.resolve(__dirname, "../../../.."); -export const fixtures = path.join(__dirname, "fixtures"); +const project = path.resolve(import.meta.dirname, "../../../.."); +export const fixtures = path.join(import.meta.dirname, "fixtures"); export const entries = ["normal", "document", "asis"]; export const createFixture = async (name = "multi-app"): Promise => { diff --git a/tests/integration/build/style/fixtures/project-roots/adnbn.config.ts b/tests/integration/build/style/fixtures/project-roots/adnbn.config.ts new file mode 100644 index 00000000..a2ff5231 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/adnbn.config.ts @@ -0,0 +1,7 @@ +import {defineConfig} from "adnbn"; + +export default defineConfig({ + version: "1.0.0", + cssFilename: "[name].css", + assetsFilename: "[name][ext]", +}); diff --git a/tests/integration/build/style/fixtures/project-roots/css.svg b/tests/integration/build/style/fixtures/project-roots/css.svg new file mode 100644 index 00000000..2a848924 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/css.svg @@ -0,0 +1 @@ + diff --git a/tests/integration/build/style/fixtures/project-roots/package.json b/tests/integration/build/style/fixtures/project-roots/package.json new file mode 100644 index 00000000..5b84ef87 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/package.json @@ -0,0 +1,6 @@ +{ + "name": "adnbn-test-project-roots", + "private": true, + "type": "module", + "dependencies": {"adnbn": "file:../../../../../.."} +} diff --git a/tests/integration/build/style/fixtures/project-roots/scss.svg b/tests/integration/build/style/fixtures/project-roots/scss.svg new file mode 100644 index 00000000..b5572e52 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/scss.svg @@ -0,0 +1 @@ + diff --git a/tests/integration/build/style/fixtures/project-roots/src/probe.page.ts b/tests/integration/build/style/fixtures/project-roots/src/probe.page.ts new file mode 100644 index 00000000..68c534b3 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/src/probe.page.ts @@ -0,0 +1,7 @@ +import {definePage} from "adnbn"; +import "./styles.css?asis"; +import "./styles.scss?asis"; + +export default definePage({ + render: () => document.createElement("div"), +}); diff --git a/tests/integration/build/style/fixtures/project-roots/src/styles.css b/tests/integration/build/style/fixtures/project-roots/src/styles.css new file mode 100644 index 00000000..ae8b579c --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/src/styles.css @@ -0,0 +1,3 @@ +.from-css { + background-image: url(/css.svg); +} diff --git a/tests/integration/build/style/fixtures/project-roots/src/styles.scss b/tests/integration/build/style/fixtures/project-roots/src/styles.scss new file mode 100644 index 00000000..2b0fe36b --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/src/styles.scss @@ -0,0 +1,5 @@ +$image: "/scss.svg"; + +.from-scss { + background-image: url($image); +} diff --git a/tests/integration/build/style/fixtures/project-roots/tsconfig.json b/tests/integration/build/style/fixtures/project-roots/tsconfig.json new file mode 100644 index 00000000..eadf1be8 --- /dev/null +++ b/tests/integration/build/style/fixtures/project-roots/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.adnbn/tsconfig.json" +} diff --git a/tests/integration/build/style/fixtures/virtual-theme/asis.js b/tests/integration/build/style/fixtures/virtual-theme/asis.js index 1f2157f7..89516c8f 100644 --- a/tests/integration/build/style/fixtures/virtual-theme/asis.js +++ b/tests/integration/build/style/fixtures/virtual-theme/asis.js @@ -1,3 +1 @@ -import styles from "./generated/style.scss?asis"; - -export default styles; +import "./generated/style.scss?asis"; diff --git a/tests/integration/build/style/resolve-style-urls-loader.cjs b/tests/integration/build/style/resolve-style-urls-loader.cjs deleted file mode 100644 index 419954ee..00000000 --- a/tests/integration/build/style/resolve-style-urls-loader.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// Rspack uses native import() for ESM loaders, which Jest's CJS VM does not host. -// Adapt only module loading; the built production loader still owns all behavior. -// The native consumer smoke test additionally exercises its real ESM entry. -module.exports = require("../../../../dist/cli/bundler/loaders/resolve-style-urls.js").default; diff --git a/tests/integration/build/style/roots.integration.test.ts b/tests/integration/build/style/roots.integration.test.ts new file mode 100644 index 00000000..24f0c578 --- /dev/null +++ b/tests/integration/build/style/roots.integration.test.ts @@ -0,0 +1,30 @@ +import {readFile, readdir} from "node:fs/promises"; +import path from "node:path"; + +import {createIntegrationFixture} from "../../utils/fixture"; + +const root = path.resolve(import.meta.dirname, "../../../.."); +const source = path.join(import.meta.dirname, "fixtures", "project-roots"); + +test("resolves root-relative CSS and SCSS URLs from the consumer project through the full style pipeline", async () => { + const fixture = await createIntegrationFixture(root, source); + + try { + const output = await fixture.build(); + const files = await readdir(path.join(output, "assets")); + + expect(files.sort()).toEqual(["css.svg", "scss.svg"]); + + for (const file of files) { + expect(await readFile(path.join(output, "assets", file), "utf8")).toBe( + await readFile(path.join(source, file), "utf8") + ); + } + + const css = await readFile(path.join(output, "css", "probe.page.css"), "utf8"); + expect(css).toContain("assets/css.svg"); + expect(css).toContain("assets/scss.svg"); + } finally { + await fixture.dispose(); + } +}); diff --git a/tests/integration/build/style/virtual-style.integration.test.ts b/tests/integration/build/style/virtual-style.integration.test.ts index ed82c80c..b6688ba0 100644 --- a/tests/integration/build/style/virtual-style.integration.test.ts +++ b/tests/integration/build/style/virtual-style.integration.test.ts @@ -62,7 +62,9 @@ const addExternalUrlResolver = (target: Compiler): void => { return original.flatMap((item, index) => { const entry = typeof item === "string" ? {loader: item} : item; - if (!/(?:^|[/\\])sass-loader(?:[/\\]|$)/.test(entry.loader)) { + // Rspack 2's RuleSetUseItem includes Falsy for conditional loaders (flag && loader). + // Preserve those entries while adapting only the existing Sass loader. + if (!entry || !/(?:^|[/\\])sass-loader(?:[/\\]|$)/.test(entry.loader)) { return [item]; } @@ -125,7 +127,7 @@ test("the production style pipeline loads its private ESM loader under native No const {stdout, stderr} = await promisify(execFile)( process.execPath, - [path.join(__dirname, "verify-native-consumer.mjs"), root], + [path.join(import.meta.dirname, "verify-native-consumer.mjs"), root], {timeout: 15_000, maxBuffer: 1024 * 1024} ); diff --git a/tests/integration/build/typescript/fixture/adnbn.config.ts b/tests/integration/build/typescript/fixture/adnbn.config.ts new file mode 100644 index 00000000..78232794 --- /dev/null +++ b/tests/integration/build/typescript/fixture/adnbn.config.ts @@ -0,0 +1,3 @@ +import {defineConfig} from "adnbn"; + +export default defineConfig({version: "1.0.0", jsFilename: "[name].js"}); diff --git a/tests/integration/build/typescript/fixture/package.json b/tests/integration/build/typescript/fixture/package.json new file mode 100644 index 00000000..badf1874 --- /dev/null +++ b/tests/integration/build/typescript/fixture/package.json @@ -0,0 +1,8 @@ +{ + "name": "adnbn-test-type-reexports", + "private": true, + "type": "module", + "dependencies": { + "adnbn": "file:../../../../.." + } +} diff --git a/tests/integration/build/typescript/fixture/src/barrel.ts b/tests/integration/build/typescript/fixture/src/barrel.ts new file mode 100644 index 00000000..c59fc0e4 --- /dev/null +++ b/tests/integration/build/typescript/fixture/src/barrel.ts @@ -0,0 +1 @@ +export {Options, Label, value} from "./types"; diff --git a/tests/integration/build/typescript/fixture/src/probe.page.ts b/tests/integration/build/typescript/fixture/src/probe.page.ts new file mode 100644 index 00000000..009d9449 --- /dev/null +++ b/tests/integration/build/typescript/fixture/src/probe.page.ts @@ -0,0 +1,11 @@ +import {definePage} from "adnbn"; +import {value} from "./barrel"; + +export default definePage({ + render: () => { + const element = document.createElement("div"); + element.textContent = value; + + return element; + }, +}); diff --git a/tests/integration/build/typescript/fixture/src/types.ts b/tests/integration/build/typescript/fixture/src/types.ts new file mode 100644 index 00000000..a20ce9e4 --- /dev/null +++ b/tests/integration/build/typescript/fixture/src/types.ts @@ -0,0 +1,6 @@ +export interface Options { + label: string; +} + +export type Label = string; +export const value = "type-reexports-value"; diff --git a/tests/integration/build/typescript/fixture/src/values.js b/tests/integration/build/typescript/fixture/src/values.js new file mode 100644 index 00000000..6555fcc2 --- /dev/null +++ b/tests/integration/build/typescript/fixture/src/values.js @@ -0,0 +1 @@ +export const value = "type-reexports-value"; diff --git a/tests/integration/build/typescript/fixture/tsconfig.json b/tests/integration/build/typescript/fixture/tsconfig.json new file mode 100644 index 00000000..eadf1be8 --- /dev/null +++ b/tests/integration/build/typescript/fixture/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.adnbn/tsconfig.json" +} diff --git a/tests/integration/build/typescript/states/missing-type.ts b/tests/integration/build/typescript/states/missing-type.ts new file mode 100644 index 00000000..9a7f1684 --- /dev/null +++ b/tests/integration/build/typescript/states/missing-type.ts @@ -0,0 +1 @@ +export {Missing, value} from "./types"; diff --git a/tests/integration/build/typescript/states/missing-value.ts b/tests/integration/build/typescript/states/missing-value.ts new file mode 100644 index 00000000..8361e3c7 --- /dev/null +++ b/tests/integration/build/typescript/states/missing-value.ts @@ -0,0 +1 @@ +export {Missing, value} from "./values.js"; diff --git a/tests/integration/build/typescript/states/package-explicit.ts b/tests/integration/build/typescript/states/package-explicit.ts new file mode 100644 index 00000000..62ad4b42 --- /dev/null +++ b/tests/integration/build/typescript/states/package-explicit.ts @@ -0,0 +1,2 @@ +export type {UserConfig} from "adnbn"; +export {value} from "./types"; diff --git a/tests/integration/build/typescript/states/package-implicit.ts b/tests/integration/build/typescript/states/package-implicit.ts new file mode 100644 index 00000000..fff7a260 --- /dev/null +++ b/tests/integration/build/typescript/states/package-implicit.ts @@ -0,0 +1,2 @@ +export {UserConfig} from "adnbn"; +export {value} from "./types"; diff --git a/tests/integration/build/typescript/states/unsupported.swcrc b/tests/integration/build/typescript/states/unsupported.swcrc new file mode 100644 index 00000000..9493c9a8 --- /dev/null +++ b/tests/integration/build/typescript/states/unsupported.swcrc @@ -0,0 +1,7 @@ +{ + "jsc": { + "experimental": { + "plugins": [["adnbn-deliberately-missing-swc-plugin", {}]] + } + } +} diff --git a/tests/integration/build/typescript/typescript.integration.test.ts b/tests/integration/build/typescript/typescript.integration.test.ts new file mode 100644 index 00000000..30f786c7 --- /dev/null +++ b/tests/integration/build/typescript/typescript.integration.test.ts @@ -0,0 +1,83 @@ +import {readFile, writeFile} from "node:fs/promises"; +import path from "node:path"; + +import {createIntegrationFixture} from "../../utils/fixture"; +import {run} from "../../utils/process"; + +const root = path.resolve(import.meta.dirname, "../../../.."); +const source = path.join(import.meta.dirname, "fixture"); + +test("builds verified type re-exports alongside values in production and development", async () => { + const fixture = await createIntegrationFixture(root, source); + + try { + const output = await fixture.build(); + const script = path.join(output, "js", "probe.page.js"); + + expect(await readFile(script, "utf8")).toContain("type-reexports-value"); + + await run( + process.execPath, + [path.join(root, "bin/adnbn.js"), "build", ".", "--mode", "development"], + fixture.directory + ); + + expect(await readFile(script, "utf8")).toContain("type-reexports-value"); + } finally { + await fixture.dispose(); + } +}); + +test.each(["missing-type", "missing-value"])("rejects a genuinely absent export (%s)", async scenario => { + const fixture = await createIntegrationFixture(root, source); + + try { + await writeFile( + path.join(fixture.directory, "src", "barrel.ts"), + await readFile(path.join(import.meta.dirname, "states", `${scenario}.ts`)) + ); + + await expect(fixture.build()).rejects.toThrow(/export 'Missing'.*not found/); + } finally { + await fixture.dispose(); + } +}); + +test("requires export type for type-only re-exports from a package with JavaScript and declarations", async () => { + const fixture = await createIntegrationFixture(root, source); + + try { + await writeFile( + path.join(fixture.directory, "src", "barrel.ts"), + await readFile(path.join(import.meta.dirname, "states", "package-implicit.ts")) + ); + + await expect(fixture.build()).rejects.toThrow(/export 'UserConfig'.*not found/); + + await writeFile( + path.join(fixture.directory, "src", "barrel.ts"), + await readFile(path.join(import.meta.dirname, "states", "package-explicit.ts")) + ); + + const output = await fixture.build(); + + expect(await readFile(path.join(output, "js", "probe.page.js"), "utf8")).toContain("type-reexports-value"); + } finally { + await fixture.dispose(); + } +}); + +test("does not load the unsupported .swcrc configuration", async () => { + const fixture = await createIntegrationFixture(root, source); + + try { + await writeFile( + path.join(fixture.directory, ".swcrc"), + await readFile(path.join(import.meta.dirname, "states", "unsupported.swcrc")) + ); + + await expect(fixture.build()).resolves.toBe(path.join(fixture.directory, "dist", "addon-chrome-mv3")); + } finally { + await fixture.dispose(); + } +}); diff --git a/tests/integration/build/virtual-modules/virtual-modules.integration.test.ts b/tests/integration/build/virtual-modules/virtual-modules.integration.test.ts index afe15ca3..596c42f6 100644 --- a/tests/integration/build/virtual-modules/virtual-modules.integration.test.ts +++ b/tests/integration/build/virtual-modules/virtual-modules.integration.test.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {copyFile, readFile} from "fs/promises"; import path from "path"; import vm from "vm"; @@ -7,7 +9,7 @@ import {createIntegrationFixture} from "../../utils/fixture"; jest.setTimeout(60_000); test("generated entrypoint data reach public getters and disappear without consumers", async () => { - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(__dirname, "fixture")); + const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, path.join(import.meta.dirname, "fixture")); try { const directory = await fixture.build(); @@ -37,7 +39,7 @@ test("generated entrypoint data reach public getters and disappear without consu ); await copyFile( - path.join(__dirname, "states/unused-background.ts"), + path.join(import.meta.dirname, "states/unused-background.ts"), path.join(fixture.directory, "src/background.ts") ); await fixture.build(); diff --git a/tests/integration/types/content.integration.test.ts b/tests/integration/types/content.integration.test.ts index d2317eb4..6733f5f1 100644 --- a/tests/integration/types/content.integration.test.ts +++ b/tests/integration/types/content.integration.test.ts @@ -2,9 +2,9 @@ import path from "path"; import ts from "typescript"; describe("content contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures/content/definition.tsx"); - const watchFixture = path.join(__dirname, "fixtures/content/watch.ts"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures/content/definition.tsx"); + const watchFixture = path.join(import.meta.dirname, "fixtures/content/watch.ts"); test.each(["source", "package"])( "checks content definitions and watch strategies through the %s API", @@ -31,11 +31,11 @@ describe("content contracts", () => { const program = ts.createProgram( [ fixture, - path.join(__dirname, "fixtures/content/styles.ts"), + path.join(import.meta.dirname, "fixtures/content/styles.ts"), watchFixture, - path.join(__dirname, "fixtures/content/prepare.tsx"), - path.join(__dirname, "fixtures/content/target.tsx"), - path.join(__dirname, "fixtures/content/boundary.tsx"), + path.join(import.meta.dirname, "fixtures/content/prepare.tsx"), + path.join(import.meta.dirname, "fixtures/content/target.tsx"), + path.join(import.meta.dirname, "fixtures/content/boundary.tsx"), ], options, host diff --git a/tests/integration/types/html.integration.test.ts b/tests/integration/types/html.integration.test.ts index 5781fdd8..c22764ec 100644 --- a/tests/integration/types/html.integration.test.ts +++ b/tests/integration/types/html.integration.test.ts @@ -2,8 +2,8 @@ import path from "path"; import ts from "typescript"; describe("HTML contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures/html/options.ts"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures/html/options.ts"); test.each(["source", "package"])( "checks static entrypoint and full configuration options through the %s API", diff --git a/tests/integration/types/locale.integration.test.ts b/tests/integration/types/locale.integration.test.ts index 2e009de5..366c6259 100644 --- a/tests/integration/types/locale.integration.test.ts +++ b/tests/integration/types/locale.integration.test.ts @@ -2,7 +2,7 @@ import path from "path"; import ts from "typescript"; describe("locale contracts", () => { - const fixture = path.join(__dirname, "fixtures/locale/substitutions.ts"); + const fixture = path.join(import.meta.dirname, "fixtures/locale/substitutions.ts"); test.each(["source", "package"])( "checks substitution arguments and plural keys through the %s API", diff --git a/tests/integration/types/manifest.integration.test.ts b/tests/integration/types/manifest.integration.test.ts index 01b0e44a..a617d4a4 100644 --- a/tests/integration/types/manifest.integration.test.ts +++ b/tests/integration/types/manifest.integration.test.ts @@ -2,8 +2,8 @@ import path from "node:path"; import ts from "typescript"; describe("manifest contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures", "manifest", "builder.ts"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures", "manifest", "builder.ts"); test.each(["source", "package"])( "checks public manifest types and builder boundaries through the %s API", diff --git a/tests/integration/types/page.integration.test.ts b/tests/integration/types/page.integration.test.ts index 2a8a0cf6..711ad837 100644 --- a/tests/integration/types/page.integration.test.ts +++ b/tests/integration/types/page.integration.test.ts @@ -2,8 +2,8 @@ import path from "path"; import ts from "typescript"; describe("page contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures/page/definition.ts"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures/page/definition.ts"); test.each(["source", "package"])( "checks page permissions, CSP and render props through the %s API", diff --git a/tests/integration/types/registries.integration.test.ts b/tests/integration/types/registries.integration.test.ts index 6a329674..e964b653 100644 --- a/tests/integration/types/registries.integration.test.ts +++ b/tests/integration/types/registries.integration.test.ts @@ -12,8 +12,8 @@ import TransportDeclaration, { import type {ReadonlyConfig} from "@typing/config"; describe("shared registries", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixtureDir = path.join(__dirname, "fixtures/registries"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixtureDir = path.join(import.meta.dirname, "fixtures/registries"); let rootDir: string; let filled: string; let empty: string; diff --git a/tests/integration/types/relay.integration.test.ts b/tests/integration/types/relay.integration.test.ts index 90be4cde..d9c066a3 100644 --- a/tests/integration/types/relay.integration.test.ts +++ b/tests/integration/types/relay.integration.test.ts @@ -2,8 +2,8 @@ import path from "path"; import ts from "typescript"; describe("Relay entrypoint contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures/relay/definition.ts"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures/relay/definition.ts"); test.each(["source", "package"])( "checks Relay constructors and bootstrap through the %s API", @@ -29,8 +29,8 @@ describe("Relay entrypoint contracts", () => { if (mode === "source") { rootNames.push( - path.join(__dirname, "fixtures/relay/register.ts"), - path.join(__dirname, "fixtures/relay/virtual.ts"), + path.join(import.meta.dirname, "fixtures/relay/register.ts"), + path.join(import.meta.dirname, "fixtures/relay/virtual.ts"), path.join(projectDir, "src/cli/virtual/relay.ts"), path.join(projectDir, "src/cli/virtual/virtual.d.ts") ); diff --git a/tests/integration/types/view.integration.test.ts b/tests/integration/types/view.integration.test.ts index 85e2fa91..e9c13877 100644 --- a/tests/integration/types/view.integration.test.ts +++ b/tests/integration/types/view.integration.test.ts @@ -2,8 +2,8 @@ import path from "path"; import ts from "typescript"; describe("view contracts", () => { - const projectDir = path.resolve(__dirname, "../../.."); - const fixture = path.join(__dirname, "fixtures/view/definition.tsx"); + const projectDir = path.resolve(import.meta.dirname, "../../.."); + const fixture = path.join(import.meta.dirname, "fixtures/view/definition.tsx"); test.each(["source", "package"])( "checks synchronous view renders, asynchronous containers and entrypoint permissions through the %s API", diff --git a/tests/integration/utils/fixture.test.ts b/tests/integration/utils/fixture.test.ts index 4b199e2a..211d8639 100644 --- a/tests/integration/utils/fixture.test.ts +++ b/tests/integration/utils/fixture.test.ts @@ -2,7 +2,7 @@ import path from "node:path"; import {findIntegrationFixtures} from "./fixture"; -const integration = path.resolve(__dirname, ".."); +const integration = path.resolve(import.meta.dirname, ".."); test("does not prepare internal CLI configs as standalone consumer applications", async () => { expect(await findIntegrationFixtures(path.join(integration, "build/cli"))).toEqual([]); diff --git a/tests/integration/utils/process.test.ts b/tests/integration/utils/process.test.ts index 6abcc1e0..9e899afe 100644 --- a/tests/integration/utils/process.test.ts +++ b/tests/integration/utils/process.test.ts @@ -32,7 +32,7 @@ test("terminates a wrapper and all descendants with inherited output on timeout" let pids: number[] = []; const outcome = run( process.execPath, - [path.join(__dirname, "fixtures/process-tree.cjs"), "2", marker], + [path.join(import.meta.dirname, "fixtures/process-tree.cjs"), "2", marker], ADNBN_TEST_ROOT, 2000, {stdio: "inherit", killTree: true} diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts index c4d5308a..6389c7a1 100644 --- a/tests/jest.setup.ts +++ b/tests/jest.setup.ts @@ -1,3 +1,5 @@ +import {jest} from "@jest/globals"; + import {startBrowserTest, stopBrowserTest} from "./browser-harness/session"; beforeEach(startBrowserTest); diff --git a/tests/main-source-policy.test.ts b/tests/main-source-policy.test.ts index 73247702..66f65b03 100644 --- a/tests/main-source-policy.test.ts +++ b/tests/main-source-policy.test.ts @@ -3,7 +3,7 @@ import {readFileSync} from "node:fs"; import path from "node:path"; import {load} from "js-yaml"; -const root = path.resolve(__dirname, ".."); +const root = path.resolve(import.meta.dirname, ".."); const script = path.join(root, ".github/scripts/validateMainPullRequestSource.mjs"); const runPolicy = (input: string) => spawnSync(process.execPath, [script], { diff --git a/tests/raw-module-resolver.cjs b/tests/raw-module-resolver.cjs index 2d86da7f..a28ef334 100644 --- a/tests/raw-module-resolver.cjs +++ b/tests/raw-module-resolver.cjs @@ -2,10 +2,5 @@ module.exports = (request, options) => { const suffix = "?raw"; const target = request.endsWith(suffix) ? request.slice(0, -suffix.length) : request; - // Storage publishes ESM only. SWC transforms that entry for Jest's CJS runtime. - if (target === "@addon-core/storage" || target.startsWith("@addon-core/storage/")) { - return options.defaultResolver(target, {...options, conditions: ["import", "default"]}); - } - return options.defaultResolver(target, options); }; diff --git a/tests/raw-module-transformer.cjs b/tests/raw-module-transformer.cjs index 3b9959fb..02b43c86 100644 --- a/tests/raw-module-transformer.cjs +++ b/tests/raw-module-transformer.cjs @@ -1,8 +1,13 @@ module.exports = { getCacheKey(source, filename) { - return require("crypto").createHash("sha256").update(source).update(filename).update("raw-v2").digest("hex"); + return require("crypto") + .createHash("sha256") + .update(source) + .update(filename) + .update("raw-esm-v3") + .digest("hex"); }, process(source) { - return {code: `module.exports = ${JSON.stringify(source)};`}; + return {code: `export default ${JSON.stringify(source)};`}; }, }; diff --git a/tests/release-automation.test.ts b/tests/release-automation.test.ts index 4816d54d..c72aece9 100644 --- a/tests/release-automation.test.ts +++ b/tests/release-automation.test.ts @@ -1,3 +1,6 @@ +import {createRequire} from "node:module"; +import {jest} from "@jest/globals"; + import {spawnSync} from "node:child_process"; import path from "node:path"; @@ -8,13 +11,15 @@ type Execute = ( options: {cwd: string; env: Environment; shell: false; stdio: "inherit"} ) => {status: number | null; error?: Error}; +const require = createRequire(import.meta.url); + const {shouldRelease, getReleaseArguments, runRelease} = require("../.github/scripts/releaseAutomation.cjs") as { shouldRelease: (env: Environment) => boolean; getReleaseArguments: (env: Environment) => string[]; runRelease: (env: Environment, execute: Execute) => number; }; -const root = path.resolve(__dirname, ".."); +const root = path.resolve(import.meta.dirname, ".."); const script = path.join(root, ".github/scripts/releaseAutomation.cjs"); const environment = (overrides: Environment = {}): Environment => ({ GITHUB_EVENT_NAME: "push", diff --git a/tests/release-it.test.ts b/tests/release-it.test.ts index e424744c..0ba5a7f0 100644 --- a/tests/release-it.test.ts +++ b/tests/release-it.test.ts @@ -1,3 +1,4 @@ +import {createRequire} from "node:module"; import {execFileSync} from "node:child_process"; import path from "node:path"; @@ -11,6 +12,8 @@ type ReleaseCommit = { type Bump = {level: 0 | 1 | 2} | null; +const require = createRequire(import.meta.url); + const {whatBump} = require("../.release-it.cjs") as { whatBump: (commits: ReleaseCommit[], currentVersion?: string) => Bump; }; @@ -105,7 +108,7 @@ describe("release-it GitHub release notes", () => { `, ], { - cwd: path.resolve(__dirname, ".."), + cwd: path.resolve(import.meta.dirname, ".."), encoding: "utf8", input: JSON.stringify({message, currentVersion}), timeout: 10_000,