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

@ardentcode/omni-router

v0.1.1

Published

Router library based on modern navigation-api

Downloads

5

Readme

Omni Router

Router library based on modern Navigation API

Build & Development

To install and build the router for production, please run following commands:

  • npm install
  • npm run build

For development, you can use:

  • npm run watch

Features

  • uses modern Navigation API
  • dependency free (except polyfills for server side)
  • framework independent
  • server side rendering
  • redirections
  • navigation aborting
  • error handling
  • link resolutions

The library uses following browser features. If you want to use it in older browsers, you should include polyfills on your own. Server side already includes the polyfills.

Concepts

Router

Router is an entity that manages application routing. It allows you to register routes, that will be automatically handled.

Route

Route is a representation of route that have name, path, params and optionally data. It can be in handled state (when data is available) or not (when data is null).

Route Declaration

Route declaration is description of how the routing should look like. It describes followings fields: name, path (pattern) and handler.

Route Handler

Route handler is a function that is executed when route is being opened. It receives params as argument and returns data, which is assigned to the route. Handlers can also be lazy loaded.

Route Processor

Route processor is an object with optional listener methods, which can be executed on specific router actions. There are 7 available listeners so far: onGetRouteStart, onGetRouteEnd, onOpenRouteStart, onOpenRouteEnd, onOpenRouteSuccess, onOpenRouteError, onOpenRouteAbort It's used for processing data returned in handlers, so that repetitive actions can be achieved for all specific routes.

Guides

Creating router

You need to define two types:

  • map with all your routes, where keys represent route names and values represent their parameters
  • structure of possible route data (returned by handler)
interface RoutesMap {
    home: HomeRouteParams;
    post: PostRouteParams;
}

interface RoutesData extends HTMLRouteData, RedirectRouteData {

}

const router = createRouter<RoutesMap, RoutesData>();

Registering routes

To register route you only need to specify three properties: name, path and handler.

router.registerRoute({
    name: 'home',
    path: '/',
    handler: async (params: HomeRouteParams) => {
        return {
            // ...
        };
    }
});

Creating handler

Route handler should only return data for further processing. It shouldn't perform any visible action to the user. Route parameters are passed as a first argument. Object containing router and abort signal is passed as a second parameter.

async function homeRouteHandler(params: HomeRouteParams, info: RouteInfo): Promise<HTMLRouteData> {
    const homeData = await homeApi.getData();
    return {
        html: {
            content: renderHomePage(homeData)
        }
    };
}

Finding the route

You can find the route by its name or path:

homeRoute = router.getRouteByName('home');
homeRoute = router.getRouteByPath('/');

Now you can use it in anchors (route is automatically serialized to its path when used as string):

<a href={homeRoute.path}/>Home</a>
<a href={homeRoute}/>Home</a>

Remember that this way you get unhandled route (without data), because data is only available after opening the route.

Opening the route

You can open the route programmatically. You will get the handled route (with data).

await router.openRouteByName('home');
await router.openRouteByPath('/');

Parameters

There are 3 types of parameters:

  • named parameters
  • query parameters
  • wildcard parameters

All parameters are strings. If you want to use them as numbers, remember to parse them firstly.

Named parameters are parameters that are defined in path pattern with ":" prefix:

interface PostsParams {
    page: string;
}

router.registerRoute({
    name: 'posts',
    path: '/posts/:page',
    handler: /* ... */
});

router.getRouteByName('posts', {page: '1'});
// {name: 'posts', path: '/posts/1', params: {page: '1'}}

Query parameters are additional parameters which are not defined in path pattern:

interface PostsParams {
    page: string;
}

router.registerRoute({
    name: 'posts',
    path: '/posts',
    handler: /* ... */
});

router.getRouteByName('posts', {page: '1'});
// {name: 'posts', path: '/posts?page=1', params: {page: '1'}}

Wildcard parameters are like named parameters, but instead of a name, they have just a number assigned (starting with 0):

interface PostsParams {
    page: string;
}

router.registerRoute({
    name: 'posts',
    path: '/posts/*',
    handler: /* ... */
});

router.getRouteByName('posts', {0: '1'});
// {name: 'posts', path: '/posts/1', params: {page: '1'}}

You can define a fallback page (404 not found) with wildcard, but you have to register it as the last one:

router.registerRoute({
    name: 'not-found',
    path: '/*',
    handler: /* ... */
});

HTML processor

HTML processor injects HTML into specified elements. By default, there is content property, which is injected into element with id rootId. You can also define additional fragments, which you want to inject HTML into. Please check the examples:


<body>
    <header id="app-header"></header>
    <main id="app-main"></main>
</body>
createHTMLRouteProcessor({
    rootId: 'app-main',
    fragmentsIds: {
        header: 'app-header'
    },
    renderHTML: (html) => `<div class="wrapper>{html}</div>`,
    renderError: (error) => `<div class="error">Error</div>`
})
async function homeRouteHandler(params: HomeRouteParams): Promise<HTMLRouteData> {
    return {
        html: {
            content: `<p>Hello World!</p>`,
            fragments: {
                header: `Home`
            }
        }
    };
}

Redirect processor

Redirect processor opens new route with history mode replace, so previous page is not available in browser history.

createRedirectRouteProcessor()
async function firstPostRouteHandler(): Promise<RedirectRouteData> {
    const firstPost = await postApi.getPosts({limit: 1})[0];
    return {
        redirect: {
            path: '/post/' + firstPost.id
        }
    };
}

Meta processor

Meta processor sets page metadata like title, description, keywords, etc.

createMetaRouteProcessor({
    modifier: (name, content) => {
        if (name === 'title') {
            return `${content} - MyApp`;
        }
        return content;
    },
    defaults: {
        title: 'MyApp'
    }
})
async function homeRouteHandler(params: HomeRouteParams): Promise<MetaRouteData> {
    return {
        meta: {
            title: 'Home Page',
            description: 'Home Page of MyApp'
        }
    };
}

Custom processor

You can create you own processors. There are 7 available methods so far:

  • onGetRouteStart
  • onGetRouteEnd
  • onOpenRouteStart
  • onOpenRouteEnd
  • onOpenRouteSuccess
  • onOpenRouteError
  • onOpenRouteAbort
interface BodyClassRouteData {
    bodyClass: string;
}

function createBodyClassRouteProcessor(): RouteProcessor<BodyClassRouteData> {
    return {
        onOpenRouteSuccess: async ({route}) => {
            document.body.className = route.data.bodyClass ?? '';
        }
    };
}
async function homeRouteHandler(params: HomeRouteParams): Promise<BodyClassRouteData> {
    return {
        bodyClass: 'home'
    };
}