wpheadless-lib 1.1.10 → 1.1.11

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 (119) hide show
  1. package/dist/api/categories.d.ts +1 -1
  2. package/dist/api/categories.js +18 -2
  3. package/dist/api/index.d.ts +7 -6
  4. package/dist/api/index.js +7 -6
  5. package/dist/api/mappers.d.ts +1 -1
  6. package/dist/api/mappers.js +1 -1
  7. package/dist/api/menus.d.ts +18 -0
  8. package/dist/api/menus.js +39 -0
  9. package/dist/api/posts.js +30 -2
  10. package/dist/api/translations.js +7 -1
  11. package/dist/api/wpClient.d.ts +5 -8
  12. package/dist/api/wpClient.js +14 -0
  13. package/dist/app/config.d.ts +2 -1
  14. package/dist/app/config.js +4 -0
  15. package/dist/app/globals.css +33 -121
  16. package/dist/base/category/index.d.ts +3 -3
  17. package/dist/base/category/index.js +9 -8
  18. package/dist/base/category/pagination.d.ts +2 -2
  19. package/dist/base/category/pagination.js +12 -10
  20. package/dist/base/home/index.js +1 -1
  21. package/dist/base/home/pagination.js +3 -3
  22. package/dist/base/index.d.ts +7 -7
  23. package/dist/base/index.js +7 -7
  24. package/dist/base/layout/RootLayoutBase.d.ts +6 -7
  25. package/dist/base/layout/RootLayoutBase.js +4 -4
  26. package/dist/base/layout/RootLayoutBase.module.css +10 -0
  27. package/dist/base/legal/index.d.ts +4 -4
  28. package/dist/base/legal/index.js +36 -24
  29. package/dist/base/post/index.d.ts +1 -1
  30. package/dist/base/post/index.js +15 -12
  31. package/dist/components/Breadcrumbs/index.js +4 -4
  32. package/dist/components/JsonLd.d.ts +5 -0
  33. package/dist/components/JsonLd.js +9 -0
  34. package/dist/components/Paginator/index.d.ts +1 -1
  35. package/dist/components/Paginator/index.js +5 -5
  36. package/dist/components/PostCard/index.d.ts +3 -2
  37. package/dist/components/PostCard/index.js +8 -6
  38. package/dist/components/PostCard/index.module.css +7 -7
  39. package/dist/components/PostCard/index.types.d.ts +2 -1
  40. package/dist/components/PostCard/index.utils.d.ts +5 -5
  41. package/dist/components/PostCard/index.utils.js +8 -6
  42. package/dist/components/index.d.ts +6 -5
  43. package/dist/components/index.js +6 -5
  44. package/dist/components/layout/Footer/index.d.ts +5 -5
  45. package/dist/components/layout/Footer/index.js +11 -22
  46. package/dist/components/layout/Footer/index.module.css +59 -9
  47. package/dist/components/layout/Header/HeaderClient.d.ts +1 -1
  48. package/dist/components/layout/Header/HeaderClient.js +22 -2
  49. package/dist/components/layout/Header/index.d.ts +5 -5
  50. package/dist/components/layout/Header/index.js +10 -29
  51. package/dist/components/layout/Header/index.module.css +77 -16
  52. package/dist/components/ui/alert.d.ts +8 -0
  53. package/dist/components/ui/alert.js +12 -0
  54. package/dist/components/ui/alert.module.css +26 -0
  55. package/dist/components/ui/badge.d.ts +3 -0
  56. package/dist/components/ui/badge.js +9 -0
  57. package/dist/components/ui/badge.module.css +23 -0
  58. package/dist/components/ui/breadcrumb.d.ts +8 -0
  59. package/dist/components/ui/breadcrumb.js +22 -0
  60. package/dist/components/ui/breadcrumb.module.css +37 -0
  61. package/dist/components/ui/button.d.ts +7 -0
  62. package/dist/components/ui/button.js +16 -0
  63. package/dist/components/ui/button.module.css +72 -0
  64. package/dist/components/ui/card.d.ts +11 -0
  65. package/dist/components/ui/card.js +16 -0
  66. package/dist/components/ui/card.module.css +37 -0
  67. package/dist/components/ui/cn.d.ts +1 -0
  68. package/dist/components/ui/cn.js +3 -0
  69. package/dist/components/ui/index.d.ts +6 -0
  70. package/dist/components/ui/index.js +6 -0
  71. package/dist/components/ui/pagination.d.ts +14 -0
  72. package/dist/components/ui/pagination.js +25 -0
  73. package/dist/components/ui/pagination.module.css +62 -0
  74. package/dist/index.d.ts +6 -6
  75. package/dist/index.js +6 -6
  76. package/dist/plugins/index.d.ts +1 -1
  77. package/dist/plugins/index.js +1 -1
  78. package/dist/plugins/yoast/index.js +10 -7
  79. package/dist/utils/hreflang.d.ts +4 -7
  80. package/dist/utils/hreflang.js +11 -6
  81. package/dist/utils/html.d.ts +2 -0
  82. package/dist/utils/html.js +25 -0
  83. package/dist/utils/index.d.ts +7 -6
  84. package/dist/utils/index.js +7 -6
  85. package/dist/utils/language.d.ts +37 -0
  86. package/dist/utils/language.js +75 -0
  87. package/dist/utils/seo.d.ts +1 -6
  88. package/dist/utils/seo.js +9 -5
  89. package/dist/utils/sitemap.d.ts +1 -1
  90. package/dist/utils/sitemap.js +107 -56
  91. package/dist/views/CategoryListView/index.d.ts +1 -1
  92. package/dist/views/CategoryListView/index.js +6 -6
  93. package/dist/views/CategoryListView/index.utils.d.ts +1 -1
  94. package/dist/views/CategoryListView/index.utils.js +1 -1
  95. package/dist/views/CategoryPaginationView/index.d.ts +1 -1
  96. package/dist/views/CategoryPaginationView/index.js +6 -6
  97. package/dist/views/CategoryPaginationView/index.utils.d.ts +1 -1
  98. package/dist/views/CategoryPaginationView/index.utils.js +1 -1
  99. package/dist/views/HomePaginationView/index.d.ts +1 -1
  100. package/dist/views/HomePaginationView/index.js +5 -4
  101. package/dist/views/HomePaginationView/index.utils.d.ts +1 -1
  102. package/dist/views/HomePaginationView/index.utils.js +1 -1
  103. package/dist/views/HomeView/index.d.ts +1 -1
  104. package/dist/views/HomeView/index.js +13 -12
  105. package/dist/views/HomeView/index.module.css +51 -8
  106. package/dist/views/HomeView/index.utils.d.ts +1 -1
  107. package/dist/views/HomeView/index.utils.js +2 -2
  108. package/dist/views/LegalPageView/index.d.ts +2 -1
  109. package/dist/views/LegalPageView/index.js +5 -5
  110. package/dist/views/LegalPageView/index.module.css +3 -3
  111. package/dist/views/LegalPageView/index.utils.js +1 -1
  112. package/dist/views/PostView/index.d.ts +3 -2
  113. package/dist/views/PostView/index.js +9 -7
  114. package/dist/views/PostView/index.module.css +46 -13
  115. package/dist/views/PostView/index.utils.d.ts +3 -3
  116. package/dist/views/PostView/index.utils.js +2 -2
  117. package/dist/views/index.d.ts +6 -6
  118. package/dist/views/index.js +6 -6
  119. package/package.json +47 -10
