unhead 0.2.7 → 0.4.1

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
@@ -311,8 +311,11 @@ const DedupesTagsPlugin = (options) => {
311
311
  if (strategy === "merge") {
312
312
  const oldProps = dupedTag.props;
313
313
  ["class", "style"].forEach((key) => {
314
- if (tag.props[key] && oldProps[key])
314
+ if (tag.props[key] && oldProps[key]) {
315
+ if (key === "style" && !oldProps[key].endsWith(";"))
316
+ oldProps[key] += ";";
315
317
  tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
318
+ }
316
319
  });
317
320
  deduping[dedupeKey].props = {
318
321
  ...oldProps,
@@ -327,8 +330,6 @@ const DedupesTagsPlugin = (options) => {
327
330
  tag.props[tag._s] = "";
328
331
  }
329
332
  tag._d = dedupeKey;
330
- } else {
331
- tag._p = dupedTag._p;
332
333
  }
333
334
  if (Object.keys(tag.props).length === 0 && !tag.children) {
334
335
  delete deduping[dedupeKey];
@@ -406,18 +407,13 @@ const HydratesStatePlugin = () => {
406
407
  return;
407
408
  if (typeof tag._d === "undefined" && entry._m === "server")
408
409
  return;
409
- const hasChildren = tag.children && tag.children.length > 0;
410
- tag._s = `data-h-${hashCode(tag._d || tag.tag + (hasChildren ? tag.children : JSON.stringify(tag.props)))}`;
410
+ tag._s = `data-h-${hashCode(tag._d || JSON.stringify({ tag: tag.tag, props: tag.props, children: tag.children }))}`;
411
411
  tag.props[tag._s] = "";
412
412
  }
413
413
  }
414
414
  });
415
415
  };
416
416
 
