Skip to content

Commit

Permalink
chore(release): v0.14.20
Browse files Browse the repository at this point in the history
  • Loading branch information
jasongitmail committed Jul 28, 2024
1 parent acf0db8 commit f01597d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 26 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "super-sitemap",
"version": "0.14.19",
"version": "0.14.20",
"description": "SvelteKit sitemap focused on ease of use and making it impossible to forget to add your paths.",
"sideEffects": false,
"repository": {
Expand Down
13 changes: 10 additions & 3 deletions src/lib/sitemap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe('sitemap.ts', () => {
});

describe('generateBody()', () => {
const paths = new Set([
const paths = [
{ path: '/path1' },
{ path: '/path2' },
// Note: in reality, an entry would already exist for /about, /es/about,
Expand All @@ -213,7 +213,7 @@ describe('sitemap.ts', () => {
{ lang: 'es', path: '/es/about' },
],
},
]);
];
const resultXml = sitemap.generateBody('https://example.com', paths, 'weekly', 0.3);

it('should generate the expected XML sitemap string', () => {
Expand Down Expand Up @@ -1141,8 +1141,15 @@ describe('sitemap.ts', () => {
{ path: '/path3' },
];
const expected = [{ path: '/path1' }, { path: '/path2' }, { path: '/path3' }];

expect(sitemap.deduplicatePaths(paths)).toEqual(expected);
});
});

describe('normalizeAdditionalPaths()', () => {
it('should normalize additionalPaths to ensure each starts with a forward slash', () => {
const additionalPaths = ['/foo', 'bar', '/baz'];
const expected = [{ path: '/foo' }, { path: '/bar' }, { path: '/baz' }];
expect(sitemap.normalizeAdditionalPaths(additionalPaths)).toEqual(expected);
});
});
});
56 changes: 34 additions & 22 deletions src/lib/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,14 @@ export async function response({
processPaths,
sort = false,
}: SitemapConfig): Promise<Response> {
// 500 error
// Cause a 500 error for visibility
if (!origin) {
throw new Error('Sitemap: `origin` property is required in sitemap config.');
}

// - Put `additionalPaths` into PathObj format and ensure each starts with a
// '/', for consistency. We will not translate any additionalPaths, b/c they
// could be something like a PDF within the user's static dir.
let paths: PathObj[] = [
let paths = [
...generatePaths(excludePatterns, paramValues, lang),
...additionalPaths.map((path) => ({ path: path.startsWith('/') ? path : '/' + path })),
...normalizeAdditionalPaths(additionalPaths),
];

if (processPaths) {
Expand All @@ -123,23 +120,21 @@ export async function response({
paths.sort((a, b) => a.path.localeCompare(b.path));
}

const pathSet = new Set(paths);
const totalPages = Math.ceil(pathSet.size / maxPerPage);
const totalPages = Math.ceil(paths.length / maxPerPage);

let body: string;
if (!page) {
// User is visiting `/sitemap.xml` or `/sitemap[[page]].xml` without page
// param provided.
if (pathSet.size <= maxPerPage) {
body = generateBody(origin, pathSet, changefreq, priority);
// User is visiting `/sitemap.xml` or `/sitemap[[page]].xml` without page.
if (paths.length <= maxPerPage) {
body = generateBody(origin, paths, changefreq, priority);
} else {
body = generateSitemapIndex(origin, totalPages);
}
} else {
// User is visiting a sitemap index's subpage–e.g. `sitemap[[page]].xml`.

// Ensure `page` param is numeric. We do it this way to avoid the need to
// instruct devs to create a route matcher, to keep set up easier for them.
// Ensure `page` param is numeric. We do it this way to avoid needing to
// instruct devs to create a route matcher, to ease set up for best DX.
if (!/^[1-9]\d*$/.test(page)) {
return new Response('Invalid page param', { status: 400 });
}
Expand All @@ -149,21 +144,20 @@ export async function response({
return new Response('Page does not exist', { status: 404 });
}

const pathsSubset = paths.slice((pageInt - 1) * maxPerPage, pageInt * maxPerPage);

body = generateBody(origin, new Set(pathsSubset), changefreq, priority);
const pathsOnThisPage = paths.slice((pageInt - 1) * maxPerPage, pageInt * maxPerPage);
body = generateBody(origin, pathsOnThisPage, changefreq, priority);
}

// Merge keys case-insensitive; custom headers take precedence over defaults.
const _headers = {
const newHeaders = {
'cache-control': 'max-age=0, s-maxage=3600', // 1h CDN cache
'content-type': 'application/xml',
...Object.fromEntries(
Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])
),
};

return new Response(body, { headers: _headers });
return new Response(body, { headers: newHeaders });
}

/**
Expand All @@ -186,15 +180,15 @@ export async function response({
*/
export function generateBody(
origin: string,
paths: Set<PathObj>,
paths: PathObj[],
changefreq: SitemapConfig['changefreq'] = false,
priority: SitemapConfig['priority'] = false
): string {
return `<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
>${Array.from(paths)
>${paths
.map(
({ alternates, path }) =>
`
Expand Down Expand Up @@ -241,14 +235,16 @@ export function generateSitemapIndex(origin: string, pages: number): string {
}

/**
* Generates an array of route paths to be included in a sitemap.
* Generates an array of paths, based on `src/routes`, to be included in a
* sitemap.
*
* @public
*
* @param excludePatterns - Optional. An array of patterns for routes to be
* excluded.
* @param paramValues - Optional. An object mapping each parameterized route to
* an array of param values for that route.
* @param lang - Optional. The language configuration.
* @returns An array of strings, each representing a path for the sitemap.
*/
export function generatePaths(
Expand Down Expand Up @@ -626,3 +622,19 @@ export function deduplicatePaths(pathObjs: PathObj[]): PathObj[] {

return Array.from(uniquePaths.values());
}

/**
* Normalizes the user-provided `additionalPaths` to ensure each starts with a
* forward slash and then returns a `PathObj[]` type.
*
* Note: `additionalPaths` are never translated based on the lang config because
* they could be something like a PDF within the user's static dir.
*
* @param additionalPaths - An array of string paths to be normalized
* @returns An array of PathObj
*/
export function normalizeAdditionalPaths(additionalPaths: string[]): PathObj[] {
return additionalPaths.map((path) => ({
path: path.startsWith('/') ? path : `/${path}`,
}));
}

0 comments on commit f01597d

Please sign in to comment.