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 🙏

© 2026 – Pkg Stats / Ryan Hefner

sparouter

v3.3.0

Published

A router developed with TypeScript

Readme

Spa Router v3

A router developed with TypeScript with :

  • 2 modes : html5 history,hash
  • page animation/transition with css or js
  • active elements
  • route guards
  • child routes
  • actions

Build Status npm version

Installation

npm i sparouter -S

Workflow

With TypeScript / es6

We could use a starter kit.

With es5

Its possible. Reference the lib in the main page.

<body>
 <script src="node_modules/sparouter/dist/sparouter.js"></script>
 <script src="src/app.js"></script>
</body>
new SpaRouter.Router().map([
    { path: "/", action: function () { return SpaRouter.render({ selector: "#main", template: "<h1>Home</h1>" }); } },
    { path: "**", redirectTo: "/" }
]).run();

Router & route configs

Router config| Description -------- | -------- mode | hash (by default) and html5 history. scroll | handle navigation to fragment (true by default)

Route config | Description -------- | -------- path | the path pattern ("/posts" or "posts/:id" or "/posts/:id([a-z]+)" for example) name | route name action | an action actions | an array of actions data | extra data to pass canActivate | route guards canDeactivate | route guards redirectTo | redirect to route url children | nested routes

import  { Router } from "sparouter";

const routes = [
    { path: "/", action: () => document.querySelector("#main").innerHTML = "<h1>Home</h1>" },
    { path: "/posts", action: () => render({ selector: "#main", templateUrl: "views/posts.html" }) },
    { path: "/posts/:id", canActivate: [MyGuard], action: ({route, router}) => console.log("Activate post details") },
    { path: "**", redirectTo: "/" },
];

new Router({
    mode: "html5"
}).map(routes).run((route) => {
    // on route change success
}, (err) => {
    //  route change error ("aborted" with a guard or "notfound" if no matched route found)
});

With html5 history mode (uris without '#'), the server have to redirect to index page.

The base tag with html5 history mode allow to set the base path. Examples:

<base href="/"/>

or

<base href="http://mysite.com/blog/"/>

Param regex (number by default)

Example:

const routes =[
    { path: "/posts/:id([a-z]+)", /* etc. */ }
];

Named routes:

const routes = [
    { name: "home", path: "/", /* etc. */ },
    { name: "posts", path: "/posts", /* etc. */ }
];

Route with actions (array of functions)

const routes = [
    {
        path: "/",
        actions: [
            () => document.querySelector("#main").innerHTML = "<h1>Home</h1>",
            ({ route, router }) => console.log("Activate home", route, router),
            /* other actions */
        ]
    }
];

Its possible to pass an "action result" to the next action

const routes = [
   {
       path: "/", actions: [
           () => { return ["a", "b", "c"]; },
           ({ result, router }) => { console.log(result); }
       ]
   }
];

... Or with a promise

const routes = [
   {
       path: "/", actions: [
           () => {
               return new Promise((resolve) => {
                   resolve(["a", "b", "c"]);
               });
           },
           ({ result, router }) => { console.log(result); }
       ]
   }
];

children

const routes = [
    { path: "/", templateUrl: "src/views/home.html" },
    {
        path: "posts", 
        children: [ 
            { path: "", action: () => { /* do something */ } },
            { path: ":id", actions: [ /* do things */ ]  }
        ]
    }
];

Links

With hash mode

<a href="#/">Home</a>
<a href="#/posts">Posts</a>
<a href="#/posts/10">With parameter</a>
<a href="#/posts/10?q=news#section1">Query and fragment</a>

With to attribute: the best way to switch easilly between "hash" and "html5 history"

<a to="/">Home</a>
<a to="/posts">Posts</a>
<a to="/posts/10">With parameter</a>
<a to="/posts/10?q=news#section1">Query and fragment</a>

Active attributes

  • active-class the css class to add if active
<a href="/posts" active-class="active">Posts</a>
.active {
   color:red
 }
  • active-path allow to set a regex pattern or to add on any element ("li" for example)
<li active-path="/posts" active-class="active"></li>
  • active-exact the css class is only added if path + query + fragment equal to link href or active-path
