unhead 1.2.2 → 1.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +331 -1061
- package/dist/index.d.ts +17 -170
- package/dist/index.mjs +330 -1012
- package/package.json +4 -7
package/dist/index.cjs
CHANGED
|
@@ -4,331 +4,355 @@ const hookable = require('hookable');
|
|
|
4
4
|
const dom = require('@unhead/dom');
|
|
5
5
|
const shared = require('@unhead/shared');
|
|
6
6
|
|
|
7
|
-
const
|
|
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
|
-
|
|
7
|
+
const UsesMergeStrategy = ["templateParams", "htmlAttrs", "bodyAttrs"];
|
|
8
|
+
const DedupePlugin = shared.defineHeadPlugin({
|
|
9
|
+
hooks: {
|
|
10
|
+
"tag:normalise": function({ tag }) {
|
|
11
|
+
["hid", "vmid", "key"].forEach((key) => {
|
|
12
|
+
if (tag.props[key]) {
|
|
13
|
+
tag.key = tag.props[key];
|
|
14
|
+
delete tag.props[key];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const generatedKey = shared.tagDedupeKey(tag);
|
|
18
|
+
const dedupe = generatedKey || (tag.key ? `${tag.tag}:${tag.key}` : false);
|
|
19
|
+
if (dedupe)
|
|
20
|
+
tag._d = dedupe;
|
|
21
|
+
},
|
|
22
|
+
"tags:resolve": function(ctx) {
|
|
23
|
+
const deduping = {};
|
|
24
|
+
ctx.tags.forEach((tag) => {
|
|
25
|
+
const dedupeKey = (tag.key ? `${tag.tag}:${tag.key}` : tag._d) || tag._p;
|
|
26
|
+
const dupedTag = deduping[dedupeKey];
|
|
27
|
+
if (dupedTag) {
|
|
28
|
+
let strategy = tag?.tagDuplicateStrategy;
|
|
29
|
+
if (!strategy && UsesMergeStrategy.includes(tag.tag))
|
|
30
|
+
strategy = "merge";
|
|
31
|
+
if (strategy === "merge") {
|
|
32
|
+
const oldProps = dupedTag.props;
|
|
33
|
+
["class", "style"].forEach((key) => {
|
|
34
|
+
if (tag.props[key] && oldProps[key]) {
|
|
35
|
+
if (key === "style" && !oldProps[key].endsWith(";"))
|
|
36
|
+
oldProps[key] += ";";
|
|
37
|
+
tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
deduping[dedupeKey].props = {
|
|
41
|
+
...oldProps,
|
|
42
|
+
...tag.props
|
|
43
|
+
};
|
|
44
|
+
return;
|
|
45
|
+
} else if (tag._e === dupedTag._e) {
|
|
46
|
+
dupedTag._duped = dupedTag._duped || [];
|
|
47
|
+
tag._d = `${dupedTag._d}:${dupedTag._duped.length + 1}`;
|
|
48
|
+
dupedTag._duped.push(tag);
|
|
49
|
+
return;
|
|
50
|
+
} else if (shared.tagWeight(tag) > shared.tagWeight(dupedTag)) {
|
|
51
|
+
return;
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const propCount = Object.keys(tag.props).length + (tag.innerHTML ? 1 : 0) + (tag.textContent ? 1 : 0);
|
|
55
|
+
if (shared.HasElementTags.includes(tag.tag) && propCount === 0) {
|
|
56
|
+
delete deduping[dedupeKey];
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
deduping[dedupeKey] = tag;
|
|
60
|
+
});
|
|
61
|
+
const newTags = [];
|
|
62
|
+
Object.values(deduping).forEach((tag) => {
|
|
63
|
+
const dupes = tag._duped;
|
|
64
|
+
delete tag._duped;
|
|
65
|
+
newTags.push(tag);
|
|
66
|
+
if (dupes)
|
|
67
|
+
newTags.push(...dupes);
|
|
68
|
+
});
|
|
69
|
+
ctx.tags = newTags;
|
|
55
70
|
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const ValidEventTags = ["script", "link", "bodyAttrs"];
|
|
75
|
+
function stripEventHandlers(tag) {
|
|
76
|
+
const props = {};
|
|
77
|
+
const eventHandlers = {};
|
|
78
|
+
Object.entries(tag.props).forEach(([key, value]) => {
|
|
79
|
+
if (key.startsWith("on") && typeof value === "function")
|
|
80
|
+
eventHandlers[key] = value;
|
|
81
|
+
else
|
|
82
|
+
props[key] = value;
|
|
56
83
|
});
|
|
84
|
+
return { props, eventHandlers };
|
|
57
85
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tags[titleTemplateIdx].textContent
|
|
79
|
-
);
|
|
80
|
-
if (newTitle !== null) {
|
|
81
|
-
tags[titleTemplateIdx].textContent = newTitle;
|
|
82
|
-
tags[titleTemplateIdx].tag = "title";
|
|
83
|
-
titleTemplateIdx = -1;
|
|
84
|
-
}
|
|
86
|
+
const EventHandlersPlugin = shared.defineHeadPlugin({
|
|
87
|
+
hooks: {
|
|
88
|
+
"ssr:render": function(ctx) {
|
|
89
|
+
ctx.tags = ctx.tags.map((tag) => {
|
|
90
|
+
if (!ValidEventTags.includes(tag.tag))
|
|
91
|
+
return tag;
|
|
92
|
+
if (!Object.entries(tag.props).find(([key, value]) => key.startsWith("on") && typeof value === "function"))
|
|
93
|
+
return tag;
|
|
94
|
+
tag.props = stripEventHandlers(tag).props;
|
|
95
|
+
return tag;
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
"tags:resolve": function(ctx) {
|
|
99
|
+
ctx.tags = ctx.tags.map((tag) => {
|
|
100
|
+
if (!ValidEventTags.includes(tag.tag))
|
|
101
|
+
return tag;
|
|
102
|
+
const { props, eventHandlers } = stripEventHandlers(tag);
|
|
103
|
+
if (Object.keys(eventHandlers).length) {
|
|
104
|
+
tag.props = props;
|
|
105
|
+
tag._eventHandlers = eventHandlers;
|
|
85
106
|
}
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
return tag;
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
"dom:renderTag": function(ctx, dom, track) {
|
|
111
|
+
if (!ctx.tag._eventHandlers)
|
|
112
|
+
return;
|
|
113
|
+
const $eventListenerTarget = ctx.tag.tag === "bodyAttrs" ? dom.defaultView : ctx.$el;
|
|
114
|
+
Object.entries(ctx.tag._eventHandlers).forEach(([k, value]) => {
|
|
115
|
+
const sdeKey = `${ctx.tag._d || ctx.tag._p}:${k}`;
|
|
116
|
+
const eventName = k.slice(2).toLowerCase();
|
|
117
|
+
const eventDedupeKey = `data-h-${eventName}`;
|
|
118
|
+
track(ctx.id, sdeKey, () => {
|
|
119
|
+
});
|
|
120
|
+
if (ctx.$el.hasAttribute(eventDedupeKey))
|
|
121
|
+
return;
|
|
122
|
+
const handler = value;
|
|
123
|
+
ctx.$el.setAttribute(eventDedupeKey, "");
|
|
124
|
+
$eventListenerTarget.addEventListener(eventName, handler);
|
|
125
|
+
if (ctx.entry) {
|
|
126
|
+
track(ctx.id, sdeKey, () => {
|
|
127
|
+
$eventListenerTarget.removeEventListener(eventName, handler);
|
|
128
|
+
ctx.$el.removeAttribute(eventDedupeKey);
|
|
129
|
+
});
|
|
88
130
|
}
|
|
89
|
-
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const DupeableTags = ["link", "style", "script", "noscript"];
|
|
137
|
+
const HashKeyedPLugin = shared.defineHeadPlugin({
|
|
138
|
+
hooks: {
|
|
139
|
+
"tag:normalise": ({ tag }) => {
|
|
140
|
+
if (tag.key && DupeableTags.includes(tag.tag)) {
|
|
141
|
+
tag.props["data-hid"] = tag._h = shared.hashCode(tag.key);
|
|
90
142
|
}
|
|
91
143
|
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
94
146
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
147
|
+
const SortPLugin = shared.defineHeadPlugin({
|
|
148
|
+
hooks: {
|
|
149
|
+
"tags:resolve": (ctx) => {
|
|
150
|
+
const tagPositionForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
|
|
151
|
+
for (const { prefix, offset } of shared.SortModifiers) {
|
|
152
|
+
for (const tag of ctx.tags.filter((tag2) => typeof tag2.tagPriority === "string" && tag2.tagPriority.startsWith(prefix))) {
|
|
153
|
+
const position = tagPositionForKey(
|
|
154
|
+
tag.tagPriority.replace(prefix, "")
|
|
155
|
+
);
|
|
156
|
+
if (typeof position !== "undefined")
|
|
157
|
+
tag._p = position + offset;
|
|
102
158
|
}
|
|
103
159
|
}
|
|
160
|
+
ctx.tags.sort((a, b) => a._p - b._p).sort((a, b) => shared.tagWeight(a) - shared.tagWeight(b));
|
|
104
161
|
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
107
164
|
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
165
|
+
const TemplateParamsPlugin = shared.defineHeadPlugin({
|
|
166
|
+
hooks: {
|
|
167
|
+
"tags:resolve": (ctx) => {
|
|
168
|
+
const { tags } = ctx;
|
|
169
|
+
const title = tags.find((tag) => tag.tag === "title")?.textContent;
|
|
170
|
+
const idx = tags.findIndex((tag) => tag.tag === "templateParams");
|
|
171
|
+
const params = idx !== -1 ? tags[idx].props : {};
|
|
172
|
+
params.separator = params.separator || "|";
|
|
173
|
+
params.pageTitle = shared.processTemplateParams(params.pageTitle || title || "", params);
|
|
174
|
+
for (const tag of tags) {
|
|
175
|
+
if (["titleTemplate", "title"].includes(tag.tag) && typeof tag.textContent === "string") {
|
|
176
|
+
tag.textContent = shared.processTemplateParams(tag.textContent, params);
|
|
177
|
+
} else if (tag.tag === "meta" && typeof tag.props.content === "string") {
|
|
178
|
+
tag.props.content = shared.processTemplateParams(tag.props.content, params);
|
|
179
|
+
} else if (tag.tag === "link" && typeof tag.props.href === "string") {
|
|
180
|
+
tag.props.href = shared.processTemplateParams(tag.props.href, params);
|
|
181
|
+
} else if (tag.tag === "script" && ["application/json", "application/ld+json"].includes(tag.props.type) && typeof tag.innerHTML === "string") {
|
|
182
|
+
try {
|
|
183
|
+
tag.innerHTML = JSON.stringify(JSON.parse(tag.innerHTML), (key, val) => {
|
|
184
|
+
if (typeof val === "string")
|
|
185
|
+
return shared.processTemplateParams(val, params);
|
|
186
|
+
return val;
|
|
187
|
+
});
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
119
190
|
}
|
|
120
191
|
}
|
|
192
|
+
ctx.tags = tags.filter((tag) => tag.tag !== "templateParams");
|
|
121
193
|
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
124
196
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (!Object.entries(tag.props).find(([key, value]) => key.startsWith("on") && typeof value === "function"))
|
|
150
|
-
return tag;
|
|
151
|
-
tag.props = stripEventHandlers("ssr", tag).props;
|
|
152
|
-
return tag;
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
"dom:beforeRenderTag": function(ctx) {
|
|
156
|
-
if (!ValidEventTags.includes(ctx.tag.tag))
|
|
157
|
-
return;
|
|
158
|
-
if (!Object.entries(ctx.tag.props).find(([key, value]) => key.startsWith("on") && typeof value === "function"))
|
|
159
|
-
return;
|
|
160
|
-
const { props, eventHandlers, delayedSrc } = stripEventHandlers("dom", ctx.tag);
|
|
161
|
-
if (!Object.keys(eventHandlers).length)
|
|
162
|
-
return;
|
|
163
|
-
ctx.tag.props = props;
|
|
164
|
-
ctx.tag._eventHandlers = eventHandlers;
|
|
165
|
-
ctx.tag._delayedSrc = delayedSrc;
|
|
166
|
-
},
|
|
167
|
-
"dom:renderTag": function(ctx) {
|
|
168
|
-
const $el = ctx.$el;
|
|
169
|
-
if (!ctx.tag._eventHandlers || !$el)
|
|
170
|
-
return;
|
|
171
|
-
const $eventListenerTarget = ctx.tag.tag === "bodyAttrs" && typeof window !== "undefined" ? window : $el;
|
|
172
|
-
Object.entries(ctx.tag._eventHandlers).forEach(([k, value]) => {
|
|
173
|
-
const sdeKey = `${ctx.tag._d || ctx.tag._p}:${k}`;
|
|
174
|
-
const eventName = k.slice(2).toLowerCase();
|
|
175
|
-
const eventDedupeKey = `data-h-${eventName}`;
|
|
176
|
-
ctx.markSideEffect(sdeKey, () => {
|
|
177
|
-
});
|
|
178
|
-
if ($el.hasAttribute(eventDedupeKey))
|
|
179
|
-
return;
|
|
180
|
-
const handler = value;
|
|
181
|
-
$el.setAttribute(eventDedupeKey, "");
|
|
182
|
-
$eventListenerTarget.addEventListener(eventName, handler);
|
|
183
|
-
if (ctx.entry) {
|
|
184
|
-
ctx.entry._sde[sdeKey] = () => {
|
|
185
|
-
$eventListenerTarget.removeEventListener(eventName, handler);
|
|
186
|
-
$el.removeAttribute(eventDedupeKey);
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
if (ctx.tag._delayedSrc) {
|
|
191
|
-
$el.setAttribute("src", ctx.tag._delayedSrc);
|
|
197
|
+
const TitleTemplatePlugin = shared.defineHeadPlugin({
|
|
198
|
+
hooks: {
|
|
199
|
+
"tags:resolve": (ctx) => {
|
|
200
|
+
const { tags } = ctx;
|
|
201
|
+
let titleTemplateIdx = tags.findIndex((i) => i.tag === "titleTemplate");
|
|
202
|
+
const titleIdx = tags.findIndex((i) => i.tag === "title");
|
|
203
|
+
if (titleIdx !== -1 && titleTemplateIdx !== -1) {
|
|
204
|
+
const newTitle = shared.resolveTitleTemplate(
|
|
205
|
+
tags[titleTemplateIdx].textContent,
|
|
206
|
+
tags[titleIdx].textContent
|
|
207
|
+
);
|
|
208
|
+
if (newTitle !== null) {
|
|
209
|
+
tags[titleIdx].textContent = newTitle || tags[titleIdx].textContent;
|
|
210
|
+
} else {
|
|
211
|
+
delete tags[titleIdx];
|
|
212
|
+
}
|
|
213
|
+
} else if (titleTemplateIdx !== -1) {
|
|
214
|
+
const newTitle = shared.resolveTitleTemplate(
|
|
215
|
+
tags[titleTemplateIdx].textContent
|
|
216
|
+
);
|
|
217
|
+
if (newTitle !== null) {
|
|
218
|
+
tags[titleTemplateIdx].textContent = newTitle;
|
|
219
|
+
tags[titleTemplateIdx].tag = "title";
|
|
220
|
+
titleTemplateIdx = -1;
|
|
192
221
|
}
|
|
193
222
|
}
|
|
223
|
+
if (titleTemplateIdx !== -1) {
|
|
224
|
+
delete tags[titleTemplateIdx];
|
|
225
|
+
}
|
|
226
|
+
ctx.tags = tags.filter(Boolean);
|
|
194
227
|
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
let activeHead;
|
|
232
|
+
function createHead(options = {}) {
|
|
233
|
+
const head = createHeadCore(options);
|
|
234
|
+
if (!head.ssr)
|
|
235
|
+
head.use(dom.PatchDomOnEntryUpdatesPlugin());
|
|
236
|
+
return activeHead = head;
|
|
237
|
+
}
|
|
238
|
+
function createServerHead(options = {}) {
|
|
239
|
+
const head = createHeadCore({
|
|
240
|
+
...options,
|
|
241
|
+
mode: "server"
|
|
195
242
|
});
|
|
243
|
+
return activeHead = head;
|
|
196
244
|
}
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
245
|
+
function createHeadCore(options = {}) {
|
|
246
|
+
const hooks = hookable.createHooks();
|
|
247
|
+
hooks.addHooks(options.hooks || {});
|
|
248
|
+
options.plugins = [
|
|
249
|
+
DedupePlugin,
|
|
250
|
+
EventHandlersPlugin,
|
|
251
|
+
HashKeyedPLugin,
|
|
252
|
+
SortPLugin,
|
|
253
|
+
TemplateParamsPlugin,
|
|
254
|
+
TitleTemplatePlugin,
|
|
255
|
+
...options?.plugins || []
|
|
256
|
+
];
|
|
257
|
+
options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
|
|
258
|
+
options.document = options.document || (shared.IsBrowser ? document : void 0);
|
|
259
|
+
const ssr = !options.document;
|
|
260
|
+
const updated = () => hooks.callHook("entries:updated", head);
|
|
261
|
+
let entryCount = 0;
|
|
262
|
+
let entries = [];
|
|
263
|
+
const head = {
|
|
264
|
+
resolvedOptions: options,
|
|
265
|
+
hooks,
|
|
266
|
+
headEntries() {
|
|
267
|
+
return entries;
|
|
268
|
+
},
|
|
269
|
+
use(plugin) {
|
|
270
|
+
if (plugin.hooks)
|
|
271
|
+
hooks.addHooks(plugin.hooks);
|
|
272
|
+
},
|
|
273
|
+
push(input, entryOptions) {
|
|
274
|
+
const activeEntry = {
|
|
275
|
+
_i: entryCount++,
|
|
276
|
+
input,
|
|
277
|
+
...entryOptions
|
|
278
|
+
};
|
|
279
|
+
const mode = activeEntry?.mode || options.mode;
|
|
280
|
+
if (mode)
|
|
281
|
+
activeEntry.mode = mode;
|
|
282
|
+
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode) {
|
|
283
|
+
entries.push(activeEntry);
|
|
284
|
+
updated();
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
dispose() {
|
|
288
|
+
entries = entries.filter((e) => e._i !== activeEntry._i);
|
|
289
|
+
hooks.callHook("entries:updated", head);
|
|
290
|
+
updated();
|
|
291
|
+
},
|
|
292
|
+
// a patch is the same as creating a new entry, just a nice DX
|
|
293
|
+
patch(input2) {
|
|
294
|
+
entries = entries.map((e) => {
|
|
295
|
+
if (e._i === activeEntry._i) {
|
|
296
|
+
activeEntry.input = e.input = input2;
|
|
244
297
|
}
|
|
298
|
+
return e;
|
|
299
|
+
});
|
|
300
|
+
updated();
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
},
|
|
304
|
+
async resolveTags() {
|
|
305
|
+
const resolveCtx = { tags: [], entries: [...entries] };
|
|
306
|
+
await hooks.callHook("entries:resolve", resolveCtx);
|
|
307
|
+
for (const entry of resolveCtx.entries) {
|
|
308
|
+
const resolved = entry.resolvedInput || entry.input;
|
|
309
|
+
entry.resolvedInput = await (entry.transform ? entry.transform(resolved) : resolved);
|
|
310
|
+
if (entry.resolvedInput) {
|
|
311
|
+
for (const tag of await shared.normaliseEntryTags(entry)) {
|
|
312
|
+
const tagCtx = { tag, entry, resolvedOptions: head.resolvedOptions };
|
|
313
|
+
await hooks.callHook("tag:normalise", tagCtx);
|
|
314
|
+
resolveCtx.tags.push(tagCtx.tag);
|
|
245
315
|
}
|
|
246
|
-
|
|
247
|
-
if (shared.HasElementTags.includes(tag.tag) && propCount === 0) {
|
|
248
|
-
delete deduping[dedupeKey];
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
deduping[dedupeKey] = tag;
|
|
252
|
-
});
|
|
253
|
-
const newTags = [];
|
|
254
|
-
Object.values(deduping).forEach((tag) => {
|
|
255
|
-
const dupes = tag._duped;
|
|
256
|
-
delete tag._duped;
|
|
257
|
-
newTags.push(tag);
|
|
258
|
-
if (dupes)
|
|
259
|
-
newTags.push(...dupes);
|
|
260
|
-
});
|
|
261
|
-
ctx.tags = newTags;
|
|
316
|
+
}
|
|
262
317
|
}
|
|
263
|
-
|
|
264
|
-
|
|
318
|
+
await hooks.callHook("tags:beforeResolve", resolveCtx);
|
|
319
|
+
await hooks.callHook("tags:resolve", resolveCtx);
|
|
320
|
+
return resolveCtx.tags;
|
|
321
|
+
},
|
|
322
|
+
ssr
|
|
323
|
+
};
|
|
324
|
+
head.hooks.callHook("init", head);
|
|
325
|
+
return head;
|
|
265
326
|
}
|
|
266
327
|
|
|
267
|
-
function
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (["s", "pageTitle"].includes(token))
|
|
272
|
-
return p.pageTitle;
|
|
273
|
-
let val;
|
|
274
|
-
if (token.includes(".")) {
|
|
275
|
-
val = token.split(".").reduce((acc, key) => acc ? acc[key] || void 0 : void 0, p);
|
|
276
|
-
} else {
|
|
277
|
-
val = p[token];
|
|
278
|
-
}
|
|
279
|
-
return typeof val !== "undefined" ? val || "" : false;
|
|
280
|
-
}
|
|
281
|
-
let decoded = s;
|
|
282
|
-
try {
|
|
283
|
-
decoded = decodeURI(s);
|
|
284
|
-
} catch {
|
|
285
|
-
}
|
|
286
|
-
const tokens = (decoded.match(/%(\w+\.+\w+)|%(\w+)/g) || []).sort().reverse();
|
|
287
|
-
tokens.forEach((token) => {
|
|
288
|
-
const re = sub(token.slice(1));
|
|
289
|
-
if (typeof re === "string") {
|
|
290
|
-
s = s.replace(new RegExp(`\\${token}(\\W|$)`, "g"), (_, args) => `${re}${args}`).trim();
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
const sep = p.separator;
|
|
294
|
-
if (s.includes(sep)) {
|
|
295
|
-
if (s.endsWith(sep))
|
|
296
|
-
s = s.slice(0, -sep.length).trim();
|
|
297
|
-
if (s.startsWith(sep))
|
|
298
|
-
s = s.slice(sep.length).trim();
|
|
299
|
-
s = s.replace(new RegExp(`\\${sep}\\s*\\${sep}`, "g"), sep);
|
|
300
|
-
}
|
|
301
|
-
return s;
|
|
302
|
-
}
|
|
303
|
-
function TemplateParamsPlugin() {
|
|
328
|
+
function HashHydrationPlugin() {
|
|
329
|
+
let prevHash = false;
|
|
330
|
+
let dirty = false;
|
|
331
|
+
let head;
|
|
304
332
|
return shared.defineHeadPlugin({
|
|
305
333
|
hooks: {
|
|
306
|
-
"
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
ctx.tags = tags.filter((tag) => tag.tag !== "templateParams");
|
|
334
|
+
"init": function(_head) {
|
|
335
|
+
head = _head;
|
|
336
|
+
prevHash = head.resolvedOptions.document?.head.querySelector('meta[name="unhead:ssr"]')?.getAttribute("content") || false;
|
|
337
|
+
},
|
|
338
|
+
"tags:resolve": function({ tags }) {
|
|
339
|
+
const hash = shared.hashCode(
|
|
340
|
+
tags.filter((tag) => {
|
|
341
|
+
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
342
|
+
return entry && entry.mode !== "server";
|
|
343
|
+
}).map((tag) => shared.hashTag(tag)).join("")
|
|
344
|
+
);
|
|
345
|
+
if (prevHash !== hash && prevHash !== false)
|
|
346
|
+
dirty = true;
|
|
347
|
+
else
|
|
348
|
+
prevHash = hash;
|
|
349
|
+
},
|
|
350
|
+
"dom:beforeRender": function(ctx) {
|
|
351
|
+
ctx.shouldRender = dirty;
|
|
352
|
+
dirty = false;
|
|
353
|
+
},
|
|
354
|
+
"ssr:render": function({ tags }) {
|
|
355
|
+
tags.push({ tag: "meta", props: { name: "unhead:ssr", content: String(prevHash) } });
|
|
332
356
|
}
|
|
333
357
|
}
|
|
334
358
|
});
|
|
@@ -372,30 +396,25 @@ function CapoPlugin(options) {
|
|
|
372
396
|
});
|
|
373
397
|
}
|
|
374
398
|
|
|
375
|
-
const
|
|
399
|
+
const unheadComposablesImports = [
|
|
400
|
+
{
|
|
401
|
+
from: "unhead",
|
|
402
|
+
imports: shared.composableNames
|
|
403
|
+
}
|
|
404
|
+
];
|
|
376
405
|
|
|
377
|
-
exports.activeHead = void 0;
|
|
378
|
-
function setActiveHead(head) {
|
|
379
|
-
return exports.activeHead = head;
|
|
380
|
-
}
|
|
381
406
|
function getActiveHead() {
|
|
382
|
-
return
|
|
407
|
+
return activeHead;
|
|
383
408
|
}
|
|
384
409
|
|
|
385
410
|
function useHead(input, options = {}) {
|
|
386
|
-
|
|
387
|
-
if (head) {
|
|
388
|
-
const isBrowser = IsBrowser || head.resolvedOptions?.document;
|
|
389
|
-
if (options.mode === "server" && isBrowser || options.mode === "client" && !isBrowser)
|
|
390
|
-
return;
|
|
391
|
-
return head.push(input, options);
|
|
392
|
-
}
|
|
411
|
+
return getActiveHead()?.push(input, options);
|
|
393
412
|
}
|
|
394
413
|
|
|
395
414
|
function useHeadSafe(input, options = {}) {
|
|
396
415
|
return useHead(input, {
|
|
397
416
|
...options || {},
|
|
398
|
-
transform: whitelistSafeInput
|
|
417
|
+
transform: shared.whitelistSafeInput
|
|
399
418
|
});
|
|
400
419
|
}
|
|
401
420
|
|
|
@@ -412,7 +431,7 @@ function useSeoMeta(input, options) {
|
|
|
412
431
|
return useHead({
|
|
413
432
|
title,
|
|
414
433
|
titleTemplate,
|
|
415
|
-
meta: unpackMeta(meta)
|
|
434
|
+
meta: shared.unpackMeta(meta)
|
|
416
435
|
}, options);
|
|
417
436
|
}
|
|
418
437
|
|
|
@@ -423,766 +442,17 @@ function useServerSeoMeta(input, options) {
|
|
|
423
442
|
});
|
|
424
443
|
}
|
|
425
444
|
|
|
426
|
-
|
|
427
|
-
return useHead({ title });
|
|
428
|
-
}
|
|
429
|
-
function useTagBase(base) {
|
|
430
|
-
return useHead({ base });
|
|
431
|
-
}
|
|
432
|
-
function useTagMeta(meta) {
|
|
433
|
-
return useHead({ meta: shared.asArray(meta) });
|
|
434
|
-
}
|
|
435
|
-
function useTagMetaFlat(meta) {
|
|
436
|
-
return useTagMeta(unpackMeta(meta));
|
|
437
|
-
}
|
|
438
|
-
function useTagLink(link) {
|
|
439
|
-
return useHead({ link: shared.asArray(link) });
|
|
440
|
-
}
|
|
441
|
-
function useTagScript(script) {
|
|
442
|
-
return useHead({ script: shared.asArray(script) });
|
|
443
|
-
}
|
|
444
|
-
function useTagStyle(style) {
|
|
445
|
-
return useHead({ style: shared.asArray(style) });
|
|
446
|
-
}
|
|
447
|
-
function useTagNoscript(noscript) {
|
|
448
|
-
return useHead({ noscript: shared.asArray(noscript) });
|
|
449
|
-
}
|
|
450
|
-
function useHtmlAttrs(attrs) {
|
|
451
|
-
return useHead({ htmlAttrs: attrs });
|
|
452
|
-
}
|
|
453
|
-
function useBodyAttrs(attrs) {
|
|
454
|
-
return useHead({ bodyAttrs: attrs });
|
|
455
|
-
}
|
|
456
|
-
function useTitleTemplate(titleTemplate) {
|
|
457
|
-
return useHead({ titleTemplate });
|
|
458
|
-
}
|
|
459
|
-
function useServerTagTitle(title) {
|
|
460
|
-
return useServerHead({ title });
|
|
461
|
-
}
|
|
462
|
-
function useServerTagBase(base) {
|
|
463
|
-
return useServerHead({ base });
|
|
464
|
-
}
|
|
465
|
-
function useServerTagMeta(meta) {
|
|
466
|
-
return useServerHead({ meta: shared.asArray(meta) });
|
|
467
|
-
}
|
|
468
|
-
function useServerTagMetaFlat(meta) {
|
|
469
|
-
return useServerTagMeta(unpackMeta(meta));
|
|
470
|
-
}
|
|
471
|
-
function useServerTagLink(link) {
|
|
472
|
-
return useServerHead({ link: shared.asArray(link) });
|
|
473
|
-
}
|
|
474
|
-
function useServerTagScript(script) {
|
|
475
|
-
return useServerHead({ script: shared.asArray(script) });
|
|
476
|
-
}
|
|
477
|
-
function useServerTagStyle(style) {
|
|
478
|
-
return useServerHead({ style: shared.asArray(style) });
|
|
479
|
-
}
|
|
480
|
-
function useServerTagNoscript(noscript) {
|
|
481
|
-
return useServerHead({ noscript: shared.asArray(noscript) });
|
|
482
|
-
}
|
|
483
|
-
function useServerHtmlAttrs(attrs) {
|
|
484
|
-
return useServerHead({ htmlAttrs: attrs });
|
|
485
|
-
}
|
|
486
|
-
function useServerBodyAttrs(attrs) {
|
|
487
|
-
return useServerHead({ bodyAttrs: attrs });
|
|
488
|
-
}
|
|
489
|
-
function useServerTitleTemplate(titleTemplate) {
|
|
490
|
-
return useServerHead({ titleTemplate });
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function asArray(input) {
|
|
494
|
-
return Array.isArray(input) ? input : [input];
|
|
495
|
-
}
|
|
496
|
-
const InternalKeySymbol = "_$key";
|
|
497
|
-
function packObject(input, options) {
|
|
498
|
-
const keys = Object.keys(input);
|
|
499
|
-
let [k, v] = keys;
|
|
500
|
-
options = options || {};
|
|
501
|
-
options.key = options.key || k;
|
|
502
|
-
options.value = options.value || v;
|
|
503
|
-
options.resolveKey = options.resolveKey || ((k2) => k2);
|
|
504
|
-
const resolveKey = (index) => {
|
|
505
|
-
const arr = asArray(options?.[index]);
|
|
506
|
-
return arr.find((k2) => {
|
|
507
|
-
if (typeof k2 === "string" && k2.includes(".")) {
|
|
508
|
-
return k2;
|
|
509
|
-
}
|
|
510
|
-
return k2 && keys.includes(k2);
|
|
511
|
-
});
|
|
512
|
-
};
|
|
513
|
-
const resolveValue = (k2, input2) => {
|
|
514
|
-
if (k2.includes(".")) {
|
|
515
|
-
const paths = k2.split(".");
|
|
516
|
-
let val = input2;
|
|
517
|
-
for (const path of paths)
|
|
518
|
-
val = val[path];
|
|
519
|
-
return val;
|
|
520
|
-
}
|
|
521
|
-
return input2[k2];
|
|
522
|
-
};
|
|
523
|
-
k = resolveKey("key") || k;
|
|
524
|
-
v = resolveKey("value") || v;
|
|
525
|
-
const dedupeKeyPrefix = input.key ? `${InternalKeySymbol}${input.key}-` : "";
|
|
526
|
-
let keyValue = resolveValue(k, input);
|
|
527
|
-
keyValue = options.resolveKey(keyValue);
|
|
528
|
-
return {
|
|
529
|
-
[`${dedupeKeyPrefix}${keyValue}`]: resolveValue(v, input)
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function packArray(input, options) {
|
|
534
|
-
const packed = {};
|
|
535
|
-
for (const i of input) {
|
|
536
|
-
const packedObj = packObject(i, options);
|
|
537
|
-
const pKey = Object.keys(packedObj)[0];
|
|
538
|
-
const isDedupeKey = pKey.startsWith(InternalKeySymbol);
|
|
539
|
-
if (!isDedupeKey && packed[pKey]) {
|
|
540
|
-
packed[pKey] = Array.isArray(packed[pKey]) ? packed[pKey] : [packed[pKey]];
|
|
541
|
-
packed[pKey].push(Object.values(packedObj)[0]);
|
|
542
|
-
} else {
|
|
543
|
-
packed[isDedupeKey ? pKey.split("-").slice(1).join("-") || pKey : pKey] = packedObj[pKey];
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
return packed;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function unpackToArray(input, options) {
|
|
550
|
-
const unpacked = [];
|
|
551
|
-
const kFn = options.resolveKeyData || ((ctx) => ctx.key);
|
|
552
|
-
const vFn = options.resolveValueData || ((ctx) => ctx.value);
|
|
553
|
-
for (const [k, v] of Object.entries(input)) {
|
|
554
|
-
unpacked.push(...(Array.isArray(v) ? v : [v]).map((i) => {
|
|
555
|
-
const ctx = { key: k, value: i };
|
|
556
|
-
const val = vFn(ctx);
|
|
557
|
-
if (typeof val === "object")
|
|
558
|
-
return unpackToArray(val, options);
|
|
559
|
-
if (Array.isArray(val))
|
|
560
|
-
return val;
|
|
561
|
-
return {
|
|
562
|
-
[typeof options.key === "function" ? options.key(ctx) : options.key]: kFn(ctx),
|
|
563
|
-
[typeof options.value === "function" ? options.value(ctx) : options.value]: val
|
|
564
|
-
};
|
|
565
|
-
}).flat());
|
|
566
|
-
}
|
|
567
|
-
return unpacked;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function unpackToString(value, options) {
|
|
571
|
-
return Object.entries(value).map(([key, value2]) => {
|
|
572
|
-
if (typeof value2 === "object")
|
|
573
|
-
value2 = unpackToString(value2, options);
|
|
574
|
-
if (options.resolve) {
|
|
575
|
-
const resolved = options.resolve({ key, value: value2 });
|
|
576
|
-
if (resolved)
|
|
577
|
-
return resolved;
|
|
578
|
-
}
|
|
579
|
-
if (typeof value2 === "number")
|
|
580
|
-
value2 = value2.toString();
|
|
581
|
-
if (typeof value2 === "string" && options.wrapValue) {
|
|
582
|
-
value2 = value2.replace(new RegExp(options.wrapValue, "g"), `\\${options.wrapValue}`);
|
|
583
|
-
value2 = `${options.wrapValue}${value2}${options.wrapValue}`;
|
|
584
|
-
}
|
|
585
|
-
return `${key}${options.keyValueSeparator || ""}${value2}`;
|
|
586
|
-
}).join(options.entrySeparator || "");
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const MetaPackingSchema = {
|
|
590
|
-
robots: {
|
|
591
|
-
unpack: {
|
|
592
|
-
keyValueSeparator: ":"
|
|
593
|
-
}
|
|
594
|
-
},
|
|
595
|
-
// Pragma directives
|
|
596
|
-
contentSecurityPolicy: {
|
|
597
|
-
unpack: {
|
|
598
|
-
keyValueSeparator: " ",
|
|
599
|
-
entrySeparator: "; "
|
|
600
|
-
},
|
|
601
|
-
metaKey: "http-equiv"
|
|
602
|
-
},
|
|
603
|
-
fbAppId: {
|
|
604
|
-
keyValue: "fb:app_id",
|
|
605
|
-
metaKey: "property"
|
|
606
|
-
},
|
|
607
|
-
ogSiteName: {
|
|
608
|
-
keyValue: "og:site_name"
|
|
609
|
-
},
|
|
610
|
-
msapplicationTileImage: {
|
|
611
|
-
keyValue: "msapplication-TileImage"
|
|
612
|
-
},
|
|
613
|
-
/**
|
|
614
|
-
* Tile colour for windows
|
|
615
|
-
*/
|
|
616
|
-
msapplicationTileColor: {
|
|
617
|
-
keyValue: "msapplication-TileColor"
|
|
618
|
-
},
|
|
619
|
-
/**
|
|
620
|
-
* URL of a config for windows tile.
|
|
621
|
-
*/
|
|
622
|
-
msapplicationConfig: {
|
|
623
|
-
keyValue: "msapplication-Config"
|
|
624
|
-
},
|
|
625
|
-
charset: {
|
|
626
|
-
metaKey: "charset"
|
|
627
|
-
},
|
|
628
|
-
contentType: {
|
|
629
|
-
metaKey: "http-equiv"
|
|
630
|
-
},
|
|
631
|
-
defaultStyle: {
|
|
632
|
-
metaKey: "http-equiv"
|
|
633
|
-
},
|
|
634
|
-
xUaCompatible: {
|
|
635
|
-
metaKey: "http-equiv"
|
|
636
|
-
},
|
|
637
|
-
refresh: {
|
|
638
|
-
metaKey: "http-equiv"
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
|
|
642
|
-
function packMeta(inputs) {
|
|
643
|
-
const mappedPackingSchema = Object.entries(MetaPackingSchema).map(([key, value]) => [key, value.keyValue]);
|
|
644
|
-
return packArray(inputs, {
|
|
645
|
-
key: ["name", "property", "httpEquiv", "http-equiv", "charset"],
|
|
646
|
-
value: ["content", "charset"],
|
|
647
|
-
resolveKey(k) {
|
|
648
|
-
let key = mappedPackingSchema.filter((sk) => sk[1] === k)?.[0]?.[0] || k;
|
|
649
|
-
const replacer = (_, letter) => letter?.toUpperCase();
|
|
650
|
-
key = key.replace(/:([a-z])/g, replacer).replace(/-([a-z])/g, replacer);
|
|
651
|
-
return key;
|
|
652
|
-
}
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const OpenGraphInputs = ["og:Image", "og:Video", "og:Audio", "twitter:Image"];
|
|
657
|
-
const SimpleArrayUnpackMetas = ["themeColor"];
|
|
658
|
-
const ColonPrefixKeys = /^(og|twitter|fb)/;
|
|
659
|
-
const PropertyPrefixKeys = /^(og|fb)/;
|
|
660
|
-
function resolveMetaKeyType(key) {
|
|
661
|
-
return PropertyPrefixKeys.test(key) ? "property" : MetaPackingSchema[key]?.metaKey || "name";
|
|
662
|
-
}
|
|
663
|
-
function resolveMetaKeyValue(key) {
|
|
664
|
-
return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
|
|
665
|
-
}
|
|
666
|
-
function fixKeyCase(key) {
|
|
667
|
-
key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
668
|
-
if (ColonPrefixKeys.test(key)) {
|
|
669
|
-
key = key.replace("secure-url", "secure_url").replace(/-/g, ":");
|
|
670
|
-
}
|
|
671
|
-
return key;
|
|
672
|
-
}
|
|
673
|
-
function changeKeyCasingDeep(input) {
|
|
674
|
-
if (Array.isArray(input)) {
|
|
675
|
-
return input.map((entry) => changeKeyCasingDeep(entry));
|
|
676
|
-
}
|
|
677
|
-
if (typeof input !== "object" || Array.isArray(input))
|
|
678
|
-
return input;
|
|
679
|
-
const output = {};
|
|
680
|
-
for (const [key, value] of Object.entries(input))
|
|
681
|
-
output[fixKeyCase(key)] = changeKeyCasingDeep(value);
|
|
682
|
-
return output;
|
|
683
|
-
}
|
|
684
|
-
function unpackMeta(input) {
|
|
685
|
-
const extras = [];
|
|
686
|
-
OpenGraphInputs.forEach((key) => {
|
|
687
|
-
const propKey = key.toLowerCase();
|
|
688
|
-
const inputKey = `${key.replace(":", "")}`;
|
|
689
|
-
const val = input[inputKey];
|
|
690
|
-
if (typeof val === "object") {
|
|
691
|
-
(Array.isArray(val) ? val : [val]).forEach((entry) => {
|
|
692
|
-
if (!entry)
|
|
693
|
-
return;
|
|
694
|
-
const unpackedEntry = unpackToArray(entry, {
|
|
695
|
-
key: key.startsWith("og") ? "property" : "name",
|
|
696
|
-
value: "content",
|
|
697
|
-
resolveKeyData({ key: key2 }) {
|
|
698
|
-
return fixKeyCase(`${propKey}${key2 !== "url" ? `:${key2}` : ""}`);
|
|
699
|
-
},
|
|
700
|
-
resolveValueData({ value }) {
|
|
701
|
-
return typeof value === "number" ? value.toString() : value;
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
extras.push(
|
|
705
|
-
...unpackedEntry.sort((a, b) => a.property === propKey ? -1 : b.property === propKey ? 1 : 0)
|
|
706
|
-
);
|
|
707
|
-
});
|
|
708
|
-
delete input[inputKey];
|
|
709
|
-
}
|
|
710
|
-
});
|
|
711
|
-
SimpleArrayUnpackMetas.forEach((meta2) => {
|
|
712
|
-
if (input[meta2] && typeof input[meta2] !== "string") {
|
|
713
|
-
const val = Array.isArray(input[meta2]) ? input[meta2] : [input[meta2]];
|
|
714
|
-
delete input[meta2];
|
|
715
|
-
val.forEach((entry) => {
|
|
716
|
-
extras.push({
|
|
717
|
-
name: fixKeyCase(meta2),
|
|
718
|
-
...entry
|
|
719
|
-
});
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
const meta = unpackToArray(input, {
|
|
724
|
-
key({ key }) {
|
|
725
|
-
return resolveMetaKeyType(key);
|
|
726
|
-
},
|
|
727
|
-
value({ key }) {
|
|
728
|
-
return key === "charset" ? "charset" : "content";
|
|
729
|
-
},
|
|
730
|
-
resolveKeyData({ key }) {
|
|
731
|
-
return resolveMetaKeyValue(key);
|
|
732
|
-
},
|
|
733
|
-
resolveValueData({ value, key }) {
|
|
734
|
-
if (value === null)
|
|
735
|
-
return "_null";
|
|
736
|
-
if (typeof value === "object")
|
|
737
|
-
return resolvePackedMetaObjectValue(value, key);
|
|
738
|
-
return typeof value === "number" ? value.toString() : value;
|
|
739
|
-
}
|
|
740
|
-
});
|
|
741
|
-
return [...extras, ...meta].filter((v) => typeof v.content === "undefined" || v.content !== "_null");
|
|
742
|
-
}
|
|
743
|
-
function resolvePackedMetaObjectValue(value, key) {
|
|
744
|
-
const definition = MetaPackingSchema[key];
|
|
745
|
-
if (key === "refresh")
|
|
746
|
-
return `${value.seconds};url=${value.url}`;
|
|
747
|
-
return unpackToString(
|
|
748
|
-
changeKeyCasingDeep(value),
|
|
749
|
-
{
|
|
750
|
-
entrySeparator: ", ",
|
|
751
|
-
keyValueSeparator: "=",
|
|
752
|
-
resolve({ value: value2, key: key2 }) {
|
|
753
|
-
if (value2 === null)
|
|
754
|
-
return "";
|
|
755
|
-
if (typeof value2 === "boolean")
|
|
756
|
-
return `${key2}`;
|
|
757
|
-
},
|
|
758
|
-
...definition?.unpack
|
|
759
|
-
}
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
async function normaliseTag(tagName, input, e) {
|
|
764
|
-
const tag = { tag: tagName, props: {} };
|
|
765
|
-
if (input instanceof Promise)
|
|
766
|
-
input = await input;
|
|
767
|
-
if (tagName === "templateParams") {
|
|
768
|
-
tag.props = input;
|
|
769
|
-
return tag;
|
|
770
|
-
}
|
|
771
|
-
if (["title", "titleTemplate"].includes(tagName)) {
|
|
772
|
-
if (input && typeof input === "object") {
|
|
773
|
-
tag.textContent = input.textContent;
|
|
774
|
-
if (input.tagPriority)
|
|
775
|
-
tag.tagPriority = input.tagPriority;
|
|
776
|
-
} else {
|
|
777
|
-
tag.textContent = input;
|
|
778
|
-
}
|
|
779
|
-
return tag;
|
|
780
|
-
}
|
|
781
|
-
if (typeof input === "string") {
|
|
782
|
-
if (!["script", "noscript", "style"].includes(tagName))
|
|
783
|
-
return false;
|
|
784
|
-
if (tagName === "script" && (/^(https?:)?\/\//.test(input) || input.startsWith("/")))
|
|
785
|
-
tag.props.src = input;
|
|
786
|
-
else
|
|
787
|
-
tag.innerHTML = input;
|
|
788
|
-
return tag;
|
|
789
|
-
}
|
|
790
|
-
tag.props = await normaliseProps(tagName, { ...input });
|
|
791
|
-
if (tag.props.children) {
|
|
792
|
-
tag.props.innerHTML = tag.props.children;
|
|
793
|
-
}
|
|
794
|
-
delete tag.props.children;
|
|
795
|
-
Object.keys(tag.props).filter((k) => shared.TagConfigKeys.includes(k)).forEach((k) => {
|
|
796
|
-
if (!["innerHTML", "textContent"].includes(k) || shared.TagsWithInnerContent.includes(tag.tag)) {
|
|
797
|
-
tag[k] = tag.props[k];
|
|
798
|
-
}
|
|
799
|
-
delete tag.props[k];
|
|
800
|
-
});
|
|
801
|
-
shared.TagConfigKeys.forEach((k) => {
|
|
802
|
-
if (!tag[k] && e[k]) {
|
|
803
|
-
tag[k] = e[k];
|
|
804
|
-
}
|
|
805
|
-
});
|
|
806
|
-
["innerHTML", "textContent"].forEach((k) => {
|
|
807
|
-
if (tag.tag === "script" && typeof tag[k] === "string" && ["application/ld+json", "application/json"].includes(tag.props.type)) {
|
|
808
|
-
try {
|
|
809
|
-
tag[k] = JSON.parse(tag[k]);
|
|
810
|
-
} catch (e2) {
|
|
811
|
-
tag[k] = "";
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
if (typeof tag[k] === "object")
|
|
815
|
-
tag[k] = JSON.stringify(tag[k]);
|
|
816
|
-
});
|
|
817
|
-
if (tag.props.class)
|
|
818
|
-
tag.props.class = normaliseClassProp(tag.props.class);
|
|
819
|
-
if (tag.props.content && Array.isArray(tag.props.content))
|
|
820
|
-
return tag.props.content.map((v) => ({ ...tag, props: { ...tag.props, content: v } }));
|
|
821
|
-
return tag;
|
|
822
|
-
}
|
|
823
|
-
function normaliseClassProp(v) {
|
|
824
|
-
if (typeof v === "object" && !Array.isArray(v)) {
|
|
825
|
-
v = Object.keys(v).filter((k) => v[k]);
|
|
826
|
-
}
|
|
827
|
-
return (Array.isArray(v) ? v.join(" ") : v).split(" ").filter((c) => c.trim()).filter(Boolean).join(" ");
|
|
828
|
-
}
|
|
829
|
-
async function normaliseProps(tagName, props) {
|
|
830
|
-
for (const k of Object.keys(props)) {
|
|
831
|
-
const isDataKey = k.startsWith("data-");
|
|
832
|
-
if (props[k] instanceof Promise) {
|
|
833
|
-
props[k] = await props[k];
|
|
834
|
-
}
|
|
835
|
-
if (String(props[k]) === "true") {
|
|
836
|
-
props[k] = isDataKey ? "true" : "";
|
|
837
|
-
} else if (String(props[k]) === "false") {
|
|
838
|
-
if (isDataKey) {
|
|
839
|
-
props[k] = "false";
|
|
840
|
-
} else {
|
|
841
|
-
delete props[k];
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
return props;
|
|
846
|
-
}
|
|
847
|
-
const TagEntityBits = 10;
|
|
848
|
-
async function normaliseEntryTags(e) {
|
|
849
|
-
const tagPromises = [];
|
|
850
|
-
Object.entries(e.resolvedInput).filter(([k, v]) => typeof v !== "undefined" && shared.ValidHeadTags.includes(k)).forEach(([k, value]) => {
|
|
851
|
-
const v = shared.asArray(value);
|
|
852
|
-
tagPromises.push(...v.map((props) => normaliseTag(k, props, e)).flat());
|
|
853
|
-
});
|
|
854
|
-
return (await Promise.all(tagPromises)).flat().filter(Boolean).map((t, i) => {
|
|
855
|
-
t._e = e._i;
|
|
856
|
-
t._p = (e._i << TagEntityBits) + i;
|
|
857
|
-
return t;
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
const WhitelistAttributes = {
|
|
862
|
-
htmlAttrs: ["id", "class", "lang", "dir"],
|
|
863
|
-
bodyAttrs: ["id", "class"],
|
|
864
|
-
meta: ["id", "name", "property", "charset", "content"],
|
|
865
|
-
noscript: ["id", "textContent"],
|
|
866
|
-
script: ["id", "type", "textContent"],
|
|
867
|
-
link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
|
|
868
|
-
};
|
|
869
|
-
function whitelistSafeInput(input) {
|
|
870
|
-
const filtered = {};
|
|
871
|
-
Object.keys(input).forEach((key) => {
|
|
872
|
-
const tagValue = input[key];
|
|
873
|
-
if (!tagValue)
|
|
874
|
-
return;
|
|
875
|
-
switch (key) {
|
|
876
|
-
case "title":
|
|
877
|
-
case "titleTemplate":
|
|
878
|
-
case "templateParams":
|
|
879
|
-
filtered[key] = tagValue;
|
|
880
|
-
break;
|
|
881
|
-
case "htmlAttrs":
|
|
882
|
-
case "bodyAttrs":
|
|
883
|
-
filtered[key] = {};
|
|
884
|
-
WhitelistAttributes[key].forEach((a) => {
|
|
885
|
-
if (tagValue[a])
|
|
886
|
-
filtered[key][a] = tagValue[a];
|
|
887
|
-
});
|
|
888
|
-
Object.keys(tagValue || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
|
|
889
|
-
filtered[key][a] = tagValue[a];
|
|
890
|
-
});
|
|
891
|
-
break;
|
|
892
|
-
case "meta":
|
|
893
|
-
if (Array.isArray(tagValue)) {
|
|
894
|
-
filtered[key] = tagValue.map((meta) => {
|
|
895
|
-
const safeMeta = {};
|
|
896
|
-
WhitelistAttributes.meta.forEach((key2) => {
|
|
897
|
-
if (meta[key2] || key2.startsWith("data-"))
|
|
898
|
-
safeMeta[key2] = meta[key2];
|
|
899
|
-
});
|
|
900
|
-
return safeMeta;
|
|
901
|
-
}).filter((meta) => Object.keys(meta).length > 0);
|
|
902
|
-
}
|
|
903
|
-
break;
|
|
904
|
-
case "link":
|
|
905
|
-
if (Array.isArray(tagValue)) {
|
|
906
|
-
filtered[key] = tagValue.map((meta) => {
|
|
907
|
-
const link = {};
|
|
908
|
-
WhitelistAttributes.link.forEach((key2) => {
|
|
909
|
-
const val = meta[key2];
|
|
910
|
-
if (key2 === "rel" && ["stylesheet", "canonical", "modulepreload", "prerender", "preload", "prefetch"].includes(val))
|
|
911
|
-
return;
|
|
912
|
-
if (key2 === "href") {
|
|
913
|
-
if (val.includes("javascript:") || val.includes("data:"))
|
|
914
|
-
return;
|
|
915
|
-
link[key2] = val;
|
|
916
|
-
} else if (val || key2.startsWith("data-")) {
|
|
917
|
-
link[key2] = val;
|
|
918
|
-
}
|
|
919
|
-
});
|
|
920
|
-
return link;
|
|
921
|
-
}).filter((link) => Object.keys(link).length > 1 && !!link.rel);
|
|
922
|
-
}
|
|
923
|
-
break;
|
|
924
|
-
case "noscript":
|
|
925
|
-
if (Array.isArray(tagValue)) {
|
|
926
|
-
filtered[key] = tagValue.map((meta) => {
|
|
927
|
-
const noscript = {};
|
|
928
|
-
WhitelistAttributes.noscript.forEach((key2) => {
|
|
929
|
-
if (meta[key2] || key2.startsWith("data-"))
|
|
930
|
-
noscript[key2] = meta[key2];
|
|
931
|
-
});
|
|
932
|
-
return noscript;
|
|
933
|
-
}).filter((meta) => Object.keys(meta).length > 0);
|
|
934
|
-
}
|
|
935
|
-
break;
|
|
936
|
-
case "script":
|
|
937
|
-
if (Array.isArray(tagValue)) {
|
|
938
|
-
filtered[key] = tagValue.map((script) => {
|
|
939
|
-
const safeScript = {};
|
|
940
|
-
WhitelistAttributes.script.forEach((s) => {
|
|
941
|
-
if (script[s] || s.startsWith("data-")) {
|
|
942
|
-
if (s === "textContent") {
|
|
943
|
-
try {
|
|
944
|
-
const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
|
|
945
|
-
safeScript[s] = JSON.stringify(jsonVal, null, 0);
|
|
946
|
-
} catch (e) {
|
|
947
|
-
}
|
|
948
|
-
} else {
|
|
949
|
-
safeScript[s] = script[s];
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
return safeScript;
|
|
954
|
-
}).filter((meta) => Object.keys(meta).length > 0);
|
|
955
|
-
}
|
|
956
|
-
break;
|
|
957
|
-
}
|
|
958
|
-
});
|
|
959
|
-
return filtered;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
function CorePlugins() {
|
|
963
|
-
return [
|
|
964
|
-
// dedupe needs to come first
|
|
965
|
-
DedupesTagsPlugin(),
|
|
966
|
-
SortTagsPlugin(),
|
|
967
|
-
TemplateParamsPlugin(),
|
|
968
|
-
TitleTemplatePlugin(),
|
|
969
|
-
ProvideTagHashPlugin(),
|
|
970
|
-
EventHandlersPlugin(),
|
|
971
|
-
DeprecatedTagAttrPlugin()
|
|
972
|
-
];
|
|
973
|
-
}
|
|
974
|
-
function DOMPlugins(options = {}) {
|
|
975
|
-
return [
|
|
976
|
-
dom.PatchDomOnEntryUpdatesPlugin({ document: options?.document, delayFn: options?.domDelayFn })
|
|
977
|
-
];
|
|
978
|
-
}
|
|
979
|
-
function createHead(options = {}) {
|
|
980
|
-
const head = createHeadCore({
|
|
981
|
-
...options,
|
|
982
|
-
plugins: [...DOMPlugins(options), ...options?.plugins || []]
|
|
983
|
-
});
|
|
984
|
-
if (options.experimentalHashHydration && head.resolvedOptions.document)
|
|
985
|
-
head._hash = dom.maybeGetSSRHash(head.resolvedOptions.document);
|
|
986
|
-
setActiveHead(head);
|
|
987
|
-
return head;
|
|
988
|
-
}
|
|
989
|
-
function createServerHead(options = {}) {
|
|
990
|
-
const head = createHeadCore({
|
|
991
|
-
...options,
|
|
992
|
-
mode: "server"
|
|
993
|
-
});
|
|
994
|
-
setActiveHead(head);
|
|
995
|
-
return head;
|
|
996
|
-
}
|
|
997
|
-
function createHeadCore(options = {}) {
|
|
998
|
-
let entries = [];
|
|
999
|
-
let _sde = {};
|
|
1000
|
-
let _eid = 0;
|
|
1001
|
-
const hooks = hookable.createHooks();
|
|
1002
|
-
if (options?.hooks)
|
|
1003
|
-
hooks.addHooks(options.hooks);
|
|
1004
|
-
options.plugins = [
|
|
1005
|
-
...CorePlugins(),
|
|
1006
|
-
...options?.plugins || []
|
|
1007
|
-
];
|
|
1008
|
-
options.plugins.forEach((p) => p.hooks && hooks.addHooks(p.hooks));
|
|
1009
|
-
options.document = options.document || (IsBrowser ? document : void 0);
|
|
1010
|
-
const updated = () => hooks.callHook("entries:updated", head);
|
|
1011
|
-
const head = {
|
|
1012
|
-
resolvedOptions: options,
|
|
1013
|
-
headEntries() {
|
|
1014
|
-
return entries;
|
|
1015
|
-
},
|
|
1016
|
-
get hooks() {
|
|
1017
|
-
return hooks;
|
|
1018
|
-
},
|
|
1019
|
-
use(plugin) {
|
|
1020
|
-
if (plugin.hooks)
|
|
1021
|
-
hooks.addHooks(plugin.hooks);
|
|
1022
|
-
},
|
|
1023
|
-
push(input, entryOptions) {
|
|
1024
|
-
const activeEntry = {
|
|
1025
|
-
_i: _eid++,
|
|
1026
|
-
input,
|
|
1027
|
-
_sde: {},
|
|
1028
|
-
...entryOptions
|
|
1029
|
-
};
|
|
1030
|
-
const mode = activeEntry?.mode || options.mode;
|
|
1031
|
-
if (mode)
|
|
1032
|
-
activeEntry.mode = mode;
|
|
1033
|
-
entries.push(activeEntry);
|
|
1034
|
-
updated();
|
|
1035
|
-
return {
|
|
1036
|
-
dispose() {
|
|
1037
|
-
entries = entries.filter((e) => {
|
|
1038
|
-
if (e._i !== activeEntry._i)
|
|
1039
|
-
return true;
|
|
1040
|
-
_sde = { ..._sde, ...e._sde || {} };
|
|
1041
|
-
e._sde = {};
|
|
1042
|
-
updated();
|
|
1043
|
-
return false;
|
|
1044
|
-
});
|
|
1045
|
-
},
|
|
1046
|
-
// a patch is the same as creating a new entry, just a nice DX
|
|
1047
|
-
patch(input2) {
|
|
1048
|
-
entries = entries.map((e) => {
|
|
1049
|
-
if (e._i === activeEntry._i) {
|
|
1050
|
-
activeEntry.input = e.input = input2;
|
|
1051
|
-
updated();
|
|
1052
|
-
}
|
|
1053
|
-
return e;
|
|
1054
|
-
});
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
},
|
|
1058
|
-
async resolveTags() {
|
|
1059
|
-
const resolveCtx = { tags: [], entries: [...entries] };
|
|
1060
|
-
await hooks.callHook("entries:resolve", resolveCtx);
|
|
1061
|
-
for (const entry of resolveCtx.entries) {
|
|
1062
|
-
const resolved = entry.resolvedInput || entry.input;
|
|
1063
|
-
entry.resolvedInput = await (entry.transform ? entry.transform(resolved) : resolved);
|
|
1064
|
-
if (entry.resolvedInput) {
|
|
1065
|
-
for (const tag of await normaliseEntryTags(entry)) {
|
|
1066
|
-
const tagCtx = { tag, entry, resolvedOptions: head.resolvedOptions };
|
|
1067
|
-
await hooks.callHook("tag:normalise", tagCtx);
|
|
1068
|
-
resolveCtx.tags.push(tagCtx.tag);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
await hooks.callHook("tags:beforeResolve", resolveCtx);
|
|
1073
|
-
await hooks.callHook("tags:resolve", resolveCtx);
|
|
1074
|
-
return resolveCtx.tags;
|
|
1075
|
-
},
|
|
1076
|
-
_popSideEffectQueue() {
|
|
1077
|
-
const sde = { ..._sde };
|
|
1078
|
-
_sde = {};
|
|
1079
|
-
return sde;
|
|
1080
|
-
},
|
|
1081
|
-
_elMap: {}
|
|
1082
|
-
};
|
|
1083
|
-
head.hooks.callHook("init", head);
|
|
1084
|
-
return head;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
const coreComposableNames = [
|
|
1088
|
-
"getActiveHead"
|
|
1089
|
-
];
|
|
1090
|
-
const composableNames = [
|
|
1091
|
-
"useHead",
|
|
1092
|
-
"useSeoMeta",
|
|
1093
|
-
"useHeadSafe",
|
|
1094
|
-
"useServerHead",
|
|
1095
|
-
"useServerSeoMeta",
|
|
1096
|
-
"useServerHeadSafe",
|
|
1097
|
-
// deprecated
|
|
1098
|
-
"useTagTitle",
|
|
1099
|
-
"useTagBase",
|
|
1100
|
-
"useTagMeta",
|
|
1101
|
-
"useTagMetaFlat",
|
|
1102
|
-
"useTagLink",
|
|
1103
|
-
"useTagScript",
|
|
1104
|
-
"useTagStyle",
|
|
1105
|
-
"useTagNoscript",
|
|
1106
|
-
"useHtmlAttrs",
|
|
1107
|
-
"useBodyAttrs",
|
|
1108
|
-
"useTitleTemplate",
|
|
1109
|
-
"useServerTagTitle",
|
|
1110
|
-
"useServerTagBase",
|
|
1111
|
-
"useServerTagMeta",
|
|
1112
|
-
"useServerTagMetaFlat",
|
|
1113
|
-
"useServerTagLink",
|
|
1114
|
-
"useServerTagScript",
|
|
1115
|
-
"useServerTagStyle",
|
|
1116
|
-
"useServerTagNoscript",
|
|
1117
|
-
"useServerHtmlAttrs",
|
|
1118
|
-
"useServerBodyAttrs",
|
|
1119
|
-
"useServerTitleTemplate"
|
|
1120
|
-
];
|
|
1121
|
-
const unheadComposablesImports = [
|
|
1122
|
-
{
|
|
1123
|
-
from: "unhead",
|
|
1124
|
-
imports: [...coreComposableNames, ...composableNames]
|
|
1125
|
-
}
|
|
1126
|
-
];
|
|
1127
|
-
|
|
445
|
+
exports.composableNames = shared.composableNames;
|
|
1128
446
|
exports.CapoPlugin = CapoPlugin;
|
|
1129
|
-
exports.
|
|
1130
|
-
exports.DOMPlugins = DOMPlugins;
|
|
1131
|
-
exports.DedupesTagsPlugin = DedupesTagsPlugin;
|
|
1132
|
-
exports.DeprecatedTagAttrPlugin = DeprecatedTagAttrPlugin;
|
|
1133
|
-
exports.EventHandlersPlugin = EventHandlersPlugin;
|
|
1134
|
-
exports.ProvideTagHashPlugin = ProvideTagHashPlugin;
|
|
1135
|
-
exports.SortModifiers = SortModifiers;
|
|
1136
|
-
exports.SortTagsPlugin = SortTagsPlugin;
|
|
1137
|
-
exports.TAG_ALIASES = TAG_ALIASES;
|
|
1138
|
-
exports.TAG_WEIGHTS = TAG_WEIGHTS;
|
|
1139
|
-
exports.TagEntityBits = TagEntityBits;
|
|
1140
|
-
exports.TemplateParamsPlugin = TemplateParamsPlugin;
|
|
1141
|
-
exports.TitleTemplatePlugin = TitleTemplatePlugin;
|
|
1142
|
-
exports.composableNames = composableNames;
|
|
447
|
+
exports.HashHydrationPlugin = HashHydrationPlugin;
|
|
1143
448
|
exports.createHead = createHead;
|
|
1144
449
|
exports.createHeadCore = createHeadCore;
|
|
1145
450
|
exports.createServerHead = createServerHead;
|
|
1146
451
|
exports.getActiveHead = getActiveHead;
|
|
1147
|
-
exports.normaliseClassProp = normaliseClassProp;
|
|
1148
|
-
exports.normaliseEntryTags = normaliseEntryTags;
|
|
1149
|
-
exports.normaliseProps = normaliseProps;
|
|
1150
|
-
exports.normaliseTag = normaliseTag;
|
|
1151
|
-
exports.packMeta = packMeta;
|
|
1152
|
-
exports.processTemplateParams = processTemplateParams;
|
|
1153
|
-
exports.resolveMetaKeyType = resolveMetaKeyType;
|
|
1154
|
-
exports.resolveMetaKeyValue = resolveMetaKeyValue;
|
|
1155
|
-
exports.resolvePackedMetaObjectValue = resolvePackedMetaObjectValue;
|
|
1156
|
-
exports.setActiveHead = setActiveHead;
|
|
1157
|
-
exports.tagWeight = tagWeight;
|
|
1158
452
|
exports.unheadComposablesImports = unheadComposablesImports;
|
|
1159
|
-
exports.unpackMeta = unpackMeta;
|
|
1160
|
-
exports.useBodyAttrs = useBodyAttrs;
|
|
1161
453
|
exports.useHead = useHead;
|
|
1162
454
|
exports.useHeadSafe = useHeadSafe;
|
|
1163
|
-
exports.useHtmlAttrs = useHtmlAttrs;
|
|
1164
455
|
exports.useSeoMeta = useSeoMeta;
|
|
1165
|
-
exports.useServerBodyAttrs = useServerBodyAttrs;
|
|
1166
456
|
exports.useServerHead = useServerHead;
|
|
1167
457
|
exports.useServerHeadSafe = useServerHeadSafe;
|
|
1168
|
-
exports.useServerHtmlAttrs = useServerHtmlAttrs;
|
|
1169
458
|
exports.useServerSeoMeta = useServerSeoMeta;
|
|
1170
|
-
exports.useServerTagBase = useServerTagBase;
|
|
1171
|
-
exports.useServerTagLink = useServerTagLink;
|
|
1172
|
-
exports.useServerTagMeta = useServerTagMeta;
|
|
1173
|
-
exports.useServerTagMetaFlat = useServerTagMetaFlat;
|
|
1174
|
-
exports.useServerTagNoscript = useServerTagNoscript;
|
|
1175
|
-
exports.useServerTagScript = useServerTagScript;
|
|
1176
|
-
exports.useServerTagStyle = useServerTagStyle;
|
|
1177
|
-
exports.useServerTagTitle = useServerTagTitle;
|
|
1178
|
-
exports.useServerTitleTemplate = useServerTitleTemplate;
|
|
1179
|
-
exports.useTagBase = useTagBase;
|
|
1180
|
-
exports.useTagLink = useTagLink;
|
|
1181
|
-
exports.useTagMeta = useTagMeta;
|
|
1182
|
-
exports.useTagMetaFlat = useTagMetaFlat;
|
|
1183
|
-
exports.useTagNoscript = useTagNoscript;
|
|
1184
|
-
exports.useTagScript = useTagScript;
|
|
1185
|
-
exports.useTagStyle = useTagStyle;
|
|
1186
|
-
exports.useTagTitle = useTagTitle;
|
|
1187
|
-
exports.useTitleTemplate = useTitleTemplate;
|
|
1188
|
-
exports.whitelistSafeInput = whitelistSafeInput;
|