From 16758892e25f7b9177106b8b095a0a1ff775d383 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Thu, 7 Nov 2024 10:23:03 -0600 Subject: [PATCH 1/6] Add cache options to getResource/getResourceCollection calls in basic starter page templates Fixes #808 Experimenting with Copilot Workspace here... --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/chapter-three/next-drupal/issues/808?shareId=XXXX-XXXX-XXXX-XXXX). --- starters/basic-starter/app/[...slug]/page.tsx | 135 +++--------------- starters/basic-starter/app/page.tsx | 3 + 2 files changed, 26 insertions(+), 112 deletions(-) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index 4e61889cb..b3c9327c6 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -1,129 +1,40 @@ -import { draftMode } from "next/headers" -import { notFound } from "next/navigation" -import { getDraftData } from "next-drupal/draft" -import { Article } from "@/components/drupal/Article" -import { BasicPage } from "@/components/drupal/BasicPage" import { drupal } from "@/lib/drupal" -import type { Metadata, ResolvingMetadata } from "next" -import type { DrupalNode, JsonApiParams } from "next-drupal" - -async function getNode(slug: string[]) { - const path = `/${slug.join("/")}` - - const params: JsonApiParams = {} - - const draftData = getDraftData() - - if (draftData.path === path) { - params.resourceVersion = draftData.resourceVersion - } - - // Translating the path also allows us to discover the entity type. - const translatedPath = await drupal.translatePath(path) - - if (!translatedPath) { - throw new Error("Resource not found", { cause: "NotFound" }) - } - - const type = translatedPath.jsonapi?.resourceName! - const uuid = translatedPath.entity.uuid - - if (type === "node--article") { - params.include = "field_image,uid" - } - - const resource = await drupal.getResource(type, uuid, { - params, - }) +import { notFound } from "next/navigation" +import { DrupalNode } from "next-drupal" - if (!resource) { - throw new Error( - `Failed to fetch resource: ${translatedPath?.jsonapi?.individual}`, - { - cause: "DrupalError", - } - ) +interface PageProps { + params: { + slug: string[] } - - return resource } -type NodePageParams = { - slug: string[] -} -type NodePageProps = { - params: NodePageParams - searchParams: { [key: string]: string | string[] | undefined } -} +export default async function Page({ params }: PageProps) { + const path = `/${params.slug.join("/")}` + const pathData = await drupal.translatePath(path) -export async function generateMetadata( - { params: { slug } }: NodePageProps, - parent: ResolvingMetadata -): Promise { - let node - try { - node = await getNode(slug) - } catch (e) { - // If we fail to fetch the node, don't return any metadata. - return {} - } - - return { - title: node.title, + if (!pathData || !pathData.entity) { + notFound() } -} -const RESOURCE_TYPES = ["node--page", "node--article"] + const tag = `${pathData.entity.type}:${pathData.entity.id}` -export async function generateStaticParams(): Promise { - const resources = await drupal.getResourceCollectionPathSegments( - RESOURCE_TYPES, - { - // The pathPrefix will be removed from the returned path segments array. - // pathPrefix: "/blog", - // The list of locales to return. - // locales: ["en", "es"], - // The default locale. - // defaultLocale: "en", - } - ) - - return resources.map((resource) => { - // resources is an array containing objects like: { - // path: "/blog/some-category/a-blog-post", - // type: "node--article", - // locale: "en", // or `undefined` if no `locales` requested. - // segments: ["blog", "some-category", "a-blog-post"], - // } - return { - slug: resource.segments, - } + const node = await drupal.getResource(pathData.entity.type, pathData.entity.id, { + params: { + include: "field_image,uid", + }, + next: { + revalidate: 3600, + // tags: [tag], + }, }) -} - -export default async function NodePage({ - params: { slug }, - searchParams, -}: NodePageProps) { - const isDraftMode = draftMode().isEnabled - - let node - try { - node = await getNode(slug) - } catch (error) { - // If getNode throws an error, tell Next.js the path is 404. - notFound() - } - // If we're not in draft mode and the resource is not published, return a 404. - if (!isDraftMode && node?.status === false) { + if (!node) { notFound() } return ( - <> - {node.type === "node--page" && } - {node.type === "node--article" &&
} - +
+

