Skip to content

Instantly share code, notes, and snippets.

@vjpr
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vjpr/b13bd1bc2f636bb28cd8 to your computer and use it in GitHub Desktop.
Save vjpr/b13bd1bc2f636bb28cd8 to your computer and use it in GitHub Desktop.
koa-mount patch to allow hot reloading of routes
const debug = require('debug')('server')
const mount = require('koa-mount-hot')
const config = require('config')
const route = require('koa-route')
// TODO: This should only be in one place.
mount.hot = (prefix, name, hot = false) => {
if (config.env !== 'development') return false;
return hot;
}
let invalidate = (moduleName) => {
var name = require.resolve(moduleName)
delete require.cache[name]
return require(moduleName)
}
let customInvalidations =
route.get(/\/api\/.*/, function *(next) {
// invalidate(...)
yield next
})
export default function(app) {
if (config.env === 'development') app.use(customInvalidations)
app.use(mount('/api/users', 'modules/user', true))
app.use(mount('/api/buildings', 'modules/app/building', true))
app.use(mount('/api/anchors', 'modules/app/anchor', true))
app.use(mount('/api/tags', 'modules/app/tag', true))
app.use(mount('/api/plans', 'modules/app/plans', true))
app.use(mount('/api/floors', 'modules/app/floor', true))
}
/**
* Module dependencies.
*/
var debug = require('debug')('koa-mount');
var compose = require('koa-compose');
var assert = require('assert');
/**
* Expose `mount()`.
*/
module.exports = mount;
/**
* Set to true to auto-reload app on each request.
*
* Automatically it will invalidate the module specified.
* If you want other related modules to be invalidated (e.g. models, etc.)
* you can do that manually.
*
*/
mount.hot = function(prefix, name, hot) {
if (process.env.NODE_ENV !== 'development') return false;
return hot;
}
/**
* Mount `app` with `prefix`, `app`
* may be a Koa application or
* middleware function.
*
* @param {String|Application|Function} prefix, app, or function
* @param {Application|Function} [app or function]
* @return {Function}
* @api public
*/
function mount(prefix, app, hot) {
if ('string' != typeof prefix) {
app = prefix;
prefix = '/';
}
var appRequirePath = null;
if ('string' == typeof app) {
appRequirePath = app;
}
assert('/' == prefix[0], 'mount path must begin with "/"');
function isHot(prefix, appRequirePath, hot) {
return mount.hot(prefix, appRequirePath, hot);
}
function invalidate(moduleName) {
var name = require.resolve(moduleName)
delete require.cache[name]
return require(moduleName)
}
function getDownstream() {
if (appRequirePath) {
// The app var contains the require path of the koa app we want to mount.
if (isHot(prefix, appRequirePath, hot)) {
debug('invalidating module:', appRequirePath)
invalidate(appRequirePath);
}
try {
app = require(appRequirePath);
} catch (e) {
console.error('could not find module:', appRequirePath)
throw e
}
}
// compose
return app.middleware ? compose(app.middleware) : app;
}
// don't need to do mounting here
if ('/' == prefix) return getDownstream();
var trailingSlash = '/' == prefix.slice(-1);
var name = app.name || 'unnamed';
debug('mount %s %s', prefix, name);
return function *(upstream){
var prev = this.path;
var newPath = match(prev);
debug('mount %s %s -> %s', prefix, name, newPath);
if (!newPath) return yield* upstream;
this.mountPath = prefix;
this.path = newPath;
debug('enter %s -> %s', prev, this.path);
yield* getDownstream().call(this, function *(){
this.path = prev;
yield* upstream;
this.path = newPath;
}.call(this));
debug('leave %s -> %s', prev, this.path);
this.path = prev;
}
/**
* Check if `prefix` satisfies a `path`.
* Returns the new path.
*
* match('/images/', '/lkajsldkjf') => false
* match('/images', '/images') => /
* match('/images/', '/images') => false
* match('/images/', '/images/asdf') => /asdf
*
* @param {String} prefix
* @param {String} path
* @return {String|Boolean}
* @api private
*/
function match(path) {
// does not match prefix at all
if (0 != path.indexOf(prefix)) return false;
var newPath = path.replace(prefix, '') || '/';
if (trailingSlash) return newPath;
// `/mount` does not match `/mountlkjalskjdf`
if ('/' != newPath[0]) return false;
return newPath;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment