diff --git a/.netlify/state.json b/.netlify/state.json new file mode 100644 index 0000000..b42b98b --- /dev/null +++ b/.netlify/state.json @@ -0,0 +1,3 @@ +{ + "siteId": "a6a3cfed-c34e-42f7-9616-07ad2eedb15f" +} \ No newline at end of file diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 0000000..ed567f2 --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +node_modules +yarn-error.log +/cypress/screenshots/ +/__sapper__/ diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..d120200 --- /dev/null +++ b/site/README.md @@ -0,0 +1,88 @@ +# sapper-template + +The default [Sapper](https://github.com/sveltejs/sapper) template, with branches for Rollup and webpack. To clone it and get started: + +```bash +# for Rollup +npx degit sveltejs/sapper-template#rollup my-app +# for webpack +npx degit sveltejs/sapper-template#webpack my-app +cd my-app +npm install # or yarn! +npm run dev +``` + +Open up [localhost:3000](http://localhost:3000) and start clicking around. + +Consult [sapper.svelte.technology](https://sapper.svelte.technology) for help getting started. + + +## Structure + +Sapper expects to find two directories in the root of your project — `src` and `static`. + + +### src + +The [src](src) directory contains the entry points for your app — `client.js`, `server.js` and (optionally) a `service-worker.js` — along with a `template.html` file and a `routes` directory. + + +#### src/routes + +This is the heart of your Sapper app. There are two kinds of routes — *pages*, and *server routes*. + +**Pages** are Svelte components written in `.html` files. When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel. (Sapper will preload and cache the code for these subsequent pages, so that navigation is instantaneous.) + +**Server routes** are modules written in `.js` files, that export functions corresponding to HTTP methods. Each function receives Express `request` and `response` objects as arguments, plus a `next` function. This is useful for creating a JSON API, for example. + +There are three simple rules for naming the files that define your routes: + +* A file called `src/routes/about.html` corresponds to the `/about` route. A file called `src/routes/blog/[slug].html` corresponds to the `/blog/:slug` route, in which case `params.slug` is available to the route +* The file `src/routes/index.html` (or `src/routes/index.js`) corresponds to the root of your app. `src/routes/about/index.html` is treated the same as `src/routes/about.html`. +* Files and directories with a leading underscore do *not* create routes. This allows you to colocate helper modules and components with the routes that depend on them — for example you could have a file called `src/routes/_helpers/datetime.js` and it would *not* create a `/_helpers/datetime` route + + +### static + +The [static](static) directory contains any static assets that should be available. These are served using [sirv](https://github.com/lukeed/sirv). + +In your [service-worker.js](app/service-worker.js) file, you can import these as `files` from the generated manifest... + +```js +import { files } from '../__sapper__/service-worker.js'; +``` + +...so that you can cache them (though you can choose not to, for example if you don't want to cache very large files). + + +## Bundler config + +Sapper uses Rollup or webpack to provide code-splitting and dynamic imports, as well as compiling your Svelte components. With webpack, it also provides hot module reloading. As long as you don't do anything daft, you can edit the configuration files to add whatever plugins you'd like. + + +## Production mode and deployment + +To start a production version of your app, run `npm run build && npm start`. This will disable live reloading, and activate the appropriate bundler plugins. + +You can deploy your application to any environment that supports Node 8 or above. As an example, to deploy to [Now](https://zeit.co/now), run these commands: + +```bash +npm install -g now +now +``` + + +## Using external components with webpack + +When using Svelte components installed from npm, such as [@sveltejs/svelte-virtual-list](https://github.com/sveltejs/svelte-virtual-list), Svelte needs the original component source (rather than any precompiled JavaScript that ships with the component). This allows the component to be rendered server-side, and also keeps your client-side app smaller. + +Because of that, it's essential that webpack doesn't treat the package as an *external dependency*. You can either modify the `externals` option in [webpack/server.config.js](webpack/server.config.js), or simply install the package to `devDependencies` rather than `dependencies`, which will cause it to get bundled (and therefore compiled) with your app: + +```bash +yarn add -D @sveltejs/svelte-virtual-list +``` + + +## Bugs and feedback + +Sapper is in early development, and may have the odd rough edge here and there. Please be vocal over on the [Sapper issue tracker](https://github.com/sveltejs/sapper/issues). diff --git a/site/appveyor.yml b/site/appveyor.yml new file mode 100644 index 0000000..e75da3b --- /dev/null +++ b/site/appveyor.yml @@ -0,0 +1,18 @@ +version: "{build}" + +shallow_clone: true + +init: + - git config --global core.autocrlf false + +build: off + +environment: + matrix: + # node.js + - nodejs_version: stable + +install: + - ps: Install-Product node $env:nodejs_version + - npm install cypress + - npm install diff --git a/site/config.js b/site/config.js new file mode 100644 index 0000000..db59814 --- /dev/null +++ b/site/config.js @@ -0,0 +1,3 @@ +export const SLUG_PRESERVE_UNICODE = false; +export const SLUG_SEPARATOR = '_'; +export const SLUG_LANG = 'en'; \ No newline at end of file diff --git a/site/content/docs/00-introduction.md b/site/content/docs/00-introduction.md new file mode 100644 index 0000000..2f971fc --- /dev/null +++ b/site/content/docs/00-introduction.md @@ -0,0 +1,52 @@ +--- +title: Introduction +--- + +### Before we begin + +> Sapper is in early development, and some things may change before we hit version 1.0. This document is a work-in-progress. If you get stuck, reach out for help in the [Discord chatroom](https://discord.gg/yy75DKs). +> +> See the [migration guides](migrating) for help upgrading from older versions. + +### What is Sapper? + +Sapper is a framework for building extremely high-performance web apps. You're looking at one right now! There are two basic concepts: + +* Each page of your app is a [Svelte](https://svelte.dev) component +* You create pages by adding files to the `src/routes` directory of your project. These will be server-rendered so that a user's first visit to your app is as fast as possible, then a client-side app takes over + +Building an app with all the modern best practices — code-splitting, offline support, server-rendered views with client-side hydration — is fiendishly complicated. Sapper does all the boring stuff for you so that you can get on with the creative part. + +You don't need to know Svelte to understand the rest of this guide, but it will help. In short, it's a UI framework that compiles your components to highly optimized vanilla JavaScript. Read the [introductory blog post](https://svelte.dev/blog/svelte-3-rethinking-reactivity) and the [tutorial](https://svelte.dev/tutorial) to learn more. + + +### Why the name? + +In war, the soldiers who build bridges, repair roads, clear minefields and conduct demolitions — all under combat conditions — are known as *sappers*. + +For web developers, the stakes are generally lower than for combat engineers. But we face our own hostile environment: underpowered devices, poor network connections, and the complexity inherent in front-end engineering. Sapper, which is short for Svelte app maker, is your courageous and dutiful ally. + + +### Comparison with Next.js + +[Next.js](https://github.com/zeit/next.js) is a React framework from [Zeit](https://zeit.co), and is the inspiration for Sapper. There are a few notable differences, however: + +* Sapper is powered by Svelte instead of React, so it's faster and your apps are smaller +* Instead of route masking, we encode route parameters in filenames (see the [routing](docs#routing) section below) +* As well as *pages*, you can create *server routes* in your `src/routes` directory. This makes it very easy to, for example, add a JSON API such as the one powering this very page (try visiting [/docs.json](/docs.json)) +* Links are just `` elements, rather than framework-specific `` components. That means, for example, that [this link right here](/), despite being inside a blob of markdown, works with the router as you'd expect + + +### Getting started + +The easiest way to start building a Sapper app is to clone the [sapper-template](https://github.com/sveltejs/sapper-template) repo with [degit](https://github.com/Rich-Harris/degit): + +```bash +npx degit sveltejs/sapper-template#rollup my-app +# or: npx degit sveltejs/sapper-template#webpack my-app +cd my-app +npm install +npm run dev +``` + +This will scaffold a new project in the `my-app` directory, install its dependencies, and start a server on [localhost:3000](http://localhost:3000). Try editing the files to get a feel for how everything works – you may not need to bother reading the rest of this guide! diff --git a/site/content/docs/01-structure.md b/site/content/docs/01-structure.md new file mode 100644 index 0000000..ce7b801 --- /dev/null +++ b/site/content/docs/01-structure.md @@ -0,0 +1,112 @@ +--- +title: Sapper app structure +--- + +This section is a reference for the curious. We recommend you play around with the project template first, and come back here when you've got a feel for how things fit together. + +If you take a look inside the [sapper-template](https://github.com/sveltejs/sapper-template) repo, you'll see some files that Sapper expects to find: + +```bash +├ package.json +├ src +│ ├ routes +│ │ ├ # your routes here +│ │ ├ _error.svelte +│ │ └ index.svelte +│ ├ client.js +│ ├ server.js +│ ├ service-worker.js +│ └ template.html +├ static +│ ├ # your files here +└ rollup.config.js / webpack.config.js +``` + +When you first run Sapper, it will create an additional `__sapper__` directory containing generated files. + +You'll notice a few extra files and a `cypress` directory which relates to [testing](docs#testing) — we don't need to worry about those right now. + +> You *can* create these files from scratch, but it's much better to use the template. See [getting started](docs#getting-started) for instructions on how to easily clone it + + +### package.json + +Your package.json contains your app's dependencies and defines a number of scripts: + +* `npm run dev` — start the app in development mode, and watch source files for changes +* `npm run build` — build the app in production mode +* `npm run export` — bake out a static version, if applicable (see [exporting](docs#exporting)) +* `npm start` — start the app in production mode after you've built it +* `npm test` — run the tests (see [testing](docs#testing)) + + +### src + +This contains the three *entry points* for your app — `src/client.js`, `src/server.js` and (optionally) `src/service-worker.js` — along with a `src/template.html` file. + +#### src/client.js + +This *must* import, and call, the `start` function from the generated `@sapper/app` module: + +```js +import * as sapper from '@sapper/app'; + +sapper.start({ + target: document.querySelector('#sapper') +}); +``` + +In many cases, that's the entirety of your entry module, though you can do as much or as little here as you wish. See the [client API](docs#client-api) section for more information on functions you can import. + + +#### src/server.js + +This is a normal Express (or [Polka](https://github.com/lukeed/polka), etc) app, with three requirements: + +* it should serve the contents of the `static` folder, using for example [sirv](https://github.com/lukeed/sirv) +* it should call `app.use(sapper.middleware())` at the end, where `sapper` is imported from `@sapper/server` +* it must listen on `process.env.PORT` + +Beyond that, you can write the server however you like. + + +#### src/service-worker.js + +Service workers act as proxy servers that give you fine-grained control over how to respond to network requests. For example, when the browser requests `/goats.jpg`, the service worker can respond with a file it previously cached, or it can pass the request on to the server, or it could even respond with something completely different, such as a picture of llamas. + +Among other things, this makes it possible to build applications that work offline. + +Because every app needs a slightly different service worker (sometimes it's appropriate to always serve from the cache, sometimes that should only be a last resort in case of no connectivity), Sapper doesn't attempt to control the service worker. Instead, you write the logic in `service-worker.js`. You can import any of the following from `@sapper/service-worker`: + +* `files` — an array of files found in the `static` directory +* `shell` — the client-side JavaScript generated by the bundler (Rollup or webpack) +* `routes` — an array of `{ pattern: RegExp }` objects you can use to determine whether a Sapper-controlled page is being requested +* `timestamp` — the time the service worker was generated (useful for generating unique cache names) + + +#### src/template.html + +This file is a template for responses from the server. Sapper will inject content that replaces the following tags: + +* `%sapper.base%` — a `` element (see [base URLs](docs#base-urls)) +* `%sapper.styles%` — critical CSS for the page being requested +* `%sapper.head%` — HTML representing page-specific `` contents, like `` +* `%sapper.html%` — HTML representing the body of the page being rendered +* `%sapper.scripts%` — script tags for the client-side app + + +### src/routes + +This is the meat of your app — the pages and server routes. See the section on [routing](docs#routing) for the juicy details. + + +### static + +This is a place to put any files that your app uses — fonts, images and so on. For example `static/favicon.png` will be served as `/favicon.png`. + +Sapper doesn't serve these files — you'd typically use [sirv](https://github.com/lukeed/sirv) or [serve-static](https://github.com/expressjs/serve-static) for that — but it will read the contents of the `static` folder so that you can easily generate a cache manifest for offline support (see [service-worker.js](docs#templates-service-worker-js)). + + +### rollup.config.js / webpack.config.js + +Sapper can use [Rollup](https://rollupjs.org/) or [webpack](https://webpack.js.org/) to bundle your app. You probably won't need to change the config, but if you want to (for example to add new loaders or plugins), you can. diff --git a/site/content/docs/02-routing.md b/site/content/docs/02-routing.md new file mode 100644 index 0000000..d587f59 --- /dev/null +++ b/site/content/docs/02-routing.md @@ -0,0 +1,127 @@ +--- +title: Routing +--- + +As we've seen, there are two types of route in Sapper — pages, and server routes. + + +### Pages + +Pages are Svelte components written in `.svelte` files. When a user first visits the application, they will be served a server-rendered version of the route in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel. + +The filename determines the route. For example, `src/routes/index.svelte` is the root of your site: + +```html +<!-- src/routes/index.svelte --> +<svelte:head> + <title>Welcome + + +

