unhead 1.3.0-beta.0 → 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 +8 -8
- package/dist/index.d.ts +15 -481
- package/dist/index.mjs +8 -8
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -257,14 +257,9 @@ function createHeadCore(options = {}) {
|
|
|
257
257
|
options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
|
|
258
258
|
options.document = options.document || (shared.IsBrowser ? document : void 0);
|
|
259
259
|
const ssr = !options.document;
|
|
260
|
+
const updated = () => hooks.callHook("entries:updated", head);
|
|
260
261
|
let entryCount = 0;
|
|
261
|
-
let entries =
|
|
262
|
-
set(target, prop, value) {
|
|
263
|
-
target[prop] = value;
|
|
264
|
-
hooks.callHook("entries:updated", head);
|
|
265
|
-
return true;
|
|
266
|
-
}
|
|
267
|
-
});
|
|
262
|
+
let entries = [];
|
|
268
263
|
const head = {
|
|
269
264
|
resolvedOptions: options,
|
|
270
265
|
hooks,
|
|
@@ -284,11 +279,15 @@ function createHeadCore(options = {}) {
|
|
|
284
279
|
const mode = activeEntry?.mode || options.mode;
|
|
285
280
|
if (mode)
|
|
286
281
|
activeEntry.mode = mode;
|
|
287
|
-
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode)
|
|
282
|
+
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode) {
|
|
288
283
|
entries.push(activeEntry);
|
|
284
|
+
updated();
|
|
285
|
+
}
|
|
289
286
|
return {
|
|
290
287
|
dispose() {
|
|
291
288
|
entries = entries.filter((e) => e._i !== activeEntry._i);
|
|
289
|
+
hooks.callHook("entries:updated", head);
|
|
290
|
+
updated();
|
|
292
291
|
},
|
|
293
292
|
// a patch is the same as creating a new entry, just a nice DX
|
|
294
293
|
patch(input2) {
|
|
@@ -298,6 +297,7 @@ function createHeadCore(options = {}) {
|
|
|
298
297
|
}
|
|
299
298
|
return e;
|
|
300
299
|
});
|
|
300
|
+
updated();
|
|
301
301
|
}
|
|
302
302
|
};
|
|
303
303
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -1,506 +1,40 @@
|
|
|
1
1
|
import * as _unhead_schema from '@unhead/schema';
|
|
2
|
-
import { Head, CreateHeadOptions, Unhead, HeadEntryOptions, ActiveHeadEntry, HeadSafe,
|
|
3
|
-
import require$$0 from 'hookable';
|
|
4
|
-
import require$$1 from '@unhead/dom';
|
|
5
|
-
import require$$2 from '@unhead/shared';
|
|
2
|
+
import { Head, CreateHeadOptions, Unhead, HeadEntryOptions, ActiveHeadEntry, HeadSafe, UseSeoMetaInput } from '@unhead/schema';
|
|
6
3
|
export { composableNames } from '@unhead/shared';
|
|
7
4
|
|
|
8
|
-
declare function createHead
|
|
9
|
-
declare function createServerHead
|
|
5
|
+
declare function createHead<T extends {} = Head>(options?: CreateHeadOptions): Unhead<T>;
|
|
6
|
+
declare function createServerHead<T extends {} = Head>(options?: CreateHeadOptions): Unhead<T>;
|
|
10
7
|
/**
|
|
11
8
|
* Creates a core instance of unhead. Does not provide a global ctx for composables to work
|
|
12
9
|
* and does not register DOM plugins.
|
|
13
10
|
*
|
|
14
11
|
* @param options
|
|
15
12
|
*/
|
|
16
|
-
declare function createHeadCore
|
|
13
|
+
declare function createHeadCore<T extends {} = Head>(options?: CreateHeadOptions): Unhead<T>;
|
|
17
14
|
|
|
18
|
-
declare function HashHydrationPlugin
|
|
15
|
+
declare function HashHydrationPlugin(): _unhead_schema.HeadPlugin;
|
|
19
16
|
|
|
20
|
-
declare function CapoPlugin
|
|
17
|
+
declare function CapoPlugin(options: {
|
|
21
18
|
track?: boolean;
|
|
22
19
|
}): _unhead_schema.HeadPlugin;
|
|
23
20
|
|
|
24
|
-
declare const unheadComposablesImports
|
|
21
|
+
declare const unheadComposablesImports: {
|
|
25
22
|
from: string;
|
|
26
23
|
imports: string[];
|
|
27
24
|
}[];
|
|
28
25
|
|
|
29
|
-
declare function getActiveHead
|
|
26
|
+
declare function getActiveHead(): _unhead_schema.Unhead<any> | undefined;
|
|
30
27
|
|
|
31
|
-
declare function useHead
|
|
28
|
+
declare function useHead<T extends Head>(input: T, options?: HeadEntryOptions): ActiveHeadEntry<T> | void;
|
|
32
29
|
|
|
33
|
-
declare function useHeadSafe
|
|
30
|
+
declare function useHeadSafe(input: HeadSafe, options?: HeadEntryOptions): ActiveHeadEntry<HeadSafe> | void;
|
|
34
31
|
|
|
35
|
-
declare function useServerHead
|
|
32
|
+
declare function useServerHead<T extends Head>(input: T, options?: HeadEntryOptions): ActiveHeadEntry<T> | void;
|
|
36
33
|
|
|
37
|
-
declare function useServerHeadSafe
|
|
34
|
+
declare function useServerHeadSafe<T extends HeadSafe>(input: T, options?: HeadEntryOptions): ActiveHeadEntry<T> | void;
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
title?: Title;
|
|
41
|
-
titleTemplate?: TitleTemplate;
|
|
42
|
-
};
|
|
43
|
-
declare function useSeoMeta$1(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
36
|
+
declare function useSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
const dom = require$$1;
|
|
49
|
-
const shared = require$$2;
|
|
50
|
-
|
|
51
|
-
const UsesMergeStrategy = ["templateParams", "htmlAttrs", "bodyAttrs"];
|
|
52
|
-
const DedupePlugin = shared.defineHeadPlugin({
|
|
53
|
-
hooks: {
|
|
54
|
-
"tag:normalise": function({ tag }) {
|
|
55
|
-
["hid", "vmid", "key"].forEach((key) => {
|
|
56
|
-
if (tag.props[key]) {
|
|
57
|
-
tag.key = tag.props[key];
|
|
58
|
-
delete tag.props[key];
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
const generatedKey = shared.tagDedupeKey(tag);
|
|
62
|
-
const dedupe = generatedKey || (tag.key ? `${tag.tag}:${tag.key}` : false);
|
|
63
|
-
if (dedupe)
|
|
64
|
-
tag._d = dedupe;
|
|
65
|
-
},
|
|
66
|
-
"tags:resolve": function(ctx) {
|
|
67
|
-
const deduping = {};
|
|
68
|
-
ctx.tags.forEach((tag) => {
|
|
69
|
-
const dedupeKey = (tag.key ? `${tag.tag}:${tag.key}` : tag._d) || tag._p;
|
|
70
|
-
const dupedTag = deduping[dedupeKey];
|
|
71
|
-
if (dupedTag) {
|
|
72
|
-
let strategy = tag?.tagDuplicateStrategy;
|
|
73
|
-
if (!strategy && UsesMergeStrategy.includes(tag.tag))
|
|
74
|
-
strategy = "merge";
|
|
75
|
-
if (strategy === "merge") {
|
|
76
|
-
const oldProps = dupedTag.props;
|
|
77
|
-
["class", "style"].forEach((key) => {
|
|
78
|
-
if (tag.props[key] && oldProps[key]) {
|
|
79
|
-
if (key === "style" && !oldProps[key].endsWith(";"))
|
|
80
|
-
oldProps[key] += ";";
|
|
81
|
-
tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
deduping[dedupeKey].props = {
|
|
85
|
-
...oldProps,
|
|
86
|
-
...tag.props
|
|
87
|
-
};
|
|
88
|
-
return;
|
|
89
|
-
} else if (tag._e === dupedTag._e) {
|
|
90
|
-
dupedTag._duped = dupedTag._duped || [];
|
|
91
|
-
tag._d = `${dupedTag._d}:${dupedTag._duped.length + 1}`;
|
|
92
|
-
dupedTag._duped.push(tag);
|
|
93
|
-
return;
|
|
94
|
-
} else if (shared.tagWeight(tag) > shared.tagWeight(dupedTag)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const propCount = Object.keys(tag.props).length + (tag.innerHTML ? 1 : 0) + (tag.textContent ? 1 : 0);
|
|
99
|
-
if (shared.HasElementTags.includes(tag.tag) && propCount === 0) {
|
|
100
|
-
delete deduping[dedupeKey];
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
deduping[dedupeKey] = tag;
|
|
104
|
-
});
|
|
105
|
-
const newTags = [];
|
|
106
|
-
Object.values(deduping).forEach((tag) => {
|
|
107
|
-
const dupes = tag._duped;
|
|
108
|
-
delete tag._duped;
|
|
109
|
-
newTags.push(tag);
|
|
110
|
-
if (dupes)
|
|
111
|
-
newTags.push(...dupes);
|
|
112
|
-
});
|
|
113
|
-
ctx.tags = newTags;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const ValidEventTags = ["script", "link", "bodyAttrs"];
|
|
119
|
-
function stripEventHandlers(tag) {
|
|
120
|
-
const props = {};
|
|
121
|
-
const eventHandlers = {};
|
|
122
|
-
Object.entries(tag.props).forEach(([key, value]) => {
|
|
123
|
-
if (key.startsWith("on") && typeof value === "function")
|
|
124
|
-
eventHandlers[key] = value;
|
|
125
|
-
else
|
|
126
|
-
props[key] = value;
|
|
127
|
-
});
|
|
128
|
-
return { props, eventHandlers };
|
|
129
|
-
}
|
|
130
|
-
const EventHandlersPlugin = shared.defineHeadPlugin({
|
|
131
|
-
hooks: {
|
|
132
|
-
"ssr:render": function(ctx) {
|
|
133
|
-
ctx.tags = ctx.tags.map((tag) => {
|
|
134
|
-
if (!ValidEventTags.includes(tag.tag))
|
|
135
|
-
return tag;
|
|
136
|
-
if (!Object.entries(tag.props).find(([key, value]) => key.startsWith("on") && typeof value === "function"))
|
|
137
|
-
return tag;
|
|
138
|
-
tag.props = stripEventHandlers(tag).props;
|
|
139
|
-
return tag;
|
|
140
|
-
});
|
|
141
|
-
},
|
|
142
|
-
"tags:resolve": function(ctx) {
|
|
143
|
-
ctx.tags = ctx.tags.map((tag) => {
|
|
144
|
-
if (!ValidEventTags.includes(tag.tag))
|
|
145
|
-
return tag;
|
|
146
|
-
const { props, eventHandlers } = stripEventHandlers(tag);
|
|
147
|
-
if (Object.keys(eventHandlers).length) {
|
|
148
|
-
tag.props = props;
|
|
149
|
-
tag._eventHandlers = eventHandlers;
|
|
150
|
-
}
|
|
151
|
-
return tag;
|
|
152
|
-
});
|
|
153
|
-
},
|
|
154
|
-
"dom:renderTag": function(ctx, dom, track) {
|
|
155
|
-
if (!ctx.tag._eventHandlers)
|
|
156
|
-
return;
|
|
157
|
-
const $eventListenerTarget = ctx.tag.tag === "bodyAttrs" ? dom.defaultView : ctx.$el;
|
|
158
|
-
Object.entries(ctx.tag._eventHandlers).forEach(([k, value]) => {
|
|
159
|
-
const sdeKey = `${ctx.tag._d || ctx.tag._p}:${k}`;
|
|
160
|
-
const eventName = k.slice(2).toLowerCase();
|
|
161
|
-
const eventDedupeKey = `data-h-${eventName}`;
|
|
162
|
-
track(ctx.id, sdeKey, () => {
|
|
163
|
-
});
|
|
164
|
-
if (ctx.$el.hasAttribute(eventDedupeKey))
|
|
165
|
-
return;
|
|
166
|
-
const handler = value;
|
|
167
|
-
ctx.$el.setAttribute(eventDedupeKey, "");
|
|
168
|
-
$eventListenerTarget.addEventListener(eventName, handler);
|
|
169
|
-
if (ctx.entry) {
|
|
170
|
-
track(ctx.id, sdeKey, () => {
|
|
171
|
-
$eventListenerTarget.removeEventListener(eventName, handler);
|
|
172
|
-
ctx.$el.removeAttribute(eventDedupeKey);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const DupeableTags = ["link", "style", "script", "noscript"];
|
|
181
|
-
const HashKeyedPLugin = shared.defineHeadPlugin({
|
|
182
|
-
hooks: {
|
|
183
|
-
"tag:normalise": ({ tag }) => {
|
|
184
|
-
if (tag.key && DupeableTags.includes(tag.tag)) {
|
|
185
|
-
tag.props["data-hid"] = tag._h = shared.hashCode(tag.key);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const SortPLugin = shared.defineHeadPlugin({
|
|
192
|
-
hooks: {
|
|
193
|
-
"tags:resolve": (ctx) => {
|
|
194
|
-
const tagPositionForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
|
|
195
|
-
for (const { prefix, offset } of shared.SortModifiers) {
|
|
196
|
-
for (const tag of ctx.tags.filter((tag2) => typeof tag2.tagPriority === "string" && tag2.tagPriority.startsWith(prefix))) {
|
|
197
|
-
const position = tagPositionForKey(
|
|
198
|
-
tag.tagPriority.replace(prefix, "")
|
|
199
|
-
);
|
|
200
|
-
if (typeof position !== "undefined")
|
|
201
|
-
tag._p = position + offset;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
ctx.tags.sort((a, b) => a._p - b._p).sort((a, b) => shared.tagWeight(a) - shared.tagWeight(b));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
const TemplateParamsPlugin = shared.defineHeadPlugin({
|
|
210
|
-
hooks: {
|
|
211
|
-
"tags:resolve": (ctx) => {
|
|
212
|
-
const { tags } = ctx;
|
|
213
|
-
const title = tags.find((tag) => tag.tag === "title")?.textContent;
|
|
214
|
-
const idx = tags.findIndex((tag) => tag.tag === "templateParams");
|
|
215
|
-
const params = idx !== -1 ? tags[idx].props : {};
|
|
216
|
-
params.separator = params.separator || "|";
|
|
217
|
-
params.pageTitle = shared.processTemplateParams(params.pageTitle || title || "", params);
|
|
218
|
-
for (const tag of tags) {
|
|
219
|
-
if (["titleTemplate", "title"].includes(tag.tag) && typeof tag.textContent === "string") {
|
|
220
|
-
tag.textContent = shared.processTemplateParams(tag.textContent, params);
|
|
221
|
-
} else if (tag.tag === "meta" && typeof tag.props.content === "string") {
|
|
222
|
-
tag.props.content = shared.processTemplateParams(tag.props.content, params);
|
|
223
|
-
} else if (tag.tag === "link" && typeof tag.props.href === "string") {
|
|
224
|
-
tag.props.href = shared.processTemplateParams(tag.props.href, params);
|
|
225
|
-
} else if (tag.tag === "script" && ["application/json", "application/ld+json"].includes(tag.props.type) && typeof tag.innerHTML === "string") {
|
|
226
|
-
try {
|
|
227
|
-
tag.innerHTML = JSON.stringify(JSON.parse(tag.innerHTML), (key, val) => {
|
|
228
|
-
if (typeof val === "string")
|
|
229
|
-
return shared.processTemplateParams(val, params);
|
|
230
|
-
return val;
|
|
231
|
-
});
|
|
232
|
-
} catch {
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
ctx.tags = tags.filter((tag) => tag.tag !== "templateParams");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const TitleTemplatePlugin = shared.defineHeadPlugin({
|
|
242
|
-
hooks: {
|
|
243
|
-
"tags:resolve": (ctx) => {
|
|
244
|
-
const { tags } = ctx;
|
|
245
|
-
let titleTemplateIdx = tags.findIndex((i) => i.tag === "titleTemplate");
|
|
246
|
-
const titleIdx = tags.findIndex((i) => i.tag === "title");
|
|
247
|
-
if (titleIdx !== -1 && titleTemplateIdx !== -1) {
|
|
248
|
-
const newTitle = shared.resolveTitleTemplate(
|
|
249
|
-
tags[titleTemplateIdx].textContent,
|
|
250
|
-
tags[titleIdx].textContent
|
|
251
|
-
);
|
|
252
|
-
if (newTitle !== null) {
|
|
253
|
-
tags[titleIdx].textContent = newTitle || tags[titleIdx].textContent;
|
|
254
|
-
} else {
|
|
255
|
-
delete tags[titleIdx];
|
|
256
|
-
}
|
|
257
|
-
} else if (titleTemplateIdx !== -1) {
|
|
258
|
-
const newTitle = shared.resolveTitleTemplate(
|
|
259
|
-
tags[titleTemplateIdx].textContent
|
|
260
|
-
);
|
|
261
|
-
if (newTitle !== null) {
|
|
262
|
-
tags[titleTemplateIdx].textContent = newTitle;
|
|
263
|
-
tags[titleTemplateIdx].tag = "title";
|
|
264
|
-
titleTemplateIdx = -1;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
if (titleTemplateIdx !== -1) {
|
|
268
|
-
delete tags[titleTemplateIdx];
|
|
269
|
-
}
|
|
270
|
-
ctx.tags = tags.filter(Boolean);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
let activeHead;
|
|
276
|
-
function createHead(options = {}) {
|
|
277
|
-
const head = createHeadCore(options);
|
|
278
|
-
if (!head.ssr)
|
|
279
|
-
head.use(dom.PatchDomOnEntryUpdatesPlugin());
|
|
280
|
-
return activeHead = head;
|
|
281
|
-
}
|
|
282
|
-
function createServerHead(options = {}) {
|
|
283
|
-
const head = createHeadCore({
|
|
284
|
-
...options,
|
|
285
|
-
mode: "server"
|
|
286
|
-
});
|
|
287
|
-
return activeHead = head;
|
|
288
|
-
}
|
|
289
|
-
function createHeadCore(options = {}) {
|
|
290
|
-
const hooks = hookable.createHooks();
|
|
291
|
-
hooks.addHooks(options.hooks || {});
|
|
292
|
-
options.plugins = [
|
|
293
|
-
DedupePlugin,
|
|
294
|
-
EventHandlersPlugin,
|
|
295
|
-
HashKeyedPLugin,
|
|
296
|
-
SortPLugin,
|
|
297
|
-
TemplateParamsPlugin,
|
|
298
|
-
TitleTemplatePlugin,
|
|
299
|
-
...options?.plugins || []
|
|
300
|
-
];
|
|
301
|
-
options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
|
|
302
|
-
options.document = options.document || (shared.IsBrowser ? document : void 0);
|
|
303
|
-
const ssr = !options.document;
|
|
304
|
-
let entryCount = 0;
|
|
305
|
-
let entries = new Proxy([], {
|
|
306
|
-
set(target, prop, value) {
|
|
307
|
-
target[prop] = value;
|
|
308
|
-
hooks.callHook("entries:updated", head);
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
const head = {
|
|
313
|
-
resolvedOptions: options,
|
|
314
|
-
hooks,
|
|
315
|
-
headEntries() {
|
|
316
|
-
return entries;
|
|
317
|
-
},
|
|
318
|
-
use(plugin) {
|
|
319
|
-
if (plugin.hooks)
|
|
320
|
-
hooks.addHooks(plugin.hooks);
|
|
321
|
-
},
|
|
322
|
-
push(input, entryOptions) {
|
|
323
|
-
const activeEntry = {
|
|
324
|
-
_i: entryCount++,
|
|
325
|
-
input,
|
|
326
|
-
...entryOptions
|
|
327
|
-
};
|
|
328
|
-
const mode = activeEntry?.mode || options.mode;
|
|
329
|
-
if (mode)
|
|
330
|
-
activeEntry.mode = mode;
|
|
331
|
-
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode)
|
|
332
|
-
entries.push(activeEntry);
|
|
333
|
-
return {
|
|
334
|
-
dispose() {
|
|
335
|
-
entries = entries.filter((e) => e._i !== activeEntry._i);
|
|
336
|
-
},
|
|
337
|
-
// a patch is the same as creating a new entry, just a nice DX
|
|
338
|
-
patch(input2) {
|
|
339
|
-
entries = entries.map((e) => {
|
|
340
|
-
if (e._i === activeEntry._i) {
|
|
341
|
-
activeEntry.input = e.input = input2;
|
|
342
|
-
}
|
|
343
|
-
return e;
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
},
|
|
348
|
-
async resolveTags() {
|
|
349
|
-
const resolveCtx = { tags: [], entries: [...entries] };
|
|
350
|
-
await hooks.callHook("entries:resolve", resolveCtx);
|
|
351
|
-
for (const entry of resolveCtx.entries) {
|
|
352
|
-
const resolved = entry.resolvedInput || entry.input;
|
|
353
|
-
entry.resolvedInput = await (entry.transform ? entry.transform(resolved) : resolved);
|
|
354
|
-
if (entry.resolvedInput) {
|
|
355
|
-
for (const tag of await shared.normaliseEntryTags(entry)) {
|
|
356
|
-
const tagCtx = { tag, entry, resolvedOptions: head.resolvedOptions };
|
|
357
|
-
await hooks.callHook("tag:normalise", tagCtx);
|
|
358
|
-
resolveCtx.tags.push(tagCtx.tag);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
await hooks.callHook("tags:beforeResolve", resolveCtx);
|
|
363
|
-
await hooks.callHook("tags:resolve", resolveCtx);
|
|
364
|
-
return resolveCtx.tags;
|
|
365
|
-
},
|
|
366
|
-
ssr
|
|
367
|
-
};
|
|
368
|
-
head.hooks.callHook("init", head);
|
|
369
|
-
return head;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function HashHydrationPlugin() {
|
|
373
|
-
let prevHash = false;
|
|
374
|
-
let dirty = false;
|
|
375
|
-
let head;
|
|
376
|
-
return shared.defineHeadPlugin({
|
|
377
|
-
hooks: {
|
|
378
|
-
"init": function(_head) {
|
|
379
|
-
head = _head;
|
|
380
|
-
prevHash = head.resolvedOptions.document?.head.querySelector('meta[name="unhead:ssr"]')?.getAttribute("content") || false;
|
|
381
|
-
},
|
|
382
|
-
"tags:resolve": function({ tags }) {
|
|
383
|
-
const hash = shared.hashCode(
|
|
384
|
-
tags.filter((tag) => {
|
|
385
|
-
const entry = head.headEntries().find((e) => e._i === tag._e);
|
|
386
|
-
return entry && entry.mode !== "server";
|
|
387
|
-
}).map((tag) => shared.hashTag(tag)).join("")
|
|
388
|
-
);
|
|
389
|
-
if (prevHash !== hash && prevHash !== false)
|
|
390
|
-
dirty = true;
|
|
391
|
-
else
|
|
392
|
-
prevHash = hash;
|
|
393
|
-
},
|
|
394
|
-
"dom:beforeRender": function(ctx) {
|
|
395
|
-
ctx.shouldRender = dirty;
|
|
396
|
-
dirty = false;
|
|
397
|
-
},
|
|
398
|
-
"ssr:render": function({ tags }) {
|
|
399
|
-
tags.push({ tag: "meta", props: { name: "unhead:ssr", content: String(prevHash) } });
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const importRe = /@import/;
|
|
406
|
-
function CapoPlugin(options) {
|
|
407
|
-
return shared.defineHeadPlugin({
|
|
408
|
-
hooks: {
|
|
409
|
-
"tags:beforeResolve": function({ tags }) {
|
|
410
|
-
for (const tag of tags) {
|
|
411
|
-
if (tag.tagPriority || tag.tagPosition && tag.tagPosition !== "head")
|
|
412
|
-
continue;
|
|
413
|
-
const isTruthy = (val) => val === "";
|
|
414
|
-
const isScript = tag.tag === "script";
|
|
415
|
-
const isLink = tag.tag === "link";
|
|
416
|
-
if (isScript && isTruthy(tag.props.async)) {
|
|
417
|
-
tag.tagPriority = 3;
|
|
418
|
-
} else if (tag.tag === "style" && tag.innerHTML && importRe.test(tag.innerHTML)) {
|
|
419
|
-
tag.tagPriority = 4;
|
|
420
|
-
} else if (isScript && tag.props.src && !isTruthy(tag.props.defer) && !isTruthy(tag.props.async) && tag.props.type !== "module" && !tag.props.type?.endsWith("json")) {
|
|
421
|
-
tag.tagPriority = 5;
|
|
422
|
-
} else if (isLink && tag.props.rel === "stylesheet" || tag.tag === "style") {
|
|
423
|
-
tag.tagPriority = 6;
|
|
424
|
-
} else if (isLink && ["preload", "modulepreload"].includes(tag.props.rel)) {
|
|
425
|
-
tag.tagPriority = 7;
|
|
426
|
-
} else if (isScript && isTruthy(tag.props.defer) && tag.props.src && !isTruthy(tag.props.async)) {
|
|
427
|
-
tag.tagPriority = 8;
|
|
428
|
-
} else if (isLink && ["prefetch", "dns-prefetch", "prerender"].includes(tag.props.rel)) {
|
|
429
|
-
tag.tagPriority = 9;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
options?.track && tags.push({
|
|
433
|
-
tag: "htmlAttrs",
|
|
434
|
-
props: {
|
|
435
|
-
"data-capo": ""
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const unheadComposablesImports = [
|
|
444
|
-
{
|
|
445
|
-
from: "unhead",
|
|
446
|
-
imports: shared.composableNames
|
|
447
|
-
}
|
|
448
|
-
];
|
|
449
|
-
|
|
450
|
-
function getActiveHead() {
|
|
451
|
-
return activeHead;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function useHead(input, options = {}) {
|
|
455
|
-
return getActiveHead()?.push(input, options);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function useHeadSafe(input, options = {}) {
|
|
459
|
-
return useHead(input, {
|
|
460
|
-
...options || {},
|
|
461
|
-
transform: shared.whitelistSafeInput
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function useServerHead(input, options = {}) {
|
|
466
|
-
return useHead(input, { ...options, mode: "server" });
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function useServerHeadSafe(input, options = {}) {
|
|
470
|
-
return useHeadSafe(input, { ...options, mode: "server" });
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function useSeoMeta(input, options) {
|
|
474
|
-
const { title, titleTemplate, ...meta } = input;
|
|
475
|
-
return useHead({
|
|
476
|
-
title,
|
|
477
|
-
titleTemplate,
|
|
478
|
-
meta: shared.unpackMeta(meta)
|
|
479
|
-
}, options);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function useServerSeoMeta$1(input, options) {
|
|
483
|
-
return useSeoMeta(input, {
|
|
484
|
-
...options || {},
|
|
485
|
-
mode: "server"
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
dist.composableNames = shared.composableNames;
|
|
490
|
-
dist.CapoPlugin = CapoPlugin;
|
|
491
|
-
dist.HashHydrationPlugin = HashHydrationPlugin;
|
|
492
|
-
dist.createHead = createHead;
|
|
493
|
-
dist.createHeadCore = createHeadCore;
|
|
494
|
-
dist.createServerHead = createServerHead;
|
|
495
|
-
dist.getActiveHead = getActiveHead;
|
|
496
|
-
dist.unheadComposablesImports = unheadComposablesImports;
|
|
497
|
-
dist.useHead = useHead;
|
|
498
|
-
dist.useHeadSafe = useHeadSafe;
|
|
499
|
-
dist.useSeoMeta = useSeoMeta;
|
|
500
|
-
dist.useServerHead = useServerHead;
|
|
501
|
-
dist.useServerHeadSafe = useServerHeadSafe;
|
|
502
|
-
dist.useServerSeoMeta = useServerSeoMeta$1;
|
|
503
|
-
|
|
504
|
-
declare function useServerSeoMeta(input: dist.UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
505
|
-
|
|
506
|
-
export { CapoPlugin$1 as CapoPlugin, HashHydrationPlugin$1 as HashHydrationPlugin, UseSeoMetaInput, createHead$1 as createHead, createHeadCore$1 as createHeadCore, createServerHead$1 as createServerHead, getActiveHead$1 as getActiveHead, unheadComposablesImports$1 as unheadComposablesImports, useHead$1 as useHead, useHeadSafe$1 as useHeadSafe, useSeoMeta$1 as useSeoMeta, useServerHead$1 as useServerHead, useServerHeadSafe$1 as useServerHeadSafe, useServerSeoMeta };
|
|
40
|
+
export { CapoPlugin, HashHydrationPlugin, createHead, createHeadCore, createServerHead, getActiveHead, unheadComposablesImports, useHead, useHeadSafe, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
|
package/dist/index.mjs
CHANGED
|
@@ -256,14 +256,9 @@ function createHeadCore(options = {}) {
|
|
|
256
256
|
options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
|
|
257
257
|
options.document = options.document || (IsBrowser ? document : void 0);
|
|
258
258
|
const ssr = !options.document;
|
|
259
|
+
const updated = () => hooks.callHook("entries:updated", head);
|
|
259
260
|
let entryCount = 0;
|
|
260
|
-
let entries =
|
|
261
|
-
set(target, prop, value) {
|
|
262
|
-
target[prop] = value;
|
|
263
|
-
hooks.callHook("entries:updated", head);
|
|
264
|
-
return true;
|
|
265
|
-
}
|
|
266
|
-
});
|
|
261
|
+
let entries = [];
|
|
267
262
|
const head = {
|
|
268
263
|
resolvedOptions: options,
|
|
269
264
|
hooks,
|
|
@@ -283,11 +278,15 @@ function createHeadCore(options = {}) {
|
|
|
283
278
|
const mode = activeEntry?.mode || options.mode;
|
|
284
279
|
if (mode)
|
|
285
280
|
activeEntry.mode = mode;
|
|
286
|
-
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode)
|
|
281
|
+
if (options.mode === "server" && ssr || options.mode === "client" && !ssr || !options.mode) {
|
|
287
282
|
entries.push(activeEntry);
|
|
283
|
+
updated();
|
|
284
|
+
}
|
|
288
285
|
return {
|
|
289
286
|
dispose() {
|
|
290
287
|
entries = entries.filter((e) => e._i !== activeEntry._i);
|
|
288
|
+
hooks.callHook("entries:updated", head);
|
|
289
|
+
updated();
|
|
291
290
|
},
|
|
292
291
|
// a patch is the same as creating a new entry, just a nice DX
|
|
293
292
|
patch(input2) {
|
|
@@ -297,6 +296,7 @@ function createHeadCore(options = {}) {
|
|
|
297
296
|
}
|
|
298
297
|
return e;
|
|
299
298
|
});
|
|
299
|
+
updated();
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
302
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unhead",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.0-beta.
|
|
4
|
+
"version": "1.3.0-beta.1",
|
|
5
5
|
"author": "Harlan Wilton <harlan@harlanzw.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/harlan-zw",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"hookable": "^5.5.3",
|
|
33
|
-
"@unhead/dom": "1.3.0-beta.
|
|
34
|
-
"@unhead/schema": "1.3.0-beta.
|
|
35
|
-
"@unhead/shared": "1.3.0-beta.
|
|
33
|
+
"@unhead/dom": "1.3.0-beta.1",
|
|
34
|
+
"@unhead/schema": "1.3.0-beta.1",
|
|
35
|
+
"@unhead/shared": "1.3.0-beta.1"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "unbuild .",
|