From 9bfd988ca6c6c40329c884a142fb9702a1bf865f Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 13 Mar 2024 07:39:35 -0700 Subject: [PATCH] feat(nuxt): enable islands if server pages/components present (#26223) --- docs/2.guide/2.directory-structure/1.pages.md | 4 ---- docs/3.api/1.components/8.nuxt-island.md | 4 ---- packages/nuxt/src/components/module.ts | 3 +++ packages/nuxt/src/core/nitro.ts | 1 - packages/nuxt/src/core/nuxt.ts | 2 +- packages/nuxt/src/core/runtime/nitro/renderer.ts | 6 +++--- packages/nuxt/src/core/templates.ts | 5 ++++- packages/nuxt/src/pages/module.ts | 4 ++++ packages/schema/src/config/experimental.ts | 7 +++++-- 9 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/2.guide/2.directory-structure/1.pages.md b/docs/2.guide/2.directory-structure/1.pages.md index 851018ba3e..bef7ad02d2 100644 --- a/docs/2.guide/2.directory-structure/1.pages.md +++ b/docs/2.guide/2.directory-structure/1.pages.md @@ -365,10 +365,6 @@ You can define a page as [client only](/docs/guide/directory-structure/component You can define a page as [server only](/docs/guide/directory-structure/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle. -::note -You will also need to enable `experimental.componentIslands` in order to make this possible. -:: - ## Custom Routing As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways. diff --git a/docs/3.api/1.components/8.nuxt-island.md b/docs/3.api/1.components/8.nuxt-island.md index 013cfb94c4..cd715534f2 100644 --- a/docs/3.api/1.components/8.nuxt-island.md +++ b/docs/3.api/1.components/8.nuxt-island.md @@ -12,10 +12,6 @@ When rendering an island component, the content of the island component is stati Changing the island component props triggers a refetch of the island component to re-render it again. -::read-more{to="/docs/guide/going-further/experimental-features#componentislands" icon="i-ph-star-duotone"} -This component is experimental and in order to use it you must enable the `experimental.componentIslands` option in your `nuxt.config`. -:: - ::note Global styles of your application are sent with the response. :: diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts index 7be0c98d61..f38acfe253 100644 --- a/packages/nuxt/src/components/module.ts +++ b/packages/nuxt/src/components/module.ts @@ -176,6 +176,9 @@ export default defineNuxtModule({ chunkName: 'components/' + component.kebabName }) } + if (component.mode === 'server' && !nuxt.options.ssr) { + logger.warn(`Using server components with \`ssr: false\` is not supported with auto-detected component islands. If you need to use server component \`${component.pascalName}\`, set \`experimental.componentIslands\` to \`true\`.`) + } } context.components = newComponents app.components = newComponents diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 114ac24338..85e415ce79 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -217,7 +217,6 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) { 'process.env.NUXT_NO_SCRIPTS': !!nuxt.options.features.noScripts && !nuxt.options.dev, 'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles, 'process.env.NUXT_JSON_PAYLOADS': !!nuxt.options.experimental.renderJsonPayloads, - 'process.env.NUXT_COMPONENT_ISLANDS': !!nuxt.options.experimental.componentIslands, 'process.env.NUXT_ASYNC_CONTEXT': !!nuxt.options.experimental.asyncContext, 'process.env.NUXT_SHARED_DATA': !!nuxt.options.experimental.sharedPrerenderData, 'process.dev': nuxt.options.dev, diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 1706349cb4..8cc2a95794 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -314,7 +314,7 @@ async function initNuxt (nuxt: Nuxt) { filePath: resolve(nuxt.options.appDir, 'components/nuxt-island') }) - if (!nuxt.options.ssr) { + if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== 'auto') { nuxt.options.ssr = true nuxt.options.nitro.routeRules ||= {} nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false }) diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 7840c36899..cde9312494 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -29,7 +29,7 @@ import unheadPlugins from '#internal/unhead-plugins.mjs' // eslint-disable-next-line import/no-restricted-paths import type { NuxtPayload, NuxtSSRContext } from '#app' // @ts-expect-error virtual file -import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag } from '#internal/nuxt.config.mjs' +import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs' // @ts-expect-error virtual file import { buildAssetsURL, publicAssetsURL } from '#paths' @@ -263,7 +263,7 @@ export default defineRenderHandler(async (event): Promise p.mode === 'server') || ctx.app.components?.some(c => c.mode === 'server') + ) return [ ...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`), `export const renderJsonPayloads = ${!!ctx.nuxt.options.experimental.renderJsonPayloads}`, - `export const componentIslands = ${!!ctx.nuxt.options.experimental.componentIslands}`, + `export const componentIslands = ${shouldEnableComponentIslands}`, `export const payloadExtraction = ${!!ctx.nuxt.options.experimental.payloadExtraction}`, `export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`, `export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`, diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index 7068817153..9eb1eefaa2 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -79,6 +79,10 @@ export default defineNuxtModule({ nuxt.hook('app:templates', async (app) => { app.pages = await resolvePagesRoutes() await nuxt.callHook('pages:extend', app.pages) + + if (!nuxt.options.ssr && app.pages.some(p => p.mode === 'server')) { + logger.warn('Using server pages with `ssr: false` is not supported with auto-detected component islands. Set `experimental.componentIslands` to `true`.') + } }) // Restart Nuxt when pages dir is added or removed diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 4c2fbda9cf..04a4748a31 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -172,7 +172,10 @@ export default defineUntypedSchema({ /** * Experimental component islands support with and .island.vue files. - * @type {true | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false} + * + * By default it is set to 'auto', which means it will be enabled only when there are islands, + * server components or server pages in your app. + * @type {true | 'auto' | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false} */ componentIslands: { $resolve: (val) => { @@ -182,7 +185,7 @@ export default defineUntypedSchema({ if (val === 'local') { return true } - return val ?? false + return val ?? 'auto' } },