subway-router
TypeScript icon, indicating that this package has built-in type declarations

1.1.1 • Public • Published

SubwayJS - the JavaScript router

Usage

Basic example

const map = new Map(); // create router
map.route('/', (request, route) => {
    // home page
});
map.group('/news', group => {
    group.route('/', (request, route) => {
        // news page
    });
    group.route('/{id:i}', (request, route) => {
        const postId = Number(request.segment(2));
        // article page
    });
});
map.build(); // routes built and ready to use
map.dispatch(window.location.href); // route

With NodeJS and NPM

Install

npm i subway-router --save

Usage

import { Map } from 'Subway';
const map = new Map();

With browser

Download the latest version from GitHub releases.
Add this line to the end of your body tag:

<script type="text/javascript" src="subway.min.js"></script>

Usage

const map = new Subway.Map();

Routes

Router or your site map

import { Map } from 'Subway';
const map = new Map();

Static path

map.route('/foo/bar', req => {})

Variables

map.route('/foo/{bar}') // {bar} - any string
map.route('/foo/{bar:i}') // {bar:i} - integers only
map.route('/foo/{bar:a}') // {bar:a} - allowed alpha (a-zA-Z), underscores and dashes
map.route('/foo/{bar:[a-z]{2}\d+}') - pattern matching (case insensitive)

Optional segments

map.route('/foo?/{bar}') // first segment is optional

Groups

map.group('/foo', group => {
    group.route('/', req => {}); // GET /foo
    group.route('/bar', req => {}); // GET /foo/bar
});

Preparing and dispatching routes

Build

Your router has to be compiled before dispatching.

map.build(); // now router is ready to use

You should re-build your router with any changes in your routes setup

Dispatch

window.addEventListener('popstate', () => {
    map.dispatch(window.location.href);
});

Routes estimation and priorities

Static segments has the highest priority.
Pattern segments has the medium priority.
Variable (any) segments has the lowest priority.

map.route('/foo/{bar}').name('any')
map.route('/foo/{bar:i}').name('integer')
map.route('/foo/bar').name('static')

map.dispatch('/foo/bar') // route named 'static' will be loaded
map.dispatch('/foo/foo') // route named 'any' will be loaded

Optional segments has less priority

map.route('/foo?/bar').name('first')
map.route('/foo/bar').name('second')

map.dispatch('/foo/bar') // route named 'second' will be loaded
// Technically both routes are fits such request,
// but 'first' route will get less estimation due to an optional segment.
// In this example 'first' route will never be loaded

Middleware hooks

import { IMiddleware, Request, Route, TOnLoad } from 'Subway';

class CustomMiddleware implements IMiddleware {

    // execute after route estimated
    onEstimated(rate : number, request : Request, route : Route) : number {
        // this hook should return integer
        // return -1 to skip current route
        return rate;
    }

    // execute before route loaded
    async onResolving(onLoad : TOnLoad, request, request : Request, route : Route) : TOnLoad | Promise<TOnLoad> {
        // this hook can by async or syncronous and should return or promise onLoad callback
        return new Promise(resolve => {
            // do something before load route
            resolve(onLoad);
        });
    }

    // execute after route loaded
    onResolved(request, request : Request, route : Route) {
        // this hook don't return anything
    }

}

map.route('/foo/bar', (request, route) => {
    // load page
}).middleware(new CustomMiddleware());

map.group('/', group => {
    group.route('/foo/bar', (request, route) => {
        // load page
    });
}).middleware(new CustomMiddleware());

Multiple middleware

class Logger implements IMiddleware {
    
    onResolved(request, route) {
        console.log(request.path, request.query, route.name);
    }
    
}

class Auth implements IMiddleware {
    
    onResolving(onLoad, request) {
        // check if user logged in
        if(document.cookie.indexOf('user=') >= 0) {
            return onLoad;
        } else {
            return () => {
                // login page
            };
        }
    }
    
}

map.group('/', group => {
    ...
}).middleware(new Logger(), new Auth());

Object reference

Request

new Request('https://example.com/foo/bar?foo=1&bar=2#foo')
    .url : string // 'https://example.com/foo/bar?foo=1&bar=2#foo'
    .origin : string // 'https://example.com'
    .path : string // 'foo/bar'
    .query : string // 'foo=1&bar=2'
    .anchor : string // '#foo'
    .segments : string[] // [ 'foo', 'bar' ]
    .segment(segmentNumber : number) : string // get segment by number (starts from 1). Example: request.segment(2) => 'bar'
    .keys : Record<string, string> // { foo: '1', bar: '2' }
    .key(keyName : string) : string // get GET property by name. Example: request.key('bar') => '2'

Route

Route
    .name : string // name of route or empty string if not defined
    .groups : string[] // array with names of groups wich contain this route 
    .inGroup(name : string) : boolean // check if this route in group with specific name
    .estimate(request : Request) : number // estimate route (middleware hooks will be executed)
    .getUrl(props : Record<string, string>) : string // get URL of this route. Keys from 'props' parameter will be used to replace variable segments. For example: map.route('/foo/{bar}').getUrl({ bar: 'something' }) will return '/foo/something'
    .resolve(request : Request) // load route (middleware hooks will be executed)

Package Sidebar

Install

npm i subway-router

Weekly Downloads

1

Version

1.1.1

License

MIT

Unpacked Size

148 kB

Total Files

55

Last publish

Collaborators

  • alcohol120