unhead 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,20 +1,19 @@
1
1
  import * as _unhead_schema from '@unhead/schema';
2
- import { Head, Meta, Link, Script, Style, Base, HtmlAttributes, BodyAttributes, Noscript } from '@unhead/schema';
3
- import { H as HeadPlugin, C as CreateHeadOptions, a as HeadClient, b as HeadEntryOptions } from './types-b4318c02.js';
4
- export { A as ActiveHeadEntry, C as CreateHeadOptions, D as DomRenderTagContext, E as EntryResolveCtx, a as HeadClient, c as HeadEntry, b as HeadEntryOptions, f as HeadHooks, H as HeadPlugin, e as HookResult, R as RuntimeMode, S as SideEffectsRecord, d as defineHeadPlugin } from './types-b4318c02.js';
5
- import 'hookable';
2
+ import { Head, HeadEntryOptions, Meta, Link, Script, Style, Base, HtmlAttributes, BodyAttributes, Noscript, HeadClient, CreateHeadOptions, HeadPlugin, HeadTag, HeadEntry } from '@unhead/schema';
6
3
 
7
- declare const DedupesTagsPlugin: HeadPlugin<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>;
4
+ interface DedupesTagsPluginOptions {
5
+ dedupeKeys?: string[];
6
+ }
7
+ declare const DedupesTagsPlugin: (options?: DedupesTagsPluginOptions) => _unhead_schema.HeadPlugin;
8
8
 
9
- declare const SortTagsPlugin: HeadPlugin<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>;
9
+ declare const SortTagsPlugin: () => _unhead_schema.HeadPlugin;
10
10
 
11
- declare const TitleTemplatePlugin: HeadPlugin<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>;
11
+ declare const TitleTemplatePlugin: () => _unhead_schema.HeadPlugin;
12
12
 
13
- declare const HydratesStatePlugin: HeadPlugin<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>;
13
+ declare const HydratesStatePlugin: () => _unhead_schema.HeadPlugin;
14
14
 
15
- declare function createHead<T extends {} = Head>(options?: CreateHeadOptions<T>): HeadClient<T>;
16
-
17
- declare function useHead<T>(input: T, options?: HeadEntryOptions): void;
15
+ declare function useHead<T extends Head>(input: T, options?: HeadEntryOptions): void;
16
+ declare function useServerHead<T extends Head>(input: T, options?: HeadEntryOptions): void;
18
17
  declare const useTitle: (title: string) => void;
19
18
  declare const useMeta: (meta: Meta) => void;
20
19
  declare const useLink: (link: Link) => void;
@@ -27,12 +26,18 @@ declare const useTitleTemplate: (titleTemplate: string) => void;
27
26
  declare const useNoscript: (noscript: Noscript) => void;
28
27
 
29
28
  declare let activeHead: HeadClient<any> | undefined;
30
- declare const setActiveHead: <T>(head: HeadClient<T> | undefined) => HeadClient<T> | undefined;
31
- declare const getActiveHead: <T>() => HeadClient<T>;
29
+ declare const setActiveHead: <T extends HeadClient<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>>(head: T | undefined) => T | undefined;
30
+ declare const getActiveHead: <T extends HeadClient<_unhead_schema.Head<_unhead_schema.SchemaAugmentations>>>() => T;
31
+
32
+ declare function createHead<T extends {} = Head>(options?: CreateHeadOptions): HeadClient<T>;
33
+
34
+ declare function defineHeadPlugin(plugin: HeadPlugin): HeadPlugin;
35
+
36
+ declare function normaliseTag<T>(tagName: HeadTag['tag'], input: HeadTag['props'], entry: HeadEntry<T>): HeadTag | HeadTag[];
37
+ declare function normaliseEntryTags<T extends {} = Head>(e: HeadEntry<T>): HeadTag[];
32
38
 
33
39
  declare type Arrayable<T> = T | Array<T>;
34
40
  declare function asArray<T>(value: Arrayable<T>): T[];
35
41
  declare const TagConfigKeys: string[];
36
- declare function hashCode(s: string): string;
37
42
 
