shelving 1.271.0 → 1.271.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.271.0",
3
+ "version": "1.271.1",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,15 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { renderToStaticMarkup } from "react-dom/server";
3
+ import { createMeta, Head, MetaContext } from "shelving/ui";
4
+
5
+ describe("Head", () => {
6
+ test("emits a <link> element when links are set in context", () => {
7
+ const html = renderToStaticMarkup(
8
+ <MetaContext value={createMeta({ root: "http://x.com/", url: "./", links: { icon: "/img/favicon.png" } })}>
9
+ <Head />
10
+ </MetaContext>,
11
+ );
12
+ expect(html).toContain('rel="icon"');
13
+ expect(html).toContain('href="http://x.com/img/favicon.png"');
14
+ });
15
+ });
package/ui/util/meta.js CHANGED
@@ -115,7 +115,7 @@ export function mergeMetaTags(current, next) {
115
115
  * @see https://shelving.cc/ui/mergeMetaLinks
116
116
  */
117
117
  export function mergeMetaLinks(current, next, url, root, caller = mergeMetaLinks) {
118
- return next ? { ...current, ..._yieldMetaLinkEntries(next, url, root, caller) } : current;
118
+ return next ? { ...current, ...Object.fromEntries(_yieldMetaLinkEntries(next, url, root, caller)) } : current;
119
119
  }
120
120
  function* _yieldMetaLinkEntries(links, url, root, caller) {
121
121
  for (const [k, link] of getDictionaryItems(links))
@@ -0,0 +1,63 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { createMeta, mergeMeta, mergeMetaAssets, mergeMetaLinks } from "shelving/ui";
3
+ import { requireURL } from "shelving/util/url";
4
+
5
+ const ROOT = requireURL("http://x.com/app/");
6
+ const URL = requireURL("http://x.com/app/sub/page");
7
+
8
+ describe("mergeMetaLinks", () => {
9
+ test("returns resolved entries when current is unset", () => {
10
+ const links = mergeMetaLinks(undefined, { icon: "/img/favicon.png" }, ROOT, ROOT);
11
+ expect(links?.icon?.href).toBe("http://x.com/app/img/favicon.png");
12
+ });
13
+
14
+ test("merges next over current — new keys override, untouched keys survive", () => {
15
+ const current = mergeMetaLinks(undefined, { icon: "/old.png", canonical: "/page" }, ROOT, ROOT);
16
+ const links = mergeMetaLinks(current, { icon: "/new.png" }, ROOT, ROOT);
17
+ expect(links?.icon?.href).toBe("http://x.com/app/new.png");
18
+ expect(links?.canonical?.href).toBe("http://x.com/app/page");
19
+ });
20
+
21
+ test("skips nullish link values", () => {
22
+ const links = mergeMetaLinks(undefined, { icon: undefined, canonical: null }, ROOT, ROOT);
23
+ expect(links).toEqual({});
24
+ });
25
+
26
+ test("resolves relative hrefs against url and absolute hrefs against root", () => {
27
+ const links = mergeMetaLinks(undefined, { canonical: "./other", icon: "/favicon.png" }, URL, ROOT);
28
+ expect(links?.canonical?.href).toBe("http://x.com/app/sub/page/other");
29
+ expect(links?.icon?.href).toBe("http://x.com/app/favicon.png");
30
+ });
31
+
32
+ test("returns current unchanged when next is unset", () => {
33
+ const current = mergeMetaLinks(undefined, { icon: "/favicon.png" }, ROOT, ROOT);
34
+ expect(mergeMetaLinks(current, undefined, ROOT, ROOT)).toBe(current);
35
+ });
36
+ });
37
+
38
+ describe("mergeMetaAssets", () => {
39
+ test("resolves relative hrefs against url and absolute hrefs against root", () => {
40
+ const assets = mergeMetaAssets(undefined, ["./style.css", "/base.css"], URL, ROOT);
41
+ expect(assets?.map(({ href }) => href)).toEqual(["http://x.com/app/sub/page/style.css", "http://x.com/app/base.css"]);
42
+ });
43
+
44
+ test("appends next after current and skips nullish values", () => {
45
+ const current = mergeMetaAssets(undefined, ["/base.css"], ROOT, ROOT);
46
+ const assets = mergeMetaAssets(current, [undefined, "/extra.css", null], ROOT, ROOT);
47
+ expect(assets?.map(({ href }) => href)).toEqual(["http://x.com/app/base.css", "http://x.com/app/extra.css"]);
48
+ });
49
+ });
50
+
51
+ describe("mergeMeta", () => {
52
+ test("meta with links set produces non-empty resolved links", () => {
53
+ const meta = createMeta({ root: "http://x.com/", url: "./", links: { icon: "/img/favicon.png" } });
54
+ expect(meta.links?.icon?.href).toBe("http://x.com/img/favicon.png");
55
+ });
56
+
57
+ test("merges new links over existing resolved links", () => {
58
+ const meta1 = createMeta({ root: "http://x.com/", url: "./", links: { icon: "/favicon.png" } });
59
+ const meta2 = mergeMeta(meta1, { url: "./page", links: { canonical: "/page" } });
60
+ expect(meta2.links?.icon?.href).toBe("http://x.com/favicon.png");
61
+ expect(meta2.links?.canonical?.href).toBe("http://x.com/page");
62
+ });
63
+ });
package/ui/util/meta.ts CHANGED
@@ -239,7 +239,7 @@ export function mergeMetaLinks(
239
239
  root: ImmutableURL | undefined,
240
240
  caller: AnyCaller = mergeMetaLinks,
241
241
  ): MetaLinks | undefined {
242
- return next ? { ...current, ..._yieldMetaLinkEntries(next, url, root, caller) } : current;
242
+ return next ? { ...current, ...Object.fromEntries(_yieldMetaLinkEntries(next, url, root, caller)) } : current;
243
243
  }
244
244
  function* _yieldMetaLinkEntries(
245
245
  links: PossibleMetaLinks,