Last active
August 29, 2015 14:22
-
-
Save vjpr/b13bd1bc2f636bb28cd8 to your computer and use it in GitHub Desktop.
koa-mount patch to allow hot reloading of routes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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