initial commit

This commit is contained in:
Rich Harris
2017-12-11 08:55:21 -05:00
commit e01ff8cb6b
8 changed files with 493 additions and 0 deletions

90
README.md Normal file
View File

@@ -0,0 +1,90 @@
# 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.