-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
head-export-handler-for-ssr.js
89 lines (80 loc) · 2.45 KB
/
head-export-handler-for-ssr.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const React = require(`react`)
const { grabMatchParams } = require(`../find-path`)
const { createElement } = require(`react`)
const { StaticQueryContext } = require(`gatsby`)
const {
headExportValidator,
filterHeadProps,
warnForInvalidTags,
} = require(`./utils`)
const { ServerLocation, Router } = require(`@gatsbyjs/reach-router`)
const { renderToString } = require(`react-dom/server`)
const { parse } = require(`node-html-parser`)
const { VALID_NODE_NAMES } = require(`./constants`)
export function headHandlerForSSR({
pageComponent,
setHeadComponents,
staticQueryContext,
pageData,
pagePath,
}) {
if (pageComponent?.Head) {
headExportValidator(pageComponent.Head)
function HeadRouteHandler(props) {
const _props = {
...props,
...pageData.result,
params: {
...grabMatchParams(props.location.pathname),
...(pageData.result?.pageContext?.__params || {}),
},
}
return createElement(pageComponent.Head, filterHeadProps(_props))
}
const routerElement = (
<StaticQueryContext.Provider value={staticQueryContext}>
<ServerLocation url={`${__BASE_PATH__}${pagePath}`}>
<Router
baseuri={__BASE_PATH__}
component={({ children }) => <>{children}</>}
>
<HeadRouteHandler path="/*" />
</Router>
</ServerLocation>
</StaticQueryContext.Provider>
)
// extract head nodes from string
const rawString = renderToString(routerElement)
const headNodes = parse(rawString).childNodes
const validHeadNodes = []
const seenIds = new Map()
for (const node of headNodes) {
const { rawTagName, attributes } = node
const id = attributes.id
if (!VALID_NODE_NAMES.includes(rawTagName)) {
warnForInvalidTags(rawTagName)
} else {
const element = createElement(
rawTagName,
{
...attributes,
"data-gatsby-head": true,
},
node.childNodes[0]?.textContent
)
if (id) {
if (!seenIds.has(id)) {
validHeadNodes.push(element)
seenIds.set(id, validHeadNodes.length - 1)
} else {
const indexOfPreviouslyInsertedNode = seenIds.get(id)
validHeadNodes[indexOfPreviouslyInsertedNode] = element
}
} else {
validHeadNodes.push(element)
}
}
}
setHeadComponents(validHeadNodes)
}
}