Skip to content

Commit 9eebf49

Browse files
committed
chore: move useContentHead to composables directory
1 parent ebc88b2 commit 9eebf49

File tree

3 files changed

+114
-112
lines changed

3 files changed

+114
-112
lines changed

src/module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export default defineNuxtModule<ModuleOptions>({
115115
{ name: 'queryCollectionSearchSections', from: resolver.resolve('./runtime/app') },
116116
{ name: 'queryCollectionNavigation', from: resolver.resolve('./runtime/app') },
117117
{ name: 'queryCollectionItemSurroundings', from: resolver.resolve('./runtime/app') },
118-
{ name: 'useContentHead', from: resolver.resolve('./runtime/app') },
118+
{ name: 'useContentHead', from: resolver.resolve('./runtime/composables/useContentHead') },
119119
])
120120
addServerImports([
121121
{ name: 'queryCollectionWithEvent', as: 'queryCollection', from: resolver.resolve('./runtime/nitro') },

src/runtime/app.ts

Lines changed: 2 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import type { Collections, PageCollections, CollectionQueryBuilder, SurroundOptions, PageCollectionItemBase } from '@nuxt/content'
2-
import { hasProtocol, withTrailingSlash, withoutTrailingSlash, joinURL } from 'ufo'
1+
import type { Collections, PageCollections, CollectionQueryBuilder, SurroundOptions } from '@nuxt/content'
32
import { collectionQureyBuilder } from './internal/query'
43
import { measurePerformance } from './internal/performance'
54
import { generateNavigationTree } from './internal/navigation'
65
import { generateItemSurround } from './internal/surround'
76
import { generateSearchSections } from './internal/search'
87
import { fetchQuery } from './internal/api'
9-
import { tryUseNuxtApp, useRuntimeConfig, useRoute, useHead } from '#imports'
8+
import { tryUseNuxtApp } from '#imports'
109

1110
export const queryCollection = <T extends keyof Collections>(collection: T): CollectionQueryBuilder<Collections[T]> => {
1211
return collectionQureyBuilder<T>(collection, (collection, sql) => executeContentQuery(collection, sql))
@@ -46,111 +45,3 @@ async function queryContentSqlClientWasm<T extends keyof Collections, Result = C
4645

4746
return rows as Result[]
4847
}
49-
50-
export const useContentHead = (content: PageCollectionItemBase | null, { host, trailingSlash }: { host?: string, trailingSlash?: boolean } = {}) => {
51-
if (!content) {
52-
return
53-
}
54-
55-
const config = useRuntimeConfig()
56-
const route = useRoute()
57-
// Default head to `data?.seo`
58-
const head = Object.assign({} as unknown as Exclude<PageCollectionItemBase['seo'], undefined>, content?.seo || {})
59-
60-
head.meta = [...(head.meta || [])]
61-
head.link = [...(head.link || [])]
62-
63-
// Great basic informations from the content
64-
head.title = head.title || content?.title
65-
if (head.title) {
66-
if (import.meta.server && !head.meta?.some(m => m.property === 'og:title')) {
67-
head.meta.push({
68-
property: 'og:title',
69-
content: head.title as string,
70-
})
71-
}
72-
}
73-
74-
if (import.meta.server && host) {
75-
const _url = joinURL(host ?? '/', config.app.baseURL, route.fullPath)
76-
const url = trailingSlash ? withTrailingSlash(_url) : withoutTrailingSlash(_url)
77-
if (!head.meta.some(m => m.property === 'og:url')) {
78-
head.meta.push({
79-
property: 'og:url',
80-
content: url,
81-
})
82-
}
83-
if (!head.link.some(m => m.rel === 'canonical')) {
84-
head.link.push({
85-
rel: 'canonical',
86-
href: url,
87-
})
88-
}
89-
}
90-
91-
// Grab description from `head.description` or fallback to `data.description`
92-
const description = head?.description || content?.description
93-
94-
// Shortcut for head.description
95-
if (description && head.meta.filter(m => m.name === 'description').length === 0) {
96-
head.meta.push({
97-
name: 'description',
98-
content: description,
99-
})
100-
}
101-
if (import.meta.server && description && !head.meta.some(m => m.property === 'og:description')) {
102-
head.meta.push({
103-
property: 'og:description',
104-
content: description,
105-
})
106-
}
107-
108-
// Grab description from `head` or fallback to `data.description`
109-
// @ts-expect-error - image does not exists in content
110-
const image = head?.image || content?.image
111-
112-
// Shortcut for head.image to og:image in meta
113-
if (image && head.meta.filter(m => m.property === 'og:image').length === 0) {
114-
// Handles `image: '/image/src.jpg'`
115-
if (typeof image === 'string') {
116-
head.meta.push({
117-
property: 'og:image',
118-
content: host && !hasProtocol(image) ? new URL(joinURL(config.app.baseURL, image), host).href : image,
119-
})
120-
}
121-
122-
// Handles: `image.src: '/image/src.jpg'` & `image.alt: 200`...
123-
if (typeof image === 'object') {
124-
// https://ogp.me/#structured
125-
const imageKeys = [
126-
'src',
127-
'secure_url',
128-
'type',
129-
'width',
130-
'height',
131-
'alt',
132-
]
133-
134-
// Look on available keys
135-
for (const key of imageKeys) {
136-
// `src` is a shorthand for the URL.
137-
if (key === 'src' && image.src) {
138-
const isAbsoluteURL = hasProtocol(image.src)
139-
const imageURL = isAbsoluteURL ? image.src : joinURL(config.app.baseURL, image.src ?? '/')
140-
head.meta.push({
141-
property: 'og:image',
142-
content: host && !isAbsoluteURL ? new URL(imageURL, host).href : imageURL,
143-
})
144-
}
145-
else if (image[key]) {
146-
head.meta.push({
147-
property: `og:image:${key}`,
148-
content: image[key],
149-
})
150-
}
151-
}
152-
}
153-
}
154-
155-
useHead(head)
156-
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import type { PageCollectionItemBase } from '@nuxt/content'
2+
import { hasProtocol, withTrailingSlash, withoutTrailingSlash, joinURL } from 'ufo'
3+
import { useRuntimeConfig, useRoute, useHead } from '#imports'
4+
5+
export const useContentHead = (content: PageCollectionItemBase | null, { host, trailingSlash }: { host?: string, trailingSlash?: boolean } = {}) => {
6+
if (!content) {
7+
return
8+
}
9+
10+
const config = useRuntimeConfig()
11+
const route = useRoute()
12+
// Default head to `data?.seo`
13+
const head = Object.assign({} as unknown as Exclude<PageCollectionItemBase['seo'], undefined>, content?.seo || {})
14+
15+
head.meta = [...(head.meta || [])]
16+
head.link = [...(head.link || [])]
17+
18+
// Great basic informations from the content
19+
head.title = head.title || content?.title
20+
if (head.title) {
21+
if (import.meta.server && !head.meta?.some(m => m.property === 'og:title')) {
22+
head.meta.push({
23+
property: 'og:title',
24+
content: head.title as string,
25+
})
26+
}
27+
}
28+
29+
if (import.meta.server && host) {
30+
const _url = joinURL(host ?? '/', config.app.baseURL, route.fullPath)
31+
const url = trailingSlash ? withTrailingSlash(_url) : withoutTrailingSlash(_url)
32+
if (!head.meta.some(m => m.property === 'og:url')) {
33+
head.meta.push({
34+
property: 'og:url',
35+
content: url,
36+
})
37+
}
38+
if (!head.link.some(m => m.rel === 'canonical')) {
39+
head.link.push({
40+
rel: 'canonical',
41+
href: url,
42+
})
43+
}
44+
}
45+
46+
// Grab description from `head.description` or fallback to `data.description`
47+
const description = head?.description || content?.description
48+
49+
// Shortcut for head.description
50+
if (description && head.meta.filter(m => m.name === 'description').length === 0) {
51+
head.meta.push({
52+
name: 'description',
53+
content: description,
54+
})
55+
}
56+
if (import.meta.server && description && !head.meta.some(m => m.property === 'og:description')) {
57+
head.meta.push({
58+
property: 'og:description',
59+
content: description,
60+
})
61+
}
62+
63+
// Grab description from `head` or fallback to `data.description`
64+
// @ts-expect-error - image does not exists in content
65+
const image = head?.image || content?.image
66+
67+
// Shortcut for head.image to og:image in meta
68+
if (image && head.meta.filter(m => m.property === 'og:image').length === 0) {
69+
// Handles `image: '/image/src.jpg'`
70+
if (typeof image === 'string') {
71+
head.meta.push({
72+
property: 'og:image',
73+
content: host && !hasProtocol(image) ? new URL(joinURL(config.app.baseURL, image), host).href : image,
74+
})
75+
}
76+
77+
// Handles: `image.src: '/image/src.jpg'` & `image.alt: 200`...
78+
if (typeof image === 'object') {
79+
// https://ogp.me/#structured
80+
const imageKeys = [
81+
'src',
82+
'secure_url',
83+
'type',
84+
'width',
85+
'height',
86+
'alt',
87+
]
88+
89+
// Look on available keys
90+
for (const key of imageKeys) {
91+
// `src` is a shorthand for the URL.
92+
if (key === 'src' && image.src) {
93+
const isAbsoluteURL = hasProtocol(image.src)
94+
const imageURL = isAbsoluteURL ? image.src : joinURL(config.app.baseURL, image.src ?? '/')
95+
head.meta.push({
96+
property: 'og:image',
97+
content: host && !isAbsoluteURL ? new URL(imageURL, host).href : imageURL,
98+
})
99+
}
100+
else if (image[key]) {
101+
head.meta.push({
102+
property: `og:image:${key}`,
103+
content: image[key],
104+
})
105+
}
106+
}
107+
}
108+
}
109+
110+
useHead(head)
111+
}

0 commit comments

Comments
 (0)