From 3771aa15a57643087be43c058a8e71e6f16f4fbd Mon Sep 17 00:00:00 2001 From: Kevin Stubbs Date: Thu, 2 Jan 2025 16:24:48 +0200 Subject: [PATCH 1/2] Solve compilation errors when strict and strictNullChecks are set to true. --- .../pantheoncloud/[...command]/api-options.ts | 2 +- .../app/articles/[...uri]/article-view.tsx | 31 +++++++++++++++---- .../[...uri]/clientside-articleview.tsx | 4 +-- .../app/articles/[...uri]/page.tsx | 18 ++++++++--- .../app/articles/page.tsx | 4 +-- .../app/examples/ssg-isr/[uri]/page.tsx | 2 +- .../app/examples/ssg-isr/page.tsx | 4 +-- .../app/layout.tsx | 3 +- .../app/search/page.tsx | 16 +++++++--- .../app/search/search-results.tsx | 10 +++--- .../components/article-list.tsx | 2 +- .../components/grid.tsx | 16 +++++++--- .../components/header/search-bar.tsx | 4 +-- .../components/page-header.tsx | 2 +- .../hooks/usePagination.ts | 2 +- .../nextjs-starter-approuter-ts/lib/utils.ts | 14 +++++++-- .../nextjs-starter-approuter-ts/next-env.d.ts | 2 +- .../nextjs-starter-approuter-ts/tsconfig.json | 3 +- .../nextjs-starter-ts/components/grid.tsx | 14 ++++++--- .../components/page-header.tsx | 2 +- .../smart-components/error-boundary.tsx | 4 +-- starters/nextjs-starter-ts/lib/utils.ts | 2 +- starters/nextjs-starter-ts/next-env.d.ts | 2 +- starters/nextjs-starter-ts/pages/_app.tsx | 4 ++- .../pages/api/utils/oembed.ts | 6 ++-- .../pages/api/utils/paginate.ts | 2 +- .../pages/articles/[...uri].tsx | 18 +++++++++-- .../pages/articles/index.tsx | 13 +++++++- .../pages/component-preview/[id].tsx | 7 +++-- .../pages/examples/ssg-isr/[uri].tsx | 14 ++++----- .../pages/examples/ssg-isr/index.tsx | 9 +++++- starters/nextjs-starter-ts/pages/index.tsx | 7 +++-- starters/nextjs-starter-ts/pages/search.tsx | 9 +++--- starters/nextjs-starter-ts/tsconfig.json | 3 +- starters/nextjs-starter/next-env.d.ts | 2 +- .../component-preview/{[id].tsx => [id].jsx} | 6 ++-- starters/nextjs-starter/tsconfig.json | 3 +- 37 files changed, 183 insertions(+), 83 deletions(-) rename starters/nextjs-starter/pages/component-preview/{[id].tsx => [id].jsx} (85%) diff --git a/starters/nextjs-starter-approuter-ts/app/api/pantheoncloud/[...command]/api-options.ts b/starters/nextjs-starter-approuter-ts/app/api/pantheoncloud/[...command]/api-options.ts index 6950f77a..922ceb1f 100644 --- a/starters/nextjs-starter-approuter-ts/app/api/pantheoncloud/[...command]/api-options.ts +++ b/starters/nextjs-starter-approuter-ts/app/api/pantheoncloud/[...command]/api-options.ts @@ -3,7 +3,7 @@ import { serverSmartComponentMap } from "../../../../components/smart-components export const pantheonAPIOptions: PantheonAPIOptions = { resolvePath: (article) => `/articles/${article.slug || article.id}`, - getSiteId: () => process.env.PCC_SITE_ID, + getSiteId: () => process.env.PCC_SITE_ID as string, smartComponentMap: serverSmartComponentMap, componentPreviewPath: (componentName) => `/component-preview/${componentName}`, diff --git a/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/article-view.tsx b/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/article-view.tsx index c1273bc3..22da44db 100644 --- a/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/article-view.tsx +++ b/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/article-view.tsx @@ -5,13 +5,30 @@ import queryString from "query-string"; import { pantheonAPIOptions } from "../../api/pantheoncloud/[...command]/api-options"; import { ClientsideArticleView } from "./clientside-articleview"; -export const ArticleView = async ({ params, searchParams }) => { - const { article, grant } = await getServersideArticle(params, searchParams); +export interface ArticleViewProps { + params: { uri: string }; + searchParams: { + publishingLevel: "PRODUCTION" | "REALTIME"; + pccGrant: string | undefined; + }; +} + +export const ArticleView = async ({ + params, + searchParams, +}: ArticleViewProps) => { + const { article, grant } = await getServersideArticle({ + params, + searchParams, + }); - return ; + return ; }; -export async function getServersideArticle(params, searchParams) { +export async function getServersideArticle({ + params, + searchParams, +}: ArticleViewProps) { const { uri } = params; const { publishingLevel, pccGrant, ...query } = searchParams; @@ -20,7 +37,8 @@ export async function getServersideArticle(params, searchParams) { const article = await PCCConvenienceFunctions.getArticleBySlugOrId( slugOrId, - publishingLevel?.toString().toUpperCase() || "PRODUCTION", + (publishingLevel?.toString().toUpperCase() as "PRODUCTION" | "REALTIME") || + "PRODUCTION", ); if (!article) { @@ -29,7 +47,8 @@ export async function getServersideArticle(params, searchParams) { if ( article.slug?.trim().length && - article.slug.toLowerCase() !== slugOrId?.trim().toLowerCase() + article.slug.toLowerCase() !== slugOrId?.trim().toLowerCase() && + pantheonAPIOptions.resolvePath != null ) { // If the article was accessed by the id rather than the slug - then redirect to the canonical // link (mostly for SEO purposes than anything else). diff --git a/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/clientside-articleview.tsx b/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/clientside-articleview.tsx index 001837f7..301e7afd 100644 --- a/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/clientside-articleview.tsx +++ b/starters/nextjs-starter-approuter-ts/app/articles/[...uri]/clientside-articleview.tsx @@ -15,11 +15,11 @@ updateConfig({ }); export const ClientsideArticleView = ({ - grant, article, + grant, }: { - grant: string; article: Article; + grant?: string | undefined; }) => { return (
@@ -16,8 +23,11 @@ export default async function ArticlePage({ params, searchParams }) { ); } -export async function generateMetadata({ params, searchParams }) { - const { article } = await getServersideArticle(params, searchParams); +export async function generateMetadata({ + params, + searchParams, +}: ArticleViewProps) { + const { article } = await getServersideArticle({ params, searchParams }); return getSeoMetadata(article); } diff --git a/starters/nextjs-starter-approuter-ts/app/articles/page.tsx b/starters/nextjs-starter-approuter-ts/app/articles/page.tsx index 3540856c..85e5cc7e 100644 --- a/starters/nextjs-starter-approuter-ts/app/articles/page.tsx +++ b/starters/nextjs-starter-approuter-ts/app/articles/page.tsx @@ -3,12 +3,12 @@ import ArticleList from "../../components/article-list"; import Layout from "../../components/layout"; import { PAGE_SIZE } from "../../constants"; -async function fetchNextPages(cursor: string) { +async function fetchNextPages(cursor?: string | null | string) { "use server"; const { data, cursor: newCursor } = await PCCConvenienceFunctions.getPaginatedArticles({ pageSize: PAGE_SIZE, - cursor, + cursor: cursor || undefined, }); return { data, diff --git a/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/[uri]/page.tsx b/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/[uri]/page.tsx index 98c46324..b874b991 100644 --- a/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/[uri]/page.tsx +++ b/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/[uri]/page.tsx @@ -53,7 +53,7 @@ export async function generateStaticParams() { return publishedArticles.flatMap((article) => { const params = [{ uri: article.id }]; - if (article.metadata.slug) { + if (article.metadata?.slug) { params.push({ uri: String(article.metadata.slug) }); } return params; diff --git a/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/page.tsx b/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/page.tsx index 143f7ac8..7b1fe31f 100644 --- a/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/page.tsx +++ b/starters/nextjs-starter-approuter-ts/app/examples/ssg-isr/page.tsx @@ -9,12 +9,12 @@ export const metadata: Metadata = { description: "Example of using SSG and ISR", }; -async function fetchNextPages(cursor: string) { +async function fetchNextPages(cursor?: string | null | undefined) { "use server"; const { data, cursor: newCursor } = await PCCConvenienceFunctions.getPaginatedArticles({ pageSize: PAGE_SIZE, - cursor, + cursor: cursor || undefined, }); return { diff --git a/starters/nextjs-starter-approuter-ts/app/layout.tsx b/starters/nextjs-starter-approuter-ts/app/layout.tsx index 8dc914ba..faefcbad 100644 --- a/starters/nextjs-starter-approuter-ts/app/layout.tsx +++ b/starters/nextjs-starter-approuter-ts/app/layout.tsx @@ -2,13 +2,14 @@ import { Poppins } from "next/font/google"; // import Script from "next/script"; import "../styles/globals.css"; +import { PropsWithChildren } from "react"; const poppins = Poppins({ subsets: ["latin"], weight: ["400", "500", "700"], }); -function MyApp({ children }) { +function MyApp({ children }: PropsWithChildren) { return ( diff --git a/starters/nextjs-starter-approuter-ts/app/search/page.tsx b/starters/nextjs-starter-approuter-ts/app/search/page.tsx index 76236e7f..e50e7312 100644 --- a/starters/nextjs-starter-approuter-ts/app/search/page.tsx +++ b/starters/nextjs-starter-approuter-ts/app/search/page.tsx @@ -2,14 +2,20 @@ import { PCCConvenienceFunctions } from "@pantheon-systems/pcc-react-sdk/server" import Layout from "../../components/layout"; import SearchResults from "./search-results"; -export default async function SearchPage({ searchParams }) { +interface Props { + searchParams: { q?: string | null | undefined }; +} + +export default async function SearchPage({ searchParams }: Props) { const searchResults = await PCCConvenienceFunctions.getAllArticlesWithSummary( { publishingLevel: "PRODUCTION", }, - { - bodyContains: searchParams.q, - }, + searchParams.q + ? { + bodyContains: searchParams.q, + } + : undefined, true, ); @@ -23,7 +29,7 @@ export default async function SearchPage({ searchParams }) { ); } -export function generateMetadata({ searchParams }) { +export function generateMetadata({ searchParams }: Props) { return { title: `Search results for "${searchParams.q}"`, description: `Search results for "${searchParams.q}"`, diff --git a/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx b/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx index c88e3cf9..0b6ee9ae 100644 --- a/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx +++ b/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx @@ -33,15 +33,15 @@ export default function SearchResults({
{isLoading ? ( - ) : ( + ) : summary ? ( {summary} - )} + ) : null}
) : null}
- {isLoading || searchResults?.length > 0 ? ( + {isLoading || (searchResults && searchResults?.length > 0) ? ( (searchResults ?? Array.from({ length: 5 })).map((result, index) => (
@@ -59,11 +59,11 @@ export default function SearchResults({

{isLoading ? ( - ) : ( + ) : result.snippet ? ( markdownToTxt( result.snippet.replaceAll(/{#h\..*}\n/g, "\n"), ) - )} + ) : null}

diff --git a/starters/nextjs-starter-approuter-ts/components/article-list.tsx b/starters/nextjs-starter-approuter-ts/components/article-list.tsx index 412b895d..c67c3d77 100644 --- a/starters/nextjs-starter-approuter-ts/components/article-list.tsx +++ b/starters/nextjs-starter-approuter-ts/components/article-list.tsx @@ -16,7 +16,7 @@ interface Props { articles: PaginatedArticle[] | ArticleWithoutContent[]; totalCount: number; cursor: string; - fetcher: (cursor: string) => Promise<{ + fetcher: (cursor?: string | null | undefined) => Promise<{ data: PaginatedArticle[] | ArticleWithoutContent[]; newCursor: string; }>; diff --git a/starters/nextjs-starter-approuter-ts/components/grid.tsx b/starters/nextjs-starter-approuter-ts/components/grid.tsx index 4831c973..46c6a65e 100644 --- a/starters/nextjs-starter-approuter-ts/components/grid.tsx +++ b/starters/nextjs-starter-approuter-ts/components/grid.tsx @@ -59,7 +59,7 @@ export function ArticleGridCard({ isWide = false, }: ArticleGridCardProps) { const targetHref = `${basePath}/${article.metadata?.slug || article.id}`; - const imageSrc = article.metadata?.["Hero Image"] || null; + const imageSrc = (article.metadata?.["Hero Image"] as string) || null; return (
{article.title} - {article.metadata?.["Description"] && ( + {article.metadata?.["Description"] ? (

{article.metadata?.["Description"]?.toString() || ""}

- )} + ) : null}
@@ -107,12 +107,18 @@ export function ArticleGridCard({ ); } -function GridItemCoverImage({ imageSrc, imageAltText }) { +function GridItemCoverImage({ + imageSrc, + imageAltText, +}: { + imageSrc: string | null; + imageAltText?: string | null | undefined; +}) { return imageSrc != null ? ( // eslint-disable-next-line @next/next/no-img-element {imageAltText} ) : ( diff --git a/starters/nextjs-starter-approuter-ts/components/header/search-bar.tsx b/starters/nextjs-starter-approuter-ts/components/header/search-bar.tsx index fa41c255..cf28d52d 100644 --- a/starters/nextjs-starter-approuter-ts/components/header/search-bar.tsx +++ b/starters/nextjs-starter-approuter-ts/components/header/search-bar.tsx @@ -77,7 +77,7 @@ export default function SearchBar() { function SearchBarForm({ defaultSearchQuery, }: { - defaultSearchQuery?: string; + defaultSearchQuery?: string | null | undefined; }) { const onSubmit: FormEventHandler = (e) => { e.preventDefault(); @@ -96,7 +96,7 @@ function SearchBarForm({ >

{title}

diff --git a/starters/nextjs-starter-approuter-ts/hooks/usePagination.ts b/starters/nextjs-starter-approuter-ts/hooks/usePagination.ts index 9a9f41f8..84b5c1f1 100644 --- a/starters/nextjs-starter-approuter-ts/hooks/usePagination.ts +++ b/starters/nextjs-starter-approuter-ts/hooks/usePagination.ts @@ -7,7 +7,7 @@ import { useEffect, useState } from "react"; interface Props { cursor?: string; initialArticles?: PaginatedArticle[] | ArticleWithoutContent[]; - fetcher: (cursor: string) => Promise<{ + fetcher: (cursor: string | null | undefined) => Promise<{ data: PaginatedArticle[] | ArticleWithoutContent[]; newCursor: string; }>; diff --git a/starters/nextjs-starter-approuter-ts/lib/utils.ts b/starters/nextjs-starter-approuter-ts/lib/utils.ts index dad3b900..d3b3a6ca 100644 --- a/starters/nextjs-starter-approuter-ts/lib/utils.ts +++ b/starters/nextjs-starter-approuter-ts/lib/utils.ts @@ -16,7 +16,17 @@ export function formatDate(input: string | number): string { }); } -export function getSeoMetadata(article: ArticleWithoutContent): Metadata { +export function getSeoMetadata( + article: ArticleWithoutContent | null, +): Metadata { + if (article == null) { + return { + openGraph: { + type: "website", + }, + }; + } + const tags: string[] = article.tags && article.tags.length > 0 ? article.tags : []; const imageProperties = [ @@ -53,7 +63,7 @@ export function getSeoMetadata(article: ArticleWithoutContent): Metadata { authors, openGraph: { type: "website", - title: article.title, + title: article.title || undefined, images: imageProperties, description, }, diff --git a/starters/nextjs-starter-approuter-ts/next-env.d.ts b/starters/nextjs-starter-approuter-ts/next-env.d.ts index 4f11a03d..40c3d680 100644 --- a/starters/nextjs-starter-approuter-ts/next-env.d.ts +++ b/starters/nextjs-starter-approuter-ts/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/starters/nextjs-starter-approuter-ts/tsconfig.json b/starters/nextjs-starter-approuter-ts/tsconfig.json index c5f3cdff..7f1f45e5 100644 --- a/starters/nextjs-starter-approuter-ts/tsconfig.json +++ b/starters/nextjs-starter-approuter-ts/tsconfig.json @@ -8,7 +8,8 @@ ], "allowJs": true, "skipLibCheck": true, - "strict": false, + "strict": true, + "strictNullChecks": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "incremental": true, diff --git a/starters/nextjs-starter-ts/components/grid.tsx b/starters/nextjs-starter-ts/components/grid.tsx index 4ea9f118..d1de8d1f 100644 --- a/starters/nextjs-starter-ts/components/grid.tsx +++ b/starters/nextjs-starter-ts/components/grid.tsx @@ -59,7 +59,7 @@ export function ArticleGridCard({ isWide = false, }: ArticleGridCardProps) { const targetHref = `${basePath}/${article.slug || article.id}`; - const imageSrc = article.metadata?.["Hero Image"] || null; + const imageSrc = (article.metadata?.["Hero Image"] as string) || null; return (
{article.title} - {article.metadata?.["Description"] && ( + {article.metadata?.["Description"] ? (

{article.metadata?.["Description"]?.toString() || ""}

- )} + ) : null}
@@ -107,7 +107,13 @@ export function ArticleGridCard({ ); } -function GridItemCoverImage({ imageSrc, imageAltText }) { +function GridItemCoverImage({ + imageSrc, + imageAltText, +}: { + imageSrc: string | null | undefined; + imageAltText?: string | undefined; +}) { return imageSrc != null ? ( // eslint-disable-next-line @next/next/no-img-element

{title}

diff --git a/starters/nextjs-starter-ts/components/smart-components/error-boundary.tsx b/starters/nextjs-starter-ts/components/smart-components/error-boundary.tsx index 81ce4662..ea8ea8c9 100644 --- a/starters/nextjs-starter-ts/components/smart-components/error-boundary.tsx +++ b/starters/nextjs-starter-ts/components/smart-components/error-boundary.tsx @@ -36,10 +36,10 @@ const SmartComponentSuspenseErrorBoundary = ({ children }: Props) => { export const withSmartComponentErrorBoundary = // eslint-disable-next-line react/display-name - (Component: React.ComponentType) => (props: Record) => ( + (Component: React.ComponentType) => (props: Record) => ( ); -export default SmartComponentErrorBoundary; \ No newline at end of file +export default SmartComponentErrorBoundary; diff --git a/starters/nextjs-starter-ts/lib/utils.ts b/starters/nextjs-starter-ts/lib/utils.ts index 1379565f..47404737 100644 --- a/starters/nextjs-starter-ts/lib/utils.ts +++ b/starters/nextjs-starter-ts/lib/utils.ts @@ -67,7 +67,7 @@ export function getSeoMetadata(article: ArticleWithoutContent) { description, openGraph: { type: "website", - title: article.title, + title: article.title || undefined, images: imageProperties, description, article: { diff --git a/starters/nextjs-starter-ts/next-env.d.ts b/starters/nextjs-starter-ts/next-env.d.ts index 4f11a03d..a4a7b3f5 100644 --- a/starters/nextjs-starter-ts/next-env.d.ts +++ b/starters/nextjs-starter-ts/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/starters/nextjs-starter-ts/pages/_app.tsx b/starters/nextjs-starter-ts/pages/_app.tsx index 6e2931af..adb920bd 100644 --- a/starters/nextjs-starter-ts/pages/_app.tsx +++ b/starters/nextjs-starter-ts/pages/_app.tsx @@ -2,13 +2,15 @@ import { Poppins } from "next/font/google"; // import Script from "next/script"; import "../styles/globals.css"; +import { AppProps } from "next/app"; +import React from "react"; const poppins = Poppins({ subsets: ["latin"], weight: ["400", "500", "700"], }); -function MyApp({ Component, pageProps }) { +function MyApp({ Component, pageProps }: AppProps) { return (
{/* Google Analytics: Replace XXXXXXXXXX with your google analytics id and uncomment the following code. */} diff --git a/starters/nextjs-starter-ts/pages/api/utils/oembed.ts b/starters/nextjs-starter-ts/pages/api/utils/oembed.ts index a9938cd4..cf7e30e5 100644 --- a/starters/nextjs-starter-ts/pages/api/utils/oembed.ts +++ b/starters/nextjs-starter-ts/pages/api/utils/oembed.ts @@ -9,7 +9,7 @@ const oembedURLs = { export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse, ) { const url = req.query.url; let type = req.query.type; @@ -20,7 +20,7 @@ export default async function handler( if (Array.isArray(type)) type = type[0]; - const oembedUrl = oembedURLs[type]; + const oembedUrl = oembedURLs[type as keyof typeof oembedURLs]; if (!oembedUrl) { res.status(404).json({ error: "Not found" }); @@ -29,7 +29,7 @@ export default async function handler( const queryParams = { url }; const response = await fetch( - `${oembedUrl}?${queryString.stringify(queryParams)}` + `${oembedUrl}?${queryString.stringify(queryParams)}`, ); if (response.ok) { diff --git a/starters/nextjs-starter-ts/pages/api/utils/paginate.ts b/starters/nextjs-starter-ts/pages/api/utils/paginate.ts index a3b06b99..926efe4f 100644 --- a/starters/nextjs-starter-ts/pages/api/utils/paginate.ts +++ b/starters/nextjs-starter-ts/pages/api/utils/paginate.ts @@ -5,7 +5,7 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - let cursor: string; + let cursor: string | undefined; if (Array.isArray(req.query.cursor)) cursor = req.query.cursor[0]; else cursor = req.query.cursor; diff --git a/starters/nextjs-starter-ts/pages/articles/[...uri].tsx b/starters/nextjs-starter-ts/pages/articles/[...uri].tsx index 45a6531a..52e39b61 100644 --- a/starters/nextjs-starter-ts/pages/articles/[...uri].tsx +++ b/starters/nextjs-starter-ts/pages/articles/[...uri].tsx @@ -27,7 +27,7 @@ export default function ArticlePage({ article, grant }: ArticlePageProps) { > @@ -43,13 +43,24 @@ export default function ArticlePage({ article, grant }: ArticlePageProps) { export async function getServerSideProps({ req: { cookies }, query: { uri, publishingLevel, pccGrant, ...query }, +}: { + req: { + cookies: Record; + }; + query: { + uri: string; + publishingLevel: "PRODUCTION" | "REALTIME" | undefined; + pccGrant: string; + }; }) { const slugOrId = uri[uri.length - 1]; const grant = pccGrant || cookies["PCC-GRANT"] || null; const article = await PCCConvenienceFunctions.getArticleBySlugOrId( slugOrId, - publishingLevel ? publishingLevel.toString().toUpperCase() : "PRODUCTION", + publishingLevel + ? (publishingLevel.toString().toUpperCase() as "PRODUCTION" | "REALTIME") + : "PRODUCTION", ); if (!article) { @@ -60,7 +71,8 @@ export async function getServerSideProps({ if ( article.slug?.trim().length && - article.slug.toLowerCase() !== slugOrId?.trim().toLowerCase() + article.slug.toLowerCase() !== slugOrId?.trim().toLowerCase() && + pantheonAPIOptions.resolvePath != null ) { // If the article was accessed by the id rather than the slug - then redirect to the canonical // link (mostly for SEO purposes than anything else). diff --git a/starters/nextjs-starter-ts/pages/articles/index.tsx b/starters/nextjs-starter-ts/pages/articles/index.tsx index f848d056..0b407b65 100644 --- a/starters/nextjs-starter-ts/pages/articles/index.tsx +++ b/starters/nextjs-starter-ts/pages/articles/index.tsx @@ -1,4 +1,5 @@ import { + Article, ArticleWithoutContent, PCCConvenienceFunctions, } from "@pantheon-systems/pcc-react-sdk"; @@ -11,7 +12,17 @@ import { usePagination } from "../../hooks/usePagination"; const PAGE_SIZE = 20; -export default function ArticlesListTemplate({ articles, totalCount, cursor }) { +interface Props { + articles: Article[]; + totalCount: number; + cursor: string; +} + +export default function ArticlesListTemplate({ + articles, + totalCount, + cursor, +}: Props) { const { data: currentArticles, onPageChange, diff --git a/starters/nextjs-starter-ts/pages/component-preview/[id].tsx b/starters/nextjs-starter-ts/pages/component-preview/[id].tsx index 00b8e3f7..626eb19f 100644 --- a/starters/nextjs-starter-ts/pages/component-preview/[id].tsx +++ b/starters/nextjs-starter-ts/pages/component-preview/[id].tsx @@ -11,8 +11,11 @@ export default function SmartComponentPreview() { ? JSON.parse(Buffer.from(attrs, "base64").toString()) : {}; - const SmartComponent = - clientSmartComponentMap[id?.toString()]?.reactComponent; + const SmartComponent = id + ? clientSmartComponentMap[ + id.toString() as keyof typeof clientSmartComponentMap + ]?.reactComponent + : null; return (
diff --git a/starters/nextjs-starter-ts/pages/examples/ssg-isr/[uri].tsx b/starters/nextjs-starter-ts/pages/examples/ssg-isr/[uri].tsx index bccc4e94..ad755ae1 100644 --- a/starters/nextjs-starter-ts/pages/examples/ssg-isr/[uri].tsx +++ b/starters/nextjs-starter-ts/pages/examples/ssg-isr/[uri].tsx @@ -18,7 +18,7 @@ export default function ArticlePage({ article }: ArticlePageProps) { return ( @@ -30,17 +30,17 @@ export default function ArticlePage({ article }: ArticlePageProps) { ); } -export const getStaticProps: GetStaticProps<{}, { uri: string }> = async ({ - params: { uri }, -}) => { - if (!uri) { +export const getStaticProps: GetStaticProps<{}> = async ({ params }) => { + if (!params?.uri) { return { notFound: true, }; } try { - const article = await PCCConvenienceFunctions.getArticleBySlugOrId(uri); + const article = await PCCConvenienceFunctions.getArticleBySlugOrId( + params?.uri?.toString(), + ); if (!article) { return { @@ -74,7 +74,7 @@ export const getStaticPaths: GetStaticPaths = async () => { const pagePaths = publishedArticles.map((article) => { const id = article.id; - const slug = article.metadata.slug; + const slug = article.metadata?.slug; // Generate both slug and id paths for each article const paths = [ diff --git a/starters/nextjs-starter-ts/pages/examples/ssg-isr/index.tsx b/starters/nextjs-starter-ts/pages/examples/ssg-isr/index.tsx index 64cdf320..4e860fd5 100644 --- a/starters/nextjs-starter-ts/pages/examples/ssg-isr/index.tsx +++ b/starters/nextjs-starter-ts/pages/examples/ssg-isr/index.tsx @@ -1,4 +1,5 @@ import { + Article, ArticleWithoutContent, PCCConvenienceFunctions, } from "@pantheon-systems/pcc-react-sdk"; @@ -11,11 +12,17 @@ import { usePagination } from "../../../hooks/usePagination"; const PAGE_SIZE = 20; +interface Props { + articles: Article[]; + totalCount: number; + cursor: string; +} + export default function SSGISRExampleTemplate({ articles, totalCount, cursor, -}) { +}: Props) { const { data: currentArticles, onPageChange, diff --git a/starters/nextjs-starter-ts/pages/index.tsx b/starters/nextjs-starter-ts/pages/index.tsx index e5460876..388e270a 100644 --- a/starters/nextjs-starter-ts/pages/index.tsx +++ b/starters/nextjs-starter-ts/pages/index.tsx @@ -1,4 +1,7 @@ -import { PCCConvenienceFunctions } from "@pantheon-systems/pcc-react-sdk"; +import { + Article, + PCCConvenienceFunctions, +} from "@pantheon-systems/pcc-react-sdk"; import { NextSeo } from "next-seo"; import Image from "next/image"; import Link from "next/link"; @@ -6,7 +9,7 @@ import { HomepageArticleGrid } from "../components/grid"; import Layout from "../components/layout"; import { Button } from "../components/ui/button"; -export default function Home({ articles }) { +export default function Home({ articles }: { articles: Article[] }) { return ( ) : ( - {data.summary} + {data?.summary || ""} )}
) : null}
- {isLoading || data?.searchResults?.length > 0 ? ( + {isLoading || + (data?.searchResults != null && data?.searchResults?.length > 0) ? ( (data?.searchResults ?? Array.from({ length: 5 })).map( (result, index) => ( @@ -87,11 +88,11 @@ export default function Search() {

{isLoading ? ( - ) : ( + ) : result.snippet ? ( markdownToTxt( result.snippet.replaceAll(/{#h\..*}\n/g, "\n"), ) - )} + ) : null}

diff --git a/starters/nextjs-starter-ts/tsconfig.json b/starters/nextjs-starter-ts/tsconfig.json index a160b879..4987446c 100644 --- a/starters/nextjs-starter-ts/tsconfig.json +++ b/starters/nextjs-starter-ts/tsconfig.json @@ -8,7 +8,8 @@ ], "allowJs": true, "skipLibCheck": true, - "strict": false, + "strict": true, + "strictNullChecks": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "incremental": true, diff --git a/starters/nextjs-starter/next-env.d.ts b/starters/nextjs-starter/next-env.d.ts index 4f11a03d..a4a7b3f5 100644 --- a/starters/nextjs-starter/next-env.d.ts +++ b/starters/nextjs-starter/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/starters/nextjs-starter/pages/component-preview/[id].tsx b/starters/nextjs-starter/pages/component-preview/[id].jsx similarity index 85% rename from starters/nextjs-starter/pages/component-preview/[id].tsx rename to starters/nextjs-starter/pages/component-preview/[id].jsx index 00b8e3f7..d4c81b4e 100644 --- a/starters/nextjs-starter/pages/component-preview/[id].tsx +++ b/starters/nextjs-starter/pages/component-preview/[id].jsx @@ -3,7 +3,6 @@ import { clientSmartComponentMap } from "../../components/smart-components"; export default function SmartComponentPreview() { const router = useRouter(); - const { id, attrs } = router.query; const decodedAttrs = @@ -11,8 +10,9 @@ export default function SmartComponentPreview() { ? JSON.parse(Buffer.from(attrs, "base64").toString()) : {}; - const SmartComponent = - clientSmartComponentMap[id?.toString()]?.reactComponent; + const SmartComponent = id + ? clientSmartComponentMap[id.toString()]?.reactComponent + : null; return (
diff --git a/starters/nextjs-starter/tsconfig.json b/starters/nextjs-starter/tsconfig.json index 6db37c02..105c03a3 100644 --- a/starters/nextjs-starter/tsconfig.json +++ b/starters/nextjs-starter/tsconfig.json @@ -8,7 +8,8 @@ ], "allowJs": true, "skipLibCheck": true, - "strict": false, + "strict": true, + "strictNullChecks": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "incremental": true, From e8b37944b516928bb4f0467958cdd26f39ac5ccf Mon Sep 17 00:00:00 2001 From: Kevin Stubbs Date: Thu, 2 Jan 2025 17:11:36 +0200 Subject: [PATCH 2/2] Remove duplicate typing & unnecessary optional chaining. --- starters/nextjs-starter-approuter-ts/app/articles/page.tsx | 2 +- .../nextjs-starter-approuter-ts/app/search/search-results.tsx | 2 +- starters/nextjs-starter-ts/pages/search.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/starters/nextjs-starter-approuter-ts/app/articles/page.tsx b/starters/nextjs-starter-approuter-ts/app/articles/page.tsx index 85e5cc7e..470bdde0 100644 --- a/starters/nextjs-starter-approuter-ts/app/articles/page.tsx +++ b/starters/nextjs-starter-approuter-ts/app/articles/page.tsx @@ -3,7 +3,7 @@ import ArticleList from "../../components/article-list"; import Layout from "../../components/layout"; import { PAGE_SIZE } from "../../constants"; -async function fetchNextPages(cursor?: string | null | string) { +async function fetchNextPages(cursor?: string | null | undefined) { "use server"; const { data, cursor: newCursor } = await PCCConvenienceFunctions.getPaginatedArticles({ diff --git a/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx b/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx index 0b6ee9ae..3b536436 100644 --- a/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx +++ b/starters/nextjs-starter-approuter-ts/app/search/search-results.tsx @@ -41,7 +41,7 @@ export default function SearchResults({ ) : null}
- {isLoading || (searchResults && searchResults?.length > 0) ? ( + {isLoading || (searchResults && searchResults.length > 0) ? ( (searchResults ?? Array.from({ length: 5 })).map((result, index) => (
diff --git a/starters/nextjs-starter-ts/pages/search.tsx b/starters/nextjs-starter-ts/pages/search.tsx index 59209872..3a84ac57 100644 --- a/starters/nextjs-starter-ts/pages/search.tsx +++ b/starters/nextjs-starter-ts/pages/search.tsx @@ -69,7 +69,7 @@ export default function Search() {
{isLoading || - (data?.searchResults != null && data?.searchResults?.length > 0) ? ( + (data?.searchResults != null && data.searchResults.length > 0) ? ( (data?.searchResults ?? Array.from({ length: 5 })).map( (result, index) => (