mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat(bridge): use useMeta
in bridge projects (#664)
This commit is contained in:
parent
dd73a8bcad
commit
a07b67ce57
@ -20,6 +20,7 @@
|
|||||||
"@nuxt/nitro": "3.0.0",
|
"@nuxt/nitro": "3.0.0",
|
||||||
"@nuxt/postcss8": "^1.1.3",
|
"@nuxt/postcss8": "^1.1.3",
|
||||||
"@vue/composition-api": "^1.2.3",
|
"@vue/composition-api": "^1.2.3",
|
||||||
|
"@vueuse/head": "^0.6.0",
|
||||||
"acorn": "^8.5.0",
|
"acorn": "^8.5.0",
|
||||||
"defu": "^5.0.0",
|
"defu": "^5.0.0",
|
||||||
"enhanced-resolve": "^5.8.3",
|
"enhanced-resolve": "^5.8.3",
|
||||||
|
@ -12,9 +12,12 @@ export function setupAppBridge (_options: any) {
|
|||||||
// Transpile runtime/
|
// Transpile runtime/
|
||||||
nuxt.options.build.transpile.push(resolve(distDir, 'runtime'))
|
nuxt.options.build.transpile.push(resolve(distDir, 'runtime'))
|
||||||
|
|
||||||
// Resolve to same vue2 path
|
// Alias vue to a vue3-compat version of vue2
|
||||||
nuxt.options.alias.vue = nuxt.options.alias.vue ||
|
nuxt.options.alias['#vue'] = nuxt.options.alias.vue || resolveModule('vue/dist/vue.runtime.esm.js', { paths: nuxt.options.modulesDir })
|
||||||
resolveModule('vue/dist/vue.runtime.esm.js', { paths: nuxt.options.modulesDir })
|
nuxt.options.alias['@vue/shared'] = 'vue'
|
||||||
|
nuxt.options.alias['@vue/reactivity'] = 'vue'
|
||||||
|
nuxt.options.alias.vue = resolve(distDir, 'runtime/vue.mjs')
|
||||||
|
nuxt.options.build.transpile.push('vue')
|
||||||
|
|
||||||
// Deprecate various Nuxt options
|
// Deprecate various Nuxt options
|
||||||
if (nuxt.options.globalName !== 'nuxt') {
|
if (nuxt.options.globalName !== 'nuxt') {
|
||||||
|
@ -3,7 +3,6 @@ import globalImports from '../../nuxt3/src/global-imports/module'
|
|||||||
|
|
||||||
// TODO: implement these: https://github.com/nuxt/framework/issues/549
|
// TODO: implement these: https://github.com/nuxt/framework/issues/549
|
||||||
const disabled = [
|
const disabled = [
|
||||||
'useMeta',
|
|
||||||
'useAsyncData',
|
'useAsyncData',
|
||||||
'asyncData'
|
'asyncData'
|
||||||
]
|
]
|
||||||
@ -17,6 +16,9 @@ const identifiers = {
|
|||||||
'useRouter',
|
'useRouter',
|
||||||
'useRuntimeConfig'
|
'useRuntimeConfig'
|
||||||
],
|
],
|
||||||
|
'#meta': [
|
||||||
|
'useMeta'
|
||||||
|
],
|
||||||
'@vue/composition-api': [
|
'@vue/composition-api': [
|
||||||
// lifecycle
|
// lifecycle
|
||||||
'onActivated',
|
'onActivated',
|
||||||
|
33
packages/bridge/src/meta.ts
Normal file
33
packages/bridge/src/meta.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { resolve } from 'pathe'
|
||||||
|
import { addTemplate, useNuxt, installModule } from '@nuxt/kit'
|
||||||
|
import metaModule from '../../nuxt3/src/meta/module'
|
||||||
|
import { distDir } from './dirs'
|
||||||
|
|
||||||
|
const checkDocsMsg = 'Please see https://v3.nuxtjs.org for more information.'
|
||||||
|
const msgPrefix = '[bridge] [meta]'
|
||||||
|
|
||||||
|
interface SetupMetaOptions {
|
||||||
|
needsExplicitEnable?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupMeta = async (opts: SetupMetaOptions) => {
|
||||||
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
|
if (opts.needsExplicitEnable) {
|
||||||
|
const metaPath = addTemplate({
|
||||||
|
filename: 'meta.mjs',
|
||||||
|
getContents: () => `export const useMeta = () => console.warn('${msgPrefix} To use \`useMeta\`, please set \`bridge.meta\` to \`true\` in your \`nuxt.config\`. ${checkDocsMsg}')`
|
||||||
|
})
|
||||||
|
nuxt.options.alias['#meta'] = metaPath.dst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nuxt.options.head && typeof nuxt.options.head === 'function') {
|
||||||
|
throw new TypeError(`${msgPrefix} The head() function in \`nuxt.config\` has been deprecated and in nuxt3 will need to be moved to \`app.vue\`. ${checkDocsMsg}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const runtimeDir = resolve(distDir, 'runtime/meta')
|
||||||
|
nuxt.options.alias['#meta'] = runtimeDir
|
||||||
|
|
||||||
|
await installModule(nuxt, metaModule)
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { setupCAPIBridge } from './capi'
|
|||||||
import { setupBetterResolve } from './resolve'
|
import { setupBetterResolve } from './resolve'
|
||||||
import { setupGlobalImports } from './global-imports'
|
import { setupGlobalImports } from './global-imports'
|
||||||
import { setupTypescript } from './typescript'
|
import { setupTypescript } from './typescript'
|
||||||
|
import { setupMeta } from './meta'
|
||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
name: 'nuxt-bridge',
|
name: 'nuxt-bridge',
|
||||||
@ -18,6 +19,7 @@ export default defineNuxtModule({
|
|||||||
capi: {},
|
capi: {},
|
||||||
globalImports: true,
|
globalImports: true,
|
||||||
constraints: true,
|
constraints: true,
|
||||||
|
meta: null,
|
||||||
// TODO: Remove from 2.16
|
// TODO: Remove from 2.16
|
||||||
postcss8: true,
|
postcss8: true,
|
||||||
typescript: true,
|
typescript: true,
|
||||||
@ -66,5 +68,8 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (opts.meta !== false && opts.capi) {
|
||||||
|
await setupMeta({ needsExplicitEnable: opts.meta === null })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,14 +1,31 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
import { createHooks } from 'hookable/dist/index.mjs'
|
import { createHooks } from 'hookable/dist/index.mjs'
|
||||||
import { setNuxtInstance } from '#app'
|
import { setNuxtInstance } from '#app'
|
||||||
|
|
||||||
export default (ctx, inject) => {
|
export default (ctx, inject) => {
|
||||||
const nuxt = {
|
const nuxt = {
|
||||||
|
app: {
|
||||||
|
component: Vue.component.bind(Vue),
|
||||||
|
config: {
|
||||||
|
globalProperties: {}
|
||||||
|
},
|
||||||
|
directive: Vue.directive.bind(Vue),
|
||||||
|
mixin: Vue.mixin.bind(Vue),
|
||||||
|
mount: () => {},
|
||||||
|
provide: inject,
|
||||||
|
unmount: () => {},
|
||||||
|
use (vuePlugin) {
|
||||||
|
vuePlugin.install(this)
|
||||||
|
},
|
||||||
|
version: Vue.version
|
||||||
|
},
|
||||||
provide: inject,
|
provide: inject,
|
||||||
globalName: 'nuxt',
|
globalName: 'nuxt',
|
||||||
payload: process.client ? ctx.nuxtState : ctx.ssrContext.nuxt,
|
payload: process.client ? ctx.nuxtState : ctx.ssrContext.nuxt,
|
||||||
isHydrating: ctx.isHMR,
|
isHydrating: ctx.isHMR,
|
||||||
legacyNuxt: ctx.app
|
legacyNuxt: ctx.app
|
||||||
}
|
}
|
||||||
|
|
||||||
nuxt.hooks = createHooks()
|
nuxt.hooks = createHooks()
|
||||||
nuxt.hook = nuxt.hooks.hook
|
nuxt.hook = nuxt.hooks.hook
|
||||||
nuxt.callHook = nuxt.hooks.callHook
|
nuxt.callHook = nuxt.hooks.callHook
|
||||||
@ -17,6 +34,10 @@ export default (ctx, inject) => {
|
|||||||
ctx.app.created = [ctx.app.created]
|
ctx.app.created = [ctx.app.created]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.server) {
|
||||||
|
nuxt.ssrContext = ctx.ssrContext
|
||||||
|
}
|
||||||
|
|
||||||
ctx.app.created.push(function () {
|
ctx.app.created.push(function () {
|
||||||
nuxt.legacyApp = this
|
nuxt.legacyApp = this
|
||||||
})
|
})
|
||||||
|
1
packages/bridge/src/runtime/meta
Symbolic link
1
packages/bridge/src/runtime/meta
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../nuxt3/src/meta/runtime
|
@ -1,27 +1,49 @@
|
|||||||
export default ({ ssrContext }) => {
|
const vueMetaRenderer = (nuxt) => {
|
||||||
ssrContext.renderMeta = () => {
|
const meta = nuxt.ssrContext.meta.inject({
|
||||||
const meta = ssrContext.meta.inject({
|
isSSR: nuxt.ssrContext.nuxt.serverRendered,
|
||||||
isSSR: ssrContext.nuxt.serverRendered,
|
ln: process.env.NODE_ENV === 'development'
|
||||||
ln: process.env.NODE_ENV === 'development'
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
htmlAttrs: meta.htmlAttrs.text(),
|
htmlAttrs: meta.htmlAttrs.text(),
|
||||||
headAttrs: meta.headAttrs.text(),
|
headAttrs: meta.headAttrs.text(),
|
||||||
headTags:
|
headTags:
|
||||||
meta.title.text() + meta.base.text() +
|
meta.title.text() + meta.base.text() +
|
||||||
meta.meta.text() + meta.link.text() +
|
meta.meta.text() + meta.link.text() +
|
||||||
meta.style.text() + meta.script.text() +
|
meta.style.text() + meta.script.text() +
|
||||||
meta.noscript.text(),
|
meta.noscript.text(),
|
||||||
bodyAttrs: meta.bodyAttrs.text(),
|
bodyAttrs: meta.bodyAttrs.text(),
|
||||||
bodyScriptsPrepend:
|
bodyScriptsPrepend:
|
||||||
meta.meta.text({ pbody: true }) + meta.link.text({ pbody: true }) +
|
meta.meta.text({ pbody: true }) + meta.link.text({ pbody: true }) +
|
||||||
meta.style.text({ pbody: true }) + meta.script.text({ pbody: true }) +
|
meta.style.text({ pbody: true }) + meta.script.text({ pbody: true }) +
|
||||||
meta.noscript.text({ pbody: true }),
|
meta.noscript.text({ pbody: true }),
|
||||||
bodyScripts:
|
bodyScripts:
|
||||||
meta.meta.text({ body: true }) + meta.link.text({ body: true }) +
|
meta.meta.text({ body: true }) + meta.link.text({ body: true }) +
|
||||||
meta.style.text({ body: true }) + meta.script.text({ body: true }) +
|
meta.style.text({ body: true }) + meta.script.text({ body: true }) +
|
||||||
meta.noscript.text({ body: true })
|
meta.noscript.text({ body: true })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default defineNuxtPlugin((nuxt) => {
|
||||||
|
const metaRenderers = [vueMetaRenderer]
|
||||||
|
|
||||||
|
nuxt.callHook('meta:register', metaRenderers)
|
||||||
|
|
||||||
|
nuxt.ssrContext.renderMeta = async () => {
|
||||||
|
const metadata = {
|
||||||
|
htmlAttrs: '',
|
||||||
|
headAttrs: '',
|
||||||
|
headTags: '',
|
||||||
|
bodyAttrs: '',
|
||||||
|
bodyScriptsPrepend: '',
|
||||||
|
bodyScripts: ''
|
||||||
|
}
|
||||||
|
for await (const renderer of metaRenderers) {
|
||||||
|
const result = await renderer(nuxt)
|
||||||
|
for (const key in result) {
|
||||||
|
metadata[key] += result[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
})
|
||||||
|
7
packages/bridge/src/runtime/vue.mjs
Normal file
7
packages/bridge/src/runtime/vue.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Vue from '#vue'
|
||||||
|
|
||||||
|
export * from '@vue/composition-api'
|
||||||
|
|
||||||
|
export const isFunction = fn => fn instanceof Function
|
||||||
|
|
||||||
|
export { Vue as default }
|
1
packages/bridge/types.d.ts
vendored
1
packages/bridge/types.d.ts
vendored
@ -11,6 +11,7 @@ export interface BridgeConfig {
|
|||||||
swc: boolean
|
swc: boolean
|
||||||
resolve: boolean
|
resolve: boolean
|
||||||
typescript: boolean
|
typescript: boolean
|
||||||
|
meta: boolean | null
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Also inherit from @nuxt/types.NuxtConfig for legacy type compat
|
// TODO: Also inherit from @nuxt/types.NuxtConfig for legacy type compat
|
||||||
|
@ -103,6 +103,22 @@ export default {
|
|||||||
script: []
|
script: []
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default configuration for `<head>` on every page.
|
||||||
|
*
|
||||||
|
* @version 3
|
||||||
|
*/
|
||||||
|
meta: {
|
||||||
|
/** Each item in the array maps to a newly-created `<meta>` element, where object properties map to attributes. */
|
||||||
|
meta: [],
|
||||||
|
/** Each item in the array maps to a newly-created `<link>` element, where object properties map to attributes. */
|
||||||
|
link: [],
|
||||||
|
/** Each item in the array maps to a newly-created `<style>` element, where object properties map to attributes. */
|
||||||
|
style: [],
|
||||||
|
/** Each item in the array maps to a newly-created `<script>` element, where object properties map to attributes. */
|
||||||
|
script: []
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for the Nuxt `fetch()` hook.
|
* Configuration for the Nuxt `fetch()` hook.
|
||||||
* @version 2
|
* @version 2
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-use-before-define */
|
||||||
import { getCurrentInstance, reactive } from 'vue'
|
import { getCurrentInstance, reactive } from 'vue'
|
||||||
import type { App, VNode } from 'vue'
|
import type { App, VNode } from 'vue'
|
||||||
import { createHooks, Hookable } from 'hookable'
|
import { createHooks, Hookable } from 'hookable'
|
||||||
@ -21,6 +22,7 @@ export interface RuntimeNuxtHooks {
|
|||||||
'app:rendered': () => HookResult
|
'app:rendered': () => HookResult
|
||||||
'page:start': (Component?: VNode) => HookResult
|
'page:start': (Component?: VNode) => HookResult
|
||||||
'page:finish': (Component?: VNode) => HookResult
|
'page:finish': (Component?: VNode) => HookResult
|
||||||
|
'meta:register': (metaRenderers: Array<(nuxt: NuxtApp) => NuxtMeta | Promise<NuxtMeta>>) => HookResult
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NuxtApp {
|
export interface NuxtApp {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { addPlugin, addTemplate, defineNuxtModule } from '@nuxt/kit'
|
import { addPlugin, addTemplate, defineNuxtModule, isNuxt3 } from '@nuxt/kit'
|
||||||
|
import defu from 'defu'
|
||||||
import { distDir } from '../dirs'
|
import { distDir } from '../dirs'
|
||||||
import type { MetaObject } from './runtime'
|
import type { MetaObject } from './runtime'
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ export default defineNuxtModule({
|
|||||||
viewport: 'width=device-width, initial-scale=1'
|
viewport: 'width=device-width, initial-scale=1'
|
||||||
},
|
},
|
||||||
setup (options, nuxt) {
|
setup (options, nuxt) {
|
||||||
const runtimeDir = resolve(distDir, 'meta/runtime')
|
const runtimeDir = nuxt.options.alias['#meta'] || resolve(distDir, 'meta/runtime')
|
||||||
|
|
||||||
// Transpile @nuxt/meta and @vueuse/head
|
// Transpile @nuxt/meta and @vueuse/head
|
||||||
nuxt.options.build.transpile.push(runtimeDir, '@vueuse/head')
|
nuxt.options.build.transpile.push(runtimeDir, '@vueuse/head')
|
||||||
@ -19,17 +20,17 @@ export default defineNuxtModule({
|
|||||||
nuxt.options.alias['#meta'] = runtimeDir
|
nuxt.options.alias['#meta'] = runtimeDir
|
||||||
|
|
||||||
// Global meta
|
// Global meta
|
||||||
const globalMeta: MetaObject = {
|
const globalMeta: MetaObject = defu(nuxt.options.meta, {
|
||||||
meta: [
|
meta: [
|
||||||
{ charset: options.charset },
|
{ charset: options.charset },
|
||||||
{ name: 'viewport', content: options.viewport }
|
{ name: 'viewport', content: options.viewport }
|
||||||
]
|
]
|
||||||
}
|
})
|
||||||
|
|
||||||
// Add global meta configuration
|
// Add global meta configuration
|
||||||
addTemplate({
|
addTemplate({
|
||||||
filename: 'meta.config.mjs',
|
filename: 'meta.config.mjs',
|
||||||
getContents: () => 'export default ' + JSON.stringify({ globalMeta })
|
getContents: () => 'export default ' + JSON.stringify({ globalMeta, mixinKey: isNuxt3() ? 'created' : 'setup' })
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add generic plugin
|
// Add generic plugin
|
||||||
|
@ -9,11 +9,12 @@ export default defineNuxtPlugin((nuxt) => {
|
|||||||
useMeta(metaConfig.globalMeta)
|
useMeta(metaConfig.globalMeta)
|
||||||
|
|
||||||
nuxt.app.mixin({
|
nuxt.app.mixin({
|
||||||
created () {
|
[metaConfig.mixinKey] () {
|
||||||
const instance = getCurrentInstance()
|
const instance = getCurrentInstance()
|
||||||
if (!instance?.type || !('head' in instance.type)) { return }
|
const options = instance?.type || /* nuxt2 */ instance?.proxy?.$options
|
||||||
|
if (!options || !('head' in options)) { return }
|
||||||
|
|
||||||
useMeta((instance.type as any).head)
|
useMeta(options.head)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
6
test/fixtures/bridge/pages/index.vue
vendored
6
test/fixtures/bridge/pages/index.vue
vendored
@ -3,11 +3,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useMeta } from '#meta'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup () {
|
setup () {
|
||||||
|
useMeta({ meta: [{ name: 'description', content: 'This is a page to demo Nuxt Bridge.' }] })
|
||||||
return {
|
return {
|
||||||
version: ref('2')
|
version: ref('2')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
head: {
|
||||||
|
title: 'Bridge test fixture'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1427,6 +1427,7 @@ __metadata:
|
|||||||
"@types/fs-extra": ^9.0.13
|
"@types/fs-extra": ^9.0.13
|
||||||
"@types/node-fetch": ^3.0.2
|
"@types/node-fetch": ^3.0.2
|
||||||
"@vue/composition-api": ^1.2.3
|
"@vue/composition-api": ^1.2.3
|
||||||
|
"@vueuse/head": ^0.6.0
|
||||||
acorn: ^8.5.0
|
acorn: ^8.5.0
|
||||||
defu: ^5.0.0
|
defu: ^5.0.0
|
||||||
enhanced-resolve: ^5.8.3
|
enhanced-resolve: ^5.8.3
|
||||||
|
Loading…
Reference in New Issue
Block a user