38
- export { Arrayable, DedupesTagsPlugin, HydratesStatePlugin, SortTagsPlugin, TagConfigKeys, TitleTemplatePlugin, activeHead, asArray, createHead, getActiveHead, hashCode, setActiveHead, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useStyle, useTitle, useTitleTemplate };
43
+ export { Arrayable, DedupesTagsPlugin, DedupesTagsPluginOptions, HydratesStatePlugin, SortTagsPlugin, TagConfigKeys, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, normaliseTag, setActiveHead, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useServerHead, useStyle, useTitle, useTitleTemplate };
package/dist/index.mjs CHANGED
@@ -1,103 +1,216 @@
1
1
  import { createHooks } from 'hookable';
2
- import { tagDedupeKey, sortCriticalTags, resolveTitleTemplateFromTags, HasElementTags, ValidHeadTags, normaliseTag as normaliseTag$1 } from 'zhead';
3
2
 
4
- let activeHead;
5
- const setActiveHead = (head) => activeHead = head;
6
- const getActiveHead = () => activeHead;
3
+ function normaliseTag$1(tagName, input, options = {}) {
4
+ const tag = { tag: tagName, props: {} };
5
+ if (tagName === "title")
6
+ tag.children = String(input);
7
+ else
8
+ tag.props = normaliseProps({ ...input });
9
+ ["children", ...options?.childrenKeys || []].forEach((key) => {
10
+ if (typeof tag.props[key] !== "undefined") {
11
+ tag.children = tag.props[key];
12
+ delete tag.props[key];
13
+ }
14
+ });
15
+ return tag;
16
+ }
17
+ function normaliseProps(props) {
18
+ for (const k in props) {
19
+ if (String(props[k]) === "true") {
20
+ props[k] = "";
21
+ } else if (String(props[k]) === "false") {
22
+ delete props[k];
23
+ }
24
+ }
25
+ return props;
26
+ }
27
+ const HasElementTags = [
28
+ "base",
29
+ "meta",
30
+ "link",
31
+ "style",
32
+ "script",
33
+ "noscript"
34
+ ];
35
+ const ValidHeadTags = [
36
+ "title",
37
+ "titleTemplate",
38
+ "base",
39
+ "htmlAttrs",
40
+ "bodyAttrs",
41
+ "meta",
42
+ "link",
43
+ "style",
44
+ "script",
45
+ "noscript"
46
+ ];
7
47
 
