mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-21 06:45:00 +00:00
make route handling more explicit
This commit is contained in:
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import * as compilers from './utils/compilers.js';
|
import get_compilers from './get_compilers.js';
|
||||||
import create_app from './utils/create_app.js';
|
import create_app from './utils/create_app.js';
|
||||||
import generate_asset_cache from './generate_asset_cache.js';
|
import generate_asset_cache from './generate_asset_cache.js';
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export default function build({ dest, dev, entry, src }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { client, server } = compilers.get_compilers(); // TODO refactor
|
const { client, server } = get_compilers();
|
||||||
|
|
||||||
client.run((err, client_stats) => {
|
client.run((err, client_stats) => {
|
||||||
handleErrors(err, client_stats);
|
handleErrors(err, client_stats);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import glob from 'glob';
|
||||||
|
|
||||||
export default function create_routes(files) {
|
export default function create_routes({ src, files = glob.sync('**/*.+(html|js|mjs)', { cwd: src }) }) {
|
||||||
const routes = files
|
const routes = files
|
||||||
.map(file => {
|
.map(file => {
|
||||||
if (/(^|\/|\\)_/.test(file)) return;
|
if (/(^|\/|\\)_/.test(file)) return;
|
||||||
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import glob from 'glob';
|
import glob from 'glob';
|
||||||
import { create_templates, render } from './templates.js';
|
import { create_templates, render } from './templates.js';
|
||||||
import * as route_manager from './route_manager.js';
|
import create_routes from './create_routes.js';
|
||||||
|
|
||||||
function ensure_array(thing) {
|
function ensure_array(thing) {
|
||||||
return Array.isArray(thing) ? thing : [thing]; // omg webpack what the HELL are you doing
|
return Array.isArray(thing) ? thing : [thing]; // omg webpack what the HELL are you doing
|
||||||
@@ -18,6 +18,8 @@ export default function generate_asset_cache({ src, dest, dev, client_info, serv
|
|||||||
const service_worker = generate_service_worker({ chunk_files, src });
|
const service_worker = generate_service_worker({ chunk_files, src });
|
||||||
const index = generate_index(main_file);
|
const index = generate_index(main_file);
|
||||||
|
|
||||||
|
const routes = create_routes({ src }); // TODO rename update
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
fs.writeFileSync(path.join(dest, 'service-worker.js'), service_worker);
|
fs.writeFileSync(path.join(dest, 'service-worker.js'), service_worker);
|
||||||
fs.writeFileSync(path.join(dest, 'index.html'), index);
|
fs.writeFileSync(path.join(dest, 'index.html'), index);
|
||||||
@@ -34,7 +36,8 @@ export default function generate_asset_cache({ src, dest, dev, client_info, serv
|
|||||||
return lookup;
|
return lookup;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
||||||
routes: route_manager.routes.reduce((lookup, route) => {
|
// TODO confusing that `routes` refers to an array *and* a lookup
|
||||||
|
routes: routes.reduce((lookup, route) => {
|
||||||
lookup[route.id] = `/client/${ensure_array(client_info.assetsByChunkName[route.id])[0]}`;
|
lookup[route.id] = `/client/${ensure_array(client_info.assetsByChunkName[route.id])[0]}`;
|
||||||
return lookup;
|
return lookup;
|
||||||
}, {}),
|
}, {}),
|
||||||
@@ -54,10 +57,10 @@ export default function generate_asset_cache({ src, dest, dev, client_info, serv
|
|||||||
function generate_service_worker({ chunk_files, src }) {
|
function generate_service_worker({ chunk_files, src }) {
|
||||||
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
||||||
|
|
||||||
route_manager.update({ src }); // TODO refactor
|
const routes = create_routes({ src }); // TODO refactor
|
||||||
|
|
||||||
const route_code = `[${
|
const route_code = `[${
|
||||||
route_manager.routes
|
routes
|
||||||
.filter(route => route.type === 'page')
|
.filter(route => route.type === 'page')
|
||||||
.map(route => `{ pattern: ${route.pattern} }`)
|
.map(route => `{ pattern: ${route.pattern} }`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import relative from 'require-relative';
|
import relative from 'require-relative';
|
||||||
|
|
||||||
export function get_compilers() {
|
export default function get_compilers() {
|
||||||
const webpack = relative('webpack', process.cwd());
|
const webpack = relative('webpack', process.cwd());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import * as route_manager from './route_manager.js';
|
|
||||||
import * as templates from './templates.js';
|
import * as templates from './templates.js';
|
||||||
import * as compilers from './utils/compilers.js';
|
|
||||||
|
|
||||||
export { default as build } from './build.js';
|
export { default as build } from './build.js';
|
||||||
export { default as export } from './export.js';
|
export { default as export } from './export.js';
|
||||||
export { default as generate_asset_cache } from './generate_asset_cache.js';
|
export { default as generate_asset_cache } from './generate_asset_cache.js';
|
||||||
|
export { default as get_compilers } from './get_compilers.js';
|
||||||
|
export { default as create_routes } from './create_routes.js';
|
||||||
export { default as create_app } from './utils/create_app.js';
|
export { default as create_app } from './utils/create_app.js';
|
||||||
export { default as create_routes } from './utils/create_routes.js';
|
|
||||||
|
|
||||||
export { compilers, route_manager, templates };
|
export { templates };
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import glob from 'glob';
|
|
||||||
import create_routes from './utils/create_routes.js';
|
|
||||||
|
|
||||||
export let routes;
|
|
||||||
|
|
||||||
export function update({ src }) {
|
|
||||||
routes = create_routes(
|
|
||||||
glob.sync('**/*.+(html|js|mjs)', { cwd: src })
|
|
||||||
);
|
|
||||||
|
|
||||||
return routes;
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as route_manager from '../route_manager.js';
|
import create_routes from '../create_routes.js';
|
||||||
import { create_templates } from '../templates.js';
|
// import { create_templates } from '../templates.js';
|
||||||
|
|
||||||
function posixify(file) {
|
function posixify(file) {
|
||||||
return file.replace(/[/\\]/g, '/');
|
return file.replace(/[/\\]/g, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function create_app({ src, dev, entry }) {
|
function create_app({ src, dev, entry }) {
|
||||||
// const { routes } = route_manager;
|
const routes = create_routes({ src });
|
||||||
route_manager.update({ src });
|
|
||||||
const { routes } = route_manager;
|
|
||||||
|
|
||||||
function create_client_main() {
|
function create_client_main() {
|
||||||
const template = fs.readFileSync('templates/main.js', 'utf-8');
|
const template = fs.readFileSync('templates/main.js', 'utf-8');
|
||||||
@@ -68,31 +66,31 @@ function create_app({ src, dev, entry }) {
|
|||||||
create_server_routes();
|
create_server_routes();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function start_watching({ src }) {
|
// export function start_watching({ src }) {
|
||||||
const chokidar = require('chokidar');
|
// const chokidar = require('chokidar');
|
||||||
|
|
||||||
const watch = (glob, callback) => {
|
// const watch = (glob, callback) => {
|
||||||
const watcher = chokidar.watch(glob, {
|
// const watcher = chokidar.watch(glob, {
|
||||||
ignoreInitial: true,
|
// ignoreInitial: true,
|
||||||
persistent: false
|
// persistent: false
|
||||||
});
|
// });
|
||||||
|
|
||||||
watcher.on('add', callback);
|
// watcher.on('add', callback);
|
||||||
watcher.on('change', callback);
|
// watcher.on('change', callback);
|
||||||
watcher.on('unlink', callback);
|
// watcher.on('unlink', callback);
|
||||||
};
|
// };
|
||||||
|
|
||||||
watch('templates/main.js', create_app);
|
// watch('templates/main.js', create_app);
|
||||||
|
|
||||||
watch('routes/**/*.+(html|js|mjs)', () => {
|
// watch('routes/**/*.+(html|js|mjs)', () => {
|
||||||
route_manager.update({ src });
|
// route_manager.update({ src });
|
||||||
create_app();
|
// create_app();
|
||||||
});
|
// });
|
||||||
|
|
||||||
watch('templates/**.html', () => {
|
// watch('templates/**.html', () => {
|
||||||
create_templates();
|
// create_templates();
|
||||||
// TODO reload current page?
|
// // TODO reload current page?
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default create_app;
|
export default create_app;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { compilers, generate_asset_cache } from 'sapper/core.js';
|
import { generate_asset_cache, create_routes } from 'sapper/core.js';
|
||||||
import { dest } from '../config.js';
|
import { dest } from '../config.js';
|
||||||
|
|
||||||
function deferred() {
|
function deferred() {
|
||||||
@@ -15,7 +15,7 @@ function deferred() {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function create_watcher() {
|
export default function create_watcher({ compilers, src, onroutes }) {
|
||||||
const deferreds = {
|
const deferreds = {
|
||||||
client: deferred(),
|
client: deferred(),
|
||||||
server: deferred()
|
server: deferred()
|
||||||
@@ -59,6 +59,36 @@ export default function create_watcher() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chokidar = require('chokidar');
|
||||||
|
|
||||||
|
function watch_files(pattern, callback) {
|
||||||
|
const watcher = chokidar.watch(pattern, {
|
||||||
|
ignoreInitial: true,
|
||||||
|
persistent: false
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher.on('add', callback);
|
||||||
|
watcher.on('change', callback);
|
||||||
|
watcher.on('unlink', callback);
|
||||||
|
|
||||||
|
// watch('templates/main.js', create_app);
|
||||||
|
|
||||||
|
// watch('routes/**/*.+(html|js|mjs)', () => {
|
||||||
|
// route_manager.update({ src });
|
||||||
|
// create_app();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// watch('templates/**.html', () => {
|
||||||
|
// create_templates();
|
||||||
|
// // TODO reload current page?
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_files('routes/**/*.+(html|js|mjs)', () => {
|
||||||
|
const routes = create_routes({ src });
|
||||||
|
onroutes(routes);
|
||||||
|
});
|
||||||
|
|
||||||
const watcher = {
|
const watcher = {
|
||||||
ready: invalidate(),
|
ready: invalidate(),
|
||||||
client: watch_compiler('client'),
|
client: watch_compiler('client'),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import mkdirp from 'mkdirp';
|
|||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
import escape_html from 'escape-html';
|
import escape_html from 'escape-html';
|
||||||
import { route_manager, templates, create_app, compilers, generate_asset_cache } from 'sapper/core.js';
|
import { create_routes, templates, create_app, get_compilers, generate_asset_cache } from 'sapper/core.js';
|
||||||
import create_watcher from './create_watcher.js';
|
import create_watcher from './create_watcher.js';
|
||||||
import { dest, dev, entry, src } from '../config.js';
|
import { dest, dev, entry, src } from '../config.js';
|
||||||
|
|
||||||
@@ -14,7 +14,16 @@ function connect_dev() {
|
|||||||
|
|
||||||
create_app({ dev, entry, src });
|
create_app({ dev, entry, src });
|
||||||
|
|
||||||
const watcher = create_watcher();
|
const compilers = get_compilers();
|
||||||
|
|
||||||
|
let routes;
|
||||||
|
|
||||||
|
const watcher = create_watcher({
|
||||||
|
compilers,
|
||||||
|
on_routes_update: _ => {
|
||||||
|
routes = _;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let asset_cache;
|
let asset_cache;
|
||||||
|
|
||||||
@@ -55,7 +64,7 @@ function connect_dev() {
|
|||||||
fn: pathname => asset_cache.client.chunks[pathname]
|
fn: pathname => asset_cache.client.chunks[pathname]
|
||||||
}),
|
}),
|
||||||
|
|
||||||
get_route_handler(() => asset_cache),
|
get_route_handler(() => asset_cache, () => routes),
|
||||||
|
|
||||||
get_not_found_handler(() => asset_cache)
|
get_not_found_handler(() => asset_cache)
|
||||||
]);
|
]);
|
||||||
@@ -76,6 +85,8 @@ function connect_prod() {
|
|||||||
server_info: read_json(path.join(dest, 'stats.server.json'))
|
server_info: read_json(path.join(dest, 'stats.server.json'))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const routes = create_routes({ src }); // TODO rename update
|
||||||
|
|
||||||
const middleware = compose_handlers([
|
const middleware = compose_handlers([
|
||||||
set_req_pathname,
|
set_req_pathname,
|
||||||
|
|
||||||
@@ -100,7 +111,7 @@ function connect_prod() {
|
|||||||
fn: pathname => asset_cache.client.chunks[pathname]
|
fn: pathname => asset_cache.client.chunks[pathname]
|
||||||
}),
|
}),
|
||||||
|
|
||||||
get_route_handler(() => asset_cache),
|
get_route_handler(() => asset_cache, () => routes),
|
||||||
|
|
||||||
get_not_found_handler(() => asset_cache)
|
get_not_found_handler(() => asset_cache)
|
||||||
]);
|
]);
|
||||||
@@ -132,7 +143,7 @@ function get_asset_handler(opts) {
|
|||||||
|
|
||||||
const resolved = Promise.resolve();
|
const resolved = Promise.resolve();
|
||||||
|
|
||||||
function get_route_handler(fn) {
|
function get_route_handler(get_assets, get_routes) {
|
||||||
function handle_route(route, req, res, next, { client, server }) {
|
function handle_route(route, req, res, next, { client, server }) {
|
||||||
req.params = route.exec(req.pathname);
|
req.params = route.exec(req.pathname);
|
||||||
|
|
||||||
@@ -204,8 +215,9 @@ function get_route_handler(fn) {
|
|||||||
|
|
||||||
resolved
|
resolved
|
||||||
.then(() => {
|
.then(() => {
|
||||||
for (const route of route_manager.routes) {
|
const routes = get_routes();
|
||||||
if (route.test(url)) return handle_route(route, req, res, next, fn());
|
for (const route of routes) {
|
||||||
|
if (route.test(url)) return handle_route(route, req, res, next, get_assets());
|
||||||
}
|
}
|
||||||
|
|
||||||
// no matching route — 404
|
// no matching route — 404
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ const { create_routes } = require('../../core.js');
|
|||||||
|
|
||||||
describe('create_routes', () => {
|
describe('create_routes', () => {
|
||||||
it('sorts routes correctly', () => {
|
it('sorts routes correctly', () => {
|
||||||
const routes = create_routes(['index.html', 'about.html', '[wildcard].html', 'post/foo.html', 'post/[id].html', 'post/bar.html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', 'about.html', '[wildcard].html', 'post/foo.html', 'post/[id].html', 'post/bar.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -19,7 +21,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates params', () => {
|
it('generates params', () => {
|
||||||
const routes = create_routes(['index.html', 'about.html', '[wildcard].html', 'post/[id].html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', 'about.html', '[wildcard].html', 'post/[id].html']
|
||||||
|
});
|
||||||
|
|
||||||
let file;
|
let file;
|
||||||
let params;
|
let params;
|
||||||
@@ -38,7 +42,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('ignores files and directories with leading underscores', () => {
|
it('ignores files and directories with leading underscores', () => {
|
||||||
const routes = create_routes(['index.html', '_foo.html', 'a/_b/c/d.html', 'e/f/g/h.html', 'i/_j.html']);
|
const routes = create_routes({
|
||||||
|
files: ['index.html', '_foo.html', 'a/_b/c/d.html', 'e/f/g/h.html', 'i/_j.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -50,8 +56,12 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('matches /foo/:bar before /:baz/qux', () => {
|
it('matches /foo/:bar before /:baz/qux', () => {
|
||||||
const a = create_routes(['foo/[bar].html', '[baz]/qux.html']);
|
const a = create_routes({
|
||||||
const b = create_routes(['[baz]/qux.html', 'foo/[bar].html']);
|
files: ['foo/[bar].html', '[baz]/qux.html']
|
||||||
|
});
|
||||||
|
const b = create_routes({
|
||||||
|
files: ['[baz]/qux.html', 'foo/[bar].html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
a.map(r => r.file),
|
a.map(r => r.file),
|
||||||
@@ -66,16 +76,22 @@ describe('create_routes', () => {
|
|||||||
|
|
||||||
it('fails if routes are indistinguishable', () => {
|
it('fails if routes are indistinguishable', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
create_routes(['[foo].html', '[bar]/index.html']);
|
create_routes({
|
||||||
|
files: ['[foo].html', '[bar]/index.html']
|
||||||
|
});
|
||||||
}, /The \[foo\].html and \[bar\]\/index.html routes clash/);
|
}, /The \[foo\].html and \[bar\]\/index.html routes clash/);
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
create_routes(['foo.html', 'foo.js']);
|
create_routes({
|
||||||
|
files: ['foo.html', 'foo.js']
|
||||||
|
});
|
||||||
}, /The foo.html and foo.js routes clash/);
|
}, /The foo.html and foo.js routes clash/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('matches nested routes', () => {
|
it('matches nested routes', () => {
|
||||||
const route = create_routes(['settings/[submenu].html'])[0];
|
const route = create_routes({
|
||||||
|
files: ['settings/[submenu].html']
|
||||||
|
})[0];
|
||||||
|
|
||||||
assert.deepEqual(route.exec('/settings/foo'), {
|
assert.deepEqual(route.exec('/settings/foo'), {
|
||||||
submenu: 'foo'
|
submenu: 'foo'
|
||||||
@@ -87,7 +103,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('prefers index routes to nested routes', () => {
|
it('prefers index routes to nested routes', () => {
|
||||||
const routes = create_routes(['settings/[submenu].html', 'settings.html']);
|
const routes = create_routes({
|
||||||
|
files: ['settings/[submenu].html', 'settings.html']
|
||||||
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
routes.map(r => r.file),
|
routes.map(r => r.file),
|
||||||
@@ -96,7 +114,9 @@ describe('create_routes', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('matches deeply nested routes', () => {
|
it('matches deeply nested routes', () => {
|
||||||
const route = create_routes(['settings/[a]/[b]/index.html'])[0];
|
const route = create_routes({
|
||||||
|
files: ['settings/[a]/[b]/index.html']
|
||||||
|
})[0];
|
||||||
|
|
||||||
assert.deepEqual(route.exec('/settings/foo/bar'), {
|
assert.deepEqual(route.exec('/settings/foo/bar'), {
|
||||||
a: 'foo',
|
a: 'foo',
|
||||||
|
|||||||
Reference in New Issue
Block a user