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

s-route

v0.2.0

Published

Clientside router

Downloads

6

Readme

s-route

NPM version Dependencies NPM license

  • A client-side router that calls functions when a matching route is hit
  • For use with single-page applications
  • Dependency-free

The primary advantage to resolving routes to functions rather than to components is flexibility. Your route logic can be independent of the view library you use, and, because route handlers are just functions, they can be easily linked to a state management system, a logger, an animated page transition function, and probably much more. In addition, this makes it simple to handle code splitting with view code fetched on-demand.

Installation

npm install --save s-route

Using a module bundler such as rollup, webpack, or browserify you can then import s-route.

// if using an es6 transpiler, like babel or buble
import { router } from 's-route';
// if not using an es6 transpiler, use the already-transpiled UMD version instead
var router = require('s-route/bundle').router;
var setRoute = require('s-route/bundle').setRoute;

A server-side setup that routes all traffic to the same html file will also be required.

const app = express();

// ensure static files can be accessed (these are not handled by the clientside router) app.use('/public', express.static(__dirname + '/public'));

// route all other GET requests to index.html app.get('*', (req, res) => res.sendFile(__dirname + '/index.html')); app.listen(process.env.PORT || 3000);

</details>

<details>
<summary>**Setup for Firebase Hosting**</summary>
In firebase.json, assuming static files are located in `/public`

```json
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "trailingSlash": false,
    "cleanUrls": true
  }
}

Defining routes

Routes here do not correspond to specific components as they do in many clientside routers, such as react-router and vue-router. Instead, when the user navigates, s-route calls the function associated with each url.

The route map is just an object where the keys are a pattern to match against the url and the values are functions to be called when a matching url has been navigated to.

Simple example:
router({
	'/foo': () => console.log('Foo page routed'),
	'*': () => console.log('Catch-all routed')
});

When the user navigates to /foo, 'Foo page routed' will have been logged to the console. When the user navigates to /bar, 'Catch-all routed' will have been logged because no pattern in the route map corresponds to /bar.

Routes with parameters (dynamic routes):
router({
	'/messages/:id': (routeData) => console.log(routeData.params),
	'*': () => console.log('Catch-all routed')
});

Sections of a route that begin with a colon : will match any value that appears in that part of the path. For instance, in this case, /messages/:id would match any of the following routes (and many more):

  • /messages/123
  • /messages/456
  • /messages/foo

It would not match the following routes:

  • /messages
  • /messages/123/456

All route handlers are passed an object of route data with the following properties:

| Property | Type | Description | | ------------ | -------- | ------------- | | path | String | The path of the route as it appears in the address bar, eg: '/messages/123' (same as window.location.pathname) | | route | String | The route that was matched against, eg: '/messages/:id'. This will be '*' if the catch-all route was matched. | | search | String | The raw query string (same as window.location.search) | | hash | String | The raw hash (same as window.location.hash) | | params | Object | Parameters that were matched in a dynamic route, eg: { id: '123' } (empty object if none). The parameters will always be strings. |

setRoute

When routing within a single-page application, you should use the provided setRoute function rather than manually setting window.location.href or using other methods that cause a refresh. setRoute('/foo') will add /foo to the browser history and trigger the corresponding route handler without refreshing the page.

import { setRoute } from 's-route';

// after router has been initialized...

const button = document.getElementById('foo-button');
button.onclick = () => setRoute('/foo');

Query string parsing

Support for parsing query strings is not included in s-route by default, but can be imported separately.

import parseQueryString from 's-route/querystring';

// in real code, you would probably get this value from `window.location.search`
const query = '?foo=bar&baz=qux';

assert.deepEqual(parseQueryString(query), {
	foo: 'bar',
	baz: 'qux'
});

If the same key is assigned more than one value, it is assumed to be an array.

import parseQueryString from 's-route/querystring';

const query = '?foo=bar&foo=baz';

assert.deepEqual(parseQueryString(query), {
	foo: ['bar', 'baz']
});

If the key ends in [], it is also assumed to be an array.

import parseQueryString from 's-route/querystring';

const query = '?foo[]=bar';

assert.deepEqual(parseQueryString(query), {
	foo: ['bar']
});

All values are assumed to be strings, unless the value is true or false in which case it is cast to a boolean.

import parseQueryString from 's-route/querystring';

const query = '?foo=true&bar=123';

assert.deepEqual(parseQueryString(query), {
	foo: true,
	bar: '123'
});

The following nonstandard features are not supported:

  • Nested objects within query strings
  • Nested arrays within query strings
  • Arrays set using index values (for instance ?foo[0]=bar&foo[1]=baz)

Complete Example

import { router, setRoute } from 's-route';
import parseQueryString from 's-route/querystring';

function rootRouted() {
	// do something to show root page content
}

function messagesRouted() {
	// do something to show messages page content
}

function messageRouted({ path, route, params, hash, search }) {
	// should do something to show message page content here,
	// but this example shows the different properties available
	// to route handler functions
	assert.equal(path, '/messages/123');
	assert.equal(route, '/messages/:id');
	assert.deepEqual(params, { id: '123' });
	assert.equal(hash, '');
	assert.deepEqual(parseQueryString(search), { foo: 'bar' });
}

function noneRouted() {
	// handle 404
}

router({
	'/': rootRouted,
	'/messages': messagesRouted,
	'/messages/:id': messageRouted,
	'*': noneRouted
});

// this will cause the router to trigger `messageRouted()`
setRoute('/messages/123?foo=bar');

Browser support

The router relies on the history API which is supported in Internet Explorer versions 10 and above.

The code for the router is written in es6. A transpiled version is available in s-route/bundle. For developers using a transpiler anyway (or writing code for es6-ready browsers only), you need only import from s-route.

Demo

A basic demo that allows you to set the route through setRoute displays a running log of the route data available to route handlers.

Try it out here.

For reference, its router is defined as follows:

router({
	'/': log,
	'/foo': log,
	'/foo/:bar': log,
	'/foo/:bar/baz': log,
	'/foo/:bar/:baz/qux': log,
	'*': log
});