@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 +1 -1
- package/dist/head/index.browser.js +189 -17
- package/dist/head/index.browser.js.map +1 -1
- package/dist/head/index.d.ts +101 -24
- package/dist/head/index.d.ts.map +1 -1
- package/dist/head/index.js +195 -18
- package/dist/head/index.js.map +1 -1
- package/package.json +3 -3
- package/src/head/helpers/SeoExpander.ts +141 -0
- package/src/head/index.browser.ts +1 -0
- package/src/head/index.ts +1 -0
- package/src/head/interfaces/Head.ts +66 -27
- package/src/head/providers/BrowserHeadProvider.ts +45 -12
- package/src/head/providers/HeadProvider.ts +26 -7
- package/src/head/providers/ServerHeadProvider.ts +14 -2
package/README.md
CHANGED
|
@@ -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
|
|
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":";;;;;AAGA,IAAa,eAAb,MAA0B;CACxB,AAAO,SAAsC,EAAE;CAE/C,AAAO,SAAS,OAAyB;AACvC,QAAM,OAAO,EACX,GAAG,MAAM,MACV;AAED,OAAK,MAAM,KAAK,KAAK,UAAU,EAAE,EAAE;GACjC,MAAM,OACJ,OAAO,MAAM,aAAa,GAAG,GAAG;AAClC,SAAM,OAAO;IACX,GAAG,MAAM;IACT,GAAG;IACH,MAAM,CAAC,GAAI,MAAM,KAAK,QAAQ,EAAE,EAAG,GAAI,KAAK,QAAQ,EAAE,CAAE;IACzD;;AAGH,OAAK,MAAM,SAAS,MAAM,OACxB,KAAI,MAAM,OAAO,QAAQ,CAAC,MAAM,MAC9B,MAAK,eAAe,MAAM,OAAO,OAAO,MAAM,SAAS,EAAE,CAAC;;CAKhE,AAAU,eACR,MACA,OACA,OACM;AACN,MAAI,CAAC,KAAK,KACR;AAGF,QAAM,SAAS,EAAE;EAEjB,MAAM,OACJ,OAAO,KAAK,SAAS,aACjB,KAAK,KAAK,OAAO,MAAM,KAAK,GAC5B,KAAK;AAEX,MAAI,KAAK,OAAO;AACd,SAAM,SAAS,EAAE;AAEjB,OAAI,MAAM,KAAK,eACb,OAAM,KAAK,QAAQ,GAAG,KAAK,QAAQ,MAAM,KAAK,iBAAiB,MAAM,KAAK;OAE1E,OAAM,KAAK,QAAQ,KAAK;AAG1B,SAAM,KAAK,iBAAiB,KAAK;;AAGnC,MAAI,KAAK,eACP,OAAM,KAAK,iBAAiB;GAC1B,GAAG,MAAM,KAAK;GACd,GAAG,KAAK;GACT;AAGH,MAAI,KAAK,eACP,OAAM,KAAK,iBAAiB;GAC1B,GAAG,MAAM,KAAK;GACd,GAAG,KAAK;GACT;AAGH,MAAI,KAAK,KACP,OAAM,KAAK,OAAO,CAAC,GAAI,MAAM,KAAK,QAAQ,EAAE,EAAG,GAAI,KAAK,QAAQ,EAAE,CAAE;;;;;;;;;AChE1E,MAAa,SAAS,YAAkC;AACtD,QAAO,gBAAgB,eAAe,QAAQ;;AAShD,IAAa,gBAAb,cAAmC,UAAgC;CACjE,AAAmB,WAAW,QAAQ,aAAa;CACnD,AAAU,SAAS;AACjB,OAAK,SAAS,SAAS,CACrB,GAAI,KAAK,SAAS,UAAU,EAAE,EAC9B,KAAK,QACN;;;AAIL,MAAM,QAAQ;;;;ACvBd,IAAa,sBAAb,MAAiC;CAC/B,AAAmB,eAAe,QAAQ,aAAa;CAEvD,IAAc,WAAqB;AACjC,SAAO,OAAO;;CAGhB,AAAmB,kBAAkB,MAAM;EACzC,IAAI;EACJ,SAAS,OAAO,EAAE,YAAY;AAC5B,QAAK,aAAa,SAAS,MAAM;AACjC,OAAI,MAAM,KACR,MAAK,WAAW,KAAK,UAAU,MAAM,KAAK;;EAG/C,CAAC;CAEF,AAAmB,kBAAkB,MAAM;EACzC,IAAI;EACJ,SAAS,OAAO,EAAE,YAAY;AAC5B,QAAK,aAAa,SAAS,MAAM;AACjC,OAAI,MAAM,KACR,MAAK,WAAW,KAAK,UAAU,MAAM,KAAK;;EAG/C,CAAC;CAEF,AAAO,QAAQ,UAA0B;AACvC,SAAO;GACL,IAAI,QAAQ;AACV,WAAO,SAAS;;GAElB,IAAI,iBAAiB;IACnB,MAAMA,QAAgC,EAAE;AACxC,SAAK,MAAM,QAAQ,SAAS,gBAAgB,WAC1C,OAAM,KAAK,QAAQ,KAAK;AAE1B,WAAO;;GAET,IAAI,iBAAiB;IACnB,MAAMA,QAAgC,EAAE;AACxC,SAAK,MAAM,QAAQ,SAAS,KAAK,WAC/B,OAAM,KAAK,QAAQ,KAAK;AAE1B,WAAO;;GAET,IAAI,OAAO;IACT,MAAMC,QAA6C,EAAE;AACrD,SAAK,MAAM,QAAQ,SAAS,KAAK,iBAAiB,aAAa,EAAE;KAC/D,MAAM,OAAO,KAAK,aAAa,OAAO;KACtC,MAAM,UAAU,KAAK,aAAa,UAAU;AAC5C,SAAI,QAAQ,QACV,OAAM,KAAK;MAAE;MAAM;MAAS,CAAC;;AAGjC,WAAO;;GAEV;;CAGH,AAAO,WAAW,UAAoB,MAAkB;AACtD,MAAI,KAAK,MACP,UAAS,QAAQ,KAAK;AAGxB,MAAI,KAAK,eACP,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,eAAe,CAC5D,KAAI,MACF,UAAS,KAAK,aAAa,KAAK,MAAM;MAEtC,UAAS,KAAK,gBAAgB,IAAI;AAKxC,MAAI,KAAK,eACP,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,eAAe,CAC5D,KAAI,MACF,UAAS,gBAAgB,aAAa,KAAK,MAAM;MAEjD,UAAS,gBAAgB,gBAAgB,IAAI;AAKnD,MAAI,KAAK,KACP,MAAK,MAAM,MAAM,KAAK,MAAM;GAC1B,MAAM,EAAE,MAAM,YAAY;GAC1B,MAAM,OAAO,SAAS,cAAc,cAAc,KAAK,IAAI;AAC3D,OAAI,KACF,MAAK,aAAa,WAAW,QAAQ;QAChC;IACL,MAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,YAAQ,aAAa,QAAQ,KAAK;AAClC,YAAQ,aAAa,WAAW,QAAQ;AACxC,aAAS,KAAK,YAAY,QAAQ;;;AAKxC,MAAI,KAAK,KACP,MAAK,MAAM,MAAM,KAAK,MAAM;GAC1B,MAAM,EAAE,KAAK,SAAS;GACtB,IAAI,OAAO,SAAS,cAAc,aAAa,IAAI,WAAW,KAAK,IAAI;AACvE,OAAI,CAAC,MAAM;AACT,WAAO,SAAS,cAAc,OAAO;AACrC,SAAK,aAAa,OAAO,IAAI;AAC7B,SAAK,aAAa,QAAQ,KAAK;AAC/B,aAAS,KAAK,YAAY,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;ACzFzC,MAAa,WAAW,YAA4C;CAClE,MAAM,SAAS,UAAU,OAAO;CAEhC,MAAM,UAAU,cAAc;AAC5B,MAAI,CAAC,OAAO,WAAW,CACrB,QAAO,EAAE;AAGX,SAAO,OAAO,OAAO,oBAAoB,CAAC,QAAQ,OAAO,SAAS;IACjE,EAAE,CAAC;CAEN,MAAM,UAAU,aAAa,SAA8C;AACzE,MAAI,CAAC,OAAO,WAAW,CACrB;AAGF,SACG,OAAO,oBAAoB,CAC3B,WACC,OAAO,UACP,OAAO,SAAS,aAAa,KAAK,QAAQ,GAAG,QAAQ,EAAE,CACxD;IACF,EAAE,CAAC;AAEN,iBAAgB;AACd,MAAI,QACF,SAAQ,QAAQ;IAEjB,EAAE,CAAC;AAEN,QAAO,CAAC,SAAS,QAAQ;;;;;;;;;;;ACjC3B,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,YAAY,CAAC,MAAM;CACnB,UAAU,CAAC,aAAa,oBAAoB;CAC7C,CAAC"}
|
|
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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"}
|
package/dist/head/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
package/dist/head/index.d.ts.map
CHANGED
|
@@ -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":"
|
|
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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"}
|