Skip to content

Commit 2f05f5d

Browse files
authored
Include trailingSlash in astro:build:done hook (#4553)
1 parent 69b640b commit 2f05f5d

File tree

10 files changed

+192
-3
lines changed

10 files changed

+192
-3
lines changed

.changeset/moody-cars-return.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Include trailingSlash in astro:build:done hook
6+
7+
This change ensures that the `pages` provided in the `astro:build:done` hook conform to the `trailingSlash` and `build.format` configs.

packages/astro/src/core/build/generate.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,26 @@ interface GeneratePathOptions {
240240
renderers: SSRLoadedRenderer[];
241241
}
242242

243+
function shouldAppendForwardSlash(trailingSlash: AstroConfig['trailingSlash'], buildFormat: AstroConfig['build']['format']): boolean {
244+
switch(trailingSlash) {
245+
case 'always': return true;
246+
case 'never': return false;
247+
case 'ignore': {
248+
switch(buildFormat) {
249+
case 'directory': return true;
250+
case 'file': return false;
251+
}
252+
}
253+
}
254+
}
255+
243256
function addPageName(pathname: string, opts: StaticBuildOptions): void {
244-
opts.pageNames.push(pathname.replace(/^\//, ''));
257+
const trailingSlash = opts.astroConfig.trailingSlash;
258+
const buildFormat = opts.astroConfig.build.format;
259+
const pageName = shouldAppendForwardSlash(trailingSlash, buildFormat) ?
260+
pathname.replace(/\/?$/, '/').replace(/^\//, '') :
261+
pathname.replace(/^\//, '')
262+
opts.pageNames.push(pageName);
245263
}
246264

247265
function getUrlForPath(

packages/integrations/sitemap/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
},
3939
"devDependencies": {
4040
"astro": "workspace:*",
41-
"astro-scripts": "workspace:*"
41+
"astro-scripts": "workspace:*",
42+
"xml2js": "0.4.23"
4243
}
4344
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'astro/config';
2+
import sitemap from '@astrojs/sitemap';
3+
4+
export default defineConfig({
5+
integrations: [sitemap()],
6+
site: 'http://example.com'
7+
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@test/sitemap-trailing-slash",
3+
"version": "0.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"astro": "workspace:*",
7+
"@astrojs/sitemap": "workspace:*"
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<html>
2+
<head>
3+
<title>One</title>
4+
</head>
5+
<body>
6+
<h1>One</h1>
7+
</body>
8+
</html>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<html>
2+
<head>
3+
<title>Two</title>
4+
</head>
5+
<body>
6+
<h1>Two</h1>
7+
</body>
8+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { loadFixture as baseLoadFixture } from '../../../astro/test/test-utils.js';
2+
import * as xml2js from 'xml2js';
3+
4+
/**
5+
* @typedef {import('../../../astro/test/test-utils').Fixture} Fixture
6+
*/
7+
8+
export function loadFixture(inlineConfig) {
9+
if (!inlineConfig || !inlineConfig.root)
10+
throw new Error("Must provide { root: './fixtures/...' }");
11+
12+
// resolve the relative root (i.e. "./fixtures/tailwindcss") to a full filepath
13+
// without this, the main `loadFixture` helper will resolve relative to `packages/astro/test`
14+
return baseLoadFixture({
15+
...inlineConfig,
16+
root: new URL(inlineConfig.root, import.meta.url).toString(),
17+
});
18+
}
19+
20+
export function readXML(fileOrPromise) {
21+
const parseString = xml2js.parseString;
22+
return Promise.resolve(fileOrPromise).then(xml => {
23+
return new Promise((resolve, reject) => {
24+
parseString(xml, function (err, result) {
25+
if(err) return reject(err);
26+
resolve(result);
27+
});
28+
})
29+
});
30+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { loadFixture, readXML } from './test-utils.js';
2+
import { expect } from 'chai';
3+
4+
describe('Trailing slash', () => {
5+
/** @type {import('./test-utils').Fixture} */
6+
let fixture;
7+
8+
describe('trailingSlash: ignore', () => {
9+
describe('build.format: directory', () => {
10+
before(async () => {
11+
fixture = await loadFixture({
12+
root: './fixtures/trailing-slash/',
13+
trailingSlash: 'ignore',
14+
build: {
15+
format: 'directory'
16+
}
17+
});
18+
await fixture.build();
19+
});
20+
21+
it('URLs end with trailing slash', async () => {
22+
const data = await readXML(fixture.readFile('/sitemap-0.xml'));
23+
const urls = data.urlset.url;
24+
expect(urls[0].loc[0]).to.equal('http://example.com/one/');
25+
});
26+
});
27+
28+
describe('build.format: file', () => {
29+
before(async () => {
30+
fixture = await loadFixture({
31+
root: './fixtures/trailing-slash/',
32+
trailingSlash: 'ignore',
33+
build: {
34+
format: 'file'
35+
}
36+
});
37+
await fixture.build();
38+
});
39+
40+
it('URLs do not end with trailing slash', async () => {
41+
const data = await readXML(fixture.readFile('/sitemap-0.xml'));
42+
const urls = data.urlset.url;
43+
expect(urls[0].loc[0]).to.equal('http://example.com/one');
44+
});
45+
});
46+
});
47+
48+
describe('trailingSlash: never', () => {
49+
before(async () => {
50+
fixture = await loadFixture({
51+
root: './fixtures/trailing-slash/',
52+
trailingSlash: 'never',
53+
});
54+
await fixture.build();
55+
});
56+
57+
it('URLs do no end with trailing slash', async () => {
58+
const data = await readXML(fixture.readFile('/sitemap-0.xml'));
59+
const urls = data.urlset.url;
60+
expect(urls[0].loc[0]).to.equal('http://example.com/one');
61+
});
62+
});
63+
64+
describe('trailingSlash: always', () => {
65+
before(async () => {
66+
fixture = await loadFixture({
67+
root: './fixtures/trailing-slash/',
68+
trailingSlash: 'always',
69+
});
70+
await fixture.build();
71+
});
72+
73+
it('URLs end with trailing slash', async () => {
74+
const data = await readXML(fixture.readFile('/sitemap-0.xml'));
75+
const urls = data.urlset.url;
76+
expect(urls[0].loc[0]).to.equal('http://example.com/one/');
77+
});
78+
});
79+
});

pnpm-lock.yaml

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)