diff --git a/.changeset/famous-otters-relax.md b/.changeset/famous-otters-relax.md new file mode 100644 index 000000000000..34c983359642 --- /dev/null +++ b/.changeset/famous-otters-relax.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: don't record a trailing slash for a prerendered root page combined with a base path diff --git a/packages/kit/.gitignore b/packages/kit/.gitignore index c7b5684faca6..806a38264d91 100644 --- a/packages/kit/.gitignore +++ b/packages/kit/.gitignore @@ -4,6 +4,7 @@ !/src/core/adapt/fixtures/*/.svelte-kit !/test/node_modules /test/apps/basics/test/errors.json +/test/prerendering/paths-base/prerendered-paths.json /types/*.map .custom-out-dir diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index f03e80cd7e8f..740d5b0352d9 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -202,6 +202,14 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { return file; } + /** + * @param {string} path + */ + function strip_base_only_trailing_slash(path) { + const base_root = `${config.paths.base}/`; + return path === base_root && base_root.length > 1 ? config.paths.base : path; + } + const files = new Set(walk(`${out}/client`).map(posixify)); files.add(`${config.appDir}/env.js`); @@ -445,7 +453,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { location: resolved }); - prerendered.paths.push(decoded); + prerendered.paths.push(strip_base_only_trailing_slash(decoded)); } } } else { @@ -487,7 +495,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { }); } - prerendered.paths.push(decoded); + prerendered.paths.push(strip_base_only_trailing_slash(decoded)); } else if (response_type !== OK) { handle_http_error({ status: response.status, path: decoded, referrer, referenceType }); } diff --git a/packages/kit/src/exports/env/index.js b/packages/kit/src/exports/env/index.js index 1f3e591e4b20..69106f0b5756 100644 --- a/packages/kit/src/exports/env/index.js +++ b/packages/kit/src/exports/env/index.js @@ -1,5 +1,8 @@ /** @import { EnvVarConfig } from '@sveltejs/kit' */ +// tsc otherwise reports EnvVarConfig as unused since it's only referenced in a @template bound +export {}; + /** * Utility for defining [environment variables](https://svelte.dev/docs/kit/environment-variables), * which are made available via `$app/env/public` and `$app/env/private`. diff --git a/packages/kit/test/prerendering/paths-base/svelte.config.js b/packages/kit/test/prerendering/paths-base/svelte.config.js index 27e3e4661b69..a97a68829159 100644 --- a/packages/kit/test/prerendering/paths-base/svelte.config.js +++ b/packages/kit/test/prerendering/paths-base/svelte.config.js @@ -1,9 +1,18 @@ -import adapter from '../../../../adapter-static/index.js'; +import { writeFileSync } from 'node:fs'; +import static_adapter from '../../../../adapter-static/index.js'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { - adapter: adapter(), + adapter: { + name: 'test', + async adapt(builder) { + // capture what adapters actually receive for prerendered paths, so + // tests can assert on it directly instead of inferring it from output + writeFileSync('./prerendered-paths.json', JSON.stringify(builder.prerendered.paths)); + await static_adapter().adapt(builder); + } + }, paths: { base: '/path-base', diff --git a/packages/kit/test/prerendering/paths-base/test/tests.spec.js b/packages/kit/test/prerendering/paths-base/test/tests.spec.js index ede2339b3bec..21641d860d94 100644 --- a/packages/kit/test/prerendering/paths-base/test/tests.spec.js +++ b/packages/kit/test/prerendering/paths-base/test/tests.spec.js @@ -7,6 +7,14 @@ const build = fileURLToPath(new URL('../build', import.meta.url)); /** @param {string} file */ const read = (file) => fs.readFileSync(`${build}/${file}`, 'utf-8'); +test('root page with a base path is recorded without a trailing slash', () => { + const paths = JSON.parse( + fs.readFileSync(fileURLToPath(new URL('../prerendered-paths.json', import.meta.url)), 'utf-8') + ); + assert.ok(paths.includes('/path-base')); + assert.ok(!paths.includes('/path-base/')); +}); + test('prerenders /path-base', () => { const content = read('index.html'); assert.ok(content.includes('favicon.png') && content.includes('nested')); diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 23f2d730e9b6..788509a0b845 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -3026,7 +3026,7 @@ declare module '@sveltejs/kit' { }>; export const VERSION: string; class HttpError_1 { - + constructor(status: number, body: { message: string; } extends App.Error ? (App.Error | string | undefined) : App.Error); @@ -3035,7 +3035,7 @@ declare module '@sveltejs/kit' { toString(): string; } class Redirect_1 { - + constructor(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string); status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308; location: string; @@ -3668,9 +3668,9 @@ declare module '$app/server' { * * */ function live(fn: (arg: void) => RemoteLiveQueryUserFunctionReturnType): RemoteLiveQueryFunction; - + function live(validate: "unchecked", fn: (arg: Input) => RemoteLiveQueryUserFunctionReturnType): RemoteLiveQueryFunction; - + function live(schema: Schema, fn: (arg: StandardSchemaV1.InferOutput) => RemoteLiveQueryUserFunctionReturnType): RemoteLiveQueryFunction, Output, StandardSchemaV1.InferOutput>; } /** @@ -3837,11 +3837,11 @@ declare module '$app/state' { declare module '$app/stores' { export function getStores(): { - + page: typeof page; - + navigating: typeof navigating; - + updated: typeof updated; }; /** @@ -4025,4 +4025,4 @@ declare module '$app/types' { export type Asset = ReturnType; } -//# sourceMappingURL=index.d.ts.map +//# sourceMappingURL=index.d.ts.map \ No newline at end of file