-
Notifications
You must be signed in to change notification settings - Fork 214
Create WP-Revalidate Next.js Plugin! #51
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
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughThis update introduces a comprehensive integration between a WordPress site and a Next.js application to enable automated and manual cache revalidation. A new WordPress plugin, including documentation and configuration options, is added to send secure webhook requests to a new Next.js API endpoint when WordPress content changes. The Next.js API route authenticates these requests and triggers cache revalidation for relevant content types. Environment variable and documentation updates clarify setup and configuration. Several package dependencies are updated, and minor code organization improvements are made. Changes
Sequence Diagram(s)sequenceDiagram
participant WP as WordPress Plugin
participant Admin as WordPress Admin
participant NextAPI as Next.js /api/revalidate
participant Cache as Next.js Cache System
Admin->>WP: Change content (post, page, etc.)
WP->>NextAPI: POST /api/revalidate (with secret, contentType, contentId)
NextAPI->>NextAPI: Authenticate secret
NextAPI->>Cache: revalidateTag(s) based on contentType/contentId
NextAPI-->>WP: JSON response (success/failure)
WP-->>Admin: (Optional) Admin notification
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 2
🧹 Nitpick comments (12)
app/api/revalidate/route.ts (2)
28-62
: Consider refactoring content type handling for better maintainabilityThe current implementation uses a long if-else chain for determining content types and building tags. This could be refactored to be more maintainable and DRY.
- // Determine which tags to revalidate - const tagsToRevalidate = ["wordpress"]; - - // Add content type specific tag - if (contentType === "post") { - tagsToRevalidate.push("posts"); - if (contentId) { - tagsToRevalidate.push(`post-${contentId}`); - } - } else if (contentType === "page") { - tagsToRevalidate.push("pages"); - if (contentId) { - tagsToRevalidate.push(`page-${contentId}`); - } - } else if (contentType === "category") { - tagsToRevalidate.push("categories"); - if (contentId) { - tagsToRevalidate.push(`category-${contentId}`); - } - } else if (contentType === "tag") { - tagsToRevalidate.push("tags"); - if (contentId) { - tagsToRevalidate.push(`tag-${contentId}`); - } - } else if (contentType === "author" || contentType === "user") { - tagsToRevalidate.push("authors"); - if (contentId) { - tagsToRevalidate.push(`author-${contentId}`); - } - } else if (contentType === "media") { - tagsToRevalidate.push("media"); - if (contentId) { - tagsToRevalidate.push(`media-${contentId}`); - } - } + // Determine which tags to revalidate + const tagsToRevalidate = ["wordpress"]; + + // Mapping of content types to tag names + const contentTypeMap = { + post: "posts", + page: "pages", + category: "categories", + tag: "tags", + author: "authors", + user: "authors", + media: "media", + }; + + // Add content type specific tag + const tagName = contentTypeMap[contentType]; + if (tagName) { + tagsToRevalidate.push(tagName); + + // For authors, we need to handle both 'author' and 'user' content types + const idPrefix = contentType === "user" ? "author" : contentType; + + if (contentId) { + tagsToRevalidate.push(`${idPrefix}-${contentId}`); + } + }
1-81
: Consider adding rate limiting to prevent abuseThe webhook endpoint doesn't implement any rate limiting, which could lead to potential DoS vulnerability if excessive revalidation requests are sent.
You might want to add a simple rate limiting mechanism or integrate with a rate limiting middleware if available in your framework.
plugin/README.md (3)
3-3
: Minor grammatical improvement suggested-This plugin enables automatic revalidation of your Next.js site when content is changed in WordPress. +This plugin enables automatic revalidation of your Next.js site when content is changed on WordPress.🧰 Tools
🪛 LanguageTool
[grammar] ~3-~3: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...ur Next.js site when content is changed in WordPress. ## Installation 1. Upload the `next-...(IN_FACEBOOK)
34-34
: Minor grammatical improvement suggested-1. When content in WordPress is created, updated, or deleted, the plugin sends a webhook to your Next.js API route +1. When content on WordPress is created, updated, or deleted, the plugin sends a webhook to your Next.js API route🧰 Tools
🪛 LanguageTool
[grammar] ~34-~34: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ..." ``` ## How It Works 1. When content in WordPress is created, updated, or deleted, the pl...(IN_FACEBOOK)
1-54
: Consider adding documentation about customizing the API endpoint pathThe README assumes a fixed API endpoint path, but users might want to customize it.
Consider adding a section about how to customize the API endpoint path if your implementation supports it.
🧰 Tools
🪛 LanguageTool
[grammar] ~3-~3: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...ur Next.js site when content is changed in WordPress. ## Installation 1. Upload the `next-...(IN_FACEBOOK)
[grammar] ~34-~34: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ..." ``` ## How It Works 1. When content in WordPress is created, updated, or deleted, the pl...(IN_FACEBOOK)
plugin/next-revalidate/README.txt (2)
15-15
: Minor grammatical improvement suggested-Next.js Revalidation is a WordPress plugin designed to work with the `next-wp` Next.js starter template. It triggers revalidation of your Next.js site's cache whenever content is added, updated, or deleted in WordPress. +Next.js Revalidation is a WordPress plugin designed to work with the `next-wp` Next.js starter template. It triggers revalidation of your Next.js site's cache whenever content is added, updated, or deleted on WordPress.🧰 Tools
🪛 LanguageTool
[grammar] ~15-~15: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...r content is added, updated, or deleted in WordPress. The plugin sends webhooks to your Nex...(IN_FACEBOOK)
13-17
: Consider adding a link to the next-wp starter templateThe README mentions a
next-wp
starter template but doesn't provide a link to it.Add a link to the
next-wp
starter template to help users find it easily.🧰 Tools
🪛 LanguageTool
[grammar] ~15-~15: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...r content is added, updated, or deleted in WordPress. The plugin sends webhooks to your Nex...(IN_FACEBOOK)
plugin/next-revalidate/next-revalidate.php (5)
131-134
: Missingvalue
attribute prevents reliable checkbox handlingHTML checkboxes should carry an explicit
value
attribute (usually"1"
).
Without it, browsers default to"on"
, which is inconsistent with the integer stored in the DB after applying the previous fix and forces extra casting later on.-echo '<input type="checkbox" id="enable_notifications" name="' . $this->option_name . '[enable_notifications]" ' . checked($value, true, false) . ' />'; +echo '<input type="checkbox" id="enable_notifications" name="' . $this->option_name . '[enable_notifications]" value="1" ' . checked($value, 1, false) . ' />';
248-252
: No revalidation when a post is moved to the trash
deleted_post
only fires after the item is permanently removed.
For most editors the first destructive action is “Move to Trash”, which triggerstrashed_post
.
Consider hooking that action as well so cached pages disappear immediately when authors trash a post.add_action( 'trashed_post', array( $this, 'on_post_delete' ), 10 );
288-313
: Duplicated taxonomy mapping – streamline the switch
on_term_change()
already convertspost_tag
➜tag
andcategory
➜category
.
The extra cases inside the switch therefore never execute.
Removing them makes the intent clearer:- case 'post_tag': - $contentType = 'tag'; - break;
324-336
: Timeout of 5 s might be too aggressive for remote buildsLarge Next.js deployments (or cold-start serverless functions) can legitimately take > 5 s to answer.
A premature timeout will surface as a false negative to editors.If latency is a concern, bump the limit (e.g. 15 s) or make it configurable through the settings page.
- 'timeout' => 5, + 'timeout' => 15,
330-335
: Send secret viaAuthorization
header instead of custom headerUsing a standard
Authorization: Bearer <token>
header improves interoperability with frameworks and middleware that automatically process auth headers and can be inspected more easily by hosting platforms.- 'Content-Type' => 'application/json', - 'x-webhook-secret' => $this->options['webhook_secret'] + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $this->options['webhook_secret']
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
plugin/next-revalidate.zip
is excluded by!**/*.zip
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
.env.example
(1 hunks).gitignore
(1 hunks)README.md
(2 hunks)app/api/revalidate/route.ts
(1 hunks)app/layout.tsx
(1 hunks)package.json
(1 hunks)plugin/README.md
(1 hunks)plugin/next-revalidate/README.txt
(1 hunks)plugin/next-revalidate/index.php
(1 hunks)plugin/next-revalidate/next-revalidate.php
(1 hunks)
🧰 Additional context used
🪛 LanguageTool
plugin/README.md
[grammar] ~3-~3: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...ur Next.js site when content is changed in WordPress. ## Installation 1. Upload the `next-...
(IN_FACEBOOK)
[grammar] ~34-~34: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ..." ``` ## How It Works 1. When content in WordPress is created, updated, or deleted, the pl...
(IN_FACEBOOK)
plugin/next-revalidate/README.txt
[grammar] ~15-~15: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ...r content is added, updated, or deleted in WordPress. The plugin sends webhooks to your Nex...
(IN_FACEBOOK)
🔇 Additional comments (30)
.gitignore (1)
37-38
: Add CLAUDE.md to .gitignore
IncludingCLAUDE.md
prevents accidental commits of local or sensitive documentation notes.plugin/next-revalidate/index.php (1)
1-2
: Add index.php for security stub
The placeholder file will prevent directory listing within the plugin directory as per WordPress best practices..env.example (1)
3-6
: DocumentWORDPRESS_WEBHOOK_SECRET
usage
Good addition of guidance for generating a secure webhook secret without exposing real keys.app/layout.tsx (1)
3-14
: Reordered imports for clarity
The import statements have been logically grouped and reorganized without affecting functionality.README.md (3)
436-438
: Clarify plugin installation path
Updating instructions to use the/plugin
directory and offering zip options aligns with the actual file structure.
444-444
: Align env var instructions with.env.example
Ensuring users addWORDPRESS_WEBHOOK_SECRET
to their environment matches the plugin requirements.
454-463
: Add detailed Plugin Features section
The new feature list clearly communicates the capabilities of the WordPress revalidation plugin.package.json (4)
13-20
: Radix UI packages version update looks good!All Radix UI packages have been consistently updated to their latest compatible versions.
25-25
: Next.js version update supports new revalidation APIThe update from 15.2.3 to 15.3.1 aligns with the new revalidation functionality being added.
27-27
: Other dependency updates are consistentThe updates to query-string, react-hook-form, and zod look good and maintain project compatibility.
Also applies to: 30-30, 35-35
39-41
: TypeScript and dev dependencies updated consistentlyAll TypeScript-related dependencies and ESLint configurations have been properly updated to maintain compatibility.
Also applies to: 43-44, 47-47
app/api/revalidate/route.ts (5)
1-3
: Imports look goodThe necessary imports for Next.js cache revalidation and request/response handling are properly included.
4-16
: Webhook security implementation is robustGood implementation of webhook secret validation before processing the request. The error response appropriately returns a 401 status code without leaking sensitive information.
18-26
: Request validation is implemented correctlyThe code properly extracts and validates the required fields from the request body.
64-73
: Revalidation loop and response handling look goodThe implementation for revalidating all determined tags and returning the response is well-structured.
74-80
: Error handling is robustGood implementation of error handling with appropriate logging and error response.
plugin/README.md (7)
1-2
: Clear title and purposeThe README title is descriptive and clearly indicates the plugin's purpose.
5-11
: Installation instructions are clear and comprehensiveThe instructions provide multiple installation methods and proper steps for activation and configuration.
12-22
: Configuration section is well-structuredThe configuration steps for the WordPress plugin settings are clear and provide good security advice for generating a webhook secret.
23-30
: Next.js environment setup is clearly explainedThe instructions for setting up the environment variables in Next.js are concise and match the implementation in the API route.
32-38
: How It Works section is informativeThe explanation of the plugin's functionality is clear and accurately describes the interaction between WordPress and Next.js.
🧰 Tools
🪛 LanguageTool
[grammar] ~34-~34: The usual collocation for “WordPress” is “on”. Did you mean “on WordPress”?
Context: ..." ``` ## How It Works 1. When content in WordPress is created, updated, or deleted, the pl...(IN_FACEBOOK)
39-45
: Features section is comprehensiveThe features list provides a good overview of what the plugin offers.
46-53
: Troubleshooting section is helpfulThe troubleshooting tips address common issues users might face.
plugin/next-revalidate/README.txt (7)
1-10
: Plugin metadata is complete and follows WordPress standardsThe metadata section includes all required information for a WordPress plugin and follows the standard format.
11-11
: Clear plugin descriptionThe one-line description is concise and clearly communicates the plugin's purpose.
19-26
: Features section is comprehensiveThe key features list provides a good overview of the plugin's capabilities.
52-54
: Custom post type support is documentedGood to see explicit mention of custom post type support, which matches the plugin's capabilities.
56-63
: Changelog is clear and follows WordPress standardsThe changelog properly documents version changes and fixes.
64-67
: Upgrade notice is appropriateThe upgrade notice follows WordPress standards and provides relevant information.
49-50
:✅ Verification successful
Check API endpoint path consistency
The README mentions an API endpoint at
/api/revalidate
, but the implementation file is at/api/revalidate/route.ts
. Make sure this path discrepancy doesn't cause confusion for users.
🏁 Script executed:
#!/bin/bash # Verify the actual API endpoint path in the codebase # Check if the file exists if [ -f "app/api/revalidate/route.ts" ]; then echo "API route exists at app/api/revalidate/route.ts" # This is correct according to Next.js App Router conventions else echo "API route not found at expected location" fiLength of output: 145
API endpoint path is correct
Theapp/api/revalidate/route.ts
file exists and correctly serves the/api/revalidate
endpoint under Next.js App Router conventions. No changes to the README are necessary.
// Determine which tags to revalidate | ||
const tagsToRevalidate = ["wordpress"]; | ||
|
||
// Add content type specific tag | ||
if (contentType === "post") { | ||
tagsToRevalidate.push("posts"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`post-${contentId}`); | ||
} | ||
} else if (contentType === "page") { | ||
tagsToRevalidate.push("pages"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`page-${contentId}`); | ||
} | ||
} else if (contentType === "category") { | ||
tagsToRevalidate.push("categories"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`category-${contentId}`); | ||
} | ||
} else if (contentType === "tag") { | ||
tagsToRevalidate.push("tags"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`tag-${contentId}`); | ||
} | ||
} else if (contentType === "author" || contentType === "user") { | ||
tagsToRevalidate.push("authors"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`author-${contentId}`); | ||
} | ||
} else if (contentType === "media") { | ||
tagsToRevalidate.push("media"); | ||
if (contentId) { | ||
tagsToRevalidate.push(`media-${contentId}`); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Add support for custom post types
The current implementation doesn't explicitly handle custom post types which are mentioned in the plugin documentation. Consider adding support for them.
// Determine which tags to revalidate
const tagsToRevalidate = ["wordpress"];
// Add content type specific tag
if (contentType === "post") {
tagsToRevalidate.push("posts");
if (contentId) {
tagsToRevalidate.push(`post-${contentId}`);
}
} else if (contentType === "page") {
tagsToRevalidate.push("pages");
if (contentId) {
tagsToRevalidate.push(`page-${contentId}`);
}
} else if (contentType === "category") {
tagsToRevalidate.push("categories");
if (contentId) {
tagsToRevalidate.push(`category-${contentId}`);
}
} else if (contentType === "tag") {
tagsToRevalidate.push("tags");
if (contentId) {
tagsToRevalidate.push(`tag-${contentId}`);
}
} else if (contentType === "author" || contentType === "user") {
tagsToRevalidate.push("authors");
if (contentId) {
tagsToRevalidate.push(`author-${contentId}`);
}
} else if (contentType === "media") {
tagsToRevalidate.push("media");
if (contentId) {
tagsToRevalidate.push(`media-${contentId}`);
}
+ } else {
+ // Handle custom post types
+ tagsToRevalidate.push(`${contentType}s`);
+ if (contentId) {
+ tagsToRevalidate.push(`${contentType}-${contentId}`);
+ }
}
📝 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.
// Determine which tags to revalidate | |
const tagsToRevalidate = ["wordpress"]; | |
// Add content type specific tag | |
if (contentType === "post") { | |
tagsToRevalidate.push("posts"); | |
if (contentId) { | |
tagsToRevalidate.push(`post-${contentId}`); | |
} | |
} else if (contentType === "page") { | |
tagsToRevalidate.push("pages"); | |
if (contentId) { | |
tagsToRevalidate.push(`page-${contentId}`); | |
} | |
} else if (contentType === "category") { | |
tagsToRevalidate.push("categories"); | |
if (contentId) { | |
tagsToRevalidate.push(`category-${contentId}`); | |
} | |
} else if (contentType === "tag") { | |
tagsToRevalidate.push("tags"); | |
if (contentId) { | |
tagsToRevalidate.push(`tag-${contentId}`); | |
} | |
} else if (contentType === "author" || contentType === "user") { | |
tagsToRevalidate.push("authors"); | |
if (contentId) { | |
tagsToRevalidate.push(`author-${contentId}`); | |
} | |
} else if (contentType === "media") { | |
tagsToRevalidate.push("media"); | |
if (contentId) { | |
tagsToRevalidate.push(`media-${contentId}`); | |
} | |
} | |
// Determine which tags to revalidate | |
const tagsToRevalidate = ["wordpress"]; | |
// Add content type specific tag | |
if (contentType === "post") { | |
tagsToRevalidate.push("posts"); | |
if (contentId) { | |
tagsToRevalidate.push(`post-${contentId}`); | |
} | |
} else if (contentType === "page") { | |
tagsToRevalidate.push("pages"); | |
if (contentId) { | |
tagsToRevalidate.push(`page-${contentId}`); | |
} | |
} else if (contentType === "category") { | |
tagsToRevalidate.push("categories"); | |
if (contentId) { | |
tagsToRevalidate.push(`category-${contentId}`); | |
} | |
} else if (contentType === "tag") { | |
tagsToRevalidate.push("tags"); | |
if (contentId) { | |
tagsToRevalidate.push(`tag-${contentId}`); | |
} | |
} else if (contentType === "author" || contentType === "user") { | |
tagsToRevalidate.push("authors"); | |
if (contentId) { | |
tagsToRevalidate.push(`author-${contentId}`); | |
} | |
} else if (contentType === "media") { | |
tagsToRevalidate.push("media"); | |
if (contentId) { | |
tagsToRevalidate.push(`media-${contentId}`); | |
} | |
} else { | |
// Handle custom post types | |
tagsToRevalidate.push(`${contentType}s`); | |
if (contentId) { | |
tagsToRevalidate.push(`${contentType}-${contentId}`); | |
} | |
} |
public function sanitize_settings($input) { | ||
$new_input = array(); | ||
|
||
if (isset($input['next_url'])) { | ||
// Normalize and sanitize Next.js site URL (remove trailing slash) | ||
$url = rtrim(trim($input['next_url']), '/'); | ||
$new_input['next_url'] = esc_url_raw($url); | ||
} | ||
|
||
if(isset($input['webhook_secret'])) { | ||
$new_input['webhook_secret'] = sanitize_text_field($input['webhook_secret']); | ||
} | ||
|
||
if(isset($input['enable_notifications'])) { | ||
$new_input['enable_notifications'] = (bool)$input['enable_notifications']; | ||
} | ||
|
||
return $new_input; | ||
} |
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.
Checkbox can never be switched off – fix enable_notifications
sanitisation
enable_notifications
is only written when the checkbox is present in the submitted payload, i.e. when it is ticked.
If the user un-ticks the box the field is omitted and the previous value is silently kept, so the option can never be disabled once enabled.
- if(isset($input['enable_notifications'])) {
- $new_input['enable_notifications'] = (bool)$input['enable_notifications'];
- }
+ // Always set a value so the option can be toggled off as well.
+ // Treat any truthy input as enabled, everything else as disabled.
+ $new_input['enable_notifications'] = !empty($input['enable_notifications']) ? 1 : 0;
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: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (1)
package.json
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
package.json
[error] 48-48: expected ,
but instead found "typescript"
Remove "typescript"
(parse)
[error] 47-47: The key typescript was already declared.
This where a duplicated key was declared again.
If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.
(lint/suspicious/noDuplicateObjectKeys)
🔇 Additional comments (5)
package.json (5)
13-19
: Approve Radix UI package version bumps.The updates to the
@radix-ui
dependencies look consistent and straightforward. Ensure you test relevant UI components to confirm there are no breaking changes.
25-25
: Verify Next.js version compatibility.You've bumped
next
to^15.3.1
. Please ensure that any custom Next.js plugin code (e.g., the WP-Revalidate integration) works as expected with this minor upgrade.
27-27
: Approvequery-string
version bump.The upgrade to
^9.1.2
is fine—no breaking changes are expected.
30-30
: Approvereact-hook-form
version bump.Moving to
^7.56.2
aligns with your other form-related updates; be sure to run existing form validation tests.
35-35
: Approvezod
version bump.Bumping to
^3.24.4
is safe; confirm schema validations still pass.
"@types/node": "^20.17.32", | ||
"@types/react": "^18.3.20", | ||
"@types/react-dom": "^18.3.6", | ||
"@types/react-dom": "^18.3.7", | ||
"autoprefixer": "^10.4.21", | ||
"eslint": "^9.24.0", | ||
"eslint-config-next": "^15.3.0", | ||
"eslint": "^9.26.0", | ||
"eslint-config-next": "^15.3.1", | ||
"postcss": "^8.5.3", | ||
"tailwindcss": "^3.4.17", | ||
"typescript": "^5.8.3" | ||
"typescript": "^5.8.3" |
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.
Remove duplicated typescript
entry.
There's a duplicate "typescript": "^5.8.3"
key in devDependencies
. JSON objects ignore all but the last occurrence, so please remove the redundant entry to avoid confusion.
@@ devDependencies
- "typescript": "^5.8.3"
📝 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.
"@types/node": "^20.17.32", | |
"@types/react": "^18.3.20", | |
"@types/react-dom": "^18.3.6", | |
"@types/react-dom": "^18.3.7", | |
"autoprefixer": "^10.4.21", | |
"eslint": "^9.24.0", | |
"eslint-config-next": "^15.3.0", | |
"eslint": "^9.26.0", | |
"eslint-config-next": "^15.3.1", | |
"postcss": "^8.5.3", | |
"tailwindcss": "^3.4.17", | |
"typescript": "^5.8.3" | |
"typescript": "^5.8.3" | |
"@types/node": "^20.17.32", | |
"@types/react": "^18.3.20", | |
"@types/react-dom": "^18.3.7", | |
"autoprefixer": "^10.4.21", | |
"eslint": "^9.26.0", | |
"eslint-config-next": "^15.3.1", | |
"postcss": "^8.5.3", | |
"tailwindcss": "^3.4.17", | |
"typescript": "^5.8.3" |
🧰 Tools
🪛 Biome (1.9.4)
[error] 48-48: expected ,
but instead found "typescript"
Remove "typescript"
(parse)
[error] 47-47: The key typescript was already declared.
This where a duplicated key was declared again.
If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.
(lint/suspicious/noDuplicateObjectKeys)
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores
Style