diff --git a/package-lock.json b/package-lock.json index eaff58c4..1dee126e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@highcharts/react": "^5.2.2", "@next/third-parties": "16.3.0", "@opennextjs/cloudflare": "^1.20.2", - "axios": "^1.19.0", "gray-matter": "^4.0.3", "gsap": "^3.15.0", "html-entities": "^2.6.0", @@ -25,13 +24,10 @@ "next-intl": "^4.13.4", "next-mdx-remote": "^6.0.0", "openapi-fetch": "^0.17.0", - "prism-themes": "^1.9.0", - "prismjs": "^1.30.0", "react": "^19.2.8", "react-dom": "^19.2.8", "react-icons": "^5.7.0", "react-share": "^5.3.0", - "react-slick": "^0.31.0", "react-tabs": "^6.1.1", "react-world-flags": "^1.6.0", "reading-time": "^1.5.0", @@ -43,7 +39,6 @@ "remark-rehype": "^11.1.2", "rss": "^1.2.2", "schema-dts": "^2.0.0", - "slick-carousel": "^1.8.1", "sugar-high": "^1.2.1", "swiper": "^14.0.7", "unified": "^11.0.5" @@ -58,15 +53,12 @@ "@testing-library/user-event": "^14.6.3", "@types/lodash": "^4.17.25", "@types/node": "^26.1.2", - "@types/prismjs": "^1.26.6", "@types/react": "19.2.18", "@types/react-dom": "19.2.4", - "@types/react-slick": "^0.23.13", "@types/react-world-flags": "^1.6.0", "@types/rss": "^0.0.32", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^4.1.10", - "axios-mock-adapter": "^2.1.0", "babel-plugin-react-compiler": "^1.0.0", "eslint": "^10.8.0", "eslint-config-next": "16.3.0", @@ -243,9 +235,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -262,9 +251,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -281,9 +267,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -300,9 +283,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2887,9 +2867,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2906,9 +2883,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2925,9 +2899,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2944,9 +2915,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2963,9 +2931,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2982,9 +2947,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3001,9 +2963,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3020,9 +2979,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -3039,9 +2995,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3064,9 +3017,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3089,9 +3039,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3114,9 +3061,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3139,9 +3083,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3164,9 +3105,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3189,9 +3127,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3214,9 +3149,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3546,9 +3478,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3565,9 +3494,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3584,9 +3510,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3603,9 +3526,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6247,9 +6167,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6267,9 +6184,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6287,9 +6201,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -6307,9 +6218,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -6744,13 +6652,6 @@ "form-data": "^4.0.4" } }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { "version": "19.2.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", @@ -6770,15 +6671,6 @@ "@types/react": "^19.2.0" } }, - "node_modules/@types/react-slick": { - "version": "0.23.13", - "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz", - "integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-world-flags": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@types/react-world-flags/-/react-world-flags-1.6.0.tgz", @@ -7919,56 +7811,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", - "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.6", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/axios-mock-adapter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz", - "integrity": "sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "is-buffer": "^2.0.5" - }, - "peerDependencies": { - "axios": ">= 0.17.0" - } - }, - "node_modules/axios/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/axios/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -10123,26 +9965,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -11394,29 +11216,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-bun-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", @@ -11892,13 +11691,6 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/jquery": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-4.0.0.tgz", - "integrity": "sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==", - "license": "MIT", - "peer": true - }, "node_modules/js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", @@ -12093,14 +11885,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json2mq": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", - "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", - "dependencies": { - "string-convert": "^0.2.0" - } - }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -12501,11 +12285,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -13824,9 +13603,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13843,9 +13619,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13862,9 +13635,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13881,9 +13651,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13900,9 +13667,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13919,9 +13683,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13938,9 +13699,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13957,9 +13715,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -13976,9 +13731,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14001,9 +13753,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14026,9 +13775,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14051,9 +13797,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14076,9 +13819,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14101,9 +13841,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14126,9 +13863,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14151,9 +13885,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -14544,6 +14275,17 @@ } } }, + "node_modules/next-intl/node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/next-mdx-remote": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-6.0.0.tgz", @@ -15287,21 +15029,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/prism-themes": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/prism-themes/-/prism-themes-1.9.0.tgz", - "integrity": "sha512-tX2AYsehKDw1EORwBps+WhBFKc2kxfoFpQAjxBndbZKr4fRmMkv47XN0BghC/K1qwodB1otbe4oF23vUTFDokw==", - "license": "MIT" - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -15335,15 +15062,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -15472,22 +15190,6 @@ "react": "^17 || ^18 || ^19" } }, - "node_modules/react-slick": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.31.0.tgz", - "integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==", - "license": "MIT", - "dependencies": { - "classnames": "^2.2.5", - "json2mq": "^0.2.0", - "lodash.debounce": "^4.0.8", - "resize-observer-polyfill": "^1.5.0" - }, - "peerDependencies": { - "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-tabs": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-6.1.1.tgz", @@ -15794,11 +15496,6 @@ "node": ">=0.10.0" } }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -16394,14 +16091,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/slick-carousel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", - "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", - "peerDependencies": { - "jquery": ">=1.8.0" - } - }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -16496,11 +16185,6 @@ "node": ">= 0.4" } }, - "node_modules/string-convert": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", - "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" - }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", diff --git a/package.json b/package.json index e13cfc3e..9d580150 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@highcharts/react": "^5.2.2", "@next/third-parties": "16.3.0", "@opennextjs/cloudflare": "^1.20.2", - "axios": "^1.19.0", "gray-matter": "^4.0.3", "gsap": "^3.15.0", "html-entities": "^2.6.0", @@ -36,13 +35,10 @@ "next-intl": "^4.13.4", "next-mdx-remote": "^6.0.0", "openapi-fetch": "^0.17.0", - "prism-themes": "^1.9.0", - "prismjs": "^1.30.0", "react": "^19.2.8", "react-dom": "^19.2.8", "react-icons": "^5.7.0", "react-share": "^5.3.0", - "react-slick": "^0.31.0", "react-tabs": "^6.1.1", "react-world-flags": "^1.6.0", "reading-time": "^1.5.0", @@ -54,7 +50,6 @@ "remark-rehype": "^11.1.2", "rss": "^1.2.2", "schema-dts": "^2.0.0", - "slick-carousel": "^1.8.1", "sugar-high": "^1.2.1", "swiper": "^14.0.7", "unified": "^11.0.5" @@ -69,15 +64,12 @@ "@testing-library/user-event": "^14.6.3", "@types/lodash": "^4.17.25", "@types/node": "^26.1.2", - "@types/prismjs": "^1.26.6", "@types/react": "19.2.18", "@types/react-dom": "19.2.4", - "@types/react-slick": "^0.23.13", "@types/react-world-flags": "^1.6.0", "@types/rss": "^0.0.32", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^4.1.10", - "axios-mock-adapter": "^2.1.0", "babel-plugin-react-compiler": "^1.0.0", "eslint": "^10.8.0", "eslint-config-next": "16.3.0", diff --git a/src/app/[locale]/[...slug]/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/[...slug]/__tests__/__snapshots__/page.test.tsx.snap index 443dd056..94811251 100644 --- a/src/app/[locale]/[...slug]/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/[...slug]/__tests__/__snapshots__/page.test.tsx.snap @@ -356,32 +356,8 @@ exports[`AsciidocPage > renders correctly for installation page 1`] = `
-
- Text -
-
- - Author - -
-
+ data-testid="author-list" + />
@@ -660,32 +636,8 @@ exports[`AsciidocPage > renders correctly with English locale 1`] = `
-
- Text -
-
- - Author - -
-
+ data-testid="author-list" + />
@@ -999,32 +951,8 @@ exports[`AsciidocPage > renders correctly with non-English locale 1`] = `
-
- Text -
-
- - Author - -
-
+ data-testid="author-list" + />
diff --git a/src/app/[locale]/[...slug]/__tests__/page.test.tsx b/src/app/[locale]/[...slug]/__tests__/page.test.tsx index f5027307..0a8cbd49 100644 --- a/src/app/[locale]/[...slug]/__tests__/page.test.tsx +++ b/src/app/[locale]/[...slug]/__tests__/page.test.tsx @@ -10,6 +10,12 @@ vi.mock("next/navigation", () => ({ notFound: vi.fn(), })); +// AuthorList is an async Server Component; stub it so the page tree renders +// synchronously under react-dom (which cannot render async children). +vi.mock("@/components/Content/Asciidoc/AuthorList", () => ({ + default: () =>
, +})); + vi.mock("next-intl/server", () => ({ getTranslations: vi.fn().mockResolvedValue({ rich: vi.fn(() =>
Translated warning
), diff --git a/src/app/[locale]/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/__tests__/__snapshots__/page.test.tsx.snap index e6399de2..36ec7830 100644 --- a/src/app/[locale]/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/__tests__/__snapshots__/page.test.tsx.snap @@ -2,16 +2,16 @@ exports[`Index page > renders correctly 1`] = `
renders correctly 1`] = `
renders correctly 1`] = `
renders correctly 1`] = ` class="min-h-screen light:bg-white" >
-
-
-
-
-
-
- - - -
- -
-
- -
-
-