{node.title}

+
) } diff --git a/starters/basic-starter/app/page.tsx b/starters/basic-starter/app/page.tsx index 01aaac5aa..a3ded1598 100644 --- a/starters/basic-starter/app/page.tsx +++ b/starters/basic-starter/app/page.tsx @@ -17,6 +17,9 @@ export default async function Home() { include: "field_image,uid", sort: "-created", }, + next: { + revalidate: 3600, + }, } ) From 84d1e076c8e907bed21960371ba8f66c5fa80fae Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Thu, 7 Nov 2024 10:27:51 -0600 Subject: [PATCH 2/6] Try to talk copilot into modifying the existing file. --- starters/basic-starter/app/[...slug]/page.tsx | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index b3c9327c6..51d0b99f3 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -38,3 +38,136 @@ export default async function Page({ params }: PageProps) {
) } +import { draftMode } from "next/headers" +import { notFound } from "next/navigation" +import { getDraftData } from "next-drupal/draft" +import { Article } from "@/components/drupal/Article" +import { BasicPage } from "@/components/drupal/BasicPage" +import { drupal } from "@/lib/drupal" +import type { Metadata, ResolvingMetadata } from "next" +import type { DrupalNode, JsonApiParams } from "next-drupal" + +async function getNode(slug: string[]) { + const path = `/${slug.join("/")}` + + const params: JsonApiParams = {} + + const draftData = getDraftData() + + if (draftData.path === path) { + params.resourceVersion = draftData.resourceVersion + } + + // Translating the path also allows us to discover the entity type. + const translatedPath = await drupal.translatePath(path) + + if (!translatedPath) { + throw new Error("Resource not found", { cause: "NotFound" }) + } + + const type = translatedPath.jsonapi?.resourceName! + const uuid = translatedPath.entity.uuid + + if (type === "node--article") { + params.include = "field_image,uid" + } + + const resource = await drupal.getResource(type, uuid, { + params, + next: { + revalidate: 3600, + // tags: [`${type}:${uuid}`], + }, + }) + + if (!resource) { + throw new Error( + `Failed to fetch resource: ${translatedPath?.jsonapi?.individual}`, + { + cause: "DrupalError", + } + ) + } + + return resource +} + +type NodePageParams = { + slug: string[] +} +type NodePageProps = { + params: NodePageParams + searchParams: { [key: string]: string | string[] | undefined } +} + +export async function generateMetadata( + { params: { slug } }: NodePageProps, + parent: ResolvingMetadata +): Promise { + let node + try { + node = await getNode(slug) + } catch (e) { + // If we fail to fetch the node, don't return any metadata. + return {} + } + + return { + title: node.title, + } +} + +const RESOURCE_TYPES = ["node--page", "node--article"] + +export async function generateStaticParams(): Promise { + const resources = await drupal.getResourceCollectionPathSegments( + RESOURCE_TYPES, + { + // The pathPrefix will be removed from the returned path segments array. + // pathPrefix: "/blog", + // The list of locales to return. + // locales: ["en", "es"], + // The default locale. + // defaultLocale: "en", + } + ) + + return resources.map((resource) => { + // resources is an array containing objects like: { + // path: "/blog/some-category/a-blog-post", + // type: "node--article", + // locale: "en", // or `undefined` if no `locales` requested. + // segments: ["blog", "some-category", "a-blog-post"], + // } + return { + slug: resource.segments, + } + }) +} + +export default async function NodePage({ + params: { slug }, + searchParams, +}: NodePageProps) { + const isDraftMode = draftMode().isEnabled + + let node + try { + node = await getNode(slug) + } catch (error) { + // If getNode throws an error, tell Next.js the path is 404. + notFound() + } + + // If we're not in draft mode and the resource is not published, return a 404. + if (!isDraftMode && node?.status === false) { + notFound() + } + + return ( + <> + {node.type === "node--page" && } + {node.type === "node--article" &&
} + + ) +} From 44fca0a80315de8e3e66ee81a11fd375e66a48cf Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Thu, 7 Nov 2024 10:33:40 -0600 Subject: [PATCH 3/6] Update page.tsx Merging copilot changes into existing file. --- starters/basic-starter/app/[...slug]/page.tsx | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index 51d0b99f3..512650e53 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -1,43 +1,3 @@ -import { drupal } from "@/lib/drupal" -import { notFound } from "next/navigation" -import { DrupalNode } from "next-drupal" - -interface PageProps { - params: { - slug: string[] - } -} - -export default async function Page({ params }: PageProps) { - const path = `/${params.slug.join("/")}` - const pathData = await drupal.translatePath(path) - - if (!pathData || !pathData.entity) { - notFound() - } - - const tag = `${pathData.entity.type}:${pathData.entity.id}` - - const node = await drupal.getResource(pathData.entity.type, pathData.entity.id, { - params: { - include: "field_image,uid", - }, - next: { - revalidate: 3600, - // tags: [tag], - }, - }) - - if (!node) { - notFound() - } - - return ( -
-

{node.title}

-
- ) -} import { draftMode } from "next/headers" import { notFound } from "next/navigation" import { getDraftData } from "next-drupal/draft" @@ -76,7 +36,7 @@ async function getNode(slug: string[]) { params, next: { revalidate: 3600, - // tags: [`${type}:${uuid}`], + // tags: [`${type}:${translatedPath.entity.id}`], }, }) From fc2f2dfc5020866ff214580e8a6d19ec51fad5b2 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Thu, 7 Nov 2024 10:36:00 -0600 Subject: [PATCH 4/6] Update page.tsx Add comment --- starters/basic-starter/app/[...slug]/page.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index 512650e53..219250fab 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -36,6 +36,7 @@ async function getNode(slug: string[]) { params, next: { revalidate: 3600, + // Replace `revalidate` with `tags` if using tag based revalidation. // tags: [`${type}:${translatedPath.entity.id}`], }, }) From 4693997bfbde0e5ae47d041d79876bf9bf6795ee Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Thu, 7 Nov 2024 16:56:49 -0600 Subject: [PATCH 5/6] fix: update example tag in slug route to match Drupal format --- starters/basic-starter/app/[...slug]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index 219250fab..176e9de8d 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -37,7 +37,7 @@ async function getNode(slug: string[]) { next: { revalidate: 3600, // Replace `revalidate` with `tags` if using tag based revalidation. - // tags: [`${type}:${translatedPath.entity.id}`], + // tags: [`${translatedPath.entity.type}:${translatedPath.entity.id}`], }, }) From 4b6eff14cdc854c88370819d07cbc3caa0fc801a Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 8 Nov 2024 10:17:33 -0600 Subject: [PATCH 6/6] fix: minor code cleanup --- starters/basic-starter/app/[...slug]/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/starters/basic-starter/app/[...slug]/page.tsx b/starters/basic-starter/app/[...slug]/page.tsx index 176e9de8d..52b0189a6 100644 --- a/starters/basic-starter/app/[...slug]/page.tsx +++ b/starters/basic-starter/app/[...slug]/page.tsx @@ -27,6 +27,7 @@ async function getNode(slug: string[]) { const type = translatedPath.jsonapi?.resourceName! const uuid = translatedPath.entity.uuid + const tag = `${translatedPath.entity.type}:${translatedPath.entity.id}` if (type === "node--article") { params.include = "field_image,uid" @@ -37,7 +38,7 @@ async function getNode(slug: string[]) { next: { revalidate: 3600, // Replace `revalidate` with `tags` if using tag based revalidation. - // tags: [`${translatedPath.entity.type}:${translatedPath.entity.id}`], + // tags: [tag], }, })