From 9dcc59256e172aab280637f770a0c23165ec60fa Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 11 Oct 2024 11:30:22 -0500 Subject: [PATCH 01/23] feat: update menu strucutre and mark areas in docs that need to be expanded for Pages router --- www/config/docs.ts | 10 +++++- www/config/site.ts | 8 +++-- www/content/blog/next-drupal-2-0.mdx | 10 ++++++ www/content/docs/client.mdx | 6 ++++ www/content/docs/configuration.mdx | 6 ++++ www/content/docs/deserializer.mdx | 4 +++ www/content/docs/faq.mdx | 4 +++ www/content/docs/pages.mdx | 4 +++ www/content/docs/reference/pages-router.mdx | 9 ++++++ www/content/docs/upgrade-guide.mdx | 36 +++++++++++++++++++++ www/pages/index.tsx | 6 ++-- 11 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 www/content/blog/next-drupal-2-0.mdx create mode 100644 www/content/docs/reference/pages-router.mdx diff --git a/www/config/docs.ts b/www/config/docs.ts index 1bdbed9d7..7a9141091 100644 --- a/www/config/docs.ts +++ b/www/config/docs.ts @@ -34,7 +34,7 @@ export const docsConfig: DocsConfig = { ], }, { - title: "Drupal Client", + title: "NextDrupal Client", items: [ { title: "Introduction", @@ -94,6 +94,10 @@ export const docsConfig: DocsConfig = { title: "Advanced Example", href: "/docs/pages#advanced-example", }, + { + title: "Pages Router", + href: "/docs/pages#pages-router", + }, ], }, { @@ -196,6 +200,10 @@ export const docsConfig: DocsConfig = { title: "getAuthorizationHeader", href: "/docs/reference/getauthorizationheader", }, + { + title: "Pages Router", + href: "/docs/reference/pages-router", + }, ], }, { diff --git a/www/config/site.ts b/www/config/site.ts index 63ec53e39..83effd88b 100644 --- a/www/config/site.ts +++ b/www/config/site.ts @@ -44,12 +44,16 @@ export const site: SiteConfig = { }, versions: [ { - version: "v1.6.0", + version: "v2.0.0", active: true, }, { - version: "v1.0.0", + version: "v1.6.0", url: "https://v1.next-drupal.org", }, + { + version: "canary", + url: "https://next.next-drupal.org", + }, ], } diff --git a/www/content/blog/next-drupal-2-0.mdx b/www/content/blog/next-drupal-2-0.mdx new file mode 100644 index 000000000..74a1552c9 --- /dev/null +++ b/www/content/blog/next-drupal-2-0.mdx @@ -0,0 +1,10 @@ +--- +title: Next-Drupal 2.0 +author: brianperry +date: October 31, 2024 +created: "2024-10-31" +excerpt: Next.js 14, Drupal 11 and App Router Support. +published: true +--- + +Write this post. diff --git a/www/content/docs/client.mdx b/www/content/docs/client.mdx index 5e6290805..a286d01db 100644 --- a/www/content/docs/client.mdx +++ b/www/content/docs/client.mdx @@ -26,6 +26,8 @@ It also comes with full support for JSON:API write operations which means you ca ## Usage +### App Router + ```ts import { NextDrupal } from "next-drupal" @@ -41,3 +43,7 @@ const article = await drupal.getResource( "f4c61473-8297-4bf3-bab7-21c9633a7ca7" ) ``` + +### Pages Router + +TODO diff --git a/www/content/docs/configuration.mdx b/www/content/docs/configuration.mdx index 3e5293afa..9813951ef 100644 --- a/www/content/docs/configuration.mdx +++ b/www/content/docs/configuration.mdx @@ -5,6 +5,8 @@ excerpt: Initialization and options for NextDrupal client. ## Initialization +### App Router + To create a new `NextDrupal` client, use the following initialization: ```ts @@ -19,6 +21,8 @@ Where `NEXT_PUBLIC_DRUPAL_BASE_URL` is the URL to your Drupal site defined as an NEXT_PUBLIC_DRUPAL_BASE_URL=http://example.com ``` +### Pages Router + --- ## Options @@ -226,3 +230,5 @@ JSON:API errors are thrown in non-production environments by default. The errors By default, the resource endpoint will be based on the resource name. If you turn this off, a JSON:API request will retrieve the resource's endpoint url. --- + +TODO - Call out any App Router or Pages Router specific options here. diff --git a/www/content/docs/deserializer.mdx b/www/content/docs/deserializer.mdx index 50a81197b..37188dac5 100644 --- a/www/content/docs/deserializer.mdx +++ b/www/content/docs/deserializer.mdx @@ -29,3 +29,7 @@ export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { deserializer: customDeserializer, }) ``` + +### Pages Router + +TODO - the option was called serializer diff --git a/www/content/docs/faq.mdx b/www/content/docs/faq.mdx index 88b0f6630..6ae8c91db 100644 --- a/www/content/docs/faq.mdx +++ b/www/content/docs/faq.mdx @@ -7,6 +7,10 @@ excerpt: Frequently Asked Questions about Next.js for Drupal Next.js for Drupal works requires Drupal 9.3+ and Drupal 10. +## Which Next.js versions are supported? + +Next.js for Drupal supports both Next.js 13 and Next.js 14. This includes support for both the App Router and the Pages Router. + ## Does it work without authentication? Yes. Authentication is only required for previewing unpublished entities and revisions. diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index 2102032cd..a76f685cd 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -159,3 +159,7 @@ export default function Page({ params }) { ## Reference See the [fetching JSON:API resources](/docs/fetching-resources) section for more examples of fetching resources and collection of resources. + +## Pages Router + +TODO diff --git a/www/content/docs/reference/pages-router.mdx b/www/content/docs/reference/pages-router.mdx new file mode 100644 index 000000000..87b8d9961 --- /dev/null +++ b/www/content/docs/reference/pages-router.mdx @@ -0,0 +1,9 @@ +--- +title: Pages Router +excerpt: An index of methods specific to the Pages Router. +--- + +TODO + +- Link 1 +- Link 2 diff --git a/www/content/docs/upgrade-guide.mdx b/www/content/docs/upgrade-guide.mdx index 49b2c9bf8..ba032dd99 100644 --- a/www/content/docs/upgrade-guide.mdx +++ b/www/content/docs/upgrade-guide.mdx @@ -3,6 +3,42 @@ title: Upgrade Guide excerpt: Guide for upgrading your site to use DrupalClient. --- +## next-drupal 2.0.0 + +1. Upgrade to the latest version + +```sh +yarn add next-drupal@latest +``` + + + +`next-drupal 2.0.0` adds support for the App Router and maintains support for the Pages Router. For information on transitioning from the Pages Router to the App Router, see the [App Router Incremental Adoption Guide](https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration) guide on the Next.js site. + + + +TODO - Outline breaking changes here. + +## next 2.0.0 + +1. Upgrade your Drupal site to use `next 2.0.0` + +```sh +composer require drupal/next +``` + +2. Run updates: + +```sh +drush updb +``` + + + +TODO - Describe changes as that will occur as a result of this upgrade. + + + ## next-drupal 1.6.0 1. Upgrade to the latest version diff --git a/www/pages/index.tsx b/www/pages/index.tsx index eacdb322f..8ecdd3a92 100644 --- a/www/pages/index.tsx +++ b/www/pages/index.tsx @@ -34,14 +34,15 @@ export default function IndexPage({ features }: IndexPageProps) { return ( + TODO - Re-order elements on home page
- Next.js 13, Drupal 10 and Stable On-demand Revalidation + Next.js 14, Drupal 11 and App Router Support

