summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar wulinsheng123 <409187100@qq.com> 2023-03-28 23:13:46 +0800
committerGravatar GitHub <noreply@github.com> 2023-03-28 23:13:46 +0800
commit4bf87c64ff7e9ca49e0f5c27e06bd49faaf60542 (patch)
tree28788d7752a655f4f84990ea18382a8bfae1de6b
parent328c671790725485873fe224bf81d1ee9cfc973e (diff)
downloadastro-4bf87c64ff7e9ca49e0f5c27e06bd49faaf60542.tar.gz
astro-4bf87c64ff7e9ca49e0f5c27e06bd49faaf60542.tar.zst
astro-4bf87c64ff7e9ca49e0f5c27e06bd49faaf60542.zip
Improve error message for endpoint getStaticPaths with undefined value (#6353)
Co-authored-by: bluwy <bjornlu.dev@gmail.com> Co-authored-by: wuls <linsheng.wu@beantechs.com>
-rw-r--r--.changeset/small-knives-sparkle.md5
-rw-r--r--packages/astro/src/core/errors/errors-data.ts21
-rw-r--r--packages/astro/src/core/render/core.ts21
-rw-r--r--packages/astro/test/dynamic-endpoint-collision.js52
-rw-r--r--packages/astro/test/fixtures/dynamic-endpoint-collision/astro.config.mjs7
-rw-r--r--packages/astro/test/fixtures/dynamic-endpoint-collision/package.json8
-rw-r--r--packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts15
-rw-r--r--packages/astro/test/fixtures/unused-slot/src/components/Card.astro1
-rw-r--r--pnpm-lock.yaml6
9 files changed, 135 insertions, 1 deletions
diff --git a/.changeset/small-knives-sparkle.md b/.changeset/small-knives-sparkle.md
new file mode 100644
index 000000000..e257d5e97
--- /dev/null
+++ b/.changeset/small-knives-sparkle.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Throw better error when a dynamic endpoint without additional extensions is prerendered with `undefined` params.
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 5e0b9a34d..760cade33 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -530,6 +530,27 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati
)} are supported for optimization.`,
hint: "If you do not need optimization, using an `img` tag directly instead of the `Image` component might be what you're looking for.",
},
+ /**
+ * @docs
+ * @see
+ * - [`getStaticPaths()`](https://docs.astro.build/en/reference/api-reference/#getstaticpaths)
+ * - [`params`](https://docs.astro.build/en/reference/api-reference/#params)
+ * @description
+ * The endpoint is prerendered with an `undefined` param so the generated path will collide with another route.
+ *
+ * If you cannot prevent passing `undefined`, then an additional extension can be added to the endpoint file name to generate the file with a different name. For example, renaming `pages/api/[slug].ts` to `pages/api/[slug].json.ts`.
+ */
+ PrerenderDynamicEndpointPathCollide: {
+ title: 'Prerendered dynamic endpoint has path collision.',
+ code: 3026,
+ message: (pathname: string) =>
+ `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. ` +
+ `Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, ` +
+ `or add an additional extension to the endpoint's filename.`,
+ hint: (filename: string) =>
+ `Rename \`${filename}\` to \`${filename.replace(/\.(js|ts)/, (m) => `.json` + m)}\``,
+ },
+
// No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users.
// Vite Errors - 4xxx
/**
diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts
index f3b8e2866..d072c85b4 100644
--- a/packages/astro/src/core/render/core.ts
+++ b/packages/astro/src/core/render/core.ts
@@ -35,6 +35,27 @@ export async function getParamsAndProps(
const paramsMatch = route.pattern.exec(pathname);
if (paramsMatch) {
params = getParams(route.params)(paramsMatch);
+
+ // If we have an endpoint at `src/pages/api/[slug].ts` that's prerendered, and the `slug`
+ // is `undefined`, throw an error as we can't generate the `/api` file and `/api` directory
+ // at the same time. Using something like `[slug].json.ts` instead will work.
+ if (route.type === 'endpoint' && mod.getStaticPaths) {
+ const lastSegment = route.segments[route.segments.length - 1];
+ const paramValues = Object.values(params);
+ const lastParam = paramValues[paramValues.length - 1];
+ // Check last segment is solely `[slug]` or `[...slug]` case (dynamic). Make sure it's not
+ // `foo[slug].js` by checking segment length === 1. Also check here if that param is undefined.
+ if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === undefined) {
+ throw new AstroError({
+ ...AstroErrorData.PrerenderDynamicEndpointPathCollide,
+ message: AstroErrorData.PrerenderDynamicEndpointPathCollide.message(route.route),
+ hint: AstroErrorData.PrerenderDynamicEndpointPathCollide.hint(route.component),
+ location: {
+ file: route.component,
+ },
+ });
+ }
+ }
}
}
let routeCacheEntry = routeCache.get(route);
diff --git a/packages/astro/test/dynamic-endpoint-collision.js b/packages/astro/test/dynamic-endpoint-collision.js
new file mode 100644
index 000000000..b8620568a
--- /dev/null
+++ b/packages/astro/test/dynamic-endpoint-collision.js
@@ -0,0 +1,52 @@
+import { expect } from 'chai';
+import { load as cheerioLoad } from 'cheerio';
+import { loadFixture } from './test-utils.js';
+
+describe('Dynamic endpoint collision', () => {
+ describe('build', () => {
+ let fixture;
+ let errorMsg;
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/dynamic-endpoint-collision/',
+ });
+ try {
+ await fixture.build();
+ } catch (error) {
+ errorMsg = error;
+ }
+ });
+
+ it('throw error when dynamic endpoint has path collision', async () => {
+ expect(errorMsg.errorCode).to.eq(3026);
+ });
+ });
+
+ describe('dev', () => {
+ let fixture;
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/dynamic-endpoint-collision/',
+ });
+
+ devServer = await fixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
+ });
+
+ it('throw error when dynamic endpoint has path collision', async () => {
+ const html = await fixture.fetch('/api/catch').then((res) => res.text());
+ const $ = cheerioLoad(html);
+ expect($('title').text()).to.equal('PrerenderDynamicEndpointPathCollide');
+ });
+
+ it("don't throw error when dynamic endpoint doesn't load the colliding path", async () => {
+ const res = await fixture.fetch('/api/catch/one').then((r) => r.text());
+ expect(res).to.equal('{"slug":"one"}');
+ });
+ });
+});
diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/astro.config.mjs b/packages/astro/test/fixtures/dynamic-endpoint-collision/astro.config.mjs
new file mode 100644
index 000000000..0c1afccde
--- /dev/null
+++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/astro.config.mjs
@@ -0,0 +1,7 @@
+import { defineConfig } from 'astro/config';
+
+
+// https://astro.build/config
+export default defineConfig({
+
+});
diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/package.json b/packages/astro/test/fixtures/dynamic-endpoint-collision/package.json
new file mode 100644
index 000000000..7a40aff87
--- /dev/null
+++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/dynamic-endpoint-collision",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts
new file mode 100644
index 000000000..8f64c2401
--- /dev/null
+++ b/packages/astro/test/fixtures/dynamic-endpoint-collision/src/pages/api/catch/[...slug].ts
@@ -0,0 +1,15 @@
+import type { APIRoute } from "astro";
+
+const slugs = ["one", undefined];
+
+export const get: APIRoute = ({ params }) => {
+ return {
+ body: JSON.stringify({
+ slug: params.slug || "index",
+ }),
+ };
+};
+
+export function getStaticPaths() {
+ return slugs.map((u) => ({ params: { slug: u } }));
+}
diff --git a/packages/astro/test/fixtures/unused-slot/src/components/Card.astro b/packages/astro/test/fixtures/unused-slot/src/components/Card.astro
index 61fafb04c..c77e31672 100644
--- a/packages/astro/test/fixtures/unused-slot/src/components/Card.astro
+++ b/packages/astro/test/fixtures/unused-slot/src/components/Card.astro
@@ -5,7 +5,6 @@ export interface Props {
href: string,
}
const {href, title, body} = Astro.props;
-debugger;
---
<li class="link-card">
<a href={href}>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b8547b9a0..6e52dbc5b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1971,6 +1971,12 @@ importers:
dependencies:
astro: link:../../..
+ packages/astro/test/fixtures/dynamic-endpoint-collision:
+ specifiers:
+ astro: workspace:*
+ dependencies:
+ astro: link:../../..
+
packages/astro/test/fixtures/dynamic-route-build-file:
specifiers:
astro: workspace:*