/dush-router

💫 A simple regex-based router for `dush`, `base`, `minibase` and anything based on them. Works on Browser and Node.js. Can extend it to use `path-match` instead.

Primary LanguageJavaScriptMIT LicenseMIT

dush-router npm version github tags mit license

A simple regex-based router for dush, base, minibase and anything based on them. Works on Browser and Node.js

You might also be interested in dush-no-chaining, dush-methods and dush-tap-report, a plugins for dush microscopic event emitter with simple & powerful plugin system.

Quality 👌

By using commitizen and conventional commit messages, maintaining meaningful ChangeLog and commit history based on global conventions, following StandardJS code style through ESLint and having always up-to-date dependencies through integrations like GreenKeeper and David-DM service, this package has top quality.

code style commitizen friendly greenkeeper friendly dependencies

Stability 💯

By following Semantic Versioning through standard-version releasing tool, this package is very stable and its tests are passing both on Windows (AppVeyor) and Linux (CircleCI) with results from 100% to 400% test coverage, reported respectively by CodeCov and nyc (istanbul).

following semver semantic releases linux build windows build code coverage nyc coverage

Support 👏

If you have any problems, consider opening an issue, ping me on twitter (@tunnckoCore), join the support chat room or queue a live session on CodeMentor with me. If you don't have any problems, you're using it somewhere or you just enjoy this product, then please consider donating some cash at PayPal, since this is OPEN Open Source project made with ❤️ at Sofia, Bulgaria 🇧🇬.

tunnckoCore support code mentor paypal donate NPM monthly downloads npm total downloads

Highlights ✨

  • Small: Really small and lightweight
  • Easy: Regex-based routing, for simple cases
  • Extensible: Can use path-match under the hood
  • Isomorphic: For the browser or Node.js >= v0.10
  • Customize: Control over route handler's arguments
  • Great: Sane and good defaults, but easy to customize
  • Simple: Based on awesome event system like dush
  • Stable: Well tested, with 400% coverage
  • Modern: Plays well with nanomorph, bel or any other thing
  • Allows: Adding multiple handlers on same route

Table of Contents

(TOC generated by verb using markdown-toc)

Install

Install with npm

$ npm install dush-router --save

or install using yarn

$ yarn add dush-router

Usage

For more use-cases see the tests

const dushRouter = require('dush-router')

API

A plugin that adds .createRoute, .addRoute and .navigate methods for any app based on dush, base or minibase. Notice that this plugin emit events - route if match, and notFound if not route found on defined routes.

Params

  • opts {Object}: no options currently
  • returns {Function}: a plugin function which should be passed to .use method

Example

var dush = require('dush')
var router = require('dush-router')

var app = dush()
app.use(router())

console.log(app._routes) // => []
console.log(app.createRoute) // => function
console.log(app.addRoute) // => function
console.log(app.navigate) // => function

Add/register an actual route with handler to the app._routes array. It uses .createRoute method to create an "route" object that is then pushed to app._routes.

Note: If route handler returns something the app.navigate method will return that exact value on route match.

Params

  • route {String}: a simple route, express-like definition, e.g. /user/:id
  • handler {Function}: a function to be called when route match
  • returns {Object}: instance for chaining

Example

app.addRoute('/foobar', (context) => {
  console.log('state:', context.state) // => { hello: 'world' }
  console.log('params:', context.params) // => {}
  console.log('route:', context.route) // => '/foobar'
  console.log('pathname:', context.pathname) // => '/foobar'
})

app.navigate('/foobar', { hello: 'world' })

// or with params
app.addRoute('/user/:id', ({ state, params, route, pathname }) => {
  console.log('Hello ', state.username) // => 'Hello Charlike'
  console.log('Your ID is', params.id) // => 'Your ID is 123'

  console.log('route', route) // => '/user/:id'
  console.log('path', pathname) // => '/user/123'
})

app.navigate('/user/123', { username: 'Charlike' })

Just create a route with handler, same as .addRoute method, but without adding it to app._routes array. This "route" object contains .match, .regex, .route and .handler properties. Where .match is a function that accepts single argument "pathname" to check against given route, .handler is the passed handler function, .regex is the generated regex for that route string and the .route is the given route. The .match function returns null if passed "pathname" string match to the given route but not params and false if passed "pathname" not match.

Note: This method does not call the given route handler.

Params

  • route {String}: a simple route, express-like definition, e.g. /user/:id
  • handler {Function}: a function to be called when route match
  • returns {Object}: a "route" object with few properties

Example

const r = app.createRoute('/user/:id', function abc (params) {
  console.log('hi user with id:', params.id)
})

console.log(r.match) // => function
console.log(r.handler) // => function
console.log(r.handler.name) // => 'abc'
console.log(r.route) // => '/user/:id'
console.log(r.regex) // => /^\/user\/(\w+)$/i

var params = r.match('/user/123')
console.log(params) // => { id: 123 }

// manually call the route handler
if (params !== false) {
  r.handler(params || {})
}

// not match, so returns `false`
params = r.match('/foobar')
console.log(params) // => false

var route = app.createRoute('/foobie', () => {})

// match, but no params, so return `null`
var res = route.match('/foobie')
console.log(res) // => null

Manually navigate to some route with url pathname and returns what the route handler returns. You can pass a custom state which will be passed to route handler's context as context.state. This method fires notFound event when not found match, and route when find a route.

Params

  • pathname {String}: a url to navigate to
  • state {any}: optionally pass a "state", passed to route's handler
  • returns {any}: basically returns what the route handler return

Example

app.on('notFound', (context) => {
  console.log(`sorry ${context.pathname} page not exist`)
  console.log('this is incoming state:', context.state)
})
app.navigate('/foo/bar/qux', { aa: 11 })

app.addRoute('/hello/:place', (context) => {
  console.log('hi', context.params.place) // => 'hi world'
})
app.navigate('/hello/world')

// remove default "on route" handler
app.off('route')

// and define your custom one,
// to change route handler arguments
app.on('route', (handler, context) => {
  return handler(context.state, context.params)
})

// notice the handler signature, it's different than
// the default one seen in above `/hello/:place` route
app.addRoute('/user/:name', (state, params) => {
  var name = state.username || params.name

  console.log('name:', name) // => 'name: john' or 'name: charlike'

  return name
})

// it returns what the route handler return
var res = app.navigate('/user/john')
console.log(res) // => 'john', because there's no passed state

var ret = app.navigate('/user/hey', { username: 'charlike '})
console.log(ret) // => 'charlike'

Notes

About "on route"

You can customize everything. By default, we call the route handler with single "context" object which contains .route, .pathname, .params and .state properties.

  • route - the route of the handler, e.g. /user/:id
  • pathname - the incoming url - 1st argument of .navigate method, e.g. /user/charlike
  • state - optional "state" for the page - 2nd argument of .navigate method, e.g. { foo: 1 }
  • params - object, containing the params of the route, e.g. { id: 'charlike' }

But instead of this you may want to pass more additional arguments to route handler or include only few of these above. To do this you can off the default .on('route') logic and provide a new logic. The listener of route event will be passed with (handler, context, el) signature. Where handler is the route handler function, context is the above context object, and el can be the "previous" returned value of the handler call (it is useful for diffing).

In above API docs have existing example, but let's try it again.

// remove the defafult
app.off('route')

Okey, let's say we want our route handlers to have (params, actions) signature. We can get the first from the "context" object, but what about "actions". Let's think of the route handler as "view", so we want to pass some actions to be done on some scenario.

Tip: This is the perfect place to plug in a virtual or real dom diffing algorithm! You definitely should try to use nanomorph here to see the magic! :)

const actions = {
  hi: (name) => alert('hi ' + name)
}

app.on('route', (handler, context) => {
  return handler(context.params, actions)
})

Now, let's define our simple view with bel, a simple DOM builder using tagged template strings.

const html = require('bel')

app.addRoute('/hello/:name', (params, actions) => {
  return html`<div>
    <h1>Hello ${params.name}</h1>
    <button onclick=${() => actions.hi(params.name)}>Click me to alert you</button>
  </div>`
})

This view just outputs one heading and a button, which when is clicked will say "hi" to different persons, based on the passed url, which in our case will be fired with .navigate method.

const res = app.navigate('/hello/charlike')

console.log(res) // => DOM element
console.log(res.toString())
// =>
// <div>
//   <h1>Hello charlike</h1>
//   <button>Click me to alert you</button>
// </div>

And because .navigate method returns what is returned value from the matched route, we can easy get the rendered page.

About routing

By default we use really simple approach for covering most common and simple cases. It is similar to what we see in Express app's routing, where :name is a placeholder for some param.

But because everything is some simple, small and pluggable, you can create another plugin that provide a different .createRoute method, for example using path-match. There's only few things that you should follow and they can be seen in the source code, it is pretty small and easy to understand.

Related

  • dush-better-use: Adds support for named plugins and better error handling, by overriding the default .use method | homepage
  • dush-methods: Plugin for dush and anything based on it. It adds helper .define and .delegate methods | homepage
  • dush-no-chaining: A plugin that removes the emitter methods chaining support for dush, base, minibase or anything based on them | homepage
  • dush-options: Adds .option, .enable and .disable methods to your dush application | homepage
  • dush-promise: Plugin for dush that makes it a Deferred promise and adds .resolve, .reject, .than and .catch methods for more better… more | homepage
  • dush-tap-report: A simple TAP report producer based on event system. A plugin for dush event emitter or anything based on it | homepage
  • dush: Microscopic & functional event emitter in ~260 bytes, extensible through plugins. | homepage
  • minibase-create-plugin: Utility for minibase and base that helps you create plugins | homepage
  • minibase-is-registered: Plugin for minibase and base, that adds isRegistered method to your application to detect if plugin is already registered and… more | homepage
  • minibase: Minimalist alternative for Base. Build complex APIs with small units called plugins. Works well with most of the already existing… more | homepage

Contributing

Pull requests and stars are always welcome. For bugs and feature requests, please create an issue.
Please read the contributing guidelines for advice on opening issues, pull requests, and coding standards.
If you need some help and can spent some cash, feel free to contact me at CodeMentor.io too.

In short: If you want to contribute to that project, please follow these things

  1. Please DO NOT edit README.md, CHANGELOG.md and .verb.md files. See "Building docs" section.
  2. Ensure anything is okey by installing the dependencies and run the tests. See "Running tests" section.
  3. Always use npm run commit to commit changes instead of git commit, because it is interactive and user-friendly. It uses commitizen behind the scenes, which follows Conventional Changelog idealogy.
  4. Do NOT bump the version in package.json. For that we use npm run release, which is standard-version and follows Conventional Changelog idealogy.

Thanks a lot! :)

Building docs

Documentation and that readme is generated using verb-generate-readme, which is a verb generator, so you need to install both of them and then run verb command like that

$ npm install verbose/verb#dev verb-generate-readme --global && verb

Please don't edit the README directly. Any changes to the readme must be made in .verb.md.

Running tests

Clone repository and run the following in that cloned directory

$ npm install && npm test

Author

Charlike Mike Reagent

License

Copyright © 2017, Charlike Mike Reagent. Released under the MIT License.


This file was generated by verb-generate-readme, v0.4.3, on April 02, 2017.
Project scaffolded using charlike cli.