Merge branch 'master' into chore/remove-dependencies

This commit is contained in:
Rich Harris
2018-03-04 22:03:37 -05:00
committed by GitHub
22 changed files with 193 additions and 210 deletions

5
.gitignore vendored
View File

@@ -3,4 +3,7 @@ node_modules
.sapper
yarn.lock
cypress/screenshots
templates/.*
templates/.*
export
build
app/manifest

7
app/client.js Normal file
View File

@@ -0,0 +1,7 @@
import { init } from 'sapper/runtime.js';
import { routes } from './manifest/client.js';
// `routes` is an array of route objects injected by Sapper
init(document.querySelector('#sapper'), routes);
if (module.hot) module.hot.accept();

29
app/server.js Normal file
View File

@@ -0,0 +1,29 @@
import fs from 'fs';
import express from 'express';
import compression from 'compression';
import sapper from 'sapper';
import serve from 'serve-static';
import fetch from 'node-fetch';
import { routes } from './manifest/server.js';
const app = express();
const { PORT = 3000 } = process.env;
// this allows us to do e.g. `fetch('/api/blog-posts')` on the server
global.fetch = (url, opts) => {
if (url[0] === '/') url = `http://localhost:${PORT}${url}`;
return fetch(url, opts);
};
app.use(compression({ threshold: 0 }));
app.use(serve('assets'));
app.use(sapper({
routes
}));
app.listen(PORT, () => {
console.log(`listening on port ${PORT}`);
});

View File

@@ -1,15 +1,12 @@
const timestamp = '__timestamp__';
import { timestamp, assets, shell, routes } from './manifest/service-worker.js';
const ASSETS = `cache${timestamp}`;
// `shell` is an array of all the files generated by webpack,
// `assets` is an array of everything in the `assets` directory
const to_cache = __shell__.concat(__assets__);
const to_cache = shell.concat(assets);
const cached = new Set(to_cache);
// `routes` is an array of `{ pattern: RegExp }` objects that
// match the pages in your app
const routes = __routes__;
self.addEventListener('install', event => {
event.waitUntil(
caches
@@ -29,7 +26,7 @@ self.addEventListener('activate', event => {
if (key !== ASSETS) await caches.delete(key);
}
await self.clients.claim();
self.clients.claim();
})
);
});
@@ -40,8 +37,11 @@ self.addEventListener('fetch', event => {
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) return;
// ignore dev server requests
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
// always serve assets and webpack-generated files from cache
if (cached.has(url.pathname)) {
if (url.host === self.location.host && cached.has(url.pathname)) {
event.respondWith(caches.match(event.request));
return;
}

View File

@@ -3,9 +3,10 @@
"description": "TODO",
"version": "0.0.1",
"scripts": {
"dev": "node server.js",
"dev": "sapper dev",
"build": "sapper build",
"start": "cross-env NODE_ENV=production node server.js",
"export": "sapper export",
"start": "sapper start",
"cy:run": "cypress run",
"cy:open": "cypress open",
"test": "run-p --race dev cy:run"
@@ -13,16 +14,13 @@
"dependencies": {
"compression": "^1.7.1",
"cross-env": "^5.1.3",
"css-loader": "^0.28.7",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2",
"node-fetch": "^1.7.3",
"sapper": "^0.5.0",
"node-fetch": "^2.0.0",
"npm-run-all": "^4.1.2",
"sapper": "^0.8.1",
"serve-static": "^1.13.1",
"style-loader": "^0.19.1",
"svelte": "^1.51.1",
"svelte": "^1.56.0",
"svelte-loader": "^2.3.3",
"uglifyjs-webpack-plugin": "^1.1.5",
"webpack": "^3.10.0"
"webpack": "^4.1.0"
}
}

43
routes/4xx.html Normal file
View File

@@ -0,0 +1,43 @@
<:Head>
<title>Not found</title>
</:Head>
<Layout page='home'>
<h1>Not found</h1>
<p>Please check the URL</p>
</Layout>
<style>
h1, p {
text-align: center;
margin: 0 auto;
}
h1 {
font-size: 2.8em;
text-transform: uppercase;
font-weight: 700;
margin: 0 0 0.5em 0;
}
p {
margin: 1em auto;
}
@media (min-width: 480px) {
h1 {
font-size: 4em;
}
}
</style>
<script>
import Layout from './_components/Layout.html';
export default {
components: {
Layout
}
};
</script>

34
routes/5xx.html Normal file
View File

@@ -0,0 +1,34 @@
<:Head>
<title>Internal server error</title>
</:Head>
<Layout page='home'>
<h1>Internal server error</h1>
</Layout>
<style>
h1 {
text-align: center;
margin: 0 auto;
font-size: 2.8em;
text-transform: uppercase;
font-weight: 700;
margin: 0 0 0.5em 0;
}
@media (min-width: 480px) {
h1 {
font-size: 4em;
}
}
</style>
<script>
import Layout from './_components/Layout.html';
export default {
components: {
Layout
}
};
</script>

View File

@@ -1,4 +1,4 @@
import posts from './_posts.js';
import posts from './blog/_posts.js';
const contents = JSON.stringify(posts.map(post => {
return {

View File

@@ -59,7 +59,7 @@
// is called [slug].html
const { slug } = params;
return fetch(`/api/blog/${slug}`).then(r => r.json()).then(post => {
return fetch(`/blog/${slug}.json`).then(r => r.json()).then(post => {
return { post };
});
}

View File

@@ -7,7 +7,7 @@ posts.forEach(post => {
export function get(req, res, next) {
// the `slug` parameter is available because this file
// is called [slug].js
// is called [slug].json.js
const { slug } = req.params;
if (lookup.has(slug)) {

View File

@@ -4,7 +4,7 @@
// service of obviousness, we're just going to leave it here.
// This file is called `_posts.js` rather than `posts.js`, because
// we don't want to create an `/api/blog/posts` route — the leading
// we don't want to create an `/blog/posts` route — the leading
// underscore tells Sapper not to do that.
const posts = [
@@ -70,7 +70,7 @@ const posts = [
<ul>
<li>It's powered by <a href='https://svelte.technology'>Svelte</a> instead of React, so it's faster and your apps are smaller</li>
<li>Instead of route masking, we encode route parameters in filenames. For example, the page you're looking at right now is <code>routes/blog/[slug].html</code></li>
<li>As well as pages (Svelte components, which render on server or client), you can create <em>server routes</em> in your <code>routes</code> directory. These are just <code>.js</code> files that export functions corresponding to HTTP methods, and receive Express <code>request</code> and <code>response</code> objects as arguments. This makes it very easy to, for example, add a JSON API such as the one powering this very page (look in <code>routes/api/blog</code>)</li>
<li>As well as pages (Svelte components, which render on server or client), you can create <em>server routes</em> in your <code>routes</code> directory. These are just <code>.js</code> files that export functions corresponding to HTTP methods, and receive Express <code>request</code> and <code>response</code> objects as arguments. This makes it very easy to, for example, add a JSON API such as the one <a href='/blog/how-is-sapper-different-from-next.json'>powering this very page</a></li>
<li>Links are just <code>&lt;a&gt;</code> elements, rather than framework-specific <code>&lt;Link&gt;</code> components. That means, for example, that <a href='/blog/how-can-i-get-involved'>this link right here</a>, despite being inside a blob of HTML, works with the router as you'd expect.</li>
</ul>
`

View File

@@ -32,7 +32,7 @@
},
preload({ params, query }) {
return fetch(`/api/blog`).then(r => r.json()).then(posts => {
return fetch(`/blog.json`).then(r => r.json()).then(posts => {
return { posts };
});
}

View File

@@ -6,7 +6,7 @@
<h1>Great success!</h1>
<figure>
<img src='/great-success.png'>
<img alt='Borat' src='/great-success.png'>
<figcaption>HIGH FIVE!</figcaption>
</figure>

View File

@@ -1,24 +0,0 @@
const fs = require('fs');
const app = require('express')();
const compression = require('compression');
const sapper = require('sapper');
const static = require('serve-static');
const { PORT = 3000 } = process.env;
// this allows us to do e.g. `fetch('/api/blog')` on the server
const fetch = require('node-fetch');
global.fetch = (url, opts) => {
if (url[0] === '/') url = `http://localhost:${PORT}${url}`;
return fetch(url, opts);
};
app.use(compression({ threshold: 0 }));
app.use(static('assets'));
app.use(sapper());
app.listen(PORT, () => {
console.log(`listening on port ${PORT}`);
});

View File

@@ -1,46 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<meta name='theme-color' content='#aa1e1e'>
<link rel='manifest' href='/manifest.json'>
<link rel='icon' type='image/png' href='/favicon.png'>
<!-- %sapper.status% is the HTTP status code, e.g. 404 -->
<title>%sapper.status%</title>
<style>
body {
max-width: 800px;
padding: 1em;
margin: 0 auto;
font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
background-color: #f4f4f4;
box-sizing: border-box;
}
h1 {
color: rgb(170,30,30);
border-bottom: 1px solid #aaa;
padding: 0 0 0.5em 0;
margin: 1em 0;
}
pre {
font-family: Menlo, monospace;
font-size: 14px;
line-height: 1.2;
overflow-x: auto;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>%sapper.title%</h1>
<p>Could not %sapper.method% %sapper.url%</p>
%sapper.scripts%
</body>
</html>

View File

@@ -1,47 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<meta name='theme-color' content='#aa1e1e'>
<link rel='manifest' href='/manifest.json'>
<link rel='icon' type='image/png' href='/favicon.png'>
<title>%sapper.status%</title>
<style>
body {
max-width: 800px;
padding: 1em;
margin: 0 auto;
box-sizing: border-box;
}
h1 {
color: rgb(170,30,30);
border-bottom: 1px solid #aaa;
padding: 0 0 0.5em 0;
margin: 1em 0;
}
pre {
font-family: Menlo, monospace;
font-size: 14px;
line-height: 1.2;
overflow-x: auto;
white-space: pre-wrap;
}
.stack {
font-size: 12px;
color: #999;
}
</style>
</head>
<body>
<h1>%sapper.title%</h1>
<pre>%sapper.error%</pre>
<pre class='stack'>%sapper.stack%</pre>
</body>
</html>

View File

@@ -1,4 +0,0 @@
import { init } from 'sapper/runtime.js';
// `routes` is an array of route objects injected by Sapper
init(document.querySelector('#sapper'), __routes__);

View File

@@ -1,59 +0,0 @@
const webpack = require('webpack');
const config = require('sapper/webpack/config.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const isDev = config.dev;
module.exports = {
entry: config.client.entry(),
output: config.client.output(),
resolve: {
extensions: ['.js', '.html']
},
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
use: {
loader: 'svelte-loader',
options: {
hydratable: true,
emitCss: !isDev,
cascade: false,
store: true
}
}
},
isDev && {
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' }
]
},
!isDev && {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{ loader: 'css-loader', options: { sourceMap:isDev } }]
})
}
].filter(Boolean)
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
minChunks: 2,
async: false,
children: true
})
].concat(isDev ? [
new webpack.HotModuleReplacementPlugin()
] : [
new ExtractTextPlugin('main.css'),
new webpack.optimize.ModuleConcatenationPlugin(),
new UglifyJSPlugin()
]).filter(Boolean),
devtool: isDev && 'inline-source-map'
};

39
webpack/client.config.js Normal file
View File

@@ -0,0 +1,39 @@
const webpack = require('webpack');
const config = require('sapper/webpack/config.js');
const mode = process.env.NODE_ENV;
const isDev = mode === 'development';
module.exports = {
entry: config.client.entry(),
output: config.client.output(),
resolve: {
extensions: ['.js', '.json', '.html']
},
module: {
rules: [
{
test: /\.html$/,
exclude: /node_modules/,
use: {
loader: 'svelte-loader',
options: {
hydratable: true,
cascade: false,
store: true,
hotReload: true
}
}
}
]
},
mode,
plugins: [
isDev && new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
].filter(Boolean),
devtool: isDev && 'inline-source-map'
};

View File

@@ -1,15 +1,14 @@
const config = require('sapper/webpack/config.js');
const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const pkg = require('../package.json');
module.exports = {
entry: config.server.entry(),
output: config.server.output(),
target: 'node',
resolve: {
extensions: ['.js', '.html']
extensions: ['.js', '.json', '.html']
},
externals: Object.keys(pkg.dependencies),
module: {
rules: [
{
@@ -26,5 +25,9 @@ module.exports = {
}
}
]
},
mode: process.env.NODE_ENV,
performance: {
hints: false // it doesn't matter if server.js is large
}
};

View File

@@ -0,0 +1,7 @@
const config = require('sapper/webpack/config.js');
module.exports = {
entry: config.serviceworker.entry(),
output: config.serviceworker.output(),
mode: process.env.NODE_ENV
};