diff --git a/documentation/docs/13-configuration.md b/documentation/docs/13-configuration.md index baaf02b13099..4bc5a413e8a6 100644 --- a/documentation/docs/13-configuration.md +++ b/documentation/docs/13-configuration.md @@ -17,6 +17,7 @@ module.exports = { adapter: null, amp: false, appDir: '_app', + clientSideRouting: true, files: { assets: 'static', lib: 'src/lib', @@ -57,6 +58,10 @@ Enable [AMP](#amp) mode. The directory relative to `paths.assets` where the built JS and CSS (and imported assets) are served from. (The filenames therein contain content-based hashes, meaning they can be cached indefinitely). +#### clientSideRouting + +Disable client side routing so that navigation will result in a full page reload. + #### files An object containing zero or more of the following `string` values: diff --git a/packages/kit/src/core/build/index.js b/packages/kit/src/core/build/index.js index 5261fa1f45a1..8c958d3c6c89 100644 --- a/packages/kit/src/core/build/index.js +++ b/packages/kit/src/core/build/index.js @@ -359,6 +359,7 @@ async function build_server( app_dir: ${s(config.kit.appDir)}, host: ${s(config.kit.host)}, host_header: ${s(config.kit.hostHeader)}, + client_side_routing: ${s(config.kit.clientSideRouting)}, get_component_path: id => ${s(`${config.kit.paths.assets}/${config.kit.appDir}/`)} + client_component_lookup[id], get_stack: error => error.stack, get_static_file, diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index 5a4381cf7632..e146d9cc3c1b 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -196,6 +196,7 @@ class Watcher extends EventEmitter { only_prerender: false, host: this.config.kit.host, host_header: this.config.kit.hostHeader, + client_side_routing: this.config.kit.clientSideRouting, get_component_path: (id) => `/${id}?import`, get_stack: (error) => { this.viteDevServer.ssrFixStacktrace(error); diff --git a/packages/kit/src/core/load_config/index.spec.js b/packages/kit/src/core/load_config/index.spec.js index 82e5b870e274..47c62f0d1376 100644 --- a/packages/kit/src/core/load_config/index.spec.js +++ b/packages/kit/src/core/load_config/index.spec.js @@ -12,6 +12,7 @@ test('fills in defaults', () => { adapter: [null], amp: false, appDir: '_app', + clientSideRouting: true, files: { assets: 'static', lib: 'src/lib', @@ -84,6 +85,7 @@ test('fills in partial blanks', () => { adapter: [null], amp: false, appDir: '_app', + clientSideRouting: true, files: { assets: 'public', lib: 'src/lib', diff --git a/packages/kit/src/core/load_config/options.js b/packages/kit/src/core/load_config/options.js index 4eb5538087e5..e5f6379ee7a0 100644 --- a/packages/kit/src/core/load_config/options.js +++ b/packages/kit/src/core/load_config/options.js @@ -55,6 +55,8 @@ const options = { appDir: expect_string('_app', false), + clientSideRouting: expect_boolean(true), + files: { type: 'branch', children: { diff --git a/packages/kit/src/core/load_config/test/index.js b/packages/kit/src/core/load_config/test/index.js index 93463b9a22ae..335078051567 100644 --- a/packages/kit/src/core/load_config/test/index.js +++ b/packages/kit/src/core/load_config/test/index.js @@ -21,6 +21,7 @@ suite('load default config', async () => { adapter: [null], amp: false, appDir: '_app', + clientSideRouting: true, files: { assets: join(cwd, 'static'), lib: join(cwd, 'src/lib'), diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index b82b5e705118..c24da7689fa6 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -17,12 +17,14 @@ export class Router { * base: string; * host: string; * pages: import('../../../types.internal').CSRPage[]; + * client_side_routing: boolean; * ignore: RegExp[]; * }} opts */ - constructor({ base, host, pages, ignore }) { + constructor({ base, host, pages, client_side_routing, ignore }) { this.base = base; this.host = host; this.pages = pages; + this.client_side_routing = client_side_routing; this.ignore = ignore; this.history = window.history || { @@ -177,15 +179,17 @@ export class Router { * @param {string[]} chain */ async goto(href, { noscroll = false, replaceState = false } = {}, chain) { - const url = new URL(href, get_base_uri(document)); - const selected = this.select(url); + if (this.client_side_routing) { + const url = new URL(href, get_base_uri(document)); + const selected = this.select(url); - if (selected) { - this.renderer.notify(selected.page); + if (selected) { + this.renderer.notify(selected.page); - // TODO shouldn't need to pass the hash here - this.history[replaceState ? 'replaceState' : 'pushState']({}, '', href); - return this.navigate(selected, noscroll ? scroll_state() : null, chain, url.hash); + // TODO shouldn't need to pass the hash here + this.history[replaceState ? 'replaceState' : 'pushState']({}, '', href); + return this.navigate(selected, noscroll ? scroll_state() : null, chain, url.hash); + } } location.href = href; diff --git a/packages/kit/src/runtime/client/start.js b/packages/kit/src/runtime/client/start.js index 59ed13a4e574..5131f7a27783 100644 --- a/packages/kit/src/runtime/client/start.js +++ b/packages/kit/src/runtime/client/start.js @@ -16,14 +16,16 @@ import { set_paths } from '../paths.js'; * session: any; * error: Error; * status: number; + * client_side_routing: boolean; * nodes: import('./types').NavigationTarget["nodes"]; * page: import('./types').NavigationTarget["page"]; * }} opts */ -export async function start({ paths, target, session, error, status, nodes, page }) { +export async function start({ paths, target, session, error, status, client_side_routing, nodes, page }) { const router = new Router({ base: paths.base, host: page.host, pages, + client_side_routing, ignore }); @@ -37,7 +39,7 @@ export async function start({ paths, target, session, error, status, nodes, page init({ router, renderer }); set_paths(paths); - router.init(renderer); + if (client_side_routing) router.init(renderer); await renderer.start({ nodes, page }, status, error); dispatchEvent(new CustomEvent('sveltekit:start')); diff --git a/packages/kit/src/runtime/server/page.js b/packages/kit/src/runtime/server/page.js index a6b435de0d66..e6cb4f3dc7be 100644 --- a/packages/kit/src/runtime/server/page.js +++ b/packages/kit/src/runtime/server/page.js @@ -324,6 +324,7 @@ async function get_response({ request, options, $session, route, status = 200, e status: ${status}, error: ${serialize_error(error)}, session: ${serialized_session}, + client_side_routing: ${options.client_side_routing}, nodes: [ ${(route ? route.parts : []) .map((part) => `import(${s(options.get_component_path(part.id))})`) diff --git a/packages/kit/types.d.ts b/packages/kit/types.d.ts index a462be3444db..1b78163e02a3 100644 --- a/packages/kit/types.d.ts +++ b/packages/kit/types.d.ts @@ -7,6 +7,7 @@ export type Config = { adapter?: string | [string, any]; amp?: boolean; appDir?: string; + clientSideRouting?: boolean; files?: { assets?: string; lib?: string; diff --git a/packages/kit/types.internal.d.ts b/packages/kit/types.internal.d.ts index b80f94d6060e..1967ed7c0941 100644 --- a/packages/kit/types.internal.d.ts +++ b/packages/kit/types.internal.d.ts @@ -22,6 +22,7 @@ export type ValidatedConfig = { adapter: [string, any]; amp: boolean; appDir: string; + clientSideRouting: boolean; files: { assets: string; lib: string; @@ -186,6 +187,7 @@ export type SSRRenderOptions = { app_dir?: string; host?: string; host_header?: string; + client_side_routing?: boolean; get_component_path?: (id: string) => string; get_stack?: (error: Error) => string; get_static_file?: (file: string) => Buffer;