Skip to content

Conversation

@huang-julien
Copy link
Member

@huang-julien huang-julien commented Jan 2, 2026

πŸ”— Linked issue

resolve #80

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

This PR adds prerender hints based on ssrContext properties access. For now, it's better to not set any filters to avoid false positive.

TODO

  • Wrap useEvent/nuxtApp.ssrContext composables to set the value to false

Next step:

  • Store data server side like hdyration hints. Theses data can be used to tell the user which route can be prerendered
  • devtools UI

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nuxt/hints/@nuxt/hints@184

commit: 279939e

@huang-julien huang-julien marked this pull request as ready for review January 29, 2026 21:46
@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

πŸ“ Walkthrough

Walkthrough

This pull request introduces prerender hint detection and flagging functionality to the Nuxt module. It adds a transform plugin that rewrites import.meta.server checks with SSR context guards, a Nitro plugin that sets prerender hints via window flags, and a server-side plugin that detects when user code accesses SSR context to mark pages as non-prerenderable. Stack trace utilities support identifying user-land code execution. The changes include new runtime files, a new demo playground component, module integration, and minor formatting updates to existing components.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Title check βœ… Passed The title 'feat: prerender hint based on ssrContext access' accurately describes the main feature added in the changesetβ€”detecting prerender eligibility via ssrContext access tracking.
Linked Issues check βœ… Passed The PR addresses issue #80 by implementing prerender hint detection based on ssrContext access, identifying pages without dynamic content as candidates for prerendering.
Out of Scope Changes check βœ… Passed All changes are within scope: prerender hint detection (nitro plugin, server plugin, transform plugin), utility functions, module integration, and a demo componentβ€”all directly support the prerender detection objective.
Docstring Coverage βœ… Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check βœ… Passed The PR description clearly explains the feature being added: prerender hints based on ssrContext access, with concrete implementation details and next steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/prerender_hint

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • ✨ 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

πŸ€– Fix all issues with AI agents
In `@src/plugins/prerender.ts`:
- Around line 21-22: The naive use of s.replaceAll('import.meta.server',
'(__tryUseNuxtApp()?.ssrContext && import.meta.server)') can match inside
strings/comments and changes the return type; update the transform to only
target real AST occurrences of import.meta.server (e.g., parse the source and
replace MemberExpression nodes) or use a regex that excludes string/comment
contexts, and ensure the replacement coerces to a boolean (e.g., wrap the
expression with !!) so code checking === true keeps working; reference the
s.replaceAll call, the import.meta.server node, and the
__tryUseNuxtApp()?.ssrContext expression when making the change.

In `@src/runtime/prerender/plugin.server.ts`:
- Around line 38-44: The function isUserLandCode misclassifies an undefined
stack line as user-land; modify isUserLandCode so it first checks that the
selected line exists (from getStackTraceLines and the local variable line) and
returns false if line is undefined or empty, then only evaluate
!line.includes('node_modules') && !line.includes('node:internal') to determine
user-land; locate function isUserLandCode and add an explicit existence check
for line before applying includes.

