Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@vueuse/head

Package Overview
Dependencies
Maintainers
3
Versions
97
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vueuse/head - npm Package Compare versions

Comparing version 0.9.7 to 1.0.0-rc.1

70

dist/index.d.ts

@@ -31,2 +31,8 @@ import * as vue from 'vue';

/**
* Text content of the tag.
*
* @deprecated This can only be used with `useHeadRaw`.
*/
innerHTML?: string;
/**
* Sets the textContent of an element.

@@ -53,4 +59,2 @@ *

* All other tags have a default priority of 10: <meta>, <script>, <link>, <style>, etc
*
* @warn Experimental feature. Only available when rendering SSR
*/

@@ -75,15 +79,15 @@ renderPriority?: number;

declare type UseHeadInput<T extends MergeHead = {}> = MaybeComputedRef<HeadObject<T>>;
declare type ResolvedUseHeadInput<T extends MergeHead = {}> = Head$1<T & VueUseHeadSchema>;
declare type UseHeadRawInput = MaybeComputedRef<ReactiveHead<RawHeadAugmentation & VueUseHeadSchema>>;
interface HeadEntryOptions {
raw?: boolean;
resolved?: boolean;
}
interface HeadEntry<T extends MergeHead = {}> {
options?: HeadEntryOptions;
input: UseHeadInput<T>;
id?: number;
options: HeadEntryOptions;
tags: HeadTag[];
input: ResolvedUseHeadInput<T>;
resolved: boolean;
id: number;
}
interface ResolvedHeadEntry<T extends MergeHead = {}> {
options?: HeadEntryOptions;
input: Head$1<T & VueUseHeadSchema>;
}
declare type TagKeys = keyof Omit<HeadObjectPlain, 'titleTemplate'>;

