Skip to content

Commit 6df8fb5

Browse files
committed
chore: improved types
1 parent 2589a37 commit 6df8fb5

15 files changed

Lines changed: 123 additions & 68 deletions

File tree

apps/site/app/[locale]/next-data/blog-data/[category]/[page]/route.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import {
33
providePaginatedBlogPosts,
44
} from '@/next-data/providers/blogData';
55
import { defaultLocale } from '@/next.locales.mjs';
6+
import type { BlogCategory } from '@/types';
67

7-
type DynamicStaticPaths = { locale: string; category: string; page: string };
8+
type DynamicStaticPaths = {
9+
locale: string;
10+
category: BlogCategory;
11+
page: string;
12+
};
813
type StaticParams = { params: Promise<DynamicStaticPaths> };
914

1015
// This is the Route Handler for the `GET` method which handles the request

apps/site/components/Common/BlogPostCard/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import FormattedTime from '@/components/Common/FormattedTime';
55
import Preview from '@/components/Common/Preview';
66
import Link from '@/components/Link';
77
import WithAvatarGroup from '@/components/withAvatarGroup';
8+
import type { BlogCategory } from '@/types';
89
import { mapBlogCategoryToPreviewType } from '@/util/blogUtils';
910

1011
import styles from './index.module.css';
1112

