Skip to content

Commit

Permalink
feat: replace @vueuse/head with @unhead/vue (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
wattanx authored Jun 21, 2023
1 parent 6e82e2d commit 3e3cf68
Show file tree
Hide file tree
Showing 22 changed files with 405 additions and 170 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export default defineNuxtConfig({
})
```

This `useHead` composable uses `@vueuse/head` under the hood (rather than `vue-meta`) to manipulate your `<head>`.
This `useHead` composable uses `@unhead/vue` under the hood (rather than `vue-meta`) to manipulate your `<head>`.
Accordingly, we recommend not to use both the native Nuxt 2 `head()` properties as well as `useHead`, as they may conflict.

For more information on how to use this composable, see [the docs](https://nuxt.com/docs/getting-started/seo-meta#seo-and-meta).
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-schema/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export default defineBuildConfig({
'vue-meta',
'vue-router',
'vue-bundle-renderer',
'@vueuse/head',
'vue',
'hookable',
'nitropack',
Expand All @@ -57,6 +56,7 @@ export default defineBuildConfig({
'postcss',
'consola',
'ignore',
'@unhead/schema',
// Implicit
'@vue/compiler-core',
'@vue/shared',
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"devDependencies": {
"@types/lodash.template": "^4.5.1",
"@types/semver": "^7.5.0",
"@vueuse/head": "^1.1.26",
"@unhead/schema": "^1.1.27",
"nitropack": "^2.4.1",
"unbuild": "latest",
"vite": "~4.3.9"
Expand Down
70 changes: 60 additions & 10 deletions packages/bridge-schema/src/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { existsSync, readdirSync } from 'node:fs'
import { defineUntypedSchema } from 'untyped'
import { resolve, join } from 'pathe'
import defu from 'defu'
import { AppHeadMetaObject } from '../types/head'

export default defineUntypedSchema({
vue: {
/**
* Properties that will be set directly on `Vue.config` for vue@2.
*
* @see [vue@2 Documentation](https://v2.vuejs.org/v2/api/#Global-Config)
* @type {typeof import('vue/types/vue').VueConfiguration}
*/
Expand All @@ -24,18 +24,75 @@ export default defineUntypedSchema({
app: {
/**
* The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set).
*
* @deprecated - use `buildAssetsDir` instead
*/
assetsPath: {
$resolve: async (val, get) => val ?? (await get('buildAssetsDir'))
},
/**
* Set default configuration for `<head>` on every page.
* @example
* ```js
* app: {
* head: {
* meta: [
* // <meta name="viewport" content="width=device-width, initial-scale=1">
* { name: 'viewport', content: 'width=device-width, initial-scale=1' }
* ],
* script: [
* // <script src="https://myawesome-lib.js"></script>
* { src: 'https://awesome-lib.js' }
* ],
* link: [
* // <link rel="stylesheet" href="https://myawesome-lib.css">
* { rel: 'stylesheet', href: 'https://awesome-lib.css' }
* ],
* // please note that this is an area that is likely to change
* style: [
* // <style type="text/css">:root { color: red }</style>
* { children: ':root { color: red }', type: 'text/css' }
* ],
* noscript: [
* // <noscript>JavaScript is required</noscript>
* { children: 'JavaScript is required' }
* ]
* }
* }
* ```
* @type {typeof import('../src/types/head').AppHeadMetaObject}
*/
head: {
$resolve: async (val, get) => {
const resolved: Required<AppHeadMetaObject> = defu(val, await get('meta'), {
meta: [],
link: [],
style: [],
script: [],
noscript: []
})

// provides default charset and viewport if not set
if (!resolved.meta.find(m => m.charset)?.charset) {
resolved.meta.unshift({ charset: resolved.charset || 'utf-8' })
}
if (!resolved.meta.find(m => m.name === 'viewport')?.content) {
resolved.meta.unshift({ name: 'viewport', content: resolved.viewport || 'width=device-width, initial-scale=1' })
}

resolved.meta = resolved.meta.filter(Boolean)
resolved.link = resolved.link.filter(Boolean)
resolved.style = resolved.style.filter(Boolean)
resolved.script = resolved.script.filter(Boolean)
resolved.noscript = resolved.noscript.filter(Boolean)

return resolved
}
}
},

/**
* The path to an HTML template file for rendering Nuxt responses.
* Uses `<srcDir>/app.html` if it exists, or the Nuxt's default template if not.
*
* @example
* ```html
* <!DOCTYPE html>
Expand Down Expand Up @@ -75,15 +132,13 @@ export default defineUntypedSchema({

/**
* Options to pass directly to `vue-meta`.
*
* @see [documentation](https://vue-meta.nuxtjs.org/api/#plugin-options).
* @type {typeof import('vue-meta').VueMetaOptions}
*/
vueMeta: null,

/**
* Set default configuration for `<head>` on every page.
*
* @see [documentation](https://vue-meta.nuxtjs.org/api/#metainfo-properties) for specifics.
* @type {typeof import('vue-meta').MetaInfo}
*/
Expand Down Expand Up @@ -123,7 +178,6 @@ export default defineUntypedSchema({
* You may want to extend plugins or change their order. For this, you can pass
* a function using `extendPlugins`. It accepts an array of plugin objects and
* should return an array of plugin objects.
*
* @type {(plugins: Array<{ src: string, mode?: 'client' | 'server' }>) => Array<{ src: string, mode?: 'client' | 'server' }>}
*/
extendPlugins: null,
Expand All @@ -132,7 +186,6 @@ export default defineUntypedSchema({
* An object where each key name maps to a path to a layout .vue file.
*
* Normally, there is no need to configure this directly.
*
* @type {Record<string, string>}
*/
layouts: {},
Expand All @@ -141,7 +194,6 @@ export default defineUntypedSchema({
* Set a custom error page layout.
*
* Normally, there is no need to configure this directly.
*
* @type {string}
*/
ErrorPage: null,
Expand Down Expand Up @@ -206,7 +258,6 @@ export default defineUntypedSchema({
*
* You can either pass a string (the transition name) or an object with properties to bind
* to the `<Transition>` component that will wrap your pages.
*
* @see [vue@2 documentation](https://v2.vuejs.org/v2/guide/transitions.html)
* @see [vue@3 documentation](https://vuejs.org/guide/built-ins/transition-group.html#enter-leave-transitions)
*/
Expand All @@ -229,7 +280,6 @@ export default defineUntypedSchema({
*
* You can either pass a string (the transition name) or an object with properties to bind
* to the `<Transition>` component that will wrap your layouts.
*
* @see [vue@2 documentation](https://v2.vuejs.org/v2/guide/transitions.html)
*/
layoutTransition: {
Expand Down
31 changes: 31 additions & 0 deletions packages/bridge-schema/src/types/head.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Head, MergeHead } from '@unhead/schema'

/** @deprecated Extend types from `@unhead/schema` directly. This may be removed in a future minor version. */
export interface HeadAugmentations extends MergeHead {
// runtime type modifications
base?: {}
link?: {}
meta?: {}
style?: {}
script?: {}
noscript?: {}
htmlAttrs?: {}
bodyAttrs?: {}
}

export type MetaObjectRaw = Head<HeadAugmentations>
export type MetaObject = MetaObjectRaw

export type AppHeadMetaObject = MetaObjectRaw & {
/**
* The character encoding in which the document is encoded => `<meta charset="<value>" />`
* @default `'utf-8'`
*/
charset?: string
/**
* Configuration of the viewport (the area of the window in which web content can be seen),
* mapped to => `<meta name="viewport" content="<value>" />`
* @default `'width=device-width, initial-scale=1'`
*/
viewport?: string
}
3 changes: 2 additions & 1 deletion packages/bridge/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineBuildConfig({
'webpack',
'vite',
'vue',
'vue-meta'
'vue-meta',
'@unhead/vue'
]
})
3 changes: 2 additions & 1 deletion packages/bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"@nuxt/postcss8": "^1.1.3",
"@nuxt/schema": "3.5.3",
"@nuxt/ui-templates": "^1.2.0",
"@unhead/ssr": "^1.1.27",
"@unhead/vue": "^1.1.27",
"@vitejs/plugin-legacy": "^4.0.4",
"@vitejs/plugin-vue2": "^2.2.0",
"acorn": "^8.9.0",
Expand Down Expand Up @@ -79,7 +81,6 @@
"@types/fs-extra": "^9.0.13",
"@types/hash-sum": "^1.0.0",
"@types/node-fetch": "^3.0.2",
"@vueuse/head": "^1.1.26",
"nuxt": "^2.17.0",
"unbuild": "1.2.1",
"vue": "^2.7.14",
Expand Down
41 changes: 33 additions & 8 deletions packages/bridge/src/head.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { resolve } from 'pathe'
import { addPlugin, addTemplate, defineNuxtModule, tryResolveModule } from '@nuxt/kit'
import { addComponent, addImportsSources, addPlugin, addTemplate, defineNuxtModule } from '@nuxt/kit'
import { defu } from 'defu'
import type { MetaObject } from '@nuxt/schema'
import { distDir } from './dirs'

const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body']

export default defineNuxtModule({
meta: {
name: 'meta'
Expand All @@ -15,13 +17,26 @@ export default defineNuxtModule({
setup (options, nuxt) {
const runtimeDir = nuxt.options.alias['#head'] || resolve(distDir, 'head/runtime')

// Transpile @nuxt/meta and @vueuse/head
nuxt.options.build.transpile.push('@vueuse/head')
// Transpile @unhead/vue and @unhead/ssr
nuxt.options.build.transpile.push('unhead')

// Add #head alias
nuxt.options.alias['#head'] = runtimeDir

// Register components
const componentsPath = resolve(runtimeDir, 'components')
for (const componentName of components) {
addComponent({
name: componentName,
filePath: componentsPath,
export: componentName,
// built-in that we do not expect the user to override
priority: 10,
// kebab case version of these tags is not valid
kebabName: componentName
})
}

// Global meta -for Bridge, this is necessary to repeat here
// and in packages/schema/src/config/_app.ts
const globalMeta: MetaObject = defu(nuxt.options.app.head, {
Expand All @@ -35,14 +50,24 @@ export default defineNuxtModule({
getContents: () => 'export default ' + JSON.stringify({ globalMeta, mixinKey: 'setup' })
})

if (!tryResolveModule('@vueuse/head')) {
console.warn('[bridge] Could not find `@vueuse/head`. You may need to install it.')
}
addImportsSources({
from: '@unhead/vue',
// hard-coded for now we so don't support auto-imports on the deprecated composables
imports: [
'injectHead',
'useHead',
'useSeoMeta',
'useHeadSafe',
'useServerHead',
'useServerSeoMeta',
'useServerHeadSafe'
]
})

// Add generic plugin
addPlugin({ src: resolve(runtimeDir, 'plugin') })

// Add library specific plugin
addPlugin({ src: resolve(runtimeDir, 'vueuse-head.plugin') })
// Add library-specific plugin
addPlugin({ src: resolve(runtimeDir, 'plugins/unhead') })
}
})
1 change: 0 additions & 1 deletion packages/bridge/src/imports/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export const commonPresets: InlinePreset[] = [
defineUnimportPreset({
from: '#head',
imports: [
'useHead',
'useMeta'
]
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge/src/runtime/app.plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default async (ctx, inject) => {
provide: inject,
unmount: () => { },
use (vuePlugin) {
runOnceWith(vuePlugin, () => vuePlugin.install(this))
runOnceWith(vuePlugin, () => Vue.use(vuePlugin))
},
version
},
Expand Down
Loading

0 comments on commit 3e3cf68

Please sign in to comment.