- Text -

-

- Text -

-
-
-
+ data-testid="documentation-header" + />
diff --git a/src/app/[locale]/docs/__tests__/page.test.tsx b/src/app/[locale]/docs/__tests__/page.test.tsx index 3ebb9990..c722e224 100644 --- a/src/app/[locale]/docs/__tests__/page.test.tsx +++ b/src/app/[locale]/docs/__tests__/page.test.tsx @@ -7,6 +7,12 @@ vi.mock("next-intl/server", () => ({ getTranslations: vi.fn().mockResolvedValue((key: string) => key), })); +// DocumentationHeader is an async Server Component; stub it so the page tree +// renders synchronously under react-dom (which cannot render async children). +vi.mock("@/components/Content/Documentation/Header", () => ({ + default: () =>
, +})); + import Docs from "../page"; describe("Docs page", () => { diff --git a/src/app/[locale]/download/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/download/__tests__/__snapshots__/page.test.tsx.snap index d3e864a3..a2ea7a2d 100644 --- a/src/app/[locale]/download/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/download/__tests__/__snapshots__/page.test.tsx.snap @@ -2,7 +2,7 @@ exports[`Download page > renders correctly 1`] = `
renders correctly 1`] = `
Eclipse Temurin

Text
renders correctly 1`] = ` > Text Text @@ -62,10 +62,10 @@ exports[`Download page > renders correctly 1`] = ` class="w-full max-w-6xl px-4 md:px-6 mb-20 relative z-10" >

Text

renders correctly 1`] = `

Text

