Skip to content
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

feat(schema): update experimental options for v4 #27132

Merged
merged 17 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 68 additions & 3 deletions docs/1.getting-started/12.upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ First, opt in to the nightly release channel [following these steps](/docs/guide

Then you can set your `compatibilityVersion` to match Nuxt 4 behavior:

```ts
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
future: {
compatibilityVersion: 4,
Expand All @@ -46,6 +46,7 @@ export default defineNuxtConfig({
// app: 'app'
// },
// experimental: {
// sharedPrerenderData: false,
// compileTemplate: true,
// templateUtils: true,
// relativeWatchPaths: true,
Expand Down Expand Up @@ -136,7 +137,7 @@ nuxt.config.ts

However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) You can also force a v3 folder structure with the following configuration:

```ts
```ts [nuxt.config.ts]
export default defineNuxtConfig({
// This reverts the new srcDir default from `app` back to your root directory
srcDir: '.',
Expand All @@ -147,6 +148,47 @@ export default defineNuxtConfig({
})
```

#### Shared Prerender Data

🚦 **Impact Level**: Medium

##### What Changed

We enabled a previously experimental feature to share data from `useAsyncData` and `useFetch` calls, across different pages. See [original PR](https://github.com/nuxt/nuxt/pull/24894).

##### Reasons for Change

This feature automatically shares payload _data_ between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages.

For example, if your site requires a `useFetch` call for every page (for example, to get navigation data for a menu, or site settings from a CMS), this data would only be fetched once when prerendering the first page that uses it, and then cached for use when prerendering other pages.

##### Migration Steps

Make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.)

```ts [app/pages/test/[slug\\].vue]
// This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference
// to the data fetched, but Nuxt can't know that because it's not reflected in the key.
const route = useRoute()
const { data } = await useAsyncData(async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
// Instead, you should use a key that uniquely identifies the data fetched.
const { data } = await useAsyncData(route.params.slug, async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
```

Alternatively, you can disable this feature with:

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
sharedPrerenderData: false
}
})
```

#### Shallow Data Reactivity in `useAsyncData` and `useFetch`

🚦 **Impact Level**: Minimal
Expand All @@ -171,7 +213,7 @@ In most cases, no migration steps are required, but if you rely on the reactivit
+ const { data } = useFetch('/api/test', { deep: true })
```
1. You can change the default behavior on a project-wide basis (not recommended):
```ts
```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
defaults: {
Expand Down Expand Up @@ -270,6 +312,29 @@ const importSources = (sources: string | string[], { lazy = false } = {}) => {
const importName = genSafeVariableName
```

#### Removal of Experimental Features

🚦 **Impact Level**: Minimal

##### What Changed

Four experimental features are no longer configurable in Nuxt 4:

* `treeshakeClientOnly` will be `true` (default since v3.0)
* `configSchema` will be `true` (default since v3.3)
* `polyfillVueUseHead` will be `false` (default since v3.4)
* `respectNoSSRHeader` will be `false` (default since v3.4)

##### Reasons for Change

These options have been set to their current values for some time and we do not have a reason to believe that they need to remain configurable.

##### Migration Steps

* `polyfillVueUseHead` is implementable in user-land with [this plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11)

* `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9)

## Nuxt 2 vs Nuxt 3

In the table below, there is a quick comparison between 3 versions of Nuxt:
Expand Down
10 changes: 10 additions & 0 deletions docs/2.guide/3.going-further/1.experimental-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,16 @@ This option allows exposing some route metadata defined in `definePageMeta` at b

This only works with static or strings/arrays rather than variables or conditional assignment. See [original issue](https://github.com/nuxt/nuxt/issues/24770) for more information and context.

<!-- You can disable this feature if it causes issues in your project.

```ts twoslash [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
scanPageMeta: false
}
})
``` -->

## cookieStore

Enables CookieStore support to listen for cookie updates (if supported by the browser) and refresh `useCookie` ref values.
Expand Down
54 changes: 48 additions & 6 deletions packages/schema/src/config/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,18 @@ export default defineUntypedSchema({
/**
* Tree shakes contents of client-only components from server bundle.
* @see [Nuxt PR #5750](https://github.com/nuxt/framework/pull/5750)
* @deprecated This option will no longer be configurable in Nuxt v4
*/
treeshakeClientOnly: true,
treeshakeClientOnly: {
async $resolve (val, get) {
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
if (isV4 && val === false) {
console.warn('Enabling `experimental.treeshakeClientOnly` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
return true
}
return val ?? true
},
},

/**
* Emit `app:chunkError` hook when there is an error loading vite/webpack
Expand Down Expand Up @@ -246,19 +256,51 @@ export default defineUntypedSchema({
/**
* Config schema support
* @see [Nuxt Issue #15592](https://github.com/nuxt/nuxt/issues/15592)
* @deprecated This option will no longer be configurable in Nuxt v4
*/
configSchema: true,
configSchema: {
async $resolve (val, get) {
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
if (isV4 && val === false) {
console.warn('Enabling `experimental.configSchema` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
return true
}
return val ?? true
},
},

/**
* Whether or not to add a compatibility layer for modules, plugins or user code relying on the old
* `@vueuse/head` API.
*
* This can be disabled for most Nuxt sites to reduce the client-side bundle by ~0.5kb.
* This is disabled to reduce the client-side bundle by ~0.5kb.
* @deprecated This feature will be removed in Nuxt v4.
*/
polyfillVueUseHead: false,
polyfillVueUseHead: {
async $resolve (val, get) {
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
if (isV4 && val === true) {
console.warn('Disabling `experimental.polyfillVueUseHead` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
return false
}
return val ?? false
},
},

/** Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header. */
respectNoSSRHeader: false,
/**
* Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header.
* @deprecated This feature will be removed in Nuxt v4.
*/
respectNoSSRHeader: {
async $resolve (val, get) {
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
if (isV4 && val === true) {
console.warn('Disabling `experimental.respectNoSSRHeader` in v4 compatibility mode as it will no longer be configurable in Nuxt v4.')
return false
}
return val ?? false
},
},

/** Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. */
localLayerAliases: true,
Expand Down
23 changes: 14 additions & 9 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { NuxtIslandResponse } from '#app'

const isWebpack = process.env.TEST_BUILDER === 'webpack'
const isTestingAppManifest = process.env.TEST_MANIFEST !== 'manifest-off'
const isV4 = process.env.TEST_V4 === 'true'

await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
Expand Down Expand Up @@ -57,7 +58,14 @@ describe('server api', () => {

describe('route rules', () => {
it('should enable spa mode', async () => {
const { script, attrs } = parseData(await $fetch('/route-rules/spa'))
const headHtml = await $fetch('/route-rules/spa')

// SPA should render appHead tags
expect(headHtml).toContain('<meta name="description" content="Nuxt Fixture">')
expect(headHtml).toContain('<meta charset="utf-8">')
expect(headHtml).toContain('<meta name="viewport" content="width=1024, initial-scale=1">')

const { script, attrs } = parseData(headHtml)
expect(script.serverRendered).toEqual(false)
if (isRenderingJson) {
expect(attrs['data-ssr']).toEqual('false')
Expand Down Expand Up @@ -876,15 +884,15 @@ describe('head tags', () => {
expect(headHtml).toContain('<meta content="0;javascript:alert(1)">')
})

it('SPA should render appHead tags', async () => {
it.skipIf(isV4)('SPA should render appHead tags', async () => {
const headHtml = await $fetch('/head', { headers: { 'x-nuxt-no-ssr': '1' } })

expect(headHtml).toContain('<meta name="description" content="Nuxt Fixture">')
expect(headHtml).toContain('<meta charset="utf-8">')
expect(headHtml).toContain('<meta name="viewport" content="width=1024, initial-scale=1">')
})

it('legacy vueuse/head works', async () => {
it.skipIf(isV4)('legacy vueuse/head works', async () => {
const headHtml = await $fetch('/vueuse-head')
expect(headHtml).toContain('<title>using provides usehead and updateDOM - VueUse head polyfill test</title>')
})
Expand Down Expand Up @@ -2187,7 +2195,6 @@ describe('component islands', () => {
result.html = result.html.replace(/ data-island-uid="([^"]*)"/g, '')

if (isDev()) {
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates'))
const fixtureDir = normalize(fileURLToPath(new URL('./fixtures/basic', import.meta.url)))
for (const link of result.head.link) {
link.href = link.href.replace(fixtureDir, '/<rootDir>').replaceAll('//', '/')
Expand All @@ -2210,14 +2217,12 @@ describe('component islands', () => {
}
`)
} else if (isDev() && !isWebpack) {
// TODO: resolve dev bug triggered by earlier fetch of /vueuse-head page
// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/runtime/nitro/renderer.ts#L139
result.head.link = result.head.link.filter(h => !h.href.includes('SharedComponent'))
expect(result.head).toMatchInlineSnapshot(`
{
"link": [
{
"href": "/_nuxt/components/SharedComponent.vue?vue&type=style&index=0&scoped=3ee84738&lang.css",
"key": "island-link",
"rel": "stylesheet",
},
{
"href": "/_nuxt/components/islands/PureComponent.vue?vue&type=style&index=0&scoped=c0c0cf89&lang.css",
"key": "island-link",
Expand Down