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.
- package/dist/api/categories.d.ts +29 -0
- package/dist/api/categories.js +91 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/api/index.js +6 -0
- package/dist/api/mappers.d.ts +5 -0
- package/dist/api/mappers.js +30 -0
- package/dist/api/posts.d.ts +44 -0
- package/dist/api/posts.js +104 -0
- package/dist/api/translations.d.ts +16 -0
- package/dist/api/translations.js +28 -0
- package/dist/api/types.d.ts +30 -0
- package/dist/api/types.js +1 -0
- package/dist/api/wpClient.d.ts +31 -0
- package/dist/api/wpClient.js +23 -0
- package/dist/app/config.d.ts +5 -0
- package/dist/app/config.js +28 -0
- package/dist/app/globals.css +326 -0
- package/dist/base/category/index.d.ts +38 -0
- package/dist/base/category/index.js +104 -0
- package/dist/base/category/pagination.d.ts +44 -0
- package/dist/base/category/pagination.js +114 -0
- package/dist/base/home/index.d.ts +13 -0
- package/dist/base/home/index.js +20 -0
- package/dist/base/home/pagination.d.ts +36 -0
- package/dist/base/home/pagination.js +83 -0
- package/dist/base/index.d.ts +7 -0
- package/dist/base/index.js +7 -0
- package/dist/base/layout/RootLayoutBase.d.ts +22 -0
- package/dist/base/layout/RootLayoutBase.js +15 -0
- package/dist/base/legal/index.d.ts +31 -0
- package/dist/base/legal/index.js +167 -0
- package/dist/base/post/index.d.ts +31 -0
- package/dist/base/post/index.js +97 -0
- package/dist/components/Breadcrumbs/index.d.ts +9 -0
- package/dist/components/Breadcrumbs/index.js +11 -0
- package/dist/components/Breadcrumbs/index.module.css +39 -0
- package/dist/components/Paginator/index.d.ts +10 -0
- package/dist/components/Paginator/index.js +16 -0
- package/dist/components/Paginator/index.module.css +13 -0
- package/dist/components/Paginator/index.utils.d.ts +7 -0
- package/dist/components/Paginator/index.utils.js +19 -0
- package/dist/components/PostCard/index.d.ts +10 -0
- package/dist/components/PostCard/index.js +22 -0
- package/dist/components/PostCard/index.module.css +56 -0
- package/dist/components/PostCard/index.types.d.ts +7 -0
- package/dist/components/PostCard/index.types.js +1 -0
- package/dist/components/PostCard/index.utils.d.ts +8 -0
- package/dist/components/PostCard/index.utils.js +23 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.js +5 -0
- package/dist/components/layout/Footer/index.d.ts +12 -0
- package/dist/components/layout/Footer/index.js +36 -0
- package/dist/components/layout/Footer/index.module.css +61 -0
- package/dist/components/layout/Header/index.d.ts +13 -0
- package/dist/components/layout/Header/index.js +36 -0
- package/dist/components/layout/Header/index.module.css +48 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/yoast/index.d.ts +16 -0
- package/dist/plugins/yoast/index.js +44 -0
- package/dist/utils/hreflang.d.ts +37 -0
- package/dist/utils/hreflang.js +95 -0
- package/dist/utils/html.d.ts +4 -0
- package/dist/utils/html.js +8 -0
- package/dist/utils/i18n.d.ts +12 -0
- package/dist/utils/i18n.js +161 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/routing.d.ts +40 -0
- package/dist/utils/routing.js +84 -0
- package/dist/utils/seo.d.ts +75 -0
- package/dist/utils/seo.js +97 -0
- package/dist/utils/sitemap.d.ts +26 -0
- package/dist/utils/sitemap.js +327 -0
- package/dist/views/CategoryListView/index.d.ts +12 -0
- package/dist/views/CategoryListView/index.js +17 -0
- package/dist/views/CategoryListView/index.module.css +39 -0
- package/dist/views/CategoryListView/index.utils.d.ts +5 -0
- package/dist/views/CategoryListView/index.utils.js +14 -0
- package/dist/views/CategoryPaginationView/index.d.ts +13 -0
- package/dist/views/CategoryPaginationView/index.js +18 -0
- package/dist/views/CategoryPaginationView/index.module.css +39 -0
- package/dist/views/CategoryPaginationView/index.utils.d.ts +5 -0
- package/dist/views/CategoryPaginationView/index.utils.js +4 -0
- package/dist/views/HomePaginationView/index.d.ts +13 -0
- package/dist/views/HomePaginationView/index.js +9 -0
- package/dist/views/HomePaginationView/index.module.css +38 -0
- package/dist/views/HomePaginationView/index.utils.d.ts +7 -0
- package/dist/views/HomePaginationView/index.utils.js +10 -0
- package/dist/views/HomeView/index.d.ts +13 -0
- package/dist/views/HomeView/index.js +21 -0
- package/dist/views/HomeView/index.module.css +78 -0
- package/dist/views/HomeView/index.utils.d.ts +23 -0
- package/dist/views/HomeView/index.utils.js +44 -0
- package/dist/views/LegalPageView/index.d.ts +8 -0
- package/dist/views/LegalPageView/index.js +12 -0
- package/dist/views/LegalPageView/index.module.css +39 -0
- package/dist/views/LegalPageView/index.utils.d.ts +11 -0
- package/dist/views/LegalPageView/index.utils.js +16 -0
- package/dist/views/PostView/index.d.ts +10 -0
- package/dist/views/PostView/index.js +26 -0
- package/dist/views/PostView/index.module.css +80 -0
- package/dist/views/PostView/index.types.d.ts +2 -0
- package/dist/views/PostView/index.types.js +1 -0
- package/dist/views/PostView/index.utils.d.ts +15 -0
- package/dist/views/PostView/index.utils.js +22 -0
- package/dist/views/index.d.ts +6 -0
- package/dist/views/index.js +6 -0
- 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 @@
|
|
|
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,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
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -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,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 {};
|