Hello and welcome to my site!

+``` + +A file called either `src/routes/about.svelte` or `src/routes/about/index.svelte` would correspond to the `/about` route: + +```html + + + About + + +

About this site

+

TODO...

+``` + +Dynamic parameters are encoded using `[brackets]`. For example, here's how you could create a page that renders a blog post: + +```html + + + + + + + {article.title} + + +

{article.title}

+ +
+ {@html article.html} +
+``` + +> See the section on [preloading](docs#preloading) for more info about `preload` and `this.fetch` + + +### Server routes + +Server routes are modules written in `.js` files that export functions corresponding to HTTP methods. Each function receives HTTP `request` and `response` objects as arguments, plus a `next` function. This is useful for creating a JSON API. For example, here's how you could create an endpoint that served the blog page above: + +```js +// routes/blog/[slug].json.js +import db from './_database.js'; // the underscore tells Sapper this isn't a route + +export async function get(req, res, next) { + // the `slug` parameter is available because this file + // is called [slug].json.js + const { slug } = req.params; + + const article = await db.get(slug); + + if (article !== null) { + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify(article)); + } else { + next(); + } +} +``` + +> `delete` is a reserved word in JavaScript. To handle DELETE requests, export a function called `del` instead. + + +### File naming rules + +There are three simple rules for naming the files that define your routes: + +* A file called `src/routes/about.svelte` corresponds to the `/about` route. A file called `src/routes/blog/[slug].svelte` corresponds to the `/blog/:slug` route, in which case `params.slug` is available to `preload` +* The file `src/routes/index.svelte` corresponds to the root of your app. `src/routes/about/index.svelte` is treated the same as `src/routes/about.svelte`. +* Files and directories with a leading underscore do *not* create routes. This allows you to colocate helper modules and components with the routes that depend on them — for example you could have a file called `src/routes/_helpers/datetime.js` and it would *not* create a `/_helpers/datetime` route + + + +### Error page + +In addition to regular pages, there is a 'special' page that Sapper expects to find — `src/routes/_error.svelte`. This will be shown when an error occurs while rendering a page. + +The `error` object is made available to the template along with the HTTP `status` code. + + + +### Regexes in routes + +You can use a subset of regular expressions to qualify route parameters, by placing them in parentheses after the parameter name. + +For example, `src/routes/items/[id([0-9]+)].svelte` would only match numeric IDs — `/items/123` would match, but `/items/xyz` would not. + +Because of technical limitations, the following characters cannot be used: `/`, `\`, `?`, `:`, `(` and `)`. diff --git a/site/content/docs/03-client-api.md b/site/content/docs/03-client-api.md new file mode 100644 index 0000000..4a68ad0 --- /dev/null +++ b/site/content/docs/03-client-api.md @@ -0,0 +1,47 @@ +--- +title: Client API +--- + +The `@sapper/app` module, which is generated by Sapper based on the shape of your app, contains functions for controlling Sapper programmatically and responding to events. + + +### start({ target }) + +* `target` — an element to render pages to + +This configures the router and starts the application — listens for clicks on `
` elements, interacts with the `history` API, and renders and updates your Svelte components. + +Returns a `Promise` that resolves when the initial page has been hydrated. + +```js +import * as sapper from '@sapper/app'; + +sapper.start({ + target: document.querySelector('#sapper') +}).then(() => { + console.log('client-side app has started'); +}); +``` + + +### goto(href, options?) + +* `href` — the page to go to +* `options` — can include a `replaceState` property, which determines whether to use `history.pushState` (the default) or `history.replaceState`). Not required + +Programmatically navigates to the given `href`. If the destination is a Sapper route, Sapper will handle the navigation, otherwise the page will be reloaded with the new `href`. (In other words, the behaviour is as though the user clicked on a link with this `href`.) + + +### prefetch(href) + +* `href` — the page to prefetch + +Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `preload` method with the appropriate options. This is the same behaviour that Sapper triggers when the user taps or mouses over an `` element with [rel=prefetch](docs#prefetching). + + + +### prefetchRoutes(routes?) + +* `routes` — an optional array of strings representing routes to prefetch + +Programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this after `sapper.start()` is complete, to speed up subsequent navigation (this is the 'L' of the [PRPL pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/)). Omitting arguments will cause all routes to be fetched, or you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.svelte`) or `/blog/*` (to match `src/routes/blog/[slug].svelte`). Unlike `prefetch`, this won't call `preload` for individual pages. diff --git a/site/content/docs/04-preloading.md b/site/content/docs/04-preloading.md new file mode 100644 index 0000000..d459bb3 --- /dev/null +++ b/site/content/docs/04-preloading.md @@ -0,0 +1,114 @@ +--- +title: Preloading +--- + +As seen in the [routing](docs#routing) section, page components can have an optional `preload` function that will load some data that the page depends on. This is similar to `getInitialProps` in Next.js or `asyncData` in Nuxt.js. + +```html + +``` + +It lives in a `context="module"` script — see the [tutorial](https://svelte.dev/tutorial/module-exports) — because it's not part of the component instance itself; instead, it runs *before* the component is created, allowing you to avoid flashes while data is fetched. + +### Argument + +The `preload` function receives two arguments — `page` and `session`. + +`page` is a `{ path, params, query }` object where `path` is the URL's pathname, `params` is derived from `path` and the route filename, and `query` is an object of values in the query string. + +So if the example above was `src/routes/blog/[slug].svelte` and the URL was `/blog/some-post?foo=bar&baz`, the following would be true: + +* `page.path === '/blog/some-post'` +* `page.params.slug === 'some-post'` +* `page.query.foo === 'bar'` +* `page.query.baz === true` + +`session` is generated on the server by the `session` option passed to `sapper.middleware` (TODO this needs further documentation. Perhaps a server API section?) + + +### Return value + +If you return a Promise from `preload`, the page will delay rendering until the promise resolves. You can also return a plain object. + +When Sapper renders a page on the server, it will attempt to serialize the resolved value (using [devalue](https://github.com/Rich-Harris/devalue)) and include it on the page, so that the client doesn't also need to call `preload` upon initialization. Serialization will fail if the value includes functions or custom classes (cyclical and repeated references are fine, as are built-ins like `Date`, `Map`, `Set` and `RegExp`). + +### Context + +Inside `preload`, you have access to three methods: + +* `this.fetch(url, options)` +* `this.error(statusCode, error)` +* `this.redirect(statusCode, location)` + + +#### this.fetch + +In browsers, you can use `fetch` to make AJAX requests, for getting data from your server routes (among other things). On the server it's a little trickier — you can make HTTP requests, but you must specify an origin, and you don't have access to cookies. This means that it's impossible to request data based on the user's session, such as data that requires you to be logged in. + +To fix this, Sapper provides `this.fetch`, which works on the server as well as in the client: + +```html + +``` + +Note that you will need to use session middleware such as [express-session](https://github.com/expressjs/session) in your `app/server.js` in order to maintain user sessions or do anything involving authentication. + + +#### this.error + +If the user navigated to `/blog/some-invalid-slug`, we would want to render a 404 Not Found page. We can do that with `this.error`: + +```html + +``` + +The same applies to other error codes you might encounter. + + +#### this.redirect + +You can abort rendering and redirect to a different location with `this.redirect`: + +```html + +``` diff --git a/site/content/docs/05-layouts.md b/site/content/docs/05-layouts.md new file mode 100644 index 0000000..02ab0fe --- /dev/null +++ b/site/content/docs/05-layouts.md @@ -0,0 +1,86 @@ +--- +title: Layouts +--- + +So far, we've treated pages as entirely standalone components — upon navigation, the existing component will be destroyed, and a new one will take its place. + +But in many apps, there are elements that should be visible on *every* page, such as top-level navigation or a footer. Instead of repeating them in every page, we can use *layout* components. + +To create a layout component that applies to every page, make a file called `src/routes/_layout.svelte`. The default layout component (the one that Sapper uses if you don't bring your own) looks like this... + +```html + +``` + +...but we can add whatever markup, styles and behaviour we want. For example, let's add a nav bar: + +```html + + + + +``` + +If we create pages for `/`, `/about` and `/settings`... + +```html + +

