sveltejs / kit Goto Github PK
View Code? Open in Web Editor NEWweb development, streamlined
Home Page: https://kit.svelte.dev
License: MIT License
web development, streamlined
Home Page: https://kit.svelte.dev
License: MIT License
If the app template doesn't include tests, then one of the first questions people will ask is 'how do we add tests'? We should probably have some way of answering this question
There's a browser feature called preload
, which Sapper/kit supports: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
It's been confusing on a number of occasions that we've overloaded this term. This might be a good opportunity to consider naming
Snowpack doesn't let you use the src/node_modules
trick, so we have to use aliases if we're to avoid ../.../../../
hell.
At present in the config there's a $components
alias that maps to src/components
. We need something that maps to runtime functions like goto
etc (which don't live in a package Snowpack can install because they rely on the generated manifest), because at present the way to do it is gross:
import { goto, prefetch, prefetchRoutes, stores } from '/_app/main/client.js';
There's lots wrong with this — importing from an 'internal' URL like /_app
is ugly, the main
(to differentiate it from the other generated paths, like the /_app/components
that $components
aliases to) is awkward, and client.js
is inaccurate since stores can be instantiated on the server.
Here's what I propose instead:
import { goto, prefetch, prefetchRoutes } from '$app/navigation';
import { getStores } from '$app/stores';
Opening this issue as a way of tracking a "migration guide" from Sapper to Kit.
One idea I've been thinking about is a package called @sveltejs/backport
which you can install to "polyfill" methods/functions/behaviour/utilities which have been removed/renamed/signature-adusted. You can then strip out legacy over time, eventually uninstalling the package, rather than trying do delay until you have time to migrate larger apps.
This might be impossible though, but either way, we should have a living migration step list:
@sapper/app
to imports from... wherever we end up putting stuff ($app
?)src/node_modules/components
, move it to src/components
and update the import path to $components
load
(fka preload
) (#272)render
/ prerender
methods#svelte
target element (#157).preloading
/ navigating
store (#215)stores
vs getStores
(#187)svelte-kit adapt
(#221)$
instead of _
Porting the hackernews app I have found that:
From the ask
page, hitting the ask
navigation link:
<li><a rel="prefetch" class='{segment === "ask" ? "selected" : ""}' href='ask/1'>ask</a></li>
in Sapper - visits the /ask/1
page
In Svelte App - visits /ask/ask/1
page (which 404s)
I'm assuming this has something to do with baseUrl.
Note that in line with the thinking in #16 - we recently made this change to baseUrl which should be considered sveltejs/sapper#1562
This also isn't specific to hrefs since I've noticed that from the ask
page, all references to static assets are attempting to be loaded under the /ask/
path.
Not yet sure what this looks like
Let's say I have a _layout.svelte
like the following:
<slot name="layout-pre"></slot>
<div id="intermediate">Some other intermediate stuff.</div>
<slot></slot>
And now I have a child view (let's call it homepage.svelte
which inherits that layout, which puts some stuff into the named slot, and the rest into the default slot:
<h1 slot="layout-pre">This should come before #intermediate.</h1>
<!-- When rendered, the div from above should get inserted here from the layout:
<div id="intermediate">Some other intermediate stuff.</div>
-->
<main>Since this doesn't use a named slot, it'll just get inserted into the unnamed slot above.</main>
Right now this is not allowed, and the following error is thrown: Element with a slot='...' attribute must be a descendant of a component or custom element
. Are there technical or usage reasons why we wouldn't want to treat a layout like any other component with respect to slots?
We're currently using a fork, inlined into the @sveltejs/snowpack-config
package. Once we can remove it (when the next version of @snowpack/plugin-svelte
is released), we should remember to remove the dependencies that are temporarily necessary
In current Sapper it's easy to intercept requests and do whatever you want with them since you're building a server — this is useful for auth, custom headers and so on. The Sapper middleware also has a session
property that populates the session store when a request is made that falls within Sapper's purview.
In a serverless-first world this gets a bit trickier. It needs to be possible to do both things, in a way that maps to the various serverless platforms out there, which most likely precludes using the (req, res) => {...}
signature (and by extension, the ecosystem of Express middleware).
Maybe this is a new src/request.js
file (naming subject to extensive bikeshedding):
export function intercept({ url, cookies, headers, blah }) {
return {
headers: {
'x-my-header': 'banana'
}
};
}
export async function session({ url, cookies, headers, blah }) {
return {
user: await get_user_from_cookies(cookies)
};
}
It probably doesn't make sense to intercept all requests (since in many cases the responses get baked out as flat files), so we probably have to work within the constraint of only intercepting requests for pages and server routes.
duh
Via this Stack Overflow question. It would be nice if there was a way to do optional parameters — e.g. /foo
, /fr/foo
and /ca/fr/foo
all resolving to the same page component (where fr
is a named lang
parameter and ca
is a named country
parameter).
@Timer from the Next.js team had a neat idea — we could mark optional parameters with double brackets: routes/[[country]]/[[lang]]/foo.svelte
At the same time, it might be nice if routes/[...parts]/foo.svelte
matched all those pathnames. At present it will match /fr/foo
and /ca/fr/foo
but not /foo
.
Starting from the current create-svelte template and then bumping @sveltejs/adapter-node
to 0.0.8 and @sveltejs/kit
to 0.0.18, I ran npm run build
and then node build
and saw an error about there being no such file or directory .../build/prerendered
. If I created an empty directory there, all seemed fine.
The output from the Node adapter should either always include a prerendered
directory, or the generated code should accommodate it being missing.
everyone's favourite topic
The Sapper template allows you to write process.browser
to determine if you're in the browser or in Node. Not sure how to do that here. Snowpack has import.meta.env
which feels like the way you're supposed to go, though I'm not sure how to differentiate between SSR and not. Perhaps the isSSR
boolean that gets passed around could also be used to populate import.meta.env.SSR
?
Stumbled into a bit of a rabbit hole while adding prerendering to adapter-node — because this.fetch
is expected to work exactly the same as fetch
, we have to account for the possibility that someone will this.fetch('/some-static-file.json
), which means that the prerenderer needs to know where the static files could live, which muddies the API somewhat.
It's also just a bit of an awkward API:
res = await this.fetch(...); data = await res.json()
dance is a bit of a pain, honestlycredentials: 'include'
if you're using session
fetch
supports non-GET methods, which don't make sense in the context of preload
(or load, as the case may be)I'd like to replace it with this.get
:
<script context="module">
export async function load({ params }) {
return await this.get(`./${params.slug}.json`);
}
</script>
fetch
), which can be relative to the requested pageContent-Type
is application/json
, is JSON.parse
d. If it's text/*
, is returned as text. Otherwise is returned as an array bufferstatus
code), which is equivalent to calling this.error(status, error)
, except that you can catch and handle if necessary. For common cases, this eliminates the need to add error handling around the response returned from this.fetch
We could also expose get
alongside the other app functions (goto
et al) for use outside preload
/load
functions. (It would probably just throw in Node.) That's not necessary for this.get
to have value though.
Of course there are still times when it's useful to be able to use fetch
in preload
/load
— hn.svelte.dev fetches data from https://api.hnpwa.com, for example, and it's good to be able to do this isomorphically (rather than forcing the app to always connect to the app's own server, like ahem some frameworks). But if it's only to be used for external URLs, then there's no need to use this.fetch
, we can just use regular fetch
. We can polyfill it in Node with @rollup/plugin-inject and node-fetch (which already powers this.fetch
). With this approach, many apps wouldn't need node-fetch at all, whereas currently it has to be included because of this.fetch
.
This seems like all upside to me, except for the breakingness of the change. But if there was ever a time for breaking changes this is it. Thoughts?
Snowpack has this superb feature: FredKSchott/snowpack#1073
For some reason it doesn't seem to be working for us
e.g. in the case of sveltejs/hn.svelte.dev#27, the 'about' page could be prerendered.
It requires answering two questions:
It can't realistically be inferred from things like whether or not preload
exists (/blog/some-post
will have a preload
but could be prerendered, /time-now
is dynamic but might not have a preload
), so I imagine we need to make it explicit, similarly to how Next has getStaticProps
vs getServerSideProps
.
My favourite idea so far:
<script context="module">
export const metadata = {
static: true
};
export function preload(page, session) {
// ...
}
</script>
Alternatively we could export a boolean directly, but export const static = true
isn't allowed (static
is a reserved word) so we'd either need a synonym like export const prerendered = true
or else export const dynamic = false
, which feels weird.
In other words how do we find /about
, in the hn.svelte.dev example?
We can't just start from /
and crawl, like we do in adapter-static
, because we can't prerender /
— it's dynamic. We do have some options though:
svelte.config.js
or via the CLI (as currently happens with sapper export
)There could be a situation where prerenderable pages on a route with parameters (routes/foo/[bar].svelte
) are only reachable via a non-prerenderable index. In this case, the children are undiscoverable without a) specifying them as entry points, as above, or b) getStaticPaths, which I really don't love. I'm inclined to view these cases as sufficiently anomalous as to not worry about.
Thoughts on any of the above?
Every time I type 'server route' I wince a little bit; it's clunky phrasing that doesn't even make that much sense alongside 'serverless' and prerendering. I just haven't thought of anything better
Not entirely sure why, maybe the ws:
request doesn't make it past http-proxy
?
Something people seem to trip over a bit is the fact that session
, despite being a writable store, doesn't get persisted. I wonder if we can address that:
<script>
import { stores } from '$tbd';
const { session } = stores();
let name;
async function update_username(name) {
// optimistic — update the client-side store, then persist
// (rolls back in case of failure)
session.update($session => ({
...$session,
user: { ...$session.user, name }
}));
session.persist();
// pessimistic — wait until success before updating
// client-side store
session.persist($session => {
...$session,
user: { ...$session.user, name }
});
}
</script>
<!-- pretend i did this properly, with a progressively enhanced <form> -->
<input bind:value={name}>
<button on:click={() => update_username(name)}>
Update
</button>
This requires that the developer add some persistence logic. Perhaps in the same file where we define logic for getting a session (#9), we have a place to put logic for persistence:
// src/session/index.js
import { parse } from 'cookie';
import * as db from './db.js';
export async function get(headers) {
const cookies = parse(headers.cookie);
const user = await db.get(cookies.session_id);
return { user };
}
export async function persist(headers, data) {
const cookies = parse(headers.cookie);
const user = await db.set(cookies.session_id, data); // validate and store
return { user };
}
Glossing over some details but what do folks think?
I'm guessing this will just have to be installing our eslint config and adding a task in the root to lint all the packages.
I can't make head nor tail of prettier since it seems to say every file has issues, and that doesn't seem right.
It's confusing having a package called kit
in a monorepo called kit
. Though such a change might fall afoul of the reasons we called this thing Svelte Kit in the first place
We should make sure it works on Windows before release
// phase one — build with Snowpack
header('Creating unoptimized build...');
await exec(`rm -rf .svelte/build/unoptimized`);
Looks like most/all of 05442a2#diff-0ac36eb0a5cc3505c582d3959e4bb51093770685d0de4a864f7cd9b68bd50b8f got published on npm. But it only has @sveltejs/[email protected]
If a Svelte file in project code imports a built-in Node module, the build crashes with
> Cannot read property 'imports' of undefined
TypeError: Cannot read property 'imports' of undefined
at find_deps (/Users/andreasehrencrona/projects/svelte/kit/packages/kit/src/api/build/index.ts:190:1)
at /Users/andreasehrencrona/projects/svelte/kit/packages/kit/src/api/build/index.ts:199:1
at Array.forEach (<anonymous>)
To reproduce, just add e.g. import "path";
to a .svelte file and run the build.
It would be easy enough to avoid the crash by checking if chunk
is null, but I guess that will result in a runtime error instead. I'm not sure what a correct solution would look like; ideally there should be a compile error if you try to access a Node module.
1.0.8 adds must-revalidate
if maxAge: 0
At present, CSS handling is a step back from current Sapper — CSS is loaded in JS strings, and the old double-loading phenomenon is back (since the SSR'd page includes critical styles)
Describe the bug
If a component used in a route has a transition applied, it will delay navigation and potentially create UI errors.
To Reproduce
In a brand new installation, create a new component e.g. Linker.svelte
and include it in the index
route.
<!-- src/lib/Linker.svelte -->
<script>
import { fade } from "svelte/transition";
let showImage = false;
</script>
<div
on:mouseenter={() => {
showImage = true;
}}
on:mouseleave={() => {
showImage = false;
}}>
<h1>Wow hi!</h1>
<a href="/about">About</a>
{#if showImage}
<img
transition:fade={{ duration: 2000 }}
src="https://picsum.photos/20"
alt="" />
{/if}
</div>
<!-- src/routes/index.svelte -->
<script>
import Linker from "../lib/Linker.svelte";
</script>
...
<Linker />
<h1>Great success!</h1>
Moving the cursor on top of the link, a small picture will slowly fade in. Once clicking on the link, the image will slowly fade out again, but the /about
page content will already be appended to the <slot />
and visible in the bottom. If the user moves the cursor back on top of the link while it is fading out, it will just fade in again and prevent the component from being removed completely - causing UI errors.
Expected behavior
When navigating, default behaviour should be to just switch route. No delays nor attempts to finalise transitions or to show both routes in the <slot />
at the same time.
Information about your Sapper Installation:
Issue appears in both Chrome and Safari from what I have tested, but I expect this to be the same across all browsers.
Using iMac 2011 with OSX Mojave
Running in localhost
Current latest release v0.27.4
Svelte v3
dynamic application
Using Rollup
Severity
Mainly it is just annoying, and I can't really find a workaround to prevent it unless I just remove the transition from the image having it appear and disappear instantly.
Additional context
Maybe a modifier to the transition could be an idea. We currently have modifiers like once
or preventDefault
on click events. Maybe add a transition:fade|ignoreRoute={{duration: 200}}
or the quite opposite - having the developer purposely asking for route specific transition.
How does Kit relate to a router? How closely are they connected? Is it possible to extract the logic into an official Svelte router? Is it possible to plugin your own router?
Is your feature request related to a problem? Please describe.
When building a website I want pages to share a specific layout with common UI elements like a navigation, sidebar, footer, etc. Other pages might want to use different layouts like a custom landing page. This works fine as long as my landing page is not a sub page of an existing page with layout.
** MY SHOP **
src/routes
├── bikes
│ ├── _layout.svelte
│ ├── category
│ │ └── index.svelte
│ └── [slug]
│ ├── index.svelte
│ └── _layout.svelte
In this example /bikes
has a custom layout. Individual bike routes like /bikes/fancy-bike-3000
add their own layout and inherit the one from /bikes
.
The problem appears when I want to make a super fancy landing page to promote all my mountain bikes. This page is completely individual and uses its own layout.
** MY SHOP **
src/routes
├── bikes
│ ├── _layout.svelte
│ ├── category
│ │ ├── index.svelte
+ │ │ └── mountain-bikes-promo
+ │ │ └── index.svelte
│ └── [slug]
│ ├── index.svelte
│ └── _layout.svelte
/bikes/category/mountain-bike-promo
will always inherit the layout from /bikes
, but in this case it's not wanted.
Sapper needs a feature to bail out of the layout inheritance and let sub pages define their own layouts.
Describe the solution you'd like
I would like an API that allows me as developer to get more flexibility when needed but has the layout inheritance by default.
Ideas:
<!-- _layout.svelte -->
<script>
export let inherit = false;
</script>
layout/
folder with custom layouts that you can specify in your page (https://nuxtjs.org/api/pages-layout). This probably doesn't work well with the current inheritance model but I like it./* vue code */
export default {
layout: 'blog',
// OR
layout (context) {
return 'blog'
}
}
Describe alternatives you've considered
In the end it's always possible to import layout components like any other component on every page. This provides full flexibility but can be a bit cumbersome if something like the inheritance model already exists.
A different solution could be to structure routes differently but a site structure should ideally not be constrained by the framework of choice.
How important is this feature to you?
I think this is a use case every application of a particular size will eventually run into. May it be custom landing pages or interactive stories with data visualizations.
Additional context
There was a former discussion in this issue sveltejs/sapper#809.
The idea to hide elements in a root layout based on segment
doesn't really work since not the full route is available.
Looking at my example bikes/_layout.svelte
has segment === "category"
but mountain-bike-promos
is needed to know if layout components need to be hidden.
I look forward to any API discussion and can maybe come up with an implementation if this is a feature you want to integrate into Sapper.
Wondering about how deployment will work for this.
I'm happy to open PRs for a generic github action that I use across my own projects - lint and test on master, and publish on tag, for example. But when thinking about this, and considering all the adapters we are likely to have, etc...
My suggestion is to have a monorepo for this and all the associated modules, so that versions are automatically updated when required. In order to suggest something concrete, I'd say that:
The reason for rush is that it will automatically generate changelogs in markdown for us, and will provide a nice organised structure for all of the different modules now that Sapper is exploded. Also the default pnpm will play nicely with everything else, whilst providing a significant speed advantage over npm when working on the project.
Deployment of new versions can also be automated easily with Rush, and deployment of documentation can also be automated (and only released when the version is bumped - or via a manually triggered task) which I know is something that we hold dear.
Obviously it's the lesser known solution compared to others but then, we're are using Snowpack and Svelte.
Happy to produce a prototype if it helps make sense of this.
Discussing over on FredKSchott/snowpack#1316
i'm now the owner of https://www.npmjs.com/package/create-svelte
Just going to plonk this here from Discord:
@rich_harris idk if you've been given a demo of remix, but they're launching next week and would serve as good inspo for a "modern serverless" metaframework. i think the logic of encouraging developers to take advantage of browser caching is a sensible one, so long as we take care to give good defaults
me:
haven't been given a demo, no, but have been following development via the newsletter etc. at present there isn't a way for the developer to control caching. i've basically been following the advice in https://jakearchibald.com/2016/caching-best-practices/, which is 'hash static assets where possible and make them immutable, and 304 everything else'
anything else is a footgun
so SvelteKit automatically reads If-None-Match and serves 304s where necessary, though this does involve rendering
with the Node adapter at least, generated files are immutable and static files are 304'd where possible. for other platforms it's just relying on CDNs doing the right thing, though i'm sure more fine-grained control is possible
would like to offer some level of control for the power users but that's the basic strategy
(I should note: server routes can control cache headers, it's just pages that currently can't)
he demoed it for us at AWS but the basic cache control tenets he wants to use are here - https://www.youtube.com/watch?v=3XkU_DXcgl0 basic idea is to only run SSR on request, inside a serverless function (eschewing SSG entirely) and encourage devs to define etags and cache control headers on the pages served. hence you never run into issues of long builds because you A) never build every page at once and B) build each page on request only once. he gives the example of the Stripe docs, where if you navigate back and forth between two different pages, you have to spend a couple seconds waiting for the page to load every time even if you had visited the page before. with more control over browser caching that navigation becomes instant.
me:
yeah i watched that video the other night, and it actually prompted me to add 304s to SvelteKit's SSR. but i don't really understand why eschewing SSG makes sense — you still need to render the page to deliver a 304. with SSG/prerendering 304s are almost free
jonatansberg:
The idea is to have your cache/CDN serve those 304s for the SSRed content as long as it stays fresh, meaning you have the more or less the same scenario as with SSG. That way you can optionally have the cache/CDN refresh the expired page/asset in the background if you use stale-while-revalidate caching. This means you would at most hit the server only once per cache interval – less for content that receives low traffic – and the cache/CDN can continue to serve 304s if nothing changed. Vercel tried to popularise this approach a while back, calling it Serverless Pre-Rendering.
In many ways you can consider SPR a simpler pull based version of what Next's and Gatsby's partial/dynamic "push" based rebuilds try to achieve. Given that simplicity I think SPR makes a lot more sense than SSG for most bigger sites, but thats not where the marketing hype is, so, yeah.. 🤷♂️
SSG + dynamic rebuilds will get you to the same place, but with much added complexity. SPR can be implemented by just adding a cache control header and (maybe) switching CDNs or by adding a local cache + reverse proxy (e.g. nginx, HAProxy, etc).
If you pair this approach with serverless rendering (i.e. SPR) or use CF Workers the frontend load wont be an issue, even if e.g. GoogleBot hits all your routes all at once. It might still put a strain on your underlying/backend systems, but still no where near the load of traditional uncached SSR or continuous full SSG rebuilds on every change.
Also Svelte SSR is basically free (we have seen about 10 ms CPU time per request on average hardware) meaning you can easily serve ~100 requests per second (6 000 requests per minute) per Node SSR process(!!).
Put another way, with adequate caching in front of it, you could run SSR/SPR with Svelte on a toaster :slight_smile:
Should be a relatively easy fix. Haven't tested but would guess it also doesn't support __dirname
and __filename
and maybe some other things
The adapter-vercel
package completely obliterates the existing vercel.json
file.
Instead it should be write the necessary values into the JSON object, only after checking that the necessary keys/values don't already exist.
Option B is to add some kind of adapter-vercel
config or CLI flag that effectively acts like --skip-config
Describe the bug
onMount
is not fired on page change, if the actual page component doesn't change. This is all routes which contain [dynamic] parameters, since the underlying components are the same.
To Reproduce
Reproduction here:
https://github.com/beyonk-adventures/sapper-onmount-issue
The colour pages set the value from the path parameters on load, and onMount sets the value in the page. Because onMount isn't refired, the colour never changes when clicking between pages, only on hard refresh, or when visiting the home page and then clicking a new colour.
Essentially it means you can't change the content of the page unless you visit an url which results in a completely different page component being loaded.
Expected behavior
Though using the same physical page component, /different/routes
and /different/paths
which map to /different/[parameter].svelte
should fire onMount, since the route is changing.
Information about your Sapper Installation:
Severity
Extremely High. I don't actually know how to work around this.
Additional context
Fixing this would be a breaking change, but I think it's far more logical an approach than the current behaviour.
I tried npm run dev
as well as node build; node build/index.js
and didn't see the link
preload header in the hn.svelte.dev sample app that's available on https://hn.svelte.dev/top/1 today
'static' is a word with lots of different meanings. Maybe something like adapter-jamstack
instead? Or is that unlikely to stand the test of time?
A file that imports a Svelte component...
import App from './App.svelte';
...gets rewritten by Snowpack thusly:
import App from './App.svelte.js';
This import will fail, because Snowpack only wants to serve App.js
. Needs a bit more investigation but at some point if I can make a minimal repro I'll raise an issue on the Snowpack issue tracker.
We should... probably have some
One of the most impactful changes that we made in the Sapper codebase recently was replacing rollup/plugin-sucrase
with rollup-plugin-typescript2
. It uncovered lots of type checking errors and made the codebase far more robust because sucrase doesn't do any type checking. There was some concern about speed but rollup-plugin-typescript2
allows you to pass a flag that disables type checking, so we did that by default, which was just as fast (or faster? I can't remember), but then added a command in package.json
that does type checking which we're able to run on the GitHub Actions CI
E.g. compare https://github.com/sveltejs/kit/tree/master/packages/kit/src/client vs https://github.com/sveltejs/sapper/tree/master/runtime/src/app
We'll have to come up with a strategy for this because there have been a lot of fixes and improvements recently. I can slow down on merging Sapper stuff if it makes it easier to manage
Since endpoint handlers don't get the req
object, but rather a normalized { host, path, params, query, body }
object (still not sure about host
, and I guess it should probably also get headers
), adapters (and the dev server) will need to parse the body before calling render
.
In Lambda environments...
exports.handler = async function(event) {
// event.body is a 'A JSON string of the request payload.'
// event.isBase64Encoded is "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
...we presumably need to JSON.parse(event.body)
, except when it's base64 encoded in which case we need to do... something else? No idea what happens to form data etc. But after you've jumped through whatever hoops, the body is available.
In Vercel, req.body
is already populated according to the following rules:
Content-Type header | Value of req.body |
---|---|
No header | undefined |
application/json | An object representing the parsed JSON sent by the request. |
application/x-www-form-urlencoded | An object representing the parsed data sent by with the request. |
text/plain | A string containing the text sent by the request. |
application/octet-stream | A Buffer containing the data sent by the request. |
In adapter-node and the dev server, we need to figure this out ourselves, possibly using body-parser and something like multer?
I think the goal should be for the behaviour to be as similar as possible between environments. Vercel's approach seems like something we could adopt for adapter-node and the dev server. Are there any bases that aren't covered by that approach?
In #46 (comment) @Conduitry said
I don't currently have a situation in mind where having access to the HTTP headers (for session) would be insufficient, or where having access to HTTP headers and a readable stream (for endpoints) would be insufficient - I'm concerned about a situation where someone only ever intends to build a Node server and really just wants to be able to access
req
andres
directly, and can't
which is fair. My instinct though is that it is worth normalizing, and the fact that Lambda and Vercel both pass an already-constructed body to handlers is strong evidence that streamable bodies are more of an academic benefit than a real one.
Properbly not the right repo to raise this but I thought you should be aware of some delicate dependency setup right now
Changes to svelte can break svelte-hmr (https://github.com/rixo/svelte-hmr/issues/17), which in turn breaks the snowpack plugin and then kit.
Thoughts:
At least there should be a testsuite covering hmr working correctly.
At best svelte-hmr moves closer to svelte domain or in the long run have it integrated in the compiler?
cc @rixo
We've somewhat glossed over the problem of internationalisation up till now. Frankly this is something SvelteKit isn't currently very good at. I'm starting to think about how to internationalise/localise https://svelte.dev, to see which parts can be solved in userland and which can't.
(For anyone unfamiliar: 'Internationalisation' or i18n refers to the process of making an app language agnostic; 'localisation' or l10n refers to the process of creating individual translations.)
This isn't an area I have a lot of experience in, so if anyone wants to chime in — particularly non-native English speakers and people who have dealt with these problems! — please do.
Where we're currently at: the best we can really do is put everything inside src/routes/[lang]
and use the lang
param in preload
to load localisations (an exercise left to the reader, albeit a fairly straightforward one). This works, but leaves a few problems unsolved.
I think we can do a lot better. I'm prepared to suggest that SvelteKit should be a little opinionated here rather than abdicating responsibility to things like i18next
, since we can make guarantees that a general-purpose framework can't, and can potentially do interesting compile-time things that are out of reach for other projects. But I'm under no illusions about how complex i18n can be (I recently discovered that a file modified two days ago will be labeled 'avant-hier' on MacOS if your language is set to French; most languages don't even have a comparable phrase. How on earth do you do that sort of thing programmatically?!) which is why I'm anxious for community input.
Some websites make the current language explicit in the pathname, e.g. https://example.com/es/foo or https://example.com/zh/foo. Sometimes the default is explicit (https://example.com/en/foo), sometimes it's implicit (https://example.com/foo). Others (e.g. Wikipedia) use a subdomain, like https://cy.example.com. Still others (Amazon) don't make the language visible, but store it in a cookie.
Having the language expressed in the URL seems like the best way to make the user's preference unambiguous. I prefer /en/foo
to /foo
since it's explicit, easier to implement, and doesn't make other languages second-class citizens. If you're using subdomains then you're probably running separate instances of an app, which means it's not SvelteKit's problem.
There still needs to be a way to detect language if someone lands on /
. I believe the most reliable way to detect a user's language preference on the server is the Accept-Language
header (please correct me if nec). Maybe this could automatically redirect to a supported localisation (see next section).
It's useful for SvelteKit to know at build time which localisations are supported. This could perhaps be achieved by having a locales
folder (configurable, obviously) in the project root:
locales
|- de.json
|- en.json
|- fr.json
|- ru.json
src
|- routes
|- ...
Single-language apps could simply omit this folder, and behave as they currently do.
The <html>
element should ideally have a lang
attribute. If SvelteKit has i18n built in, we could achieve this the same way we inject other variables into src/template.html
:
<html lang="%svelte.lang%">
If we have localisations available at build time, we can localise URLs themselves. For example, you could have /en/meet-the-team
and /de/triff-das-team
without having to use a [parameter]
in the route filename. One way we could do this is by encasing localisation keys in curlies:
src
|- routes
|- index.svelte
|- {meet_the_team}.svelte
In theory, we could generate a different route manifest for each supported language, so that English-speaking users would get a manifest with this...
{
// index.svelte
pattern: /^\/en\/?$/,
parts: [...]
},
{
// {meet_the_team}.svelte
pattern: /^\/en/meet-the-team\/?$/,
parts: [...]
}
...while German-speaking users download this instead:
{
// index.svelte
pattern: /^\/de\/?$/,
parts: [...]
},
{
// {meet_the_team}.svelte
pattern: /^\/de/triff-das-team\/?$/,
parts: [...]
}
I think the best way to make the translations themselves available inside components is to use a store:
<script>
import { t } from '$app/stores';
</script>
<h1>{$t.hello_world}</h1>
Then, if you've got files like these...
// locales/en.json
{ "hello_world": "Hello world" }
// locales/fr.json
{ "hello_world": "Bonjour le monde" }
...SvelteKit can load them as necessary and coordinate everything. There's probably a commonly-used format for things like this as well — something like "Willkommen zurück, $1"
:
<p>{$t.welcome_back(name)}</p>
(In development, we could potentially do all sorts of fun stuff like making $t
be a proxy that warns us if a particular translation is missing, or tracks which translations are unused.)
We probably wouldn't want to put all the localisations in locales/xx.json
— just the stuff that's needed globally. Perhaps we could have something like this:
locales
|- de.json
|- en.json
|- fr.json
|- ru.json
src
|- routes
|- settings
|- _locales
|- de.json
|- en.json
|- fr.json
|- ru.json
|- index.svelte
Again, we're in the fortunate position that SvelteKit can easily coordinate all the loading for us, including any necessary build-time preparation. Here, any keys in src/routes/settings/_locales/en.json
would take precedence over the global keys in locales/en.json
.
It's probably best if SvelteKit doesn't have too many opinions about how content (like blog posts) should be translated, since this is an area where you're far more likely to need to e.g. talk to a database, or otherwise do something that doesn't fit neatly into the structure we've outlined. Here again, there's an advantage to having the current language preference expressed in the URL, since userland middleware can easily extract that from req.path
and use that to fetch appropriate content. (I guess we could also set a req.lang
property or something if we wanted?)
Sapper (ab)used the <base>
element to make it easy to mount apps on a path other than /
. <base>
could also include the language prefix so that we don't need to worry about it when creating links:
<!-- with <base href="de">, this would link to `/de/triff-das-team` -->
<a href={$t.meet_the_team}>{$t.text.meet_the_team}</a>
Base URLs haven't been entirely pain-free though, so this might warrant further thought.
Having gone through this thought process I'm more convinced than ever that SvelteKit should have i18n built in. We can make it so much easier to do i18n than is currently possible with libraries, with zero boilerplate. But this could just be arrogance and naivety from someone who hasn't really done this stuff before, so please do help fill in the missing pieces.
Watching Ron Au's (amazing) Svelte Summit talk reminded me that we need to integrate the REPL with Svelte Kit, since it's currently based on the hopefully-soon-to-be-deprecated sveltejs/template
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.