Skip to content

Commit ffba5d7

Browse files
authoredSep 13, 2024··
Fix getStaticPaths regression (#11993)
* Revert "Remove dependency on path-to-regexp (#11983)" This reverts commit 633eeaa. * Add test for regression * Add a changeset * Pin path-to-regexp
1 parent 2d016d4 commit ffba5d7

File tree

6 files changed

+99
-31
lines changed

6 files changed

+99
-31
lines changed
 

‎.changeset/happy-ways-sin.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fix getStaticPaths regression
6+
7+
This reverts a previous change meant to remove a dependency, to fix a regression with multiple nested spread routes.

‎packages/astro/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
"ora": "^8.1.0",
169169
"p-limit": "^6.1.0",
170170
"p-queue": "^8.0.1",
171+
"path-to-regexp": "6.2.2",
171172
"preferred-pm": "^4.0.0",
172173
"prompts": "^2.4.2",
173174
"rehype": "^13.0.1",
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { AstroConfig, RoutePart } from '../../../@types/astro.js';
22

3+
import { compile } from 'path-to-regexp';
4+
35
/**
46
* Sanitizes the parameters object by normalizing string values and replacing certain characters with their URL-encoded equivalents.
57
* @param {Record<string, string | number | undefined>} params - The parameters object to be sanitized.
@@ -22,40 +24,45 @@ export function getRouteGenerator(
2224
segments: RoutePart[][],
2325
addTrailingSlash: AstroConfig['trailingSlash'],
2426
) {
27+
const template = segments
28+
.map((segment) => {
29+
return (
30+
'/' +
31+
segment
32+
.map((part) => {
33+
if (part.spread) {
34+
return `:${part.content.slice(3)}(.*)?`;
35+
} else if (part.dynamic) {
36+
return `:${part.content}`;
37+
} else {
38+
return part.content
39+
.normalize()
40+
.replace(/\?/g, '%3F')
41+
.replace(/#/g, '%23')
42+
.replace(/%5B/g, '[')
43+
.replace(/%5D/g, ']')
44+
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
45+
}
46+
})
47+
.join('')
48+
);
49+
})
50+
.join('');
51+
52+
// Unless trailingSlash config is set to 'always', don't automatically append it.
53+
let trailing: '/' | '' = '';
54+
if (addTrailingSlash === 'always' && segments.length) {
55+
trailing = '/';
56+
}
57+
const toPath = compile(template + trailing);
2558
return (params: Record<string, string | number | undefined>): string => {
2659
const sanitizedParams = sanitizeParams(params);
60+
const path = toPath(sanitizedParams);
2761

28-
// Unless trailingSlash config is set to 'always', don't automatically append it.
29-
let trailing: '/' | '' = '';
30-
if (addTrailingSlash === 'always' && segments.length) {
31-
trailing = '/';
32-
}
33-
34-
const path =
35-
segments
36-
.map((segment) => {
37-
return (
38-
'/' +
39-
segment
40-
.map((part) => {
41-
if (part.spread) {
42-
return `${sanitizedParams[part.content.slice(3)] || ''}`;
43-
} else if (part.dynamic) {
44-
return `${sanitizedParams[part.content] || ''}`;
45-
} else {
46-
return part.content
47-
.normalize()
48-
.replace(/\?/g, '%3F')
49-
.replace(/#/g, '%23')
50-
.replace(/%5B/g, '[')
51-
.replace(/%5D/g, ']');
52-
}
53-
})
54-
.join('')
55-
);
56-
})
57-
.join('') + trailing;
58-
62+
// When generating an index from a rest parameter route, `path-to-regexp` will return an
63+
// empty string instead "/". This causes an inconsistency with static indexes that may result
64+
// in the incorrect routes being rendered.
65+
// To fix this, we return "/" when the path is empty.
5966
return path || '/';
6067
};
6168
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
export async function getStaticPaths({ paginate }) {
3+
4+
const paths = [
5+
{
6+
slug: 'news/july-2024',
7+
items: ['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6'],
8+
contentType: 'news',
9+
monthYear: 'july-2024',
10+
}
11+
];
12+
13+
return paths.flatMap((path) => {
14+
return paginate(path.items, {
15+
params: { slug: path.slug },
16+
props: {
17+
contentType: path.contentType,
18+
monthYear: path.monthYear,
19+
},
20+
pageSize: 2,
21+
});
22+
});
23+
}
24+
25+
const { slug, page } = Astro.params;
26+
---
27+
28+
<html>
29+
<head>
30+
<title>Testing</title>
31+
</head>
32+
<body>
33+
<h1>Testing</h1>
34+
<p id="slug">{slug}</p>
35+
<p id="page">{page}</p>
36+
</body>
37+
</html>

‎packages/astro/test/get-static-paths-pages.test.js

+8
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ describe('getStaticPaths with trailingSlash: ignore', () => {
2525
let $ = cheerio.load(html);
2626
assert.equal($('h1').text(), 'Page 2');
2727
});
28+
29+
// for regression: https://github.com/withastro/astro/issues/11990
30+
it('nested static paths generate', async () => {
31+
let html = await fixture.readFile('/archive/news/july-2024/2/index.html');
32+
let $ = cheerio.load(html);
33+
assert.equal($('#slug').text(), 'news');
34+
assert.equal($('#page').text(), 'july-2024/2');
35+
})
2836
});

‎pnpm-lock.yaml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.