Merge branch 'main' into patch-21

This commit is contained in:
Michael Brevard 2024-03-30 16:12:02 +03:00 committed by GitHub
commit 586cfa5a3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 623 additions and 422 deletions

View File

@ -1,37 +1,19 @@
<!---
☝️ PR title should follow conventional commits (https://conventionalcommits.org)
Please carefully read the contribution docs before creating a pull request
👉 https://nuxt.com/docs/community/contribution
-->
### 🔗 Linked issue ### 🔗 Linked issue
<!-- Please ensure there is an open issue and mention its number as #123 --> <!-- Please ensure there is an open issue and mention its number. For example, "resolves #123" -->
### ❓ Type of change
<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->
- [ ] 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
- [ ] 🐞 Bug fix (a non-breaking change that fixes an issue)
- [ ] 👌 Enhancement (improving an existing functionality like performance)
- [ ] ✨ 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 ### 📚 Description
<!-- Describe your changes in detail --> <!-- Describe your changes in detail. Why is this change required? What problem does it solve? -->
<!-- Why is this change required? What problem does it solve? -->
<!-- If it resolves an open issue, please link to the issue here. For example "Resolves #1337" -->
### 📝 Checklist <!----------------------------------------------------------------------
Before creating the pull request, please make sure you do the following:
<!-- Put an `x` in all the boxes that apply. --> - Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us reviewing it.
<!-- If your change requires a documentation PR, please link it appropriately --> - Read the contribution docs at https://nuxt.com/docs/community/contribution
<!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - Ensure that PR title follows conventional commits (https://conventionalcommits.org)
- Update the corresponding documentation if needed.
- Include relevant tests that fail without this PR but pass with it.
- [ ] I have linked an issue or discussion. Thank you for contributing to Nuxt!
- [ ] I have added tests (if possible). ----------------------------------------------------------------------->
- [ ] I have updated the documentation accordingly.

View File

@ -99,7 +99,7 @@ Nuxt uses a custom merging strategy for the `AppConfig` within [the layers](/doc
This strategy is implemented using a [Function Merger](https://github.com/unjs/defu#function-merger), which allows defining a custom merging strategy for every key in `app.config` that has an array as value. This strategy is implemented using a [Function Merger](https://github.com/unjs/defu#function-merger), which allows defining a custom merging strategy for every key in `app.config` that has an array as value.
::note ::note
The Function Merger should only be used in the base `app.config` of your application. The function merger can only be used in the extended layers and not the main `app.config` in project.
:: ::
Here's an example of how you can use: Here's an example of how you can use:

View File

@ -87,6 +87,8 @@ console.log(layoutCustomProps.title) // I am a custom layout
`<NuxtLayout />` renders incoming content via `<slot />`, which is then wrapped around Vues `<Transition />` component to activate layout transition. For this to work as expected, it is recommended that `<NuxtLayout />` is **not** the root element of the page component. `<NuxtLayout />` renders incoming content via `<slot />`, which is then wrapped around Vues `<Transition />` component to activate layout transition. For this to work as expected, it is recommended that `<NuxtLayout />` is **not** the root element of the page component.
::code-group
```vue [pages/index.vue] ```vue [pages/index.vue]
<template> <template>
<div> <div>
@ -97,13 +99,27 @@ console.log(layoutCustomProps.title) // I am a custom layout
</template> </template>
``` ```
```vue [layouts/custom.vue]
<template>
<div>
<!-- named slot -->
<slot name="header" />
<slot />
</div>
</template>
```
::
:read-more{to="/docs/getting-started/transitions"} :read-more{to="/docs/getting-started/transitions"}
## Layout's Ref ## Layout's Ref
To get the ref of a layout component, access it through `ref.value.layoutRef`. To get the ref of a layout component, access it through `ref.value.layoutRef`.
````vue [app.vue] ::code-group
```vue [app.vue]
<script setup lang="ts"> <script setup lang="ts">
const layout = ref() const layout = ref()
@ -113,8 +129,28 @@ function logFoo () {
</script> </script>
<template> <template>
<NuxtLayout ref="layout" /> <NuxtLayout ref="layout">
default layout
</NuxtLayout>
</template> </template>
```` ```
```vue [layouts/default.vue]
<script setup lang="ts">
const foo = () => console.log('foo')
defineExpose({
foo
})
</script>
<template>
<div>
default layout
<slot />
</div>
</template>
```
::
:read-more{to="/docs/guide/directory-structure/layouts"} :read-more{to="/docs/guide/directory-structure/layouts"}

View File

@ -14,6 +14,10 @@ When prerendering, you can hint to Nitro to prerender additional paths, even if
`prerenderRoutes` can only be called within the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context). `prerenderRoutes` can only be called within the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context).
:: ::
::note
`prerenderRoutes` has to be executed during prerendering. If the `prerenderRoutes` is used in dynamic pages/routes which are not prerendered, then it will not be executed.
::
```js ```js
const route = useRoute() const route = useRoute()

View File

@ -31,9 +31,7 @@ Check [Discussions](https://github.com/nuxt/nuxt/discussions) and [RFCs](https:/
Milestone | Expected date | Notes | Description Milestone | Expected date | Notes | Description
-------------|---------------|------------------------------------------------------------------------|----------------------- -------------|---------------|------------------------------------------------------------------------|-----------------------
SEO & PWA | 2024 | [nuxt/nuxt#18395](https://github.com/nuxt/nuxt/discussions/18395) | Migrating from [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) for built-in SEO utils and service worker support SEO & PWA | 2024 | [nuxt/nuxt#18395](https://github.com/nuxt/nuxt/discussions/18395) | Migrating from [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) for built-in SEO utils and service worker support
Scripts | 2024 | [nuxt/nuxt#22016](https://github.com/nuxt/nuxt/discussions/22016) | Easy 3rd party script management.
Assets | 2024 | [nuxt/nuxt#22012](https://github.com/nuxt/nuxt/discussions/22012) | Allow developers and modules to handle loading third-party assets. Assets | 2024 | [nuxt/nuxt#22012](https://github.com/nuxt/nuxt/discussions/22012) | Allow developers and modules to handle loading third-party assets.
A11y | 2024 | [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255) | Accessibility hinting and utilities
Translations | - | [nuxt/translations#4](https://github.com/nuxt/translations/discussions/4) ([request access](https://github.com/nuxt/nuxt/discussions/16054)) | A collaborative project for a stable translation process for Nuxt 3 docs. Currently pending for ideas and documentation tooling support (content v2 with remote sources). Translations | - | [nuxt/translations#4](https://github.com/nuxt/translations/discussions/4) ([request access](https://github.com/nuxt/nuxt/discussions/16054)) | A collaborative project for a stable translation process for Nuxt 3 docs. Currently pending for ideas and documentation tooling support (content v2 with remote sources).
## Core Modules Roadmap ## Core Modules Roadmap
@ -42,7 +40,10 @@ In addition to the Nuxt framework, there are modules that are vital for the ecos
Module | Status | Nuxt Support | Repository | Description Module | Status | Nuxt Support | Repository | Description
---------------|---------------------|--------------|------------|------------------- ---------------|---------------------|--------------|------------|-------------------
Scripts | April 2024 | 3.x | `nuxt/scripts` to be announced | Easy 3rd party script management. [nuxt/nuxt#22016](https://github.com/nuxt/nuxt/discussions/22016)
A11y | Planned | 3.x | `nuxt/a11y` to be announced | Accessibility hinting and utilities [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255)
Auth | Planned | 3.x | `nuxt/auth` to be announced | Nuxt 3 support is planned after session support Auth | Planned | 3.x | `nuxt/auth` to be announced | Nuxt 3 support is planned after session support
Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices
## Release Cycle ## Release Cycle

View File

@ -37,9 +37,9 @@
"@nuxt/schema": "workspace:*", "@nuxt/schema": "workspace:*",
"@nuxt/vite-builder": "workspace:*", "@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*", "@nuxt/webpack-builder": "workspace:*",
"rollup": "^4.13.1", "rollup": "^4.13.2",
"nuxt": "workspace:*", "nuxt": "workspace:*",
"vite": "5.2.6", "vite": "5.2.7",
"vue": "3.4.21", "vue": "3.4.21",
"magic-string": "^0.30.8" "magic-string": "^0.30.8"
}, },
@ -50,7 +50,7 @@
"@nuxt/webpack-builder": "workspace:*", "@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.0.3", "@testing-library/vue": "8.0.3",
"@types/fs-extra": "11.0.4", "@types/fs-extra": "11.0.4",
"@types/node": "20.11.30", "@types/node": "20.12.2",
"@types/semver": "7.5.8", "@types/semver": "7.5.8",
"@vitest/coverage-v8": "1.4.0", "@vitest/coverage-v8": "1.4.0",
"@vue/test-utils": "2.4.5", "@vue/test-utils": "2.4.5",
@ -61,7 +61,7 @@
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-config-standard": "17.1.0", "eslint-config-standard": "17.1.0",
"eslint-plugin-import": "2.29.1", "eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "48.2.1", "eslint-plugin-jsdoc": "48.2.2",
"eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-unicorn": "51.0.1", "eslint-plugin-unicorn": "51.0.1",
"execa": "8.0.1", "execa": "8.0.1",

View File

@ -52,7 +52,7 @@
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"nitropack": "2.9.5", "nitropack": "2.9.5",
"unbuild": "latest", "unbuild": "latest",
"vite": "5.2.6", "vite": "5.2.7",
"vitest": "1.4.0", "vitest": "1.4.0",
"webpack": "5.91.0" "webpack": "5.91.0"
}, },

View File

@ -60,20 +60,20 @@
}, },
"dependencies": { "dependencies": {
"@nuxt/devalue": "^2.0.2", "@nuxt/devalue": "^2.0.2",
"@nuxt/devtools": "^1.1.4", "@nuxt/devtools": "^1.1.5",
"@nuxt/kit": "workspace:*", "@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*", "@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.5.3", "@nuxt/telemetry": "^2.5.3",
"@nuxt/ui-templates": "^1.3.1", "@nuxt/ui-templates": "^1.3.1",
"@nuxt/vite-builder": "workspace:*", "@nuxt/vite-builder": "workspace:*",
"@unhead/dom": "^1.9.1", "@unhead/dom": "^1.9.3",
"@unhead/ssr": "^1.9.1", "@unhead/ssr": "^1.9.3",
"@unhead/vue": "^1.9.1", "@unhead/vue": "^1.9.3",
"@vue/shared": "^3.4.21", "@vue/shared": "^3.4.21",
"acorn": "8.11.3", "acorn": "8.11.3",
"c12": "^1.10.0", "c12": "^1.10.0",
"chokidar": "^3.6.0", "chokidar": "^3.6.0",
"cookie-es": "^1.0.0", "cookie-es": "^1.1.0",
"defu": "^6.1.4", "defu": "^6.1.4",
"destr": "^2.0.3", "destr": "^2.0.3",
"devalue": "^4.3.2", "devalue": "^4.3.2",
@ -100,14 +100,14 @@
"radix3": "^1.1.2", "radix3": "^1.1.2",
"scule": "^1.3.0", "scule": "^1.3.0",
"std-env": "^3.7.0", "std-env": "^3.7.0",
"strip-literal": "^2.0.0", "strip-literal": "^2.1.0",
"ufo": "^1.5.3", "ufo": "^1.5.3",
"ultrahtml": "^1.5.3", "ultrahtml": "^1.5.3",
"uncrypto": "^0.1.3", "uncrypto": "^0.1.3",
"unctx": "^2.3.1", "unctx": "^2.3.1",
"unenv": "^1.9.0", "unenv": "^1.9.0",
"unimport": "^3.7.1", "unimport": "^3.7.1",
"unplugin": "^1.10.0", "unplugin": "^1.10.1",
"unplugin-vue-router": "^0.7.0", "unplugin-vue-router": "^0.7.0",
"unstorage": "^1.10.2", "unstorage": "^1.10.2",
"untyped": "^1.4.2", "untyped": "^1.4.2",
@ -122,7 +122,7 @@
"@types/fs-extra": "11.0.4", "@types/fs-extra": "11.0.4",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"unbuild": "latest", "unbuild": "latest",
"vite": "5.2.6", "vite": "5.2.7",
"vitest": "1.4.0" "vitest": "1.4.0"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -88,30 +88,32 @@ export default defineComponent({
onMounted(() => { mounted.value = true; teleportKey.value++ }) onMounted(() => { mounted.value = true; teleportKey.value++ })
function setPayload (key: string, result: NuxtIslandResponse) { function setPayload (key: string, result: NuxtIslandResponse) {
const toRevive: Partial<NuxtIslandResponse> = {}
if (result.props) { toRevive.props = result.props }
if (result.slots) { toRevive.slots = result.slots }
if (result.components) { toRevive.components = result.components }
nuxtApp.payload.data[key] = { nuxtApp.payload.data[key] = {
__nuxt_island: { __nuxt_island: {
key, key,
...(import.meta.server && import.meta.prerender) ...(import.meta.server && import.meta.prerender)
? {} ? {}
: { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } }, : { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } },
result: { result: toRevive
props: result.props,
slots: result.slots,
components: result.components
}
}, },
...result ...result
} }
} }
const payloads: Required<Pick<NuxtIslandResponse, 'slots' | 'components'>> = { const payloads: Partial<Pick<NuxtIslandResponse, 'slots' | 'components'>> = {}
slots: {},
components: {}
}
if (instance.vnode.el) { if (instance.vnode.el) {
payloads.slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots ?? {} const slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots
payloads.components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components ?? {} if (slots) { payloads.slots = slots }
if (selectiveClient) {
const components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components
if (components) { payloads.components = components }
}
} }
const ssrHTML = ref<string>('') const ssrHTML = ref<string>('')
@ -137,12 +139,15 @@ export default defineComponent({
} }
} }
return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => { if (payloads.slots) {
if (!currentSlots.includes(slotName)) { return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
return full + (payloads.slots[slotName]?.fallback || '') if (!currentSlots.includes(slotName)) {
} return full + (payloads.slots?.[slotName]?.fallback || '')
return full }
}) return full
})
}
return html
}) })
const cHead = ref<Record<'link' | 'style', Array<Record<string, string>>>>({ link: [], style: [] }) const cHead = ref<Record<'link' | 'style', Array<Record<string, string>>>>({ link: [], style: [] })
@ -256,24 +261,26 @@ export default defineComponent({
teleports.push(createVNode(Teleport, teleports.push(createVNode(Teleport,
// use different selectors for even and odd teleportKey to force trigger the teleport // use different selectors for even and odd teleportKey to force trigger the teleport
{ to: import.meta.client ? `${isKeyOdd ? 'div' : ''}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` }, { to: import.meta.client ? `${isKeyOdd ? 'div' : ''}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` },
{ default: () => (payloads.slots[slot].props?.length ? payloads.slots[slot].props : [{}]).map((data: any) => slots[slot]?.(data)) }) { default: () => (payloads.slots?.[slot].props?.length ? payloads.slots[slot].props : [{}]).map((data: any) => slots[slot]?.(data)) })
) )
} }
} }
if (selectiveClient) { if (selectiveClient) {
if (import.meta.server) { if (import.meta.server) {
for (const [id, info] of Object.entries(payloads.components ?? {})) { if (payloads.components) {
const { html, slots } = info for (const [id, info] of Object.entries(payloads.components)) {
let replaced = html.replaceAll('data-island-uid', `data-island-uid="${uid.value}"`) const { html, slots } = info
for (const slot in slots) { let replaced = html.replaceAll('data-island-uid', `data-island-uid="${uid.value}"`)
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, full => full + slots[slot]) for (const slot in slots) {
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, full => full + slots[slot])
}
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
default: () => [createStaticVNode(replaced, 1)]
}))
} }
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
default: () => [createStaticVNode(replaced, 1)]
}))
} }
} else if (canLoadClientComponent.value) { } else if (canLoadClientComponent.value && payloads.components) {
for (const [id, info] of Object.entries(payloads.components ?? {})) { for (const [id, info] of Object.entries(payloads.components)) {
const { props, slots } = info const { props, slots } = info
const component = components!.get(id)! const component = components!.get(id)!
// use different selectors for even and odd teleportKey to force trigger the teleport // use different selectors for even and odd teleportKey to force trigger the teleport

View File

@ -351,13 +351,11 @@ export function useAsyncData<
if (instance && !instance._nuxtOnBeforeMountCbs) { if (instance && !instance._nuxtOnBeforeMountCbs) {
instance._nuxtOnBeforeMountCbs = [] instance._nuxtOnBeforeMountCbs = []
const cbs = instance._nuxtOnBeforeMountCbs const cbs = instance._nuxtOnBeforeMountCbs
if (instance) { onBeforeMount(() => {
onBeforeMount(() => { cbs.forEach((cb) => { cb() })
cbs.forEach((cb) => { cb() }) cbs.splice(0, cbs.length)
cbs.splice(0, cbs.length) })
}) onUnmounted(() => cbs.splice(0, cbs.length))
onUnmounted(() => cbs.splice(0, cbs.length))
}
} }
if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || hasCachedData())) { if (fetchOnServer && nuxtApp.isHydrating && (asyncData.error.value || hasCachedData())) {

View File

@ -125,18 +125,16 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na
const toPath = typeof to === 'string' ? to : (withQuery((to as RouteLocationPathRaw).path || '/', to.query || {}) + (to.hash || '')) const toPath = typeof to === 'string' ? to : (withQuery((to as RouteLocationPathRaw).path || '/', to.query || {}) + (to.hash || ''))
// Early open handler // Early open handler
if (options?.open) { if (import.meta.client && options?.open) {
if (import.meta.client) { const { target = '_blank', windowFeatures = {} } = options.open
const { target = '_blank', windowFeatures = {} } = options.open
const features = Object.entries(windowFeatures) const features = Object.entries(windowFeatures)
.filter(([_, value]) => value !== undefined) .filter(([_, value]) => value !== undefined)
.map(([feature, value]) => `${feature.toLowerCase()}=${value}`) .map(([feature, value]) => `${feature.toLowerCase()}=${value}`)
.join(', ') .join(', ')
open(toPath, target, features) open(toPath, target, features)
return Promise.resolve() return Promise.resolve()
}
} }
const isExternal = options?.external || hasProtocol(toPath, { acceptRelative: true }) const isExternal = options?.external || hasProtocol(toPath, { acceptRelative: true })

View File

@ -338,7 +338,7 @@ export function createNuxtApp (options: CreateOptions) {
} }
// Expose runtime config // Expose runtime config
const runtimeConfig = import.meta.server ? options.ssrContext!.runtimeConfig : reactive(nuxtApp.payload.config!) const runtimeConfig = import.meta.server ? options.ssrContext!.runtimeConfig : nuxtApp.payload.config!
nuxtApp.provide('config', runtimeConfig) nuxtApp.provide('config', runtimeConfig)
return nuxtApp return nuxtApp

View File

@ -37,7 +37,11 @@ export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('dev:ssr-logs', (logs) => { nuxtApp.hook('dev:ssr-logs', (logs) => {
for (const log of logs) { for (const log of logs) {
// deduplicate so we don't print out things that are logged on client // deduplicate so we don't print out things that are logged on client
if (!hydrationLogs.size || !hydrationLogs.has(JSON.stringify(log.args))) { try {
if (!hydrationLogs.size || !hydrationLogs.has(JSON.stringify(log.args))) {
logger.log(normalizeServerLog({ ...log }))
}
} catch {
logger.log(normalizeServerLog({ ...log })) logger.log(normalizeServerLog({ ...log }))
} }
} }
@ -50,7 +54,8 @@ export default defineNuxtPlugin((nuxtApp) => {
// pass SSR logs after hydration // pass SSR logs after hydration
nuxtApp.hooks.hook('app:suspense:resolve', async () => { nuxtApp.hooks.hook('app:suspense:resolve', async () => {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
const logs = parse(document.getElementById('__NUXT_LOGS__')?.textContent || '[]', nuxtApp._payloadRevivers) as LogObject[] const content = document.getElementById('__NUXT_LOGS__')?.textContent
const logs = content ? parse(content, nuxtApp._payloadRevivers) as LogObject[] : []
await nuxtApp.hooks.callHook('dev:ssr-logs', logs) await nuxtApp.hooks.callHook('dev:ssr-logs', logs)
} }
}) })

View File

@ -109,12 +109,22 @@ export const componentsTypeTemplate = {
: c.filePath.replace(/(?<=\w)\.(?!vue)\w+$/g, ''), { wrapper: false })}['${c.export}']` : c.filePath.replace(/(?<=\w)\.(?!vue)\w+$/g, ''), { wrapper: false })}['${c.export}']`
]) ])
return `// Generated by components discovery return `
interface _GlobalComponents {
${componentTypes.map(([pascalName, type]) => ` '${pascalName}': ${type}`).join('\n')}
${componentTypes.map(([pascalName, type]) => ` 'Lazy${pascalName}': ${type}`).join('\n')}
}
declare module '@vue/runtime-core' {
export interface GlobalComponents extends _GlobalComponents { }
}
declare module '@vue/runtime-dom' {
export interface GlobalComponents extends _GlobalComponents { }
}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents extends _GlobalComponents { }
${componentTypes.map(([pascalName, type]) => ` '${pascalName}': ${type}`).join('\n')}
${componentTypes.map(([pascalName, type]) => ` 'Lazy${pascalName}': ${type}`).join('\n')}
}
} }
${componentTypes.map(([pascalName, type]) => `export const ${pascalName}: ${type}`).join('\n')} ${componentTypes.map(([pascalName, type]) => `export const ${pascalName}: ${type}`).join('\n')}

