# 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](https://github.com/zeit/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: ```js 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](https://joshduff.com/2015-06-why-you-need-a-state-router.md)) 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: ```js // 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: ```js // 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.