1213
type BlogPostCardProps = {
1314
title: string;
14-
category: string;
15+
category: BlogCategory;
1516
description?: string;
1617
authors?: Array<string>;
1718
date?: Date;

apps/site/components/Containers/MetaBar/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Link from '@/components/Link';
88
import styles from './index.module.css';
99

1010
type MetaBarProps = {
11-
items: Record<string, React.ReactNode>;
11+
items: Partial<Record<IntlMessageKeys, React.ReactNode>>;
1212
headings?: {
1313
items: Array<Heading>;
1414
minDepth?: number;
@@ -33,7 +33,7 @@ const MetaBar: FC<MetaBarProps> = ({ items, headings }) => {
3333
.filter(([, value]) => !!value)
3434
.map(([key, value]) => (
3535
<Fragment key={key}>
36-
<dt>{t(key)}</dt>
36+
<dt>{t(key as IntlMessageKeys)}</dt>
3737
<dd>{value}</dd>
3838
</Fragment>
3939
))}

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

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ const ReleaseCodeBox: FC = () => {
7070
// Determines if the code box should render the skeleton loader
7171
const renderSkeleton = os === 'LOADING' || platform === '';
7272

73+
// Defines fallbacks for the currentPlatform object
74+
const {
75+
label = '',
76+
url = '',
77+
info = 'layouts.download.codeBox.platformInfo.default',
78+
} = currentPlatform ?? {};
79+
7380
return (
7481
<div className="mb-6 mt-4 flex flex-col gap-2">
7582
{release.status === 'End-of-life' && (
@@ -78,11 +85,11 @@ const ReleaseCodeBox: FC = () => {
7885
</AlertBox>
7986
)}
8087

81-
<Skeleton loading={renderSkeleton} hide={currentPlatform?.recommended}>
88+
{!currentPlatform || currentPlatform.recommended || (
8289
<AlertBox title="Info" level="info" size="small">
8390
{t('layouts.download.codeBox.communityPlatformInfo')}
8491
</AlertBox>
85-
</Skeleton>
92+
)}
8693

8794
<Skeleton loading={renderSkeleton}>
8895
<JSXCodeBox language={codeLanguage} className="min-h-[15.5rem]">
@@ -92,22 +99,12 @@ const ReleaseCodeBox: FC = () => {
9299

93100
<span className="text-center text-xs text-neutral-800 dark:text-neutral-200">
94101
<Skeleton loading={renderSkeleton} hide={!currentPlatform}>
95-
{t(
96-
currentPlatform?.bottomInfo ??
97-
'layouts.download.codeBox.platformInfo.default',
98-
{ platform: currentPlatform?.label as string }
99-
)}
100-
</Skeleton>
101-
102-
<br />
103-
104-
<Skeleton loading={renderSkeleton} hide={!currentPlatform}>
102+
{t(info, { platform: label })}{' '}
105103
{t.rich('layouts.download.codeBox.externalSupportInfo', {
106-
platform: currentPlatform?.label as string,
107-
b: chunks => <b>{chunks}</b>,
108-
link: chunks => (
109-
<LinkWithArrow href={currentPlatform?.url}>
110-
{chunks}
104+
platform: label,
105+
link: text => (
106+
<LinkWithArrow href={url}>
107+
<b>{text}</b>
111108
</LinkWithArrow>
112109
),
113110
})}

apps/site/components/withBlogCrossLinks.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import type { FC } from 'react';
33
import { getClientContext } from '@/client-context';
44
import CrossLink from '@/components/Common/CrossLink';
55
import getBlogData from '@/next-data/blogData';
6+
import type { BlogCategory } from '@/types';
67

78
const WithBlogCrossLinks: FC = async () => {
89
const { pathname } = getClientContext();
910

1011
// Extracts from the static URL the components used for the Blog Post slug
1112
const [, , category, postname] = pathname.split('/');
1213

13-
const { posts } = await getBlogData(category);
14+
const { posts } = await getBlogData(category as BlogCategory);
1415

1516
const currentItem = posts.findIndex(
1617
({ slug }) => slug === `/blog/${category}/${postname}`

apps/site/global.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type baseMessages from '@node-core/website-i18n/locales/en.json';
2+
import type { MessageKeys, NestedValueOf, NestedKeyOf } from 'next-intl';
3+
4+
declare global {
5+
// Defines a type for all the IntlMessage shape (which is used internall by next-intl)
6+
// @see https://next-intl.dev/docs/workflows/typescript
7+
type IntlMessages = typeof baseMessages;
8+
9+
// Defines a generic type for all available i18n translation keys, by default not using any namespace
10+
type IntlMessageKeys<
11+
NestedKey extends NamespaceKeys<
12+
IntlMessages,
13+
NestedKeyOf<IntlMessages>
14+
> = never,
15+
> = MessageKeys<
16+
NestedValueOf<
17+
{ '!': IntlMessages },
18+
[NestedKey] extends [never] ? '!' : `!.${NestedKey}`
19+
>,
20+
NestedKeyOf<
21+
NestedValueOf<
22+
{ '!': IntlMessages },
23+
[NestedKey] extends [never] ? '!' : `!.${NestedKey}`
24+
>
25+
>
26+
>;
27+
}
28+
29+
export {};

apps/site/hooks/react-generic/useSiteNavigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const useSiteNavigation = () => {
3838
const t = useTranslations();
3939

4040
const mapNavigationEntries = (entries: Navigation, context: Context = {}) => {
41-
const getFormattedMessage = (label: string, key: string) =>
41+
const getFormattedMessage = (label: IntlMessageKeys, key: string) =>
4242
t.rich(label, context[key] || {}) as FormattedMessage;
4343

4444
return Object.entries(entries).map(

apps/site/layouts/Blog.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import WithBlogCategories from '@/components/withBlogCategories';
77
import WithFooter from '@/components/withFooter';
88
import WithNavBar from '@/components/withNavBar';
99
import getBlogData from '@/next-data/blogData';
10+
import type { BlogCategory } from '@/types';
1011

1112
import styles from './layouts.module.css';
1213

@@ -18,7 +19,10 @@ const getBlogCategory = async (pathname: string) => {
1819
// note that malformed routes can't happen as they are all statically generated
1920
const [, , category = 'all', , page = 1] = pathname.split('/');
2021

21-
const { posts, pagination } = await getBlogData(category, Number(page));
22+
const { posts, pagination } = await getBlogData(
23+
category as BlogCategory,
24+
Number(page)
25+
);
2226

2327
return { category, posts, pagination, page: Number(page) };
2428
};
@@ -27,7 +31,7 @@ const BlogLayout: FC = async () => {
2731
const { pathname } = getClientContext();
2832
const t = await getTranslations();
2933

30-
const mapCategoriesToTabs = (categories: Array<string>) =>
34+
const mapCategoriesToTabs = (categories: Array<BlogCategory>) =>
3135
categories.map(category => ({
3236
key: category,
3337
label: t(`layouts.blog.categories.${category}`),

apps/site/next-data/blogData.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import {
55
VERCEL_ENV,
66
VERCEL_REGION,
77
} from '@/next.constants.mjs';
8-
import type { BlogPostsRSC } from '@/types';
8+
import type { BlogCategory, BlogPostsRSC } from '@/types';
99

10-
const getBlogData = (cat: string, page?: number): Promise<BlogPostsRSC> => {
10+
const getBlogData = (
11+
cat: BlogCategory,
12+
page?: number
13+
): Promise<BlogPostsRSC> => {
1114
const IS_NOT_VERCEL_RUNTIME_ENV =
1215
(!IS_DEV_ENV && VERCEL_ENV && !VERCEL_REGION) ||
1316
(!IS_DEV_ENV && !VERCEL_ENV);

apps/site/next-data/providers/blogData.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,37 @@ import { cache } from 'react';
22

33
import generateBlogData from '@/next-data/generators/blogData.mjs';
44
import { BLOG_POSTS_PER_PAGE } from '@/next.constants.mjs';
5-
import type { BlogPostsRSC } from '@/types';
5+
import type { BlogCategory, BlogPostsRSC } from '@/types';
66

77
const { categories, posts } = await generateBlogData();
88

99
export const provideBlogCategories = cache(() => categories);
1010

11-
export const provideBlogPosts = cache((category: string): BlogPostsRSC => {
12-
const categoryPosts = posts
13-
.filter(post => post.categories.includes(category))
14-
.sort((a, b) => b.date.getTime() - a.date.getTime());
11+
export const provideBlogPosts = cache(
12+
(category: BlogCategory): BlogPostsRSC => {
13+
const categoryPosts = posts
14+
.filter(post => post.categories.includes(category))
15+
.sort((a, b) => b.date.getTime() - a.date.getTime());
1516

16-
// Total amount of possible pages given the amount of blog posts
17-
const total = categoryPosts.length / BLOG_POSTS_PER_PAGE;
17+
// Total amount of possible pages given the amount of blog posts
18+
const total = categoryPosts.length / BLOG_POSTS_PER_PAGE;
1819

19-
return {
20-
posts: categoryPosts,
21-
pagination: {
22-
prev: null,
23-
next: null,
24-
// In case the division results on a remainder we need
25-
// to have an extra page containing the remainder entries
26-
pages: Math.floor(total % 1 === 0 ? total : total + 1),
27-
total: categoryPosts.length,
28-
},
29-
};
30-
});
20+
return {
21+
posts: categoryPosts,
22+
pagination: {
23+
prev: null,
24+
next: null,
25+
// In case the division results on a remainder we need
26+
// to have an extra page containing the remainder entries
27+
pages: Math.floor(total % 1 === 0 ? total : total + 1),
28+
total: categoryPosts.length,
29+
},
30+
};
31+
}
32+
);
3133

3234
export const providePaginatedBlogPosts = cache(
33-
(category: string, page: number): BlogPostsRSC => {
35+
(category: BlogCategory, page: number): BlogPostsRSC => {
3436
const { posts, pagination } = provideBlogPosts(category);
3537

3638
// This autocorrects if invalid numbers are given to only allow

0 commit comments

Comments
 (0)