-
Notifications
You must be signed in to change notification settings - Fork 274
Feat/add coauthors 2 #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Dockerize app
fix: remove placeholder image if none for preview
Refactor Wordpress API
|
@Tabarzin is attempting to deploy a commit to the 9d8 Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughComprehensive project restructuring featuring environment variable renaming (WORDPRESS_URL → NEXT_PUBLIC_WORDPRESS_URL, WORDPRESS_WEBHOOK_SECRET → NEXT_WORDPRESS_WEBHOOK_SECRET), Docker containerization infrastructure, major data layer refactoring with typed queries and cache tagging, simplified homepage rendering, and enhanced webhook revalidation with slug-aware cache invalidation. Changes
Sequence Diagram(s)sequenceDiagram
participant WordPress as WordPress REST API
participant Next as Next.js App
participant Cache as Next.js Data Cache
participant Webhook as WP Plugin Webhook
participant Revalidate as /api/revalidate Route
Note over Webhook: Post published/updated with slug
Webhook->>Revalidate: Send contentId, contentType, contentSlug
Revalidate->>Revalidate: Extract & validate payload
activate Revalidate
Note over Revalidate: Generate cache tags:<br/>post-{id}, post-{slug}<br/>posts, wordpress (if default)
Revalidate->>Cache: revalidateTag(tag) per type
Revalidate->>Cache: revalidatePath("/layout")
deactivate Revalidate
participant User as User Browser
User->>Next: Request page/post
activate Next
alt Cache hit
Next->>Cache: Check cache tags
Cache-->>Next: Return cached data
else Cache miss/revalidated
Next->>WordPress: Fetch with _fields=[slug, title...]
WordPress-->>Next: Return typed fields
Next->>Cache: Store with tags
end
deactivate Next
Next-->>User: Render page
sequenceDiagram
participant Client as Client Browser
participant App as app/page.tsx (async)
participant WP as lib/wordpress.ts
participant Cache as fetch cache + revalidate
Client->>App: Request /
activate App
App->>WP: getPostsPaginated(1, 30, {categories: !news_id})
activate WP
WP->>WP: Build query with _fields: postCardFields
WP->>Cache: wordpressFetchWithPagination(..., cacheTags)
Cache->>Cache: Fetch with 1-day revalidation
WP-->>App: WordPressResponse<CardPost[]>
deactivate WP
App->>App: Render Container with Prose + PostCard grid
alt Posts exist
App->>Client: Render 3-col grid of CardPost
else No posts
App->>Client: "No posts found" message
end
deactivate App
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/posts/[slug]/page.tsx (1)
82-112: Primary author disappears when no co-authors
getPostDataonly returns co-author slugs, so for a normal single-author post theauthorsarray comes back empty. The new rendering path therefore drops the byline entirely, which is a regression from the previous behavior. Please fall back to the primary author (viagetAuthorById(post.author)) whenauthorsis empty, and reuse that list for the UI. This keeps co-author support while preserving the existing single-author output.- const { post, featuredMedia, category, authors } = postData; + const { post, featuredMedia, category, authors } = postData; + const primaryAuthor = + (!authors || authors.length === 0) && post.author + ? await getAuthorById(post.author) + : null; + const displayAuthors = + authors && authors.length > 0 + ? authors + : primaryAuthor + ? [primaryAuthor] + : []; @@ - {authors?.length > 0 && ( + {displayAuthors.length > 0 && ( <> {" by "} - {authors.map((a: any, i: number) => ( + {displayAuthors.map((a: any, i: number) => ( <span key={a.id}> <a href={`/posts/?author=${a.id}`}>{a.name}</a> - {i < authors.length - 1 ? ", " : ""} + {i < displayAuthors.length - 1 ? ", " : ""} </span> ))} </> )}components/posts/post-card.tsx (1)
23-59: Avoid indexing into empty term arrays.On Line 24 the code reads
post._embedded?.["wp:term"][0][0].taxonomywithout verifying that the inner array has an element. If the API returns an empty term list,term[0]is undefined and React will crash rendering this card. Please guard against empty arrays before dereferencing.- const category = post._embedded?.["wp:term"][0] && post._embedded?.["wp:term"][0][0].taxonomy === "category" ? - post._embedded?.["wp:term"][0][0] - : post.categories?.[0] - ? await getCategoryById(post.categories[0]) - : null; + const primaryTerms = post._embedded?.["wp:term"]?.[0]; + const embeddedCategory = + Array.isArray(primaryTerms) && primaryTerms[0]?.taxonomy === "category" + ? primaryTerms[0] + : null; + const category = + embeddedCategory ?? + (post.categories?.[0] ? await getCategoryById(post.categories[0]) : null);
🧹 Nitpick comments (3)
.env.example (1)
1-6: Remove quotes from environment variable values.In
.envfiles, quotes around URLs are typically unnecessary and can cause issues as they may be included in the actual value. Thedotenv-linterstatic analysis tool has flagged this.Apply this diff to remove the quotes:
-NEXT_PUBLIC_WORDPRESS_URL="https://sysblok.ru" -NEXT_PUBLIC_URL="https://next.sysblok.team" +NEXT_PUBLIC_WORDPRESS_URL=https://sysblok.ru +NEXT_PUBLIC_URL=https://next.sysblok.team # If using the revalidate plugin # You can generate by running `openssl rand -base64 32` in the terminal -NEXT_WORDPRESS_WEBHOOK_SECRET="your-secret-key-here" +NEXT_WORDPRESS_WEBHOOK_SECRET=your-secret-key-herenext.config.ts (1)
1-21: Remove commented code.This commented-out configuration should be removed. Version control preserves the history, so there's no need to keep it in the file.
Apply this diff:
-// import type { NextConfig } from "next"; - -// const nextConfig: NextConfig = { -// images: { -// remotePatterns: [ -// new URL(`${process.env.NEXT_PUBLIC_WORDPRESS_URL}/wp-content/**`), -// ], -// }, -// async redirects() { -// return [ -// { -// source: "/admin", -// destination: `${process.env.NEXT_PUBLIC_WORDPRESS_URL}/wp-admin`, -// permanent: true, -// }, -// ]; -// }, -// output: "standalone", -// }; - -// export default nextConfig; - import type { NextConfig } from "next";Dockerfile.dev (1)
1-32: Consider using pnpm consistently for both install and run commands.The Dockerfile uses
pnpmfor dependency installation (line 19) butnpmfor running the dev server (line 32). While this works because both package managers can read the samepackage.jsonscripts, usingpnpmconsistently would be clearer.Apply this diff:
-CMD ["npm", "run", "dev"] +CMD ["pnpm", "run", "dev"]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
app/favicon.icois excluded by!**/*.icoplugin/next-revalidate.zipis excluded by!**/*.zip
📒 Files selected for processing (28)
.env.example(1 hunks).gitignore(1 hunks)CLAUDE.md(1 hunks)Dockerfile(1 hunks)Dockerfile.dev(1 hunks)Makefile(1 hunks)README.md(5 hunks)app/api/revalidate/route.ts(3 hunks)app/layout.tsx(5 hunks)app/page.tsx(1 hunks)app/pages/[slug]/page.tsx(1 hunks)app/pages/page.tsx(1 hunks)app/posts/[slug]/page.tsx(4 hunks)app/posts/page.tsx(1 hunks)app/sitemap.ts(1 hunks)components/nav/mobile-nav.tsx(1 hunks)components/posts/post-card.tsx(2 hunks)compose.yml(1 hunks)dockerignore(1 hunks)lib/utils.ts(1 hunks)lib/wordpress.d.ts(6 hunks)lib/wordpress.ts(9 hunks)menu.config.ts(1 hunks)next.config.ts(1 hunks)plugin/README.md(1 hunks)plugin/next-revalidate/README.txt(1 hunks)plugin/next-revalidate/next-revalidate.php(6 hunks)site.config.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: When working with a headless WordPress starter using Next.js 15 App Router and TypeScript, centralize all WordPress API interactions in a single module (e.g., lib/wordpress.ts) to maintain a clean data layer.
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Set up a revalidation system where a WordPress plugin sends webhooks to a Next.js endpoint, which validates the webhook secret and calls revalidateTag(), with a default cache duration (e.g., 1 hour).
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Store required environment variables (such as WORDPRESS_URL, WORDPRESS_HOSTNAME, WORDPRESS_WEBHOOK_SECRET) in a .env.example file to document configuration requirements.
Applied to files:
CLAUDE.mdplugin/README.md.env.exampleREADME.md
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Set up a revalidation system where a WordPress plugin sends webhooks to a Next.js endpoint, which validates the webhook secret and calls revalidateTag(), with a default cache duration (e.g., 1 hour).
Applied to files:
CLAUDE.mdplugin/README.mdplugin/next-revalidate/README.txt.env.exampleREADME.mdplugin/next-revalidate/next-revalidate.phpapp/api/revalidate/route.tslib/wordpress.ts
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Leverage Next.js cache tags in data-fetching functions to enable granular and efficient cache revalidation.
Applied to files:
app/posts/page.tsxlib/wordpress.ts
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Use parallel data fetching with Promise.all() in Server Components to optimize performance.
Applied to files:
app/posts/page.tsx
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: When working with a headless WordPress starter using Next.js 15 App Router and TypeScript, centralize all WordPress API interactions in a single module (e.g., lib/wordpress.ts) to maintain a clean data layer.
Applied to files:
next.config.tsapp/page.tsxREADME.mdapp/api/revalidate/route.tslib/wordpress.d.tsapp/posts/[slug]/page.tsxlib/wordpress.ts
📚 Learning: 2025-06-26T17:28:47.375Z
Learnt from: CR
Repo: 9d8dev/next-wp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-26T17:28:47.375Z
Learning: Define all TypeScript interfaces for WordPress entities (Post, Page, Category, Tag, Author, Media) in a dedicated type definition file (e.g., lib/wordpress.d.ts) to ensure strict typing and maintainability.
Applied to files:
README.mdcomponents/posts/post-card.tsxlib/wordpress.d.tsapp/posts/[slug]/page.tsxlib/wordpress.ts
🧬 Code graph analysis (11)
app/pages/page.tsx (1)
lib/wordpress.ts (1)
getAllPages(396-400)
app/layout.tsx (3)
components/craft.tsx (1)
Container(227-231)site.config.ts (1)
siteConfig(13-17)menu.config.ts (1)
contentMenu(12-13)
app/posts/page.tsx (1)
lib/wordpress.ts (1)
getPostsPaginated(197-229)
app/sitemap.ts (1)
lib/wordpress.ts (1)
getAllPosts(231-237)
app/pages/[slug]/page.tsx (1)
lib/wordpress.ts (1)
getAllPages(396-400)
components/nav/mobile-nav.tsx (1)
menu.config.ts (1)
contentMenu(12-13)
app/page.tsx (3)
lib/wordpress.ts (2)
getCategoryBySlug(319-325)getPostsPaginated(197-229)components/craft.tsx (2)
Container(227-231)Prose(253-266)components/posts/post-card.tsx (1)
PostCard(12-71)
components/posts/post-card.tsx (2)
lib/wordpress.ts (2)
CardPost(181-194)getMediaById(513-521)lib/utils.ts (1)
extractExcerptText(16-22)
lib/wordpress.d.ts (2)
app/posts/[slug]/page.tsx (1)
Page(69-143)app/posts/page.tsx (1)
Page(35-164)
app/posts/[slug]/page.tsx (1)
lib/wordpress.ts (2)
getAllPostSlugs(563-563)getPostData(635-660)
lib/wordpress.ts (3)
lib/wordpress.d.ts (9)
WordPressQuery(325-332)CacheTag(334-349)WordPressResponse(252-255)Post(55-92)Category(119-122)Tag(124-126)Page(94-106)Author(128-137)Media(41-52)app/posts/[slug]/page.tsx (1)
Page(69-143)app/posts/page.tsx (1)
Page(35-164)
🪛 ast-grep (0.39.7)
components/posts/post-card.tsx
[warning] 49-49: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
components/posts/post-card.tsx
[error] 50-50: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🪛 checkmake (0.2.2)
Makefile
[warning] 69-69: Target "restart" should be declared PHONY.
(phonydeclared)
[warning] 13-13: Target body for "help" exceeds allowed length of 5 (14).
(maxbodylength)
[warning] 12-12: Missing required phony target "all"
(minphony)
[warning] 12-12: Missing required phony target "clean"
(minphony)
[warning] 12-12: Missing required phony target "test"
(minphony)
🪛 dotenv-linter (4.0.0)
.env.example
[warning] 1-1: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 2-2: [UnorderedKey] The NEXT_PUBLIC_URL key should go before the NEXT_PUBLIC_WORDPRESS_URL key
(UnorderedKey)
[warning] 6-6: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
🪛 LanguageTool
README.md
[uncategorized] ~406-~406: The official name of this content management system is spelled with a capital “P”.
Context: ..., and getCategoryByIdfunctions fromlib/wordpress.ts`. 2. It formats the post date using...
(WORDPRESS)
🪛 markdownlint-cli2 (0.18.1)
README.md
9-9: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
10-10: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
11-11: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
12-12: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
13-13: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
14-14: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
16-16: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
17-17: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
18-18: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
19-19: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
20-20: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
21-21: Unordered list indentation
Expected: 0; Actual: 1
(MD007, ul-indent)
🔇 Additional comments (17)
.gitignore (1)
28-30: LGTM!Adding
.envto the ignore list aligns with the Docker and compose workflows introduced in this PR. The comment update appropriately reflects the broader scope.dockerignore (1)
1-7: LGTM!The
.dockerignorepatterns are appropriate for excluding unnecessary files from the Docker build context. Note that.envis intentionally not excluded, allowing environment files to be available during builds if needed.Dockerfile (2)
1-28: LGTM!The base and deps stages are well-structured. Using Alpine for a smaller image and pnpm for efficient dependency management are good choices.
52-75: Standalone output configuration is correctly set.The
next.config.tsfile hasoutput: "standalone"configured on line 46, which enables the Dockerfile to successfully copy from/app/.next/standaloneduring the build process.plugin/README.md (1)
27-30: LGTM!The environment variable rename from
WORDPRESS_WEBHOOK_SECRETtoNEXT_WORDPRESS_WEBHOOK_SECRETaligns with the broader naming convention changes across the project. This improves clarity by indicating these are Next.js-specific environment variables.plugin/next-revalidate/README.txt (1)
37-37: LGTM!The environment variable reference is correctly updated to match the new naming convention used throughout the project.
.env.example (2)
1-2: Verify example URLs are intentional.The example file contains what appear to be real URLs (
sysblok.ruandnext.sysblok.team). Ensure these are intended as example values rather than placeholder URLs likeexample.com.
1-6: Approve environment variable naming changes.The renaming of environment variables improves clarity:
NEXT_PUBLIC_WORDPRESS_URLandNEXT_PUBLIC_URLcorrectly use theNEXT_PUBLIC_prefix for client-side exposureNEXT_WORDPRESS_WEBHOOK_SECRETappropriately omits theNEXT_PUBLIC_prefix to keep the secret server-side onlyThis aligns with Next.js best practices and the broader refactoring in this PR.
Based on learnings
app/pages/page.tsx (1)
16-16: LGTM!Limiting the fetched fields to only
["id", "slug", "title"]improves performance by reducing the payload size from the WordPress API. This aligns with the actual data usage in the rendering logic (lines 24-26).app/posts/page.tsx (1)
55-55: LGTM!The parameter mapping correctly transforms URL search params (singular:
tag,category) to the API function's expected parameters (plural:tags,categories). This aligns with the updatedgetPostsPaginatedsignature inlib/wordpress.ts.next.config.ts (1)
46-46: LGTM!The standalone output mode is appropriate for the Docker-based deployment infrastructure introduced in this PR.
CLAUDE.md (1)
62-63: LGTM!The environment variable documentation correctly reflects the new naming convention (
NEXT_PUBLIC_WORDPRESS_URLandNEXT_WORDPRESS_WEBHOOK_SECRET) used throughout the PR.app/sitemap.ts (1)
6-6: LGTM!Field limiting to only
slugandmodifiedis an excellent optimization that reduces the data payload for sitemap generation while fetching exactly what's needed.components/nav/mobile-nav.tsx (1)
63-71: LGTM!Conditional rendering prevents displaying an empty Blog Menu section, improving the user experience. This aligns with similar conditional rendering in the Footer.
compose.yml (1)
7-8: Verify the need to load both.env.exampleand.env.Loading both environment files can cause confusion about which values take precedence. Docker Compose loads them in order, with
.envoverriding.env.example.Typically, only
.envshould be loaded in development, as.env.exampleserves as a template for developers to create their own.envfile.Consider whether
.env.exampleshould be removed:env_file: - - .env.example - .envapp/pages/[slug]/page.tsx (1)
11-11: LGTM!Returning the result directly from
getAllPagesis cleaner and more efficient than the previous approach. The field limitation ensures only necessary data is fetched for static path generation.lib/utils.ts (1)
9-14: LGTM!The defensive type checking and HTML tag stripping logic is sound.
| const newsCategpry = await getCategoryBySlug("news"); | ||
| const { data: posts } = await getPostsPaginated(1, 30, { categories_exclude: newsCategpry.id }); | ||
|
|
||
| // This is just some example TSX | ||
| const ToDelete = () => { | ||
| return ( | ||
| <main className="space-y-6"> | ||
| <Container> | ||
| <Prose> | ||
| <h1> | ||
| <Balancer>Headless WordPress built with the Next.js</Balancer> | ||
| Системный Блокъ | ||
| </h1> | ||
|
|
||
| <p> | ||
| This is <a href="https://github.com/9d8dev/next-wp">next-wp</a>, | ||
| created as a way to build WordPress sites with Next.js at rapid speed. | ||
| This starter is designed with{" "} | ||
| <a href="https://ui.shadcn.com">shadcn/ui</a>,{" "} | ||
| <a href="https://craft-ds.com">craft-ds</a>, and Tailwind CSS. Use{" "} | ||
| <a href="https://components.work">brijr/components</a> to build your | ||
| site with prebuilt components. The data fetching and typesafety is | ||
| handled in <code>lib/wordpress.ts</code> and{" "} | ||
| <code>lib/wordpress.d.ts</code>. | ||
| </p> | ||
| </Prose> | ||
|
|
||
| <div className="flex justify-between items-center gap-4"> | ||
| {/* Vercel Clone Starter */} | ||
| <div className="flex items-center gap-3"> | ||
| <a | ||
| className="h-auto block" | ||
| href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2F9d8dev%2Fnext-wp&env=WORDPRESS_URL,WORDPRESS_HOSTNAME&envDescription=Add%20WordPress%20URL%20with%20Rest%20API%20enabled%20(ie.%20https%3A%2F%2Fwp.example.com)%20abd%20the%20hostname%20for%20Image%20rendering%20in%20Next%20JS%20(ie.%20wp.example.com)&project-name=next-wp&repository-name=next-wp&demo-title=Next%20JS%20and%20WordPress%20Starter&demo-url=https%3A%2F%2Fwp.9d8.dev" | ||
| > | ||
| {/* eslint-disable-next-line */} | ||
| <img | ||
| className="not-prose my-4" | ||
| src="https://vercel.com/button" | ||
| alt="Deploy with Vercel" | ||
| width={105} | ||
| height={32.62} | ||
| /> | ||
| </a> | ||
| <p className="!text-sm sr-only sm:not-sr-only text-muted-foreground"> | ||
| Deploy with Vercel in seconds. | ||
| </p> | ||
| </div> | ||
|
|
||
| <div className="flex gap-2 items-center"> | ||
| <WordPressIcon className="text-foreground" width={32} height={32} /> | ||
| <NextJsIcon className="text-foreground" width={32} height={32} /> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="grid md:grid-cols-3 gap-4 mt-6"> | ||
| <Link | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="/posts" | ||
| > | ||
| <Pen size={32} /> | ||
| <span> | ||
| Posts{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| All posts from your WordPress | ||
| </span> | ||
| </span> | ||
| </Link> | ||
| <Link | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="/pages" | ||
| > | ||
| <File size={32} /> | ||
| <span> | ||
| Pages{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| Custom pages from your WordPress | ||
| </span> | ||
| </span> | ||
| </Link> | ||
| <Link | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="/posts/authors" | ||
| > | ||
| <User size={32} /> | ||
| <span> | ||
| Authors{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| List of the authors from your WordPress | ||
| </span> | ||
| </span> | ||
| </Link> | ||
| <Link | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="/posts/tags" | ||
| > | ||
| <Tag size={32} /> | ||
| <span> | ||
| Tags{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| Content by tags from your WordPress | ||
| </span> | ||
| </span> | ||
| </Link> | ||
| <Link | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="/posts/categories" | ||
| > | ||
| <Diamond size={32} /> | ||
| <span> | ||
| Categories{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| Categories from your WordPress | ||
| </span> | ||
| </span> | ||
| </Link> | ||
| <a | ||
| className="border h-48 bg-accent/50 rounded-lg p-4 flex flex-col justify-between hover:scale-[1.02] transition-all" | ||
| href="https://github.com/9d8dev/next-wp/blob/main/README.md" | ||
| > | ||
| <Folder size={32} /> | ||
| <span> | ||
| Documentation{" "} | ||
| <span className="block text-sm text-muted-foreground"> | ||
| How to use `next-wp` | ||
| </span> | ||
| </span> | ||
| </a> | ||
| </div> | ||
| </main> | ||
| {posts.length > 0 ? ( | ||
| <div className="grid md:grid-cols-3 gap-4"> | ||
| {posts.map((post) => ( | ||
| <PostCard key={post.id} post={post} /> | ||
| ))} | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against a missing “news” category before dereferencing.
Line 10 calls newsCategpry.id without verifying that getCategoryBySlug("news") returned a value. If the slug is absent (or the API response changes), the page throws Cannot read properties of undefined, breaking the homepage render. Please null‑check the category before building the query and only pass categories_exclude when you have an ID.
- const newsCategpry = await getCategoryBySlug("news");
- const { data: posts } = await getPostsPaginated(1, 30, { categories_exclude: newsCategpry.id });
+ const newsCategory = await getCategoryBySlug("news");
+ const { data: posts } = await getPostsPaginated(1, 30, {
+ ...(newsCategory?.id ? { categories_exclude: newsCategory.id } : {}),
+ });🤖 Prompt for AI Agents
In app/page.tsx around lines 9 to 24, guard against a missing "news" category
before dereferencing: after calling getCategoryBySlug("news") check whether the
returned category is truthy and only include categories_exclude when you have an
id (e.g. build the query params conditionally or pass undefined when absent).
Update the getPostsPaginated call to use either { categories_exclude:
category.id } when category exists or omit/pass undefined when it does not,
avoiding direct access to category.id and preventing runtime crashes.
| FROM base AS builder | ||
|
|
||
| # Copy node modules from dependencies | ||
| COPY --from=deps /app/node_modules ./node_modules | ||
|
|
||
| # Copy source code into the container | ||
| COPY . . | ||
|
|
||
| # Next.js collects completely anonymous telemetry data about general usage. | ||
| # Learn more here: https://nextjs.org/telemetry | ||
| # Following line disables telemetry. | ||
| ENV NEXT_TELEMETRY_DISABLED=1 | ||
|
|
||
| # Build the application | ||
| RUN npm run build |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent package manager usage.
The deps stage installs dependencies with pnpm (line 28), but the builder stage uses npm run build (line 47). For consistency and to ensure the lockfile is respected, consider using pnpm throughout.
Apply this diff to use pnpm for the build:
-# Build the application
-RUN npm run build
+# Build the application
+RUN pnpm build📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| FROM base AS builder | |
| # Copy node modules from dependencies | |
| COPY --from=deps /app/node_modules ./node_modules | |
| # Copy source code into the container | |
| COPY . . | |
| # Next.js collects completely anonymous telemetry data about general usage. | |
| # Learn more here: https://nextjs.org/telemetry | |
| # Following line disables telemetry. | |
| ENV NEXT_TELEMETRY_DISABLED=1 | |
| # Build the application | |
| RUN npm run build | |
| FROM base AS builder | |
| # Copy node modules from dependencies | |
| COPY --from=deps /app/node_modules ./node_modules | |
| # Copy source code into the container | |
| COPY . . | |
| # Next.js collects completely anonymous telemetry data about general usage. | |
| # Learn more here: https://nextjs.org/telemetry | |
| # Following line disables telemetry. | |
| ENV NEXT_TELEMETRY_DISABLED=1 | |
| # Build the application | |
| RUN pnpm build |
🤖 Prompt for AI Agents
In Dockerfile around lines 33 to 47, the builder stage uses npm to run the build
despite dependencies being installed with pnpm in the deps stage; replace the
npm invocation with pnpm (e.g., change RUN npm run build to RUN pnpm run build)
and ensure pnpm is available in the builder image (either install pnpm or enable
Corepack before running the build) so the lockfile and package manager remain
consistent.
| export function extractExcerptText(excerpt: string): string { | ||
| if (!excerpt || typeof excerpt !== 'string') { | ||
| return ''; | ||
| } | ||
| const matches = excerpt.match(/<p>([\s\S]*)<\/p>/); | ||
| return matches ? stripHtmlTags(matches[1]) : ''; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greedy regex incorrectly extracts content from multiple paragraphs.
The regex /<p>([\s\S]*)<\/p>/ uses a greedy quantifier that will match from the first <p> to the last </p> in the string. For example, if the excerpt is "<p>First</p><p>Second</p>", it will capture "First</p><p>Second" instead of just "First".
Apply this diff to use a non-greedy quantifier:
- const matches = excerpt.match(/<p>([\s\S]*)<\/p>/);
+ const matches = excerpt.match(/<p>([\s\S]*?)<\/p>/);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function extractExcerptText(excerpt: string): string { | |
| if (!excerpt || typeof excerpt !== 'string') { | |
| return ''; | |
| } | |
| const matches = excerpt.match(/<p>([\s\S]*)<\/p>/); | |
| return matches ? stripHtmlTags(matches[1]) : ''; | |
| } | |
| export function extractExcerptText(excerpt: string): string { | |
| if (!excerpt || typeof excerpt !== 'string') { | |
| return ''; | |
| } | |
| const matches = excerpt.match(/<p>([\s\S]*?)<\/p>/); | |
| return matches ? stripHtmlTags(matches[1]) : ''; | |
| } |
🤖 Prompt for AI Agents
In lib/utils.ts around lines 16 to 22, the current greedy regex
/<p>([\s\S]*)<\/p>/ can capture across multiple paragraph tags; change it to a
non-greedy match so the function returns only the first paragraph (use
/<p>([\s\S]*?)<\/p>/ or equivalent), then keep returning
stripHtmlTags(matches[1]) when a match exists; no other logic changes needed.
| export interface MediaQuery extends EntityQuery<FeaturedMedia> { | ||
| parent?: number | Array<number>; // Limit result set to items with particular parent IDs. | ||
| parent_exclude?: number | Array<number>; // Limit result set to all items except those of a particular parent ID. | ||
| media_type?: "image" | "video" | "text" | "application" | "audio"; // Limit result set to attachments of a particular media type. | ||
| mime_type?: string; // Limit result set to attachments of a particular MIME type. | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update MediaQuery to use the renamed Media type.
Line 300 still references FeaturedMedia, which no longer exists after renaming the interface. This breaks type checking for every media query helper. Please switch the generic to the new Media type.
-export interface MediaQuery extends EntityQuery<FeaturedMedia> {
+export interface MediaQuery extends EntityQuery<Media> {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface MediaQuery extends EntityQuery<FeaturedMedia> { | |
| parent?: number | Array<number>; // Limit result set to items with particular parent IDs. | |
| parent_exclude?: number | Array<number>; // Limit result set to all items except those of a particular parent ID. | |
| media_type?: "image" | "video" | "text" | "application" | "audio"; // Limit result set to attachments of a particular media type. | |
| mime_type?: string; // Limit result set to attachments of a particular MIME type. | |
| } | |
| export interface MediaQuery extends EntityQuery<Media> { | |
| parent?: number | Array<number>; // Limit result set to items with particular parent IDs. | |
| parent_exclude?: number | Array<number>; // Limit result set to all items except those of a particular parent ID. | |
| media_type?: "image" | "video" | "text" | "application" | "audio"; // Limit result set to attachments of a particular media type. | |
| mime_type?: string; // Limit result set to attachments of a particular MIME type. | |
| } |
🤖 Prompt for AI Agents
In lib/wordpress.d.ts around lines 299 to 304, the MediaQuery interface still
uses the old generic type FeaturedMedia which was renamed to Media; update the
generic parameter from FeaturedMedia to Media so MediaQuery extends
EntityQuery<Media> instead, preserving all existing properties and imports
(ensure any import/typedef references match the new Media name).
| const res = await fetch( | ||
| `${process.env.WP_URL}/wp-json/wp/v2/coauthors?post=${postId}` | ||
| ); | ||
| if (!res.ok) return []; | ||
| const coauthors = await res.json(); | ||
|
|
||
| return coauthors.map((a: any) => a.name.replace(/^cap-/, "")); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix coauthor fetch to use configured base URL.
getCoAuthorsByPost calls fetch against ${process.env.WP_URL}, but the module now standardizes on NEXT_PUBLIC_WORDPRESS_URL (exposed as baseUrl). In production this leaves WP_URL undefined, yielding an invalid URL at runtime and breaking co-author loading. Reuse baseUrl (or the existing wordpressFetch helper) so the request honors the configured endpoint, headers, and cache tags.
export async function getCoAuthorsByPost(postId: number) {
- const res = await fetch(
- `${process.env.WP_URL}/wp-json/wp/v2/coauthors?post=${postId}`
- );
- if (!res.ok) return [];
- const coauthors = await res.json();
-
- return coauthors.map((a: any) => a.name.replace(/^cap-/, ""));
+ const res = await fetch(
+ `${baseUrl}/wp-json/wp/v2/coauthors?post=${postId}`,
+ {
+ headers: { "User-Agent": "Next.js WordPress Client" },
+ next: {
+ tags: ["wordpress", "authors", `post-${postId}`],
+ revalidate: 86400,
+ },
+ }
+ );
+ if (!res.ok) return [];
+ const coauthors = await res.json();
+
+ return coauthors.map((a: any) => a.name.replace(/^cap-/, ""));
}Based on learnings
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In lib/wordpress.ts around lines 626 to 633, the function uses
process.env.WP_URL directly which will be undefined in production; replace that
fetch with the module's configured baseUrl or, better, call the existing
wordpressFetch helper so the request uses the standardized base URL, headers and
cache tags. Update the code to build the URL from baseUrl (or call
wordpressFetch with path `/wp-json/wp/v2/coauthors?post=${postId}`) and keep the
same response handling (return [] on non-ok and map names stripping the cap-
prefix).
| stop: | ||
| docker stop $(CONTAINER_NAME) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make stop idempotent so restart succeeds.
Right now restart depends on stop, but docker stop $(CONTAINER_NAME) exits 1 when the container is absent, causing make restart to abort before run executes. Add a harmless fallback so the target succeeds even if the container is already down.
stop:
- docker stop $(CONTAINER_NAME)
+ docker stop $(CONTAINER_NAME) || true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| stop: | |
| docker stop $(CONTAINER_NAME) | |
| stop: | |
| docker stop $(CONTAINER_NAME) || true |
🤖 Prompt for AI Agents
In Makefile around lines 57 to 58, the stop target fails when the container is
absent which makes restart abort; make stop idempotent by making the docker stop
command tolerate a missing container (for example append "|| true" or redirect
errors and "|| true") so the target returns success even if the container is
already stopped, preserving current behavior when the container exists.
| hostname: "sysblok.ru", | ||
| pathname: "/wp-content/**", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded hostname limits flexibility across environments.
The hardcoded sysblok.ru hostname prevents using different WordPress instances for development, staging, or production. This contradicts the environment-variable-driven approach documented in CLAUDE.md.
Consider deriving the hostname from the environment variable:
- hostname: "sysblok.ru",
- pathname: "/wp-content/**",
+ ...new URL(`${process.env.NEXT_PUBLIC_WORDPRESS_URL}/wp-content/**`),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| hostname: "sysblok.ru", | |
| pathname: "/wp-content/**", | |
| hostname: new URL(process.env.NEXT_PUBLIC_WORDPRESS_URL).hostname, | |
| pathname: "/wp-content/**", |
🤖 Prompt for AI Agents
In next.config.ts around lines 30-31 the hostname is hardcoded to "sysblok.ru";
replace it with a value derived from an environment variable (e.g. use
process.env.WP_HOST or parse process.env.WP_URL to extract the hostname) and
provide a sensible default like "sysblok.ru" if the env var is absent, validate
that the resulting value is a string/valid hostname, and ensure the env var is
documented/available in your deployment/runtime so different WordPress instances
(dev/staging/prod) can be used without code changes.
| destination: `${ | ||
| process.env.WORDPRESS_URL || "https://sysblok.ru" | ||
| }/wp-admin`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Environment variable name mismatch breaks redirect.
The redirect uses WORDPRESS_URL, but CLAUDE.md documents NEXT_PUBLIC_WORDPRESS_URL as the required environment variable. This inconsistency will cause the redirect to fail.
Apply this diff:
destination: `${
- process.env.WORDPRESS_URL || "https://sysblok.ru"
+ process.env.NEXT_PUBLIC_WORDPRESS_URL || "https://sysblok.ru"
}/wp-admin`,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| destination: `${ | |
| process.env.WORDPRESS_URL || "https://sysblok.ru" | |
| }/wp-admin`, | |
| destination: `${ | |
| process.env.NEXT_PUBLIC_WORDPRESS_URL || "https://sysblok.ru" | |
| }/wp-admin`, |
🤖 Prompt for AI Agents
In next.config.ts around lines 39 to 41, the redirect is reading
process.env.WORDPRESS_URL while the docs and app expect
NEXT_PUBLIC_WORDPRESS_URL; update the code to use
process.env.NEXT_PUBLIC_WORDPRESS_URL (keeping the same fallback
"https://sysblok.ru") so the environment variable name matches CLAUDE.md and the
redirect works reliably.
| public function on_post_trash($post_id) { | ||
| $post_type = get_post_type($post_id); | ||
| error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}"); | ||
| $this->send_revalidation_request($post_type, $post_id); | ||
| $post = get_post($post_id); | ||
| error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post->type}"); | ||
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | ||
| } | ||
|
|
||
| public function on_post_untrash($post_id) { | ||
| $post_type = get_post_type($post_id); | ||
| error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}"); | ||
| $this->send_revalidation_request($post_type, $post_id); | ||
| $post = get_post($post_id); | ||
| error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post->type}"); | ||
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | ||
| } | ||
|
|
||
| public function on_post_delete($post_id) { | ||
| $post_type = get_post_type($post_id); | ||
| if (!$post_type) { | ||
| $post_type = 'unknown'; // Fallback if post type can't be determined | ||
| public function on_post_delete($post_id, $post = null) { | ||
| // Get the post if not provided | ||
| if (null === $post) { | ||
| $post = get_post($post_id); | ||
| } | ||
| error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post_type}"); | ||
| $this->send_revalidation_request($post_type, $post_id); | ||
|
|
||
| error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post->type}"); | ||
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use post_type and guard against missing WP_Post instances.
Lines 377‑395 assume $post is always present and expose $post->type. In WordPress, the property is post_type; $post->type is undefined and results in empty payloads. Worse, when after_delete_post fires, $post can be null, so $post->type / $post->post_name trigger fatal errors. Please bail out if no WP_Post is available and use post_type to populate the payload.
public function on_post_trash($post_id) {
- $post = get_post($post_id);
- error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post->type}");
- $this->send_revalidation_request($post->type, $post_id, $post->post_name);
+ $post = get_post($post_id);
+ if (!$post instanceof \WP_Post) {
+ error_log("Next.js Revalidation: Post trashed {$post_id} but WP_Post instance was unavailable");
+ return;
+ }
+ $post_type = $post->post_type;
+ error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}");
+ $this->send_revalidation_request($post_type, $post_id, $post->post_name);
}
public function on_post_untrash($post_id) {
- $post = get_post($post_id);
- error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post->type}");
- $this->send_revalidation_request($post->type, $post_id, $post->post_name);
+ $post = get_post($post_id);
+ if (!$post instanceof \WP_Post) {
+ error_log("Next.js Revalidation: Post untrashed {$post_id} but WP_Post instance was unavailable");
+ return;
+ }
+ $post_type = $post->post_type;
+ error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}");
+ $this->send_revalidation_request($post_type, $post_id, $post->post_name);
}
public function on_post_delete($post_id, $post = null) {
- if (null === $post) {
- $post = get_post($post_id);
- }
-
- error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post->type}");
- $this->send_revalidation_request($post->type, $post_id, $post->post_name);
+ if (!$post instanceof \WP_Post) {
+ $post = get_post($post_id);
+ }
+
+ if (!$post instanceof \WP_Post) {
+ error_log("Next.js Revalidation: Post deleted {$post_id} but WP_Post instance was unavailable");
+ return;
+ }
+
+ $post_type = $post->post_type;
+ error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post_type}");
+ $this->send_revalidation_request($post_type, $post_id, $post->post_name);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public function on_post_trash($post_id) { | |
| $post_type = get_post_type($post_id); | |
| error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id); | |
| $post = get_post($post_id); | |
| error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post->type}"); | |
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | |
| } | |
| public function on_post_untrash($post_id) { | |
| $post_type = get_post_type($post_id); | |
| error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id); | |
| $post = get_post($post_id); | |
| error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post->type}"); | |
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | |
| } | |
| public function on_post_delete($post_id) { | |
| $post_type = get_post_type($post_id); | |
| if (!$post_type) { | |
| $post_type = 'unknown'; // Fallback if post type can't be determined | |
| public function on_post_delete($post_id, $post = null) { | |
| // Get the post if not provided | |
| if (null === $post) { | |
| $post = get_post($post_id); | |
| } | |
| error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id); | |
| error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post->type}"); | |
| $this->send_revalidation_request($post->type, $post_id, $post->post_name); | |
| } | |
| public function on_post_trash($post_id) { | |
| $post = get_post($post_id); | |
| if (!$post instanceof \WP_Post) { | |
| error_log("Next.js Revalidation: Post trashed {$post_id} but WP_Post instance was unavailable"); | |
| return; | |
| } | |
| $post_type = $post->post_type; | |
| error_log("Next.js Revalidation: Post trashed {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id, $post->post_name); | |
| } | |
| public function on_post_untrash($post_id) { | |
| $post = get_post($post_id); | |
| if (!$post instanceof \WP_Post) { | |
| error_log("Next.js Revalidation: Post untrashed {$post_id} but WP_Post instance was unavailable"); | |
| return; | |
| } | |
| $post_type = $post->post_type; | |
| error_log("Next.js Revalidation: Post untrashed {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id, $post->post_name); | |
| } | |
| public function on_post_delete($post_id, $post = null) { | |
| if (!$post instanceof \WP_Post) { | |
| $post = get_post($post_id); | |
| } | |
| if (!$post instanceof \WP_Post) { | |
| error_log("Next.js Revalidation: Post deleted {$post_id} but WP_Post instance was unavailable"); | |
| return; | |
| } | |
| $post_type = $post->post_type; | |
| error_log("Next.js Revalidation: Post deleted {$post_id} of type {$post_type}"); | |
| $this->send_revalidation_request($post_type, $post_id, $post->post_name); | |
| } |
🤖 Prompt for AI Agents
In plugin/next-revalidate/next-revalidate.php around lines 375 to 395, the
handlers assume $post exists and use the non-existent $post->type and
$post->post_name, which can be undefined or cause fatal errors when $post is
null; update each method to first ensure $post is a WP_Post (if null, call
get_post and if still falsy return early), then use $post->post_type (not
$post->type) and $post->post_name when calling send_revalidation_request; bail
out gracefully if $post is missing so no properties are accessed on a
non-object.
It seems working

Summary by CodeRabbit
New Features
Style
Documentation