Skip to content

Commit f467de6

Browse files
authored
feat: support pagination (#17)
* pagination * tests * Add rare-lemons-share.md * Update rare-lemons-share.md
1 parent bda4038 commit f467de6

8 files changed

Lines changed: 97 additions & 0 deletions

File tree

.changeset/rare-lemons-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tanstack-meta": minor
3+
---
4+
5+
feat: support pagination

packages/meta/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ An object containing `meta` and `links` properties, which can be used as the ret
7878
- `archives`
7979
- `assets`
8080
- `bookmarks`
81+
- `pagination`
8182
- `category`
8283
- `classification`
8384
- `other`

packages/meta/src/generate/basic.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,33 @@ describe("generateBasicLinks", () => {
184184
{ rel: "bookmarks", href: "https://example.com/bookmark2" },
185185
]);
186186
});
187+
188+
test("emits pagination links when previous and next are provided", () => {
189+
const metadata = normalizeMetadata({
190+
pagination: {
191+
previous: "https://example.com/page/1",
192+
next: "https://example.com/page/3",
193+
},
194+
});
195+
196+
expect(generateBasicLinks(metadata)).toEqual([
197+
{ rel: "previous", href: "https://example.com/page/1" },
198+
{ rel: "next", href: "https://example.com/page/3" },
199+
]);
200+
});
201+
202+
test("omits missing pagination entries and stringifies URLs", () => {
203+
const metadata = normalizeMetadata({
204+
pagination: {
205+
previous: new URL("https://example.com/page/2"),
206+
next: null,
207+
},
208+
});
209+
210+
expect(generateBasicLinks(metadata)).toEqual([
211+
{ rel: "previous", href: "https://example.com/page/2" },
212+
]);
213+
});
187214
});
188215

189216
describe("generateItunes", () => {

packages/meta/src/generate/basic.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ export function generateBasicLinks(metadata: NormalizedMetadata): OutputLinks {
7070
href: bookmark,
7171
}))
7272
: []),
73+
...(metadata.pagination
74+
? [
75+
metadata.pagination.previous
76+
? { rel: "previous", href: metadata.pagination.previous }
77+
: null,
78+
metadata.pagination.next
79+
? { rel: "next", href: metadata.pagination.next }
80+
: null,
81+
]
82+
: []),
7383
]);
7484
}
7585

packages/meta/src/normalize/basic.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
normalizeAppleWebApp,
66
normalizeFacebook,
77
normalizeItunes,
8+
normalizePagination,
89
normalizeRobotsValue,
910
normalizeVerification,
1011
} from "./basic";
@@ -141,6 +142,26 @@ describe("normalizeItunes", () => {
141142
});
142143
});
143144

145+
describe("normalizePagination", () => {
146+
test("returns null values when input is falsy", () => {
147+
const result = normalizePagination(undefined);
148+
149+
expect(result).toEqual({ previous: null, next: null });
150+
});
151+
152+
test("converts URL instances to strings and preserves provided values", () => {
153+
const result = normalizePagination({
154+
previous: new URL("https://example.com/page/1"),
155+
next: "https://example.com/page/3",
156+
});
157+
158+
expect(result).toEqual({
159+
previous: "https://example.com/page/1",
160+
next: "https://example.com/page/3",
161+
});
162+
});
163+
});
164+
144165
describe("normalizeRobotsValue", () => {
145166
test("returns null when input is falsy", () => {
146167
const result = normalizeRobotsValue(undefined);

packages/meta/src/normalize/basic.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,15 @@ export const normalizeFacebook = (facebook: InputMetadata["facebook"]) => {
169169
admins: resolveAsArrayOrUndefined(facebook.admins),
170170
};
171171
};
172+
173+
function resolveUrl(url: string | URL): string {
174+
return url instanceof URL ? url.toString() : url;
175+
}
176+
export const normalizePagination = (
177+
pagination: InputMetadata["pagination"],
178+
) => {
179+
return {
180+
previous: pagination?.previous ? resolveUrl(pagination.previous) : null,
181+
next: pagination?.next ? resolveUrl(pagination.next) : null,
182+
};
183+
};

packages/meta/src/normalize/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
normalizeAppleWebApp,
66
normalizeFacebook,
77
normalizeItunes,
8+
normalizePagination,
89
normalizeRobotsValue,
910
normalizeVerification,
1011
} from "./basic";
@@ -45,6 +46,7 @@ export function normalizeMetadata(metadata: InputMetadata): NormalizedMetadata {
4546
archives: resolveAsArrayOrUndefined(metadata.archives) ?? null,
4647
assets: resolveAsArrayOrUndefined(metadata.assets) ?? null,
4748
bookmarks: resolveAsArrayOrUndefined(metadata.bookmarks) ?? null,
49+
pagination: normalizePagination(metadata.pagination),
4850
category: metadata.category ?? null,
4951
classification: metadata.classification ?? null,
5052
other: metadata.other ?? null,

packages/meta/src/types/io.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,24 @@ export type InputMetadata = {
193193
* ```
194194
*/
195195
bookmarks?: NextMetadata["bookmarks"];
196+
/**
197+
* The pagination link rel properties.
198+
*
199+
* @example
200+
* ```tsx
201+
* pagination: {
202+
* previous: "https://example.com/items?page=1",
203+
* next: "https://example.com/items?page=3"
204+
* }
205+
*
206+
* // Renders
207+
* <link rel="prev" href="https://example.com/items?page=1" />
208+
* <link rel="next" href="https://example.com/items?page=3" />
209+
* ```
210+
*
211+
* @see https://developers.google.com/search/blog/2011/09/pagination-with-relnext-and-relprev
212+
*/
213+
pagination?: NextMetadata["pagination"];
196214
/**
197215
* The category meta name property.
198216
*
@@ -465,6 +483,7 @@ export type NormalizedMetadata = {
465483
archives: ResolvedMetadata["archives"];
466484
assets: ResolvedMetadata["assets"];
467485
bookmarks: ResolvedMetadata["bookmarks"];
486+
pagination: ResolvedMetadata["pagination"];
468487
category: ResolvedMetadata["category"];
469488
classification: ResolvedMetadata["classification"];
470489
other: ResolvedMetadata["other"];

0 commit comments

Comments
 (0)