Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DS-965: Linting Cache Config & ADR #370

Merged
merged 12 commits into from
Jan 8, 2025
40 changes: 35 additions & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"extends": "next/core-web-vitals",
"extends": [
"next/core-web-vitals",
"next/typescript"
],
"rules": {
"comma-dangle": ["error", {
"objects": "always-multiline",
Expand All @@ -19,30 +22,57 @@
"jsx-a11y/label-has-associated-control": [ 2, {
"components": [ "Label" ],
"required": {
"some": [ "nesting", "id" ]
"some": [ "nesting", "id" ]
}
}],
"jsx-quotes": ["error", "prefer-double"],
"quotes": [2, "single",
{ "avoidEscape": true, "allowTemplateLiterals": true }
],
"space-before-function-paren": 0,
"react/jsx-props-no-spreading": 0,
"react/prop-types": 0,
"react/jsx-handler-names": 0,
"react/jsx-filename-extension": [
"error",
{
"extensions": [
".js",
".ts",
".jsx",
".tsx"
]
}
],
"react/jsx-fragments": 0,
"react/jsx-one-expression-per-line": 0,
"react/no-unused-prop-types": 0,
"react/require-default-props": 0,
"react/function-component-definition": [2, {
"namedComponents": ["function-declaration", "arrow-function"]
}],
"react/jsx-no-useless-fragment": [2, { "allowExpressions": true }], // https://github.com/jsx-eslint/eslint-plugin-react/issues/2584
"react/jsx-indent": [2, 2],
"react/display-name": 0,
"class-methods-use-this": 0,
"import/prefer-default-export": 0,
"import/no-extraneous-dependencies": 0,
"import/no-cycle": 0,
"import/no-unresolved": 0,
"no-unused-vars": 0,
"no-multi-spaces": "error",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"vars": "local",
"args": "none"
}
],
"import/export": 0,
"func-names": 0,
"semi": [1, "always"], // 1 is for warning
"@next/next/no-img-element": 0
},
"settings": {
"tailwindcss": {
"callees": ["classnames", "clsx", "ctl", "cva", "tv", "cn", "dcnb", "cnb"] // Tailwind utility class detection for specific libraries
}
}
}
4 changes: 2 additions & 2 deletions app/(editor)/editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { components as Components } from '@/components/StoryblokProvider';
import { resolveRelations } from '@/utilities/resolveRelations';
import { notFound } from 'next/navigation';
import { ComponentNotFound } from '@/components/Storyblok/ComponentNotFound';
import getStoryData from '@/utilities/data/getStoryData';
import getStoryList from '@/utilities/data/getStoryList';
import { getStoryData } from '@/utilities/data/getStoryData';
import { getStoryList } from '@/utilities/data/getStoryList';

// Control what happens when a dynamic segment is visited that was not generated with generateStaticParams.
export const dynamic = 'force-dynamic';
Expand Down
45 changes: 21 additions & 24 deletions app/(storyblok)/[[...slug]]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React from 'react';
import StoryblokProvider from '@/components/StoryblokProvider';
import {
ISbStoriesParams, getStoryblokApi, storyblokInit, apiPlugin, StoryblokStory, StoryblokClient,
storyblokInit, apiPlugin, StoryblokStory,
} from '@storyblok/react/rsc';
import { components as Components } from '@/components/StoryblokProvider';
import { resolveRelations } from '@/utilities/resolveRelations';
import { ComponentNotFound } from '@/components/Storyblok/ComponentNotFound';
import { isProduction } from '@/utilities/getActiveEnv';
import { getStoryDataCached } from '@/utilities/data/getStoryData';
import { getConfigBlokCached } from '@/utilities/data/getConfigBlok';
import { getSlugPrefix } from '@/utilities/getSlugPrefix';
import { getPageMetadata } from '@/utilities/getPageMetadata';
import { Metadata } from 'next';

// Storyblok bridge options.
const bridgeOptions = {
Expand All @@ -31,34 +35,27 @@ storyblokInit({
});

/**
* Get the data out of the Storyblok API for the page.
*
* Make sure to not export the below functions otherwise there will be a typescript error
* https://github.com/vercel/next.js/discussions/48724
* Generate the SEO metadata for the page.
*/
async function getStoryData(slug = 'momentum/page-not-found') {
const isProd = isProduction();
const storyblokApi: StoryblokClient = getStoryblokApi();
const sbParams: ISbStoriesParams = {
version: isProd ? 'published' : 'draft',
resolve_relations: resolveRelations,
};
export async function generateMetadata(): Promise<Metadata> {

try {
const story = await storyblokApi.get(`cdn/stories/${slug}`, sbParams);
return story;
} catch (error: any) {
const slugPrefix = getSlugPrefix();
const prefixedSlug = slugPrefix + '/page-not-found';
const config = await getConfigBlokCached();

if (error && error.status && error.status === 404) {
return { data: 404 };
}
// Get the story data.
const { data: { story } } = await getStoryDataCached({ path: prefixedSlug });

throw error;
}
};
// Generate the metadata.
const meta = getPageMetadata({ story, sbConfig: config, slug: prefixedSlug });
return meta;
}

/**
* Get the story data from the Storyblok API through the cache.
*/
export default async function PageNotFound() {
const { data } = await getStoryData();
const { data } = await getStoryDataCached({ path: 'momentum/page-not-found' });

if (data === 404) {
return (
Expand Down
68 changes: 22 additions & 46 deletions app/(storyblok)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Metadata } from 'next';
import {
ISbStoriesParams, getStoryblokApi, storyblokInit, apiPlugin, StoryblokStory, StoryblokClient,
storyblokInit, apiPlugin, StoryblokStory,
} from '@storyblok/react/rsc';
import { components as Components } from '@/components/StoryblokProvider';
import { resolveRelations } from '@/utilities/resolveRelations';
import { getPageMetadata } from '@/utilities/getPageMetadata';
import { ComponentNotFound } from '@/components/Storyblok/ComponentNotFound';
import { notFound } from 'next/navigation';
import getStoryData from '@/utilities/data/getStoryData';
import getStoryList from '@/utilities/data/getStoryList';
import { getStoryDataCached, getConfigBlokCached, getAllStoriesCached } from '@/utilities/data/';
import { getStoryListCached } from '@/utilities/data/getStoryList';
import { isProduction } from '@/utilities/getActiveEnv';
import { getSlugPrefix } from '@/utilities/getSlugPrefix';

Expand Down Expand Up @@ -57,29 +57,19 @@ storyblokInit({
*/
export async function generateStaticParams() {
const isProd = isProduction();
// Fetch new content from storyblok.
const storyblokApi: StoryblokClient = getStoryblokApi();
let sbParams: ISbStoriesParams = {
version: isProd ? 'published' : 'draft',
resolve_links: '0',
resolve_assets: 0,
per_page: 100,
starts_with: getSlugPrefix() + '/',
};

// Use the `cdn/links` endpoint to get a list of all stories without all the extra data.
const response = await storyblokApi.getAll('cdn/links', sbParams);

// Filters
let stories = response;

// Get all the stories.
let stories = await getAllStoriesCached();
// Filter out folders.
stories = response.filter((link) => link.is_folder === false);
stories = stories.filter((link) => link.is_folder === false);
// Filter out test content by filtering out the `test` folder.
stories = stories.filter((link) => !link.slug.startsWith(getSlugPrefix() + '/test'));
if (isProd) {
stories = stories.filter((link) => !link.slug.startsWith(getSlugPrefix() + '/test'));
}
// Filter out globals by filtering out the `global-components` folder.
stories = stories.filter((link) => !link.slug.startsWith(getSlugPrefix() + '/global-components'));

let paths: PathsType[] = [];
const paths: PathsType[] = [];

stories.forEach((story) => {

Expand All @@ -101,9 +91,6 @@ export async function generateStaticParams() {

});

// Add the home page.
paths.push({ slug: [''] });

return paths;
};

Expand All @@ -112,28 +99,17 @@ export async function generateStaticParams() {
*/
export async function generateMetadata({ params }: ParamsType): Promise<Metadata> {
const { slug } = params;
try {

// Convert the slug to a path.
const slugPath = slug ? slug.join('/') : '';

// Construct the slug for Storyblok.
const prefixedSlug = getSlugPrefix() + '/' + slugPath;
const slugPrefix = getSlugPrefix();
const slugPath = slug ? slug.join('/') : '';
const prefixedSlug = slugPrefix + '/' + slugPath;
const config = await getConfigBlokCached();

// Get the story data.
const { data } = await getStoryData({ path: prefixedSlug });
if (!data.story || !data.story.content) {
notFound();
}
const blok = data.story.content;
const meta = getPageMetadata({ blok, slug: slugPath });
return meta;
}
catch (error) {
console.log('Metadata error:', error, slug);
}
// Get the story data.
const { data: { story } } = await getStoryDataCached({ path: prefixedSlug });

notFound();
// Generate the metadata.
const meta = getPageMetadata({ story, sbConfig: config, slug: slugPath });
return meta;
}

/**
Expand All @@ -149,15 +125,15 @@ export default async function Page({ params }: ParamsType) {
const prefixedSlug = getSlugPrefix() + '/' + slugPath;

// Get data out of the API.
const { data } = await getStoryData({ path: prefixedSlug });
const { data } = await getStoryDataCached({ path: prefixedSlug });

// Define an additional data container to pass through server data fetch to client components.
// as everything below the `StoryblokStory` is a client side component.
let extra = {};

// Get additional data for those stories that need it.
if (data?.story?.content?.component === 'sbStoryFilterPage') {
extra = await getStoryList({ path: prefixedSlug });
extra = await getStoryListCached({ path: prefixedSlug });
}

// Failed to fetch from API because story slug was not found.
Expand Down
4 changes: 2 additions & 2 deletions app/(storyblok)/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export default function Error({error, reset}: {
}, [error]);

return (
<div className='bg-black'>
<div className="bg-black">
<Masthead />
<main>
<Container width='site' className='rs-my-8 text-white'>
<Container width="site" className="rs-my-8 text-white">
<h1>Something went wrong.</h1>
<p>Try refreshing your browser.</p>
</Container>
Expand Down
10 changes: 5 additions & 5 deletions app/(storyblok)/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ const Skeleton = DynamicLoad(() => import('react-loading-skeleton'), { ssr: fals

const Loading = () => {
return (
<div className='bg-black'>
<div className="bg-black">
<Masthead />
<main>
<Container width='site' className='rs-my-10'>
<FlexBox gap direction='row' wrap='wrap'>
<Skeleton height={500} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName='w-full'/>
<Skeleton height={300} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName='w-full'/>
<Container width="site" className="rs-my-10">
<FlexBox gap direction="row" wrap="wrap">
<Skeleton height={500} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName="w-full"/>
<Skeleton height={300} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName="w-full"/>
</FlexBox>
</Container>
</main>
Expand Down
4 changes: 2 additions & 2 deletions app/global-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export default function Error({error, reset}: {
}, [error]);

return (
<div className='bg-black'>
<div className="bg-black">
<Masthead />
<main>
<Container width='site' className='rs-my-8 text-white'>
<Container width="site" className="rs-my-8 text-white">
<h1>Something went very wrong.</h1>
<p>Try refreshing your browser.</p>
</Container>
Expand Down
10 changes: 5 additions & 5 deletions app/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ const Skeleton = DynamicLoad(() => import('react-loading-skeleton'), { ssr: fals

const Loading = () => {
return (
<div className='bg-black'>
<div className="bg-black">
<Masthead />
<main>
<Container width='site' className='rs-my-10'>
<FlexBox gap direction='row' wrap='wrap'>
<Skeleton height={500} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName='w-full'/>
<Skeleton height={300} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName='w-full'/>
<Container width="site" className="rs-my-10">
<FlexBox gap direction="row" wrap="wrap">
<Skeleton height={500} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName="w-full"/>
<Skeleton height={300} highlightColor="rgba(255,255,255,0.2)" baseColor="rgba(0,0,0,0.6)" containerClassName="w-full"/>
</FlexBox>
</Container>
</main>
Expand Down
24 changes: 24 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { components as Components } from '@/components/StoryblokProvider';
import { resolveRelations } from '@/utilities/resolveRelations';
import { ComponentNotFound } from '@/components/Storyblok/ComponentNotFound';
import { isProduction } from '@/utilities/getActiveEnv';
import { getSlugPrefix } from '@/utilities/getSlugPrefix';
import { Metadata } from 'next';
import { getStoryDataCached } from '@/utilities/data/getStoryData';
import { getConfigBlokCached } from '@/utilities/data/getConfigBlok';
import { getPageMetadata } from '@/utilities/getPageMetadata';

// Storyblok bridge options.
const bridgeOptions = {
Expand Down Expand Up @@ -47,6 +52,7 @@ async function getStoryData(slug = 'momentum/page-not-found') {
try {
const story = await storyblokApi.get(`cdn/stories/${slug}`, sbParams);
return story;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {

if (error && error.status && error.status === 404) {
Expand All @@ -57,6 +63,24 @@ async function getStoryData(slug = 'momentum/page-not-found') {
}
};

/**
* Generate the SEO metadata for the page.
*/
export async function generateMetadata(): Promise<Metadata> {

const slugPrefix = getSlugPrefix();
const prefixedSlug = slugPrefix + '/page-not-found';
const config = await getConfigBlokCached();


// Get the story data.
const { data: { story } } = await getStoryDataCached({ path: prefixedSlug });

// Generate the metadata.
const meta = getPageMetadata({ story, sbConfig: config, slug: prefixedSlug });
return meta;
}

export default async function PageNotFound() {
const { data } = await getStoryData();

Expand Down
Loading
Loading