The idea is to create universal [...path]
that will capture all, and our urls.js
will look like this:
export const default_error = () => import('/src/error.svelte');
const base_layout = {page: () => import('/src/base.svelte'), default_error}
export const patterns = [
{re: /^\/\/?$/, page: () => import('/src/home.svelte'), layouts: [base_layout], js: import('/src/home.js')},
{re: /^\/about\/?$/, page: () => import('/src/about.svelte'), layouts: [base_layout], endpoint: import('/src/about.js')},
{re: /^\/article\/([0-9]+)\/?$/, slugs: ['id'], page: () => import('/src/article.svelte'), layouts: [base_layout], js: import('/src/article.js'), endpoint: import('/src/article_endpoint.js')},
]
Final structure can look like:
router/
[...path]/
page.js
page.server.js
+page.svelte
+error.svelte
router.js
Since router/
is renamed routes/
putted in project root along with src/
, we have to specify it in configs:
svelte.config.js:
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
// Add this:
files: {
routes: 'router/',
},
}
};
export default config;
vite.config.js:
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
server: {
// Add this:
fs: {
allow: ['..'], // Allow serving files from one level up to the project root
},
},
});
Next, just contents of router/
folder
router.js:
import { writable } from 'svelte/store';
import { error } from '@sveltejs/kit';
import { patterns, default_error } from '/urls.js';
export const route = writable();
export const Router = {}
Router.get_pattern = function(pathname) {
for (const pattern of patterns) {
if (pattern.re.test(pathname)) {
return pattern;
}
}
}
Router.check_404 = function(pattern) {
if (!pattern) {
throw error(404, 'Not found')
}
}
Router.call_loads = async function(params, pattern, type) {
for (const page of [pattern, ...(pattern.layouts || [])]) {
if (!page[type]) continue;
const load = (await page[type]()).load;
params.data = {...params.data, ...await load(params)};
}
return params.data;
}
Router.get_route = async function(pattern, url) {
const route = {}
route.pattern = pattern;
route.url = url;
route.slugs = Router.get_slugs(pattern, url);
route.error = await Router.get_error(pattern)
return route;
}
Router.get_templates = async function(params, pattern) {
const templates = {}
templates.page = (await pattern.page()).default;
templates.layouts = [];
for (const layout of pattern.layouts || []) {
templates.layouts.push((await layout.page()).default);
}
return templates;
}
Router.get_slugs = function(pattern, url) {
const slugs = {}
const matches = pattern.re.exec(url.pathname);
for (const [index, match] of Object.entries(matches)) {
const int_index = parseInt(index);
if (int_index && int_index > 0) {
slugs[pattern.slugs[index - 1]] = match
}
}
return slugs;
}
Router.get_error = async function(pattern) {
if (!pattern) return (await default_error()).default;
for (const page of [...(pattern.layouts || []), pattern].reverse()) {
if (!page.error) continue;
const error = (await page.error()).default;
return error;
}
}
[...path]/page.js:
import { Router, route } from '/router/router.js';
export async function load(params) {
const pattern = Router.get_pattern(params.url.pathname);
const raw_route = await Router.get_route(pattern, params.url);
route.update((v) => ({...v, ...raw_route}));
params.data = await Router.call_loads(params, pattern, 'js');
const templates = await Router.get_templates(params, pattern);
route.update((v) => ({...v, ...templates}));
return params.data;
}
[...path]/page.server.js
import { Router, route } from '/router/router.js';
export async function load(params) {
const pattern = Router.get_pattern(params.url.pathname);
Router.check_404(pattern);
const raw_route = await Router.get_route(pattern, params.url);
route.update((v) => ({...v, ...raw_route}));
params.data = await Router.call_loads(params, pattern, 'server');
return params.data;
}
[...path]/+page.svelte:
<script>
import {route} from '/router/router.js';
export let data;
</script>
{#snippet draw(route, index)}
{#if route.layouts.length && index < route.layouts.length}
<svelte:component this={route.layouts[index]} {data}>
{@render draw(route, index + 1)}
</svelte:component>
{:else}
<svelte:component this={route.page} {data}/>
{/if}
{/snippet}
{@render draw($route, 0)}
[...path]/+error.svelte:
<script>
import { Router, route } from '/router/router.js';
import { page } from '$app/state';
let Error = $state($route?.error);
$effect(async() => {
let pattern = Router.get_pattern(page.url.pathname);
Error = await Router.get_error(pattern);
});
</script>
{#if Error}
<Error/>
{/if}
Now, we just have global route
store, that stores current slugs, matched page, etc.:
article.js:
<script>
import {route} from '/router/router.js';
</script>
{$route.slugs.id}
That's all
Top comments (0)