Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 436a09d

Browse files
authored
fix(unhead)!: sync resolve tag engine (#619)
* fix(unhead)!: sync resolve tag engine * chore: clean up * chore: fix types
1 parent a347e4e commit 436a09d

38 files changed

Lines changed: 419 additions & 363 deletions

docs/head/1.guides/2.advanced/11.extending-unhead.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ The recommended way to access the head state is through the `resolveTags` functi
7171
import { injectHead, useHead } from '@unhead/dynamic-import'
7272

7373
const head = injectHead()
74-
const tags = await head.resolveTags()
74+
const tags = head.resolveTags()
7575

7676
// Now you can inspect or manipulate the tags
7777
console.log(tags)

packages/angular/vitest.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
import { resolve } from 'node:path'
12
import { defineProject } from 'vitest/config'
23

34
export default defineProject({
5+
resolve: {
6+
alias: {
7+
'unhead/server': resolve(__dirname, '../unhead/src/server/index.ts'),
8+
'unhead/client': resolve(__dirname, '../unhead/src/client/index.ts'),
9+
'unhead/types': resolve(__dirname, '../unhead/src/types/index.ts'),
10+
'unhead/plugins': resolve(__dirname, '../unhead/src/plugins/index.ts'),
11+
'unhead/utils': resolve(__dirname, '../unhead/src/utils/index.ts'),
12+
'unhead/scripts': resolve(__dirname, '../unhead/src/scripts/index.ts'),
13+
'unhead/parser': resolve(__dirname, '../unhead/src/parser/index.ts'),
14+
'unhead/legacy': resolve(__dirname, '../unhead/src/legacy.ts'),
15+
'unhead': resolve(__dirname, '../unhead/src/index.ts'),
16+
},
17+
},
418
test: {
519
globals: true,
620
environment: 'jsdom',

packages/react/vitest.config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1+
import { resolve } from 'node:path'
12
/// <reference types="vitest" />
23
/// <reference types="vitest/globals" />
34
import { defineProject } from 'vitest/config'
45

56
export default defineProject({
7+
resolve: {
8+
alias: {
9+
'unhead/server': resolve(__dirname, '../unhead/src/server/index.ts'),
10+
'unhead/client': resolve(__dirname, '../unhead/src/client/index.ts'),
11+
'unhead/types': resolve(__dirname, '../unhead/src/types/index.ts'),
12+
'unhead/plugins': resolve(__dirname, '../unhead/src/plugins/index.ts'),
13+
'unhead/utils': resolve(__dirname, '../unhead/src/utils/index.ts'),
14+
'unhead/scripts': resolve(__dirname, '../unhead/src/scripts/index.ts'),
15+
'unhead/parser': resolve(__dirname, '../unhead/src/parser/index.ts'),
16+
'unhead/legacy': resolve(__dirname, '../unhead/src/legacy.ts'),
17+
'unhead': resolve(__dirname, '../unhead/src/index.ts'),
18+
},
19+
},
620
test: {
721
globals: true,
822
},

packages/schema-org/src/plugin.ts

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,6 @@ import {
66
createSchemaOrgGraph,
77
} from './core/graph'
88
import { resolveMeta } from './core/resolve'
9-
import { loadResolver } from './resolver'
10-
11-
// Recursively collect all resolver strings from nested objects and preload them
12-
async function preloadNestedResolvers(obj: any): Promise<void> {
13-
if (!obj || typeof obj !== 'object')
14-
return
15-
16-
const promises: Promise<void>[] = []
17-
18-
if (typeof obj._resolver === 'string') {
19-
const resolverName = obj._resolver
20-
promises.push(loadResolver(resolverName).then((loaded) => {
21-
if (loaded)
22-
obj._resolver = loaded
23-
}))
24-
}
25-
26-
for (const key in obj) {
27-
const val = obj[key]
28-
if (val && typeof val === 'object') {
29-
if (Array.isArray(val)) {
30-
for (const item of val) {
31-
promises.push(preloadNestedResolvers(item))
32-
}
33-
}
34-
else {
35-
promises.push(preloadNestedResolvers(val))
36-
}
37-
}
38-
}
39-
40-
await Promise.all(promises)
41-
}
429

4310
// Simple merge utility that recursively merges objects
4411
function mergeObjects(target: any, source: any): any {
@@ -90,23 +57,23 @@ export function SchemaOrgUnheadPlugin(config: MetaInput, meta: () => Partial<Met
9057
return {
9158
key: 'schema-org',
9259
hooks: {
93-
'entries:normalize': async ({ tags }) => {
60+
'entries:normalize': ({ tags }) => {
9461
graph = graph || createSchemaOrgGraph()
9562
for (const tag of tags) {
9663
if (tag.tag === 'script' && tag.props.type === 'application/ld+json' && tag.props.nodes) {
9764
// this is a bit expensive, load in seperate chunk
98-
const nodes = await tag.props.nodes
65+
const nodes = tag.props.nodes
9966
for (const node of Array.isArray(nodes) ? nodes : [nodes]) {
100-
// malformed input
101-
if (typeof node !== 'object' || Object.keys(node).length === 0) {
67+
// malformed input - skip null/undefined but allow empty objects
68+
if (typeof node !== 'object' || node === null) {
10269
continue
10370
}
104-
// Preload all nested resolvers (mutates _resolver strings to loaded objects)
105-
await preloadNestedResolvers(node)
71+
10672
const newNode = {
10773
...node,
10874
_dedupeStrategy: tag.tagDuplicateStrategy,
10975
}
76+
// Push node (it already has _resolver if it came from a defineXXX function)
11077
graph.push(newNode)
11178
}
11279
tag.tagPosition = tag.tagPosition || config.tagPosition === 'head' ? 'head' : 'bodyClose'
@@ -151,7 +118,7 @@ export function SchemaOrgUnheadPlugin(config: MetaInput, meta: () => Partial<Met
151118
const tag = ctx.tags[k]
152119
if (tag.tag === 'script' && tag.props.type === 'application/ld+json' && tag.props.nodes) {
153120
delete tag.props.nodes
154-
const resolvedGraph = graph.resolveGraph({ ...(await meta?.() || {}), ...config, ...resolvedMeta })
121+
const resolvedGraph = graph.resolveGraph({ ...(meta?.() || {}), ...config, ...resolvedMeta })
155122
if (!resolvedGraph.length) {
156123
// removes the tag
157124
tag.props = {}

packages/schema-org/src/runtime.ts

Lines changed: 96 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -48,160 +48,203 @@ import type {
4848
WebPage,
4949
WebSite,
5050
} from './nodes'
51-
import type { Arrayable, Thing } from './types'
51+
import type { Arrayable, SchemaOrgNodeDefinition, Thing } from './types'
52+
import { aggregateOfferResolver } from './nodes/AggregateOffer'
53+
import { aggregateRatingResolver } from './nodes/AggregateRating'
54+
import { articleResolver } from './nodes/Article'
55+
import { bookEditionResolver, bookResolver } from './nodes/Book'
56+
import { breadcrumbResolver } from './nodes/Breadcrumb'
57+
import { commentResolver } from './nodes/Comment'
58+
import { courseResolver } from './nodes/Course'
59+
import { datasetResolver } from './nodes/Dataset'
60+
import { eventResolver } from './nodes/Event'
61+
import { foodEstablishmentResolver } from './nodes/FoodEstablishment'
62+
import { howToResolver } from './nodes/HowTo'
63+
import { howToStepResolver } from './nodes/HowTo/HowToStep'
64+
import { imageResolver } from './nodes/Image'
65+
import { itemListResolver } from './nodes/ItemList'
66+
import { jobPostingResolver } from './nodes/JobPosting'
67+
import { listItemResolver } from './nodes/ListItem'
68+
import { localBusinessResolver } from './nodes/LocalBusiness'
69+
import { movieResolver } from './nodes/Movie'
70+
import { musicAlbumResolver } from './nodes/MusicAlbum'
71+
import { musicGroupResolver } from './nodes/MusicGroup'
72+
import { musicPlaylistResolver } from './nodes/MusicPlaylist'
73+
import { musicRecordingResolver } from './nodes/MusicRecording'
74+
import { offerResolver } from './nodes/Offer'
75+
import { openingHoursResolver } from './nodes/OpeningHours'
76+
import { organizationResolver } from './nodes/Organization'
77+
import { personResolver } from './nodes/Person'
78+
import { placeResolver } from './nodes/Place'
79+
import { podcastEpisodeResolver } from './nodes/PodcastEpisode'
80+
import { podcastSeasonResolver } from './nodes/PodcastSeason'
81+
import { podcastSeriesResolver } from './nodes/PodcastSeries'
82+
import { addressResolver } from './nodes/PostalAddress'
83+
import { productResolver } from './nodes/Product'
84+
import { questionResolver } from './nodes/Question'
85+
import { recipeResolver } from './nodes/Recipe'
86+
import { reviewResolver } from './nodes/Review'
87+
import { serviceResolver } from './nodes/Service'
88+
import { softwareAppResolver } from './nodes/SoftwareApp'
89+
import { tvEpisodeResolver } from './nodes/TVEpisode'
90+
import { tvSeasonResolver } from './nodes/TVSeason'
91+
import { tvSeriesResolver } from './nodes/TVSeries'
92+
import { videoResolver } from './nodes/Video'
93+
import { virtualLocationResolver } from './nodes/VirtualLocation'
94+
import { webPageResolver } from './nodes/WebPage'
95+
import { readActionResolver } from './nodes/WebPage/ReadAction'
96+
import { webSiteResolver } from './nodes/WebSite'
97+
import { searchActionResolver } from './nodes/WebSite/SearchAction'
5298
import { UnheadSchemaOrg } from './plugin'
5399

54-
function provideResolver<T>(input?: T, resolver?: string) {
100+
function provideResolver<T>(input?: T, resolver?: SchemaOrgNodeDefinition<any>): T & { _resolver?: SchemaOrgNodeDefinition<any> } {
55101
if (!input)
56102
input = {} as T
57-
// avoid unreferring by wrapping it in a function
58-
// @ts-expect-error untyped
59-
input._resolver = resolver
60-
return input
103+
return { ...input, _resolver: resolver } as T & { _resolver?: SchemaOrgNodeDefinition<any> }
61104
}
62105

63106
export function defineAddress<T extends Record<string, any>>(input?: PostalAddress & T) {
64-
return provideResolver(input, 'address')
107+
return provideResolver(input, addressResolver)
65108
}
66109
export function defineAggregateOffer<T extends Record<string, any>>(input?: AggregateOffer & T) {
67-
return provideResolver(input, 'aggregateOffer')
110+
return provideResolver(input, aggregateOfferResolver)
68111
}
69112
export function defineAggregateRating<T extends Record<string, any>>(input?: AggregateRating & T) {
70-
return provideResolver(input, 'aggregateRating')
113+
return provideResolver(input, aggregateRatingResolver)
71114
}
72115
export function defineArticle<T extends Record<string, any>>(input?: Article & T) {
73-
return provideResolver(input, 'article')
116+
return provideResolver(input, articleResolver)
74117
}
75118
export function defineBreadcrumb<T extends Record<string, any>>(input?: BreadcrumbList & T) {
76-
return provideResolver(input, 'breadcrumb')
119+
return provideResolver(input, breadcrumbResolver)
77120
}
78121
export function defineComment<T extends Record<string, any>>(input?: Comment & T) {
79-
return provideResolver(input, 'comment')
122+
return provideResolver(input, commentResolver)
80123
}
81124
export function defineEvent<T extends Record<string, any>>(input?: Event & T) {
82-
return provideResolver(input, 'event')
125+
return provideResolver(input, eventResolver)
83126
}
84127
export function defineFoodEstablishment<T extends Record<string, any>>(input?: FoodEstablishment & T) {
85-
return provideResolver(input, 'foodEstablishment')
128+
return provideResolver(input, foodEstablishmentResolver)
86129
}
87130
export function defineVirtualLocation<T extends Record<string, any>>(input?: VirtualLocation & T) {
88-
return provideResolver(input, 'virtualLocation')
131+
return provideResolver(input, virtualLocationResolver)
89132
}
90133
export function definePlace<T extends Record<string, any>>(input?: Place & T) {
91-
return provideResolver(input, 'place')
134+
return provideResolver(input, placeResolver)
92135
}
93136
export function defineHowTo<T extends Record<string, any>>(input?: HowTo & T) {
94-
return provideResolver(input, 'howTo')
137+
return provideResolver(input, howToResolver)
95138
}
96139
export function defineHowToStep<T extends Record<string, any>>(input?: HowToStep & T) {
97-
return provideResolver(input, 'howToStep')
140+
return provideResolver(input, howToStepResolver)
98141
}
99142
export function defineImage<T extends Record<string, any>>(input?: ImageObject & T) {
100-
return provideResolver(input, 'image')
143+
return provideResolver(input, imageResolver)
101144
}
102145
export function defineJobPosting<T extends Record<string, any>>(input?: JobPosting & T) {
103-
return provideResolver(input, 'jobPosting')
146+
return provideResolver(input, jobPostingResolver)
104147
}
105148
export function defineLocalBusiness<T extends Record<string, any>>(input?: LocalBusiness & T) {
106-
return provideResolver(input, 'localBusiness')
149+
return provideResolver(input, localBusinessResolver)
107150
}
108151
export function defineOffer<T extends Record<string, any>>(input?: Offer & T) {
109-
return provideResolver(input, 'offer')
152+
return provideResolver(input, offerResolver)
110153
}
111154
export function defineOpeningHours<T extends Record<string, any>>(input?: OpeningHoursSpecification & T) {
112-
return provideResolver(input, 'openingHours')
155+
return provideResolver(input, openingHoursResolver)
113156
}
114157
export function defineOrganization<T extends Record<string, any>>(input?: Organization & T) {
115-
return provideResolver(input, 'organization')
158+
return provideResolver(input, organizationResolver)
116159
}
117160
export function definePerson<T extends Record<string, any>>(input?: Person & T) {
118-
return provideResolver(input, 'person')
161+
return provideResolver(input, personResolver)
119162
}
120163
export function defineProduct<T extends Record<string, any>>(input?: Product & T) {
121-
return provideResolver(input, 'product')
164+
return provideResolver(input, productResolver)
122165
}
123166
export function defineQuestion<T extends Record<string, any>>(input?: Question & T) {
124-
return provideResolver(input, 'question')
167+
return provideResolver(input, questionResolver)
125168
}
126169
export function defineRecipe<T extends Record<string, any>>(input?: Recipe & T) {
127-
return provideResolver(input, 'recipe')
170+
return provideResolver(input, recipeResolver)
128171
}
129172
export function defineReview<T extends Record<string, any>>(input?: Review & T) {
130-
return provideResolver(input, 'review')
173+
return provideResolver(input, reviewResolver)
131174
}
132175
export function defineVideo<T extends Record<string, any>>(input?: VideoObject & T) {
133-
return provideResolver(input, 'video')
176+
return provideResolver(input, videoResolver)
134177
}
135178
export function defineWebPage<T extends Record<string, any>>(input?: WebPage & T) {
136-
return provideResolver(input, 'webPage')
179+
return provideResolver(input, webPageResolver)
137180
}
138181
export function defineWebSite<T extends Record<string, any>>(input?: WebSite & T) {
139-
return provideResolver(input, 'webSite')
182+
return provideResolver(input, webSiteResolver)
140183
}
141184
export function defineBook<T extends Record<string, any>>(input?: Book & T) {
142-
return provideResolver(input, 'book')
185+
return provideResolver(input, bookResolver)
143186
}
144187
export function defineCourse<T extends Record<string, any>>(input?: Course & T) {
145-
return provideResolver(input, 'course')
188+
return provideResolver(input, courseResolver)
146189
}
147190
export function defineItemList<T extends Record<string, any>>(input?: ItemList & T) {
148-
return provideResolver(input, 'itemList')
191+
return provideResolver(input, itemListResolver)
149192
}
150193
export function defineListItem<T extends Record<string, any>>(input?: ListItem & T) {
151-
return provideResolver(input, 'listItem')
194+
return provideResolver(input, listItemResolver)
152195
}
153196
export function defineMovie<T extends Record<string, any>>(input?: Movie & T) {
154-
return provideResolver(input, 'movie')
197+
return provideResolver(input, movieResolver)
155198
}
156199
export function defineSearchAction<T extends Record<string, any>>(input?: SearchAction & T) {
157-
return provideResolver(input, 'searchAction')
200+
return provideResolver(input, searchActionResolver)
158201
}
159202
export function defineReadAction<T extends Record<string, any>>(input?: ReadAction & T) {
160-
return provideResolver(input, 'readAction')
203+
return provideResolver(input, readActionResolver)
161204
}
162205
export function defineDataset<T extends Record<string, any>>(input?: Dataset & T) {
163-
return provideResolver(input, 'dataset')
206+
return provideResolver(input, datasetResolver)
164207
}
165208
export function defineMusicRecording<T extends Record<string, any>>(input?: MusicRecording & T) {
166-
return provideResolver(input, 'musicRecording')
209+
return provideResolver(input, musicRecordingResolver)
167210
}
168211
export function defineMusicAlbum<T extends Record<string, any>>(input?: MusicAlbum & T) {
169-
return provideResolver(input, 'musicAlbum')
212+
return provideResolver(input, musicAlbumResolver)
170213
}
171214
export function defineMusicGroup<T extends Record<string, any>>(input?: MusicGroup & T) {
172-
return provideResolver(input, 'musicGroup')
215+
return provideResolver(input, musicGroupResolver)
173216
}
174217
export function defineMusicPlaylist<T extends Record<string, any>>(input?: MusicPlaylist & T) {
175-
return provideResolver(input, 'musicPlaylist')
218+
return provideResolver(input, musicPlaylistResolver)
176219
}
177220
export function definePodcastSeries<T extends Record<string, any>>(input?: PodcastSeries & T) {
178-
return provideResolver(input, 'podcastSeries')
221+
return provideResolver(input, podcastSeriesResolver)
179222
}
180223
export function definePodcastEpisode<T extends Record<string, any>>(input?: PodcastEpisode & T) {
181-
return provideResolver(input, 'podcastEpisode')
224+
return provideResolver(input, podcastEpisodeResolver)
182225
}
183226
export function definePodcastSeason<T extends Record<string, any>>(input?: PodcastSeason & T) {
184-
return provideResolver(input, 'podcastSeason')
227+
return provideResolver(input, podcastSeasonResolver)
185228
}
186229
export function defineTVSeries<T extends Record<string, any>>(input?: TVSeries & T) {
187-
return provideResolver(input, 'tvSeries')
230+
return provideResolver(input, tvSeriesResolver)
188231
}
189232
export function defineTVSeason<T extends Record<string, any>>(input?: TVSeason & T) {
190-
return provideResolver(input, 'tvSeason')
233+
return provideResolver(input, tvSeasonResolver)
191234
}
192235
export function defineTVEpisode<T extends Record<string, any>>(input?: TVEpisode & T) {
193-
return provideResolver(input, 'tvEpisode')
236+
return provideResolver(input, tvEpisodeResolver)
194237
}
195238
export function defineService<T extends Record<string, any>>(input?: Service & T) {
196-
return provideResolver(input, 'service')
239+
return provideResolver(input, serviceResolver)
197240
}
198241

199242
/* simple-only */
200243
export function defineSoftwareApp<T extends Record<string, any>>(input?: SoftwareApp & T) {
201-
return provideResolver(input, 'softwareApp')
244+
return provideResolver(input, softwareAppResolver)
202245
}
203246
export function defineBookEdition<T extends Record<string, any>>(input?: BookEdition & T) {
204-
return provideResolver(input, 'bookEdition')
247+
return provideResolver(input, bookEditionResolver)
205248
}
206249
/* end-simple-only */
207250

0 commit comments

Comments
 (0)