Compare commits

...

63 Commits

Author SHA1 Message Date
Rich Harris
a6dc61a182 -> v0.5.1 2018-01-16 08:47:50 -05:00
Rich Harris
96666d05ec only write to filesystem in dev mode 2018-01-16 08:47:44 -05:00
Rich Harris
6390ba692b -> v0.5.0 2018-01-15 10:28:48 -05:00
Rich Harris
0e131cc81e Merge pull request #88 from sveltejs/gh-9
Exporting
2018-01-15 10:24:35 -05:00
Rich Harris
bd3d5713cb Merge pull request #64 from lukeed/lazy-chokidar
Lazily Require Chokidar
2018-01-15 10:04:37 -05:00
Rich Harris
9ec23c47ad update test 2018-01-15 10:04:24 -05:00
Rich Harris
b7bb69925e rename extract to export, for familiarity to next.js devs 2018-01-15 10:03:27 -05:00
Rich Harris
25124f6ee7 Merge branch 'master' into lazy-chokidar 2018-01-15 09:58:39 -05:00
Rich Harris
73d491cd19 Merge branch 'master' into lazy-chokidar 2018-01-15 09:58:03 -05:00
Rich Harris
e25fceb4b8 node 6 is the new IE 2018-01-15 09:36:12 -05:00
Rich Harris
3807147c57 add missing dependency 2018-01-14 22:45:59 -05:00
Rich Harris
a523ba58ff save pages as index.html, ignore hashes 2018-01-14 22:45:51 -05:00
Rich Harris
fe03fd3a52 update lockfile 2018-01-14 18:21:37 -05:00
Rich Harris
89c430a0cb slightly different approach to extracting 2018-01-14 18:19:51 -05:00
Rich Harris
8ef312849c always write service-worker.js and shell index.html file 2018-01-14 18:16:10 -05:00
Rich Harris
4200446684 ignore trailing slash in pathnames 2018-01-14 18:15:40 -05:00
Rich Harris
681ed005b8 remove unused deps 2018-01-14 18:15:12 -05:00
Rich Harris
d457af8d51 update test 2018-01-14 18:14:07 -05:00
Rich Harris
0c158b9e1f Merge branch 'master' of https://github.com/freedmand/sapper into freedmand-master 2018-01-14 13:48:07 -05:00
Rich Harris
50011e2077 always print stdout/stderr, to avoid wild goose chase debugging 2018-01-14 13:47:23 -05:00
Rich Harris
f27b7973e3 Build before extracting 2018-01-14 13:46:25 -05:00
Rich Harris
2af2ab3cb9 Ensure output dir exists, return Promise 2018-01-14 13:45:47 -05:00
Rich Harris
6a4dc1901c Enforce prod mode, return a Promise so it can be used programmatically 2018-01-14 13:45:01 -05:00
Rich Harris
fbbc0e9e19 Merge branch 'master' of https://github.com/freedmand/sapper into freedmand-master 2018-01-14 12:29:15 -05:00
Rich Harris
1213c3da46 fix lockfile mess 2018-01-14 12:27:35 -05:00
Rich Harris
4cc2104088 add back test that got lost in a merge conflict somewhere 2018-01-14 12:23:07 -05:00
Rich Harris
d6dda371ca typo 2018-01-14 12:22:41 -05:00
Rich Harris
304c06085e Merge branch 'master' into master 2018-01-14 12:19:47 -05:00
Rich Harris
33b6450e34 Merge branch 'master' into master 2018-01-14 12:16:09 -05:00
Rich Harris
8faa98af6a -> v0.4.0 2018-01-14 12:07:06 -05:00
Rich Harris
14df138528 Merge pull request #87 from sveltejs/gh-3
Serialize preloaded data for initial page and serve to client
2018-01-14 11:50:18 -05:00
Rich Harris
44285cdb2f serialized preloaded data and send to client as initial payload - fixes #3 2018-01-14 11:22:00 -05:00
Rich Harris
bd656cfd5b Merge branch 'fix-tests-without-async' into gh-3 2018-01-14 10:45:55 -05:00
Rich Harris
c4b4bd587d use %sapper.scripts% 2018-01-14 10:45:21 -05:00
Rich Harris
2abfdb03d5 wait for window.READY so that this tests we dont double-preload 2018-01-14 10:44:38 -05:00
Rich Harris
a80ac3a8b8 change tests back to non-async versions 2018-01-14 00:43:57 -05:00
Rich Harris
887cb09386 merge 2018-01-14 00:21:55 -05:00
Rich Harris
cfeeafded4 Merge pull request #84 from sveltejs/test-in-node-6
test in node 6
2018-01-14 00:18:23 -05:00
Rich Harris
2cae674033 Merge branch 'master' into test-in-node-6 2018-01-14 00:14:40 -05:00
Rich Harris
7c0f32662d remove async from new test 2018-01-14 00:10:20 -05:00
Rich Harris
b4fb1c3268 Merge branch 'master' of github.com:sveltejs/sapper 2018-01-14 00:02:32 -05:00
Rich Harris
ecd0f673a9 test in node 6 2018-01-14 00:01:51 -05:00
Rich Harris
40d16852f7 prevent unnecessary promise chains, and hoist handler function 2018-01-14 00:01:10 -05:00
Rich Harris
133be03791 Merge pull request #72 from nolanlawson/fix-style-loader
Explicitly load style-loader/css-loader deps
2018-01-13 23:33:38 -05:00
Rich Harris
727a76ebb5 fall through to 404 if no handler exists for method 2018-01-13 23:29:41 -05:00
Rich Harris
e3c047831a add test for #77 2018-01-13 23:29:16 -05:00
Emil Tholin
81b5e0d764 Handle DELETE requests with del export 2018-01-10 19:24:22 +01:00
Emil Tholin
98e904dcfc Remove async/await from test.js 2018-01-07 12:22:40 +01:00
Nolan Lawson
ca51372150 Explicitly load style-loader/css-loader deps
This appears to fix sveltejs/sapper-template#27
2018-01-06 17:21:40 -08:00
Luke Edwards
7cef1f1120 only return from show-url if in server context 2018-01-06 16:11:39 -08:00
Luke Edwards
1b73baabce [temp - debug]: log out the preload value 2018-01-06 15:40:40 -08:00
Luke Edwards
5aa01b922b add page.title() Nightmare action 2018-01-06 15:16:18 -08:00
Luke Edwards
f0bc68be88 use built-in for document.title 2018-01-06 15:07:43 -08:00
Luke Edwards
be7c53becc quick attempt at fixing test for travis 2018-01-06 14:53:14 -08:00
freedmand
9ea4137b87 Add option to extract server-side routes at directories other than /api.
Also clarifies some texts and documentation.
2018-01-05 19:21:25 -08:00
freedmand
7588911108 Removes all async/await from the extraction pipeline, and adds unit tests for extracted client pages that match a regular expression 2018-01-05 14:56:58 -08:00
freedmand
fc8280adea Fixes small issue with reading chunk files 2018-01-05 14:42:04 -08:00
freedmand
d08f9eb5a4 Fixes funky indentation in extraction unit test 2018-01-05 14:29:46 -08:00
Emil Tholin
2b3472b1b1 Remove async/await from lib 2018-01-05 23:19:40 +01:00
freedmand
30ddb3dd7e Adds a sapper extract CLI command, which scrapes the server to run as a static website starting at the site's root.
TESTED=Basic unit test ensuring relevant routes are added.
2018-01-05 14:16:30 -08:00
Luke Edwards
0c891ba79e require chokidar lazily; dev-mode only 2018-01-05 00:39:17 -08:00
Rich Harris
ee94f355d5 update marked due to vulnerability 2018-01-04 17:17:08 -05:00
Rich Harris
bea9b7965a update package-lock 2018-01-04 17:13:17 -05:00
25 changed files with 945 additions and 386 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
node_modules node_modules
cypress/screenshots cypress/screenshots
test/app/.sapper test/app/.sapper
runtime.js runtime.js
yarn.lock

View File

@@ -3,6 +3,7 @@ sudo: false
language: node_js language: node_js
node_js: node_js:
- "6"
- "stable" - "stable"
env: env:

View File

