1
1
import { RouteLocationNormalized , RouteLocationNormalizedLoaded } from 'vue-router'
2
2
import type { HeadObjectPlain } from '@vueuse/head'
3
3
import type { Ref } from 'vue'
4
+ import { hasProtocol , joinURL , withTrailingSlash , withoutTrailingSlash } from 'ufo'
4
5
import { ParsedContent } from '../types'
5
6
import { useRoute , nextTick , useHead , unref , watch } from '#imports'
6
7
@@ -9,6 +10,7 @@ export const useContentHead = (
9
10
to : RouteLocationNormalized | RouteLocationNormalizedLoaded = useRoute ( )
10
11
) => {
11
12
const content = unref ( _content )
13
+ const config = useRuntimeConfig ( )
12
14
13
15
const refreshHead = ( data : ParsedContent = content ) => {
14
16
// Don't call this function if no route is yet available
@@ -17,12 +19,45 @@ export const useContentHead = (
17
19
// Default head to `data?.head`
18
20
const head : HeadObjectPlain = Object . assign ( { } , data ?. head || { } )
19
21
22
+ head . meta = [ ...( head . meta || [ ] ) ]
23
+ head . link = [ ...( head . link || [ ] ) ]
24
+
20
25
// Great basic informations from the data
21
26
const title = head . title || data ?. title
22
27
if ( title ) {
23
28
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
+ }
24
60
}
25
- head . meta = [ ...( head . meta || [ ] ) ]
26
61
27
62
// Grab description from `head.description` or fallback to `data.description`
28
63
// @ts -ignore - We expect `head.description` from Nuxt configurations...
@@ -35,25 +70,31 @@ export const useContentHead = (
35
70
content : description
36
71
} )
37
72
}
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
+ }
38
79
39
80
// Grab description from `head` or fallback to `data.description`
40
81
// @ts -ignore - We expect `head.image` from Nuxt configurations...
41
82
const image = head ?. image || data ?. image
42
83
43
84
// 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'`
46
87
if ( typeof image === 'string' ) {
47
88
head . meta . push ( {
48
89
property : 'og:image' ,
49
90
// @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
51
92
} )
52
93
}
53
94
54
95
// Handles: `image.src: '/image/src.jpg'` & `image.alt: 200`...
55
96
if ( typeof image === 'object' ) {
56
- // https://ogp.me/#structured
97
+ // https://ogp.me/#structured
57
98
const imageKeys = [
58
99
'src' ,
59
100
'secure_url' ,
@@ -65,11 +106,13 @@ export const useContentHead = (
65
106
66
107
// Look on available keys
67
108
for ( const key of imageKeys ) {
68
- // `src` is a shorthand for the URL.
109
+ // `src` is a shorthand for the URL.
69
110
if ( key === 'src' && image . src ) {
111
+ const isAbsoluteURL = hasProtocol ( image . src )
112
+ const imageURL = isAbsoluteURL ? image . src : joinURL ( config . app . baseURL , image . src ?? '/' )
70
113
head . meta . push ( {
71
114
property : 'og:image' ,
72
- content : image [ key ]
115
+ content : host && ! isAbsoluteURL ? new URL ( imageURL , url ) . href : imageURL
73
116
} )
74
117
} else if ( image [ key ] ) {
75
118
head . meta . push ( {
0 commit comments