Skip to content

Commit c463a36

Browse files
authored
meta: refactor data generation to server-side only (#6137)
* meta: refactor data generation to server-side only * chore: use import for this small data * meta: refactor to an inteligent cache system * chore: some code review * chore: final code review changes * meta: updated certain packages
1 parent e51a529 commit c463a36

Some content is hidden

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

47 files changed

+621
-484
lines changed

.eslintignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ node_modules
66
.swc
77
build
88

9-
# Public Files
10-
public/node-releases-data.json
11-
public/blog-posts-data.json
12-
139
# We don't want to lint/prettify the Coverage Results
1410
coverage
1511
junit.xml

.husky/pre-commit

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
#!/usr/bin/env sh
22
. "$(dirname -- "$0")/_/husky.sh"
33

4-
# if the generated files got tracked to this commit we revert them
5-
git reset public/node-releases-data.json
6-
git reset public/blog-posts-data.json
7-
84
# lint and format staged files
95
npx lint-staged

.prettierignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ build
99

1010
# Next.js Generated Files
1111
public/static/documents
12-
public/node-releases-data.json
13-
public/blog-posts-data.json
1412

1513
# Jest
1614
coverage

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import type { FC } from 'react';
55
import { setClientContext } from '@/client-context';
66
import { MDXRenderer } from '@/components/mdxRenderer';
77
import { WithLayout } from '@/components/withLayout';
8-
import { DEFAULT_VIEWPORT, ENABLE_STATIC_EXPORT } from '@/next.constants.mjs';
8+
import { ENABLE_STATIC_EXPORT } from '@/next.constants.mjs';
9+
import { DEFAULT_VIEWPORT } from '@/next.dynamic.constants.mjs';
910
import { dynamicRouter } from '@/next.dynamic.mjs';
1011
import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs';
1112
import { MatterProvider } from '@/providers/matterProvider';
@@ -14,8 +15,8 @@ type DynamicStaticPaths = { path: string[]; locale: string };
1415
type DynamicParams = { params: DynamicStaticPaths };
1516

1617
// This is the default Viewport Metadata
17-
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport
18-
export const viewport = DEFAULT_VIEWPORT;
18+
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
19+
export const generateViewport = async () => ({ ...DEFAULT_VIEWPORT });
1920

2021
// This generates each page's HTML Metadata
2122
// @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata
@@ -113,7 +114,13 @@ const getPage: FC<DynamicParams> = async ({ params }) => {
113114
return notFound();
114115
};
115116

116-
// Enforce that all these routes are compatible with SSR
117+
// In this case we want to catch-all possible pages even to this page. This ensures that we use our 404
118+
// and that all pages including existing ones are handled here and provide `next-intl` locale also
119+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
120+
export const dynamicParams = true;
121+
122+
// Enforces that this route is used as static rendering
123+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
117124
export const dynamic = 'error';
118125

119126
export default getPage;

app/[locale]/feed/[feed]/route.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
11
import { NextResponse } from 'next/server';
22

3-
import { generateWebsiteFeeds } from '@/next.data.mjs';
4-
import { blogData } from '@/next.json.mjs';
3+
import provideWebsiteFeeds from '@/next-data/providers/websiteFeeds';
4+
import { siteConfig } from '@/next.json.mjs';
55
import { defaultLocale } from '@/next.locales.mjs';
66

7-
// loads all the data from the blog-posts-data.json file
8-
const websiteFeeds = generateWebsiteFeeds(blogData);
7+
// We only support fetching these pages from the /en/ locale code
8+
const locale = defaultLocale.code;
99

10-
type StaticParams = { params: { feed: string } };
10+
type StaticParams = { params: { feed: string; locale: string } };
1111

1212
// This is the Route Handler for the `GET` method which handles the request
13-
// for Blog Feeds within the Node.js Website
13+
// for the Node.js Website Blog Feeds (RSS)
1414
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
15-
export const GET = (_: Request, { params }: StaticParams) => {
16-
if (params.feed.includes('.xml') && websiteFeeds.has(params.feed)) {
17-
return new NextResponse(websiteFeeds.get(params.feed)?.rss2(), {
18-
headers: { 'Content-Type': 'application/xml' },
19-
});
20-
}
21-
22-
return new NextResponse(null, { status: 404 });
15+
export const GET = async (_: Request, { params }: StaticParams) => {
16+
// Generate the Feed for the given feed type (blog, releases, etc)
17+
const websiteFeed = await provideWebsiteFeeds(params.feed);
18+
19+
return new NextResponse(websiteFeed, {
20+
headers: { 'Content-Type': 'application/xml' },
21+
status: websiteFeed ? 200 : 404,
22+
});
2323
};
2424

2525
// This function generates the static paths that come from the dynamic segments
26-
// `en/feeds/[feed]` and returns an array of all available static paths
27-
// this is useful for static exports, for example.
28-
// Note that differently from the App Router these don't get built at the build time
29-
// only if the export is already set for static export
30-
export const generateStaticParams = () =>
31-
[...websiteFeeds.keys()].map(feed => ({ feed, locale: defaultLocale.code }));
32-
33-
// Enforces that this route is used as static rendering
26+
// `[locale]/feeds/[feed]` and returns an array of all available static paths
27+
// This is used for ISR static validation and generation
28+
export const generateStaticParams = async () =>
29+
siteConfig.rssFeeds.map(feed => ({ feed: feed.file, locale }));
30+
31+
// Forces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary
32+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
33+
export const dynamicParams = false;
34+
35+
// Enforces that this route is cached and static as much as possible
3436
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
3537
export const dynamic = 'error';
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import provideBlogData from '@/next-data/providers/blogData';
2+
import { defaultLocale } from '@/next.locales.mjs';
3+
4+
// We only support fetching these pages from the /en/ locale code
5+
const locale = defaultLocale.code;
6+
7+
type StaticParams = { params: { category: string; locale: string } };
8+
9+
// This is the Route Handler for the `GET` method which handles the request
10+
// for providing Blog Posts, Pagination for every supported Blog Category
11+
// this includes the `year-XXXX` categories for yearly archives (pagination)
12+
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
13+
export const GET = async (_: Request, { params }: StaticParams) => {
14+
const { posts, pagination } = await provideBlogData(params.category);
15+
16+
return Response.json(
17+
{ posts, pagination },
18+
{ status: posts.length ? 200 : 404 }
19+
);
20+
};
21+
22+
// This function generates the static paths that come from the dynamic segments
23+
// `[locale]/next-data/blog-data/[category]` and returns an array of all available static paths
24+
// This is used for ISR static validation and generation
25+
export const generateStaticParams = async () => {
26+
// This metadata is the original list of all available categories and all available years
27+
// within the Node.js Website Blog Posts (2011, 2012...)
28+
const { meta } = await provideBlogData();
29+
30+
return [
31+
...meta.categories.map(category => ({ category, locale })),
32+
...meta.pagination.map(year => ({ category: `year-${year}`, locale })),
33+
];
34+
};
35+
36+
// Forces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary
37+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
38+
export const dynamicParams = false;
39+
40+
// Enforces that this route is cached and static as much as possible
41+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
42+
export const dynamic = 'error';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import provideReleaseData from '@/next-data/providers/releaseData';
2+
import { defaultLocale } from '@/next.locales.mjs';
3+
4+
// We only support fetching these pages from the /en/ locale code
5+
const locale = defaultLocale.code;
6+
7+
// This is the Route Handler for the `GET` method which handles the request
8+
// for generating static data related to the Node.js Release Data
9+
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
10+
export const GET = async () => {
11+
const releaseData = await provideReleaseData();
12+
13+
return Response.json(releaseData);
14+
};
15+
16+
// This function generates the static paths that come from the dynamic segments
17+
// `[locale]/next-data/release-data/` and returns an array of all available static paths
18+
// This is used for ISR static validation and generation
19+
export const generateStaticParams = async () => [{ locale }];
20+
21+
// Forces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary
22+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
23+
export const dynamicParams = false;
24+
25+
// Enforces that this route is used as static rendering
26+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
27+
export const dynamic = 'error';

app/sitemap.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
1818
const paths: string[] = [];
1919

2020
for (const locale of availableLocaleCodes) {
21-
const routesForLanguage = await dynamicRouter.getRoutesByLanguage(locale);
22-
paths.push(
23-
...routesForLanguage.map(route => `${baseUrlAndPath}/${locale}/${route}`)
24-
);
21+
const routes = await dynamicRouter.getRoutesByLanguage(locale);
22+
23+
paths.push(...routes.map(route => `${baseUrlAndPath}/${locale}/${route}`));
2524
}
2625

2726
const currentDate = new Date().toISOString();

components/Docs/NodeApiVersionLinks.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import type { FC } from 'react';
2+
3+
import getReleaseData from '@/next-data/releaseData';
14
import { DOCS_URL } from '@/next.constants.mjs';
2-
import { releaseData } from '@/next.json.mjs';
35

4-
const NodeApiVersionLinks = () => {
6+
// This is a React Async Server Component
7+
// Note that Hooks cannot be used in a RSC async component
8+
// Async Components do not get re-rendered at all.
9+
const NodeApiVersionLinks: FC = async () => {
10+
const releaseData = await getReleaseData();
11+
512
// Gets all major releases without the 0x release as those are divided on 0.12x and 0.10x
613
const mappedReleases = releaseData.slice(0, -1).map(({ major }) => (
714
<li key={major}>

components/Downloads/DownloadReleasesTable.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import { useTranslations } from 'next-intl';
1+
import { getTranslations } from 'next-intl/server';
22
import type { FC } from 'react';
33

4-
import { releaseData } from '@/next.json.mjs';
4+
import getReleaseData from '@/next-data/releaseData';
55
import { getNodeApiLink } from '@/util/getNodeApiLink';
66
import { getNodejsChangelog } from '@/util/getNodeJsChangelog';
77

8-
const DownloadReleasesTable: FC = () => {
9-
const t = useTranslations();
8+
// This is a React Async Server Component
9+
// Note that Hooks cannot be used in a RSC async component
10+
// Async Components do not get re-rendered at all.
11+
const DownloadReleasesTable: FC = async () => {
12+
const releaseData = await getReleaseData();
13+
14+
const t = await getTranslations();
1015

1116
return (
1217
<table id="tbVersions" className="download-table full-width">

0 commit comments

Comments
 (0)