Skip to content

Commit 383f707

Browse files
authored
fix: add og:title,url,description meta tags and prefix og:image with host (#1769)
1 parent b2eb178 commit 383f707

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-7
lines changed

src/module.ts

+4
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export interface ModuleOptions {
204204
* @default false
205205
*/
206206
documentDriven: boolean | {
207+
host?: string
207208
page?: boolean
208209
navigation?: boolean
209210
surround?: boolean
@@ -212,6 +213,7 @@ export interface ModuleOptions {
212213
}
213214
layoutFallbacks?: string[]
214215
injectPage?: boolean
216+
trailingSlash?: boolean
215217
},
216218
experimental: {
217219
clientDB: boolean
@@ -575,6 +577,8 @@ export default defineNuxtModule<ModuleOptions>({
575577
wsUrl: '',
576578
// Document-driven configuration
577579
documentDriven: options.documentDriven as any,
580+
host: typeof options.documentDriven !== 'boolean' ? options.documentDriven?.host ?? '' : '',
581+
trailingSlash: typeof options.documentDriven !== 'boolean' ? options.documentDriven?.trailingSlash ?? false : false,
578582
// Anchor link generation config
579583
anchorLinks: options.markdown.anchorLinks
580584
})

src/runtime/composables/head.ts

+50-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router'
22
import type { HeadObjectPlain } from '@vueuse/head'
33
import type { Ref } from 'vue'
4+
import { hasProtocol, joinURL, withTrailingSlash, withoutTrailingSlash } from 'ufo'
45
import { ParsedContent } from '../types'
56
import { useRoute, nextTick, useHead, unref, watch } from '#imports'
67

@@ -9,6 +10,7 @@ export const useContentHead = (
910
to: RouteLocationNormalized | RouteLocationNormalizedLoaded = useRoute()
1011
) => {
1112
const content = unref(_content)
13+
const config = useRuntimeConfig()
1214

1315
const refreshHead = (data: ParsedContent = content) => {
1416
// Don't call this function if no route is yet available
@@ -17,12 +19,45 @@ export const useContentHead = (
1719
// Default head to `data?.head`
1820
const head: HeadObjectPlain = Object.assign({}, data?.head || {})
1921

22+
head.meta = [...(head.meta || [])]
23+
head.link = [...(head.link || [])]
24+
2025
// Great basic informations from the data
2126
const title = head.title || data?.title
2227
if (title) {
2328
head.title = title
29+
if (process.server && !head.meta.some(m => m.property === 'og:title')) {
30+
head.meta.push({
31+
name: 'og:title',
32+
content: title
33+
})
34+
}
35+
}
36+
37+
let host = config.public.content.host
38+
if (process.server && !host) {
39+
const req = useRequestEvent().node?.req
40+
if (req) {
41+
const protocol = req.headers['x-forwarded-proto'] || req.connection.encrypted ? 'https' : 'http'
42+
host = `${protocol}://${req.headers.host}`
43+
}
44+
}
45+
if (process.server && host) {
46+
const _url = joinURL(host ?? '/', config.app.baseURL, to.fullPath)
47+
const url = config.public.content.trailingSlash ? withTrailingSlash(_url) : withoutTrailingSlash(_url)
48+
if (!head.meta.some(m => m.property === 'og:url')) {
49+
head.meta.push({
50+
name: 'og:url',
51+
content: url
52+
})
53+
}
54+
if (!head.link.some(m => m.rel === 'canonical')) {
55+
head.link.push({
56+
rel: 'canonical',
57+
href: url
58+
})
59+
}
2460
}
25-
head.meta = [...(head.meta || [])]
2661

2762
// Grab description from `head.description` or fallback to `data.description`
2863
// @ts-ignore - We expect `head.description` from Nuxt configurations...
@@ -35,25 +70,31 @@ export const useContentHead = (
3570
content: description
3671
})
3772
}
73+
if (process.server && description && !head.meta.some(m => m.property === 'og:description')) {
74+
head.meta.push({
75+
name: 'og:description',
76+
content: description
77+
})
78+
}
3879

3980
// Grab description from `head` or fallback to `data.description`
4081
// @ts-ignore - We expect `head.image` from Nuxt configurations...
4182
const image = head?.image || data?.image
4283

4384
// Shortcut for head.image to og:image in meta
44-
if (image && head.meta.filter(m => m.property === 'og:image').length === 0) {
45-
// Handles `image: '/image/src.jpg'`
85+
if (process.server && image && head.meta.filter(m => m.property === 'og:image').length === 0) {
86+
// Handles `image: '/image/src.jpg'`
4687
if (typeof image === 'string') {
4788
head.meta.push({
4889
property: 'og:image',
4990
// @ts-ignore - We expect `head.image` from Nuxt configurations...
50-
content: image
91+
content: host && !hasProtocol(image) ? new URL(joinURL(config.app.baseURL, image), url).href : image
5192
})
5293
}
5394

5495
// Handles: `image.src: '/image/src.jpg'` & `image.alt: 200`...
5596
if (typeof image === 'object') {
56-
// https://ogp.me/#structured
97+
// https://ogp.me/#structured
5798
const imageKeys = [
5899
'src',
59100
'secure_url',
@@ -65,11 +106,13 @@ export const useContentHead = (
65106

66107
// Look on available keys
67108
for (const key of imageKeys) {
68-
// `src` is a shorthand for the URL.
109+
// `src` is a shorthand for the URL.
69110
if (key === 'src' && image.src) {
111+
const isAbsoluteURL = hasProtocol(image.src)
112+
const imageURL = isAbsoluteURL ? image.src : joinURL(config.app.baseURL, image.src ?? '/')
70113
head.meta.push({
71114
property: 'og:image',
72-
content: image[key]
115+
content: host && !isAbsoluteURL ? new URL(imageURL, url).href : imageURL
73116
})
74117
} else if (image[key]) {
75118
head.meta.push({

0 commit comments

Comments
 (0)