417
- function defineHeadPlugin(plugin) {
418
- return plugin;
419
- }
420
-
421
417
  const PatchDomOnEntryUpdatesPlugin = (options) => {
422
418
  return defineHeadPlugin({
423
419
  hooks: {
@@ -435,6 +431,52 @@ const PatchDomOnEntryUpdatesPlugin = (options) => {
435
431
  });
436
432
  };
437
433
 
434
+ const EventHandlersPlugin = () => {
435
+ const stripEventHandlers = (tag) => {
436
+ const props = {};
437
+ const eventHandlers = {};
438
+ Object.entries(tag.props).forEach(([key, value]) => {
439
+ if (key.startsWith("on") && typeof value === "function")
440
+ eventHandlers[key] = value;
441
+ else
442
+ props[key] = value;
443
+ });
444
+ return { props, eventHandlers };
445
+ };
446
+ return defineHeadPlugin({
447
+ hooks: {
448
+ "ssr:beforeRender": function(ctx) {
449
+ ctx.tags = ctx.tags.map((tag) => {
450
+ tag.props = stripEventHandlers(tag).props;
451
+ return tag;
452
+ });
453
+ },
454
+ "dom:beforeRenderTag": function(ctx) {
455
+ const { props, eventHandlers } = stripEventHandlers(ctx.tag);
456
+ if (!Object.keys(eventHandlers).length)
457
+ return;
458
+ ctx.tag.props = props;
459
+ ctx.tag._eventHandlers = eventHandlers;
460
+ },
461
+ "dom:renderTag": function(ctx) {
462
+ const $el = ctx.$el;
463
+ if (!ctx.tag._eventHandlers || !$el)
464
+ return;
465
+ Object.entries(ctx.tag._eventHandlers).forEach(([k, value]) => {
466
+ const sdeKey = `${ctx.tag._s || ctx.tag._p}:${k}`;
467
+ const eventName = k.slice(2).toLowerCase();
468
+ const handler = value;
469
+ $el?.addEventListener(eventName, handler);
470
+ ctx.entry._sde[sdeKey] = () => {
471
+ $el.removeEventListener(eventName, handler);
472
+ };
473
+ delete ctx.queuedSideEffects[sdeKey];
474
+ });
475
+ }
476
+ }
477
+ });
478
+ };
479
+
438
480
  function asArray(value) {
439
481
  return Array.isArray(value) ? value : [value];
440
482
  }
@@ -544,18 +586,17 @@ function createHead(options = {}) {
544
586
  DedupesTagsPlugin(),
545
587
  SortTagsPlugin(),
546
588
  TitleTemplatePlugin(),
547
- PatchDomOnEntryUpdatesPlugin({ document: options?.document, delayFn: options?.domDelayFn })
589
+ PatchDomOnEntryUpdatesPlugin({ document: options?.document, delayFn: options?.domDelayFn }),
590
+ EventHandlersPlugin()
548
591
  ];
549
592
  plugins.push(...options.plugins || []);
550
593
  plugins.forEach((plugin) => hooks.addHooks(plugin.hooks || {}));
551
594
  const triggerUpdate = () => hooks.callHook("entries:updated", head);
552
595
  const head = {
553
- _removeQueuedSideEffect(key) {
554
- delete _sde[key];
555
- },
556
- _flushQueuedSideEffects() {
557
- Object.values(_sde).forEach((fn) => fn());
596
+ _popSideEffectQueue() {
597
+ const sde = { ..._sde };
558
598
  _sde = {};
599
+ return sde;
559
600
  },
560
601
  headEntries() {
561
602
  return entries;
@@ -616,8 +657,13 @@ function createHead(options = {}) {
616
657
  return head;
617
658
  }
618
659
 
660
+ function defineHeadPlugin(plugin) {
661
+ return plugin;
662
+ }
663
+
619
664
  exports.DedupesTagsPlugin = DedupesTagsPlugin;
620
665
  exports.DeprecatedTagAttrPlugin = DeprecatedTagAttrPlugin;
666
+ exports.EventHandlersPlugin = EventHandlersPlugin;
621
667
  exports.HydratesStatePlugin = HydratesStatePlugin;
622
668
  exports.PatchDomOnEntryUpdatesPlugin = PatchDomOnEntryUpdatesPlugin;
623
669
  exports.SortTagsPlugin = SortTagsPlugin;
package/dist/index.d.ts CHANGED
@@ -20,6 +20,13 @@ interface TriggerDomPatchingOnUpdatesPluginOptions extends RenderDomHeadOptions
20
20
  }
21
21
  declare const PatchDomOnEntryUpdatesPlugin: (options?: TriggerDomPatchingOnUpdatesPluginOptions) => _unhead_schema.HeadPlugin;
22
22
 
23
+ /**
24
+ * Supports DOM event handlers (i.e `onload`) as functions.
25
+ *
26
+ * When SSR we need to strip out these values. On CSR we
27
+ */
28
+ declare const EventHandlersPlugin: () => _unhead_schema.HeadPlugin;
29
+
23
30
  declare type Arrayable<T> = T | Array<T>;
24
31
  declare function asArray<T>(value: Arrayable<T>): T[];
25
32
 
@@ -59,4 +66,4 @@ declare function defineHeadPlugin(plugin: HeadPlugin): HeadPlugin;
59
66
 
60
67
  declare function normaliseEntryTags<T extends {} = Head>(e: HeadEntry<T>): HeadTag[];
61
68
 
62
- export { Arrayable, DedupesTagsPlugin, DedupesTagsPluginOptions, DeprecatedTagAttrPlugin, HydratesStatePlugin, PatchDomOnEntryUpdatesPlugin, SortTagsPlugin, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, setActiveHead, useBodyAttrs, useHead, useHtmlAttrs, useServerBodyAttrs, useServerHead, useServerHtmlAttrs, useServerTagBase, useServerTagLink, useServerTagMeta, useServerTagMetaFlat, useServerTagNoscript, useServerTagScript, useServerTagStyle, useServerTagTitle, useServerTitleTemplate, useTagBase, useTagLink, useTagMeta, useTagMetaFlat, useTagNoscript, useTagScript, useTagStyle, useTagTitle, useTitleTemplate };
69
+ export { Arrayable, DedupesTagsPlugin, DedupesTagsPluginOptions, DeprecatedTagAttrPlugin, EventHandlersPlugin, HydratesStatePlugin, PatchDomOnEntryUpdatesPlugin, SortTagsPlugin, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, setActiveHead, 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
@@ -309,8 +309,11 @@ const DedupesTagsPlugin = (options) => {
309
309
  if (strategy === "merge") {
310
310
  const oldProps = dupedTag.props;
311
311
  ["class", "style"].forEach((key) => {
312
- if (tag.props[key] && oldProps[key])
312
+ if (tag.props[key] && oldProps[key]) {
313
+ if (key === "style" && !oldProps[key].endsWith(";"))
314
+ oldProps[key] += ";";
313
315
  tag.props[key] = `${oldProps[key]} ${tag.props[key]}`;
316
+ }
314
317
  });
315
318
  deduping[dedupeKey].props = {
316
319
  ...oldProps,
@@ -325,8 +328,6 @@ const DedupesTagsPlugin = (options) => {
325
328
  tag.props[tag._s] = "";
326
329
  }
327
330
  tag._d = dedupeKey;
328
- } else {
329
- tag._p = dupedTag._p;
330
331
  }
331
332
  if (Object.keys(tag.props).length === 0 && !tag.children) {
332
333
  delete deduping[dedupeKey];
@@ -404,18 +405,13 @@ const HydratesStatePlugin = () => {
404
405
  return;
405
406
  if (typeof tag._d === "undefined" && entry._m === "server")
406
407
  return;
407
- const hasChildren = tag.children && tag.children.length > 0;
408
- tag._s = `data-h-${hashCode(tag._d || tag.tag + (hasChildren ? tag.children : JSON.stringify(tag.props)))}`;
408
+ tag._s = `data-h-${hashCode(tag._d || JSON.stringify({ tag: tag.tag, props: tag.props, children: tag.children }))}`;
409
409
  tag.props[tag._s] = "";
410
410
  }
411
411
  }
412
412
  });
413
413
  };
414
414
 
415
- function defineHeadPlugin(plugin) {
416
- return plugin;
417
- }
418
-
419
415
  const PatchDomOnEntryUpdatesPlugin = (options) => {
420
416
  return defineHeadPlugin({
421
417
  hooks: {
@@ -433,6 +429,52 @@ const PatchDomOnEntryUpdatesPlugin = (options) => {
433
429
  });
434
430
  };
435
431
 
432
+ const EventHandlersPlugin = () => {
433
+ const stripEventHandlers = (tag) => {
434
+ const props = {};
435
+ const eventHandlers = {};
436
+ Object.entries(tag.props).forEach(([key, value]) => {
437
+ if (key.startsWith("on") && typeof value === "function")
438
+ eventHandlers[key] = value;
439
+ else
440
+ props[key] = value;
441
+ });
442
+ return { props, eventHandlers };
443
+ };
444
+ return defineHeadPlugin({
445
+ hooks: {
446
+ "ssr:beforeRender": function(ctx) {
447
+ ctx.tags = ctx.tags.map((tag) => {
448
+ tag.props = stripEventHandlers(tag).props;
449
+ return tag;
450
+ });
451
+ },
452
+ "dom:beforeRenderTag": function(ctx) {
453
+ const { props, eventHandlers } = stripEventHandlers(ctx.tag);
454
+ if (!Object.keys(eventHandlers).length)
455
+ return;
456
+ ctx.tag.props = props;
457
+ ctx.tag._eventHandlers = eventHandlers;
458
+ },
459
+ "dom:renderTag": function(ctx) {
460
+ const $el = ctx.$el;
461
+ if (!ctx.tag._eventHandlers || !$el)
462
+ return;
463
+ Object.entries(ctx.tag._eventHandlers).forEach(([k, value]) => {
464
+ const sdeKey = `${ctx.tag._s || ctx.tag._p}:${k}`;
465
+ const eventName = k.slice(2).toLowerCase();
466
+ const handler = value;
467
+ $el?.addEventListener(eventName, handler);
468
+ ctx.entry._sde[sdeKey] = () => {
469
+ $el.removeEventListener(eventName, handler);
470
+ };
471
+ delete ctx.queuedSideEffects[sdeKey];
472
+ });
473
+ }
474
+ }
475
+ });
476
+ };
477
+
436
478
  function asArray(value) {
437
479
  return Array.isArray(value) ? value : [value];
438
480
  }
@@ -542,18 +584,17 @@ function createHead(options = {}) {
542
584
  DedupesTagsPlugin(),
543
585
  SortTagsPlugin(),
544
586
  TitleTemplatePlugin(),
545
- PatchDomOnEntryUpdatesPlugin({ document: options?.document, delayFn: options?.domDelayFn })
587
+ PatchDomOnEntryUpdatesPlugin({ document: options?.document, delayFn: options?.domDelayFn }),
588
+ EventHandlersPlugin()
546
589
  ];
547
590
  plugins.push(...options.plugins || []);
548
591
  plugins.forEach((plugin) => hooks.addHooks(plugin.hooks || {}));
549
592
  const triggerUpdate = () => hooks.callHook("entries:updated", head);
550
593
  const head = {
551
- _removeQueuedSideEffect(key) {
552
- delete _sde[key];
553
- },
554
- _flushQueuedSideEffects() {
555
- Object.values(_sde).forEach((fn) => fn());
594
+ _popSideEffectQueue() {
595
+ const sde = { ..._sde };
556
596
  _sde = {};
597
+ return sde;
557
598
  },
558
599
  headEntries() {
559
600
  return entries;
@@ -614,4 +655,8 @@ function createHead(options = {}) {
614
655
  return head;
615
656
  }
616
657
 
617
- export { DedupesTagsPlugin, DeprecatedTagAttrPlugin, HydratesStatePlugin, PatchDomOnEntryUpdatesPlugin, SortTagsPlugin, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, setActiveHead, useBodyAttrs, useHead, useHtmlAttrs, useServerBodyAttrs, useServerHead, useServerHtmlAttrs, useServerTagBase, useServerTagLink, useServerTagMeta, useServerTagMetaFlat, useServerTagNoscript, useServerTagScript, useServerTagStyle, useServerTagTitle, useServerTitleTemplate, useTagBase, useTagLink, useTagMeta, useTagMetaFlat, useTagNoscript, useTagScript, useTagStyle, useTagTitle, useTitleTemplate };
658
+ function defineHeadPlugin(plugin) {
659
+ return plugin;
660
+ }
661
+
662
+ export { DedupesTagsPlugin, DeprecatedTagAttrPlugin, EventHandlersPlugin, HydratesStatePlugin, PatchDomOnEntryUpdatesPlugin, SortTagsPlugin, TitleTemplatePlugin, activeHead, asArray, createHead, defineHeadPlugin, getActiveHead, normaliseEntryTags, setActiveHead, 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",
3
3
  "type": "module",
4
- "version": "0.2.7",
4
+ "version": "0.4.1",
5
5
  "packageManager": "pnpm@7.14.0",
6
6
  "author": "Harlan Wilton <harlan@harlanzw.com>",
7
7
  "license": "MIT",
@@ -30,11 +30,12 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@unhead/schema": "0.2.7",
33
+ "@unhead/dom": "0.4.1",
34
+ "@unhead/schema": "0.4.1",
34
35
  "hookable": "^5.4.1"
35
36
  },
36
37
  "devDependencies": {
37
- "zhead": "1.0.0-beta.10"
38
+ "zhead": "1.0.0-beta.11"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "unbuild .",