Skip to content

Commit 710a4bf

Browse files
alan-agius4thePunderWoman
authored andcommitted
fix(docs-infra): remove trailing slash from sitemap URLs
Fixes an issue where a trailing slash was sometimes added to the end of URLs in the sitemap, causing unnecessary redirects. Example of problematic URL: ```xml <loc>https://angular.dev/</loc> ```
1 parent 95e8c2f commit 710a4bf

2 files changed

Lines changed: 76 additions & 42 deletions

File tree

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,53 @@
11
import {Deployment} from './deployments.mjs';
2-
import {join} from 'path';
3-
import {readFileSync, writeFileSync} from 'fs';
4-
5-
export async function generateSitemap(deployment: Deployment, distDir: string) {
6-
const servingUrlWithoutEndingSlash = deployment.servingUrl.endsWith('/')
7-
? deployment.servingUrl.slice(0, -1)
8-
: deployment.servingUrl;
2+
import {join} from 'node:path';
3+
import {readFile, writeFile} from 'node:fs/promises';
94

5+
export async function generateSitemap(deployment: Deployment, distDir: string): Promise<void> {
106
/** Timestamp string used to of the last file update. */
117
const lastModifiedTimestamp = new Date().toISOString();
128
/** An object containing all of the routes available within the application. */
13-
const routes = JSON.parse(readFileSync(join(distDir, 'prerendered-routes.json'), 'utf-8'));
9+
const {routes} = JSON.parse(await readFile(join(distDir, 'prerendered-routes.json'), 'utf-8'));
10+
const routePaths = Object.keys(routes);
11+
1412
/** The generated sitemap string. */
1513
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
1614
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
17-
${Object.keys(routes.routes)
18-
.map((route) => {
19-
const routeWithoutLeadingSlash = route.startsWith('/') ? route.slice(1) : route;
20-
return `
21-
<url>
22-
<loc>${servingUrlWithoutEndingSlash}/${routeWithoutLeadingSlash}</loc>
15+
${routePaths
16+
.map(
17+
(route) => `<url>
18+
<loc>${joinUrlParts(deployment.servingUrl, route)}</loc>
2319
<lastmod>${lastModifiedTimestamp}</lastmod>
2420
<changefreq>daily</changefreq>
2521
<priority>1.0</priority>
26-
</url>
27-
`;
28-
})
22+
</url>`,
23+
)
2924
.join('')}
3025
</urlset>`;
31-
writeFileSync(join(distDir, 'browser', 'sitemap.xml'), sitemap, 'utf-8');
32-
console.log(`Generated sitemap with ${Object.keys(routes.routes).length} entries.`);
26+
27+
await writeFile(join(distDir, 'browser', 'sitemap.xml'), sitemap, 'utf-8');
28+
29+
console.log(`Generated sitemap with ${routePaths.length} entries.`);
30+
}
31+
32+
function joinUrlParts(...parts: string[]): string {
33+
const normalizeParts: string[] = [];
34+
for (const part of parts) {
35+
if (part === '') {
36+
// Skip any empty parts
37+
continue;
38+
}
39+
40+
let normalizedPart = part;
41+
if (part[0] === '/') {
42+
normalizedPart = normalizedPart.slice(1);
43+
}
44+
if (part.at(-1) === '/') {
45+
normalizedPart = normalizedPart.slice(0, -1);
46+
}
47+
if (normalizedPart !== '') {
48+
normalizeParts.push(normalizedPart);
49+
}
50+
}
51+
52+
return normalizeParts.join('/');
3353
}

.github/actions/deploy-docs-site/main.js

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,7 @@ var require_summary = __commonJS({
15001500
exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0;
15011501
var os_1 = __require("os");
15021502
var fs_1 = __require("fs");
1503-
var { access, appendFile, writeFile: writeFile3 } = fs_1.promises;
1503+
var { access, appendFile, writeFile: writeFile4 } = fs_1.promises;
15041504
exports.SUMMARY_ENV_VAR = "GITHUB_STEP_SUMMARY";
15051505
exports.SUMMARY_DOCS_URL = "https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary";
15061506
var Summary = class {
@@ -1558,7 +1558,7 @@ var require_summary = __commonJS({
15581558
return __awaiter(this, void 0, void 0, function* () {
15591559
const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite);
15601560
const filePath = yield this.filePath();
1561-
const writeFunc = overwrite ? writeFile3 : appendFile;
1561+
const writeFunc = overwrite ? writeFile4 : appendFile;
15621562
yield writeFunc(filePath, this._buffer, { encoding: "utf8" });
15631563
return this.emptyBuffer();
15641564
});
@@ -8549,7 +8549,7 @@ var require_lockfile = __commonJS({
85498549
}
85508550
const file = _ref22;
85518551
if (yield exists(file)) {
8552-
return readFile2(file);
8552+
return readFile3(file);
85538553
}
85548554
}
85558555
return null;
@@ -8568,7 +8568,7 @@ var require_lockfile = __commonJS({
85688568
})();
85698569
let readJsonAndFile = exports2.readJsonAndFile = (() => {
85708570
var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) {
8571-
const file = yield readFile2(loc);
8571+
const file = yield readFile3(loc);
85728572
try {
85738573
return {
85748574
object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))),
@@ -8712,7 +8712,7 @@ var require_lockfile = __commonJS({
87128712
if (eol !== "\n") {
87138713
data = data.replace(/\n/g, eol);
87148714
}
8715-
yield writeFile3(path, data);
8715+
yield writeFile4(path, data);
87168716
});
87178717
return function writeFilePreservingEol2(_x30, _x31) {
87188718
return _ref31.apply(this, arguments);
@@ -8724,7 +8724,7 @@ var require_lockfile = __commonJS({
87248724
const file = (_path || _load_path()).default.join(dir, filename);
87258725
const fileLink = (_path || _load_path()).default.join(dir, filename + "-link");
87268726
try {
8727-
yield writeFile3(file, "test");
8727+
yield writeFile4(file, "test");
87288728
yield link(file, fileLink);
87298729
} catch (err) {
87308730
return false;
@@ -8814,7 +8814,7 @@ var require_lockfile = __commonJS({
88148814
};
88158815
})();
88168816
exports2.copy = copy;
8817-
exports2.readFile = readFile2;
8817+
exports2.readFile = readFile3;
88188818
exports2.readFileRaw = readFileRaw;
88198819
exports2.normalizeOS = normalizeOS;
88208820
var _fs;
@@ -8879,7 +8879,7 @@ var require_lockfile = __commonJS({
88798879
const lockQueue = exports2.lockQueue = new (_blockingQueue || _load_blockingQueue()).default("fs lock");
88808880
const readFileBuffer = exports2.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile);
88818881
const open = exports2.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open);
8882-
const writeFile3 = exports2.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile);
8882+
const writeFile4 = exports2.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile);
88838883
const readlink = exports2.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink);
88848884
const realpath = exports2.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath);
88858885
const readdir = exports2.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir);
@@ -8913,7 +8913,7 @@ var require_lockfile = __commonJS({
89138913
});
89148914
});
89158915
}
8916-
function readFile2(loc) {
8916+
function readFile3(loc) {
89178917
return _readFile(loc, "utf8").then(normalizeOS);
89188918
}
89198919
function readFileRaw(loc) {
@@ -35356,28 +35356,42 @@ async function getDeployments() {
3535635356
}
3535735357

3535835358
// .github/actions/deploy-docs-site/lib/sitemap.mjs
35359-
import { join as join4 } from "path";
35360-
import { readFileSync as readFileSync4, writeFileSync } from "fs";
35359+
import { join as join4 } from "node:path";
35360+
import { readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
3536135361
async function generateSitemap(deployment, distDir) {
35362-
const servingUrlWithoutEndingSlash = deployment.servingUrl.endsWith("/") ? deployment.servingUrl.slice(0, -1) : deployment.servingUrl;
3536335362
const lastModifiedTimestamp = (/* @__PURE__ */ new Date()).toISOString();
35364-
const routes = JSON.parse(readFileSync4(join4(distDir, "prerendered-routes.json"), "utf-8"));
35363+
const { routes } = JSON.parse(await readFile2(join4(distDir, "prerendered-routes.json"), "utf-8"));
35364+
const routePaths = Object.keys(routes);
3536535365
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
3536635366
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
35367-
${Object.keys(routes.routes).map((route) => {
35368-
const routeWithoutLeadingSlash = route.startsWith("/") ? route.slice(1) : route;
35369-
return `
35370-
<url>
35371-
<loc>${servingUrlWithoutEndingSlash}/${routeWithoutLeadingSlash}</loc>
35367+
${routePaths.map((route) => `<url>
35368+
<loc>${joinUrlParts(deployment.servingUrl, route)}</loc>
3537235369
<lastmod>${lastModifiedTimestamp}</lastmod>
3537335370
<changefreq>daily</changefreq>
3537435371
<priority>1.0</priority>
35375-
</url>
35376-
`;
35377-
}).join("")}
35372+
</url>`).join("")}
3537835373
</urlset>`;
35379-
writeFileSync(join4(distDir, "browser", "sitemap.xml"), sitemap, "utf-8");
35380-
console.log(`Generated sitemap with ${Object.keys(routes.routes).length} entries.`);
35374+
await writeFile3(join4(distDir, "browser", "sitemap.xml"), sitemap, "utf-8");
35375+
console.log(`Generated sitemap with ${routePaths.length} entries.`);
35376+
}
35377+
function joinUrlParts(...parts) {
35378+
const normalizeParts = [];
35379+
for (const part of parts) {
35380+
if (part === "") {
35381+
continue;
35382+
}
35383+
let normalizedPart = part;
35384+
if (part[0] === "/") {
35385+
normalizedPart = normalizedPart.slice(1);
35386+
}
35387+
if (part.at(-1) === "/") {
35388+
normalizedPart = normalizedPart.slice(0, -1);
35389+
}
35390+
if (normalizedPart !== "") {
35391+
normalizeParts.push(normalizedPart);
35392+
}
35393+
}
35394+
return normalizeParts.join("/");
3538135395
}
3538235396

3538335397
// .github/actions/deploy-docs-site/lib/main.mts

0 commit comments

Comments
 (0)