Skip to content

Commit 8b643f6

Browse files
ovflowdAndrulkocanerakdas
authored
feat: download snippet generation (#7351)
* feat: download snippet generation * feat: changed approach * chore: simplify and reduce bundle size * chore: reduce bundle * chore: minor code fixes * chore: more code review * feat: introduced skeleton and animation * chore: synchronous shiki * chore: prevent internal errors * chore: attempt to handle null situations * fix: attempt to fix vercel env * fix: erroneous env usage for fetch * chore: fix select styles * chore: updated allowlist for env vars * feat: streamline detectos (only run query once) * fix: avatar rendering and avatar group margins * fix: pass ref * chore: optimized release provider memo * chore: next-image mock * Update crowdin.yml (#7353) * chore: improved loading state * Apply suggestions from code review Co-authored-by: Caner Akdas <[email protected]> Signed-off-by: Claudio W <[email protected]> * chore: correct fixes * fix: unit tests * chore: final code review * fix: server component test * fix: rsc still risky for storybook --------- Signed-off-by: Claudio W <[email protected]> Co-authored-by: Andriy Poznakhovskyy <[email protected]> Co-authored-by: Caner Akdas <[email protected]>
1 parent e56edcc commit 8b643f6

File tree

85 files changed

+5170
-2394
lines changed

Some content is hidden

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

85 files changed

+5170
-2394
lines changed

.github/workflows/translations-pr-lint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ on:
1515
- '!apps/site/pages/en/**/*.mdx'
1616
- 'packages/i18n/locales/*.json'
1717
- '!packages/i18n/locales/en.json'
18+
- 'apps/site/snippets/**/*.bash'
19+
- '!apps/site/snippets/en/**/*.bash'
1820

1921
# Cancel any runs on the same branch
2022
concurrency:

apps/site/.storybook/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const config: StorybookConfig = {
4545
resolve: {
4646
...config.resolve,
4747
alias: {
48+
'next/image': join(mocksFolder, './next-image.mjs'),
4849
'next-intl/navigation': join(mocksFolder, './next-intl.mjs'),
4950
'@/client-context': join(mocksFolder, './client-context.mjs'),
5051
'@': join(__dirname, '../'),

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import provideWebsiteFeeds from '@/next-data/providers/websiteFeeds';
44
import { siteConfig } from '@/next.json.mjs';
55
import { defaultLocale } from '@/next.locales.mjs';
66

7-
// We only support fetching these pages from the /en/ locale code
8-
const locale = defaultLocale.code;
9-
10-
type StaticParams = { params: Promise<{ feed: string; locale: string }> };
7+
type DynamicStaticPaths = { locale: string; feed: string };
8+
type StaticParams = { params: Promise<DynamicStaticPaths> };
119

1210
// This is the Route Handler for the `GET` method which handles the request
1311
// for the Node.js Website Blog Feeds (RSS)
@@ -20,15 +18,18 @@ export const GET = async (_: Request, props: StaticParams) => {
2018

2119
return new NextResponse(websiteFeed, {
2220
headers: { 'Content-Type': 'application/xml' },
23-
status: websiteFeed ? 200 : 404,
21+
status: websiteFeed !== undefined ? 200 : 404,
2422
});
2523
};
2624

2725
// This function generates the static paths that come from the dynamic segments
2826
// `[locale]/feeds/[feed]` and returns an array of all available static paths
2927
// This is used for ISR static validation and generation
3028
export const generateStaticParams = async () =>
31-
siteConfig.rssFeeds.map(feed => ({ feed: feed.file, locale }));
29+
siteConfig.rssFeeds.map(feed => ({
30+
locale: defaultLocale.code,
31+
feed: feed.file,
32+
}));
3233

3334
// Enforces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary
3435
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {
44
} from '@/next-data/providers/blogData';
55
import { defaultLocale } from '@/next.locales.mjs';
66

7-
type StaticParams = {
8-
params: Promise<{ locale: string; category: string; page: string }>;
9-
};
7+
type DynamicStaticPaths = { locale: string; category: string; page: string };
8+
type StaticParams = { params: Promise<DynamicStaticPaths> };
109

1110
// This is the Route Handler for the `GET` method which handles the request
1211
// for providing Blog Posts for Blog Categories and Pagination Metadata
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import provideDownloadSnippets from '@/next-data/providers/downloadSnippets';
2+
import { defaultLocale } from '@/next.locales.mjs';
3+
4+
type DynamicStaticPaths = { locale: string };
5+
type StaticParams = { params: Promise<DynamicStaticPaths> };
6+
7+
// This is the Route Handler for the `GET` method which handles the request
8+
// for generating JSON data for Download Snippets
9+
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
10+
export const GET = async (_: Request, props: StaticParams) => {
11+
const params = await props.params;
12+
13+
// Retrieve all available Download snippets for a given locale if available
14+
const snippets = provideDownloadSnippets(params.locale);
15+
16+
// We append always the default/fallback snippets when a result is found
17+
return Response.json(snippets, {
18+
status: snippets !== undefined ? 200 : 404,
19+
});
20+
};
21+
22+
// This function generates the static paths that come from the dynamic segments
23+
// `[locale]/next-data/download-snippets/` this will return a default value as we don't want to
24+
// statically generate this route as it is compute-expensive.
25+
// Hence we generate a fake route just to satisfy Next.js requirements.
26+
export const generateStaticParams = async () => [
27+
{ locale: defaultLocale.code },
28+
];
29+
30+
// Enforces that this route is cached and static as much as possible
31+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
32+
export const dynamic = 'force-static';
33+
34+
// Ensures that this endpoint is invalidated and re-executed every X minutes
35+
// so that when new deployments happen, the data is refreshed
36+
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate
37+
export const revalidate = 300;

apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@ const CATEGORY_TO_THEME_COLOUR_MAP = {
1313
vulnerability: tailwindConfig.theme.colors.warning['600'],
1414
};
1515

16-
type StaticParams = {
17-
params: Promise<{
18-
locale: string;
19-
category: keyof typeof CATEGORY_TO_THEME_COLOUR_MAP;
20-
title: string;
21-
}>;
22-
};
16+
type Category = keyof typeof CATEGORY_TO_THEME_COLOUR_MAP;
17+
18+
type DynamicStaticPaths = { locale: string; category: Category; title: string };
19+
type StaticParams = { params: Promise<DynamicStaticPaths> };
2320

2421
// This is the Route Handler for the `GET` method which handles the request
2522
// for generating OpenGraph images for Blog Posts and Pages

apps/site/components/Blog/BlogHeader/index.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use client';
2-
31
import { RssIcon } from '@heroicons/react/24/solid';
42
import { useTranslations } from 'next-intl';
53
import type { FC } from 'react';
@@ -9,15 +7,13 @@ import { siteConfig } from '@/next.json.mjs';
97

108
import styles from './index.module.css';
119

12-
type BlogHeaderProps = {
13-
category: string;
14-
};
10+
type BlogHeaderProps = { category: string };
1511

1612
const BlogHeader: FC<BlogHeaderProps> = ({ category }) => {
1713
const t = useTranslations();
18-
const currentFile =
19-
siteConfig.rssFeeds.find(item => item.category === category)?.file ??
20-
'blog.xml';
14+
15+
const feed = siteConfig.rssFeeds.find(item => item.category === category);
16+
const currentFile = feed ? feed.file : 'blog.xml';
2117

2218
return (
2319
<header className={styles.blogHeader}>

apps/site/components/Common/AvatarGroup/Avatar/index.module.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
justify-center
1111
rounded-full
1212
border-2
13-
border-white
13+
border-transparent
1414
bg-neutral-100
1515
object-cover
1616
text-xs
1717
text-neutral-800
18-
dark:border-neutral-950
1918
dark:bg-neutral-900
2019
dark:text-neutral-300;
2120
}

apps/site/components/Common/AvatarGroup/Avatar/index.stories.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,4 @@ export const NoSquare: Story = {
2020
},
2121
};
2222

23-
export const FallBack: Story = {
24-
args: {
25-
image: 'https://avatars.githubusercontent.com/u/',
26-
nickname: 'John Doe',
27-
fallback: 'JD',
28-
},
29-
};
30-
3123
export default { component: Avatar } as Meta;

apps/site/components/Common/AvatarGroup/Avatar/index.tsx

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import * as RadixAvatar from '@radix-ui/react-avatar';
21
import classNames from 'classnames';
3-
import type { ComponentPropsWithoutRef, ElementRef } from 'react';
2+
import Image from 'next/image';
3+
import type { HTMLAttributes } from 'react';
44
import { forwardRef } from 'react';
55

66
import Link from '@/components/Link';
@@ -16,37 +16,45 @@ export type AvatarProps = {
1616
url?: string;
1717
};
1818

19+
// @TODO: We temporarily removed the Avatar Radix UI primitive, since it was causing flashing
20+
// during initial load and not being able to render nicely when images are already cached.
21+
// @see https://github.com/radix-ui/primitives/pull/3008
1922
const Avatar = forwardRef<
20-
ElementRef<typeof RadixAvatar.Root>,
21-
ComponentPropsWithoutRef<typeof RadixAvatar.Root> & AvatarProps
23+
HTMLSpanElement,
24+
HTMLAttributes<HTMLSpanElement> & AvatarProps
2225
>(({ image, nickname, name, fallback, url, size = 'small', ...props }, ref) => {
2326
const Wrapper = url ? Link : 'div';
2427

2528
return (
26-
<RadixAvatar.Root
29+
<span
2730
{...props}
28-
className={classNames(styles.avatar, styles[size], props.className)}
2931
ref={ref}
32+
className={classNames(styles.avatar, styles[size], props.className)}
3033
>
3134
<Wrapper
3235
href={url || undefined}
3336
target={url ? '_blank' : undefined}
3437
className={styles.wrapper}
3538
>
36-
<RadixAvatar.Image
37-
loading="lazy"
38-
src={image}
39-
alt={name || nickname}
40-
className={styles.item}
41-
/>
42-
<RadixAvatar.Fallback
43-
delayMs={500}
44-
className={classNames(styles.item, styles[size])}
45-
>
46-
{fallback}
47-
</RadixAvatar.Fallback>
39+
{image && (
40+
<Image
41+
width={40}
42+
height={40}
43+
loading="lazy"
44+
decoding="async"
45+
src={image}
46+
alt={name || nickname}
47+
className={styles.item}
48+
/>
49+
)}
50+
51+
{!image && (
52+
<span className={classNames(styles.item, styles[size])}>
53+
{fallback}
54+
</span>
55+
)}
4856
</Wrapper>
49-
</RadixAvatar.Root>
57+
</span>
5058
);
5159
});
5260

0 commit comments

Comments
 (0)