@puzzle-ui/seo 0.0.0-stage → 0.5.0
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 +755 -2
- package/dist/LICENSE +21 -0
- package/dist/context/config.cjs +82 -0
- package/dist/context/config.cjs.map +1 -0
- package/dist/context/config.d.cts +6 -0
- package/dist/context/config.d.ts +6 -0
- package/dist/context/config.js +80 -0
- package/dist/context/config.js.map +1 -0
- package/dist/context/index.cjs +6 -0
- package/dist/context/index.d.cts +4 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.js +3 -0
- package/dist/context/types.d.cts +46 -0
- package/dist/context/types.d.ts +46 -0
- package/dist/core/graph.cjs +464 -0
- package/dist/core/graph.cjs.map +1 -0
- package/dist/core/graph.d.cts +62 -0
- package/dist/core/graph.d.ts +62 -0
- package/dist/core/graph.js +456 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/index.cjs +11 -0
- package/dist/core/index.d.cts +3 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +1 -0
- package/dist/core/types.d.cts +92 -0
- package/dist/core/types.d.ts +92 -0
- package/dist/index.cjs +96 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata/index.cjs +4 -0
- package/dist/metadata/index.d.cts +3 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.js +1 -0
- package/dist/metadata/resolve.cjs +122 -0
- package/dist/metadata/resolve.cjs.map +1 -0
- package/dist/metadata/resolve.d.cts +4 -0
- package/dist/metadata/resolve.d.ts +4 -0
- package/dist/metadata/resolve.js +121 -0
- package/dist/metadata/resolve.js.map +1 -0
- package/dist/metadata/types.d.cts +82 -0
- package/dist/metadata/types.d.ts +82 -0
- package/dist/next/index.cjs +13 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +15 -0
- package/dist/next/index.d.ts +14 -0
- package/dist/next/index.js +12 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/context.cjs +30 -0
- package/dist/react/context.cjs.map +1 -0
- package/dist/react/context.d.cts +14 -0
- package/dist/react/context.d.ts +14 -0
- package/dist/react/context.js +27 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/index.cjs +48 -0
- package/dist/react/index.d.cts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +5 -0
- package/dist/react/metadata.cjs +100 -0
- package/dist/react/metadata.cjs.map +1 -0
- package/dist/react/metadata.d.cts +18 -0
- package/dist/react/metadata.d.ts +18 -0
- package/dist/react/metadata.js +99 -0
- package/dist/react/metadata.js.map +1 -0
- package/dist/react/provider.cjs +42 -0
- package/dist/react/provider.cjs.map +1 -0
- package/dist/react/provider.d.cts +3 -0
- package/dist/react/provider.d.ts +3 -0
- package/dist/react/provider.js +42 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/schema-components.cjs +294 -0
- package/dist/react/schema-components.cjs.map +1 -0
- package/dist/react/schema-components.d.cts +84 -0
- package/dist/react/schema-components.d.ts +80 -0
- package/dist/react/schema-components.js +254 -0
- package/dist/react/schema-components.js.map +1 -0
- package/dist/react/types.d.cts +81 -0
- package/dist/react/types.d.ts +80 -0
- package/dist/react-router/index.cjs +13 -0
- package/dist/react-router/index.cjs.map +1 -0
- package/dist/react-router/index.d.cts +21 -0
- package/dist/react-router/index.d.ts +20 -0
- package/dist/react-router/index.js +12 -0
- package/dist/react-router/index.js.map +1 -0
- package/dist/robots/generate.cjs +88 -0
- package/dist/robots/generate.cjs.map +1 -0
- package/dist/robots/generate.d.cts +4 -0
- package/dist/robots/generate.d.ts +4 -0
- package/dist/robots/generate.js +86 -0
- package/dist/robots/generate.js.map +1 -0
- package/dist/robots/index.cjs +5 -0
- package/dist/robots/index.d.cts +3 -0
- package/dist/robots/index.d.ts +3 -0
- package/dist/robots/index.js +1 -0
- package/dist/robots/types.d.cts +27 -0
- package/dist/robots/types.d.ts +27 -0
- package/dist/schema/create-registry.cjs +12 -0
- package/dist/schema/create-registry.cjs.map +1 -0
- package/dist/schema/create-registry.d.cts +2 -0
- package/dist/schema/create-registry.d.ts +2 -0
- package/dist/schema/create-registry.js +12 -0
- package/dist/schema/create-registry.js.map +1 -0
- package/dist/schema/generated/index.d.cts +2 -0
- package/dist/schema/generated/schemaorg.cjs +36386 -0
- package/dist/schema/generated/schemaorg.cjs.map +1 -0
- package/dist/schema/generated/schemaorg.d.cts +945 -0
- package/dist/schema/generated/schemaorg.d.ts +945 -0
- package/dist/schema/generated/schemaorg.js +36384 -0
- package/dist/schema/generated/schemaorg.js.map +1 -0
- package/dist/schema/index.cjs +11 -0
- package/dist/schema/index.d.cts +7 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/registry.cjs +194 -0
- package/dist/schema/registry.cjs.map +1 -0
- package/dist/schema/registry.d.cts +2 -0
- package/dist/schema/registry.d.ts +2 -0
- package/dist/schema/registry.js +194 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/schema/types.d.cts +79 -0
- package/dist/schema/types.d.ts +79 -0
- package/dist/schema/validate.cjs +131 -0
- package/dist/schema/validate.cjs.map +1 -0
- package/dist/schema/validate.d.cts +16 -0
- package/dist/schema/validate.d.ts +16 -0
- package/dist/schema/validate.js +131 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/server/handlers.cjs +96 -0
- package/dist/server/handlers.cjs.map +1 -0
- package/dist/server/handlers.d.cts +3 -0
- package/dist/server/handlers.d.ts +3 -0
- package/dist/server/handlers.js +95 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/index.cjs +6 -0
- package/dist/server/index.d.cts +4 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +3 -0
- package/dist/server/routes.cjs +17 -0
- package/dist/server/routes.cjs.map +1 -0
- package/dist/server/routes.d.cts +2 -0
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.js +17 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/types.d.cts +61 -0
- package/dist/server/types.d.ts +61 -0
- package/dist/sitemap/generate.cjs +147 -0
- package/dist/sitemap/generate.cjs.map +1 -0
- package/dist/sitemap/generate.d.cts +3 -0
- package/dist/sitemap/generate.d.ts +3 -0
- package/dist/sitemap/generate.js +146 -0
- package/dist/sitemap/generate.js.map +1 -0
- package/dist/sitemap/index.cjs +11 -0
- package/dist/sitemap/index.d.cts +4 -0
- package/dist/sitemap/index.d.ts +4 -0
- package/dist/sitemap/index.js +3 -0
- package/dist/sitemap/types.d.cts +82 -0
- package/dist/sitemap/types.d.ts +82 -0
- package/dist/sitemap/xml.cjs +215 -0
- package/dist/sitemap/xml.cjs.map +1 -0
- package/dist/sitemap/xml.d.cts +16 -0
- package/dist/sitemap/xml.d.ts +16 -0
- package/dist/sitemap/xml.js +210 -0
- package/dist/sitemap/xml.js.map +1 -0
- package/dist/tanstack/index.cjs +13 -0
- package/dist/tanstack/index.cjs.map +1 -0
- package/dist/tanstack/index.d.cts +19 -0
- package/dist/tanstack/index.d.ts +18 -0
- package/dist/tanstack/index.js +12 -0
- package/dist/tanstack/index.js.map +1 -0
- package/dist/testing/diagnostics.cjs +27 -0
- package/dist/testing/diagnostics.cjs.map +1 -0
- package/dist/testing/diagnostics.d.cts +3 -0
- package/dist/testing/diagnostics.d.ts +3 -0
- package/dist/testing/diagnostics.js +27 -0
- package/dist/testing/diagnostics.js.map +1 -0
- package/dist/testing/fixture.cjs +80 -0
- package/dist/testing/fixture.cjs.map +1 -0
- package/dist/testing/fixture.d.cts +4 -0
- package/dist/testing/fixture.d.ts +4 -0
- package/dist/testing/fixture.js +78 -0
- package/dist/testing/fixture.js.map +1 -0
- package/dist/testing/index.cjs +9 -0
- package/dist/testing/index.d.cts +5 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/inspect.cjs +63 -0
- package/dist/testing/inspect.cjs.map +1 -0
- package/dist/testing/inspect.d.cts +5 -0
- package/dist/testing/inspect.d.ts +5 -0
- package/dist/testing/inspect.js +63 -0
- package/dist/testing/inspect.js.map +1 -0
- package/dist/testing/types.d.cts +40 -0
- package/dist/testing/types.d.ts +40 -0
- package/dist/urls/index.cjs +8 -0
- package/dist/urls/index.d.cts +3 -0
- package/dist/urls/index.d.ts +3 -0
- package/dist/urls/index.js +1 -0
- package/dist/urls/normalize.cjs +65 -0
- package/dist/urls/normalize.cjs.map +1 -0
- package/dist/urls/normalize.d.cts +14 -0
- package/dist/urls/normalize.d.ts +14 -0
- package/dist/urls/normalize.js +60 -0
- package/dist/urls/normalize.js.map +1 -0
- package/dist/urls/types.d.cts +22 -0
- package/dist/urls/types.d.ts +22 -0
- package/dist/validation/index.cjs +5 -0
- package/dist/validation/index.d.cts +3 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/types.d.cts +75 -0
- package/dist/validation/types.d.ts +75 -0
- package/dist/validation/validate.cjs +452 -0
- package/dist/validation/validate.cjs.map +1 -0
- package/dist/validation/validate.d.cts +7 -0
- package/dist/validation/validate.d.ts +6 -0
- package/dist/validation/validate.js +450 -0
- package/dist/validation/validate.js.map +1 -0
- package/package.json +203 -4
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { resolveSeoUrl } from "../context/config.js";
|
|
2
|
+
// src/metadata/resolve.ts
|
|
3
|
+
const normalizeLocale = (locale) => {
|
|
4
|
+
try {
|
|
5
|
+
const normalized = Intl.getCanonicalLocales(locale)[0];
|
|
6
|
+
if (!normalized) throw new Error();
|
|
7
|
+
return normalized;
|
|
8
|
+
} catch {
|
|
9
|
+
throw new Error(`Invalid SEO page locale: "${locale}".`);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const isSupportedLocale = (locale, config) => config.supportedLocales.includes(locale) || config.supportedLocales.includes(locale.split("-")[0]);
|
|
13
|
+
const resolvePageLocale = (locale, config) => {
|
|
14
|
+
const resolved = normalizeLocale(locale ?? config.defaultLocale);
|
|
15
|
+
if (!isSupportedLocale(resolved, config)) throw new Error(`Unsupported SEO page locale "${resolved}". Supported locales: ${config.supportedLocales.join(", ")}.`);
|
|
16
|
+
return resolved;
|
|
17
|
+
};
|
|
18
|
+
const resolveSafeUrl = (value, config) => {
|
|
19
|
+
const resolved = resolveSeoUrl(value, config);
|
|
20
|
+
const url = new URL(resolved);
|
|
21
|
+
if (url.username || url.password) throw new Error(`SEO URLs must not contain credentials: "${value}".`);
|
|
22
|
+
return resolved;
|
|
23
|
+
};
|
|
24
|
+
const resolveTitle = (pageTitle, config) => {
|
|
25
|
+
const fallback = config.metadata?.title ?? config.defaults?.title ?? config.title?.default ?? config.identity?.name ?? "";
|
|
26
|
+
const title = pageTitle ?? fallback;
|
|
27
|
+
if (!title) return "";
|
|
28
|
+
if (!pageTitle || !config.title?.template) return title;
|
|
29
|
+
return config.title.template.replace("%s", pageTitle);
|
|
30
|
+
};
|
|
31
|
+
const resolveDescription = (pageDescription, config) => pageDescription ?? config.metadata?.description ?? config.defaults?.description ?? config.identity?.description;
|
|
32
|
+
const resolveSocialImage = (image, config) => {
|
|
33
|
+
if (image.width !== void 0 && (!Number.isInteger(image.width) || image.width <= 0)) throw new Error("SEO social image width must be a positive integer.");
|
|
34
|
+
if (image.height !== void 0 && (!Number.isInteger(image.height) || image.height <= 0)) throw new Error("SEO social image height must be a positive integer.");
|
|
35
|
+
return {
|
|
36
|
+
...image,
|
|
37
|
+
url: resolveSafeUrl(image.url, config)
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const resolveAlternates = (alternates, config) => {
|
|
41
|
+
if (!alternates?.length) return;
|
|
42
|
+
const seenLocales = /* @__PURE__ */ new Set();
|
|
43
|
+
return alternates.map((alternate) => {
|
|
44
|
+
const locale = alternate.locale === "x-default" ? "x-default" : normalizeLocale(alternate.locale);
|
|
45
|
+
if (locale !== "x-default" && !isSupportedLocale(locale, config)) throw new Error(`Unsupported hreflang locale "${locale}". Supported locales: ${config.supportedLocales.join(", ")}.`);
|
|
46
|
+
if (seenLocales.has(locale)) throw new Error(`Duplicate hreflang locale "${locale}".`);
|
|
47
|
+
seenLocales.add(locale);
|
|
48
|
+
return {
|
|
49
|
+
locale,
|
|
50
|
+
url: resolveSafeUrl(alternate.url, config)
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const resolveSeoMetadata = (metadata = {}, config) => {
|
|
55
|
+
const locale = resolvePageLocale(metadata.locale, config);
|
|
56
|
+
const title = resolveTitle(metadata.title, config);
|
|
57
|
+
const description = resolveDescription(metadata.description, config);
|
|
58
|
+
const canonicalValue = metadata.canonical ?? config.metadata?.canonical ?? config.defaults?.canonical;
|
|
59
|
+
const canonical = canonicalValue ? resolveSafeUrl(canonicalValue, config) : void 0;
|
|
60
|
+
const alternates = resolveAlternates(metadata.alternates?.alternates, config);
|
|
61
|
+
const openGraphImages = metadata.openGraph?.images?.map((image) => resolveSocialImage(image, config));
|
|
62
|
+
const openGraph = {
|
|
63
|
+
...metadata.openGraph,
|
|
64
|
+
title: metadata.openGraph?.title ?? title,
|
|
65
|
+
description: metadata.openGraph?.description ?? description,
|
|
66
|
+
type: metadata.openGraph?.type ?? "website",
|
|
67
|
+
url: metadata.openGraph?.url ? resolveSafeUrl(metadata.openGraph.url, config) : canonical ?? resolveSafeUrl(config.siteUrl, config),
|
|
68
|
+
siteName: metadata.openGraph?.siteName ?? config.identity?.name,
|
|
69
|
+
locale: metadata.openGraph?.locale ?? locale.replace("-", "_"),
|
|
70
|
+
images: openGraphImages
|
|
71
|
+
};
|
|
72
|
+
const twitterImages = metadata.twitter?.images ? metadata.twitter.images.map((image) => resolveSafeUrl(image, config)) : openGraphImages?.map((image) => image.url);
|
|
73
|
+
const twitter = {
|
|
74
|
+
...metadata.twitter,
|
|
75
|
+
card: metadata.twitter?.card ?? "summary_large_image",
|
|
76
|
+
title: metadata.twitter?.title ?? title,
|
|
77
|
+
description: metadata.twitter?.description ?? description,
|
|
78
|
+
images: twitterImages,
|
|
79
|
+
imageAlt: metadata.twitter?.imageAlt ?? openGraphImages?.[0]?.alt
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
...metadata,
|
|
83
|
+
title,
|
|
84
|
+
description,
|
|
85
|
+
canonical,
|
|
86
|
+
locale,
|
|
87
|
+
alternates: alternates ? { alternates } : void 0,
|
|
88
|
+
openGraph,
|
|
89
|
+
twitter
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
const validatePreviewValue = (name, value) => {
|
|
93
|
+
if (!Number.isInteger(value) || value < -1) throw new Error(`SEO robots ${name} must be an integer greater than or equal to -1.`);
|
|
94
|
+
};
|
|
95
|
+
const resolveRobotsDirectives = (robots) => {
|
|
96
|
+
const directives = [robots.index === false ? "noindex" : "index", robots.follow === false ? "nofollow" : "follow"];
|
|
97
|
+
if (robots.noarchive) directives.push("noarchive");
|
|
98
|
+
if (robots.nosnippet) directives.push("nosnippet");
|
|
99
|
+
if (robots.maxSnippet !== void 0) {
|
|
100
|
+
validatePreviewValue("maxSnippet", robots.maxSnippet);
|
|
101
|
+
directives.push(`max-snippet:${robots.maxSnippet}`);
|
|
102
|
+
}
|
|
103
|
+
if (robots.maxImagePreview !== void 0 && ![
|
|
104
|
+
"none",
|
|
105
|
+
"standard",
|
|
106
|
+
"large"
|
|
107
|
+
].includes(robots.maxImagePreview)) throw new Error(`Invalid SEO robots maxImagePreview: "${robots.maxImagePreview}".`);
|
|
108
|
+
if (robots.maxImagePreview) directives.push(`max-image-preview:${robots.maxImagePreview}`);
|
|
109
|
+
if (robots.maxVideoPreview !== void 0) {
|
|
110
|
+
validatePreviewValue("maxVideoPreview", robots.maxVideoPreview);
|
|
111
|
+
directives.push(`max-video-preview:${robots.maxVideoPreview}`);
|
|
112
|
+
}
|
|
113
|
+
return directives.join(", ");
|
|
114
|
+
};
|
|
115
|
+
const resolveSeoRobots = (metadata, config) => {
|
|
116
|
+
if (metadata.robots) return resolveRobotsDirectives(metadata.robots);
|
|
117
|
+
return config.metadata?.robots ?? config.defaults?.robots;
|
|
118
|
+
};
|
|
119
|
+
export { resolveSeoMetadata, resolveSeoRobots };
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","names":[],"sources":["../../src/metadata/resolve.ts"],"sourcesContent":["import type { SeoLocale, SeoUrl } from \"../core/types\";\n\nimport { resolveSeoUrl } from \"../context/config\";\n\nimport type { SeoResolvedConfig } from \"../context/types\";\n\nimport type {\n ResolvedSeoMetadata,\n SeoAlternateLanguage,\n SeoPageMetadata,\n SeoRobotsMetadata,\n SeoSocialImage,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Locale\n// ---------------------------------------------------------------------------\n\nconst normalizeLocale = (locale: string): SeoLocale => {\n try {\n const normalized = Intl.getCanonicalLocales(locale)[0];\n\n if (!normalized) {\n throw new Error();\n }\n\n return normalized as SeoLocale;\n } catch {\n throw new Error(`Invalid SEO page locale: \"${locale}\".`);\n }\n};\n\n// A regional variant (nl-NL) is supported when its base language (nl) is.\nconst isSupportedLocale = (\n locale: SeoLocale,\n config: SeoResolvedConfig,\n): boolean =>\n config.supportedLocales.includes(locale) ||\n config.supportedLocales.includes(locale.split(\"-\")[0] as SeoLocale);\n\nconst resolvePageLocale = (\n locale: string | undefined,\n config: SeoResolvedConfig,\n): SeoLocale => {\n const resolved = normalizeLocale(locale ?? config.defaultLocale);\n\n if (!isSupportedLocale(resolved, config)) {\n throw new Error(\n `Unsupported SEO page locale \"${resolved}\". ` +\n `Supported locales: ${config.supportedLocales.join(\", \")}.`,\n );\n }\n\n return resolved;\n};\n\n// ---------------------------------------------------------------------------\n// URL\n// ---------------------------------------------------------------------------\n\nconst resolveSafeUrl = (value: string, config: SeoResolvedConfig): SeoUrl => {\n const resolved = resolveSeoUrl(value, config);\n\n const url = new URL(resolved);\n\n if (url.username || url.password) {\n throw new Error(`SEO URLs must not contain credentials: \"${value}\".`);\n }\n\n return resolved;\n};\n\n// ---------------------------------------------------------------------------\n// Title\n// ---------------------------------------------------------------------------\n\nconst resolveTitle = (\n pageTitle: string | undefined,\n config: SeoResolvedConfig,\n): string => {\n const fallback =\n config.metadata?.title ??\n config.defaults?.title ??\n config.title?.default ??\n config.identity?.name ??\n \"\";\n\n const title = pageTitle ?? fallback;\n\n if (!title) {\n return \"\";\n }\n\n if (!pageTitle || !config.title?.template) {\n return title;\n }\n\n return config.title.template.replace(\"%s\", pageTitle);\n};\n\n// ---------------------------------------------------------------------------\n// Description\n// ---------------------------------------------------------------------------\n\nconst resolveDescription = (\n pageDescription: string | undefined,\n config: SeoResolvedConfig,\n): string | undefined =>\n pageDescription ??\n config.metadata?.description ??\n config.defaults?.description ??\n config.identity?.description;\n\n// ---------------------------------------------------------------------------\n// Social images\n// ---------------------------------------------------------------------------\n\nconst resolveSocialImage = (\n image: SeoSocialImage,\n config: SeoResolvedConfig,\n): SeoSocialImage => {\n if (\n image.width !== undefined &&\n (!Number.isInteger(image.width) || image.width <= 0)\n ) {\n throw new Error(\"SEO social image width must be a positive integer.\");\n }\n\n if (\n image.height !== undefined &&\n (!Number.isInteger(image.height) || image.height <= 0)\n ) {\n throw new Error(\"SEO social image height must be a positive integer.\");\n }\n\n return {\n ...image,\n url: resolveSafeUrl(image.url, config),\n };\n};\n\n// ---------------------------------------------------------------------------\n// Hreflang\n// ---------------------------------------------------------------------------\n\nconst resolveAlternates = (\n alternates: SeoAlternateLanguage[] | undefined,\n config: SeoResolvedConfig,\n): SeoAlternateLanguage[] | undefined => {\n if (!alternates?.length) {\n return undefined;\n }\n\n const seenLocales = new Set<string>();\n\n return alternates.map((alternate) => {\n const locale =\n alternate.locale === \"x-default\"\n ? \"x-default\"\n : normalizeLocale(alternate.locale);\n\n if (\n locale !== \"x-default\" &&\n !isSupportedLocale(locale as SeoLocale, config)\n ) {\n throw new Error(\n `Unsupported hreflang locale \"${locale}\". ` +\n `Supported locales: ${config.supportedLocales.join(\", \")}.`,\n );\n }\n\n if (seenLocales.has(locale)) {\n throw new Error(`Duplicate hreflang locale \"${locale}\".`);\n }\n\n seenLocales.add(locale);\n\n return {\n locale,\n url: resolveSafeUrl(alternate.url, config),\n };\n });\n};\n\n// ---------------------------------------------------------------------------\n// Metadata\n// ---------------------------------------------------------------------------\n\nexport const resolveSeoMetadata = (\n metadata: SeoPageMetadata = {},\n config: SeoResolvedConfig,\n): ResolvedSeoMetadata => {\n const locale = resolvePageLocale(metadata.locale, config);\n\n const title = resolveTitle(metadata.title, config);\n\n const description = resolveDescription(metadata.description, config);\n\n const canonicalValue =\n metadata.canonical ??\n config.metadata?.canonical ??\n config.defaults?.canonical;\n\n const canonical = canonicalValue\n ? resolveSafeUrl(canonicalValue, config)\n : undefined;\n\n const alternates = resolveAlternates(metadata.alternates?.alternates, config);\n\n const openGraphImages = metadata.openGraph?.images?.map((image) =>\n resolveSocialImage(image, config),\n );\n\n const openGraph = {\n ...metadata.openGraph,\n\n title: metadata.openGraph?.title ?? title,\n\n description: metadata.openGraph?.description ?? description,\n\n type: metadata.openGraph?.type ?? \"website\",\n\n // og:url is required by Open Graph; fall back to the site root when no page URL is known.\n url: metadata.openGraph?.url\n ? resolveSafeUrl(metadata.openGraph.url, config)\n : (canonical ?? resolveSafeUrl(config.siteUrl, config)),\n\n siteName: metadata.openGraph?.siteName ?? config.identity?.name,\n\n locale: metadata.openGraph?.locale ?? locale.replace(\"-\", \"_\"),\n\n images: openGraphImages,\n };\n\n const twitterImages = metadata.twitter?.images\n ? metadata.twitter.images.map((image) => resolveSafeUrl(image, config))\n : openGraphImages?.map((image) => image.url);\n\n const twitter = {\n ...metadata.twitter,\n\n card: metadata.twitter?.card ?? (\"summary_large_image\" as const),\n\n title: metadata.twitter?.title ?? title,\n\n description: metadata.twitter?.description ?? description,\n\n images: twitterImages,\n\n imageAlt: metadata.twitter?.imageAlt ?? openGraphImages?.[0]?.alt,\n };\n\n return {\n ...metadata,\n\n title,\n description,\n canonical,\n locale,\n\n alternates: alternates ? { alternates } : undefined,\n\n openGraph,\n twitter,\n };\n};\n\n// ---------------------------------------------------------------------------\n// Robots\n// ---------------------------------------------------------------------------\n\nconst validatePreviewValue = (name: string, value: number): void => {\n if (!Number.isInteger(value) || value < -1) {\n throw new Error(\n `SEO robots ${name} must be an integer greater than or equal to -1.`,\n );\n }\n};\n\nconst resolveRobotsDirectives = (robots: SeoRobotsMetadata): string => {\n const directives = [\n robots.index === false ? \"noindex\" : \"index\",\n robots.follow === false ? \"nofollow\" : \"follow\",\n ];\n\n if (robots.noarchive) {\n directives.push(\"noarchive\");\n }\n\n if (robots.nosnippet) {\n directives.push(\"nosnippet\");\n }\n\n if (robots.maxSnippet !== undefined) {\n validatePreviewValue(\"maxSnippet\", robots.maxSnippet);\n\n directives.push(`max-snippet:${robots.maxSnippet}`);\n }\n\n if (\n robots.maxImagePreview !== undefined &&\n ![\"none\", \"standard\", \"large\"].includes(robots.maxImagePreview)\n ) {\n throw new Error(\n `Invalid SEO robots maxImagePreview: \"${robots.maxImagePreview}\".`,\n );\n }\n\n if (robots.maxImagePreview) {\n directives.push(`max-image-preview:${robots.maxImagePreview}`);\n }\n\n if (robots.maxVideoPreview !== undefined) {\n validatePreviewValue(\"maxVideoPreview\", robots.maxVideoPreview);\n\n directives.push(`max-video-preview:${robots.maxVideoPreview}`);\n }\n\n return directives.join(\", \");\n};\n\nexport const resolveSeoRobots = (\n metadata: SeoPageMetadata,\n config: SeoResolvedConfig,\n): string | undefined => {\n if (metadata.robots) {\n return resolveRobotsDirectives(metadata.robots);\n }\n\n return config.metadata?.robots ?? config.defaults?.robots;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { SeoLocale, SeoUrl } from "../core/types.cjs";
|
|
2
|
+
export interface SeoTitleMetadata {
|
|
3
|
+
title?: string;
|
|
4
|
+
template?: string;
|
|
5
|
+
default?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SeoDescriptionMetadata {
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SeoCanonicalMetadata {
|
|
11
|
+
canonical?: SeoUrl | string;
|
|
12
|
+
}
|
|
13
|
+
export interface SeoRobotsMetadata {
|
|
14
|
+
index?: boolean;
|
|
15
|
+
follow?: boolean;
|
|
16
|
+
noarchive?: boolean;
|
|
17
|
+
nosnippet?: boolean;
|
|
18
|
+
maxSnippet?: number;
|
|
19
|
+
maxImagePreview?: "none" | "standard" | "large";
|
|
20
|
+
maxVideoPreview?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SeoOpenGraphMetadata {
|
|
23
|
+
title?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
type?: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
siteName?: string;
|
|
28
|
+
locale?: string;
|
|
29
|
+
images?: SeoSocialImage[];
|
|
30
|
+
}
|
|
31
|
+
export interface SeoTwitterMetadata {
|
|
32
|
+
card?: "summary" | "summary_large_image";
|
|
33
|
+
title?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
images?: string[];
|
|
36
|
+
imageAlt?: string;
|
|
37
|
+
site?: string;
|
|
38
|
+
creator?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface SeoSocialImage {
|
|
41
|
+
url: string;
|
|
42
|
+
width?: number;
|
|
43
|
+
height?: number;
|
|
44
|
+
alt?: string;
|
|
45
|
+
type?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SeoAlternateLanguage {
|
|
48
|
+
locale: SeoLocale | string;
|
|
49
|
+
url: SeoUrl | string;
|
|
50
|
+
}
|
|
51
|
+
export interface SeoHreflangMetadata {
|
|
52
|
+
alternates: SeoAlternateLanguage[];
|
|
53
|
+
}
|
|
54
|
+
export interface SeoPageMetadata {
|
|
55
|
+
title?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
canonical?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Locale of this specific page.
|
|
60
|
+
*
|
|
61
|
+
* Must be included in SeoConfig.supportedLocales.
|
|
62
|
+
* Falls back to the provider's defaultLocale.
|
|
63
|
+
*/
|
|
64
|
+
locale?: SeoLocale | string;
|
|
65
|
+
robots?: SeoRobotsMetadata;
|
|
66
|
+
openGraph?: SeoOpenGraphMetadata;
|
|
67
|
+
twitter?: SeoTwitterMetadata;
|
|
68
|
+
alternates?: SeoHreflangMetadata;
|
|
69
|
+
keywords?: string[];
|
|
70
|
+
author?: string;
|
|
71
|
+
publishedTime?: string;
|
|
72
|
+
modifiedTime?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ResolvedSeoMetadata extends SeoPageMetadata {
|
|
75
|
+
title: string;
|
|
76
|
+
canonical?: SeoUrl;
|
|
77
|
+
locale: SeoLocale;
|
|
78
|
+
description?: string;
|
|
79
|
+
alternates?: SeoHreflangMetadata;
|
|
80
|
+
openGraph: SeoOpenGraphMetadata;
|
|
81
|
+
twitter: SeoTwitterMetadata;
|
|
82
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { SeoLocale, SeoUrl } from "../core/types.js";
|
|
2
|
+
export interface SeoTitleMetadata {
|
|
3
|
+
title?: string;
|
|
4
|
+
template?: string;
|
|
5
|
+
default?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SeoDescriptionMetadata {
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SeoCanonicalMetadata {
|
|
11
|
+
canonical?: SeoUrl | string;
|
|
12
|
+
}
|
|
13
|
+
export interface SeoRobotsMetadata {
|
|
14
|
+
index?: boolean;
|
|
15
|
+
follow?: boolean;
|
|
16
|
+
noarchive?: boolean;
|
|
17
|
+
nosnippet?: boolean;
|
|
18
|
+
maxSnippet?: number;
|
|
19
|
+
maxImagePreview?: "none" | "standard" | "large";
|
|
20
|
+
maxVideoPreview?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SeoOpenGraphMetadata {
|
|
23
|
+
title?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
type?: string;
|
|
26
|
+
url?: string;
|
|
27
|
+
siteName?: string;
|
|
28
|
+
locale?: string;
|
|
29
|
+
images?: SeoSocialImage[];
|
|
30
|
+
}
|
|
31
|
+
export interface SeoTwitterMetadata {
|
|
32
|
+
card?: "summary" | "summary_large_image";
|
|
33
|
+
title?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
images?: string[];
|
|
36
|
+
imageAlt?: string;
|
|
37
|
+
site?: string;
|
|
38
|
+
creator?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface SeoSocialImage {
|
|
41
|
+
url: string;
|
|
42
|
+
width?: number;
|
|
43
|
+
height?: number;
|
|
44
|
+
alt?: string;
|
|
45
|
+
type?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SeoAlternateLanguage {
|
|
48
|
+
locale: SeoLocale | string;
|
|
49
|
+
url: SeoUrl | string;
|
|
50
|
+
}
|
|
51
|
+
export interface SeoHreflangMetadata {
|
|
52
|
+
alternates: SeoAlternateLanguage[];
|
|
53
|
+
}
|
|
54
|
+
export interface SeoPageMetadata {
|
|
55
|
+
title?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
canonical?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Locale of this specific page.
|
|
60
|
+
*
|
|
61
|
+
* Must be included in SeoConfig.supportedLocales.
|
|
62
|
+
* Falls back to the provider's defaultLocale.
|
|
63
|
+
*/
|
|
64
|
+
locale?: SeoLocale | string;
|
|
65
|
+
robots?: SeoRobotsMetadata;
|
|
66
|
+
openGraph?: SeoOpenGraphMetadata;
|
|
67
|
+
twitter?: SeoTwitterMetadata;
|
|
68
|
+
alternates?: SeoHreflangMetadata;
|
|
69
|
+
keywords?: string[];
|
|
70
|
+
author?: string;
|
|
71
|
+
publishedTime?: string;
|
|
72
|
+
modifiedTime?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ResolvedSeoMetadata extends SeoPageMetadata {
|
|
75
|
+
title: string;
|
|
76
|
+
canonical?: SeoUrl;
|
|
77
|
+
locale: SeoLocale;
|
|
78
|
+
description?: string;
|
|
79
|
+
alternates?: SeoHreflangMetadata;
|
|
80
|
+
openGraph: SeoOpenGraphMetadata;
|
|
81
|
+
twitter: SeoTwitterMetadata;
|
|
82
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_routes = require("../server/routes.cjs");
|
|
3
|
+
// src/next/index.ts
|
|
4
|
+
const createNextSeoRoutes = (options) => {
|
|
5
|
+
const handlers = require_routes.createSeoRouteHandlers(options);
|
|
6
|
+
return {
|
|
7
|
+
sitemapGET: handlers.sitemap,
|
|
8
|
+
robotsGET: handlers.robots
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
exports.createNextSeoRoutes = createNextSeoRoutes;
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createSeoRouteHandlers"],"sources":["../../src/next/index.ts"],"sourcesContent":["import { createSeoRouteHandlers } from \"../server\";\n\nimport type { SeoRouteOptions, SeoHttpHandler } from \"../server\";\n\nexport interface NextSeoRoutes {\n /**\n * Mount on a dynamic root route, e.g. app/[seoFile]/route.ts.\n *\n * Handles the sitemap index and every generated sitemap file.\n */\n sitemapGET: SeoHttpHandler;\n\n /**\n * Mount on the static robots route, e.g. app/robots.txt/route.ts.\n */\n robotsGET: SeoHttpHandler;\n}\n\nexport const createNextSeoRoutes = (\n options: SeoRouteOptions,\n): NextSeoRoutes => {\n const handlers = createSeoRouteHandlers(options);\n\n return {\n sitemapGET: handlers.sitemap,\n robotsGET: handlers.robots,\n };\n};\n"],"mappings":";;;AAkBA,MAAa,uBACX,YACkB;CAClB,MAAM,WAAWA,eAAAA,uBAAuB,OAAO;CAE/C,OAAO;EACL,YAAY,SAAS;EACrB,WAAW,SAAS;CACtB;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SeoHttpHandler, SeoRouteOptions } from "../server/types.cjs";
|
|
2
|
+
import "../server/index.cjs";
|
|
3
|
+
export interface NextSeoRoutes {
|
|
4
|
+
/**
|
|
5
|
+
* Mount on a dynamic root route, e.g. app/[seoFile]/route.ts.
|
|
6
|
+
*
|
|
7
|
+
* Handles the sitemap index and every generated sitemap file.
|
|
8
|
+
*/
|
|
9
|
+
sitemapGET: SeoHttpHandler;
|
|
10
|
+
/**
|
|
11
|
+
* Mount on the static robots route, e.g. app/robots.txt/route.ts.
|
|
12
|
+
*/
|
|
13
|
+
robotsGET: SeoHttpHandler;
|
|
14
|
+
}
|
|
15
|
+
export declare const createNextSeoRoutes: (options: SeoRouteOptions) => NextSeoRoutes;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SeoHttpHandler, SeoRouteOptions } from "../server/types.js";
|
|
2
|
+
export interface NextSeoRoutes {
|
|
3
|
+
/**
|
|
4
|
+
* Mount on a dynamic root route, e.g. app/[seoFile]/route.ts.
|
|
5
|
+
*
|
|
6
|
+
* Handles the sitemap index and every generated sitemap file.
|
|
7
|
+
*/
|
|
8
|
+
sitemapGET: SeoHttpHandler;
|
|
9
|
+
/**
|
|
10
|
+
* Mount on the static robots route, e.g. app/robots.txt/route.ts.
|
|
11
|
+
*/
|
|
12
|
+
robotsGET: SeoHttpHandler;
|
|
13
|
+
}
|
|
14
|
+
export declare const createNextSeoRoutes: (options: SeoRouteOptions) => NextSeoRoutes;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createSeoRouteHandlers } from "../server/routes.js";
|
|
2
|
+
// src/next/index.ts
|
|
3
|
+
const createNextSeoRoutes = (options) => {
|
|
4
|
+
const handlers = createSeoRouteHandlers(options);
|
|
5
|
+
return {
|
|
6
|
+
sitemapGET: handlers.sitemap,
|
|
7
|
+
robotsGET: handlers.robots
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export { createNextSeoRoutes };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/next/index.ts"],"sourcesContent":["import { createSeoRouteHandlers } from \"../server\";\n\nimport type { SeoRouteOptions, SeoHttpHandler } from \"../server\";\n\nexport interface NextSeoRoutes {\n /**\n * Mount on a dynamic root route, e.g. app/[seoFile]/route.ts.\n *\n * Handles the sitemap index and every generated sitemap file.\n */\n sitemapGET: SeoHttpHandler;\n\n /**\n * Mount on the static robots route, e.g. app/robots.txt/route.ts.\n */\n robotsGET: SeoHttpHandler;\n}\n\nexport const createNextSeoRoutes = (\n options: SeoRouteOptions,\n): NextSeoRoutes => {\n const handlers = createSeoRouteHandlers(options);\n\n return {\n sitemapGET: handlers.sitemap,\n robotsGET: handlers.robots,\n };\n};\n"],"mappings":";;AAkBA,MAAa,uBACX,YACkB;CAClB,MAAM,WAAW,uBAAuB,OAAO;CAE/C,OAAO;EACL,YAAY,SAAS;EACrB,WAAW,SAAS;CACtB;AACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const require_resolve = require("../metadata/resolve.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
// src/react/context.tsx
|
|
5
|
+
const SeoReactContext = (0, react.createContext)(null);
|
|
6
|
+
const useSeoReactContext = () => {
|
|
7
|
+
const context = (0, react.useContext)(SeoReactContext);
|
|
8
|
+
if (!context) throw new Error("Puzzle SEO hooks must be used inside SeoProvider.");
|
|
9
|
+
return context;
|
|
10
|
+
};
|
|
11
|
+
const useSeoConfig = () => {
|
|
12
|
+
return useSeoReactContext().config;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Returns the metadata from the closest SEO context.
|
|
16
|
+
*
|
|
17
|
+
* Passing metadata explicitly resolves it against the current config.
|
|
18
|
+
* This is useful when a route component owns its metadata object.
|
|
19
|
+
*/
|
|
20
|
+
const useSeoMetadata = (metadata) => {
|
|
21
|
+
const context = useSeoReactContext();
|
|
22
|
+
if (metadata) return require_resolve.resolveSeoMetadata(metadata, context.config);
|
|
23
|
+
return context.metadata;
|
|
24
|
+
};
|
|
25
|
+
exports.SeoReactContext = SeoReactContext;
|
|
26
|
+
exports.useSeoConfig = useSeoConfig;
|
|
27
|
+
exports.useSeoMetadata = useSeoMetadata;
|
|
28
|
+
exports.useSeoReactContext = useSeoReactContext;
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=context.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.cjs","names":["createContext","useContext","resolveSeoMetadata"],"sources":["../../src/react/context.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\n\nimport type { SeoResolvedConfig } from \"../context/types\";\n\nimport { resolveSeoMetadata } from \"../metadata\";\n\nimport type { ResolvedSeoMetadata, SeoPageMetadata } from \"../metadata/types\";\n\nexport interface SeoReactContextValue {\n config: SeoResolvedConfig;\n metadata?: ResolvedSeoMetadata;\n}\n\nexport const SeoReactContext = createContext<SeoReactContextValue | null>(null);\n\nexport const useSeoReactContext = (): SeoReactContextValue => {\n const context = useContext(SeoReactContext);\n\n if (!context) {\n throw new Error(\"Puzzle SEO hooks must be used inside SeoProvider.\");\n }\n\n return context;\n};\n\nexport const useSeoConfig = (): SeoResolvedConfig => {\n return useSeoReactContext().config;\n};\n\n/**\n * Returns the metadata from the closest SEO context.\n *\n * Passing metadata explicitly resolves it against the current config.\n * This is useful when a route component owns its metadata object.\n */\nexport const useSeoMetadata = (\n metadata?: SeoPageMetadata,\n): ResolvedSeoMetadata | undefined => {\n const context = useSeoReactContext();\n\n if (metadata) {\n return resolveSeoMetadata(metadata, context.config);\n }\n\n return context.metadata;\n};\n"],"mappings":";;;;AAeA,MAAa,mBAAA,GAAkBA,MAAAA,cAAAA,CAA2C,IAAI;AAE9E,MAAa,2BAAiD;CAC5D,MAAM,WAAA,GAAUC,MAAAA,WAAAA,CAAW,eAAe;CAE1C,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,mDAAmD;CAGrE,OAAO;AACT;AAEA,MAAa,qBAAwC;CACnD,OAAO,mBAAmB,CAAC,CAAC;AAC9B;;;;;;;AAQA,MAAa,kBACX,aACoC;CACpC,MAAM,UAAU,mBAAmB;CAEnC,IAAI,UACF,OAAOC,gBAAAA,mBAAmB,UAAU,QAAQ,MAAM;CAGpD,OAAO,QAAQ;AACjB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SeoResolvedConfig } from "../context/types.cjs";
|
|
2
|
+
import { ResolvedSeoMetadata, SeoPageMetadata } from "../metadata/types.cjs";
|
|
3
|
+
export interface SeoReactContextValue {
|
|
4
|
+
config: SeoResolvedConfig;
|
|
5
|
+
metadata?: ResolvedSeoMetadata;
|
|
6
|
+
}
|
|
7
|
+
export declare const useSeoConfig: () => SeoResolvedConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the metadata from the closest SEO context.
|
|
10
|
+
*
|
|
11
|
+
* Passing metadata explicitly resolves it against the current config.
|
|
12
|
+
* This is useful when a route component owns its metadata object.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useSeoMetadata: (metadata?: SeoPageMetadata) => ResolvedSeoMetadata | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SeoResolvedConfig } from "../context/types.js";
|
|
2
|
+
import { ResolvedSeoMetadata, SeoPageMetadata } from "../metadata/types.js";
|
|
3
|
+
export interface SeoReactContextValue {
|
|
4
|
+
config: SeoResolvedConfig;
|
|
5
|
+
metadata?: ResolvedSeoMetadata;
|
|
6
|
+
}
|
|
7
|
+
export declare const useSeoConfig: () => SeoResolvedConfig;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the metadata from the closest SEO context.
|
|
10
|
+
*
|
|
11
|
+
* Passing metadata explicitly resolves it against the current config.
|
|
12
|
+
* This is useful when a route component owns its metadata object.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useSeoMetadata: (metadata?: SeoPageMetadata) => ResolvedSeoMetadata | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { resolveSeoMetadata } from "../metadata/resolve.js";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
// src/react/context.tsx
|
|
5
|
+
const SeoReactContext = createContext(null);
|
|
6
|
+
const useSeoReactContext = () => {
|
|
7
|
+
const context = useContext(SeoReactContext);
|
|
8
|
+
if (!context) throw new Error("Puzzle SEO hooks must be used inside SeoProvider.");
|
|
9
|
+
return context;
|
|
10
|
+
};
|
|
11
|
+
const useSeoConfig = () => {
|
|
12
|
+
return useSeoReactContext().config;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Returns the metadata from the closest SEO context.
|
|
16
|
+
*
|
|
17
|
+
* Passing metadata explicitly resolves it against the current config.
|
|
18
|
+
* This is useful when a route component owns its metadata object.
|
|
19
|
+
*/
|
|
20
|
+
const useSeoMetadata = (metadata) => {
|
|
21
|
+
const context = useSeoReactContext();
|
|
22
|
+
if (metadata) return resolveSeoMetadata(metadata, context.config);
|
|
23
|
+
return context.metadata;
|
|
24
|
+
};
|
|
25
|
+
export { SeoReactContext, useSeoConfig, useSeoMetadata, useSeoReactContext };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../../src/react/context.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\n\nimport type { SeoResolvedConfig } from \"../context/types\";\n\nimport { resolveSeoMetadata } from \"../metadata\";\n\nimport type { ResolvedSeoMetadata, SeoPageMetadata } from \"../metadata/types\";\n\nexport interface SeoReactContextValue {\n config: SeoResolvedConfig;\n metadata?: ResolvedSeoMetadata;\n}\n\nexport const SeoReactContext = createContext<SeoReactContextValue | null>(null);\n\nexport const useSeoReactContext = (): SeoReactContextValue => {\n const context = useContext(SeoReactContext);\n\n if (!context) {\n throw new Error(\"Puzzle SEO hooks must be used inside SeoProvider.\");\n }\n\n return context;\n};\n\nexport const useSeoConfig = (): SeoResolvedConfig => {\n return useSeoReactContext().config;\n};\n\n/**\n * Returns the metadata from the closest SEO context.\n *\n * Passing metadata explicitly resolves it against the current config.\n * This is useful when a route component owns its metadata object.\n */\nexport const useSeoMetadata = (\n metadata?: SeoPageMetadata,\n): ResolvedSeoMetadata | undefined => {\n const context = useSeoReactContext();\n\n if (metadata) {\n return resolveSeoMetadata(metadata, context.config);\n }\n\n return context.metadata;\n};\n"],"mappings":";;;;AAeA,MAAa,kBAAkB,cAA2C,IAAI;AAE9E,MAAa,2BAAiD;CAC5D,MAAM,UAAU,WAAW,eAAe;CAE1C,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,mDAAmD;CAGrE,OAAO;AACT;AAEA,MAAa,qBAAwC;CACnD,OAAO,mBAAmB,CAAC,CAAC;AAC9B;;;;;;;AAQA,MAAa,kBACX,aACoC;CACpC,MAAM,UAAU,mBAAmB;CAEnC,IAAI,UACF,OAAO,mBAAmB,UAAU,QAAQ,MAAM;CAGpD,OAAO,QAAQ;AACjB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_provider = require("./provider.cjs");
|
|
3
|
+
const require_metadata = require("./metadata.cjs");
|
|
4
|
+
const require_context = require("./context.cjs");
|
|
5
|
+
const require_schema_components = require("./schema-components.cjs");
|
|
6
|
+
exports.LDAggregateRating = require_schema_components.LDAggregateRating;
|
|
7
|
+
exports.LDArticle = require_schema_components.LDArticle;
|
|
8
|
+
exports.LDBlogPosting = require_schema_components.LDBlogPosting;
|
|
9
|
+
exports.LDBreadcrumbList = require_schema_components.LDBreadcrumbList;
|
|
10
|
+
exports.LDBreadcrumbs = require_schema_components.LDBreadcrumbs;
|
|
11
|
+
exports.LDCollectionPage = require_schema_components.LDCollectionPage;
|
|
12
|
+
exports.LDContactPoint = require_schema_components.LDContactPoint;
|
|
13
|
+
exports.LDCourse = require_schema_components.LDCourse;
|
|
14
|
+
exports.LDEvent = require_schema_components.LDEvent;
|
|
15
|
+
exports.LDFAQPage = require_schema_components.LDFAQPage;
|
|
16
|
+
exports.LDFaqPage = require_schema_components.LDFaqPage;
|
|
17
|
+
exports.LDHowTo = require_schema_components.LDHowTo;
|
|
18
|
+
exports.LDImageObject = require_schema_components.LDImageObject;
|
|
19
|
+
exports.LDItemList = require_schema_components.LDItemList;
|
|
20
|
+
exports.LDJobPosting = require_schema_components.LDJobPosting;
|
|
21
|
+
exports.LDLocalBusiness = require_schema_components.LDLocalBusiness;
|
|
22
|
+
exports.LDOffer = require_schema_components.LDOffer;
|
|
23
|
+
exports.LDOrganization = require_schema_components.LDOrganization;
|
|
24
|
+
exports.LDPerson = require_schema_components.LDPerson;
|
|
25
|
+
exports.LDPlace = require_schema_components.LDPlace;
|
|
26
|
+
exports.LDPostalAddress = require_schema_components.LDPostalAddress;
|
|
27
|
+
exports.LDProduct = require_schema_components.LDProduct;
|
|
28
|
+
exports.LDProfilePage = require_schema_components.LDProfilePage;
|
|
29
|
+
exports.LDRecipe = require_schema_components.LDRecipe;
|
|
30
|
+
exports.LDReview = require_schema_components.LDReview;
|
|
31
|
+
exports.LDSearchAction = require_schema_components.LDSearchAction;
|
|
32
|
+
exports.LDService = require_schema_components.LDService;
|
|
33
|
+
exports.LDSoftwareApplication = require_schema_components.LDSoftwareApplication;
|
|
34
|
+
exports.LDVideoObject = require_schema_components.LDVideoObject;
|
|
35
|
+
exports.LDWebPage = require_schema_components.LDWebPage;
|
|
36
|
+
exports.LDWebSite = require_schema_components.LDWebSite;
|
|
37
|
+
exports.LDWebsite = require_schema_components.LDWebsite;
|
|
38
|
+
exports.Schema = require_schema_components.Schema;
|
|
39
|
+
exports.SchemaGraph = require_schema_components.SchemaGraph;
|
|
40
|
+
exports.SchemaReference = require_schema_components.SchemaReference;
|
|
41
|
+
exports.SchemaScript = require_schema_components.SchemaScript;
|
|
42
|
+
exports.SeoHead = require_metadata.SeoHead;
|
|
43
|
+
exports.SeoJsonLd = require_schema_components.SeoJsonLd;
|
|
44
|
+
exports.SeoMetadata = require_metadata.SeoMetadata;
|
|
45
|
+
exports.SeoProvider = require_provider.SeoProvider;
|
|
46
|
+
exports.useSeo = require_schema_components.useSeo;
|
|
47
|
+
exports.useSeoConfig = require_context.useSeoConfig;
|
|
48
|
+
exports.useSeoMetadata = require_context.useSeoMetadata;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SchemaComponentProps, SchemaComponentType, SchemaGraphProps, SchemaProperties, SchemaProps, SchemaReferenceProps, SchemaValue, SeoContextValue, SeoHeadProps, SeoJsonLdProps, SeoMetadataProps, SeoProviderProps, SeoSchemaValidationError } from "./types.cjs";
|
|
2
|
+
import { SeoProvider } from "./provider.cjs";
|
|
3
|
+
import { SeoHead, SeoMetadata } from "./metadata.cjs";
|
|
4
|
+
import { useSeoConfig, useSeoMetadata } from "./context.cjs";
|
|
5
|
+
import { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, SchemaGraph, SchemaReference, SchemaScript, SeoJsonLd, useSeo } from "./schema-components.cjs";
|
|
6
|
+
export { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, type SchemaComponentProps, type SchemaComponentType, SchemaGraph, type SchemaGraphProps, type SchemaProperties, type SchemaProps, SchemaReference, type SchemaReferenceProps, SchemaScript, type SchemaValue, type SeoContextValue, SeoHead, type SeoHeadProps, SeoJsonLd, type SeoJsonLdProps, SeoMetadata, type SeoMetadataProps, SeoProvider, type SeoProviderProps, type SeoSchemaValidationError, useSeo, useSeoConfig, useSeoMetadata };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SchemaComponentProps, SchemaComponentType, SchemaGraphProps, SchemaProperties, SchemaProps, SchemaReferenceProps, SchemaValue, SeoContextValue, SeoHeadProps, SeoJsonLdProps, SeoMetadataProps, SeoProviderProps, SeoSchemaValidationError } from "./types.js";
|
|
2
|
+
import { SeoProvider } from "./provider.js";
|
|
3
|
+
import { SeoHead, SeoMetadata } from "./metadata.js";
|
|
4
|
+
import { useSeoConfig, useSeoMetadata } from "./context.js";
|
|
5
|
+
import { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, SchemaGraph, SchemaReference, SchemaScript, SeoJsonLd, useSeo } from "./schema-components.js";
|
|
6
|
+
export { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, type SchemaComponentProps, type SchemaComponentType, SchemaGraph, type SchemaGraphProps, type SchemaProperties, type SchemaProps, SchemaReference, type SchemaReferenceProps, SchemaScript, type SchemaValue, type SeoContextValue, SeoHead, type SeoHeadProps, SeoJsonLd, type SeoJsonLdProps, SeoMetadata, type SeoMetadataProps, SeoProvider, type SeoProviderProps, type SeoSchemaValidationError, useSeo, useSeoConfig, useSeoMetadata };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SeoProvider } from "./provider.js";
|
|
2
|
+
import { SeoHead, SeoMetadata } from "./metadata.js";
|
|
3
|
+
import { useSeoConfig, useSeoMetadata } from "./context.js";
|
|
4
|
+
import { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, SchemaGraph, SchemaReference, SchemaScript, SeoJsonLd, useSeo } from "./schema-components.js";
|
|
5
|
+
export { LDAggregateRating, LDArticle, LDBlogPosting, LDBreadcrumbList, LDBreadcrumbs, LDCollectionPage, LDContactPoint, LDCourse, LDEvent, LDFAQPage, LDFaqPage, LDHowTo, LDImageObject, LDItemList, LDJobPosting, LDLocalBusiness, LDOffer, LDOrganization, LDPerson, LDPlace, LDPostalAddress, LDProduct, LDProfilePage, LDRecipe, LDReview, LDSearchAction, LDService, LDSoftwareApplication, LDVideoObject, LDWebPage, LDWebSite, LDWebsite, Schema, SchemaGraph, SchemaReference, SchemaScript, SeoHead, SeoJsonLd, SeoMetadata, SeoProvider, useSeo, useSeoConfig, useSeoMetadata };
|