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/postcss8": "^1.1.3",
|
||||
"@vue/composition-api": "^1.2.3",
|
||||
"@vueuse/head": "^0.6.0",
|
||||
"acorn": "^8.5.0",
|
||||
"defu": "^5.0.0",
|
||||
"enhanced-resolve": "^5.8.3",
|
||||
|
@ -12,9 +12,12 @@ export function setupAppBridge (_options: any) {
|
||||
// Transpile runtime/
|
||||
nuxt.options.build.transpile.push(resolve(distDir, 'runtime'))
|
||||
|
||||
// Resolve to same vue2 path
|
||||
nuxt.options.alias.vue = nuxt.options.alias.vue ||
|
||||
resolveModule('vue/dist/vue.runtime.esm.js', { paths: nuxt.options.modulesDir })
|
||||
// Alias vue to a vue3-compat version of vue2
|
||||
nuxt.options.alias['#vue'] = nuxt.options.alias.vue || 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
|
||||
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
|
||||
const disabled = [
|
||||
'useMeta',
|
||||
'useAsyncData',
|
||||
'asyncData'
|
||||
]
|
||||
@ -17,6 +16,9 @@ const identifiers = {
|
||||
'useRouter',
|
||||
'useRuntimeConfig'
|
||||
],
|
||||
'#meta': [
|
||||
'useMeta'
|
||||
],
|
||||
'@vue/composition-api': [
|
||||
// lifecycle
|
||||
'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 { setupGlobalImports } from './global-imports'
|
||||
import { setupTypescript } from './typescript'
|
||||
import { setupMeta } from './meta'
|
||||
|
||||
export default defineNuxtModule({
|
||||
name: 'nuxt-bridge',
|
||||
@ -18,6 +19,7 @@ export default defineNuxtModule({
|
||||
capi: {},
|
||||
globalImports: true,
|
||||
constraints: true,
|
||||
meta: null,
|
||||
// TODO: Remove from 2.16
|
||||
postcss8: 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 { setNuxtInstance } from '#app'
|
||||
|
||||
export default (ctx, inject) => {
|
||||
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,
|
||||
globalName: 'nuxt',
|
||||
payload: process.client ? ctx.nuxtState : ctx.ssrContext.nuxt,
|
||||
isHydrating: ctx.isHMR,
|
||||
legacyNuxt: ctx.app
|
||||
}
|
||||
|
||||
nuxt.hooks = createHooks()
|
||||
nuxt.hook = nuxt.hooks.hook
|
||||
nuxt.callHook = nuxt.hooks.callHook
|
||||
@ -17,6 +34,10 @@ export default (ctx, inject) => {
|
||||
ctx.app.created = [ctx.app.created]
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
nuxt.ssrContext = ctx.ssrContext
|
||||
}
|
||||
|
||||
ctx.app.created.push(function () {
|
||||
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 }) => {
|
||||
ssrContext.renderMeta = () => {
|
||||
const meta = ssrContext.meta.inject({
|
||||
isSSR: ssrContext.nuxt.serverRendered,
|
||||
ln: process.env.NODE_ENV === 'development'
|
||||
})
|
||||
const vueMetaRenderer = (nuxt) => {
|
||||
const meta = nuxt.ssrContext.meta.inject({
|
||||
isSSR: nuxt.ssrContext.nuxt.serverRendered,
|
||||
ln: process.env.NODE_ENV === 'development'
|
||||
})
|
||||
|
||||
return {
|
||||
htmlAttrs: meta.htmlAttrs.text(),
|
||||
headAttrs: meta.headAttrs.text(),
|
||||
headTags:
|
||||
return {
|
||||
htmlAttrs: meta.htmlAttrs.text(),
|
||||
headAttrs: meta.headAttrs.text(),
|
||||
headTags:
|
||||
meta.title.text() + meta.base.text() +
|
||||
meta.meta.text() + meta.link.text() +
|
||||
meta.style.text() + meta.script.text() +
|
||||
meta.noscript.text(),
|
||||
bodyAttrs: meta.bodyAttrs.text(),
|
||||
bodyScriptsPrepend:
|
||||
bodyAttrs: meta.bodyAttrs.text(),
|
||||
bodyScriptsPrepend:
|
||||
meta.meta.text({ pbody: true }) + meta.link.text({ pbody: true }) +
|
||||
meta.style.text({ pbody: true }) + meta.script.text({ pbody: true }) +
|
||||
meta.noscript.text({ pbody: true }),
|
||||
bodyScripts:
|
||||
bodyScripts:
|
||||
meta.meta.text({ body: true }) + meta.link.text({ body: true }) +
|
||||
meta.style.text({ body: true }) + meta.script.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
|
||||
resolve: boolean
|
||||
typescript: boolean
|
||||
meta: boolean | null
|
||||
}
|
||||
|
||||
// TODO: Also inherit from @nuxt/types.NuxtConfig for legacy type compat
|
||||
|
@ -103,6 +103,22 @@ export default {
|
||||
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.
|
||||
* @version 2
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { getCurrentInstance, reactive } from 'vue'
|
||||
import type { App, VNode } from 'vue'
|
||||
import { createHooks, Hookable } from 'hookable'
|
||||
@ -21,6 +22,7 @@ export interface RuntimeNuxtHooks {
|
||||
'app:rendered': () => HookResult
|
||||
'page:start': (Component?: VNode) => HookResult
|
||||
'page:finish': (Component?: VNode) => HookResult
|
||||
'meta:register': (metaRenderers: Array<(nuxt: NuxtApp) => NuxtMeta | Promise<NuxtMeta>>) => HookResult
|
||||
}
|
||||
|
||||
export interface NuxtApp {
|
||||
|
@ -1,5 +1,6 @@
|
||||
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 type { MetaObject } from './runtime'
|
||||
|
||||
@ -10,7 +11,7 @@ export default defineNuxtModule({
|
||||
viewport: 'width=device-width, initial-scale=1'
|
||||
},
|
||||
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
|
||||
nuxt.options.build.transpile.push(runtimeDir, '@vueuse/head')
|
||||
@ -19,17 +20,17 @@ export default defineNuxtModule({
|
||||
nuxt.options.alias['#meta'] = runtimeDir
|
||||
|
||||
// Global meta
|
||||
const globalMeta: MetaObject = {
|
||||
const globalMeta: MetaObject = defu(nuxt.options.meta, {
|
||||
meta: [
|
||||
{ charset: options.charset },
|
||||
{ name: 'viewport', content: options.viewport }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// Add global meta configuration
|
||||
addTemplate({
|
||||
filename: 'meta.config.mjs',
|
||||
getContents: () => 'export default ' + JSON.stringify({ globalMeta })
|
||||
getContents: () => 'export default ' + JSON.stringify({ globalMeta, mixinKey: isNuxt3() ? 'created' : 'setup' })
|
||||
})
|
||||
|
||||
// Add generic plugin
|
||||
|
@ -9,11 +9,12 @@ export default defineNuxtPlugin((nuxt) => {
|
||||
useMeta(metaConfig.globalMeta)
|
||||
|
||||
nuxt.app.mixin({
|
||||
created () {
|
||||
[metaConfig.mixinKey] () {
|
||||
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>
|
||||
|
||||
<script>
|
||||
import { useMeta } from '#meta'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
useMeta({ meta: [{ name: 'description', content: 'This is a page to demo Nuxt Bridge.' }] })
|
||||
return {
|
||||
version: ref('2')
|
||||
}
|
||||
},
|
||||
head: {
|
||||
title: 'Bridge test fixture'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user