en-route
Version:
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.
124 lines (107 loc) • 2.71 kB
JavaScript
'use strict';
const assert = require('assert');
const toRegex = require('./to-regex');
/**
* Create a new `Layer` with the given `pattern`, handler function and options.
*
* ```js
* const layer = new Layer('/', file => {
* // do stuff to file
* file.extname = '.html';
* });
* ```
* @name Layer
* @param {string} `pattern`
* @param {function} `handler`
* @param {object} `options`
* @api public
*/
class Layer {
constructor(pattern, fn, options) {
assert.equal(typeof fn, 'function', 'expected handler to be a function');
this.options = { strict: false, end: false, ...options };
this.pattern = pattern;
this.handler = file => {
let params = this.match(file.path);
if (params) {
return fn(file, params);
}
};
}
/**
* Calls the layer handler on the given file if the `file.path` matches
* the layer pattern.
*
* ```js
* layer.handle(file)
* .then(() => console.log('Done:', file))
* .then(console.error)
* ```
* @name .handle
* @param {object} `file` File object
* @return {Promise}
* @api public
*/
handle(file) {
let params = this.handler(file);
if (params instanceof Promise) {
return params.then(res => {
file.params = res;
return file;
});
}
file.params = params;
return file;
}
/**
* Attempts to match a file path with the layer pattern. If the path matches,
* an object of params is returned (see [path-to-regexp][] for details), otherwise
* `null` is returned.
*
* ```js
* const layer = new Layer('/:name');
* console.log(layer.match('/foo')) //=> { name: 'foo' }
* ```
* @name .match
* @param {string} `filepath`
* @return {object|null}
* @api public
*/
match(filepath) {
if (!filepath) return null;
let match = this.regex.exec(filepath);
if (!match) return null;
let captures = match.slice(1);
let params = {};
let n = 0;
for (let i = 0; i < captures.length; i++) {
let val = captures[i];
let key = this._regex.keys[i];
let prop = key && key.name ? key.name : n++;
params[prop] = val;
}
return params;
}
/**
* Lazily create the regex to use for matching
*/
get regex() {
if (this._regex instanceof RegExp) {
this._regex.lastIndex = 0;
return this._regex;
}
if (this.pattern && !this._regex) {
let pattern = this.pattern;
let keys = [];
let regex = this._regex = toRegex(pattern, keys, this.options);
regex.lastIndex = 0;
regex.keys = keys;
this._regex = regex;
return regex;
}
}
}
/**
* Expose `Layer`
*/
module.exports = Layer;