DEV Community

Max Core
Max Core

Posted on • Edited on

3 1 1 2

SvelteKit: How to make code-based router, instead of file-based router [December 2022]

Dudes. There are breaking changes in SvelteKit [on December 2022]. That means that my previous post on that topic is garbage now.

For those, who builds enterprise projects, not just landing-pages, and want to name your files and organise your folders based on domains (DDD), or based on technical convenience, rather than on url structure dictated by "marketing needs"; and get full control and flexibility, like — two different urls match one component etc. — please — welcome.

So. After a full day of research and code I've ended up with..

As a first step, let's check what is going on in SvelteKit's depths. Open file:
node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/index.js Navigate to:

292: prevent_conflicts(routes);
Enter fullscreen mode Exit fullscreen mode

And print:

+ 293: console.log(routes);
Enter fullscreen mode Exit fullscreen mode

It'l give us a complex array of objects as the result of defaults walking through file structure.
And that's exactly what we have to define somehow and somewhere in code.

So, what literally have I done...

I. In root of src/ create routes.js...

... where I've places both routes array with custom basic rules and router() function that transforms that rules to array of objects that we've seen in console.log(routes):

const routes = [
    {id: '/',       pattern: /^\/$/,                              page: 'home.svelte',            layout: 'layout.svelte',         segment: ''},
    {id: '/[slug]', pattern: /^\/([^/]+?)\/?$/, params: ['slug'], page: 'article/article.svelte', layout: 'article/layout.svelte', parent_layout_segment: ''}
]

export function router(routes_dir) {
    const result = []
    for (const route of routes) {
        const parent = result.find(o => o.segment === route.parent_layout_segment)
        const new_route = {
            parent: parent,
            id: route.id,
            segment: route.segment,
            pattern: route.pattern,
            params: (route.params || []).map((param, index) => ({name: param, matcher: undefined, optional: false, rest: false, chained: false})),
            layout: {
                depth: route.layout_depth || route.depth || 0,
                child_pages: [],
                component:route.layout && routes_dir + route.layout,
                shared: route.layout_js && routes_dir + route.layout_js,
                server: route.layout_server_js && routes_dir + route.layout_server_js,
            },
            error: {
                depth: route.error_depth || route.depth || 0,
                component: route.error && routes_dir + route.error
            },
            leaf: {
                depth: route.leaf_depth || route.depth || 0,
                shared: route.page_js && routes_dir + route.page_js,
                server: route.page_server_js && routes_dir + route.page_server_js,
                component: routes_dir + route.page,
            },
            endpoint: route.server_js && {
                file: routes_dir + route.server_js,
            },
            page: null,  // Have no idea what is it for, but let it be here, just not to forget
        }

        result.push(new_route)
    }
    return result;
}
Enter fullscreen mode Exit fullscreen mode

About params in routes object:

  1. id — need to be unique. Also goes to client manifest.
  2. segment — also need to be unique, and it is needed to create nested layouts. So, another rule can use parent_layout_segment to refer parent layout (empty string is also ok).
  3. params — needed in case of dynamic slugs. Each round parentheses in pattern should be presented in params array, like: {pattern: /^\/(\d+)\/(\d+)\/?$/}, params: ['year', 'month']}
  4. You know, SvelteKit do not have only +page.svelte and +layout.svelte, but also:
  5. +page.js so { ... page_js: 'my_page.js' ... } could be passed in rule;
  6. +page.server.js —> page_server_js;
  7. +server.js —> server_js;
  8. +layout.js —> layout_js;
  9. +layout.server.js —> layout.server_js;
  10. Didn't notice any changes in playing with some depth param, but, just in case we can pass:
  11. layout_depth;
  12. page_depth;
  13. error_depth;
  14. depth (global); Just in case we'll find one day that it matters)

II. In svelte.config.js

import adapter from '@sveltejs/adapter-auto';
+ import {router} from './routes.js';  // <— add this

/** @type {import('@sveltejs/kit').Config} */
const config = {
+   routes: router(),  // <— add this
    kit: {
        adapter: adapter(),
    }
};
export default config;
Enter fullscreen mode Exit fullscreen mode

III. In already familiar

node_modules/@sveltejs/kit/src/core/sync/create_manifest_data/index.js:

1) Replace file system walking result with our code routes

292:    prevent_conflicts(routes);
+ 293:  routes.length = 0;
+ 294:  routes.push(...config.routes);
Enter fullscreen mode Exit fullscreen mode

2) We do not need any magic sorting any more

372:    return {
373:    nodes,
- 374:  routes: sort_routes(routes)
+ 375:  routes: routes
376:    };
Enter fullscreen mode Exit fullscreen mode

IV. Install patch-package, so this changes will be automatically applied in future without manual hacks:

> npm i patch-package
> npx patch-package @sveltejs/kit
Enter fullscreen mode Exit fullscreen mode

package.json:

{
  ...
  "scripts": {
    ...
    "postinstall": "patch-package" // <— add this
Enter fullscreen mode Exit fullscreen mode

That's all! Easy :D

Image of Datadog

Master Mobile Monitoring for iOS Apps

Monitor your app’s health with real-time insights into crash-free rates, start times, and more. Optimize performance and prevent user churn by addressing critical issues like app hangs, and ANRs. Learn how to keep your iOS app running smoothly across all devices by downloading this eBook.

Get The eBook

Top comments (2)

Collapse
 
ktibow profile image
Kendell R

but why
also you sound a bit weird, are you a non-native speaker?

Collapse
 
maxcore profile image
Max Core • Edited

Hi! At least because "me as a developer" (sounds like user story :D ), want to organise my code and name files and folders according to project's code-style etc., which was designed to serve "that current enterprise project" needs. So, let's say I just do not want framework to dictate how to name my files and folders, and how to respect its hierarchy. I need total control and flexibility (which only code can give).
And I know, that I am not the only one
So, just for them) I feel their pain too) Probably it would help someone

Ye, I am not native

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more