support HMR

This commit is contained in:
Rich Harris
2017-12-16 20:10:49 -05:00
parent 08ff7ad234
commit e4936375db
7 changed files with 546 additions and 338 deletions

View File

@@ -3,6 +3,7 @@ const path = require('path');
const glob = require('glob'); const glob = require('glob');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const mkdirp = require('mkdirp'); const mkdirp = require('mkdirp');
const webpack = require('webpack');
const create_routes = require('./utils/create_routes.js'); const create_routes = require('./utils/create_routes.js');
const templates = require('./templates.js'); const templates = require('./templates.js');
const create_app = require('./utils/create_app.js'); const create_app = require('./utils/create_app.js');
@@ -20,34 +21,41 @@ module.exports = function connect(opts) {
create_app(src, dest, routes, opts); create_app(src, dest, routes, opts);
const client = webpack(
require(path.resolve('webpack.client.config.js'))
);
const server = webpack(
require(path.resolve('webpack.server.config.js'))
);
const compiler = create_compiler( const compiler = create_compiler(
client,
server,
dest, dest,
routes, routes,
dev dev
); );
return async function(req, res, next) { const dev_middleware = dev ? require('webpack-dev-middleware')(client, {
const url = req.url.replace(/\?.+/, ''); noInfo: true,
logLevel: 'silent',
publicPath: '/client/'
}) : null;
if (url === '/service-worker.js') { const hot_middleware = dev ? require('webpack-hot-middleware')(client, {
await compiler.ready; reload: true,
res.set({ path: '/__webpack_hmr',
'Content-Type': 'application/javascript', heartbeat: 10 * 1000
'Cache-Control': dev ? 'no-cache' : 'max-age=600' }) : null;
async function handle_webpack_generated_files(url, req, res, next) {
if (dev) {
dev_middleware(req, res, () => {
hot_middleware(req, res, next);
}); });
res.end(compiler.service_worker); } else {
} if (url.startsWith('/client/')) {
else if (url === '/index.html') {
await compiler.ready;
res.set({
'Content-Type': 'text/html',
'Cache-Control': dev ? 'no-cache' : 'max-age=600'
});
res.end(compiler.shell);
}
else if (url.startsWith('/client/')) {
await compiler.ready; await compiler.ready;
res.set({ res.set({
'Content-Type': 'application/javascript', 'Content-Type': 'application/javascript',
@@ -55,8 +63,36 @@ module.exports = function connect(opts) {
}); });
res.end(compiler.asset_cache[url]); res.end(compiler.asset_cache[url]);
} }
}
}
else { async function handle_index(url, req, res, next) {
if (url === '/index.html') {
await compiler.ready;
res.set({
'Content-Type': 'text/html',
'Cache-Control': dev ? 'no-cache' : 'max-age=600'
});
res.end(compiler.shell);
} else {
next();
}
}
async function handle_service_worker(url, req, res, next) {
if (url === '/service-worker.js') {
await compiler.ready;
res.set({
'Content-Type': 'application/javascript',
'Cache-Control': dev ? 'no-cache' : 'max-age=600'
});
res.end(compiler.service_worker);
} else {
next();
}
}
async function handle_route(url, req, res) {
// whatever happens, we're going to serve some HTML // whatever happens, we're going to serve some HTML
res.set({ res.set({
'Content-Type': 'text/html' 'Content-Type': 'text/html'
@@ -125,5 +161,16 @@ module.exports = function connect(opts) {
})); }));
} }
} }
return async function(req, res, next) {
const url = req.url.replace(/\?.+/, '');
handle_index(url, req, res, () => {
handle_service_worker(url, req, res, () => {
handle_webpack_generated_files(url, req, res, () => {
handle_route(url, req, res);
});
});
});
}; };
}; };

View File

@@ -25,7 +25,7 @@ module.exports = function create_app(src, dest, routes, options) {
// need to fudge the mtime, because webpack is soft in the head // need to fudge the mtime, because webpack is soft in the head
const stats = fs.statSync(main_built); const stats = fs.statSync(main_built);
fs.utimesSync(main_built, stats.atimeMs - 9999, stats.mtimeMs - 9999); fs.utimesSync(main_built, stats.atimeMs - 999999, stats.mtimeMs - 999999);
} }
function create_server_routes() { function create_server_routes() {
@@ -42,7 +42,7 @@ module.exports = function create_app(src, dest, routes, options) {
fs.writeFileSync(server_routes, `${imports}\n\n${exports}`); fs.writeFileSync(server_routes, `${imports}\n\n${exports}`);
const stats = fs.statSync(server_routes); const stats = fs.statSync(server_routes);
fs.utimesSync(server_routes, stats.atimeMs - 9999, stats.mtimeMs - 9999); fs.utimesSync(server_routes, stats.atimeMs - 999999, stats.mtimeMs - 999999);
} }
// TODO in dev mode, watch files // TODO in dev mode, watch files

View File

@@ -1,50 +1,32 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const glob = require('glob'); const glob = require('glob');
const webpack = require('webpack'); const chalk = require('chalk');
const hot_middleware = require('webpack-hot-middleware');
const { dev } = require('../config.js'); const { dev } = require('../config.js');
const templates = require('../templates.js'); const templates = require('../templates.js');
module.exports = function create_webpack_compiler(dest, routes, dev) { module.exports = function create_compiler(client, server, dest, routes, dev) {
const compiler = {}; const compiler = {};
function go() { function client_updated(stats) {
const client = webpack(
require(path.resolve('webpack.client.config.js'))
);
const server = webpack(
require(path.resolve('webpack.server.config.js'))
);
function client_updated(err, stats, reject) {
console.log(stats.toString({ colors: true })); console.log(stats.toString({ colors: true }));
const info = stats.toJson(); const info = stats.toJson();
if (err || stats.hasErrors()) {
reject(err || info.errors[0]);
}
compiler.client_main = `/client/${info.assetsByChunkName.main}`; compiler.client_main = `/client/${info.assetsByChunkName.main}`;
compiler.assets = info.assets.map(asset => `/client/${asset.name}`); compiler.assets = info.assets.map(asset => `/client/${asset.name}`);
const fs = client.outputFileSystem;
compiler.asset_cache = {}; compiler.asset_cache = {};
compiler.assets.forEach(file => { compiler.assets.forEach(file => {
compiler.asset_cache[file] = fs.readFileSync(path.join(dest, file), 'utf-8'); compiler.asset_cache[file] = fs.readFileSync(path.join(dest, file), 'utf-8');
}); });
} }
function server_updated(err, stats, reject) { function server_updated(stats) {
console.log(stats.toString({ colors: true })); console.log(stats.toString({ colors: true }));
const info = stats.toJson(); const info = stats.toJson();
if (err || stats.hasErrors()) {
reject(err || info.errors[0]);
}
compiler.server_routes = path.resolve(dest, 'server', info.assetsByChunkName.server_routes); compiler.server_routes = path.resolve(dest, 'server', info.assetsByChunkName.server_routes);
compiler.chunks = info.assetsByChunkName; compiler.chunks = info.assetsByChunkName;
} }
@@ -78,8 +60,6 @@ module.exports = function create_webpack_compiler(dest, routes, dev) {
} }
if (dev) { if (dev) {
compiler.hot_middleware = hot_middleware(client);
let client_is_ready = false; let client_is_ready = false;
let server_is_ready = false; let server_is_ready = false;
@@ -93,32 +73,64 @@ module.exports = function create_webpack_compiler(dest, routes, dev) {
compiler.ready = invalidate(); compiler.ready = invalidate();
client.plugin('invalid', () => { client.plugin('invalid', filename => {
console.log(chalk.red(`client bundle invalidated, file changed: ${chalk.bold(filename)}`));
client_is_ready = false; client_is_ready = false;
compiler.ready = invalidate(); compiler.ready = invalidate();
}); });
server.plugin('invalid', () => { client.plugin('done', stats => {
server_is_ready = false; if (stats.hasErrors()) {
compiler.ready = invalidate(); reject(stats.toJson().errors[0]);
}); } else {
client_updated(stats);
}
client.watch({}, (err, stats) => {
client_updated(err, stats, reject);
client_is_ready = true; client_is_ready = true;
if (server_is_ready) fulfil(); if (server_is_ready) fulfil();
}); });
server.watch({}, (err, stats) => { client.plugin('failed', reject);
server_updated(err, stats, reject);
server.plugin('invalid', filename => {
console.log(chalk.red(`server bundle invalidated, file changed: ${chalk.bold(filename)}`));
server_is_ready = false;
compiler.ready = invalidate();
});
server.plugin('done', stats => {
if (stats.hasErrors()) {
reject(stats.toJson().errors[0]);
} else {
server_updated(stats);
}
server_is_ready = true; server_is_ready = true;
if (client_is_ready) fulfil(); if (client_is_ready) fulfil();
}); });
server.plugin('failed', reject);
// client is already being watched by the middleware,
// so we only need to start the server compiler
server.watch({}, (err, stats) => {
if (stats.hasErrors()) {
reject(stats.toJson().errors[0]);
} else {
server_updated(stats);
server_is_ready = true;
if (client_is_ready) fulfil();
}
});
} else { } else {
compiler.ready = Promise.all([ compiler.ready = Promise.all([
new Promise((fulfil, reject) => { new Promise((fulfil, reject) => {
client.run((err, stats) => { client.run((err, stats) => {
client_updated(err, stats, reject); if (stats.hasErrors()) {
reject(stats.toJson().errors[0]);
} else {
client_updated(stats);
}
fulfil(); fulfil();
}); });
}), }),
@@ -131,11 +143,6 @@ module.exports = function create_webpack_compiler(dest, routes, dev) {
}) })
]).then(both_updated); ]).then(both_updated);
} }
}
// TODO rerun go when routes are added/renamed
// (or webpack config/templates change?)
go();
return compiler; return compiler;
}; };