<a href="/posts/10?q=abc#section1" active-class="active" active-exact="true">Details</a>
<!-- with active-path -->
<a href="/posts/10?q=abc#section1" active-path="/c/([a-z]+)\\?q=10#section1" active-class="active" active-exact="true">Details</a>

Navigate programmatically

Navigate by route name

router.navigateTo("home");
// with parameter
router.navigateTo("post-detail",{ id: 10});
// with query and fragment
router.navigateTo("post-detail",{ id: 10},{ q: "news" },"section1");

Navigate by url

router.navigateToUrl("/");
// with parameter
router.navigateToUrl("/posts/10");
// with query and fragment
router.navigateToUrl("/posts/10?q=news#section1");

Go back

router.goBack();

Go forward

router.goForward();

render function

Allow to render content in an HTMLElement, and create an instance of a vm and pass args.

import { render } from "sparouter";

class PostDetail {
    onActivate(route, router,scope) {
       // route with params, query, fragment and data
    }
}

const routes = [
    { path: "/posts", action: () => render({ selector: "#main", templateUrl: "views/posts.html" }) },
    { path: "/posts/:id", action: ({ route, router }) => render({ selector: "#main", templateUrl: "views/post-detail.html", vm: PostDetail, args: [route, router] }) }
];

Async await or promises

Allow to wait the end of the action before reach the next

Example with async await

function doSomething() {
    return new Promise((resolve) => {
        setTimeout(function () {
            console.log("Completed");
            resolve();
        }, 5000);
    });
}

const routes = [{ path: "/", action: async() => {
    await doSomething();
}}];

Example with promise

const routes = [{
    path: "/", action: () => {
        return new Promise((resolve) => {
            setTimeout(function () {
                console.log("Completed");
                resolve();
            }, 5000);
        });
    }
}];

Page transition

with navigate function

Animation "leave" and "enter" (could be played simultaneously)

Example simple , a slide in / slide out

const routes = [
    { path: "/", action: () => navigate({ selector: "#main", template: "<h1>Home</h1>", enter: "slideInRight", leave: "slideOutLeft" }) }
];

Other example "Shuffle" on the container and

const routes = [
    { path: "/", action: () => navigate({ selector: "#main", template: "<h1>Home</h1>", enter: "navInPrev", leave: "navOutPrev", simultaneous: true }) }
];

Before each and after each

Usefull for page animations with javaScript (SVG for example)

var router = new SpaRouter.Router().map(routes).beforeEach((next) => {
    next();
}).afterEach(() => {
    
}).run();

Route guards

class PostDetail {
    checkDeactivate() {
        return confirm("Leave this page?");
    }
}

class MyGuard implements CanActivate, CanDeactivate {
     canActivate(route, next) {
        let result = confirm("Navigate?");
        next(result);
    }

    canDeactivate(activeVms, route, next) {
        let vm = activeVms["PostDetail"];
        let result = vm && vm.checkDeactivate ? vm.checkDeactivate() : true;
        next(result);
    }
}

Example route with guard:

const routes = [
    { path:"/posts/:id", canActivate: [MyGuard], canDeactivate: [MyGuard], /* etc. */ }
]);

Or register with injector

injector.registerSecure("MyGuard",MyGuard);

const routes = [
    { path:"/posts/:id", canActivate: ["MyGuard"], /* etc. */ }
]);

injector

Allow to inject services

Example

Create and register a service

function MyService() {
    this.getAll = function () {
        // return some data
    }
}
injector.register("MyService", MyService);

Inject the service

function MyVM(myService) { }
injector.register("MyVM", MyVM, ["MyService"]);

Register a secure service (service is not returned with getInstance/ getNewInstance and cannot be removed)

injector.registerSecure("MySecureService", MySecureService);

Chaining registrations

injector
	.register("MyService1", MyService1)
	.register("MyService2", MyService2);

Get an instance (create or get a cached instance)

let instance = injector.getInstance("MyService");

Get a new instance

let instance = injector.getNewInstance("MyService");

Invoke a function with Injector

injector.invoke(myFunc);

View usefull functions

Allow to select and animate HTML elements.

import { qs, qsa } from "sparouter";

qs(".box").changeContent("<h1>New content</h1>");

qs(".box").animate("fadeIn",() => {
    // completed
});