@@ -0,0 +1,72 @@
1
+ .button {
2
+ display: inline-flex;
3
+ flex-shrink: 0;
4
+ align-items: center;
5
+ justify-content: center;
6
+ gap: 0.5rem;
7
+ border: 0;
8
+ border-radius: 0.5rem;
9
+ font-size: 0.875rem;
10
+ font-weight: 600;
11
+ line-height: 1;
12
+ text-decoration: none;
13
+ cursor: pointer;
14
+ transition:
15
+ color 0.2s ease,
16
+ background-color 0.2s ease,
17
+ border-color 0.2s ease,
18
+ box-shadow 0.2s ease;
19
+ }
20
+
21
+ .button:disabled {
22
+ pointer-events: none;
23
+ opacity: 0.5;
24
+ }
25
+
26
+ .default {
27
+ background: var(--primary);
28
+ color: var(--primary-foreground);
29
+ }
30
+
31
+ .default:hover {
32
+ background: var(--primary-hover);
33
+ color: var(--primary-foreground);
34
+ }
35
+
36
+ .outline {
37
+ border: 1px solid var(--border);
38
+ background: var(--background);
39
+ color: var(--foreground);
40
+ }
41
+
42
+ .outline:hover {
43
+ background: var(--background-alt);
44
+ color: var(--foreground);
45
+ }
46
+
47
+ .ghost {
48
+ background: transparent;
49
+ color: var(--foreground);
50
+ }
51
+
52
+ .ghost:hover {
53
+ background: var(--background-alt);
54
+ color: var(--foreground);
55
+ }
56
+
57
+ .defaultSize {
58
+ min-height: 2.25rem;
59
+ padding-inline: 0.75rem;
60
+ }
61
+
62
+ .small {
63
+ min-height: 2rem;
64
+ padding-inline: 0.625rem;
65
+ font-size: 0.75rem;
66
+ }
67
+
68
+ .icon {
69
+ width: 2.25rem;
70
+ height: 2.25rem;
71
+ padding: 0;
72
+ }
@@ -0,0 +1,11 @@
1
+ import type { ComponentPropsWithoutRef, ElementType } from "react";
2
+ type CardProps<T extends ElementType> = {
3
+ as?: T;
4
+ size?: "default" | "sm";
5
+ className?: string;
6
+ } & Omit<ComponentPropsWithoutRef<T>, "as" | "className">;
7
+ export declare function Card<T extends ElementType = "div">({ as, size, className, ...props }: CardProps<T>): import("react/jsx-runtime").JSX.Element;
8
+ export declare function CardHeader({ className, ...props }: ComponentPropsWithoutRef<"div">): import("react/jsx-runtime").JSX.Element;
9
+ export declare function CardContent({ className, ...props }: ComponentPropsWithoutRef<"div">): import("react/jsx-runtime").JSX.Element;
10
+ export declare function CardFooter({ className, ...props }: ComponentPropsWithoutRef<"div">): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "./cn.js";
3
+ import styles from "./card.module.css";
4
+ export function Card({ as, size = "default", className, ...props }) {
5
+ const Comp = as || "div";
6
+ return (_jsx(Comp, { "data-slot": "card", "data-size": size, className: cn(styles.card, size === "sm" && styles.small, className), ...props }));
7
+ }
8
+ export function CardHeader({ className, ...props }) {
9
+ return (_jsx("div", { "data-slot": "card-header", className: cn(styles.header, className), ...props }));
10
+ }
11
+ export function CardContent({ className, ...props }) {
12
+ return (_jsx("div", { "data-slot": "card-content", className: cn(styles.content, className), ...props }));
13
+ }
14
+ export function CardFooter({ className, ...props }) {
15
+ return (_jsx("div", { "data-slot": "card-footer", className: cn(styles.footer, className), ...props }));
16
+ }
@@ -0,0 +1,37 @@
1
+ .card {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ overflow: hidden;
6
+ border-radius: 0.75rem;
7
+ background: var(--card-bg);
8
+ color: var(--foreground);
9
+ box-shadow: 0 1px 3px var(--shadow);
10
+ transition: box-shadow 0.2s ease;
11
+ }
12
+
13
+ .card:hover {
14
+ box-shadow: 0 10px 25px var(--shadow-hover);
15
+ }
16
+
17
+ .small {
18
+ gap: 0.75rem;
19
+ }
20
+
21
+ .header {
22
+ display: grid;
23
+ gap: 0.25rem;
24
+ padding-inline: 1rem;
25
+ }
26
+
27
+ .content {
28
+ padding-inline: 1rem;
29
+ }
30
+
31
+ .footer {
32
+ display: flex;
33
+ align-items: center;
34
+ border-top: 1px solid var(--border);
35
+ background: var(--background-alt);
36
+ padding: 1rem;
37
+ }
@@ -0,0 +1 @@
1
+ export declare function cn(...classes: Array<string | false | null | undefined>): string;
@@ -0,0 +1,3 @@
1
+ export function cn(...classes) {
2
+ return classes.filter(Boolean).join(" ");
3
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./alert.js";
2
+ export * from "./badge.js";
3
+ export * from "./breadcrumb.js";
4
+ export * from "./button.js";
5
+ export * from "./card.js";
6
+ export * from "./pagination.js";
@@ -0,0 +1,6 @@
1
+ export * from "./alert.js";
2
+ export * from "./badge.js";
3
+ export * from "./breadcrumb.js";
4
+ export * from "./button.js";
5
+ export * from "./card.js";
6
+ export * from "./pagination.js";
@@ -0,0 +1,14 @@
1
+ import Link from "next/link";
2
+ import type { ComponentPropsWithoutRef } from "react";
3
+ export declare function Pagination({ className, ...props }: ComponentPropsWithoutRef<"nav">): import("react/jsx-runtime").JSX.Element;
4
+ export declare function PaginationContent({ className, ...props }: ComponentPropsWithoutRef<"ul">): import("react/jsx-runtime").JSX.Element;
5
+ export declare function PaginationItem(props: ComponentPropsWithoutRef<"li">): import("react/jsx-runtime").JSX.Element;
6
+ type PaginationLinkProps = ComponentPropsWithoutRef<typeof Link> & {
7
+ isActive?: boolean;
8
+ size?: "default" | "icon";
9
+ };
10
+ export declare function PaginationLink({ className, isActive, size, ...props }: PaginationLinkProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function PaginationPrevious({ children, className, ...props }: Omit<PaginationLinkProps, "size">): import("react/jsx-runtime").JSX.Element;
12
+ export declare function PaginationNext({ children, className, ...props }: Omit<PaginationLinkProps, "size">): import("react/jsx-runtime").JSX.Element;
13
+ export declare function PaginationEllipsis({ className, ...props }: ComponentPropsWithoutRef<"span">): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Link from "next/link";
3
+ import { cn } from "./cn.js";
4
+ import styles from "./pagination.module.css";
5
+ export function Pagination({ className, ...props }) {
6
+ return (_jsx("nav", { "data-slot": "pagination", className: cn(styles.pagination, className), ...props }));
7
+ }
8
+ export function PaginationContent({ className, ...props }) {
9
+ return (_jsx("ul", { "data-slot": "pagination-content", className: cn(styles.content, className), ...props }));
10
+ }
11
+ export function PaginationItem(props) {
12
+ return _jsx("li", { "data-slot": "pagination-item", ...props });
13
+ }
14
+ export function PaginationLink({ className, isActive, size = "icon", ...props }) {
15
+ return (_jsx(Link, { "data-slot": "pagination-link", "data-active": isActive, "aria-current": isActive ? "page" : undefined, className: cn(styles.link, size === "icon" ? styles.icon : styles.defaultSize, isActive && styles.active, className), ...props }));
16
+ }
17
+ export function PaginationPrevious({ children, className, ...props }) {
18
+ return (_jsxs(PaginationLink, { size: "default", className: className, ...props, children: [_jsx("span", { "aria-hidden": "true", children: "\u2190" }), _jsx("span", { children: children })] }));
19
+ }
20
+ export function PaginationNext({ children, className, ...props }) {
21
+ return (_jsxs(PaginationLink, { size: "default", className: className, ...props, children: [_jsx("span", { children: children }), _jsx("span", { "aria-hidden": "true", children: "\u2192" })] }));
22
+ }
23
+ export function PaginationEllipsis({ className, ...props }) {
24
+ return (_jsx("span", { "data-slot": "pagination-ellipsis", "aria-hidden": "true", className: cn(styles.ellipsis, className), ...props, children: "\u2026" }));
25
+ }
@@ -0,0 +1,62 @@
1
+ .pagination {
2
+ display: flex;
3
+ width: 100%;
4
+ justify-content: center;
5
+ margin-inline: auto;
6
+ }
7
+
8
+ .content {
9
+ display: flex;
10
+ flex-wrap: wrap;
11
+ align-items: center;
12
+ justify-content: center;
13
+ gap: 0.25rem;
14
+ list-style: none;
15
+ margin: 0;
16
+ padding: 0;
17
+ }
18
+
19
+ .link {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ border-radius: 0.5rem;
24
+ color: var(--foreground);
25
+ font-size: 0.875rem;
26
+ font-weight: 600;
27
+ text-decoration: none;
28
+ transition:
29
+ color 0.2s ease,
30
+ background-color 0.2s ease,
31
+ border-color 0.2s ease;
32
+ }
33
+
34
+ .link:hover {
35
+ background: var(--background-alt);
36
+ color: var(--foreground);
37
+ }
38
+
39
+ .icon {
40
+ width: 2.25rem;
41
+ height: 2.25rem;
42
+ }
43
+
44
+ .defaultSize {
45
+ min-height: 2.25rem;
46
+ gap: 0.5rem;
47
+ padding-inline: 0.75rem;
48
+ }
49
+
50
+ .active {
51
+ border: 1px solid var(--border);
52
+ background: var(--background);
53
+ }
54
+
55
+ .ellipsis {
56
+ display: flex;
57
+ width: 2.25rem;
58
+ height: 2.25rem;
59
+ align-items: center;
60
+ justify-content: center;
61
+ color: var(--foreground-light);
62
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +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";
1
+ export * from "./base/index.js";
2
+ export * from "./utils/index.js";
3
+ export * from "./plugins/index.js";
4
+ export * from "./views/index.js";
5
+ export * from "./components/index.js";
6
+ export * from "./api/index.js";
package/dist/index.js CHANGED
@@ -1,6 +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";
1
+ export * from "./base/index.js";
2
+ export * from "./utils/index.js";
3
+ export * from "./plugins/index.js";
4
+ export * from "./views/index.js";
5
+ export * from "./components/index.js";
6
+ export * from "./api/index.js";
@@ -1 +1 @@
1
- export * from "./yoast";
1
+ export * from "./yoast/index.js";
@@ -1 +1 @@
1
- export * from "./yoast";
1
+ export * from "./yoast/index.js";
@@ -1,4 +1,5 @@
1
- import { buildYoastRobotsMeta, rewriteYoastUrls } from "../../utils/seo";
1
+ import { buildYoastRobotsMeta, rewriteYoastUrls } from "../../utils/seo.js";
2
+ import { toPlainText } from "../../utils/html.js";
2
3
  const hasYoast = (entity) => {
3
4
  if (!entity || typeof entity !== "object")
4
5
  return false;
@@ -18,8 +19,8 @@ export const extractYoastMetadata = (entity, fallback = {}, opts = {}) => {
18
19
  return null;
19
20
  const robots = buildYoastRobotsMeta(yoast.robots);
20
21
  const imageUrl = yoast.og_image?.[0]?.url || fallback.image;
21
- const title = yoast.title || fallback.title;
22
- const description = yoast.description || fallback.description;
22
+ const title = toPlainText(yoast.title) || fallback.title;
23
+ const description = toPlainText(yoast.description) || fallback.description;
23
24
  const canonical = fallback.url;
24
25
  return {
25
26
  title,
@@ -29,8 +30,9 @@ export const extractYoastMetadata = (entity, fallback = {}, opts = {}) => {
29
30
  canonical,
30
31
  },
31
32
  openGraph: {
32
- title: yoast.title || fallback.title,
33
- description: yoast.og_description || yoast.description || fallback.description,
33
+ title: toPlainText(yoast.title) || fallback.title,
34
+ description: toPlainText(yoast.og_description || yoast.description) ||
35
+ fallback.description,
34
36
  url: fallback.url,
35
37
  type: yoast.og_type || "website",
36
38
  siteName: yoast.og_site_name || fallback.siteName,
@@ -38,8 +40,9 @@ export const extractYoastMetadata = (entity, fallback = {}, opts = {}) => {
38
40
  },
39
41
  twitter: {
40
42
  card: yoast.twitter_card || "summary_large_image",
41
- title: yoast.twitter_title || yoast.title || fallback.title,
42
- description: yoast.twitter_description || yoast.description || fallback.description,
43
+ title: toPlainText(yoast.twitter_title || yoast.title) || fallback.title,
44
+ description: toPlainText(yoast.twitter_description || yoast.description) ||
45
+ fallback.description,
43
46
  images: imageUrl ? [imageUrl] : undefined,
44
47
  },
45
48
  };
@@ -1,13 +1,10 @@
1
- export type LanguageConfig = {
2
- code: string;
3
- basePath: string;
4
- langId?: string;
5
- };
1
+ import { type SiteLanguage, type SiteLanguageInput } from "./language.js";
2
+ export type LanguageConfig = SiteLanguageInput;
6
3
  export declare function getTranslationKey(entity: unknown): string | undefined;
7
4
  export declare function getEntityLanguageId(entity: unknown): string | undefined;
8
5
  type BuildUrlParams<T> = {
9
6
  entity: T;
10
- langCfg: LanguageConfig;
7
+ langCfg: SiteLanguage;
11
8
  };
12
9
  export declare function buildLanguageAlternatesFromTranslations<T>({ translations, languages, buildUrl, ensurePageExists, }: {
13
10
  translations: T[];
@@ -29,7 +26,7 @@ export declare function buildLanguageAlternatesPerLanguage<T>({ languages, fetch
29
26
  ensurePageExists?: (params: BuildUrlParams<T>) => Promise<boolean>;
30
27
  }): Promise<Record<string, string> | undefined>;
31
28
  export declare function buildAbsoluteLangUrl({ basePath, path, siteUrl, }: {
32
- basePath: string;
29
+ basePath?: string;
33
30
  path: string;
34
31
  siteUrl?: string;
35
32
  }): string;
@@ -1,4 +1,5 @@
1
- import { getAbsoluteUrl } from "./routing";
1
+ import { getAbsoluteUrl } from "./routing.js";
2
+ import { normalizeLanguages, shouldRenderLanguageAlternates, } from "./language.js";
2
3
  const normalizeBasePath = (basePath) => basePath.endsWith("/") ? basePath : `${basePath}/`;
3
4
  export function getTranslationKey(entity) {
4
5
  const meta = entity?.meta;
@@ -13,10 +14,11 @@ export function getEntityLanguageId(entity) {
13
14
  return typeof lang === "number" ? lang.toString() : lang;
14
15
  }
15
16
  export async function buildLanguageAlternatesFromTranslations({ translations, languages, buildUrl, ensurePageExists, }) {
16
- if (!translations?.length)
17
+ if (!translations?.length || !shouldRenderLanguageAlternates(languages)) {
17
18
  return undefined;
19
+ }
18
20
  const alternates = {};
19
- for (const langCfg of languages) {
21
+ for (const langCfg of normalizeLanguages(languages)) {
20
22
  if (!langCfg.langId)
21
23
  continue;
22
24
  const match = translations.find((translation) => getEntityLanguageId(translation)?.toString() ===
@@ -36,8 +38,9 @@ export async function buildLanguageAlternatesFromTranslations({ translations, la
36
38
  return Object.keys(alternates).length > 1 ? alternates : undefined;
37
39
  }
38
40
  export async function resolveLanguageAlternates({ translationKey, languages, fetchTranslations, buildUrl, ensurePageExists, }) {
39
- if (!translationKey)
41
+ if (!translationKey || !shouldRenderLanguageAlternates(languages)) {
40
42
  return undefined;
43
+ }
41
44
  const translations = await fetchTranslations(translationKey);
42
45
  return buildLanguageAlternatesFromTranslations({
43
46
  translations,
@@ -47,8 +50,10 @@ export async function resolveLanguageAlternates({ translationKey, languages, fet
47
50
  });
48
51
  }
49
52
  export async function buildLanguageAlternatesPerLanguage({ languages, fetcher, buildUrl, ensurePageExists, }) {
53
+ if (!shouldRenderLanguageAlternates(languages))
54
+ return undefined;
50
55
  const alternates = {};
51
- for (const langCfg of languages) {
56
+ for (const langCfg of normalizeLanguages(languages)) {
52
57
  if (!langCfg.langId)
53
58
  continue;
54
59
  try {
@@ -72,7 +77,7 @@ export async function buildLanguageAlternatesPerLanguage({ languages, fetcher, b
72
77
  return Object.keys(alternates).length > 1 ? alternates : undefined;
73
78
  }
74
79
  export function buildAbsoluteLangUrl({ basePath, path, siteUrl, }) {
75
- const normalizedBase = normalizeBasePath(basePath);
80
+ const normalizedBase = normalizeBasePath(basePath || "/");
76
81
  const normalizedPath = path.startsWith("/")
77
82
  ? path.slice(1)
78
83
  : path.replace(/^\/+/, "");
@@ -2,3 +2,5 @@
2
2
  * Remove HTML tags from a string.
3
3
  */
4
4
  export declare function stripTags(html?: string): string | undefined;
5
+ export declare function decodeHtmlEntities(value?: string): string | undefined;
6
+ export declare function toPlainText(html?: string): string | undefined;
@@ -6,3 +6,28 @@ export function stripTags(html) {
6
6
  return undefined;
7
7
  return html.replace(/<[^>]*>/g, "");
8
8
  }
9
+ const namedEntities = {
10
+ amp: "&",
11
+ apos: "'",
12
+ gt: ">",
13
+ lt: "<",
14
+ nbsp: " ",
15
+ quot: '"',
16
+ };
17
+ export function decodeHtmlEntities(value) {
18
+ if (!value)
19
+ return value;
20
+ return value.replace(/&(#(?:x[0-9a-fA-F]+|\d+)|[a-zA-Z]+);/g, (match, entity) => {
21
+ if (entity[0] === "#") {
22
+ const isHex = entity[1]?.toLowerCase() === "x";
23
+ const codePoint = Number.parseInt(entity.slice(isHex ? 2 : 1), isHex ? 16 : 10);
24
+ return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
25
+ }
26
+ return namedEntities[entity] ?? match;
27
+ });
28
+ }
29
+ export function toPlainText(html) {
30
+ const stripped = stripTags(html);
31
+ const decoded = decodeHtmlEntities(stripped);
32
+ return decoded?.replace(/\s+/g, " ").trim() || undefined;
33
+ }
@@ -1,6 +1,7 @@
1
- export * from "./i18n";
2
- export * from "./routing";
3
- export * from "./seo";
4
- export * from "./html";
5
- export * from "./hreflang";
6
- export * from "./sitemap";
1
+ export * from "./i18n.js";
2
+ export * from "./routing.js";
3
+ export * from "./seo.js";
4
+ export * from "./html.js";
5
+ export * from "./language.js";
6
+ export * from "./hreflang.js";
7
+ export * from "./sitemap.js";
@@ -1,6 +1,7 @@
1
- export * from "./i18n";
2
- export * from "./routing";
3
- export * from "./seo";
4
- export * from "./html";
5
- export * from "./hreflang";
6
- export * from "./sitemap";
1
+ export * from "./i18n.js";
2
+ export * from "./routing.js";
3
+ export * from "./seo.js";
4
+ export * from "./html.js";
5
+ export * from "./language.js";
6
+ export * from "./hreflang.js";
7
+ export * from "./sitemap.js";
@@ -0,0 +1,37 @@
1
+ export type SiteLanguageInput = {
2
+ code: string;
3
+ basePath?: string;
4
+ langId?: string;
5
+ wpLang?: string;
6
+ locale?: string;
7
+ dateLocale?: string;
8
+ isDefault?: boolean;
9
+ };
10
+ export type SiteLanguage = Required<Pick<SiteLanguageInput, "code" | "basePath" | "locale" | "isDefault">> & Omit<SiteLanguageInput, "basePath" | "locale" | "isDefault"> & {
11
+ prefixed: boolean;
12
+ };
13
+ export type LanguageMode = "single" | "multi";
14
+ export type LanguageConfigOptions = {
15
+ defaultCode?: string;
16
+ defaultLocale?: string;
17
+ };
18
+ export declare function normalizeLanguages(languages?: SiteLanguageInput[], options?: LanguageConfigOptions): SiteLanguage[];
19
+ export declare function getLanguageMode(languages?: SiteLanguageInput[]): LanguageMode;
20
+ export declare function getDefaultLanguage(languages?: SiteLanguageInput[]): SiteLanguage;
21
+ export declare function getLanguageByCode(languages: SiteLanguageInput[] | undefined, code?: string): SiteLanguage | undefined;
22
+ export declare function getLanguageBasePath(language: SiteLanguageInput, allLanguages?: SiteLanguageInput[]): string;
23
+ export declare function shouldRenderLanguageAlternates(languages?: SiteLanguageInput[]): boolean;
24
+ export declare function buildLanguagePath(language: SiteLanguageInput, path?: string, allLanguages?: SiteLanguageInput[]): string;
25
+ export declare function getWpLanguageCode(language?: SiteLanguageInput): string | undefined;
26
+ export declare function getWpLanguageFilter(language?: SiteLanguageInput): {
27
+ taxonomies?: undefined;
28
+ lang?: undefined;
29
+ } | {
30
+ taxonomies: {
31
+ language: string[];
32
+ };
33
+ lang?: undefined;
34
+ } | {
35
+ lang: string;
36
+ taxonomies?: undefined;
37
+ };
@@ -0,0 +1,75 @@
1
+ const normalizePath = (path) => {
2
+ if (!path || path === "/")
3
+ return "/";
4
+ const withLeadingSlash = path.startsWith("/") ? path : `/${path}`;
5
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash : `${withLeadingSlash}/`;
6
+ };
7
+ const defaultSecondaryPath = (code) => normalizePath(code);
8
+ export function normalizeLanguages(languages, options = {}) {
9
+ const fallbackCode = options.defaultCode || options.defaultLocale || "en";
10
+ const source = languages && languages.length > 0
11
+ ? languages
12
+ : [{ code: fallbackCode, locale: options.defaultLocale || fallbackCode }];
13
+ const explicitDefaultIndex = source.findIndex((language) => language.isDefault);
14
+ const defaultIndex = explicitDefaultIndex >= 0 ? explicitDefaultIndex : 0;
15
+ const isMulti = source.length > 1;
16
+ return source.map((language, index) => {
17
+ const isDefault = index === defaultIndex;
18
+ const code = language.code;
19
+ const basePath = language.basePath !== undefined
20
+ ? normalizePath(language.basePath)
21
+ : isMulti && !isDefault
22
+ ? defaultSecondaryPath(code)
23
+ : "/";
24
+ return {
25
+ ...language,
26
+ code,
27
+ locale: language.locale || code,
28
+ basePath: isDefault ? "/" : basePath,
29
+ isDefault,
30
+ prefixed: isMulti && !isDefault,
31
+ };
32
+ });
33
+ }
34
+ export function getLanguageMode(languages) {
35
+ return normalizeLanguages(languages).length > 1 ? "multi" : "single";
36
+ }
37
+ export function getDefaultLanguage(languages) {
38
+ return normalizeLanguages(languages).find((language) => language.isDefault) ?? normalizeLanguages(languages)[0];
39
+ }
40
+ export function getLanguageByCode(languages, code) {
41
+ const normalized = normalizeLanguages(languages);
42
+ if (!code)
43
+ return normalized.find((language) => language.isDefault) ?? normalized[0];
44
+ return normalized.find((language) => language.code === code || language.locale === code);
45
+ }
46
+ export function getLanguageBasePath(language, allLanguages) {
47
+ if (allLanguages) {
48
+ const normalized = normalizeLanguages(allLanguages);
49
+ const match = normalized.find((item) => item.code === language.code) ??
50
+ normalized.find((item) => item.locale === language.locale);
51
+ if (match)
52
+ return match.basePath;
53
+ }
54
+ return normalizeLanguages([language])[0].basePath;
55
+ }
56
+ export function shouldRenderLanguageAlternates(languages) {
57
+ return normalizeLanguages(languages).length > 1;
58
+ }
59
+ export function buildLanguagePath(language, path = "", allLanguages) {
60
+ const basePath = getLanguageBasePath(language, allLanguages);
61
+ const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
62
+ return normalizePath(`${basePath}${normalizedPath}`);
63
+ }
64
+ export function getWpLanguageCode(language) {
65
+ return language?.wpLang;
66
+ }
67
+ export function getWpLanguageFilter(language) {
68
+ if (!language)
69
+ return {};
70
+ if (language.langId) {
71
+ return { taxonomies: { language: [language.langId] } };
72
+ }
73
+ const wpLang = getWpLanguageCode(language);
74
+ return wpLang ? { lang: wpLang } : {};
75
+ }
@@ -20,11 +20,6 @@ type FeaturedMedia = {
20
20
  [key: string]: unknown;
21
21
  };
22
22
  type FeaturedMediaLike = Pick<FeaturedMedia, "source_url">;
23
- type YoastSchemaHolder = {
24
- yoast_head_json?: {
25
- schema?: unknown;
26
- };
27
- };
28
23
  type YoastHolder = {
29
24
  yoast_head_json?: WPYoastHeadJson;
30
25
  };
@@ -46,7 +41,7 @@ export declare function rewriteYoastUrls<T extends WPYoastHeadJson | undefined>(
46
41
  export declare function getYoastHead(entity?: YoastHolder, opts?: RewriteOpts): WPYoastHeadJson | undefined;
47
42
  export declare function buildYoastRobotsMeta(robots?: WPYoastHeadJson["robots"]): Metadata["robots"] | undefined;
48
43
  export declare function isYoastNoindex(yoast?: WPYoastHeadJson): boolean;
49
- export declare function getYoastSchema(entity?: YoastSchemaHolder, opts?: RewriteOpts): unknown;
44
+ export declare function getYoastSchema(entity?: unknown, opts?: RewriteOpts): unknown;
50
45
  export declare function stripBreadcrumbsFromSchema(schema: unknown): unknown;
51
46
  export declare function getFeaturedMedia(entity?: WithEmbeddedMedia): FeaturedMedia | undefined;
52
47
  export declare function getPlainTextExcerpt(html?: string, maxLength?: number): string | undefined;
package/dist/utils/seo.js CHANGED
@@ -1,4 +1,4 @@
1
- import { stripTags } from "./html";
1
+ import { stripTags, toPlainText } from "./html.js";
2
2
  export function getOgImageUrl(entity, featuredMedia) {
3
3
  const ogUrl = entity?.yoast_head_json?.og_image?.[0]?.url;
4
4
  return ogUrl || featuredMedia?.source_url;
@@ -196,10 +196,14 @@ export function getFeaturedMedia(entity) {
196
196
  return entity?._embedded?.["wp:featuredmedia"]?.[0];
197
197
  }
198
198
  export function getPlainTextExcerpt(html, maxLength = 160) {
199
- if (!html)
200
- return undefined;
201
- const text = html.replace(/<[^>]*>/g, "");
202
- return maxLength > 0 ? text.slice(0, maxLength) : text;
199
+ const text = toPlainText(html);
200
+ if (!text || maxLength <= 0 || text.length <= maxLength)
201
+ return text;
202
+ const truncated = text.slice(0, maxLength).trimEnd();
203
+ const lastSpace = truncated.lastIndexOf(" ");
204
+ return lastSpace > maxLength * 0.6
205
+ ? truncated.slice(0, lastSpace).trimEnd()
206
+ : truncated;
203
207
  }
204
208
  export function buildBreadcrumbSchema(items) {
205
209
  return {
@@ -1,4 +1,4 @@
1
- import type { LanguageConfig } from "./hreflang";
1
+ import type { LanguageConfig } from "./hreflang.js";
2
2
  export type SitemapRuntimeConfig = {
3
3
  postsPerPagePagination: number;
4
4
  };