New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

router-dom

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

router-dom - npm Package Compare versions

Comparing version 1.2.0 to 1.2.1

src/testForm.html

5

dist/router.d.ts

@@ -6,3 +6,3 @@ export default class Router {

constructor(routes: [RouteParam, ...RouteParam[]], options?: Options);
private triggerEvent;
private doRouting;
go(path: string, state: LooseObject, params?: string): void;

@@ -37,3 +37,4 @@ removeRoute(path: string): void;

interface Options {
errorHandler?(e: Error): Promise<any> | void;
errorHandler?(err: Error, e?: PopStateEvent | Event): Promise<any> | void;
formHandler?(res: Response, e: Event): Promise<any> | void;
}

@@ -40,0 +41,0 @@ interface RoutingProps {

import { listen } from "quicklink";
import { pathToRegexp } from "path-to-regexp";
import { render, html, hydro, $ } from "hydro-js";
import { render, html, hydro, $, $$ } from "hydro-js";
listen();

@@ -13,58 +13,5 @@ let router;

}
addEventListener("popstate", async () => {
const to = location.pathname;
const from = router.oldRoute ?? to;
const route = getMatchingRoute(to);
if (route) {
try {
const [_, ...values] = to.match(route.path);
const params = Array.from(route.originalPath.matchAll(flagsRegex))
.flat()
.map((i) => i.replace(":", ""))
.reduce((state, key, idx) => {
state[key] = values[idx];
return state;
}, {});
const allParams = { ...router.getParams(), ...params };
const props = {
from: from.replace(base, ""),
to: to.replace(base, ""),
...(Object.keys(allParams).length ? { params: allParams } : {}),
...history.state,
};
// Trigger leave
if (router.oldRoute) {
const oldRoute = router.routes.find((route) => route.path.exec(router.oldRoute));
if (oldRoute) {
await oldRoute["leave" /* leave */]?.(props);
router.oldRoute = route.originalPath;
}
}
// Trigger beforeEnter
await route["beforeEnter" /* beforeEnter */]?.(props);
// Handle template / element
if (route?.templateUrl) {
const data = await fetch(route.templateUrl);
const _html = await data.text();
render(html `<div data-outlet>${_html}</div>`, outletSelector, false);
}
else if (route?.element) {
render(html `<div data-outlet>${route?.element}</div>`, outletSelector, false);
}
else {
// Clear outlet
$(outletSelector).textContent = null;
}
// Trigger afterEnter
await route["afterEnter" /* afterEnter */]?.(props);
}
catch (e) {
if (router.options.errorHandler) {
await router.options.errorHandler(e);
}
else {
console.error(e);
}
}
}
addEventListener("popstate", async (e) => {
//@ts-ignore
router.doRouting(location.pathname, e);
});

@@ -83,13 +30,71 @@ export default class Router {

router = this;
this.triggerEvent();
this.doRouting();
}
triggerEvent() {
dispatchEvent(new Event("popstate"));
async doRouting(to = location.pathname, e) {
dispatchEvent(new Event("beforeRouting"));
const from = this.oldRoute ?? to;
const route = getMatchingRoute(to);
if (route) {
try {
const [_, ...values] = to.match(route.path);
const params = Array.from(route.originalPath.matchAll(flagsRegex))
.flat()
.map((i) => i.replace(":", ""))
.reduce((state, key, idx) => {
state[key] = values[idx];
return state;
}, {});
const allParams = { ...this.getParams(), ...params };
const props = {
from: from.replace(base, ""),
to: to.replace(base, ""),
...(Object.keys(allParams).length ? { params: allParams } : {}),
...history.state,
};
// Trigger leave
if (this.oldRoute) {
const oldRoute = this.routes.find((route) => route.path.exec(this.oldRoute));
if (oldRoute) {
await oldRoute["leave" /* leave */]?.(props);
this.oldRoute = route.originalPath;
}
}
// Trigger beforeEnter
await route["beforeEnter" /* beforeEnter */]?.(props);
// Handle template / element
if (route?.templateUrl) {
const data = await fetch(route.templateUrl);
const _html = await data.text();
render(html `<div data-outlet>${_html}</div>`, outletSelector, false);
}
else if (route?.element) {
render(html `<div data-outlet>${route?.element}</div>`, outletSelector, false);
}
else {
// Clear outlet
$(outletSelector).textContent = null;
}
// Trigger afterEnter
await route["afterEnter" /* afterEnter */]?.(props);
}
catch (err) {
if (this.options.errorHandler) {
await this.options.errorHandler(err, e);
}
else {
console.error(err, e);
}
}
finally {
dispatchEvent(new Event("afterRouting"));
}
}
}
go(path, state, params = "") {
this.oldRoute = location.pathname;
const newPath = base + path + params;
// Only navigate when the path differs
if (path !== this.oldRoute) {
history.pushState({ ...state }, "", base + path + params);
this.triggerEvent();
if (newPath !== this.oldRoute) {
history.pushState({ ...state }, "", newPath);
this.doRouting(newPath);
}

@@ -144,2 +149,26 @@ }

}
function registerFormEvent(form) {
form.addEventListener("submit", (e) => {
if (!router.options.formHandler)
return;
e.preventDefault();
const action = form.getAttribute("action");
const method = form.getAttribute("method");
fetch(action, {
method,
...(!["HEAD", "GET"].includes(method.toUpperCase())
? { body: new FormData(form) }
: {}),
})
.then((res) => router.options.formHandler(res, e))
.catch(async (err) => {
if (router.options.errorHandler) {
await router.options.errorHandler(err, e);
}
else {
console.error(err, e);
}
});
});
}
function replaceBars(hydroTerm) {

@@ -151,10 +180,11 @@ if (hydroTerm === null || !hydroTerm.includes("{{"))

}
// Add EventListener for every added anchor Element
document.body.querySelectorAll("a").forEach(registerAnchorEvent);
// Add EventListener for every added anchor and form Element
$$("a").forEach(registerAnchorEvent);
$$("form").forEach(registerFormEvent);
new MutationObserver((entries) => {
for (const entry of entries) {
for (const node of entry.addedNodes) {
const anchors = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT, {
const nodes = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT, {
acceptNode(elem) {
return elem.localName === "a"
return ["form", "a"].includes(elem.localName)
? NodeFilter.FILTER_ACCEPT

@@ -164,5 +194,10 @@ : NodeFilter.FILTER_REJECT;

});
let anchor;
while ((anchor = anchors.nextNode())) {
registerAnchorEvent(anchor);
let formOrA;
while ((formOrA = nodes.nextNode())) {
if (formOrA.localName === "a") {
registerAnchorEvent(formOrA);
}
else {
registerFormEvent(formOrA);
}
}

@@ -169,0 +204,0 @@ }

4

package.json
{
"name": "router-dom",
"version": "1.2.0",
"version": "1.2.1",
"description": "A lightweight router for everyone",

@@ -40,3 +40,3 @@ "type": "module",

"dependencies": {
"hydro-js": "^1.4.0",
"hydro-js": "^1.4.1",
"path-to-regexp": "^6.2.0",

@@ -43,0 +43,0 @@ "quicklink": "^2.1.0"

@@ -9,2 +9,3 @@ # router-dom

> - base href support.
> - opt-in errorHandler and formHandler.
> - support in all modern browsers.

@@ -51,5 +52,9 @@

### Events
- window: beforeRouting & afterRouting
### Constructor
The router class takes an array with at least one entry. Only the path is mandatory. Either a template or and element will be rendered in your element with attribute `data-outlet`. The second argument is the optional object options: it can take a general errorHandler.
The router class takes an array with at least one entry. Only the path is mandatory. Either a template or and element will be rendered in your element with attribute `data-outlet`. The second argument is the optional object options: it can take a general errorHandler and a formHandler. If there is a formHandler, form submits will handled via attributes on the form element and fetch.

@@ -101,3 +106,2 @@ ```js

- Handle Form Submits
- Add nested routes
import { listen } from "quicklink";
import { pathToRegexp } from "path-to-regexp";
import { render, html, hydro, $ } from "hydro-js";
import { render, html, hydro, $, $$ } from "hydro-js";

@@ -16,66 +16,5 @@ listen();

addEventListener("popstate", async () => {
const to = location.pathname;
const from = router.oldRoute ?? to;
const route = getMatchingRoute(to);
if (route) {
try {
const [_, ...values] = to.match(route.path)!;
const params = Array.from(route.originalPath.matchAll(flagsRegex))
.flat()
.map((i) => i.replace(":", ""))
.reduce((state: LooseObject, key, idx) => {
state[key] = values[idx];
return state;
}, {});
const allParams = { ...router.getParams(), ...params };
const props = {
from: from.replace(base, ""),
to: to.replace(base, ""),
...(Object.keys(allParams).length ? { params: allParams } : {}),
...history.state,
};
// Trigger leave
if (router.oldRoute) {
const oldRoute = router.routes.find((route) =>
route.path.exec(router.oldRoute!)
);
if (oldRoute) {
await oldRoute[cycles.leave]?.(props);
router.oldRoute = route.originalPath;
}
}
// Trigger beforeEnter
await route[cycles.beforeEnter]?.(props);
// Handle template / element
if (route?.templateUrl) {
const data = await fetch(route.templateUrl);
const _html = await data.text();
render(html`<div data-outlet>${_html}</div>`, outletSelector, false);
} else if (route?.element) {
render(
html`<div data-outlet>${route?.element}</div>`,
outletSelector,
false
);
} else {
// Clear outlet
$(outletSelector)!.textContent = null;
}
// Trigger afterEnter
await route[cycles.afterEnter]?.(props);
} catch (e) {
if (router.options.errorHandler) {
await router.options.errorHandler(e);
} else {
console.error(e);
}
}
}
addEventListener("popstate", async (e) => {
//@ts-ignore
router.doRouting(location.pathname, e);
});

@@ -101,7 +40,70 @@

this.triggerEvent();
this.doRouting();
}
private triggerEvent() {
dispatchEvent(new Event("popstate"));
private async doRouting(to: string = location.pathname, e?: PopStateEvent) {
dispatchEvent(new Event("beforeRouting"));
const from = this.oldRoute ?? to;
const route = getMatchingRoute(to);
if (route) {
try {
const [_, ...values] = to.match(route.path)!;
const params = Array.from(route.originalPath.matchAll(flagsRegex))
.flat()
.map((i) => i.replace(":", ""))
.reduce((state: LooseObject, key, idx) => {
state[key] = values[idx];
return state;
}, {});
const allParams = { ...this.getParams(), ...params };
const props = {
from: from.replace(base, ""),
to: to.replace(base, ""),
...(Object.keys(allParams).length ? { params: allParams } : {}),
...history.state,
};
// Trigger leave
if (this.oldRoute) {
const oldRoute = this.routes.find((route) =>
route.path.exec(this.oldRoute!)
);
if (oldRoute) {
await oldRoute[cycles.leave]?.(props);
this.oldRoute = route.originalPath;
}
}
// Trigger beforeEnter
await route[cycles.beforeEnter]?.(props);
// Handle template / element
if (route?.templateUrl) {
const data = await fetch(route.templateUrl);
const _html = await data.text();
render(html`<div data-outlet>${_html}</div>`, outletSelector, false);
} else if (route?.element) {
render(
html`<div data-outlet>${route?.element}</div>`,
outletSelector,
false
);
} else {
// Clear outlet
$(outletSelector)!.textContent = null;
}
// Trigger afterEnter
await route[cycles.afterEnter]?.(props);
} catch (err) {
if (this.options.errorHandler) {
await this.options.errorHandler(err, e);
} else {
console.error(err, e);
}
} finally {
dispatchEvent(new Event("afterRouting"));
}
}
}

@@ -111,8 +113,8 @@

this.oldRoute = location.pathname;
const newPath = base + path + params;
// Only navigate when the path differs
if (path !== this.oldRoute) {
history.pushState({ ...state }, "", base + path + params);
this.triggerEvent();
if (newPath !== this.oldRoute) {
history.pushState({ ...state }, "", newPath);
this.doRouting(newPath);
}

@@ -179,2 +181,27 @@ }

function registerFormEvent(form: HTMLFormElement) {
form.addEventListener("submit", (e) => {
if (!router.options.formHandler) return;
e.preventDefault();
const action = form.getAttribute("action")!;
const method = form.getAttribute("method")!;
fetch(action, {
method,
...(!["HEAD", "GET"].includes(method.toUpperCase())
? { body: new FormData(form) }
: {}),
})
.then((res) => router.options.formHandler!(res, e))
.catch(async (err) => {
if (router.options.errorHandler) {
await router.options.errorHandler(err, e);
} else {
console.error(err, e);
}
});
});
}
function replaceBars(hydroTerm: string | null) {

@@ -187,21 +214,24 @@ if (hydroTerm === null || !hydroTerm.includes("{{")) return hydroTerm;

// Add EventListener for every added anchor Element
document.body.querySelectorAll("a").forEach(registerAnchorEvent);
// Add EventListener for every added anchor and form Element
$$("a").forEach(registerAnchorEvent);
$$("form").forEach(registerFormEvent);
new MutationObserver((entries) => {
for (const entry of entries) {
for (const node of entry.addedNodes) {
const anchors = document.createNodeIterator(
node,
NodeFilter.SHOW_ELEMENT,
{
acceptNode(elem: Element) {
return elem.localName === "a"
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT;
},
const nodes = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT, {
acceptNode(elem: Element) {
return ["form", "a"].includes(elem.localName)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT;
},
});
let formOrA: HTMLAnchorElement | HTMLFormElement;
while (
(formOrA = nodes.nextNode() as HTMLAnchorElement | HTMLFormElement)
) {
if (formOrA.localName === "a") {
registerAnchorEvent(formOrA as HTMLAnchorElement);
} else {
registerFormEvent(formOrA as HTMLFormElement);
}
);
let anchor;
while ((anchor = anchors.nextNode() as HTMLAnchorElement)) {
registerAnchorEvent(anchor);
}

@@ -232,3 +262,4 @@ }

interface Options {
errorHandler?(e: Error): Promise<any> | void;
errorHandler?(err: Error, e?: PopStateEvent | Event): Promise<any> | void;
formHandler?(res: Response, e: Event): Promise<any> | void;
}

@@ -235,0 +266,0 @@ interface RoutingProps {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc