mirror of
https://github.com/sbrl/powahroot.git
synced 2024-11-21 22:22:59 +00:00
Start writing the documentation, but there's a bunch of work left still to do.
This commit is contained in:
parent
c3e352b77b
commit
71fc76521a
7 changed files with 119 additions and 58 deletions
|
@ -2,7 +2,18 @@
|
||||||
|
|
||||||
import EventEmitter from 'event-emitter-es6';
|
import EventEmitter from 'event-emitter-es6';
|
||||||
|
|
||||||
|
import { pathspec_to_regex } from '../Shared/Pathspec.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client-side request router.
|
||||||
|
* @extends EventEmitter
|
||||||
|
*/
|
||||||
class ClientRouter extends EventEmitter {
|
class ClientRouter extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Creates a new client-side request router.
|
||||||
|
* You should use this in a browser. If you're not in a browser, you probably want ServerRouter instead.
|
||||||
|
* @param {object} options The options object to use when creating the router.
|
||||||
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -30,18 +41,34 @@ class ClientRouter extends EventEmitter {
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the function to execute when no other route could be matched.
|
||||||
|
* @param {Function} callback The callback to execute.
|
||||||
|
* @example router.add_404((path) => console.log(`Oops! Couldn't find a route to handle '${path}'.`));
|
||||||
|
*/
|
||||||
add_404(callback) {
|
add_404(callback) {
|
||||||
this.callback_404 = callback;
|
this.callback_404 = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a route to the router.
|
||||||
|
* @param {string|RegExp} routespec The route specification that the route should match against. May contain regular expression syntax, and the domain-specific :param syntax. A raw regular expression may also be passed, if you need the flexibility.
|
||||||
|
* @param {Function} callback The callback to execute when the route is matched.
|
||||||
|
* @example router.add_page("/add/vegetable/:name/:weight", (params) => console.log(`We added a ${params.name} with a weight of ${params.weight}g.`));
|
||||||
|
*/
|
||||||
add_page(routespec, callback) {
|
add_page(routespec, callback) {
|
||||||
this.routes.push({
|
this.routes.push({
|
||||||
spec: routespec,
|
spec: routespec,
|
||||||
match: this.pathspec_to_regex(routespec),
|
match: pathspec instanceof RegExp ? { regex: routespec, tokens: [] } : pathspec_to_regex(routespec),
|
||||||
callback
|
callback
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually navigate to a given path.
|
||||||
|
* @param {string} path The path to navigate to.
|
||||||
|
* @example router.navigate("/add/carrot/frederick/10001");
|
||||||
|
*/
|
||||||
navigate(path) {
|
navigate(path) {
|
||||||
for(let route_info of this.routes) {
|
for(let route_info of this.routes) {
|
||||||
const matches = path.match(route_info.match.regex);
|
const matches = path.match(route_info.match.regex);
|
||||||
|
@ -71,35 +98,13 @@ class ClientRouter extends EventEmitter {
|
||||||
this.callback_404(path);
|
this.callback_404(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the current URL's hash value - i.e. the bit after the '#' symbol in the URL.
|
||||||
|
* @example router.navigate_current_hash();
|
||||||
|
*/
|
||||||
navigate_current_hash() {
|
navigate_current_hash() {
|
||||||
this.navigate(window.location.hash.substr(1));
|
this.navigate(window.location.hash.substr(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a path specification into a regular expression.
|
|
||||||
* From the server-side sibling of this (client-side) router.
|
|
||||||
* @param {string} pathspec The path specification to convert.
|
|
||||||
* @return {RegExp} The resulting regular expression
|
|
||||||
*/
|
|
||||||
pathspec_to_regex(pathspec) {
|
|
||||||
if(pathspec == "*") // Support wildcards
|
|
||||||
return { regex: /^/, tokens: [] };
|
|
||||||
|
|
||||||
let tokens = [];
|
|
||||||
let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not used)*/) => {
|
|
||||||
tokens.push(substr.replace(/:/g, ""));
|
|
||||||
|
|
||||||
// FUTURE: We could add optional param support here too
|
|
||||||
if(substr.startsWith("::"))
|
|
||||||
return `(.+)`;
|
|
||||||
else
|
|
||||||
return `([^\/]+)`;
|
|
||||||
}) + "$", "i");
|
|
||||||
|
|
||||||
if(this.verbose) console.info("[router/verbose] Created regex", regex);
|
|
||||||
|
|
||||||
return { regex, tokens };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ClientRouter;
|
export default ClientRouter;
|
||||||
|
|
47
README.md
47
README.md
|
@ -1,7 +1,52 @@
|
||||||
# powahroot
|
# ![](https://raw.githubusercontent.com/sbrl/powahroot/master/logo-large.png)powahroot
|
||||||
|
|
||||||
> Client and server-side routing micro frameworks
|
> Client and server-side routing micro frameworks
|
||||||
|
|
||||||
|
_Powerahroot_ is a pair of micro routing frameworks, presented as an ES6 module:
|
||||||
|
|
||||||
|
- The first is for client-side single-page web applications
|
||||||
|
- The other is for handling server-side Node.js requests
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
Install powahroot as a dependency with npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save powahroot
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `import` the router you're after:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ServerRouter } from 'powahroot';
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ClientRouter } from 'powahroot';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Paths
|
||||||
|
Powahroot supports multiple syntax bells and whistles when defining routes. These are documented below:
|
||||||
|
|
||||||
|
Syntax | Meaning
|
||||||
|
--------------------------------|----------------------------------------
|
||||||
|
`/index.html` | Regular route. Matches exactly what it says on the tin.
|
||||||
|
`*` | Special key(word?) that matches _any_ route. Must be present on its own without any other characters.
|
||||||
|
`/add/vegetable/:name/:weight` | Parameters. Match values an pull them into an object automatically. Does not like forward slashes in parameter values.
|
||||||
|
`/images/::path` | Parameter values with forward slashes. If you want to use parameters, but need values to be able to contain forward slashes `/`, this is for you. Don't forget you can mix-and-match this with the previous example!
|
||||||
|
|
||||||
|
|
||||||
|
### Client
|
||||||
|
Initialise a new router like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const router = new ClientRouter({
|
||||||
|
// Options object. Default settings:
|
||||||
|
verbose: false, // Whether to be verbose in console.log() messages
|
||||||
|
listen_pushstate: true, // Whether to react to browser pushstate events (excluding those generated by powahroot itself, because that would cause an infinite loop :P)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
Everything in this repository _except_ the logo is licenced under the _Mozilla Public License 2.0.
|
Everything in this repository _except_ the logo is licenced under the _Mozilla Public License 2.0.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import RouterContext from './RouterContext.mjs';
|
import RouterContext from './RouterContext.mjs';
|
||||||
|
import { pathspec_to_regex } from '../Shared/Pathspec.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A standalone HTTP router that's based on the principle of middleware.
|
* A standalone HTTP router that's based on the principle of middleware.
|
||||||
|
@ -50,7 +51,7 @@ class Router
|
||||||
* @param {Function} action The action to execute. Will be passed the parameters `context` (Object) and `next` (Function).
|
* @param {Function} action The action to execute. Will be passed the parameters `context` (Object) and `next` (Function).
|
||||||
*/
|
*/
|
||||||
on(methods, pathspec, action) {
|
on(methods, pathspec, action) {
|
||||||
let regex_info = pathspec instanceof RegExp ? {regex: pathspec, tokens: [] } : this.pathspec_to_regex(pathspec);
|
let regex_info = pathspec instanceof RegExp ? {regex: pathspec, tokens: [] } : pathspec_to_regex(pathspec);
|
||||||
|
|
||||||
// next must be a generator that returns each action in turn
|
// next must be a generator that returns each action in turn
|
||||||
this.actions.push(async (context, next) => {
|
this.actions.push(async (context, next) => {
|
||||||
|
@ -93,31 +94,6 @@ class Router
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a path specification into a regular expression.
|
|
||||||
* @param {string} pathspec The path specification to convert.
|
|
||||||
* @return {RegExp} The resulting regular expression
|
|
||||||
*/
|
|
||||||
pathspec_to_regex(pathspec) {
|
|
||||||
if(pathspec == "*") // Support wildcards
|
|
||||||
return { regex: /^/, tokens: [] };
|
|
||||||
|
|
||||||
let tokens = [];
|
|
||||||
let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not actually used)*/) => {
|
|
||||||
tokens.push(substr.replace(/:/g, ""));
|
|
||||||
|
|
||||||
// FUTURE: We could add optional param support here too
|
|
||||||
if(substr.startsWith("::"))
|
|
||||||
return `(.+)`;
|
|
||||||
else
|
|
||||||
return `([^\/]+)`;
|
|
||||||
}) + "$", "i");
|
|
||||||
|
|
||||||
/*if(this.verbose)*/ console.error("[router/verbose] Created regex", regex);
|
|
||||||
|
|
||||||
return { regex, tokens };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the specified request.
|
* Handles the specified request.
|
||||||
* @param {http.ClientRequest} request The request to handle.
|
* @param {http.ClientRequest} request The request to handle.
|
||||||
|
|
28
Shared/Pathspec.mjs
Normal file
28
Shared/Pathspec.mjs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Converts a path specification into a regular expression.
|
||||||
|
* Originally from the server-side sibling of this (client-side) router.
|
||||||
|
* @param {string} pathspec The path specification to convert.
|
||||||
|
* @param {Boolean} verbose Optional. Whether to be verbose and log some stuff to the console. Default: false
|
||||||
|
* @return {RegExp} The resulting regular expression
|
||||||
|
*/
|
||||||
|
function pathspec_to_regex(pathspec, verbose = false) {
|
||||||
|
if(pathspec == "*") // Support wildcards
|
||||||
|
return { regex: /^/, tokens: [] };
|
||||||
|
|
||||||
|
let tokens = [];
|
||||||
|
let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not used)*/) => {
|
||||||
|
tokens.push(substr.replace(/:/g, ""));
|
||||||
|
|
||||||
|
// FUTURE: We could add optional param support here too
|
||||||
|
if(substr.startsWith("::"))
|
||||||
|
return `(.+)`;
|
||||||
|
else
|
||||||
|
return `([^\/]+)`;
|
||||||
|
}) + "$", "i");
|
||||||
|
|
||||||
|
if(verbose) console.info("[router/verbose] Created regex", regex);
|
||||||
|
|
||||||
|
return { regex, tokens };
|
||||||
|
}
|
||||||
|
|
||||||
|
export { pathspec_to_regex };
|
BIN
logo-large.png
Normal file
BIN
logo-large.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 696 B |
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1,9 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "powerroot",
|
"name": "powahroot",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/event-emitter-es6": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/event-emitter-es6/-/event-emitter-es6-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-A0010GUMSopVYUlDdzhYAD8Z2qGC2hG2H32IHy554NpGEHmsx8M6/lLZRRqxXCxLq28aYrmaZtGwDJjeNXbTDQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"await-fs": {
|
"await-fs": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/await-fs/-/await-fs-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/await-fs/-/await-fs-1.0.0.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "powerroot",
|
"name": "powahroot",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"description": "Client and server-side routing micro frameworks",
|
"description": "Client and server-side routing micro frameworks",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
"nightink": "^0.1.2"
|
"nightink": "^0.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/event-emitter-es6": "^1.1.0",
|
||||||
"event-emitter-es6": "^1.1.5"
|
"event-emitter-es6": "^1.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue