wpheadless-lib 1.0.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.
Files changed (111) hide show
  1. package/dist/api/categories.d.ts +29 -0
  2. package/dist/api/categories.js +91 -0
  3. package/dist/api/index.d.ts +6 -0
  4. package/dist/api/index.js +6 -0
  5. package/dist/api/mappers.d.ts +5 -0
  6. package/dist/api/mappers.js +30 -0
  7. package/dist/api/posts.d.ts +44 -0
  8. package/dist/api/posts.js +104 -0
  9. package/dist/api/translations.d.ts +16 -0
  10. package/dist/api/translations.js +28 -0
  11. package/dist/api/types.d.ts +30 -0
  12. package/dist/api/types.js +1 -0
  13. package/dist/api/wpClient.d.ts +31 -0
  14. package/dist/api/wpClient.js +23 -0
  15. package/dist/app/config.d.ts +5 -0
  16. package/dist/app/config.js +28 -0
  17. package/dist/app/globals.css +326 -0
  18. package/dist/base/category/index.d.ts +38 -0
  19. package/dist/base/category/index.js +104 -0
  20. package/dist/base/category/pagination.d.ts +44 -0
  21. package/dist/base/category/pagination.js +114 -0
  22. package/dist/base/home/index.d.ts +13 -0
  23. package/dist/base/home/index.js +20 -0
  24. package/dist/base/home/pagination.d.ts +36 -0
  25. package/dist/base/home/pagination.js +83 -0
  26. package/dist/base/index.d.ts +7 -0
  27. package/dist/base/index.js +7 -0
  28. package/dist/base/layout/RootLayoutBase.d.ts +22 -0
  29. package/dist/base/layout/RootLayoutBase.js +15 -0
  30. package/dist/base/legal/index.d.ts +31 -0
  31. package/dist/base/legal/index.js +167 -0
  32. package/dist/base/post/index.d.ts +31 -0
  33. package/dist/base/post/index.js +97 -0
  34. package/dist/components/Breadcrumbs/index.d.ts +9 -0
  35. package/dist/components/Breadcrumbs/index.js +11 -0
  36. package/dist/components/Breadcrumbs/index.module.css +39 -0
  37. package/dist/components/Paginator/index.d.ts +10 -0
  38. package/dist/components/Paginator/index.js +16 -0
  39. package/dist/components/Paginator/index.module.css +13 -0
  40. package/dist/components/Paginator/index.utils.d.ts +7 -0
  41. package/dist/components/Paginator/index.utils.js +19 -0
  42. package/dist/components/PostCard/index.d.ts +10 -0
  43. package/dist/components/PostCard/index.js +22 -0
  44. package/dist/components/PostCard/index.module.css +56 -0
  45. package/dist/components/PostCard/index.types.d.ts +7 -0
  46. package/dist/components/PostCard/index.types.js +1 -0
  47. package/dist/components/PostCard/index.utils.d.ts +8 -0
  48. package/dist/components/PostCard/index.utils.js +23 -0
  49. package/dist/components/index.d.ts +5 -0
  50. package/dist/components/index.js +5 -0
  51. package/dist/components/layout/Footer/index.d.ts +12 -0
  52. package/dist/components/layout/Footer/index.js +36 -0
  53. package/dist/components/layout/Footer/index.module.css +61 -0
  54. package/dist/components/layout/Header/index.d.ts +13 -0
  55. package/dist/components/layout/Header/index.js +36 -0
  56. package/dist/components/layout/Header/index.module.css +48 -0
  57. package/dist/index.d.ts +6 -0
  58. package/dist/index.js +6 -0
  59. package/dist/plugins/index.d.ts +1 -0
  60. package/dist/plugins/index.js +1 -0
  61. package/dist/plugins/yoast/index.d.ts +16 -0
  62. package/dist/plugins/yoast/index.js +44 -0
  63. package/dist/utils/hreflang.d.ts +37 -0
  64. package/dist/utils/hreflang.js +95 -0
  65. package/dist/utils/html.d.ts +4 -0
  66. package/dist/utils/html.js +8 -0
  67. package/dist/utils/i18n.d.ts +12 -0
  68. package/dist/utils/i18n.js +161 -0
  69. package/dist/utils/index.d.ts +6 -0
  70. package/dist/utils/index.js +6 -0
  71. package/dist/utils/routing.d.ts +40 -0
  72. package/dist/utils/routing.js +84 -0
  73. package/dist/utils/seo.d.ts +75 -0
  74. package/dist/utils/seo.js +97 -0
  75. package/dist/utils/sitemap.d.ts +26 -0
  76. package/dist/utils/sitemap.js +327 -0
  77. package/dist/views/CategoryListView/index.d.ts +12 -0
  78. package/dist/views/CategoryListView/index.js +17 -0
  79. package/dist/views/CategoryListView/index.module.css +39 -0
  80. package/dist/views/CategoryListView/index.utils.d.ts +5 -0
  81. package/dist/views/CategoryListView/index.utils.js +14 -0
  82. package/dist/views/CategoryPaginationView/index.d.ts +13 -0
  83. package/dist/views/CategoryPaginationView/index.js +18 -0
  84. package/dist/views/CategoryPaginationView/index.module.css +39 -0
  85. package/dist/views/CategoryPaginationView/index.utils.d.ts +5 -0
  86. package/dist/views/CategoryPaginationView/index.utils.js +4 -0
  87. package/dist/views/HomePaginationView/index.d.ts +13 -0
  88. package/dist/views/HomePaginationView/index.js +9 -0
  89. package/dist/views/HomePaginationView/index.module.css +38 -0
  90. package/dist/views/HomePaginationView/index.utils.d.ts +7 -0
  91. package/dist/views/HomePaginationView/index.utils.js +10 -0
  92. package/dist/views/HomeView/index.d.ts +13 -0
  93. package/dist/views/HomeView/index.js +21 -0
  94. package/dist/views/HomeView/index.module.css +78 -0
  95. package/dist/views/HomeView/index.utils.d.ts +23 -0
  96. package/dist/views/HomeView/index.utils.js +44 -0
  97. package/dist/views/LegalPageView/index.d.ts +8 -0
  98. package/dist/views/LegalPageView/index.js +12 -0
  99. package/dist/views/LegalPageView/index.module.css +39 -0
  100. package/dist/views/LegalPageView/index.utils.d.ts +11 -0
  101. package/dist/views/LegalPageView/index.utils.js +16 -0
  102. package/dist/views/PostView/index.d.ts +10 -0
  103. package/dist/views/PostView/index.js +26 -0
  104. package/dist/views/PostView/index.module.css +80 -0
  105. package/dist/views/PostView/index.types.d.ts +2 -0
  106. package/dist/views/PostView/index.types.js +1 -0
  107. package/dist/views/PostView/index.utils.d.ts +15 -0
  108. package/dist/views/PostView/index.utils.js +22 -0
  109. package/dist/views/index.d.ts +6 -0
  110. package/dist/views/index.js +6 -0
  111. package/package.json +45 -0
@@ -0,0 +1,56 @@
1
+ .card {
2
+ position: relative;
3
+ display: flex;
4
+ flex-direction: column;
5
+ height: 100%;
6
+ }
7
+
8
+ .badges {
9
+ display: flex;
10
+ gap: 0.5rem;
11
+ flex-wrap: wrap;
12
+ padding: var(--spacing-sm) var(--spacing-md) 0;
13
+ }
14
+
15
+ .badge {
16
+ text-decoration: none;
17
+ }
18
+
19
+ .postLink {
20
+ text-decoration: none;
21
+ color: inherit;
22
+ display: block;
23
+ height: 100%;
24
+ }
25
+
26
+ .imageWrapper {
27
+ position: relative;
28
+ height: 220px;
29
+ overflow: hidden;
30
+ }
31
+
32
+ .body {
33
+ padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md);
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--spacing-sm);
37
+ align-items: flex-start;
38
+ }
39
+
40
+ .title {
41
+ font-size: 1.25rem;
42
+ font-weight: 700;
43
+ line-height: 1.3;
44
+ color: var(--foreground);
45
+ }
46
+
47
+ .excerpt {
48
+ font-size: 0.9375rem;
49
+ color: var(--foreground-light);
50
+ line-height: 1.5;
51
+ }
52
+
53
+ .date {
54
+ font-size: 0.875rem;
55
+ color: var(--foreground-light);
56
+ }
@@ -0,0 +1,7 @@
1
+ import type { WPPost } from "wpjsapi-lib";
2
+ import { type Locale } from "../../utils";
3
+ export type BuildUrlArgs = {
4
+ post: WPPost;
5
+ categorySlug?: string;
6
+ locale?: Locale;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { WPPost } from "wpjsapi-lib";
2
+ import { type Locale } from "../../utils";
3
+ import type { BuildUrlArgs } from "./index.types";
4
+ export declare const getLangPrefix: (locale?: Locale) => string;
5
+ export declare const resolveCategorySlug: (post: WPPost, categorySlug?: string) => string | undefined;
6
+ export declare const buildPostUrl: ({ post, categorySlug, locale }: BuildUrlArgs) => string;
7
+ export declare const buildCategoryUrl: (slug: string, locale?: Locale) => string;
8
+ export declare const formatPostDate: (date: string, dateLocale?: string) => string;
@@ -0,0 +1,23 @@
1
+ import { ensureTrailingSlash } from "../../utils";
2
+ export const getLangPrefix = (locale) => locale && locale !== "en" ? `/${locale}` : "";
3
+ export const resolveCategorySlug = (post, categorySlug) => {
4
+ if (categorySlug)
5
+ return categorySlug;
6
+ const categories = (post._embedded?.["wp:term"]?.[0] || []);
7
+ return categories[0]?.slug;
8
+ };
9
+ export const buildPostUrl = ({ post, categorySlug, locale }) => {
10
+ const langPrefix = getLangPrefix(locale);
11
+ const resolvedCategory = categorySlug || resolveCategorySlug(post);
12
+ const postPath = resolvedCategory
13
+ ? `/${resolvedCategory}/${post.slug}`
14
+ : `/${post.slug}`;
15
+ const url = `${langPrefix}${postPath}`.replace(/\/+/g, "/");
16
+ return ensureTrailingSlash(url);
17
+ };
18
+ export const buildCategoryUrl = (slug, locale) => ensureTrailingSlash(`${getLangPrefix(locale)}/${slug}`.replace(/\/+/g, "/"));
19
+ export const formatPostDate = (date, dateLocale) => new Date(date).toLocaleDateString(dateLocale || "en-US", {
20
+ year: "numeric",
21
+ month: "long",
22
+ day: "numeric",
23
+ });
@@ -0,0 +1,5 @@
1
+ export { default as Header } from "./layout/Header";
2
+ export { default as Footer } from "./layout/Footer";
3
+ export { default as Paginator } from "./Paginator";
4
+ export { default as PostCard } from "./PostCard";
5
+ export { Breadcrumbs } from "./Breadcrumbs";
@@ -0,0 +1,5 @@
1
+ export { default as Header } from "./layout/Header";
2
+ export { default as Footer } from "./layout/Footer";
3
+ export { default as Paginator } from "./Paginator";
4
+ export { default as PostCard } from "./PostCard";
5
+ export { Breadcrumbs } from "./Breadcrumbs";
@@ -0,0 +1,12 @@
1
+ export declare const dynamic = "force-static";
2
+ export declare const revalidate = 3600;
3
+ type FooterProps = {
4
+ menuId: string;
5
+ wpApiUrl: string;
6
+ wpUsername: string;
7
+ wpAppPassword: string;
8
+ siteName: string;
9
+ locale?: string;
10
+ };
11
+ export default function Footer({ menuId, wpApiUrl, wpUsername, wpAppPassword, siteName, locale, }: FooterProps): Promise<import("react/jsx-runtime").JSX.Element>;
12
+ export {};
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Link from "next/link";
3
+ import { createMenuEndpoints, createAuth } from "wpjsapi-lib";
4
+ import { convertWpUrl, getTranslator } from "../../../utils";
5
+ import styles from "./index.module.css";
6
+ export const dynamic = "force-static";
7
+ export const revalidate = 3600;
8
+ export default async function Footer({ menuId, wpApiUrl, wpUsername, wpAppPassword, siteName, locale, }) {
9
+ const auth = createAuth({
10
+ method: "basic",
11
+ credentials: {
12
+ username: wpUsername,
13
+ password: wpAppPassword,
14
+ },
15
+ });
16
+ const menusApi = createMenuEndpoints({
17
+ baseUrl: wpApiUrl,
18
+ auth,
19
+ });
20
+ let menuItems = [];
21
+ const t = getTranslator(locale);
22
+ try {
23
+ const parsedMenuId = parseInt(menuId);
24
+ const itemsResponse = await menusApi.items.list({
25
+ menus: [parsedMenuId],
26
+ per_page: 100,
27
+ orderby: "menu_order",
28
+ order: "asc",
29
+ });
30
+ menuItems = itemsResponse.items;
31
+ }
32
+ catch (e) {
33
+ console.error("Error obteniendo menú footer:", e);
34
+ }
35
+ return (_jsx("footer", { className: styles.footer, children: _jsx("div", { className: "container", children: _jsxs("div", { className: styles.wrapper, children: [_jsxs("div", { className: styles.grid, children: [_jsxs("div", { children: [_jsx("h3", { className: styles.siteName, children: siteName }), _jsx("p", { className: `${styles.muted} text-muted`, children: t("footer.tagline") })] }), menuItems.length > 0 && (_jsxs("nav", { "aria-label": t("footer.navAria"), className: styles.nav, children: [_jsx("h4", { className: styles.navTitle, children: t("footer.navTitle") }), _jsx("ul", { className: styles.list, children: menuItems.map((item) => (_jsx("li", { children: _jsx(Link, { href: convertWpUrl(item.url, wpApiUrl), className: "footer-link", children: item.title.rendered }) }, item.id))) })] }))] }), _jsxs("div", { className: styles.bottomBar, children: [_jsxs("span", { children: ["\u00A9 ", new Date().getFullYear(), " ", siteName] }), _jsx("span", { children: t("footer.noCookies") })] })] }) }) }));
36
+ }
@@ -0,0 +1,61 @@
1
+ .footer {
2
+ background: var(--background-alt);
3
+ border-top: 1px solid var(--border);
4
+ margin-top: var(--spacing-xl);
5
+ }
6
+
7
+ .wrapper {
8
+ padding: var(--spacing-xl) 0 var(--spacing-lg);
9
+ }
10
+
11
+ .grid {
12
+ display: grid;
13
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
14
+ gap: var(--spacing-lg);
15
+ margin-bottom: var(--spacing-lg);
16
+ }
17
+
18
+ .siteName {
19
+ font-size: 1.25rem;
20
+ font-weight: bold;
21
+ margin-bottom: 1rem;
22
+ color: var(--foreground);
23
+ }
24
+
25
+ .muted {
26
+ font-size: 0.875rem;
27
+ }
28
+
29
+ .nav {
30
+ display: block;
31
+ }
32
+
33
+ .navTitle {
34
+ font-size: 0.875rem;
35
+ font-weight: 600;
36
+ margin-bottom: 1rem;
37
+ color: var(--foreground);
38
+ text-transform: uppercase;
39
+ letter-spacing: 0.05em;
40
+ }
41
+
42
+ .list {
43
+ list-style: none;
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 0.5rem;
47
+ margin: 0;
48
+ padding: 0;
49
+ }
50
+
51
+ .bottomBar {
52
+ display: flex;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ border-top: 1px solid var(--border);
56
+ padding-top: var(--spacing-md);
57
+ font-size: 0.875rem;
58
+ color: var(--foreground-light);
59
+ gap: var(--spacing-md);
60
+ flex-wrap: wrap;
61
+ }
@@ -0,0 +1,13 @@
1
+ export declare const dynamic = "force-static";
2
+ export declare const revalidate = 3600;
3
+ type HeaderProps = {
4
+ menuId: string;
5
+ wpApiUrl: string;
6
+ wpUsername: string;
7
+ wpAppPassword: string;
8
+ siteName: string;
9
+ homeHref?: string;
10
+ locale?: string;
11
+ };
12
+ export default function Header({ menuId, wpApiUrl, wpUsername, wpAppPassword, siteName, homeHref, locale, }: HeaderProps): Promise<import("react/jsx-runtime").JSX.Element>;
13
+ export {};
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Link from "next/link";
3
+ import { createMenuEndpoints, createAuth } from "wpjsapi-lib";
4
+ import { convertWpUrl, getTranslator } from "../../../utils";
5
+ import styles from "./index.module.css";
6
+ export const dynamic = "force-static";
7
+ export const revalidate = 3600;
8
+ export default async function Header({ menuId, wpApiUrl, wpUsername, wpAppPassword, siteName, homeHref = "/", locale, }) {
9
+ const t = getTranslator(locale);
10
+ const auth = createAuth({
11
+ method: "basic",
12
+ credentials: {
13
+ username: wpUsername,
14
+ password: wpAppPassword,
15
+ },
16
+ });
17
+ const menusApi = createMenuEndpoints({
18
+ baseUrl: wpApiUrl,
19
+ auth,
20
+ });
21
+ let menuItems = [];
22
+ try {
23
+ const parsedMenuId = parseInt(menuId);
24
+ const itemsResponse = await menusApi.items.list({
25
+ menus: [parsedMenuId],
26
+ per_page: 100,
27
+ orderby: "menu_order",
28
+ order: "asc",
29
+ });
30
+ menuItems = itemsResponse.items;
31
+ }
32
+ catch (e) {
33
+ console.error("Error obteniendo menú:", e);
34
+ }
35
+ return (_jsx("header", { className: styles.header, children: _jsx("div", { className: "container", children: _jsxs("div", { className: styles.inner, children: [_jsx(Link, { href: homeHref, className: styles.logo, children: siteName }), _jsx("div", { className: styles.navWrapper, children: menuItems.length > 0 && (_jsx("nav", { "aria-label": t("header.navAria"), className: styles.nav, children: _jsx("ul", { className: styles.list, children: menuItems.map((item) => (_jsx("li", { children: _jsx(Link, { href: convertWpUrl(item.url, wpApiUrl), className: "header-link", children: item.title.rendered }) }, item.id))) }) })) })] }) }) }));
36
+ }
@@ -0,0 +1,48 @@
1
+ .header {
2
+ position: sticky;
3
+ top: 0;
4
+ z-index: 100;
5
+ border-bottom: 1px solid var(--border);
6
+ backdrop-filter: blur(20px);
7
+ background: var(--card-bg);
8
+ box-shadow: 0 1px 3px var(--shadow);
9
+ }
10
+
11
+ .inner {
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: space-between;
15
+ padding: 1.25rem 0;
16
+ gap: 3rem;
17
+ }
18
+
19
+ .logo {
20
+ font-size: 1.75rem;
21
+ font-weight: 700;
22
+ color: var(--primary);
23
+ font-family: var(--font-display);
24
+ letter-spacing: -0.02em;
25
+ text-decoration: none;
26
+ }
27
+
28
+ .navWrapper {
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 2rem;
32
+ flex: 1;
33
+ }
34
+
35
+ .nav {
36
+ flex: 1;
37
+ display: flex;
38
+ justify-content: flex-end;
39
+ }
40
+
41
+ .list {
42
+ display: flex;
43
+ gap: 2.5rem;
44
+ list-style: none;
45
+ align-items: center;
46
+ margin: 0;
47
+ padding: 0;
48
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./base";
2
+ export * from "./utils";
3
+ export * from "./plugins";
4
+ export * from "./views";
5
+ export * from "./components";
6
+ export * from "./api";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./base";
2
+ export * from "./utils";
3
+ export * from "./plugins";
4
+ export * from "./views";
5
+ export * from "./components";
6
+ export * from "./api";
@@ -0,0 +1 @@
1
+ export * from "./yoast";
@@ -0,0 +1 @@
1
+ export * from "./yoast";
@@ -0,0 +1,16 @@
1
+ import type { Metadata } from "next";
2
+ import { WPYoastSchema } from "wpjsapi-lib";
3
+ export declare const extractYoastSchema: (entity: unknown) => WPYoastSchema | null;
4
+ type YoastMetaFallback = {
5
+ title?: string;
6
+ description?: string;
7
+ url?: string;
8
+ type?: string;
9
+ siteName?: string;
10
+ image?: string;
11
+ };
12
+ export declare const extractYoastMetadata: (entity: unknown, fallback?: YoastMetaFallback, opts?: {
13
+ siteUrl?: string;
14
+ wpApiUrl?: string;
15
+ }) => Metadata | null;
16
+ export {};
@@ -0,0 +1,44 @@
1
+ import { rewriteYoastUrls } from "../../utils/seo";
2
+ const hasYoast = (entity) => {
3
+ if (!entity || typeof entity !== "object")
4
+ return false;
5
+ if (!("yoast_head_json" in entity))
6
+ return false;
7
+ const yoast = entity.yoast_head_json;
8
+ return yoast !== undefined && yoast !== null;
9
+ };
10
+ export const extractYoastSchema = (entity) => {
11
+ const yoastEntity = hasYoast(entity) ? entity : null;
12
+ return yoastEntity?.yoast_head_json?.schema || null;
13
+ };
14
+ export const extractYoastMetadata = (entity, fallback = {}, opts = {}) => {
15
+ const yoastEntity = hasYoast(entity) ? entity : null;
16
+ const yoast = rewriteYoastUrls(yoastEntity?.yoast_head_json, opts);
17
+ if (!yoast)
18
+ return null;
19
+ const imageUrl = yoast.og_image?.[0]?.url || fallback.image;
20
+ const title = yoast.title || fallback.title;
21
+ const description = yoast.description || fallback.description;
22
+ const canonical = fallback.url;
23
+ return {
24
+ title,
25
+ description,
26
+ alternates: {
27
+ canonical,
28
+ },
29
+ openGraph: {
30
+ title: yoast.title || fallback.title,
31
+ description: yoast.og_description || yoast.description || fallback.description,
32
+ url: fallback.url,
33
+ type: yoast.og_type || "website",
34
+ siteName: yoast.og_site_name || fallback.siteName,
35
+ images: imageUrl ? [{ url: imageUrl }] : [],
36
+ },
37
+ twitter: {
38
+ card: yoast.twitter_card || "summary_large_image",
39
+ title: yoast.twitter_title || yoast.title || fallback.title,
40
+ description: yoast.twitter_description || yoast.description || fallback.description,
41
+ images: imageUrl ? [imageUrl] : undefined,
42
+ },
43
+ };
44
+ };
@@ -0,0 +1,37 @@
1
+ export type LanguageConfig = {
2
+ code: string;
3
+ basePath: string;
4
+ langId?: string;
5
+ };
6
+ export declare function getTranslationKey(entity: unknown): string | undefined;
7
+ export declare function getEntityLanguageId(entity: unknown): string | undefined;
8
+ type BuildUrlParams<T> = {
9
+ entity: T;
10
+ langCfg: LanguageConfig;
11
+ };
12
+ export declare function buildLanguageAlternatesFromTranslations<T>({ translations, languages, buildUrl, ensurePageExists, }: {
13
+ translations: T[];
14
+ languages: LanguageConfig[];
15
+ buildUrl: (params: BuildUrlParams<T>) => string | null | undefined;
16
+ ensurePageExists?: (params: BuildUrlParams<T>) => Promise<boolean>;
17
+ }): Promise<Record<string, string> | undefined>;
18
+ export declare function resolveLanguageAlternates<T>({ translationKey, languages, fetchTranslations, buildUrl, ensurePageExists, }: {
19
+ translationKey?: string;
20
+ languages: LanguageConfig[];
21
+ fetchTranslations: (translationKey: string) => Promise<T[]>;
22
+ buildUrl: (params: BuildUrlParams<T>) => string | null | undefined;
23
+ ensurePageExists?: (params: BuildUrlParams<T>) => Promise<boolean>;
24
+ }): Promise<Record<string, string> | undefined>;
25
+ export declare function buildLanguageAlternatesPerLanguage<T>({ languages, fetcher, buildUrl, ensurePageExists, }: {
26
+ languages: LanguageConfig[];
27
+ fetcher: (langCfg: LanguageConfig) => Promise<T | null>;
28
+ buildUrl: (params: BuildUrlParams<T>) => string | null | undefined;
29
+ ensurePageExists?: (params: BuildUrlParams<T>) => Promise<boolean>;
30
+ }): Promise<Record<string, string> | undefined>;
31
+ export declare function buildAbsoluteLangUrl({ basePath, path, siteUrl, }: {
32
+ basePath: string;
33
+ path: string;
34
+ siteUrl?: string;
35
+ }): string;
36
+ export declare function withXDefault(languages?: Record<string, string>): Record<string, string> | undefined;
37
+ export {};
@@ -0,0 +1,95 @@
1
+ import { getAbsoluteUrl } from "./routing";
2
+ const normalizeBasePath = (basePath) => basePath.endsWith("/") ? basePath : `${basePath}/`;
3
+ export function getTranslationKey(entity) {
4
+ const meta = entity?.meta;
5
+ const value = meta?.translation_key_v2;
6
+ return typeof value === "string" && value.trim().length > 0
7
+ ? value
8
+ : undefined;
9
+ }
10
+ export function getEntityLanguageId(entity) {
11
+ const lang = entity?.language?.[0] ??
12
+ entity?.taxonomies?.language?.[0];
13
+ return typeof lang === "number" ? lang.toString() : lang;
14
+ }
15
+ export async function buildLanguageAlternatesFromTranslations({ translations, languages, buildUrl, ensurePageExists, }) {
16
+ if (!translations?.length)
17
+ return undefined;
18
+ const alternates = {};
19
+ for (const langCfg of languages) {
20
+ if (!langCfg.langId)
21
+ continue;
22
+ const match = translations.find((translation) => getEntityLanguageId(translation)?.toString() ===
23
+ langCfg.langId?.toString());
24
+ if (!match)
25
+ continue;
26
+ if (ensurePageExists) {
27
+ const exists = await ensurePageExists({ entity: match, langCfg });
28
+ if (!exists)
29
+ continue;
30
+ }
31
+ const url = buildUrl({ entity: match, langCfg });
32
+ if (!url)
33
+ continue;
34
+ alternates[langCfg.code] = url;
35
+ }
36
+ return Object.keys(alternates).length > 1 ? alternates : undefined;
37
+ }
38
+ export async function resolveLanguageAlternates({ translationKey, languages, fetchTranslations, buildUrl, ensurePageExists, }) {
39
+ if (!translationKey)
40
+ return undefined;
41
+ const translations = await fetchTranslations(translationKey);
42
+ return buildLanguageAlternatesFromTranslations({
43
+ translations,
44
+ languages,
45
+ buildUrl,
46
+ ensurePageExists,
47
+ });
48
+ }
49
+ export async function buildLanguageAlternatesPerLanguage({ languages, fetcher, buildUrl, ensurePageExists, }) {
50
+ const alternates = {};
51
+ for (const langCfg of languages) {
52
+ if (!langCfg.langId)
53
+ continue;
54
+ try {
55
+ const entity = await fetcher(langCfg);
56
+ if (!entity)
57
+ continue;
58
+ if (ensurePageExists) {
59
+ const exists = await ensurePageExists({ entity, langCfg });
60
+ if (!exists)
61
+ continue;
62
+ }
63
+ const url = buildUrl({ entity, langCfg });
64
+ if (!url)
65
+ continue;
66
+ alternates[langCfg.code] = url;
67
+ }
68
+ catch (error) {
69
+ console.error("Error building hreflang for lang", langCfg.code, error);
70
+ }
71
+ }
72
+ return Object.keys(alternates).length > 1 ? alternates : undefined;
73
+ }
74
+ export function buildAbsoluteLangUrl({ basePath, path, siteUrl, }) {
75
+ const normalizedBase = normalizeBasePath(basePath);
76
+ const normalizedPath = path.startsWith("/")
77
+ ? path.slice(1)
78
+ : path.replace(/^\/+/, "");
79
+ return getAbsoluteUrl(`${normalizedBase}${normalizedPath}`, siteUrl);
80
+ }
81
+ export function withXDefault(languages) {
82
+ if (!languages)
83
+ return undefined;
84
+ const keys = Object.keys(languages);
85
+ if (keys.length === 0)
86
+ return undefined;
87
+ const preferred = languages["en"] ??
88
+ languages["es"] ??
89
+ languages["default"] ??
90
+ languages[keys[0]];
91
+ const result = preferred
92
+ ? { ...languages, "x-default": preferred }
93
+ : { ...languages };
94
+ return Object.keys(result).length > 1 ? result : undefined;
95
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Remove HTML tags from a string.
3
+ */
4
+ export declare function stripTags(html?: string): string | undefined;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove HTML tags from a string.
3
+ */
4
+ export function stripTags(html) {
5
+ if (!html)
6
+ return undefined;
7
+ return html.replace(/<[^>]*>/g, "");
8
+ }
@@ -0,0 +1,12 @@
1
+ type MessageTree = {
2
+ [key: string]: string | MessageTree;
3
+ };
4
+ type Messages = Record<string, MessageTree>;
5
+ /**
6
+ * Allow projects to override/extend default translations.
7
+ * Call once during app bootstrap with per-locale overrides.
8
+ */
9
+ export declare function extendTranslations(overrides: Partial<Messages>): void;
10
+ export type Locale = string;
11
+ export declare function getTranslator(locale?: string): (key: string, vars?: Record<string, string | number>) => string;
12
+ export {};