diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b28f316 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2017 [these people](https://github.com/sveltejs/sapper/graphs/contributors). + +Permission is hereby granted by the authors of this software, to any person, to use the software for any purpose, free of charge, including the rights to run, read, copy, change, distribute and sell it, and including usage rights to any patents the authors may hold on it, subject to the following conditions: + +This license, or a link to its text, must be included with all copies of the software and any derivative works. + +Any modification to the software submitted to the authors may be incorporated into the software under the terms of this license. + +The software is provided "as is", without warranty of any kind, including but not limited to the warranties of title, fitness, merchantability and non-infringement. The authors have no obligation to provide support or updates for the software, and may not be held liable for any damages, claims or other liability arising from its use. \ No newline at end of file diff --git a/README.md b/README.md index 2f0c954..87d57a6 100644 --- a/README.md +++ b/README.md @@ -1,150 +1,37 @@ # 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 -* Links are just `` tags, no special `` components +[Military-grade progressive web apps, powered by Svelte.](https://sapper.svelte.technology) -## Design +## What is Sapper? -A Sapper app is just an Express app (conventionally, `server.js`) that uses the `sapper` middleware: +Sapper is a framework for building high-performance universal web apps. [Read the guide](https://sapper.svelte.technology/guide) or the [introductory blog post](https://svelte.technology/blog/sapper-towards-the-ideal-web-app-framework) to learn more. -```js -const app = require('express')(); -const sapper = require('sapper'); -app.use(sapper()); +## Get started -const { PORT = 3000 } = process.env; -app.listen(PORT, () => { - console.log(`listening on port ${PORT}`); -}); +Clone the [starter project template](https://github.com/sveltejs/sapper-template) with [degit](https://github.com/rich-harris/degit)... + +```bash +npx degit sveltejs/sapper-template my-app ``` -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. +...then install dependencies and start the dev server... - -## 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); - } -} +```bash +cd my-app +npm install +npm run dev ``` -Or, if you omit the `res` argument, it can use the return value: +...and navigate to [localhost:3000](http://localhost:3000). To build and run in production mode: -```js -// routes/api/post/[id].js -export async function get(req) { - return await getPostFromDatabase(req.params.id); -} +```bash +npm run build +npm start ``` -## Client-side app +## License -Sapper will create (and in development mode, update) a barebones `main.js` file that dynamically imports individual routes and renders them — something like this: - -```js -window.addEventListener('click', event => { - let a = event.target; - while (a && a.nodeName !== 'A') a = a.parentNode; - if (!a) return; - - if (navigate(new URL(a.href))) event.preventDefault(); -}); - -const target = document.querySelector('#sapper'); -let component; - -function navigate(url) { - if (url.origin !== window.location.origin) return; - - let match; - let params = {}; - const query = {}; - - function render(mod) { - if (component) { - component.destroy(); - } else { - target.innerHTML = ''; - } - - component = new mod.default({ - target, - data: { query, params }, - hydrate: !!component - }); - } - - if (url.pathname === '/about') { - import('/about/index.html').then(render); - } else if (url.pathname === '/') { - import('/index.js').then(render); - } else if (match = /^\/post\/([^\/]+)$/.exec(url.pathname)) { - params.id = match[1]; - import('/post/[id].html').then(render); - } else if (match = /^\/([^\/]+)$/.exec(url.pathname)) { - params.wildcard = match[1]; - import('/[wildcard].html').then(render); - } - - return true; -} - -navigate(window.location); -``` - -We're glossing over a lot of important stuff here — e.g. handling `popstate` — but you get the idea. Knowledge of all the possible routes means we can generate optimal code, much in the same way that statically analysing Svelte templates allows the compiler to generate optimal code. - - -## 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` -* A good story for realtime/GraphQL stuff -* Service worker -* Using `Link...rel=preload` headers to push main.js/[route].js plus styles -* ...and lots of other things that haven't occurred to me yet. \ No newline at end of file +[LIL](LICENSE) \ No newline at end of file