Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

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

Merged
merged 14 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 6 additions & 3 deletions packages/bridge/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
4 changes: 3 additions & 1 deletion packages/bridge/src/global-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
]
Expand All @@ -17,6 +16,9 @@ const identifiers = {
'useRouter',
'useRuntimeConfig'
],
'#meta': [
'useMeta'
],
'@vue/composition-api': [
// lifecycle
'onActivated',
Expand Down
33 changes: 33 additions & 0 deletions packages/bridge/src/meta.ts
Original file line number Diff line number Diff line change
@@ -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)
}
5 changes: 5 additions & 0 deletions packages/bridge/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -18,6 +19,7 @@ export default defineNuxtModule({
capi: {},
globalImports: true,
constraints: true,
meta: null,
// TODO: Remove from 2.16
postcss8: true,
typescript: true,
Expand Down Expand Up @@ -66,5 +68,8 @@ export default defineNuxtModule({
}
})
}
if (opts.meta !== false && opts.capi) {
await setupMeta({ needsExplicitEnable: opts.meta === null })
}
}
})
21 changes: 21 additions & 0 deletions packages/bridge/src/runtime/app.plugin.mjs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
})
Expand Down
1 change: 1 addition & 0 deletions packages/bridge/src/runtime/meta
50 changes: 36 additions & 14 deletions packages/bridge/src/runtime/nitro-bridge.server.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions packages/bridge/src/runtime/vue.mjs
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions packages/bridge/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions packages/kit/src/config/schema/_app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions packages/nuxt3/src/app/nuxt.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions packages/nuxt3/src/meta/module.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -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')
Expand All @@ -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
Expand Down
7 changes: 4 additions & 3 deletions packages/nuxt3/src/meta/runtime/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
danielroe marked this conversation as resolved.
Show resolved Hide resolved
if (!options || !('head' in options)) { return }

useMeta((instance.type as any).head)
useMeta(options.head)
}
})

Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/bridge/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down