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

pinecone-router

v4.4.1

Published

The extendable client-side router for Alpine.js.

Downloads

763

Readme

GitHub tag (latest by date) npm bundle size Downloads from JSDelivr npm Changelog Sponsor

Pinecone Router

The extendable client-side router for Alpine.js v3.

v4 update brings new features with minor breaking changes, see ChangeLog for what to update!

About

An easy to use but feature-packed client-side router for use with Alpine.js.

Features:

  • :smile: Easy and familiar syntax well integrated with Alpine.js.
  • :gear: Handler functions allow you to run code before content is displayed
  • :sparkles: Magic $router helper to access current route, params, redirect, ect. from all alpine components!
  • :beginner: Inline and external templates.
  • :link: Automatically dispatch relative links and handle them.
  • :hash: Hash routing.
  • :heavy_plus_sign: Extendable using tiny Middlewares!.

Demo: Pinecone example, (source code).

Installation

Check the CHANGELOG before updates.

CDN

Include the following <script> tag in the <head> of your document, before Alpine.js:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/router.min.js"></script>

ES6 Module on the browser:

import PineconeRouter from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/router.esm.js'
import Alpine from 'https://esm.sh/alpinejs'
Alpine.plugin(PineconeRouter)
Alpine.start()

NPM

npm install pinecone-router
// load pinecone router
import PineconeRouter from 'pinecone-router'
// then load alpine.js
import Alpine from 'alpinejs'
// add the router as a plugin
Alpine.plugin(PineconeRouter)
// start alpine
Alpine.start()

Usage

Demo & Usage Example

x-route

Declare routes by creating a template tag with the x-route directive.

<!-- inside an Alpine component -->
<template x-route="/hello/:name">
	<div>
		Hello <span x-text="$router.params.name"></span>
	</div>
</template>

See more about the $router magic helper

Route matching

Parameters can be made optional by adding a ?, or turned into a wildcard match by adding * (zero or more characters) or + (one or more characters):

<template x-route="/a/:id"></template>
<template x-route="/b/:optional?/:params?"></template>
<template x-route="/c/:remaining_path*"></template>
<template x-route="/d/:remaining_path+"></template>
<template x-route="notfound"></template>

Then you access paramaters with $router.params.X.

Borrowed from Preact Router

Note: alternatively you can use Javascript to add routes

Inline templates

If you add a child to the <template> element, Pinecone Router will render it when the route is matched. It works similair to x-if therefore they cannot be used together, use x-handler instead for conditionally showing a template.

<template x-route="/">
	<div>Hello World!</div>
</template>

In this example it will add the div with "Hello World" to the document the same way x-if does: after the template tag.

Modifiers

  • .target: Takes an ID paramater for example .target.app will render the inline template inside the element with the app ID:
<template x-route.target.app="/">
	<div>Hello World!</div>
</template>
<div id="app"></div>

Default Target ID can be set globally in settings

x-template

This directive allows you to specify an external template file fetched from a URL

<!-- when the route is matched, this will fetch the content of home.html -->
<!-- then inserts it into the page after this template element-->
<template x-route="/" x-template="/home.html"></template>

Modifiers

  • .preload: Fetches the template on page load, without waiting for the route to be matched.
  • .target: Takes an ID paramater for example .target.app will render the template inside the element with the app ID

Can be used simulateneously: x-template.preload.target.app

Default Target ID can be set globally in settings

<!-- you can preload templates without having to wait for the route to be matched-->
<template x-route="notfound" x-template.preload="/404.html"></template>

<!-- you can specify an element to display the content of the template inside -->
<template x-route="/profile/:id" x-template.target.app="/profile.html"></template>

<div id="app">
	<!-- profile.html content will be displayed here -->
</div>

x-handler

This powerful directive can be used alone or alongisde x-template, it allow you to excute one or more methods when a route is matched. This runs before inline templates and x-template allowing you to redirect before showing any content, detect implement authentication / authorization, or fetch any data you need.

<div x-data="router()">
	<!-- You can pass in a function name -->
	<template x-route="/" x-handler="home"></template>

	<!-- Or an anonymous/arrow function -->
	<template x-route="/home" x-handler="[(ctx) => ctx.redirect('/'), thisWontRun]"></template>

	<!-- Or even an array of multiple function names/anonymous functions! -->
	<template x-route="/hello/:name" x-handler="[checkName, hello]"></template>

	<!-- 404 handler -->
	<template x-route="notfound" x-handler="notfound"></template>
</div>

<div id="app"></div>

The javascript:

can also be embedded inside x-data.

function router() {
	return {
		home(context) {
			document.querySelector('#app').innerHTML = `<h1>Home</h1>`
		},
		// async functions will be automatically awaited by Pinecone Router
		async checkName(context) {
			await new Promise(resolve => setTimeout(resolve, 1000))
			if (context.params.name.toLowerCase() == 'rafik') {
				alert('we have the same name!')
			}
		},
		hello(context) {
			document.querySelector(
				'#app'
			).innerHTML = `<h1>Hello, ${context.params.name}</h1>`
		},
		notfound(context) {
			document.querySelector('#app').innerHTML = `<h1>Not Found</h1>`
		},
		thisWontRun() {
			// This function wont run because the previous function returned ctx.redirect()
			console.log("skipped!")
		}
	}
}

See Redirecting

Multiple Handlers for a single route

