Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-sitemap-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/nimbus-docs": patch
---

Avoid duplicate root sitemap entries in directory builds deployed under a base path without a trailing slash.
15 changes: 15 additions & 0 deletions packages/nimbus-docs/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,21 @@ export function nimbus(
),
}),
});
const configureSitemap = sitemapIntegration.hooks["astro:config:done"];
sitemapIntegration.hooks["astro:config:done"] = async (options) => {
const { config: astroConfig } = options;
// Sitemap joins the empty home-page path to base verbatim, but
// adds a slash for the same root's route in directory builds.
const base =
astroConfig.build.format === "directory" &&
astroConfig.trailingSlash !== "never"
? `${astroConfig.base.replace(/\/$/, "")}/`
: astroConfig.base;
await configureSitemap?.({
...options,
config: { ...astroConfig, base },
});
};
integrationsToAdd.push(sitemapIntegration);
}

Expand Down
79 changes: 79 additions & 0 deletions packages/nimbus-docs/test/integration-sitemap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { test } from "node:test";
import type { AstroIntegration } from "astro";
import { nimbus } from "../src/integration.js";

for (const base of ["/", "/my-project", "/my-project/"]) {
test(`sitemap emits one root entry with base ${base}`, async () => {
const root = await mkdtemp(path.join(tmpdir(), "nimbus-sitemap-"));
const directory = pathToFileURL(root + path.sep);
const logger = {
warn() {},
info() {},
error(message: string) {
throw new Error(message);
},
};
const config = {
root: directory,
srcDir: new URL("src/", directory),
cacheDir: new URL(".cache/", directory),
site: "https://example.test",
base,
trailingSlash: "ignore",
build: { format: "directory" },
};
let integrations: AstroIntegration[] = [];
const integration = nimbus(
{ site: config.site, title: "Test" },
{
validateMdx: false,
admonitions: false,
markdown: { processor: {} as never },
},
);
await integration.hooks["astro:config:setup"]!({
config,
logger,
updateConfig(update: { integrations: AstroIntegration[] }) {
integrations = update.integrations;
return {};
},
} as never);
const sitemap = integrations.find(
(item) => item.name === "@astrojs/sitemap",
)!;
await sitemap.hooks["astro:config:done"]!({ config } as never);
assert.equal(config.base, base, "the app's base must remain unchanged");
await sitemap.hooks["astro:routes:resolved"]!({
routes: [
{
type: "page",
pathname: "/",
generate: () => "/",
},
],
} as never);
await sitemap.hooks["astro:build:done"]!({
dir: directory,
pages: [{ pathname: "" }, { pathname: "guide/" }],
logger,
} as never);
const xml = await readFile(new URL("sitemap-0.xml", directory), "utf8");
const urls = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map(
(match) => match[1],
);
const prefix = base.replace(/\/$/, "");
assert.deepEqual(
urls.sort(),
[
`https://example.test${prefix}/`,
`https://example.test${prefix}/guide/`,
].sort(),
);
});
}