diff --git a/.gitignore b/.gitignore index f82222f8decc4..aa56284814f58 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ build public/robots.txt public/sitemap.xml public/en/feed/*.xml -public/node-releases-data.json pages/en/blog/year-[0-9][0-9][0-9][0-9].md # Jest diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000000..c306023e82ea8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +DIR=$(cd `dirname $0` && pwd -P) + +echo "[]" > $DIR/../public/node-releases-data.json + +git add --sparse $DIR/../public/node-releases-data.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00f14f5cf9b7a..5113880bebc76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,6 +164,12 @@ Commits should be signed. You can read more about [Commit Signing][] here. - Commit messages **must** start with a capital letter - Commit messages **must not** end with a period `.` +### Pre-commit Hooks + +This project uses [husky][] for pre-commit hooks. + +Some JSON files are generated during Build time with empty files as placeholders. Build time happens when you run `npx turbo serve` or `npx turbo build`. We don't want to commit those unnecessary changes. Since these files exist in the repository, `.gitignore` won't work for them. As the workaround, we have a pre-commit hook to discard those changes. + # Pull Request Policy This policy governs how contributions should land within this repository. The lines below state the checks and policies to be followed before merging and on the act of merging. @@ -219,3 +225,4 @@ By contributing to this project, I certify that: [`squash`]: https://help.github.com/en/articles/about-pull-request-merges#squash-and-merge-your-pull-request-commits [Conventional Commits]: https://www.conventionalcommits.org/ [Commit Signing]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits +[husky]: https://typicode.github.io/husky/ diff --git a/hooks/useFetchNodeReleases.ts b/hooks/useFetchNodeReleases.ts deleted file mode 100644 index b7d484125288b..0000000000000 --- a/hooks/useFetchNodeReleases.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useMemo } from 'react'; -import useSWR from 'swr'; -import { useRouter } from './useRouter'; -import { getNodeReleaseStatus } from '../util/nodeRelease'; -import type { NodeRelease } from '../types'; - -interface NodeReleaseJSON { - major: number; - version: string; - codename?: string; - currentStart: string; - ltsStart?: string; - maintenanceStart?: string; - endOfLife: string; - npm?: string; - v8?: string; - releaseDate?: string; - modules?: string; -} - -const fetcher = (...args: Parameters) => - fetch(...args).then(res => res.json()); - -export const useFetchNodeReleases = (): NodeRelease[] => { - const { basePath } = useRouter(); - - const { data = [] } = useSWR( - `${basePath}/node-releases-data.json`, - fetcher - ); - - return useMemo(() => { - const now = new Date(); - - return data.map(raw => { - const support = { - currentStart: raw.currentStart, - ltsStart: raw.ltsStart, - maintenanceStart: raw.maintenanceStart, - endOfLife: raw.endOfLife, - }; - - const status = getNodeReleaseStatus(now, support); - - return { - ...support, - major: raw.major, - version: raw.version, - versionWithPrefix: `v${raw.version}`, - codename: raw.codename || '', - isLts: status === 'Active LTS' || status === 'Maintenance LTS', - status: status, - npm: raw.npm || '', - v8: raw.v8 || '', - releaseDate: raw.releaseDate || '', - modules: raw.modules || '', - }; - }); - }, [data]); -}; diff --git a/package-lock.json b/package-lock.json index 70e53388b1475..3980dfce7e4a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,7 @@ "feed": "^4.2.2", "gray-matter": "^4.0.3", "handlebars": "^4.7.7", + "husky": "^8.0.0", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "next-sitemap": "^4.1.3", @@ -15956,6 +15957,21 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index a749db48f4b36..5bc8c68d9ed5f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "test:storybook": "concurrently -P -k -s first -n \"storybook,test-storybook\" -c \"magenta,blue\" \"npm:storybook -- --ci\" \"wait-on http://localhost:6006 && test-storybook {@}\"", "test:storybook:snapshot": "npm run test:storybook -- -- --updateSnapshot", "test:storybook:watch": "npm run test:storybook -- -- --watch", - "test": "concurrently -s all -n \"test:unit,test:storybook\" -c \"yellow,green\" \"npm:test:unit\" \"npm:test:storybook\"" + "test": "concurrently -s all -n \"test:unit,test:storybook\" -c \"yellow,green\" \"npm:test:unit\" \"npm:test:storybook\"", + "prepare": "husky install" }, "dependencies": { "@emotion/react": "^11.11.1", @@ -105,6 +106,7 @@ "stylelint-selector-bem-pattern": "^2.1.1", "typescript": "^5.0.4", "user-agent-data-types": "^0.3.1", - "wait-on": "^7.0.1" + "wait-on": "^7.0.1", + "husky": "^8.0.0" } } diff --git a/providers/nodeReleasesProvider.tsx b/providers/nodeReleasesProvider.tsx index 821c29ed6f84c..f1cbe3d9d4e25 100644 --- a/providers/nodeReleasesProvider.tsx +++ b/providers/nodeReleasesProvider.tsx @@ -1,12 +1,40 @@ -import { createContext } from 'react'; -import { useFetchNodeReleases } from '../hooks/useFetchNodeReleases'; +import { createContext, useMemo } from 'react'; +import nodeReleasesData from '../public/node-releases-data.json'; +import { getNodeReleaseStatus } from '../util/nodeRelease'; import type { FC, PropsWithChildren } from 'react'; -import type { NodeRelease } from '../types'; +import type { NodeReleaseSource, NodeRelease } from '../types'; export const NodeReleasesContext = createContext([]); export const NodeReleasesProvider: FC = ({ children }) => { - const releases = useFetchNodeReleases(); + const releases = useMemo(() => { + const now = new Date(); + + return nodeReleasesData.map((raw: NodeReleaseSource) => { + const support = { + currentStart: raw.currentStart, + ltsStart: raw.ltsStart, + maintenanceStart: raw.maintenanceStart, + endOfLife: raw.endOfLife, + }; + + const status = getNodeReleaseStatus(now, support); + + return { + ...support, + major: raw.major, + version: raw.version, + versionWithPrefix: `v${raw.version}`, + codename: raw.codename || '', + isLts: status === 'Active LTS' || status === 'Maintenance LTS', + status: status, + npm: raw.npm || '', + v8: raw.v8 || '', + releaseDate: raw.releaseDate || '', + modules: raw.modules || '', + }; + }); + }, []); return ( diff --git a/public/node-releases-data.json b/public/node-releases-data.json new file mode 100644 index 0000000000000..fe51488c7066f --- /dev/null +++ b/public/node-releases-data.json @@ -0,0 +1 @@ +[] diff --git a/types/releases.ts b/types/releases.ts index 7b98efe595143..db708a3489e89 100644 --- a/types/releases.ts +++ b/types/releases.ts @@ -16,21 +16,24 @@ export type NodeReleaseStatus = | 'End-of-life' | 'Pending'; -export interface NodeRelease { +export interface NodeReleaseSource { major: number; version: string; - versionWithPrefix: string; - codename: string; - isLts: boolean; - status: NodeReleaseStatus; + codename?: string; currentStart: string; ltsStart?: string; maintenanceStart?: string; endOfLife: string; - npm: string; - v8: string; - releaseDate: string; - modules: string; + npm?: string; + v8?: string; + releaseDate?: string; + modules?: string; +} + +export interface NodeRelease extends NodeReleaseSource { + versionWithPrefix: string; + isLts: boolean; + status: NodeReleaseStatus; } export type NodeReleaseSupport = Pick<