A simple, powerful library for generalized hierarchical path-routing for client-side and server-side applications.
grapetree-core powers the grapetree url routing library and was inspired by
Like crossroads.js, router.js, and cherrytree,
grapetree-core embraces the single-responsibility principle
and is entirely stand-alone, free of any dependencies on a framework of any kind.
npm install grapetree-core
var GrapeTreeCore =
GrapeTreeCore(routeDefinition) - Returns a new router instance based on
routeDefinition, which should be a function that gets a
Route object as its
GrapeTreeCore.param - Special value used by
Route.route - see below.
GrapeTreeCore.Future - A reference to the async-future module, which
grapetree-core uses internally. This does not have to be the futures/promises implementation you use to return a future from
exit handlers, but a future must have a
router.go(newPath[, emitChangeEvent][, softQueue]) - Changes the current path and triggers the router to fire all the appropriate handlers. Returns a future that is resolved when the route is complete or has an error that isn't handled by a Route's error handler.
newPath- The path to change to
emitChangeEvent- (default true) Whether to emit the
softQueue- (default true) If true, causes the path to only be executed if it's the last one in the queue (and be discarded otherwise). If false, every queued path is handled in order.
router.cur - gets the current path (transformed if a transform is being used).
router.transformPath(trasformFns) - Sets up path transformation, which modifies the internal path before passing it as an argument to the
"change" event and
Route.default handlers and after getting an external path from the
Route.route functions. This is mostly used for libraries that want to extend grapetree-core (like grapetree itself). Transform functions can be passed both full paths and path segements.
router.on - router inherits from EventEmitter and so gets all the methods from it like
router.removeListener. This can throw an exception if no Route
error handlers catch an exception.
router.currentPath - the current path loaded in the router.
this.route(pathSegment, routeDefinition) - creates a sub-path route. The routes are tested for a match in the order they are called - only one will be used.
pathSegment- the parts of the path to match a route path against (e.g. ['a','b'] or 'x'). If
pathSegmentis an array, the route only matches if each item in
pathSegmentmatches the corresponding parts in the path being changed to. If
pathSegmentis not an array, it is treated as
[pathSegment]. If any of the items in the array are
GrapeTreeCore.param, matching parts of the path being changed to are treated as parameters that will be passed to the
routeDefinition- a function that gets a
Routeobject as its
thiscontext. It is passed any parameters that exist in
pathSegmentin the same order.
this.default(routeDefinition) - creates a default sub-path route that is matched if no other route is. The
routeDefinition function is passed the full sub-route (ie if the default is within ['a','b'] and the path is ['a','b','c','d'], default will get ['c','d'])
this.redirect(newPath[, emitOldPath=false]) - Changes the route to be loaded only if no subroute matches. If a subroute matches, the redirect is ignored.
newPath - the new route
emitOldPath - if true, the 'change' event will be triggered with the original path
routeDefinition - a function that gets a
Route object as its
this context. It is passed the new pathSegment being changed to. If
router.transformPath has been called, the parameter will have been transformed with the transform.
this.enter(handler) - sets up a handler that is called when a path newly "enters" the subroute (see Route Lifecycle Hooks for details).
handler(parentValue)- a function that will be called when the path is "entered". The handler may return a future, which will be waited on before child enter-handlers are called.
parentValueis the value of the future returned by its parent's enter handler, or undefined if no future was returned by its parent.
this.exit(handler) - sets up a handler that is called when a new path "exits" the subroute (see Route Lifecycle Hooks for details).
handler(parentValue, divergenceDistance)- a function that will be called when the path is "exited". The handler may return a future, which will be waited on before parent exit-handlers are called.
parentValueis the value of the future returned by its parent's enter handler (not its parent's or child's exit handler), or undefined if no future was returned by its parent.
divergenceDistanceis the number of routes between it and the recent path-change (e.g. for a change from ['a','b','c','d'] to ['a','b','x','y'], c's divergence distance is 0, and d's is 1). This is useful, for example, if some work your exit handler does is unnecessary if its parent route's exit handler is called.
this.error(errorHandler) - Sets up an error handler that is passed errors that happen anywhere in the router. If an error handler is not defined for a particular subroute, the error will be passed to its parent. If an error bubbles to the top, the error is thrown from the
router.go function itself. The handler may return a future, which will propogate errors from that future to the next error handler up, if that future resolves to an error.
errorHandler(error, info)- A function that handles the
error. The second parameter is an object with info about where the error happened. It has the following members:
info.stage- the stage of path-changing the error happened in.
stagecan be either "enter", "exit", or "route"
info.location- the path segement (relative to the current route) where the error happened ( indicates the current route)
The routing hooks in
grapetree-core are simple but powerful. Basically exit handlers are called from leaf-node routes inward, and enter handlers are called outward toward the leaf-nodes.
var router =routerrouter
The order the handlers are called in the above example is:
Error handling in grape-tree is an attempt to be as intuitive as possible, but they are a bit different from traditional try-catch, because the success of a parent route should not depend on the success of a child route (as opposed to normal try-catch situations where the calling code's success depends on the called code). Here are some facts about how errors are handled:
See the unit tests for exhaustive examples of how error handling works. For the most part, you should be able to use it well without fully understanding the intricacies of how it works.
Like the error handlers, default handlers for a route also cover the scope of that route's children. In other words, if a child doesn't have a default route, its parent's (or grandparent's etc) default route will be used. This allows you to have a single default handlers at the top level that will catch any invalid route.
implement a way to match on arbitrary paths (like putting a function in for hte path so you can say "i matched 4 tokens on the path"
If an error happens in a enter handler and is not handled by an error handler in that route, enter handlers of parents should probably continue to run
gowhen something queues
transformPathmethod to allow transforms in both directions
How to submit pull requests:
npm installat its root
Released under the MIT license: http://opensource.org/licenses/MIT