Start writing the documentation, but there's a bunch of work left still to do.

This commit is contained in:
Starbeamrainbowlabs 2019-04-27 00:38:02 +01:00
parent c3e352b77b
commit 71fc76521a
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
7 changed files with 119 additions and 58 deletions

View File

@ -2,7 +2,18 @@
import EventEmitter from 'event-emitter-es6';
import { pathspec_to_regex } from '../Shared/Pathspec.mjs';
/**
* Client-side request router.
* @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) {
super();
@ -30,18 +41,34 @@ class ClientRouter extends EventEmitter {
}).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) {
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) {
this.routes.push({
spec: routespec,
match: this.pathspec_to_regex(routespec),
match: pathspec instanceof RegExp ? { regex: routespec, tokens: [] } : pathspec_to_regex(routespec),
callback
});
}
/**
* Manually navigate to a given path.
* @param {string} path The path to navigate to.
* @example router.navigate("/add/carrot/frederick/10001");
*/
navigate(path) {
for(let route_info of this.routes) {
const matches = path.match(route_info.match.regex);
@ -71,35 +98,13 @@ class ClientRouter extends EventEmitter {
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() {
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;

View File

@ -1,7 +1,52 @@
# powahroot
# ![](https://raw.githubusercontent.com/sbrl/powahroot/master/logo-large.png)powahroot
> 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
Everything in this repository _except_ the logo is licenced under the _Mozilla Public License 2.0.

View File

@ -1,6 +1,7 @@
"use strict";
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.
@ -50,7 +51,7 @@ class Router
* @param {Function} action The action to execute. Will be passed the parameters `context` (Object) and `next` (Function).
*/
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
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.
* @param {http.ClientRequest} request The request to handle.

28
Shared/Pathspec.mjs Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

10
package-lock.json generated
View File

@ -1,9 +1,15 @@
{
"name": "powerroot",
"version": "1.0.0",
"name": "powahroot",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/await-fs/-/await-fs-1.0.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "powerroot",
"version": "1.0.0",
"name": "powahroot",
"version": "0.1.0",
"description": "Client and server-side routing micro frameworks",
"main": "index.mjs",
"scripts": {
@ -24,6 +24,7 @@
"nightink": "^0.1.2"
},
"devDependencies": {
"@types/event-emitter-es6": "^1.1.0",
"event-emitter-es6": "^1.1.5"
}
}