@alepha/react 0.14.0 → 0.14.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Alepha React
1
+ # Alepha @alepha/react
2
2
 
3
3
  React components and hooks for building Alepha applications.
4
4
 
@@ -2,25 +2,174 @@ import { AlephaReact, useInject } from "@alepha/react";
2
2
  import { $hook, $inject, $module, Alepha, KIND, Primitive, createPrimitive } from "alepha";
3
3
  import { useCallback, useEffect, useMemo } from "react";
4
4
 
5
+ //#region ../../src/head/helpers/SeoExpander.ts
6
+ /**
7
+ * Expands Head configuration into SEO meta tags.
8
+ *
9
+ * Generates:
10
+ * - `<meta name="description">` from head.description
11
+ * - `<meta property="og:*">` OpenGraph tags
12
+ * - `<meta name="twitter:*">` Twitter Card tags
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const helper = new SeoExpander();
17
+ * const { meta, link } = helper.expand({
18
+ * title: "My App",
19
+ * description: "Build amazing apps",
20
+ * image: "https://example.com/og.png",
21
+ * url: "https://example.com/",
22
+ * });
23
+ * ```
24
+ */
25
+ var SeoExpander = class {
26
+ expand(head) {
27
+ const meta = [];
28
+ const link = [];
29
+ if (!(head.description || head.image || head.url || head.siteName || head.locale || head.type || head.og || head.twitter)) return {
30
+ meta,
31
+ link
32
+ };
33
+ if (head.description) meta.push({
34
+ name: "description",
35
+ content: head.description
36
+ });
37
+ if (head.url) link.push({
38
+ rel: "canonical",
39
+ href: head.url
40
+ });
41
+ this.expandOpenGraph(head, meta);
42
+ this.expandTwitter(head, meta);
43
+ return {
44
+ meta,
45
+ link
46
+ };
47
+ }
48
+ expandOpenGraph(head, meta) {
49
+ const ogTitle = head.og?.title ?? head.title;
50
+ const ogDescription = head.og?.description ?? head.description;
51
+ const ogImage = head.og?.image ?? head.image;
52
+ if (head.type || ogTitle) meta.push({
53
+ property: "og:type",
54
+ content: head.type ?? "website"
55
+ });
56
+ if (head.url) meta.push({
57
+ property: "og:url",
58
+ content: head.url
59
+ });
60
+ if (ogTitle) meta.push({
61
+ property: "og:title",
62
+ content: ogTitle
63
+ });
64
+ if (ogDescription) meta.push({
65
+ property: "og:description",
66
+ content: ogDescription
67
+ });
68
+ if (ogImage) {
69
+ meta.push({
70
+ property: "og:image",
71
+ content: ogImage
72
+ });
73
+ if (head.imageWidth) meta.push({
74
+ property: "og:image:width",
75
+ content: String(head.imageWidth)
76
+ });
77
+ if (head.imageHeight) meta.push({
78
+ property: "og:image:height",
79
+ content: String(head.imageHeight)
80
+ });
81
+ if (head.imageAlt) meta.push({
82
+ property: "og:image:alt",
83
+ content: head.imageAlt
84
+ });
85
+ }
86
+ if (head.siteName) meta.push({
87
+ property: "og:site_name",
88
+ content: head.siteName
89
+ });
90
+ if (head.locale) meta.push({
91
+ property: "og:locale",
92
+ content: head.locale
93
+ });
94
+ }
95
+ expandTwitter(head, meta) {
96
+ const twitterTitle = head.twitter?.title ?? head.title;
97
+ const twitterDescription = head.twitter?.description ?? head.description;
98
+ const twitterImage = head.twitter?.image ?? head.image;
99
+ if (head.twitter?.card || twitterTitle || twitterImage) meta.push({
100
+ name: "twitter:card",
101
+ content: head.twitter?.card ?? (twitterImage ? "summary_large_image" : "summary")
102
+ });
103
+ if (head.url) meta.push({
104
+ name: "twitter:url",
105
+ content: head.url
106
+ });
107
+ if (twitterTitle) meta.push({
108
+ name: "twitter:title",
109
+ content: twitterTitle
110
+ });
111
+ if (twitterDescription) meta.push({
112
+ name: "twitter:description",
113
+ content: twitterDescription
114
+ });
115
+ if (twitterImage) {
116
+ meta.push({
117
+ name: "twitter:image",
118
+ content: twitterImage
119
+ });
120
+ if (head.imageAlt) meta.push({
121
+ name: "twitter:image:alt",
122
+ content: head.imageAlt
123
+ });
124
+ }
125
+ if (head.twitter?.site) meta.push({
126
+ name: "twitter:site",
127
+ content: head.twitter.site
128
+ });
129
+ if (head.twitter?.creator) meta.push({
130
+ name: "twitter:creator",
131
+ content: head.twitter.creator
132
+ });
133
+ }
134
+ };
135
+
136
+ //#endregion
5
137
  //#region ../../src/head/providers/HeadProvider.ts
6
138
  var HeadProvider = class {
139
+ seoExpander = $inject(SeoExpander);
7
140
  global = [];
8
141
  fillHead(state) {
9
142
  state.head = { ...state.head };
10
143
  for (const h of this.global ?? []) {
11
144
  const head = typeof h === "function" ? h() : h;
12
- state.head = {
13
- ...state.head,
14
- ...head,
15
- meta: [...state.head.meta ?? [], ...head.meta ?? []]
16
- };
145
+ this.mergeHead(state, head);
17
146
  }
18
147
  for (const layer of state.layers) if (layer.route?.head && !layer.error) this.fillHeadByPage(layer.route, state, layer.props ?? {});
19
148
  }
149
+ mergeHead(state, head) {
150
+ const { meta, link } = this.seoExpander.expand(head);
151
+ state.head = {
152
+ ...state.head,
153
+ ...head,
154
+ meta: [
155
+ ...state.head.meta ?? [],
156
+ ...meta,
157
+ ...head.meta ?? []
158
+ ],
159
+ link: [
160
+ ...state.head.link ?? [],
161
+ ...link,
162
+ ...head.link ?? []
163
+ ]
164
+ };
165
+ }
20
166
  fillHeadByPage(page, state, props) {
21
167
  if (!page.head) return;
22
168
  state.head ??= {};
23
169
  const head = typeof page.head === "function" ? page.head(props, state.head) : page.head;
170
+ const { meta, link } = this.seoExpander.expand(head);
171
+ state.head.meta = [...state.head.meta ?? [], ...meta];
172
+ state.head.link = [...state.head.link ?? [], ...link];
24
173
  if (head.title) {
25
174
  state.head ??= {};
26
175
  if (state.head.titleSeparator) state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;
@@ -36,6 +185,7 @@ var HeadProvider = class {
36
185
  ...head.bodyAttributes
37
186
  };
38
187
  if (head.meta) state.head.meta = [...state.head.meta ?? [], ...head.meta ?? []];
188
+ if (head.link) state.head.link = [...state.head.link ?? [], ...head.link ?? []];
39
189
  }
40
190
  };
41
191
 
@@ -101,6 +251,14 @@ var BrowserHeadProvider = class {
101
251
  content
102
252
  });
103
253
  }
254
+ for (const meta of document.head.querySelectorAll("meta[property]")) {
255
+ const property = meta.getAttribute("property");
256
+ const content = meta.getAttribute("content");
257
+ if (property && content) metas.push({
258
+ property,
259
+ content
260
+ });
261
+ }
104
262
  return metas;
105
263
  }
106
264
  };
@@ -111,17 +269,7 @@ var BrowserHeadProvider = class {
111
269
  else document.body.removeAttribute(key);
112
270
  if (head.htmlAttributes) for (const [key, value] of Object.entries(head.htmlAttributes)) if (value) document.documentElement.setAttribute(key, value);
113
271
  else document.documentElement.removeAttribute(key);
114
- if (head.meta) for (const it of head.meta) {
115
- const { name, content } = it;
116
- const meta = document.querySelector(`meta[name="${name}"]`);
117
- if (meta) meta.setAttribute("content", content);
118
- else {
119
- const newMeta = document.createElement("meta");
120
- newMeta.setAttribute("name", name);
121
- newMeta.setAttribute("content", content);
122
- document.head.appendChild(newMeta);
123
- }
124
- }
272
+ if (head.meta) for (const it of head.meta) this.renderMetaTag(document, it);
125
273
  if (head.link) for (const it of head.link) {
126
274
  const { rel, href } = it;
127
275
  let link = document.querySelector(`link[rel="${rel}"][href="${href}"]`);
@@ -133,6 +281,30 @@ var BrowserHeadProvider = class {
133
281
  }
134
282
  }
135
283
  }
284
+ renderMetaTag(document, meta) {
285
+ const { content } = meta;
286
+ if (meta.property) {
287
+ const existing = document.querySelector(`meta[property="${meta.property}"]`);
288
+ if (existing) existing.setAttribute("content", content);
289
+ else {
290
+ const newMeta = document.createElement("meta");
291
+ newMeta.setAttribute("property", meta.property);
292
+ newMeta.setAttribute("content", content);
293
+ document.head.appendChild(newMeta);
294
+ }
295
+ return;
296
+ }
297
+ if (meta.name) {
298
+ const existing = document.querySelector(`meta[name="${meta.name}"]`);
299
+ if (existing) existing.setAttribute("content", content);
300
+ else {
301
+ const newMeta = document.createElement("meta");
302
+ newMeta.setAttribute("name", meta.name);
303
+ newMeta.setAttribute("content", content);
304
+ document.head.appendChild(newMeta);
305
+ }
306
+ }
307
+ }
136
308
  };
137
309
 
138
310
  //#endregion
@@ -185,5 +357,5 @@ const AlephaReactHead = $module({
185
357
  });
186
358
 
187
359
  //#endregion
188
- export { $head, AlephaReactHead, BrowserHeadProvider, HeadPrimitive, useHead };
360
+ export { $head, AlephaReactHead, BrowserHeadProvider, HeadPrimitive, SeoExpander, useHead };
189
361
  //# sourceMappingURL=index.browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","names":["attrs: Record<string, string>","metas: { name: string; content: string }[]"],"sources":["../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { PageRoute, ReactRouterState } from \"@alepha/react\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\nexport class HeadProvider {\n public global?: Array<Head | (() => Head)> = [];\n\n public fillHead(state: ReactRouterState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head =\n typeof h === \"function\" ? h() : h;\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...(head.meta ?? [])],\n };\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected fillHeadByPage(\n page: PageRoute,\n state: ReactRouterState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n if (head.htmlAttributes) {\n state.head.htmlAttributes = {\n ...state.head.htmlAttributes,\n ...head.htmlAttributes,\n };\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n }\n}\n","import { $inject, createPrimitive, Primitive, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [\n ...(this.provider.global ?? []),\n this.options,\n ];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $hook, $inject } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class BrowserHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: { name: string; content: string }[] = [];\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n const { name, content } = it;\n const meta = document.querySelector(`meta[name=\"${name}\"]`);\n if (meta) {\n meta.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n document.head.appendChild(link);\n }\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.browser.js","names":["meta: HeadMeta[]","link: Array<{ rel: string; href: string }>","attrs: Record<string, string>","metas: HeadMeta[]"],"sources":["../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { Head, HeadMeta } from \"../interfaces/Head.ts\";\n\n/**\n * Expands Head configuration into SEO meta tags.\n *\n * Generates:\n * - `<meta name=\"description\">` from head.description\n * - `<meta property=\"og:*\">` OpenGraph tags\n * - `<meta name=\"twitter:*\">` Twitter Card tags\n *\n * @example\n * ```ts\n * const helper = new SeoExpander();\n * const { meta, link } = helper.expand({\n * title: \"My App\",\n * description: \"Build amazing apps\",\n * image: \"https://example.com/og.png\",\n * url: \"https://example.com/\",\n * });\n * ```\n */\nexport class SeoExpander {\n public expand(head: Head): {\n meta: HeadMeta[];\n link: Array<{ rel: string; href: string }>;\n } {\n const meta: HeadMeta[] = [];\n const link: Array<{ rel: string; href: string }> = [];\n\n // Only expand SEO if there's meaningful content beyond just title\n const hasSeoContent =\n head.description ||\n head.image ||\n head.url ||\n head.siteName ||\n head.locale ||\n head.type ||\n head.og ||\n head.twitter;\n\n if (!hasSeoContent) {\n return { meta, link };\n }\n\n // Base description\n if (head.description) {\n meta.push({ name: \"description\", content: head.description });\n }\n\n // Canonical URL\n if (head.url) {\n link.push({ rel: \"canonical\", href: head.url });\n }\n\n // OpenGraph tags\n this.expandOpenGraph(head, meta);\n\n // Twitter Card tags\n this.expandTwitter(head, meta);\n\n return { meta, link };\n }\n\n protected expandOpenGraph(head: Head, meta: HeadMeta[]): void {\n const ogTitle = head.og?.title ?? head.title;\n const ogDescription = head.og?.description ?? head.description;\n const ogImage = head.og?.image ?? head.image;\n\n if (head.type || ogTitle) {\n meta.push({ property: \"og:type\", content: head.type ?? \"website\" });\n }\n if (head.url) {\n meta.push({ property: \"og:url\", content: head.url });\n }\n if (ogTitle) {\n meta.push({ property: \"og:title\", content: ogTitle });\n }\n if (ogDescription) {\n meta.push({ property: \"og:description\", content: ogDescription });\n }\n if (ogImage) {\n meta.push({ property: \"og:image\", content: ogImage });\n if (head.imageWidth) {\n meta.push({\n property: \"og:image:width\",\n content: String(head.imageWidth),\n });\n }\n if (head.imageHeight) {\n meta.push({\n property: \"og:image:height\",\n content: String(head.imageHeight),\n });\n }\n if (head.imageAlt) {\n meta.push({ property: \"og:image:alt\", content: head.imageAlt });\n }\n }\n if (head.siteName) {\n meta.push({ property: \"og:site_name\", content: head.siteName });\n }\n if (head.locale) {\n meta.push({ property: \"og:locale\", content: head.locale });\n }\n }\n\n protected expandTwitter(head: Head, meta: HeadMeta[]): void {\n const twitterTitle = head.twitter?.title ?? head.title;\n const twitterDescription = head.twitter?.description ?? head.description;\n const twitterImage = head.twitter?.image ?? head.image;\n\n if (head.twitter?.card || twitterTitle || twitterImage) {\n meta.push({\n name: \"twitter:card\",\n content:\n head.twitter?.card ?? (twitterImage ? \"summary_large_image\" : \"summary\"),\n });\n }\n if (head.url) {\n meta.push({ name: \"twitter:url\", content: head.url });\n }\n if (twitterTitle) {\n meta.push({ name: \"twitter:title\", content: twitterTitle });\n }\n if (twitterDescription) {\n meta.push({ name: \"twitter:description\", content: twitterDescription });\n }\n if (twitterImage) {\n meta.push({ name: \"twitter:image\", content: twitterImage });\n if (head.imageAlt) {\n meta.push({ name: \"twitter:image:alt\", content: head.imageAlt });\n }\n }\n if (head.twitter?.site) {\n meta.push({ name: \"twitter:site\", content: head.twitter.site });\n }\n if (head.twitter?.creator) {\n meta.push({ name: \"twitter:creator\", content: head.twitter.creator });\n }\n }\n}\n","import type { PageRoute, ReactRouterState } from \"@alepha/react\";\nimport { $inject } from \"alepha\";\nimport { SeoExpander } from \"../helpers/SeoExpander.ts\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\nexport class HeadProvider {\n protected readonly seoExpander = $inject(SeoExpander);\n\n public global?: Array<Head | (() => Head)> = [];\n\n public fillHead(state: ReactRouterState) {\n state.head = {\n ...state.head,\n };\n\n for (const h of this.global ?? []) {\n const head = typeof h === \"function\" ? h() : h;\n this.mergeHead(state, head);\n }\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected mergeHead(state: ReactRouterState, head: Head): void {\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head = {\n ...state.head,\n ...head,\n meta: [...(state.head.meta ?? []), ...meta, ...(head.meta ?? [])],\n link: [...(state.head.link ?? []), ...link, ...(head.link ?? [])],\n };\n }\n\n protected fillHeadByPage(\n page: PageRoute,\n state: ReactRouterState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n // Expand SEO fields into meta tags\n const { meta, link } = this.seoExpander.expand(head);\n state.head.meta = [...(state.head.meta ?? []), ...meta];\n state.head.link = [...(state.head.link ?? []), ...link];\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n if (head.htmlAttributes) {\n state.head.htmlAttributes = {\n ...state.head.htmlAttributes,\n ...head.htmlAttributes,\n };\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n\n if (head.link) {\n state.head.link = [...(state.head.link ?? []), ...(head.link ?? [])];\n }\n }\n}\n","import { $inject, createPrimitive, Primitive, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = [\n ...(this.provider.global ?? []),\n this.options,\n ];\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $hook, $inject } from \"alepha\";\nimport type { Head, HeadMeta } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class BrowserHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: HeadMeta[] = [];\n // Get meta tags with name attribute\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n // Get meta tags with property attribute (OpenGraph)\n for (const meta of document.head.querySelectorAll(\"meta[property]\")) {\n const property = meta.getAttribute(\"property\");\n const content = meta.getAttribute(\"content\");\n if (property && content) {\n metas.push({ property, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n this.renderMetaTag(document, it);\n }\n }\n\n if (head.link) {\n for (const it of head.link) {\n const { rel, href } = it;\n let link = document.querySelector(`link[rel=\"${rel}\"][href=\"${href}\"]`);\n if (!link) {\n link = document.createElement(\"link\");\n link.setAttribute(\"rel\", rel);\n link.setAttribute(\"href\", href);\n document.head.appendChild(link);\n }\n }\n }\n }\n\n protected renderMetaTag(document: Document, meta: HeadMeta): void {\n const { content } = meta;\n\n // Handle OpenGraph tags (property attribute)\n if (meta.property) {\n const existing = document.querySelector(\n `meta[property=\"${meta.property}\"]`,\n );\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"property\", meta.property);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n return;\n }\n\n // Handle standard meta tags (name attribute)\n if (meta.name) {\n const existing = document.querySelector(`meta[name=\"${meta.name}\"]`);\n if (existing) {\n existing.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", meta.name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./helpers/SeoExpander.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}
