diff --git a/.gitignore b/.gitignore index 81c2c19..216e636 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ node_modules .sapper yarn.lock cypress/screenshots -templates/.* \ No newline at end of file +templates/.* +export +build +app/manifest \ No newline at end of file diff --git a/app/client.js b/app/client.js new file mode 100644 index 0000000..05d30aa --- /dev/null +++ b/app/client.js @@ -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(); \ No newline at end of file diff --git a/app/server.js b/app/server.js new file mode 100644 index 0000000..0af7864 --- /dev/null +++ b/app/server.js @@ -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}`); +}); \ No newline at end of file diff --git a/templates/service-worker.js b/app/service-worker.js similarity index 84% rename from templates/service-worker.js rename to app/service-worker.js index cccb12a..dca8106 100644 --- a/templates/service-worker.js +++ b/app/service-worker.js @@ -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; } diff --git a/templates/2xx.html b/app/template.html similarity index 100% rename from templates/2xx.html rename to app/template.html diff --git a/package.json b/package.json index 4d601e8..98ce2af 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/routes/4xx.html b/routes/4xx.html new file mode 100644 index 0000000..0a98eb7 --- /dev/null +++ b/routes/4xx.html @@ -0,0 +1,43 @@ +<:Head> + Not found + + + +

Not found

+ +

Please check the URL

+
+ + + + diff --git a/routes/5xx.html b/routes/5xx.html new file mode 100644 index 0000000..b920a35 --- /dev/null +++ b/routes/5xx.html @@ -0,0 +1,34 @@ +<:Head> + Internal server error + + + +

Internal server error

+
+ + + + diff --git a/routes/api/blog/index.js b/routes/blog.json.js similarity index 85% rename from routes/api/blog/index.js rename to routes/blog.json.js index 4c10788..7120e67 100644 --- a/routes/api/blog/index.js +++ b/routes/blog.json.js @@ -1,4 +1,4 @@ -import posts from './_posts.js'; +import posts from './blog/_posts.js'; const contents = JSON.stringify(posts.map(post => { return { diff --git a/routes/blog/[slug].html b/routes/blog/[slug].html index 2646930..96c4699 100644 --- a/routes/blog/[slug].html +++ b/routes/blog/[slug].html @@ -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 }; }); } diff --git a/routes/api/blog/[slug].js b/routes/blog/[slug].json.js similarity index 93% rename from routes/api/blog/[slug].js rename to routes/blog/[slug].json.js index f1a151f..33fc51e 100644 --- a/routes/api/blog/[slug].js +++ b/routes/blog/[slug].json.js @@ -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)) { diff --git a/routes/api/blog/_posts.js b/routes/blog/_posts.js similarity index 96% rename from routes/api/blog/_posts.js rename to routes/blog/_posts.js index 7fe65c5..9e1ac88 100644 --- a/routes/api/blog/_posts.js +++ b/routes/blog/_posts.js @@ -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 = [ ` diff --git a/routes/blog/index.html b/routes/blog/index.html index 16ff29e..d8deaf5 100644 --- a/routes/blog/index.html +++ b/routes/blog/index.html @@ -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 }; }); } diff --git a/routes/index.html b/routes/index.html index ddde68e..898bb5f 100644 --- a/routes/index.html +++ b/routes/index.html @@ -6,7 +6,7 @@

Great success!

- + Borat
HIGH FIVE!
diff --git a/server.js b/server.js deleted file mode 100644 index 5b46a61..0000000 --- a/server.js +++ /dev/null @@ -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}`); -}); \ No newline at end of file diff --git a/templates/4xx.html b/templates/4xx.html deleted file mode 100644 index d362dcc..0000000 --- a/templates/4xx.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - %sapper.status% - - - - -

%sapper.title%

-

Could not %sapper.method% %sapper.url%

- - %sapper.scripts% - - \ No newline at end of file diff --git a/templates/5xx.html b/templates/5xx.html deleted file mode 100644 index 357152d..0000000 --- a/templates/5xx.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - %sapper.status% - - - - -

%sapper.title%

-
%sapper.error%
-
%sapper.stack%
- - \ No newline at end of file diff --git a/templates/main.js b/templates/main.js deleted file mode 100644 index aa08cb0..0000000 --- a/templates/main.js +++ /dev/null @@ -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__); \ No newline at end of file diff --git a/webpack.client.config.js b/webpack.client.config.js deleted file mode 100644 index 904aa06..0000000 --- a/webpack.client.config.js +++ /dev/null @@ -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' -}; diff --git a/webpack/client.config.js b/webpack/client.config.js new file mode 100644 index 0000000..b8798da --- /dev/null +++ b/webpack/client.config.js @@ -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' +}; diff --git a/webpack.server.config.js b/webpack/server.config.js similarity index 64% rename from webpack.server.config.js rename to webpack/server.config.js index 97a977c..e400d67 100644 --- a/webpack.server.config.js +++ b/webpack/server.config.js @@ -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 } }; \ No newline at end of file diff --git a/webpack/service-worker.config.js b/webpack/service-worker.config.js new file mode 100644 index 0000000..5e086a9 --- /dev/null +++ b/webpack/service-worker.config.js @@ -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 +}; \ No newline at end of file