Home

+``` + +```html + +

About

+``` + +```html + +

Settings

+``` + +...the nav will always be visible, and clicking between the three pages will only result in the `

` being replaced. + + +### Nested routes + +Suppose we don't just have a single `/settings` page, but instead have nested pages like `/settings/profile` and `/settings/notifications` with a shared submenu (for an real-life example, see [github.com/settings](https://github.com/settings)). + +We can create a layout that only applies to pages below `/settings` (while inheriting the root layout with the top-level nav): + +```html + +

Settings

+ + + + +``` + +Layout components receive a `segment` property which is useful for things like styling: + +```diff ++ ++ + +``` \ No newline at end of file diff --git a/site/content/docs/06-server-side-rendering.md b/site/content/docs/06-server-side-rendering.md new file mode 100644 index 0000000..621d7ba --- /dev/null +++ b/site/content/docs/06-server-side-rendering.md @@ -0,0 +1,32 @@ +--- +title: Server-side rendering +--- + +Sapper, by default, renders server-side first (SSR), and then re-mounts any dynamic elements on the client. Svelte provides [excellent support for this](https://svelte.dev/docs#server-side-rendering). This has benefits in performance and search engine indexing, among others, but comes with its own set of complexities. + +### Making a component SSR compatible + +Sapper works well with most third-party libraries you are likely to come across. However, sometimes, a third-party library comes bundled in a way which allows it to work with multiple different module loaders. Sometimes, this code creates a dependency on `window`, such as checking for the existence of `window.global` might do. + +Since there is no `window` in a server-side environment like Sapper's, the action of simply importing such a module can cause the import to fail, and terminate the Sapper's server with an error such as: + +```bash +ReferenceError: window is not defined +``` + +The way to get around this is to use a dynamic import for your component, from within the `onMount` function (which is only called on the client), so that your import code is never called on the server. + +```html + + + +``` \ No newline at end of file diff --git a/site/content/docs/07-state-management.md b/site/content/docs/07-state-management.md new file mode 100644 index 0000000..8ec9b51 --- /dev/null +++ b/site/content/docs/07-state-management.md @@ -0,0 +1,40 @@ +--- +title: Stores +--- + +The `page` and `session` values passed to `preload` functions are available to components as [stores](https://svelte.dev/tutorial/writable-stores), along with `preloading`. + +Inside a component, get references to the stores like so: + +```html + +``` + +* `preloading` contains a readonly boolean value, indicating whether or not a navigation is pending +* `page` contains a readonly `{ path, params, query }` object, identical to that passed to `preload` functions +* `session` contains whatever data was seeded on the server. It is a [writable store](https://svelte.dev/tutorial/writable-stores), meaning you can update it with new data (for example, after the user logs in) and your app will be refreshed + + +### Seeding session data + +On the server, you can populate `session` by passing an option to `sapper.middleware`: + +```js +// src/server.js +express() // or Polka, or a similar framework + .use( + serve('assets'), + authenticationMiddleware(), + sapper.middleware({ + session: (req, res) => ({ + user: req.user + }) + }) + ) + .listen(process.env.PORT); +``` + +> Session data must be serializable (using [devalue](https://github.com/Rich-Harris/devalue)) — no functions or custom classes, just built-in JavaScript data types \ No newline at end of file diff --git a/site/content/docs/08-prefetching.md b/site/content/docs/08-prefetching.md new file mode 100644 index 0000000..de2c61d --- /dev/null +++ b/site/content/docs/08-prefetching.md @@ -0,0 +1,22 @@ +--- +title: Prefetching +--- + +Sapper uses code splitting to break your app into small chunks (one per route), ensuring fast startup times. + +For *dynamic* routes, such as our `src/routes/blog/[slug].svelte` example, that's not enough. In order to render the blog post, we need to fetch the data for it, and we can't do that until we know what `slug` is. In the worst case, that could cause lag as the browser waits for the data to come back from the server. + + +### rel=prefetch + +We can mitigate that by *prefetching* the data. Adding a `rel=prefetch` attribute to a link... + +```html +What is Sapper? +``` + +...will cause Sapper to run the page's `preload` function as soon as the user hovers over the link (on a desktop) or touches it (on mobile), rather than waiting for the `click` event to trigger navigation. Typically, this buys us an extra couple of hundred milliseconds, which is the difference between a user interface that feels laggy, and one that feels snappy. + +> `rel=prefetch` is a Sapper idiom, not a standard attribute for `` elements + + \ No newline at end of file diff --git a/site/content/docs/09-building.md b/site/content/docs/09-building.md new file mode 100644 index 0000000..d396a3b --- /dev/null +++ b/site/content/docs/09-building.md @@ -0,0 +1,15 @@ +--- +title: Building +--- + +Up until now we've been using `sapper dev` to build our application and run a development server. But when it comes to production, we want to create a self-contained optimized build. + +### sapper build + +This command packages up your application into the `__sapper__/build` directory. (You can change this to a custom directory, as well as controlling various other options — do `sapper build --help` for more information.) + +The output is a Node app that you can run from the project root: + +```bash +node __sapper__/build +``` \ No newline at end of file diff --git a/site/content/docs/10-exporting.md b/site/content/docs/10-exporting.md new file mode 100644 index 0000000..21de1ee --- /dev/null +++ b/site/content/docs/10-exporting.md @@ -0,0 +1,63 @@ +--- +title: Exporting +--- + +Many sites are effectively *static*, which is to say they don't actually need an Express server backing them. Instead, they can be hosted and served as static files, which allows them to be deployed to more hosting environments (such as [Netlify](https://www.netlify.com/) or [GitHub Pages](https://pages.github.com/)). Static sites are generally cheaper to operate and have better performance characteristics. + +Sapper allows you to *export* a static site with a single zero-config `sapper export` command. In fact, you're looking at an exported site right now! + +Static doesn't mean non-interactive — your Svelte components work exactly as they do normally, and you still get all the benefits of client-side routing and prefetching. + + +### sapper export + +Inside your Sapper project, try this: + +```bash +# npx allows you to use locally-installed dependencies +npx sapper export +``` + +This will create a `__sapper__/export` folder with a production-ready build of your site. You can launch it like so: + +```bash +npx serve __sapper__/export +``` + +Navigate to [localhost:5000](http://localhost:5000) (or whatever port `serve` picked), and verify that your site works as expected. + +You can also add a script to your package.json... + +```js +{ + "scripts": { + ... + "export": "sapper export" + } +} +``` + +...allowing you to `npm run export` your app. + + +### How it works + +When you run `sapper export`, Sapper first builds a production version of your app, as though you had run `sapper build`, and copies the contents of your `assets` folder to the destination. It then starts the server, and navigates to the root of your app. From there, it follows any `` elements it finds, and captures any data served by the app. + +Because of this, any pages you want to be included in the exported site must be reachable by `` elements. Additionally, any non-page routes should be requested in `preload`, *not* in `oncreate` or elsewhere. + + +### When not to export + +The basic rule is this: for an app to be exportable, any two users hitting the same page of your app must get the same content from the server. In other words, any app that involves user sessions or authentication is *not* a candidate for `sapper export`. + +Note that you can still export apps with dynamic routes, like our `src/routes/blog/[slug].svelte` example from earlier. `sapper export` will intercept `fetch` requests made inside `preload`, so the data served from `src/routes/blog/[slug].json.js` will also be captured. + + +### Route conflicts + +Because `sapper export` writes to the filesystem, it isn't possible to have two server routes that would cause a directory and a file to have the same name. For example, `src/routes/foo/index.js` and `src/routes/foo/bar.js` would try to create `export/foo` and `export/foo/bar`, which is impossible. + +The solution is to rename one of the routes to avoid conflict — for example, `src/routes/foo-bar.js`. (Note that you would also need to update any code that fetches data from `/foo/bar` to reference `/foo-bar` instead.) + +For *pages*, we skirt around this problem by writing `export/foo/index.html` instead of `export/foo`. \ No newline at end of file diff --git a/site/content/docs/11-deploying.md b/site/content/docs/11-deploying.md new file mode 100644 index 0000000..2c255b5 --- /dev/null +++ b/site/content/docs/11-deploying.md @@ -0,0 +1,64 @@ +--- +title: Deployment +--- + +Sapper apps run anywhere that supports Node 8 or higher. + + +### Deploying to Now + +> This section relates to Now 1, not Now 2 + +We can very easily deploy our apps to [Now][]: + +```bash +npm install -g now +now +``` + +This will upload the source code to Now, whereupon it will do `npm run build` and `npm start` and give you a URL for the deployed app. + +For other hosting environments, you may need to do `npm run build` yourself. + +### Deploying service workers + +Sapper makes the Service Worker file (`service-worker.js`) unique by including a timestamp in the source code +(calculated using `Date.now()`). + +In environments where the app is deployed to multiple servers (such as [Now][]), it is advisable to use a +consistent timestamp for all deployments. Otherwise, users may run into issues where the Service Worker +updates unexpectedly because the app hits server 1, then server 2, and they have slightly different timestamps. + +To override Sapper's timestamp, you can use an environment variable (e.g. `SAPPER_TIMESTAMP`) and then modify +the `service-worker.js`: + +```js +const timestamp = process.env.SAPPER_TIMESTAMP; // instead of `import { timestamp }` + +const ASSETS = `cache${timestamp}`; + +export default { + /* ... */ + plugins: [ + /* ... */ + replace({ + /* ... */ + 'process.env.SAPPER_TIMESTAMP': process.env.SAPPER_TIMESTAMP || Date.now() + }) + ] +} +``` + +Then you can set it using the environment variable, e.g.: + +```bash +SAPPER_TIMESTAMP=$(date +%s%3N) npm run build +``` + +When deploying to [Now][], you can pass the environment variable into Now itself: + +```bash +now -e SAPPER_TIMESTAMP=$(date +%s%3N) +``` + +[Now]: https://zeit.co/now \ No newline at end of file diff --git a/site/content/docs/12-security.md b/site/content/docs/12-security.md new file mode 100644 index 0000000..0365681 --- /dev/null +++ b/site/content/docs/12-security.md @@ -0,0 +1,39 @@ +--- +title: Security +--- + +By default, Sapper does not add security headers to your app, but you may add them yourself using middleware such as [Helmet][]. + +### Content Security Policy (CSP) + +Sapper generates inline ` +``` + + +#### Stores + +It is also available, along with `page` and `preloading`, as a store inside components: + +```html + +``` + +`page` and `preloading` are [readable stores](https://svelte.dev/tutorial/readable-stores), while `session` is [writable](https://svelte.dev/tutorial/writable-stores). Writing to the session store (for example, after the user logs in) will cause any `preload` functions that rely on session data to re-run; it will not persist anything to the server. + + +#### Layouts + +Your layout components should now use a `` element to render nested routes, instead of ``: + +```diff +
+- ++ +
+``` + +The layout component itself receives a `segment` prop, which is equivalent to `child.segment` in earlier versions. + + +### 0.21 to 0.22 + +Instead of importing middleware from the `sapper` package, or importing the client runtime from `sapper/runtime.js`, the app is *compiled into* the generated files: + +```diff +// src/client.js +-import { init } from 'sapper/runtime.js'; +-import { manifest } from './manifest/client.js'; ++import * as sapper from '../__sapper__/client.js'; + +-init({ ++sapper.start({ + target: document.querySelector('#sapper'), +- manifest +}); +``` + +```diff +// src/server.js +import sirv from 'sirv'; +import polka from 'polka'; +import compression from 'compression'; +-import sapper from 'sapper'; +-import { manifest } from './manifest/server.js'; ++import * as sapper from '../__sapper__/server.js'; + +const { PORT, NODE_ENV } = process.env; +const dev = NODE_ENV === 'development'; + +polka() // You can also use Express + .use( + compression({ threshold: 0 }), +- sirv('assets', { dev }), ++ sirv('static', { dev }), +- sapper({ manifest }) ++ sapper.middleware() + ) + .listen(PORT, err => { + if (err) console.log('error', err); + }); +``` + +```diff +// src/service-worker.js +-import { assets, shell, routes, timestamp } from './manifest/service-worker.js'; ++import { files, shell, routes, timestamp } from '../__sapper__/service-worker.js'; +``` + +In addition, the default build and export directories are now `__sapper__/build` and `__sapper__/export` respectively. + + +### 0.20 to 0.21 + +* The `app` directory is now `src` +* The `routes` directory is now `src/routes` +* The `assets` directory is now `static` (remember to update your `src/server.js` file to reflect this change as well) +* Instead of having three separate config files (`webpack/client.config.js`, `webpack/server.config.js` and `webpack/service-worker.config.js`), there is a single `webpack.config.js` file that exports `client`, `server` and `serviceworker` configs. + + +### 0.17 to 0.18 + +The `sapper/webpack/config.js` file (required in the `webpack/*.config.js` files) is now `sapper/config/webpack.js`. + + +### 0.14 to 0.15 + +This release changed how routing is handled, resulting in a number of changes. + +Instead of a single `App.html` component, you can place `_layout.html` components in any directory under `routes`. You should move `app/App.html` to `routes/_layout.html` and modify it like so: + +```diff +- ++ + +-