unhead 1.9.16 → 1.10.0-beta.3
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 +268 -132
- package/dist/index.d.cts +10 -6
- package/dist/index.d.mts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.mjs +268 -133
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -4,72 +4,80 @@ const hookable = require('hookable');
|
|
|
4
4
|
const dom = require('@unhead/dom');
|
|
5
5
|
const shared = require('@unhead/shared');
|
|
6
6
|
|
|
7
|
-
const UsesMergeStrategy = ["templateParams", "htmlAttrs", "bodyAttrs"];
|
|
7
|
+
const UsesMergeStrategy = /* @__PURE__ */ new Set(["templateParams", "htmlAttrs", "bodyAttrs"]);
|
|
8
8
|
const DedupePlugin = shared.defineHeadPlugin({
|
|
9
9
|
hooks: {
|
|
10
|
-
"tag:normalise":
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
"tag:normalise": ({ tag }) => {
|
|
11
|
+
if (tag.props.hid) {
|
|
12
|
+
tag.key = tag.props.hid;
|
|
13
|
+
delete tag.props.hid;
|
|
14
|
+
}
|
|
15
|
+
if (tag.props.vmid) {
|
|
16
|
+
tag.key = tag.props.vmid;
|
|
17
|
+
delete tag.props.vmid;
|
|
18
|
+
}
|
|
19
|
+
if (tag.props.key) {
|
|
20
|
+
tag.key = tag.props.key;
|
|
21
|
+
delete tag.props.key;
|
|
22
|
+
}
|
|
17
23
|
const generatedKey = shared.tagDedupeKey(tag);
|
|
18
24
|
const dedupe = generatedKey || (tag.key ? `${tag.tag}:${tag.key}` : false);
|
|
19
25
|
if (dedupe)
|
|
20
26
|
tag._d = dedupe;
|
|
21
27
|
},
|
|
22
|
-
"tags:resolve":
|
|
23
|
-
const deduping =
|
|
24
|
-
ctx.tags
|
|
28
|
+
"tags:resolve": (ctx) => {
|
|
29
|
+
const deduping = /* @__PURE__ */ Object.create(null);
|
|
30
|
+
for (const tag of ctx.tags) {
|
|
25
31
|
const dedupeKey = (tag.key ? `${tag.tag}:${tag.key}` : tag._d) || tag._p;
|
|
26
32
|
const dupedTag = deduping[dedupeKey];
|
|
27
33
|
if (dupedTag) {
|
|
28
34
|
let strategy = tag?.tagDuplicateStrategy;
|
|
29
|
-
if (!strategy && UsesMergeStrategy.
|
|
35
|
+
if (!strategy && UsesMergeStrategy.has(tag.tag))
|
|
30
36
|
strategy = "merge";
|
|
31
37
|
if (strategy === "merge") {
|
|
32
38
|
const oldProps = dupedTag.props;
|
|
33
|
-
|
|
34
|
-
if (oldProps[
|
|
35
|
-
|
|
36
|
-
if (key === "style" && !oldProps[key].endsWith(";"))
|
|
37
|
-
oldProps[key] += ";";
|
|
38
|
-
tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
|
|
39
|
-
} else {
|
|
40
|
-
tag.props[key] = oldProps[key];
|
|
41
|
-
}
|
|
39
|
+
if (oldProps.style && tag.props.style) {
|
|
40
|
+
if (oldProps.style[oldProps.style.length - 1] !== ";") {
|
|
41
|
+
oldProps.style += ";";
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
tag.props.style = `${oldProps.style} ${tag.props.style}`;
|
|
44
|
+
}
|
|
45
|
+
if (oldProps.class && tag.props.class) {
|
|
46
|
+
tag.props.class = `${oldProps.class} ${tag.props.class}`;
|
|
47
|
+
} else if (oldProps.class) {
|
|
48
|
+
tag.props.class = oldProps.class;
|
|
49
|
+
}
|
|
44
50
|
deduping[dedupeKey].props = {
|
|
45
51
|
...oldProps,
|
|
46
52
|
...tag.props
|
|
47
53
|
};
|
|
48
|
-
|
|
54
|
+
continue;
|
|
49
55
|
} else if (tag._e === dupedTag._e) {
|
|
50
56
|
dupedTag._duped = dupedTag._duped || [];
|
|
51
57
|
tag._d = `${dupedTag._d}:${dupedTag._duped.length + 1}`;
|
|
52
58
|
dupedTag._duped.push(tag);
|
|
53
|
-
|
|
59
|
+
continue;
|
|
54
60
|
} else if (shared.tagWeight(tag) > shared.tagWeight(dupedTag)) {
|
|
55
|
-
|
|
61
|
+
continue;
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
|
-
const
|
|
59
|
-
if (shared.HasElementTags.
|
|
64
|
+
const hasProps = tag.innerHTML || tag.textContent || Object.keys(tag.props).length !== 0;
|
|
65
|
+
if (!hasProps && shared.HasElementTags.has(tag.tag)) {
|
|
60
66
|
delete deduping[dedupeKey];
|
|
61
|
-
|
|
67
|
+
continue;
|
|
62
68
|
}
|
|
63
69
|
deduping[dedupeKey] = tag;
|
|
64
|
-
}
|
|
70
|
+
}
|
|
65
71
|
const newTags = [];
|
|
66
|
-
|
|
72
|
+
for (const key in deduping) {
|
|
73
|
+
const tag = deduping[key];
|
|
67
74
|
const dupes = tag._duped;
|
|
68
|
-
delete tag._duped;
|
|
69
75
|
newTags.push(tag);
|
|
70
|
-
if (dupes)
|
|
76
|
+
if (dupes) {
|
|
77
|
+
delete tag._duped;
|
|
71
78
|
newTags.push(...dupes);
|
|
72
|
-
|
|
79
|
+
}
|
|
80
|
+
}
|
|
73
81
|
ctx.tags = newTags;
|
|
74
82
|
ctx.tags = ctx.tags.filter((t) => !(t.tag === "meta" && (t.props.name || t.props.property) && !t.props.content));
|
|
75
83
|
}
|
|
@@ -79,53 +87,84 @@ const DedupePlugin = shared.defineHeadPlugin({
|
|
|
79
87
|
const PayloadPlugin = shared.defineHeadPlugin({
|
|
80
88
|
mode: "server",
|
|
81
89
|
hooks: {
|
|
82
|
-
"tags:resolve":
|
|
90
|
+
"tags:resolve": (ctx) => {
|
|
83
91
|
const payload = {};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
+
let hasPayload = false;
|
|
93
|
+
for (const tag of ctx.tags) {
|
|
94
|
+
if (tag._m !== "server" || tag.tag !== "titleTemplate" && tag.tag !== "templateParams" && tag.tag !== "title") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
payload[tag.tag] = tag.tag === "title" || tag.tag === "titleTemplate" ? tag.textContent : tag.props;
|
|
98
|
+
hasPayload = true;
|
|
99
|
+
}
|
|
100
|
+
if (hasPayload) {
|
|
101
|
+
ctx.tags.push({
|
|
102
|
+
tag: "script",
|
|
103
|
+
innerHTML: JSON.stringify(payload),
|
|
104
|
+
props: { id: "unhead:payload", type: "application/json" }
|
|
105
|
+
});
|
|
106
|
+
}
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
});
|
|
95
110
|
|
|
96
|
-
const ValidEventTags = ["script", "link", "bodyAttrs"];
|
|
111
|
+
const ValidEventTags = /* @__PURE__ */ new Set(["script", "link", "bodyAttrs"]);
|
|
97
112
|
const EventHandlersPlugin = shared.defineHeadPlugin((head) => ({
|
|
98
113
|
hooks: {
|
|
99
|
-
"tags:resolve":
|
|
100
|
-
for (const tag of ctx.tags
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
tag._eventHandlers[key] = value;
|
|
114
|
+
"tags:resolve": (ctx) => {
|
|
115
|
+
for (const tag of ctx.tags) {
|
|
116
|
+
if (!ValidEventTags.has(tag.tag)) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const props = tag.props;
|
|
120
|
+
for (const key in props) {
|
|
121
|
+
if (key[0] !== "o" || key[1] !== "n") {
|
|
122
|
+
continue;
|
|
109
123
|
}
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
if (!Object.prototype.hasOwnProperty.call(props, key)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const value = props[key];
|
|
128
|
+
if (typeof value !== "function") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (head.ssr && shared.NetworkEvents.has(key)) {
|
|
132
|
+
props[key] = `this.dataset.${key}fired = true`;
|
|
133
|
+
} else {
|
|
134
|
+
delete props[key];
|
|
135
|
+
}
|
|
136
|
+
tag._eventHandlers = tag._eventHandlers || {};
|
|
137
|
+
tag._eventHandlers[key] = value;
|
|
138
|
+
}
|
|
139
|
+
if (head.ssr && tag._eventHandlers && (tag.props.src || tag.props.href)) {
|
|
112
140
|
tag.key = tag.key || shared.hashCode(tag.props.src || tag.props.href);
|
|
141
|
+
}
|
|
113
142
|
}
|
|
114
143
|
},
|
|
115
|
-
"dom:renderTag":
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
"dom:renderTag": ({ $el, tag }) => {
|
|
145
|
+
const dataset = $el?.dataset;
|
|
146
|
+
if (!dataset) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const k in dataset) {
|
|
150
|
+
if (!k.endsWith("fired")) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const ek = k.slice(0, -5);
|
|
154
|
+
if (!shared.NetworkEvents.has(ek)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
tag._eventHandlers?.[ek]?.call($el, new Event(ek.substring(2)));
|
|
119
158
|
}
|
|
120
159
|
}
|
|
121
160
|
}
|
|
122
161
|
}));
|
|
123
162
|
|
|
124
|
-
const DupeableTags = ["link", "style", "script", "noscript"];
|
|
163
|
+
const DupeableTags = /* @__PURE__ */ new Set(["link", "style", "script", "noscript"]);
|
|
125
164
|
const HashKeyedPlugin = shared.defineHeadPlugin({
|
|
126
165
|
hooks: {
|
|
127
166
|
"tag:normalise": ({ tag }) => {
|
|
128
|
-
if (tag.key && DupeableTags.
|
|
167
|
+
if (tag.key && DupeableTags.has(tag.tag)) {
|
|
129
168
|
tag.props["data-hid"] = tag._h = shared.hashCode(tag.key);
|
|
130
169
|
}
|
|
131
170
|
}
|
|
@@ -135,17 +174,32 @@ const HashKeyedPlugin = shared.defineHeadPlugin({
|
|
|
135
174
|
const SortPlugin = shared.defineHeadPlugin({
|
|
136
175
|
hooks: {
|
|
137
176
|
"tags:resolve": (ctx) => {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
|
|
177
|
+
for (const tag of ctx.tags) {
|
|
178
|
+
if (typeof tag.tagPriority !== "string") {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
for (const { prefix, offset } of shared.SortModifiers) {
|
|
182
|
+
if (!tag.tagPriority.startsWith(prefix)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const key = tag.tagPriority.substring(prefix.length);
|
|
186
|
+
const position = ctx.tags.find((tag2) => tag2._d === key)?._p;
|
|
187
|
+
if (position !== void 0) {
|
|
145
188
|
tag._p = position + offset;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
146
191
|
}
|
|
147
192
|
}
|
|
148
|
-
ctx.tags.sort((a, b) =>
|
|
193
|
+
ctx.tags.sort((a, b) => {
|
|
194
|
+
const aWeight = shared.tagWeight(a);
|
|
195
|
+
const bWeight = shared.tagWeight(b);
|
|
196
|
+
if (aWeight < bWeight) {
|
|
197
|
+
return -1;
|
|
198
|
+
} else if (aWeight > bWeight) {
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
return a._p - b._p;
|
|
202
|
+
});
|
|
149
203
|
}
|
|
150
204
|
}
|
|
151
205
|
});
|
|
@@ -155,30 +209,45 @@ const SupportedAttrs = {
|
|
|
155
209
|
link: "href",
|
|
156
210
|
htmlAttrs: "lang"
|
|
157
211
|
};
|
|
212
|
+
const contentAttrs = ["innerHTML", "textContent"];
|
|
158
213
|
const TemplateParamsPlugin = shared.defineHeadPlugin((head) => ({
|
|
159
214
|
hooks: {
|
|
160
215
|
"tags:resolve": (ctx) => {
|
|
161
216
|
const { tags } = ctx;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
217
|
+
let templateParams;
|
|
218
|
+
for (let i = 0; i < tags.length; i += 1) {
|
|
219
|
+
const tag = tags[i];
|
|
220
|
+
if (tag.tag !== "templateParams") {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
templateParams = ctx.tags.splice(i, 1)[0].props;
|
|
224
|
+
i -= 1;
|
|
225
|
+
}
|
|
226
|
+
const params = templateParams || {};
|
|
165
227
|
const sep = params.separator || "|";
|
|
166
228
|
delete params.separator;
|
|
167
|
-
params.pageTitle = shared.processTemplateParams(
|
|
168
|
-
|
|
229
|
+
params.pageTitle = shared.processTemplateParams(
|
|
230
|
+
// find templateParams
|
|
231
|
+
params.pageTitle || tags.find((tag) => tag.tag === "title")?.textContent || "",
|
|
232
|
+
params,
|
|
233
|
+
sep
|
|
234
|
+
);
|
|
235
|
+
for (const tag of tags) {
|
|
236
|
+
if (tag.processTemplateParams === false) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
169
239
|
const v = SupportedAttrs[tag.tag];
|
|
170
240
|
if (v && typeof tag.props[v] === "string") {
|
|
171
241
|
tag.props[v] = shared.processTemplateParams(tag.props[v], params, sep);
|
|
172
|
-
} else if (tag.processTemplateParams
|
|
173
|
-
|
|
242
|
+
} else if (tag.processTemplateParams || tag.tag === "titleTemplate" || tag.tag === "title") {
|
|
243
|
+
for (const p of contentAttrs) {
|
|
174
244
|
if (typeof tag[p] === "string")
|
|
175
245
|
tag[p] = shared.processTemplateParams(tag[p], params, sep);
|
|
176
|
-
}
|
|
246
|
+
}
|
|
177
247
|
}
|
|
178
248
|
}
|
|
179
249
|
head._templateParams = params;
|
|
180
250
|
head._separator = sep;
|
|
181
|
-
ctx.tags = tags.filter((tag) => tag.tag !== "templateParams");
|
|
182
251
|
}
|
|
183
252
|
}
|
|
184
253
|
}));
|
|
@@ -187,42 +256,49 @@ const TitleTemplatePlugin = shared.defineHeadPlugin({
|
|
|
187
256
|
hooks: {
|
|
188
257
|
"tags:resolve": (ctx) => {
|
|
189
258
|
const { tags } = ctx;
|
|
190
|
-
let
|
|
191
|
-
|
|
192
|
-
|
|
259
|
+
let titleTag;
|
|
260
|
+
let titleTemplateTag;
|
|
261
|
+
for (let i = 0; i < tags.length; i += 1) {
|
|
262
|
+
const tag = tags[i];
|
|
263
|
+
if (tag.tag === "title") {
|
|
264
|
+
titleTag = tag;
|
|
265
|
+
} else if (tag.tag === "titleTemplate") {
|
|
266
|
+
titleTemplateTag = tag;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (titleTemplateTag && titleTag) {
|
|
193
270
|
const newTitle = shared.resolveTitleTemplate(
|
|
194
|
-
|
|
195
|
-
|
|
271
|
+
titleTemplateTag.textContent,
|
|
272
|
+
titleTag.textContent
|
|
196
273
|
);
|
|
197
274
|
if (newTitle !== null) {
|
|
198
|
-
|
|
275
|
+
titleTag.textContent = newTitle || titleTag.textContent;
|
|
199
276
|
} else {
|
|
200
|
-
|
|
277
|
+
ctx.tags.splice(ctx.tags.indexOf(titleTag), 1);
|
|
201
278
|
}
|
|
202
|
-
} else if (
|
|
279
|
+
} else if (titleTemplateTag) {
|
|
203
280
|
const newTitle = shared.resolveTitleTemplate(
|
|
204
|
-
|
|
281
|
+
titleTemplateTag.textContent
|
|
205
282
|
);
|
|
206
283
|
if (newTitle !== null) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
284
|
+
titleTemplateTag.textContent = newTitle;
|
|
285
|
+
titleTemplateTag.tag = "title";
|
|
286
|
+
titleTemplateTag = void 0;
|
|
210
287
|
}
|
|
211
288
|
}
|
|
212
|
-
if (
|
|
213
|
-
|
|
289
|
+
if (titleTemplateTag) {
|
|
290
|
+
ctx.tags.splice(ctx.tags.indexOf(titleTemplateTag), 1);
|
|
214
291
|
}
|
|
215
|
-
ctx.tags = tags.filter(Boolean);
|
|
216
292
|
}
|
|
217
293
|
}
|
|
218
294
|
});
|
|
219
295
|
|
|
220
296
|
const XSSPlugin = shared.defineHeadPlugin({
|
|
221
297
|
hooks: {
|
|
222
|
-
"tags:afterResolve":
|
|
298
|
+
"tags:afterResolve": (ctx) => {
|
|
223
299
|
for (const tag of ctx.tags) {
|
|
224
300
|
if (typeof tag.innerHTML === "string") {
|
|
225
|
-
if (tag.innerHTML &&
|
|
301
|
+
if (tag.innerHTML && (tag.props.type === "application/ld+json" || tag.props.type === "application/json")) {
|
|
226
302
|
tag.innerHTML = tag.innerHTML.replace(/</g, "\\u003C");
|
|
227
303
|
} else {
|
|
228
304
|
tag.innerHTML = tag.innerHTML.replace(new RegExp(`</${tag.tag}`, "g"), `<\\/${tag.tag}`);
|
|
@@ -293,12 +369,11 @@ function createHeadCore(options = {}) {
|
|
|
293
369
|
},
|
|
294
370
|
// a patch is the same as creating a new entry, just a nice DX
|
|
295
371
|
patch(input2) {
|
|
296
|
-
|
|
372
|
+
for (const e of entries) {
|
|
297
373
|
if (e._i === entry._i) {
|
|
298
374
|
e.input = entry.input = input2;
|
|
299
375
|
}
|
|
300
|
-
|
|
301
|
-
});
|
|
376
|
+
}
|
|
302
377
|
updated();
|
|
303
378
|
}
|
|
304
379
|
};
|
|
@@ -349,7 +424,7 @@ const importRe = /@import/;
|
|
|
349
424
|
function CapoPlugin(options) {
|
|
350
425
|
return shared.defineHeadPlugin({
|
|
351
426
|
hooks: {
|
|
352
|
-
"tags:beforeResolve":
|
|
427
|
+
"tags:beforeResolve": ({ tags }) => {
|
|
353
428
|
for (const tag of tags) {
|
|
354
429
|
if (tag.tagPosition && tag.tagPosition !== "head")
|
|
355
430
|
continue;
|
|
@@ -367,11 +442,11 @@ function CapoPlugin(options) {
|
|
|
367
442
|
tag.tagPriority = 50;
|
|
368
443
|
} else if (isLink && tag.props.rel === "stylesheet" || tag.tag === "style") {
|
|
369
444
|
tag.tagPriority = 60;
|
|
370
|
-
} else if (isLink &&
|
|
445
|
+
} else if (isLink && (tag.props.rel === "preload" || tag.props.rel === "modulepreload")) {
|
|
371
446
|
tag.tagPriority = 70;
|
|
372
447
|
} else if (isScript && isTruthy(tag.props.defer) && tag.props.src && !isTruthy(tag.props.async)) {
|
|
373
448
|
tag.tagPriority = 80;
|
|
374
|
-
} else if (isLink &&
|
|
449
|
+
} else if (isLink && (tag.props.rel === "prefetch" || tag.props.rel === "dns-prefetch" || tag.props.rel === "prerender")) {
|
|
375
450
|
tag.tagPriority = 90;
|
|
376
451
|
}
|
|
377
452
|
}
|
|
@@ -402,9 +477,9 @@ function useHead(input, options = {}) {
|
|
|
402
477
|
return head?.push(input, options);
|
|
403
478
|
}
|
|
404
479
|
|
|
405
|
-
function useHeadSafe(input, options
|
|
480
|
+
function useHeadSafe(input, options) {
|
|
406
481
|
return useHead(input, {
|
|
407
|
-
...options
|
|
482
|
+
...options,
|
|
408
483
|
transform: shared.whitelistSafeInput
|
|
409
484
|
});
|
|
410
485
|
}
|
|
@@ -441,18 +516,25 @@ function useSeoMeta(input, options) {
|
|
|
441
516
|
|
|
442
517
|
function useServerSeoMeta(input, options) {
|
|
443
518
|
return useSeoMeta(input, {
|
|
444
|
-
...options
|
|
519
|
+
...options,
|
|
445
520
|
mode: "server"
|
|
446
521
|
});
|
|
447
522
|
}
|
|
448
523
|
|
|
524
|
+
const ScriptProxyTarget = Symbol("ScriptProxyTarget");
|
|
525
|
+
function sharedTarget() {
|
|
526
|
+
}
|
|
527
|
+
sharedTarget[ScriptProxyTarget] = true;
|
|
528
|
+
function resolveScriptKey(input) {
|
|
529
|
+
return input.key || shared.hashCode(input.src || (typeof input.innerHTML === "string" ? input.innerHTML : ""));
|
|
530
|
+
}
|
|
449
531
|
function useScript(_input, _options) {
|
|
450
532
|
const input = typeof _input === "string" ? { src: _input } : _input;
|
|
451
533
|
const options = _options || {};
|
|
452
534
|
const head = options.head || getActiveHead();
|
|
453
535
|
if (!head)
|
|
454
536
|
throw new Error("Missing Unhead context.");
|
|
455
|
-
const id =
|
|
537
|
+
const id = resolveScriptKey(input);
|
|
456
538
|
if (head._scripts?.[id])
|
|
457
539
|
return head._scripts[id];
|
|
458
540
|
options.beforeInit?.();
|
|
@@ -460,7 +542,7 @@ function useScript(_input, _options) {
|
|
|
460
542
|
script.status = s;
|
|
461
543
|
head.hooks.callHook(`script:updated`, hookCtx);
|
|
462
544
|
};
|
|
463
|
-
const trigger =
|
|
545
|
+
const trigger = options.trigger !== void 0 ? options.trigger : "client";
|
|
464
546
|
shared.ScriptNetworkEvents.forEach((fn) => {
|
|
465
547
|
const _fn = typeof input[fn] === "function" ? input[fn].bind(options.eventContext) : null;
|
|
466
548
|
input[fn] = (e) => {
|
|
@@ -468,15 +550,23 @@ function useScript(_input, _options) {
|
|
|
468
550
|
_fn?.(e);
|
|
469
551
|
};
|
|
470
552
|
});
|
|
471
|
-
const
|
|
553
|
+
const _cbs = { loaded: [], error: [] };
|
|
554
|
+
const _registerCb = (key, cb) => {
|
|
555
|
+
const i = _cbs[key].push(cb);
|
|
556
|
+
return () => _cbs[key].splice(i - 1, 1);
|
|
557
|
+
};
|
|
472
558
|
const loadPromise = new Promise((resolve, reject) => {
|
|
559
|
+
if (head.ssr)
|
|
560
|
+
return;
|
|
473
561
|
const emit = (api) => requestAnimationFrame(() => resolve(api));
|
|
474
562
|
const _ = head.hooks.hook("script:updated", ({ script: script2 }) => {
|
|
475
563
|
if (script2.id === id && (script2.status === "loaded" || script2.status === "error")) {
|
|
476
564
|
if (script2.status === "loaded") {
|
|
477
565
|
if (typeof options.use === "function") {
|
|
478
566
|
const api = options.use();
|
|
479
|
-
|
|
567
|
+
if (api) {
|
|
568
|
+
emit(api);
|
|
569
|
+
}
|
|
480
570
|
} else {
|
|
481
571
|
emit({});
|
|
482
572
|
}
|
|
@@ -486,8 +576,10 @@ function useScript(_input, _options) {
|
|
|
486
576
|
_();
|
|
487
577
|
}
|
|
488
578
|
});
|
|
489
|
-
})
|
|
490
|
-
const script = {
|
|
579
|
+
});
|
|
580
|
+
const script = Object.assign(loadPromise, {
|
|
581
|
+
instance: !head.ssr && options?.use?.() || null,
|
|
582
|
+
proxy: null,
|
|
491
583
|
id,
|
|
492
584
|
status: "awaitingLoad",
|
|
493
585
|
remove() {
|
|
@@ -499,7 +591,7 @@ function useScript(_input, _options) {
|
|
|
499
591
|
}
|
|
500
592
|
return false;
|
|
501
593
|
},
|
|
502
|
-
load() {
|
|
594
|
+
load(cb) {
|
|
503
595
|
if (!script.entry) {
|
|
504
596
|
syncStatus("loading");
|
|
505
597
|
const defaults = {
|
|
@@ -514,9 +606,26 @@ function useScript(_input, _options) {
|
|
|
514
606
|
script: [{ ...defaults, ...input, key: `script.${id}` }]
|
|
515
607
|
}, options);
|
|
516
608
|
}
|
|
609
|
+
if (cb)
|
|
610
|
+
_registerCb("loaded", cb);
|
|
517
611
|
return loadPromise;
|
|
518
|
-
}
|
|
519
|
-
|
|
612
|
+
},
|
|
613
|
+
onLoaded(cb) {
|
|
614
|
+
return _registerCb("loaded", cb);
|
|
615
|
+
},
|
|
616
|
+
onError(cb) {
|
|
617
|
+
return _registerCb("error", cb);
|
|
618
|
+
},
|
|
619
|
+
_cbs
|
|
620
|
+
});
|
|
621
|
+
loadPromise.then((api) => {
|
|
622
|
+
script.instance = api;
|
|
623
|
+
_cbs.loaded.forEach((cb) => cb(api));
|
|
624
|
+
_cbs.loaded = [];
|
|
625
|
+
}).catch((err) => {
|
|
626
|
+
_cbs.error.forEach((cb) => cb(err));
|
|
627
|
+
_cbs.error = [];
|
|
628
|
+
});
|
|
520
629
|
const hookCtx = { script };
|
|
521
630
|
if (trigger === "client" && !head.ssr || trigger === "server" && head.ssr)
|
|
522
631
|
script.load();
|
|
@@ -524,27 +633,53 @@ function useScript(_input, _options) {
|
|
|
524
633
|
trigger.then(script.load);
|
|
525
634
|
else if (typeof trigger === "function")
|
|
526
635
|
trigger(async () => script.load());
|
|
527
|
-
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
636
|
+
script.$script = script;
|
|
637
|
+
const proxyChain = (instance, accessor, accessors) => {
|
|
638
|
+
return new Proxy((!accessor ? instance : instance?.[accessor]) || sharedTarget, {
|
|
639
|
+
get(_, k, r) {
|
|
640
|
+
head.hooks.callHook("script:instance-fn", { script, fn: k, exists: k in _ });
|
|
641
|
+
if (!accessor) {
|
|
642
|
+
const stub = options.stub?.({ script, fn: k });
|
|
643
|
+
if (stub)
|
|
644
|
+
return stub;
|
|
645
|
+
}
|
|
646
|
+
if (_ && k in _) {
|
|
647
|
+
return Reflect.get(_, k, r);
|
|
648
|
+
}
|
|
649
|
+
if (k === Symbol.iterator) {
|
|
650
|
+
return [][Symbol.iterator];
|
|
651
|
+
}
|
|
652
|
+
return proxyChain(accessor ? instance?.[accessor] : instance, k, accessors || [k]);
|
|
653
|
+
},
|
|
654
|
+
async apply(_, _this, args) {
|
|
655
|
+
if (head.ssr && _[ScriptProxyTarget])
|
|
656
|
+
return;
|
|
657
|
+
let instance2;
|
|
658
|
+
const access = (fn2) => {
|
|
659
|
+
instance2 = fn2 || instance2;
|
|
660
|
+
for (let i = 0; i < (accessors || []).length; i++) {
|
|
661
|
+
const k = (accessors || [])[i];
|
|
662
|
+
fn2 = fn2?.[k];
|
|
663
|
+
}
|
|
664
|
+
return fn2;
|
|
665
|
+
};
|
|
666
|
+
const fn = access(script.instance) || access(await loadPromise);
|
|
667
|
+
return typeof fn === "function" ? Reflect.apply(fn, instance2, args) : fn;
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
};
|
|
671
|
+
script.proxy = proxyChain(script.instance);
|
|
672
|
+
const res = new Proxy(script, {
|
|
673
|
+
get(_, k) {
|
|
674
|
+
const target = k in script ? script : script.proxy;
|
|
675
|
+
if (k === "then" || k === "catch") {
|
|
676
|
+
return script[k].bind(script);
|
|
677
|
+
}
|
|
678
|
+
return Reflect.get(target, k, target);
|
|
541
679
|
}
|
|
542
680
|
});
|
|
543
|
-
head._scripts = Object.assign(
|
|
544
|
-
|
|
545
|
-
{ [id]: instance }
|
|
546
|
-
);
|
|
547
|
-
return instance;
|
|
681
|
+
head._scripts = Object.assign(head._scripts || {}, { [id]: res });
|
|
682
|
+
return res;
|
|
548
683
|
}
|
|
549
684
|
|
|
550
685
|
exports.composableNames = shared.composableNames;
|
|
@@ -554,6 +689,7 @@ exports.createHead = createHead;
|
|
|
554
689
|
exports.createHeadCore = createHeadCore;
|
|
555
690
|
exports.createServerHead = createServerHead;
|
|
556
691
|
exports.getActiveHead = getActiveHead;
|
|
692
|
+
exports.resolveScriptKey = resolveScriptKey;
|
|
557
693
|
exports.unheadComposablesImports = unheadComposablesImports;
|
|
558
694
|
exports.useHead = useHead;
|
|
559
695
|
exports.useHeadSafe = useHeadSafe;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _unhead_schema from '@unhead/schema';
|
|
2
|
-
import { Head, CreateHeadOptions, Unhead, MergeHead, HeadEntryOptions, ActiveHeadEntry, HeadSafe, UseSeoMetaInput, UseScriptInput, UseScriptOptions,
|
|
2
|
+
import { Head, CreateHeadOptions, Unhead, MergeHead, HeadEntryOptions, ActiveHeadEntry, HeadSafe, UseSeoMetaInput, ScriptInstance, AsAsyncFunctionValues, UseScriptResolvedInput, UseScriptInput, UseScriptOptions, UseFunctionType } from '@unhead/schema';
|
|
3
3
|
export { composableNames } from '@unhead/shared';
|
|
4
4
|
|
|
5
5
|
declare function createHead<T extends {} = Head>(options?: CreateHeadOptions): Unhead<T>;
|
|
@@ -41,14 +41,18 @@ declare function useSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions):
|
|
|
41
41
|
|
|
42
42
|
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
43
43
|
|
|
44
|
+
type UseScriptContext<T extends Record<symbol | string, any>> = (Promise<T> & ScriptInstance<T>) & AsAsyncFunctionValues<T> & {
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use top-level functions instead.
|
|
47
|
+
*/
|
|
48
|
+
$script: Promise<T> & ScriptInstance<T>;
|
|
49
|
+
};
|
|
50
|
+
declare function resolveScriptKey(input: UseScriptResolvedInput): any;
|
|
44
51
|
/**
|
|
45
52
|
* Load third-party scripts with SSR support and a proxied API.
|
|
46
53
|
*
|
|
47
|
-
* @experimental
|
|
48
54
|
* @see https://unhead.unjs.io/usage/composables/use-script
|
|
49
55
|
*/
|
|
50
|
-
declare function useScript<T extends Record<symbol | string, any>>(_input: UseScriptInput, _options?: UseScriptOptions<T>): T
|
|
51
|
-
$script: Promise<T> & ScriptInstance<T>;
|
|
52
|
-
};
|
|
56
|
+
declare function useScript<T extends Record<symbol | string, any> = Record<symbol | string, any>, U = Record<symbol | string, any>>(_input: UseScriptInput, _options?: UseScriptOptions<T, U>): UseScriptContext<UseFunctionType<UseScriptOptions<T, U>, T>>;
|
|
53
57
|
|
|
54
|
-
export { CapoPlugin, HashHydrationPlugin, type UseHeadInput, createHead, createHeadCore, createServerHead, getActiveHead, unheadComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
|
|
58
|
+
export { CapoPlugin, HashHydrationPlugin, type UseHeadInput, type UseScriptContext, createHead, createHeadCore, createServerHead, getActiveHead, resolveScriptKey, unheadComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
|