Skip to content

Commit

Permalink
Merge pull request #23 from CalebBarnes/feat/generate-static-params-a…
Browse files Browse the repository at this point in the history
…rchive-taxonomy-pagination

Feat/generate static params archive taxonomy pagination
  • Loading branch information
CalebBarnes committed Jan 18, 2024
2 parents 97473bc + 3abbfea commit f2e1425
Show file tree
Hide file tree
Showing 7 changed files with 437 additions and 53 deletions.
13 changes: 10 additions & 3 deletions apps/next-wordpress-starter/src/app/[[...paths]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { RouteParams, SearchParams } from "@nextwp/core";
import { WordpressTemplate } from "@nextwp/core";
import {
WordpressTemplate,
generateStaticParams as nextWpStaticParams,
} from "@nextwp/core";
import templates from "@/templates";

export default function PageRoute(props: {
Expand All @@ -15,6 +18,10 @@ export default function PageRoute(props: {
);
}

export { generateMetadata, generateStaticParams } from "@nextwp/core";
export { generateMetadata } from "@nextwp/core";

// export const dynamicParams = true; // true | false,
export async function generateStaticParams() {
return nextWpStaticParams({
postTypes: ["pages", "posts", "product"],
});
}
2 changes: 1 addition & 1 deletion packages/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// nextjs app functions
export { generateMetadata } from "./src/next-app-functions/generate-meta-data";
export { generateStaticParams } from "./src/next-app-functions/generate-static-params";
export { generateStaticParams } from "./src/next-app-functions/generate-static-params/generate-static-params";
export { generateSitemap } from "./src/next-app-functions/generate-sitemap";

// route handlers
Expand Down
105 changes: 85 additions & 20 deletions packages/core/src/api/get-post-types.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
import { getAuthHeaders } from "./get-auth-headers";

export type PostType = {
/**
* `false` if no archive, `string` if archive slug is different from post type slug
*/
has_archive: boolean | string;
/**
* The slug of this post type.
*/
slug: string;
/**
* The base path for this post type's REST API endpoint.
*/
rest_base: string;

labels?: {
name?: string;
plural_name?: string;
};
};

type PostTypes = {
post: PostType;
page: PostType;
Expand Down Expand Up @@ -62,3 +42,88 @@ ${err.message}`
);
}
}

export interface PostType {
has_archive: boolean | string;
slug: string;
rest_base: string;
labels?: {
name?: string;
plural_name?: string;
singular_name?: string;
add_new?: string;
add_new_item?: string;
edit_item?: string;
new_item?: string;
view_item?: string;
view_items?: string;
search_items?: string;
not_found?: string;
not_found_in_trash?: string;
parent_item_colon?: string | null;
all_items?: string;
archives?: string;
attributes?: string;
insert_into_item?: string;
uploaded_to_this_item?: string;
featured_image?: string;
set_featured_image?: string;
remove_featured_image?: string;
use_featured_image?: string;
filter_items_list?: string;
filter_by_date?: string;
items_list_navigation?: string;
items_list?: string;
item_published?: string;
item_published_privately?: string;
item_reverted_to_draft?: string;
item_trashed?: string;
item_scheduled?: string;
item_updated?: string;
item_link?: string;
item_link_description?: string;
menu_name?: string;
name_admin_bar?: string;
};
name?: string;
icon?: string;
supports?: {
title?: boolean;
editor?: boolean;
author?: boolean;
thumbnail?: boolean;
excerpt?: boolean;
trackbacks?: boolean;
custom_fields?: boolean;
comments?: boolean;
revisions?: boolean;
post_formats?: boolean;
page_attributes?: boolean;
};
taxonomies?: string[];
rest_namespace?: string;
capabilities?: {
edit_post?: string;
read_post?: string;
delete_post?: string;
edit_posts?: string;
edit_others_posts?: string;
delete_posts?: string;
publish_posts?: string;
read_private_posts?: string;
read?: string;
delete_private_posts?: string;
delete_published_posts?: string;
delete_others_posts?: string;
edit_private_posts?: string;
edit_published_posts?: string;
create_posts?: string;
};
description?: string;
hierarchical?: boolean;
visibility?: {
show_in_nav_menus?: boolean;
show_ui?: boolean;
};
viewable?: boolean;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getAllItems } from "../api/get-all-items";

import { getArchiveStaticParams } from "./get-archive-static-params";
import { getSingleItemsStaticParams } from "./get-single-items-static-params";
import { getTaxonomyStaticParams } from "./get-taxonomy-static-params";
/**
* This function is used to statically generate routes at build time instead of on-demand at request time.
* To statically generate routes for your WordPress content in Next.js, you can export this function from your dynamic route `src/app/[[...paths]]/page.tsx` file.
Expand All @@ -15,7 +16,7 @@ import { getAllItems } from "../api/get-all-items";
*/
export async function generateStaticParams({
wpUrl = process.env.NEXT_PUBLIC_WP_URL || "",
postTypes = ["pages", "posts"],
postTypes = ["pages", "posts"], // taxonomies = ["category", "post_tag"],
}: {
/**
* The URL of the WP SITE to fetch data from the REST API.
Expand All @@ -33,39 +34,19 @@ export async function generateStaticParams({
}

const staticParams: { paths: string[] }[] = [];
const allItems = await getAllItems(postTypes);

for (const item of allItems) {
// if (item.path === "/") {
// staticParams.push({
// paths: ["/"],
// });
// continue;
// }

// if (item.path === "/") {
// staticParams.push({
// paths: ["index"],
// });
// continue;
// }
const singleParams = await getSingleItemsStaticParams({ postTypes });
staticParams.push(...singleParams);

const pathBreadcrumbs = item.path.split("/").filter((x) => x);
const archiveParams = await getArchiveStaticParams({ postTypes });
staticParams.push(...archiveParams);

staticParams.push({
paths: [...(pathBreadcrumbs || "/")],
});
}
const taxonomyParams = await getTaxonomyStaticParams({ postTypes });
staticParams.push(...taxonomyParams);

staticParams.push({
paths: ["/"],
});
staticParams.push({
paths: ["index"],
});
staticParams.push({
paths: ["all-products"],
});

return staticParams;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { getPostTypes } from "../../api/get-post-types";
import { getSiteSettings } from "../../api/get-site-settings";

export async function getArchiveStaticParams({ postTypes }) {
const wpPostTypes = await getPostTypes();
const settings = await getSiteSettings();
const staticParams: { paths: string[] }[] = [];

if (settings.page_for_posts) {
try {
const endpoint = `${process.env.NEXT_PUBLIC_WP_URL}/wp-json/wp/v2/pages/${settings.page_for_posts}`;
const req = await fetch(endpoint);
const postsPage = await req.json();

staticParams.push({
paths: [postsPage.slug],
});

const paginationInfo = await getArchivePaginationInfo({
rest_base: "posts",
per_page: settings.posts_per_page,
});

const postArchiveStaticParams = getStaticParamsFromPaginationInfo({
archiveSlug: postsPage.slug,
paginationInfo,
});

staticParams.push(...postArchiveStaticParams);
} catch (error) {
console.error(error);
}
}

for (const postType of postTypes) {
const itemKey = Object.keys(wpPostTypes).find(
(key) => wpPostTypes[key].rest_base === postType
);
const matchingPostType = wpPostTypes[itemKey];

if (
matchingPostType.has_archive &&
typeof matchingPostType.has_archive === "string"
) {
const paginationInfo = await getArchivePaginationInfo({
rest_base: matchingPostType.rest_base,
per_page: settings.posts_per_page,
});

const postTypeArchiveStaticParams = getStaticParamsFromPaginationInfo({
archiveSlug: matchingPostType.has_archive,
paginationInfo,
});

staticParams.push({
paths: [matchingPostType.has_archive],
});

staticParams.push(...postTypeArchiveStaticParams);

// if (matchingPostType.taxonomies) {
// for (const taxonomy of matchingPostType.taxonomies) {
// const taxonomyStaticParams = getTaxonomyStaticParams({ taxonomy });

// // console.log(matchingPostType.slug);
// // console.log({ taxonomy });

// // const params = {}

// // if (taxonomy.slug === "category") {
// // params.categories = String(term.id);
// // } else if (taxonomy.slug === "post_tag") {
// // params.tags = String(term.id);
// // } else {
// // params[taxonomy.slug] = String(term.id);
// // }

// // const taxonomyPaginationInfo = await getArchivePaginationInfo({
// // rest_base: matchingPostType.rest_base,
// // per_page: settings.posts_per_page,
// // params: {

// // },
// // });
// }
// }
}

// todo: check which taxonomies are registered for this post type
// todo: then fetch all terms for each taxonomy
// todo: then generate static paths for each term
}

return staticParams;
}

async function getArchivePaginationInfo({
rest_base,
per_page,
params = {},
}: {
rest_base: string;
per_page: number;
params?: Record<string, string>;
}) {
const queryString = new URLSearchParams({
per_page: String(per_page),
...params,
}).toString();

try {
const endpoint = `${process.env.NEXT_PUBLIC_WP_URL}/wp-json/wp/v2/${rest_base}?${queryString}`;
const req = await fetch(endpoint);

// Check if the fetch request was successful
if (!req.ok) {
throw new Error(`HTTP error! status: ${req.status}`);
}

const totalPages = req.headers.get("X-WP-TotalPages")
? Number(req.headers.get("X-WP-TotalPages") || 1)
: undefined;

const totalItems = req.headers.get("X-WP-Total")
? Number(req.headers.get("X-WP-Total") || 0)
: undefined;

return {
totalPages,
totalItems,
};
} catch (error) {
console.error("Error fetching data:", error);
return null;
}
}

function getStaticParamsFromPaginationInfo({ archiveSlug, paginationInfo }) {
const staticParams = [];

for (let i = 1; i <= paginationInfo.totalPages; i++) {
if (i === 1) {
continue;
}
staticParams.push({
paths: [archiveSlug, String(i)],
});
}

return staticParams;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getAllItems } from "../../api/get-all-items";

export async function getSingleItemsStaticParams({
postTypes,
}: {
postTypes: string[];
}) {
const staticParams: { paths: string[] }[] = [];
const allItems = await getAllItems(postTypes);

for (const item of allItems) {
if (!item.path) {
continue;
}
const pathBreadcrumbs = item.path.split("/").filter((x) => x);
staticParams.push({
paths: [...pathBreadcrumbs],
});
}

return staticParams;
}
Loading

1 comment on commit f2e1425

@vercel
Copy link

@vercel vercel bot commented on f2e1425 Jan 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-wordpress-starter – ./apps/next-wordpress-starter

next-wordpress-starter-alpha.vercel.app
next-wordpress-starter-calebbarnes.vercel.app
next-wordpress-starter-git-main-calebbarnes.vercel.app

Please sign in to comment.