Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineNuxtModule, addPlugin, createResolver, addBuildPlugin, addComponent, addServerPlugin, addServerHandler } from '@nuxt/kit'
import { defineNuxtModule, addPlugin, createResolver, addBuildPlugin, addComponent, addServerPlugin, addServerHandler, addImports, addImportsSources } from '@nuxt/kit'

Check failure on line 1 in src/module.ts

View workflow job for this annotation

GitHub Actions / ci

'addImportsSources' is defined but never used. Allowed unused vars must match /^_/u

Check failure on line 1 in src/module.ts

View workflow job for this annotation

GitHub Actions / ci

'addImports' is defined but never used. Allowed unused vars must match /^_/u
import { HYDRATION_ROUTE, HYDRATION_SSE_ROUTE } from './runtime/hydration/utils'
import { setupDevToolsUI } from './devtools'
import { InjectHydrationPlugin } from './plugins/hydration'
Expand Down Expand Up @@ -53,6 +53,10 @@
addPlugin(resolver.resolve('./runtime/third-party-scripts/plugin.client'))
addServerPlugin(resolver.resolve('./runtime/third-party-scripts/nitro.plugin'))

// prerender
addServerPlugin(resolver.resolve('./runtime/prerender/nitro.plugin'))
addPlugin(resolver.resolve('./runtime/prerender/plugin.server'))

nuxt.hook('prepare:types', ({ references }) => {
references.push({
types: resolver.resolve('./runtime/types.d.ts'),
Expand Down
20 changes: 20 additions & 0 deletions src/runtime/prerender/nitro.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { NitroAppPlugin } from 'nitropack'

export default <NitroAppPlugin> function nitroHintsPlugin(nitroApp) {
nitroApp.hooks.hook('render:before', ({ event }) => {
event.context.shouldPrerender = true
})

nitroApp.hooks.hook('render:html', (htmlContext, { event }) => {
if (event.context.shouldPrerender === false) {
htmlContext.bodyAppend.push(
`<script>window.__NUXT_HINTS_SHOULD_PRERENDER__ = false</script>`,
)
}
else {
htmlContext.bodyAppend.push(
`<script>window.__NUXT_HINTS_SHOULD_PRERENDER__ = true</script>`,
)
}
})
}
43 changes: 43 additions & 0 deletions src/runtime/prerender/plugin.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { defineNuxtPlugin } from '#imports'
import { getStackTraceLines } from './utils'

export default defineNuxtPlugin({
name: 'hints:prerender-detection',
setup(nuxtApp) {
const event = nuxtApp.ssrContext!.event
const originalSsrContext = nuxtApp.ssrContext!

let watching = true
// Access to any property on ssrContext will mark the page as non-prerenderable
nuxtApp.ssrContext = new Proxy(originalSsrContext, {
get(target, prop, receiver) {
if (watching && isUserLandCode()) {
// Mark as non-prerenderable
// we only want to do this when user-land code is being executed
// to avoid false positives from internal framework code
// it's better to be slightly overzealous here than miss actual user code
event.context.shouldPrerender = false
}
return Reflect.get(target, prop, receiver)
},
})

nuxtApp.hook('app:rendered', () => {
watching = false
})
},
order: -100000,
})

/**
* Determine if the current execution context is user-land code
* by analyzing the stack trace.
* Should ignore the first line as the fn call is this function itself.
*/
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
}
13 changes: 13 additions & 0 deletions src/runtime/prerender/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 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.
*/
export function getStackTraceLines(): string[] {
const stackObject: { stack: string } = {} as { stack: string }
Error.captureStackTrace(stackObject)

return stackObject.stack
.split('\n')
.slice(1)
.map(line => line.trim())
}
Loading