unhead 1.3.3 → 1.3.5

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
@@ -71,6 +71,23 @@ const DedupePlugin = shared.defineHeadPlugin({
71
71
  }
72
72
  });
73
73
 
74
+ const PayloadPlugin = shared.defineHeadPlugin((head) => ({
75
+ mode: "server",
76
+ hooks: {
77
+ "tags:resolve": function(ctx) {
78
+ const csrPayload = {};
79
+ ctx.tags.filter((tag) => ["titleTemplate", "templateParams"].includes(tag.tag) && tag._m === "server").forEach((tag) => {
80
+ csrPayload[tag.tag] = tag.tag === "titleTemplate" ? tag.textContent : tag.props;
81
+ });
82
+ Object.keys(csrPayload).length && ctx.tags.push({
83
+ tag: "script",
84
+ innerHTML: JSON.stringify(csrPayload),
85
+ props: { type: "text/javascript", id: "unhead:payload" }
86
+ });
87
+ }
88
+ }
89
+ }));
90
+
74
91
  const ValidEventTags = ["script", "link", "bodyAttrs"];
75
92
  function stripEventHandlers(tag) {
76
93
  const props = {};
@@ -134,7 +151,7 @@ const EventHandlersPlugin = shared.defineHeadPlugin({
134
151
  });
135
152
 
136
153
  const DupeableTags = ["link", "style", "script", "noscript"];
137
- const HashKeyedPLugin = shared.defineHeadPlugin({
154
+ const HashKeyedPlugin = shared.defineHeadPlugin({
138
155
  hooks: {
139
156
  "tag:normalise": ({ tag }) => {
140
157
  if (tag.key && DupeableTags.includes(tag.tag)) {
@@ -144,7 +161,7 @@ const HashKeyedPLugin = shared.defineHeadPlugin({
144
161
  }
145
162
  });
146
163
 
147
- const SortPLugin = shared.defineHeadPlugin({
164
+ const SortPlugin = shared.defineHeadPlugin({
148
165
  hooks: {
149
166
  "tags:resolve": (ctx) => {
150
167
  const tagPositionForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
@@ -231,28 +248,30 @@ const TitleTemplatePlugin = shared.defineHeadPlugin({
231
248
  let activeHead;
232
249
  function createHead(options = {}) {
233
250
  const head = createHeadCore(options);
234
- if (!head.ssr)
235
- head.use(dom.PatchDomOnEntryUpdatesPlugin());
251
+ head.use(dom.DomPlugin());
236
252
  return activeHead = head;
237
253
  }
238
254
  function createServerHead(options = {}) {
239
255
  return activeHead = createHeadCore(options);
240
256
  }
257
+ function filterMode(mode, ssr) {
258
+ return !mode || mode === "server" && ssr || mode === "client" && !ssr;
259
+ }
241
260
  function createHeadCore(options = {}) {
242
261
  const hooks = hookable.createHooks();
243
262
  hooks.addHooks(options.hooks || {});
263
+ options.document = options.document || (shared.IsBrowser ? document : void 0);
264
+ const ssr = !options.document;
244
265
  options.plugins = [
245
266
  DedupePlugin,
267
+ PayloadPlugin,
246
268
  EventHandlersPlugin,
247
- HashKeyedPLugin,
248
- SortPLugin,
269
+ HashKeyedPlugin,
270
+ SortPlugin,
249
271
  TemplateParamsPlugin,
250
272
  TitleTemplatePlugin,
251
273
  ...options?.plugins || []
252
274
  ];
253
- options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
254
- options.document = options.document || (shared.IsBrowser ? document : void 0);
255
- const ssr = !options.document;
256
275
  const updated = () => hooks.callHook("entries:updated", head);
257
276
  let entryCount = 0;
258
277
  let entries = [];
@@ -262,32 +281,31 @@ function createHeadCore(options = {}) {
262
281
  headEntries() {
263
282
  return entries;
264
283
  },
265
- use(plugin) {
266
- if (plugin.hooks)
267
- hooks.addHooks(plugin.hooks);
284
+ use(p) {
285
+ const plugin = typeof p === "function" ? p(head) : p;
286
+ filterMode(plugin.mode, ssr) && hooks.addHooks(plugin.hooks || {});
268
287
  },
269
288
  push(input, entryOptions) {
270
- const activeEntry = {
289
+ const entry = {
271
290
  _i: entryCount++,
272
291
  input,
273
292
  ...entryOptions
274
293
  };
275
- const mode = activeEntry.mode;
276
- if (!mode || mode === "server" && ssr || mode === "client" && !ssr) {
277
- entries.push(activeEntry);
294
+ if (filterMode(entry.mode, ssr)) {
295
+ entries.push(entry);
278
296
  updated();
279
297
  }
280
298
  return {
281
299
  dispose() {
282
- entries = entries.filter((e) => e._i !== activeEntry._i);
300
+ entries = entries.filter((e) => e._i !== entry._i);
283
301
  hooks.callHook("entries:updated", head);
284
302
  updated();
285
303
  },
286
304
  // a patch is the same as creating a new entry, just a nice DX
287
305
  patch(input2) {
288
306
  entries = entries.map((e) => {
289
- if (e._i === activeEntry._i) {
290
- activeEntry.input = e.input = input2;
307
+ if (e._i === entry._i) {
308
+ e.input = entry.input = input2;
291
309
  }
292
310
  return e;
293
311
  });
@@ -315,6 +333,7 @@ function createHeadCore(options = {}) {
315
333
  },
316
334
  ssr
317
335
  };
336
+ options.plugins.forEach((p) => head.use(p));
318
337
  head.hooks.callHook("init", head);
319
338
  return head;
320
339
  }
@@ -333,10 +352,7 @@ function HashHydrationPlugin() {
333
352
  dirty = true;
334
353
  },
335
354
  "tags:resolve": function({ tags }) {
336
- const nonServerTags = tags.filter((tag) => {
337
- const entry = head.headEntries().find((e) => e._i === tag._e);
338
- return entry && entry.mode !== "server";
339
- });
355
+ const nonServerTags = tags.filter((tag) => tag._m !== "server");
340
356
  const hash = !nonServerTags.length ? false : shared.hashCode(
341
357
  nonServerTags.map((tag) => shared.hashTag(tag)).join("")
342
358
  );
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHooks } from 'hookable';
2
- import { PatchDomOnEntryUpdatesPlugin } from '@unhead/dom';
2
+ import { DomPlugin } from '@unhead/dom';
3
3
  import { defineHeadPlugin, tagDedupeKey, tagWeight, HasElementTags, hashCode, SortModifiers, processTemplateParams, resolveTitleTemplate, IsBrowser, normaliseEntryTags, hashTag, composableNames, whitelistSafeInput, unpackMeta } from '@unhead/shared';
4
4
  export { composableNames } from '@unhead/shared';
5
5
 
@@ -70,6 +70,23 @@ const DedupePlugin = defineHeadPlugin({
70
70
  }
71
71
  });
72
72
 
73
+ const PayloadPlugin = defineHeadPlugin((head) => ({
74
+ mode: "server",
75
+ hooks: {
76
+ "tags:resolve": function(ctx) {
77
+ const csrPayload = {};
78
+ ctx.tags.filter((tag) => ["titleTemplate", "templateParams"].includes(tag.tag) && tag._m === "server").forEach((tag) => {
79
+ csrPayload[tag.tag] = tag.tag === "titleTemplate" ? tag.textContent : tag.props;
80
+ });
81
+ Object.keys(csrPayload).length && ctx.tags.push({
82
+ tag: "script",
83
+ innerHTML: JSON.stringify(csrPayload),
84
+ props: { type: "text/javascript", id: "unhead:payload" }
85
+ });
86
+ }
87
+ }
88
+ }));
89
+
73
90
  const ValidEventTags = ["script", "link", "bodyAttrs"];
74
91
  function stripEventHandlers(tag) {
75
92
  const props = {};
@@ -133,7 +150,7 @@ const EventHandlersPlugin = defineHeadPlugin({
133
150
  });
134
151
 
135
152
  const DupeableTags = ["link", "style", "script", "noscript"];
136
- const HashKeyedPLugin = defineHeadPlugin({
153
+ const HashKeyedPlugin = defineHeadPlugin({
137
154
  hooks: {
138
155
  "tag:normalise": ({ tag }) => {
139
156
  if (tag.key && DupeableTags.includes(tag.tag)) {
@@ -143,7 +160,7 @@ const HashKeyedPLugin = defineHeadPlugin({
143
160
  }
144
161
  });
145
162
 
146
- const SortPLugin = defineHeadPlugin({
163
+ const SortPlugin = defineHeadPlugin({
147
164
  hooks: {
148
165
  "tags:resolve": (ctx) => {
149
166
  const tagPositionForKey = (key) => ctx.tags.find((tag) => tag._d === key)?._p;
@@ -230,28 +247,30 @@ const TitleTemplatePlugin = defineHeadPlugin({
230
247
  let activeHead;
231
248
  function createHead(options = {}) {
232
249
  const head = createHeadCore(options);
233
- if (!head.ssr)
234
- head.use(PatchDomOnEntryUpdatesPlugin());
250
+ head.use(DomPlugin());
235
251
  return activeHead = head;
236
252
  }
237
253
  function createServerHead(options = {}) {
238
254
  return activeHead = createHeadCore(options);
239
255
  }
256
+ function filterMode(mode, ssr) {
257
+ return !mode || mode === "server" && ssr || mode === "client" && !ssr;
258
+ }
240
259
  function createHeadCore(options = {}) {
241
260
  const hooks = createHooks();
242
261
  hooks.addHooks(options.hooks || {});
262
+ options.document = options.document || (IsBrowser ? document : void 0);
263
+ const ssr = !options.document;
243
264
  options.plugins = [
244
265
  DedupePlugin,
266
+ PayloadPlugin,
245
267
  EventHandlersPlugin,
246
- HashKeyedPLugin,
247
- SortPLugin,
268
+ HashKeyedPlugin,
269
+ SortPlugin,
248
270
  TemplateParamsPlugin,
249
271
  TitleTemplatePlugin,
250
272
  ...options?.plugins || []
251
273
  ];
252
- options.plugins.forEach((p) => hooks.addHooks(p.hooks || {}));
253
- options.document = options.document || (IsBrowser ? document : void 0);
254
- const ssr = !options.document;
255
274
  const updated = () => hooks.callHook("entries:updated", head);
256
275
  let entryCount = 0;
257
276
  let entries = [];
@@ -261,32 +280,31 @@ function createHeadCore(options = {}) {
261
280
  headEntries() {
262
281
  return entries;
263
282
  },
264
- use(plugin) {
265
- if (plugin.hooks)
266
- hooks.addHooks(plugin.hooks);
283
+ use(p) {
284
+ const plugin = typeof p === "function" ? p(head) : p;
285
+ filterMode(plugin.mode, ssr) && hooks.addHooks(plugin.hooks || {});
267
286
  },
268
287
  push(input, entryOptions) {
269
- const activeEntry = {
288
+ const entry = {
270
289
  _i: entryCount++,
271
290
  input,
272
291
  ...entryOptions
273
292
  };
274
- const mode = activeEntry.mode;
275
- if (!mode || mode === "server" && ssr || mode === "client" && !ssr) {
276
- entries.push(activeEntry);
293
+ if (filterMode(entry.mode, ssr)) {
294
+ entries.push(entry);
277
295
  updated();
278
296
  }
279
297
  return {
280
298
  dispose() {
281
- entries = entries.filter((e) => e._i !== activeEntry._i);
299
+ entries = entries.filter((e) => e._i !== entry._i);
282
300
  hooks.callHook("entries:updated", head);
283
301
  updated();
284
302
  },
285
303
  // a patch is the same as creating a new entry, just a nice DX
286
304
  patch(input2) {
287
305
  entries = entries.map((e) => {
288
- if (e._i === activeEntry._i) {
289
- activeEntry.input = e.input = input2;
306
+ if (e._i === entry._i) {
307
+ e.input = entry.input = input2;
290
308
  }
291
309
  return e;
292
310
  });
@@ -314,6 +332,7 @@ function createHeadCore(options = {}) {
314
332
  },
315
333
  ssr
316
334
  };
335
+ options.plugins.forEach((p) => head.use(p));
317
336
  head.hooks.callHook("init", head);
318
337
  return head;
319
338
  }
@@ -332,10 +351,7 @@ function HashHydrationPlugin() {
332
351
  dirty = true;
333
352
  },
334
353
  "tags:resolve": function({ tags }) {
335
- const nonServerTags = tags.filter((tag) => {
336
- const entry = head.headEntries().find((e) => e._i === tag._e);
337
- return entry && entry.mode !== "server";
338
- });
354
+ const nonServerTags = tags.filter((tag) => tag._m !== "server");
339
355
  const hash = !nonServerTags.length ? false : hashCode(
340
356
  nonServerTags.map((tag) => hashTag(tag)).join("")
341
357
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unhead",
3
3
  "type": "module",
4
- "version": "1.3.3",
4
+ "version": "1.3.5",
5
5
  "author": "Harlan Wilton <harlan@harlanzw.com>",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -30,9 +30,9 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "hookable": "^5.5.3",
33
- "@unhead/dom": "1.3.3",
34
- "@unhead/schema": "1.3.3",
35
- "@unhead/shared": "1.3.3"
33
+ "@unhead/dom": "1.3.5",
34
+ "@unhead/schema": "1.3.5",
35
+ "@unhead/shared": "1.3.5"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "unbuild .",