diff --git a/.changeset/heavy-news-argue.md b/.changeset/heavy-news-argue.md new file mode 100644 index 000000000000..31dc77f73d81 --- /dev/null +++ b/.changeset/heavy-news-argue.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': patch +--- + +fix: construct correct pathname for isr from route with nested params diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js index e2f506c22859..5a6c2f64e2fd 100644 --- a/packages/adapter-vercel/files/serverless.js +++ b/packages/adapter-vercel/files/serverless.js @@ -22,10 +22,12 @@ export default async (req, res) => { const [path, search] = req.url.split('?'); const params = new URLSearchParams(search); - const pathname = params.get('__pathname'); + let pathname = params.get('__pathname'); if (pathname) { params.delete('__pathname'); + // Optional routes' pathname replacements look like `/foo/$1/bar` which means we could end up with an url like /foo//bar + pathname = pathname.replace(/\/+/g, '/'); req.url = `${pathname}${path.endsWith(DATA_SUFFIX) ? DATA_SUFFIX : ''}?${params}`; } } diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 60851cc6bd47..c947d80bab12 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -3,6 +3,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { nodeFileTrace } from '@vercel/nft'; import esbuild from 'esbuild'; +import { get_pathname } from './utils.js'; const VALID_RUNTIMES = ['edge', 'nodejs16.x', 'nodejs18.x']; @@ -292,13 +293,7 @@ const plugin = function (defaults = {}) { fs.symlinkSync(relative, `${base}.func`); fs.symlinkSync(`../${relative}`, `${base}/__data.json.func`); - let i = 1; - const pathname = route.segments - .map((segment) => { - return segment.dynamic ? `$${i++}` : segment.content; - }) - .join('/'); - + const pathname = get_pathname(route); const json = JSON.stringify(isr, null, '\t'); write(`${base}.prerender-config.json`, json); diff --git a/packages/adapter-vercel/package.json b/packages/adapter-vercel/package.json index 075380f038fc..b631b947771b 100644 --- a/packages/adapter-vercel/package.json +++ b/packages/adapter-vercel/package.json @@ -21,12 +21,14 @@ "files": [ "files", "index.js", + "utils.js", "index.d.ts" ], "scripts": { "lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore", "format": "pnpm lint --write", - "check": "tsc" + "check": "tsc", + "test": "uvu test .spec.js" }, "dependencies": { "@vercel/nft": "^0.22.1", @@ -35,7 +37,8 @@ "devDependencies": { "@sveltejs/kit": "workspace:^", "@types/node": "^16.18.6", - "typescript": "^4.9.4" + "typescript": "^4.9.4", + "uvu": "^0.5.6" }, "peerDependencies": { "@sveltejs/kit": "^1.5.0" diff --git a/packages/adapter-vercel/test/utils.spec.js b/packages/adapter-vercel/test/utils.spec.js new file mode 100644 index 000000000000..980d2687d47b --- /dev/null +++ b/packages/adapter-vercel/test/utils.spec.js @@ -0,0 +1,38 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { get_pathname } from '../utils.js'; + +/** + * @param {import('@sveltejs/kit').RouteDefinition['segments']} segments + * @param {string} expected + */ +function run_get_pathname_test(segments, expected) { + const route = /** @type {import('@sveltejs/kit').RouteDefinition} */ ({ segments }); + assert.equal(get_pathname(route), expected); +} + +test('get_pathname for simple route', () => { + run_get_pathname_test([{ content: 'foo', dynamic: false, rest: false }], 'foo'); +}); + +test('get_pathname for route with parameters', () => { + run_get_pathname_test( + [ + { content: 'foo', dynamic: false, rest: false }, + { content: '[bar]', dynamic: true, rest: false } + ], + 'foo/$1' + ); +}); + +test('get_pathname for route with parameters within segment', () => { + run_get_pathname_test( + [ + { content: 'foo-[bar]', dynamic: true, rest: false }, + { content: '[baz]-buz', dynamic: true, rest: false } + ], + 'foo-$1/$2-buz' + ); +}); + +test.run(); diff --git a/packages/adapter-vercel/utils.js b/packages/adapter-vercel/utils.js new file mode 100644 index 000000000000..de10073e8846 --- /dev/null +++ b/packages/adapter-vercel/utils.js @@ -0,0 +1,23 @@ +/** @param {import("@sveltejs/kit").RouteDefinition} route */ +export function get_pathname(route) { + let i = 1; + + return route.segments + .map((segment) => { + if (!segment.dynamic) { + return segment.content; + } + + const parts = segment.content.split(/\[(.+?)\](?!\])/); + return parts + .map((content, j) => { + if (j % 2) { + return `$${i++}`; + } else { + return content; + } + }) + .join(''); + }) + .join('/'); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86bbb1fff966..d5c0d788705b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -274,6 +274,9 @@ importers: typescript: specifier: ^4.9.4 version: 4.9.4 + uvu: + specifier: ^0.5.6 + version: 0.5.6 packages/amp: dependencies: