Skip to content

Commit 8bec046

Browse files
canerakdasCopilotavivkeller
authored
feat: Introduce Downloads Archive page (#7794)
Co-authored-by: Copilot <[email protected]> Co-authored-by: Aviv Keller <[email protected]>
1 parent 84c25ab commit 8bec046

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+950
-194
lines changed

.github/workflows/lighthouse.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
${{ needs.get-vercel-preview.outputs.url }}/en/about
9898
${{ needs.get-vercel-preview.outputs.url }}/en/about/previous-releases
9999
${{ needs.get-vercel-preview.outputs.url }}/en/download
100+
${{ needs.get-vercel-preview.outputs.url }}/en/download/archive/current
100101
${{ needs.get-vercel-preview.outputs.url }}/en/blog
101102
uploadArtifacts: true # save results as a action artifacts
102103
temporaryPublicStorage: true # upload lighthouse report to the temporary storage

apps/site/app/[locale]/[...path]/page.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import { ENABLE_STATIC_EXPORT_LOCALE } from '#site/next.constants.mjs';
1515
import { dynamicRouter } from '#site/next.dynamic.mjs';
1616
import * as basePage from '#site/next.dynamic.page.mjs';
1717
import { availableLocaleCodes, defaultLocale } from '#site/next.locales.mjs';
18+
import type { DynamicParams } from '#site/types';
1819

19-
type DynamicStaticPaths = { path: Array<string>; locale: string };
20-
type DynamicParams = { params: Promise<DynamicStaticPaths> };
20+
type PageParams = DynamicParams<{ path: Array<string> }>;
2121

2222
// This is the default Viewport Metadata
2323
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
@@ -59,9 +59,11 @@ export const generateStaticParams = async () => {
5959
// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component
6060
// finally it returns (if the locale and route are valid) the React Component with the relevant context
6161
// and attached context providers for rendering the current page
62-
const getPage: FC<DynamicParams> = async props => {
62+
const getPage: FC<PageParams> = async props => {
63+
const { path, locale: routeLocale } = await props.params;
64+
6365
// Gets the current full pathname for a given path
64-
const [locale, pathname] = await basePage.getLocaleAndPath(props);
66+
const [locale, pathname] = basePage.getLocaleAndPath(path, routeLocale);
6567

6668
// Gets the Markdown content and context
6769
const [content, context] = await basePage.getMarkdownContext({

apps/site/app/[locale]/blog/[...path]/page.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { ENABLE_STATIC_EXPORT } from '#site/next.constants.mjs';
55
import { BLOG_DYNAMIC_ROUTES } from '#site/next.dynamic.constants.mjs';
66
import * as basePage from '#site/next.dynamic.page.mjs';
77
import { defaultLocale } from '#site/next.locales.mjs';
8+
import type { DynamicParams } from '#site/types';
89

9-
type DynamicStaticPaths = { path: Array<string>; locale: string };
10-
type DynamicParams = { params: Promise<DynamicStaticPaths> };
10+
type PageParams = DynamicParams<{ path: Array<string> }>;
1111

1212
// This is the default Viewport Metadata
1313
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
@@ -38,9 +38,11 @@ export const generateStaticParams = async () => {
3838
// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component
3939
// finally it returns (if the locale and route are valid) the React Component with the relevant context
4040
// and attached context providers for rendering the current page
41-
const getPage: FC<DynamicParams> = async props => {
41+
const getPage: FC<PageParams> = async props => {
42+
const { path, locale: routeLocale } = await props.params;
43+
4244
// Gets the current full pathname for a given path
43-
const [locale, pathname] = await basePage.getLocaleAndPath(props);
45+
const [locale, pathname] = basePage.getLocaleAndPath(path, routeLocale);
4446

4547
// Verifies if the current route is a dynamic route
4648
const isDynamicRoute = BLOG_DYNAMIC_ROUTES.some(r => r.includes(pathname));
@@ -52,7 +54,7 @@ const getPage: FC<DynamicParams> = async props => {
5254
pathname: `blog/${pathname}`,
5355
});
5456

55-
// If this isn't a valid dynamic route for blog post or there's no mardown file
57+
// If this isn't a valid dynamic route for blog post or there's no markdown file
5658
// for this, then we fail as not found as there's nothing we can do.
5759
if (isDynamicRoute || context.filename) {
5860
return basePage.renderPage({
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { notFound, redirect } from 'next/navigation';
2+
import type { FC } from 'react';
3+
4+
import provideReleaseData from '#site/next-data/providers/releaseData';
5+
import { ENABLE_STATIC_EXPORT } from '#site/next.constants.mjs';
6+
import { ARCHIVE_DYNAMIC_ROUTES } from '#site/next.dynamic.constants.mjs';
7+
import * as basePage from '#site/next.dynamic.page.mjs';
8+
import { defaultLocale } from '#site/next.locales.mjs';
9+
import type { DynamicParams } from '#site/types';
10+
11+
type PageParams = DynamicParams<{ version: string }>;
12+
13+
// This is the default Viewport Metadata
14+
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
15+
export const generateViewport = basePage.generateViewport;
16+
17+
// This generates each page's HTML Metadata
18+
// @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata
19+
export const generateMetadata = basePage.generateMetadata;
20+
21+
// Generates all possible static paths based on the locales and environment configuration
22+
// - Returns an empty array if static export is disabled (`ENABLE_STATIC_EXPORT` is false)
23+
// - If `ENABLE_STATIC_EXPORT_LOCALE` is true, generates paths for all available locales
24+
// - Otherwise, generates paths only for the default locale
25+
// @see https://nextjs.org/docs/app/api-reference/functions/generate-static-params
26+
export const generateStaticParams = async () => {
27+
// Return an empty array if static export is disabled
28+
if (!ENABLE_STATIC_EXPORT) {
29+
return [];
30+
}
31+
32+
return ARCHIVE_DYNAMIC_ROUTES.map(version => ({
33+
locale: defaultLocale.code,
34+
version: version,
35+
}));
36+
};
37+
38+
// This method parses the current pathname and does any sort of modifications needed on the route
39+
// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component
40+
// finally it returns (if the locale and route are valid) the React Component with the relevant context
41+
// and attached context providers for rendering the current page
42+
const getPage: FC<PageParams> = async props => {
43+
const { version, locale: routeLocale } = await props.params;
44+
45+
// Gets the current full pathname for a given path
46+
const [locale, pathname] = basePage.getLocaleAndPath(version, routeLocale);
47+
48+
if (version === 'current') {
49+
const releaseData = provideReleaseData();
50+
51+
const release = releaseData.find(release => release.status === 'Current');
52+
53+
redirect(`/${locale}/download/archive/${release?.versionWithPrefix}`);
54+
}
55+
56+
// Verifies if the current route is a dynamic route
57+
const isDynamicRoute = ARCHIVE_DYNAMIC_ROUTES.some(r => r.includes(pathname));
58+
59+
// Gets the Markdown content and context for Download Archive pages
60+
const [content, context] = await basePage.getMarkdownContext({
61+
locale: locale,
62+
pathname: 'download/archive',
63+
});
64+
65+
// If this isn't a valid dynamic route for archive version or there's no markdown
66+
// file for this, then we fail as not found as there's nothing we can do.
67+
if (isDynamicRoute && context.filename) {
68+
return basePage.renderPage({
69+
content: content,
70+
layout: context.frontmatter.layout!,
71+
context: { ...context, pathname: `/download/archive/${pathname}` },
72+
});
73+
}
74+
75+
return notFound();
76+
};
77+
78+
// Enforces that this route is used as static rendering
79+
// Except whenever on the Development mode as we want instant-refresh when making changes
80+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
81+
export const dynamic = 'force-static';
82+
83+
// Ensures that this endpoint is invalidated and re-executed every X minutes
84+
// so that when new deployments happen, the data is refreshed
85+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate
86+
export const revalidate = 300;
87+
88+
export default getPage;

apps/site/app/[locale]/page.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { ENABLE_STATIC_EXPORT_LOCALE } from '#site/next.constants.mjs';
66
import * as basePage from '#site/next.dynamic.page.mjs';
77
import { availableLocaleCodes } from '#site/next.locales.mjs';
88
import { defaultLocale } from '#site/next.locales.mjs';
9+
import type { DynamicParams } from '#site/types';
910

10-
type DynamicStaticPaths = { path: Array<string>; locale: string };
11-
type DynamicParams = { params: Promise<DynamicStaticPaths> };
11+
type PageParams = DynamicParams<{ path: Array<string> }>;
1212

1313
// This is the default Viewport Metadata
1414
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
@@ -49,9 +49,11 @@ export const generateStaticParams = async () => {
4949
// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component
5050
// finally it returns (if the locale and route are valid) the React Component with the relevant context
5151
// and attached context providers for rendering the current page
52-
const getPage: FC<DynamicParams> = async props => {
52+
const getPage: FC<PageParams> = async props => {
53+
const { path, locale: routeLocale } = await props.params;
54+
5355
// Gets the current full pathname for a given path
54-
const [locale, pathname] = await basePage.getLocaleAndPath(props);
56+
const [locale, pathname] = basePage.getLocaleAndPath(path, routeLocale);
5557

5658
// Gets the Markdown content and context
5759
const [content, context] = await basePage.getMarkdownContext({

apps/site/components/Downloads/DownloadButton/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const DownloadButton: FC<PropsWithChildren<DownloadButtonProps>> = ({
2121
const { os, bitness, architecture } = useClientContext();
2222

2323
const platform = getUserPlatform(architecture, bitness);
24-
const downloadLink = getNodeDownloadUrl(versionWithPrefix, os, platform);
24+
const downloadLink = getNodeDownloadUrl({ versionWithPrefix, os, platform });
2525

2626
return (
2727
<>

apps/site/components/Downloads/DownloadLink.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ const DownloadLink: FC<PropsWithChildren<DownloadLinkProps>> = ({
1919

2020
const platform = getUserPlatform(architecture, bitness);
2121

22-
const downloadLink = getNodeDownloadUrl(
22+
const downloadLink = getNodeDownloadUrl({
2323
versionWithPrefix,
2424
os,
2525
platform,
26-
kind
27-
);
26+
kind,
27+
});
2828

2929
return <LinkWithArrow href={downloadLink}>{children}</LinkWithArrow>;
3030
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useTranslations } from 'next-intl';
2+
import type { FC } from 'react';
3+
4+
import Link from '#site/components/Link';
5+
import type { DownloadArtifact } from '#site/types';
6+
import { OperatingSystemLabel } from '#site/util/download';
7+
8+
type DownloadsTableProps = {
9+
source: Array<DownloadArtifact>;
10+
};
11+
12+
const DownloadsTable: FC<DownloadsTableProps> = ({ source }) => {
13+
const t = useTranslations();
14+
15+
return (
16+
<table>
17+
<thead>
18+
<tr>
19+
<th>{t('components.downloadsTable.fileName')}</th>
20+
<th className="md:w-28">
21+
{t('components.downloadsTable.operatingSystem')}
22+
</th>
23+
<th className="md:w-28">
24+
{t('components.downloadsTable.architecture')}
25+
</th>
26+
</tr>
27+
</thead>
28+
<tbody>
29+
{source.map(release => (
30+
<tr key={`${release.fileName}-${release.architecture}`}>
31+
<td data-label={t('components.downloadsTable.fileName')}>
32+
<Link href={release.url}>{release.fileName}</Link>
33+
</td>
34+
<td data-label={t('components.downloadsTable.operatingSystem')}>
35+
{OperatingSystemLabel[release.os]}
36+
</td>
37+
<td data-label={t('components.downloadsTable.architecture')}>
38+
{release.architecture}
39+
</td>
40+
</tr>
41+
))}
42+
</tbody>
43+
</table>
44+
);
45+
};
46+
47+
export default DownloadsTable;

apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,21 @@ const PrebuiltDownloadButtons: FC = () => {
2222
const { release, os, platform } = useContext(ReleaseContext);
2323

2424
const installerUrl = platform
25-
? getNodeDownloadUrl(release.versionWithPrefix, os, platform, 'installer')
25+
? getNodeDownloadUrl({
26+
versionWithPrefix: release.versionWithPrefix,
27+
os: os,
28+
platform: platform,
29+
kind: 'installer',
30+
})
2631
: '';
2732

2833
const binaryUrl = platform
29-
? getNodeDownloadUrl(release.versionWithPrefix, os, platform, 'binary')
34+
? getNodeDownloadUrl({
35+
versionWithPrefix: release.versionWithPrefix,
36+
os: os,
37+
platform: platform,
38+
kind: 'binary',
39+
})
3040
: '';
3141

3242
return (

apps/site/components/Downloads/Release/ReleaseCodeBox.tsx

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useContext, useMemo } from 'react';
1010
import CodeBox from '#site/components/Common/CodeBox';
1111
import LinkWithArrow from '#site/components/Common/LinkWithArrow';
1212
import Link from '#site/components/Link';
13+
import WithReleaseAlertBox from '#site/components/withReleaseAlertBox';
1314
import { createSval } from '#site/next.jsx.compiler.mjs';
1415
import {
1516
ReleaseContext,
@@ -107,37 +108,15 @@ const ReleaseCodeBox: FC = () => {
107108
>
108109
{t.rich('layouts.download.codeBox.noScriptDetected', {
109110
link: text => (
110-
<Link href="/about/previous-releases#looking-for-latest-release-of-a-version-branch">
111+
<Link href="/download/archive/current">
111112
<b>{text}</b>
112113
</Link>
113114
),
114115
})}
115116
</AlertBox>
116117
</noscript>
117118

118-
{release.status === 'End-of-life' && (
119-
<AlertBox
120-
title={t('components.common.alertBox.warning')}
121-
level="warning"
122-
size="small"
123-
>
124-
{t.rich('layouts.download.codeBox.unsupportedVersionWarning', {
125-
link: text => <Link href="/eol">{text}</Link>,
126-
})}
127-
</AlertBox>
128-
)}
129-
130-
{release.isLts && (
131-
<AlertBox
132-
title={t('components.common.alertBox.info')}
133-
level="info"
134-
size="small"
135-
>
136-
{t.rich('layouts.download.codeBox.ltsVersionFeaturesNotice', {
137-
link: text => <Link href="/download/current">{text}</Link>,
138-
})}
139-
</AlertBox>
140-
)}
119+
<WithReleaseAlertBox status={release.status} />
141120

142121
{!currentPlatform || currentPlatform.recommended || (
143122
<AlertBox

0 commit comments

Comments
 (0)