mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-12 03:05:12 +00:00
wip
This commit is contained in:
268
src/middleware/index.js
Normal file
268
src/middleware/index.js
Normal file
@@ -0,0 +1,268 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import serialize from 'serialize-javascript';
|
||||
import escape_html from 'escape-html';
|
||||
import * as route_manager from '../shared/route_manager.js';
|
||||
import * as templates from '../shared/templates.js';
|
||||
import create_app from '../shared/utils/create_app.js';
|
||||
import create_watcher from '../shared/utils/create_watcher.js';
|
||||
import * as compilers from '../shared/utils/compilers.js';
|
||||
import generate_asset_cache from '../shared/generate_asset_cache.js';
|
||||
import { dest, dev } from '../config.js';
|
||||
|
||||
function connect_dev() {
|
||||
create_app();
|
||||
|
||||
const watcher = create_watcher();
|
||||
|
||||
let asset_cache;
|
||||
|
||||
const middleware = compose_handlers([
|
||||
require('webpack-hot-middleware')(compilers.client, {
|
||||
reload: true,
|
||||
path: '/__webpack_hmr',
|
||||
heartbeat: 10 * 1000
|
||||
}),
|
||||
|
||||
(req, res, next) => {
|
||||
watcher.ready.then(cache => {
|
||||
asset_cache = cache;
|
||||
next();
|
||||
});
|
||||
},
|
||||
|
||||
set_req_pathname,
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname === '/index.html',
|
||||
type: 'text/html',
|
||||
cache: 'max-age=600',
|
||||
fn: () => asset_cache.client.index
|
||||
}),
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname === '/service-worker.js',
|
||||
type: 'application/javascript',
|
||||
cache: 'max-age=600',
|
||||
fn: () => asset_cache.client.service_worker
|
||||
}),
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname.startsWith('/client/'),
|
||||
type: 'application/javascript',
|
||||
cache: 'max-age=31536000',
|
||||
fn: pathname => asset_cache.client.chunks[pathname]
|
||||
}),
|
||||
|
||||
get_route_handler(() => asset_cache),
|
||||
|
||||
get_not_found_handler(() => asset_cache)
|
||||
]);
|
||||
|
||||
middleware.close = () => {
|
||||
watcher.close();
|
||||
// TODO shut down chokidar
|
||||
};
|
||||
|
||||
return middleware;
|
||||
}
|
||||
|
||||
function connect_prod() {
|
||||
const asset_cache = generate_asset_cache(
|
||||
read_json(path.join(dest, 'stats.client.json')),
|
||||
read_json(path.join(dest, 'stats.server.json'))
|
||||
);
|
||||
|
||||
const middleware = compose_handlers([
|
||||
set_req_pathname,
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname === '/index.html',
|
||||
type: 'text/html',
|
||||
cache: 'max-age=600',
|
||||
fn: () => asset_cache.client.index
|
||||
}),
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname === '/service-worker.js',
|
||||
type: 'application/javascript',
|
||||
cache: 'max-age=600',
|
||||
fn: () => asset_cache.client.service_worker
|
||||
}),
|
||||
|
||||
get_asset_handler({
|
||||
filter: pathname => pathname.startsWith('/client/'),
|
||||
type: 'application/javascript',
|
||||
cache: 'max-age=31536000',
|
||||
fn: pathname => asset_cache.client.chunks[pathname]
|
||||
}),
|
||||
|
||||
get_route_handler(() => asset_cache),
|
||||
|
||||
get_not_found_handler(() => asset_cache)
|
||||
]);
|
||||
|
||||
// here for API consistency between dev, and prod, but
|
||||
// doesn't actually need to do anything
|
||||
middleware.close = () => {};
|
||||
|
||||
return middleware;
|
||||
}
|
||||
|
||||
export default dev ? connect_dev : connect_prod;
|
||||
|
||||
function set_req_pathname(req, res, next) {
|
||||
req.pathname = req.url.replace(/\?.+/, '');
|
||||
next();
|
||||
}
|
||||
|
||||
function get_asset_handler(opts) {
|
||||
return (req, res, next) => {
|
||||
if (!opts.filter(req.pathname)) return next();
|
||||
|
||||
res.setHeader('Content-Type', opts.type);
|
||||
res.setHeader('Cache-Control', opts.cache);
|
||||
|
||||
res.end(opts.fn(req.pathname));
|
||||
};
|
||||
}
|
||||
|
||||
const resolved = Promise.resolve();
|
||||
|
||||
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.setHeader('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
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
||||
resolved
|
||||
.then(() => {
|
||||
for (const route of route_manager.routes) {
|
||||
if (route.test(url)) return handle_route(route, req, res, next, fn());
|
||||
}
|
||||
|
||||
// no matching route — 404
|
||||
next();
|
||||
})
|
||||
.catch(err => {
|
||||
res.statusCode = 500;
|
||||
res.end(templates.render(500, {
|
||||
title: (err && err.name) || 'Internal server error',
|
||||
url,
|
||||
error: escape_html(err && (err.details || err.message || err) || 'Unknown error'),
|
||||
stack: err && err.stack.split('\n').slice(1).join('\n')
|
||||
}));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function get_not_found_handler(fn) {
|
||||
return function handle_not_found(req, res) {
|
||||
const asset_cache = fn();
|
||||
|
||||
res.statusCode = 404;
|
||||
res.end(templates.render(404, {
|
||||
title: 'Not found',
|
||||
status: 404,
|
||||
method: req.method,
|
||||
scripts: `<script src='${asset_cache.client.main_file}'></script>`,
|
||||
url: req.url
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
function compose_handlers(handlers) {
|
||||
return (req, res, next) => {
|
||||
let i = 0;
|
||||
function go() {
|
||||
const handler = handlers[i];
|
||||
|
||||
if (handler) {
|
||||
handler(req, res, () => {
|
||||
i += 1;
|
||||
go();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
go();
|
||||
};
|
||||
}
|
||||
|
||||
function read_json(file) {
|
||||
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
||||
}
|
||||
|
||||
function try_serialize(data) {
|
||||
try {
|
||||
return serialize(data);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
154
src/middleware/tmp.json
Normal file
154
src/middleware/tmp.json
Normal file
@@ -0,0 +1,154 @@
|
||||
[
|
||||
{
|
||||
"x": 1979,
|
||||
"y": 7.19
|
||||
},
|
||||
{
|
||||
"x": 1980,
|
||||
"y": 7.83
|
||||
},
|
||||
{
|
||||
"x": 1981,
|
||||
"y": 7.24
|
||||
},
|
||||
{
|
||||
"x": 1982,
|
||||
"y": 7.44
|
||||
},
|
||||
{
|
||||
"x": 1983,
|
||||
"y": 7.51
|
||||
},
|
||||
{
|
||||
"x": 1984,
|
||||
"y": 7.1
|
||||
},
|
||||
{
|
||||
"x": 1985,
|
||||
"y": 6.91
|
||||
},
|
||||
{
|
||||
"x": 1986,
|
||||
"y": 7.53
|
||||
},
|
||||
{
|
||||
"x": 1987,
|
||||
"y": 7.47
|
||||
},
|
||||
{
|
||||
"x": 1988,
|
||||
"y": 7.48
|
||||
},
|
||||
{
|
||||
"x": 1989,
|
||||
"y": 7.03
|
||||
},
|
||||
{
|
||||
"x": 1990,
|
||||
"y": 6.23
|
||||
},
|
||||
{
|
||||
"x": 1991,
|
||||
"y": 6.54
|
||||
},
|
||||
{
|
||||
"x": 1992,
|
||||
"y": 7.54
|
||||
},
|
||||
{
|
||||
"x": 1993,
|
||||
"y": 6.5
|
||||
},
|
||||
{
|
||||
"x": 1994,
|
||||
"y": 7.18
|
||||
},
|
||||
{
|
||||
"x": 1995,
|
||||
"y": 6.12
|
||||
},
|
||||
{
|
||||
"x": 1996,
|
||||
"y": 7.87
|
||||
},
|
||||
{
|
||||
"x": 1997,
|
||||
"y": 6.73
|
||||
},
|
||||
{
|
||||
"x": 1998,
|
||||
"y": 6.55
|
||||
},
|
||||
{
|
||||
"x": 1999,
|
||||
"y": 6.23
|
||||
},
|
||||
{
|
||||
"x": 2000,
|
||||
"y": 6.31
|
||||
},
|
||||
{
|
||||
"x": 2001,
|
||||
"y": 6.74
|
||||
},
|
||||
{
|
||||
"x": 2002,
|
||||
"y": 5.95
|
||||
},
|
||||
{
|
||||
"x": 2003,
|
||||
"y": 6.13
|
||||
},
|
||||
{
|
||||
"x": 2004,
|
||||
"y": 6.04
|
||||
},
|
||||
{
|
||||
"x": 2005,
|
||||
"y": 5.56
|
||||
},
|
||||
{
|
||||
"x": 2006,
|
||||
"y": 5.91
|
||||
},
|
||||
{
|
||||
"x": 2007,
|
||||
"y": 4.29
|
||||
},
|
||||
{
|
||||
"x": 2008,
|
||||
"y": 4.72
|
||||
},
|
||||
{
|
||||
"x": 2009,
|
||||
"y": 5.38
|
||||
},
|
||||
{
|
||||
"x": 2010,
|
||||
"y": 4.92
|
||||
},
|
||||
{
|
||||
"x": 2011,
|
||||
"y": 4.61
|
||||
},
|
||||
{
|
||||
"x": 2012,
|
||||
"y": 3.62
|
||||
},
|
||||
{
|
||||
"x": 2013,
|
||||
"y": 5.35
|
||||
},
|
||||
{
|
||||
"x": 2014,
|
||||
"y": 5.28
|
||||
},
|
||||
{
|
||||
"x": 2015,
|
||||
"y": 4.63
|
||||
},
|
||||
{
|
||||
"x": 2016,
|
||||
"y": 4.72
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user