The future of Drupal is headless @@ -159,6 +160,7 @@ export default function IndexPage({ features }: IndexPageProps) { A powerful client for working with JSON:API.

+ TODO - Update code below for app router
{features.map((feature) => ( From 11a18fc6bada811f26bee4b87ee690c829d271a3 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Tue, 5 Nov 2024 11:00:58 -0600 Subject: [PATCH 02/23] feat: add pages router example to client intro page --- www/content/docs/client.mdx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/www/content/docs/client.mdx b/www/content/docs/client.mdx index a286d01db..e6f4b6373 100644 --- a/www/content/docs/client.mdx +++ b/www/content/docs/client.mdx @@ -46,4 +46,18 @@ const article = await drupal.getResource( ### Pages Router -TODO +```ts +import { NextDrupalPages } from "next-drupal" + +// Create a new DrupalClient. +const drupal = new NextDrupalPages("https://example.com") + +// Fetch articles. +const articles = await drupal.getResourceCollection("node--article") + +// Fetch one article by id. +const article = await drupal.getResource( + "node--article", + "f4c61473-8297-4bf3-bab7-21c9633a7ca7" +) +``` From cfcd0266666a71f11077bc41b8cc777891a36487 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Tue, 5 Nov 2024 11:24:24 -0600 Subject: [PATCH 03/23] chore: add todo to apply patches doc --- www/content/tutorials/quick-start/apply-patches.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/content/tutorials/quick-start/apply-patches.mdx b/www/content/tutorials/quick-start/apply-patches.mdx index 94f384f37..8e37ba90c 100644 --- a/www/content/tutorials/quick-start/apply-patches.mdx +++ b/www/content/tutorials/quick-start/apply-patches.mdx @@ -10,6 +10,8 @@ At the time of this writing, there are two patches we need to wire everything to 1. Open your Drupal `composer.json` file. 2. Add the following patches under `"extra"`. +TODO - validate that these patches are necessary and correct. + ```json title=composer.json "extra": { // highlight-start From e3d39cbbea251e05e52037e2b60a0074e729d53c Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Tue, 5 Nov 2024 13:20:02 -0600 Subject: [PATCH 04/23] fix: update apply patches documentation --- .../tutorials/graphql/apply-patches.mdx | 22 +++++------------ .../tutorials/quick-start/apply-patches.mdx | 24 ++++++++----------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/www/content/tutorials/graphql/apply-patches.mdx b/www/content/tutorials/graphql/apply-patches.mdx index 43ad17a81..e75f0d00f 100644 --- a/www/content/tutorials/graphql/apply-patches.mdx +++ b/www/content/tutorials/graphql/apply-patches.mdx @@ -5,45 +5,35 @@ weight: 30 group: Quick Start (GraphQL) --- +At the time of this writing, a patch is required in order to use multilingual features such as translation. + -In a future release, these patches will NOT be required for using Next.js and GraphQL. +**Note:** If you're not using multilingual features such as translation, you can skip this step. -At the time of this writing, there are two patches we need to wire everything together. - 1. Open your Drupal `composer.json` file. -2. Add the following patches under `"extra"`. +2. Add the following patch under `"extra"`. ```json title=composer.json "extra": { // highlight-start "patches": { - "drupal/subrequests": { - "#3049395-47 - Page Cache causes different subrequests to return the same responses": "https://www.drupal.org/files/issues/2022-12-06/subrequests-3049395-chnage-request-type-47.patch" - }, "drupal/decoupled_router": { - "#3111456-59 - Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2022-12-01/decouple_router-3111456-resolve-language-issue-58--get-translation.patch" + "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2024-10-22/decoupled_router-3111456-resolve-langcode-issue-78--external-redirects.patch" } }, // highlight-end } ``` - - -**Note:** If you're not using multilingual features such as translation, you can skip the second patch. - - - 3. Run the following command: ```sh composer require cweagans/composer-patches ``` -You can read more about the patches here: +You can read more about the patch here: -1. https://www.drupal.org/project/subrequests/issues/3049395 2. https://www.drupal.org/project/decoupled_router/issues/3111456 diff --git a/www/content/tutorials/quick-start/apply-patches.mdx b/www/content/tutorials/quick-start/apply-patches.mdx index 8e37ba90c..8b836c1eb 100644 --- a/www/content/tutorials/quick-start/apply-patches.mdx +++ b/www/content/tutorials/quick-start/apply-patches.mdx @@ -5,10 +5,16 @@ weight: 30 group: Quick Start --- -At the time of this writing, there are two patches we need to wire everything together. +At the time of this writing, a patch is required in order to use multilingual features such as translation. + + + +**Note:** If you're not using multilingual features such as translation, you can skip this step. + + 1. Open your Drupal `composer.json` file. -2. Add the following patches under `"extra"`. +2. Add the following patch under `"extra"`. TODO - validate that these patches are necessary and correct. @@ -16,30 +22,20 @@ TODO - validate that these patches are necessary and correct. "extra": { // highlight-start "patches": { - "drupal/subrequests": { - "Get same results on different request": "https://www.drupal.org/files/issues/2019-07-18/change_request_type-63049395-09.patch" - }, "drupal/decoupled_router": { - "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2022-11-30/decouple_router-3111456-resolve-language-issue-58.patch" + "Unable to resolve path on node in other language than default": "https://www.drupal.org/files/issues/2024-10-22/decoupled_router-3111456-resolve-langcode-issue-78--external-redirects.patch" } }, // highlight-end } ``` - - -**Note:** If you're not using multilingual features such as translation, you can skip the second patch. - - - 3. Run the following command: ```sh composer require cweagans/composer-patches ``` -You can read more about the patches here: +You can read more about the patch here: -1. https://www.drupal.org/project/subrequests/issues/3049395 2. https://www.drupal.org/project/decoupled_router/issues/3111456 From 46eeb8c886f305cf8565c05b86243f77e755ab95 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 8 Nov 2024 10:53:40 -0600 Subject: [PATCH 05/23] docs: adjustments to configuration entry --- www/content/docs/configuration.mdx | 36 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/www/content/docs/configuration.mdx b/www/content/docs/configuration.mdx index 9813951ef..25c60cae3 100644 --- a/www/content/docs/configuration.mdx +++ b/www/content/docs/configuration.mdx @@ -23,6 +23,20 @@ NEXT_PUBLIC_DRUPAL_BASE_URL=http://example.com ### Pages Router +To create a new `NextDrupal` client, use the following initialization: + +```ts +import { NextDrupaPages } from "next-drupal" + +const drupal = new NextDrupalPages(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL) +``` + +Where `NEXT_PUBLIC_DRUPAL_BASE_URL` is the URL to your Drupal site defined as an [environment variable](/docs/environment-variables). + +```txt title=.env.local +NEXT_PUBLIC_DRUPAL_BASE_URL=http://example.com +``` + --- ## Options @@ -93,6 +107,24 @@ new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { You can find more info about using a custom deserializer [here](/docs/deserializer). +#### Pages Router + +This option is called `serializer` in the `NextDrupalPages` client. Aside from that, the usage is the same. + +```ts +import { Deserializer } from "jsonapi-serializer" + +const jsonDeserializer = new Deserializer({ + keyForAttribute: "camelCase", +}) + +const customDeserializer = jsonSerializer.deserialize.bind(jsonSerializer) + +new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { + serializer: customDeserializer, +}) +``` + --- ### fetcher @@ -228,7 +260,3 @@ JSON:API errors are thrown in non-production environments by default. The errors - **Required**: No By default, the resource endpoint will be based on the resource name. If you turn this off, a JSON:API request will retrieve the resource's endpoint url. - ---- - -TODO - Call out any App Router or Pages Router specific options here. From 04f42950f294050aebe4e1f64ba0436f6d76ef46 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 8 Nov 2024 10:59:54 -0600 Subject: [PATCH 06/23] docs: update additional serializer related options --- www/content/docs/configuration.mdx | 2 +- www/content/docs/deserializer.mdx | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/www/content/docs/configuration.mdx b/www/content/docs/configuration.mdx index 25c60cae3..2dbf33005 100644 --- a/www/content/docs/configuration.mdx +++ b/www/content/docs/configuration.mdx @@ -120,7 +120,7 @@ const jsonDeserializer = new Deserializer({ const customDeserializer = jsonSerializer.deserialize.bind(jsonSerializer) -new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { +new NextDrupalPages(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { serializer: customDeserializer, }) ``` diff --git a/www/content/docs/deserializer.mdx b/www/content/docs/deserializer.mdx index 37188dac5..2c7d1d95e 100644 --- a/www/content/docs/deserializer.mdx +++ b/www/content/docs/deserializer.mdx @@ -32,4 +32,24 @@ export const drupal = new NextDrupal(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, { ### Pages Router -TODO - the option was called serializer +This option is called `serializer` in the `NextDrupalPages` client. Aside from that, the usage is the same. + +```ts title=lib/drupal.ts +import { NextDrupal } from "next-drupal" +import { Deserializer } from "jsonapi-serializer" + +// Create a custom deserializer. +const jsonDeserializer = new Deserializer({ + keyForAttribute: "camelCase", +}) + +const customDeserializer = jsonDeserializer.deserialize.bind(jsonDeserializer) + +// Pass the custom deserializer to the client. +export const drupal = new NextDrupalPages( + process.env.NEXT_PUBLIC_DRUPAL_BASE_URL, + { + serializer: customDeserializer, + } +) +``` From 06ebfb89276559aceb8b292a172618e285ba836e Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 8 Nov 2024 14:21:16 -0600 Subject: [PATCH 07/23] docs: edits to building pages entry - work in progress --- www/content/docs/pages.mdx | 21 ++++++--------------- www/pages/index.tsx | 3 ++- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index a76f685cd..bd6564507 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -3,23 +3,10 @@ title: Building Pages excerpt: How to build pages using JSON:API resources from Drupal. --- -In Next.js V14, data fetching has evolved significantly from previous versions. Instead of using `getStaticProps` and `getServerSideProps` you now use the native `fetch` function enhanced by Next.js to handle server-side data fetching. - -The `NextDrupal` client provides several functions to help you query JSON:API resources from Drupal. - - - --- ## Basic Example @@ -53,6 +40,8 @@ export default function AboutPage() { } ``` +TODO - time based revalidation. + --- ## Dynamic pages @@ -156,10 +145,12 @@ export default function Page({ params }) { } ``` +TODO - tag based revalidation. + ## Reference See the [fetching JSON:API resources](/docs/fetching-resources) section for more examples of fetching resources and collection of resources. ## Pages Router -TODO +TODO - Port some examples from the previous docs. diff --git a/www/pages/index.tsx b/www/pages/index.tsx index 8ecdd3a92..f41bbddff 100644 --- a/www/pages/index.tsx +++ b/www/pages/index.tsx @@ -34,7 +34,8 @@ export default function IndexPage({ features }: IndexPageProps) { return ( - TODO - Re-order elements on home page + TODO - Re-order elements on home page TODO - A Last docs wide search for + 'TODO'
Date: Fri, 8 Nov 2024 15:24:06 -0600 Subject: [PATCH 08/23] docs: add revalidation examples to building pages entry --- www/content/docs/pages.mdx | 67 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index bd6564507..703ca4457 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -3,7 +3,7 @@ title: Building Pages excerpt: How to build pages using JSON:API resources from Drupal. --- -In Next.js V14, data fetching has evolved significantly from previous versions. Instead of using `getStaticProps` and `getServerSideProps` you now use the native `fetch` function enhanced by Next.js to handle server-side data fetching. This also allows you to configure caching and revalidation directly within your fetch requests. These can be used in Server Components, Route Handlers, and Server Actions. You can read more about it [here](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating). +In the Next.js App Router, data fetching has evolved significantly from previous versions. The native `fetch` function enhanced by Next.js can be used to handle server-side data fetching. This also allows you to configure caching and revalidation directly within your fetch requests. These can be used in Server Components, Route Handlers, and Server Actions. You can read more about it [here](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating). The `NextDrupal` client provides several functions to help you query JSON:API resources from Drupal and manage these revalidation options on the `fetch` request level. @@ -40,7 +40,17 @@ export default function AboutPage() { } ``` -TODO - time based revalidation. +### Time-based Revalidation + +To use time-based revalidation, you can pass a `revalidate` option to the `getResource` function. This will set the cache lifetime of a resource (in seconds). + +```tsx +const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8", + { next: { revalidate: 3600 } } +) +``` --- @@ -145,7 +155,58 @@ export default function Page({ params }) { } ``` -TODO - tag based revalidation. +### Tag-based Revalidation + +In addition to revalidating based on time, it is also possible to revalidate +based on cache tag values. This is useful when you want to revalidate a resource +based on changes to other resources. + +Below we've adapted the Page function from the example above to include +tag-based revalidation. + +```tsx title=app/[...slug]/page.tsx +import { DrupalJsonApiParams } from "drupal-jsonapi-params" + +... + +export default function Page({ params }) { + const {slug} = params; + + const path = drupal.translatePath(slug) + + // Get the resource type. + const type = path.jsonapi.resourceName + const tag = `${path.entity.type}:${path.entity.id}` + + const params = new DrupalJsonApiParams() + + // Fetch the title, path and body field for pages. + if (type === "node--page") { + params.addFields("node--page", ["title", "path", "body"]) + } + + // Fetch additional fields for articles. + if (type === "node--article") { + params.addFields("node--article", ["title", "path", "body", "uid"]) + } + + const node = await drupal.getResource(path, path.entity.uuid { + params: params.getQueryObject(), + tags: [tag] + }) + + // Render different Components based on Node type. + if (node.type === "node--page") { + return + } + + if (node.type === "node--article") { + return + } + + return null +} +``` ## Reference From 5f184c010737242d66c82bac0afd357cefdc6fbb Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 8 Nov 2024 15:35:54 -0600 Subject: [PATCH 09/23] docs: building pages updates * Add pages router examples to building pages entry. * Add todos about possible changes to on-demand revalidation section. --- www/content/docs/pages.mdx | 90 ++++++++++++++++++- .../on-demand-revalidation/index.mdx | 4 + 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index 703ca4457..712e9ef18 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -214,4 +214,92 @@ See the [fetching JSON:API resources](/docs/fetching-resources) section for more ## Pages Router -TODO - Port some examples from the previous docs. +When using the Pages Router, you will use the `getStaticProps` and `getServerSideProps` methods to fetch data for your pages. + +### Basic Example + +Here's an example which uses `getResource` to fetch a `page` node by ID: + +```tsx +const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8" +) +``` + +A full page would look like this: + +```tsx title=pages/about.tsx +// node will be populated at build time by getStaticProps +export default function AboutPage({ node }) { + return ( +
+

{node.title}

+ // ... +
+ ) +} + +export async function getStaticProps() { + // Fetch the node from Drupal. + const node = await drupal.getResource( + "node--page", + "07464e9f-9221-4a4f-b7f2-01389408e6c8" + ) + + // Pass the node as props to the AboutPage. + return { + props: { + node, + }, + } +} +``` + +--- + +## Dynamic pages + +You can use Next.js [dynamic route](https://nextjs.org/docs/basic-features/pages#pages-with-dynamic-routes) to build static pages for Drupal entity types. + +Start by creating a page at `/pages/[...slug].tsx`, where `[...slug]` maps to the **path alias** for an entity type (or content type) in Drupal. + +This means `/pages/[...slug].tsx` will handle all pages with the following aliases: `/about`, `/team`, `/another/path` ...etc. + +To build static pages, there are two functions we need to implement: + +1. `getStaticPaths`: to tell Next.js all the routes that we want to be rendered. +2. `getStaticProps`: to fetch data for pages. + +```tsx title=pages/[...slug].tsx +export default function Page({ node }) { + return ( +
+

{node.title}

+ // ... +
+ ) +} + +export async function getStaticPaths(context) { + // Build paths for all `node--page`. + return { + paths: await drupal.getStaticPathsFromContext("node--page", context), + fallback: false, + } +} + +export async function getStaticProps(context) { + // Fetch the node based on the context. + // next-drupal automatically handles the slug value. + const node = await drupal.getResourceFromContext("node--page", context) + + return { + props: { + node, + }, + } +} +``` + +--- diff --git a/www/content/tutorials/on-demand-revalidation/index.mdx b/www/content/tutorials/on-demand-revalidation/index.mdx index b6966acf1..9165758af 100644 --- a/www/content/tutorials/on-demand-revalidation/index.mdx +++ b/www/content/tutorials/on-demand-revalidation/index.mdx @@ -8,3 +8,7 @@ group: On-demand Revalidation [On-demand Revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation) makes it easy to update your Next.js site when content on Drupal is created, updated or deleted. Starting with `v1.6.0`, Next.js for Drupal supports On-demand Revalidation configurable per entity types. + +TODO - in v2, Next.js for Drupal supports... +TODO - maybe this section should just be renamed to validation? +TODO - maybe move revalidation examples from Building Pages to this section... From dbfb228036d4760d71b4cef748b25e25f967fe6b Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Mon, 18 Nov 2024 14:20:50 -0600 Subject: [PATCH 10/23] docs: updates to upgrade guide entry --- www/content/docs/pages.mdx | 2 +- www/content/docs/upgrade-guide.mdx | 44 ++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/www/content/docs/pages.mdx b/www/content/docs/pages.mdx index 712e9ef18..144831fb5 100644 --- a/www/content/docs/pages.mdx +++ b/www/content/docs/pages.mdx @@ -258,7 +258,7 @@ export async function getStaticProps() { --- -## Dynamic pages +### Dynamic pages You can use Next.js [dynamic route](https://nextjs.org/docs/basic-features/pages#pages-with-dynamic-routes) to build static pages for Drupal entity types. diff --git a/www/content/docs/upgrade-guide.mdx b/www/content/docs/upgrade-guide.mdx index ba032dd99..f0cb016a2 100644 --- a/www/content/docs/upgrade-guide.mdx +++ b/www/content/docs/upgrade-guide.mdx @@ -1,6 +1,6 @@ --- title: Upgrade Guide -excerpt: Guide for upgrading your site to use DrupalClient. +excerpt: Upgrading your site to use the latest version of Next.js for Drupal. --- ## next-drupal 2.0.0 @@ -17,7 +17,37 @@ yarn add next-drupal@latest -TODO - Outline breaking changes here. +Breaking changes: + +- When Drupal uses the Preview Url and the Revalidate Url, the "slug" search param + has been renamed to "path". +- The JsonApiWithAuthOptions type has been renamed to JsonApiWithAuthOption. The + JsonApiWithLocaleOptions type is now considered deprecated for DrupalClient + usage and can be replaced with the JsonApiOptions type. +- The forceIframeSameSiteCookie option to DrupalClient has been removed. The + DrupalClient now automatically makes this change when the Next.js website is + running in development mode (and the change is not needed for production sites). +- The options to the DrupalClient.preview() method were previously ignored. The + options are now passed to the NextApiResponse.setDraftMode() method and their + TypeScript definition have now changed to match the options parameter of the + setDraftMode method. +- DrupalClient previously had a debug property that indicated if debugging was + enabled. This property has been removed and replaced with a debug() method that + accepts a string and logs it as a debug message if debugging is enabled. +- The useMenu() client hook has moved out of the main entry point and into its own + entry point. Any import or require of that function needs to be updated: + +Old usage: + +```js +import { useMenu } from "next-drupal" +``` + +New usage: + +```js +import { useMenu } from "next-drupal/navigation" +``` ## next 2.0.0 @@ -35,7 +65,11 @@ drush updb -TODO - Describe changes as that will occur as a result of this upgrade. +Notable changes: + +- A new 'tag' On-demand validation plugin is available to configure for use with Next.js entity types. +- When Drupal uses the Preview Url and the Revalidate Url, the "slug" search param + has been renamed to "path". @@ -199,3 +233,7 @@ export const drupal = new DrupalClient( 6. Done. You are now ready to run your site using the new `DrupalClient`. If you see any bugs during the upgrade, feel free to create an issue on [GitHub](https://github.com/chapter-three/next-drupal). + +``` + +``` From 6e2b9d86504a48e0d5ac2d5a75243d91b0de7242 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Tue, 19 Nov 2024 11:03:44 -0600 Subject: [PATCH 11/23] Generate API documentation via TypeDoc Fixes #796 --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/chapter-three/next-drupal/issues/796?shareId=XXXX-XXXX-XXXX-XXXX). --- packages/next-drupal/src/next-drupal-base.ts | 100 +++++++++++++- packages/next-drupal/src/next-drupal-pages.ts | 91 ++++++++++++ packages/next-drupal/src/next-drupal.ts | 130 +++++++++++++++++- www/package.json | 7 +- www/typedoc.json | 5 + 5 files changed, 326 insertions(+), 7 deletions(-) create mode 100644 www/typedoc.json diff --git a/packages/next-drupal/src/next-drupal-base.ts b/packages/next-drupal/src/next-drupal-base.ts index 2feb82c50..5d9db158e 100644 --- a/packages/next-drupal/src/next-drupal-base.ts +++ b/packages/next-drupal/src/next-drupal-base.ts @@ -30,6 +30,9 @@ const DEFAULT_HEADERS = { Accept: "application/json", } +/** + * The base class for NextDrupal clients. + */ export class NextDrupalBase { accessToken?: NextDrupalBaseOptions["accessToken"] @@ -173,6 +176,13 @@ export class NextDrupalBase { return this._token } + /** + * Fetches a resource from the given input URL or path. + * + * @param {RequestInfo} input The input URL or path. + * @param {FetchOptions} init The fetch options. + * @returns {Promise} The fetch response. + */ async fetch( input: RequestInfo, { withAuth, ...init }: FetchOptions = {} @@ -215,6 +225,12 @@ export class NextDrupalBase { return await fetch(input, init) } + /** + * Gets the authorization header value based on the provided auth configuration. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {Promise} The authorization header value. + */ async getAuthorizationHeader(auth: NextDrupalAuth) { let header: string @@ -250,6 +266,13 @@ export class NextDrupalBase { return header } + /** + * Builds a URL with the given path and search parameters. + * + * @param {string} path The URL path. + * @param {EndpointSearchParams} searchParams The search parameters. + * @returns {URL} The constructed URL. + */ buildUrl(path: string, searchParams?: EndpointSearchParams): URL { const url = new URL(path, this.baseUrl) @@ -269,7 +292,15 @@ export class NextDrupalBase { return url } - // async so subclasses can query for endpoint discovery. + /** + * Builds an endpoint URL with the given options. + * + * @param {Object} options The options for building the endpoint. + * @param {string} options.locale The locale. + * @param {string} options.path The path. + * @param {EndpointSearchParams} options.searchParams The search parameters. + * @returns {Promise} The constructed endpoint URL. + */ async buildEndpoint({ locale = "", path = "", @@ -291,6 +322,16 @@ export class NextDrupalBase { ).toString() } + /** + * Constructs a path from the given segment and options. + * + * @param {string | string[]} segment The path segment. + * @param {Object} options The options for constructing the path. + * @param {Locale} options.locale The locale. + * @param {Locale} options.defaultLocale The default locale. + * @param {PathPrefix} options.pathPrefix The path prefix. + * @returns {string} The constructed path. + */ constructPathFromSegment( segment: string | string[], options: { @@ -338,6 +379,15 @@ export class NextDrupalBase { }) } + /** + * Adds a locale prefix to the given path. + * + * @param {string} path The path. + * @param {Object} options The options for adding the locale prefix. + * @param {Locale} options.locale The locale. + * @param {Locale} options.defaultLocale The default locale. + * @returns {string} The path with the locale prefix. + */ addLocalePrefix( path: string, options: { locale?: Locale; defaultLocale?: Locale } = {} @@ -356,6 +406,12 @@ export class NextDrupalBase { return `${localePrefix}${path}` } + /** + * Gets an access token using the provided client ID and secret. + * + * @param {NextDrupalAuthClientIdSecret} clientIdSecret The client ID and secret. + * @returns {Promise} The access token. + */ async getAccessToken( clientIdSecret?: NextDrupalAuthClientIdSecret ): Promise { @@ -435,6 +491,12 @@ export class NextDrupalBase { return result } + /** + * Validates the draft URL using the provided search parameters. + * + * @param {URLSearchParams} searchParams The search parameters. + * @returns {Promise} The validation response. + */ async validateDraftUrl(searchParams: URLSearchParams): Promise { const path = searchParams.get("path") @@ -468,10 +530,22 @@ export class NextDrupalBase { return response } + /** + * Logs a debug message if debug mode is enabled. + * + * @param {string} message The debug message. + */ debug(message) { this.isDebugEnabled && this.logger.debug(message) } + /** + * Throws an error if the response contains JSON:API errors. + * + * @param {Response} response The fetch response. + * @param {string} messagePrefix The error message prefix. + * @throws {JsonApiErrors} The JSON:API errors. + */ async throwIfJsonErrors(response: Response, messagePrefix = "") { if (!response?.ok) { const errors = await this.getErrorsFromResponse(response) @@ -479,6 +553,12 @@ export class NextDrupalBase { } } + /** + * Extracts errors from the fetch response. + * + * @param {Response} response The fetch response. + * @returns {Promise} The extracted errors. + */ async getErrorsFromResponse(response: Response) { const type = response.headers.get("content-type") let error: JsonApiResponse | { message: string } @@ -506,6 +586,12 @@ export class NextDrupalBase { } } +/** + * Checks if the provided auth configuration is basic auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is basic auth, false otherwise. + */ export function isBasicAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthUsernamePassword { @@ -515,6 +601,12 @@ export function isBasicAuth( ) } +/** + * Checks if the provided auth configuration is access token auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is access token auth, false otherwise. + */ export function isAccessTokenAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthAccessToken { @@ -524,6 +616,12 @@ export function isAccessTokenAuth( ) } +/** + * Checks if the provided auth configuration is client ID and secret auth. + * + * @param {NextDrupalAuth} auth The auth configuration. + * @returns {boolean} True if the auth configuration is client ID and secret auth, false otherwise. + */ export function isClientIdSecretAuth( auth: NextDrupalAuth ): auth is NextDrupalAuthClientIdSecret { diff --git a/packages/next-drupal/src/next-drupal-pages.ts b/packages/next-drupal/src/next-drupal-pages.ts index fd0af89a1..e37d4916c 100644 --- a/packages/next-drupal/src/next-drupal-pages.ts +++ b/packages/next-drupal/src/next-drupal-pages.ts @@ -27,6 +27,10 @@ import type { NextApiResponse, } from "next" +/** + * The NextDrupalPages class extends the NextDrupal class and provides methods + * for interacting with a Drupal backend in the context of Next.js pages. + */ export class NextDrupalPages extends NextDrupal { private serializer: DrupalClientOptions["serializer"] @@ -59,6 +63,13 @@ export class NextDrupalPages extends NextDrupal { ) => this.serializer.deserialize(body, options) } + /** + * Gets the entry point for a given resource type. + * + * @param {string} resourceType The resource type. + * @param {Locale} locale The locale. + * @returns {Promise} The entry point URL. + */ async getEntryForResourceType( resourceType: string, locale?: Locale @@ -74,6 +85,14 @@ export class NextDrupalPages extends NextDrupal { return new DrupalMenuTree(links, parent) } + /** + * Gets a resource from the context. + * + * @param {string | DrupalTranslatedPath} input The input path or translated path. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResourceFromContext( input: string | DrupalTranslatedPath, context: GetStaticPropsContext, @@ -157,6 +176,14 @@ export class NextDrupalPages extends NextDrupal { return resource } + /** + * Gets a collection of resources from the context. + * + * @param {string} type The type of the resources. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched collection of resources. + */ async getResourceCollectionFromContext( type: string, context: GetStaticPropsContext, @@ -177,6 +204,14 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Gets a search index from the context. + * + * @param {string} name The name of the search index. + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The fetched search index. + */ async getSearchIndexFromContext( name: string, context: GetStaticPropsContext, @@ -189,6 +224,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Translates a path from the context. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {Promise} The translated path. + */ async translatePathFromContext( context: GetStaticPropsContext, options?: { @@ -208,6 +250,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Gets the path from the context. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {Object} options Options for the request. + * @returns {string} The constructed path. + */ getPathFromContext( context: GetStaticPropsContext, options?: { @@ -223,6 +272,14 @@ export class NextDrupalPages extends NextDrupal { getPathsFromContext = this.getStaticPathsFromContext + /** + * Gets static paths from the context. + * + * @param {string | string[]} types The types of the resources. + * @param {GetStaticPathsContext} context The static paths context. + * @param {Object} options Options for the request. + * @returns {Promise["paths"]>} The fetched static paths. + */ async getStaticPathsFromContext( types: string | string[], context: GetStaticPathsContext, @@ -291,6 +348,13 @@ export class NextDrupalPages extends NextDrupal { return paths.flat() } + /** + * Builds static paths from resources. + * + * @param {Object[]} resources The resources. + * @param {Object} options Options for the request. + * @returns {Object[]} The built static paths. + */ buildStaticPathsFromResources( resources: { path: DrupalPathAlias @@ -313,6 +377,13 @@ export class NextDrupalPages extends NextDrupal { : [] } + /** + * Builds static paths parameters from paths. + * + * @param {string[]} paths The paths. + * @param {Object} options Options for the request. + * @returns {Object[]} The built static paths parameters. + */ buildStaticPathsParamsFromPaths( paths: string[], options?: { pathPrefix?: PathPrefix; locale?: Locale } @@ -342,6 +413,13 @@ export class NextDrupalPages extends NextDrupal { }) } + /** + * Handles preview mode. + * + * @param {NextApiRequest} request The API request. + * @param {NextApiResponse} response The API response. + * @param {Object} options Options for the request. + */ async preview( request: NextApiRequest, response: NextApiResponse, @@ -411,6 +489,12 @@ export class NextDrupalPages extends NextDrupal { } } + /** + * Disables preview mode. + * + * @param {NextApiRequest} request The API request. + * @param {NextApiResponse} response The API response. + */ async previewDisable(request: NextApiRequest, response: NextApiResponse) { // Disable both preview and draft modes. response.clearPreviewData() @@ -427,6 +511,13 @@ export class NextDrupalPages extends NextDrupal { response.end() } + /** + * Gets the authentication configuration from the context and options. + * + * @param {GetStaticPropsContext} context The static props context. + * @param {JsonApiWithAuthOption} options Options for the request. + * @returns {NextDrupalAuth} The authentication configuration. + */ getAuthFromContextAndOptions( context: GetStaticPropsContext, options: JsonApiWithAuthOption diff --git a/packages/next-drupal/src/next-drupal.ts b/packages/next-drupal/src/next-drupal.ts index 1c837c7c1..2041ebb58 100644 --- a/packages/next-drupal/src/next-drupal.ts +++ b/packages/next-drupal/src/next-drupal.ts @@ -44,6 +44,10 @@ export function useJsonaDeserialize() { } } +/** + * The NextDrupal class extends the NextDrupalBase class and provides methods + * for interacting with a Drupal backend. + */ export class NextDrupal extends NextDrupalBase { cache?: NextDrupalOptions["cache"] @@ -86,6 +90,14 @@ export class NextDrupal extends NextDrupalBase { } } + /** + * Creates a new resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {JsonApiCreateResourceBody} body The body of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The created resource. + */ async createResource( type: string, body: JsonApiCreateResourceBody, @@ -126,6 +138,14 @@ export class NextDrupal extends NextDrupalBase { : /* c8 ignore next */ json } + /** + * Creates a new file resource for the specified media type. + * + * @param {string} type The type of the media. + * @param {JsonApiCreateFileResourceBody} body The body of the file resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The created file resource. + */ async createFileResource( type: string, body: JsonApiCreateFileResourceBody, @@ -170,6 +190,15 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Updates an existing resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiUpdateResourceBody} body The body of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} The updated resource. + */ async updateResource( type: string, uuid: string, @@ -213,6 +242,14 @@ export class NextDrupal extends NextDrupalBase { : /* c8 ignore next */ json } + /** + * Deletes an existing resource of the specified type. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiOptions} options Options for the request. + * @returns {Promise} True if the resource was deleted, false otherwise. + */ async deleteResource( type: string, uuid: string, @@ -246,6 +283,14 @@ export class NextDrupal extends NextDrupalBase { return response.status === 204 } + /** + * Fetches a resource of the specified type by its UUID. + * + * @param {string} type The type of the resource. + * @param {string} uuid The UUID of the resource. + * @param {JsonApiOptions & JsonApiWithCacheOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResource( type: string, uuid: string, @@ -301,6 +346,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Fetches a resource of the specified type by its path. + * + * @param {string} path The path of the resource. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched resource. + */ async getResourceByPath( path: string, options?: { @@ -410,6 +462,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(data) : data } + /** + * Fetches a collection of resources of the specified type. + * + * @param {string} type The type of the resources. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched collection of resources. + */ async getResourceCollection( type: string, options?: { @@ -447,6 +506,13 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Fetches path segments for a collection of resources of the specified types. + * + * @param {string | string[]} types The types of the resources. + * @param {JsonApiOptions & JsonApiWithAuthOption & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise<{ path: string, type: string, locale: Locale, segments: string[] }[]>} The fetched path segments. + */ async getResourceCollectionPathSegments( types: string | string[], options?: { @@ -555,6 +621,13 @@ export class NextDrupal extends NextDrupalBase { return paths.flat(2) } + /** + * Translates a path to a DrupalTranslatedPath object. + * + * @param {string} path The path to translate. + * @param {JsonApiWithAuthOption & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The translated path. + */ async translatePath( path: string, options?: JsonApiWithAuthOption & JsonApiWithNextFetchOptions @@ -586,6 +659,13 @@ export class NextDrupal extends NextDrupalBase { return await response.json() } + /** + * Fetches the JSON:API index. + * + * @param {Locale} locale The locale for the request. + * @param {JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The JSON:API index. + */ async getIndex( locale?: Locale, options?: JsonApiWithNextFetchOptions @@ -610,6 +690,12 @@ export class NextDrupal extends NextDrupalBase { return await response.json() } + /** + * Builds an endpoint URL for the specified parameters. + * + * @param {Parameters[0] & { resourceType?: string }} params The parameters for the endpoint. + * @returns {Promise} The built endpoint URL. + */ async buildEndpoint({ locale = "", resourceType = "", @@ -647,6 +733,13 @@ export class NextDrupal extends NextDrupalBase { ).toString() } + /** + * Fetches the endpoint URL for the specified resource type. + * + * @param {string} type The type of the resource. + * @param {Locale} locale The locale for the request. + * @returns {Promise} The fetched endpoint URL. + */ async fetchResourceEndpoint(type: string, locale?: Locale): Promise { const index = await this.getIndex(locale) @@ -670,6 +763,13 @@ export class NextDrupal extends NextDrupalBase { return url } + /** + * Fetches a menu by its name. + * + * @param {string} menuName The name of the menu. + * @param {JsonApiOptions & JsonApiWithCacheOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise<{ items: T[], tree: T[] }>} The fetched menu. + */ async getMenu( menuName: string, options?: JsonApiOptions & @@ -735,6 +835,13 @@ export class NextDrupal extends NextDrupalBase { return menu } + /** + * Fetches a view by its name. + * + * @param {string} name The name of the view. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise>} The fetched view. + */ async getView( name: string, options?: JsonApiOptions & JsonApiWithNextFetchOptions @@ -776,6 +883,13 @@ export class NextDrupal extends NextDrupalBase { } } + /** + * Fetches a search index by its name. + * + * @param {string} name The name of the search index. + * @param {JsonApiOptions & JsonApiWithNextFetchOptions} options Options for the request. + * @returns {Promise} The fetched search index. + */ async getSearchIndex( name: string, options?: JsonApiOptions & JsonApiWithNextFetchOptions @@ -810,16 +924,24 @@ export class NextDrupal extends NextDrupalBase { return options.deserialize ? this.deserialize(json) : json } + /** + * Deserializes the response body. + * + * @param {any} body The response body. + * @param {any} options Options for deserialization. + * @returns {any} The deserialized response body. + */ deserialize(body, options?) { if (!body) return null return this.deserializer(body, options) } - // Error handling. - // If throwJsonApiErrors is enabled, we show errors in the Next.js overlay. - // Otherwise, we log the errors even if debugging is turned off. - // In production, errors are always logged never thrown. + /** + * Logs or throws an error based on the throwJsonApiErrors flag. + * + * @param {Error} error The error to log or throw. + */ logOrThrowError(error: Error) { if (!this.throwJsonApiErrors) { this.logger.error(error) diff --git a/www/package.json b/www/package.json index 6834ba166..1d0c3cb55 100644 --- a/www/package.json +++ b/www/package.json @@ -8,7 +8,8 @@ "scripts": { "dev": "next dev -p 4444", "build": "next build", - "preview": "next build && next start -p 4444" + "preview": "next build && next start -p 4444", + "generate:docs": "typedoc --out content/api ../packages/next-drupal/src" }, "dependencies": { "@docsearch/react": "^3.6.0", @@ -23,7 +24,9 @@ "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dom": "^18.2.0", - "sharp": "^0.30.7" + "sharp": "^0.30.7", + "typedoc": "^0.23.9", + "typedoc-plugin-markdown": "^3.11.0" }, "devDependencies": { "@mapbox/rehype-prism": "^0.8.0", diff --git a/www/typedoc.json b/www/typedoc.json new file mode 100644 index 000000000..a0533d787 --- /dev/null +++ b/www/typedoc.json @@ -0,0 +1,5 @@ +{ + "entryPoints": ["../packages/next-drupal/src"], + "out": "content/api", + "plugin": ["typedoc-plugin-markdown"] +} From 1d7ffe8f4069760436b5cf63ff08812cb5f35708 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Tue, 19 Nov 2024 12:45:11 -0600 Subject: [PATCH 12/23] build: add todo --- www/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/pages/index.tsx b/www/pages/index.tsx index f41bbddff..3dccbbbb0 100644 --- a/www/pages/index.tsx +++ b/www/pages/index.tsx @@ -35,7 +35,7 @@ export default function IndexPage({ features }: IndexPageProps) { return ( TODO - Re-order elements on home page TODO - A Last docs wide search for - 'TODO' + TODO - check for broken links
Date: Tue, 19 Nov 2024 12:46:53 -0600 Subject: [PATCH 13/23] build: lock yarn updates --- yarn.lock | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index c11557a73..c62f6b474 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3341,6 +3341,11 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +ansi-sequence-parser@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" + integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -8210,6 +8215,11 @@ jsonc-parser@^3.0.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== +jsonc-parser@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -8757,6 +8767,11 @@ lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" @@ -8861,6 +8876,11 @@ markdown-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== +marked@^4.2.12: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + mdast-squeeze-paragraphs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" @@ -9070,6 +9090,13 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^7.1.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + minimatch@^8.0.2: version "8.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229" @@ -11426,6 +11453,16 @@ shiki@^0.11.1: vscode-oniguruma "^1.6.1" vscode-textmate "^6.0.0" +shiki@^0.14.1: + version "0.14.7" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e" + integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg== + dependencies: + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -11778,7 +11815,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11882,7 +11928,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11910,6 +11956,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12572,6 +12625,23 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== +typedoc-plugin-markdown@^3.11.0: + version "3.17.1" + resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz#c33f42363c185adf842f4699166015f7fe0ed02b" + integrity sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw== + dependencies: + handlebars "^4.7.7" + +typedoc@^0.23.9: + version "0.23.28" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.28.tgz#3ce9c36ef1c273fa849d2dea18651855100d3ccd" + integrity sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w== + dependencies: + lunr "^2.3.9" + marked "^4.2.12" + minimatch "^7.1.3" + shiki "^0.14.1" + "typescript@>=3 < 6", typescript@^5.4.5: version "5.4.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" @@ -12976,7 +13046,7 @@ void-elements@3.1.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== -vscode-oniguruma@^1.6.1: +vscode-oniguruma@^1.6.1, vscode-oniguruma@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== @@ -12986,6 +13056,11 @@ vscode-textmate@^6.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-6.0.0.tgz#a3777197235036814ac9a92451492f2748589210" integrity sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ== +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + wait-on@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.2.0.tgz#d76b20ed3fc1e2bebc051fae5c1ff93be7892928" @@ -13157,7 +13232,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -13184,6 +13259,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 93a0cdcaaa6fca1cdd3f5683997fc23f52632341 Mon Sep 17 00:00:00 2001 From: Brian Perry Date: Fri, 10 Jan 2025 10:18:37 -0600 Subject: [PATCH 14/23] docs: re-order elements on homepage to prioritize text content --- www/pages/index.tsx | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/www/pages/index.tsx b/www/pages/index.tsx index 3dccbbbb0..28273666a 100644 --- a/www/pages/index.tsx +++ b/www/pages/index.tsx @@ -34,8 +34,7 @@ export default function IndexPage({ features }: IndexPageProps) { return ( - TODO - Re-order elements on home page TODO - A Last docs wide search for - TODO - check for broken links + TODO - A Last docs wide search for TODO - check for broken links
Next.js 14, Drupal 11 and App Router Support

- The future of Drupal is headless + Next.js for Drupal

-

+ {/*

Next.js for Drupal has everything you need to build a next-generation front-end for your Drupal site. -

+

*/}
-
-