feat(bridge): use `useMeta` in bridge projects (#664)

This commit is contained in:
Daniel Roe 2021-10-06 14:37:45 +02:00 committed by GitHub
parent dd73a8bcad
commit a07b67ce57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 149 additions and 26 deletions

View File

@ -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",

View File

@ -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') {

View File

@ -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',

View 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)
}

View File

@ -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 })
}
}
})

View File

@ -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
})

View File

@ -0,0 +1 @@
../../../nuxt3/src/meta/runtime

View File

@ -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
}
})

View 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 }

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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)
}
})

View File

@ -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>

View File

@ -1427,6 +1427,7 @@ __metadata:
"@types/fs-extra": ^9.0.13
"@types/node-fetch": ^3.0.2
"@vue/composition-api": ^1.2.3
"@vueuse/head": ^0.6.0
acorn: ^8.5.0
defu: ^5.0.0
enhanced-resolve: ^5.8.3