@unhead/vue 0.1.4 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,21 +26,16 @@ function asArray(value) {
26
26
  return Array.isArray(value) ? value : [value];
27
27
  }
28
28
 
29
- const VueTriggerDomPatchingOnUpdatesPlugin = () => {
29
+ const VueTriggerDomPatchingOnUpdatesPlugin = (options) => {
30
30
  return unhead.defineHeadPlugin({
31
31
  hooks: {
32
32
  "entries:updated": function(head) {
33
- dom.debouncedRenderDOMHead(vue.nextTick, head);
33
+ dom.debouncedRenderDOMHead(vue.nextTick, head, { document: options?.document });
34
34
  }
35
35
  }
36
36
  });
37
37
  };
38
38
 
39
- const vueTriggerDomPatchingOnUpdatesPlugin = {
40
- __proto__: null,
41
- VueTriggerDomPatchingOnUpdatesPlugin: VueTriggerDomPatchingOnUpdatesPlugin
42
- };
43
-
44
39
  const VueReactiveInputPlugin = () => {
45
40
  return unhead.defineHeadPlugin({
46
41
  hooks: {
@@ -52,20 +47,151 @@ const VueReactiveInputPlugin = () => {
52
47
  });
53
48
  };
54
49
 
55
- const Vue3 = vue.version.startsWith("3");
56
- vue.version.startsWith("2.");
57
- const IsBrowser = typeof window !== "undefined";
50
+ function unpackToArray(input, options) {
51
+ const unpacked = [];
52
+ const kFn = options.resolveKeyData || ((ctx) => ctx.key);
53
+ const vFn = options.resolveValueData || ((ctx) => ctx.value);
54
+ for (const [k, v] of Object.entries(input)) {
55
+ unpacked.push(...(Array.isArray(v) ? v : [v]).map((i) => {
56
+ const ctx = { key: k, value: i };
57
+ const val = vFn(ctx);
58
+ if (typeof val === "object")
59
+ return unpackToArray(val, options);
60
+ if (Array.isArray(val))
61
+ return val;
62
+ return {
63
+ [typeof options.key === "function" ? options.key(ctx) : options.key]: kFn(ctx),
64
+ [typeof options.value === "function" ? options.value(ctx) : options.value]: val
65
+ };
66
+ }).flat());
67
+ }
68
+ return unpacked;
69
+ }
58
70
 
59
- function useHead$2(input, options = {}) {
60
- const head = injectHead();
61
- head.push(input, options);
71
+ function unpackToString(value, options) {
72
+ return Object.entries(value).map(([key, value2]) => {
73
+ if (typeof value2 === "object")
74
+ value2 = unpackToString(value2, options);
75
+ if (options.resolve) {
76
+ const resolved = options.resolve({ key, value: value2 });
77
+ if (resolved)
78
+ return resolved;
79
+ }
80
+ if (typeof value2 === "number")
81
+ value2 = value2.toString();
82
+ if (typeof value2 === "string" && options.wrapValue) {
83
+ value2 = value2.replace(new RegExp(options.wrapValue, "g"), `\\${options.wrapValue}`);
84
+ value2 = `${options.wrapValue}${value2}${options.wrapValue}`;
85
+ }
86
+ return `${key}${options.keyValueSeparator || ""}${value2}`;
87
+ }).join(options.entrySeparator || "");
62
88
  }
63
89
 
64
- function useServerHead$1(input, options = {}) {
65
- useHead$2(input, { ...options, mode: "server" });
90
+ const MetaPackingSchema = {
91
+ robots: {
92
+ unpack: {
93
+ keyValueSeparator: ":"
94
+ }
95
+ },
96
+ contentSecurityPolicy: {
97
+ unpack: {
98
+ keyValueSeparator: " ",
99
+ entrySeparator: "; "
100
+ },
101
+ metaKey: "http-equiv"
102
+ },
103
+ fbAppId: {
104
+ keyValue: "fb:app_id",
105
+ metaKey: "property"
106
+ },
107
+ msapplicationTileImage: {
108
+ keyValue: "msapplication-TileImage"
109
+ },
110
+ msapplicationTileColor: {
111
+ keyValue: "msapplication-TileColor"
112
+ },
113
+ msapplicationConfig: {
114
+ keyValue: "msapplication-Config"
115
+ },
116
+ charset: {
117
+ metaKey: "charset"
118
+ },
119
+ contentType: {
120
+ metaKey: "http-equiv"
121
+ },
122
+ defaultStyle: {
123
+ metaKey: "http-equiv"
124
+ },
125
+ xUaCompatible: {
126
+ metaKey: "http-equiv"
127
+ },
128
+ refresh: {
129
+ metaKey: "http-equiv"
130
+ }
131
+ };
132
+ function resolveMetaKeyType(key) {
133
+ return PropertyPrefixKeys.test(key) ? "property" : MetaPackingSchema[key]?.metaKey || "name";
134
+ }
135
+
136
+ function unpackMeta(input) {
137
+ return unpackToArray(input, {
138
+ key({ key }) {
139
+ return resolveMetaKeyType(key);
140
+ },
141
+ value({ key }) {
142
+ return key === "charset" ? "charset" : "content";
143
+ },
144
+ resolveKeyData({ key }) {
145
+ return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
146
+ },
147
+ resolveValueData({ value, key }) {
148
+ if (typeof value === "object") {
149
+ const definition = MetaPackingSchema[key];
150
+ if (key === "refresh")
151
+ return `${value.seconds};url=${value.url}`;
152
+ return unpackToString(
153
+ changeKeyCasingDeep(value),
154
+ {
155
+ entrySeparator: ", ",
156
+ keyValueSeparator: "=",
157
+ resolve({ value: value2, key: key2 }) {
158
+ if (typeof value2 === "boolean")
159
+ return `${key2}`;
160
+ },
161
+ ...definition?.unpack
162
+ }
163
+ );
164
+ }
165
+ return typeof value === "number" ? value.toString() : value;
166
+ }
167
+ });
168
+ }
169
+
170
+ const PropertyPrefixKeys = /^(og|twitter|fb)/;
171
+ function fixKeyCase(key) {
172
+ key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
173
+ if (PropertyPrefixKeys.test(key)) {
174
+ key = key.replace("secure-url", "secure_url").replace(/-/g, ":");
175
+ }
176
+ return key;
66
177
  }
178
+ function changeKeyCasingDeep(input) {
179
+ if (Array.isArray(input)) {
180
+ return input.map((entry) => changeKeyCasingDeep(entry));
181
+ }
182
+ if (typeof input !== "object" || Array.isArray(input))
183
+ return input;
184
+ const output = {};
185
+ for (const [key, value] of Object.entries(input))
186
+ output[fixKeyCase(key)] = changeKeyCasingDeep(value);
187
+ return output;
188
+ }
189
+
190
+ const Vue3 = vue.version.startsWith("3");
191
+ vue.version.startsWith("2.");
192
+ const IsBrowser = typeof window !== "undefined";
67
193
 
68
- function useHead$1(input, options = {}) {
194
+ function clientUseHead(input, options = {}) {
69
195
  const head = injectHead();
70
196
  const vm = vue.getCurrentInstance();
71
197
  if (!vm) {
@@ -88,41 +214,66 @@ function useHead$1(input, options = {}) {
88
214
  });
89
215
  }
90
216
 
217
+ function serverUseHead(input, options = {}) {
218
+ const head = injectHead();
219
+ head.push(input, options);
220
+ }
221
+
91
222
  function useServerHead(input, options = {}) {
92
- if (!IsBrowser)
93
- useServerHead$1(input, options);
223
+ useHead(input, { ...options, mode: "server" });
94
224
  }
225
+ const useServerTagTitle = (title) => useServerHead({ title });
226
+ const useServerTitleTemplate = (titleTemplate) => useServerHead({ titleTemplate });
227
+ const useServerTagMeta = (meta) => useServerHead({ meta: asArray(meta) });
228
+ const useServerTagMetaFlat = (meta) => {
229
+ const input = vue.ref({});
230
+ vue.watchEffect(() => {
231
+ input.value = unpackMeta(resolveUnrefHeadInput(meta));
232
+ });
233
+ return useServerHead({ meta: input });
234
+ };
235
+ const useServerTagLink = (link) => useServerHead({ link: asArray(link) });
236
+ const useServerTagScript = (script) => useServerHead({ script: asArray(script) });
237
+ const useServerTagStyle = (style) => useServerHead({ style: asArray(style) });
238
+ const useServerTagNoscript = (noscript) => useServerHead({ noscript: asArray(noscript) });
239
+ const useServerTagBase = (base) => useServerHead({ base });
240
+ const useServerHtmlAttrs = (attrs) => useServerHead({ htmlAttrs: attrs });
241
+ const useServerBodyAttrs = (attrs) => useHead({ bodyAttrs: attrs });
242
+
95
243
  function useHead(input, options = {}) {
96
244
  if (options.mode === "server" && IsBrowser || options.mode === "client" && !IsBrowser)
97
245
  return;
98
- IsBrowser ? useHead$1(input, options) : useHead$2(input, options);
246
+ IsBrowser ? clientUseHead(input, options) : serverUseHead(input, options);
99
247
  }
100
- const useTitle = (title) => useHead({ title });
248
+ const useTagTitle = (title) => useHead({ title });
101
249
  const useTitleTemplate = (titleTemplate) => useHead({ titleTemplate });
102
- const useMeta = (meta) => useHead({ meta: asArray(meta) });
103
- const useLink = (link) => useHead({ link: asArray(link) });
104
- const useScript = (script) => useHead({ script: asArray(script) });
105
- const useStyle = (style) => useHead({ style: asArray(style) });
106
- const useNoscript = (noscript) => useHead({ noscript: asArray(noscript) });
107
- const useBase = (base) => useHead({ base });
250
+ const useTagMeta = (meta) => useHead({ meta: asArray(meta) });
251
+ const useTagMetaFlat = (meta) => {
252
+ const input = vue.ref({});
253
+ vue.watchEffect(() => {
254
+ input.value = unpackMeta(resolveUnrefHeadInput(meta));
255
+ });
256
+ return useHead({ meta: input });
257
+ };
258
+ const useTagLink = (link) => useHead({ link: asArray(link) });
259
+ const useTagScript = (script) => useHead({ script: asArray(script) });
260
+ const useTagStyle = (style) => useHead({ style: asArray(style) });
261
+ const useTagNoscript = (noscript) => useHead({ noscript: asArray(noscript) });
262
+ const useTagBase = (base) => useHead({ base });
108
263
  const useHtmlAttrs = (attrs) => useHead({ htmlAttrs: attrs });
109
264
  const useBodyAttrs = (attrs) => useHead({ bodyAttrs: attrs });
110
265
 
111
- const headSymbol = Symbol("unhead");
266
+ const headSymbol = "usehead";
112
267
  function injectHead() {
113
268
  return vue.getCurrentInstance() && vue.inject(headSymbol) || unhead.getActiveHead();
114
269
  }
115
- async function createHead(options = {}) {
270
+ function createHead(options = {}) {
116
271
  const plugins = [
117
272
  unhead.HydratesStatePlugin(),
118
273
  VueReactiveInputPlugin(),
119
274
  ...options?.plugins || []
120
275
  ];
121
- if (IsBrowser) {
122
- const { VueTriggerDomPatchingOnUpdatesPlugin } = await Promise.resolve().then(function () { return vueTriggerDomPatchingOnUpdatesPlugin; });
123
- plugins.push(VueTriggerDomPatchingOnUpdatesPlugin());
124
- }
125
- const head = await unhead.createHead({
276
+ const head = unhead.createHead({
126
277
  ...options,
127
278
  plugins
128
279
  });
@@ -148,6 +299,16 @@ async function createHead(options = {}) {
148
299
  return { ...vuePlugin, ...head };
149
300
  }
150
301
 
302
+ function createDomHead(options = {}) {
303
+ return createHead({
304
+ ...options,
305
+ plugins: [
306
+ VueTriggerDomPatchingOnUpdatesPlugin({ document: options?.document }),
307
+ ...options.plugins || []
308
+ ]
309
+ });
310
+ }
311
+
151
312
  const HeadVuePlugin = function(_Vue) {
152
313
  _Vue.mixin({
153
314
  beforeCreate() {
@@ -178,21 +339,34 @@ exports.HeadVuePlugin = HeadVuePlugin;
178
339
  exports.VueReactiveInputPlugin = VueReactiveInputPlugin;
179
340
  exports.VueTriggerDomPatchingOnUpdatesPlugin = VueTriggerDomPatchingOnUpdatesPlugin;
180
341
  exports.asArray = asArray;
342
+ exports.createDomHead = createDomHead;
181
343
  exports.createHead = createHead;
182
344
  exports.headSymbol = headSymbol;
183
345
  exports.injectHead = injectHead;
184
346
  exports.resolveUnrefHeadInput = resolveUnrefHeadInput;
185
- exports.useBase = useBase;
186
347
  exports.useBodyAttrs = useBodyAttrs;
187
348
  exports.useHead = useHead;
188
349
  exports.useHtmlAttrs = useHtmlAttrs;
189
- exports.useLink = useLink;
190
- exports.useMeta = useMeta;
191
- exports.useNoscript = useNoscript;
192
- exports.useScript = useScript;
350
+ exports.useServerBodyAttrs = useServerBodyAttrs;
193
351
  exports.useServerHead = useServerHead;
194
- exports.useStyle = useStyle;
195
- exports.useTitle = useTitle;
352
+ exports.useServerHtmlAttrs = useServerHtmlAttrs;
353
+ exports.useServerTagBase = useServerTagBase;
354
+ exports.useServerTagLink = useServerTagLink;
355
+ exports.useServerTagMeta = useServerTagMeta;
356
+ exports.useServerTagMetaFlat = useServerTagMetaFlat;
357
+ exports.useServerTagNoscript = useServerTagNoscript;
358
+ exports.useServerTagScript = useServerTagScript;
359
+ exports.useServerTagStyle = useServerTagStyle;
360
+ exports.useServerTagTitle = useServerTagTitle;
361
+ exports.useServerTitleTemplate = useServerTitleTemplate;
362
+ exports.useTagBase = useTagBase;
363
+ exports.useTagLink = useTagLink;
364
+ exports.useTagMeta = useTagMeta;
365
+ exports.useTagMetaFlat = useTagMetaFlat;
366
+ exports.useTagNoscript = useTagNoscript;
367
+ exports.useTagScript = useTagScript;
368
+ exports.useTagStyle = useTagStyle;
369
+ exports.useTagTitle = useTagTitle;
196
370
  exports.useTitleTemplate = useTitleTemplate;
197
371
  for (const k in dom) {
198
372
  if (k !== 'default' && !exports.hasOwnProperty(k)) exports[k] = dom[k];
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { MaybeComputedRef, MaybeRef } from '@vueuse/shared';
2
2
  export { MaybeComputedRef } from '@vueuse/shared';
3
3
  import * as _unhead_schema from '@unhead/schema';
4
- import { 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, MergeHead, BaseHtmlAttr, MaybeArray, BaseBodyAttr, HeadEntryOptions, HeadClient, CreateHeadOptions } from '@unhead/schema';
4
+ import { 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, MergeHead, BaseHtmlAttr, MaybeArray, BaseBodyAttr, HeadEntryOptions, MetaFlatInput, HeadClient, CreateHeadOptions } from '@unhead/schema';
5
5
  export { ActiveHeadEntry, Head, HeadClient, HeadEntryOptions, HeadTag, MergeHead } from '@unhead/schema';
6
- import { Plugin } from 'vue';
6
+ import { RenderDomHeadOptions } from '@unhead/dom';
7
7
  export * from '@unhead/dom';
8
+ import { Plugin } from 'vue';
8
9
 
9
10
  declare type MaybeComputedRefEntries<T> = MaybeComputedRef<T> | {
10
11
  [key in keyof T]?: MaybeComputedRef<T[key]>;
@@ -103,33 +104,50 @@ interface ReactiveHead<E extends MergeHead = MergeHead> {
103
104
  */
104
105
  bodyAttrs?: BodyAttributes<E['bodyAttrs']>;
105
106
  }
106
- declare type UseHeadInput = MaybeComputedRef<ReactiveHead>;
107
+ declare type UseHeadInput<T extends MergeHead = {}> = MaybeComputedRef<ReactiveHead<T>>;
107
108
 
108
109
  declare function resolveUnrefHeadInput(ref: any): any;
109
110
  declare function asArray<T>(value: Arrayable<T>): T[];
110
111
 
111
- declare const VueTriggerDomPatchingOnUpdatesPlugin: () => _unhead_schema.HeadPlugin;
112
+ declare const VueTriggerDomPatchingOnUpdatesPlugin: (options?: RenderDomHeadOptions) => _unhead_schema.HeadPlugin;
112
113
 
113
114
  declare const VueReactiveInputPlugin: () => _unhead_schema.HeadPlugin;
114
115
 
115
- declare function useServerHead(input: ReactiveHead, options?: HeadEntryOptions): void;
116
- declare function useHead(input: UseHeadInput, options?: HeadEntryOptions): void;
117
- declare const useTitle: (title: ReactiveHead['title']) => void;
116
+ declare function useServerHead<T extends MergeHead>(input: UseHeadInput<T>, options?: HeadEntryOptions): void;
117
+ declare const useServerTagTitle: (title: ReactiveHead['title']) => void;
118
+ declare const useServerTitleTemplate: (titleTemplate: ReactiveHead['titleTemplate']) => void;
119
+ declare const useServerTagMeta: (meta: Arrayable<Meta>) => void;
120
+ declare const useServerTagMetaFlat: (meta: MaybeComputedRefEntries<MetaFlatInput>) => void;
121
+ declare const useServerTagLink: (link: Arrayable<Link>) => void;
122
+ declare const useServerTagScript: (script: Arrayable<Script>) => void;
123
+ declare const useServerTagStyle: (style: Arrayable<Style>) => void;
124
+ declare const useServerTagNoscript: (noscript: Arrayable<Noscript>) => void;
125
+ declare const useServerTagBase: (base: ReactiveHead['base']) => void;
126
+ declare const useServerHtmlAttrs: (attrs: ReactiveHead['htmlAttrs']) => void;
127
+ declare const useServerBodyAttrs: (attrs: ReactiveHead['bodyAttrs']) => void;
128
+
129
+ declare function useHead<T extends MergeHead>(input: UseHeadInput<T>, options?: HeadEntryOptions): void;
130
+ declare const useTagTitle: (title: ReactiveHead['title']) => void;
118
131
  declare const useTitleTemplate: (titleTemplate: ReactiveHead['titleTemplate']) => void;
119
- declare const useMeta: (meta: Arrayable<Meta>) => void;
120
- declare const useLink: (link: Arrayable<Link>) => void;
121
- declare const useScript: (script: Arrayable<Script>) => void;
122
- declare const useStyle: (style: Arrayable<Style>) => void;
123
- declare const useNoscript: (noscript: Arrayable<Noscript>) => void;
124
- declare const useBase: (base: ReactiveHead['base']) => void;
132
+ declare const useTagMeta: (meta: Arrayable<Meta>) => void;
133
+ declare const useTagMetaFlat: (meta: MaybeComputedRefEntries<MetaFlatInput>) => void;
134
+ declare const useTagLink: (link: Arrayable<Link>) => void;
135
+ declare const useTagScript: (script: Arrayable<Script>) => void;
136
+ declare const useTagStyle: (style: Arrayable<Style>) => void;
137
+ declare const useTagNoscript: (noscript: Arrayable<Noscript>) => void;
138
+ declare const useTagBase: (base: ReactiveHead['base']) => void;
125
139
  declare const useHtmlAttrs: (attrs: ReactiveHead['htmlAttrs']) => void;
126
140
  declare const useBodyAttrs: (attrs: ReactiveHead['bodyAttrs']) => void;
127
141
 
128
142
  declare type VueHeadClient<T extends MergeHead> = HeadClient<MaybeComputedRef<ReactiveHead<T>>> & Plugin;
129
- declare const headSymbol: unique symbol;
143
+ declare const headSymbol = "usehead";
130
144
  declare function injectHead<T extends MergeHead>(): VueHeadClient<T>;
131
- declare function createHead<T extends MergeHead>(options?: CreateHeadOptions): Promise<VueHeadClient<T>>;
145
+ declare function createHead<T extends MergeHead>(options?: CreateHeadOptions): VueHeadClient<T>;
146
+
147
+ declare function createDomHead<T extends MergeHead>(options?: CreateHeadOptions & {
148
+ document?: Document;
149
+ }): VueHeadClient<T>;
132
150
 
133
151
  declare const HeadVuePlugin: Plugin;
134
152
 
135
- export { Arrayable, Base, BodyAttributes, HeadVuePlugin, HtmlAttributes, Link, MaybeComputedRefEntries, Meta, Noscript, ReactiveHead, Script, Style, Title, TitleTemplate, UseHeadInput, VueHeadClient, VueReactiveInputPlugin, VueTriggerDomPatchingOnUpdatesPlugin, asArray, createHead, headSymbol, injectHead, resolveUnrefHeadInput, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useServerHead, useStyle, useTitle, useTitleTemplate };
153
+ export { Arrayable, Base, BodyAttributes, HeadVuePlugin, HtmlAttributes, Link, MaybeComputedRefEntries, Meta, Noscript, ReactiveHead, Script, Style, Title, TitleTemplate, UseHeadInput, VueHeadClient, VueReactiveInputPlugin, VueTriggerDomPatchingOnUpdatesPlugin, asArray, createDomHead, createHead, headSymbol, injectHead, resolveUnrefHeadInput, useBodyAttrs, useHead, useHtmlAttrs, useServerBodyAttrs, useServerHead, useServerHtmlAttrs, useServerTagBase, useServerTagLink, useServerTagMeta, useServerTagMetaFlat, useServerTagNoscript, useServerTagScript, useServerTagStyle, useServerTagTitle, useServerTitleTemplate, useTagBase, useTagLink, useTagMeta, useTagMetaFlat, useTagNoscript, useTagScript, useTagStyle, useTagTitle, useTitleTemplate };
package/dist/index.mjs CHANGED
@@ -25,21 +25,16 @@ function asArray(value) {
25
25
  return Array.isArray(value) ? value : [value];
26
26
  }
27
27
 
28
- const VueTriggerDomPatchingOnUpdatesPlugin = () => {
28
+ const VueTriggerDomPatchingOnUpdatesPlugin = (options) => {
29
29
  return defineHeadPlugin({
30
30
  hooks: {
31
31
  "entries:updated": function(head) {
32
- debouncedRenderDOMHead(nextTick, head);
32
+ debouncedRenderDOMHead(nextTick, head, { document: options?.document });
33
33
  }
34
34
  }
35
35
  });
36
36
  };
37
37
 
38
- const vueTriggerDomPatchingOnUpdatesPlugin = {
39
- __proto__: null,
40
- VueTriggerDomPatchingOnUpdatesPlugin: VueTriggerDomPatchingOnUpdatesPlugin
41
- };
42
-
43
38
  const VueReactiveInputPlugin = () => {
44
39
  return defineHeadPlugin({
45
40
  hooks: {
@@ -51,20 +46,151 @@ const VueReactiveInputPlugin = () => {
51
46
  });
52
47
  };
53
48
 
54
- const Vue3 = version.startsWith("3");
55
- version.startsWith("2.");
56
- const IsBrowser = typeof window !== "undefined";
49
+ function unpackToArray(input, options) {
50
+ const unpacked = [];
51
+ const kFn = options.resolveKeyData || ((ctx) => ctx.key);
52
+ const vFn = options.resolveValueData || ((ctx) => ctx.value);
53
+ for (const [k, v] of Object.entries(input)) {
54
+ unpacked.push(...(Array.isArray(v) ? v : [v]).map((i) => {
55
+ const ctx = { key: k, value: i };
56
+ const val = vFn(ctx);
57
+ if (typeof val === "object")
58
+ return unpackToArray(val, options);
59
+ if (Array.isArray(val))
60
+ return val;
61
+ return {
62
+ [typeof options.key === "function" ? options.key(ctx) : options.key]: kFn(ctx),
63
+ [typeof options.value === "function" ? options.value(ctx) : options.value]: val
64
+ };
65
+ }).flat());
66
+ }
67
+ return unpacked;
68
+ }
57
69
 
58
- function useHead$2(input, options = {}) {
59
- const head = injectHead();
60
- head.push(input, options);
70
+ function unpackToString(value, options) {
71
+ return Object.entries(value).map(([key, value2]) => {
72
+ if (typeof value2 === "object")
73
+ value2 = unpackToString(value2, options);
74
+ if (options.resolve) {
75
+ const resolved = options.resolve({ key, value: value2 });
76
+ if (resolved)
77
+ return resolved;
78
+ }
79
+ if (typeof value2 === "number")
80
+ value2 = value2.toString();
81
+ if (typeof value2 === "string" && options.wrapValue) {
82
+ value2 = value2.replace(new RegExp(options.wrapValue, "g"), `\\${options.wrapValue}`);
83
+ value2 = `${options.wrapValue}${value2}${options.wrapValue}`;
84
+ }
85
+ return `${key}${options.keyValueSeparator || ""}${value2}`;
86
+ }).join(options.entrySeparator || "");
61
87
  }
62
88
 
63
- function useServerHead$1(input, options = {}) {
64
- useHead$2(input, { ...options, mode: "server" });
89
+ const MetaPackingSchema = {
90
+ robots: {
91
+ unpack: {
92
+ keyValueSeparator: ":"
93
+ }
94
+ },
95
+ contentSecurityPolicy: {
96
+ unpack: {
97
+ keyValueSeparator: " ",
98
+ entrySeparator: "; "
99
+ },
100
+ metaKey: "http-equiv"
101
+ },
102
+ fbAppId: {
103
+ keyValue: "fb:app_id",
104
+ metaKey: "property"
105
+ },
106
+ msapplicationTileImage: {
107
+ keyValue: "msapplication-TileImage"
108
+ },
109
+ msapplicationTileColor: {
110
+ keyValue: "msapplication-TileColor"
111
+ },
112
+ msapplicationConfig: {
113
+ keyValue: "msapplication-Config"
114
+ },
115
+ charset: {
116
+ metaKey: "charset"
117
+ },
118
+ contentType: {
119
+ metaKey: "http-equiv"
120
+ },
121
+ defaultStyle: {
122
+ metaKey: "http-equiv"
123
+ },
124
+ xUaCompatible: {
125
+ metaKey: "http-equiv"
126
+ },
127
+ refresh: {
128
+ metaKey: "http-equiv"
129
+ }
130
+ };
131
+ function resolveMetaKeyType(key) {
132
+ return PropertyPrefixKeys.test(key) ? "property" : MetaPackingSchema[key]?.metaKey || "name";
133
+ }
134
+
135
+ function unpackMeta(input) {
136
+ return unpackToArray(input, {
137
+ key({ key }) {
138
+ return resolveMetaKeyType(key);
139
+ },
140
+ value({ key }) {
141
+ return key === "charset" ? "charset" : "content";
142
+ },
143
+ resolveKeyData({ key }) {
144
+ return MetaPackingSchema[key]?.keyValue || fixKeyCase(key);
145
+ },
146
+ resolveValueData({ value, key }) {
147
+ if (typeof value === "object") {
148
+ const definition = MetaPackingSchema[key];
149
+ if (key === "refresh")
150
+ return `${value.seconds};url=${value.url}`;
151
+ return unpackToString(
152
+ changeKeyCasingDeep(value),
153
+ {
154
+ entrySeparator: ", ",
155
+ keyValueSeparator: "=",
156
+ resolve({ value: value2, key: key2 }) {
157
+ if (typeof value2 === "boolean")
158
+ return `${key2}`;
159
+ },
160
+ ...definition?.unpack
161
+ }
162
+ );
163
+ }
164
+ return typeof value === "number" ? value.toString() : value;
165
+ }
166
+ });
167
+ }
168
+
169
+ const PropertyPrefixKeys = /^(og|twitter|fb)/;
170
+ function fixKeyCase(key) {
171
+ key = key.replace(/([A-Z])/g, "-$1").toLowerCase();
172
+ if (PropertyPrefixKeys.test(key)) {
173
+ key = key.replace("secure-url", "secure_url").replace(/-/g, ":");
174
+ }
175
+ return key;
65
176
  }
177
+ function changeKeyCasingDeep(input) {
178
+ if (Array.isArray(input)) {
179
+ return input.map((entry) => changeKeyCasingDeep(entry));
180
+ }
181
+ if (typeof input !== "object" || Array.isArray(input))
182
+ return input;
183
+ const output = {};
184
+ for (const [key, value] of Object.entries(input))
185
+ output[fixKeyCase(key)] = changeKeyCasingDeep(value);
186
+ return output;
187
+ }
188
+
189
+ const Vue3 = version.startsWith("3");
190
+ version.startsWith("2.");
191
+ const IsBrowser = typeof window !== "undefined";
66
192
 
67
- function useHead$1(input, options = {}) {
193
+ function clientUseHead(input, options = {}) {
68
194
  const head = injectHead();
69
195
  const vm = getCurrentInstance();
70
196
  if (!vm) {
@@ -87,41 +213,66 @@ function useHead$1(input, options = {}) {
87
213
  });
88
214
  }
89
215
 
216
+ function serverUseHead(input, options = {}) {
217
+ const head = injectHead();
218
+ head.push(input, options);
219
+ }
220
+
90
221
  function useServerHead(input, options = {}) {
91
- if (!IsBrowser)
92
- useServerHead$1(input, options);
222
+ useHead(input, { ...options, mode: "server" });
93
223
  }
224
+ const useServerTagTitle = (title) => useServerHead({ title });
225
+ const useServerTitleTemplate = (titleTemplate) => useServerHead({ titleTemplate });
226
+ const useServerTagMeta = (meta) => useServerHead({ meta: asArray(meta) });
227
+ const useServerTagMetaFlat = (meta) => {
228
+ const input = ref({});
229
+ watchEffect(() => {
230
+ input.value = unpackMeta(resolveUnrefHeadInput(meta));
231
+ });
232
+ return useServerHead({ meta: input });
233
+ };
234
+ const useServerTagLink = (link) => useServerHead({ link: asArray(link) });
235
+ const useServerTagScript = (script) => useServerHead({ script: asArray(script) });
236
+ const useServerTagStyle = (style) => useServerHead({ style: asArray(style) });
237
+ const useServerTagNoscript = (noscript) => useServerHead({ noscript: asArray(noscript) });
238
+ const useServerTagBase = (base) => useServerHead({ base });
239
+ const useServerHtmlAttrs = (attrs) => useServerHead({ htmlAttrs: attrs });
240
+ const useServerBodyAttrs = (attrs) => useHead({ bodyAttrs: attrs });
241
+
94
242
  function useHead(input, options = {}) {
95
243
  if (options.mode === "server" && IsBrowser || options.mode === "client" && !IsBrowser)
96
244
  return;
97
- IsBrowser ? useHead$1(input, options) : useHead$2(input, options);
245
+ IsBrowser ? clientUseHead(input, options) : serverUseHead(input, options);
98
246
  }
99
- const useTitle = (title) => useHead({ title });
247
+ const useTagTitle = (title) => useHead({ title });
100
248
  const useTitleTemplate = (titleTemplate) => useHead({ titleTemplate });
101
- const useMeta = (meta) => useHead({ meta: asArray(meta) });
102
- const useLink = (link) => useHead({ link: asArray(link) });
103
- const useScript = (script) => useHead({ script: asArray(script) });
104
- const useStyle = (style) => useHead({ style: asArray(style) });
105
- const useNoscript = (noscript) => useHead({ noscript: asArray(noscript) });
106
- const useBase = (base) => useHead({ base });
249
+ const useTagMeta = (meta) => useHead({ meta: asArray(meta) });
250
+ const useTagMetaFlat = (meta) => {
251
+ const input = ref({});
252
+ watchEffect(() => {
253
+ input.value = unpackMeta(resolveUnrefHeadInput(meta));
254
+ });
255
+ return useHead({ meta: input });
256
+ };
257
+ const useTagLink = (link) => useHead({ link: asArray(link) });
258
+ const useTagScript = (script) => useHead({ script: asArray(script) });
259
+ const useTagStyle = (style) => useHead({ style: asArray(style) });
260
+ const useTagNoscript = (noscript) => useHead({ noscript: asArray(noscript) });
261
+ const useTagBase = (base) => useHead({ base });
107
262
  const useHtmlAttrs = (attrs) => useHead({ htmlAttrs: attrs });
108
263
  const useBodyAttrs = (attrs) => useHead({ bodyAttrs: attrs });
109
264
 
110
- const headSymbol = Symbol("unhead");
265
+ const headSymbol = "usehead";
111
266
  function injectHead() {
112
267
  return getCurrentInstance() && inject(headSymbol) || getActiveHead();
113
268
  }
114
- async function createHead(options = {}) {
269
+ function createHead(options = {}) {
115
270
  const plugins = [
116
271
  HydratesStatePlugin(),
117
272
  VueReactiveInputPlugin(),
118
273
  ...options?.plugins || []
119
274
  ];
120
- if (IsBrowser) {
121
- const { VueTriggerDomPatchingOnUpdatesPlugin } = await Promise.resolve().then(function () { return vueTriggerDomPatchingOnUpdatesPlugin; });
122
- plugins.push(VueTriggerDomPatchingOnUpdatesPlugin());
123
- }
124
- const head = await createHead$1({
275
+ const head = createHead$1({
125
276
  ...options,
126
277
  plugins
127
278
  });
@@ -147,6 +298,16 @@ async function createHead(options = {}) {
147
298
  return { ...vuePlugin, ...head };
148
299
  }
149
300
 
301
+ function createDomHead(options = {}) {
302
+ return createHead({
303
+ ...options,
304
+ plugins: [
305
+ VueTriggerDomPatchingOnUpdatesPlugin({ document: options?.document }),
306
+ ...options.plugins || []
307
+ ]
308
+ });
309
+ }
310
+
150
311
  const HeadVuePlugin = function(_Vue) {
151
312
  _Vue.mixin({
152
313
  beforeCreate() {
@@ -173,4 +334,4 @@ const HeadVuePlugin = function(_Vue) {
173
334
  });
174
335
  };
175
336
 
176
- export { HeadVuePlugin, VueReactiveInputPlugin, VueTriggerDomPatchingOnUpdatesPlugin, asArray, createHead, headSymbol, injectHead, resolveUnrefHeadInput, useBase, useBodyAttrs, useHead, useHtmlAttrs, useLink, useMeta, useNoscript, useScript, useServerHead, useStyle, useTitle, useTitleTemplate };
337
+ export { HeadVuePlugin, VueReactiveInputPlugin, VueTriggerDomPatchingOnUpdatesPlugin, asArray, createDomHead, createHead, headSymbol, injectHead, resolveUnrefHeadInput, useBodyAttrs, useHead, useHtmlAttrs, useServerBodyAttrs, useServerHead, useServerHtmlAttrs, useServerTagBase, useServerTagLink, useServerTagMeta, useServerTagMetaFlat, useServerTagNoscript, useServerTagScript, useServerTagStyle, useServerTagTitle, useServerTitleTemplate, useTagBase, useTagLink, useTagMeta, useTagMetaFlat, useTagNoscript, useTagScript, useTagStyle, useTagTitle, useTitleTemplate };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unhead/vue",
3
3
  "type": "module",
4
- "version": "0.1.4",
4
+ "version": "0.2.3",
5
5
  "packageManager": "pnpm@7.14.0",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -33,10 +33,10 @@
33
33
  "vue": ">=2.7 || >=3"
34
34
  },
35
35
  "dependencies": {
36
- "@unhead/dom": "0.1.4",
37
- "@unhead/schema": "0.1.4",
36
+ "@unhead/dom": "0.2.3",
37
+ "@unhead/schema": "0.2.3",
38
38
  "@vueuse/shared": "latest",
39
- "unhead": "0.1.4"
39
+ "unhead": "0.2.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "vue": "latest"
@@ -1,2 +0,0 @@
1
- export * from './useHead';
2
- export * from './useServerHead';
@@ -1,2 +0,0 @@
1
- export * from "./useHead.mjs";
2
- export * from "./useServerHead.mjs";
@@ -1,3 +0,0 @@
1
- import type { HeadEntryOptions } from '@unhead/schema';
2
- import type { UseHeadInput } from '../..';
3
- export declare function useHead(input: UseHeadInput, options?: HeadEntryOptions): void;
@@ -1,5 +0,0 @@
1
- import { injectHead } from "../..";
2
- export function useHead(input, options = {}) {
3
- const head = injectHead();
4
- head.push(input, options);
5
- }
@@ -1,3 +0,0 @@
1
- import type { HeadEntryOptions } from '@unhead/schema';
2
- import type { UseHeadInput } from '../../types';
3
- export declare function useServerHead(input: UseHeadInput, options?: HeadEntryOptions): void;
@@ -1,4 +0,0 @@
1
- import { useHead } from "./index.mjs";
2
- export function useServerHead(input, options = {}) {
3
- useHead(input, { ...options, mode: "server" });
4
- }
@@ -1,2 +0,0 @@
1
- export * from './useHead';
2
- export * from './useServerHead';
@@ -1,2 +0,0 @@
1
- export * from "./useHead.mjs";
2
- export * from "./useServerHead.mjs";
@@ -1,3 +0,0 @@
1
- import type { HeadEntryOptions } from '@unhead/schema';
2
- import type { UseHeadInput } from '../../index';
3
- export declare function useHead(input: UseHeadInput, options?: HeadEntryOptions): void;
@@ -1,24 +0,0 @@
1
- import { getCurrentInstance, onBeforeUnmount, ref, watch, watchEffect } from "vue";
2
- import { injectHead, resolveUnrefHeadInput } from "../../index";
3
- export function useHead(input, options = {}) {
4
- const head = injectHead();
5
- const vm = getCurrentInstance();
6
- if (!vm) {
7
- head.push(input, options);
8
- return;
9
- }
10
- const resolvedInput = ref({});
11
- watchEffect(() => {
12
- resolvedInput.value = resolveUnrefHeadInput(input);
13
- });
14
- let entry;
15
- watch(resolvedInput, (e) => {
16
- if (!entry)
17
- entry = head.push(e, options);
18
- else
19
- entry.patch(e);
20
- }, { immediate: true });
21
- onBeforeUnmount(() => {
22
- entry?.dispose();
23
- });
24
- }
@@ -1,3 +0,0 @@
1
- import type { HeadEntryOptions } from '@unhead/schema';
2
- import type { UseHeadInput } from '../../types';
3
- export declare function useServerHead(input: UseHeadInput, options?: HeadEntryOptions): void;
@@ -1,2 +0,0 @@
1
- export function useServerHead(input, options = {}) {
2
- }