@@ -1,5 +1,22 @@
# sapper changelog # sapper changelog
## 0.5.1
* Only write service-worker.js to filesystem in dev mode ([#90](https://github.com/sveltejs/sapper/issues/90))
## 0.5.0
* Experimental support for `sapper export` ([#9](https://github.com/sveltejs/sapper/issues/9))
* Lazily load chokidar, for faster startup ([#64](https://github.com/sveltejs/sapper/pull/64))
## 0.4.0
* `%sapper.main%` has been replaced with `%sapper.scripts%` ([#86](https://github.com/sveltejs/sapper/issues/86))
* Node 6 support ([#67](https://github.com/sveltejs/sapper/pull/67))
* Explicitly load css-loader and style-loader ([#72](https://github.com/sveltejs/sapper/pull/72))
* DELETE requests are handled with `del` exports ([#77](https://github.com/sveltejs/sapper/issues/77))
* Send preloaded data for first route to client, where possible ([#3](https://github.com/sveltejs/sapper/issues/3))
## 0.3.2 ## 0.3.2
* Expose `prefetch` function ([#61](https://github.com/sveltejs/sapper/pull/61)) * Expose `prefetch` function ([#61](https://github.com/sveltejs/sapper/pull/61))

View File

@@ -1,8 +1,29 @@
#!/usr/bin/env node #!/usr/bin/env node
const build = require('../lib/build.js');
const cmd = process.argv[2]; const cmd = process.argv[2];
const start = Date.now();
if (cmd === 'build') { if (cmd === 'build') {
process.env.NODE_ENV = 'production'; build()
require('../lib/build.js')(); .then(() => {
} const elapsed = Date.now() - start;
console.error(`built in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
})
.catch(err => {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error');
});
} else if (cmd === 'export') {
const start = Date.now();
build()
.then(() => require('../lib/utils/export.js')())
.then(() => {
const elapsed = Date.now() - start;
console.error(`extracted in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
})
.catch(err => {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error');
});
}

View File

@@ -1,3 +1,5 @@
process.env.NODE_ENV = 'production';
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const mkdirp = require('mkdirp'); const mkdirp = require('mkdirp');
@@ -14,29 +16,32 @@ module.exports = () => {
// create main.js and server-routes.js // create main.js and server-routes.js
create_app(); create_app();
function handleErrors(err, stats) { return new Promise((fulfil, reject) => {
if (err) { function handleErrors(err, stats) {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error'); if (err) {
process.exit(1); reject(err);
process.exit(1);
}
if (stats.hasErrors()) {
console.error(stats.toString({ colors: true }));
reject(new Error(`Encountered errors while building app`));
}
} }
if (stats.hasErrors()) { client.run((err, clientStats) => {
console.log(stats.toString({ colors: true })); handleErrors(err, clientStats);
process.exit(1); const clientInfo = clientStats.toJson();
} fs.writeFileSync(path.join(dest, 'stats.client.json'), JSON.stringify(clientInfo, null, ' '));
}
client.run((err, clientStats) => { server.run((err, serverStats) => {
handleErrors(err, clientStats); handleErrors(err, serverStats);
const clientInfo = clientStats.toJson(); const serverInfo = serverStats.toJson();
fs.writeFileSync(path.join(dest, 'stats.client.json'), JSON.stringify(clientInfo, null, ' ')); fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(serverInfo, null, ' '));
server.run((err, serverStats) => { generate_asset_cache(clientInfo, serverInfo);
handleErrors(err, serverStats); fulfil();
const serverInfo = serverStats.toJson(); });
fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(serverInfo, null, ' '));
generate_asset_cache(clientInfo, serverInfo);
}); });
}); });
}; };

View File

@@ -1,5 +1,6 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const serialize = require('serialize-javascript');
const route_manager = require('./route_manager.js'); const route_manager = require('./route_manager.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');
@@ -23,9 +24,11 @@ function connect_dev() {
heartbeat: 10 * 1000 heartbeat: 10 * 1000
}), }),
async (req, res, next) => { (req, res, next) => {
asset_cache = await watcher.ready; watcher.ready.then(cache => {
next(); asset_cache = cache;
next();
});
}, },
set_req_pathname, set_req_pathname,
@@ -125,76 +128,98 @@ function get_asset_handler(opts) {
}; };
} }
function get_route_handler(fn) { const resolved = Promise.resolve();
return async function handle_route(req, res, next) {
const url = req.pathname;
const { client, server } = fn(); function get_route_handler(fn) {
function handle_route(route, req, res, next, { client, server }) {
req.params = route.exec(req.pathname);
const mod = require(server.entry)[route.id];
if (route.type === 'page') {
// preload main.js and current route
// TODO detect other stuff we can preload? images, CSS, fonts?
res.set('Link', `<${client.main_file}>;rel="preload";as="script", <${client.routes[route.id]}>;rel="preload";as="script"`);
const data = { params: req.params, query: req.query };
if (mod.preload) {
const promise = Promise.resolve(mod.preload(req)).then(preloaded => {
const serialized = try_serialize(preloaded);
Object.assign(data, preloaded);
return { rendered: mod.render(data), serialized };
});
return templates.stream(res, 200, {
scripts: promise.then(({ serialized }) => {
const main = `<script src='${client.main_file}'></script>`;
if (serialized) {
return `<script>__SAPPER__ = { preloaded: ${serialized} };</script>${main}`;
}
return main;
}),
html: promise.then(({ rendered }) => rendered.html),
head: promise.then(({ rendered }) => `<noscript id='sapper-head-start'></noscript>${rendered.head}<noscript id='sapper-head-end'></noscript>`),
styles: promise.then(({ rendered }) => (rendered.css && rendered.css.code ? `<style>${rendered.css.code}</style>` : ''))
});
} else {
const { html, head, css } = mod.render(data);
const page = templates.render(200, {
scripts: `<script src='${client.main_file}'></script>`,
html,
head: `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`,
styles: (css && css.code ? `<style>${css.code}</style>` : '')
});
res.end(page);
}
}
else {
const method = req.method.toLowerCase();
// 'delete' cannot be exported from a module because it is a keyword,
// so check for 'del' instead
const method_export = method === 'delete' ? 'del' : method;
const handler = mod[method_export];
if (handler) {
handler(req, res, next);
} else {
// no matching handler for method — 404
next();
}
}
}
return function find_route(req, res, next) {
const url = req.pathname;
// 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'
}); });
try { resolved
for (const route of route_manager.routes) { .then(() => {
if (route.test(url)) { for (const route of route_manager.routes) {
req.params = route.exec(url); if (route.test(url)) return handle_route(route, req, res, next, fn());
const mod = require(server.entry)[route.id];
if (route.type === 'page') {
// preload main.js and current route
// TODO detect other stuff we can preload? images, CSS, fonts?
res.set('Link', `<${client.main_file}>;rel="preload";as="script", <${client.routes[route.id]}>;rel="preload";as="script"`);
const data = { params: req.params, query: req.query };
if (mod.preload) {
const promise = Promise.resolve(mod.preload(req)).then(preloaded => {
Object.assign(data, preloaded);
return mod.render(data);
});
await templates.stream(res, 200, {
main: client.main_file,
html: promise.then(rendered => rendered.html),
head: promise.then(({ head }) => `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`),
styles: promise.then(({ css }) => (css && css.code ? `<style>${css.code}</style>` : ''))
});
} else {
const { html, head, css } = mod.render(data);
const page = templates.render(200, {
main: client.main_file,
html,
head: `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`,
styles: (css && css.code ? `<style>${css.code}</style>` : '')
});
res.end(page);
}
}
else {
const handler = mod[req.method.toLowerCase()];
if (handler) handler(req, res, next);
}
return;
} }
}
next(); // no matching route — 404
} catch(err) { next();
res.status(500); })
res.end(templates.render(500, { .catch(err => {
title: (err && err.name) || 'Internal server error', res.status(500);
url, res.end(templates.render(500, {
error: escape_html(err && (err.details || err.message || err) || 'Unknown error'), title: (err && err.name) || 'Internal server error',
stack: err && err.stack.split('\n').slice(1).join('\n') url,
})); error: escape_html(err && (err.details || err.message || err) || 'Unknown error'),
} stack: err && err.stack.split('\n').slice(1).join('\n')
}));
});
}; };
} }
@@ -207,7 +232,7 @@ function get_not_found_handler(fn) {
title: 'Not found', title: 'Not found',
status: 404, status: 404,
method: req.method, method: req.method,
main: asset_cache.client.main_file, scripts: `<script src='${asset_cache.client.main_file}'></script>`,
url: req.url url: req.url
})); }));
}; };
@@ -235,4 +260,12 @@ function compose_handlers(handlers) {
function read_json(file) { function read_json(file) {
return JSON.parse(fs.readFileSync(file, 'utf-8')); return JSON.parse(fs.readFileSync(file, 'utf-8'));
}
function try_serialize(data) {
try {
return serialize(data);
} catch (err) {
return null;
}
} }

View File

@@ -1,5 +1,4 @@
const glob = require('glob'); const glob = require('glob');
const chokidar = require('chokidar');
const create_routes = require('./utils/create_routes.js'); const create_routes = require('./utils/create_routes.js');
const { src, dev } = require('./config.js'); const { src, dev } = require('./config.js');
@@ -20,7 +19,7 @@ function update() {
update(); update();
if (dev) { if (dev) {
const watcher = chokidar.watch(`${src}/**/*.+(html|js|mjs)`, { const watcher = require('chokidar').watch(`${src}/**/*.+(html|js|mjs)`, {
ignoreInitial: true, ignoreInitial: true,
persistent: false persistent: false
}); });
@@ -28,4 +27,4 @@ if (dev) {
watcher.on('add', update); watcher.on('add', update);
watcher.on('change', update); watcher.on('change', update);
watcher.on('unlink', update); watcher.on('unlink', update);
} }

View File

@@ -1,10 +1,21 @@
const fs = require('fs'); const fs = require('fs');
const glob = require('glob'); const glob = require('glob');
const chokidar = require('chokidar'); const chalk = require('chalk');
const framer = require('code-frame');
const { locate } = require('locate-character');
const { dev } = require('./config.js'); const { dev } = require('./config.js');
let templates; let templates;
function error(e) {
if (e.title) console.error(chalk.bold.red(e.title));
if (e.body) console.error(chalk.red(e.body));
if (e.url) console.error(chalk.cyan(e.url));
if (e.frame) console.error(chalk.grey(e.frame));
process.exit(1);
}
function create_templates() { function create_templates() {
templates = glob.sync('*.html', { cwd: 'templates' }) templates = glob.sync('*.html', { cwd: 'templates' })
.map(file => { .map(file => {
@@ -12,7 +23,24 @@ function create_templates() {
const status = file.replace('.html', '').toLowerCase(); const status = file.replace('.html', '').toLowerCase();
if (!/^[0-9x]{3}$/.test(status)) { if (!/^[0-9x]{3}$/.test(status)) {
throw new Error(`Bad template — should be a valid status code like 404.html, or a wildcard like 2xx.html`); error({
title: `templates/${file}`,
body: `Bad template — should be a valid status code like 404.html, or a wildcard like 2xx.html`
});
}
const index = template.indexOf('%sapper.main%');
if (index !== -1) {
// TODO remove this in a future version
const { line, column } = locate(template, index, { offsetLine: 1 });
const frame = framer(template, line, column);
error({
title: `templates/${file}`,
body: `<script src='%sapper.main%'> is unsupported — use %sapper.scripts% (without the <script> tag) instead`,
url: 'https://github.com/sveltejs/sapper/issues/86',
frame
});
} }
const specificity = ( const specificity = (
@@ -31,10 +59,14 @@ function create_templates() {
return key in data ? data[key] : ''; return key in data ? data[key] : '';
}); });
}, },
stream: async (res, data) => { stream: (res, data) => {
let i = 0; let i = 0;
do { function stream_inner() {
if (i >= template.length) {
return;
}
const start = template.indexOf('%sapper', i); const start = template.indexOf('%sapper', i);
if (start === -1) { if (start === -1) {
@@ -53,9 +85,14 @@ function create_templates() {
const match = /sapper\.(\w+)/.exec(tag); const match = /sapper\.(\w+)/.exec(tag);
if (!match || !(match[1] in data)) throw new Error(`Bad template`); // TODO ditto if (!match || !(match[1] in data)) throw new Error(`Bad template`); // TODO ditto
res.write(await data[match[1]]); return Promise.resolve(data[match[1]]).then(datamatch => {
i = end + 1; res.write(datamatch);
} while (i < template.length); i = end + 1;
return stream_inner();
});
}
return Promise.resolve().then(stream_inner);
} }
}; };
}) })
@@ -65,7 +102,7 @@ function create_templates() {
create_templates(); create_templates();
if (dev) { if (dev) {
const watcher = chokidar.watch('templates/**.html', { const watcher = require('chokidar').watch('templates/**.html', {
ignoreInitial: true, ignoreInitial: true,
persistent: false persistent: false
}); });
@@ -87,4 +124,4 @@ exports.stream = (res, status, data) => {
if (template) return template.stream(res, data); if (template) return template.stream(res, data);
return `Missing template for status code ${status}`; return `Missing template for status code ${status}`;
}; };

View File

@@ -1,6 +1,5 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const chokidar = require('chokidar');
const route_manager = require('../route_manager.js'); const route_manager = require('../route_manager.js');
const { src, entry, dev } = require('../config.js'); const { src, entry, dev } = require('../config.js');
@@ -70,7 +69,7 @@ function create_app() {
if (dev) { if (dev) {
route_manager.onchange(create_app); route_manager.onchange(create_app);
const watcher = chokidar.watch(`templates/main.js`, { const watcher = require('chokidar').watch(`templates/main.js`, {
ignoreInitial: true, ignoreInitial: true,
persistent: false persistent: false
}); });
@@ -80,4 +79,4 @@ if (dev) {
watcher.on('unlink', create_app); watcher.on('unlink', create_app);
} }
module.exports = create_app; module.exports = create_app;

View File

@@ -31,7 +31,7 @@ module.exports = function create_matchers(files) {
} }
} }
const pattern = new RegExp(`^${pattern_string || '\\/'}$`); const pattern = new RegExp(`^${pattern_string}\\/?$`);
const test = url => pattern.test(url); const test = url => pattern.test(url);

87
lib/utils/export.js Normal file
View File

@@ -0,0 +1,87 @@
const sander = require('sander');
const app = require('express')();
const cheerio = require('cheerio');
const fetch = require('node-fetch');
const URL = require('url-parse');
const sapper = require('../index.js');
const { PORT = 3000, OUTPUT_DIR = 'dist' } = process.env;
const { dest } = require('../config.js');
const origin = `http://localhost:${PORT}`;
module.exports = function() {
// Prep output directory
sander.rimrafSync(OUTPUT_DIR);
sander.copydirSync('assets').to(OUTPUT_DIR);
sander.copydirSync(`${dest}/client`).to(`${OUTPUT_DIR}/client`);
sander.copyFileSync(`${dest}/service-worker.js`).to(`${OUTPUT_DIR}/service-worker.js`);
// Intercept server route fetches
function save(res) {
res = res.clone();
return res.text().then(body => {
const { pathname } = new URL(res.url);
let dest = OUTPUT_DIR + pathname;
const type = res.headers.get('Content-Type');
if (type.startsWith('text/html;')) dest += '/index.html';
sander.writeFileSync(dest, body);
return body;
});
}
global.fetch = (url, opts) => {
if (url[0] === '/') {
url = `http://localhost:${PORT}${url}`;
return fetch(url, opts)
.then(r => {
save(r);
return r;
});
}
return fetch(url, opts);
};
app.use(sapper());
const server = app.listen(PORT);
const seen = new Set();
function handle(url) {
if (url.origin !== origin) return;
if (seen.has(url.pathname)) return;
seen.add(url.pathname);
return fetch(url.href)
.then(r => {
save(r);
return r.text();
})
.then(body => {
const $ = cheerio.load(body);
const hrefs = [];
$('a[href]').each((i, $a) => {
hrefs.push($a.attribs.href);
});
return hrefs.reduce((promise, href) => {
return promise.then(() => handle(new URL(href, url.href)));
}, Promise.resolve());
})
.catch(err => {
console.error(`Error rendering ${url.pathname}: ${err.message}`);
});
}
return handle(new URL(origin)) // TODO all static routes
.then(() => server.close());
};

413
package-lock.json generated
View File

@@ -1,14 +1,13 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.2.10", "version": "0.4.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/node": { "@types/node": {
"version": "7.0.51", "version": "7.0.52",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.51.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.52.tgz",
"integrity": "sha512-h5u7FnEnG+Fn44HfknTTvu199FzFWVSo97ToSRWvXl1F11UfN6wGnE7exUy23pFfDn+CeluvEoCoe4l2eCVC3g==", "integrity": "sha512-jjpyQsKGsOF/wUElNjfPULk+d8PKvJOIXk3IUeBYYmNCy5dMWfrI+JiixYNw8ppKOlcRwWTXFl0B+i5oGrf95Q=="
"dev": true
}, },
"accepts": { "accepts": {
"version": "1.3.4", "version": "1.3.4",
@@ -21,9 +20,9 @@
} }
}, },
"acorn": { "acorn": {
"version": "5.2.1", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz",
"integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==" "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug=="
}, },
"acorn-dynamic-import": { "acorn-dynamic-import": {
"version": "2.0.2", "version": "2.0.2",
@@ -256,7 +255,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"browserslist": "1.7.7", "browserslist": "1.7.7",
"caniuse-db": "1.0.30000784", "caniuse-db": "1.0.30000791",
"normalize-range": "0.1.2", "normalize-range": "0.1.2",
"num2fraction": "1.2.2", "num2fraction": "1.2.2",
"postcss": "5.2.18", "postcss": "5.2.18",
@@ -357,7 +356,7 @@
"bytes": "3.0.0", "bytes": "3.0.0",
"content-type": "1.0.4", "content-type": "1.0.4",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.1", "depd": "1.1.2",
"http-errors": "1.6.2", "http-errors": "1.6.2",
"iconv-lite": "0.4.19", "iconv-lite": "0.4.19",
"on-finished": "2.3.0", "on-finished": "2.3.0",
@@ -377,6 +376,11 @@
} }
} }
}, },
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"boom": { "boom": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
@@ -455,7 +459,7 @@
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"requires": { "requires": {
"bn.js": "4.11.8", "bn.js": "4.11.8",
"randombytes": "2.0.5" "randombytes": "2.0.6"
} }
}, },
"browserify-sign": { "browserify-sign": {
@@ -486,7 +490,7 @@
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
"dev": true, "dev": true,
"requires": { "requires": {
"caniuse-db": "1.0.30000784", "caniuse-db": "1.0.30000791",
"electron-to-chromium": "1.3.30" "electron-to-chromium": "1.3.30"
} }
}, },
@@ -566,15 +570,15 @@
"dev": true, "dev": true,
"requires": { "requires": {
"browserslist": "1.7.7", "browserslist": "1.7.7",
"caniuse-db": "1.0.30000784", "caniuse-db": "1.0.30000791",
"lodash.memoize": "4.1.2", "lodash.memoize": "4.1.2",
"lodash.uniq": "4.5.0" "lodash.uniq": "4.5.0"
} }
}, },
"caniuse-db": { "caniuse-db": {
"version": "1.0.30000784", "version": "1.0.30000791",
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000784.tgz", "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000791.tgz",
"integrity": "sha1-G+lQEtlInHcZB0+BruV9vf/mNhs=", "integrity": "sha1-Bnh/VsrvQwChfjXRN0RxI731Nvk=",
"dev": true "dev": true
}, },
"caseless": { "caseless": {
@@ -608,6 +612,19 @@
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
"dev": true "dev": true
}, },
"cheerio": {
"version": "1.0.0-rc.2",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz",
"integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=",
"requires": {
"css-select": "1.2.0",
"dom-serializer": "0.1.0",
"entities": "1.1.1",
"htmlparser2": "3.9.2",
"lodash": "4.17.4",
"parse5": "3.0.3"
}
},
"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",
@@ -720,6 +737,14 @@
"q": "1.5.1" "q": "1.5.1"
} }
}, },
"code-frame": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/code-frame/-/code-frame-5.0.0.tgz",
"integrity": "sha1-QAMHB+xG4dmdXlLcnzV/ytkNmT0=",
"requires": {
"left-pad": "1.2.0"
}
},
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"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",
@@ -943,7 +968,7 @@
"inherits": "2.0.3", "inherits": "2.0.3",
"pbkdf2": "3.0.14", "pbkdf2": "3.0.14",
"public-encrypt": "4.0.0", "public-encrypt": "4.0.0",
"randombytes": "2.0.5", "randombytes": "2.0.6",
"randomfill": "1.0.3" "randomfill": "1.0.3"
} }
}, },
@@ -954,9 +979,9 @@
"dev": true "dev": true
}, },
"css-loader": { "css-loader": {
"version": "0.28.7", "version": "0.28.8",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.8.tgz",
"integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==", "integrity": "sha512-4jGj7Ag6WUZ5lQyE4te9sJLn0lgkz6HI3WDE4aw98AkW1IAKXPP4blTpPeorlLDpNsYvojo0SYgRJOdz2KbuAw==",
"dev": true, "dev": true,
"requires": { "requires": {
"babel-code-frame": "6.26.0", "babel-code-frame": "6.26.0",
@@ -975,6 +1000,17 @@
"source-list-map": "2.0.0" "source-list-map": "2.0.0"
} }
}, },
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"requires": {
"boolbase": "1.0.0",
"css-what": "2.1.0",
"domutils": "1.5.1",
"nth-check": "1.0.1"
}
},
"css-selector-tokenizer": { "css-selector-tokenizer": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
@@ -986,6 +1022,11 @@
"regexpu-core": "1.0.0" "regexpu-core": "1.0.0"
} }
}, },
"css-what": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
"integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0="
},
"cssesc": { "cssesc": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
@@ -1171,9 +1212,9 @@
"dev": true "dev": true
}, },
"depd": { "depd": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"dev": true "dev": true
}, },
"des.js": { "des.js": {
@@ -1204,23 +1245,61 @@
"requires": { "requires": {
"bn.js": "4.11.8", "bn.js": "4.11.8",
"miller-rabin": "4.0.1", "miller-rabin": "4.0.1",
"randombytes": "2.0.5" "randombytes": "2.0.6"
} }
}, },
"doctrine": { "doctrine": {
"version": "2.0.2", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-y0tm5Pq6ywp3qSTZ1vPgVdAnbDEoeoc5wlOHXoY1c4Wug/a7JvqHIl7BTvwodaHmejWkK/9dSb3sCYfyo/om8A==", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true, "dev": true,
"requires": { "requires": {
"esutils": "2.0.2" "esutils": "2.0.2"
} }
}, },
"dom-serializer": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
"integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
"requires": {
"domelementtype": "1.1.3",
"entities": "1.1.1"
},
"dependencies": {
"domelementtype": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
}
}
},
"domain-browser": { "domain-browser": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
"integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw="
}, },
"domelementtype": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
"integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
},
"domhandler": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz",
"integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=",
"requires": {
"domelementtype": "1.3.0"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0.1.0",
"domelementtype": "1.3.0"
}
},
"duplexer": { "duplexer": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
@@ -1249,7 +1328,7 @@
"integrity": "sha1-Oj6D2WX9f6/kc76N349HJWG2JT0=", "integrity": "sha1-Oj6D2WX9f6/kc76N349HJWG2JT0=",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "7.0.51", "@types/node": "7.0.52",
"electron-download": "3.3.0", "electron-download": "3.3.0",
"extract-zip": "1.6.6" "extract-zip": "1.6.6"
} }
@@ -1266,7 +1345,7 @@
"minimist": "1.2.0", "minimist": "1.2.0",
"nugget": "2.0.1", "nugget": "2.0.1",
"path-exists": "2.1.0", "path-exists": "2.1.0",
"rc": "1.2.2", "rc": "1.2.3",
"semver": "5.4.1", "semver": "5.4.1",
"sumchecker": "1.3.1" "sumchecker": "1.3.1"
}, },
@@ -1374,6 +1453,16 @@
} }
} }
}, },
"ensure-posix-path": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.0.2.tgz",
"integrity": "sha1-pls+QtC3HPxYXrd0+ZQ8jZuRsMI="
},
"entities": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
},
"errno": { "errno": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz",
@@ -1506,9 +1595,9 @@
} }
}, },
"eslint": { "eslint": {
"version": "4.13.1", "version": "4.15.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.13.1.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.15.0.tgz",
"integrity": "sha512-UCJVV50RtLHYzBp1DZ8CMPtRSg4iVZvjgO9IJHIKyWU/AnJVjtdRikoUPLB29n5pzMB7TnsLQWf0V6VUJfoPfw==", "integrity": "sha512-zEO/Z1ZUxIQ+MhDVKkVTUYpIPDTEJLXGMrkID+5v1NeQHtCz6FZikWuFRgxE1Q/RV2V4zVl1u3xmpPADHhMZ6A==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "5.5.2", "ajv": "5.5.2",
@@ -1517,11 +1606,11 @@
"concat-stream": "1.6.0", "concat-stream": "1.6.0",
"cross-spawn": "5.1.0", "cross-spawn": "5.1.0",
"debug": "3.1.0", "debug": "3.1.0",
"doctrine": "2.0.2", "doctrine": "2.1.0",
"eslint-scope": "3.7.1", "eslint-scope": "3.7.1",
"eslint-visitor-keys": "1.0.0",
"espree": "3.5.2", "espree": "3.5.2",
"esquery": "1.0.0", "esquery": "1.0.0",
"estraverse": "4.2.0",
"esutils": "2.0.2", "esutils": "2.0.2",
"file-entry-cache": "2.0.0", "file-entry-cache": "2.0.0",
"functional-red-black-tree": "1.0.1", "functional-red-black-tree": "1.0.1",
@@ -1584,9 +1673,9 @@
} }
}, },
"eslint-import-resolver-node": { "eslint-import-resolver-node": {
"version": "0.3.1", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
"integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
@@ -1635,7 +1724,7 @@
"contains-path": "0.1.0", "contains-path": "0.1.0",
"debug": "2.6.9", "debug": "2.6.9",
"doctrine": "1.5.0", "doctrine": "1.5.0",
"eslint-import-resolver-node": "0.3.1", "eslint-import-resolver-node": "0.3.2",
"eslint-module-utils": "2.1.1", "eslint-module-utils": "2.1.1",
"has": "1.0.1", "has": "1.0.1",
"lodash.cond": "4.5.2", "lodash.cond": "4.5.2",
@@ -1674,13 +1763,19 @@
"estraverse": "4.2.0" "estraverse": "4.2.0"
} }
}, },
"eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
"dev": true
},
"espree": { "espree": {
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz",
"integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "5.2.1", "acorn": "5.3.0",
"acorn-jsx": "3.0.1" "acorn-jsx": "3.0.1"
} }
}, },
@@ -1813,7 +1908,7 @@
"cookie": "0.3.1", "cookie": "0.3.1",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.1", "depd": "1.1.2",
"encodeurl": "1.0.1", "encodeurl": "1.0.1",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"etag": "1.8.1", "etag": "1.8.1",
@@ -3113,6 +3208,19 @@
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
}, },
"htmlparser2": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
"integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
"requires": {
"domelementtype": "1.3.0",
"domhandler": "2.4.1",
"domutils": "1.5.1",
"entities": "1.1.1",
"inherits": "2.0.3",
"readable-stream": "2.3.3"
}
},
"http-errors": { "http-errors": {
"version": "1.6.2", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
@@ -3125,6 +3233,12 @@
"statuses": "1.3.1" "statuses": "1.3.1"
}, },
"dependencies": { "dependencies": {
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=",
"dev": true
},
"setprototypeof": { "setprototypeof": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
@@ -3167,18 +3281,18 @@
"integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
"dev": true, "dev": true,
"requires": { "requires": {
"postcss": "6.0.14" "postcss": "6.0.16"
}, },
"dependencies": { "dependencies": {
"postcss": { "postcss": {
"version": "6.0.14", "version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.3.0", "chalk": "2.3.0",
"source-map": "0.6.1", "source-map": "0.6.1",
"supports-color": "4.5.0" "supports-color": "5.1.0"
} }
}, },
"source-map": { "source-map": {
@@ -3186,6 +3300,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"supports-color": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
"integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
} }
} }
}, },
@@ -3680,6 +3803,11 @@
"invert-kv": "1.0.0" "invert-kv": "1.0.0"
} }
}, },
"left-pad": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz",
"integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4="
},
"levn": { "levn": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -3716,6 +3844,11 @@
"json5": "0.5.1" "json5": "0.5.1"
} }
}, },
"locate-character": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-2.0.5.tgz",
"integrity": "sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg=="
},
"locate-path": { "locate-path": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
@@ -3796,6 +3929,14 @@
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=",
"dev": true "dev": true
}, },
"matcher-collection": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.0.5.tgz",
"integrity": "sha512-nUCmzKipcJEwYsBVAFh5P+d7JBuhJaW1xs85Hara9xuMLqtCVUrW6DSC0JVIkluxEH2W45nPBM/wjHtBXa/tYA==",
"requires": {
"minimatch": "3.0.4"
}
},
"math-expression-evaluator": { "math-expression-evaluator": {
"version": "1.2.17", "version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
@@ -4069,9 +4210,9 @@
} }
}, },
"mocha": { "mocha": {
"version": "4.0.1", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz",
"integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==",
"dev": true, "dev": true,
"requires": { "requires": {
"browser-stdout": "1.3.0", "browser-stdout": "1.3.0",
@@ -4317,6 +4458,14 @@
"path-key": "2.0.1" "path-key": "2.0.1"
} }
}, },
"nth-check": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
"integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=",
"requires": {
"boolbase": "1.0.0"
}
},
"nugget": { "nugget": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz",
@@ -4461,18 +4610,26 @@
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
}, },
"p-limit": { "p-limit": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
"integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=" "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
"requires": {
"p-try": "1.0.0"
}
}, },
"p-locate": { "p-locate": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"requires": { "requires": {
"p-limit": "1.1.0" "p-limit": "1.2.0"
} }
}, },
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
},
"pako": { "pako": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
@@ -4509,6 +4666,14 @@
"error-ex": "1.3.1" "error-ex": "1.3.1"
} }
}, },
"parse5": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"requires": {
"@types/node": "7.0.52"
}
},
"parseurl": { "parseurl": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
@@ -4884,18 +5049,18 @@
"integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=",
"dev": true, "dev": true,
"requires": { "requires": {
"postcss": "6.0.14" "postcss": "6.0.16"
}, },
"dependencies": { "dependencies": {
"postcss": { "postcss": {
"version": "6.0.14", "version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.3.0", "chalk": "2.3.0",
"source-map": "0.6.1", "source-map": "0.6.1",
"supports-color": "4.5.0" "supports-color": "5.1.0"
} }
}, },
"source-map": { "source-map": {
@@ -4903,6 +5068,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"supports-color": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
"integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
} }
} }
}, },
@@ -4913,18 +5087,18 @@
"dev": true, "dev": true,
"requires": { "requires": {
"css-selector-tokenizer": "0.7.0", "css-selector-tokenizer": "0.7.0",
"postcss": "6.0.14" "postcss": "6.0.16"
}, },
"dependencies": { "dependencies": {
"postcss": { "postcss": {
"version": "6.0.14", "version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.3.0", "chalk": "2.3.0",
"source-map": "0.6.1", "source-map": "0.6.1",
"supports-color": "4.5.0" "supports-color": "5.1.0"
} }
}, },
"source-map": { "source-map": {
@@ -4932,6 +5106,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"supports-color": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
"integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
} }
} }
}, },
@@ -4942,18 +5125,18 @@
"dev": true, "dev": true,
"requires": { "requires": {
"css-selector-tokenizer": "0.7.0", "css-selector-tokenizer": "0.7.0",
"postcss": "6.0.14" "postcss": "6.0.16"
}, },
"dependencies": { "dependencies": {
"postcss": { "postcss": {
"version": "6.0.14", "version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.3.0", "chalk": "2.3.0",
"source-map": "0.6.1", "source-map": "0.6.1",
"supports-color": "4.5.0" "supports-color": "5.1.0"
} }
}, },
"source-map": { "source-map": {
@@ -4961,6 +5144,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"supports-color": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
"integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
} }
} }
}, },
@@ -4971,18 +5163,18 @@
"dev": true, "dev": true,
"requires": { "requires": {
"icss-replace-symbols": "1.1.0", "icss-replace-symbols": "1.1.0",
"postcss": "6.0.14" "postcss": "6.0.16"
}, },
"dependencies": { "dependencies": {
"postcss": { "postcss": {
"version": "6.0.14", "version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", "integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.3.0", "chalk": "2.3.0",
"source-map": "0.6.1", "source-map": "0.6.1",
"supports-color": "4.5.0" "supports-color": "5.1.0"
} }
}, },
"source-map": { "source-map": {
@@ -4990,6 +5182,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
},
"supports-color": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz",
"integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
} }
} }
}, },
@@ -5196,7 +5397,7 @@
"browserify-rsa": "4.0.1", "browserify-rsa": "4.0.1",
"create-hash": "1.1.3", "create-hash": "1.1.3",
"parse-asn1": "5.1.0", "parse-asn1": "5.1.0",
"randombytes": "2.0.5" "randombytes": "2.0.6"
} }
}, },
"punycode": { "punycode": {
@@ -5274,9 +5475,9 @@
} }
}, },
"randombytes": { "randombytes": {
"version": "2.0.5", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
"integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
"requires": { "requires": {
"safe-buffer": "5.1.1" "safe-buffer": "5.1.1"
} }
@@ -5286,7 +5487,7 @@
"resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz",
"integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==", "integrity": "sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ==",
"requires": { "requires": {
"randombytes": "2.0.5", "randombytes": "2.0.6",
"safe-buffer": "5.1.1" "safe-buffer": "5.1.1"
} }
}, },
@@ -5309,9 +5510,9 @@
} }
}, },
"rc": { "rc": {
"version": "1.2.2", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.3.tgz",
"integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", "integrity": "sha1-UVdakA+N1oOBxxC0cSwhVMPiA1s=",
"dev": true, "dev": true,
"requires": { "requires": {
"deep-extend": "0.4.2", "deep-extend": "0.4.2",
@@ -5596,9 +5797,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "0.53.0", "version": "0.53.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-0.53.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.53.4.tgz",
"integrity": "sha512-bG5RzkF7wcOHmKoVAFtERZ5P9TNJP9/AF+ldwGm/Rx6pejura+Z9BDU0GJtzWu+lYXwjfINmgiCclhLJzP/OXA==", "integrity": "sha512-ErW5cFw5KY/qiyUlPDJ7iBhw51Iro/oyvxETupO85bMg5T7MLlFj3lEDzwjLTOxJAyzWQanUYj/LZHm6aLLm5w==",
"dev": true "dev": true
}, },
"rollup-plugin-typescript": { "rollup-plugin-typescript": {
@@ -5667,6 +5868,16 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
}, },
"sander": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.6.0.tgz",
"integrity": "sha1-rxYkzX+2362Y6+9WUxn5IAeNqSU=",
"requires": {
"graceful-fs": "4.1.11",
"mkdirp": "0.5.1",
"rimraf": "2.6.2"
}
},
"sax": { "sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@@ -5694,7 +5905,7 @@
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "2.6.9", "debug": "2.6.9",
"depd": "1.1.1", "depd": "1.1.2",
"destroy": "1.0.4", "destroy": "1.0.4",
"encodeurl": "1.0.1", "encodeurl": "1.0.1",
"escape-html": "1.0.3", "escape-html": "1.0.3",
@@ -5719,6 +5930,11 @@
} }
} }
}, },
"serialize-javascript": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz",
"integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU="
},
"serve-static": { "serve-static": {
"version": "1.13.1", "version": "1.13.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
@@ -6109,15 +6325,15 @@
} }
}, },
"svelte": { "svelte": {
"version": "1.49.1", "version": "1.51.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-1.49.1.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-1.51.1.tgz",
"integrity": "sha512-oiB4nF19AAQsD+lvJ1ycb58Iqc1NoZjNvyf0dG+i/RjFU3CLQtCO7pdiJN72IwPYtHAqMRFra/Qkayg8VSFH4A==", "integrity": "sha512-Uf9huHmtXLzdmkfuEYQeXMvFE9dt8M9/jzujX3/Q2Ek2dzvVz1ARTLP76hlnGqUf4q28w8wfkQxFEx4LgDV5MQ==",
"dev": true "dev": true
}, },
"svelte-loader": { "svelte-loader": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-2.3.2.tgz", "resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-2.3.3.tgz",
"integrity": "sha512-0lIt2GGqHtLETRl8rixXW8Vkij/GnukU+3gZ3Yvww1c2XwVjaucVXQgui5J7bStAtdAmOq+DHjGbwLPsgg47cg==", "integrity": "sha512-OdBoANPvNdP1G3DVzHXdjMetv2kFrv7+R5lhKhcp+E6ew3Gna20+Ja+Sq3gVN9O4vwfhTfrWBhWMFtbfJr1kug==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "1.1.0", "loader-utils": "1.1.0",
@@ -6497,9 +6713,9 @@
} }
}, },
"wait-on": { "wait-on": {
"version": "2.0.2", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-2.0.2.tgz", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-2.1.0.tgz",
"integrity": "sha1-CoT9BwJMb8Joyw6r5YW+IXqvK6o=", "integrity": "sha512-hDwJ674+7dfiiK/cxtYCwPxlnjXDjto/pCz1PF02sXUhqCqCWsgvxZln0699PReWqXXgkxqkF6DDo5Rj9sjNvw==",
"dev": true, "dev": true,
"requires": { "requires": {
"core-js": "2.5.3", "core-js": "2.5.3",
@@ -6517,6 +6733,15 @@
} }
} }
}, },
"walk-sync": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-0.3.2.tgz",
"integrity": "sha512-FMB5VqpLqOCcqrzA9okZFc0wq0Qbmdm396qJxvQZhDpyu0W95G9JCmp74tx7iyYnyOcBtUuKJsgIKAqjozvmmQ==",
"requires": {
"ensure-posix-path": "1.0.2",
"matcher-collection": "1.0.5"
}
},
"watchpack": { "watchpack": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz",
@@ -6532,7 +6757,7 @@
"resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.10.0.tgz",
"integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==", "integrity": "sha512-fxxKXoicjdXNUMY7LIdY89tkJJJ0m1Oo8PQutZ5rLgWbV5QVKI15Cn7+/IHnRTd3vfKfiwBx6SBqlorAuNA8LA==",
"requires": { "requires": {
"acorn": "5.2.1", "acorn": "5.3.0",
"acorn-dynamic-import": "2.0.2", "acorn-dynamic-import": "2.0.2",
"ajv": "5.5.2", "ajv": "5.5.2",
"ajv-keywords": "2.1.1", "ajv-keywords": "2.1.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.3.2", "version": "0.5.1",
"description": "Military-grade apps, engineered by Svelte", "description": "Military-grade apps, engineered by Svelte",
"main": "lib/index.js", "main": "lib/index.js",
"bin": { "bin": {
@@ -18,12 +18,19 @@
}, },
"dependencies": { "dependencies": {
"chalk": "^2.3.0", "chalk": "^2.3.0",
"cheerio": "^1.0.0-rc.2",
"chokidar": "^1.7.0", "chokidar": "^1.7.0",
"code-frame": "^5.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"locate-character": "^2.0.5",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"relative": "^3.0.2", "relative": "^3.0.2",
"require-relative": "^0.8.7", "require-relative": "^0.8.7",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"sander": "^0.6.0",
"serialize-javascript": "^1.4.0",
"url-parse": "^1.2.0",
"walk-sync": "^0.3.2",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-hot-middleware": "^2.21.0" "webpack-hot-middleware": "^2.21.0"
}, },

View File

@@ -68,10 +68,16 @@ function render(Component: ComponentConstructor, data: any, scroll: ScrollPositi
} }
} }
function prepare_route(Component, data) { function prepare_route(Component: ComponentConstructor, data: RouteData) {
return Promise.resolve( if (!Component.preload) {
Component.preload ? Component.preload(data) : {} return { Component, data };
).then(preloaded => { }
if (!component && window.__SAPPER__ && window.__SAPPER__.preloaded) {
return { Component, data: Object.assign(data, window.__SAPPER__.preloaded) };
}
return Promise.resolve(Component.preload(data)).then(preloaded => {
Object.assign(data, preloaded) Object.assign(data, preloaded)
return { Component, data }; return { Component, data };
}); });
@@ -176,10 +182,10 @@ export function prefetch(href: string) {
} }
function handle_touchstart_mouseover(event: MouseEvent | TouchEvent) { function handle_touchstart_mouseover(event: MouseEvent | TouchEvent) {
const a: HTMLAnchorElement = <HTMLAnchorElement>findAnchor(<Node>event.target); const a: HTMLAnchorElement = <HTMLAnchorElement>findAnchor(<Node>event.target);
if (!a || a.rel !== 'prefetch') return; if (!a || a.rel !== 'prefetch') return;
prefetch(a.href); prefetch(a.href);
} }
let inited: boolean; let inited: boolean;

3
test/app/.gitignore vendored
View File

@@ -3,4 +3,5 @@ node_modules
.sapper .sapper
yarn.lock yarn.lock
cypress/screenshots cypress/screenshots
templates/.* templates/.*
dist

View File

@@ -2722,9 +2722,9 @@
"integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ="
}, },
"marked": { "marked": {
"version": "0.3.7", "version": "0.3.9",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.7.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.9.tgz",
"integrity": "sha512-zBEP4qO1YQp5aXHt8S5wTiOv9i2X74V/LQL0zhUNvVaklt6Ywa6lChxIvS+ibYlCGgADwKwZFhjC3+XfpsvQvQ==" "integrity": "sha512-nW5u0dxpXxHfkHzzrveY45gCbi+R4PaO4WRZYqZNl+vB0hVGeqlFn0aOg1c8AKL63TrNFn9Bm2UP4AdiZ9TPLw=="
}, },
"math-expression-evaluator": { "math-expression-evaluator": {
"version": "1.2.17", "version": "1.2.17",

View File

@@ -15,7 +15,7 @@
"express": "^4.16.2", "express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"glob": "^7.1.2", "glob": "^7.1.2",
"marked": "^0.3.7", "marked": "^0.3.9",
"node-fetch": "^1.7.3", "node-fetch": "^1.7.3",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"serve-static": "^1.13.1", "serve-static": "^1.13.1",

View File

@@ -0,0 +1,9 @@
export function del(req, res) {
res.set({
'Content-Type': 'application/json'
});
res.end(JSON.stringify({
id: req.params.id
}));
}

View File

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

View File

@@ -0,0 +1,15 @@
<button class='del' on:click='del()'>delete</button>
<script>
export default {
methods: {
del() {
fetch(`/api/delete/42`, { method: 'DELETE' })
.then(r => r.json())
.then(data => {
window.deleted = data;
});
}
}
};
</script>

View File

@@ -3,7 +3,7 @@
<script> <script>
export default { export default {
preload({ url }) { preload({ url }) {
return { url }; if (url) return { url };
} }
}; };
</script> </script>

View File

@@ -32,6 +32,6 @@
<!-- Sapper creates a <script> tag containing `templates/main.js` <!-- Sapper creates a <script> tag containing `templates/main.js`
and anything else it needs to hydrate the app and and anything else it needs to hydrate the app and
initialise the router --> initialise the router -->
<script src='%sapper.main%'></script> %sapper.scripts%
</body> </body>
</html> </html>

View File

@@ -5,10 +5,17 @@ const serve = require('serve-static');
const Nightmare = require('nightmare'); const Nightmare = require('nightmare');
const getPort = require('get-port'); const getPort = require('get-port');
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const walkSync = require('walk-sync');
run('production'); run('production');
run('development'); run('development');
Nightmare.action('page', {
title(done) {
this.evaluate_now(() => document.querySelector('h1').textContent, done);
}
});
function run(env) { function run(env) {
describe(`env=${env}`, function () { describe(`env=${env}`, function () {
this.timeout(20000); this.timeout(20000);
@@ -62,52 +69,60 @@ function run(env) {
}); });
} }
before(async () => { before(() => {
process.chdir(path.resolve(__dirname, '../app')); process.chdir(path.resolve(__dirname, '../app'));
process.env.NODE_ENV = env; process.env.NODE_ENV = env;
let exec_promise = Promise.resolve();
let sapper;
if (env === 'production') { if (env === 'production') {
const cli = path.resolve(__dirname, '../../cli/index.js'); const cli = path.resolve(__dirname, '../../cli/index.js');
await exec(`${cli} build`); exec_promise = exec(`${cli} build`).then(() => exec(`${cli} export`));
} }
const resolved = require.resolve('../..'); return exec_promise.then(() => {
delete require.cache[resolved]; const resolved = require.resolve('../..');
const sapper = require(resolved); delete require.cache[resolved];
sapper = require(resolved);
PORT = await getPort(); return getPort();
base = `http://localhost:${PORT}`; }).then(port => {
PORT = port;
base = `http://localhost:${PORT}`;
global.fetch = (url, opts) => { global.fetch = (url, opts) => {
if (url[0] === '/') url = `${base}${url}`; if (url[0] === '/') url = `${base}${url}`;
return fetch(url, opts); return fetch(url, opts);
}; };
let captured; let captured;
capture = async fn => { capture = fn => {
const result = captured = []; const result = captured = [];
await fn(); return fn().then(() => {
captured = null; captured = null;
return result; return result;
}; });
};
const app = express(); const app = express();
app.use(serve('assets')); app.use(serve('assets'));
app.use((req, res, next) => { app.use((req, res, next) => {
if (captured) captured.push(req); if (captured) captured.push(req);
next(); next();
}); });
middleware = sapper(); middleware = sapper();
app.use(middleware); app.use(middleware);
return new Promise((fulfil, reject) => { return new Promise((fulfil, reject) => {
server = app.listen(PORT, err => { server = app.listen(PORT, err => {
if (err) reject(err); if (err) reject(err);
else fulfil(); else fulfil();
});
}); });
}); });
}); });
@@ -137,189 +152,265 @@ function run(env) {
}); });
}); });
afterEach(async () => { afterEach(() => {
await nightmare.end(); return nightmare.end();
}); });
it('serves /', async () => { it('serves /', () => {
const title = await nightmare return nightmare.goto(base).page.title().then(title => {
.goto(base) assert.equal(title, 'Great success!');
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'Great success!');
});
it('serves static route', async () => {
const title = await nightmare
.goto(`${base}/about`)
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'About this site');
});
it('serves dynamic route', async () => {
const title = await nightmare
.goto(`${base}/blog/what-is-sapper`)
.evaluate(() => document.querySelector('h1').textContent);
assert.equal(title, 'What is Sapper?');
});
it('navigates to a new page without reloading', async () => {
await nightmare.goto(base).wait(() => window.READY).wait(200);
const requests = await capture(async () => {
await nightmare.click('a[href="/about"]');
}); });
assert.equal(
await nightmare.path(),
'/about'
);
assert.equal(
await nightmare.evaluate(() => document.title),
'About'
);
assert.deepEqual(requests.map(r => r.url), []);
}); });
it('navigates programmatically', async () => { it('serves static route', () => {
await nightmare return nightmare.goto(`${base}/about`).page.title().then(title => {
assert.equal(title, 'About this site');
});
});
it('serves dynamic route', () => {
return nightmare.goto(`${base}/blog/what-is-sapper`).page.title().then(title => {
assert.equal(title, 'What is Sapper?');
});
});
it('navigates to a new page without reloading', () => {
return nightmare.goto(base).wait(() => window.READY).wait(200)
.then(() => {
return capture(() => nightmare.click('a[href="/about"]'));
})
.then(requests => {
assert.deepEqual(requests.map(r => r.url), []);
return nightmare.path();
})
.then(path => {
assert.equal(path, '/about');
return nightmare.title();
})
.then(title => {
assert.equal(title, 'About');
});
});
it('navigates programmatically', () => {
return nightmare
.goto(`${base}/about`) .goto(`${base}/about`)
.wait(() => window.READY) .wait(() => window.READY)
.click('.goto') .click('.goto')
.wait(() => window.location.pathname === '/blog/what-is-sapper') .wait(() => window.location.pathname === '/blog/what-is-sapper')
.wait(100); .wait(100)
.title()
assert.equal( .then(title => {
await nightmare.evaluate(() => document.title), assert.equal(title, 'What is Sapper?');
'What is Sapper?' });
);
}); });
it('prefetches programmatically', async () => { it('prefetches programmatically', () => {
await nightmare return nightmare
.goto(`${base}/about`) .goto(`${base}/about`)
.wait(() => window.READY); .wait(() => window.READY)
.then(() => {
const requests = await capture(async () => { return capture(() => {
return await nightmare return nightmare
.click('.prefetch') .click('.prefetch')
.wait(100); .wait(100);
}); });
})
assert.ok(!!requests.find(r => r.url === '/api/blog/why-the-name')); .then(requests => {
assert.ok(!!requests.find(r => r.url === '/api/blog/why-the-name'));
});
}); });
it('scrolls to active deeplink', async () => { it('scrolls to active deeplink', () => {
const scrollY = await nightmare return nightmare
.goto(`${base}/blog/a-very-long-post#four`) .goto(`${base}/blog/a-very-long-post#four`)
.wait(() => window.READY) .wait(() => window.READY)
.wait(100) .wait(100)
.evaluate(() => window.scrollY); .evaluate(() => window.scrollY)
.then(scrollY => {
assert.ok(scrollY > 0, scrollY); assert.ok(scrollY > 0, scrollY);
});
}); });
it('reuses prefetch promise', async () => { it('reuses prefetch promise', () => {
await nightmare return nightmare
.goto(`${base}/blog`) .goto(`${base}/blog`)
.wait(() => window.READY) .wait(() => window.READY)
.wait(200); .wait(200)
.then(() => {
return capture(() => {
return nightmare
.mouseover('[href="/blog/what-is-sapper"]')
.wait(200);
});
})
.then(mouseover_requests => {
assert.deepEqual(mouseover_requests.map(r => r.url), [
'/api/blog/what-is-sapper'
]);
const mouseover_requests = (await capture(async () => { return capture(() => {
await nightmare return nightmare
.mouseover('[href="/blog/what-is-sapper"]') .click('[href="/blog/what-is-sapper"]')
.wait(200); .wait(200);
})).map(r => r.url); });
})
assert.deepEqual(mouseover_requests, [ .then(click_requests => {
'/api/blog/what-is-sapper' assert.deepEqual(click_requests.map(r => r.url), []);
]); });
const click_requests = (await capture(async () => {
await nightmare
.click('[href="/blog/what-is-sapper"]')
.wait(200);
})).map(r => r.url);
assert.deepEqual(click_requests, []);
}); });
it('cancels navigation if subsequent navigation occurs during preload', async () => { it('cancels navigation if subsequent navigation occurs during preload', () => {
await nightmare return nightmare
.goto(base) .goto(base)
.wait(() => window.READY) .wait(() => window.READY)
.click('a[href="/slow-preload"]') .click('a[href="/slow-preload"]')
.wait(100) .wait(100)
.click('a[href="/about"]') .click('a[href="/about"]')
.wait(100); .wait(100)
.then(() => nightmare.path())
assert.equal( .then(path => {
await nightmare.path(), assert.equal(path, '/about');
'/about' return nightmare.title();
); })
.then(title => {
assert.equal( assert.equal(title, 'About');
await nightmare.evaluate(() => document.querySelector('h1').textContent), return nightmare.evaluate(() => window.fulfil({})).wait(100);
'About this site' })
); .then(() => nightmare.path())
.then(path => {
await nightmare assert.equal(path, '/about');
.evaluate(() => window.fulfil({})) return nightmare.title();
.wait(100); })
.then(title => {
assert.equal( assert.equal(title, 'About');
await nightmare.path(), });
'/about'
);
assert.equal(
await nightmare.evaluate(() => document.querySelector('h1').textContent),
'About this site'
);
}); });
it('passes entire request object to preload', async () => { it('passes entire request object to preload', () => {
const html = await nightmare return nightmare
.goto(`${base}/show-url`) .goto(`${base}/show-url`)
.evaluate(() => document.querySelector('p').innerHTML); .wait(() => window.READY)
.evaluate(() => document.querySelector('p').innerHTML)
.end().then(html => {
assert.equal(html, `URL is /show-url`);
});
});
assert.equal(html, `URL is /show-url`); it('calls a delete handler', () => {
return nightmare
.goto(`${base}/delete-test`)
.wait(() => window.READY)
.click('.del')
.wait(() => window.deleted)
.evaluate(() => window.deleted.id)
.then(id => {
assert.equal(id, 42);
});
}); });
}); });
describe('headers', () => { describe('headers', () => {
it('sets Content-Type and Link...preload headers', async () => { it('sets Content-Type and Link...preload headers', () => {
const { headers } = await get('/'); return get('/').then(({ headers }) => {
assert.equal(
headers['Content-Type'],
'text/html'
);
assert.equal( assert.ok(
headers['Content-Type'], /<\/client\/main.\w+\.js>;rel="preload";as="script", <\/client\/_.\d+.\w+.js>;rel="preload";as="script"/.test(headers['Link']),
'text/html' headers['Link']
); );
});
assert.ok(
/<\/client\/main.\w+\.js>;rel="preload";as="script", <\/client\/_.\d+.\w+.js>;rel="preload";as="script"/.test(headers['Link']),
headers['Link']
);
}); });
}); });
if (env === 'production') {
describe('export', () => {
it('export all pages', () => {
const dest = path.resolve(__dirname, '../app/dist');
// Pages that should show up in the extraction directory.
const expectedPages = [
'index.html',
'about/index.html',
'slow-preload/index.html',
'blog/index.html',
'blog/a-very-long-post/index.html',
'blog/how-can-i-get-involved/index.html',
'blog/how-is-sapper-different-from-next/index.html',
'blog/how-to-use-sapper/index.html',
'blog/what-is-sapper/index.html',
'blog/why-the-name/index.html',
'api/blog/contents',
'api/blog/a-very-long-post',
'api/blog/how-can-i-get-involved',
'api/blog/how-is-sapper-different-from-next',
'api/blog/how-to-use-sapper',
'api/blog/what-is-sapper',
'api/blog/why-the-name',
'favicon.png',
'global.css',
'great-success.png',
'manifest.json',
'service-worker.js',
'svelte-logo-192.png',
'svelte-logo-512.png',
];
// Client scripts that should show up in the extraction directory.
const expectedClientRegexes = [
/client\/_\..*?\.js/,
/client\/about\..*?\.js/,
/client\/blog_\$slug\$\..*?\.js/,
/client\/blog\..*?\.js/,
/client\/main\..*?\.js/,
/client\/show_url\..*?\.js/,
/client\/slow_preload\..*?\.js/,
];
const allPages = walkSync(dest);
expectedPages.forEach((expectedPage) => {
assert.ok(allPages.includes(expectedPage),
`Could not find page matching ${expectedPage}`);
});
expectedClientRegexes.forEach((expectedRegex) => {
// Ensure each client page regular expression matches at least one
// generated page.
let matched = false;
for (const page of allPages) {
if (expectedRegex.test(page)) {
matched = true;
break;
}
}
assert.ok(matched,
`Could not find client page matching ${expectedRegex}`);
});
});
});
}
}); });
} }
function exec(cmd) { function exec(cmd) {
return new Promise((fulfil, reject) => { return new Promise((fulfil, reject) => {
require('child_process').exec(cmd, (err, stdout, stderr) => { const parts = cmd.split(' ');
if (err) { const proc = require('child_process').spawn(parts.shift(), parts);
process.stdout.write(stdout);
process.stderr.write(stderr);
return reject(err); proc.stdout.on('data', data => {
} process.stdout.write(data);
fulfil();
}); });
proc.stderr.on('data', data => {
process.stderr.write(data);
});
proc.on('error', reject);
proc.on('close', () => fulfil());
}); });
} }

View File

@@ -6,7 +6,12 @@ module.exports = {
client: { client: {
entry: () => { entry: () => {
return { return {
main: entry.client main: [
entry.client,
// workaround for https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/456
'style-loader/lib/addStyles',
'css-loader/lib/css-base'
]
}; };
}, },
@@ -36,4 +41,4 @@ module.exports = {
}; };
} }
} }
}; };