diff --git a/package.json b/package.json index 8cfc68a1f7..65448f7e84 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,9 @@ "@nuxt/vite-builder": "workspace:*", "@nuxt/webpack-builder": "workspace:*", "@types/node": "22.10.7", - "@unhead/schema": "2.0.0-alpha.6", - "@unhead/vue": "2.0.0-alpha.6", - "@unhead/shared": "2.0.0-alpha.6", + "@unhead/schema": "2.0.0-alpha.7", + "@unhead/vue": "2.0.0-alpha.7", + "@unhead/shared": "2.0.0-alpha.7", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13", @@ -63,7 +63,7 @@ "typescript": "5.7.3", "ufo": "1.5.4", "unbuild": "3.3.1", - "unhead": "2.0.0-alpha.6", + "unhead": "2.0.0-alpha.7", "unimport": "3.14.6", "vite": "6.0.9", "vue": "3.5.13" @@ -83,8 +83,8 @@ "@types/babel__helper-plugin-utils": "7.10.3", "@types/node": "22.10.7", "@types/semver": "7.5.8", - "@unhead/schema": "2.0.0-alpha.6", - "@unhead/vue": "2.0.0-alpha.6", + "@unhead/schema": "2.0.0-alpha.7", + "@unhead/vue": "2.0.0-alpha.7", "@vitest/coverage-v8": "3.0.2", "@vue/test-utils": "2.4.6", "acorn": "8.14.0", diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 3975598507..9c2e2938e1 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -71,7 +71,7 @@ "@nuxt/schema": "workspace:*", "@nuxt/telemetry": "^2.6.4", "@nuxt/vite-builder": "workspace:*", - "@unhead/vue": "^2.0.0-alpha.6", + "@unhead/vue": "^2.0.0-alpha.7", "@vue/shared": "^3.5.13", "acorn": "8.14.0", "c12": "^2.0.1", diff --git a/packages/nuxt/src/app/components/nuxt-island.ts b/packages/nuxt/src/app/components/nuxt-island.ts index 26bcd88e9e..22441b0c7e 100644 --- a/packages/nuxt/src/app/components/nuxt-island.ts +++ b/packages/nuxt/src/app/components/nuxt-island.ts @@ -91,7 +91,7 @@ export default defineComponent({ const instance = getCurrentInstance()! const event = useRequestEvent() - let activeHead: ActiveHeadEntry> + let activeHead: ActiveHeadEntry // TODO: remove use of `$fetch.raw` when nitro 503 issues on windows dev server are resolved const eventFetch = import.meta.server ? event!.fetch : import.meta.dev ? $fetch.raw : globalThis.fetch diff --git a/packages/nuxt/src/app/composables/head.ts b/packages/nuxt/src/app/composables/head.ts index 4ece97ad7c..320121da35 100644 --- a/packages/nuxt/src/app/composables/head.ts +++ b/packages/nuxt/src/app/composables/head.ts @@ -1,78 +1,9 @@ -import type { UseHeadInput, UseHeadOptions, UseHeadSafeInput, UseSeoMetaInput, VueHeadClient } from '@unhead/vue' -import type { ActiveHeadEntry, MergeHead } from '@unhead/schema' -import { hasInjectionContext, inject } from 'vue' -import { - useHead as headCore, - useHeadSafe as headSafe, - headSymbol, - useSeoMeta as seoMeta, useServerHead as serverHead, useServerHeadSafe as serverHeadSafe, - useServerSeoMeta as serverSeoMeta, -} from '@unhead/vue' -import { tryUseNuxtApp, useNuxtApp } from '#app' -import type { NuxtApp } from '#app' -// @ts-expect-error build-time -import { isNuxt4 } from '#build/nuxt.config.mjs' - -function resolveUnheadInject (): VueHeadClient | undefined { - // try use Vue inject - if (hasInjectionContext()) { - return inject>(headSymbol)! - } -} - -/** - * Injects the head client from the Nuxt context or Vue inject. - * - * In Nuxt v3 this function will not throw an error if the context is missing. - */ -export function injectHead (nuxtApp?: NuxtApp): VueHeadClient { - // Nuxt 4 will throw an error if the context is missing - const nuxt = nuxtApp || (isNuxt4 ? useNuxtApp() : tryUseNuxtApp()) - return nuxt?.ssrContext?.head || nuxt?.runWithContext(resolveUnheadInject) as VueHeadClient || resolveUnheadInject() -} - -interface NuxtUseHeadOptions extends UseHeadOptions { - nuxt?: NuxtApp -} - -export function useHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return headCore(input, { head, ...options }) as ActiveHeadEntry> - } -} - -export function useHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return headSafe(input, { head, ...options }) as ActiveHeadEntry - } -} - -export function useSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return seoMeta(input, { head, ...options }) as ActiveHeadEntry - } -} - -export function useServerHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return serverHead(input, { head, ...options }) as ActiveHeadEntry> - } -} - -export function useServerHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return serverHeadSafe(input, { head, ...options }) as ActiveHeadEntry - } -} - -export function useServerSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { - const head = injectHead(options.nuxt) - if (isNuxt4 || head) { - return serverSeoMeta(input, { head, ...options }) as ActiveHeadEntry - } -} +export { + injectHead, + useHead, + useServerHead, + useSeoMeta, + useServerSeoMeta, + useHeadSafe, + useServerHeadSafe, +} from '#unhead/composables' diff --git a/packages/nuxt/src/app/composables/script-stubs.ts b/packages/nuxt/src/app/composables/script-stubs.ts index 4f9364d84e..d7a12ba03e 100644 --- a/packages/nuxt/src/app/composables/script-stubs.ts +++ b/packages/nuxt/src/app/composables/script-stubs.ts @@ -1,4 +1,4 @@ -import type { UseScriptInput, UseScriptOptions } from '@unhead/vue/legacy' +import type { UseScriptInput } from '@unhead/vue/legacy' import { createError } from './error' function renderStubMessage (name: string) { @@ -13,7 +13,7 @@ function renderStubMessage (name: string) { } // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function useScript> (input: UseScriptInput, options?: UseScriptOptions) { +export function useScript> (input: UseScriptInput, options?: Record) { renderStubMessage('useScript') } diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 56912a5951..7a6890bf93 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -525,7 +525,6 @@ export const nuxtConfigTemplate: NuxtTemplate = { `export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === '@nuxt/vite-builder' ? '"vite:preloadError"' : '"nuxt:preloadError"' : 'false'}`, `export const crawlLinks = ${!!((ctx.nuxt as any)._nitro as Nitro).options.prerender.crawlLinks}`, `export const spaLoadingTemplateOutside = ${ctx.nuxt.options.experimental.spaLoadingTemplateLocation === 'body'}`, - `export const isNuxt4 = ${ctx.nuxt.options.future.compatibilityVersion === 4}`, ].join('\n\n') }, } diff --git a/packages/nuxt/src/head/module.ts b/packages/nuxt/src/head/module.ts index f9d6514669..4500f0b859 100644 --- a/packages/nuxt/src/head/module.ts +++ b/packages/nuxt/src/head/module.ts @@ -1,7 +1,8 @@ -import { dirname, resolve } from 'pathe' -import { addComponent, addPlugin, addTemplate, defineNuxtModule, tryResolveModule } from '@nuxt/kit' +import { resolve } from 'pathe' +import { addBuildPlugin, addComponent, addPlugin, addTemplate, defineNuxtModule, tryResolveModule } from '@nuxt/kit' import type { NuxtOptions } from '@nuxt/schema' import { distDir } from '../dirs' +import { UnheadImportsPlugin } from './plugins/unhead-imports' const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body'] @@ -10,13 +11,13 @@ export default defineNuxtModule({ name: 'nuxt:meta', configKey: 'unhead', }, - async setup (options, nuxt) { + setup (options, nuxt) { const runtimeDir = resolve(distDir, 'head/runtime') // Transpile @unhead/vue nuxt.options.build.transpile.push('@unhead/vue') - const isNuxtV4 = nuxt.options.future?.compatibilityVersion === 4 + const isNuxtV4 = nuxt.options._majorVersion === 4 || nuxt.options.future?.compatibilityVersion === 4 // Register components const componentsPath = resolve(runtimeDir, 'components') for (const componentName of components) { @@ -38,44 +39,11 @@ export default defineNuxtModule({ ] } - // for Nuxt v3 users we will alias `@unhead/vue` to our custom export path so that - // import { useHead } from '@unhead/vue' - // will work in a context without the Vue app such as Nuxt plugins and such - // for Nuxt v4 user should import from #imports - if (!isNuxtV4) { - const realUnheadPath = await tryResolveModule('@unhead/vue', nuxt.options.modulesDir) || '@unhead/vue' - // Transpile @unhead/vue - nuxt.options.build.transpile.push(realUnheadPath) - for (const subpath of ['legacy', 'types']) { - nuxt.options.alias[`@unhead/vue/${subpath}`] = resolve(dirname(realUnheadPath), subpath) - } - addTemplate({ - filename: 'unhead-exports.mjs', - getContents () { - return ` -export { - injectHead, - useHead, - useHeadSafe, - useSeoMeta, - useServerHead, - useServerHeadSafe, - useServerSeoMeta, -} from '#app/composables/head' - -export { - createHeadCore, - resolveUnrefHeadInput, - unheadVueComposablesImports, -} from '${JSON.stringify(realUnheadPath)}' - -export * from '@unhead/vue/types' -` - }, - }) - - nuxt.options.alias['@unhead/vue'] = '#build/unhead-exports.mjs' - } + nuxt.options.alias['#unhead/composables'] = resolve(runtimeDir, 'composables', isNuxtV4 ? 'v4' : 'v3') + addBuildPlugin(UnheadImportsPlugin({ + sourcemap: !!nuxt.options.sourcemap.server, + rootDir: nuxt.options.rootDir, + })) addTemplate({ filename: 'unhead-options.mjs', diff --git a/packages/nuxt/src/head/plugins/unhead-imports.ts b/packages/nuxt/src/head/plugins/unhead-imports.ts new file mode 100644 index 0000000000..25e0531b9d --- /dev/null +++ b/packages/nuxt/src/head/plugins/unhead-imports.ts @@ -0,0 +1,85 @@ +import { createUnplugin } from 'unplugin' +import MagicString from 'magic-string' +import type { ImportSpecifier } from 'estree' +import { relative } from 'pathe' +import { parseAndWalk, withLocations } from '../../core/utils/parse' +import { isJS, isVue } from '../../core/utils' +import { distDir } from '../../dirs' +import { logger } from '../../utils' + +interface UnheadImportsPluginOptions { + sourcemap: boolean + rootDir: string +} + +const UNHEAD_LIB_RE = /node_modules\/(?:@unhead\/[^/]+|unhead)\// + +function toImports (specifiers: ImportSpecifier[]) { + return specifiers.map((specifier) => { + const isNamedImport = specifier.imported && specifier.imported.name !== specifier.local.name + return isNamedImport ? `${specifier.imported.name} as ${specifier.local.name}` : specifier.local.name + }) +} + +const UnheadNuxtContextComposables = [ + 'useHead', + 'useHeadSafe', + 'useServerHeadSafe', + 'useSeoMeta', + 'useServerSeoMeta', + 'useServerHead', +] + +/** + * To use composable in an async context we need to pass Nuxt context to the Unhead composables. + * + * We swap imports from @unhead/vue to #app/composables/head and warn users for type safety. + */ +export const UnheadImportsPlugin = (options: UnheadImportsPluginOptions) => createUnplugin(() => { + return { + name: 'nuxt:head:unhead-imports', + enforce: 'post', + transformInclude (id) { + return (isJS(id) || isVue(id, { type: ['script'] })) && !id.startsWith(distDir) && !UNHEAD_LIB_RE.test(id) + }, + transform (code, id) { + if (!code.includes('@unhead/vue')) { + return + } + const s = new MagicString(code) + const importsToAdd: ImportSpecifier[] = [] + // Without setup function, vue compiler does not generate __name + parseAndWalk(code, id, function (node) { + // find any imports from @unhead/vue, swap the matchImports for an import from #app/composables/head + if (node.type === 'ImportDeclaration' && ['@unhead/vue', '#app/composables/head'].includes(node.source.value)) { + importsToAdd.push(...node.specifiers as ImportSpecifier[]) + const { start, end } = withLocations(node) + s.remove(start, end) + } + }) + + const importsFromUnhead = importsToAdd.filter(specifier => UnheadNuxtContextComposables.includes(specifier.imported.name)) + const importsFromHead = importsToAdd.filter(specifier => !UnheadNuxtContextComposables.includes(specifier.imported.name)) + if (importsFromUnhead.length) { + // warn if user has imported from @unhead/vue themselves + if (!id.includes('node_modules')) { + logger.warn(`You are importing from \`@unhead/vue\` in \`./${relative(options.rootDir, id)}\`. Please import from \`#app\` instead for full type safety.`) + } + s.prepend(`import { ${toImports(importsFromUnhead).join(', ')} } from '#app/composables/head'\n`) + } + // if there are imports from #app/composables/head, add an import from @unhead/vue + if (importsFromHead.length) { + s.prepend(`import { ${toImports(importsFromHead).join(', ')} } from '@unhead/vue'\n`) + } + + if (s.hasChanged()) { + return { + code: s.toString(), + map: options.sourcemap + ? s.generateMap({ hires: true }) + : undefined, + } + } + }, + } +}) diff --git a/packages/nuxt/src/head/runtime/components.ts b/packages/nuxt/src/head/runtime/components.ts index ccf1fd5b78..64ccc0ae03 100644 --- a/packages/nuxt/src/head/runtime/components.ts +++ b/packages/nuxt/src/head/runtime/components.ts @@ -1,4 +1,4 @@ -import { computed, defineComponent } from 'vue' +import { defineComponent } from 'vue' import type { PropType, SetupContext } from 'vue' import type { CrossOrigin, @@ -23,7 +23,7 @@ const removeUndefinedProps = (props: Props) => { } const setupForUseMeta = (metaFactory: (props: Props, ctx: SetupContext) => Record, renderChild?: boolean) => (props: Props, ctx: SetupContext) => { - useHead(computed(() => metaFactory({ ...removeUndefinedProps(props), ...ctx.attrs }, ctx))) + useHead(() => metaFactory({ ...removeUndefinedProps(props), ...ctx.attrs }, ctx)) return () => renderChild ? ctx.slots.default?.() : null } diff --git a/packages/nuxt/src/head/runtime/composables/v3.ts b/packages/nuxt/src/head/runtime/composables/v3.ts new file mode 100644 index 0000000000..bbc7f0011f --- /dev/null +++ b/packages/nuxt/src/head/runtime/composables/v3.ts @@ -0,0 +1,73 @@ +import type { UseHeadInput, UseHeadOptions, UseHeadSafeInput, UseSeoMetaInput, VueHeadClient } from '@unhead/vue' +import type { ActiveHeadEntry, MergeHead } from '@unhead/schema' +import { hasInjectionContext, inject } from 'vue' +import { + useHead as headCore, + useHeadSafe as headSafe, + headSymbol, + useSeoMeta as seoMeta, useServerHead as serverHead, useServerHeadSafe as serverHeadSafe, + useServerSeoMeta as serverSeoMeta, +} from '@unhead/vue' +import { tryUseNuxtApp } from '#app' +import type { NuxtApp } from '#app' + +/** + * Injects the head client from the Nuxt context or Vue inject. + * + * In Nuxt v3 this function will not throw an error if the context is missing. + */ +export function injectHead (nuxtApp?: NuxtApp): VueHeadClient { + // Nuxt 4 will throw an error if the context is missing + const nuxt = nuxtApp || tryUseNuxtApp() + return nuxt?.ssrContext?.head || nuxt?.runWithContext(() => { + if (hasInjectionContext()) { + return inject>(headSymbol)! + } + }) as VueHeadClient +} + +interface NuxtUseHeadOptions extends UseHeadOptions { + nuxt?: NuxtApp +} + +export function useHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { + const head = injectHead(options.nuxt) + if (head) { + return headCore(input, { head, ...options }) as ActiveHeadEntry> + } +} + +export function useHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { + const head = injectHead(options.nuxt) + if (head) { + return headSafe(input, { head, ...options }) as ActiveHeadEntry + } +} + +export function useSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { + const head = injectHead(options.nuxt) + if (head) { + return seoMeta(input, { head, ...options }) as ActiveHeadEntry + } +} + +export function useServerHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> | void { + const head = injectHead(options.nuxt) + if (head) { + return serverHead(input, { head, ...options }) as ActiveHeadEntry> + } +} + +export function useServerHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { + const head = injectHead(options.nuxt) + if (head) { + return serverHeadSafe(input, { head, ...options }) as ActiveHeadEntry + } +} + +export function useServerSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry | void { + const head = injectHead(options.nuxt) + if (head) { + return serverSeoMeta(input, { head, ...options }) as ActiveHeadEntry + } +} diff --git a/packages/nuxt/src/head/runtime/composables/v4.ts b/packages/nuxt/src/head/runtime/composables/v4.ts new file mode 100644 index 0000000000..34c20457af --- /dev/null +++ b/packages/nuxt/src/head/runtime/composables/v4.ts @@ -0,0 +1,59 @@ +import type { UseHeadInput, UseHeadOptions, UseHeadSafeInput, UseSeoMetaInput, VueHeadClient } from '@unhead/vue' +import type { ActiveHeadEntry, MergeHead } from '@unhead/schema' +import { hasInjectionContext, inject } from 'vue' +import { + useHead as headCore, + useHeadSafe as headSafe, + headSymbol, + useSeoMeta as seoMeta, useServerHead as serverHead, useServerHeadSafe as serverHeadSafe, + useServerSeoMeta as serverSeoMeta, +} from '@unhead/vue' +import { useNuxtApp } from '#app' +import type { NuxtApp } from '#app' + +/** + * Injects the head client from the Nuxt context or Vue inject. + */ +export function injectHead (nuxtApp?: NuxtApp): VueHeadClient { + // Nuxt 4 will throw an error if the context is missing + const nuxt = nuxtApp || useNuxtApp() + return nuxt.ssrContext?.head || nuxt.runWithContext(() => { + if (hasInjectionContext()) { + return inject>(headSymbol)! + } + }) as VueHeadClient +} + +interface NuxtUseHeadOptions extends UseHeadOptions { + nuxt?: NuxtApp +} + +export function useHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> { + const head = injectHead(options.nuxt) + return headCore(input, { head, ...options }) as ActiveHeadEntry> +} + +export function useHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> { + const head = injectHead(options.nuxt) + return headSafe(input, { head, ...options }) as ActiveHeadEntry +} + +export function useSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry { + const head = injectHead(options.nuxt) + return seoMeta(input, { head, ...options }) as ActiveHeadEntry +} + +export function useServerHead (input: UseHeadInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry> { + const head = injectHead(options.nuxt) + return serverHead(input, { head, ...options }) as ActiveHeadEntry> +} + +export function useServerHeadSafe (input: UseHeadSafeInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry { + const head = injectHead(options.nuxt) + return serverHeadSafe(input, { head, ...options }) as ActiveHeadEntry +} + +export function useServerSeoMeta (input: UseSeoMetaInput, options: NuxtUseHeadOptions = {}): ActiveHeadEntry { + const head = injectHead(options.nuxt) + return serverSeoMeta(input, { head, ...options }) as ActiveHeadEntry +} diff --git a/packages/schema/package.json b/packages/schema/package.json index 2a07d488c2..fdcf6d0942 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/pug": "2.0.10", - "@unhead/schema": "2.0.0-alpha.6", + "@unhead/schema": "2.0.0-alpha.7", "@vitejs/plugin-vue": "5.2.1", "@vitejs/plugin-vue-jsx": "4.1.1", "@vue/compiler-core": "3.5.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fe19bf3f4..5d35c760e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,9 +12,9 @@ overrides: '@nuxt/vite-builder': workspace:* '@nuxt/webpack-builder': workspace:* '@types/node': 22.10.7 - '@unhead/schema': 2.0.0-alpha.6 - '@unhead/vue': 2.0.0-alpha.6 - '@unhead/shared': 2.0.0-alpha.6 + '@unhead/schema': 2.0.0-alpha.7 + '@unhead/vue': 2.0.0-alpha.7 + '@unhead/shared': 2.0.0-alpha.7 '@vue/compiler-core': 3.5.13 '@vue/compiler-dom': 3.5.13 '@vue/shared': 3.5.13 @@ -31,7 +31,7 @@ overrides: typescript: 5.7.3 ufo: 1.5.4 unbuild: 3.3.1 - unhead: 2.0.0-alpha.6 + unhead: 2.0.0-alpha.7 unimport: 3.14.6 vite: 6.0.9 vue: 3.5.13 @@ -83,11 +83,11 @@ importers: specifier: 7.5.8 version: 7.5.8 '@unhead/schema': - specifier: 2.0.0-alpha.6 - version: 2.0.0-alpha.6 + specifier: 2.0.0-alpha.7 + version: 2.0.0-alpha.7 '@unhead/vue': - specifier: 2.0.0-alpha.6 - version: 2.0.0-alpha.6(vue@3.5.13(typescript@5.7.3)) + specifier: 2.0.0-alpha.7 + version: 2.0.0-alpha.7(vue@3.5.13(typescript@5.7.3)) '@vitest/coverage-v8': specifier: 3.0.2 version: 3.0.2(vitest@3.0.2(@types/node@22.10.7)(happy-dom@16.6.0)(jiti@2.4.2)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1)) @@ -339,8 +339,8 @@ importers: specifier: 22.10.7 version: 22.10.7 '@unhead/vue': - specifier: 2.0.0-alpha.6 - version: 2.0.0-alpha.6(vue@3.5.13(typescript@5.7.3)) + specifier: 2.0.0-alpha.7 + version: 2.0.0-alpha.7(vue@3.5.13(typescript@5.7.3)) '@vue/shared': specifier: 3.5.13 version: 3.5.13 @@ -684,8 +684,8 @@ importers: specifier: 2.0.10 version: 2.0.10 '@unhead/schema': - specifier: 2.0.0-alpha.6 - version: 2.0.0-alpha.6 + specifier: 2.0.0-alpha.7 + version: 2.0.0-alpha.7 '@vitejs/plugin-vue': specifier: 5.2.1 version: 5.2.1(vite@6.0.9(@types/node@22.10.7)(jiti@2.4.2)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.3)) @@ -2842,14 +2842,14 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@unhead/schema@2.0.0-alpha.6': - resolution: {integrity: sha512-UjObgqdmWaKYwGk0KzK5snTdETikaU2A2Dqvz1ElS/zFNLsFmBIg4HwidvlA/oxF4/FvbZTlKsTutgiKWAHePA==} + '@unhead/schema@2.0.0-alpha.7': + resolution: {integrity: sha512-HzCoMwgex4RJRf37ASI6QBYsWTu1Q/de5TRDb0B8lgp/lXMnG3+gQQP2jXLv6h5Nvo7dT7opVxhgyj6zxXkVTA==} - '@unhead/shared@2.0.0-alpha.6': - resolution: {integrity: sha512-Uvp+Qzb/b8Me+VOqddjOK7aLFuBw/mk3Fx//ipB93FfjtXZ02WErDPTsOv+4aOK86JKG8CrxOkYosGcw8WceLw==} + '@unhead/shared@2.0.0-alpha.7': + resolution: {integrity: sha512-ttyCQUbY/0UQ3mEix3dKhk+5NL8vPp69zv25wY71nHXaLE3lrnGjOUjkETiE7XH2nflhnZdIslUumZADnl3Tjw==} - '@unhead/vue@2.0.0-alpha.6': - resolution: {integrity: sha512-VfSycV+tLTGpPKlihmQNdHo/cxvyvtUKnPRUFAB1KMHjf9xn5twq2fXetf2LYZtTQunKuqZ08kwlRt+pAvd3dg==} + '@unhead/vue@2.0.0-alpha.7': + resolution: {integrity: sha512-7Zt0RteYvl+rDlh+4HuELqaLM9SI+rTPuuZWftiml4c6gG96UrAnjq7d0gljFL1jfi2TsUwQJdjrLPEXNY8Ftw==} peerDependencies: vue: 3.5.13 @@ -7432,8 +7432,8 @@ packages: unenv@1.10.0: resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} - unhead@2.0.0-alpha.6: - resolution: {integrity: sha512-kLQLu+wMbFgdULGyeq6uCXuAPXi6XFlNFPQCsur1dLfNGBxoJptY/uACrLiB1ttc8EFSxYVm0JWn17urm/t1iA==} + unhead@2.0.0-alpha.7: + resolution: {integrity: sha512-iaiL8eHACK8t+PXkeTsVgMs52CCqd9LFJplisRqFqqwspxzsTzpXPppmqizA0UZHtCG9P5rg8eEiDhHMQCzOtg==} unicode-emoji-modifier-base@1.0.0: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} @@ -9110,7 +9110,7 @@ snapshots: '@types/google.maps': 3.58.1 '@types/vimeo__player': 2.18.3 '@types/youtube': 0.1.0 - '@unhead/vue': 2.0.0-alpha.6(vue@3.5.13(typescript@5.7.3)) + '@unhead/vue': 2.0.0-alpha.7(vue@3.5.13(typescript@5.7.3)) '@vueuse/core': 11.1.0(vue@3.5.13(typescript@5.7.3)) consola: 3.4.0 defu: 6.1.4 @@ -10066,22 +10066,22 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unhead/schema@2.0.0-alpha.6': + '@unhead/schema@2.0.0-alpha.7': dependencies: hookable: 5.5.3 zhead: 2.2.4 - '@unhead/shared@2.0.0-alpha.6': + '@unhead/shared@2.0.0-alpha.7': dependencies: - '@unhead/schema': 2.0.0-alpha.6 + '@unhead/schema': 2.0.0-alpha.7 packrup: 0.1.2 - '@unhead/vue@2.0.0-alpha.6(vue@3.5.13(typescript@5.7.3))': + '@unhead/vue@2.0.0-alpha.7(vue@3.5.13(typescript@5.7.3))': dependencies: - '@unhead/schema': 2.0.0-alpha.6 - '@unhead/shared': 2.0.0-alpha.6 + '@unhead/schema': 2.0.0-alpha.7 + '@unhead/shared': 2.0.0-alpha.7 hookable: 5.5.3 - unhead: 2.0.0-alpha.6 + unhead: 2.0.0-alpha.7 vue: 3.5.13(typescript@5.7.3) '@unocss/astro@0.62.4(rollup@4.31.0)(vite@6.0.9(@types/node@22.10.7)(jiti@2.4.2)(terser@5.32.0)(tsx@4.19.2)(yaml@2.6.1))': @@ -15630,10 +15630,10 @@ snapshots: node-fetch-native: 1.6.4 pathe: 1.1.2 - unhead@2.0.0-alpha.6: + unhead@2.0.0-alpha.7: dependencies: - '@unhead/schema': 2.0.0-alpha.6 - '@unhead/shared': 2.0.0-alpha.6 + '@unhead/schema': 2.0.0-alpha.7 + '@unhead/shared': 2.0.0-alpha.7 hookable: 5.5.3 unicode-emoji-modifier-base@1.0.0: {} diff --git a/test/fixtures/basic-types/types.ts b/test/fixtures/basic-types/types.ts index 15392572c1..d4cda0b4b5 100644 --- a/test/fixtures/basic-types/types.ts +++ b/test/fixtures/basic-types/types.ts @@ -405,7 +405,7 @@ describe('head', () => { link: computed(() => []), meta: [ { key: 'key', name: 'description', content: 'some description ' }, - computed(() => ({ key: 'key', name: 'description', content: 'some description ' })), + () => ({ key: 'key', name: 'description', content: 'some description ' }), ], titleTemplate: (titleChunk) => { return titleChunk ? `${titleChunk} - Site Title` : 'Site Title' diff --git a/tsconfig.json b/tsconfig.json index 6763732a3f..00df769628 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,6 +32,9 @@ ], "#app/*": [ "./packages/nuxt/src/app/*" + ], + "#unhead/composables": [ + "./packages/nuxt/src/head/runtime/composables/v4" ] } },