@@ -4,33 +4,69 @@ import { KIND, Primitive } from "alepha";
4
4
  import { ServerTimingProvider } from "alepha/server";
5
5
 
6
6
  //#region ../../src/head/interfaces/Head.d.ts
7
- interface Head extends SimpleHead {
7
+
8
+ /**
9
+ * Complete head configuration combining basic head elements with SEO fields.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * $head({
14
+ * title: "My App",
15
+ * description: "Build amazing apps",
16
+ * image: "https://example.com/og.png",
17
+ * url: "https://example.com/",
18
+ * siteName: "My App",
19
+ * twitter: { card: "summary_large_image" },
20
+ * })
21
+ * ```
22
+ */
23
+ interface Head extends SimpleHead, Seo {}
24
+ /**
25
+ * SEO configuration for automatic meta tag generation.
26
+ * Fields are used for meta description, OpenGraph, and Twitter Card tags.
27
+ */
28
+ interface Seo {
29
+ /** Page description - used for meta description, og:description, twitter:description */
8
30
  description?: string;
9
- keywords?: string[];
10
- author?: string;
11
- robots?: string;
12
- themeColor?: string;
13
- viewport?: string | {
14
- width?: string;
15
- height?: string;
16
- initialScale?: string;
17
- maximumScale?: string;
18
- userScalable?: "no" | "yes" | "0" | "1";
19
- interactiveWidget?: "resizes-visual" | "resizes-content" | "overlays-content";
20
- };
21
- og?: {
31
+ /** Primary image URL - used for og:image and twitter:image */
32
+ image?: string;
33
+ /** Canonical URL - used for og:url and link rel="canonical" */
34
+ url?: string;
35
+ /** Site name - used for og:site_name */
36
+ siteName?: string;
37
+ /** Locale - used for og:locale (e.g., "en_US") */
38
+ locale?: string;
39
+ /** Content type - used for og:type (default: "website") */
40
+ type?: "website" | "article" | "product" | "profile" | string;
41
+ /** Image width in pixels - used for og:image:width */
42
+ imageWidth?: number;
43
+ /** Image height in pixels - used for og:image:height */
44
+ imageHeight?: number;
45
+ /** Image alt text - used for og:image:alt and twitter:image:alt */
46
+ imageAlt?: string;
47
+ /** Twitter-specific overrides */
48
+ twitter?: {
49
+ /** Twitter card type */
50
+ card?: "summary" | "summary_large_image" | "app" | "player";
51
+ /** @username of website */
52
+ site?: string;
53
+ /** @username of content creator */
54
+ creator?: string;
55
+ /** Override title for Twitter */
22
56
  title?: string;
57
+ /** Override description for Twitter */
23
58
  description?: string;
59
+ /** Override image for Twitter */
24
60
  image?: string;
25
- url?: string;
26
- type?: string;
27
61
  };
28
- twitter?: {
29
- card?: string;
62
+ /** OpenGraph-specific overrides */
63
+ og?: {
64
+ /** Override title for OpenGraph */
30
65
  title?: string;
66
+ /** Override description for OpenGraph */
31
67
  description?: string;
68
+ /** Override image for OpenGraph */
32
69
  image?: string;
33
- site?: string;
34
70
  };
35
71
  }
36
72
  interface SimpleHead {
@@ -38,20 +74,60 @@ interface SimpleHead {
38
74
  titleSeparator?: string;
39
75
  htmlAttributes?: Record<string, string>;
40
76
  bodyAttributes?: Record<string, string>;
41
- meta?: Array<{
42
- name: string;
43
- content: string;
44
- }>;
77
+ /** Meta tags - supports both name and property attributes */
78
+ meta?: Array<HeadMeta>;
45
79
  link?: Array<{
46
80
  rel: string;
47
81
  href: string;
48
82
  }>;
49
83
  }
84
+ interface HeadMeta {
85
+ /** Meta name attribute (e.g., "description", "twitter:card") */
86
+ name?: string;
87
+ /** Meta property attribute (e.g., "og:title", "og:image") */
88
+ property?: string;
89
+ /** Meta content value */
90
+ content: string;
91
+ }
92
+ //#endregion
93
+ //#region ../../src/head/helpers/SeoExpander.d.ts
94
+ /**
95
+ * Expands Head configuration into SEO meta tags.
96
+ *
97
+ * Generates:
98
+ * - `<meta name="description">` from head.description
99
+ * - `<meta property="og:*">` OpenGraph tags
100
+ * - `<meta name="twitter:*">` Twitter Card tags
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const helper = new SeoExpander();
105
+ * const { meta, link } = helper.expand({
106
+ * title: "My App",
107
+ * description: "Build amazing apps",
108
+ * image: "https://example.com/og.png",
109
+ * url: "https://example.com/",
110
+ * });
111
+ * ```
112
+ */
113
+ declare class SeoExpander {
114
+ expand(head: Head): {
115
+ meta: HeadMeta[];
116
+ link: Array<{
117
+ rel: string;
118
+ href: string;
119
+ }>;
120
+ };
121
+ protected expandOpenGraph(head: Head, meta: HeadMeta[]): void;
122
+ protected expandTwitter(head: Head, meta: HeadMeta[]): void;
123
+ }
50
124
  //#endregion
51
125
  //#region ../../src/head/providers/HeadProvider.d.ts
52
126
  declare class HeadProvider {
127
+ protected readonly seoExpander: SeoExpander;
53
128
  global?: Array<Head | (() => Head)>;
54
129
  fillHead(state: ReactRouterState): void;
130
+ protected mergeHead(state: ReactRouterState, head: Head): void;
55
131
  protected fillHeadByPage(page: PageRoute, state: ReactRouterState, props: Record<string, any>): void;
56
132
  }
57
133
  //#endregion
@@ -100,6 +176,7 @@ declare class ServerHeadProvider {
100
176
  protected mergeAttributes(existing: string, attrs: Record<string, string>): string;
101
177
  protected parseAttributes(attrStr: string): Record<string, string>;
102
178
  protected escapeHtml(str: string): string;
179
+ protected renderMetaTag(meta: HeadMeta): string;
103
180
  }
104
181
  //#endregion
105
182
  //#region ../../src/head/index.d.ts
@@ -119,5 +196,5 @@ declare module "@alepha/react" {
119
196
  */
120
197
  declare const AlephaReactHead: alepha1.Service<alepha1.Module>;
121
198
  //#endregion
122
- export { $head, AlephaReactHead, Head, HeadPrimitive, HeadPrimitiveOptions, ServerHeadProvider, SimpleHead, UseHeadOptions, UseHeadReturn, useHead };
199
+ export { $head, AlephaReactHead, Head, HeadMeta, HeadPrimitive, HeadPrimitiveOptions, Seo, SeoExpander, ServerHeadProvider, SimpleHead, UseHeadOptions, UseHeadReturn, useHead };
123
200
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/head/interfaces/Head.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/hooks/useHead.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/head/interfaces/Head.ts","../../src/head/helpers/SeoExpander.ts","../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/hooks/useHead.ts","../../src/head/providers/ServerHeadProvider.ts","../../src/head/index.ts"],"sourcesContent":[],"mappings":"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"}