View File

@ -649,7 +649,7 @@ const SSR_CLIENT_TELEPORT_MARKER = /^uid=([^;]*);client=(.*)$/
const SSR_CLIENT_SLOT_MARKER = /^island-slot=(?:[^;]*);(.*)$/ const SSR_CLIENT_SLOT_MARKER = /^island-slot=(?:[^;]*);(.*)$/
function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['slots'] { function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['slots'] {
if (!ssrContext.islandContext) { return {} } if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.slots).length) { return undefined }
const response: NuxtIslandResponse['slots'] = {} const response: NuxtIslandResponse['slots'] = {}
for (const slot in ssrContext.islandContext.slots) { for (const slot in ssrContext.islandContext.slots) {
response[slot] = { response[slot] = {
@ -661,7 +661,7 @@ function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse[
} }
function getClientIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['components'] { function getClientIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['components'] {
if (!ssrContext.islandContext) { return {} } if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.components).length) { return undefined }
const response: NuxtIslandResponse['components'] = {} const response: NuxtIslandResponse['components'] = {}
for (const clientUid in ssrContext.islandContext.components) { for (const clientUid in ssrContext.islandContext.components) {

View File

@ -38,7 +38,7 @@
"@types/file-loader": "5.0.4", "@types/file-loader": "5.0.4",
"@types/pug": "2.0.10", "@types/pug": "2.0.10",
"@types/sass-loader": "8.0.8", "@types/sass-loader": "8.0.8",
"@unhead/schema": "1.9.1", "@unhead/schema": "1.9.3",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"@vitejs/plugin-vue-jsx": "3.1.0", "@vitejs/plugin-vue-jsx": "3.1.0",
"@vue/compiler-core": "3.4.21", "@vue/compiler-core": "3.4.21",
@ -52,13 +52,13 @@
"unbuild": "latest", "unbuild": "latest",
"unctx": "2.3.1", "unctx": "2.3.1",
"unenv": "1.9.0", "unenv": "1.9.0",
"vite": "5.2.6", "vite": "5.2.7",
"vue": "3.4.21", "vue": "3.4.21",
"vue-bundle-renderer": "2.0.0", "vue-bundle-renderer": "2.0.0",
"vue-loader": "17.4.2", "vue-loader": "17.4.2",
"vue-router": "4.3.0", "vue-router": "4.3.0",
"webpack": "5.91.0", "webpack": "5.91.0",
"webpack-dev-middleware": "7.1.1" "webpack-dev-middleware": "7.2.0"
}, },
"dependencies": { "dependencies": {
"@nuxt/ui-templates": "^1.3.1", "@nuxt/ui-templates": "^1.3.1",

View File

@ -32,7 +32,22 @@ export default defineUntypedSchema({
*/ */
hoist: { hoist: {
$resolve: (val) => { $resolve: (val) => {
const defaults = ['nitropack', 'defu', 'h3', '@unhead/vue', 'vue', 'vue-router', '@nuxt/schema', 'nuxt', 'consola', 'ofetch'] const defaults = [
// Nitro auto-imported/augmented dependencies
'nitropack',
'defu',
'h3',
'consola',
'ofetch',
// Key nuxt dependencies
'@unhead/vue',
'vue',
'@vue/runtime-core',
'@vue/runtime-dom',
'vue-router',
'@nuxt/schema',
'nuxt'
]
return val === false ? [] : (Array.isArray(val) ? val.concat(defaults) : defaults) return val === false ? [] : (Array.isArray(val) ? val.concat(defaults) : defaults)
} }
}, },

View File

@ -58,11 +58,11 @@
"postcss": "^8.4.38", "postcss": "^8.4.38",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"std-env": "^3.7.0", "std-env": "^3.7.0",
"strip-literal": "^2.0.0", "strip-literal": "^2.1.0",
"ufo": "^1.5.3", "ufo": "^1.5.3",
"unenv": "^1.9.0", "unenv": "^1.9.0",
"unplugin": "^1.10.0", "unplugin": "^1.10.1",
"vite": "^5.2.6", "vite": "^5.2.7",
"vite-node": "^1.4.0", "vite-node": "^1.4.0",
"vite-plugin-checker": "^0.6.4", "vite-plugin-checker": "^0.6.4",
"vue-bundle-renderer": "^2.0.0" "vue-bundle-renderer": "^2.0.0"

View File

@ -58,13 +58,13 @@
"time-fix-plugin": "^2.0.7", "time-fix-plugin": "^2.0.7",
"ufo": "^1.5.3", "ufo": "^1.5.3",
"unenv": "^1.9.0", "unenv": "^1.9.0",
"unplugin": "^1.10.0", "unplugin": "^1.10.1",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vue-bundle-renderer": "^2.0.0", "vue-bundle-renderer": "^2.0.0",
"vue-loader": "^17.4.2", "vue-loader": "^17.4.2",
"webpack": "^5.91.0", "webpack": "^5.91.0",
"webpack-bundle-analyzer": "^4.10.1", "webpack-bundle-analyzer": "^4.10.1",
"webpack-dev-middleware": "^7.1.1", "webpack-dev-middleware": "^7.2.0",
"webpack-hot-middleware": "^2.26.1", "webpack-hot-middleware": "^2.26.1",
"webpack-virtual-modules": "^0.6.1", "webpack-virtual-modules": "^0.6.1",
"webpackbar": "^6.0.1" "webpackbar": "^6.0.1"

File diff suppressed because it is too large Load Diff

View File

@ -1968,14 +1968,12 @@ describe('component islands', () => {
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
{ {
"components": {},
"head": { "head": {
"link": [], "link": [],
"style": [], "style": [],
}, },
"html": "<pre data-island-uid> Route: /foo "html": "<pre data-island-uid> Route: /foo
</pre>", </pre>",
"slots": {},
"state": {}, "state": {},
} }
`) `)
@ -1993,7 +1991,6 @@ describe('component islands', () => {
result.html = result.html.replaceAll(/ (data-island-uid|data-island-component)="([^"]*)"/g, '') result.html = result.html.replaceAll(/ (data-island-uid|data-island-component)="([^"]*)"/g, '')
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
{ {
"components": {},
"head": { "head": {
"link": [], "link": [],
"style": [], "style": [],

View File

@ -19,7 +19,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
for (const outputDir of ['.output', '.output-inline']) { for (const outputDir of ['.output', '.output-inline']) {
it('default client bundle size', async () => { it('default client bundle size', async () => {
const clientStats = await analyzeSizes('**/*.js', join(rootDir, outputDir, 'public')) const clientStats = await analyzeSizes('**/*.js', join(rootDir, outputDir, 'public'))
expect.soft(roundToKilobytes(clientStats.totalBytes)).toMatchInlineSnapshot('"106k"') expect.soft(roundToKilobytes(clientStats.totalBytes)).toMatchInlineSnapshot('"105k"')
expect(clientStats.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` expect(clientStats.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
[ [
"_nuxt/entry.js", "_nuxt/entry.js",
@ -75,7 +75,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"526k"') expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"526k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir) const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"76.0k"') expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"75.5k"')
const packages = modules.files const packages = modules.files
.filter(m => m.endsWith('package.json')) .filter(m => m.endsWith('package.json'))