diff --git a/constants/swr.ts b/constants/swr.ts new file mode 100644 index 0000000000000..9905c9dda32bf --- /dev/null +++ b/constants/swr.ts @@ -0,0 +1 @@ +export const UserAgentBitness = 'user-agent-bitness'; diff --git a/hooks/__tests__/useDetectOS.test.ts b/hooks/__tests__/useDownloadLink.test.ts similarity index 70% rename from hooks/__tests__/useDetectOS.test.ts rename to hooks/__tests__/useDownloadLink.test.ts index 53df39aa7587b..94b157111170c 100644 --- a/hooks/__tests__/useDetectOS.test.ts +++ b/hooks/__tests__/useDownloadLink.test.ts @@ -1,5 +1,5 @@ import { renderHook, waitFor } from '@testing-library/react'; -import { useDetectOS } from '../useDetectOS'; +import { useDownloadLink } from '../useDownloadLink'; const mockNavigator = { userAgent: @@ -11,7 +11,7 @@ const mockNavigator = { const originalNavigator = global.navigator; -describe('useDetectOS', () => { +describe('useDownloadLink', () => { afterEach(() => { // Reset the navigator global to the original value Object.defineProperty(global, 'navigator', { @@ -27,14 +27,12 @@ describe('useDetectOS', () => { writable: true, }); - const { result } = renderHook(() => useDetectOS()); + const { result } = renderHook(() => + useDownloadLink({ version: 'v18.16.0' }) + ); await waitFor(() => { - expect(result.current.userOS).toBe('WIN'); - }); - - await waitFor(() => { - expect(result.current.getDownloadLink('v18.16.0')).toBe( + expect(result.current).toBe( 'https://nodejs.org/dist/v18.16.0/node-v18.16.0-x64.msi' ); }); @@ -51,15 +49,13 @@ describe('useDetectOS', () => { writable: true, }); - const { result } = renderHook(() => useDetectOS()); - - await waitFor(() => { - expect(result.current.userOS).toBe('OTHER'); - }); + const { result } = renderHook(() => + useDownloadLink({ version: 'v20.1.0' }) + ); await waitFor(() => { - expect(result.current.getDownloadLink('v18.16.0')).toBe( - 'https://nodejs.org/dist/v18.16.0/node-v18.16.0.tar.gz' + expect(result.current).toBe( + 'https://nodejs.org/dist/v20.1.0/node-v20.1.0.tar.gz' ); }); }); diff --git a/hooks/useDetectOS.ts b/hooks/useDetectOS.ts deleted file mode 100644 index 2dcf1588e5efe..0000000000000 --- a/hooks/useDetectOS.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useEffect, useState } from 'react'; -import { detectOS } from '../util/detectOS'; -import { downloadUrlByOS } from '../util/downloadUrlByOS'; - -import type { UserOS } from '../types/userOS'; - -export const useDetectOS = () => { - const [userOS, setUserOS] = useState('OTHER'); - const [bitness, setBitness] = useState(''); - - useEffect(() => { - setUserOS(detectOS()); - - // This is necessary to detect Windows 11 on Edge. - // [MDN](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) - // [MSFT](https://learn.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11) - // @ts-expect-error no types for "userAgentData" because this API is experimental - if (typeof navigator?.userAgentData?.getHighEntropyValues === 'function') { - // @ts-expect-error no types for "userAgentData" because this API is experimental - navigator.userAgentData - .getHighEntropyValues(['bitness']) - .then((ua: { bitness: string }) => setBitness(ua.bitness)) - .catch(); - } - }, []); - - return { - userOS, - getDownloadLink: (version: string) => - downloadUrlByOS({ - userAgent: navigator?.userAgent, - userOS, - version, - bitness, - }), - }; -}; diff --git a/hooks/useDownloadLink.ts b/hooks/useDownloadLink.ts new file mode 100644 index 0000000000000..c29bddba3232e --- /dev/null +++ b/hooks/useDownloadLink.ts @@ -0,0 +1,28 @@ +import { useMemo } from 'react'; +import useSWR from 'swr'; +import { detectOS } from '../util/detectOS'; +import { downloadUrlByOS } from '../util/downloadUrlByOS'; + +import { getBitness } from '../util/getBitness'; +import { UserAgentBitness } from '../constants/swr'; + +type UseDownloadLinkArgs = { + version: string; +}; + +export const useDownloadLink = ({ version }: UseDownloadLinkArgs) => { + const { data: bitness } = useSWR(UserAgentBitness, getBitness); + + const downloadLink = useMemo( + () => + downloadUrlByOS({ + userAgent: navigator?.userAgent, + userOS: detectOS(), + version, + bitness, + }), + [bitness, version] + ); + + return downloadLink; +}; diff --git a/package-lock.json b/package-lock.json index 72415e323d6d9..bc06c3bd8e6c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,6 +82,7 @@ "stylelint-selector-bem-pattern": "^2.1.1", "turbo": "^1.9.3", "typescript": "^5.0.4", + "user-agent-data-types": "^0.3.1", "wait-on": "^7.0.1" }, "engines": { @@ -27605,6 +27606,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/user-agent-data-types": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/user-agent-data-types/-/user-agent-data-types-0.3.1.tgz", + "integrity": "sha512-vS7pZmuEVPlf2CQf+PfNbwWJZA4aQAEa8sH7xhMrjZ1zXNvW7HHEdzmSvI7z/qyyIRVD50DEi4ckuzZcebKHGg==", + "dev": true + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", diff --git a/package.json b/package.json index 503098f16d929..30416e8319f3c 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,6 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", "@types/jest": "^29.5.1", - "concurrently": "^8.0.1", "@types/mdx": "^2.0.5", "@types/node": "^18.16.3", "@types/react": "^18.2.2", @@ -86,6 +85,7 @@ "@types/testing-library__jest-dom": "^5.14.5", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", + "concurrently": "^8.0.1", "critters": "^0.0.16", "cross-env": "^7.0.3", "eslint": "^8.39.0", @@ -95,7 +95,6 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-mdx": "^2.0.5", "eslint-plugin-prettier": "^4.2.1", - "wait-on": "^7.0.1", "eslint-plugin-testing-library": "^5.10.3", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", @@ -109,6 +108,8 @@ "stylelint-order": "^6.0.3", "stylelint-selector-bem-pattern": "^2.1.1", "turbo": "^1.9.3", - "typescript": "^5.0.4" + "typescript": "^5.0.4", + "user-agent-data-types": "^0.3.1", + "wait-on": "^7.0.1" } } diff --git a/util/getBitness.ts b/util/getBitness.ts new file mode 100644 index 0000000000000..95c4ed4ef4e60 --- /dev/null +++ b/util/getBitness.ts @@ -0,0 +1,14 @@ +/// + +export const getBitness = async () => { + // This is necessary to detect Windows 11 on Edge. + // [MDN](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) + // [MSFT](https://learn.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11) + if (typeof navigator?.userAgentData?.getHighEntropyValues === 'function') { + return navigator.userAgentData + .getHighEntropyValues(['bitness']) + .then(ua => ua.bitness); + } + + return undefined; +};