8
- function defineHeadPlugin(plugin) {
9
- return plugin;
48
+ const sortCriticalTags = (aTag, bTag) => {
49
+ const tagWeight = (tag) => {
50
+ switch (tag.tag) {
51
+ case "base":
52
+ return -1;
53
+ case "title":
54
+ return 1;
55
+ case "meta":
56
+ if (tag.props.charset)
57
+ return -2;
58
+ if (tag.props["http-equiv"] === "content-security-policy")
59
+ return 0;
60
+ return 10;
61
+ default:
62
+ return 10;
63
+ }
64
+ };
65
+ return tagWeight(aTag) - tagWeight(bTag);
66
+ };
67
+
68
+ function tagDedupeKey(tag) {
69
+ const { props, tag: tagName } = tag;
70
+ if (["base", "title", "titleTemplate", "bodyAttrs", "htmlAttrs"].includes(tagName))
71
+ return tagName;
72
+ if (tagName === "link" && props.rel === "canonical")
73
+ return "canonical";
74
+ if (props.charset)
75
+ return "charset";
76
+ const name = ["id"];
77
+ if (tagName === "meta")
78
+ name.push(...["name", "property", "http-equiv"]);
79
+ for (const n of name) {
80
+ if (typeof props[n] !== "undefined") {
81
+ return `${tagName}:${n}:${props[n]}`;
82
+ }
83
+ }
84
+ return false;
10
85
  }
11
86
 
12
- const DedupesTagsPlugin = defineHeadPlugin({
13
- hooks: {
14
- "tag:normalise": function({ tag }) {
15
- ["hid", "vmid", "key"].forEach((key) => {
16
- if (tag.props[key]) {
17
- tag.key = tag.props[key];
18
- delete tag.props[key];
19
- }
20
- });
21
- const dedupe = tag.key ? `${tag.tag}:${tag.key}` : tagDedupeKey(tag);
22
- if (dedupe)
23
- tag._d = dedupe;
24
- },
25
- "tags:resolve": function(ctx) {
26
- const deduping = {};
27
- ctx.tags.forEach((tag, i) => {
28
- let dedupeKey = tag._d || tag._p || i;
29
- const dupedTag = deduping[dedupeKey];
30
- if (dupedTag) {
31
- let strategy = tag?.tagDuplicateStrategy;
32
- if (!strategy && (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs"))
33
- strategy = "merge";
34
- if (strategy === "merge") {
35
- const oldProps = dupedTag.props;
36
- ["class", "style"].forEach((key) => {
37
- if (tag.props[key] && oldProps[key])
38
- tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
39
- });
40
- deduping[dedupeKey].props = {
41
- ...oldProps,
42
- ...tag.props
43
- };
44
- return;
45
- } else if (tag._e === dupedTag._e) {
46
- dedupeKey = `${dedupeKey}:entry(${tag._e}:${tag._p})`;
47
- tag._d = dedupeKey;
48
- } else {
49
- tag._p = dupedTag._p;
87
+ const renderTitleTemplate = (template, title) => {
88
+ if (template == null)
89
+ return title || null;
90
+ if (typeof template === "function")
91
+ return template(title);
92
+ return template.replace("%s", title ?? "");
93
+ };
94
+ function resolveTitleTemplateFromTags(tags) {
95
+ const titleTemplateIdx = tags.findIndex((i) => i.tag === "titleTemplate");
96
+ const titleIdx = tags.findIndex((i) => i.tag === "title");
97
+ if (titleIdx !== -1 && titleTemplateIdx !== -1) {
98
+ const newTitle = renderTitleTemplate(
99
+ tags[titleTemplateIdx].children,
100
+ tags[titleIdx].children
101
+ );
102
+ if (newTitle !== null) {
103
+ tags[titleIdx].children = newTitle || tags[titleIdx].children;
104
+ } else {
105
+ tags = tags.filter((_, i) => i !== titleIdx);
106
+ }
107
+ } else if (titleTemplateIdx !== -1) {
108
+ const newTitle = renderTitleTemplate(
109
+ tags[titleTemplateIdx].children
110
+ );
111
+ if (newTitle !== null) {
112
+ tags[titleTemplateIdx].children = newTitle;
113
+ tags[titleTemplateIdx].tag = "title";
114
+ }
115
+ }
116
+ if (titleTemplateIdx !== -1)
117
+ tags = tags.filter((_, i) => i !== titleTemplateIdx);
118
+ return tags;
119
+ }
120
+
121
+ const DedupesTagsPlugin = (options) => {
122
+ options = options || {};
123
+ const dedupeKeys = options.dedupeKeys || ["hid", "vmid", "key"];
124
+ return defineHeadPlugin({
125
+ hooks: {
126
+ "tag:normalise": function({ tag }) {
127
+ dedupeKeys.forEach((key) => {
128
+ if (tag.props[key]) {
129
+ tag.key = tag.props[key];
130
+ delete tag.props[key];
50
131
  }
51
- if (Object.keys(tag.props).length === 0 && !tag.children) {
52
- delete deduping[dedupeKey];
53
- return;
132
+ });
133
+ const dedupe = tag.key ? `${tag.tag}:${tag.key}` : tagDedupeKey(tag);
134
+ if (dedupe)
135
+ tag._d = dedupe;
136
+ },
137
+ "tags:resolve": function(ctx) {
138
+ const deduping = {};
139
+ ctx.tags.forEach((tag, i) => {
140
+ let dedupeKey = tag._d || tag._p || i;
141
+ const dupedTag = deduping[dedupeKey];
142
+ if (dupedTag) {
143
+ let strategy = tag?.tagDuplicateStrategy;
144
+ if (!strategy && (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs"))
145
+ strategy = "merge";
146
+ if (strategy === "merge") {
147
+ const oldProps = dupedTag.props;
148
+ ["class", "style"].forEach((key) => {
149
+ if (tag.props[key] && oldProps[key])
150
+ tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
151
+ });
152
+ deduping[dedupeKey].props = {
153
+ ...oldProps,
154
+ ...tag.props
155
+ };
156
+ return;
157
+ } else if (tag._e === dupedTag._e) {
158
+ dedupeKey = `${dedupeKey}:entry(${tag._e}:${tag._p})`;
159
+ tag._d = dedupeKey;
160
+ } else {
161
+ tag._p = dupedTag._p;
162
+ }
163
+ if (Object.keys(tag.props).length === 0 && !tag.children) {
164
+ delete deduping[dedupeKey];
165
+ return;
166
+ }
54
167
  }
55
- }
56
- deduping[dedupeKey] = tag;
57
- });
58
- ctx.tags = Object.values(deduping);
168
+ deduping[dedupeKey] = tag;
169
+ });
170
+ ctx.tags = Object.values(deduping);
171
+ }
59
172
  }
60
- }
61
- });
173
+ });
174
+ };
62
175
 
63
- const SortTagsPlugin = defineHeadPlugin({
64
- hooks: {
65
- "tags:resolve": (ctx) => {
66
- const tagIndexForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
67
- for (const tag of ctx.tags) {
68
- if (!tag?.tagPriority)
69
- continue;
70
- if (typeof tag.tagPriority === "number") {
71
- tag._p = tag.tagPriority;
72
- continue;
73
- }
74
- const modifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
75
- for (const { prefix, offset } of modifiers) {
76
- if (tag.tagPriority.startsWith(prefix)) {
77
- const key = tag.tagPriority.replace(prefix, "");
78
- const index = tagIndexForKey(key);
79
- if (typeof index !== "undefined")
80
- tag._p = index + offset;
176
+ const SortTagsPlugin = () => {
177
+ return defineHeadPlugin({
178
+ hooks: {
179
+ "tags:resolve": (ctx) => {
180
+ const tagIndexForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
181
+ for (const tag of ctx.tags) {
182
+ if (!tag?.tagPriority)
183
+ continue;
184
+ if (typeof tag.tagPriority === "number") {
185
+ tag._p = tag.tagPriority;
186
+ continue;
187
+ }
188
+ const modifiers = [{ prefix: "before:", offset: -1 }, { prefix: "after:", offset: 1 }];
189
+ for (const { prefix, offset } of modifiers) {
190
+ if (tag.tagPriority.startsWith(prefix)) {
191
+ const key = tag.tagPriority.replace(prefix, "");
192
+ const index = tagIndexForKey(key);
193
+ if (typeof index !== "undefined")
194
+ tag._p = index + offset;
195
+ }
81
196
  }
82
197
  }
198
+ ctx.tags.sort((a, b) => a._p - b._p).sort(sortCriticalTags);
83
199
  }
84
- ctx.tags.sort((a, b) => a._p - b._p).sort(sortCriticalTags);
85
200
  }
86
- }
87
- });
201
+ });
202
+ };
88
203
 
89
- const TitleTemplatePlugin = defineHeadPlugin({
90
- hooks: {
91
- "tags:resolve": (ctx) => {
92
- ctx.tags = resolveTitleTemplateFromTags(ctx.tags);
204
+ const TitleTemplatePlugin = () => {
205
+ return defineHeadPlugin({
206
+ hooks: {
207
+ "tags:resolve": (ctx) => {
208
+ ctx.tags = resolveTitleTemplateFromTags(ctx.tags);
209
+ }
93
210
  }
94
- }
95
- });
211
+ });
212
+ };
96
213
 
97
- function asArray(value) {
98
- return Array.isArray(value) ? value : [value];
99
- }
100
- const TagConfigKeys = ["tagPosition", "tagPriority", "tagDuplicateStrategy"];
101
214
  function hashCode(s) {
102
215
  let h = 9;
103
216
  for (let i = 0; i < s.length; )
@@ -105,20 +218,73 @@ function hashCode(s) {
105
218
  return ((h ^ h >>> 9) + 65536).toString(16).substring(1, 7).toLowerCase();
106
219
  }
107
220
 
108
- const HydratesStatePlugin = defineHeadPlugin({
109
- hooks: {
110
- "tag:normalise": (ctx) => {
111
- const { tag, entry } = ctx;
112
- if (!HasElementTags.includes(tag.tag))
113
- return;
114
- if (typeof tag._d === "undefined" && entry._m === "server")
115
- return;
116
- const hasChildren = tag.children && tag.children.length > 0;
117
- tag._s = `data-h-${hashCode(tag._d || tag.tag + (hasChildren ? tag.children : JSON.stringify(tag.props)))}`;
118
- tag.props[tag._s] = "";
221
+ const HydratesStatePlugin = () => {
222
+ return defineHeadPlugin({
223
+ hooks: {
224
+ "tag:normalise": (ctx) => {
225
+ const { tag, entry } = ctx;
226
+ if (!HasElementTags.includes(tag.tag))
227
+ return;
228
+ if (typeof tag._d === "undefined" && entry._m === "server")
229
+ return;
230
+ const hasChildren = tag.children && tag.children.length > 0;
231
+ tag._s = `data-h-${hashCode(tag._d || tag.tag + (hasChildren ? tag.children : JSON.stringify(tag.props)))}`;
232
+ tag.props[tag._s] = "";
233
+ }
119
234
  }
120
- }
121
- });
235
+ });
236
+ };
237
+
238
+ const IsClient = typeof window !== "undefined";
239
+
240
+ let activeHead;
241
+ const setActiveHead = (head) => activeHead = head;
242
+ const getActiveHead = () => activeHead;
243
+
244
+ function useHead(input, options = {}) {
245
+ if (options.mode === "server" && IsClient || options.mode === "client" && !IsClient)
246
+ return;
247
+ const head = getActiveHead();
248
+ head.push(input, options);
249
+ }
250
+ function useServerHead(input, options = {}) {
251
+ useHead(input, { ...options, mode: "server" });
252
+ }
253
+ const useTitle = (title) => {
254
+ useHead({ title });
255
+ };
256
+ const useMeta = (meta) => {
257
+ useHead({ meta: [meta] });
258
+ };
259
+ const useLink = (link) => {
260
+ useHead({ link: [link] });
261
+ };
262
+ const useScript = (script) => {
263
+ useHead({ script: [script] });
264
+ };
265
+ const useStyle = (style) => {
266
+ useHead({ style: [style] });
267
+ };
268
+ const useBase = (base) => {
269
+ useHead({ base });
270
+ };
271
+ const useHtmlAttrs = (attrs) => {
272
+ useHead({ htmlAttrs: attrs });
273
+ };
274
+ const useBodyAttrs = (attrs) => {
275
+ useHead({ bodyAttrs: attrs });
276
+ };
277
+ const useTitleTemplate = (titleTemplate) => {
278
+ useHead({ titleTemplate });
279
+ };
280
+ const useNoscript = (noscript) => {
281
+ useHead({ noscript: [noscript] });
282
+ };
283
+
284
+ function asArray(value) {
285
+ return Array.isArray(value) ? value : [value];
286
+ }
287
+ const TagConfigKeys = ["tagPosition", "tagPriority", "tagDuplicateStrategy"];
122
288
 
123
289
  function normaliseTag(tagName, input, entry) {
124
290
  const tag = normaliseTag$1(tagName, input, { childrenKeys: ["innerHTML", "textContent"] });
@@ -159,9 +325,9 @@ function createHead(options = {}) {
159
325
  if (options.hooks)
160
326
  hooks.addHooks(options.hooks);
161
327
  const plugins = [
162
- DedupesTagsPlugin,
163
- SortTagsPlugin,
164
- TitleTemplatePlugin
328
+ DedupesTagsPlugin(),
329
+ SortTagsPlugin(),
330
+ TitleTemplatePlugin()
165
331
  ];
166
332
  plugins.push(...options.plugins || []);
167
333
  plugins.forEach((plugin) => hooks.addHooks(plugin.hooks || {}));
@@ -227,39 +393,8 @@ function createHead(options = {}) {
227
393
  return head;
228
394
  }
229
395
 
230
- function useHead(input, options = {}) {
231
- const head = getActiveHead();
232
- head.push(input, options);
396
+ function defineHeadPlugin(plugin) {
397
+ return plugin;
233
398
  }
234
- const useTitle = (title) => {
235
- useHead({ title });
236
- };
237
- const useMeta = (meta) => {
238
- useHead({ meta: [meta] });
239
- };
240
- const useLink = (link) => {
241
- useHead({ link: [link] });
242
- };
243
- const useScript = (script) => {
244
- useHead({ script: [script] });
245
- };
246
- const useStyle = (style) => {
247
- useHead({ style: [style] });
248
- };
249
- const useBase = (base) => {
250
- useHead({ base });
251
- };
252
- const useHtmlAttrs = (attrs) => {
253
- useHead({ htmlAttrs: attrs });
254
- };
255
- const useBodyAttrs = (attrs) => {
256
- useHead({ bodyAttrs: attrs });
257
- };
258
- const useTitleTemplate = (titleTemplate) => {
259
- useHead({ titleTemplate });
260
- };
261
- const useNoscript = (noscript) => {
262
- useHead({ noscript: [noscript] });
263
- };
264
399
 
265
- export { DedupesTagsPlugin, HydratesStatePlugin, SortTagsPlugin, TagConfigKeys, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, hashCode, setActiveHead, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useStyle, useTitle, useTitleTemplate };
400
+ export { DedupesTagsPlugin, HydratesStatePlugin, SortTagsPlugin, TagConfigKeys, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, normaliseTag, setActiveHead, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useServerHead, useStyle, useTitle, useTitleTemplate };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unhead",
3
3
  "type": "module",
4
- "version": "0.0.7",
4
+ "version": "0.1.0",
5
5
  "packageManager": "pnpm@7.14.0",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -20,14 +20,6 @@
20
20
  ".": {
21
21
  "types": "./dist/index.d.ts",
22
22
  "import": "./dist/index.mjs"
23
- },
24
- "./server": {
25
- "types": "./dist/server.d.ts",
26
- "import": "./dist/server.mjs"
27
- },
28
- "./client": {
29
- "types": "./dist/client.d.ts",
30
- "import": "./dist/client.mjs"
31
23
  }
32
24
  },
33
25
  "module": "dist/index.mjs",
@@ -36,12 +28,15 @@
36
28
  "dist"
37
29
  ],
38
30
  "dependencies": {
39
- "@unhead/schema": "0.0.7",
40
- "hookable": "^5.4.1",
31
+ "@unhead/schema": "0.1.0",
32
+ "hookable": "^5.4.1"
33
+ },
34
+ "devDependencies": {
41
35
  "zhead": "1.0.0-beta.4"
42
36
  },
43
37
  "scripts": {
44
38
  "build": "unbuild .",
45
- "stub": "unbuild . --stub"
39
+ "stub": "unbuild . --stub",
40
+ "export:sizes": "npx export-size . -r"
46
41
  }
47
42
  }
package/dist/client.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import { a as HeadClient } from './types-b4318c02.js';
2
- import 'hookable';
3
- import '@unhead/schema';
4
-
5
- interface RenderDomHeadOptions {
6
- /**
7
- * Document to use for rendering. Allows stubbing for testing.
8
- */
9
- document?: Document;
10
- }
11
- /**
12
- * Render the head tags to the DOM.
13
- */
14
- declare function renderDOMHead<T extends HeadClient<any>>(head: T, options?: RenderDomHeadOptions): Promise<void>;
15
- /**
16
- * Global instance of the dom update promise. Used for debounding head updates.
17
- */
18
- declare let domUpdatePromise: Promise<void> | null;
19
- /**
20
- * Queue a debounced update of the DOM head.
21
- */
22
- declare function debouncedRenderDOMHead<T extends HeadClient<any>>(delayedFn: (fn: () => void) => void, head: T, options?: RenderDomHeadOptions): Promise<void>;
23
-
24
- export { RenderDomHeadOptions, debouncedRenderDOMHead, domUpdatePromise, renderDOMHead };
package/dist/client.mjs DELETED
@@ -1,89 +0,0 @@
1
- import { createElement, TagsWithInnerContent } from 'zhead';
2
-
3
- function setAttributesWithSideEffects(head, $el, entry, tag) {
4
- const attrs = tag.props || {};
5
- const sdeKey = `${tag._p}:attr`;
6
- Object.entries(entry._sde).filter(([key]) => key.startsWith(sdeKey)).forEach(([key, fn]) => {
7
- delete entry._sde[key] && fn();
8
- });
9
- Object.entries(attrs).forEach(([k, value]) => {
10
- value = String(value);
11
- const attrSdeKey = `${sdeKey}:${k}`;
12
- head._removeQueuedSideEffect(attrSdeKey);
13
- if (k === "class") {
14
- for (const c of value.split(" ")) {
15
- if (!$el.classList.contains(c)) {
16
- $el.classList.add(c);
17
- head._removeQueuedSideEffect(`${attrSdeKey}:${c}`);
18
- entry._sde[`${attrSdeKey}:${c}`] = () => $el.classList.remove(c);
19
- }
20
- }
21
- return;
22
- }
23
- if ($el.getAttribute(k) !== value) {
24
- $el.setAttribute(k, value);
25
- if (!k.startsWith("data-h-"))
26
- entry._sde[attrSdeKey] = () => $el.removeAttribute(k);
27
- }
28
- });
29
- }
30
-
31
- async function renderDOMHead(head, options = {}) {
32
- const dom = options.document || window.document;
33
- const tags = await head.resolveTags();
34
- await head.hooks.callHook("dom:beforeRender", { head, tags, document: dom });
35
- for (const tag of tags) {
36
- const entry = head.headEntries().find((e) => e._i === Number(tag._e));
37
- const sdeKey = `${tag._s || tag._p}:el`;
38
- const $newEl = createElement(tag, dom);
39
- const $el = tag._s ? dom.querySelector(`[${tag._s}]`) : null;
40
- const renderCtx = { tag, document: dom, head };
41
- await head.hooks.callHook("dom:renderTag", renderCtx);
42
- if ($el) {
43
- head._removeQueuedSideEffect(sdeKey);
44
- if ($newEl.isEqualNode($el))
45
- continue;
46
- if (Object.keys(tag.props).length === 0) {
47
- $el.remove();
48
- continue;
49
- }
50
- setAttributesWithSideEffects(head, $el, entry, tag);
51
- if (TagsWithInnerContent.includes(tag.tag))
52
- $el.innerHTML = tag.children || "";
53
- entry._sde[sdeKey] = () => $el?.remove();
54
- continue;
55
- }
56
- if (tag.tag === "title" && tag.children) {
57
- dom.title = tag.children;
58
- continue;
59
- }
60
- if (tag.tag === "htmlAttrs" || tag.tag === "bodyAttrs") {
61
- setAttributesWithSideEffects(head, dom[tag.tag === "htmlAttrs" ? "documentElement" : "body"], entry, tag);
62
- continue;
63
- }
64
- switch (tag.tagPosition) {
65
- case "bodyClose":
66
- dom.body.appendChild($newEl);
67
- break;
68
- case "bodyOpen":
69
- dom.body.insertBefore($newEl, dom.body.firstChild);
70
- break;
71
- case "head":
72
- default:
73
- dom.head.appendChild($newEl);
74
- break;
75
- }
76
- entry._sde[sdeKey] = () => $newEl?.remove();
77
- }
78
- head._flushQueuedSideEffects();
79
- }
80
- let domUpdatePromise = null;
81
- async function debouncedRenderDOMHead(delayedFn, head, options = {}) {
82
- function doDomUpdate() {
83
- domUpdatePromise = null;
84
- return renderDOMHead(head, options);
85
- }
86
- return domUpdatePromise = domUpdatePromise || new Promise((resolve) => delayedFn(() => resolve(doDomUpdate())));
87
- }
88
-
89
- export { debouncedRenderDOMHead, domUpdatePromise, renderDOMHead };
package/dist/server.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { g as SSRHeadPayload, r as renderSSRHead } from './types-b4318c02.js';
2
- import 'hookable';
3
- import '@unhead/schema';
package/dist/server.mjs DELETED
@@ -1,13 +0,0 @@
1
- import { ssrRenderTags } from 'zhead';
2
-
3
- async function renderSSRHead(ctx) {
4
- const tags = await ctx.resolveTags();
5
- const beforeRenderCtx = { tags };
6
- await ctx.hooks.callHook("ssr:beforeRender", beforeRenderCtx);
7
- const html = ssrRenderTags(beforeRenderCtx.tags);
8
- const renderCXx = { tags, html };
9
- await ctx.hooks.callHook("ssr:render", renderCXx);
10
- return renderCXx.html;
11
- }
12
-
13
- export { renderSSRHead };
@@ -1,131 +0,0 @@
1
- import { NestedHooks, Hookable } from 'hookable';
2
- import { Head, HeadTag } from '@unhead/schema';
3
-
4
- declare type HeadPlugin<O> = Omit<CreateHeadOptions<O>, 'plugins'>;
5
- declare function defineHeadPlugin<O = Head>(plugin: HeadPlugin<O>): HeadPlugin<O>;
6
-
7
- interface SSRHeadPayload {
8
- headTags: string;
9
- bodyTags: string;
10
- bodyTagsOpen: string;
11
- htmlAttrs: string;
12
- bodyAttrs: string;
13
- }
14
- declare function renderSSRHead<T extends HeadClient<any>>(ctx: T): Promise<SSRHeadPayload>;
15
-
16
- /**
17
- * An active head entry provides an API to manipulate it.
18
- */
19
- interface ActiveHeadEntry<T> {
20
- /**
21
- * Updates the entry with new input.
22
- *
23
- * Will first clear any side effects for previous input.
24
- */
25
- patch: (resolvedInput: T) => void;
26
- /**
27
- * Dispose the entry, removing it from the active head.
28
- *
29
- * Will queue side effects for removal.
30
- */
31
- dispose: () => void;
32
- }
33
- /**
34
- * Side effects are mapped with a key and their cleanup function.
35
- *
36
- * For example `meta:data-h-4h46h465`: () => { document.querySelector('meta[data-h-4h46h465]').remove() }
37
- */
38
- declare type SideEffectsRecord = Record<string, () => void>;
39
- declare type RuntimeMode = 'server' | 'client' | 'all';
40
- interface HeadEntry<T> {
41
- /**
42
- * User provided input for the entry.
43
- */
44
- input: T;
45
- /**
46
- * The mode that the entry should be used in.
47
- *
48
- * @internal
49
- */
50
- _m?: RuntimeMode;
51
- /**
52
- * Head entry index
53
- *
54
- * @internal
55
- */
56
- _i: number;
57
- /**
58
- * Side effects
59
- *
60
- * @internal
61
- */
62
- _sde: SideEffectsRecord;
63
- }
64
- declare type HookResult = Promise<void> | void;
65
- interface DomRenderTagContext {
66
- head: HeadClient;
67
- tag: HeadTag;
68
- document: Document;
69
- }
70
- interface EntryResolveCtx<T> {
71
- tags: HeadTag[];
72
- entries: HeadEntry<T>[];
73
- }
74
- interface HeadHooks<T> {
75
- 'entries:resolve': (ctx: EntryResolveCtx<T>) => HookResult;
76
- 'tag:normalise': (ctx: {
77
- tag: HeadTag;
78
- entry: HeadEntry<T>;
79
- }) => HookResult;
80
- 'tags:resolve': (ctx: {
81
- tags: HeadTag[];
82
- }) => HookResult;
83
- 'dom:beforeRender': (ctx: {
84
- head: HeadClient;
85
- tags: HeadTag[];
86
- document: Document;
87
- }) => HookResult;
88
- 'dom:renderTag': (ctx: DomRenderTagContext) => HookResult;
89
- 'ssr:beforeRender': (ctx: {
90
- tags: HeadTag[];
91
- }) => HookResult;
92
- 'ssr:render': (ctx: {
93
- tags: HeadTag[];
94
- html: SSRHeadPayload;
95
- }) => HookResult;
96
- }
97
- interface CreateHeadOptions<T> {
98
- plugins?: HeadPlugin<any>[];
99
- hooks?: NestedHooks<HeadHooks<T>>;
100
- }
101
- interface HeadEntryOptions {
102
- mode?: RuntimeMode;
103
- }
104
- interface HeadClient<T = Head> {
105
- /**
106
- * The active head entries.
107
- */
108
- headEntries: () => HeadEntry<T>[];
109
- /**
110
- * Create a new head entry.
111
- */
112
- push: (entry: T, options?: HeadEntryOptions) => ActiveHeadEntry<T>;
113
- /**
114
- * Resolve tags from head entries.
115
- */
116
- resolveTags: () => Promise<HeadTag[]>;
117
- /**
118
- * Exposed hooks for easier extension.
119
- */
120
- hooks: Hookable<HeadHooks<T>>;
121
- /**
122
- * @internal
123
- */
124
- _removeQueuedSideEffect: (key: string) => void;
125
- /**
126
- * @internal
127
- */
128
- _flushQueuedSideEffects: () => void;
129
- }
130
-
131
- export { ActiveHeadEntry as A, CreateHeadOptions as C, DomRenderTagContext as D, EntryResolveCtx as E, HeadPlugin as H, RuntimeMode as R, SideEffectsRecord as S, HeadClient as a, HeadEntryOptions as b, HeadEntry as c, defineHeadPlugin as d, HookResult as e, HeadHooks as f, SSRHeadPayload as g, renderSSRHead as r };