@@ -38,3 +38,136 @@ export default async function Page({ params }: PageProps) {
3838 </ article >
3939 )
4040}
41+ import { draftMode } from "next/headers"
42+ import { notFound } from "next/navigation"
43+ import { getDraftData } from "next-drupal/draft"
44+ import { Article } from "@/components/drupal/Article"
45+ import { BasicPage } from "@/components/drupal/BasicPage"
46+ import { drupal } from "@/lib/drupal"
47+ import type { Metadata , ResolvingMetadata } from "next"
48+ import type { DrupalNode , JsonApiParams } from "next-drupal"
49+
50+ async function getNode ( slug : string [ ] ) {
51+ const path = `/${ slug . join ( "/" ) } `
52+
53+ const params : JsonApiParams = { }
54+
55+ const draftData = getDraftData ( )
56+
57+ if ( draftData . path === path ) {
58+ params . resourceVersion = draftData . resourceVersion
59+ }
60+
61+ // Translating the path also allows us to discover the entity type.
62+ const translatedPath = await drupal . translatePath ( path )
63+
64+ if ( ! translatedPath ) {
65+ throw new Error ( "Resource not found" , { cause : "NotFound" } )
66+ }
67+
68+ const type = translatedPath . jsonapi ?. resourceName !
69+ const uuid = translatedPath . entity . uuid
70+
71+ if ( type === "node--article" ) {
72+ params . include = "field_image,uid"
73+ }
74+
75+ const resource = await drupal . getResource < DrupalNode > ( type , uuid , {
76+ params,
77+ next : {
78+ revalidate : 3600 ,
79+ // tags: [`${type}:${uuid}`],
80+ } ,
81+ } )
82+
83+ if ( ! resource ) {
84+ throw new Error (
85+ `Failed to fetch resource: ${ translatedPath ?. jsonapi ?. individual } ` ,
86+ {
87+ cause : "DrupalError" ,
88+ }
89+ )
90+ }
91+
92+ return resource
93+ }
94+
95+ type NodePageParams = {
96+ slug : string [ ]
97+ }
98+ type NodePageProps = {
99+ params : NodePageParams
100+ searchParams : { [ key : string ] : string | string [ ] | undefined }
101+ }
102+
103+ export async function generateMetadata (
104+ { params : { slug } } : NodePageProps ,
105+ parent : ResolvingMetadata
106+ ) : Promise < Metadata > {
107+ let node
108+ try {
109+ node = await getNode ( slug )
110+ } catch ( e ) {
111+ // If we fail to fetch the node, don't return any metadata.
112+ return { }
113+ }
114+
115+ return {
116+ title : node . title ,
117+ }
118+ }
119+
120+ const RESOURCE_TYPES = [ "node--page" , "node--article" ]
121+
122+ export async function generateStaticParams ( ) : Promise < NodePageParams [ ] > {
123+ const resources = await drupal . getResourceCollectionPathSegments (
124+ RESOURCE_TYPES ,
125+ {
126+ // The pathPrefix will be removed from the returned path segments array.
127+ // pathPrefix: "/blog",
128+ // The list of locales to return.
129+ // locales: ["en", "es"],
130+ // The default locale.
131+ // defaultLocale: "en",
132+ }
133+ )
134+
135+ return resources . map ( ( resource ) => {
136+ // resources is an array containing objects like: {
137+ // path: "/blog/some-category/a-blog-post",
138+ // type: "node--article",
139+ // locale: "en", // or `undefined` if no `locales` requested.
140+ // segments: ["blog", "some-category", "a-blog-post"],
141+ // }
142+ return {
143+ slug : resource . segments ,
144+ }
145+ } )
146+ }
147+
148+ export default async function NodePage ( {
149+ params : { slug } ,
150+ searchParams,
151+ } : NodePageProps ) {
152+ const isDraftMode = draftMode ( ) . isEnabled
153+
154+ let node
155+ try {
156+ node = await getNode ( slug )
157+ } catch ( error ) {
158+ // If getNode throws an error, tell Next.js the path is 404.
159+ notFound ( )
160+ }
161+
162+ // If we're not in draft mode and the resource is not published, return a 404.
163+ if ( ! isDraftMode && node ?. status === false ) {
164+ notFound ( )
165+ }
166+
167+ return (
168+ < >
169+ { node . type === "node--page" && < BasicPage node = { node } /> }
170+ { node . type === "node--article" && < Article node = { node } /> }
171+ </ >
172+ )
173+ }
0 commit comments