Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@solidjs/router

Package Overview
Dependencies
Maintainers
3
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solidjs/router - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

9

dist/components.d.ts
import type { Component, JSX } from "solid-js";
import type { Location, LocationChangeSignal, Navigator, RouteDataFunc, RouteDefinition, RouterIntegration } from "./types";
import type { Location, LocationChangeSignal, MatchFilters, Navigator, RouteDataFunc, RouteDefinition, RouterIntegration } from "./types";
declare module "solid-js" {

@@ -32,6 +32,7 @@ namespace JSX {

export declare const useRoutes: (routes: RouteDefinition | RouteDefinition[], base?: string) => () => JSX.Element;
export type RouteProps = {
path: string | string[];
export type RouteProps<S extends string> = {
path: S | S[];
children?: JSX.Element;
data?: RouteDataFunc;
matchFilters?: MatchFilters<S>;
} & ({

@@ -45,3 +46,3 @@ element?: never;

});
export declare const Route: (props: RouteProps) => JSX.Element;
export declare const Route: <S extends string>(props: RouteProps<S>) => JSX.Element;
export declare const Outlet: () => JSX.Element;

@@ -48,0 +49,0 @@ export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {

@@ -6,3 +6,3 @@ /*@refresh skip*/

import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj, useHref, useLocation, useNavigate, useResolvedPath, useRoute, useRouter } from "./routing";
import { joinPaths, normalizePath } from "./utils";
import { joinPaths, normalizePath, createMemoObject } from "./utils";
export const Router = (props) => {

@@ -20,2 +20,10 @@ const { source, url, base, data, out } = props;

const matches = createMemo(() => getRouteMatches(branches(), router.location.pathname));
const params = createMemoObject(() => {
const m = matches();
const params = {};
for (let i = 0; i < m.length; i++) {
Object.assign(params, m[i].params);
}
return params;
});
if (router.out) {

@@ -47,3 +55,3 @@ router.out.matches.push(matches().map(({ route, path, params }) => ({

disposers[i] = dispose;
next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i]);
next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i], params);
});

@@ -82,3 +90,10 @@ }

props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
const [, rest] = splitProps(props, [
"href",
"state",
"class",
"activeClass",
"inactiveClass",
"end"
]);
const to = useResolvedPath(() => props.href);

@@ -85,0 +100,0 @@ const href = useHref(to);

@@ -188,3 +188,3 @@ import { isServer, delegateEvents, createComponent as createComponent$1, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';

}
function createMatcher(path, partial) {
function createMatcher(path, partial, matchFilters) {
const [pattern, splat] = path.split("/*", 2);

@@ -203,10 +203,10 @@ const segments = pattern.split("/").filter(Boolean);

};
const matchFilter = s => matchFilters === undefined ? undefined : matchFilters[s];
for (let i = 0; i < len; i++) {
const segment = segments[i];
const locSegment = locSegments[i];
if (segment[0] === ":") {
match.params[segment.slice(1)] = locSegment;
} else if (segment.localeCompare(locSegment, undefined, {
sensitivity: "base"
}) !== 0) {
const key = segment[0] === ":" ? segment.slice(1) : segment;
if (segment[0] === ":" && matchSegment(locSegment, matchFilter(key))) {
match.params[key] = locSegment;
} else if (!matchSegment(locSegment, segment)) {
return null;

@@ -217,3 +217,8 @@ }

if (splat) {
match.params[splat] = lenDiff ? locSegments.slice(-lenDiff).join("/") : "";
const remainder = lenDiff ? locSegments.slice(-lenDiff).join("/") : "";
if (matchSegment(remainder, matchFilter(splat))) {
match.params[splat] = remainder;
} else {
return null;
}
}

@@ -223,2 +228,19 @@ return match;

}
function matchSegment(input, filter) {
const isEqual = s => s.localeCompare(input, undefined, {
sensitivity: "base"
}) === 0;
if (filter === undefined) {
return true;
} else if (typeof filter === "string") {
return isEqual(filter);
} else if (typeof filter === "function") {
return filter(input);
} else if (Array.isArray(filter)) {
return filter.some(isEqual);
} else if (filter instanceof RegExp) {
return filter.test(input);
}
return false;
}
function scoreRoute(route) {

@@ -301,5 +323,5 @@ const [pattern, splat] = route.pattern.split("/*", 2);

const useIsRouting = () => useRouter().isRouting;
const useMatch = path => {
const useMatch = (path, matchFilters) => {
const location = useLocation();
const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path)));
const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
return createMemo(() => {

@@ -361,3 +383,3 @@ for (const matcher of matchers()) {

pattern,
matcher: createMatcher(pattern, !isLeaf)
matcher: createMatcher(pattern, !isLeaf, routeDef.matchFilters)
});

@@ -651,3 +673,3 @@ }

}
function createRouteContext(router, parent, child, match) {
function createRouteContext(router, parent, child, match, params) {
const {

@@ -665,3 +687,2 @@ base,

const path = createMemo(() => match().path);
const params = createMemoObject(() => match().params);
preload && preload();

@@ -724,2 +745,10 @@ const route = {

const matches = createMemo(() => getRouteMatches(branches(), router.location.pathname));
const params = createMemoObject(() => {
const m = matches();
const params = {};
for (let i = 0; i < m.length; i++) {
Object.assign(params, m[i].params);
}
return params;
});
if (router.out) {

@@ -754,3 +783,3 @@ router.out.matches.push(matches().map(({

disposers[i] = dispose;
next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i]);
next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i], params);
});

@@ -757,0 +786,0 @@ }

import type { Component, Accessor } from "solid-js";
import type { BeforeLeaveEventArgs, Branch, Location, LocationChangeSignal, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDataFunc, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types";
import type { BeforeLeaveEventArgs, Branch, Location, LocationChangeSignal, MatchFilters, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDataFunc, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types";
export declare const RouterContextObj: import("solid-js").Context<RouterContext | undefined>;

@@ -12,3 +12,3 @@ export declare const RouteContextObj: import("solid-js").Context<RouteContext | undefined>;

export declare const useIsRouting: () => () => boolean;
export declare const useMatch: (path: () => string) => Accessor<import("./types").PathMatch | undefined>;
export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types").PathMatch | undefined>;
export declare const useParams: <T extends Params>() => T;

@@ -25,3 +25,3 @@ type MaybeReturnType<T> = T extends (...args: any) => infer R ? R : T;

export declare function createRouterContext(integration?: RouterIntegration | LocationChangeSignal, base?: string, data?: RouteDataFunc, out?: object): RouterContext;
export declare function createRouteContext(router: RouterContext, parent: RouteContext, child: () => RouteContext, match: () => RouteMatch): RouteContext;
export declare function createRouteContext(router: RouterContext, parent: RouteContext, child: () => RouteContext, match: () => RouteMatch, params: Params): RouteContext;
export {};

@@ -26,5 +26,5 @@ import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js";

export const useIsRouting = () => useRouter().isRouting;
export const useMatch = (path) => {
export const useMatch = (path, matchFilters) => {
const location = useLocation();
const matchers = createMemo(() => expandOptionals(path()).map((path) => createMatcher(path)));
const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
return createMemo(() => {

@@ -45,3 +45,7 @@ for (const matcher of matchers()) {

const searchString = untrack(() => mergeSearchString(location.search, params));
navigate(location.pathname + searchString + location.hash, { scroll: false, resolve: false, ...options });
navigate(location.pathname + searchString + location.hash, {
scroll: false,
resolve: false,
...options
});
};

@@ -51,3 +55,7 @@ return [location.query, setSearchParams];

export const useBeforeLeave = (listener) => {
const s = useRouter().beforeLeave.subscribe({ listener, location: useLocation(), navigate: useNavigate() });
const s = useRouter().beforeLeave.subscribe({
listener,
location: useLocation(),
navigate: useNavigate()
});
onCleanup(s);

@@ -81,3 +89,3 @@ };

pattern,
matcher: createMatcher(pattern, !isLeaf)
matcher: createMatcher(pattern, !isLeaf, routeDef.matchFilters)
});

@@ -370,7 +378,6 @@ }

}
export function createRouteContext(router, parent, child, match) {
export function createRouteContext(router, parent, child, match, params) {
const { base, location, navigatorFactory } = router;
const { pattern, element: outlet, preload, data } = match().route;
const path = createMemo(() => match().path);
const params = createMemoObject(() => match().params);
preload && preload();

@@ -377,0 +384,0 @@ const route = {

@@ -43,4 +43,5 @@ import { Component, JSX } from "solid-js";

export type RouteDataFunc<T = unknown, R = unknown> = (args: RouteDataFuncArgs<T>) => R;
export type RouteDefinition = {
path: string | string[];
export type RouteDefinition<S extends string | string[] = any> = {
path: S;
matchFilters?: MatchFilters<S>;
data?: RouteDataFunc;

@@ -56,2 +57,7 @@ children?: RouteDefinition | RouteDefinition[];

});
export type MatchFilter = string[] | RegExp | ((s: string) => boolean);
export type PathParams<P extends string | readonly string[]> = P extends `${infer Head}/${infer Tail}` ? [...PathParams<Head>, ...PathParams<Tail>] : P extends `:${infer S}?` ? [S] : P extends `:${infer S}` ? [S] : P extends `*${infer S}` ? [S] : [];
export type MatchFilters<P extends string | readonly string[] = any> = P extends string ? {
[K in PathParams<P>[number]]?: MatchFilter;
} : Record<string, MatchFilter>;
export interface PathMatch {

@@ -78,2 +84,3 @@ params: Params;

matcher: (location: string) => PathMatch | null;
matchFilters?: MatchFilters;
}

@@ -80,0 +87,0 @@ export interface Branch {

@@ -1,2 +0,2 @@

import type { Params, PathMatch, Route, SetParams } from "./types";
import type { MatchFilters, Params, PathMatch, Route, SetParams } from "./types";
export declare function normalizePath(path: string, omitSlash?: boolean): string;

@@ -7,3 +7,3 @@ export declare function resolvePath(base: string, path: string, from?: string): string | undefined;

export declare function extractSearchParams(url: URL): Params;
export declare function createMatcher(path: string, partial?: boolean): (location: string) => PathMatch | null;
export declare function createMatcher<S extends string>(path: S, partial?: boolean, matchFilters?: MatchFilters<S>): (location: string) => PathMatch | null;
export declare function scoreRoute(route: Route): number;

@@ -10,0 +10,0 @@ export declare function createMemoObject<T extends Record<string | symbol, unknown>>(fn: () => T): T;

@@ -42,3 +42,3 @@ import { createMemo, getOwner, runWithOwner } from "solid-js";

}
export function createMatcher(path, partial) {
export function createMatcher(path, partial, matchFilters) {
const [pattern, splat] = path.split("/*", 2);

@@ -57,9 +57,11 @@ const segments = pattern.split("/").filter(Boolean);

};
const matchFilter = (s) => matchFilters === undefined ? undefined : matchFilters[s];
for (let i = 0; i < len; i++) {
const segment = segments[i];
const locSegment = locSegments[i];
if (segment[0] === ":") {
match.params[segment.slice(1)] = locSegment;
const key = segment[0] === ":" ? segment.slice(1) : segment;
if (segment[0] === ":" && matchSegment(locSegment, matchFilter(key))) {
match.params[key] = locSegment;
}
else if (segment.localeCompare(locSegment, undefined, { sensitivity: "base" }) !== 0) {
else if (!matchSegment(locSegment, segment)) {
return null;

@@ -70,3 +72,9 @@ }

if (splat) {
match.params[splat] = lenDiff ? locSegments.slice(-lenDiff).join("/") : "";
const remainder = lenDiff ? locSegments.slice(-lenDiff).join("/") : "";
if (matchSegment(remainder, matchFilter(splat))) {
match.params[splat] = remainder;
}
else {
return null;
}
}

@@ -76,2 +84,21 @@ return match;

}
function matchSegment(input, filter) {
const isEqual = (s) => s.localeCompare(input, undefined, { sensitivity: "base" }) === 0;
if (filter === undefined) {
return true;
}
else if (typeof filter === "string") {
return isEqual(filter);
}
else if (typeof filter === "function") {
return filter(input);
}
else if (Array.isArray(filter)) {
return filter.some(isEqual);
}
else if (filter instanceof RegExp) {
return filter.test(input);
}
return false;
}
export function scoreRoute(route) {

@@ -78,0 +105,0 @@ const [pattern, splat] = route.pattern.split("/*", 2);

@@ -9,3 +9,3 @@ {

"license": "MIT",
"version": "0.6.0",
"version": "0.7.0",
"homepage": "https://github.com/solidjs/solid-router#readme",

@@ -12,0 +12,0 @@ "repository": {

@@ -9,3 +9,3 @@ <p>

Solid Router is a universal router for SolidJS - it works whether you're rendering on the client or on the server. It was inspired by and combines paradigms of React Router and the Ember Router. Routes can be defined directly in your app's template using JSX, but you can also pass your route configuration directly as an object. It also supports nested routing, so navigation can change a part of a component, rather than completely replacing it.
Solid Router is a universal router for SolidJS - it works whether you're rendering on the client or on the server. It was inspired by and combines paradigms of React Router and the Ember Router. Routes can be defined directly in your app's template using JSX, but you can also pass your route configuration directly as an object. It also supports nested routing, so navigation can change a part of a component, rather than completely replacing it.

@@ -20,4 +20,4 @@ It supports all of Solid's SSR methods and has Solid's transitions baked in, so use it freely with suspense, resources, and lazy components. Solid Router also allows you to define a data function that loads parallel to the routes ([render-as-you-fetch](https://epicreact.dev/render-as-you-fetch/)).

- [Data Functions](#data-functions)
- [Nested Routes](#nested-routes)
- [Hash Mode Router](#hash-mode-router)
- [Nested Routes](#nested-routes)
- [Hash Mode Router](#hash-mode-router)
- [Config Based Routing](#config-based-routing)

@@ -68,3 +68,2 @@ - [Router Primitives](#router-primitives)

```jsx

@@ -75,7 +74,7 @@ import { Routes, Route } from "@solidjs/router"

return <>
<h1>My Site with Lots of Pages</h1>
<h1>My Site with Lots of Pages</h1>
<Routes>
</Routes>
</>
</>
}

@@ -94,9 +93,9 @@ ```

return <>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
}

@@ -117,9 +116,9 @@ ```

return <>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
}

@@ -140,13 +139,13 @@ ```

return <>
<h1>My Site with Lots of Pages</h1>
<nav>
<A href="/about">About</A>
<A href="/">Home</A>
</nav>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
<h1>My Site with Lots of Pages</h1>
<nav>
<A href="/about">About</A>
<A href="/">Home</A>
</nav>
<Routes>
<Route path="/users" component={Users} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
}

@@ -173,3 +172,3 @@ ```

function getPath ({navigate, location}) {
//navigate is the result of calling useNavigate(); location is the result of calling useLocation().
//navigate is the result of calling useNavigate(); location is the result of calling useLocation().
//You can use those to dynamically determine a path to navigate to

@@ -185,3 +184,3 @@ return "/some-path";

If you don't know the path ahead of time, you might want to treat part of the path as a flexible parameter that is passed on to the component.
If you don't know the path ahead of time, you might want to treat part of the path as a flexible parameter that is passed on to the component.

@@ -197,10 +196,10 @@ ```jsx

return <>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/users/:id" component={User} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users" component={Users} />
<Route path="/users/:id" component={User} />
<Route path="/" component={Home} />
<Route path="/about" element={<div>This site was made with Solid</div>} />
</Routes>
</>
}

@@ -213,3 +212,45 @@ ```

---
Each path parameter can be validated using a `MatchFilter`.
This allows for more complex routing descriptions than just checking the presence of a parameter.
```tsx
import {lazy} from "solid-js";
import {Routes, Route} from "@solidjs/router"
import type {SegmentValidators} from "./types";
const Users = lazy(() => import("./pages/Users"));
const User = lazy(() => import("./pages/User"));
const Home = lazy(() => import("./pages/Home"));
const filters: MatchFilters = {
parent: ['mom', 'dad'], // allow enum values
id: /^\d+$/, // only allow numbers
withHtmlExtension: (v: string) => v.length > 5 && v.endsWith('.html') // we want an `*.html` extension
}
export default function App() {
return <>
<h1>My Site with Lots of Pages</h1>
<Routes>
<Route path="/users/:parent/:id/:withHtmlExtension" component={User} matchFilters={filters}/>
</Routes>
</>
}
```
Here, we have added the `matchFilters` prop. This allows us to validate the `parent`, `id` and `withHtmlExtension` parameters against the filters defined in `filters`.
If the validation fails, the route will not match.
So in this example:
- `/users/mom/123/contact.html` would match,
- `/users/dad/123/about.html` would match,
- `/users/aunt/123/contact.html` would not match as `:parent` is not 'mom' or 'dad',
- `/users/mom/me/contact.html` would not match as `:id` is not a number,
- `/users/dad/123/contact` would not match as `:withHtmlExtension` is missing `.html`.
---
```jsx

@@ -264,9 +305,7 @@ //async fetching function

## Data Functions
In the [above example](#dynamic-routes), the User component is lazy-loaded and then the data is fetched. With route data functions, we can instead start fetching the data parallel to loading the route, so we can use the data as soon as possible.
To do this, create a function that fetches and returns the data using `createResource`. Then pass that function to the `data` prop of the `Route` component.
To do this, create a function that fetches and returns the data using `createResource`. Then pass that function to the `data` prop of the `Route` component.
```js

@@ -314,3 +353,3 @@ import { lazy } from "solid-js";

import { Route } from "@solidjs/router";
import { fetchUser } ...
import { fetchUser } ...
import UserData from "./pages/users/[id].data.js";

@@ -357,3 +396,2 @@ const User = lazy(() => import("/pages/users/[id].js"));

```jsx
import { Outlet } from "@solidjs/router";

@@ -363,6 +401,6 @@

return <div>
<h1> We love our users! </h1>
<h1> We love our users! </h1>
<Outlet/>
<A href="/">Back Home</A>
</div>
<A href="/">Back Home</A>
</div>
}

@@ -387,3 +425,3 @@

If you declare a `data` function on a parent and a child, the result of the parent's data function will be passed to the child's data function as the `data` property of the argument, as described in the last section. This works even if it isn't a direct child, because by default every route forwards its parent's data.
If you declare a `data` function on a parent and a child, the result of the parent's data function will be passed to the child's data function as the `data` property of the argument, as described in the last section. This works even if it isn't a direct child, because by default every route forwards its parent's data.

@@ -397,3 +435,3 @@ ## Hash Mode Router

<Router source={hashIntegration()}><App></Router>
<Router source={hashIntegration()}><App /></Router>
```

@@ -580,3 +618,3 @@

// user wants to proceed anyway so retry with force=true
e.retry(true);
e.retry(true);
}

@@ -587,3 +625,1 @@ }, 100);

```
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