-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
head-export-handler-for-ssr.js
100 lines (90 loc) · 2.74 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
90
91
92
93
94
95
96
97
98
99
100
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 } = node
const id = node.attributes.id
if (!VALID_NODE_NAMES.includes(rawTagName)) {
warnForInvalidTags(rawTagName)
} else {
let element
const attributes = { ...node.attributes, "data-gatsby-head": true }
if (rawTagName === `script`) {
element = (
<script
{...attributes}
dangerouslySetInnerHTML={{
__html: node.text,
}}
/>
)
} else {
element = (
<node.rawTagName {...attributes}>
{node.childNodes[0]?.textContent}
</node.rawTagName>
)
}
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)
}
}