To prevent / stop the next handlers from executing and templates from rendering, return 'stop' from the current handler or return ctx.redirect('/some/path').

Context object / $router magic helper

Contains information about the current route. This is available at all times:

  • Using the $router magic helper in Alpine components
  • From Javascript using window.PineconeRouter.context
  • Every handler method takes the context object as the only argument

Reference:

  • $router.route (/path/:var) The route set with x-route.
  • $router.path (/path/something) The path visited by the client.
  • $router.params ({var: something}) Object that contains route parameters if any.
  • $router.hash hash fragment without the #
  • $router.query search query without the ?
  • $router.redirect(path: string) function that allow you to redirect to another page.
    • Note: usage within x-handler: return context.redirect('/path');
  • $router.navigate(path: string) same as clicking a link

Inside x-handler: context.params.id, context.route, etc

Redirecting

From an Alpine component:

Redirecting from a handler:

To redirect from inside a handler function return the context's redirect method:

This will prevent any following handlers from executing

handler(context) {
	...
	return context.redirect(path)
}

Remember: inside the handler you must return the context.redirect() function to redirect without running the next handlers.

if you wish to prevent execution of any following handlers without redirecting, use return 'stop'

Middlewares

Pinecone Router is extendable through middlewares!

Create your own middlewares using this template!

Settings:

<script>
document.addEventListener('alpine:init', () => {
	window.PineconeRouter.settings.hash = false // use hash routing
	window.PineconeRouter.settings.basePath = '/' // set the base for the URL, doesn't work with hash routing
	window.PineconeRouter.settings.templateTargetId = 'app' // Set an optional ID for where the internal & external templates will render by default.
	window.PineconeRouter.settings.interceptLinks = truefalse // Set to false to disable global handling of links by the router, see Disable link handling globally for more.

})
</script>

Advanced

Bypass link handling

Adding a native / data-native attribute to a link will prevent Pinecone Router from handling it:

<a href="/foo" native>Foo</a>

Disable link handling globally

You can set PineconeRouter.settings.interceptLinks to false to disable handling links by the router, unless an x-link attribute is set on the link, or using $router.navigate('/path').

<script>
document.addEventListener('alpine:init', () => {
	window.PineconeRouter.settings.interceptLinks = false // Set to false to disable global handling of links by the router

})
</script>

<a href="/path">This will reload the page</a>
<a href="/path" x-link>This won't reload the page</a>

Events / Loading bar

You can easily use nProgress with x-template:

document.addEventListener('pinecone-start', () => NProgress.start());
document.addEventListener('pinecone-end', () => NProgress.done());
document.addEventListener('fetch-error', (err) => console.error(err));

| name | recipient | when it is dispatched | | ------------------ | --------- | -------------------------------------------- | | pinecone-start | document | when the template start fetching | | pinecone-end | document | when the fetching ends successfuly | | fetch-error | document | when the fetching of external templates fail |

Adding and Removing routes & templates programmatically with Javascript

you can add routes & remove them anytime programmatically using Javascript.

Adding a route:

window.PineconeRouter.add(path, options)
  • path: string, the route's path.
  • options: array of options:
{handlers: [], template: '', templateTargetId: 'app'}

Note that by adding handlers this way you wont have access to the this of the alpine.js component if the handler is part of one.

Adding a template

You must set a templateTargetId in settings:

<script>
document.addEventListener('alpine:init', () => {
	window.PineconeRouter.settings.templateTargetId = 'app'
	window.PineconeRouter.add('/route', {template: '/route.html'})
	window.PineconeRouter.add('/notfound', {template: '/404.html'})
})
</script>

Note: The template won't be cleared automatically until you access another route with a template, so make sure all your routes have one if you use this method.

Removing a route:

window.PineconeRouter.remove(path)
  • path: string, the path of the route you want to remove.

Navigating from Javascript:

To navigate to another page from javascript you can use:

window.PineconeRouter.context.navigate(path)

Handling link clicks while handlers are in progress

If a user enter a link while handlers haven't finished yet, only the current one will finish while others will be canceled.

Make use of multiple handlers, for example one for fetching the data, 2nd one for redirecting if needed or displaying it.

This way if a user click a link while data is being fetched, the redirection handler wont be ran.

Compatibility

| Version | Alpine.js Version | | ------- | ----------------- | | ^v2.x | ^v3 | | v1.x | v2 |

Contributing:

Please refer to CONTRIBUTING.md

Credits

This library uses modified chunks of code from this tutorial & from page.js.

Acknowledgment

@shaun/alpinejs-router for the entire template system implementation! I copied the code and adjusted it for this router including adding the target modifier and using a separate directive for x-template instead. Code is licensed under MIT.

@KevinBatdorf for many ideas and early feedback!

Disclaimer: Not affiliated with the Alpine.js team, developed independently.

Versioning

This projects follow the Semantic Versioning guidelines.

License

Copyright (c) 2024 Rafik El Hadi Houari and contributors

Licensed under the MIT license, see LICENSE.md for details.

Pinecone Router Logo by Rafik El Hadi Houari is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Code from Page.js is licensed under the MIT License. Copyright (c) 2012 TJ Holowaychuk [email protected]

Code from Simple-javascript-router tutorial is licensed under the MIT License. Copyright (c) 2021 Vijit Ail (https://github.com/vijitail).

Route matching function from Preact Router is licensed under the MIT License. Copyright (c) 2015 Jason Miller