{"id":21754631,"url":"https://github.com/aegisjsproject/router","last_synced_at":"2026-02-08T19:24:51.552Z","repository":{"id":257826476,"uuid":"872152564","full_name":"AegisJSProject/router","owner":"AegisJSProject","description":"A simple router module","archived":false,"fork":false,"pushed_at":"2025-04-10T15:31:56.000Z","size":872,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-13T09:08:29.113Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@aegisjsproject/router","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AegisJSProject.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"shgysk8zer0","liberapay":"shgysk8zer0"}},"created_at":"2024-10-13T22:59:50.000Z","updated_at":"2025-04-10T15:31:58.000Z","dependencies_parsed_at":"2024-12-22T22:23:44.726Z","dependency_job_id":"7a4a01b7-869c-49db-92fd-3ec27d8b81bb","html_url":"https://github.com/AegisJSProject/router","commit_stats":null,"previous_names":["aegisjsproject/router"],"tags_count":16,"template":false,"template_full_name":"shgysk8zer0/npm-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AegisJSProject%2Frouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AegisJSProject%2Frouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AegisJSProject%2Frouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AegisJSProject%2Frouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AegisJSProject","download_url":"https://codeload.github.com/AegisJSProject/router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248688565,"owners_count":21145766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-26T09:14:32.375Z","updated_at":"2026-02-08T19:24:50.835Z","avatar_url":"https://github.com/AegisJSProject.png","language":"JavaScript","funding_links":["https://github.com/sponsors/shgysk8zer0","https://liberapay.com/shgysk8zer0","https://liberapay.com/shgysk8zer0/donate"],"categories":[],"sub_categories":[],"readme":"# `@aegisjsproject/router`\n\nA simple but powerful router module\n\n[![CodeQL](https://github.com/AegisJSProject/router/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/AegisJSProject/router/actions/workflows/codeql-analysis.yml)\n![Node CI](https://github.com/AegisJSProject/router/workflows/Node%20CI/badge.svg)\n![Lint Code Base](https://github.com/AegisJSProject/router/workflows/Lint%20Code%20Base/badge.svg)\n\n[![GitHub license](https://img.shields.io/github/license/AegisJSProject/router.svg)](https://github.com/AegisJSProject/router/blob/master/LICENSE)\n[![GitHub last commit](https://img.shields.io/github/last-commit/AegisJSProject/router.svg)](https://github.com/AegisJSProject/router/commits/master)\n[![GitHub release](https://img.shields.io/github/release/AegisJSProject/router?logo=github)](https://github.com/AegisJSProject/router/releases)\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/shgysk8zer0?logo=github)](https://github.com/sponsors/shgysk8zer0)\n\n[![npm](https://img.shields.io/npm/v/@aegisjsproject/router)](https://www.npmjs.com/package/@aegisjsproject/router)\n![node-current](https://img.shields.io/node/v/@aegisjsproject/router)\n![npm bundle size gzipped](https://img.shields.io/bundlephobia/minzip/@aegisjsproject/router)\n[![npm](https://img.shields.io/npm/dw/@aegisjsproject/router?logo=npm)](https://www.npmjs.com/package/@aegisjsproject/router)\n\n[![GitHub followers](https://img.shields.io/github/followers/shgysk8zer0.svg?style=social)](https://github.com/shgysk8zer0)\n![GitHub forks](https://img.shields.io/github/forks/AegisJSProject/router.svg?style=social)\n![GitHub stars](https://img.shields.io/github/stars/AegisJSProject/router.svg?style=social)\n[![Twitter Follow](https://img.shields.io/twitter/follow/shgysk8zer0.svg?style=social)](https://twitter.com/shgysk8zer0)\n\n[![Donate using Liberapay](https://img.shields.io/liberapay/receives/shgysk8zer0.svg?logo=liberapay)](https://liberapay.com/shgysk8zer0/donate \"Donate using Liberapay\")\n- - -\n\n- [Code of Conduct](./.github/CODE_OF_CONDUCT.md)\n- [Contributing](./.github/CONTRIBUTING.md)\n\u003c!-- - [Security Policy](./.github/SECURITY.md) --\u003e\n\n\u003e [!CRITICAL]\n\u003e This package requires [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) to be polyfilled *before*\n\u003e any paths are registered. A common polyfill recommended by MDN can be [found here](https://github.com/kenchris/urlpattern-polyfill).\n\n## Installation\n```bash\nnpm install @aegisjsproject/router\n```\n\n## CDN and importmap\nYou do not even need to `npm install` this or use any build process. You may either import it directly from a CDN\nor use an [importmap](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap).\n\n```html\n\u003cscript type=\"importmap\"\u003e\n{\n  \"imports\": {\n    \"@aegisjsproject/router\": \"https://unpkg.com/@aegisjsproject/router[@version]/router.mjs\",\n    \"@aegisjsproject/state\": \"https://unpkg.com/@aegisjsproject/state[@version]/state.mjs\"\n  }\n}\n\u003c/script\u003e\n```\n\n## Advantages of `@aegisjsproject/router`\n\n* **Lightweight and Efficient:** The library is designed to be small and performant, with a focus on efficient URL matching and module loading.\n* **Dynamic Loading:** Modules are loaded on-demand, improving initial page load performance and reducing resource usage.\n* **Flexible Exports:** Supports a variety of module exports, including custom elements, functions, and HTML structures, making it adaptable to different UI architectures.\n* **Component Injection:** Automatically injects relevant URL information and state into registered components, simplifying component development and data management.\n* **History Integration:** Seamlessly manages browser history, allowing users to navigate back and forward without reloading the entire page.\n* **Error Handling:** Provides built-in error handling mechanisms to gracefully handle unexpected situations during module loading or navigation.\n* **Customizable:** Offers flexibility for customization, allowing you to tailor the router's behavior to your specific project requirements.\n* **Easy to Use:** The library provides a simple and intuitive API, making it easy to learn and integrate into your projects.\n\n## Fundamentals\n\nAt its core, this package matches URLs matching a [`URLPattern`](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API#pattern_syntax)\nto modules to be dynamically imported. This yields a powerful but minimal package size, dynamic\nloading of \"View\"s as-needed, high reusability of code, and potentially nearly instant navigations,\nespecially when used in conjunction with service workers and caches. Just create a script that has a\n`default` export that is a `Document`, `DocumentFragment`, `HTMLElement` and especially a custom element\nor web component, and map the `URLPattern`s to their respective modules.\n\n## Example\n\n```js\nimport { init, navigate, back, forward, reload } from '@aegisjsproject/router';\n\ninit({\n  '/product/:productId': '@scope/views/product.js',\n  '/test/': '@scope/views/home.js',\n  '/search?q=:query': '@scope/views/search.js',\n  '/img': '/views/img.js',\n  '/path/page-:page(\\\\d+)': '@scope/foo.js',\n}, {\n  preload: true, // Preload all registered modules\n  notFound: './views/404.js', // Set custom 404 module\n  rootNode: '#root', // Declares element for base of content updates\n  interceptRoot: document.body, // Use `MutationObserver` to observer `\u003ca\u003e` elements and intercept navigations\n  signal: controller.signal, // An `AbortSignal`, should you want to disable routing funcitonality\n});\n\ndocument.querySelectorAll('[data-link]').forEach(el =\u003e {\n  el.addEventListener('click', ({ currentTarget }) =\u003e {\n    const { link, ...state } = currentTarget.dataset;\n    navigate(link, state);\n  });\n});\n\ndocument.querySelectorAll('[data-nav]').forEach(el =\u003e {\n  el.addEventListener('click', ({ currentTarget }) =\u003e {\n    switch (currentTarget.dataset.nav) {\n      case 'back':\n        back();\n        break;\n\n      case 'forward':\n        forward();\n        break;\n\n      case 'reload':\n        reload();\n\n      default:\n        throw new TypeError(`Invalid nav button type: ${currentTarget.dataset.nav}.`);\n    }\n  });\n});\n```\n\n## Registering Paths\nAt the core, this router module just uses `URLPattern`s in a map, mapped to a source for a module. When a URL\nis navigated to, it finds the pattern that the URL matches, dynamically imports that module, and passes the\ncurrent state and URL and the results of `urlPattern.exec(url)` to the function or constructor.\n\nYou may register paths via either `registerPath()` or through an object given to the `init()` function. `registerPath()`\nallows for the use of `new URLPattern()` to be used, but as `init()` requires an object, its keys must be strings\nto be converted into `URLPattern` through `new URLPattern(key, moduleSrc)`.\n\n## Handling Navigation\nIf you call the `init()` function, the `popstate` listener will be added automatically and the module for the\ncurrent page will be loaded. Should you want more manual loading, you may also call `addListener()` on your own.\n\nThere is also a `MutationObserver` that adds `click` event handlers to intercept clicks on same-origin `\u003ca\u003e`s.\nThis observer watches for `\u003ca\u003e`s in the children of what it is set to observe and calls `event.preventDefault()`\nto avoid the default navigation, then calls `navigate(a.href)`.\n\n\u003e [!NOTE]\n\u003e While the `MutationObserver` automatically adds the necessary click handlers on all `\u003ca\u003e` and `\u003cform\u003e` elements under its\n\u003e root, it cannot reach into Shadow DOM. For any web component with shadow, you should call `interceptNav(shadow)`\n\u003e in either the constructor or `connectedCallback`.\n\n## Cleanup\nFor all \"Views\"/modules that export a function or constructor, they are given an `AbortSignal` which is aborted\non any navigation. This can and should be used for any necessary cleanup/freeing up memory, such as aborting\nany pending requests and removing event listeners.\n\n## 404 Pages\nYou can register a module for 404 pages using either `set404()` or by passing it via `{ notFound }` in `init()`.\nThis component or function will be given the current state and URL and can be dynamically generated.\n\n## Preloading\nYou can preload modules for views by using `preloadModule()` or by passing `{ preload: true }` in `init()`.\nPreloading modules will make navigation effectively instant and potentially without network traffic, however\nit will increase initial load times (though it defaults to a low priority).\n\n\u003e [!IMPORTANT]\n\u003e Be advised that there may be a functional difference between using the router in the context of a `\u003cscript type=\"module\"\u003e`\n\u003e vs as a non-module, namely in the availability of [`import.meta.resolve()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve)\n\u003e for preloading. Also, that importmaps are not quite univerally supported yet. For best compatibility,\n\u003e you **SHOULD** use either absolute or relative URLs when declaring modules for routes, though use of\n\u003e module specifiers (e.g. `@scope/package`) is supported in certain contexts, with decent browser support.\n\n## State Management\nThis currently uses [`@aegisjsproject/state`](https://npmjs.com/package/@aegisjsproject/state) for state\nmangement. It is a lightweight wrapper around `history.state` that uses `BroadcastChannel` to sync state\nchanges between tabs/windows. It should be noted that this is *global* state and not specific to some component,\nso please avoid generic names and be aware of the global nature.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faegisjsproject%2Frouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faegisjsproject%2Frouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faegisjsproject%2Frouter/lists"}