149
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.0.20", "version": "0.0.21",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -25,9 +25,9 @@
} }
}, },
"ajv": { "ajv": {
"version": "5.5.1", "version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"requires": { "requires": {
"co": "4.6.0", "co": "4.6.0",
"fast-deep-equal": "1.0.0", "fast-deep-equal": "1.0.0",
@@ -50,11 +50,24 @@
"repeat-string": "1.6.1" "repeat-string": "1.6.1"
} }
}, },
"ansi-html": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
"integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4="
},
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
}, },
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"requires": {
"color-convert": "1.9.1"
}
},
"anymatch": { "anymatch": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@@ -77,6 +90,11 @@
"resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
}, },
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
"integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
},
"array-unique": { "array-unique": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
@@ -271,6 +289,16 @@
"lazy-cache": "1.0.4" "lazy-cache": "1.0.4"
} }
}, },
"chalk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.5.0"
}
},
"chokidar": { "chokidar": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@@ -316,6 +344,19 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"color-convert": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"commander": { "commander": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
@@ -406,6 +447,14 @@
"randomfill": "1.0.3" "randomfill": "1.0.3"
} }
}, },
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
"integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
"requires": {
"array-find-index": "1.0.2"
}
},
"d": { "d": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
@@ -581,8 +630,7 @@
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"dev": true
}, },
"escope": { "escope": {
"version": "3.6.0", "version": "3.6.0",
@@ -1606,6 +1654,11 @@
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
"integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg=="
}, },
"html-entities": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
},
"https-browserify": { "https-browserify": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
@@ -1827,11 +1880,38 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
}, },
"log-symbols": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.1.0.tgz",
"integrity": "sha512-zLeLrzMA1A2vRF1e/0Mo+LNINzi6jzBylHj5WqvQ/WK/5WCZt8si9SyN4p9llr/HRYvVR1AoXHRHl4WTHyQAzQ==",
"requires": {
"chalk": "2.3.0"
}
},
"loglevel": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.0.tgz",
"integrity": "sha1-rgyqVhERSYxboTcj1vtjHSQAOTQ="
},
"loglevel-plugin-prefix": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.5.3.tgz",
"integrity": "sha512-zRAJw3WYCQAJ6xfEIi04/oqlmR6jkwg3hmBcMW82Zic3iPWyju1gwntcgic0m5NgqYNJ62alCmb0g/div26WjQ=="
},
"longest": { "longest": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
}, },
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
"integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
"requires": {
"currently-unhandled": "0.4.1",
"signal-exit": "3.0.2"
}
},
"lru-cache": { "lru-cache": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
@@ -1907,6 +1987,11 @@
"brorand": "1.1.0" "brorand": "1.1.0"
} }
}, },
"mime": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.0.3.tgz",
"integrity": "sha512-TrpAd/vX3xaLPDgVRm6JkZwLR0KHfukMdU2wTEbqMDdCnY6Yo3mE+mjs9YE6oMNw2QRfXVeBEYpmpO94BIqiug=="
},
"mimic-fn": { "mimic-fn": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
@@ -2288,6 +2373,11 @@
"safe-buffer": "5.1.1" "safe-buffer": "5.1.1"
} }
}, },
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"read-pkg": { "read-pkg": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -2559,6 +2649,11 @@
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz",
"integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
}, },
"time-stamp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.0.0.tgz",
"integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c="
},
"timers-browserify": { "timers-browserify": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz",
@@ -2632,6 +2727,11 @@
} }
} }
}, },
"url-join": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.2.tgz",
"integrity": "sha1-wHJ1aWetJLi1nldBVRyqx49QuLc="
},
"util": { "util": {
"version": "0.10.3", "version": "0.10.3",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
@@ -2652,6 +2752,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
}, },
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
},
"validate-npm-package-license": { "validate-npm-package-license": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
@@ -2686,7 +2791,7 @@
"requires": { "requires": {
"acorn": "5.2.1", "acorn": "5.2.1",
"acorn-dynamic-import": "2.0.2", "acorn-dynamic-import": "2.0.2",
"ajv": "5.5.1", "ajv": "5.5.2",
"ajv-keywords": "2.1.1", "ajv-keywords": "2.1.1",
"async": "2.6.0", "async": "2.6.0",
"enhanced-resolve": "3.4.1", "enhanced-resolve": "3.4.1",
@@ -2708,6 +2813,36 @@
"yargs": "8.0.2" "yargs": "8.0.2"
} }
}, },
"webpack-dev-middleware": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.1.tgz",
"integrity": "sha512-jEQgJK+eblBzE4blKmNuJqNd4cz3t4K3mFmN6uZz4Iq44x2vc1r+CwZBgcX+GzQoSOk5iWSVB3bIN5AYKpFRTw==",
"requires": {
"chalk": "2.3.0",
"log-symbols": "2.1.0",
"loglevel": "1.6.0",
"loglevel-plugin-prefix": "0.5.3",
"loud-rejection": "1.6.0",
"memory-fs": "0.4.1",
"mime": "2.0.3",
"path-is-absolute": "1.0.1",
"range-parser": "1.2.0",
"time-stamp": "2.0.0",
"url-join": "2.0.2",
"uuid": "3.1.0"
}
},
"webpack-hot-middleware": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.21.0.tgz",
"integrity": "sha512-P6xiOLy10QlSVSO7GanU9PLxN6zLLQ7RG16MPTvmFwf2KUG7jMp6m+fmdgsR7xoaVVLA7OlX3YO6JjoZEKjCuA==",
"requires": {
"ansi-html": "0.0.7",
"html-entities": "1.2.1",
"querystring": "0.2.0",
"strip-ansi": "3.0.1"
}
},
"webpack-sources": { "webpack-sources": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",

View File

@@ -2,15 +2,18 @@
"name": "sapper", "name": "sapper",
"version": "0.0.21", "version": "0.0.21",
"description": "Combat-ready apps, engineered by Svelte", "description": "Combat-ready apps, engineered by Svelte",
"main": "connect.js", "main": "lib/index.js",
"directories": { "directories": {
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"chalk": "^2.3.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"webpack": "^3.10.0" "webpack": "^3.10.0",
"webpack-dev-middleware": "^2.0.1",
"webpack-hot-middleware": "^2.21.0"
}, },
"devDependencies": { "devDependencies": {
"mocha": "^4.0.1" "mocha": "^4.0.1"

View File

@@ -3,6 +3,8 @@ const detach = node => {
}; };
let component; let component;
let target;
let routes;
const scroll_history = {}; const scroll_history = {};
let uid = 1; let uid = 1;
@@ -12,8 +14,6 @@ if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual' history.scrollRestoration = 'manual'
} }
const app = {
init(target, routes) {
function select_route(url) { function select_route(url) {
if (url.origin !== window.location.origin) return null; if (url.origin !== window.location.origin) return null;
@@ -88,12 +88,7 @@ const app = {
} }
} }
function findAnchor(node) { function handle_click(event) {
while (node && node.nodeName.toUpperCase() !== 'A') node = node.parentNode; // SVG <a> elements have a lowercase name
return node;
}
window.addEventListener('click', event => {
// Adapted from https://github.com/visionmedia/page.js // Adapted from https://github.com/visionmedia/page.js
// MIT license https://github.com/visionmedia/page.js#license // MIT license https://github.com/visionmedia/page.js#license
if (which(event) !== 1) return; if (which(event) !== 1) return;
@@ -129,9 +124,21 @@ const app = {
if (navigate(url, null)) { if (navigate(url, null)) {
event.preventDefault(); event.preventDefault();
} }
}); }
function preload(event) { function handle_popstate(event) {
scroll_history[cid] = scroll_state();
if (event.state) {
navigate(new URL(window.location), event.state.id);
} else {
// hashchange
cid = ++uid;
history.replaceState({ id: cid }, '', window.location.href);
}
}
function prefetch(event) {
const a = findAnchor(event.target); const a = findAnchor(event.target);
if (!a || a.rel !== 'prefetch') return; if (!a || a.rel !== 'prefetch') return;
@@ -144,20 +151,28 @@ const app = {
} }
} }
window.addEventListener('touchstart', preload); function findAnchor(node) {
window.addEventListener('mouseover', preload); while (node && node.nodeName.toUpperCase() !== 'A') node = node.parentNode; // SVG <a> elements have a lowercase name
return node;
window.addEventListener('popstate', event => { }
scroll_history[cid] = scroll_state();
let inited;
if (event.state) {
navigate(new URL(window.location), event.state.id); const app = {
} else { init(_target, _routes) {
// hashchange target = _target;
cid = ++uid; routes = _routes;
history.replaceState({ id: cid }, '', window.location.href);
if (!inited) { // this check makes HMR possible
window.addEventListener('click', handle_click);
window.addEventListener('popstate', handle_popstate);
// prefetch
window.addEventListener('touchstart', prefetch);
window.addEventListener('mouseover', prefetch);
inited = true;
} }
});
const scroll = scroll_history[uid] = scroll_state(); const scroll = scroll_history[uid] = scroll_state();

1
webpack/hmr.js Normal file
View File

@@ -0,0 +1 @@
import 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000';