|
| 1 | +- Start Date: 2020-09-16 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- Svelte Issue: (leave this empty) |
| 4 | + |
| 5 | +# Refactoring out SvelteKit's Routing |
| 6 | + |
| 7 | +## Summary |
| 8 | + |
| 9 | +Split SvelteKit's routing functionality into a standalone library and make it possible to use SvelteKit's routing outside of SvelteKit. |
| 10 | + |
| 11 | +## Motivation |
| 12 | + |
| 13 | +Benefits include: |
| 14 | + |
| 15 | +* Making it possible to use SvelteKit's routing outside of SvelteKit. The routing component should be able to be used in a client-side only application |
| 16 | +* Making SvelteKit's routing more configurable |
| 17 | + * User requests for configuring the router include [routes aliases](https://github.com/sveltejs/sapper/issues/1450) and [configuring trailing slashes](https://github.com/sveltejs/sapper/issues/519), which we could expose APIs for |
| 18 | +* Improved testability. Right now to test routing functionality you need a sample application and all tests are integration tests |
| 19 | +* I believe it would become possible to make it such that SvelteKit's routing can be used with or without generating it from the file system though this is perhaps not a primary goal. |
| 20 | +* Finally, SvelteKit's routing and Routify are quite similar. It may be possible to combine these two systems if everyone involved were open to such a possibility. I think it'd be good for the Svelte community to have a single solution because it means that solution would get more developer support than two individual solutions. Right now [there are many Svelte routing solutions](https://twitter.com/lihautan/status/1315482668440580096?s=19). |
| 21 | + |
| 22 | +## Detailed design |
| 23 | + |
| 24 | +### Implementation |
| 25 | + |
| 26 | +This could be split up into multiple PRs that are smaller and easier to review. |
| 27 | + |
| 28 | +#### Step 1 |
| 29 | + |
| 30 | +Remove need to specify CSS files in routing components. |
| 31 | + |
| 32 | +Update: this was completed in [#1508](https://github.com/sveltejs/sapper/pull/1508). |
| 33 | + |
| 34 | + |
| 35 | +#### Step 2 |
| 36 | + |
| 37 | +Put all routing information in a single routing component. Historically, in Sapper, it has been split between `start` and `app`. |
| 38 | + |
| 39 | +Update: this has basically been completed with the router now living `packages/kit/runtime/internal/router` |
| 40 | + |
| 41 | + |
| 42 | +#### Step 3 |
| 43 | + |
| 44 | +Parse routes at runtime to simplify API. |
| 45 | + |
| 46 | +Right now, the generated `manifest.js` contains something like: |
| 47 | + |
| 48 | +``` |
| 49 | +const components = [ |
| 50 | + () => import("/_app/routes/index.svelte.js"), |
| 51 | + () => import("/_app/routes/about.svelte.js"), |
| 52 | + () => import("/_app/routes/item/[id].svelte.js"), |
| 53 | + () => import("/_app/routes/user/[name].svelte.js") |
| 54 | +]; |
| 55 | +
|
| 56 | +const d = decodeURIComponent; |
| 57 | +const empty = () => ({}); |
| 58 | +
|
| 59 | +export const pages = [ |
| 60 | + { |
| 61 | + // index.svelte |
| 62 | + pattern: /^\/$/, |
| 63 | + params: empty, |
| 64 | + parts: [components[0]] |
| 65 | + }, |
| 66 | +
|
| 67 | + { |
| 68 | + // about.svelte |
| 69 | + pattern: /^\/about\/?$/, |
| 70 | + params: empty, |
| 71 | + parts: [components[1]] |
| 72 | + }, |
| 73 | +
|
| 74 | + { |
| 75 | + // item/[id].svelte |
| 76 | + pattern: /^\/item\/([^/]+?)\/?$/, |
| 77 | + params: (m) => ({ id: d(m[1])}), |
| 78 | + parts: [components[2]] |
| 79 | + }, |
| 80 | +
|
| 81 | + { |
| 82 | + // user/[name].svelte |
| 83 | + pattern: /^\/user\/([^/]+?)\/?$/, |
| 84 | + params: (m) => ({ name: d(m[1])}), |
| 85 | + parts: [components[3]] |
| 86 | + } |
| 87 | +]; |
| 88 | +``` |
| 89 | + |
| 90 | +This API is a bit complex. However, it could be greatly simplified by making the route parsing be part of the router runtime instead of happening at compile time: |
| 91 | + |
| 92 | +``` |
| 93 | +const pages = { |
| 94 | + '/': () => import("../../../routes/index.svelte" |
| 95 | + '/[list]/[page]': () => import("../../../routes/[list]/[page].svelte" |
| 96 | +} |
| 97 | +
|
| 98 | +const router = new Router({ |
| 99 | + base: paths.base, |
| 100 | + host, |
| 101 | + pages, |
| 102 | + ignore |
| 103 | +}); |
| 104 | +``` |
| 105 | + |
| 106 | +The `pattern` is needed on the server-side too, so we will still have to include it in the manifest. |
| 107 | + |
| 108 | +#### Step 4 |
| 109 | + |
| 110 | +Refactor out routes generation into separate plugin. Publish components separately. |
| 111 | + |
| 112 | +Review the API and make sure we like it. There's probably some minor cleanup to do before exposing the routing API more broadly |
| 113 | + |
| 114 | +We could potentially have a library for the core routing functionality. And then a second for the file-system-based routes generator. |
| 115 | + |
| 116 | + |
| 117 | +## How we teach this |
| 118 | + |
| 119 | +Update the documentation, migration guide, and templates. |
| 120 | + |
| 121 | +## Drawbacks |
| 122 | + |
| 123 | +Some user migration may be necessary. E.g. if the router is a separate component, then it will need to be included in `package.json` as a dependency. |
| 124 | + |
| 125 | +The bundle size may increase slightly if the routes parsing is included in the bundle. This probably would result in about 50 lines of additional code in the router runtime. However, it would reduce the number of lines required to register a route so it may end up being a savings for larger applications. If we are concerned about this it could be made an optional pluggable component of the router. Most users will not register components at runtime and so using generated regexes will continue to be just fine for the existing use cases. Though where it is likely to be very helpful is if we want to allow programmatic access to configuring the routes (e.g. to add a header) |
| 126 | + |
| 127 | +## Alternatives |
| 128 | + |
| 129 | +Probably the main alternative would be the status quo. If people want to use Sapper's routing without the rest of Sapper then we point them to Routify, page.js, or some other solution. |
| 130 | + |
| 131 | +## Unresolved questions |
| 132 | + |
| 133 | +The last step seems the one that we would need to put the most time and thought into. E.g. should it live in a Rollup plugin, would we continue to support webpack, are there reason we don't want to make everything into plugins? |
| 134 | + |
| 135 | +When publishing new npm packages should we put them in a `@sapper` namespace? Can/should we publish multiple packages from the existing Saper repo or should we make a new repo for each package? |
0 commit comments