@@ -94,17 +98,18 @@

}
declare type HookBeforeDomUpdate = ((tags: Record<string, HeadTag[]>) => void | boolean)[];
declare type HookTagsResolved = ((tags: HeadTag[]) => void)[];
declare type HookBeforeDomUpdate = ((tags: HeadTag[]) => Promise<void | boolean> | void | boolean)[];
declare type HookTagsResolved = ((tags: HeadTag[]) => Promise<void> | void)[];
declare type HeadTagRuntime = HeadEntryOptions & HandlesDuplicates & HasRenderPriority & RendersToBody & HasTextContent & {
position: number;
entryId: number;
};
interface HeadTag {
tag: TagKeys;
props: HandlesDuplicates & HasRenderPriority & RendersToBody & HasTextContent & {
props: {
[k: string]: any;
};
_options?: HeadEntryOptions;
_position?: number;
_runtime: HeadTagRuntime;
}
interface DomUpdateCtx {
title: string | undefined;
htmlAttrs: HeadAttrs;
bodyAttrs: HeadAttrs;
actualTags: Record<string, HeadTag[]>;
interface HeadObjectApi<T extends MergeHead = {}> {
update: (resolvedInput: ResolvedUseHeadInput<T>) => void;
remove: () => void;
}

@@ -120,5 +125,3 @@ interface HTMLResult {

declare const createElement: (tag: string, attrs: {
[k: string]: any;
}, document: Document) => HTMLElement;
declare const createElement: (tag: HeadTag, document: Document) => HTMLElement;

@@ -151,16 +154,16 @@ declare const updateElements: (document: Document | undefined, type: string, tags: HeadTag[]) => void;

declare const tagToString: (tag: HeadTag) => string;
declare const renderHeadToString: (head: HeadClient) => HTMLResult;
declare const resolveHeadEntry: (entries: HeadEntry[], force?: boolean) => HeadEntry<{}>[];
declare const renderHeadToString: <T extends MergeHead = {}>(head: HeadClient<T>) => Promise<HTMLResult>;
declare const sortTags: (aTag: HeadTag, bTag: HeadTag) => number;
declare const tagDedupeKey: <T extends HeadTag>(tag: T) => string | false;
declare function resolveHeadEntry<T extends MergeHead = {}>(obj: HeadEntry<T>): ResolvedHeadEntry;
declare const tagDedupeKey: <T extends HeadTag>(tag: T) => string | number;
declare function resolveUnrefHeadInput<T extends MergeHead = {}>(ref: UseHeadInput<T>): ResolvedUseHeadInput<T>;
declare const headInputToTags: (e: HeadEntry) => HeadTag[];
declare const resolveHeadEntriesToTags: (entries: HeadEntry[]) => HeadTag[];
interface HeadClient<T extends MergeHead = {}> {
install: (app: App) => void;
headTags: HeadTag[];
addHeadObjs: (objs: UseHeadInput<T>, options?: HeadEntryOptions) => () => void;
/**
* @deprecated Use the return function from `addHeadObjs`
*/
removeHeadObjs: (objs: UseHeadInput<T>) => void;
headEntries: HeadEntry<T>[];
addEntry: (entry: UseHeadInput<T>, options?: HeadEntryOptions) => HeadObjectApi<T>;
addReactiveEntry: (objs: UseHeadInput<T>, options?: HeadEntryOptions) => () => void;
updateDOM: (document?: Document, force?: boolean) => void;

@@ -181,2 +184,3 @@ /**

}
declare const IS_BROWSER: boolean;
/**

@@ -187,6 +191,6 @@ * Inject the head manager instance

declare const injectHead: <T extends MergeHead = {}>() => HeadClient<T>;
declare const createHead: <T extends MergeHead = {}>(initHeadObject?: UseHeadInput<T> | undefined) => HeadClient<T>;
declare const createHead: <T extends MergeHead = {}>(initHeadObject?: ResolvedUseHeadInput<T> | undefined) => HeadClient<T>;
declare const useHead: <T extends MergeHead = {}>(headObj: UseHeadInput<T>) => void;
declare const useHeadRaw: (headObj: UseHeadRawInput) => void;
export { DomUpdateCtx, HTMLResult, HandlesDuplicates, HasRenderPriority, HasTextContent, Head, HeadAttrs, HeadClient, HeadEntry, HeadEntryOptions, HeadObject, HeadObjectPlain, HeadTag, HookBeforeDomUpdate, HookTagsResolved, Never, RendersToBody, ResolvedHeadEntry, TagKeys, UseHeadInput, UseHeadRawInput, VueUseHeadSchema, createElement, createHead, escapeHtml, escapeJS, injectHead, isEqualNode, renderHeadToString, resolveHeadEntry, setAttrs, sortTags, stringifyAttrName, stringifyAttrValue, stringifyAttrs, tagDedupeKey, tagToString, updateElements, useHead, useHeadRaw };
export { HTMLResult, HandlesDuplicates, HasRenderPriority, HasTextContent, Head, HeadAttrs, HeadClient, HeadEntry, HeadEntryOptions, HeadObject, HeadObjectApi, HeadObjectPlain, HeadTag, HeadTagRuntime, HookBeforeDomUpdate, HookTagsResolved, IS_BROWSER, Never, RendersToBody, ResolvedUseHeadInput, TagKeys, UseHeadInput, UseHeadRawInput, VueUseHeadSchema, createElement, createHead, escapeHtml, escapeJS, headInputToTags, injectHead, isEqualNode, renderHeadToString, resolveHeadEntriesToTags, resolveHeadEntry, resolveUnrefHeadInput, setAttrs, sortTags, stringifyAttrName, stringifyAttrValue, stringifyAttrs, tagDedupeKey, tagToString, updateElements, useHead, useHeadRaw };

@@ -24,2 +24,3 @@ "use strict";

Head: () => Head,
IS_BROWSER: () => IS_BROWSER,
createElement: () => createElement,

@@ -29,6 +30,9 @@ createHead: () => createHead,

escapeJS: () => escapeJS,
headInputToTags: () => headInputToTags,
injectHead: () => injectHead,
isEqualNode: () => isEqualNode,
renderHeadToString: () => renderHeadToString,
resolveHeadEntriesToTags: () => resolveHeadEntriesToTags,
resolveHeadEntry: () => resolveHeadEntry,
resolveUnrefHeadInput: () => resolveUnrefHeadInput,
setAttrs: () => setAttrs,

@@ -58,6 +62,127 @@ sortTags: () => sortTags,

var import_vue = require("vue");
// src/ssr/stringify-attrs.ts
var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
var escapeJS = (s) => s.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
switch (character) {
case '"':
case "'":
case "\\":
return `\\${character}`;
case "\n":
return "\\n";
case "\r":
return "\\r";
case "\u2028":
return "\\u2028";
case "\u2029":
return "\\u2029";
}
return character;
});
var stringifyAttrName = (str) => str.replace(/[\s"'><\/=]/g, "").replace(/[^a-zA-Z0-9_-]/g, "");
var stringifyAttrValue = (str) => escapeJS(str.replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
var stringifyAttrs = (attributes, options = {}) => {
const handledAttributes = [];
for (const [key, value] of Object.entries(attributes)) {
if (key === "children" || key === "innerHTML" || key === "textContent" || key === "key")
continue;
if (value === false || value == null)
continue;
let attribute = stringifyAttrName(key);
if (value !== true) {
const val = String(value);
if (options.raw) {
attribute += `="${val}"`;
} else {
if (attribute === "href" || attribute === "src")
attribute += `="${stringifyAttrValue(encodeURI(val))}"`;
else
attribute += `="${stringifyAttrValue(val)}"`;
}
}
handledAttributes.push(attribute);
}
return handledAttributes.length > 0 ? ` ${handledAttributes.join(" ")}` : "";
};
// src/ssr/index.ts
var tagToString = (tag) => {
var _a;
const body = tag._runtime.body ? ` ${BODY_TAG_ATTR_NAME}="true"` : "";
const attrs = stringifyAttrs(tag.props, tag._runtime);
if (SELF_CLOSING_TAGS.includes(tag.tag))
return `<${tag.tag}${attrs}${body}>`;
let innerContent = "";
if (((_a = tag._runtime) == null ? void 0 : _a.raw) && tag._runtime.innerHTML)
innerContent = tag._runtime.innerHTML;
if (!innerContent && tag._runtime.textContent)
innerContent = escapeJS(escapeHtml(tag._runtime.textContent));
if (!innerContent && tag._runtime.children)
innerContent = escapeJS(escapeHtml(tag._runtime.children));
return `<${tag.tag}${attrs}${body}>${innerContent}</${tag.tag}>`;
};
var resolveHeadEntry = (entries, force) => {
return entries.map((e) => {
if (e.input && (force || !e.resolved))
e.input = resolveUnrefHeadInput(e.input);
return e;
});
};
var renderHeadToString = async (head) => {
const headHtml = [];
const bodyHtml = [];
let titleHtml = "";
const attrs = { htmlAttrs: {}, bodyAttrs: {} };
const resolvedEntries = resolveHeadEntry(head.headEntries, true);
const headTags = resolveHeadEntriesToTags(resolvedEntries);
for (const h in head.hookTagsResolved)
await head.hookTagsResolved[h](headTags);
for (const tag of headTags) {
if (tag.tag === "title") {
titleHtml = tagToString(tag);
} else if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
for (const k in tag.props) {
attrs[tag.tag][stringifyAttrName(k)] = stringifyAttrValue(tag.props[k]);
}
} else if (tag._runtime.body) {
bodyHtml.push(tagToString(tag));
} else {
headHtml.push(tagToString(tag));
}
}
headHtml.push(`<meta name="${HEAD_COUNT_KEY}" content="${headHtml.length}">`);
return {
get headTags() {
return titleHtml + headHtml.join("");
},
get htmlAttrs() {
return stringifyAttrs(
{
...attrs.htmlAttrs,
[HEAD_ATTRS_KEY]: Object.keys(attrs.htmlAttrs).join(",")
},
{ raw: true }
);
},
get bodyAttrs() {
return stringifyAttrs(
{
...attrs.bodyAttrs,
[HEAD_ATTRS_KEY]: Object.keys(attrs.bodyAttrs).join(",")
},
{ raw: true }
);
},
get bodyTags() {
return bodyHtml.join("");
}
};
};
// src/utils.ts
var sortTags = (aTag, bTag) => {
const tagWeight = (tag) => {
if (tag.props.renderPriority)
return tag.props.renderPriority;
if (tag._runtime.renderPriority)
return tag._runtime.renderPriority;
switch (tag.tag) {

@@ -79,5 +204,3 @@ case "base":

var tagDedupeKey = (tag) => {
if (!["meta", "base", "script", "link", "title"].includes(tag.tag))
return false;
const { props, tag: tagName } = tag;
const { props, tag: tagName, _runtime } = tag;
if (tagName === "base" || tagName === "title")

@@ -89,21 +212,22 @@ return tagName;

return "charset";
const name = ["key", "id", "name", "property", "http-equiv"];
if (_runtime.key)
return `${tagName}:${_runtime.key}`;
const name = ["id"];
if (tagName === "meta")
name.push(...["name", "property", "http-equiv"]);
for (const n of name) {
let value;
if (typeof props.getAttribute === "function" && props.hasAttribute(n))
value = props.getAttribute(n);
else
value = props[n];
if (value !== void 0) {
return `${tagName}-${n}-${value}`;
if (typeof props[n] !== "undefined") {
return `${tagName}:${n}:${props[n]}`;
}
}
return false;
return tag._runtime.position;
};
function resolveUnrefDeeply(ref2) {
function resolveUnrefHeadInput(ref2) {
const root = (0, import_shared.resolveUnref)(ref2);
if (!ref2 || !root)
if (!ref2 || !root) {
return root;
if (Array.isArray(root))
return root.map(resolveUnrefDeeply);
}
if (Array.isArray(root)) {
return root.map(resolveUnrefHeadInput);
}
if (typeof root === "object") {

@@ -116,3 +240,3 @@ return Object.fromEntries(

key,
resolveUnrefDeeply(value)
resolveUnrefHeadInput(value)
];

@@ -124,8 +248,84 @@ })

}
function resolveHeadEntry(obj) {
return {
...obj,
input: resolveUnrefDeeply(obj.input)
var resolveTag = (name, input, e) => {
const tag = {
tag: name,
props: [],
_runtime: {
entryId: e.id,
position: 0
}
};
}
["hid", "vmid"].forEach((key) => {
if (input[key]) {
tag._runtime.key = input[key];
delete input[key];
}
});
tag._runtime = {
...tag._runtime,
...e.options
};
["body", "renderPriority", "key", "children", "innerHTML", "textContent"].forEach((key) => {
if (typeof input[key] !== "undefined") {
tag._runtime[key] = input[key];
delete input[key];
}
});
tag.props = input;
return tag;
};
var headInputToTags = (e) => {
return Object.entries(e.input).filter(([k, v]) => typeof v !== "undefined" && v !== null && k !== "titleTemplate").map(([key, value]) => {
return (Array.isArray(value) ? value : [value]).map((props) => {
switch (key) {
case "title":
return resolveTag(key, { textContent: props }, e);
case "base":
case "meta":
case "link":
case "style":
case "script":
case "noscript":
case "htmlAttrs":
case "bodyAttrs":
return resolveTag(key, props, e);
default:
return false;
}
});
}).flat().filter((v) => !!v);
};
var renderTitleTemplate = (template, title) => {
if (template == null)
return "";
if (typeof template === "function")
return template(title);
return template.replace("%s", title ?? "");
};
var resolveHeadEntriesToTags = (entries) => {
const deduping = {};
const resolvedEntries = resolveHeadEntry(entries);
const titleTemplate = resolvedEntries.map((i) => i.input.titleTemplate).reverse().find((i) => i != null);
resolvedEntries.forEach((entry, entryIndex) => {
const tags = headInputToTags(entry);
tags.forEach((tag, tagIdx) => {
var _a;
tag._runtime.position = entryIndex * 1e4 + tagIdx;
if (titleTemplate && tag.tag === "title") {
tag._runtime.textContent = renderTitleTemplate(
titleTemplate,
tag._runtime.textContent
);
}
if (!((_a = tag._runtime) == null ? void 0 : _a.raw)) {
for (const k in tag.props) {
if (k.startsWith("on") || k === "innerHTML")
delete tag.props[k];
}
}
deduping[tagDedupeKey(tag)] = tag;
});
});
return Object.values(deduping).sort((a, b) => a._runtime.position - b._runtime.position).sort(sortTags);
};

@@ -171,20 +371,16 @@ // src/dom/utils.ts

// src/dom/create-element.ts
var createElement = (tag, attrs, document) => {
const el = document.createElement(tag);
for (const key of Object.keys(attrs)) {
if (key === "body" && attrs.body === true) {
el.setAttribute(BODY_TAG_ATTR_NAME, "true");
} else {
const value = attrs[key];
if (key === "renderPriority" || key === "key" || value === false)
continue;
if (key === "children" || key === "textContent")
el.textContent = value;
else if (key === "innerHTML")
el.innerHTML = value;
else
el.setAttribute(key, value);
}
var createElement = (tag, document) => {
const $el = document.createElement(tag.tag);
Object.entries(tag.props).forEach(([k, v]) => {
if (v !== false)
$el.setAttribute(k, v);
});
if (tag._runtime.body === true) {
$el.setAttribute(BODY_TAG_ATTR_NAME, "true");
}
return el;
if (tag._runtime.raw && tag._runtime.innerHTML)
$el.innerHTML = tag._runtime.innerHTML;
else
$el.textContent = tag._runtime.textContent || tag._runtime.children || "";
return $el;
};

@@ -220,4 +416,4 @@

let newElements = tags.map((tag) => ({
element: createElement(tag.tag, tag.props, document),
body: tag.props.body ?? false
element: createElement(tag, document),
body: tag._runtime.body ?? false
}));

@@ -262,14 +458,33 @@ newElements = newElements.filter((newEl) => {

// src/dom/update-dom.ts
var updateDOM = ({ domCtx, document, previousTags }) => {
var updateDOM = async (head, previousTags, document) => {
const tags = {};
if (!document)
document = window.document;
if (domCtx.title !== void 0)
document.title = domCtx.title;
setAttrs(document.documentElement, domCtx.htmlAttrs);
setAttrs(document.body, domCtx.bodyAttrs);
const tags = /* @__PURE__ */ new Set([...Object.keys(domCtx.actualTags), ...previousTags]);
for (const tag of tags)
updateElements(document, tag, domCtx.actualTags[tag] || []);
const headTags = resolveHeadEntriesToTags(head.headEntries);
for (const h in head.hookTagsResolved)
await head.hookTagsResolved[h](headTags);
for (const k in head.hookBeforeDomUpdate) {
if (await head.hookBeforeDomUpdate[k](headTags) === false)
return;
}
for (const tag of headTags) {
switch (tag.tag) {
case "title":
if (typeof tag._runtime.textContent !== "undefined")
document.title = tag._runtime.textContent;
break;
case "htmlAttrs":
case "bodyAttrs":
setAttrs(document[tag.tag === "htmlAttrs" ? "documentElement" : "body"], tag.props);
break;
default:
tags[tag.tag] = tags[tag.tag] || [];
tags[tag.tag].push(tag);
}
}
const tagKeys = /* @__PURE__ */ new Set([...Object.keys(tags), ...previousTags]);
for (const tag of tagKeys)
updateElements(document, tag, tags[tag] || []);
previousTags.clear();
Object.keys(domCtx.actualTags).forEach((i) => previousTags.add(i));
Object.keys(tags).forEach((i) => previousTags.add(i));
};

@@ -283,10 +498,11 @@

return;
const props = {
...node.props,
children: Array.isArray(node.children) ? node.children[0].children : node.children
};
const props = node.props || {};
if (node.children) {
const k = type === "script" ? "innerHTML" : "textContent";
props[k] = Array.isArray(node.children) ? node.children[0].children : node.children;
}
if (Array.isArray(obj[type]))
obj[type].push(props);
else if (type === "title")
obj.title = props.children;
obj.title = props.textContent;
else

@@ -321,9 +537,11 @@ obj[type] = props;

const head = injectHead();
let obj;
(0, import_vue2.onBeforeUnmount)(() => {
if (obj) {
head.removeHeadObjs(obj);
head.updateDOM();
}
});
const obj = (0, import_vue2.ref)({});
if (IS_BROWSER) {
const cleanUp = head.addReactiveEntry(obj, { raw: true });
(0, import_vue2.onBeforeUnmount)(() => {
cleanUp();
});
} else {
head.addEntry(obj, { raw: true });
}
return () => {

@@ -333,8 +551,3 @@ (0, import_vue2.watchEffect)(() => {

return;
if (obj)
head.removeHeadObjs(obj);
obj = (0, import_vue2.ref)(vnodesToHeadObj(slots.default()));
head.addHeadObjs(obj);
if (typeof window !== "undefined")
head.updateDOM();
obj.value = vnodesToHeadObj(slots.default());
});

@@ -346,120 +559,4 @@ return null;

// src/ssr/stringify-attrs.ts
var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
var escapeJS = (s) => s.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
switch (character) {
case '"':
case "'":
case "\\":
return `\\${character}`;
case "\n":
return "\\n";
case "\r":
return "\\r";
case "\u2028":
return "\\u2028";
case "\u2029":
return "\\u2029";
}
return character;
});
var stringifyAttrName = (str) => str.replace(/[\s"'><\/=]/g, "").replace(/[^a-zA-Z0-9_-]/g, "");
var stringifyAttrValue = (str) => escapeJS(str.replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
var stringifyAttrs = (attributes, options = {}) => {
const handledAttributes = [];
for (const [key, value] of Object.entries(attributes)) {
if (key === "children" || key === "innerHTML" || key === "textContent" || key === "key")
continue;
if (value === false || value == null)
continue;
let attribute = stringifyAttrName(key);
if (value !== true) {
const val = String(value);
if (options.raw) {
attribute += `="${val}"`;
} else {
if (attribute === "href" || attribute === "src")
attribute += `="${stringifyAttrValue(encodeURI(val))}"`;
else
attribute += `="${stringifyAttrValue(val)}"`;
}
}
handledAttributes.push(attribute);
}
return handledAttributes.length > 0 ? ` ${handledAttributes.join(" ")}` : "";
};
// src/ssr/index.ts
var tagToString = (tag) => {
var _a;
let isBodyTag = false;
if (tag.props.body) {
isBodyTag = true;
delete tag.props.body;
}
if (tag.props.renderPriority)
delete tag.props.renderPriority;
const attrs = stringifyAttrs(tag.props, tag._options);
if (SELF_CLOSING_TAGS.includes(tag.tag)) {
return `<${tag.tag}${attrs}${isBodyTag ? ` ${BODY_TAG_ATTR_NAME}="true"` : ""}>`;
}
let innerContent = "";
if (((_a = tag._options) == null ? void 0 : _a.raw) && tag.props.innerHTML)
innerContent = tag.props.innerHTML;
if (!innerContent && tag.props.textContent)
innerContent = escapeJS(escapeHtml(tag.props.textContent));
if (!innerContent && tag.props.children)
innerContent = escapeJS(escapeHtml(tag.props.children));
return `<${tag.tag}${attrs}${isBodyTag ? ` ${BODY_TAG_ATTR_NAME}="true"` : ""}>${innerContent}</${tag.tag}>`;
};
var renderHeadToString = (head) => {
var _a;
const tags = [];
const bodyTags = [];
let titleTag = "";
const attrs = { htmlAttrs: {}, bodyAttrs: {} };
for (const tag of head.headTags.sort(sortTags)) {
if (tag.tag === "title") {
titleTag = tagToString(tag);
} else if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
for (const k in tag.props) {
const keyName = stringifyAttrName(k);
attrs[tag.tag][keyName] = ((_a = tag._options) == null ? void 0 : _a.raw) ? tag.props[keyName] : tag.props[stringifyAttrValue(keyName)];
}
} else if (tag.props.body) {
bodyTags.push(tagToString(tag));
} else {
tags.push(tagToString(tag));
}
}
tags.push(`<meta name="${HEAD_COUNT_KEY}" content="${tags.length}">`);
return {
get headTags() {
return titleTag + tags.join("");
},
get htmlAttrs() {
return stringifyAttrs(
{
...attrs.htmlAttrs,
[HEAD_ATTRS_KEY]: Object.keys(attrs.htmlAttrs).join(",")
},
{ raw: true }
);
},
get bodyAttrs() {
return stringifyAttrs(
{
...attrs.bodyAttrs,
[HEAD_ATTRS_KEY]: Object.keys(attrs.bodyAttrs).join(",")
},
{ raw: true }
);
},
get bodyTags() {
return bodyTags.join("");
}
};
};
// src/index.ts
var IS_BROWSER = typeof window !== "undefined";
var injectHead = () => {

@@ -471,73 +568,9 @@ const head = (0, import_vue3.inject)(PROVIDE_KEY);

};
var acceptFields = [
"title",
"meta",
"link",
"base",
"style",
"script",
"noscript",
"htmlAttrs",
"bodyAttrs"
];
var renderTitleTemplate = (template, title) => {
if (template == null)
return "";
if (typeof template === "function")
return template(title);
return template.replace("%s", title ?? "");
};
var headObjToTags = (obj) => {
const tags = [];
const keys = Object.keys(obj);
const convertLegacyKey = (value) => {
if (value.hid) {
value.key = value.hid;
delete value.hid;
}
if (value.vmid) {
value.key = value.vmid;
delete value.vmid;
}
return value;
};
for (const key of keys) {
if (obj[key] == null)
continue;
switch (key) {
case "title":
tags.push({ tag: key, props: { textContent: obj[key] } });
break;
case "titleTemplate":
break;
case "base":
tags.push({ tag: key, props: { key: "default", ...obj[key] } });
break;
default:
if (acceptFields.includes(key)) {
const value = obj[key];
if (Array.isArray(value)) {
value.forEach((item) => {
const props = convertLegacyKey(item);
tags.push({ tag: key, props });
});
} else if (value) {
tags.push({ tag: key, props: convertLegacyKey(value) });
}
}
break;
}
}
return tags;
};
var createHead = (initHeadObject) => {
let allHeadObjs = [];
let entries = [];
let entryId = 0;
const previousTags = /* @__PURE__ */ new Set();
let headObjId = 0;
const hookBeforeDomUpdate = [];
const hookTagsResolved = [];
if (initHeadObject)
allHeadObjs.push({ input: initHeadObject });
let domUpdateTick = null;
let domCtx;
const head = {

@@ -550,102 +583,74 @@ install(app) {

hookTagsResolved,
get headTags() {
const deduped = [];
const deduping = {};
const resolvedHeadObjs = allHeadObjs.map(resolveHeadEntry);
const titleTemplate = resolvedHeadObjs.map((i) => i.input.titleTemplate).reverse().find((i) => i != null);
resolvedHeadObjs.forEach((objs, headObjectIdx) => {
const tags2 = headObjToTags(objs.input);
tags2.forEach((tag, tagIdx) => {
var _a;
tag._position = headObjectIdx * 1e4 + tagIdx;
if (tag._options)
delete tag._options;
if (objs.options)
tag._options = objs.options;
if (titleTemplate && tag.tag === "title") {
tag.props.textContent = renderTitleTemplate(
titleTemplate,
tag.props.textContent
);
}
if (!((_a = tag._options) == null ? void 0 : _a.raw)) {
for (const k in tag.props) {
if (k.startsWith("on")) {
console.warn("[@vueuse/head] Warning, you must use `useHeadRaw` to set event listeners. See https://github.com/vueuse/head/pull/118", tag);
delete tag.props[k];
}
}
if (tag.props.innerHTML) {
console.warn("[@vueuse/head] Warning, you must use `useHeadRaw` to use `innerHTML`", tag);
delete tag.props.innerHTML;
}
}
const dedupeKey = tagDedupeKey(tag);
if (dedupeKey)
deduping[dedupeKey] = tag;
else
deduped.push(tag);
});
});
deduped.push(...Object.values(deduping));
const tags = deduped.sort((a, b) => a._position - b._position);
head.hookTagsResolved.forEach((fn) => fn(tags));
return tags;
get headEntries() {
return entries;
},
addHeadObjs(objs, options) {
const entry = { input: objs, options, id: headObjId++ };
allHeadObjs.push(entry);
return () => {
allHeadObjs = allHeadObjs.filter((_objs) => _objs.id !== entry.id);
addEntry(input, options = {}) {
let resolved = false;
if (options == null ? void 0 : options.resolved) {
resolved = true;
delete options.resolved;
}
const entry = {
id: entryId++,
options,
resolved,
input
};
entries.push(entry);
return {
remove() {
entries = entries.filter((_objs) => _objs.id !== entry.id);
},
update(updatedInput) {
entries = entries.map((e) => {
if (e.id === entry.id)
e.input = updatedInput;
return e;
});
}
};
},
removeHeadObjs(objs) {
allHeadObjs = allHeadObjs.filter((_objs) => _objs.input !== objs);
},
updateDOM: (document, force) => {
domCtx = {
title: void 0,
htmlAttrs: {},
bodyAttrs: {},
actualTags: {}
};
for (const tag of head.headTags.sort(sortTags)) {
if (tag.tag === "title") {
domCtx.title = tag.props.textContent;
continue;
}
if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
Object.assign(domCtx[tag.tag], tag.props);
continue;
}
domCtx.actualTags[tag.tag] = domCtx.actualTags[tag.tag] || [];
domCtx.actualTags[tag.tag].push(tag);
}
async updateDOM(document, force) {
const doDomUpdate = () => {
domUpdateTick = null;
for (const k in head.hookBeforeDomUpdate) {
if (head.hookBeforeDomUpdate[k](domCtx.actualTags) === false)
return;
return updateDOM(head, previousTags, document);
};
if (force)
return doDomUpdate();
return domUpdateTick = domUpdateTick || new Promise((resolve) => (0, import_vue3.nextTick)(() => resolve(doDomUpdate())));
},
addReactiveEntry(input, options = {}) {
let entrySideEffect = null;
const cleanUpWatch = (0, import_vue3.watchEffect)(() => {
const resolvedInput = resolveUnrefHeadInput(input);
if (entrySideEffect === null) {
entrySideEffect = head.addEntry(
resolvedInput,
{ ...options, resolved: true }
);
} else {
entrySideEffect.update(resolvedInput);
}
updateDOM({ domCtx, document, previousTags });
if (IS_BROWSER)
head.updateDOM();
});
return () => {
cleanUpWatch();
if (entrySideEffect)
entrySideEffect.remove();
};
if (force) {
doDomUpdate();
return;
}
domUpdateTick = domUpdateTick || (0, import_vue3.nextTick)(() => doDomUpdate());
}
};
if (initHeadObject)
head.addEntry(initHeadObject);
return head;
};
var IS_BROWSER = typeof window !== "undefined";
var _useHead = (headObj, options = {}) => {
const head = injectHead();
const removeHeadObjs = head.addHeadObjs(headObj, options);
if (IS_BROWSER) {
(0, import_vue3.watchEffect)(() => {
head.updateDOM();
});
if (!IS_BROWSER) {
head.addEntry(headObj, options);
} else {
const cleanUp = head.addReactiveEntry(headObj, options);
(0, import_vue3.onBeforeUnmount)(() => {
removeHeadObjs();
cleanUp();
head.updateDOM();

@@ -664,2 +669,3 @@ });

Head,
IS_BROWSER,
createElement,

@@ -669,6 +675,9 @@ createHead,

escapeJS,
headInputToTags,
injectHead,
isEqualNode,
renderHeadToString,
resolveHeadEntriesToTags,
resolveHeadEntry,
resolveUnrefHeadInput,
setAttrs,

@@ -675,0 +684,0 @@ sortTags,

{
"name": "@vueuse/head",
"version": "0.9.7",
"version": "1.0.0-rc.1",
"packageManager": "pnpm@7.5.0",

@@ -58,3 +58,3 @@ "description": "Document head manager for Vue 3. SSR ready.",

"cheerio": "1.0.0-rc.12",
"eslint": "^8.24.0",
"eslint": "^8.25.0",
"execa": "^6.1.0",

@@ -67,9 +67,9 @@ "get-port-please": "^2.6.1",

"nuxt": "3.0.0-rc.11",
"pathe": "^0.3.8",
"playwright": "^1.26.1",
"pathe": "^0.3.9",
"playwright": "^1.27.0",
"simple-git-hooks": "^2.8.0",
"tsup": "^6.2.3",
"typescript": "^4.8.4",
"vite": "^3.1.4",
"vitest": "^0.23.4",
"vite": "^3.1.6",
"vitest": "^0.24.0",
"vue": "^3.2.40",

@@ -76,0 +76,0 @@ "vue-router": "^4.1.5"

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc