npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

easyrouter

v2.0.2

Published

The tiny, fast, easy, yet powerful hash router in JavaScript

Downloads

25

Readme

EasyRouter

npm Version License Build Status Coverage Bundle Size

The tiny, fast, easy, yet powerful hash router in JavaScript.

  • About 3K minified, 1.4K gzipped
  • No performance drop as you add routes
  • Order of route declaration doesn't matter: the most specific route wins
  • Parses query strings (see note)
  • TypeScript v3 definitions
  • Zero dependencies

NOTE

For easyRoute to recognize query strings, the query must follow the hash (be part of it). This is not standard and the assignment through the href property will not work, you must assign the location directly or use setAttribute.

Example:

location.hash = `/customers/1?order=${orderNo}`

// or...
const anchor = document.getElementById('anchor-id')
anchor.setAttribute('href', `#/customers/1?order=${orderNo}`)

Changes in v2.0.0

The most important change is the renaming of the methods enter and exit of the router (now onEnter and onExit), and the removal of the alias concat.

From v2.0 the hash is not case sensitive, except in the parameters' names and values.

Please see the Changelog for more information.

Install

npm

npm install easyrouter --save
# or
yarn add easyrouter

Bower

bower install easyrouter --save

In the browser

<script src="https://unpkg.com/easyrouter/easyrouter.min.js"></script>

The folder dist has tree builds:

Filename | Description ----------------- | ----------- easyrouter.js | CommonJS build for node, browserify, brunch, webpack, etc. easyrouter.es.js | ESM build for rollup or other module capable blunders. easyrouter.umd.js | Generic UMD build

In the root is easyrouter.min.js, a minified UMD version for browsers that stores the router instance in the global variable window.router.

Usage

// Require the router if using brunch, browserify, webpack, etc.
const router = require('easyrouter')

// handler for '#/login', defined by the `enter` method of the route.
const login = () => {
  // Here you can, by example, show a popup or change all the content
  // of the page.
  console.log('Login')
}

// This function handles routes for two rules, one of them has a placeholder
// ':id' whose value will be extracted from the hash that enters the route
// and placed in the property 'id' of 'params'.
// 'title' is a custom property of the route context defined by us.
//
// NOTE the use of the 'function' keyword, since we are accessing `this`,
//      we should not use an arrow function here.
//
const resourceEditor = function (params) {
  $('#header').html(this.title)

  // IMPORTANT: Parameter values are of type 'string', always.
  if (params.id) {
    console.log(`Editing the resource ${params.id}`)
  } else {
    console.log(`Creating a new resource.`)
  }
}

// Data for the routes.
// The 'path' property defines the rule and is the only required property.
// 'title' is a custom property and will be part of the route context.
// 'enter' methods here takes precedence over the callback passed to the
// router `add` function.
const data = [
  {
    path: '#/resources',
    title: 'Resources'
  }, {
    path: '#/resource/:id',
    title: 'Edit resource',
    enter: resourceEditor
  }, {
    path: '#/resource/new',
    title: 'New resource',
    enter: resourceEditor
  }, {
    path: '#/resource/ext/*',  // the '*' allows hashes that start with
    title: 'Other resource'    // '#/resources/ext', the complete hash can
  }, {                         // be obtained from the context.
    path: '#/login',
    enter: login
  }
]

// Configure and start the router.
// This is a singleton that maintains its state between hash changes.
// You can reset it completely with the `reset` method.
router
  // The `add` method adds routes without eliminating the previous ones.
  // Its additional parameter is the default `enter` method for the routes
  // that are added.
  .add(data, function (params) {
    // The `enter` method is executed in the context of the current route
    // and receives a parameter with values in the current hash.
    console.log(this.hash, params)
  })
  .onExit((route) => {
    // global callback called on exit a previous route
    console.log(`Leaving ${route.hash}`)
  })
  .onEnter((route) => {
    // global callback called before run the current route
    console.log(`Entering ${route.hash}`)
  })
  .rescue((hash) => {
    // executed for non-existing routes or routes without `enter` method
    location.href = 'errors/404.html'
  })
  // starts the router using "#/login" for users that arrives to this
  // page without a hash.
  .listen('#login')

API

add(data [, callback]) ⇒ router

Registers one or more routes.

The received data is used as a template to generate the routes.

Parameter | Description --------- | ----------- data | Can be an object or array of objects, its property path specifies the rule. callback | Optional enter method for routes without one.

Note: The alias concat was removed in v2.0

clear() ⇒ router

Empties the routes.

The global callbacks (onEnter, onExit, rescue) are preserved.

getContext() ⇒ context

Returns the context of the router. It includes the last saved route or null if there's no such route (like after a reset).

The object returned by getContext has this properties:

Property | Type | Description --------- | -------- | ----------- isActive | boolean | Is the router handling hash changes? lastHash | string | Last handled hash. lastRoute | Object | Last handled route, including hash and parameters. prevRoute | Object | Previous handled route, including hash and parameters. onEnter | Function | Global callback registered by the onEnter method. onExit | Function | Global callback registered by the onExit method. rescue | Function | Fallback function registered by the rescue method.

listen(root) ⇒ router

Start handling hash changes.

root is the hash for URLs without a defined path to which a user will be redirected.

This route will be automatically selected by this method unless the page already has a hash.

match(hash) ⇒ route context

Returns an object with the route for a matching hash.

The route includes the parameters given in the hash.

navigate(hash [, force]) ⇒ router

Goes to the given hash.

If force is true, the route and global callbacks run even if the hash is the current.

onEnter(callback) ⇒ router

Set the global callback executed whenever the hash changes, after the route.query, route.exit and router.onExit methods.

The first parameter received by the callback is an object with the new route data, and the router as the default context (this).

The second parameter is the previous route, if any.

NOTE:

This callback will be called even if there's no match for the next hash or the new location has no hash. In this cases the parameter passed to the callback will be null. In the last case it will be called once, when the hash is removed.

onExit(callback) ⇒ router

Set the global callback executed whenever the hash changes, before the route.enter, router.onEnter and router.rescue methods.

The first parameter received by the callback is an object with the previous route data, and the router as the default context (this).

The second one is the new route data.

NOTE:

This callback will be called even if there's no match for the previous hash or the previous location has no hash. In this cases the parameter passed to the callback will be null.

rescue(callback) ⇒ router

Set the global callback called with the current hash when no rule matches the hash or the route has no an enter method.

If you do not provide a rescue method, the router will set one to redirect your users to the "root" defined by the listen method.

This lets you provide instant user feedback if they click an undefined route.

reset() ⇒ router

Clears the registered routes and global callbacks, without stopping the router.

Generally, this method will be followed by stop or by a re-initialization.

route(rule) ⇒ route object

Returns the route object assigned to the given rule.

The parameter is the rule used to register a route, it is not the hash of the current location.

The returned route does not includes the hash nor parameters values.

stop() ⇒ router

Stops the router.

Any routes and global callbacks are preserved.

You will need to call listen to re-enable the router.


Route Instances

A route is un plain object with the following propertites:

Property | Type | Description -------- | -------- | ----------- path | string | Rule with the path and parameter markers query | function | Called only when the query-string changes for a **same hash** exit | function | Called before call the enter and onEnter methods enter | function | Called when the hash changes, with the parameters of the current hash hash | string | Matched hash (normalized) params | object | Object with the parameter names and values extracted from the hash.Note: All the parameter values are strings.

When passing route templates to the router add method, you can omit anything except path.

Any custom properties are copied to the route, so it can be accessed through this within the query, enter and exit methods of the route.

Event Pipeline

This is the order of event when on hash changes:

              efective hash change
                       |
                       v
      (if only the query-string was changed)
            oldRoute.query(newParams)
(abort and back to previous hash if it returns `false`)
                       |
                       v
           (if there's an old route)
            oldRoute.exit(oldParams)
  (abort without go back, if it returns `false`)
                       |
                       v
        router.onExit(oldRoute, newRoute)
  (called with undefined if there's no old route)
                       |
                       v
  (here, the router grab the change, so entering
    again this hash will no repeat the process)
                       |
                       v
        router.onEnter(newRoute, oldRoute)
                       |
                       v
 there's a match with an `enter` method for the hash?
                       |
          +------------+-------------+
          |                          |
          v                          v
         Yes                         No
newRoute.enter(params)       router.rescue(hash)

All this work is done after the hash is set in the browser address-bar, so returning false from the exit method does not reset this, but in the case of query the router will emit navigate(prevRoute.hash) to restore the previous route.

Also, the query and exit methods are called with the old route as context (this), and the enter method with the new one.

These routes have two additional properties: hash, a string with the normalized value of the matched hash, and params, an object that contains the parameter values parsed from the hash as strings.

This is an example showing an async modal dialog if a leaving form has changes:

function oldRouteExit () {
  if ($('form').hasClass('dirty')) {
    App.ui.confirm('Are you sure to cancel your edition?')
      .then((button) => {
        if (button === App.ui.YES) {
          router.navigate(location.hash)
        }
      })
    router.navigate(this.hash) // not required if used in `query`.
    return false
  }
}

TODO

  • [ ] Rules with optional parameters
  • [ ] CI test for more browsers
  • [ ] Enhanced documentation

Support my Work

I'm a full-stack developer with more than 20 year of experience and I try to share most of my work for free and help others, but this takes a significant amount of time and effort so, if you like my work, please consider...

Of course, feedback, PRs, and stars are also welcome 🙃

Thanks for your support!

License

The MIT License (MIT)