diff --git a/.changeset/heavy-donuts-grab.md b/.changeset/heavy-donuts-grab.md new file mode 100644 index 000000000000..3ef1445e939c --- /dev/null +++ b/.changeset/heavy-donuts-grab.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Pass trailingSlash config to adapters diff --git a/.changeset/modern-grapes-tell.md b/.changeset/modern-grapes-tell.md new file mode 100644 index 000000000000..41ef6fdd5126 --- /dev/null +++ b/.changeset/modern-grapes-tell.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-vercel': patch +--- + +Clean URLs and handle trailingSlash configuration diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js index 269f88e59e3f..e938b5353be6 100644 --- a/packages/adapter-vercel/index.js +++ b/packages/adapter-vercel/index.js @@ -5,6 +5,83 @@ import esbuild from 'esbuild'; const dir = '.vercel_build_output'; +// rules for clean URLs and trailing slash handling, +// generated with @vercel/routing-utils +const redirects = { + always: [ + { + src: '^/(?:(.+)/)?index(?:\\.html)?/?$', + headers: { + Location: '/$1/' + }, + status: 308 + }, + { + src: '^/(.*)\\.html/?$', + headers: { + Location: '/$1/' + }, + status: 308 + }, + { + src: '^/\\.well-known(?:/.*)?$' + }, + { + src: '^/((?:[^/]+/)*[^/\\.]+)$', + headers: { + Location: '/$1/' + }, + status: 308 + }, + { + src: '^/((?:[^/]+/)*[^/]+\\.\\w+)/$', + headers: { + Location: '/$1' + }, + status: 308 + } + ], + never: [ + { + src: '^/(?:(.+)/)?index(?:\\.html)?/?$', + headers: { + Location: '/$1' + }, + status: 308 + }, + { + src: '^/(.*)\\.html/?$', + headers: { + Location: '/$1' + }, + status: 308 + }, + { + src: '^/(.*)/$', + headers: { + Location: '/$1' + }, + status: 308 + } + ], + ignore: [ + { + src: '^/(?:(.+)/)?index(?:\\.html)?/?$', + headers: { + Location: '/$1' + }, + status: 308 + }, + { + src: '^/(.*)\\.html/?$', + headers: { + Location: '/$1' + }, + status: 308 + } + ] +}; + /** @type {import('.')} **/ export default function ({ external = [] } = {}) { return { @@ -69,6 +146,7 @@ export default function ({ external = [] } = {}) { writeFileSync( `${dir}/config/routes.json`, JSON.stringify([ + ...redirects[builder.trailingSlash], { src: `/${builder.appDir}/.+`, headers: { diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index e127d8816ba5..28ece10289a3 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -33,6 +33,7 @@ export function create_builder({ cwd, config, build_data, log }) { copy, appDir: config.kit.appDir, + trailingSlash: config.kit.trailingSlash, createEntries(fn) { generated_manifest = true; diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts index 18cb8e32d507..f28a4efb707e 100644 --- a/packages/kit/types/config.d.ts +++ b/packages/kit/types/config.d.ts @@ -41,6 +41,7 @@ export interface Builder { mkdirp(dir: string): void; appDir: string; + trailingSlash: 'always' | 'never' | 'ignore'; /** * Create entry points that map to individual functions