Skip to content
Merged
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
12 changes: 12 additions & 0 deletions e2e/create-pages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,18 @@ test.describe(`create-pages`, () => {
});
});

test('static api wildcard passes correct params', async () => {
const res1 = await fetch(
`http://localhost:${port}/api/static-wildcard/a/b`,
);
expect(res1.status).toBe(200);
expect(await res1.json()).toEqual({ params: { slugs: ['a', 'b'] } });

const res2 = await fetch(`http://localhost:${port}/api/static-wildcard/c`);
expect(res2.status).toBe(200);
expect(await res2.json()).toEqual({ params: { slugs: ['c'] } });
});

test('exactPath', async ({ page }) => {
await page.goto(`http://localhost:${port}/exact/[slug]/[...wild]`);
await expect(
Expand Down
10 changes: 10 additions & 0 deletions e2e/fixtures/create-pages/src/waku.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,16 @@ const pages: ReturnType<typeof createPages> = createPages(
},
}),

createApi({
path: '/api/static-wildcard/[...slugs]',
render: 'static',
method: 'GET',
staticPaths: [['a', 'b'], ['c']],
handler: async (_req, ctx) => {
return Response.json({ params: (ctx as any).params });
},
}),

createPage({
render: 'static',
path: '/exact/[slug]/[...wild]',
Expand Down
23 changes: 20 additions & 3 deletions packages/waku/src/router/create-pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ export const createPages = <
render: 'static' | 'dynamic';
pathSpec: PathSpec;
handlers: Partial<Record<Method | 'all', ApiHandler>>;
staticParams?: Record<string, string | string[]>;
}
>();
const staticComponentMap = new Map<string, FunctionComponent<any>>();
Expand Down Expand Up @@ -481,6 +482,12 @@ export const createPages = <
if (staticPath.length !== numSlugs && numWildcards === 0) {
throw new Error('staticPaths does not match with slug pattern');
}
if (staticPath.length === 0 && numWildcards > 0) {
console.warn(
`Empty staticPaths entry is not supported for wildcard routes. ` +
`Route "${page.path}" has a wildcard segment, so each staticPaths entry should contain at least one element.`,
);
}
const { definedPath, pathItems, mapping } = expandStaticPathSpec(
pathSpec,
staticPath,
Expand Down Expand Up @@ -558,7 +565,13 @@ export const createPages = <
if (staticPath.length !== numSlugs && numWildcards === 0) {
throw new Error('staticPaths does not match with slug pattern');
}
const { definedPath, pathItems } = expandStaticPathSpec(
if (staticPath.length === 0 && numWildcards > 0) {
console.warn(
`Empty staticPaths entry is not supported for wildcard routes. ` +
`Route "${options.path}" has a wildcard segment, so each staticPaths entry should contain at least one element.`,
);
}
const { definedPath, pathItems, mapping } = expandStaticPathSpec(
pathSpec,
staticPath,
);
Expand All @@ -570,6 +583,7 @@ export const createPages = <
render: 'static',
pathSpec: pathItems.map((name) => ({ type: 'literal', name })),
handlers: { GET: options.handler },
staticParams: mapping,
});
}
} else {
Expand Down Expand Up @@ -861,7 +875,7 @@ export const createPages = <
});
}
const apiConfigs = Array.from(apiPathMap.values()).map(
({ pathSpec, render, handlers }) => {
({ pathSpec, render, handlers, staticParams }) => {
return {
type: 'api' as const,
path: pathSpec,
Expand All @@ -878,7 +892,10 @@ export const createPages = <
'API method not found: ' + method + 'for path: ' + path,
);
}
return handler(req, apiContext);
return handler(
req,
staticParams ? { params: staticParams } : apiContext,
);
},
};
},
Expand Down
64 changes: 64 additions & 0 deletions packages/waku/tests/create-pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2133,6 +2133,70 @@ describe('createPages api', () => {
expect(text).toEqual('Hello World foo');
expect(res.status).toEqual(200);
});

it('static api with wildcard passes correct params', async () => {
const receivedParams: unknown[] = [];
createPages(async ({ createApi }) => [
createApi({
path: '/test/[...slugs]',
render: 'static',
method: 'GET',
staticPaths: [['a', 'b'], ['c']],
handler: async (_req, ctx) => {
receivedParams.push((ctx as any).params);
return new Response('ok');
},
}),
]);
const { getConfigs } = injectedFunctions();
const configs = Array.from(await getConfigs()) as any[];
const apiConfigs = configs.filter((c: any) => c.type === 'api');
expect(apiConfigs).toHaveLength(2);

// Verify paths are all-literal
expect(apiConfigs[0]!.path).toEqual([
{ type: 'literal', name: 'test' },
{ type: 'literal', name: 'a' },
{ type: 'literal', name: 'b' },
]);
expect(apiConfigs[1]!.path).toEqual([
{ type: 'literal', name: 'test' },
{ type: 'literal', name: 'c' },
]);

// Call handlers and verify params
await apiConfigs[0]!.handler(
new Request('http://localhost:3000/test/a/b'),
{ params: {} },
);
await apiConfigs[1]!.handler(new Request('http://localhost:3000/test/c'), {
params: {},
});
expect(receivedParams).toEqual([{ slugs: ['a', 'b'] }, { slugs: ['c'] }]);
});

it('static api with wildcard and empty path warns', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
createPages(async ({ createApi }) => [
createApi({
path: '/test/[...slugs]',
render: 'static',
method: 'GET',
staticPaths: [[], ['foo']],
handler: async () => {
return new Response('ok');
},
}),
]);
const { getConfigs } = injectedFunctions();
await getConfigs();
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(
'Empty staticPaths entry is not supported for wildcard routes',
),
);
warnSpy.mockRestore();
});
});

describe('createPages - exactPath', () => {
Expand Down
Loading