unhead 1.8.0-beta.9 → 1.8.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 +59 -26
- package/dist/index.d.cts +6 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +59 -26
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -468,6 +468,10 @@ function useServerSeoMeta(input, options) {
|
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
const UseScriptDefaults = {
|
|
472
|
+
defer: true,
|
|
473
|
+
fetchpriority: "low"
|
|
474
|
+
};
|
|
471
475
|
function useScript(input, _options) {
|
|
472
476
|
const options = _options || {};
|
|
473
477
|
const head = options.head || getActiveHead();
|
|
@@ -483,29 +487,53 @@ function useScript(input, _options) {
|
|
|
483
487
|
await head.hooks.callHook("script:transform", ctx);
|
|
484
488
|
return { script: [ctx.script] };
|
|
485
489
|
}
|
|
490
|
+
function maybeHintEarlyConnection(rel) {
|
|
491
|
+
if (
|
|
492
|
+
// opt-out
|
|
493
|
+
options.skipEarlyConnections || !input.src.includes("//") || !head.ssr
|
|
494
|
+
)
|
|
495
|
+
return;
|
|
496
|
+
const key2 = `use-script.${id}.early-connection`;
|
|
497
|
+
head.push({
|
|
498
|
+
link: [{ key: key2, rel, href: new URL(input.src).origin }]
|
|
499
|
+
}, { mode: "server" });
|
|
500
|
+
}
|
|
486
501
|
const script = {
|
|
487
502
|
id,
|
|
488
503
|
status: "awaitingLoad",
|
|
489
504
|
loaded: false,
|
|
490
|
-
remove
|
|
505
|
+
remove() {
|
|
491
506
|
if (script.status === "loaded") {
|
|
492
507
|
script.entry?.dispose();
|
|
493
508
|
script.status = "removed";
|
|
494
509
|
head.hooks.callHook(`script:updated`, hookCtx);
|
|
510
|
+
delete head._scripts?.[id];
|
|
495
511
|
return true;
|
|
496
512
|
}
|
|
497
513
|
return false;
|
|
498
514
|
},
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
515
|
+
waitForLoad() {
|
|
516
|
+
return new Promise((resolve) => {
|
|
517
|
+
if (script.status === "loaded")
|
|
518
|
+
resolve(options.use());
|
|
519
|
+
function watchForScriptLoaded({ script: script2 }) {
|
|
520
|
+
if (script2.id === id && script2.status === "loaded") {
|
|
521
|
+
resolve(options.use?.());
|
|
522
|
+
head.hooks.removeHook("script:updated", watchForScriptLoaded);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
head.hooks.hook("script:updated", watchForScriptLoaded);
|
|
526
|
+
});
|
|
527
|
+
},
|
|
528
|
+
load() {
|
|
502
529
|
if (script.status !== "awaitingLoad")
|
|
503
|
-
return script.
|
|
530
|
+
return script.waitForLoad();
|
|
504
531
|
script.status = "loading";
|
|
505
532
|
head.hooks.callHook(`script:updated`, hookCtx);
|
|
506
533
|
script.entry = head.push({
|
|
507
534
|
script: [
|
|
508
|
-
|
|
535
|
+
// async by default
|
|
536
|
+
{ ...UseScriptDefaults, ...input, key }
|
|
509
537
|
]
|
|
510
538
|
}, {
|
|
511
539
|
...options,
|
|
@@ -513,42 +541,47 @@ function useScript(input, _options) {
|
|
|
513
541
|
transform,
|
|
514
542
|
head
|
|
515
543
|
});
|
|
516
|
-
return script.
|
|
544
|
+
return script.waitForLoad();
|
|
517
545
|
}
|
|
518
546
|
};
|
|
519
547
|
const hookCtx = { script };
|
|
520
548
|
shared.NetworkEvents.forEach((fn) => {
|
|
521
549
|
const _fn = typeof input[fn] === "function" ? input[fn].bind({}) : null;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
};
|
|
528
|
-
}
|
|
550
|
+
input[fn] = (e) => {
|
|
551
|
+
script.status = fn === "onload" ? "loaded" : fn === "onerror" ? "error" : "loading";
|
|
552
|
+
head.hooks.callHook(`script:updated`, hookCtx);
|
|
553
|
+
_fn && _fn(e);
|
|
554
|
+
};
|
|
529
555
|
});
|
|
530
556
|
let trigger = options.trigger;
|
|
531
557
|
if (trigger) {
|
|
532
|
-
trigger === "idle"
|
|
558
|
+
const isIdle = trigger === "idle";
|
|
559
|
+
if (isIdle) {
|
|
560
|
+
if (head.ssr)
|
|
561
|
+
trigger = "manual";
|
|
562
|
+
else
|
|
563
|
+
trigger = new Promise((resolve) => requestIdleCallback(() => resolve()));
|
|
564
|
+
}
|
|
533
565
|
trigger === "manual" && (trigger = new Promise(() => {
|
|
534
566
|
}));
|
|
535
567
|
trigger instanceof Promise && trigger.then(script.load);
|
|
568
|
+
maybeHintEarlyConnection(isIdle ? "preconnect" : "dns-prefetch");
|
|
536
569
|
} else {
|
|
537
570
|
script.load();
|
|
571
|
+
maybeHintEarlyConnection("preconnect");
|
|
538
572
|
}
|
|
539
|
-
script.waitForUse = () => new Promise((resolve) => {
|
|
540
|
-
if (typeof options.use === "function") {
|
|
541
|
-
if (script.status === "loaded")
|
|
542
|
-
resolve(options.use());
|
|
543
|
-
head.hooks.hook("script:loaded", ({ script: script2 }) => script2.id === id && resolve(options.use()));
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
573
|
function resolveInnerHtmlLoad(ctx) {
|
|
547
574
|
if (ctx.tag.key === key) {
|
|
548
575
|
if (ctx.tag.innerHTML) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
576
|
+
setTimeout(
|
|
577
|
+
() => {
|
|
578
|
+
script.status = "loaded";
|
|
579
|
+
head.hooks.callHook("script:updated", hookCtx);
|
|
580
|
+
typeof input.onload === "function" && input.onload(new Event("load"));
|
|
581
|
+
},
|
|
582
|
+
5
|
|
583
|
+
/* give inline script a chance to run */
|
|
584
|
+
);
|
|
552
585
|
}
|
|
553
586
|
head.hooks.removeHook("dom:renderTag", resolveInnerHtmlLoad);
|
|
554
587
|
}
|
|
@@ -568,7 +601,7 @@ function useScript(input, _options) {
|
|
|
568
601
|
const api = options.use();
|
|
569
602
|
return api[fn](...args);
|
|
570
603
|
} else {
|
|
571
|
-
return script.
|
|
604
|
+
return script.waitForLoad().then(
|
|
572
605
|
(api) => {
|
|
573
606
|
api[fn](...args);
|
|
574
607
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -41,6 +41,12 @@ declare function useSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions):
|
|
|
41
41
|
|
|
42
42
|
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Load third-party scripts with SSR support and a proxied API.
|
|
46
|
+
*
|
|
47
|
+
* @experimental
|
|
48
|
+
* @see https://unhead.unjs.io/usage/composables/use-script
|
|
49
|
+
*/
|
|
44
50
|
declare function useScript<T>(input: UseScriptInput, _options?: UseScriptOptions<T>): T & {
|
|
45
51
|
$script: ScriptInstance<T>;
|
|
46
52
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -41,6 +41,12 @@ declare function useSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions):
|
|
|
41
41
|
|
|
42
42
|
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Load third-party scripts with SSR support and a proxied API.
|
|
46
|
+
*
|
|
47
|
+
* @experimental
|
|
48
|
+
* @see https://unhead.unjs.io/usage/composables/use-script
|
|
49
|
+
*/
|
|
44
50
|
declare function useScript<T>(input: UseScriptInput, _options?: UseScriptOptions<T>): T & {
|
|
45
51
|
$script: ScriptInstance<T>;
|
|
46
52
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,12 @@ declare function useSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions):
|
|
|
41
41
|
|
|
42
42
|
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: HeadEntryOptions): ActiveHeadEntry<any> | void;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Load third-party scripts with SSR support and a proxied API.
|
|
46
|
+
*
|
|
47
|
+
* @experimental
|
|
48
|
+
* @see https://unhead.unjs.io/usage/composables/use-script
|
|
49
|
+
*/
|
|
44
50
|
declare function useScript<T>(input: UseScriptInput, _options?: UseScriptOptions<T>): T & {
|
|
45
51
|
$script: ScriptInstance<T>;
|
|
46
52
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -467,6 +467,10 @@ function useServerSeoMeta(input, options) {
|
|
|
467
467
|
});
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
const UseScriptDefaults = {
|
|
471
|
+
defer: true,
|
|
472
|
+
fetchpriority: "low"
|
|
473
|
+
};
|
|
470
474
|
function useScript(input, _options) {
|
|
471
475
|
const options = _options || {};
|
|
472
476
|
const head = options.head || getActiveHead();
|
|
@@ -482,29 +486,53 @@ function useScript(input, _options) {
|
|
|
482
486
|
await head.hooks.callHook("script:transform", ctx);
|
|
483
487
|
return { script: [ctx.script] };
|
|
484
488
|
}
|
|
489
|
+
function maybeHintEarlyConnection(rel) {
|
|
490
|
+
if (
|
|
491
|
+
// opt-out
|
|
492
|
+
options.skipEarlyConnections || !input.src.includes("//") || !head.ssr
|
|
493
|
+
)
|
|
494
|
+
return;
|
|
495
|
+
const key2 = `use-script.${id}.early-connection`;
|
|
496
|
+
head.push({
|
|
497
|
+
link: [{ key: key2, rel, href: new URL(input.src).origin }]
|
|
498
|
+
}, { mode: "server" });
|
|
499
|
+
}
|
|
485
500
|
const script = {
|
|
486
501
|
id,
|
|
487
502
|
status: "awaitingLoad",
|
|
488
503
|
loaded: false,
|
|
489
|
-
remove
|
|
504
|
+
remove() {
|
|
490
505
|
if (script.status === "loaded") {
|
|
491
506
|
script.entry?.dispose();
|
|
492
507
|
script.status = "removed";
|
|
493
508
|
head.hooks.callHook(`script:updated`, hookCtx);
|
|
509
|
+
delete head._scripts?.[id];
|
|
494
510
|
return true;
|
|
495
511
|
}
|
|
496
512
|
return false;
|
|
497
513
|
},
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
514
|
+
waitForLoad() {
|
|
515
|
+
return new Promise((resolve) => {
|
|
516
|
+
if (script.status === "loaded")
|
|
517
|
+
resolve(options.use());
|
|
518
|
+
function watchForScriptLoaded({ script: script2 }) {
|
|
519
|
+
if (script2.id === id && script2.status === "loaded") {
|
|
520
|
+
resolve(options.use?.());
|
|
521
|
+
head.hooks.removeHook("script:updated", watchForScriptLoaded);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
head.hooks.hook("script:updated", watchForScriptLoaded);
|
|
525
|
+
});
|
|
526
|
+
},
|
|
527
|
+
load() {
|
|
501
528
|
if (script.status !== "awaitingLoad")
|
|
502
|
-
return script.
|
|
529
|
+
return script.waitForLoad();
|
|
503
530
|
script.status = "loading";
|
|
504
531
|
head.hooks.callHook(`script:updated`, hookCtx);
|
|
505
532
|
script.entry = head.push({
|
|
506
533
|
script: [
|
|
507
|
-
|
|
534
|
+
// async by default
|
|
535
|
+
{ ...UseScriptDefaults, ...input, key }
|
|
508
536
|
]
|
|
509
537
|
}, {
|
|
510
538
|
...options,
|
|
@@ -512,42 +540,47 @@ function useScript(input, _options) {
|
|
|
512
540
|
transform,
|
|
513
541
|
head
|
|
514
542
|
});
|
|
515
|
-
return script.
|
|
543
|
+
return script.waitForLoad();
|
|
516
544
|
}
|
|
517
545
|
};
|
|
518
546
|
const hookCtx = { script };
|
|
519
547
|
NetworkEvents.forEach((fn) => {
|
|
520
548
|
const _fn = typeof input[fn] === "function" ? input[fn].bind({}) : null;
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
};
|
|
527
|
-
}
|
|
549
|
+
input[fn] = (e) => {
|
|
550
|
+
script.status = fn === "onload" ? "loaded" : fn === "onerror" ? "error" : "loading";
|
|
551
|
+
head.hooks.callHook(`script:updated`, hookCtx);
|
|
552
|
+
_fn && _fn(e);
|
|
553
|
+
};
|
|
528
554
|
});
|
|
529
555
|
let trigger = options.trigger;
|
|
530
556
|
if (trigger) {
|
|
531
|
-
trigger === "idle"
|
|
557
|
+
const isIdle = trigger === "idle";
|
|
558
|
+
if (isIdle) {
|
|
559
|
+
if (head.ssr)
|
|
560
|
+
trigger = "manual";
|
|
561
|
+
else
|
|
562
|
+
trigger = new Promise((resolve) => requestIdleCallback(() => resolve()));
|
|
563
|
+
}
|
|
532
564
|
trigger === "manual" && (trigger = new Promise(() => {
|
|
533
565
|
}));
|
|
534
566
|
trigger instanceof Promise && trigger.then(script.load);
|
|
567
|
+
maybeHintEarlyConnection(isIdle ? "preconnect" : "dns-prefetch");
|
|
535
568
|
} else {
|
|
536
569
|
script.load();
|
|
570
|
+
maybeHintEarlyConnection("preconnect");
|
|
537
571
|
}
|
|
538
|
-
script.waitForUse = () => new Promise((resolve) => {
|
|
539
|
-
if (typeof options.use === "function") {
|
|
540
|
-
if (script.status === "loaded")
|
|
541
|
-
resolve(options.use());
|
|
542
|
-
head.hooks.hook("script:loaded", ({ script: script2 }) => script2.id === id && resolve(options.use()));
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
572
|
function resolveInnerHtmlLoad(ctx) {
|
|
546
573
|
if (ctx.tag.key === key) {
|
|
547
574
|
if (ctx.tag.innerHTML) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
575
|
+
setTimeout(
|
|
576
|
+
() => {
|
|
577
|
+
script.status = "loaded";
|
|
578
|
+
head.hooks.callHook("script:updated", hookCtx);
|
|
579
|
+
typeof input.onload === "function" && input.onload(new Event("load"));
|
|
580
|
+
},
|
|
581
|
+
5
|
|
582
|
+
/* give inline script a chance to run */
|
|
583
|
+
);
|
|
551
584
|
}
|
|
552
585
|
head.hooks.removeHook("dom:renderTag", resolveInnerHtmlLoad);
|
|
553
586
|
}
|
|
@@ -567,7 +600,7 @@ function useScript(input, _options) {
|
|
|
567
600
|
const api = options.use();
|
|
568
601
|
return api[fn](...args);
|
|
569
602
|
} else {
|
|
570
|
-
return script.
|
|
603
|
+
return script.waitForLoad().then(
|
|
571
604
|
(api) => {
|
|
572
605
|
api[fn](...args);
|
|
573
606
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unhead",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.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.8.
|
|
34
|
-
"@unhead/schema": "1.8.
|
|
35
|
-
"@unhead/shared": "1.8.
|
|
33
|
+
"@unhead/dom": "1.8.1",
|
|
34
|
+
"@unhead/schema": "1.8.1",
|
|
35
|
+
"@unhead/shared": "1.8.1"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "unbuild .",
|