├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── test.js └── utils.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | 9 | "globals": { 10 | "document": false, 11 | "navigator": false, 12 | "window": false 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-spacing": [2, { "before": true, "after": true }], 18 | "block-spacing": [2, "always"], 19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 20 | "comma-dangle": [2, "never"], 21 | "comma-spacing": [2, { "before": false, "after": true }], 22 | "comma-style": [2, "last"], 23 | "constructor-super": 2, 24 | "curly": [2, "multi-line"], 25 | "dot-location": [2, "property"], 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "generator-star-spacing": [2, { "before": true, "after": true }], 29 | "handle-callback-err": [2, "^(err|error)$" ], 30 | "indent": [2, 2, { "SwitchCase": 1 }], 31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 32 | "keyword-spacing": [2, { "before": true, "after": true }], 33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 34 | "new-parens": 2, 35 | "no-array-constructor": 2, 36 | "no-caller": 2, 37 | "no-class-assign": 2, 38 | "no-cond-assign": 2, 39 | "no-const-assign": 2, 40 | "no-control-regex": 2, 41 | "no-debugger": 2, 42 | "no-delete-var": 2, 43 | "no-dupe-args": 2, 44 | "no-dupe-class-members": 2, 45 | "no-dupe-keys": 2, 46 | "no-duplicate-case": 2, 47 | "no-empty-character-class": 2, 48 | "no-eval": 2, 49 | "no-ex-assign": 2, 50 | "no-extend-native": 2, 51 | "no-extra-bind": 2, 52 | "no-extra-boolean-cast": 2, 53 | "no-extra-parens": [2, "functions"], 54 | "no-fallthrough": 2, 55 | "no-floating-decimal": 2, 56 | "no-func-assign": 2, 57 | "no-implied-eval": 2, 58 | "no-inner-declarations": [2, "functions"], 59 | "no-invalid-regexp": 2, 60 | "no-irregular-whitespace": 2, 61 | "no-iterator": 2, 62 | "no-label-var": 2, 63 | "no-labels": 2, 64 | "no-lone-blocks": 2, 65 | "no-mixed-spaces-and-tabs": 2, 66 | "no-multi-spaces": 2, 67 | "no-multi-str": 2, 68 | "no-multiple-empty-lines": [2, { "max": 1 }], 69 | "no-native-reassign": 0, 70 | "no-negated-in-lhs": 2, 71 | "no-new": 2, 72 | "no-new-func": 2, 73 | "no-new-object": 2, 74 | "no-new-require": 2, 75 | "no-new-wrappers": 2, 76 | "no-obj-calls": 2, 77 | "no-octal": 2, 78 | "no-octal-escape": 2, 79 | "no-proto": 0, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-return-assign": 2, 83 | "no-self-compare": 2, 84 | "no-sequences": 2, 85 | "no-shadow-restricted-names": 2, 86 | "no-spaced-func": 2, 87 | "no-sparse-arrays": 2, 88 | "no-this-before-super": 2, 89 | "no-throw-literal": 2, 90 | "no-trailing-spaces": 0, 91 | "no-undef": 2, 92 | "no-undef-init": 2, 93 | "no-unexpected-multiline": 2, 94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 95 | "no-unreachable": 2, 96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 97 | "no-useless-call": 0, 98 | "no-with": 2, 99 | "one-var": [0, { "initialized": "never" }], 100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 101 | "padded-blocks": [0, "never"], 102 | "quotes": [2, "single", "avoid-escape"], 103 | "radix": 2, 104 | "semi": [2, "always"], 105 | "semi-spacing": [2, { "before": false, "after": true }], 106 | "space-before-blocks": [2, "always"], 107 | "space-before-function-paren": [2, "never"], 108 | "space-in-parens": [2, "never"], 109 | "space-infix-ops": 2, 110 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 112 | "use-isnan": 2, 113 | "valid-typeof": 2, 114 | "wrap-iife": [2, "any"], 115 | "yoda": [2, "never"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '8' 9 | - '7' 10 | - '6' 11 | - '5' 12 | - '4' 13 | - '0.12' 14 | - '0.10' 15 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Register the plugin with your [base][] application. 4 | 5 | ```js 6 | var routes = require('{%= name %}'); 7 | var Base = require('base-app'); 8 | 9 | var app = new Base(); 10 | app.use(routes()); 11 | ``` 12 | 13 | ## API 14 | {%= apidocs("index.js") %} 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017, Jon Schlinkert. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base-routes [![NPM version](https://img.shields.io/npm/v/base-routes.svg?style=flat)](https://www.npmjs.com/package/base-routes) [![NPM monthly downloads](https://img.shields.io/npm/dm/base-routes.svg?style=flat)](https://npmjs.org/package/base-routes) [![NPM total downloads](https://img.shields.io/npm/dt/base-routes.svg?style=flat)](https://npmjs.org/package/base-routes) [![Linux Build Status](https://img.shields.io/travis/node-base/base-routes.svg?style=flat&label=Travis)](https://travis-ci.org/node-base/base-routes) 2 | 3 | > Plugin for adding routes support to your `base` application. Requires templates support to work. 4 | 5 | - [Install](#install) 6 | - [Usage](#usage) 7 | - [API](#api) 8 | - [About](#about) 9 | 10 | _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ 11 | 12 | ## Install 13 | 14 | Install with [npm](https://www.npmjs.com/): 15 | 16 | ```sh 17 | $ npm install --save base-routes 18 | ``` 19 | 20 | ## Usage 21 | 22 | Register the plugin with your [base](https://github.com/node-base/base) application. 23 | 24 | ```js 25 | var routes = require('base-routes'); 26 | var Base = require('base-app'); 27 | 28 | var app = new Base(); 29 | app.use(routes()); 30 | ``` 31 | 32 | ## API 33 | 34 | ### [.Router](index.js#L23) 35 | 36 | The `Router` and `Route` classes are on the `app` instance, in case they need to be accessed directly. 37 | 38 | **Example** 39 | 40 | ```js 41 | var router = new app.Router(); 42 | var route = new app.Route(); 43 | ``` 44 | 45 | ### [.handle](index.js#L54) 46 | 47 | Handle middleware `method` for the given `file`. 48 | 49 | **Params** 50 | 51 | * `methodName` **{String}**: Name of the router method to handle. 52 | * `file` **{Object}**: View object 53 | * `next` **{Function}**: Callback function 54 | * `returns` **{undefined}** 55 | 56 | **Example** 57 | 58 | ```js 59 | app.handle('methodName', file, next); 60 | ``` 61 | 62 | ### [.handleOnce](index.js#L114) 63 | 64 | Run the given middleware handler only if the file has not already been handled by `method`. 65 | 66 | **Params** 67 | 68 | * `method` **{Object}**: The name of the handler method to call. 69 | * `file` **{Object}** 70 | * `returns` **{undefined}** 71 | 72 | **Example** 73 | 74 | ```js 75 | app.handleOnce(method, file, callback); 76 | // example 77 | app.handleOnce('onLoad', file, callback); 78 | ``` 79 | 80 | ### [.route](index.js#L200) 81 | 82 | Create a new Route for the given path. Each route contains a separate middleware stack. See the [en-route](https://github.com/jonschlinkert/en-route) API documentation for details on adding handlers and middleware to routes. 83 | 84 | **Params** 85 | 86 | * `path` **{String}** 87 | * `returns` **{Object}**: Returns the instance for chaining. 88 | 89 | **Example** 90 | 91 | ```js 92 | app.create('posts'); 93 | app.route(/blog/) 94 | .all(function(file, next) { 95 | // do something with file 96 | next(); 97 | }); 98 | 99 | app.post('whatever', {path: 'blog/foo.bar', content: 'bar baz'}); 100 | ``` 101 | 102 | ### [.param](index.js#L228) 103 | 104 | Add callback triggers to route parameters, where `name` is the name of the parameter and `fn` is the callback function. 105 | 106 | **Params** 107 | 108 | * `name` **{String}** 109 | * `fn` **{Function}** 110 | * `returns` **{Object}**: Returns the instance for chaining. 111 | 112 | **Example** 113 | 114 | ```js 115 | app.param('title', function(view, next, title) { 116 | //=> title === 'foo.js' 117 | next(); 118 | }); 119 | 120 | app.onLoad('/blog/:title', function(view, next) { 121 | //=> view.path === '/blog/foo.js' 122 | next(); 123 | }); 124 | ``` 125 | 126 | ### [.all](index.js#L252) 127 | 128 | Special route method that works just like the `router.METHOD()` methods, except that it matches all verbs. 129 | 130 | **Params** 131 | 132 | * `path` **{String}** 133 | * `callback` **{Function}** 134 | * `returns` **{Object}** `this`: for chaining 135 | 136 | **Example** 137 | 138 | ```js 139 | app.all(/\.hbs$/, function(view, next) { 140 | // do stuff to view 141 | next(); 142 | }); 143 | ``` 144 | 145 | ### [.handler](index.js#L274) 146 | 147 | Add a router handler method to the instance. Interchangeable with the [handlers](#handlers) method. 148 | 149 | **Params** 150 | 151 | * `method` **{String}**: Name of the handler method to define. 152 | * `returns` **{Object}**: Returns the instance for chaining 153 | 154 | **Example** 155 | 156 | ```js 157 | app.handler('onFoo'); 158 | // or 159 | app.handler(['onFoo', 'onBar']); 160 | ``` 161 | 162 | ### [.handlers](index.js#L293) 163 | 164 | Add one or more router handler methods to the instance. 165 | 166 | **Params** 167 | 168 | * `methods` **{Array|String}**: One or more method names to define. 169 | * `returns` **{Object}**: Returns the instance for chaining 170 | 171 | **Example** 172 | 173 | ```js 174 | app.handlers(['onFoo', 'onBar', 'onBaz']); 175 | // or 176 | app.handlers('onFoo'); 177 | ``` 178 | 179 | ## About 180 | 181 | ### Related projects 182 | 183 | You might also be interested in these projects: 184 | 185 | * [base](https://www.npmjs.com/package/base): Framework for rapidly creating high quality node.js applications, using plugins like building blocks | [homepage](https://github.com/node-base/base "Framework for rapidly creating high quality node.js applications, using plugins like building blocks") 186 | * [en-route](https://www.npmjs.com/package/en-route): Routing for static site generators, build systems and task runners, heavily based on express.js routes… [more](https://github.com/jonschlinkert/en-route) | [homepage](https://github.com/jonschlinkert/en-route "Routing for static site generators, build systems and task runners, heavily based on express.js routes but works with file objects. Used by Assemble, Verb, and Template.") 187 | * [gulp-routes](https://www.npmjs.com/package/gulp-routes): Add middleware to run for specified routes in your gulp pipeline. | [homepage](https://github.com/assemble/gulp-routes "Add middleware to run for specified routes in your gulp pipeline.") 188 | 189 | ### Contributing 190 | 191 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 192 | 193 | ### Building docs 194 | 195 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 196 | 197 | To generate the readme, run the following command: 198 | 199 | ```sh 200 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 201 | ``` 202 | 203 | ### Running tests 204 | 205 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 206 | 207 | ```sh 208 | $ npm install && npm test 209 | ``` 210 | 211 | ### Author 212 | 213 | **Jon Schlinkert** 214 | 215 | * [github/jonschlinkert](https://github.com/jonschlinkert) 216 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 217 | 218 | ### License 219 | 220 | Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). 221 | Released under the [MIT License](LICENSE). 222 | 223 | *** 224 | 225 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on August 07, 2017._ -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('debug')('base:routes'); 4 | var rethrow = require('template-error'); 5 | var router = require('en-route'); 6 | var utils = require('./utils'); 7 | 8 | module.exports = function(options) { 9 | return function baseRoutes(app) { 10 | if (!utils.isValid(app)) return; 11 | 12 | /** 13 | * The `Router` and `Route` classes are on the `app` instance, 14 | * in case they need to be accessed directly. 15 | * 16 | * ```js 17 | * var router = new app.Router(); 18 | * var route = new app.Route(); 19 | * ``` 20 | * @api public 21 | */ 22 | 23 | this.Router = router.Router; 24 | this.Route = router.Route; 25 | 26 | /** 27 | * Lazily initalize `router`, to allow options and 28 | * custom methods to be defined after instantiation. 29 | */ 30 | 31 | this.define('lazyRouter', function(methods) { 32 | if (this.router == null) { 33 | this.router = new this.Router({methods: utils.methods}); 34 | } 35 | if (typeof methods !== 'undefined') { 36 | this.router.method(methods); 37 | } 38 | }); 39 | 40 | /** 41 | * Handle middleware `method` for the given `file`. 42 | * 43 | * ```js 44 | * app.handle('methodName', file, next); 45 | * ``` 46 | * @name .handle 47 | * @param {String} `methodName` Name of the router method to handle. 48 | * @param {Object} `file` View object 49 | * @param {Function} `next` Callback function 50 | * @return {undefined} 51 | * @api public 52 | */ 53 | 54 | this.define('handle', function(method, file, next) { 55 | debug('handling "%s" middleware for "%s"', method, file.basename); 56 | this.lazyRouter(); 57 | 58 | if (typeof next !== 'function') { 59 | next = function(err, file) { 60 | app.handleError(method, file, function() { 61 | throw err; 62 | }); 63 | }; 64 | } 65 | 66 | file.options = file.options || {}; 67 | if (!file.options.handled) { 68 | file.options.handled = []; 69 | } 70 | 71 | // set router method on file.options 72 | file.options.method = method; 73 | file.options.handled.push(method); 74 | this.emit(method, file); 75 | 76 | // create callback 77 | var cb = this.handleError(method, file, next); 78 | 79 | // if not an instance of `Templates`, or if we're inside a collection 80 | // or the collection is not specified on file.options just handle the route and return 81 | if (!this.isTemplates || this.isCollection || !file.options.collection) { 82 | this.router.handle(file, cb); 83 | return; 84 | } 85 | 86 | // handle the app routes first, then handle the collection routes 87 | var collection = this[file.options.collection]; 88 | 89 | this.router.handle(file, function(err) { 90 | if (err) { 91 | cb(err); 92 | return; 93 | } 94 | collection.handle(method, file, cb); 95 | }); 96 | }); 97 | 98 | /** 99 | * Run the given middleware handler only if the file has not 100 | * already been handled by `method`. 101 | * 102 | * ```js 103 | * app.handleOnce(method, file, callback); 104 | * // example 105 | * app.handleOnce('onLoad', file, callback); 106 | * ``` 107 | * @name .handleOnce 108 | * @param {Object} `method` The name of the handler method to call. 109 | * @param {Object} `file` 110 | * @return {undefined} 111 | * @api public 112 | */ 113 | 114 | this.define('handleOnce', function(method, file, next) { 115 | if (!file.options.handled) { 116 | file.options.handled = []; 117 | } 118 | 119 | if (typeof next !== 'function') { 120 | next = file.next; 121 | } 122 | 123 | if (file.options.handled.indexOf(method) === -1) { 124 | this.handle(method, file, next); 125 | return; 126 | } 127 | next(null, file); 128 | }); 129 | 130 | /** 131 | * Handle middleware errors. 132 | */ 133 | 134 | this.define('handleError', function(method, file, next) { 135 | var app = this; 136 | return function(err) { 137 | next = next || file.next; 138 | 139 | if (typeof next !== 'function') { 140 | throw new TypeError('expected a callback function'); 141 | } 142 | 143 | if (err) { 144 | if (err._handled === true) { 145 | next(); 146 | return; 147 | } 148 | 149 | err._handled = true; 150 | err.source = err.stack.split('\n')[1].trim(); 151 | err.reason = app._name + '#handle("' + method + '"): ' + file.path; 152 | err.file = file; 153 | 154 | if (app.hasListeners('error')) { 155 | app.emit('error', err); 156 | } 157 | 158 | if (typeof next !== 'function') { 159 | throw err; 160 | } 161 | 162 | if (err instanceof ReferenceError) { 163 | try { 164 | rethrow(file.content, file.data); 165 | } catch (e) { 166 | next(e); 167 | return; 168 | } 169 | } 170 | 171 | next(err); 172 | return; 173 | } 174 | next(null, file); 175 | }; 176 | }); 177 | 178 | /** 179 | * Create a new Route for the given path. Each route 180 | * contains a separate middleware stack. See the [en-route][] 181 | * API documentation for details on adding handlers and 182 | * middleware to routes. 183 | * 184 | * ```js 185 | * app.create('posts'); 186 | * app.route(/blog/) 187 | * .all(function(file, next) { 188 | * // do something with file 189 | * next(); 190 | * }); 191 | * 192 | * app.post('whatever', {path: 'blog/foo.bar', content: 'bar baz'}); 193 | * ``` 194 | * @name .route 195 | * @param {String} `path` 196 | * @return {Object} Returns the instance for chaining. 197 | * @api public 198 | */ 199 | 200 | this.define('route', function(/*path*/) { 201 | this.lazyRouter(); 202 | return this.router.route.apply(this.router, arguments); 203 | }); 204 | 205 | /** 206 | * Add callback triggers to route parameters, where 207 | * `name` is the name of the parameter and `fn` is the 208 | * callback function. 209 | * 210 | * ```js 211 | * app.param('title', function(view, next, title) { 212 | * //=> title === 'foo.js' 213 | * next(); 214 | * }); 215 | * 216 | * app.onLoad('/blog/:title', function(view, next) { 217 | * //=> view.path === '/blog/foo.js' 218 | * next(); 219 | * }); 220 | * ``` 221 | * @name .param 222 | * @param {String} `name` 223 | * @param {Function} `fn` 224 | * @return {Object} Returns the instance for chaining. 225 | * @api public 226 | */ 227 | 228 | this.define('param', function(/*name, fn*/) { 229 | this.lazyRouter(); 230 | this.router.param.apply(this.router, arguments); 231 | return this; 232 | }); 233 | 234 | /** 235 | * Special route method that works just like the 236 | * `router.METHOD()` methods, except that it matches 237 | * all verbs. 238 | * 239 | * ```js 240 | * app.all(/\.hbs$/, function(view, next) { 241 | * // do stuff to view 242 | * next(); 243 | * }); 244 | * ``` 245 | * @name .all 246 | * @param {String} `path` 247 | * @param {Function} `callback` 248 | * @return {Object} `this` for chaining 249 | * @api public 250 | */ 251 | 252 | this.define('all', function(path/*, callback*/) { 253 | this.lazyRouter(); 254 | var route = this.route(path); 255 | route.all.apply(route, [].slice.call(arguments, 1)); 256 | return this; 257 | }); 258 | 259 | /** 260 | * Add a router handler method to the instance. Interchangeable 261 | * with the [handlers]() method. 262 | * 263 | * ```js 264 | * app.handler('onFoo'); 265 | * // or 266 | * app.handler(['onFoo', 'onBar']); 267 | * ``` 268 | * @name .handler 269 | * @param {String} `method` Name of the handler method to define. 270 | * @return {Object} Returns the instance for chaining 271 | * @api public 272 | */ 273 | 274 | this.define('handler', function(method) { 275 | this.handlers(method); 276 | return this; 277 | }); 278 | 279 | /** 280 | * Add one or more router handler methods to the instance. 281 | * 282 | * ```js 283 | * app.handlers(['onFoo', 'onBar', 'onBaz']); 284 | * // or 285 | * app.handlers('onFoo'); 286 | * ``` 287 | * @name .handlers 288 | * @param {Array|String} `methods` One or more method names to define. 289 | * @return {Object} Returns the instance for chaining 290 | * @api public 291 | */ 292 | 293 | this.define('handlers', function(methods) { 294 | this.lazyRouter(methods); 295 | mixinHandlers(methods); 296 | return this; 297 | }); 298 | 299 | function mixinHandlers(methods) { 300 | utils.arrayify(methods).forEach(function(method) { 301 | app.define(method, function(path) { 302 | var route = this.route(path); 303 | var args = [].slice.call(arguments, 1); 304 | route[method].apply(route, args); 305 | return this; 306 | }); 307 | }); 308 | } 309 | 310 | // Mix router handler methods onto the intance 311 | mixinHandlers(utils.methods); 312 | return baseRoutes; 313 | }; 314 | }; 315 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-routes", 3 | "description": "Plugin for adding routes support to your `base` application. Requires templates support to work.", 4 | "version": "1.0.1", 5 | "homepage": "https://github.com/node-base/base-routes", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "repository": "node-base/base-routes", 8 | "bugs": { 9 | "url": "https://github.com/node-base/base-routes/issues" 10 | }, 11 | "license": "MIT", 12 | "files": [ 13 | "index.js", 14 | "utils.js" 15 | ], 16 | "main": "index.js", 17 | "engines": { 18 | "node": ">=0.10.0" 19 | }, 20 | "scripts": { 21 | "test": "mocha" 22 | }, 23 | "dependencies": { 24 | "debug": "^2.6.8", 25 | "en-route": "^1.0.1", 26 | "is-valid-app": "^0.3.0", 27 | "template-error": "^0.1.2" 28 | }, 29 | "devDependencies": { 30 | "base-app": "^0.2.6", 31 | "gulp-format-md": "^1.0.0", 32 | "mocha": "^3.5.0", 33 | "should": "^11.2.1" 34 | }, 35 | "keywords": [ 36 | "api", 37 | "app", 38 | "application", 39 | "base", 40 | "base-plugin", 41 | "baseplugin", 42 | "building-blocks", 43 | "create", 44 | "framework", 45 | "middleware", 46 | "plugin", 47 | "plugins", 48 | "router", 49 | "routes", 50 | "routing", 51 | "tool", 52 | "toolkit", 53 | "tools" 54 | ], 55 | "verb": { 56 | "related": { 57 | "list": [ 58 | "base", 59 | "en-route", 60 | "gulp-routes" 61 | ] 62 | }, 63 | "toc": true, 64 | "layout": "default", 65 | "tasks": [ 66 | "readme" 67 | ], 68 | "plugins": [ 69 | "gulp-format-md" 70 | ], 71 | "lint": { 72 | "reflinks": true 73 | }, 74 | "reflinks": [ 75 | "verb", 76 | "base" 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | require('should'); 5 | var assert = require('assert'); 6 | var Base = require('base-app'); 7 | var routes = require('./'); 8 | var app; 9 | 10 | describe('routes', function() { 11 | beforeEach(function() { 12 | app = new Base(); 13 | app.isApp = true; 14 | app.use(routes()); 15 | }); 16 | 17 | describe('routes', function() { 18 | it('should create a route for the given path:', function(cb) { 19 | var count = 0; 20 | 21 | app.once('onLoad', function(view) { 22 | count++; 23 | assert.deepEqual(view, { 24 | path: 'blog/foo.js', 25 | options: { 26 | handled: [ 'onLoad' ], 27 | method: 'onLoad' 28 | } 29 | }); 30 | }); 31 | 32 | app.route('blog/:title') 33 | .onLoad(function(view, next) { 34 | count++; 35 | next(); 36 | }); 37 | 38 | app.handle('onLoad', { path: 'blog/foo.js' }, function(err) { 39 | if (err) { 40 | cb(err); 41 | return; 42 | } 43 | assert.equal(count, 2); 44 | cb(); 45 | }); 46 | }); 47 | 48 | it('should emit events when a route method is called:', function(cb) { 49 | var count = 0; 50 | 51 | app.once('onLoad', function(view) { 52 | count++; 53 | assert.deepEqual(view, { 54 | path: 'blog/foo.js', 55 | options: { 56 | handled: [ 'onLoad' ], 57 | method: 'onLoad' 58 | } 59 | }); 60 | }); 61 | 62 | app.param('title', function(view, next, title) { 63 | assert.equal(title, 'foo.js'); 64 | count++; 65 | next(); 66 | }); 67 | 68 | app.onLoad('blog/:title', function(view, next) { 69 | assert.equal(view.path, 'blog/foo.js'); 70 | count++; 71 | next(); 72 | }); 73 | 74 | app.handle('onLoad', {path: 'blog/foo.js'}, function(err) { 75 | if (err) { 76 | cb(err); 77 | return; 78 | } 79 | assert.equal(count, 3); 80 | cb(); 81 | }); 82 | }); 83 | 84 | it('should emit and handle errors', function(cb) { 85 | var count = 0; 86 | 87 | app.on('error', function(err) { 88 | count++; 89 | assert.equal(err.message, "'foo.js' == 'fo.js'"); 90 | }); 91 | 92 | // should be wrong... 93 | app.param('title', function(view, next, title) { 94 | count++; 95 | assert.equal(title, 'fo.js'); 96 | next(); 97 | }); 98 | 99 | app.onLoad('blog/:title', function(view, next) { 100 | count++; 101 | assert.equal(view.path, '/blog/foo.js'); 102 | next(); 103 | }); 104 | 105 | app.handle('onLoad', {path: 'blog/foo.js'}, function(err) { 106 | assert(err); 107 | assert.equal(count, 2); 108 | cb(); 109 | }); 110 | }); 111 | 112 | it('should have path property', function() { 113 | var route = new app.Route('/blog/:year/:month/:day/:slug').all([ 114 | function() {} 115 | ]); 116 | assert.equal(route.path, '/blog/:year/:month/:day/:slug'); 117 | }); 118 | 119 | it('should have stack property', function() { 120 | var route = new app.Route('/blog/:year/:month/:day/:slug').all([ 121 | function() {} 122 | ]); 123 | 124 | assert.equal(Array.isArray(route.stack), true); 125 | assert.equal(route.stack.length, 1); 126 | }); 127 | }); 128 | 129 | describe('params', function() { 130 | it('should call param function when routing', function(cb) { 131 | var count = 0; 132 | 133 | app.param('id', function(view, next, id) { 134 | count++; 135 | assert.equal(id, '123'); 136 | next(); 137 | }); 138 | 139 | app.all('/foo/:id/bar', function(view, next) { 140 | count++; 141 | assert.equal(view.options.params.id, '123'); 142 | next(); 143 | }); 144 | 145 | app.router.handle({ path: '/foo/123/bar' }, function(err) { 146 | if (err) { 147 | cb(err); 148 | return; 149 | } 150 | assert.equal(count, 2); 151 | cb(); 152 | }); 153 | }); 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('debug')('base:routes'); 4 | var isValidApp = require('is-valid-app'); 5 | var utils = module.exports; 6 | 7 | /** 8 | * Returns true if `app` is valid and is not already registered. 9 | */ 10 | 11 | utils.isValid = function(app) { 12 | if (!isValidApp(app, 'base-routes', ['app', 'collection', 'views', 'list'])) { 13 | return false; 14 | } 15 | debug('loading routes methods'); 16 | return true; 17 | }; 18 | 19 | /** 20 | * Cast `val` to an array. 21 | */ 22 | 23 | utils.arrayify = function(val) { 24 | return val ? (Array.isArray(val) ? val : [val]) : []; 25 | }; 26 | 27 | /** 28 | * Default router methods 29 | */ 30 | 31 | utils.methods = [ 32 | 'onLoad', 33 | 'preCompile', 34 | 'preLayout', 35 | 'onLayout', 36 | 'postLayout', 37 | 'onMerge', 38 | 'onStream', 39 | 'postCompile', 40 | 'preRender', 41 | 'postRender', 42 | 'preWrite', 43 | 'postWrite' 44 | ]; 45 | 46 | /** 47 | * Expose `utils` modules 48 | */ 49 | 50 | module.exports = utils; 51 | --------------------------------------------------------------------------------