2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00
2017-12-11 08:55:21 -05:00

sapper

Combat-ready apps, engineered by Svelte.

This is not a thing yet

If you visit this README in a few weeks, hopefully it will have blossomed into the app development framework we deserve. Right now, it's just a set of ideas.


Next.js introduced a beautiful idea — that you should be able to build your app as universal React components in a special pages directory, and the framework should take care of routing and rendering on both client and server. What if we did the same thing for Svelte?

High-level goals:

  • Extreme ease of development
  • Code-splitting and HMR out of the box (probably via webpack)
  • Best-in-class performance
  • As little magic as possible. Anyone should be able to understand how everything fits together, and e.g. make changes to the webpack config

Design

A Sapper app is just an Express app (conventionally, server.js) that uses the sapper middleware:

const app = require('express')();
const sapper = require('sapper');

const app = express();

app.use(sapper());

const { PORT = 3000 } = process.env;
app.listen(PORT, () => {
  console.log(`listening on port ${PORT}`);
});

The middleware serves pages that match files in the routes directory, and assets generated by webpack. In development mode, the middleware once activated watches routes to keep the app up-to-date.

Routing

Like Next, routes are defined by the project directory structure, but with some crucial differences:

  • Files with an .html extension are treated as Svelte components. The routes/about.html (or routes/about/index.html) would create the /about route.
  • Files with a .js or .mjs extension are more generic route handlers. These files should export functions corresponding to the HTTP methods they support (example below).
  • Instead of route masking, we embed parameters in the filename. For example post/%id%.html maps to /post/:id, and the component will be rendered with the appropriate parameter.
  • Nested routes (read this article) can be handled by creating a file that matches the subroute — for example, routes/app/settings/%submenu%.html would match /app/settings/profile and app/settings, but in the latter case the submenu parameter would be null.

An example of a generic route:

// routes/api/post/%id%.js
export async function get(req, res) {
  try {
    const data = await getPostFromDatabase(req.params.id);
    const json = JSON.stringify(data);

    res.set({
      'Content-Type': 'application/json',
      'Content-Length': json.length
    });

    res.send(json);
  } catch (err) {
    res.status(500).send(err.message);
  }
}

Or, if you omit the res argument, it can use the return value:

// routes/api/post/%id%.js
export async function get(req, res) {
  return await getPostFromDatabase(req.params.id);
}

Things to figure out

  • How to customise the overall page template
  • An equivalent of getInitialProps
  • Critical CSS
  • store integration
  • Route transitions
  • Equivalent of next export
  • ...and lots of other things that haven't occurred to me yet.
Description
No description provided
Readme MIT 3.4 MiB
Languages
TypeScript 58.1%
JavaScript 32%
Svelte 5.8%
HTML 3.3%
CSS 0.8%