In `@src/runtime/prerender/utils.ts`:
- Around line 1-4: Fix the typo in the top comment block that begins with "Get
all stacktrace lines without the current file" by replacing "hiints" with
"hints" so the comment reads correctly; update that docstring/comment text in
the utils file (the block containing "Don't use in build files if we ever goes
into build mode nuxt hiints. Thank you.") to use "hints".
🧹 Nitpick comments (3)
src/plugins/prerender.ts (1)

15-18: Redundant node_modules check in handler.

The filter configuration at lines 11-13 already excludes node_modules via the id.exclude pattern. The additional check at line 16 is redundant but harmless.

🧹 Simplify by removing redundant check
       handler(code, id) {
-        if (id.includes('node_modules') || !code.includes('import.meta.server')) {
+        if (!code.includes('import.meta.server')) {
           return null
         }
src/runtime/prerender/utils.ts (1)

5-13: Consider adding a defensive check for Error.captureStackTrace.

Error.captureStackTrace is a V8-specific API. While this server-side code will run on Node.js (V8-based), adding a fallback ensures robustness if the runtime environment changes or for edge cases.

πŸ›‘οΈ Suggested defensive implementation
 export function getStackTraceLines(): string[] {
+  if (typeof Error.captureStackTrace !== 'function') {
+    // Fallback for non-V8 environments
+    return new Error().stack?.split('\n').slice(1).map(line => line.trim()) ?? []
+  }
   const stackObject: { stack: string } = {} as { stack: string }
   Error.captureStackTrace(stackObject)

   return stackObject.stack
     .split('\n')
     .slice(1)
     .map(line => line.trim())
 }
src/runtime/prerender/plugin.server.ts (1)

13-24: Consider adding property descriptor options for robustness.

The Object.defineProperty call doesn't specify configurable or enumerable, which default to false. This prevents other code from modifying or iterating over ssrContext. If this is intentional (to protect the interception), consider adding a comment. Otherwise, explicitly set these options.

πŸ’‘ Suggested improvement
     Object.defineProperty(nuxtApp, 'ssrContext', {
+      configurable: true,
+      enumerable: true,
       get() {
         if (watching && isUserLandCode()) {
           event.context.shouldPrerender = false
         }
         return ssrContext
       },
     })

Comment on lines 21 to 22
// ssrContext access will trigger prerender flagging
s.replaceAll('import.meta.server', '(__tryUseNuxtApp()?.ssrContext && import.meta.server)')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Naive string replacement may cause unintended matches.

Using replaceAll on raw source code can match occurrences inside string literals, template literals, or comments (e.g., "import.meta.server" or // import.meta.server). Consider using an AST-based approach or a more precise regex that excludes these contexts.

Additionally, the replacement changes the expression's return type: import.meta.server returns a strict boolean, but (__tryUseNuxtApp()?.ssrContext && import.meta.server) can return the ssrContext object (truthy) or undefined (falsy) instead of true/false. This could break code that uses strict equality checks like === true.

πŸ’‘ Suggested improvement for stricter replacement

Consider using a regex that better targets actual code usage, or use an AST parser like acorn or recast for precise transformations:

-        s.replaceAll('import.meta.server', '(__tryUseNuxtApp()?.ssrContext && import.meta.server)')
+        // Use regex to avoid matching inside strings/comments (basic approach)
+        s.replace(/\bimport\.meta\.server\b/g, '(!!(__tryUseNuxtApp()?.ssrContext) && import.meta.server)')

The !! coercion ensures a boolean result is maintained.

πŸ“ 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.

Suggested change
// ssrContext access will trigger prerender flagging
s.replaceAll('import.meta.server', '(__tryUseNuxtApp()?.ssrContext && import.meta.server)')
// ssrContext access will trigger prerender flagging
// Use regex to avoid matching inside strings/comments (basic approach)
s.replace(/\bimport\.meta\.server\b/g, '(!!(__tryUseNuxtApp()?.ssrContext) && import.meta.server)')
πŸ€– Prompt for AI Agents
In `@src/plugins/prerender.ts` around lines 21 - 22, The naive use of
s.replaceAll('import.meta.server', '(__tryUseNuxtApp()?.ssrContext &&
import.meta.server)') can match inside strings/comments and changes the return
type; update the transform to only target real AST occurrences of
import.meta.server (e.g., parse the source and replace MemberExpression nodes)
or use a regex that excludes string/comment contexts, and ensure the replacement
coerces to a boolean (e.g., wrap the expression with !!) so code checking ===
true keeps working; reference the s.replaceAll call, the import.meta.server
node, and the __tryUseNuxtApp()?.ssrContext expression when making the change.

Comment on lines +38 to +44
function isUserLandCode(offset: number = 1): boolean {
const stack = getStackTraceLines()
const lines = stack.slice(2)
const line = lines[offset]
const isUserLand = !line?.includes('node_modules') && !line?.includes('node:internal')
return isUserLand
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bug: undefined line is incorrectly classified as user-land code.

If line is undefined (e.g., when the stack is shorter than expected), the expression !line?.includes('node_modules') && !line?.includes('node:internal') evaluates to !undefined && !undefined = true && true = true, incorrectly marking the code as user-land.

πŸ› Proposed fix
 function isUserLandCode(offset: number = 1): boolean {
   const stack = getStackTraceLines()
   const lines = stack.slice(2)
   const line = lines[offset]
+  if (!line) {
+    return false
+  }
-  const isUserLand = !line?.includes('node_modules') && !line?.includes('node:internal')
+  const isUserLand = !line.includes('node_modules') && !line.includes('node:internal')
   return isUserLand
 }
πŸ“ 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.

Suggested change
function isUserLandCode(offset: number = 1): boolean {
const stack = getStackTraceLines()
const lines = stack.slice(2)
const line = lines[offset]
const isUserLand = !line?.includes('node_modules') && !line?.includes('node:internal')
return isUserLand
}
function isUserLandCode(offset: number = 1): boolean {
const stack = getStackTraceLines()
const lines = stack.slice(2)
const line = lines[offset]
if (!line) {
return false
}
const isUserLand = !line.includes('node_modules') && !line.includes('node:internal')
return isUserLand
}
πŸ€– Prompt for AI Agents
In `@src/runtime/prerender/plugin.server.ts` around lines 38 - 44, The function
isUserLandCode misclassifies an undefined stack line as user-land; modify
isUserLandCode so it first checks that the selected line exists (from
getStackTraceLines and the local variable line) and returns false if line is
undefined or empty, then only evaluate !line.includes('node_modules') &&
!line.includes('node:internal') to determine user-land; locate function
isUserLandCode and add an explicit existence check for line before applying
includes.

Comment on lines +1 to +4
/**
* Get all stacktrace lines without the current file
* Don't use in build files if we ever goes into build mode nuxt hiints. Thank you.
*/
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

Typo in comment.

"hiints" should be "hints".

 /**
  * Get all stacktrace lines without the current file
- * Don't use in build files if we ever goes into build mode nuxt hiints. Thank you.
+ * Don't use in build files if we ever go into build mode nuxt hints. Thank you.
  */
πŸ“ 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.

Suggested change
/**
* Get all stacktrace lines without the current file
* Don't use in build files if we ever goes into build mode nuxt hiints. Thank you.
*/
/**
* Get all stacktrace lines without the current file
* Don't use in build files if we ever go into build mode nuxt hints. Thank you.
*/
πŸ€– Prompt for AI Agents
In `@src/runtime/prerender/utils.ts` around lines 1 - 4, Fix the typo in the top
comment block that begins with "Get all stacktrace lines without the current
file" by replacing "hiints" with "hints" so the comment reads correctly; update
that docstring/comment text in the utils file (the block containing "Don't use
in build files if we ever goes into build mode nuxt hiints. Thank you.") to use
"hints".


const ssrContext = nuxtApp.ssrContext
// Access to any property on ssrContext will mark the page as non-prerenderable
Object.defineProperty(nuxtApp, 'ssrContext', {
Copy link
Member

Choose a reason for hiding this comment

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

I think it depends what you're accessing. ssrContext.event.url would be fine, for example.

Copy link
Member Author

@huang-julien huang-julien Feb 3, 2026

Choose a reason for hiding this comment

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

Is there any other property you think it is fine ? or should we check request headers and event.context acess only ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

detect and recommend static/prerendering

3 participants