diff --git a/src/app/[locale]/download/client.tsx b/src/app/[locale]/download/client.tsx index e76c9815..3860c1ff 100644 --- a/src/app/[locale]/download/client.tsx +++ b/src/app/[locale]/download/client.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import { useSearchParams, redirect } from "next/navigation"; @@ -40,7 +41,7 @@ export default function DownloadPageClient() { } return ( -
+
{/* Meta Redirect for Auto-Download */} {link && } @@ -54,7 +55,7 @@ export default function DownloadPageClient() { {vendor === "Adoptium" ? ( <> {t("thank-you")}
- + Eclipse Temurin @@ -63,21 +64,21 @@ export default function DownloadPageClient() { {t.rich("vendor-alert", { vendor: capitalize(vendor), vendorWrapper: (chunks) => ( - {chunks} + {chunks} ), })} )} -

+

{t("auto-start-p1")}
{t("auto-start-p2")}
{t("click-here")} @@ -88,15 +89,15 @@ export default function DownloadPageClient() { {/* The Appeal Section - The "Sexy" Part */}

-
+
{/* Background Effects */} -
+
{/* Left Column: The "Hook" */}
-
+
{t("community-action")}
@@ -104,14 +105,14 @@ export default function DownloadPageClient() {

{t("free-to-use")}
- {t("not-free-to-build")} + {t("not-free-to-build")}

-

+

“{t("appeal-quote")}”

-

+

{t("appeal-text")}

@@ -120,7 +121,7 @@ export default function DownloadPageClient() { href="https://github.com/sponsors/adoptium" target="_blank" rel="noopener noreferrer" - className="inline-flex items-center justify-center gap-3 bg-[#ff1365] hover:bg-[#ff4f8b] text-white px-8 py-4 rounded-xl font-bold text-lg transition-all transform hover:-translate-y-1 hover:shadow-lg shadow-[#ff1365]/20" + className="inline-flex items-center justify-center gap-3 bg-pink hover:bg-[#ff4f8b] text-white px-8 py-4 rounded-xl font-bold text-lg transition-all transform hover:-translate-y-1 hover:shadow-lg shadow-pink/20" > {t("github-sponsor-btn")} @@ -129,7 +130,7 @@ export default function DownloadPageClient() { href="https://www.eclipse.org/sponsor/adoptium/?scope=website&campaign=thank-you-page" target="_blank" rel="noopener noreferrer" - className="inline-flex items-center justify-center gap-3 bg-[#ff1365] hover:bg-[#ff4f8b] text-white px-8 py-4 rounded-xl font-bold text-lg transition-all transform hover:-translate-y-1 hover:shadow-lg shadow-[#ff1365]/20" + className="inline-flex items-center justify-center gap-3 bg-pink hover:bg-[#ff4f8b] text-white px-8 py-4 rounded-xl font-bold text-lg transition-all transform hover:-translate-y-1 hover:shadow-lg shadow-pink/20" > {t("become-sustainer-btn")} @@ -145,46 +146,46 @@ export default function DownloadPageClient() {
{/* Right Column: Visual Reinforcement / Stats (Abstract Representation) */} -
+

{t("why-we-need-you")}

  • -
    -
    +
    +
    {t("infra-costs-title")} - + {t("infra-costs-desc")}
  • -
    -
    +
    +
    {t("security-speed-title")} - + {t("security-speed-desc")}
  • -
    -
    +
    +
    {t("open-source-title")} - + {t("open-source-desc")}
    @@ -196,7 +197,7 @@ export default function DownloadPageClient() {
    {/* Secondary Info Section */} -
    +

    {t("resources-next-steps")} @@ -204,34 +205,34 @@ export default function DownloadPageClient() {
    -
    +

    {t("installation-guide-title")}

    -

    +

    {t("installation-guide-desc")}

    -
    +

    {t("documentation-title")}

    -

    +

    {t("documentation-desc")}

    diff --git a/src/app/[locale]/members/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/members/__tests__/__snapshots__/page.test.tsx.snap index 80007d5e..338932f1 100644 --- a/src/app/[locale]/members/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/members/__tests__/__snapshots__/page.test.tsx.snap @@ -1,4 +1,3 @@ -// Some portions generated by Co-Pilot // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Members page > renders correctly 1`] = ` @@ -110,7 +109,7 @@ exports[`Members page > renders correctly 1`] = ` Why Join Adoptium?

    The Adoptium Working Group brings together organizations to shape the future of Java by collaborating on the delivery of high-quality, TCK-certified OpenJDK binaries through Eclipse Temurin. As a member, your organization gains strategic influence, community leadership opportunities, and a voice in driving long-term innovation across the Java ecosystem.

    diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index 126b186f..be8cfd2f 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -20,7 +20,7 @@ export default async function HomePage() { const availableReleases = await fetchAvailableReleases(); const latestLTS = availableReleases.most_recent_lts ?? 25; return ( -
    +
    diff --git a/src/app/[locale]/sustainers/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/sustainers/__tests__/__snapshots__/page.test.tsx.snap index 4c736137..dad9d8d4 100644 --- a/src/app/[locale]/sustainers/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/sustainers/__tests__/__snapshots__/page.test.tsx.snap @@ -127,7 +127,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Eclipse Temurin Sustainer Program

    The Eclipse Temurin Sustainer Program invites enterprises to invest in the long-term sustainment of Eclipse Temurin and other Adoptium projects. By becoming a Sustainer, your company ensures that Temurin remains the industry's leading community-driven open source JDK for mission-critical Java workloads. This program supports the vendor-neutral development of runtimes and development kits, infrastructure and tools, quality assurance, enhanced security practices, community engagement, and more.

    @@ -150,7 +150,7 @@ exports[`Sustainers page > renders correctly 1`] = ` />

    Runtime binaries High performance Cross-platform Open source licenses (GPLv2 + CE) Spec compliant

    @@ -170,7 +170,7 @@ exports[`Sustainers page > renders correctly 1`] = ` />

    Verification program, running a large pool of tests Assuring security durability, scalability, and performance requirements

    @@ -190,7 +190,7 @@ exports[`Sustainers page > renders correctly 1`] = ` />

    This migration toolkit helps organizations with the migration from Java 8 to 11+

    @@ -210,7 +210,7 @@ exports[`Sustainers page > renders correctly 1`] = ` />

    Low-overhead monitoring and management of running Java applications

    @@ -579,7 +579,7 @@ exports[`Sustainers page > renders correctly 1`] = `

Join the Eclipse Temurin Sustainer Program and play a crucial role in maintaining the vitality and reliability of the world's fastest-growing open source Java SE runtime. Your support accelerates our initiatives, driving greater impact and innovation within the Java ecosystem.

@@ -613,7 +613,7 @@ exports[`Sustainers page > renders correctly 1`] = ` The average company* using Eclipse Temurin saves more than $1.6M annually. Isn't it time you invest in sustaining your savings?

*Self-reported number of employees, n = 211

@@ -622,7 +622,7 @@ exports[`Sustainers page > renders correctly 1`] = ` data-project="temurin-calculator" />

With the recent increase in licensing costs for paid Java SE options, we've seen Temurin downloads surge—from over 75M in 2023 to over 250M by the end of 2023, and doubling again to more than 500M by the end of 2024. This growth strongly indicates a significant transition away from paid Java options toward open-source solutions like Temurin. If your organization has made this migration, you've likely experienced significant cost savings. Dedicate a portion of your savings to Temurin to invest in your corporate software supply chain by ensuring Temurin's long-term viability, enhance security, and accelerate faster releases for the Java community.

@@ -954,7 +954,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Diamond

€100k/year

@@ -980,7 +980,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Platinum

€75k/year

@@ -1006,7 +1006,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Gold

€50k/year

@@ -1032,7 +1032,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Silver

€30k/year

@@ -1058,7 +1058,7 @@ exports[`Sustainers page > renders correctly 1`] = ` Bronze

€10k/year

diff --git a/src/app/[locale]/sustainers/page.tsx b/src/app/[locale]/sustainers/page.tsx index 3fe34f71..ed388def 100644 --- a/src/app/[locale]/sustainers/page.tsx +++ b/src/app/[locale]/sustainers/page.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot import { Metadata } from "next"; import React from "react"; import { Link } from "@/i18n/navigation"; @@ -84,7 +85,7 @@ export default function SustainersPage() { initiatives={initiatives} title="How Your Support Fuels Key Initiatives" /> -

+

Join the Eclipse Temurin Sustainer Program and play a crucial role in maintaining the vitality and reliability of the world's fastest-growing open source Java SE runtime. Your support accelerates diff --git a/src/app/[locale]/temurin/__tests__/__snapshots__/page.test.tsx.snap b/src/app/[locale]/temurin/__tests__/__snapshots__/page.test.tsx.snap index e32a1e8b..b2aa5d49 100644 --- a/src/app/[locale]/temurin/__tests__/__snapshots__/page.test.tsx.snap +++ b/src/app/[locale]/temurin/__tests__/__snapshots__/page.test.tsx.snap @@ -3,13 +3,13 @@ exports[`TemurinIndex page > renders correctly 1`] = `

diff --git a/src/app/globals.css b/src/app/globals.css index f40d3f6b..b1beab79 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -31,6 +31,18 @@ --color-primary: #7026b9; --color-code-bg: rgb(110, 118, 129, 40%); --color-code: white; + + /* sugar-high syntax highlighting palette (dark, default) — replaces the + former prism-themes "shades of purple" theme after the move off PrismJS */ + --sh-class: #fad000; + --sh-identifier: #e6e6e6; + --sh-sign: rgb(255, 255, 255, 80%); + --sh-string: #a5ff90; + --sh-keyword: #ff9d00; + --sh-comment: #8b7fa8; + --sh-jsxliterals: #ff628c; + --sh-property: #9effff; + --sh-entity: #9effff; } /* Global styles */ @@ -43,7 +55,7 @@ section[id] { scroll-margin-top: 4rem; } -/* Inline code blocks (not processed by Prism) */ +/* Inline code blocks (not processed by the syntax highlighter) */ :not(pre) > code:not(.install-row-cmd) { background: var(--color-code-bg); color: var(--color-code); @@ -216,6 +228,8 @@ body { & pre[class*="language-"] { padding: 1rem; border: 0; + background: #1e1e3f; + overflow-x: auto; font-size: 0.8rem !important; } @@ -439,6 +453,19 @@ body { .doc-light .doc-content pre { background: #f3f4f6 !important; border-color: #d1d5db; + + /* Light mode syntax highlighting palette (higher contrast) — sugar-high + reads these CSS variables via inline `color: var(--sh-*)` on its token + spans. */ + --sh-class: #8250df; + --sh-identifier: #1f2328; + --sh-sign: #24292f; + --sh-string: #0a3069; + --sh-keyword: #cf222e; + --sh-comment: #57606a; + --sh-jsxliterals: #0a3069; + --sh-property: #0550ae; + --sh-entity: #6f42c1; } .doc-light .doc-content pre, @@ -455,127 +482,6 @@ body { text-shadow: none !important; } -/* Light mode token color overrides (higher contrast) */ -.doc-light .doc-content pre .token.comment, -.doc-light .doc-content pre .token.prolog, -.doc-light .doc-content pre .token.doctype, -.doc-light .doc-content pre .token.cdata, -.doc-light .doc-content pre .hljs-comment, -.doc-light .doc-content pre .hljs-quote { - color: #57606a !important; -} - -.doc-light .doc-content pre .token.keyword, -.doc-light .doc-content pre .token.boolean, -.doc-light .doc-content pre .token.operator, -.doc-light .doc-content pre .hljs-keyword, -.doc-light .doc-content pre .hljs-selector-tag, -.doc-light .doc-content pre .hljs-literal, -.doc-light .doc-content pre .hljs-type { - color: #cf222e !important; -} - -.doc-light .doc-content pre .token.parameter, -.doc-light .doc-content pre .token.parameter.variable { - color: #1f2328 !important; -} - -.doc-light .doc-content pre .token.string, -.doc-light .doc-content pre .token.char, -.doc-light .doc-content pre .token.builtin, -.doc-light .doc-content pre .hljs-string, -.doc-light .doc-content pre .hljs-doctag { - color: #1f2328 !important; -} - -.doc-light .doc-content pre .token.function, -.doc-light .doc-content pre .token.class-name, -.doc-light .doc-content pre .hljs-title, -.doc-light .doc-content pre .hljs-function, -.doc-light .doc-content pre .hljs-name { - color: #8250df !important; -} - -.doc-light .doc-content pre .token.number, -.doc-light .doc-content pre .token.constant, -.doc-light .doc-content pre .token.symbol, -.doc-light .doc-content pre .hljs-number, -.doc-light .doc-content pre .hljs-attribute { - color: #0550ae !important; -} - -.doc-light .doc-content pre .token.regex, -.doc-light .doc-content pre .token.important, -.doc-light .doc-content pre .token.variable, -.doc-light .doc-content pre .hljs-variable, -.doc-light .doc-content pre .hljs-params, -.doc-light .doc-content pre .hljs-regexp { - color: #bc4c00 !important; -} - -.doc-light .doc-content pre .token.punctuation { - color: #24292f !important; -} - -.doc-light .doc-content pre .token.property, -.doc-light .doc-content pre .token.tag, -.doc-light .doc-content pre .token.boolean, -.doc-light .doc-content pre .token.number, -.doc-light .doc-content pre .token.constant, -.doc-light .doc-content pre .token.symbol, -.doc-light .doc-content pre .token.deleted { - color: #005cc5; -} - -.doc-light .doc-content pre .token.selector, -.doc-light .doc-content pre .token.attr-name, -.doc-light .doc-content pre .token.string, -.doc-light .doc-content pre .token.char, -.doc-light .doc-content pre .token.builtin, -.doc-light .doc-content pre .token.inserted { - color: #032f62; -} - -.doc-light .doc-content pre .token.operator, -.doc-light .doc-content pre .token.entity, -.doc-light .doc-content pre .token.url, -.doc-light .doc-content pre .token.variable { - color: #d73a49; -} - -.doc-light .doc-content pre .token.atrule, -.doc-light .doc-content pre .token.attr-value, -.doc-light .doc-content pre .token.function, -.doc-light .doc-content pre .token.class-name { - color: #6f42c1; -} - -.doc-light .doc-content pre .token.keyword { - color: #d73a49; -} - -.doc-light .doc-content pre .token.regex, -.doc-light .doc-content pre .token.important { - color: #e36209; -} - -.doc-light .doc-content pre .token.property, -.doc-light .doc-content pre .token.tag, -.doc-light .doc-content pre .token.boolean, -.doc-light .doc-content pre .token.number, -.doc-light .doc-content pre .token.constant, -.doc-light .doc-content pre .token.symbol { - color: #005cc5; -} - -.doc-light .doc-content pre .token.selector, -.doc-light .doc-content pre .token.attr-name, -.doc-light .doc-content pre .token.string, -.doc-light .doc-content pre .token.char, -.doc-light .doc-content pre .token.builtin { - color: #032f62; -} - .prose pre { border: 0; } diff --git a/src/components/Banner/Middle/index.tsx b/src/components/Banner/Middle/index.tsx index 0f0017a0..4b26e773 100644 --- a/src/components/Banner/Middle/index.tsx +++ b/src/components/Banner/Middle/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React, { useState, useEffect } from "react"; @@ -32,7 +33,7 @@ const BannerMiddle = () => { } return ( -
+
{ className="relative overflow-hidden rounded-3xl bg-white/5 border border-white/10 p-8 md:p-12 shadow-2xl backdrop-blur-sm" > {/* Glow effects */} -
+
@@ -59,7 +60,7 @@ const BannerMiddle = () => { href={announcement.ctaLink} target="_blank" rel="noopener noreferrer" - className="inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white transition-all duration-300 bg-[#ff1365] rounded-full hover:bg-[#ff1365]/90 hover:scale-105 shadow-lg hover:shadow-[#ff1365]/25 group" + className="inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white transition-all duration-300 bg-pink rounded-full hover:bg-pink/90 hover:scale-105 shadow-lg hover:shadow-pink/25 group" > {announcement.cta} { } return ( -
+
{ className="relative overflow-hidden rounded-3xl bg-white/5 border border-white/10 p-8 md:p-12 shadow-2xl backdrop-blur-sm" > {/* Glow effects */} -
+
@@ -59,7 +60,7 @@ const BannerMiddle = () => { href={announcement.ctaLink} target="_blank" rel="noopener noreferrer" - className="inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white transition-all duration-300 bg-[#ff1365] rounded-full hover:bg-[#ff1365]/90 hover:scale-105 shadow-lg hover:shadow-[#ff1365]/25 group" + className="inline-flex items-center justify-center px-8 py-4 text-base font-bold text-white transition-all duration-300 bg-pink rounded-full hover:bg-pink/90 hover:scale-105 shadow-lg hover:shadow-pink/25 group" > {announcement.cta} = ({ content }) => { - const t = useTranslations("Asciidoc"); const options: HTMLReactParserOptions = { replace: (node) => { diff --git a/src/components/Content/Asciidoc/AuthorList/__tests__/AuthorList.test.tsx b/src/components/Content/Asciidoc/AuthorList/__tests__/AuthorList.test.tsx new file mode 100644 index 00000000..d8c479d4 --- /dev/null +++ b/src/components/Content/Asciidoc/AuthorList/__tests__/AuthorList.test.tsx @@ -0,0 +1,28 @@ +// Some portions generated by Co-Pilot +import React from "react"; +import { render } from "@testing-library/react"; +import { describe, it, expect, vi } from "vitest"; + +vi.mock("next-intl/server", () => ({ + getTranslations: vi.fn().mockResolvedValue((key: string) => key), +})); + +import AuthorList from "../index"; + +describe("AuthorList", () => { + it("renders an entry for each author", async () => { + const ui = await AuthorList({ authors: ["octocat", "hubot"] }); + const { container } = render(ui); + + const authorLinks = container.querySelectorAll("a"); + expect(authorLinks.length).toBe(2); + // Translation mock echoes the key back. + expect(container.textContent).toContain("documentation-authors"); + }); + + it("renders nothing when there are no authors", async () => { + const ui = await AuthorList({ authors: [] }); + const { container } = render(ui); + expect(container.firstChild).toBeNull(); + }); +}); diff --git a/src/components/Content/Asciidoc/AuthorList/__tests__/__snapshots__/author-list.test.tsx.snap b/src/components/Content/Asciidoc/AuthorList/__tests__/__snapshots__/author-list.test.tsx.snap index 303afcea..f9127c61 100644 --- a/src/components/Content/Asciidoc/AuthorList/__tests__/__snapshots__/author-list.test.tsx.snap +++ b/src/components/Content/Asciidoc/AuthorList/__tests__/__snapshots__/author-list.test.tsx.snap @@ -1,47 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`AuthorsList component > renders correctly 1`] = ` - -`; +exports[`AuthorsList component > renders correctly 1`] = `
`; diff --git a/src/components/Content/Asciidoc/AuthorList/index.tsx b/src/components/Content/Asciidoc/AuthorList/index.tsx index 9aec64ee..19171c15 100644 --- a/src/components/Content/Asciidoc/AuthorList/index.tsx +++ b/src/components/Content/Asciidoc/AuthorList/index.tsx @@ -1,15 +1,14 @@ -"use client"; - +// Some portions generated by Co-Pilot import React from "react"; -import { useTranslations } from "next-intl"; +import { getTranslations } from "next-intl/server"; import Author from "./Author"; interface Props { authors: string[]; } -const AuthorList: React.FC = ({ authors = [] }) => { - const t = useTranslations("AuthorList"); +const AuthorList = async ({ authors = [] }: Props) => { + const t = await getTranslations("AuthorList"); if (authors.length) { return (
diff --git a/src/components/Content/Documentation/Header/__tests__/index.test.tsx b/src/components/Content/Documentation/Header/__tests__/index.test.tsx index 62ccdc0c..de2f3e90 100644 --- a/src/components/Content/Documentation/Header/__tests__/index.test.tsx +++ b/src/components/Content/Documentation/Header/__tests__/index.test.tsx @@ -3,13 +3,8 @@ import React from "react"; import { render, screen, cleanup } from "@testing-library/react"; import { describe, it, expect, afterEach, vi } from "vitest"; -vi.mock("next-intl", () => ({ - useTranslations: () => { - const t = (key: string) => key; - t.rich = (key: string) => key; - return t; - }, - useLocale: () => "en", +vi.mock("next-intl/server", () => ({ + getTranslations: vi.fn().mockResolvedValue((key: string) => key), })); import DocumentationHeader from "../index"; @@ -29,26 +24,26 @@ describe("DocumentationHeader", () => { cleanup(); }); - it("renders the Documentation title and subtitle", () => { - render(); + it("renders the Documentation title and subtitle", async () => { + render(await DocumentationHeader()); expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent( "title", ); expect(screen.getByText("subtitle")).toBeInTheDocument(); }); - it("renders the DocumentationSearch component", () => { - render(); + it("renders the DocumentationSearch component", async () => { + render(await DocumentationHeader()); expect(screen.getByTestId("doc-search")).toBeInTheDocument(); }); - it("renders the DocThemeToggle component", () => { - render(); + it("renders the DocThemeToggle component", async () => { + render(await DocumentationHeader()); expect(screen.getByTestId("doc-theme-toggle")).toBeInTheDocument(); }); - it("renders the breadcrumb navigation", () => { - render(); + it("renders the breadcrumb navigation", async () => { + render(await DocumentationHeader()); expect(screen.getByText("home")).toBeInTheDocument(); expect(screen.getByLabelText("Breadcrumb")).toBeInTheDocument(); }); diff --git a/src/components/Content/Documentation/Header/index.tsx b/src/components/Content/Documentation/Header/index.tsx index 6eb4ba7c..52d98fe2 100644 --- a/src/components/Content/Documentation/Header/index.tsx +++ b/src/components/Content/Documentation/Header/index.tsx @@ -1,13 +1,11 @@ // Some portions generated by Co-Pilot -"use client"; - import React from "react"; import DocumentationSearch from "@/components/Content/Documentation/Search"; import DocThemeToggle from "@/components/DocThemeToggle"; -import { useTranslations } from "next-intl"; +import { getTranslations } from "next-intl/server"; -const DocumentationHeader: React.FC = () => { - const t = useTranslations("DocsPage"); +const DocumentationHeader = async () => { + const t = await getTranslations("DocsPage"); return (
diff --git a/src/components/Content/SudoToggle/__tests__/SudoToggle.test.tsx b/src/components/Content/SudoToggle/__tests__/SudoToggle.test.tsx index b55babdb..2a9286b1 100644 --- a/src/components/Content/SudoToggle/__tests__/SudoToggle.test.tsx +++ b/src/components/Content/SudoToggle/__tests__/SudoToggle.test.tsx @@ -4,11 +4,9 @@ import { render, cleanup, act, fireEvent } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import SudoToggle, { stripSudo, containsSudo } from "../index"; -// Mock Prism so highlightElement is a no-op -vi.mock("prismjs", () => ({ - default: { - highlightElement: vi.fn(), - }, +// Mock sugar-high so highlight is a no-op returning the input wrapped in a span +vi.mock("sugar-high", () => ({ + highlight: vi.fn((code: string) => `${code}`), })); // ─── Unit tests for pure functions ─── diff --git a/src/components/Content/SudoToggle/index.tsx b/src/components/Content/SudoToggle/index.tsx index 6e433c96..ec5a848c 100644 --- a/src/components/Content/SudoToggle/index.tsx +++ b/src/components/Content/SudoToggle/index.tsx @@ -2,7 +2,7 @@ "use client"; import { useEffect } from "react"; -import Prism from "prismjs"; +import { highlight } from "sugar-high"; /** * Strip `sudo ` from the start of lines and `| sudo tee` → `| tee`. @@ -154,8 +154,7 @@ const SudoToggle: React.FC = () => { const rootTab = makeTab("root", false); const applyText = (text: string) => { - (codeEl as HTMLElement).textContent = text; - Prism.highlightElement(codeEl); + (codeEl as HTMLElement).innerHTML = highlight(text); }; tabBar.appendChild(userTab); @@ -203,7 +202,7 @@ const SudoToggle: React.FC = () => { }); }; - // Run after Prism has had time to highlight + // Run after the code blocks have been highlighted on mount const timer = setTimeout(processBlocks, 300); return () => clearTimeout(timer); }, []); diff --git a/src/components/Content/SyntaxHighlighter/__tests__/SyntaxHighlighter.test.tsx b/src/components/Content/SyntaxHighlighter/__tests__/SyntaxHighlighter.test.tsx index 7acdbe9f..a58104e1 100644 --- a/src/components/Content/SyntaxHighlighter/__tests__/SyntaxHighlighter.test.tsx +++ b/src/components/Content/SyntaxHighlighter/__tests__/SyntaxHighlighter.test.tsx @@ -3,24 +3,11 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, act, cleanup } from "@testing-library/react"; import SyntaxHighlighter, { highlightCode, addCopyButtons } from "../index"; -// Mock Prism -vi.mock("prismjs", () => ({ - default: { - highlightElement: vi.fn(), - }, +// Mock sugar-high +vi.mock("sugar-high", () => ({ + highlight: vi.fn((code: string) => `${code}`), })); -// Mock Prism language imports -vi.mock("prismjs/components/prism-java", () => ({})); -vi.mock("prismjs/components/prism-bash", () => ({})); -vi.mock("prismjs/components/prism-powershell", () => ({})); -vi.mock("prismjs/components/prism-json", () => ({})); -vi.mock("prismjs/components/prism-batch", () => ({})); -vi.mock("prismjs/components/prism-yaml", () => ({})); -vi.mock("prismjs/components/prism-markup", () => ({})); -vi.mock("prismjs/components/prism-docker", () => ({})); -vi.mock("prism-themes/themes/prism-shades-of-purple.css", () => ({})); - // Mock react-icons vi.mock("react-icons/fa", () => ({ FaClipboard: () => null, @@ -55,7 +42,7 @@ beforeEach(() => { }); afterEach(() => { - cleanup(); // unmounts React components, disconnecting MutationObserver + cleanup(); // unmounts React components document.body.innerHTML = ""; vi.useRealTimers(); }); @@ -65,23 +52,23 @@ afterEach(() => { // --------------------------------------------------------------------------- describe("highlightCode", () => { - it("calls Prism.highlightElement for each code block", async () => { - const Prism = (await import("prismjs")).default; + it("calls sugar-high highlight for each code block", async () => { + const { highlight } = await import("sugar-high"); makeCodeBlock("bash"); makeCodeBlock("java"); highlightCode(); - expect(Prism.highlightElement).toHaveBeenCalledTimes(2); + expect(highlight).toHaveBeenCalledTimes(2); }); it("skips pre blocks with the no-highlight class", async () => { - const Prism = (await import("prismjs")).default; + const { highlight } = await import("sugar-high"); makeCodeBlock(undefined, "no-highlight"); highlightCode(); - expect(Prism.highlightElement).not.toHaveBeenCalled(); + expect(highlight).not.toHaveBeenCalled(); }); it("does nothing when there are no code blocks", () => { @@ -90,13 +77,25 @@ describe("highlightCode", () => { }); it("highlights blocks without no-highlight alongside those with it", async () => { - const Prism = (await import("prismjs")).default; + const { highlight } = await import("sugar-high"); makeCodeBlock("bash"); makeCodeBlock(undefined, "no-highlight"); highlightCode(); - expect(Prism.highlightElement).toHaveBeenCalledTimes(1); + expect(highlight).toHaveBeenCalledTimes(1); + }); + + it("injects the highlighted markup into the code block", async () => { + const { highlight } = await import("sugar-high"); + (highlight as unknown as { mockClear: () => void }).mockClear?.(); + const pre = makeCodeBlock("bash"); + const code = pre.querySelector("code")!; + code.textContent = "echo hi"; + + highlightCode(); + + expect(code.querySelector(".sh__line")).not.toBeNull(); }); }); @@ -249,74 +248,15 @@ describe("SyntaxHighlighter component", () => { }); it("calls highlightCode and addCopyButtons on mount", async () => { - const Prism = (await import("prismjs")).default; + const { highlight } = await import("sugar-high"); makeCodeBlock("bash"); await act(async () => { render(); }); - expect(Prism.highlightElement).toHaveBeenCalled(); + expect(highlight).toHaveBeenCalled(); const button = document.body.querySelector("button"); expect(button).not.toBeNull(); }); - - it("does not trigger highlight when a non-code element is mutated", async () => { - const Prism = (await import("prismjs")).default; - - const { unmount } = render(); - vi.clearAllMocks(); - - // Add a plain div with no pre/code — shouldHighlight stays false - await act(async () => { - const div = document.createElement("div"); - div.textContent = "plain text"; - document.body.appendChild(div); - await Promise.resolve(); - await Promise.resolve(); - }); - - expect(Prism.highlightElement).not.toHaveBeenCalled(); - unmount(); - }); - - it("triggers highlight when a node containing a code block is mutated", async () => { - const Prism = (await import("prismjs")).default; - - const { unmount } = render(); - vi.clearAllMocks(); - - // Add a wrapper div that *contains* a pre>code (querySelector branch) - await act(async () => { - const wrapper = document.createElement("div"); - const pre = document.createElement("pre"); - const code = document.createElement("code"); - code.textContent = "hello"; - pre.appendChild(code); - wrapper.appendChild(pre); - document.body.appendChild(wrapper); - await Promise.resolve(); - await Promise.resolve(); - }); - - expect(Prism.highlightElement).toHaveBeenCalled(); - unmount(); - }); - - it("reacts to dynamically added code blocks via MutationObserver", async () => { - const Prism = (await import("prismjs")).default; - - const { unmount } = render(); - vi.clearAllMocks(); - - // Add a code block — MutationObserver fires asynchronously as a microtask - makeCodeBlock("java"); - await act(async () => { - await Promise.resolve(); - await Promise.resolve(); // two ticks to ensure observer callback + highlightCode run - }); - - expect(Prism.highlightElement).toHaveBeenCalled(); - unmount(); - }); }); diff --git a/src/components/Content/SyntaxHighlighter/index.tsx b/src/components/Content/SyntaxHighlighter/index.tsx index c9cfcb17..3cf5b9c1 100644 --- a/src/components/Content/SyntaxHighlighter/index.tsx +++ b/src/components/Content/SyntaxHighlighter/index.tsx @@ -2,31 +2,25 @@ "use client"; import React, { useEffect } from "react"; -import Prism from "prismjs"; +import { highlight } from "sugar-high"; import { renderToStaticMarkup } from "react-dom/server"; import { FaClipboard, FaCheck } from "react-icons/fa"; -// Load specific languages as needed from Prism's components directory -import "prismjs/components/prism-java"; -import "prismjs/components/prism-bash"; -import "prismjs/components/prism-powershell"; -import "prismjs/components/prism-json"; -import "prismjs/components/prism-batch"; -import "prismjs/components/prism-yaml"; -import "prismjs/components/prism-markup"; -import "prismjs/components/prism-docker"; - -// Import the Shades of Purple theme -import "prism-themes/themes/prism-shades-of-purple.css"; - const COPY_ICON = renderToStaticMarkup(); const CHECK_ICON = renderToStaticMarkup(); const WRAPPER_CLASSES = "relative group"; const BUTTON_CLASSES = - "absolute top-1/2 right-2 -translate-y-1/2 p-1.5 bg-black/35 border border-white/15 rounded text-white/45 cursor-pointer opacity-0 transition-all duration-150 leading-[0] flex items-center justify-center group-hover:opacity-100 focus-visible:opacity-100 hover:text-[#ff1365] hover:border-[#ff1365]/50 hover:bg-[#ff1365]/10"; + "absolute top-1/2 right-2 -translate-y-1/2 p-1.5 bg-black/35 border border-white/15 rounded text-white/45 cursor-pointer opacity-0 transition-all duration-150 leading-[0] flex items-center justify-center group-hover:opacity-100 focus-visible:opacity-100 hover:text-pink hover:border-pink/50 hover:bg-pink/10"; const COPIED_ADD = ["!text-[#4ade80]", "!border-[#4ade80]/50", "!opacity-100"]; +// Highlight a single code block with sugar-high. The raw source is read from +// textContent so re-highlighting an already-highlighted block is idempotent. +function highlightElement(codeBlock: Element) { + const code = codeBlock.textContent || ""; + codeBlock.innerHTML = highlight(code); +} + // Export the highlighting function so it can be called directly export function highlightCode() { const codeBlocks = document.querySelectorAll("pre code"); @@ -35,9 +29,7 @@ export function highlightCode() { if (codeBlock.parentElement?.classList.contains("no-highlight")) { return; } - if (typeof codeBlock === "object") { - Prism.highlightElement(codeBlock); - } + highlightElement(codeBlock); }); } @@ -85,51 +77,14 @@ export function addCopyButtons() { }); } -// Client component that enhances code blocks with PrismJS +// Client component that enhances code blocks with sugar-high highlighting. +// AsciiDoc content is server-rendered and present on mount, so a single pass +// is sufficient — no MutationObserver required. export default function SyntaxHighlighter() { useEffect(() => { - // Run highlighting and copy buttons on every render, just like in the Gatsby implementation highlightCode(); addCopyButtons(); - - // Set up MutationObserver to catch dynamically added code blocks - const observer = new MutationObserver((mutations) => { - let shouldHighlight = false; - - mutations.forEach((mutation) => { - if (mutation.type === "childList" && mutation.addedNodes.length) { - // Check if any added nodes contain code blocks - mutation.addedNodes.forEach((node) => { - // Make sure we're dealing with an Element node before accessing Element-specific properties - if (node.nodeType === Node.ELEMENT_NODE) { - // TypeScript needs us to cast to Element to access tagName - const element = node as Element; - if ( - element.tagName === "PRE" || - element.tagName === "CODE" || - element.querySelector("pre, code") - ) { - shouldHighlight = true; - } - } - }); - } - }); - - if (shouldHighlight) { - highlightCode(); - addCopyButtons(); - } - }); - - // Start observing the document with the configured parameters - observer.observe(document.body, { - childList: true, - subtree: true, - }); - - return () => observer.disconnect(); - }); // No dependency array means this runs on every render + }, []); return null; // This component doesn't render anything } diff --git a/src/components/Contributors/home.tsx b/src/components/Contributors/home.tsx index 9e719035..7b575437 100644 --- a/src/components/Contributors/home.tsx +++ b/src/components/Contributors/home.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import { useTranslations } from "next-intl"; @@ -64,7 +65,7 @@ const ContributorsHome = () => {

{t.rich("thank-you", { highlight: (chunks) => ( - + {chunks} ), diff --git a/src/components/HeroSection/DownloadButton.tsx b/src/components/HeroSection/DownloadButton.tsx index 4f427b0d..824d0b9d 100644 --- a/src/components/HeroSection/DownloadButton.tsx +++ b/src/components/HeroSection/DownloadButton.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React, { RefObject, useRef } from "react"; @@ -64,7 +65,7 @@ const DownloadButton: React.FC = ({ latestLTS }) => {
{binary ? (
-
+
= ({ latestLTS }) => { vendor: "Adoptium", }, }} - className="relative flex items-center justify-center gap-4 bg-[#14003c] text-white px-10 py-5 rounded-full ring-1 ring-white/20 hover:ring-[#ff1365]/50 transition-all duration-300 hover:scale-[1.02]" + className="relative flex items-center justify-center gap-4 bg-purple text-white px-10 py-5 rounded-full ring-1 ring-white/20 hover:ring-pink/50 transition-all duration-300 hover:scale-[1.02]" onClick={() => sendDownloadEvent({ link: binary.link, @@ -85,7 +86,7 @@ const DownloadButton: React.FC = ({ latestLTS }) => { }) } > - +
@@ -129,7 +130,7 @@ const DownloadButton: React.FC = ({ latestLTS }) => { {t("other-downloads")} { return (
{/* Background Gradient/Mesh */}
-
+
{/* Animated Background Logo */} @@ -63,7 +64,7 @@ const HeroSection = ({ latestLTS }: HeroSectionProps) => { {tHomePage("the-power")} - . + .

{ [], ); const t = useTranslations("LogoCarousel"); - const settings = { - dots: false, - infinite: true, - arrows: false, - slidesToShow: 6, - slidesToScroll: 1, - autoplay: true, - speed: 2000, - autoplaySpeed: 2000, - cssEase: "linear", - responsive: [ - { - breakpoint: 1024, - settings: { - slidesToShow: 4, - }, - }, - { - breakpoint: 768, - settings: { - slidesToShow: 3, - }, - }, - { - breakpoint: 480, - settings: { - slidesToShow: 2, - }, - }, - { - breakpoint: 375, - settings: { - slidesToShow: 1.5, - }, - }, - ], - }; return (

{t("title")}

- + {featuredAdopters.map((adopter, index) => ( -
- {adopter.name} -
+ +
+ {adopter.name} +
+
))} -
+
); }; diff --git a/src/components/NavBar/index.tsx b/src/components/NavBar/index.tsx index 45ba13a0..b4953387 100644 --- a/src/components/NavBar/index.tsx +++ b/src/components/NavBar/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React, { useState, useEffect, Fragment } from "react"; @@ -323,7 +324,7 @@ const NavBar = ({ locale }: { locale: string }) => { {/* Grid */}
-

+

Membership

@@ -343,7 +344,7 @@ const NavBar = ({ locale }: { locale: string }) => {
-

+

Sponsorship

@@ -369,7 +370,7 @@ const NavBar = ({ locale }: { locale: string }) => {
-

+

Contribute

@@ -383,7 +384,7 @@ const NavBar = ({ locale }: { locale: string }) => {
-

+

Adopt

@@ -406,7 +407,7 @@ const NavBar = ({ locale }: { locale: string }) => {
setOpenedMenu(undefined)} > Join Us Overview → diff --git a/src/components/Sustainers/KeyInitiatives/index.tsx b/src/components/Sustainers/KeyInitiatives/index.tsx index 41b0a61e..c8ab7134 100644 --- a/src/components/Sustainers/KeyInitiatives/index.tsx +++ b/src/components/Sustainers/KeyInitiatives/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React, { useEffect } from "react"; @@ -65,7 +66,7 @@ const KeyInitiatives: React.FC = ({ {title} {description && ( -

+

{description}

)} diff --git a/src/components/Sustainers/Program/index.tsx b/src/components/Sustainers/Program/index.tsx index 14b10ba1..653c2b59 100644 --- a/src/components/Sustainers/Program/index.tsx +++ b/src/components/Sustainers/Program/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot import React from "react" import Image from "next/image" @@ -39,7 +40,7 @@ const SustainerProgramSection: React.FC = () => {

Eclipse Temurin Sustainer Program

-

+

The Eclipse Temurin Sustainer Program invites enterprises to invest in the long-term sustainment of Eclipse Temurin and other Adoptium projects. By becoming a Sustainer, your company ensures @@ -66,7 +67,7 @@ const SustainerProgramSection: React.FC = () => { height={110} />

-

+

{description}

diff --git a/src/components/Sustainers/SavingsCalculator/index.tsx b/src/components/Sustainers/SavingsCalculator/index.tsx index 5224f899..aba102f1 100644 --- a/src/components/Sustainers/SavingsCalculator/index.tsx +++ b/src/components/Sustainers/SavingsCalculator/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot import React from "react" import Calculator from "./calculator" @@ -17,11 +18,11 @@ const SavingsCalculator: React.FC = () => { The average company* using Eclipse Temurin saves more than $1.6M annually. Isn't it time you invest in sustaining your savings?

-

+

*Self-reported number of employees, n = 211

-

+

With the recent increase in licensing costs for paid Java SE options, we've seen Temurin downloads surge—from over 75M in 2023 to over 250M by the end of 2023, and doubling again to more diff --git a/src/components/TemurinFeatures/BentoGrid.tsx b/src/components/TemurinFeatures/BentoGrid.tsx index af8b0e01..db9e7257 100644 --- a/src/components/TemurinFeatures/BentoGrid.tsx +++ b/src/components/TemurinFeatures/BentoGrid.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React from "react"; @@ -19,13 +20,13 @@ const BentoGrid = () => { key: "aqavit-verification", icon: "/images/initiatives/testing.svg", colSpan: "md:col-span-1", - bg: "bg-[#0d002a]", + bg: "bg-blue", }, { key: "performance-optimization", icon: "/images/initiatives/deploy.svg", colSpan: "md:col-span-1", - bg: "bg-[#0d002a]", + bg: "bg-blue", }, { key: "cross-platform", @@ -37,18 +38,18 @@ const BentoGrid = () => { key: "community-driven", icon: "/images/initiatives/community.svg", colSpan: "md:col-span-1", - bg: "bg-[#0d002a]", + bg: "bg-blue", }, { key: "enterprise-ready", icon: "/images/initiatives/security.svg", colSpan: "md:col-span-1", - bg: "bg-[#0d002a]", + bg: "bg-blue", }, ]; return ( -

+
{
{/* Decorative gradient glow on hover */} -
+
))}
diff --git a/src/components/UiVirtualScroll/UiVirtualContent.tsx b/src/components/UiVirtualScroll/UiVirtualContent.tsx index 2f8ddfeb..5a665ee6 100644 --- a/src/components/UiVirtualScroll/UiVirtualContent.tsx +++ b/src/components/UiVirtualScroll/UiVirtualContent.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import React, { useRef } from "react"; @@ -116,7 +117,7 @@ const FeatureImage = ({

{item.subtext.title}

-

+

{item.subtext.amount}

diff --git a/src/components/WorkingGroup/Projects/index.tsx b/src/components/WorkingGroup/Projects/index.tsx index e46ce1af..662129c4 100644 --- a/src/components/WorkingGroup/Projects/index.tsx +++ b/src/components/WorkingGroup/Projects/index.tsx @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot "use client"; import { Link } from "@/i18n/navigation"; import Image from "next/image"; @@ -36,7 +37,7 @@ const projects: Project[] = [ const WGProjects = () => { const t = useTranslations("WGProjects"); return ( -
+
{ beforeEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); }); it("fetches events from the Eclipse newsroom API", async () => { @@ -17,14 +13,15 @@ describe("fetchLatestEvents", () => { { id: "2", title: "Event 2", infoLink: "https://example.com", date: "2024-02-01" }, ]; - mockedAxios.get.mockResolvedValue({ - data: { events: mockEvents }, - }); + const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: true, + json: async () => ({ events: mockEvents }), + } as Response); const result = await fetchLatestEvents(); - expect(mockedAxios.get).toHaveBeenCalledTimes(1); - const calledUrl = mockedAxios.get.mock.calls[0][0]; + expect(fetchSpy).toHaveBeenCalledTimes(1); + const calledUrl = fetchSpy.mock.calls[0][0] as string; expect(calledUrl).toContain("https://newsroom.eclipse.org/api/events"); expect(calledUrl).toContain("parameters%5Bpublish_to%5D=adoptium"); expect(calledUrl).toContain("pagesize=20"); @@ -32,7 +29,7 @@ describe("fetchLatestEvents", () => { }); it("returns empty array on API error", async () => { - mockedAxios.get.mockRejectedValue(new Error("Network error")); + vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("Network error")); const result = await fetchLatestEvents(); expect(result).toEqual([]); diff --git a/src/hooks/__tests__/fetchMarketplace.test.ts b/src/hooks/__tests__/fetchMarketplace.test.ts index 8e32d3fd..524b2387 100644 --- a/src/hooks/__tests__/fetchMarketplace.test.ts +++ b/src/hooks/__tests__/fetchMarketplace.test.ts @@ -8,23 +8,20 @@ import { import { createMockTemurinFeatureReleaseAPI } from "@/hooks/__fixtures__/hooks"; import vendors from "@/data/marketplace.json"; import getVendorIdentifier from "@/utils/vendors"; -import AxiosInstance from "axios"; -import MockAdapter from "axios-mock-adapter"; import type { MarketplaceVendor } from "@/services/adoptiumApi"; -const mock = new MockAdapter(AxiosInstance); let mockResponse = [createMockTemurinFeatureReleaseAPI(false)]; const selectedVendorIdentifiers = vendors.map( (vendor) => getVendorIdentifier(vendor) as MarketplaceVendor, ); afterEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); }); describe("getAllPkgsForVersion", () => { it("returns valid JSON", async () => { - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await getAllPkgsForVersion( @@ -40,7 +37,7 @@ describe("getAllPkgsForVersion", () => { }); it("returns valid JSON - Alpine Linux", async () => { - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await getAllPkgsForVersion( @@ -58,7 +55,7 @@ describe("getAllPkgsForVersion", () => { it("returns valid JSON - installer", async () => { mockResponse = [createMockTemurinFeatureReleaseAPI(true)]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await getAllPkgsForVersion( @@ -92,7 +89,7 @@ describe("getAllPkgsForVersion", () => { }); it("MarketplaceReleases to be null on error", async () => { - mock.onGet().reply(500); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: false, status: 500, json: async () => [] } as Response); await renderHook(async () => { await getAllPkgsForVersion( diff --git a/src/hooks/__tests__/fetchTemurinReleases.test.ts b/src/hooks/__tests__/fetchTemurinReleases.test.ts index e3f9afec..ce5bf21a 100644 --- a/src/hooks/__tests__/fetchTemurinReleases.test.ts +++ b/src/hooks/__tests__/fetchTemurinReleases.test.ts @@ -1,21 +1,19 @@ +// Some portions generated by Co-Pilot import { renderHook } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { loadLatestAssets } from "../fetchTemurinReleases"; import { createMockTemurinReleaseAPI } from "@/hooks/__fixtures__/hooks"; -import AxiosInstance from "axios"; -import MockAdapter from "axios-mock-adapter"; import { getVersionAsString } from "../index"; -const mock = new MockAdapter(AxiosInstance); let mockResponse = [createMockTemurinReleaseAPI(false, "jdk")]; afterEach(() => { - vi.clearAllMocks(); + vi.restoreAllMocks(); }); describe("loadLatestAssets", () => { it("returns valid JSON", async () => { - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -30,7 +28,7 @@ describe("loadLatestAssets", () => { createMockTemurinReleaseAPI(false, "jdk"), ]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -45,7 +43,7 @@ describe("loadLatestAssets", () => { createMockTemurinReleaseAPI(true, "jre"), ]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -63,7 +61,7 @@ describe("loadLatestAssets", () => { mockResponse = [r1, r2]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -88,7 +86,7 @@ describe("loadLatestAssets", () => { mockResponse = [r1, r2]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -109,7 +107,7 @@ describe("loadLatestAssets", () => { mockResponse = [r1, r2, r3, r4]; - mock.onGet().reply(200, mockResponse); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => mockResponse } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { @@ -119,7 +117,7 @@ describe("loadLatestAssets", () => { }); it("pkgsFound to be empty on error", async () => { - mock.onGet().reply(500); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: false, status: 500, json: async () => [] } as Response); renderHook(async () => { await loadLatestAssets(8, "linux", "x64").then((data) => { diff --git a/src/hooks/fetchLatestEvents.ts b/src/hooks/fetchLatestEvents.ts index 88cb91f2..b9c46fdd 100644 --- a/src/hooks/fetchLatestEvents.ts +++ b/src/hooks/fetchLatestEvents.ts @@ -1,53 +1,54 @@ -import axios from "axios" - -const baseUrl = "https://newsroom.eclipse.org/api" +// Some portions generated by Co-Pilot +const baseUrl = "https://newsroom.eclipse.org/api"; export async function fetchLatestEvents() { - const url = new URL(`${baseUrl}/events`) - url.searchParams.append("parameters[publish_to]", "adoptium") - url.searchParams.append("pagesize", "20") - - return axios - .get(url.toString()) - .then(function (response) { - return response.data.events - }) - .catch(function () { - return [] - }) + const url = new URL(`${baseUrl}/events`); + url.searchParams.append("parameters[publish_to]", "adoptium"); + url.searchParams.append("pagesize", "20"); + + try { + const response = await fetch(url.toString()); + if (!response.ok) { + return []; + } + const data = await response.json(); + return Array.isArray(data.events) ? data.events : []; + } catch { + return []; + } } export interface News { - news: NewsResponse - events: EventItem[] + news: NewsResponse; + events: EventItem[]; } export interface NewsResponse { - news: NewsItem[] - pager: { - current_page: number - items_per_page: number - total_items: number - total_pages: number - } | null + news: NewsItem[]; + pager: { + current_page: number; + items_per_page: number; + total_items: number; + total_pages: number; + } | null; } export interface NewsItem { - id: string - title: string - body: string - date: Date - link: URL - image?: string // Optional image URL from the Eclipse Foundation news API + id: string; + title: string; + body: string; + date: Date; + link: URL; + image?: string; // Optional image URL from the Eclipse Foundation news API } export interface EventItem { - id: string - title: string - infoLink: URL - date: Date + id: string; + title: string; + infoLink: URL; + date: Date; } export interface EventAPI { - events: EventItem[] + events: EventItem[]; } diff --git a/src/hooks/useAdoptiumContributorsApi.ts b/src/hooks/useAdoptiumContributorsApi.ts index 74f8dfc1..11ad1326 100644 --- a/src/hooks/useAdoptiumContributorsApi.ts +++ b/src/hooks/useAdoptiumContributorsApi.ts @@ -2,7 +2,6 @@ "use client"; import { useEffect, useState } from "react"; -import axios from "axios"; import type { Endpoints } from "@octokit/types"; export type ContributorApiResponse = @@ -78,13 +77,11 @@ async function getMaxContributors( // this call is used to know how many contributors there are in this repo // check the Link header to compute first and last - const linksHeaderValue = await axios - .get(repositoryURI) + const linksHeaderValue = await fetch(repositoryURI) .then(function (response) { - return response.headers.link; + return response.headers.get("link") ?? undefined; }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .catch(function (error) { + .catch(function () { return undefined; }); @@ -109,13 +106,14 @@ async function getContributor( ): Promise { const repositoryURI = `https://api.github.com/repos/adoptium/${repository}/contributors?per_page=1&page=${randomPage}`; - const contributor = await axios - .get(repositoryURI) + const contributor = await fetch(repositoryURI) .then(function (response) { - return response.data[0] as ContributorApiResponse; + return response.json(); }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .catch(function (error) { + .then(function (data) { + return data[0] as ContributorApiResponse; + }) + .catch(function () { return undefined; }); diff --git a/src/services/adoptiumApi.ts b/src/services/adoptiumApi.ts index d2b2d69c..6e5671a1 100644 --- a/src/services/adoptiumApi.ts +++ b/src/services/adoptiumApi.ts @@ -9,7 +9,7 @@ * npm run generate:api-types * * All hooks in src/hooks/ call these functions instead of making raw - * axios/fetch calls directly. + * fetch calls directly. */ import createClient from "openapi-fetch"; import type { paths, components } from "@/types/adoptiumApiTypes"; diff --git a/src/utils/__tests__/eclipse.test.ts b/src/utils/__tests__/eclipse.test.ts index 2b0d3bf5..9d0b0987 100644 --- a/src/utils/__tests__/eclipse.test.ts +++ b/src/utils/__tests__/eclipse.test.ts @@ -1,17 +1,10 @@ -import { describe, it, expect, afterEach, beforeEach } from "vitest"; +// Some portions generated by Co-Pilot +import { describe, it, expect, afterEach, vi } from "vitest"; import { fetchLatestNews } from "../eclipse"; -import axios from "axios"; -import MockAdapter from "axios-mock-adapter"; describe("fetchLatestNews", () => { - let mock: MockAdapter; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - afterEach(() => { - mock.restore(); + vi.restoreAllMocks(); }); it("returns data from the API on success", async () => { @@ -19,14 +12,20 @@ describe("fetchLatestNews", () => { news: [{ id: 1, title: "Test News" }], pager: { page: 1 }, }; - mock.onGet(/\/news/).reply(200, mockData); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: true, + json: async () => mockData, + } as Response); const result = await fetchLatestNews(); expect(result).toEqual(mockData); }); it("returns default empty news and pager on error", async () => { - mock.onGet(/\/news/).reply(500); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ + ok: false, + json: async () => ({}), + } as Response); const result = await fetchLatestNews(); expect(result).toEqual({ news: [], pager: null }); }); diff --git a/src/utils/eclipse.ts b/src/utils/eclipse.ts index b6539e23..c715b9d6 100644 --- a/src/utils/eclipse.ts +++ b/src/utils/eclipse.ts @@ -1,19 +1,19 @@ -import axios from "axios" - -const baseUrl = "https://newsroom.eclipse.org/api" +// Some portions generated by Co-Pilot +const baseUrl = "https://newsroom.eclipse.org/api"; export async function fetchLatestNews() { - const url = new URL(`${baseUrl}/news`) - url.searchParams.append("parameters[publish_to]", "adoptium") - url.searchParams.append("page", "1") - url.searchParams.append("pagesize", "100") + const url = new URL(`${baseUrl}/news`); + url.searchParams.append("parameters[publish_to]", "adoptium"); + url.searchParams.append("page", "1"); + url.searchParams.append("pagesize", "100"); - return axios - .get(url.toString()) - .then(function (response) { - return response.data - }) - .catch(function () { - return { news: [], pager: null } - }) + try { + const response = await fetch(url.toString(), { cache: "no-store" }); + if (!response.ok) { + return { news: [], pager: null }; + } + return await response.json(); + } catch { + return { news: [], pager: null }; + } } diff --git a/src/utils/news.ts b/src/utils/news.ts index 820a56a2..0c45c6bf 100644 --- a/src/utils/news.ts +++ b/src/utils/news.ts @@ -1,3 +1,4 @@ +// Some portions generated by Co-Pilot import { getBlogPosts } from "./markdown"; import { fetchLatestNews } from "./eclipse"; import { getFormattedAuthorData } from "@/utils/authors"; @@ -39,7 +40,7 @@ async function getAllNewsData(includeEF: boolean) { if (includeEF) { const efNews = await fetchLatestNews(); - const efPosts = efNews.news + const efPosts = (efNews.news ?? []) .filter((newsItem: EclipseNewsItem) => !isAdoptiumHost(newsItem.link)) .map((newsItem: EclipseNewsItem) => { const rawAuthor = newsItem.author?.[0]?.full_name?.trim(); diff --git a/vitest-setup.tsx b/vitest-setup.tsx index 21a5a946..44a148c9 100644 --- a/vitest-setup.tsx +++ b/vitest-setup.tsx @@ -449,41 +449,6 @@ if (typeof HTMLCanvasElement !== "undefined") { })); } -/** - * Mock for react-slick to avoid "window is not defined" errors in tests - */ -vi.mock("react-slick", () => { - const Slider = ({ - children, - className, - }: { - children: React.ReactNode; - className?: string; - }) => { - return ( -
- {children} -
- ); - }; - - Slider.defaultProps = { - dots: false, - infinite: true, - speed: 500, - slidesToShow: 1, - slidesToScroll: 1, - }; - - return { - __esModule: true, - default: Slider, - }; -}); - -// Mock slick-carousel CSS imports that might be required by components -vi.mock("slick-carousel/slick/slick.css", () => ({})); -vi.mock("slick-carousel/slick/slick-theme.css", () => ({})); /** * Suppress jsdom "Not implemented" warnings for getComputedStyle with pseudo-elements.