Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Commit

Permalink
Merge pull request #34 from sveltejs/gh-13
Browse files Browse the repository at this point in the history
Rebundle when routes/templates change
  • Loading branch information
Rich-Harris authored Dec 20, 2017
2 parents cd91bf2 + 4232f75 commit 8d40992
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 75 deletions.
23 changes: 14 additions & 9 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function connect_dev() {

get_route_handler(() => asset_cache),

not_found
get_not_found_handler(() => asset_cache)
]);
}

Expand Down Expand Up @@ -95,7 +95,7 @@ function connect_prod() {

get_route_handler(() => asset_cache),

not_found
get_not_found_handler(() => asset_cache)
]);
}

Expand Down Expand Up @@ -174,13 +174,18 @@ function get_route_handler(fn) {
}
}

function not_found(req, res) {
res.status(404).end(templates.render(404, {
title: 'Not found',
status: 404,
method: req.method,
url: req.url
}));
function get_not_found_handler(fn) {
return function handle_not_found(req, res) {
const asset_cache = fn();

res.status(404).end(templates.render(404, {
title: 'Not found',
status: 404,
method: req.method,
main: asset_cache.client.main_file,
url: req.url
}));
}
}

function compose_handlers(handlers) {
Expand Down
34 changes: 24 additions & 10 deletions lib/route_manager.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
const glob = require('glob');
const chokidar = require('chokidar');
const create_routes = require('./utils/create_routes.js');
const { src } = require('./config.js');
const { src, dev } = require('./config.js');

const route_manager = {
routes: create_routes(
glob.sync('**/*.+(html|js|mjs)', { cwd: src })
),
const callbacks = [];

onchange(fn) {
// TODO in dev mode, keep this updated, and allow
// webpack compiler etc to hook into it
}
exports.onchange = fn => {
callbacks.push(fn);
};

module.exports = route_manager;
function update() {
exports.routes = create_routes(
glob.sync('**/*.+(html|js|mjs)', { cwd: src })
);

callbacks.forEach(fn => fn());
}

update();

if (dev) {
const watcher = chokidar.watch(`${src}/**/*.+(html|js|mjs)`, {
ignoreInitial: true
});

watcher.on('add', update);
watcher.on('change', update);
watcher.on('unlink', update);
}
77 changes: 45 additions & 32 deletions lib/templates.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,56 @@
const fs = require('fs');
const glob = require('glob');
const chokidar = require('chokidar');
const { dev } = require('./config.js');

const templates = glob.sync('*.html', { cwd: 'templates' })
.map(file => {
const template = fs.readFileSync(`templates/${file}`, 'utf-8');
const status = file.replace('.html', '').toLowerCase();

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`);
}

const specificity = (
(status[0] === 'x' ? 0 : 4) +
(status[1] === 'x' ? 0 : 2) +
(status[2] === 'x' ? 0 : 1)
);

const pattern = new RegExp(`^${status.split('').map(d => d === 'x' ? '\\d' : d).join('')}$`);

return {
test: status => pattern.test(status),
specificity,
render(data) {
return template.replace(/%sapper\.(\w+)%/g, (match, key) => {
return key in data ? data[key] : '';
});
let templates;

function create_templates() {
templates = glob.sync('*.html', { cwd: 'templates' })
.map(file => {
const template = fs.readFileSync(`templates/${file}`, 'utf-8');
const status = file.replace('.html', '').toLowerCase();

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`);
}

const specificity = (
(status[0] === 'x' ? 0 : 4) +
(status[1] === 'x' ? 0 : 2) +
(status[2] === 'x' ? 0 : 1)
);

const pattern = new RegExp(`^${status.split('').map(d => d === 'x' ? '\\d' : d).join('')}$`);

return {
test: status => pattern.test(status),
specificity,
render(data) {
return template.replace(/%sapper\.(\w+)%/g, (match, key) => {
return key in data ? data[key] : '';
});
}
}
}
})
.sort((a, b) => b.specificity - a.specificity);
})
.sort((a, b) => b.specificity - a.specificity);
}

create_templates();

if (dev) {
const watcher = chokidar.watch('templates/**.html', {
ignoreInitial: true
});

watcher.on('add', create_templates);
watcher.on('change', create_templates);
watcher.on('unlink', create_templates);
}

exports.render = (status, data) => {
const template = templates.find(template => template.test(status));
if (template) return template.render(data);

return `Missing template for status code ${status}`;
};

exports.onchange = fn => {
// TODO in dev mode, keep this updated, and allow
// webpack compiler etc to hook into it
};
20 changes: 17 additions & 3 deletions lib/utils/create_app.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const route_manager = require('../route_manager.js');
const { src, dest, server_routes, dev } = require('../config.js');

function posixify(file) {
return file.replace(/[\/\\]/g, '/');
}

module.exports = function create_app() {
function create_app() {
const { routes } = route_manager;

function create_client_main() {
Expand Down Expand Up @@ -64,7 +65,20 @@ module.exports = function create_app() {
fs.utimesSync(server_routes, new Date(atime.getTime() - 999999), new Date(mtime.getTime() - 999999));
}

// TODO in dev mode, watch files
create_client_main();
create_server_routes();
};
}

if (dev) {
route_manager.onchange(create_app);

const watcher = chokidar.watch(`templates/main.js`, {
ignoreInitial: true
});

watcher.on('add', create_app);
watcher.on('change', create_app);
watcher.on('unlink', create_app);
}

module.exports = create_app;
8 changes: 8 additions & 0 deletions lib/utils/create_watcher.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const compilers = require('./compilers.js');
const generate_asset_cache = require('./generate_asset_cache.js');
const { dest } = require('../config.js');

function deferred() {
const d = {};
Expand All @@ -24,6 +26,12 @@ module.exports = function create_watcher() {
deferreds.client.promise,
deferreds.server.promise
]).then(([client_stats, server_stats]) => {
const client_info = client_stats.toJson();
fs.writeFileSync(path.join(dest, 'stats.client.json'), JSON.stringify(client_info, null, ' '));

const server_info = server_stats.toJson();
fs.writeFileSync(path.join(dest, 'stats.server.json'), JSON.stringify(server_info, null, ' '));

return generate_asset_cache(
client_stats.toJson(),
server_stats.toJson()
Expand Down
5 changes: 4 additions & 1 deletion lib/utils/generate_asset_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ const route_manager = require('../route_manager.js');
const { dest } = require('../config.js');

module.exports = function generate_asset_cache(clientInfo, serverInfo) {
const main_file = `/client/${clientInfo.assetsByChunkName.main}`;
let main_file = Array.isArray(clientInfo.assetsByChunkName.main) ?
`/client/${clientInfo.assetsByChunkName.main[0]}` : // omg webpack what the HELL are you doing
`/client/${clientInfo.assetsByChunkName.main}`;

const chunk_files = clientInfo.assets.map(chunk => `/client/${chunk.name}`);

const service_worker = generate_service_worker(chunk_files);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"chalk": "^2.3.0",
"chokidar": "^1.7.0",
"escape-html": "^1.0.3",
"mkdirp": "^0.5.1",
"relative": "^3.0.2",
Expand Down
1 change: 0 additions & 1 deletion webpack/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const path = require('path');
const route_manager = require('../lib/route_manager.js');
const { src, dest, dev, server_routes } = require('../lib/config.js');

module.exports = {
Expand Down
Loading

0 comments on commit 8d40992

Please sign in to comment.