Skip to content

Commit

Permalink
fix: sitemap
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-lefebvre committed Apr 30, 2024
1 parent bc8fcd1 commit a23b965
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/cyan-ghosts-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrolicious/i18n": patch
---

Fixes trailing slash handling in sitemap
5 changes: 5 additions & 0 deletions .changeset/fast-dolphins-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrolicious/i18n": patch
---

Fixes duplicated urls with complex routes
43 changes: 19 additions & 24 deletions package/src/sitemap/generate-sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import type { LinkItem, SitemapItemLoose } from "sitemap";
import type { Route } from "./integration.js";
import type { SitemapOptions } from "./options.js";
import { createImpossibleError } from "./utils.js";

const normalizeDynamicParams = (
_params: Route["sitemapOptions"][number]["dynamicParams"],
) => {
if (!_params) {
return [];
}

if (Array.isArray(_params)) {
return _params;
}

return Object.entries(_params).map(([locale, params]) => ({
locale,
params,
}));
};
import {
createImpossibleError,
handleTrailingSlash,
normalizeDynamicParams,
} from "./utils.js";
import type { AstroConfig } from "astro";

type NoUndefinedField<T> = {
[P in keyof T]-?: NonNullable<T[P]>;
Expand All @@ -29,6 +17,7 @@ export function generateSitemap(
routes: Array<Route>,
_finalSiteUrl: string,
opts: SitemapOptions,
config: AstroConfig,
) {
const { changefreq, priority, lastmod: lastmodSrc } = opts;
const lastmod = lastmodSrc?.toISOString();
Expand Down Expand Up @@ -57,14 +46,17 @@ export function generateSitemap(
for (const equivalentRoute of equivalentRoutes) {
links.push({
lang: equivalentRoute.route.locale,
url: `${new URL(page).origin}${
equivalentRoute.route.injectedRoute.pattern
}`,
url: handleTrailingSlash(
`${new URL(page).origin}${
equivalentRoute.route.injectedRoute.pattern
}`,
config,
),
});
}

return [...links].sort((a, b) =>
a.url.localeCompare(b.url, "en", { numeric: true }),
a.lang.localeCompare(b.lang, "en", { numeric: true }),
);
}

Expand Down Expand Up @@ -97,14 +89,17 @@ export function generateSitemap(

newPage = newPage.replace(`[${key}]`, value);
}
newPage = `${new URL(page).origin}${newPage}`;
newPage = handleTrailingSlash(
`${new URL(page).origin}${newPage}`,
config,
);
links.push({
lang: equivalentRoute.route.locale,
url: newPage,
});
}
return [...links].sort((a, b) =>
a.url.localeCompare(b.url, "en", { numeric: true }),
a.lang.localeCompare(b.lang, "en", { numeric: true }),
);
};

Expand Down
56 changes: 36 additions & 20 deletions package/src/sitemap/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { AstroError } from "astro/errors";
import { z } from "astro/zod";
import { simpleSitemapAndIndex } from "sitemap";
import { withTrailingSlash, withoutTrailingSlash } from "ufo";
import { withoutTrailingSlash } from "ufo";
import { normalizePath } from "vite";
import type { Route as InternalRoute } from "../types.js";
import { generateSitemap } from "./generate-sitemap.js";
Expand All @@ -20,7 +20,9 @@ import {
createImpossibleError,
formatConfigErrorMessage,
getPathnameFromRouteData,
handleTrailingSlash,
isStatusCodePage,
normalizeDynamicParams,
} from "./utils.js";

const OUTFILE = "sitemap-index.xml";
Expand Down Expand Up @@ -105,6 +107,23 @@ export const integration = defineIntegration({
);
}
route.sitemapOptions.push(response.data);
if (route.route) {
const { locale, injectedRoute } = route.route;
const params = normalizeDynamicParams(
response.data.dynamicParams,
)?.find((e) => e.locale === locale);
if (params) {
let page = injectedRoute.pattern;
for (const [key, value] of Object.entries(
params.params,
)) {
if (value) {
page = page.replace(`[${key}]`, value);
}
}
route.pages.push(page);
}
}
}
}
},
Expand All @@ -113,6 +132,12 @@ export const integration = defineIntegration({
"astro:build:done": async (params) => {
const { logger } = params;

for (const route of initialRoutes) {
if (route.pages.length === 0 && route.route) {
route.pages.push(route.route.injectedRoute.pattern);
}
}

for (const r of initialRoutes.filter((e) => !e.routeData)) {
const routeData = params.routes.find(
(e) =>
Expand Down Expand Up @@ -202,16 +227,7 @@ export const integration = defineIntegration({

const newUrl = new URL(fullPath, finalSiteUrl).href;

if (config.trailingSlash === "never") {
urls.push(newUrl);
} else if (
config.build.format === "directory" &&
!newUrl.endsWith("/")
) {
urls.push(`${newUrl}/`);
} else {
urls.push(newUrl);
}
urls.push(handleTrailingSlash(newUrl, config));
}

return urls;
Expand Down Expand Up @@ -242,21 +258,21 @@ export const integration = defineIntegration({
}

for (const route of _routes.filter((e) => e.include)) {
for (const rawPage of pageUrls) {
const page = normalizePath(
`/${relative(config.base, new URL(rawPage).pathname)}`,
);
// biome-ignore lint/style/noNonNullAssertion: <explanation>
if (route.routeData!.pattern.test(withTrailingSlash(page))) {
route.pages.push(rawPage);
}
}
route.pages = route.pages.map((page) =>
page.startsWith("/")
? handleTrailingSlash(
new URL(page, finalSiteUrl).href,
config,
)
: page,
);
}

const urlData = generateSitemap(
_routes.filter((e) => e.include),
finalSiteUrl.href,
options,
config,
);

const destDir = fileURLToPath(params.dir);
Expand Down
30 changes: 29 additions & 1 deletion package/src/sitemap/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RouteData } from "astro";
import type { AstroConfig, RouteData } from "astro";
import { AstroError } from "astro/errors";
import type { ZodError } from "astro/zod";
import type { Route } from "./integration.js";

const STATUS_CODE_PAGES = new Set(["404", "500"]);

Expand Down Expand Up @@ -37,3 +38,30 @@ export const getPathnameFromRouteData = ({ segments }: RouteData) => {

return `/${pathname}`;
};

export const normalizeDynamicParams = (
_params: Route["sitemapOptions"][number]["dynamicParams"],
) => {
if (!_params) {
return [];
}

if (Array.isArray(_params)) {
return _params;
}

return Object.entries(_params).map(([locale, params]) => ({
locale,
params,
}));
};

export const handleTrailingSlash = (url: string, config: AstroConfig) => {
if (config.trailingSlash === "never") {
return url;
}
if (config.build.format === "directory" && !url.endsWith("/")) {
return `${url}/`;
}
return url;
};

0 comments on commit a23b965

Please sign in to comment.