}
+ */
+const generateChangelogData = async version => {
+ // Get the raw changelog for the latest minor for a given major
+ const changelog = await fetchNodeJsChangelog(version);
+
+ return changelog;
+};
+
+export default generateChangelogData;
diff --git a/next-data/generators/releaseData.mjs b/next-data/generators/releaseData.mjs
index 8162fbdcdf983..cd34ecf3e4ef1 100644
--- a/next-data/generators/releaseData.mjs
+++ b/next-data/generators/releaseData.mjs
@@ -37,32 +37,35 @@ const generateReleaseData = () => {
// Basically those not in schedule.json
const majors = Object.values(nodevuOutput).filter(major => !!major.support);
- const nodeReleases = majors.map(major => {
- const [latestVersion] = Object.values(major.releases);
+ const nodeReleases = Promise.all(
+ majors.map(async major => {
+ const [latestVersion] = Object.values(major.releases);
- const support = {
- currentStart: major.support.phases.dates.start,
- ltsStart: major.support.phases.dates.lts,
- maintenanceStart: major.support.phases.dates.maintenance,
- endOfLife: major.support.phases.dates.end,
- };
+ const support = {
+ currentStart: major.support.phases.dates.start,
+ ltsStart: major.support.phases.dates.lts,
+ maintenanceStart: major.support.phases.dates.maintenance,
+ endOfLife: major.support.phases.dates.end,
+ };
- const status = getNodeReleaseStatus(new Date(), support);
+ // Get the major release status based on our Release Schedule
+ const status = getNodeReleaseStatus(new Date(), support);
- return {
- ...support,
- status,
- major: latestVersion.semver.major,
- version: latestVersion.semver.raw,
- versionWithPrefix: `v${latestVersion.semver.raw}`,
- codename: major.support.codename || '',
- isLts: status === 'Active LTS' || status === 'Maintenance LTS',
- npm: latestVersion.dependencies.npm || '',
- v8: latestVersion.dependencies.v8 || '',
- releaseDate: latestVersion.releaseDate || '',
- modules: latestVersion.modules.version || '',
- };
- });
+ return {
+ ...support,
+ status,
+ major: latestVersion.semver.major,
+ version: latestVersion.semver.raw,
+ versionWithPrefix: `v${latestVersion.semver.raw}`,
+ codename: major.support.codename || '',
+ isLts: status === 'Active LTS' || status === 'Maintenance LTS',
+ npm: latestVersion.dependencies.npm || '',
+ v8: latestVersion.dependencies.v8 || '',
+ releaseDate: latestVersion.releaseDate || '',
+ modules: latestVersion.modules.version || '',
+ };
+ })
+ );
// nodevu returns duplicated v0.x versions (v0.12, v0.10, ...).
// This behavior seems intentional as the case is hardcoded in nodevu,
@@ -70,7 +73,9 @@ const generateReleaseData = () => {
// This line ignores those duplicated versions and takes the latest
// v0.x version (v0.12.18). It is also consistent with the legacy
// nodejs.org implementation.
- return nodeReleases.filter(r => r.major !== 0 || r.version === '0.12.18');
+ return nodeReleases.then(releases =>
+ releases.filter(r => r.major !== 0 || r.version === '0.12.18')
+ );
});
};
diff --git a/next-data/providers/changelogData.ts b/next-data/providers/changelogData.ts
new file mode 100644
index 0000000000000..8a07bab07fd2e
--- /dev/null
+++ b/next-data/providers/changelogData.ts
@@ -0,0 +1,9 @@
+import { cache } from 'react';
+
+import generateChangelogData from '@/next-data/generators/changelogData.mjs';
+
+export const provideChangelogData = cache((version: string) => {
+ const changelog = generateChangelogData(version);
+
+ return changelog;
+});
diff --git a/next.mdx.compiler.mjs b/next.mdx.compiler.mjs
index 1bc2ebc96f83f..6fcdf963cdd39 100644
--- a/next.mdx.compiler.mjs
+++ b/next.mdx.compiler.mjs
@@ -35,7 +35,6 @@ export async function compileMDX(source, fileExtension) {
rehypePlugins: NEXT_REHYPE_PLUGINS,
remarkPlugins: NEXT_REMARK_PLUGINS,
format: fileExtension,
- baseUrl: import.meta.url,
...reactRuntime,
});
diff --git a/next.mdx.shiki.mjs b/next.mdx.shiki.mjs
index aeeb67254bebf..b3253f6646cfc 100644
--- a/next.mdx.shiki.mjs
+++ b/next.mdx.shiki.mjs
@@ -10,11 +10,6 @@ import { getShiki, highlightToHast } from './util/getHighlighter';
// to attribute the current language of the element
const languagePrefix = 'language-';
-// We do a top-level await, since the Unist-tree visitor
-// is synchronous, and it makes more sense to do a top-level
-// await, rather than an await inside the visitor function
-const memoizedShiki = await getShiki();
-
/**
* Retrieve the value for the given meta key.
*
@@ -60,6 +55,11 @@ function isCodeBlock(node) {
export default function rehypeShikiji() {
return async function (tree) {
+ // We do a top-level await, since the Unist-tree visitor
+ // is synchronous, and it makes more sense to do a top-level
+ // await, rather than an await inside the visitor function
+ const memoizedShiki = await getShiki();
+
visit(tree, 'element', (_, index, parent) => {
const languages = [];
const displayNames = [];
diff --git a/next.mdx.use.client.mjs b/next.mdx.use.client.mjs
new file mode 100644
index 0000000000000..9c75bf92355ff
--- /dev/null
+++ b/next.mdx.use.client.mjs
@@ -0,0 +1,39 @@
+'use strict';
+
+import Blockquote from './components/Common/Blockquote';
+import Button from './components/Common/Button';
+import LinkWithArrow from './components/Downloads/Release/LinkWithArrow';
+import Link from './components/Link';
+import MDXCodeBox from './components/MDX/CodeBox';
+import MDXCodeTabs from './components/MDX/CodeTabs';
+import MDXImage from './components/MDX/Image';
+
+/**
+ * A full list of React Components that we want to pass through to MDX
+ *
+ * @satisfies {import('mdx/types').MDXComponents}
+ */
+export const clientMdxComponents = {
+ // Renders MDX CodeTabs
+ CodeTabs: MDXCodeTabs,
+ // Renders a Button Component for `button` tags
+ Button: Button,
+ // Links with External Arrow
+ LinkWithArrow: LinkWithArrow,
+};
+
+/**
+ * A full list of wired HTML elements into custom React Components
+ *
+ * @type {import('mdx/types').MDXComponents}
+ */
+export const htmlComponents = {
+ // Renders a Link Component for `a` tags
+ a: Link,
+ // Renders a Blockquote Component for `blockquote` tags
+ blockquote: Blockquote,
+ // Renders a CodeBox Component for `pre` tags
+ pre: MDXCodeBox,
+ // Renders an Image Component for `img` tags
+ img: MDXImage,
+};
diff --git a/next.mdx.use.mjs b/next.mdx.use.mjs
index cc27b12efffbd..54191ff55b952 100644
--- a/next.mdx.use.mjs
+++ b/next.mdx.use.mjs
@@ -1,14 +1,12 @@
'use strict';
-import Blockquote from './components/Common/Blockquote';
-import Button from './components/Common/Button';
import DownloadButton from './components/Downloads/DownloadButton';
import DownloadLink from './components/Downloads/DownloadLink';
import DownloadReleasesTable from './components/Downloads/DownloadReleasesTable';
import BitnessDropdown from './components/Downloads/Release/BitnessDropdown';
import BlogPostLink from './components/Downloads/Release/BlogPostLink';
+import ChangelogLink from './components/Downloads/Release/ChangelogLink';
import ReleaseDownloadButton from './components/Downloads/Release/DownloadButton';
-import LinkWithArrow from './components/Downloads/Release/LinkWithArrow';
import NpmLink from './components/Downloads/Release/NpmLink';
import OperatingSystemDropdown from './components/Downloads/Release/OperatingSystemDropdown';
import PlatformDropdown from './components/Downloads/Release/PlatformDropdown';
@@ -18,11 +16,7 @@ import ReleaseVersion from './components/Downloads/Release/ReleaseVersion';
import SourceButton from './components/Downloads/Release/SourceButton';
import VerifyingBinariesLink from './components/Downloads/Release/VerifyingBinariesLink';
import VersionDropdown from './components/Downloads/Release/VersionDropdown';
-import Link from './components/Link';
import UpcomingMeetings from './components/MDX/Calendar/UpcomingMeetings';
-import MDXCodeBox from './components/MDX/CodeBox';
-import MDXCodeTabs from './components/MDX/CodeTabs';
-import MDXImage from './components/MDX/Image';
import SearchPage from './components/MDX/SearchPage';
import WithBadge from './components/withBadge';
import WithBanner from './components/withBanner';
@@ -41,20 +35,14 @@ export const mdxComponents = {
WithBanner: WithBanner,
// HOC for providing Badge Data
WithBadge: WithBadge,
- // Renders MDX CodeTabs
- CodeTabs: MDXCodeTabs,
// Renders a Download Button
DownloadButton: DownloadButton,
// Renders a Download Link
DownloadLink: DownloadLink,
- // Renders a Button Component for `button` tags
- Button: Button,
// Renders a Search Page
SearchPage: SearchPage,
// Renders an container for Upcoming Node.js Meetings
UpcomingMeetings: UpcomingMeetings,
- // Links with External Arrow
- LinkWithArrow: LinkWithArrow,
// Group of components that enable you to select versions for Node.js
// releases and download selected versions. Uses `releaseProvider` as a provider
Release: {
@@ -82,21 +70,7 @@ export const mdxComponents = {
SourceButton: SourceButton,
// Renders a Release CodeBox
ReleaseCodeBox: ReleaseCodeBox,
+ // Renders a Changelog Modal Link Button
+ ChangelogLink: ChangelogLink,
},
};
-
-/**
- * A full list of wired HTML elements into custom React Components
- *
- * @type {import('mdx/types').MDXComponents}
- */
-export const htmlComponents = {
- // Renders a Link Component for `a` tags
- a: Link,
- // Renders a Blockquote Component for `blockquote` tags
- blockquote: Blockquote,
- // Renders a CodeBox Component for `pre` tags
- pre: MDXCodeBox,
- // Renders an Image Component for `img` tags
- img: MDXImage,
-};
diff --git a/pages/en/download/current.mdx b/pages/en/download/current.mdx
index 637327b30099e..764ed0c3ee56e 100644
--- a/pages/en/download/current.mdx
+++ b/pages/en/download/current.mdx
@@ -13,6 +13,8 @@ I want the version of Node.js for
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/index.mdx b/pages/en/download/index.mdx
index 637327b30099e..764ed0c3ee56e 100644
--- a/pages/en/download/index.mdx
+++ b/pages/en/download/index.mdx
@@ -13,6 +13,8 @@ I want the version of Node.js for
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/package-manager/current.mdx b/pages/en/download/package-manager/current.mdx
index 7b970af146d64..b1df735f28000 100644
--- a/pages/en/download/package-manager/current.mdx
+++ b/pages/en/download/package-manager/current.mdx
@@ -13,6 +13,8 @@ Install Node.js on
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/package-manager/index.mdx b/pages/en/download/package-manager/index.mdx
index 87f714dffbbba..53cef9690e467 100644
--- a/pages/en/download/package-manager/index.mdx
+++ b/pages/en/download/package-manager/index.mdx
@@ -13,6 +13,8 @@ Install Node.js on
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/prebuilt-binaries/current.mdx b/pages/en/download/prebuilt-binaries/current.mdx
index c3138a51457ac..c101cf05ac441 100644
--- a/pages/en/download/prebuilt-binaries/current.mdx
+++ b/pages/en/download/prebuilt-binaries/current.mdx
@@ -13,6 +13,8 @@ I want the version of Node.js for
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/prebuilt-binaries/index.mdx b/pages/en/download/prebuilt-binaries/index.mdx
index c3138a51457ac..c101cf05ac441 100644
--- a/pages/en/download/prebuilt-binaries/index.mdx
+++ b/pages/en/download/prebuilt-binaries/index.mdx
@@ -13,6 +13,8 @@ I want the version of Node.js for
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/source-code/current.mdx b/pages/en/download/source-code/current.mdx
index 75dd8002f3a7f..0732ee94ce4fb 100644
--- a/pages/en/download/source-code/current.mdx
+++ b/pages/en/download/source-code/current.mdx
@@ -13,6 +13,8 @@ I want the version of the Node.js source code.
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/pages/en/download/source-code/index.mdx b/pages/en/download/source-code/index.mdx
index 75dd8002f3a7f..0732ee94ce4fb 100644
--- a/pages/en/download/source-code/index.mdx
+++ b/pages/en/download/source-code/index.mdx
@@ -13,6 +13,8 @@ I want the version of the Node.js source code.
Node.js includes .
+Read the changelog for this version
+
Read the blog post for this version
Learn how to verify signed SHASUMS
diff --git a/providers/releaseProvider.tsx b/providers/releaseProvider.tsx
index c17e8748af4cd..10668654937eb 100644
--- a/providers/releaseProvider.tsx
+++ b/providers/releaseProvider.tsx
@@ -3,6 +3,7 @@
import type { Dispatch, PropsWithChildren, FC } from 'react';
import { createContext, useMemo, useReducer } from 'react';
+import WithChangelogModal from '@/components/withChangelogModal';
import type { NodeRelease } from '@/types';
import type {
ReleaseDispatchActions,
@@ -18,6 +19,7 @@ const initialState: ReleaseState = {
os: 'OTHER',
bitness: '',
platform: 'NVM',
+ modalOpen: false,
};
const createDispatchActions = (
@@ -27,6 +29,7 @@ const createDispatchActions = (
setOS: payload => dispatch({ type: 'SET_OS', payload }),
setBitness: payload => dispatch({ type: 'SET_BITNESS', payload }),
setPlatform: payload => dispatch({ type: 'SET_PLATFORM', payload }),
+ setModalOpen: payload => dispatch({ type: 'SET_MODAL_OPEN', payload }),
});
export const ReleaseContext = createContext({
@@ -53,6 +56,8 @@ export const ReleaseProvider: FC> = ({
return { ...state, bitness: action.payload };
case 'SET_PLATFORM':
return { ...state, platform: action.payload };
+ case 'SET_MODAL_OPEN':
+ return { ...state, modalOpen: action.payload };
default:
return state;
}
@@ -69,6 +74,12 @@ export const ReleaseProvider: FC> = ({
return (
{children}
+
+
);
};
diff --git a/types/release.ts b/types/release.ts
index 4cbe96bc8fd04..e231e55a921e7 100644
--- a/types/release.ts
+++ b/types/release.ts
@@ -11,19 +11,22 @@ export interface ReleaseState {
releases: Array;
bitness: string | number;
platform: PackageManager;
+ modalOpen: boolean;
}
export type ReleaseAction =
| { type: 'SET_OS'; payload: UserOS }
| { type: 'SET_VERSION'; payload: string }
| { type: 'SET_BITNESS'; payload: string | number }
- | { type: 'SET_PLATFORM'; payload: PackageManager };
+ | { type: 'SET_PLATFORM'; payload: PackageManager }
+ | { type: 'SET_MODAL_OPEN'; payload: boolean };
export interface ReleaseDispatchActions {
setVersion: (version: string) => void;
setOS: (os: UserOS) => void;
setBitness: (bitness: string | number) => void;
setPlatform: (platform: PackageManager) => void;
+ setModalOpen: (open: boolean) => void;
}
export interface ReleaseContextType
diff --git a/util/__tests__/getNodeJsChangelog.test.mjs b/util/__tests__/getNodeJsChangelog.test.mjs
index a5f76e633b140..5cc56521750b2 100644
--- a/util/__tests__/getNodeJsChangelog.test.mjs
+++ b/util/__tests__/getNodeJsChangelog.test.mjs
@@ -1,12 +1,12 @@
-import { getNodejsChangelog } from '../getNodeJsChangelog';
+import { getNodeJsChangelog } from '../getNodeJsChangelog';
-describe('getNodejsChangelog', () => {
+describe('getNodeJsChangelog', () => {
it('returns the correct changelog URL for major version >= 4', () => {
const version = '14.2.0';
const expectedUrl =
'https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V14.md#14.2.0';
- const result = getNodejsChangelog(version);
+ const result = getNodeJsChangelog(version);
expect(result).toBe(expectedUrl);
});
@@ -16,7 +16,7 @@ describe('getNodejsChangelog', () => {
const expectedUrl =
'https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_IOJS.md#1.8.3';
- const result = getNodejsChangelog(version);
+ const result = getNodeJsChangelog(version);
expect(result).toBe(expectedUrl);
});
@@ -26,7 +26,7 @@ describe('getNodejsChangelog', () => {
const expectedUrl1 =
'https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V6.md#6.12.0';
- const result1 = getNodejsChangelog(version1);
+ const result1 = getNodeJsChangelog(version1);
expect(result1).toBe(expectedUrl1);
@@ -34,7 +34,7 @@ describe('getNodejsChangelog', () => {
const expectedUrl2 =
'https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V8.md#8.10.0';
- const result2 = getNodejsChangelog(version2);
+ const result2 = getNodeJsChangelog(version2);
expect(result2).toBe(expectedUrl2);
});
@@ -44,7 +44,7 @@ describe('getNodejsChangelog', () => {
const expectedUrl =
'https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V012.md#0.12.7';
- const result = getNodejsChangelog(version);
+ const result = getNodeJsChangelog(version);
expect(result).toBe(expectedUrl);
});
diff --git a/util/fetchNodeJsChangelog.ts b/util/fetchNodeJsChangelog.ts
new file mode 100644
index 0000000000000..eea20bb3ab878
--- /dev/null
+++ b/util/fetchNodeJsChangelog.ts
@@ -0,0 +1,24 @@
+import { getNodeJsChangelog } from './getNodeJsChangelog';
+
+const getChangelogSectionRegex = (sectionId: string) =>
+ new RegExp(`\\n([\\s\\S]+?)(?:\\n