feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Harlan Wilton <harlan@harlanzw.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+12
View File
@@ -0,0 +1,12 @@
# @unhead/dom
## Install
```bash
# yarn add @unhead/dom
npm install @unhead/dom
```
## Documentation
See the [@unhead/dom](https://unhead.unjs.io/guide/getting-started/how-it-works#unheaddom) for how this works.
+201
View File
@@ -0,0 +1,201 @@
'use strict';
const shared = require('@unhead/shared');
async function renderDOMHead(head, options = {}) {
const dom = options.document || head.resolvedOptions.document;
if (!dom || !head.dirty)
return;
const beforeRenderCtx = { shouldRender: true, tags: [] };
await head.hooks.callHook("dom:beforeRender", beforeRenderCtx);
if (!beforeRenderCtx.shouldRender)
return;
if (head._domUpdatePromise) {
return head._domUpdatePromise;
}
head._domUpdatePromise = new Promise(async (resolve) => {
const tags = (await head.resolveTags()).map((tag) => ({
tag,
id: shared.HasElementTags.has(tag.tag) ? shared.hashTag(tag) : tag.tag,
shouldRender: true
}));
let state = head._dom;
if (!state) {
state = {
elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
};
const takenDedupeKeys = /* @__PURE__ */ new Set();
for (const key of ["body", "head"]) {
const children = dom[key]?.children;
for (const c of children) {
const tag = c.tagName.toLowerCase();
if (!shared.HasElementTags.has(tag)) {
continue;
}
const t = {
tag,
props: await shared.normaliseProps(
c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
),
innerHTML: c.innerHTML
};
const dedupeKey = shared.tagDedupeKey(t);
let d = dedupeKey;
let i = 1;
while (d && takenDedupeKeys.has(d))
d = `${dedupeKey}:${i++}`;
if (d) {
t._d = d;
takenDedupeKeys.add(d);
}
state.elMap[c.getAttribute("data-hid") || shared.hashTag(t)] = c;
}
}
}
state.pendingSideEffects = { ...state.sideEffects };
state.sideEffects = {};
function track(id, scope, fn) {
const k = `${id}:${scope}`;
state.sideEffects[k] = fn;
delete state.pendingSideEffects[k];
}
function trackCtx({ id, $el, tag }) {
const isAttrTag = tag.tag.endsWith("Attrs");
state.elMap[id] = $el;
if (!isAttrTag) {
if (tag.textContent && tag.textContent !== $el.textContent) {
$el.textContent = tag.textContent;
}
if (tag.innerHTML && tag.innerHTML !== $el.innerHTML) {
$el.innerHTML = tag.innerHTML;
}
track(id, "el", () => {
state.elMap[id]?.remove();
delete state.elMap[id];
});
}
if (tag._eventHandlers) {
for (const k in tag._eventHandlers) {
if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
continue;
}
if ($el.getAttribute(`data-${k}`) !== "") {
(tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(
// onload -> load
k.substring(2),
tag._eventHandlers[k].bind($el)
);
$el.setAttribute(`data-${k}`, "");
}
}
}
for (const k in tag.props) {
if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
continue;
}
const value = tag.props[k];
const ck = `attr:${k}`;
if (k === "class") {
if (!value) {
continue;
}
for (const c of value.split(" ")) {
isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
!$el.classList.contains(c) && $el.classList.add(c);
}
} else if (k === "style") {
if (!value) {
continue;
}
for (const c of value.split(";")) {
const propIndex = c.indexOf(":");
const k2 = c.substring(0, propIndex).trim();
const v = c.substring(propIndex + 1).trim();
track(id, `${ck}:${k2}`, () => {
$el.style.removeProperty(k2);
});
$el.style.setProperty(k2, v);
}
} else {
$el.getAttribute(k) !== value && $el.setAttribute(k, value === true ? "" : String(value));
isAttrTag && track(id, ck, () => $el.removeAttribute(k));
}
}
}
const pending = [];
const frag = {
bodyClose: void 0,
bodyOpen: void 0,
head: void 0
};
for (const ctx of tags) {
const { tag, shouldRender, id } = ctx;
if (!shouldRender)
continue;
if (tag.tag === "title") {
dom.title = tag.textContent;
continue;
}
ctx.$el = ctx.$el || state.elMap[id];
if (ctx.$el) {
trackCtx(ctx);
} else if (shared.HasElementTags.has(tag.tag)) {
pending.push(ctx);
}
}
for (const ctx of pending) {
const pos = ctx.tag.tagPosition || "head";
ctx.$el = dom.createElement(ctx.tag.tag);
trackCtx(ctx);
frag[pos] = frag[pos] || dom.createDocumentFragment();
frag[pos].appendChild(ctx.$el);
}
for (const ctx of tags)
await head.hooks.callHook("dom:renderTag", ctx, dom, track);
frag.head && dom.head.appendChild(frag.head);
frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
frag.bodyClose && dom.body.appendChild(frag.bodyClose);
for (const k in state.pendingSideEffects) {
state.pendingSideEffects[k]();
}
head._dom = state;
await head.hooks.callHook("dom:rendered", { renders: tags });
resolve();
}).finally(() => {
head._domUpdatePromise = void 0;
head.dirty = false;
});
return head._domUpdatePromise;
}
function debouncedRenderDOMHead(head, options = {}) {
const fn = options.delayFn || ((fn2) => setTimeout(fn2, 10));
return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise((resolve) => fn(() => {
return renderDOMHead(head, options).then(() => {
delete head._domDebouncedUpdatePromise;
resolve();
});
}));
}
// @__NO_SIDE_EFFECTS__
function DomPlugin(options) {
return shared.defineHeadPlugin((head) => {
const initialPayload = head.resolvedOptions.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML || false;
if (initialPayload) {
head.push(JSON.parse(initialPayload));
}
return {
mode: "client",
hooks: {
"entries:updated": (head2) => {
debouncedRenderDOMHead(head2, options);
}
}
};
});
}
exports.DomPlugin = DomPlugin;
exports.debouncedRenderDOMHead = debouncedRenderDOMHead;
exports.renderDOMHead = renderDOMHead;
+31
View File
@@ -0,0 +1,31 @@
import * as _unhead_schema from '@unhead/schema';
import { Unhead } from '@unhead/schema';
interface RenderDomHeadOptions {
/**
* Document to use for rendering. Allows stubbing for testing.
*/
document?: Document;
}
/**
* Render the head tags to the DOM.
*/
declare function renderDOMHead<T extends Unhead<any>>(head: T, options?: RenderDomHeadOptions): Promise<void>;
interface DebouncedRenderDomHeadOptions extends RenderDomHeadOptions {
/**
* Specify a custom delay function for delaying the render.
*/
delayFn?: (fn: () => void) => void;
}
/**
* Queue a debounced update of the DOM head.
*/
declare function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options?: DebouncedRenderDomHeadOptions): Promise<void>;
interface DomPluginOptions extends RenderDomHeadOptions {
delayFn?: (fn: () => void) => void;
}
declare function DomPlugin(options?: DomPluginOptions): _unhead_schema.HeadPluginInput;
export { type DebouncedRenderDomHeadOptions, DomPlugin, type DomPluginOptions, type RenderDomHeadOptions, debouncedRenderDOMHead, renderDOMHead };
+31
View File
@@ -0,0 +1,31 @@
import * as _unhead_schema from '@unhead/schema';
import { Unhead } from '@unhead/schema';
interface RenderDomHeadOptions {
/**
* Document to use for rendering. Allows stubbing for testing.
*/
document?: Document;
}
/**
* Render the head tags to the DOM.
*/
declare function renderDOMHead<T extends Unhead<any>>(head: T, options?: RenderDomHeadOptions): Promise<void>;
interface DebouncedRenderDomHeadOptions extends RenderDomHeadOptions {
/**
* Specify a custom delay function for delaying the render.
*/
delayFn?: (fn: () => void) => void;
}
/**
* Queue a debounced update of the DOM head.
*/
declare function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options?: DebouncedRenderDomHeadOptions): Promise<void>;
interface DomPluginOptions extends RenderDomHeadOptions {
delayFn?: (fn: () => void) => void;
}
declare function DomPlugin(options?: DomPluginOptions): _unhead_schema.HeadPluginInput;
export { type DebouncedRenderDomHeadOptions, DomPlugin, type DomPluginOptions, type RenderDomHeadOptions, debouncedRenderDOMHead, renderDOMHead };
+31
View File
@@ -0,0 +1,31 @@
import * as _unhead_schema from '@unhead/schema';
import { Unhead } from '@unhead/schema';
interface RenderDomHeadOptions {
/**
* Document to use for rendering. Allows stubbing for testing.
*/
document?: Document;
}
/**
* Render the head tags to the DOM.
*/
declare function renderDOMHead<T extends Unhead<any>>(head: T, options?: RenderDomHeadOptions): Promise<void>;
interface DebouncedRenderDomHeadOptions extends RenderDomHeadOptions {
/**
* Specify a custom delay function for delaying the render.
*/
delayFn?: (fn: () => void) => void;
}
/**
* Queue a debounced update of the DOM head.
*/
declare function debouncedRenderDOMHead<T extends Unhead<any>>(head: T, options?: DebouncedRenderDomHeadOptions): Promise<void>;
interface DomPluginOptions extends RenderDomHeadOptions {
delayFn?: (fn: () => void) => void;
}
declare function DomPlugin(options?: DomPluginOptions): _unhead_schema.HeadPluginInput;
export { type DebouncedRenderDomHeadOptions, DomPlugin, type DomPluginOptions, type RenderDomHeadOptions, debouncedRenderDOMHead, renderDOMHead };
+197
View File
@@ -0,0 +1,197 @@
import { HasElementTags, hashTag, normaliseProps, tagDedupeKey, defineHeadPlugin } from '@unhead/shared';
async function renderDOMHead(head, options = {}) {
const dom = options.document || head.resolvedOptions.document;
if (!dom || !head.dirty)
return;
const beforeRenderCtx = { shouldRender: true, tags: [] };
await head.hooks.callHook("dom:beforeRender", beforeRenderCtx);
if (!beforeRenderCtx.shouldRender)
return;
if (head._domUpdatePromise) {
return head._domUpdatePromise;
}
head._domUpdatePromise = new Promise(async (resolve) => {
const tags = (await head.resolveTags()).map((tag) => ({
tag,
id: HasElementTags.has(tag.tag) ? hashTag(tag) : tag.tag,
shouldRender: true
}));
let state = head._dom;
if (!state) {
state = {
elMap: { htmlAttrs: dom.documentElement, bodyAttrs: dom.body }
};
const takenDedupeKeys = /* @__PURE__ */ new Set();
for (const key of ["body", "head"]) {
const children = dom[key]?.children;
for (const c of children) {
const tag = c.tagName.toLowerCase();
if (!HasElementTags.has(tag)) {
continue;
}
const t = {
tag,
props: await normaliseProps(
c.getAttributeNames().reduce((props, name) => ({ ...props, [name]: c.getAttribute(name) }), {})
),
innerHTML: c.innerHTML
};
const dedupeKey = tagDedupeKey(t);
let d = dedupeKey;
let i = 1;
while (d && takenDedupeKeys.has(d))
d = `${dedupeKey}:${i++}`;
if (d) {
t._d = d;
takenDedupeKeys.add(d);
}
state.elMap[c.getAttribute("data-hid") || hashTag(t)] = c;
}
}
}
state.pendingSideEffects = { ...state.sideEffects };
state.sideEffects = {};
function track(id, scope, fn) {
const k = `${id}:${scope}`;
state.sideEffects[k] = fn;
delete state.pendingSideEffects[k];
}
function trackCtx({ id, $el, tag }) {
const isAttrTag = tag.tag.endsWith("Attrs");
state.elMap[id] = $el;
if (!isAttrTag) {
if (tag.textContent && tag.textContent !== $el.textContent) {
$el.textContent = tag.textContent;
}
if (tag.innerHTML && tag.innerHTML !== $el.innerHTML) {
$el.innerHTML = tag.innerHTML;
}
track(id, "el", () => {
state.elMap[id]?.remove();
delete state.elMap[id];
});
}
if (tag._eventHandlers) {
for (const k in tag._eventHandlers) {
if (!Object.prototype.hasOwnProperty.call(tag._eventHandlers, k)) {
continue;
}
if ($el.getAttribute(`data-${k}`) !== "") {
(tag.tag === "bodyAttrs" ? dom.defaultView : $el).addEventListener(
// onload -> load
k.substring(2),
tag._eventHandlers[k].bind($el)
);
$el.setAttribute(`data-${k}`, "");
}
}
}
for (const k in tag.props) {
if (!Object.prototype.hasOwnProperty.call(tag.props, k)) {
continue;
}
const value = tag.props[k];
const ck = `attr:${k}`;
if (k === "class") {
if (!value) {
continue;
}
for (const c of value.split(" ")) {
isAttrTag && track(id, `${ck}:${c}`, () => $el.classList.remove(c));
!$el.classList.contains(c) && $el.classList.add(c);
}
} else if (k === "style") {
if (!value) {
continue;
}
for (const c of value.split(";")) {
const propIndex = c.indexOf(":");
const k2 = c.substring(0, propIndex).trim();
const v = c.substring(propIndex + 1).trim();
track(id, `${ck}:${k2}`, () => {
$el.style.removeProperty(k2);
});
$el.style.setProperty(k2, v);
}
} else {
$el.getAttribute(k) !== value && $el.setAttribute(k, value === true ? "" : String(value));
isAttrTag && track(id, ck, () => $el.removeAttribute(k));
}
}
}
const pending = [];
const frag = {
bodyClose: void 0,
bodyOpen: void 0,
head: void 0
};
for (const ctx of tags) {
const { tag, shouldRender, id } = ctx;
if (!shouldRender)
continue;
if (tag.tag === "title") {
dom.title = tag.textContent;
continue;
}
ctx.$el = ctx.$el || state.elMap[id];
if (ctx.$el) {
trackCtx(ctx);
} else if (HasElementTags.has(tag.tag)) {
pending.push(ctx);
}
}
for (const ctx of pending) {
const pos = ctx.tag.tagPosition || "head";
ctx.$el = dom.createElement(ctx.tag.tag);
trackCtx(ctx);
frag[pos] = frag[pos] || dom.createDocumentFragment();
frag[pos].appendChild(ctx.$el);
}
for (const ctx of tags)
await head.hooks.callHook("dom:renderTag", ctx, dom, track);
frag.head && dom.head.appendChild(frag.head);
frag.bodyOpen && dom.body.insertBefore(frag.bodyOpen, dom.body.firstChild);
frag.bodyClose && dom.body.appendChild(frag.bodyClose);
for (const k in state.pendingSideEffects) {
state.pendingSideEffects[k]();
}
head._dom = state;
await head.hooks.callHook("dom:rendered", { renders: tags });
resolve();
}).finally(() => {
head._domUpdatePromise = void 0;
head.dirty = false;
});
return head._domUpdatePromise;
}
function debouncedRenderDOMHead(head, options = {}) {
const fn = options.delayFn || ((fn2) => setTimeout(fn2, 10));
return head._domDebouncedUpdatePromise = head._domDebouncedUpdatePromise || new Promise((resolve) => fn(() => {
return renderDOMHead(head, options).then(() => {
delete head._domDebouncedUpdatePromise;
resolve();
});
}));
}
// @__NO_SIDE_EFFECTS__
function DomPlugin(options) {
return defineHeadPlugin((head) => {
const initialPayload = head.resolvedOptions.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML || false;
if (initialPayload) {
head.push(JSON.parse(initialPayload));
}
return {
mode: "client",
hooks: {
"entries:updated": (head2) => {
debouncedRenderDOMHead(head2, options);
}
}
};
});
}
export { DomPlugin, debouncedRenderDOMHead, renderDOMHead };
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@unhead/dom",
"type": "module",
"version": "1.11.20",
"author": "Harlan Wilton <harlan@harlanzw.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/harlan-zw",
"homepage": "https://unhead.unjs.io",
"repository": {
"type": "git",
"url": "git+https://github.com/unjs/unhead.git",
"directory": "packages/dom"
},
"bugs": {
"url": "https://github.com/unjs/unhead/issues"
},
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"dependencies": {
"@unhead/schema": "1.11.20",
"@unhead/shared": "1.11.20"
},
"scripts": {
"build": "unbuild .",
"stub": "unbuild . --stub",
"export:sizes": "npx export-size . -r"
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Harlan Wilton <harlan@harlanzw.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+16
View File
@@ -0,0 +1,16 @@
# `@unhead/schema`
Typescript definitions for document `<head>`.
## Installation
```bash
npm install --save-dev @unhead/schema
# Using yarn
yarn add --dev @unhead/schema
```
## Types
See [head.ts](./src/head.ts) for the full list of types.
+2
View File
@@ -0,0 +1,2 @@
'use strict';
+598
View File
@@ -0,0 +1,598 @@
import { NestedHooks, Hookable } from 'hookable';
import { MergeHead, BaseBodyAttributes, HtmlAttributes as HtmlAttributes$1, Meta as Meta$1, Stringable, Merge, Base as Base$1, DefinedValueOrEmptyObject, LinkBase, HttpEventAttributes, DataKeys, Style as Style$1, ScriptBase, Noscript as Noscript$1, BodyEvents, MetaFlatInput } from 'zhead';
export { BodyEvents, DataKeys, DefinedValueOrEmptyObject, MergeHead, MetaFlatInput, ScriptBase, SpeculationRules } from 'zhead';
type Never<T> = {
[P in keyof T]?: never;
};
type FalsyEntries<T> = {
[key in keyof T]?: T[key] | null | false | undefined;
};
type UserTagConfigWithoutInnerContent = TagPriority & TagPosition & ResolvesDuplicates & Never<InnerContent> & {
processTemplateParams?: false;
};
type UserAttributesConfig = ResolvesDuplicates & TagPriority & Never<InnerContent & TagPosition>;
interface SchemaAugmentations extends MergeHead {
title: TagPriority;
titleTemplate: TagPriority;
base: UserAttributesConfig;
htmlAttrs: UserAttributesConfig;
bodyAttrs: UserAttributesConfig;
link: UserTagConfigWithoutInnerContent;
meta: UserTagConfigWithoutInnerContent;
style: TagUserProperties;
script: TagUserProperties;
noscript: TagUserProperties;
}
type MaybeArray<T> = T | T[];
type BaseBodyAttr = BaseBodyAttributes;
type BaseHtmlAttr = HtmlAttributes$1;
interface BodyAttr extends Omit<BaseBodyAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface HtmlAttr extends Omit<HtmlAttributes$1, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface BaseMeta extends Omit<Meta$1, 'content'> {
/**
* This attribute contains the value for the http-equiv, name or property attribute, depending on which is used.
*
* You can provide an array of values to create multiple tags sharing the same name, property or http-equiv.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-content
*/
content?: MaybeArray<Stringable> | null;
}
type EntryAugmentation = undefined | Record<string, any>;
type MaybeFunctionEntries<T> = {
[key in keyof T]?: T[key] | ((e: Event) => void);
};
type TitleTemplateResolver = string | ((title?: string) => string | null);
type Title = string | FalsyEntries<({
textContent: string;
} & SchemaAugmentations['title']) | null>;
type TitleTemplate = TitleTemplateResolver | null | ({
textContent: TitleTemplateResolver;
} & SchemaAugmentations['titleTemplate']);
type Base<E extends EntryAugmentation = Record<string, any>> = Partial<Merge<SchemaAugmentations['base'], FalsyEntries<Base$1>>> & DefinedValueOrEmptyObject<E>;
type Link<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<LinkBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['link'] & DefinedValueOrEmptyObject<E>;
type Meta<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BaseMeta> & DataKeys & SchemaAugmentations['meta'] & DefinedValueOrEmptyObject<E>;
type Style<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Style$1> & DataKeys & SchemaAugmentations['style'] & DefinedValueOrEmptyObject<E>;
type Script<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<ScriptBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['script'] & DefinedValueOrEmptyObject<E>;
type Noscript<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Noscript$1> & DataKeys & SchemaAugmentations['noscript'] & DefinedValueOrEmptyObject<E>;
type HtmlAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<HtmlAttr> & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>;
type BodyAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BodyAttr> & MaybeFunctionEntries<BodyEvents> & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>;
interface HeadUtils {
/**
* Generate the title from a template.
*
* Should include a `%s` placeholder for the title, for example `%s - My Site`.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: TemplateParams;
}
interface Head<E extends MergeHead = SchemaAugmentations> extends HeadUtils {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: Link<E['link']>[];
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: Meta<E['meta']>[];
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: (Style<E['style']> | string)[];
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: (Script<E['script']> | string)[];
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: (Noscript<E['noscript']> | string)[];
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseSeoMetaInput = MetaFlatInput & {
title?: Title;
titleTemplate?: TitleTemplate;
};
interface ResolvesDuplicates {
/**
* By default, tags which share the same unique key `name`, `property` are de-duped. To allow duplicates
* to be made you can provide a unique key for each entry.
*/
key?: string;
/**
* The strategy to use when a duplicate tag is encountered.
*
* - `replace` - Replace the existing tag with the new tag
* - `merge` - Merge the existing tag with the new tag
*
* @default 'replace' (some tags will default to 'merge', such as htmlAttr)
*/
tagDuplicateStrategy?: 'replace' | 'merge';
/**
* @deprecated Use `key` instead
*/
hid?: string;
/**
* @deprecated Use `key` instead
*/
vmid?: string;
}
type ValidTagPositions = 'head' | 'bodyClose' | 'bodyOpen';
interface TagPosition {
/**
* Specify where to render the tag.
*
* @default 'head'
*/
tagPosition?: ValidTagPositions;
}
type InnerContentVal = string | Record<string, any>;
interface InnerContent {
/**
* Text content of the tag.
*
* Warning: This is not safe for XSS. Do not use this with user input, use `textContent` instead.
*/
innerHTML?: InnerContentVal;
/**
* Sets the textContent of an element. Safer for XSS.
*/
textContent?: InnerContentVal;
/**
* Sets the textContent of an element.
*
* @deprecated Use `textContent` or `innerHTML`.
*/
children?: InnerContentVal;
}
interface TagPriority {
/**
* The priority for rendering the tag, without this all tags are rendered as they are registered
* (besides some special tags).
*
* The following special tags have default priorities:
* -2 `<meta charset ...>`
* -1 `<base>`
* 0 `<meta http-equiv="content-security-policy" ...>`
*
* All other tags have a default priority of 10: `<meta>`, `<script>`, `<link>`, `<style>`, etc
*/
tagPriority?: number | 'critical' | 'high' | 'low' | `before:${string}` | `after:${string}`;
}
type TagUserProperties = FalsyEntries<TagPriority & TagPosition & InnerContent & ResolvesDuplicates & ProcessesTemplateParams>;
type TagKey = keyof Head;
type TemplateParams = {
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | Record<string, string>>;
interface ProcessesTemplateParams {
processTemplateParams?: boolean;
}
interface HasTemplateParams {
templateParams?: TemplateParams;
}
interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, HasTemplateParams {
tag: TagKey;
props: Record<string, string>;
processTemplateParams?: boolean;
innerHTML?: string;
textContent?: string;
/**
* Entry ID
* @internal
*/
_e?: number;
/**
* Position
* @internal
*/
_p?: number;
/**
* Dedupe key
* @internal
*/
_d?: string;
/**
* Hash code used to represent the tag.
* @internal
*/
_h?: string;
/**
* @internal
*/
_m?: RuntimeMode;
/**
* @internal
*/
_eventHandlers?: Record<string, ((e: Event) => {})>;
}
type HeadTagKeys = (keyof HeadTag)[];
type HookResult = Promise<void> | void;
interface SSRHeadPayload {
headTags: string;
bodyTags: string;
bodyTagsOpen: string;
htmlAttrs: string;
bodyAttrs: string;
}
interface RenderSSRHeadOptions {
omitLineBreaks?: boolean;
}
interface EntryResolveCtx<T> {
tags: HeadTag[];
entries: HeadEntry<T>[];
}
interface DomRenderTagContext {
id: string;
$el: Element;
shouldRender: boolean;
tag: HeadTag;
entry?: HeadEntry<any>;
markSideEffect: (key: string, fn: () => void) => void;
}
interface DomBeforeRenderCtx extends ShouldRenderContext {
/**
* @deprecated will always be empty, prefer other hooks
*/
tags: DomRenderTagContext[];
}
interface ShouldRenderContext {
shouldRender: boolean;
}
interface SSRRenderContext {
tags: HeadTag[];
html: SSRHeadPayload;
}
interface HeadHooks {
'init': (ctx: Unhead<any>) => HookResult;
'entries:updated': (ctx: Unhead<any>) => HookResult;
'entries:resolve': (ctx: EntryResolveCtx<any>) => HookResult;
'tag:normalise': (ctx: {
tag: HeadTag;
entry: HeadEntry<any>;
resolvedOptions: CreateHeadOptions;
}) => HookResult;
'tags:beforeResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:resolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:afterResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'dom:beforeRender': (ctx: DomBeforeRenderCtx) => HookResult;
'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult;
'dom:rendered': (ctx: {
renders: DomRenderTagContext[];
}) => HookResult;
'ssr:beforeRender': (ctx: ShouldRenderContext) => HookResult;
'ssr:render': (ctx: {
tags: HeadTag[];
}) => HookResult;
'ssr:rendered': (ctx: SSRRenderContext) => HookResult;
'script:updated': (ctx: {
script: ScriptInstance<any>;
}) => HookResult;
'script:instance-fn': (ctx: {
script: ScriptInstance<any>;
fn: string | symbol;
exists: boolean;
}) => HookResult;
}
/**
* Side effects are mapped with a key and their cleanup function.
*
* For example, `meta:data-h-4h46h465`: () => { document.querySelector('meta[data-h-4h46h465]').remove() }
*/
type SideEffectsRecord = Record<string, () => void>;
type RuntimeMode = 'server' | 'client';
interface HeadEntry<Input> {
/**
* User provided input for the entry.
*/
input: Input;
/**
* Optional resolved input which will be used if set.
*/
resolvedInput?: Input;
/**
* The mode that the entry should be used in.
*
* @internal
*/
mode?: RuntimeMode;
/**
* Transformer function for the entry.
*
* @internal
*/
transform?: (input: Input) => Promise<Input> | Input;
/**
* Head entry index
*
* @internal
*/
_i: number;
/**
* Default tag position.
*
* @internal
*/
tagPosition?: TagPosition['tagPosition'];
/**
* Default tag priority.
*
* @internal
*/
tagPriority?: TagPriority['tagPriority'];
}
type HeadPluginOptions = Omit<CreateHeadOptions, 'plugins'> & {
mode?: RuntimeMode;
};
type HeadPluginInput = (HeadPluginOptions & {
key?: string;
}) | ((head: Unhead) => HeadPluginOptions & {
key?: string;
});
type HeadPlugin = HeadPluginOptions & {
key?: string;
};
/**
* An active head entry provides an API to manipulate it.
*/
interface ActiveHeadEntry<Input> {
/**
* Updates the entry with new input.
*
* Will first clear any side effects for previous input.
*/
patch: (input: Input) => void;
/**
* Dispose the entry, removing it from the active head.
*
* Will queue side effects for removal.
*/
dispose: () => void;
}
interface CreateHeadOptions {
domDelayFn?: (fn: () => void) => void;
document?: Document;
plugins?: HeadPluginInput[];
hooks?: NestedHooks<HeadHooks>;
}
interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTemplateParams, ResolvesDuplicates {
mode?: RuntimeMode;
transform?: (input: unknown) => unknown;
head?: Unhead;
}
interface Unhead<Input extends {} = Head> {
/**
* Registered plugins.
*/
plugins: HeadPlugin[];
/**
* The active head entries.
*/
headEntries: () => HeadEntry<Input>[];
/**
* Create a new head entry.
*/
push: (entry: Input, options?: HeadEntryOptions) => ActiveHeadEntry<Input>;
/**
* Resolve tags from head entries.
*/
resolveTags: () => Promise<HeadTag[]>;
/**
* Exposed hooks for easier extension.
*/
hooks: Hookable<HeadHooks>;
/**
* Resolved options
*/
resolvedOptions: CreateHeadOptions;
/**
* Use a head plugin, loads the plugins hooks.
*/
use: (plugin: HeadPluginInput) => void;
/**
* Is it a server-side render context.
*/
ssr: boolean;
/**
* @internal
*/
_dom?: DomState;
/**
* @internal
*/
_domUpdatePromise?: Promise<void>;
/**
* @internal
*/
_domDebouncedUpdatePromise?: Promise<void>;
/**
* @internal
*/
dirty: boolean;
/**
* @internal
*/
_scripts?: Record<string, any>;
/**
* @internal
*/
_templateParams?: TemplateParams;
/**
* @internal
*/
_separator?: string;
}
interface DomState {
pendingSideEffects: SideEffectsRecord;
sideEffects: SideEffectsRecord;
elMap: Record<string, Element>;
}
type SafeBodyAttr = Pick<BodyAttr, 'id' | 'class'> & DataKeys;
type SafeHtmlAttr = Pick<HtmlAttr, 'id' | 'class' | 'lang' | 'dir'> & DataKeys;
type SafeMeta = Pick<Meta, 'id' | 'name' | 'property' | 'content' | 'charset'> & DataKeys;
type SafeLink = Pick<Link, 'color' | 'crossorigin' | 'fetchpriority' | 'href' | 'hreflang' | 'imagesizes' | 'imagesrcset' | 'integrity' | 'media' | 'referrerpolicy' | 'sizes' | 'id'> & {
rel?: Omit<Link['rel'], 'stylesheet' | 'canonical' | 'modulepreload' | 'prerender' | 'preload' | 'prefetch'>;
type?: 'audio/aac' | 'application/x-abiword' | 'application/x-freearc' | 'image/avif' | 'video/x-msvideo' | 'application/vnd.amazon.ebook' | 'application/octet-stream' | 'image/bmp' | 'application/x-bzip' | 'application/x-bzip2' | 'application/x-cdf' | 'application/x-csh' | 'text/csv' | 'application/msword' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.ms-fontobject' | 'application/epub+zip' | 'application/gzip' | 'image/gif' | 'image/vnd.microsoft.icon' | 'text/calendar' | 'application/java-archive' | 'image/jpeg' | 'application/json' | 'application/ld+json' | 'audio/midi' | 'audio/x-midi' | 'audio/mpeg' | 'video/mp4' | 'video/mpeg' | 'application/vnd.apple.installer+xml' | 'application/vnd.oasis.opendocument.presentation' | 'application/vnd.oasis.opendocument.spreadsheet' | 'application/vnd.oasis.opendocument.text' | 'audio/ogg' | 'video/ogg' | 'application/ogg' | 'audio/opus' | 'font/otf' | 'image/png' | 'application/pdf' | 'application/x-httpd-php' | 'application/vnd.ms-powerpoint' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'application/vnd.rar' | 'application/rtf' | 'application/x-sh' | 'image/svg+xml' | 'application/x-tar' | 'image/tiff' | 'video/mp2t' | 'font/ttf' | 'text/plain' | 'application/vnd.visio' | 'audio/wav' | 'audio/webm' | 'video/webm' | 'image/webp' | 'font/woff' | 'font/woff2' | 'application/xhtml+xml' | 'application/vnd.ms-excel' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'text/xml' | 'application/atom+xml' | 'application/xml' | 'application/vnd.mozilla.xul+xml' | 'application/zip' | 'video/3gpp' | 'audio/3gpp' | 'video/3gpp2' | 'audio/3gpp2' | (string & Record<never, never>);
} & DataKeys;
type SafeScript = Pick<Script, 'id' | 'textContent'> & {
type: 'application/json' | 'application/ld+json';
} & DataKeys;
type SafeNoscript = Pick<Noscript, 'id' | 'textContent'> & DataKeys;
interface HeadSafe extends Pick<Head, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: SafeMeta[];
link?: SafeLink[];
noscript?: SafeNoscript[];
script?: SafeScript[];
htmlAttrs?: SafeHtmlAttr;
bodyAttrs?: SafeBodyAttr;
}
type UseScriptStatus = 'awaitingLoad' | 'loading' | 'loaded' | 'error' | 'removed';
/**
* Either a string source for the script or full script properties.
*/
type UseScriptInput = string | (Omit<Script, 'src'> & {
src: string;
});
type UseScriptResolvedInput = Omit<Script, 'src'> & {
src: string;
};
type BaseScriptApi = Record<symbol | string, any>;
type AsAsyncFunctionValues<T extends BaseScriptApi> = {
[key in keyof T]: T[key] extends any[] ? T[key] : T[key] extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : T[key] extends Record<any, any> ? AsAsyncFunctionValues<T[key]> : never;
};
interface ScriptInstance<T extends BaseScriptApi> {
proxy: AsAsyncFunctionValues<T>;
instance?: T;
id: string;
status: UseScriptStatus;
entry?: ActiveHeadEntry<any>;
load: () => Promise<T>;
remove: () => boolean;
setupTriggerHandler: (trigger: UseScriptOptions['trigger']) => void;
onLoaded: (fn: (instance: T) => void | Promise<void>) => void;
onError: (fn: (err?: Error) => void | Promise<void>) => void;
/**
* @internal
*/
_triggerAbortController?: AbortController | null;
/**
* @internal
*/
_triggerAbortPromise?: Promise<void>;
/**
* @internal
*/
_triggerPromises?: Promise<void>[];
/**
* @internal
*/
_cbs: {
loaded: null | ((instance: T) => void | Promise<void>)[];
error: null | ((err?: Error) => void | Promise<void>)[];
};
}
type UseFunctionType<T, U> = T extends {
use: infer V;
} ? V extends (...args: any) => any ? ReturnType<V> : U : U;
interface UseScriptOptions<T extends BaseScriptApi = {}, U = {}> extends HeadEntryOptions {
/**
* Resolve the script instance from the window.
*/
use?: () => T | undefined | null;
/**
* Stub the script instance. Useful for SSR or testing.
*/
stub?: ((ctx: {
script: ScriptInstance<T>;
fn: string | symbol;
}) => any);
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
*/
trigger?: 'client' | 'server' | 'manual' | Promise<boolean | void> | ((fn: any) => any) | null;
/**
* Context to run events with. This is useful in Vue to attach the current instance context before
* calling the event, allowing the event to be reactive.
*/
eventContext?: any;
/**
* Called before the script is initialized. Will not be triggered when the script is already loaded. This means
* this is guaranteed to be called only once, unless the script is removed and re-added.
*/
beforeInit?: () => void;
}
export type { ActiveHeadEntry, AsAsyncFunctionValues, Base, BaseBodyAttr, BaseHtmlAttr, BaseMeta, BodyAttr, BodyAttributes, CreateHeadOptions, DomBeforeRenderCtx, DomRenderTagContext, DomState, EntryAugmentation, EntryResolveCtx, HasTemplateParams, Head, HeadEntry, HeadEntryOptions, HeadHooks, HeadPlugin, HeadPluginInput, HeadPluginOptions, HeadSafe, HeadTag, HeadTagKeys, HeadUtils, HookResult, HtmlAttr, HtmlAttributes, InnerContent, InnerContentVal, Link, MaybeArray, MaybeFunctionEntries, Meta, Noscript, ProcessesTemplateParams, RenderSSRHeadOptions, ResolvesDuplicates, RuntimeMode, SSRHeadPayload, SSRRenderContext, SafeBodyAttr, SafeHtmlAttr, SafeLink, SafeMeta, SafeNoscript, SafeScript, SchemaAugmentations, Script, ScriptInstance, ShouldRenderContext, SideEffectsRecord, Style, TagKey, TagPosition, TagPriority, TagUserProperties, TemplateParams, Title, TitleTemplate, Unhead, UseFunctionType, UseScriptInput, UseScriptOptions, UseScriptResolvedInput, UseScriptStatus, UseSeoMetaInput, UserAttributesConfig, UserTagConfigWithoutInnerContent, ValidTagPositions };
+598
View File
@@ -0,0 +1,598 @@
import { NestedHooks, Hookable } from 'hookable';
import { MergeHead, BaseBodyAttributes, HtmlAttributes as HtmlAttributes$1, Meta as Meta$1, Stringable, Merge, Base as Base$1, DefinedValueOrEmptyObject, LinkBase, HttpEventAttributes, DataKeys, Style as Style$1, ScriptBase, Noscript as Noscript$1, BodyEvents, MetaFlatInput } from 'zhead';
export { BodyEvents, DataKeys, DefinedValueOrEmptyObject, MergeHead, MetaFlatInput, ScriptBase, SpeculationRules } from 'zhead';
type Never<T> = {
[P in keyof T]?: never;
};
type FalsyEntries<T> = {
[key in keyof T]?: T[key] | null | false | undefined;
};
type UserTagConfigWithoutInnerContent = TagPriority & TagPosition & ResolvesDuplicates & Never<InnerContent> & {
processTemplateParams?: false;
};
type UserAttributesConfig = ResolvesDuplicates & TagPriority & Never<InnerContent & TagPosition>;
interface SchemaAugmentations extends MergeHead {
title: TagPriority;
titleTemplate: TagPriority;
base: UserAttributesConfig;
htmlAttrs: UserAttributesConfig;
bodyAttrs: UserAttributesConfig;
link: UserTagConfigWithoutInnerContent;
meta: UserTagConfigWithoutInnerContent;
style: TagUserProperties;
script: TagUserProperties;
noscript: TagUserProperties;
}
type MaybeArray<T> = T | T[];
type BaseBodyAttr = BaseBodyAttributes;
type BaseHtmlAttr = HtmlAttributes$1;
interface BodyAttr extends Omit<BaseBodyAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface HtmlAttr extends Omit<HtmlAttributes$1, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface BaseMeta extends Omit<Meta$1, 'content'> {
/**
* This attribute contains the value for the http-equiv, name or property attribute, depending on which is used.
*
* You can provide an array of values to create multiple tags sharing the same name, property or http-equiv.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-content
*/
content?: MaybeArray<Stringable> | null;
}
type EntryAugmentation = undefined | Record<string, any>;
type MaybeFunctionEntries<T> = {
[key in keyof T]?: T[key] | ((e: Event) => void);
};
type TitleTemplateResolver = string | ((title?: string) => string | null);
type Title = string | FalsyEntries<({
textContent: string;
} & SchemaAugmentations['title']) | null>;
type TitleTemplate = TitleTemplateResolver | null | ({
textContent: TitleTemplateResolver;
} & SchemaAugmentations['titleTemplate']);
type Base<E extends EntryAugmentation = Record<string, any>> = Partial<Merge<SchemaAugmentations['base'], FalsyEntries<Base$1>>> & DefinedValueOrEmptyObject<E>;
type Link<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<LinkBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['link'] & DefinedValueOrEmptyObject<E>;
type Meta<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BaseMeta> & DataKeys & SchemaAugmentations['meta'] & DefinedValueOrEmptyObject<E>;
type Style<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Style$1> & DataKeys & SchemaAugmentations['style'] & DefinedValueOrEmptyObject<E>;
type Script<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<ScriptBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['script'] & DefinedValueOrEmptyObject<E>;
type Noscript<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Noscript$1> & DataKeys & SchemaAugmentations['noscript'] & DefinedValueOrEmptyObject<E>;
type HtmlAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<HtmlAttr> & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>;
type BodyAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BodyAttr> & MaybeFunctionEntries<BodyEvents> & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>;
interface HeadUtils {
/**
* Generate the title from a template.
*
* Should include a `%s` placeholder for the title, for example `%s - My Site`.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: TemplateParams;
}
interface Head<E extends MergeHead = SchemaAugmentations> extends HeadUtils {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: Link<E['link']>[];
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: Meta<E['meta']>[];
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: (Style<E['style']> | string)[];
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: (Script<E['script']> | string)[];
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: (Noscript<E['noscript']> | string)[];
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseSeoMetaInput = MetaFlatInput & {
title?: Title;
titleTemplate?: TitleTemplate;
};
interface ResolvesDuplicates {
/**
* By default, tags which share the same unique key `name`, `property` are de-duped. To allow duplicates
* to be made you can provide a unique key for each entry.
*/
key?: string;
/**
* The strategy to use when a duplicate tag is encountered.
*
* - `replace` - Replace the existing tag with the new tag
* - `merge` - Merge the existing tag with the new tag
*
* @default 'replace' (some tags will default to 'merge', such as htmlAttr)
*/
tagDuplicateStrategy?: 'replace' | 'merge';
/**
* @deprecated Use `key` instead
*/
hid?: string;
/**
* @deprecated Use `key` instead
*/
vmid?: string;
}
type ValidTagPositions = 'head' | 'bodyClose' | 'bodyOpen';
interface TagPosition {
/**
* Specify where to render the tag.
*
* @default 'head'
*/
tagPosition?: ValidTagPositions;
}
type InnerContentVal = string | Record<string, any>;
interface InnerContent {
/**
* Text content of the tag.
*
* Warning: This is not safe for XSS. Do not use this with user input, use `textContent` instead.
*/
innerHTML?: InnerContentVal;
/**
* Sets the textContent of an element. Safer for XSS.
*/
textContent?: InnerContentVal;
/**
* Sets the textContent of an element.
*
* @deprecated Use `textContent` or `innerHTML`.
*/
children?: InnerContentVal;
}
interface TagPriority {
/**
* The priority for rendering the tag, without this all tags are rendered as they are registered
* (besides some special tags).
*
* The following special tags have default priorities:
* -2 `<meta charset ...>`
* -1 `<base>`
* 0 `<meta http-equiv="content-security-policy" ...>`
*
* All other tags have a default priority of 10: `<meta>`, `<script>`, `<link>`, `<style>`, etc
*/
tagPriority?: number | 'critical' | 'high' | 'low' | `before:${string}` | `after:${string}`;
}
type TagUserProperties = FalsyEntries<TagPriority & TagPosition & InnerContent & ResolvesDuplicates & ProcessesTemplateParams>;
type TagKey = keyof Head;
type TemplateParams = {
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | Record<string, string>>;
interface ProcessesTemplateParams {
processTemplateParams?: boolean;
}
interface HasTemplateParams {
templateParams?: TemplateParams;
}
interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, HasTemplateParams {
tag: TagKey;
props: Record<string, string>;
processTemplateParams?: boolean;
innerHTML?: string;
textContent?: string;
/**
* Entry ID
* @internal
*/
_e?: number;
/**
* Position
* @internal
*/
_p?: number;
/**
* Dedupe key
* @internal
*/
_d?: string;
/**
* Hash code used to represent the tag.
* @internal
*/
_h?: string;
/**
* @internal
*/
_m?: RuntimeMode;
/**
* @internal
*/
_eventHandlers?: Record<string, ((e: Event) => {})>;
}
type HeadTagKeys = (keyof HeadTag)[];
type HookResult = Promise<void> | void;
interface SSRHeadPayload {
headTags: string;
bodyTags: string;
bodyTagsOpen: string;
htmlAttrs: string;
bodyAttrs: string;
}
interface RenderSSRHeadOptions {
omitLineBreaks?: boolean;
}
interface EntryResolveCtx<T> {
tags: HeadTag[];
entries: HeadEntry<T>[];
}
interface DomRenderTagContext {
id: string;
$el: Element;
shouldRender: boolean;
tag: HeadTag;
entry?: HeadEntry<any>;
markSideEffect: (key: string, fn: () => void) => void;
}
interface DomBeforeRenderCtx extends ShouldRenderContext {
/**
* @deprecated will always be empty, prefer other hooks
*/
tags: DomRenderTagContext[];
}
interface ShouldRenderContext {
shouldRender: boolean;
}
interface SSRRenderContext {
tags: HeadTag[];
html: SSRHeadPayload;
}
interface HeadHooks {
'init': (ctx: Unhead<any>) => HookResult;
'entries:updated': (ctx: Unhead<any>) => HookResult;
'entries:resolve': (ctx: EntryResolveCtx<any>) => HookResult;
'tag:normalise': (ctx: {
tag: HeadTag;
entry: HeadEntry<any>;
resolvedOptions: CreateHeadOptions;
}) => HookResult;
'tags:beforeResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:resolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:afterResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'dom:beforeRender': (ctx: DomBeforeRenderCtx) => HookResult;
'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult;
'dom:rendered': (ctx: {
renders: DomRenderTagContext[];
}) => HookResult;
'ssr:beforeRender': (ctx: ShouldRenderContext) => HookResult;
'ssr:render': (ctx: {
tags: HeadTag[];
}) => HookResult;
'ssr:rendered': (ctx: SSRRenderContext) => HookResult;
'script:updated': (ctx: {
script: ScriptInstance<any>;
}) => HookResult;
'script:instance-fn': (ctx: {
script: ScriptInstance<any>;
fn: string | symbol;
exists: boolean;
}) => HookResult;
}
/**
* Side effects are mapped with a key and their cleanup function.
*
* For example, `meta:data-h-4h46h465`: () => { document.querySelector('meta[data-h-4h46h465]').remove() }
*/
type SideEffectsRecord = Record<string, () => void>;
type RuntimeMode = 'server' | 'client';
interface HeadEntry<Input> {
/**
* User provided input for the entry.
*/
input: Input;
/**
* Optional resolved input which will be used if set.
*/
resolvedInput?: Input;
/**
* The mode that the entry should be used in.
*
* @internal
*/
mode?: RuntimeMode;
/**
* Transformer function for the entry.
*
* @internal
*/
transform?: (input: Input) => Promise<Input> | Input;
/**
* Head entry index
*
* @internal
*/
_i: number;
/**
* Default tag position.
*
* @internal
*/
tagPosition?: TagPosition['tagPosition'];
/**
* Default tag priority.
*
* @internal
*/
tagPriority?: TagPriority['tagPriority'];
}
type HeadPluginOptions = Omit<CreateHeadOptions, 'plugins'> & {
mode?: RuntimeMode;
};
type HeadPluginInput = (HeadPluginOptions & {
key?: string;
}) | ((head: Unhead) => HeadPluginOptions & {
key?: string;
});
type HeadPlugin = HeadPluginOptions & {
key?: string;
};
/**
* An active head entry provides an API to manipulate it.
*/
interface ActiveHeadEntry<Input> {
/**
* Updates the entry with new input.
*
* Will first clear any side effects for previous input.
*/
patch: (input: Input) => void;
/**
* Dispose the entry, removing it from the active head.
*
* Will queue side effects for removal.
*/
dispose: () => void;
}
interface CreateHeadOptions {
domDelayFn?: (fn: () => void) => void;
document?: Document;
plugins?: HeadPluginInput[];
hooks?: NestedHooks<HeadHooks>;
}
interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTemplateParams, ResolvesDuplicates {
mode?: RuntimeMode;
transform?: (input: unknown) => unknown;
head?: Unhead;
}
interface Unhead<Input extends {} = Head> {
/**
* Registered plugins.
*/
plugins: HeadPlugin[];
/**
* The active head entries.
*/
headEntries: () => HeadEntry<Input>[];
/**
* Create a new head entry.
*/
push: (entry: Input, options?: HeadEntryOptions) => ActiveHeadEntry<Input>;
/**
* Resolve tags from head entries.
*/
resolveTags: () => Promise<HeadTag[]>;
/**
* Exposed hooks for easier extension.
*/
hooks: Hookable<HeadHooks>;
/**
* Resolved options
*/
resolvedOptions: CreateHeadOptions;
/**
* Use a head plugin, loads the plugins hooks.
*/
use: (plugin: HeadPluginInput) => void;
/**
* Is it a server-side render context.
*/
ssr: boolean;
/**
* @internal
*/
_dom?: DomState;
/**
* @internal
*/
_domUpdatePromise?: Promise<void>;
/**
* @internal
*/
_domDebouncedUpdatePromise?: Promise<void>;
/**
* @internal
*/
dirty: boolean;
/**
* @internal
*/
_scripts?: Record<string, any>;
/**
* @internal
*/
_templateParams?: TemplateParams;
/**
* @internal
*/
_separator?: string;
}
interface DomState {
pendingSideEffects: SideEffectsRecord;
sideEffects: SideEffectsRecord;
elMap: Record<string, Element>;
}
type SafeBodyAttr = Pick<BodyAttr, 'id' | 'class'> & DataKeys;
type SafeHtmlAttr = Pick<HtmlAttr, 'id' | 'class' | 'lang' | 'dir'> & DataKeys;
type SafeMeta = Pick<Meta, 'id' | 'name' | 'property' | 'content' | 'charset'> & DataKeys;
type SafeLink = Pick<Link, 'color' | 'crossorigin' | 'fetchpriority' | 'href' | 'hreflang' | 'imagesizes' | 'imagesrcset' | 'integrity' | 'media' | 'referrerpolicy' | 'sizes' | 'id'> & {
rel?: Omit<Link['rel'], 'stylesheet' | 'canonical' | 'modulepreload' | 'prerender' | 'preload' | 'prefetch'>;
type?: 'audio/aac' | 'application/x-abiword' | 'application/x-freearc' | 'image/avif' | 'video/x-msvideo' | 'application/vnd.amazon.ebook' | 'application/octet-stream' | 'image/bmp' | 'application/x-bzip' | 'application/x-bzip2' | 'application/x-cdf' | 'application/x-csh' | 'text/csv' | 'application/msword' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.ms-fontobject' | 'application/epub+zip' | 'application/gzip' | 'image/gif' | 'image/vnd.microsoft.icon' | 'text/calendar' | 'application/java-archive' | 'image/jpeg' | 'application/json' | 'application/ld+json' | 'audio/midi' | 'audio/x-midi' | 'audio/mpeg' | 'video/mp4' | 'video/mpeg' | 'application/vnd.apple.installer+xml' | 'application/vnd.oasis.opendocument.presentation' | 'application/vnd.oasis.opendocument.spreadsheet' | 'application/vnd.oasis.opendocument.text' | 'audio/ogg' | 'video/ogg' | 'application/ogg' | 'audio/opus' | 'font/otf' | 'image/png' | 'application/pdf' | 'application/x-httpd-php' | 'application/vnd.ms-powerpoint' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'application/vnd.rar' | 'application/rtf' | 'application/x-sh' | 'image/svg+xml' | 'application/x-tar' | 'image/tiff' | 'video/mp2t' | 'font/ttf' | 'text/plain' | 'application/vnd.visio' | 'audio/wav' | 'audio/webm' | 'video/webm' | 'image/webp' | 'font/woff' | 'font/woff2' | 'application/xhtml+xml' | 'application/vnd.ms-excel' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'text/xml' | 'application/atom+xml' | 'application/xml' | 'application/vnd.mozilla.xul+xml' | 'application/zip' | 'video/3gpp' | 'audio/3gpp' | 'video/3gpp2' | 'audio/3gpp2' | (string & Record<never, never>);
} & DataKeys;
type SafeScript = Pick<Script, 'id' | 'textContent'> & {
type: 'application/json' | 'application/ld+json';
} & DataKeys;
type SafeNoscript = Pick<Noscript, 'id' | 'textContent'> & DataKeys;
interface HeadSafe extends Pick<Head, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: SafeMeta[];
link?: SafeLink[];
noscript?: SafeNoscript[];
script?: SafeScript[];
htmlAttrs?: SafeHtmlAttr;
bodyAttrs?: SafeBodyAttr;
}
type UseScriptStatus = 'awaitingLoad' | 'loading' | 'loaded' | 'error' | 'removed';
/**
* Either a string source for the script or full script properties.
*/
type UseScriptInput = string | (Omit<Script, 'src'> & {
src: string;
});
type UseScriptResolvedInput = Omit<Script, 'src'> & {
src: string;
};
type BaseScriptApi = Record<symbol | string, any>;
type AsAsyncFunctionValues<T extends BaseScriptApi> = {
[key in keyof T]: T[key] extends any[] ? T[key] : T[key] extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : T[key] extends Record<any, any> ? AsAsyncFunctionValues<T[key]> : never;
};
interface ScriptInstance<T extends BaseScriptApi> {
proxy: AsAsyncFunctionValues<T>;
instance?: T;
id: string;
status: UseScriptStatus;
entry?: ActiveHeadEntry<any>;
load: () => Promise<T>;
remove: () => boolean;
setupTriggerHandler: (trigger: UseScriptOptions['trigger']) => void;
onLoaded: (fn: (instance: T) => void | Promise<void>) => void;
onError: (fn: (err?: Error) => void | Promise<void>) => void;
/**
* @internal
*/
_triggerAbortController?: AbortController | null;
/**
* @internal
*/
_triggerAbortPromise?: Promise<void>;
/**
* @internal
*/
_triggerPromises?: Promise<void>[];
/**
* @internal
*/
_cbs: {
loaded: null | ((instance: T) => void | Promise<void>)[];
error: null | ((err?: Error) => void | Promise<void>)[];
};
}
type UseFunctionType<T, U> = T extends {
use: infer V;
} ? V extends (...args: any) => any ? ReturnType<V> : U : U;
interface UseScriptOptions<T extends BaseScriptApi = {}, U = {}> extends HeadEntryOptions {
/**
* Resolve the script instance from the window.
*/
use?: () => T | undefined | null;
/**
* Stub the script instance. Useful for SSR or testing.
*/
stub?: ((ctx: {
script: ScriptInstance<T>;
fn: string | symbol;
}) => any);
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
*/
trigger?: 'client' | 'server' | 'manual' | Promise<boolean | void> | ((fn: any) => any) | null;
/**
* Context to run events with. This is useful in Vue to attach the current instance context before
* calling the event, allowing the event to be reactive.
*/
eventContext?: any;
/**
* Called before the script is initialized. Will not be triggered when the script is already loaded. This means
* this is guaranteed to be called only once, unless the script is removed and re-added.
*/
beforeInit?: () => void;
}
export type { ActiveHeadEntry, AsAsyncFunctionValues, Base, BaseBodyAttr, BaseHtmlAttr, BaseMeta, BodyAttr, BodyAttributes, CreateHeadOptions, DomBeforeRenderCtx, DomRenderTagContext, DomState, EntryAugmentation, EntryResolveCtx, HasTemplateParams, Head, HeadEntry, HeadEntryOptions, HeadHooks, HeadPlugin, HeadPluginInput, HeadPluginOptions, HeadSafe, HeadTag, HeadTagKeys, HeadUtils, HookResult, HtmlAttr, HtmlAttributes, InnerContent, InnerContentVal, Link, MaybeArray, MaybeFunctionEntries, Meta, Noscript, ProcessesTemplateParams, RenderSSRHeadOptions, ResolvesDuplicates, RuntimeMode, SSRHeadPayload, SSRRenderContext, SafeBodyAttr, SafeHtmlAttr, SafeLink, SafeMeta, SafeNoscript, SafeScript, SchemaAugmentations, Script, ScriptInstance, ShouldRenderContext, SideEffectsRecord, Style, TagKey, TagPosition, TagPriority, TagUserProperties, TemplateParams, Title, TitleTemplate, Unhead, UseFunctionType, UseScriptInput, UseScriptOptions, UseScriptResolvedInput, UseScriptStatus, UseSeoMetaInput, UserAttributesConfig, UserTagConfigWithoutInnerContent, ValidTagPositions };
+598
View File
@@ -0,0 +1,598 @@
import { NestedHooks, Hookable } from 'hookable';
import { MergeHead, BaseBodyAttributes, HtmlAttributes as HtmlAttributes$1, Meta as Meta$1, Stringable, Merge, Base as Base$1, DefinedValueOrEmptyObject, LinkBase, HttpEventAttributes, DataKeys, Style as Style$1, ScriptBase, Noscript as Noscript$1, BodyEvents, MetaFlatInput } from 'zhead';
export { BodyEvents, DataKeys, DefinedValueOrEmptyObject, MergeHead, MetaFlatInput, ScriptBase, SpeculationRules } from 'zhead';
type Never<T> = {
[P in keyof T]?: never;
};
type FalsyEntries<T> = {
[key in keyof T]?: T[key] | null | false | undefined;
};
type UserTagConfigWithoutInnerContent = TagPriority & TagPosition & ResolvesDuplicates & Never<InnerContent> & {
processTemplateParams?: false;
};
type UserAttributesConfig = ResolvesDuplicates & TagPriority & Never<InnerContent & TagPosition>;
interface SchemaAugmentations extends MergeHead {
title: TagPriority;
titleTemplate: TagPriority;
base: UserAttributesConfig;
htmlAttrs: UserAttributesConfig;
bodyAttrs: UserAttributesConfig;
link: UserTagConfigWithoutInnerContent;
meta: UserTagConfigWithoutInnerContent;
style: TagUserProperties;
script: TagUserProperties;
noscript: TagUserProperties;
}
type MaybeArray<T> = T | T[];
type BaseBodyAttr = BaseBodyAttributes;
type BaseHtmlAttr = HtmlAttributes$1;
interface BodyAttr extends Omit<BaseBodyAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface HtmlAttr extends Omit<HtmlAttributes$1, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<string> | Record<string, boolean>;
}
interface BaseMeta extends Omit<Meta$1, 'content'> {
/**
* This attribute contains the value for the http-equiv, name or property attribute, depending on which is used.
*
* You can provide an array of values to create multiple tags sharing the same name, property or http-equiv.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#attr-content
*/
content?: MaybeArray<Stringable> | null;
}
type EntryAugmentation = undefined | Record<string, any>;
type MaybeFunctionEntries<T> = {
[key in keyof T]?: T[key] | ((e: Event) => void);
};
type TitleTemplateResolver = string | ((title?: string) => string | null);
type Title = string | FalsyEntries<({
textContent: string;
} & SchemaAugmentations['title']) | null>;
type TitleTemplate = TitleTemplateResolver | null | ({
textContent: TitleTemplateResolver;
} & SchemaAugmentations['titleTemplate']);
type Base<E extends EntryAugmentation = Record<string, any>> = Partial<Merge<SchemaAugmentations['base'], FalsyEntries<Base$1>>> & DefinedValueOrEmptyObject<E>;
type Link<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<LinkBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['link'] & DefinedValueOrEmptyObject<E>;
type Meta<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BaseMeta> & DataKeys & SchemaAugmentations['meta'] & DefinedValueOrEmptyObject<E>;
type Style<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Style$1> & DataKeys & SchemaAugmentations['style'] & DefinedValueOrEmptyObject<E>;
type Script<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<ScriptBase> & MaybeFunctionEntries<HttpEventAttributes> & DataKeys & SchemaAugmentations['script'] & DefinedValueOrEmptyObject<E>;
type Noscript<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<Noscript$1> & DataKeys & SchemaAugmentations['noscript'] & DefinedValueOrEmptyObject<E>;
type HtmlAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<HtmlAttr> & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>;
type BodyAttributes<E extends EntryAugmentation = Record<string, any>> = FalsyEntries<BodyAttr> & MaybeFunctionEntries<BodyEvents> & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>;
interface HeadUtils {
/**
* Generate the title from a template.
*
* Should include a `%s` placeholder for the title, for example `%s - My Site`.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: TemplateParams;
}
interface Head<E extends MergeHead = SchemaAugmentations> extends HeadUtils {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: Link<E['link']>[];
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: Meta<E['meta']>[];
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: (Style<E['style']> | string)[];
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: (Script<E['script']> | string)[];
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: (Noscript<E['noscript']> | string)[];
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseSeoMetaInput = MetaFlatInput & {
title?: Title;
titleTemplate?: TitleTemplate;
};
interface ResolvesDuplicates {
/**
* By default, tags which share the same unique key `name`, `property` are de-duped. To allow duplicates
* to be made you can provide a unique key for each entry.
*/
key?: string;
/**
* The strategy to use when a duplicate tag is encountered.
*
* - `replace` - Replace the existing tag with the new tag
* - `merge` - Merge the existing tag with the new tag
*
* @default 'replace' (some tags will default to 'merge', such as htmlAttr)
*/
tagDuplicateStrategy?: 'replace' | 'merge';
/**
* @deprecated Use `key` instead
*/
hid?: string;
/**
* @deprecated Use `key` instead
*/
vmid?: string;
}
type ValidTagPositions = 'head' | 'bodyClose' | 'bodyOpen';
interface TagPosition {
/**
* Specify where to render the tag.
*
* @default 'head'
*/
tagPosition?: ValidTagPositions;
}
type InnerContentVal = string | Record<string, any>;
interface InnerContent {
/**
* Text content of the tag.
*
* Warning: This is not safe for XSS. Do not use this with user input, use `textContent` instead.
*/
innerHTML?: InnerContentVal;
/**
* Sets the textContent of an element. Safer for XSS.
*/
textContent?: InnerContentVal;
/**
* Sets the textContent of an element.
*
* @deprecated Use `textContent` or `innerHTML`.
*/
children?: InnerContentVal;
}
interface TagPriority {
/**
* The priority for rendering the tag, without this all tags are rendered as they are registered
* (besides some special tags).
*
* The following special tags have default priorities:
* -2 `<meta charset ...>`
* -1 `<base>`
* 0 `<meta http-equiv="content-security-policy" ...>`
*
* All other tags have a default priority of 10: `<meta>`, `<script>`, `<link>`, `<style>`, etc
*/
tagPriority?: number | 'critical' | 'high' | 'low' | `before:${string}` | `after:${string}`;
}
type TagUserProperties = FalsyEntries<TagPriority & TagPosition & InnerContent & ResolvesDuplicates & ProcessesTemplateParams>;
type TagKey = keyof Head;
type TemplateParams = {
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | Record<string, string>>;
interface ProcessesTemplateParams {
processTemplateParams?: boolean;
}
interface HasTemplateParams {
templateParams?: TemplateParams;
}
interface HeadTag extends TagPriority, TagPosition, ResolvesDuplicates, HasTemplateParams {
tag: TagKey;
props: Record<string, string>;
processTemplateParams?: boolean;
innerHTML?: string;
textContent?: string;
/**
* Entry ID
* @internal
*/
_e?: number;
/**
* Position
* @internal
*/
_p?: number;
/**
* Dedupe key
* @internal
*/
_d?: string;
/**
* Hash code used to represent the tag.
* @internal
*/
_h?: string;
/**
* @internal
*/
_m?: RuntimeMode;
/**
* @internal
*/
_eventHandlers?: Record<string, ((e: Event) => {})>;
}
type HeadTagKeys = (keyof HeadTag)[];
type HookResult = Promise<void> | void;
interface SSRHeadPayload {
headTags: string;
bodyTags: string;
bodyTagsOpen: string;
htmlAttrs: string;
bodyAttrs: string;
}
interface RenderSSRHeadOptions {
omitLineBreaks?: boolean;
}
interface EntryResolveCtx<T> {
tags: HeadTag[];
entries: HeadEntry<T>[];
}
interface DomRenderTagContext {
id: string;
$el: Element;
shouldRender: boolean;
tag: HeadTag;
entry?: HeadEntry<any>;
markSideEffect: (key: string, fn: () => void) => void;
}
interface DomBeforeRenderCtx extends ShouldRenderContext {
/**
* @deprecated will always be empty, prefer other hooks
*/
tags: DomRenderTagContext[];
}
interface ShouldRenderContext {
shouldRender: boolean;
}
interface SSRRenderContext {
tags: HeadTag[];
html: SSRHeadPayload;
}
interface HeadHooks {
'init': (ctx: Unhead<any>) => HookResult;
'entries:updated': (ctx: Unhead<any>) => HookResult;
'entries:resolve': (ctx: EntryResolveCtx<any>) => HookResult;
'tag:normalise': (ctx: {
tag: HeadTag;
entry: HeadEntry<any>;
resolvedOptions: CreateHeadOptions;
}) => HookResult;
'tags:beforeResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:resolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'tags:afterResolve': (ctx: {
tags: HeadTag[];
}) => HookResult;
'dom:beforeRender': (ctx: DomBeforeRenderCtx) => HookResult;
'dom:renderTag': (ctx: DomRenderTagContext, document: Document, track: any) => HookResult;
'dom:rendered': (ctx: {
renders: DomRenderTagContext[];
}) => HookResult;
'ssr:beforeRender': (ctx: ShouldRenderContext) => HookResult;
'ssr:render': (ctx: {
tags: HeadTag[];
}) => HookResult;
'ssr:rendered': (ctx: SSRRenderContext) => HookResult;
'script:updated': (ctx: {
script: ScriptInstance<any>;
}) => HookResult;
'script:instance-fn': (ctx: {
script: ScriptInstance<any>;
fn: string | symbol;
exists: boolean;
}) => HookResult;
}
/**
* Side effects are mapped with a key and their cleanup function.
*
* For example, `meta:data-h-4h46h465`: () => { document.querySelector('meta[data-h-4h46h465]').remove() }
*/
type SideEffectsRecord = Record<string, () => void>;
type RuntimeMode = 'server' | 'client';
interface HeadEntry<Input> {
/**
* User provided input for the entry.
*/
input: Input;
/**
* Optional resolved input which will be used if set.
*/
resolvedInput?: Input;
/**
* The mode that the entry should be used in.
*
* @internal
*/
mode?: RuntimeMode;
/**
* Transformer function for the entry.
*
* @internal
*/
transform?: (input: Input) => Promise<Input> | Input;
/**
* Head entry index
*
* @internal
*/
_i: number;
/**
* Default tag position.
*
* @internal
*/
tagPosition?: TagPosition['tagPosition'];
/**
* Default tag priority.
*
* @internal
*/
tagPriority?: TagPriority['tagPriority'];
}
type HeadPluginOptions = Omit<CreateHeadOptions, 'plugins'> & {
mode?: RuntimeMode;
};
type HeadPluginInput = (HeadPluginOptions & {
key?: string;
}) | ((head: Unhead) => HeadPluginOptions & {
key?: string;
});
type HeadPlugin = HeadPluginOptions & {
key?: string;
};
/**
* An active head entry provides an API to manipulate it.
*/
interface ActiveHeadEntry<Input> {
/**
* Updates the entry with new input.
*
* Will first clear any side effects for previous input.
*/
patch: (input: Input) => void;
/**
* Dispose the entry, removing it from the active head.
*
* Will queue side effects for removal.
*/
dispose: () => void;
}
interface CreateHeadOptions {
domDelayFn?: (fn: () => void) => void;
document?: Document;
plugins?: HeadPluginInput[];
hooks?: NestedHooks<HeadHooks>;
}
interface HeadEntryOptions extends TagPosition, TagPriority, ProcessesTemplateParams, ResolvesDuplicates {
mode?: RuntimeMode;
transform?: (input: unknown) => unknown;
head?: Unhead;
}
interface Unhead<Input extends {} = Head> {
/**
* Registered plugins.
*/
plugins: HeadPlugin[];
/**
* The active head entries.
*/
headEntries: () => HeadEntry<Input>[];
/**
* Create a new head entry.
*/
push: (entry: Input, options?: HeadEntryOptions) => ActiveHeadEntry<Input>;
/**
* Resolve tags from head entries.
*/
resolveTags: () => Promise<HeadTag[]>;
/**
* Exposed hooks for easier extension.
*/
hooks: Hookable<HeadHooks>;
/**
* Resolved options
*/
resolvedOptions: CreateHeadOptions;
/**
* Use a head plugin, loads the plugins hooks.
*/
use: (plugin: HeadPluginInput) => void;
/**
* Is it a server-side render context.
*/
ssr: boolean;
/**
* @internal
*/
_dom?: DomState;
/**
* @internal
*/
_domUpdatePromise?: Promise<void>;
/**
* @internal
*/
_domDebouncedUpdatePromise?: Promise<void>;
/**
* @internal
*/
dirty: boolean;
/**
* @internal
*/
_scripts?: Record<string, any>;
/**
* @internal
*/
_templateParams?: TemplateParams;
/**
* @internal
*/
_separator?: string;
}
interface DomState {
pendingSideEffects: SideEffectsRecord;
sideEffects: SideEffectsRecord;
elMap: Record<string, Element>;
}
type SafeBodyAttr = Pick<BodyAttr, 'id' | 'class'> & DataKeys;
type SafeHtmlAttr = Pick<HtmlAttr, 'id' | 'class' | 'lang' | 'dir'> & DataKeys;
type SafeMeta = Pick<Meta, 'id' | 'name' | 'property' | 'content' | 'charset'> & DataKeys;
type SafeLink = Pick<Link, 'color' | 'crossorigin' | 'fetchpriority' | 'href' | 'hreflang' | 'imagesizes' | 'imagesrcset' | 'integrity' | 'media' | 'referrerpolicy' | 'sizes' | 'id'> & {
rel?: Omit<Link['rel'], 'stylesheet' | 'canonical' | 'modulepreload' | 'prerender' | 'preload' | 'prefetch'>;
type?: 'audio/aac' | 'application/x-abiword' | 'application/x-freearc' | 'image/avif' | 'video/x-msvideo' | 'application/vnd.amazon.ebook' | 'application/octet-stream' | 'image/bmp' | 'application/x-bzip' | 'application/x-bzip2' | 'application/x-cdf' | 'application/x-csh' | 'text/csv' | 'application/msword' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/vnd.ms-fontobject' | 'application/epub+zip' | 'application/gzip' | 'image/gif' | 'image/vnd.microsoft.icon' | 'text/calendar' | 'application/java-archive' | 'image/jpeg' | 'application/json' | 'application/ld+json' | 'audio/midi' | 'audio/x-midi' | 'audio/mpeg' | 'video/mp4' | 'video/mpeg' | 'application/vnd.apple.installer+xml' | 'application/vnd.oasis.opendocument.presentation' | 'application/vnd.oasis.opendocument.spreadsheet' | 'application/vnd.oasis.opendocument.text' | 'audio/ogg' | 'video/ogg' | 'application/ogg' | 'audio/opus' | 'font/otf' | 'image/png' | 'application/pdf' | 'application/x-httpd-php' | 'application/vnd.ms-powerpoint' | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' | 'application/vnd.rar' | 'application/rtf' | 'application/x-sh' | 'image/svg+xml' | 'application/x-tar' | 'image/tiff' | 'video/mp2t' | 'font/ttf' | 'text/plain' | 'application/vnd.visio' | 'audio/wav' | 'audio/webm' | 'video/webm' | 'image/webp' | 'font/woff' | 'font/woff2' | 'application/xhtml+xml' | 'application/vnd.ms-excel' | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | 'text/xml' | 'application/atom+xml' | 'application/xml' | 'application/vnd.mozilla.xul+xml' | 'application/zip' | 'video/3gpp' | 'audio/3gpp' | 'video/3gpp2' | 'audio/3gpp2' | (string & Record<never, never>);
} & DataKeys;
type SafeScript = Pick<Script, 'id' | 'textContent'> & {
type: 'application/json' | 'application/ld+json';
} & DataKeys;
type SafeNoscript = Pick<Noscript, 'id' | 'textContent'> & DataKeys;
interface HeadSafe extends Pick<Head, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: SafeMeta[];
link?: SafeLink[];
noscript?: SafeNoscript[];
script?: SafeScript[];
htmlAttrs?: SafeHtmlAttr;
bodyAttrs?: SafeBodyAttr;
}
type UseScriptStatus = 'awaitingLoad' | 'loading' | 'loaded' | 'error' | 'removed';
/**
* Either a string source for the script or full script properties.
*/
type UseScriptInput = string | (Omit<Script, 'src'> & {
src: string;
});
type UseScriptResolvedInput = Omit<Script, 'src'> & {
src: string;
};
type BaseScriptApi = Record<symbol | string, any>;
type AsAsyncFunctionValues<T extends BaseScriptApi> = {
[key in keyof T]: T[key] extends any[] ? T[key] : T[key] extends (...args: infer A) => infer R ? (...args: A) => R extends Promise<any> ? R : Promise<R> : T[key] extends Record<any, any> ? AsAsyncFunctionValues<T[key]> : never;
};
interface ScriptInstance<T extends BaseScriptApi> {
proxy: AsAsyncFunctionValues<T>;
instance?: T;
id: string;
status: UseScriptStatus;
entry?: ActiveHeadEntry<any>;
load: () => Promise<T>;
remove: () => boolean;
setupTriggerHandler: (trigger: UseScriptOptions['trigger']) => void;
onLoaded: (fn: (instance: T) => void | Promise<void>) => void;
onError: (fn: (err?: Error) => void | Promise<void>) => void;
/**
* @internal
*/
_triggerAbortController?: AbortController | null;
/**
* @internal
*/
_triggerAbortPromise?: Promise<void>;
/**
* @internal
*/
_triggerPromises?: Promise<void>[];
/**
* @internal
*/
_cbs: {
loaded: null | ((instance: T) => void | Promise<void>)[];
error: null | ((err?: Error) => void | Promise<void>)[];
};
}
type UseFunctionType<T, U> = T extends {
use: infer V;
} ? V extends (...args: any) => any ? ReturnType<V> : U : U;
interface UseScriptOptions<T extends BaseScriptApi = {}, U = {}> extends HeadEntryOptions {
/**
* Resolve the script instance from the window.
*/
use?: () => T | undefined | null;
/**
* Stub the script instance. Useful for SSR or testing.
*/
stub?: ((ctx: {
script: ScriptInstance<T>;
fn: string | symbol;
}) => any);
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
*/
trigger?: 'client' | 'server' | 'manual' | Promise<boolean | void> | ((fn: any) => any) | null;
/**
* Context to run events with. This is useful in Vue to attach the current instance context before
* calling the event, allowing the event to be reactive.
*/
eventContext?: any;
/**
* Called before the script is initialized. Will not be triggered when the script is already loaded. This means
* this is guaranteed to be called only once, unless the script is removed and re-added.
*/
beforeInit?: () => void;
}
export type { ActiveHeadEntry, AsAsyncFunctionValues, Base, BaseBodyAttr, BaseHtmlAttr, BaseMeta, BodyAttr, BodyAttributes, CreateHeadOptions, DomBeforeRenderCtx, DomRenderTagContext, DomState, EntryAugmentation, EntryResolveCtx, HasTemplateParams, Head, HeadEntry, HeadEntryOptions, HeadHooks, HeadPlugin, HeadPluginInput, HeadPluginOptions, HeadSafe, HeadTag, HeadTagKeys, HeadUtils, HookResult, HtmlAttr, HtmlAttributes, InnerContent, InnerContentVal, Link, MaybeArray, MaybeFunctionEntries, Meta, Noscript, ProcessesTemplateParams, RenderSSRHeadOptions, ResolvesDuplicates, RuntimeMode, SSRHeadPayload, SSRRenderContext, SafeBodyAttr, SafeHtmlAttr, SafeLink, SafeMeta, SafeNoscript, SafeScript, SchemaAugmentations, Script, ScriptInstance, ShouldRenderContext, SideEffectsRecord, Style, TagKey, TagPosition, TagPriority, TagUserProperties, TemplateParams, Title, TitleTemplate, Unhead, UseFunctionType, UseScriptInput, UseScriptOptions, UseScriptResolvedInput, UseScriptStatus, UseSeoMetaInput, UserAttributesConfig, UserTagConfigWithoutInnerContent, ValidTagPositions };
+1
View File
@@ -0,0 +1 @@
+44
View File
@@ -0,0 +1,44 @@
{
"name": "@unhead/schema",
"type": "module",
"version": "1.11.20",
"author": "Harlan Wilton <harlan@harlanzw.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/harlan-zw",
"homepage": "https://unhead.unjs.io",
"repository": {
"type": "git",
"url": "git+https://github.com/unjs/unhead.git",
"directory": "packages/schema"
},
"bugs": {
"url": "https://github.com/unjs/unhead/issues"
},
"keywords": [
"head",
"meta tags",
"types"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"dependencies": {
"hookable": "^5.5.3",
"zhead": "^2.2.4"
},
"scripts": {
"build": "unbuild .",
"stub": "unbuild . --stub"
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Harlan Wilton <harlan@harlanzw.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+16
View File
@@ -0,0 +1,16 @@
# `@unhead/schema`
Typescript definitions for document `<head>`.
## Installation
```bash
npm install --save-dev @unhead/schema
# Using yarn
yarn add --dev @unhead/schema
```
## Types
See [head.ts](./src/head.ts) for the full list of types.
+675
View File
@@ -0,0 +1,675 @@
'use strict';
const packrup = require('packrup');
function asArray(value) {
return Array.isArray(value) ? value : [value];
}
const SelfClosingTags = /* @__PURE__ */ new Set(["meta", "link", "base"]);
const TagsWithInnerContent = /* @__PURE__ */ new Set(["title", "titleTemplate", "script", "style", "noscript"]);
const HasElementTags = /* @__PURE__ */ new Set([
"base",
"meta",
"link",
"style",
"script",
"noscript"
]);
const ValidHeadTags = /* @__PURE__ */ new Set([
"title",
"titleTemplate",
"templateParams",
"base",
"htmlAttrs",
"bodyAttrs",
"meta",
"link",
"style",
"script",
"noscript"
]);
const UniqueTags = /* @__PURE__ */ new Set(["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs", "templateParams"]);
const TagConfigKeys = /* @__PURE__ */ new Set(["tagPosition", "tagPriority", "tagDuplicateStrategy", "children", "innerHTML", "textContent", "processTemplateParams"]);
const IsBrowser = typeof window !== "undefined";
const composableNames = [
"getActiveHead",
"useHead",
"useSeoMeta",
"useHeadSafe",
"useServerHead",
"useServerSeoMeta",
"useServerHeadSafe"
];
function defineHeadPlugin(plugin) {
return plugin;
}
function hashCode(s) {
let h = 9;
for (let i = 0; i < s.length; )
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
}
function hashTag(tag) {
if (tag._h) {
return tag._h;
}
if (tag._d) {
return hashCode(tag._d);
}
let content = `${tag.tag}:${tag.textContent || tag.innerHTML || ""}:`;
for (const key in tag.props) {
content += `${key}:${String(tag.props[key])},`;
}
return hashCode(content);
}
const p = (p2) => ({ keyValue: p2, metaKey: "property" });
const k = (p2) => ({ keyValue: p2 });
const MetaPackingSchema = {
appleItunesApp: {
unpack: {
entrySeparator: ", ",
resolve({ key, value }) {
return `${fixKeyCase(key)}=${value}`;
}
}
},
articleExpirationTime: p("article:expiration_time"),
articleModifiedTime: p("article:modified_time"),
articlePublishedTime: p("article:published_time"),
bookReleaseDate: p("book:release_date"),
charset: {
metaKey: "charset"
},
contentSecurityPolicy: {
unpack: {
entrySeparator: "; ",
resolve({ key, value }) {
return `${fixKeyCase(key)} ${value}`;
}
},
metaKey: "http-equiv"
},
contentType: {
metaKey: "http-equiv"
},
defaultStyle: {
metaKey: "http-equiv"
},
fbAppId: p("fb:app_id"),
msapplicationConfig: k("msapplication-Config"),
msapplicationTileColor: k("msapplication-TileColor"),
msapplicationTileImage: k("msapplication-TileImage"),
ogAudioSecureUrl: p("og:audio:secure_url"),
ogAudioUrl: p("og:audio"),
ogImageSecureUrl: p("og:image:secure_url"),
ogImageUrl: p("og:image"),
ogSiteName: p("og:site_name"),
ogVideoSecureUrl: p("og:video:secure_url"),
ogVideoUrl: p("og:video"),
profileFirstName: p("profile:first_name"),
profileLastName: p("profile:last_name"),
profileUsername: p("profile:username"),
refresh: {
metaKey: "http-equiv",
unpack: {
entrySeparator: ";",
resolve({ key, value }) {
if (key === "seconds")
return `${value}`;
}
}
},
robots: {
unpack: {
entrySeparator: ", ",
resolve({ key, value }) {
if (typeof value === "boolean")
return `${fixKeyCase(key)}`;
else
return `${fixKeyCase(key)}:${value}`;
}
}
},
xUaCompatible: {
metaKey: "http-equiv"
}
};
const openGraphNamespaces = /* @__PURE__ */ new Set([
"og",
"book",
"article",
"profile"
]);
function resolveMetaKeyType(key) {
const fKey = fixKeyCase(key);
const prefixIndex = fKey.indexOf(":");
if (openGraphNamespaces.has(fKey.substring(0, prefixIndex)))
return "property";
return MetaPackingSchema[key]?.metaKey || "name";
}
function resolveMetaKeyValue(key) {
return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
}
function fixKeyCase(key) {
const updated = key.replace(/([A-Z])/g, "-$1").toLowerCase();
const prefixIndex = updated.indexOf("-");
const fKey = updated.substring(0, prefixIndex);
if (fKey === "twitter" || openGraphNamespaces.has(fKey))
return key.replace(/([A-Z])/g, ":$1").toLowerCase();
return updated;
}
function changeKeyCasingDeep(input) {
if (Array.isArray(input)) {
return input.map((entry) => changeKeyCasingDeep(entry));
}
if (typeof input !== "object" || Array.isArray(input))
return input;
const output = {};
for (const key in input) {
if (!Object.prototype.hasOwnProperty.call(input, key)) {
continue;
}
output[fixKeyCase(key)] = changeKeyCasingDeep(input[key]);
}
return output;
}
function resolvePackedMetaObjectValue(value, key) {
const definition = MetaPackingSchema[key];
if (key === "refresh")
return `${value.seconds};url=${value.url}`;
return packrup.unpackToString(
changeKeyCasingDeep(value),
{
keyValueSeparator: "=",
entrySeparator: ", ",
resolve({ value: value2, key: key2 }) {
if (value2 === null)
return "";
if (typeof value2 === "boolean")
return `${key2}`;
},
...definition?.unpack
}
);
}
const ObjectArrayEntries = /* @__PURE__ */ new Set(["og:image", "og:video", "og:audio", "twitter:image"]);
function sanitize(input) {
const out = {};
for (const k2 in input) {
if (!Object.prototype.hasOwnProperty.call(input, k2)) {
continue;
}
const v = input[k2];
if (String(v) !== "false" && k2)
out[k2] = v;
}
return out;
}
function handleObjectEntry(key, v) {
const value = sanitize(v);
const fKey = fixKeyCase(key);
const attr = resolveMetaKeyType(fKey);
if (ObjectArrayEntries.has(fKey)) {
const input = {};
for (const k2 in value) {
if (!Object.prototype.hasOwnProperty.call(value, k2)) {
continue;
}
input[`${key}${k2 === "url" ? "" : `${k2[0].toUpperCase()}${k2.slice(1)}`}`] = value[k2];
}
return unpackMeta(input).sort((a, b) => (a[attr]?.length || 0) - (b[attr]?.length || 0));
}
return [{ [attr]: fKey, ...value }];
}
function unpackMeta(input) {
const extras = [];
const primitives = {};
for (const key in input) {
if (!Object.prototype.hasOwnProperty.call(input, key)) {
continue;
}
const value = input[key];
if (!Array.isArray(value)) {
if (typeof value === "object" && value) {
if (ObjectArrayEntries.has(fixKeyCase(key))) {
extras.push(...handleObjectEntry(key, value));
continue;
}
primitives[key] = sanitize(value);
} else {
primitives[key] = value;
}
continue;
}
for (const v of value) {
extras.push(...typeof v === "string" ? unpackMeta({ [key]: v }) : handleObjectEntry(key, v));
}
}
const meta = packrup.unpackToArray(primitives, {
key({ key }) {
return resolveMetaKeyType(key);
},
value({ key }) {
return key === "charset" ? "charset" : "content";
},
resolveKeyData({ key }) {
return resolveMetaKeyValue(key);
},
resolveValueData({ value, key }) {
if (value === null)
return "_null";
if (typeof value === "object")
return resolvePackedMetaObjectValue(value, key);
return typeof value === "number" ? value.toString() : value;
}
});
return [...extras, ...meta].map((m) => {
if (m.content === "_null")
m.content = null;
return m;
});
}
function packMeta(inputs) {
const mappedPackingSchema = Object.entries(MetaPackingSchema).map(([key, value]) => [key, value.keyValue]);
return packrup.packArray(inputs, {
key: ["name", "property", "httpEquiv", "http-equiv", "charset"],
value: ["content", "charset"],
resolveKey(k2) {
let key = mappedPackingSchema.filter((sk) => sk[1] === k2)?.[0]?.[0] || k2;
const replacer = (_, letter) => letter?.toUpperCase();
key = key.replace(/:([a-z])/g, replacer).replace(/-([a-z])/g, replacer);
return key;
}
});
}
function thenable(val, thenFn) {
if (val instanceof Promise) {
return val.then(thenFn);
}
return thenFn(val);
}
function normaliseTag(tagName, input, e, normalizedProps) {
const props = normalizedProps || normaliseProps(
// explicitly check for an object
// @ts-expect-error untyped
typeof input === "object" && typeof input !== "function" && !(input instanceof Promise) ? { ...input } : { [tagName === "script" || tagName === "noscript" || tagName === "style" ? "innerHTML" : "textContent"]: input },
tagName === "templateParams" || tagName === "titleTemplate"
);
if (props instanceof Promise) {
return props.then((val) => normaliseTag(tagName, input, e, val));
}
const tag = {
tag: tagName,
props
};
for (const k of TagConfigKeys) {
const val = tag.props[k] !== void 0 ? tag.props[k] : e[k];
if (val !== void 0) {
if (!(k === "innerHTML" || k === "textContent" || k === "children") || TagsWithInnerContent.has(tag.tag)) {
tag[k === "children" ? "innerHTML" : k] = val;
}
delete tag.props[k];
}
}
if (tag.props.body) {
tag.tagPosition = "bodyClose";
delete tag.props.body;
}
if (tag.tag === "script") {
if (typeof tag.innerHTML === "object") {
tag.innerHTML = JSON.stringify(tag.innerHTML);
tag.props.type = tag.props.type || "application/json";
}
}
return Array.isArray(tag.props.content) ? tag.props.content.map((v) => ({ ...tag, props: { ...tag.props, content: v } })) : tag;
}
function normaliseStyleClassProps(key, v) {
const sep = key === "class" ? " " : ";";
if (v && typeof v === "object" && !Array.isArray(v)) {
v = Object.entries(v).filter(([, v2]) => v2).map(([k, v2]) => key === "style" ? `${k}:${v2}` : k);
}
return String(Array.isArray(v) ? v.join(sep) : v)?.split(sep).filter((c) => Boolean(c.trim())).join(sep);
}
function nestedNormaliseProps(props, virtual, keys, startIndex) {
for (let i = startIndex; i < keys.length; i += 1) {
const k = keys[i];
if (k === "class" || k === "style") {
props[k] = normaliseStyleClassProps(k, props[k]);
continue;
}
if (props[k] instanceof Promise) {
return props[k].then((val) => {
props[k] = val;
return nestedNormaliseProps(props, virtual, keys, i);
});
}
if (!virtual && !TagConfigKeys.has(k)) {
const v = String(props[k]);
const isDataKey = k.startsWith("data-");
if (v === "true" || v === "") {
props[k] = isDataKey ? "true" : true;
} else if (!props[k]) {
if (isDataKey && v === "false")
props[k] = "false";
else
delete props[k];
}
}
}
}
function normaliseProps(props, virtual = false) {
const resolvedProps = nestedNormaliseProps(props, virtual, Object.keys(props), 0);
if (resolvedProps instanceof Promise) {
return resolvedProps.then(() => props);
}
return props;
}
const TagEntityBits = 10;
function nestedNormaliseEntryTags(headTags, tagPromises, startIndex) {
for (let i = startIndex; i < tagPromises.length; i += 1) {
const tags = tagPromises[i];
if (tags instanceof Promise) {
return tags.then((val) => {
tagPromises[i] = val;
return nestedNormaliseEntryTags(headTags, tagPromises, i);
});
}
if (Array.isArray(tags)) {
headTags.push(...tags);
} else {
headTags.push(tags);
}
}
}
function normaliseEntryTags(e) {
const tagPromises = [];
const input = e.resolvedInput;
for (const k in input) {
if (!Object.prototype.hasOwnProperty.call(input, k)) {
continue;
}
const v = input[k];
if (v === void 0 || !ValidHeadTags.has(k)) {
continue;
}
if (Array.isArray(v)) {
for (const props of v) {
tagPromises.push(normaliseTag(k, props, e));
}
continue;
}
tagPromises.push(normaliseTag(k, v, e));
}
if (tagPromises.length === 0) {
return [];
}
const headTags = [];
return thenable(nestedNormaliseEntryTags(headTags, tagPromises, 0), () => headTags.map((t, i) => {
t._e = e._i;
e.mode && (t._m = e.mode);
t._p = (e._i << TagEntityBits) + i;
return t;
}));
}
const WhitelistAttributes = {
htmlAttrs: ["id", "class", "lang", "dir"],
bodyAttrs: ["id", "class"],
meta: ["id", "name", "property", "charset", "content"],
noscript: ["id", "textContent"],
script: ["id", "type", "textContent"],
link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
};
function acceptDataAttrs(value) {
const filtered = {};
Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
filtered[a] = value[a];
});
return filtered;
}
function whitelistSafeInput(input) {
const filtered = {};
Object.keys(input).forEach((key) => {
const tagValue = input[key];
if (!tagValue)
return;
switch (key) {
// always safe
case "title":
case "titleTemplate":
case "templateParams":
filtered[key] = tagValue;
break;
case "htmlAttrs":
case "bodyAttrs":
filtered[key] = acceptDataAttrs(tagValue);
WhitelistAttributes[key].forEach((a) => {
if (tagValue[a])
filtered[key][a] = tagValue[a];
});
break;
// meta is safe, except for http-equiv
case "meta":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const safeMeta = acceptDataAttrs(meta);
WhitelistAttributes.meta.forEach((key2) => {
if (meta[key2])
safeMeta[key2] = meta[key2];
});
return safeMeta;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
// link tags we don't allow stylesheets, scripts, preloading, prerendering, prefetching, etc
case "link":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const link = acceptDataAttrs(meta);
WhitelistAttributes.link.forEach((key2) => {
const val = meta[key2];
if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
return;
if (key2 === "href") {
if (val.includes("javascript:") || val.includes("data:"))
return;
link[key2] = val;
} else if (val) {
link[key2] = val;
}
});
return link;
}).filter((link) => Object.keys(link).length > 1 && !!link.rel);
}
break;
case "noscript":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const noscript = acceptDataAttrs(meta);
WhitelistAttributes.noscript.forEach((key2) => {
if (meta[key2])
noscript[key2] = meta[key2];
});
return noscript;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
// we only allow JSON in scripts
case "script":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((script) => {
const safeScript = acceptDataAttrs(script);
WhitelistAttributes.script.forEach((s) => {
if (script[s]) {
if (s === "textContent") {
try {
const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
safeScript[s] = JSON.stringify(jsonVal, null, 0);
} catch (e) {
}
} else {
safeScript[s] = script[s];
}
}
});
return safeScript;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
}
});
return filtered;
}
const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
const TAG_WEIGHTS = {
// tags
base: -10,
title: 10
};
const TAG_ALIASES = {
// relative scores to their default values
critical: -80,
high: -10,
low: 20
};
function tagWeight(tag) {
const priority = tag.tagPriority;
if (typeof priority === "number")
return priority;
let weight = 100;
if (tag.tag === "meta") {
if (tag.props["http-equiv"] === "content-security-policy")
weight = -30;
else if (tag.props.charset)
weight = -20;
else if (tag.props.name === "viewport")
weight = -15;
} else if (tag.tag === "link" && tag.props.rel === "preconnect") {
weight = 20;
} else if (tag.tag in TAG_WEIGHTS) {
weight = TAG_WEIGHTS[tag.tag];
}
if (priority && priority in TAG_ALIASES) {
return weight + TAG_ALIASES[priority];
}
return weight;
}
const SortModifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
const allowedMetaProperties = ["name", "property", "http-equiv"];
function tagDedupeKey(tag) {
const { props, tag: tagName } = tag;
if (UniqueTags.has(tagName))
return tagName;
if (tagName === "link" && props.rel === "canonical")
return "canonical";
if (props.charset)
return "charset";
if (props.id) {
return `${tagName}:id:${props.id}`;
}
for (const n of allowedMetaProperties) {
if (props[n] !== void 0) {
return `${tagName}:${n}:${props[n]}`;
}
}
return false;
}
const sepSub = "%separator";
function sub(p, token, isJson = false) {
let val;
if (token === "s" || token === "pageTitle") {
val = p.pageTitle;
} else if (token.includes(".")) {
const dotIndex = token.indexOf(".");
val = p[token.substring(0, dotIndex)]?.[token.substring(dotIndex + 1)];
} else {
val = p[token];
}
if (val !== void 0) {
return isJson ? (val || "").replace(/"/g, '\\"') : val || "";
}
return void 0;
}
const sepSubRe = new RegExp(`${sepSub}(?:\\s*${sepSub})*`, "g");
function processTemplateParams(s, p, sep, isJson = false) {
if (typeof s !== "string" || !s.includes("%"))
return s;
let decoded = s;
try {
decoded = decodeURI(s);
} catch {
}
const tokens = decoded.match(/%\w+(?:\.\w+)?/g);
if (!tokens) {
return s;
}
const hasSepSub = s.includes(sepSub);
s = s.replace(/%\w+(?:\.\w+)?/g, (token) => {
if (token === sepSub || !tokens.includes(token)) {
return token;
}
const re = sub(p, token.slice(1), isJson);
return re !== void 0 ? re : token;
}).trim();
if (hasSepSub) {
if (s.endsWith(sepSub))
s = s.slice(0, -sepSub.length);
if (s.startsWith(sepSub))
s = s.slice(sepSub.length);
s = s.replace(sepSubRe, sep).trim();
}
return s;
}
function resolveTitleTemplate(template, title) {
if (template == null)
return title || null;
if (typeof template === "function")
return template(title);
return template;
}
exports.HasElementTags = HasElementTags;
exports.IsBrowser = IsBrowser;
exports.NetworkEvents = NetworkEvents;
exports.ScriptNetworkEvents = ScriptNetworkEvents;
exports.SelfClosingTags = SelfClosingTags;
exports.SortModifiers = SortModifiers;
exports.TAG_ALIASES = TAG_ALIASES;
exports.TAG_WEIGHTS = TAG_WEIGHTS;
exports.TagConfigKeys = TagConfigKeys;
exports.TagEntityBits = TagEntityBits;
exports.TagsWithInnerContent = TagsWithInnerContent;
exports.UniqueTags = UniqueTags;
exports.ValidHeadTags = ValidHeadTags;
exports.asArray = asArray;
exports.composableNames = composableNames;
exports.defineHeadPlugin = defineHeadPlugin;
exports.hashCode = hashCode;
exports.hashTag = hashTag;
exports.normaliseEntryTags = normaliseEntryTags;
exports.normaliseProps = normaliseProps;
exports.normaliseStyleClassProps = normaliseStyleClassProps;
exports.normaliseTag = normaliseTag;
exports.packMeta = packMeta;
exports.processTemplateParams = processTemplateParams;
exports.resolveMetaKeyType = resolveMetaKeyType;
exports.resolveMetaKeyValue = resolveMetaKeyValue;
exports.resolvePackedMetaObjectValue = resolvePackedMetaObjectValue;
exports.resolveTitleTemplate = resolveTitleTemplate;
exports.tagDedupeKey = tagDedupeKey;
exports.tagWeight = tagWeight;
exports.thenable = thenable;
exports.unpackMeta = unpackMeta;
exports.whitelistSafeInput = whitelistSafeInput;
+69
View File
@@ -0,0 +1,69 @@
import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
type Arrayable<T> = T | Array<T>;
declare function asArray<T>(value: Arrayable<T>): T[];
declare const SelfClosingTags: Set<string>;
declare const TagsWithInnerContent: Set<string>;
declare const HasElementTags: Set<string>;
declare const ValidHeadTags: Set<string>;
declare const UniqueTags: Set<string>;
declare const TagConfigKeys: Set<string>;
declare const IsBrowser: boolean;
declare const composableNames: string[];
declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
declare function hashCode(s: string): string;
declare function hashTag(tag: HeadTag): string;
declare function resolveMetaKeyType(key: string): keyof BaseMeta;
declare function resolveMetaKeyValue(key: string): string;
declare function resolvePackedMetaObjectValue(value: string, key: string): string;
/**
* Converts a flat meta object into an array of meta entries.
* @param input
*/
declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['meta'];
/**
* Convert an array of meta entries to a flat object.
* @param inputs
*/
declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
type Thenable<T> = Promise<T> | T;
declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
declare function normaliseTag<T extends HeadTag>(tagName: T['tag'], input: HeadTag['props'] | string, e: HeadEntry<T>, normalizedProps?: HeadTag['props']): Thenable<T | T[]>;
declare function normaliseStyleClassProps<T extends 'class' | 'style'>(key: T, v: Required<Required<Head>['htmlAttrs']['class']> | Required<Required<Head>['htmlAttrs']['style']>): string;
declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?: boolean): Thenable<T['props']>;
declare const TagEntityBits = 10;
declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
declare const NetworkEvents: Set<string>;
declare const ScriptNetworkEvents: Set<string>;
declare const TAG_WEIGHTS: {
readonly base: -10;
readonly title: 10;
};
declare const TAG_ALIASES: {
readonly critical: -80;
readonly high: -10;
readonly low: 20;
};
declare function tagWeight<T extends HeadTag>(tag: T): number;
declare const SortModifiers: {
prefix: string;
offset: number;
}[];
declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
declare function processTemplateParams(s: string, p: TemplateParams, sep: string, isJson?: boolean): string;
declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
+69
View File
@@ -0,0 +1,69 @@
import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
type Arrayable<T> = T | Array<T>;
declare function asArray<T>(value: Arrayable<T>): T[];
declare const SelfClosingTags: Set<string>;
declare const TagsWithInnerContent: Set<string>;
declare const HasElementTags: Set<string>;
declare const ValidHeadTags: Set<string>;
declare const UniqueTags: Set<string>;
declare const TagConfigKeys: Set<string>;
declare const IsBrowser: boolean;
declare const composableNames: string[];
declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
declare function hashCode(s: string): string;
declare function hashTag(tag: HeadTag): string;
declare function resolveMetaKeyType(key: string): keyof BaseMeta;
declare function resolveMetaKeyValue(key: string): string;
declare function resolvePackedMetaObjectValue(value: string, key: string): string;
/**
* Converts a flat meta object into an array of meta entries.
* @param input
*/
declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['meta'];
/**
* Convert an array of meta entries to a flat object.
* @param inputs
*/
declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
type Thenable<T> = Promise<T> | T;
declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
declare function normaliseTag<T extends HeadTag>(tagName: T['tag'], input: HeadTag['props'] | string, e: HeadEntry<T>, normalizedProps?: HeadTag['props']): Thenable<T | T[]>;
declare function normaliseStyleClassProps<T extends 'class' | 'style'>(key: T, v: Required<Required<Head>['htmlAttrs']['class']> | Required<Required<Head>['htmlAttrs']['style']>): string;
declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?: boolean): Thenable<T['props']>;
declare const TagEntityBits = 10;
declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
declare const NetworkEvents: Set<string>;
declare const ScriptNetworkEvents: Set<string>;
declare const TAG_WEIGHTS: {
readonly base: -10;
readonly title: 10;
};
declare const TAG_ALIASES: {
readonly critical: -80;
readonly high: -10;
readonly low: 20;
};
declare function tagWeight<T extends HeadTag>(tag: T): number;
declare const SortModifiers: {
prefix: string;
offset: number;
}[];
declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
declare function processTemplateParams(s: string, p: TemplateParams, sep: string, isJson?: boolean): string;
declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
+69
View File
@@ -0,0 +1,69 @@
import { HeadPluginInput, HeadTag, BaseMeta, MetaFlatInput, Head, HeadEntry, MaybeArray, HeadSafe, TemplateParams } from '@unhead/schema';
type Arrayable<T> = T | Array<T>;
declare function asArray<T>(value: Arrayable<T>): T[];
declare const SelfClosingTags: Set<string>;
declare const TagsWithInnerContent: Set<string>;
declare const HasElementTags: Set<string>;
declare const ValidHeadTags: Set<string>;
declare const UniqueTags: Set<string>;
declare const TagConfigKeys: Set<string>;
declare const IsBrowser: boolean;
declare const composableNames: string[];
declare function defineHeadPlugin(plugin: HeadPluginInput): HeadPluginInput;
declare function hashCode(s: string): string;
declare function hashTag(tag: HeadTag): string;
declare function resolveMetaKeyType(key: string): keyof BaseMeta;
declare function resolveMetaKeyValue(key: string): string;
declare function resolvePackedMetaObjectValue(value: string, key: string): string;
/**
* Converts a flat meta object into an array of meta entries.
* @param input
*/
declare function unpackMeta<T extends MetaFlatInput>(input: T): Required<Head>['meta'];
/**
* Convert an array of meta entries to a flat object.
* @param inputs
*/
declare function packMeta<T extends Required<Head>['meta']>(inputs: T): MetaFlatInput;
type Thenable<T> = Promise<T> | T;
declare function thenable<T, R>(val: T, thenFn: (val: Awaited<T>) => R): Promise<R> | R;
declare function normaliseTag<T extends HeadTag>(tagName: T['tag'], input: HeadTag['props'] | string, e: HeadEntry<T>, normalizedProps?: HeadTag['props']): Thenable<T | T[]>;
declare function normaliseStyleClassProps<T extends 'class' | 'style'>(key: T, v: Required<Required<Head>['htmlAttrs']['class']> | Required<Required<Head>['htmlAttrs']['style']>): string;
declare function normaliseProps<T extends HeadTag>(props: T['props'], virtual?: boolean): Thenable<T['props']>;
declare const TagEntityBits = 10;
declare function normaliseEntryTags<T extends object = Head>(e: HeadEntry<T>): Thenable<HeadTag[]>;
declare function whitelistSafeInput(input: Record<string, MaybeArray<Record<string, string>>>): HeadSafe;
declare const NetworkEvents: Set<string>;
declare const ScriptNetworkEvents: Set<string>;
declare const TAG_WEIGHTS: {
readonly base: -10;
readonly title: 10;
};
declare const TAG_ALIASES: {
readonly critical: -80;
readonly high: -10;
readonly low: 20;
};
declare function tagWeight<T extends HeadTag>(tag: T): number;
declare const SortModifiers: {
prefix: string;
offset: number;
}[];
declare function tagDedupeKey<T extends HeadTag>(tag: T): string | false;
declare function processTemplateParams(s: string, p: TemplateParams, sep: string, isJson?: boolean): string;
declare function resolveTitleTemplate(template: string | ((title?: string) => string | null) | null, title?: string): string | null;
export { type Arrayable, HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, type Thenable, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
+641
View File
@@ -0,0 +1,641 @@
import { unpackToString, unpackToArray, packArray } from 'packrup';
function asArray(value) {
return Array.isArray(value) ? value : [value];
}
const SelfClosingTags = /* @__PURE__ */ new Set(["meta", "link", "base"]);
const TagsWithInnerContent = /* @__PURE__ */ new Set(["title", "titleTemplate", "script", "style", "noscript"]);
const HasElementTags = /* @__PURE__ */ new Set([
"base",
"meta",
"link",
"style",
"script",
"noscript"
]);
const ValidHeadTags = /* @__PURE__ */ new Set([
"title",
"titleTemplate",
"templateParams",
"base",
"htmlAttrs",
"bodyAttrs",
"meta",
"link",
"style",
"script",
"noscript"
]);
const UniqueTags = /* @__PURE__ */ new Set(["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs", "templateParams"]);
const TagConfigKeys = /* @__PURE__ */ new Set(["tagPosition", "tagPriority", "tagDuplicateStrategy", "children", "innerHTML", "textContent", "processTemplateParams"]);
const IsBrowser = typeof window !== "undefined";
const composableNames = [
"getActiveHead",
"useHead",
"useSeoMeta",
"useHeadSafe",
"useServerHead",
"useServerSeoMeta",
"useServerHeadSafe"
];
function defineHeadPlugin(plugin) {
return plugin;
}
function hashCode(s) {
let h = 9;
for (let i = 0; i < s.length; )
h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 8).toLowerCase();
}
function hashTag(tag) {
if (tag._h) {
return tag._h;
}
if (tag._d) {
return hashCode(tag._d);
}
let content = `${tag.tag}:${tag.textContent || tag.innerHTML || ""}:`;
for (const key in tag.props) {
content += `${key}:${String(tag.props[key])},`;
}
return hashCode(content);
}
const p = (p2) => ({ keyValue: p2, metaKey: "property" });
const k = (p2) => ({ keyValue: p2 });
const MetaPackingSchema = {
appleItunesApp: {
unpack: {
entrySeparator: ", ",
resolve({ key, value }) {
return `${fixKeyCase(key)}=${value}`;
}
}
},
articleExpirationTime: p("article:expiration_time"),
articleModifiedTime: p("article:modified_time"),
articlePublishedTime: p("article:published_time"),
bookReleaseDate: p("book:release_date"),
charset: {
metaKey: "charset"
},
contentSecurityPolicy: {
unpack: {
entrySeparator: "; ",
resolve({ key, value }) {
return `${fixKeyCase(key)} ${value}`;
}
},
metaKey: "http-equiv"
},
contentType: {
metaKey: "http-equiv"
},
defaultStyle: {
metaKey: "http-equiv"
},
fbAppId: p("fb:app_id"),
msapplicationConfig: k("msapplication-Config"),
msapplicationTileColor: k("msapplication-TileColor"),
msapplicationTileImage: k("msapplication-TileImage"),
ogAudioSecureUrl: p("og:audio:secure_url"),
ogAudioUrl: p("og:audio"),
ogImageSecureUrl: p("og:image:secure_url"),
ogImageUrl: p("og:image"),
ogSiteName: p("og:site_name"),
ogVideoSecureUrl: p("og:video:secure_url"),
ogVideoUrl: p("og:video"),
profileFirstName: p("profile:first_name"),
profileLastName: p("profile:last_name"),
profileUsername: p("profile:username"),
refresh: {
metaKey: "http-equiv",
unpack: {
entrySeparator: ";",
resolve({ key, value }) {
if (key === "seconds")
return `${value}`;
}
}
},
robots: {
unpack: {
entrySeparator: ", ",
resolve({ key, value }) {
if (typeof value === "boolean")
return `${fixKeyCase(key)}`;
else
return `${fixKeyCase(key)}:${value}`;
}
}
},
xUaCompatible: {
metaKey: "http-equiv"
}
};
const openGraphNamespaces = /* @__PURE__ */ new Set([
"og",
"book",
"article",
"profile"
]);
function resolveMetaKeyType(key) {
const fKey = fixKeyCase(key);
const prefixIndex = fKey.indexOf(":");
if (openGraphNamespaces.has(fKey.substring(0, prefixIndex)))
return "property";
return MetaPackingSchema[key]?.metaKey || "name";
}
function resolveMetaKeyValue(key) {
return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
}
function fixKeyCase(key) {
const updated = key.replace(/([A-Z])/g, "-$1").toLowerCase();
const prefixIndex = updated.indexOf("-");
const fKey = updated.substring(0, prefixIndex);
if (fKey === "twitter" || openGraphNamespaces.has(fKey))
return key.replace(/([A-Z])/g, ":$1").toLowerCase();
return updated;
}
function changeKeyCasingDeep(input) {
if (Array.isArray(input)) {
return input.map((entry) => changeKeyCasingDeep(entry));
}
if (typeof input !== "object" || Array.isArray(input))
return input;
const output = {};
for (const key in input) {
if (!Object.prototype.hasOwnProperty.call(input, key)) {
continue;
}
output[fixKeyCase(key)] = changeKeyCasingDeep(input[key]);
}
return output;
}
function resolvePackedMetaObjectValue(value, key) {
const definition = MetaPackingSchema[key];
if (key === "refresh")
return `${value.seconds};url=${value.url}`;
return unpackToString(
changeKeyCasingDeep(value),
{
keyValueSeparator: "=",
entrySeparator: ", ",
resolve({ value: value2, key: key2 }) {
if (value2 === null)
return "";
if (typeof value2 === "boolean")
return `${key2}`;
},
...definition?.unpack
}
);
}
const ObjectArrayEntries = /* @__PURE__ */ new Set(["og:image", "og:video", "og:audio", "twitter:image"]);
function sanitize(input) {
const out = {};
for (const k2 in input) {
if (!Object.prototype.hasOwnProperty.call(input, k2)) {
continue;
}
const v = input[k2];
if (String(v) !== "false" && k2)
out[k2] = v;
}
return out;
}
function handleObjectEntry(key, v) {
const value = sanitize(v);
const fKey = fixKeyCase(key);
const attr = resolveMetaKeyType(fKey);
if (ObjectArrayEntries.has(fKey)) {
const input = {};
for (const k2 in value) {
if (!Object.prototype.hasOwnProperty.call(value, k2)) {
continue;
}
input[`${key}${k2 === "url" ? "" : `${k2[0].toUpperCase()}${k2.slice(1)}`}`] = value[k2];
}
return unpackMeta(input).sort((a, b) => (a[attr]?.length || 0) - (b[attr]?.length || 0));
}
return [{ [attr]: fKey, ...value }];
}
function unpackMeta(input) {
const extras = [];
const primitives = {};
for (const key in input) {
if (!Object.prototype.hasOwnProperty.call(input, key)) {
continue;
}
const value = input[key];
if (!Array.isArray(value)) {
if (typeof value === "object" && value) {
if (ObjectArrayEntries.has(fixKeyCase(key))) {
extras.push(...handleObjectEntry(key, value));
continue;
}
primitives[key] = sanitize(value);
} else {
primitives[key] = value;
}
continue;
}
for (const v of value) {
extras.push(...typeof v === "string" ? unpackMeta({ [key]: v }) : handleObjectEntry(key, v));
}
}
const meta = unpackToArray(primitives, {
key({ key }) {
return resolveMetaKeyType(key);
},
value({ key }) {
return key === "charset" ? "charset" : "content";
},
resolveKeyData({ key }) {
return resolveMetaKeyValue(key);
},
resolveValueData({ value, key }) {
if (value === null)
return "_null";
if (typeof value === "object")
return resolvePackedMetaObjectValue(value, key);
return typeof value === "number" ? value.toString() : value;
}
});
return [...extras, ...meta].map((m) => {
if (m.content === "_null")
m.content = null;
return m;
});
}
function packMeta(inputs) {
const mappedPackingSchema = Object.entries(MetaPackingSchema).map(([key, value]) => [key, value.keyValue]);
return packArray(inputs, {
key: ["name", "property", "httpEquiv", "http-equiv", "charset"],
value: ["content", "charset"],
resolveKey(k2) {
let key = mappedPackingSchema.filter((sk) => sk[1] === k2)?.[0]?.[0] || k2;
const replacer = (_, letter) => letter?.toUpperCase();
key = key.replace(/:([a-z])/g, replacer).replace(/-([a-z])/g, replacer);
return key;
}
});
}
function thenable(val, thenFn) {
if (val instanceof Promise) {
return val.then(thenFn);
}
return thenFn(val);
}
function normaliseTag(tagName, input, e, normalizedProps) {
const props = normalizedProps || normaliseProps(
// explicitly check for an object
// @ts-expect-error untyped
typeof input === "object" && typeof input !== "function" && !(input instanceof Promise) ? { ...input } : { [tagName === "script" || tagName === "noscript" || tagName === "style" ? "innerHTML" : "textContent"]: input },
tagName === "templateParams" || tagName === "titleTemplate"
);
if (props instanceof Promise) {
return props.then((val) => normaliseTag(tagName, input, e, val));
}
const tag = {
tag: tagName,
props
};
for (const k of TagConfigKeys) {
const val = tag.props[k] !== void 0 ? tag.props[k] : e[k];
if (val !== void 0) {
if (!(k === "innerHTML" || k === "textContent" || k === "children") || TagsWithInnerContent.has(tag.tag)) {
tag[k === "children" ? "innerHTML" : k] = val;
}
delete tag.props[k];
}
}
if (tag.props.body) {
tag.tagPosition = "bodyClose";
delete tag.props.body;
}
if (tag.tag === "script") {
if (typeof tag.innerHTML === "object") {
tag.innerHTML = JSON.stringify(tag.innerHTML);
tag.props.type = tag.props.type || "application/json";
}
}
return Array.isArray(tag.props.content) ? tag.props.content.map((v) => ({ ...tag, props: { ...tag.props, content: v } })) : tag;
}
function normaliseStyleClassProps(key, v) {
const sep = key === "class" ? " " : ";";
if (v && typeof v === "object" && !Array.isArray(v)) {
v = Object.entries(v).filter(([, v2]) => v2).map(([k, v2]) => key === "style" ? `${k}:${v2}` : k);
}
return String(Array.isArray(v) ? v.join(sep) : v)?.split(sep).filter((c) => Boolean(c.trim())).join(sep);
}
function nestedNormaliseProps(props, virtual, keys, startIndex) {
for (let i = startIndex; i < keys.length; i += 1) {
const k = keys[i];
if (k === "class" || k === "style") {
props[k] = normaliseStyleClassProps(k, props[k]);
continue;
}
if (props[k] instanceof Promise) {
return props[k].then((val) => {
props[k] = val;
return nestedNormaliseProps(props, virtual, keys, i);
});
}
if (!virtual && !TagConfigKeys.has(k)) {
const v = String(props[k]);
const isDataKey = k.startsWith("data-");
if (v === "true" || v === "") {
props[k] = isDataKey ? "true" : true;
} else if (!props[k]) {
if (isDataKey && v === "false")
props[k] = "false";
else
delete props[k];
}
}
}
}
function normaliseProps(props, virtual = false) {
const resolvedProps = nestedNormaliseProps(props, virtual, Object.keys(props), 0);
if (resolvedProps instanceof Promise) {
return resolvedProps.then(() => props);
}
return props;
}
const TagEntityBits = 10;
function nestedNormaliseEntryTags(headTags, tagPromises, startIndex) {
for (let i = startIndex; i < tagPromises.length; i += 1) {
const tags = tagPromises[i];
if (tags instanceof Promise) {
return tags.then((val) => {
tagPromises[i] = val;
return nestedNormaliseEntryTags(headTags, tagPromises, i);
});
}
if (Array.isArray(tags)) {
headTags.push(...tags);
} else {
headTags.push(tags);
}
}
}
function normaliseEntryTags(e) {
const tagPromises = [];
const input = e.resolvedInput;
for (const k in input) {
if (!Object.prototype.hasOwnProperty.call(input, k)) {
continue;
}
const v = input[k];
if (v === void 0 || !ValidHeadTags.has(k)) {
continue;
}
if (Array.isArray(v)) {
for (const props of v) {
tagPromises.push(normaliseTag(k, props, e));
}
continue;
}
tagPromises.push(normaliseTag(k, v, e));
}
if (tagPromises.length === 0) {
return [];
}
const headTags = [];
return thenable(nestedNormaliseEntryTags(headTags, tagPromises, 0), () => headTags.map((t, i) => {
t._e = e._i;
e.mode && (t._m = e.mode);
t._p = (e._i << TagEntityBits) + i;
return t;
}));
}
const WhitelistAttributes = {
htmlAttrs: ["id", "class", "lang", "dir"],
bodyAttrs: ["id", "class"],
meta: ["id", "name", "property", "charset", "content"],
noscript: ["id", "textContent"],
script: ["id", "type", "textContent"],
link: ["id", "color", "crossorigin", "fetchpriority", "href", "hreflang", "imagesrcset", "imagesizes", "integrity", "media", "referrerpolicy", "rel", "sizes", "type"]
};
function acceptDataAttrs(value) {
const filtered = {};
Object.keys(value || {}).filter((a) => a.startsWith("data-")).forEach((a) => {
filtered[a] = value[a];
});
return filtered;
}
function whitelistSafeInput(input) {
const filtered = {};
Object.keys(input).forEach((key) => {
const tagValue = input[key];
if (!tagValue)
return;
switch (key) {
// always safe
case "title":
case "titleTemplate":
case "templateParams":
filtered[key] = tagValue;
break;
case "htmlAttrs":
case "bodyAttrs":
filtered[key] = acceptDataAttrs(tagValue);
WhitelistAttributes[key].forEach((a) => {
if (tagValue[a])
filtered[key][a] = tagValue[a];
});
break;
// meta is safe, except for http-equiv
case "meta":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const safeMeta = acceptDataAttrs(meta);
WhitelistAttributes.meta.forEach((key2) => {
if (meta[key2])
safeMeta[key2] = meta[key2];
});
return safeMeta;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
// link tags we don't allow stylesheets, scripts, preloading, prerendering, prefetching, etc
case "link":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const link = acceptDataAttrs(meta);
WhitelistAttributes.link.forEach((key2) => {
const val = meta[key2];
if (key2 === "rel" && (val === "stylesheet" || val === "canonical" || val === "modulepreload" || val === "prerender" || val === "preload" || val === "prefetch"))
return;
if (key2 === "href") {
if (val.includes("javascript:") || val.includes("data:"))
return;
link[key2] = val;
} else if (val) {
link[key2] = val;
}
});
return link;
}).filter((link) => Object.keys(link).length > 1 && !!link.rel);
}
break;
case "noscript":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((meta) => {
const noscript = acceptDataAttrs(meta);
WhitelistAttributes.noscript.forEach((key2) => {
if (meta[key2])
noscript[key2] = meta[key2];
});
return noscript;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
// we only allow JSON in scripts
case "script":
if (Array.isArray(tagValue)) {
filtered[key] = tagValue.map((script) => {
const safeScript = acceptDataAttrs(script);
WhitelistAttributes.script.forEach((s) => {
if (script[s]) {
if (s === "textContent") {
try {
const jsonVal = typeof script[s] === "string" ? JSON.parse(script[s]) : script[s];
safeScript[s] = JSON.stringify(jsonVal, null, 0);
} catch (e) {
}
} else {
safeScript[s] = script[s];
}
}
});
return safeScript;
}).filter((meta) => Object.keys(meta).length > 0);
}
break;
}
});
return filtered;
}
const NetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror", "onabort", "onprogress", "onloadstart"]);
const ScriptNetworkEvents = /* @__PURE__ */ new Set(["onload", "onerror"]);
const TAG_WEIGHTS = {
// tags
base: -10,
title: 10
};
const TAG_ALIASES = {
// relative scores to their default values
critical: -80,
high: -10,
low: 20
};
function tagWeight(tag) {
const priority = tag.tagPriority;
if (typeof priority === "number")
return priority;
let weight = 100;
if (tag.tag === "meta") {
if (tag.props["http-equiv"] === "content-security-policy")
weight = -30;
else if (tag.props.charset)
weight = -20;
else if (tag.props.name === "viewport")
weight = -15;
} else if (tag.tag === "link" && tag.props.rel === "preconnect") {
weight = 20;
} else if (tag.tag in TAG_WEIGHTS) {
weight = TAG_WEIGHTS[tag.tag];
}
if (priority && priority in TAG_ALIASES) {
return weight + TAG_ALIASES[priority];
}
return weight;
}
const SortModifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
const allowedMetaProperties = ["name", "property", "http-equiv"];
function tagDedupeKey(tag) {
const { props, tag: tagName } = tag;
if (UniqueTags.has(tagName))
return tagName;
if (tagName === "link" && props.rel === "canonical")
return "canonical";
if (props.charset)
return "charset";
if (props.id) {
return `${tagName}:id:${props.id}`;
}
for (const n of allowedMetaProperties) {
if (props[n] !== void 0) {
return `${tagName}:${n}:${props[n]}`;
}
}
return false;
}
const sepSub = "%separator";
function sub(p, token, isJson = false) {
let val;
if (token === "s" || token === "pageTitle") {
val = p.pageTitle;
} else if (token.includes(".")) {
const dotIndex = token.indexOf(".");
val = p[token.substring(0, dotIndex)]?.[token.substring(dotIndex + 1)];
} else {
val = p[token];
}
if (val !== void 0) {
return isJson ? (val || "").replace(/"/g, '\\"') : val || "";
}
return void 0;
}
const sepSubRe = new RegExp(`${sepSub}(?:\\s*${sepSub})*`, "g");
function processTemplateParams(s, p, sep, isJson = false) {
if (typeof s !== "string" || !s.includes("%"))
return s;
let decoded = s;
try {
decoded = decodeURI(s);
} catch {
}
const tokens = decoded.match(/%\w+(?:\.\w+)?/g);
if (!tokens) {
return s;
}
const hasSepSub = s.includes(sepSub);
s = s.replace(/%\w+(?:\.\w+)?/g, (token) => {
if (token === sepSub || !tokens.includes(token)) {
return token;
}
const re = sub(p, token.slice(1), isJson);
return re !== void 0 ? re : token;
}).trim();
if (hasSepSub) {
if (s.endsWith(sepSub))
s = s.slice(0, -sepSub.length);
if (s.startsWith(sepSub))
s = s.slice(sepSub.length);
s = s.replace(sepSubRe, sep).trim();
}
return s;
}
function resolveTitleTemplate(template, title) {
if (template == null)
return title || null;
if (typeof template === "function")
return template(title);
return template;
}
export { HasElementTags, IsBrowser, NetworkEvents, ScriptNetworkEvents, SelfClosingTags, SortModifiers, TAG_ALIASES, TAG_WEIGHTS, TagConfigKeys, TagEntityBits, TagsWithInnerContent, UniqueTags, ValidHeadTags, asArray, composableNames, defineHeadPlugin, hashCode, hashTag, normaliseEntryTags, normaliseProps, normaliseStyleClassProps, normaliseTag, packMeta, processTemplateParams, resolveMetaKeyType, resolveMetaKeyValue, resolvePackedMetaObjectValue, resolveTitleTemplate, tagDedupeKey, tagWeight, thenable, unpackMeta, whitelistSafeInput };
+45
View File
@@ -0,0 +1,45 @@
{
"name": "@unhead/shared",
"type": "module",
"version": "1.11.20",
"author": "Harlan Wilton <harlan@harlanzw.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/harlan-zw",
"homepage": "https://unhead.unjs.io",
"repository": {
"type": "git",
"url": "git+https://github.com/unjs/unhead.git",
"directory": "packages/schema"
},
"bugs": {
"url": "https://github.com/unjs/unhead/issues"
},
"keywords": [
"head",
"meta tags",
"types"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"dependencies": {
"packrup": "^0.1.2",
"@unhead/schema": "1.11.20"
},
"scripts": {
"build": "unbuild .",
"stub": "unbuild . --stub",
"export:sizes": "npx export-size . -r"
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) Harlan Wilton <harlan@harlanzw.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+74
View File
@@ -0,0 +1,74 @@
'use strict';
const vue = require('vue');
const injectHead = require('./shared/vue.DWlmwWrc.cjs');
require('unhead');
require('@unhead/shared');
function addVNodeToHeadObj(node, obj) {
const nodeType = !injectHead.Vue3 ? node.tag : node.type;
const type = nodeType === "html" ? "htmlAttrs" : nodeType === "body" ? "bodyAttrs" : nodeType;
if (typeof type !== "string" || !(type in obj))
return;
const nodeData = !injectHead.Vue3 ? node.data : node;
const props = (!injectHead.Vue3 ? nodeData.attrs : node.props) || {};
if (!injectHead.Vue3) {
if (nodeData.staticClass)
props.class = nodeData.staticClass;
if (nodeData.staticStyle)
props.style = Object.entries(nodeData.staticStyle).map(([key, value]) => `${key}:${value}`).join(";");
}
if (node.children) {
const childrenAttr = !injectHead.Vue3 ? "text" : "children";
props.children = Array.isArray(node.children) ? node.children[0][childrenAttr] : node[childrenAttr];
}
if (Array.isArray(obj[type]))
obj[type].push(props);
else if (type === "title")
obj.title = props.children;
else
obj[type] = props;
}
function vnodesToHeadObj(nodes) {
const obj = {
title: void 0,
htmlAttrs: void 0,
bodyAttrs: void 0,
base: void 0,
meta: [],
link: [],
style: [],
script: [],
noscript: []
};
for (const node of nodes) {
if (typeof node.type === "symbol" && Array.isArray(node.children)) {
for (const childNode of node.children)
addVNodeToHeadObj(childNode, obj);
} else {
addVNodeToHeadObj(node, obj);
}
}
return obj;
}
const Head = /* @__PURE__ */ vue.defineComponent({
name: "Head",
setup(_, { slots }) {
const head = injectHead.injectHead();
const obj = vue.ref({});
const entry = head.push(obj);
vue.onBeforeUnmount(() => {
entry.dispose();
});
return () => {
vue.watchEffect(() => {
if (!slots.default)
return;
entry.patch(vnodesToHeadObj(slots.default()));
});
return null;
};
}
});
exports.Head = Head;
+5
View File
@@ -0,0 +1,5 @@
import { DefineComponent } from 'vue';
declare const Head: DefineComponent;
export { Head };
+5
View File
@@ -0,0 +1,5 @@
import { DefineComponent } from 'vue';
declare const Head: DefineComponent;
export { Head };
+5
View File
@@ -0,0 +1,5 @@
import { DefineComponent } from 'vue';
declare const Head: DefineComponent;
export { Head };
+72
View File
@@ -0,0 +1,72 @@
import { defineComponent, ref, onBeforeUnmount, watchEffect } from 'vue';
import { i as injectHead, V as Vue3 } from './shared/vue.ziyDaVMR.mjs';
import 'unhead';
import '@unhead/shared';
function addVNodeToHeadObj(node, obj) {
const nodeType = !Vue3 ? node.tag : node.type;
const type = nodeType === "html" ? "htmlAttrs" : nodeType === "body" ? "bodyAttrs" : nodeType;
if (typeof type !== "string" || !(type in obj))
return;
const nodeData = !Vue3 ? node.data : node;
const props = (!Vue3 ? nodeData.attrs : node.props) || {};
if (!Vue3) {
if (nodeData.staticClass)
props.class = nodeData.staticClass;
if (nodeData.staticStyle)
props.style = Object.entries(nodeData.staticStyle).map(([key, value]) => `${key}:${value}`).join(";");
}
if (node.children) {
const childrenAttr = !Vue3 ? "text" : "children";
props.children = Array.isArray(node.children) ? node.children[0][childrenAttr] : node[childrenAttr];
}
if (Array.isArray(obj[type]))
obj[type].push(props);
else if (type === "title")
obj.title = props.children;
else
obj[type] = props;
}
function vnodesToHeadObj(nodes) {
const obj = {
title: void 0,
htmlAttrs: void 0,
bodyAttrs: void 0,
base: void 0,
meta: [],
link: [],
style: [],
script: [],
noscript: []
};
for (const node of nodes) {
if (typeof node.type === "symbol" && Array.isArray(node.children)) {
for (const childNode of node.children)
addVNodeToHeadObj(childNode, obj);
} else {
addVNodeToHeadObj(node, obj);
}
}
return obj;
}
const Head = /* @__PURE__ */ defineComponent({
name: "Head",
setup(_, { slots }) {
const head = injectHead();
const obj = ref({});
const entry = head.push(obj);
onBeforeUnmount(() => {
entry.dispose();
});
return () => {
watchEffect(() => {
if (!slots.default)
return;
entry.patch(vnodesToHeadObj(slots.default()));
});
return null;
};
}
});
export { Head };
+179
View File
@@ -0,0 +1,179 @@
'use strict';
const unhead = require('unhead');
const injectHead = require('./shared/vue.DWlmwWrc.cjs');
const shared = require('@unhead/shared');
const useHead = require('./shared/vue.BmMjB48i.cjs');
const vue = require('vue');
const coreComposableNames = [
"injectHead"
];
const unheadVueComposablesImports = {
"@unhead/vue": [...coreComposableNames, ...shared.composableNames]
};
function useHeadSafe(input, options = {}) {
return useHead.useHead(input, { ...options, transform: shared.whitelistSafeInput });
}
function registerVueScopeHandlers(script, scope) {
if (!scope) {
return;
}
const _registerCb = (key, cb) => {
if (!script._cbs[key]) {
cb(script.instance);
return () => {
};
}
let i = script._cbs[key].push(cb);
const destroy = () => {
if (i) {
script._cbs[key]?.splice(i - 1, 1);
i = null;
}
};
vue.onScopeDispose(destroy);
return destroy;
};
script.onLoaded = (cb) => _registerCb("loaded", cb);
script.onError = (cb) => _registerCb("error", cb);
vue.onScopeDispose(() => {
script._triggerAbortController?.abort();
});
}
function useScript(_input, _options) {
const input = typeof _input === "string" ? { src: _input } : _input;
const options = _options || {};
const head = options?.head || injectHead.injectHead();
options.head = head;
const scope = vue.getCurrentInstance();
options.eventContext = scope;
if (scope && typeof options.trigger === "undefined") {
options.trigger = vue.onMounted;
} else if (vue.isRef(options.trigger)) {
const refTrigger = options.trigger;
let off;
options.trigger = new Promise((resolve) => {
off = vue.watch(refTrigger, (val) => {
if (val) {
resolve(true);
}
}, {
immediate: true
});
vue.onScopeDispose(() => resolve(false), true);
}).then((val) => {
off?.();
return val;
});
}
head._scriptStatusWatcher = head._scriptStatusWatcher || head.hooks.hook("script:updated", ({ script: s }) => {
s._statusRef.value = s.status;
});
const script = unhead.useScript(input, options);
script._statusRef = script._statusRef || vue.ref(script.status);
registerVueScopeHandlers(script, scope);
return new Proxy(script, {
get(_, key, a) {
return Reflect.get(_, key === "status" ? "_statusRef" : key, a);
}
});
}
function useSeoMeta(input, options) {
const { title, titleTemplate, ...meta } = input;
return useHead.useHead({
title,
titleTemplate,
// @ts-expect-error runtime type
_flatMeta: meta
}, {
...options,
transform(t) {
const meta2 = shared.unpackMeta({ ...t._flatMeta });
delete t._flatMeta;
return {
// @ts-expect-error runtime type
...t,
meta: meta2
};
}
});
}
function useServerHead(input, options = {}) {
const head = options.head || injectHead.injectHead();
delete options.head;
if (head)
return head.push(input, { ...options, mode: "server" });
}
function useServerHeadSafe(input, options = {}) {
return useHeadSafe(input, { ...options, mode: "server" });
}
function useServerSeoMeta(input, options) {
return useSeoMeta(input, { ...options, mode: "server" });
}
const Vue2ProvideUnheadPlugin = (_Vue, head) => {
_Vue.mixin({
beforeCreate() {
const options = this.$options;
const origProvide = options.provide;
options.provide = function() {
let origProvideResult;
if (typeof origProvide === "function")
origProvideResult = origProvide.call(this);
else
origProvideResult = origProvide || {};
return {
...origProvideResult,
[injectHead.headSymbol]: head
};
};
}
});
};
const VueHeadMixin = {
created() {
let source = false;
if (injectHead.Vue3) {
const instance = vue.getCurrentInstance();
if (!instance)
return;
const options = instance.type;
if (!options || !("head" in options))
return;
source = typeof options.head === "function" ? () => options.head.call(instance.proxy) : options.head;
} else {
const head = this.$options.head;
if (head) {
source = typeof head === "function" ? () => head.call(this) : head;
}
}
source && useHead.useHead(source);
}
};
exports.CapoPlugin = unhead.CapoPlugin;
exports.HashHydrationPlugin = unhead.HashHydrationPlugin;
exports.createHeadCore = unhead.createHeadCore;
exports.createHead = injectHead.createHead;
exports.createServerHead = injectHead.createServerHead;
exports.injectHead = injectHead.injectHead;
exports.resolveUnrefHeadInput = injectHead.resolveUnrefHeadInput;
exports.setHeadInjectionHandler = injectHead.setHeadInjectionHandler;
exports.useHead = useHead.useHead;
exports.Vue2ProvideUnheadPlugin = Vue2ProvideUnheadPlugin;
exports.VueHeadMixin = VueHeadMixin;
exports.unheadVueComposablesImports = unheadVueComposablesImports;
exports.useHeadSafe = useHeadSafe;
exports.useScript = useScript;
exports.useSeoMeta = useSeoMeta;
exports.useServerHead = useServerHead;
exports.useServerHeadSafe = useServerHeadSafe;
exports.useServerSeoMeta = useServerSeoMeta;
+78
View File
@@ -0,0 +1,78 @@
export { CapoPlugin, HashHydrationPlugin, createHeadCore } from 'unhead';
import * as _unhead_schema from '@unhead/schema';
import { SafeMeta, SafeLink, SafeNoscript, SafeScript, SafeHtmlAttr, SafeBodyAttr, MergeHead, CreateHeadOptions, ActiveHeadEntry, ScriptInstance, UseScriptStatus, ScriptBase, DataKeys, SchemaAugmentations, HeadEntryOptions, UseScriptOptions as UseScriptOptions$1, AsAsyncFunctionValues, UseFunctionType } from '@unhead/schema';
export { ActiveHeadEntry, Head, HeadEntryOptions, HeadTag, MergeHead, Unhead } from '@unhead/schema';
import { R as ReactiveHead, M as MaybeComputedRefEntries, a as MaybeComputedRef, V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions, c as MaybeComputedRefEntriesOnly, d as UseSeoMetaInput } from './shared/vue.fwis0K4Q.cjs';
export { f as Base, B as BodyAttr, j as BodyAttributes, H as HtmlAttr, i as HtmlAttributes, L as Link, l as MaybeComputedRefOrFalsy, m as MaybeComputedRefOrPromise, k as MaybeReadonlyRef, g as Meta, N as Noscript, h as Script, S as Style, T as Title, e as TitleTemplate } from './shared/vue.fwis0K4Q.cjs';
import { Ref, Plugin } from 'vue';
interface HeadSafe extends Pick<ReactiveHead, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: MaybeComputedRefEntries<SafeMeta>[];
link?: MaybeComputedRefEntries<SafeLink>[];
noscript?: MaybeComputedRefEntries<SafeNoscript>[];
script?: MaybeComputedRefEntries<SafeScript>[];
htmlAttrs?: MaybeComputedRefEntries<SafeHtmlAttr>;
bodyAttrs?: MaybeComputedRefEntries<SafeBodyAttr>;
}
type UseHeadSafeInput = MaybeComputedRef<HeadSafe>;
declare function createServerHead<T extends MergeHead>(options?: Omit<CreateHeadOptions, 'domDelayFn' | 'document'>): VueHeadClient<T>;
declare function createHead<T extends MergeHead>(options?: CreateHeadOptions): VueHeadClient<T>;
declare function resolveUnrefHeadInput(ref: any): any;
declare const unheadVueComposablesImports: {
'@unhead/vue': string[];
};
declare function setHeadInjectionHandler(handler: () => VueHeadClient<any> | undefined): void;
declare function injectHead<T extends MergeHead>(): VueHeadClient<T>;
declare function useHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): ActiveHeadEntry<UseHeadInput<T>> | void;
declare function useHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): ActiveHeadEntry<UseHeadSafeInput> | void;
interface VueScriptInstance<T extends Record<symbol | string, any>> extends Omit<ScriptInstance<T>, 'status'> {
status: Ref<UseScriptStatus>;
}
type UseScriptInput = string | (MaybeComputedRefEntriesOnly<Omit<ScriptBase & DataKeys & SchemaAugmentations['script'], 'src'>> & {
src: string;
});
interface UseScriptOptions<T extends Record<symbol | string, any> = {}, U = {}> extends HeadEntryOptions, Pick<UseScriptOptions$1<T, U>, 'use' | 'stub' | 'eventContext' | 'beforeInit'> {
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
* - `ref` - Load the script when the ref is true.
*/
trigger?: UseScriptOptions$1['trigger'] | Ref<boolean>;
}
type UseScriptContext<T extends Record<symbol | string, any>> = (Promise<T> & VueScriptInstance<T>) & AsAsyncFunctionValues<T> & {
/**
* @deprecated Use top-level functions instead.
*/
$script: Promise<T> & VueScriptInstance<T>;
};
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>>;
declare function useSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
declare function useServerHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): _unhead_schema.ActiveHeadEntry<MaybeComputedRef<ReactiveHead<any>>> | undefined;
declare function useServerHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): void | _unhead_schema.ActiveHeadEntry<UseHeadSafeInput>;
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
/**
* @deprecated Import { UnheadPlugin } from `@unhead/vue/vue2` and use Vue.mixin(UnheadPlugin(head)) instead.
*/
declare const Vue2ProvideUnheadPlugin: Plugin;
declare const VueHeadMixin: {
created(): void;
};
export { type HeadSafe, MaybeComputedRef, MaybeComputedRefEntries, MaybeComputedRefEntriesOnly, ReactiveHead, UseHeadInput, UseHeadOptions, type UseHeadSafeInput, type UseScriptContext, type UseScriptInput, type UseScriptOptions, UseSeoMetaInput, Vue2ProvideUnheadPlugin, VueHeadClient, VueHeadMixin, type VueScriptInstance, createHead, createServerHead, injectHead, resolveUnrefHeadInput, setHeadInjectionHandler, unheadVueComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
+78
View File
@@ -0,0 +1,78 @@
export { CapoPlugin, HashHydrationPlugin, createHeadCore } from 'unhead';
import * as _unhead_schema from '@unhead/schema';
import { SafeMeta, SafeLink, SafeNoscript, SafeScript, SafeHtmlAttr, SafeBodyAttr, MergeHead, CreateHeadOptions, ActiveHeadEntry, ScriptInstance, UseScriptStatus, ScriptBase, DataKeys, SchemaAugmentations, HeadEntryOptions, UseScriptOptions as UseScriptOptions$1, AsAsyncFunctionValues, UseFunctionType } from '@unhead/schema';
export { ActiveHeadEntry, Head, HeadEntryOptions, HeadTag, MergeHead, Unhead } from '@unhead/schema';
import { R as ReactiveHead, M as MaybeComputedRefEntries, a as MaybeComputedRef, V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions, c as MaybeComputedRefEntriesOnly, d as UseSeoMetaInput } from './shared/vue.fwis0K4Q.mjs';
export { f as Base, B as BodyAttr, j as BodyAttributes, H as HtmlAttr, i as HtmlAttributes, L as Link, l as MaybeComputedRefOrFalsy, m as MaybeComputedRefOrPromise, k as MaybeReadonlyRef, g as Meta, N as Noscript, h as Script, S as Style, T as Title, e as TitleTemplate } from './shared/vue.fwis0K4Q.mjs';
import { Ref, Plugin } from 'vue';
interface HeadSafe extends Pick<ReactiveHead, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: MaybeComputedRefEntries<SafeMeta>[];
link?: MaybeComputedRefEntries<SafeLink>[];
noscript?: MaybeComputedRefEntries<SafeNoscript>[];
script?: MaybeComputedRefEntries<SafeScript>[];
htmlAttrs?: MaybeComputedRefEntries<SafeHtmlAttr>;
bodyAttrs?: MaybeComputedRefEntries<SafeBodyAttr>;
}
type UseHeadSafeInput = MaybeComputedRef<HeadSafe>;
declare function createServerHead<T extends MergeHead>(options?: Omit<CreateHeadOptions, 'domDelayFn' | 'document'>): VueHeadClient<T>;
declare function createHead<T extends MergeHead>(options?: CreateHeadOptions): VueHeadClient<T>;
declare function resolveUnrefHeadInput(ref: any): any;
declare const unheadVueComposablesImports: {
'@unhead/vue': string[];
};
declare function setHeadInjectionHandler(handler: () => VueHeadClient<any> | undefined): void;
declare function injectHead<T extends MergeHead>(): VueHeadClient<T>;
declare function useHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): ActiveHeadEntry<UseHeadInput<T>> | void;
declare function useHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): ActiveHeadEntry<UseHeadSafeInput> | void;
interface VueScriptInstance<T extends Record<symbol | string, any>> extends Omit<ScriptInstance<T>, 'status'> {
status: Ref<UseScriptStatus>;
}
type UseScriptInput = string | (MaybeComputedRefEntriesOnly<Omit<ScriptBase & DataKeys & SchemaAugmentations['script'], 'src'>> & {
src: string;
});
interface UseScriptOptions<T extends Record<symbol | string, any> = {}, U = {}> extends HeadEntryOptions, Pick<UseScriptOptions$1<T, U>, 'use' | 'stub' | 'eventContext' | 'beforeInit'> {
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
* - `ref` - Load the script when the ref is true.
*/
trigger?: UseScriptOptions$1['trigger'] | Ref<boolean>;
}
type UseScriptContext<T extends Record<symbol | string, any>> = (Promise<T> & VueScriptInstance<T>) & AsAsyncFunctionValues<T> & {
/**
* @deprecated Use top-level functions instead.
*/
$script: Promise<T> & VueScriptInstance<T>;
};
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>>;
declare function useSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
declare function useServerHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): _unhead_schema.ActiveHeadEntry<MaybeComputedRef<ReactiveHead<any>>> | undefined;
declare function useServerHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): void | _unhead_schema.ActiveHeadEntry<UseHeadSafeInput>;
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
/**
* @deprecated Import { UnheadPlugin } from `@unhead/vue/vue2` and use Vue.mixin(UnheadPlugin(head)) instead.
*/
declare const Vue2ProvideUnheadPlugin: Plugin;
declare const VueHeadMixin: {
created(): void;
};
export { type HeadSafe, MaybeComputedRef, MaybeComputedRefEntries, MaybeComputedRefEntriesOnly, ReactiveHead, UseHeadInput, UseHeadOptions, type UseHeadSafeInput, type UseScriptContext, type UseScriptInput, type UseScriptOptions, UseSeoMetaInput, Vue2ProvideUnheadPlugin, VueHeadClient, VueHeadMixin, type VueScriptInstance, createHead, createServerHead, injectHead, resolveUnrefHeadInput, setHeadInjectionHandler, unheadVueComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
+78
View File
@@ -0,0 +1,78 @@
export { CapoPlugin, HashHydrationPlugin, createHeadCore } from 'unhead';
import * as _unhead_schema from '@unhead/schema';
import { SafeMeta, SafeLink, SafeNoscript, SafeScript, SafeHtmlAttr, SafeBodyAttr, MergeHead, CreateHeadOptions, ActiveHeadEntry, ScriptInstance, UseScriptStatus, ScriptBase, DataKeys, SchemaAugmentations, HeadEntryOptions, UseScriptOptions as UseScriptOptions$1, AsAsyncFunctionValues, UseFunctionType } from '@unhead/schema';
export { ActiveHeadEntry, Head, HeadEntryOptions, HeadTag, MergeHead, Unhead } from '@unhead/schema';
import { R as ReactiveHead, M as MaybeComputedRefEntries, a as MaybeComputedRef, V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions, c as MaybeComputedRefEntriesOnly, d as UseSeoMetaInput } from './shared/vue.fwis0K4Q.js';
export { f as Base, B as BodyAttr, j as BodyAttributes, H as HtmlAttr, i as HtmlAttributes, L as Link, l as MaybeComputedRefOrFalsy, m as MaybeComputedRefOrPromise, k as MaybeReadonlyRef, g as Meta, N as Noscript, h as Script, S as Style, T as Title, e as TitleTemplate } from './shared/vue.fwis0K4Q.js';
import { Ref, Plugin } from 'vue';
interface HeadSafe extends Pick<ReactiveHead, 'title' | 'titleTemplate' | 'templateParams'> {
meta?: MaybeComputedRefEntries<SafeMeta>[];
link?: MaybeComputedRefEntries<SafeLink>[];
noscript?: MaybeComputedRefEntries<SafeNoscript>[];
script?: MaybeComputedRefEntries<SafeScript>[];
htmlAttrs?: MaybeComputedRefEntries<SafeHtmlAttr>;
bodyAttrs?: MaybeComputedRefEntries<SafeBodyAttr>;
}
type UseHeadSafeInput = MaybeComputedRef<HeadSafe>;
declare function createServerHead<T extends MergeHead>(options?: Omit<CreateHeadOptions, 'domDelayFn' | 'document'>): VueHeadClient<T>;
declare function createHead<T extends MergeHead>(options?: CreateHeadOptions): VueHeadClient<T>;
declare function resolveUnrefHeadInput(ref: any): any;
declare const unheadVueComposablesImports: {
'@unhead/vue': string[];
};
declare function setHeadInjectionHandler(handler: () => VueHeadClient<any> | undefined): void;
declare function injectHead<T extends MergeHead>(): VueHeadClient<T>;
declare function useHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): ActiveHeadEntry<UseHeadInput<T>> | void;
declare function useHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): ActiveHeadEntry<UseHeadSafeInput> | void;
interface VueScriptInstance<T extends Record<symbol | string, any>> extends Omit<ScriptInstance<T>, 'status'> {
status: Ref<UseScriptStatus>;
}
type UseScriptInput = string | (MaybeComputedRefEntriesOnly<Omit<ScriptBase & DataKeys & SchemaAugmentations['script'], 'src'>> & {
src: string;
});
interface UseScriptOptions<T extends Record<symbol | string, any> = {}, U = {}> extends HeadEntryOptions, Pick<UseScriptOptions$1<T, U>, 'use' | 'stub' | 'eventContext' | 'beforeInit'> {
/**
* The trigger to load the script:
* - `undefined` | `client` - (Default) Load the script on the client when this js is loaded.
* - `manual` - Load the script manually by calling `$script.load()`, exists only on the client.
* - `Promise` - Load the script when the promise resolves, exists only on the client.
* - `Function` - Register a callback function to load the script, exists only on the client.
* - `server` - Have the script injected on the server.
* - `ref` - Load the script when the ref is true.
*/
trigger?: UseScriptOptions$1['trigger'] | Ref<boolean>;
}
type UseScriptContext<T extends Record<symbol | string, any>> = (Promise<T> & VueScriptInstance<T>) & AsAsyncFunctionValues<T> & {
/**
* @deprecated Use top-level functions instead.
*/
$script: Promise<T> & VueScriptInstance<T>;
};
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>>;
declare function useSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
declare function useServerHead<T extends MergeHead>(input: UseHeadInput<T>, options?: UseHeadOptions): _unhead_schema.ActiveHeadEntry<MaybeComputedRef<ReactiveHead<any>>> | undefined;
declare function useServerHeadSafe(input: UseHeadSafeInput, options?: UseHeadOptions): void | _unhead_schema.ActiveHeadEntry<UseHeadSafeInput>;
declare function useServerSeoMeta(input: UseSeoMetaInput, options?: UseHeadOptions): ActiveHeadEntry<any> | void;
/**
* @deprecated Import { UnheadPlugin } from `@unhead/vue/vue2` and use Vue.mixin(UnheadPlugin(head)) instead.
*/
declare const Vue2ProvideUnheadPlugin: Plugin;
declare const VueHeadMixin: {
created(): void;
};
export { type HeadSafe, MaybeComputedRef, MaybeComputedRefEntries, MaybeComputedRefEntriesOnly, ReactiveHead, UseHeadInput, UseHeadOptions, type UseHeadSafeInput, type UseScriptContext, type UseScriptInput, type UseScriptOptions, UseSeoMetaInput, Vue2ProvideUnheadPlugin, VueHeadClient, VueHeadMixin, type VueScriptInstance, createHead, createServerHead, injectHead, resolveUnrefHeadInput, setHeadInjectionHandler, unheadVueComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
+162
View File
@@ -0,0 +1,162 @@
import { useScript as useScript$1 } from 'unhead';
export { CapoPlugin, HashHydrationPlugin, createHeadCore } from 'unhead';
import { i as injectHead, h as headSymbol, V as Vue3 } from './shared/vue.ziyDaVMR.mjs';
export { c as createHead, a as createServerHead, r as resolveUnrefHeadInput, s as setHeadInjectionHandler } from './shared/vue.ziyDaVMR.mjs';
import { composableNames, whitelistSafeInput, unpackMeta } from '@unhead/shared';
import { u as useHead } from './shared/vue.-sixQ7xP.mjs';
import { getCurrentInstance, onMounted, isRef, watch, onScopeDispose, ref } from 'vue';
const coreComposableNames = [
"injectHead"
];
const unheadVueComposablesImports = {
"@unhead/vue": [...coreComposableNames, ...composableNames]
};
function useHeadSafe(input, options = {}) {
return useHead(input, { ...options, transform: whitelistSafeInput });
}
function registerVueScopeHandlers(script, scope) {
if (!scope) {
return;
}
const _registerCb = (key, cb) => {
if (!script._cbs[key]) {
cb(script.instance);
return () => {
};
}
let i = script._cbs[key].push(cb);
const destroy = () => {
if (i) {
script._cbs[key]?.splice(i - 1, 1);
i = null;
}
};
onScopeDispose(destroy);
return destroy;
};
script.onLoaded = (cb) => _registerCb("loaded", cb);
script.onError = (cb) => _registerCb("error", cb);
onScopeDispose(() => {
script._triggerAbortController?.abort();
});
}
function useScript(_input, _options) {
const input = typeof _input === "string" ? { src: _input } : _input;
const options = _options || {};
const head = options?.head || injectHead();
options.head = head;
const scope = getCurrentInstance();
options.eventContext = scope;
if (scope && typeof options.trigger === "undefined") {
options.trigger = onMounted;
} else if (isRef(options.trigger)) {
const refTrigger = options.trigger;
let off;
options.trigger = new Promise((resolve) => {
off = watch(refTrigger, (val) => {
if (val) {
resolve(true);
}
}, {
immediate: true
});
onScopeDispose(() => resolve(false), true);
}).then((val) => {
off?.();
return val;
});
}
head._scriptStatusWatcher = head._scriptStatusWatcher || head.hooks.hook("script:updated", ({ script: s }) => {
s._statusRef.value = s.status;
});
const script = useScript$1(input, options);
script._statusRef = script._statusRef || ref(script.status);
registerVueScopeHandlers(script, scope);
return new Proxy(script, {
get(_, key, a) {
return Reflect.get(_, key === "status" ? "_statusRef" : key, a);
}
});
}
function useSeoMeta(input, options) {
const { title, titleTemplate, ...meta } = input;
return useHead({
title,
titleTemplate,
// @ts-expect-error runtime type
_flatMeta: meta
}, {
...options,
transform(t) {
const meta2 = unpackMeta({ ...t._flatMeta });
delete t._flatMeta;
return {
// @ts-expect-error runtime type
...t,
meta: meta2
};
}
});
}
function useServerHead(input, options = {}) {
const head = options.head || injectHead();
delete options.head;
if (head)
return head.push(input, { ...options, mode: "server" });
}
function useServerHeadSafe(input, options = {}) {
return useHeadSafe(input, { ...options, mode: "server" });
}
function useServerSeoMeta(input, options) {
return useSeoMeta(input, { ...options, mode: "server" });
}
const Vue2ProvideUnheadPlugin = (_Vue, head) => {
_Vue.mixin({
beforeCreate() {
const options = this.$options;
const origProvide = options.provide;
options.provide = function() {
let origProvideResult;
if (typeof origProvide === "function")
origProvideResult = origProvide.call(this);
else
origProvideResult = origProvide || {};
return {
...origProvideResult,
[headSymbol]: head
};
};
}
});
};
const VueHeadMixin = {
created() {
let source = false;
if (Vue3) {
const instance = getCurrentInstance();
if (!instance)
return;
const options = instance.type;
if (!options || !("head" in options))
return;
source = typeof options.head === "function" ? () => options.head.call(instance.proxy) : options.head;
} else {
const head = this.$options.head;
if (head) {
source = typeof head === "function" ? () => head.call(this) : head;
}
}
source && useHead(source);
}
};
export { Vue2ProvideUnheadPlugin, VueHeadMixin, injectHead, unheadVueComposablesImports, useHead, useHeadSafe, useScript, useSeoMeta, useServerHead, useServerHeadSafe, useServerSeoMeta };
+30
View File
@@ -0,0 +1,30 @@
'use strict';
const useHead = require('./shared/vue.BmMjB48i.cjs');
require('vue');
require('./shared/vue.DWlmwWrc.cjs');
require('unhead');
require('@unhead/shared');
function polyfillAsVueUseHead(head) {
const polyfilled = head;
polyfilled.headTags = head.resolveTags;
polyfilled.addEntry = head.push;
polyfilled.addHeadObjs = head.push;
polyfilled.addReactiveEntry = (input, options) => {
const api = useHead.useHead(input, options);
if (api !== void 0)
return api.dispose;
return () => {
};
};
polyfilled.removeHeadObjs = () => {
};
polyfilled.updateDOM = () => {
head.hooks.callHook("entries:updated", head);
};
polyfilled.unhead = head;
return polyfilled;
}
exports.polyfillAsVueUseHead = polyfillAsVueUseHead;
+40
View File
@@ -0,0 +1,40 @@
import { MergeHead } from '@unhead/schema';
import { V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions } from './shared/vue.fwis0K4Q.cjs';
import 'vue';
type VueHeadClientPollyFill<T extends MergeHead> = VueHeadClient<T> & {
/**
* @deprecated use `resolveTags`
*/
headTags: VueHeadClient<T>['resolveTags'];
/**
* @deprecated use `push`
*/
addEntry: VueHeadClient<T>['push'];
/**
* @deprecated use `push`
*/
addHeadObjs: VueHeadClient<T>['push'];
/**
* @deprecated use `useHead`
*/
addReactiveEntry: (input: UseHeadInput<T>, options?: UseHeadOptions) => (() => void);
/**
* @deprecated Use useHead API.
*/
removeHeadObjs: () => void;
/**
* @deprecated Call hook `entries:resolve` or update an entry
*/
updateDOM: () => void;
/**
* @deprecated Access unhead properties directly.
*/
unhead: VueHeadClient<T>;
};
/**
* @deprecated Will be removed in v2.
*/
declare function polyfillAsVueUseHead<T extends MergeHead>(head: VueHeadClient<T>): VueHeadClientPollyFill<T>;
export { type VueHeadClientPollyFill, polyfillAsVueUseHead };
+40
View File
@@ -0,0 +1,40 @@
import { MergeHead } from '@unhead/schema';
import { V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions } from './shared/vue.fwis0K4Q.mjs';
import 'vue';
type VueHeadClientPollyFill<T extends MergeHead> = VueHeadClient<T> & {
/**
* @deprecated use `resolveTags`
*/
headTags: VueHeadClient<T>['resolveTags'];
/**
* @deprecated use `push`
*/
addEntry: VueHeadClient<T>['push'];
/**
* @deprecated use `push`
*/
addHeadObjs: VueHeadClient<T>['push'];
/**
* @deprecated use `useHead`
*/
addReactiveEntry: (input: UseHeadInput<T>, options?: UseHeadOptions) => (() => void);
/**
* @deprecated Use useHead API.
*/
removeHeadObjs: () => void;
/**
* @deprecated Call hook `entries:resolve` or update an entry
*/
updateDOM: () => void;
/**
* @deprecated Access unhead properties directly.
*/
unhead: VueHeadClient<T>;
};
/**
* @deprecated Will be removed in v2.
*/
declare function polyfillAsVueUseHead<T extends MergeHead>(head: VueHeadClient<T>): VueHeadClientPollyFill<T>;
export { type VueHeadClientPollyFill, polyfillAsVueUseHead };
+40
View File
@@ -0,0 +1,40 @@
import { MergeHead } from '@unhead/schema';
import { V as VueHeadClient, U as UseHeadInput, b as UseHeadOptions } from './shared/vue.fwis0K4Q.js';
import 'vue';
type VueHeadClientPollyFill<T extends MergeHead> = VueHeadClient<T> & {
/**
* @deprecated use `resolveTags`
*/
headTags: VueHeadClient<T>['resolveTags'];
/**
* @deprecated use `push`
*/
addEntry: VueHeadClient<T>['push'];
/**
* @deprecated use `push`
*/
addHeadObjs: VueHeadClient<T>['push'];
/**
* @deprecated use `useHead`
*/
addReactiveEntry: (input: UseHeadInput<T>, options?: UseHeadOptions) => (() => void);
/**
* @deprecated Use useHead API.
*/
removeHeadObjs: () => void;
/**
* @deprecated Call hook `entries:resolve` or update an entry
*/
updateDOM: () => void;
/**
* @deprecated Access unhead properties directly.
*/
unhead: VueHeadClient<T>;
};
/**
* @deprecated Will be removed in v2.
*/
declare function polyfillAsVueUseHead<T extends MergeHead>(head: VueHeadClient<T>): VueHeadClientPollyFill<T>;
export { type VueHeadClientPollyFill, polyfillAsVueUseHead };
+28
View File
@@ -0,0 +1,28 @@
import { u as useHead } from './shared/vue.-sixQ7xP.mjs';
import 'vue';
import './shared/vue.ziyDaVMR.mjs';
import 'unhead';
import '@unhead/shared';
function polyfillAsVueUseHead(head) {
const polyfilled = head;
polyfilled.headTags = head.resolveTags;
polyfilled.addEntry = head.push;
polyfilled.addHeadObjs = head.push;
polyfilled.addReactiveEntry = (input, options) => {
const api = useHead(input, options);
if (api !== void 0)
return api.dispose;
return () => {
};
};
polyfilled.removeHeadObjs = () => {
};
polyfilled.updateDOM = () => {
head.hooks.callHook("entries:updated", head);
};
polyfilled.unhead = head;
return polyfilled;
}
export { polyfillAsVueUseHead };
+37
View File
@@ -0,0 +1,37 @@
import { ref, watchEffect, watch, getCurrentInstance, onBeforeUnmount, onDeactivated, onActivated } from 'vue';
import { i as injectHead, r as resolveUnrefHeadInput } from './vue.ziyDaVMR.mjs';
function useHead(input, options = {}) {
const head = options.head || injectHead();
if (head) {
if (!head.ssr)
return clientUseHead(head, input, options);
return head.push(input, options);
}
}
function clientUseHead(head, input, options = {}) {
const deactivated = ref(false);
const resolvedInput = ref({});
watchEffect(() => {
resolvedInput.value = deactivated.value ? {} : resolveUnrefHeadInput(input);
});
const entry = head.push(resolvedInput.value, options);
watch(resolvedInput, (e) => {
entry.patch(e);
});
const vm = getCurrentInstance();
if (vm) {
onBeforeUnmount(() => {
entry.dispose();
});
onDeactivated(() => {
deactivated.value = true;
});
onActivated(() => {
deactivated.value = false;
});
}
return entry;
}
export { useHead as u };
+39
View File
@@ -0,0 +1,39 @@
'use strict';
const vue = require('vue');
const injectHead = require('./vue.DWlmwWrc.cjs');
function useHead(input, options = {}) {
const head = options.head || injectHead.injectHead();
if (head) {
if (!head.ssr)
return clientUseHead(head, input, options);
return head.push(input, options);
}
}
function clientUseHead(head, input, options = {}) {
const deactivated = vue.ref(false);
const resolvedInput = vue.ref({});
vue.watchEffect(() => {
resolvedInput.value = deactivated.value ? {} : injectHead.resolveUnrefHeadInput(input);
});
const entry = head.push(resolvedInput.value, options);
vue.watch(resolvedInput, (e) => {
entry.patch(e);
});
const vm = vue.getCurrentInstance();
if (vm) {
vue.onBeforeUnmount(() => {
entry.dispose();
});
vue.onDeactivated(() => {
deactivated.value = true;
});
vue.onActivated(() => {
deactivated.value = false;
});
}
return entry;
}
exports.useHead = useHead;
+94
View File
@@ -0,0 +1,94 @@
'use strict';
const unhead = require('unhead');
const vue = require('vue');
const shared = require('@unhead/shared');
const Vue3 = vue.version[0] === "3";
function resolveUnref(r) {
return typeof r === "function" ? r() : vue.unref(r);
}
function resolveUnrefHeadInput(ref) {
if (ref instanceof Promise || ref instanceof Date || ref instanceof RegExp)
return ref;
const root = resolveUnref(ref);
if (!ref || !root)
return root;
if (Array.isArray(root))
return root.map((r) => resolveUnrefHeadInput(r));
if (typeof root === "object") {
const resolved = {};
for (const k in root) {
if (!Object.prototype.hasOwnProperty.call(root, k)) {
continue;
}
if (k === "titleTemplate" || k[0] === "o" && k[1] === "n") {
resolved[k] = vue.unref(root[k]);
continue;
}
resolved[k] = resolveUnrefHeadInput(root[k]);
}
return resolved;
}
return root;
}
const VueReactivityPlugin = shared.defineHeadPlugin({
hooks: {
"entries:resolve": (ctx) => {
for (const entry of ctx.entries)
entry.resolvedInput = resolveUnrefHeadInput(entry.input);
}
}
});
const headSymbol = "usehead";
function vueInstall(head) {
const plugin = {
install(app) {
if (Vue3) {
app.config.globalProperties.$unhead = head;
app.config.globalProperties.$head = head;
app.provide(headSymbol, head);
}
}
};
return plugin.install;
}
function createServerHead(options = {}) {
const head = unhead.createServerHead(options);
head.use(VueReactivityPlugin);
head.install = vueInstall(head);
return head;
}
function createHead(options = {}) {
options.domDelayFn = options.domDelayFn || ((fn) => vue.nextTick(() => setTimeout(() => fn(), 0)));
const head = unhead.createHead(options);
head.use(VueReactivityPlugin);
head.install = vueInstall(head);
return head;
}
const _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
const globalKey = "__unhead_injection_handler__";
function setHeadInjectionHandler(handler) {
_global[globalKey] = handler;
}
function injectHead() {
if (globalKey in _global) {
return _global[globalKey]();
}
const head = vue.inject(headSymbol);
if (!head && process.env.NODE_ENV !== "production")
console.warn("Unhead is missing Vue context, falling back to shared context. This may have unexpected results.");
return head || unhead.getActiveHead();
}
exports.Vue3 = Vue3;
exports.createHead = createHead;
exports.createServerHead = createServerHead;
exports.headSymbol = headSymbol;
exports.injectHead = injectHead;
exports.resolveUnrefHeadInput = resolveUnrefHeadInput;
exports.setHeadInjectionHandler = setHeadInjectionHandler;
+132
View File
@@ -0,0 +1,132 @@
import { BaseHtmlAttr, MaybeArray, BaseBodyAttr, Title as Title$1, TitleTemplate as TitleTemplate$1, EntryAugmentation, Base as Base$1, Link as Link$1, Meta as Meta$1, Style as Style$1, Script as Script$1, Noscript as Noscript$1, DataKeys, SchemaAugmentations, DefinedValueOrEmptyObject, MaybeFunctionEntries, BodyEvents, MergeHead, HeadEntryOptions, MetaFlatInput, Unhead } from '@unhead/schema';
import { ComputedRef, Ref, Plugin } from 'vue';
type MaybeReadonlyRef<T> = (() => T) | ComputedRef<T>;
type MaybeComputedRef<T> = T | MaybeReadonlyRef<T> | Ref<T>;
type MaybeComputedRefOrFalsy<T> = undefined | false | null | T | MaybeReadonlyRef<T> | Ref<T>;
/**
* @deprecated Use MaybeComputedRefOrFalsy
*/
type MaybeComputedRefOrPromise<T> = MaybeComputedRefOrFalsy<T>;
type MaybeComputedRefEntries<T> = MaybeComputedRef<T> | {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
type MaybeComputedRefEntriesOnly<T> = {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
interface HtmlAttr extends Omit<BaseHtmlAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
}
interface BodyAttr extends Omit<BaseBodyAttr, 'class' | 'style'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
style?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<string | boolean>>;
}
type Title = MaybeComputedRef<Title$1>;
type TitleTemplate = TitleTemplate$1 | Ref<TitleTemplate$1> | ((title?: string) => TitleTemplate$1);
type Base<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<Base$1<E>>>;
type Link<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Link$1<E>>;
type Meta<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Meta$1<E>>;
type Style<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Style$1<E>>;
type Script<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Script$1<E>>;
type Noscript<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Noscript$1<E>>;
type HtmlAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<HtmlAttr & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>>>;
type BodyAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<BodyAttr & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>> & MaybeFunctionEntries<BodyEvents>>;
interface ReactiveHead<E extends MergeHead = MergeHead> {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* Generate the title from a template.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: MaybeComputedRefEntries<{
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | MaybeComputedRefEntries<Record<string, null | string>>>>;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: MaybeComputedRef<Link<E['link']>[]>;
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: MaybeComputedRef<Meta<E['meta']>[]>;
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: MaybeComputedRef<(Style<E['style']> | string)[]>;
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: MaybeComputedRef<(Script<E['script']> | string)[]>;
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: MaybeComputedRef<(Noscript<E['noscript']> | string)[]>;
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseHeadOptions = Omit<HeadEntryOptions, 'head'> & {
head?: VueHeadClient<any>;
};
type UseHeadInput<T extends MergeHead = {}> = MaybeComputedRef<ReactiveHead<T>>;
type UseSeoMetaInput = MaybeComputedRefEntriesOnly<MetaFlatInput> & {
title?: ReactiveHead['title'];
titleTemplate?: ReactiveHead['titleTemplate'];
};
type VueHeadClient<T extends MergeHead> = Unhead<MaybeComputedRef<ReactiveHead<T>>> & Plugin;
export type { BodyAttr as B, HtmlAttr as H, Link as L, MaybeComputedRefEntries as M, Noscript as N, ReactiveHead as R, Style as S, Title as T, UseHeadInput as U, VueHeadClient as V, MaybeComputedRef as a, UseHeadOptions as b, MaybeComputedRefEntriesOnly as c, UseSeoMetaInput as d, TitleTemplate as e, Base as f, Meta as g, Script as h, HtmlAttributes as i, BodyAttributes as j, MaybeReadonlyRef as k, MaybeComputedRefOrFalsy as l, MaybeComputedRefOrPromise as m };
+132
View File
@@ -0,0 +1,132 @@
import { BaseHtmlAttr, MaybeArray, BaseBodyAttr, Title as Title$1, TitleTemplate as TitleTemplate$1, EntryAugmentation, Base as Base$1, Link as Link$1, Meta as Meta$1, Style as Style$1, Script as Script$1, Noscript as Noscript$1, DataKeys, SchemaAugmentations, DefinedValueOrEmptyObject, MaybeFunctionEntries, BodyEvents, MergeHead, HeadEntryOptions, MetaFlatInput, Unhead } from '@unhead/schema';
import { ComputedRef, Ref, Plugin } from 'vue';
type MaybeReadonlyRef<T> = (() => T) | ComputedRef<T>;
type MaybeComputedRef<T> = T | MaybeReadonlyRef<T> | Ref<T>;
type MaybeComputedRefOrFalsy<T> = undefined | false | null | T | MaybeReadonlyRef<T> | Ref<T>;
/**
* @deprecated Use MaybeComputedRefOrFalsy
*/
type MaybeComputedRefOrPromise<T> = MaybeComputedRefOrFalsy<T>;
type MaybeComputedRefEntries<T> = MaybeComputedRef<T> | {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
type MaybeComputedRefEntriesOnly<T> = {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
interface HtmlAttr extends Omit<BaseHtmlAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
}
interface BodyAttr extends Omit<BaseBodyAttr, 'class' | 'style'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
style?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<string | boolean>>;
}
type Title = MaybeComputedRef<Title$1>;
type TitleTemplate = TitleTemplate$1 | Ref<TitleTemplate$1> | ((title?: string) => TitleTemplate$1);
type Base<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<Base$1<E>>>;
type Link<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Link$1<E>>;
type Meta<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Meta$1<E>>;
type Style<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Style$1<E>>;
type Script<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Script$1<E>>;
type Noscript<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Noscript$1<E>>;
type HtmlAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<HtmlAttr & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>>>;
type BodyAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<BodyAttr & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>> & MaybeFunctionEntries<BodyEvents>>;
interface ReactiveHead<E extends MergeHead = MergeHead> {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* Generate the title from a template.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: MaybeComputedRefEntries<{
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | MaybeComputedRefEntries<Record<string, null | string>>>>;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: MaybeComputedRef<Link<E['link']>[]>;
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: MaybeComputedRef<Meta<E['meta']>[]>;
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: MaybeComputedRef<(Style<E['style']> | string)[]>;
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: MaybeComputedRef<(Script<E['script']> | string)[]>;
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: MaybeComputedRef<(Noscript<E['noscript']> | string)[]>;
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseHeadOptions = Omit<HeadEntryOptions, 'head'> & {
head?: VueHeadClient<any>;
};
type UseHeadInput<T extends MergeHead = {}> = MaybeComputedRef<ReactiveHead<T>>;
type UseSeoMetaInput = MaybeComputedRefEntriesOnly<MetaFlatInput> & {
title?: ReactiveHead['title'];
titleTemplate?: ReactiveHead['titleTemplate'];
};
type VueHeadClient<T extends MergeHead> = Unhead<MaybeComputedRef<ReactiveHead<T>>> & Plugin;
export type { BodyAttr as B, HtmlAttr as H, Link as L, MaybeComputedRefEntries as M, Noscript as N, ReactiveHead as R, Style as S, Title as T, UseHeadInput as U, VueHeadClient as V, MaybeComputedRef as a, UseHeadOptions as b, MaybeComputedRefEntriesOnly as c, UseSeoMetaInput as d, TitleTemplate as e, Base as f, Meta as g, Script as h, HtmlAttributes as i, BodyAttributes as j, MaybeReadonlyRef as k, MaybeComputedRefOrFalsy as l, MaybeComputedRefOrPromise as m };
+132
View File
@@ -0,0 +1,132 @@
import { BaseHtmlAttr, MaybeArray, BaseBodyAttr, Title as Title$1, TitleTemplate as TitleTemplate$1, EntryAugmentation, Base as Base$1, Link as Link$1, Meta as Meta$1, Style as Style$1, Script as Script$1, Noscript as Noscript$1, DataKeys, SchemaAugmentations, DefinedValueOrEmptyObject, MaybeFunctionEntries, BodyEvents, MergeHead, HeadEntryOptions, MetaFlatInput, Unhead } from '@unhead/schema';
import { ComputedRef, Ref, Plugin } from 'vue';
type MaybeReadonlyRef<T> = (() => T) | ComputedRef<T>;
type MaybeComputedRef<T> = T | MaybeReadonlyRef<T> | Ref<T>;
type MaybeComputedRefOrFalsy<T> = undefined | false | null | T | MaybeReadonlyRef<T> | Ref<T>;
/**
* @deprecated Use MaybeComputedRefOrFalsy
*/
type MaybeComputedRefOrPromise<T> = MaybeComputedRefOrFalsy<T>;
type MaybeComputedRefEntries<T> = MaybeComputedRef<T> | {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
type MaybeComputedRefEntriesOnly<T> = {
[key in keyof T]?: MaybeComputedRefOrFalsy<T[key]>;
};
interface HtmlAttr extends Omit<BaseHtmlAttr, 'class'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
}
interface BodyAttr extends Omit<BaseBodyAttr, 'class' | 'style'> {
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
class?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<boolean>>;
/**
* The class global attribute is a space-separated list of the case-sensitive classes of the element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
*/
style?: MaybeArray<MaybeComputedRef<string>> | Record<string, MaybeComputedRef<string | boolean>>;
}
type Title = MaybeComputedRef<Title$1>;
type TitleTemplate = TitleTemplate$1 | Ref<TitleTemplate$1> | ((title?: string) => TitleTemplate$1);
type Base<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<Base$1<E>>>;
type Link<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Link$1<E>>;
type Meta<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Meta$1<E>>;
type Style<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Style$1<E>>;
type Script<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Script$1<E>>;
type Noscript<E extends EntryAugmentation = {}> = MaybeComputedRefEntries<Noscript$1<E>>;
type HtmlAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<HtmlAttr & DataKeys & SchemaAugmentations['htmlAttrs'] & DefinedValueOrEmptyObject<E>>>;
type BodyAttributes<E extends EntryAugmentation = {}> = MaybeComputedRef<MaybeComputedRefEntries<BodyAttr & DataKeys & SchemaAugmentations['bodyAttrs'] & DefinedValueOrEmptyObject<E>> & MaybeFunctionEntries<BodyEvents>>;
interface ReactiveHead<E extends MergeHead = MergeHead> {
/**
* The `<title>` HTML element defines the document's title that is shown in a browser's title bar or a page's tab.
* It only contains text; tags within the element are ignored.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title
*/
title?: Title;
/**
* Generate the title from a template.
*/
titleTemplate?: TitleTemplate;
/**
* Variables used to substitute in the title and meta content.
*/
templateParams?: MaybeComputedRefEntries<{
separator?: '|' | '-' | '·' | string;
} & Record<string, null | string | MaybeComputedRefEntries<Record<string, null | string>>>>;
/**
* The `<base>` HTML element specifies the base URL to use for all relative URLs in a document.
* There can be only one <base> element in a document.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
*/
base?: Base<E['base']>;
/**
* The `<link>` HTML element specifies relationships between the current document and an external resource.
* This element is most commonly used to link to stylesheets, but is also used to establish site icons
* (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as
*/
link?: MaybeComputedRef<Link<E['link']>[]>;
/**
* The `<meta>` element represents metadata that cannot be expressed in other HTML elements, like `<link>` or `<script>`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
*/
meta?: MaybeComputedRef<Meta<E['meta']>[]>;
/**
* The `<style>` HTML element contains style information for a document, or part of a document.
* It contains CSS, which is applied to the contents of the document containing the `<style>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
*/
style?: MaybeComputedRef<(Style<E['style']> | string)[]>;
/**
* The `<script>` HTML element is used to embed executable code or data; this is typically used to embed or refer to JavaScript code.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
*/
script?: MaybeComputedRef<(Script<E['script']> | string)[]>;
/**
* The `<noscript>` HTML element defines a section of HTML to be inserted if a script type on the page is unsupported
* or if scripting is currently turned off in the browser.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript
*/
noscript?: MaybeComputedRef<(Noscript<E['noscript']> | string)[]>;
/**
* Attributes for the `<html>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html
*/
htmlAttrs?: HtmlAttributes<E['htmlAttrs']>;
/**
* Attributes for the `<body>` HTML element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body
*/
bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
}
type UseHeadOptions = Omit<HeadEntryOptions, 'head'> & {
head?: VueHeadClient<any>;
};
type UseHeadInput<T extends MergeHead = {}> = MaybeComputedRef<ReactiveHead<T>>;
type UseSeoMetaInput = MaybeComputedRefEntriesOnly<MetaFlatInput> & {
title?: ReactiveHead['title'];
titleTemplate?: ReactiveHead['titleTemplate'];
};
type VueHeadClient<T extends MergeHead> = Unhead<MaybeComputedRef<ReactiveHead<T>>> & Plugin;
export type { BodyAttr as B, HtmlAttr as H, Link as L, MaybeComputedRefEntries as M, Noscript as N, ReactiveHead as R, Style as S, Title as T, UseHeadInput as U, VueHeadClient as V, MaybeComputedRef as a, UseHeadOptions as b, MaybeComputedRefEntriesOnly as c, UseSeoMetaInput as d, TitleTemplate as e, Base as f, Meta as g, Script as h, HtmlAttributes as i, BodyAttributes as j, MaybeReadonlyRef as k, MaybeComputedRefOrFalsy as l, MaybeComputedRefOrPromise as m };
+86
View File
@@ -0,0 +1,86 @@
import { createServerHead as createServerHead$1, createHead as createHead$1, getActiveHead } from 'unhead';
import { version, unref, nextTick, inject } from 'vue';
import { defineHeadPlugin } from '@unhead/shared';
const Vue3 = version[0] === "3";
function resolveUnref(r) {
return typeof r === "function" ? r() : unref(r);
}
function resolveUnrefHeadInput(ref) {
if (ref instanceof Promise || ref instanceof Date || ref instanceof RegExp)
return ref;
const root = resolveUnref(ref);
if (!ref || !root)
return root;
if (Array.isArray(root))
return root.map((r) => resolveUnrefHeadInput(r));
if (typeof root === "object") {
const resolved = {};
for (const k in root) {
if (!Object.prototype.hasOwnProperty.call(root, k)) {
continue;
}
if (k === "titleTemplate" || k[0] === "o" && k[1] === "n") {
resolved[k] = unref(root[k]);
continue;
}
resolved[k] = resolveUnrefHeadInput(root[k]);
}
return resolved;
}
return root;
}
const VueReactivityPlugin = defineHeadPlugin({
hooks: {
"entries:resolve": (ctx) => {
for (const entry of ctx.entries)
entry.resolvedInput = resolveUnrefHeadInput(entry.input);
}
}
});
const headSymbol = "usehead";
function vueInstall(head) {
const plugin = {
install(app) {
if (Vue3) {
app.config.globalProperties.$unhead = head;
app.config.globalProperties.$head = head;
app.provide(headSymbol, head);
}
}
};
return plugin.install;
}
function createServerHead(options = {}) {
const head = createServerHead$1(options);
head.use(VueReactivityPlugin);
head.install = vueInstall(head);
return head;
}
function createHead(options = {}) {
options.domDelayFn = options.domDelayFn || ((fn) => nextTick(() => setTimeout(() => fn(), 0)));
const head = createHead$1(options);
head.use(VueReactivityPlugin);
head.install = vueInstall(head);
return head;
}
const _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
const globalKey = "__unhead_injection_handler__";
function setHeadInjectionHandler(handler) {
_global[globalKey] = handler;
}
function injectHead() {
if (globalKey in _global) {
return _global[globalKey]();
}
const head = inject(headSymbol);
if (!head && process.env.NODE_ENV !== "production")
console.warn("Unhead is missing Vue context, falling back to shared context. This may have unexpected results.");
return head || getActiveHead();
}
export { Vue3 as V, createServerHead as a, createHead as c, headSymbol as h, injectHead as i, resolveUnrefHeadInput as r, setHeadInjectionHandler as s };
+46
View File
@@ -0,0 +1,46 @@
'use strict';
const useHead = require('./shared/vue.BmMjB48i.cjs');
const injectHead = require('./shared/vue.DWlmwWrc.cjs');
require('vue');
require('unhead');
require('@unhead/shared');
const UnheadPlugin = (_Vue) => {
_Vue.config.optionMergeStrategies.head = function(toVal, fromVal) {
return [toVal, fromVal].flat().filter(Boolean);
};
_Vue.mixin({
created() {
const head = this.$options.head;
if (head) {
if (Array.isArray(head)) {
head.forEach((h) => {
useHead.useHead(typeof h === "function" ? h.call(this) : h);
});
} else {
useHead.useHead(typeof head === "function" ? head.call(this) : head);
}
}
},
beforeCreate() {
const options = this.$options;
if (options.unhead) {
const origProvide = options.provide;
options.provide = function() {
let origProvideResult;
if (typeof origProvide === "function")
origProvideResult = origProvide.call(this);
else
origProvideResult = origProvide || {};
return {
...origProvideResult,
[injectHead.headSymbol]: options.unhead
};
};
}
}
});
};
exports.UnheadPlugin = UnheadPlugin;
+5
View File
@@ -0,0 +1,5 @@
import { Plugin } from 'vue';
declare const UnheadPlugin: Plugin;
export { UnheadPlugin };
+5
View File
@@ -0,0 +1,5 @@
import { Plugin } from 'vue';
declare const UnheadPlugin: Plugin;
export { UnheadPlugin };
+5
View File
@@ -0,0 +1,5 @@
import { Plugin } from 'vue';
declare const UnheadPlugin: Plugin;
export { UnheadPlugin };
+44
View File
@@ -0,0 +1,44 @@
import { u as useHead } from './shared/vue.-sixQ7xP.mjs';
import { h as headSymbol } from './shared/vue.ziyDaVMR.mjs';
import 'vue';
import 'unhead';
import '@unhead/shared';
const UnheadPlugin = (_Vue) => {
_Vue.config.optionMergeStrategies.head = function(toVal, fromVal) {
return [toVal, fromVal].flat().filter(Boolean);
};
_Vue.mixin({
created() {
const head = this.$options.head;
if (head) {
if (Array.isArray(head)) {
head.forEach((h) => {
useHead(typeof h === "function" ? h.call(this) : h);
});
} else {
useHead(typeof head === "function" ? head.call(this) : head);
}
}
},
beforeCreate() {
const options = this.$options;
if (options.unhead) {
const origProvide = options.provide;
options.provide = function() {
let origProvideResult;
if (typeof origProvide === "function")
origProvideResult = origProvide.call(this);
else
origProvideResult = origProvide || {};
return {
...origProvideResult,
[headSymbol]: options.unhead
};
};
}
}
});
};
export { UnheadPlugin };
+78
View File
@@ -0,0 +1,78 @@
{
"name": "@unhead/vue",
"type": "module",
"version": "1.11.20",
"author": "Harlan Wilton <harlan@harlanzw.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/harlan-zw",
"homepage": "https://unhead.unjs.io",
"repository": {
"type": "git",
"url": "git+https://github.com/unjs/unhead.git",
"directory": "packages/vue"
},
"bugs": {
"url": "https://github.com/unjs/unhead/issues"
},
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./polyfill": {
"types": "./dist/polyfill.d.ts",
"import": "./dist/polyfill.mjs",
"require": "./dist/polyfill.cjs"
},
"./vue2": {
"types": "./dist/vue2.d.ts",
"import": "./dist/vue2.mjs",
"require": "./dist/vue2.cjs"
},
"./components": {
"types": "./dist/components.d.ts",
"import": "./dist/components.mjs",
"require": "./dist/components.cjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"typesVersions": {
"*": {
"components": [
"dist/components"
],
"vue2": [
"dist/vue2"
],
"polyfill": [
"dist/polyfill"
]
}
},
"files": [
"dist"
],
"peerDependencies": {
"vue": ">=2.7 || >=3"
},
"build": {
"external": [
"vue"
]
},
"dependencies": {
"hookable": "^5.5.3",
"@unhead/schema": "1.11.20",
"@unhead/shared": "1.11.20",
"unhead": "1.11.20"
},
"scripts": {
"build": "unbuild .",
"stub": "unbuild . --stub",
"export:sizes": "npx export-size . -r"
}
}