Code Monkey home page Code Monkey logo

kit's People

Contributors

antony avatar baseballyama avatar benmccann avatar bluwy avatar conduitry avatar dominikg avatar dummdidumm avatar ehrencrona avatar eltigerchino avatar geoffrich avatar ghostdevv avatar github-actions[bot] avatar grygrflzr avatar gtm-nayan avatar hyunbinseo avatar ignatiusmb avatar ivanhofer avatar jeanjpnm avatar lukeed avatar mrkishi avatar ph4ntomiki avatar pngwn avatar puruvj avatar renovate[bot] avatar repsac-by avatar rich-harris avatar rmunn avatar stalkerg avatar tanhauhau avatar tcc-sejohnson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kit's Issues

Svelte transition impacts navigation

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.

ezgif-4-99f035a10dee

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.

A way of intercepting requests and populating `session`

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.

Consider replacing rollup/plugin-sucrase with rollup-plugin-typescript2

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

CSS handling

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)

Replace `this.fetch` with `this.get`

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:

  • Sapper doesn't currently handle URLs relative to the requested page (though this could easily change, and has in fact changed in the current app-utils)
  • The whole res = await this.fetch(...); data = await res.json() dance is a bit of a pain, honestly
  • It's easy to forget that you need credentials: '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>
  • Only works with server routes, not static files or external resources. Simpler
  • Accepts a URL (easiest way to identify server routes, which will of course continue to work with plain fetch), which can be relative to the requested page
  • Always includes credentials
  • Returns a promise for the body of the response. If the Content-Type is application/json, is JSON.parsed. If it's text/*, is returned as text. Otherwise is returned as an array buffer
  • Non-200 responses throw an error (augmented with status 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?

Rename `@sveltejs/kit` -> `@sveltejs/cli`?

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

Before and after hooks for routing

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.

Migration Path

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:

  • Remove rollup.config.js or webpack.config.js. Add any custom config to vite.config.js
  • Replace imports from @sapper/app to imports from... wherever we end up putting stuff ($app?)
  • If you have src/node_modules/components, move it to src/components and update the import path to $components
  • Move any custom server.js/client.js code to [TBD]
  • Update endpoints (no more req/res)
  • load (fka preload) (#272)
  • render / prerender methods
  • #svelte target element (#157).
  • preloading / navigating store (#215)
  • stores vs getStores (#187)
  • adapters and svelte-kit adapt (#221)
  • Svelte-specific prefix (#288)
  • error and layout pages prefixed by $ instead of _

Importing a Node module crashes the build with unhelpful message

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.

Treat layout pages like components when using named slots

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?

process.browser etc

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?

Relative paths considered relative to the current url (differs from Sapper behaviour)

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.

Node adapter build output expects `prerendered` directory to exist, which is might not

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.

i18n brainstorming

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.


Language detection/URL structure

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).

Supported localisations

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.

lang attribute

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%">

Localised URLs

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: [...]
}

Localisation in components

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.)

Route-scoped localisations

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.

Translating content

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?)

Base URLs

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.

Feature request: Reset layout inheritance in nested routes

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:

  • Use a special prop in layout components, that tell Sapper to not include layouts up in the file tree. Maybe something like this:
<!-- _layout.svelte -->
<script>
  export let inherit = false;
</script>
  • Nuxt.js has a 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.

Importing .svelte files fails unless the request is modified

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.

Optional params/zero-length rest routes

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.

Use @snowpack/plugin-svelte

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

HMR doesn't work

Not entirely sure why, maybe the ws: request doesn't make it past http-proxy?

Prerender static files where possible

e.g. in the case of sveltejs/hn.svelte.dev#27, the 'about' page could be prerendered.

It requires answering two questions:

How do we know whether a given page is prerenderable?

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.

How do we find pages in the first place, to test whether or not they are prerenderable?

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:

  • We could specify a list of entry points in svelte.config.js or via the CLI (as currently happens with sapper export)
  • We could look at all pages in the manifest that don't have parameters
  • Both of the above

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?

REPL integration

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

Router

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?

relationship to svelte-hmr

Properbly not the right repo to raise this but I thought you should be aware of some delicate dependency setup right now

  • kit depends on snowpack plugin svelte which depends on svelte-hmr
  • kit and svelte-hmr depend on svelte
  • svelte-hmr needs to use svelte internals to work correctly

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

Body parsing

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 and res 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.

Caching

Just going to plonk this here from Discord:

@swyx:

@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)

@swyx:

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:

Continuous deployment

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:

  • RushJS monorepo with a structure for core modules (/libraries), adapters (/adapters), site (/site) as well as anything else required.
  • PNPM (rush default) in order to make managing the monorepo sane

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.

Template tests

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

Windows support

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`);

Respect existing deploy config

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

Rename adapter-static?

'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?

sessions

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?

prettier / eslint

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.

Aliases

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';

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.