Socket
Socket
Sign inDemoInstall

@sanity/state-router

Package Overview
Dependencies
Maintainers
34
Versions
702
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sanity/state-router - npm Package Compare versions

Comparing version 2.21.11-reference-updates.62 to 2.21.12-purple-unicorn.1300

lib/dts/src/findMatchingRoutes.d.ts

53

package.json
{
"name": "@sanity/state-router",
"version": "2.21.11-reference-updates.62+f98497b413",
"version": "2.21.12-purple-unicorn.1300+abdd014728",
"description": "A path pattern => state object bidirectional mapper",
"main": "./lib/_exports/index.js",
"types": "index.d.ts",
"//": "the typesVersion config below is a workaround for TypeScript's lack of support for package exports",
"typesVersions": {
"*": {
"*": [
"dist/dts/_exports/*"
]
"source": "./src/index.ts",
"main": "./lib/index.cjs",
"module": "./lib/index.js",
"exports": {
".": {
"source": "./src/index.ts",
"require": "./lib/index.cjs",
"default": "./lib/index.js"
}
},
"types": "./lib/dts/src/index.d.ts",
"files": [
"lib",
"src"
],
"scripts": {
"start": "cd demo && vite .",
"benchmark": "run-s build benchmark:run",
"benchmark:run": "node --prof --logfile=benchmarks.log perf/benchmark.js",
"build": "../../../bin/pkg-utils bundle --tsconfig tsconfig.lib.json",
"clean": "rimraf *.log lib",
"dev": "cd demo && vite",
"test": "jest",
"compile:watch": "babel --watch --out-dir lib/ src/",
"benchmark": "npm run compile && node --prof --logfile=benchmarks.log perf/benchmark.js",
"clean": "rimraf coverage dist lib"
"test:coverage": "jest --coverage",
"prebuild": "yarn clean",
"watch": "../../../bin/pkg-utils bundle --tsconfig tsconfig.lib.json --watch"
},

@@ -34,15 +43,15 @@ "keywords": [

"debug": "^3.2.7",
"lodash": "^4.17.15",
"nano-pubsub": "^2.0.1"
"lodash": "^4.17.21"
},
"devDependencies": {
"@vitejs/plugin-react-refresh": "^1.3.6",
"@sanity/ui": "^0.37.5",
"@types/object-inspect": "^1.8.1",
"@vitejs/plugin-react": "^1.1.3",
"history": "^4.6.3",
"object-inspect": "^1.6.0",
"prop-types": "^15.6.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"rimraf": "^2.7.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"rimraf": "^3.0.2",
"ts-node": "^9.1.1",
"vite": "^2.4.4"
"vite": "^2.7.2"
},

@@ -62,3 +71,3 @@ "peerDependencies": {

"homepage": "https://www.sanity.io/",
"gitHead": "f98497b413e20f06fcca0923f540b8ae2c8ae564"
"gitHead": "abdd014728981dd514951705252054db61bcd3f4"
}

@@ -1,3 +0,7 @@

## @sanity/state-router
# `@sanity/state-router`
```sh
npm install @sanity/state-router
```
## Features

@@ -14,6 +18,6 @@ Based on a routing schema:

const router = route('/', [
route('/products/:productId'),
route('/users/:userId'),
route('/:page'),
const router = route.create('/', [
route.create('/products/:productId'),
route.create('/users/:userId'),
route.create('/:page'),
])

@@ -51,6 +55,6 @@

import {route} from '@sanity/state-router'
import {RouterProvider, withRouter} from '@sanity/state-router/components'
import {RouterProvider, withRouter} from '@sanity/state-router'
const router = route('/', [
route('/bikes/:bikeId')
const router = route.create('/', [
route.create('/bikes/:bikeId')
])

@@ -83,5 +87,6 @@

<RouterProvider
router={router}
onNavigate={handleNavigate}
state={router.decode(location.pathname)}>
router={router}
onNavigate={handleNavigate}
state={router.decode(location.pathname)}
>
<App />

@@ -97,3 +102,3 @@ </RouterProvider>

- `route(path : string, ?options : Options, ?children : ) : Router`
- `route.create(path : string, ?options : Options, ?children : ) : Router`
- `route.scope(name : string, path : string, ?options : Options, ?children : ) : Router`

@@ -122,3 +127,3 @@ - `Router`:

- `children` can be either another router returned from another `route()-call`, an array of routers or a function that gets passed the matched parameters, and conditionally returns child routes
- `children` can be either another router returned from another `route.create()-call`, an array of routers or a function that gets passed the matched parameters, and conditionally returns child routes

@@ -130,5 +135,5 @@ ## Limitations

```js
const router = route('/', [
route('/about'),
route('/contact')
const router = route.create('/', [
route.create('/about'),
route.create('/contact')
])

@@ -139,3 +144,3 @@ ```

```js
const router = route('/', route('/:page'))
const router = route.create('/', route.create('/:page'))
```

@@ -162,3 +167,3 @@

const router = route('/some/:section/:settings', {
const router = route.create('/some/:section/:settings', {
transform: {

@@ -170,3 +175,3 @@ settings: {

}
}, route('/other/:page'))
}, route.create('/other/:page'))
```

@@ -210,9 +215,9 @@ This call...

name: 'pokemon',
router: route('/:section', route('/:pokemonName'))
router: route.create('/:section', route.create('/:pokemonName'))
}
}
const router = route('/', [
route('/users/:username'),
route('/apps/:appName', params => {
const router = route.create('/', [
route.create('/users/:username'),
route.create('/apps/:appName', params => {
const app = findAppByName(params.appName)

@@ -257,4 +262,4 @@ return app && route.scope(app.name, '/', app.router)

```
const router = route('/', [
route('/users/:username'),
const router = route.create('/', [
route.create('/users/:username'),
route.intents('/intents') // <-- sets up intent routes at the /intents base path

@@ -280,3 +285,3 @@ ])

```js
const router = route('/pages/:page')
const router = route.create('/pages/:page')

@@ -292,5 +297,5 @@ router.isNotFound('/some/invalid/path')

```js
const router = route('/some/basepath', [
route('/:foo'),
route('/:bar')
const router = route.create('/some/basepath', [
route.create('/:foo'),
route.create('/:bar')
])

@@ -297,0 +302,0 @@ ```

@@ -9,4 +9,6 @@ import {Route, Segment} from './types'

}
if (segment.startsWith(':')) {
const paramName = segment.substring(1)
if (!VALID_PARAM_SEGMENT.test(paramName)) {

@@ -21,8 +23,10 @@ const addendum = segment.includes('*')

}
return {type: 'param', name: paramName}
}
return {type: 'dir', name: segment}
}
export default function parseRoute(route: string): Route {
export function parseRoute(route: string): Route {
const [pathname] = route.split('?')

@@ -29,0 +33,0 @@

@@ -1,10 +0,14 @@

import {Node, MatchResult} from './types'
import findMatchingNodes from './findMatchingNodes'
import {flatten} from 'lodash'
import {findMatchingRoutes} from './findMatchingRoutes'
import {RouterNode, MatchResult} from './types'
import {debug} from './utils/debug'
export default function resolvePathFromState(node: Node, state: Record<string, unknown>): string {
/**
* @public
*/
export function resolvePathFromState(node: RouterNode, state: Record<string, unknown>): string {
debug('Resolving path from state %o', state)
const match: MatchResult = findMatchingNodes(node, state)
const match: MatchResult = findMatchingRoutes(node, state)
if (match.remaining.length > 0) {

@@ -24,2 +28,3 @@ const remaining = match.remaining

let scopedState: Record<string, unknown> = state
const relative = flatten(

@@ -30,2 +35,3 @@ match.nodes.map((matchNode) => {

}
return matchNode.route.segments.map((segment) => {

@@ -35,4 +41,8 @@ if (segment.type === 'dir') {

}
const transform = matchNode.transform && matchNode.transform[segment.name]
return transform ? transform.toPath(scopedState[segment.name]) : scopedState[segment.name]
return transform
? transform.toPath(scopedState[segment.name] as any)
: scopedState[segment.name]
})

@@ -39,0 +49,0 @@ })

@@ -1,13 +0,9 @@

import {Node} from './types'
import {RouterNode} from './types'
import {debug} from './utils/debug'
import arrayify from './utils/arrayify'
import {arrayify} from './utils/arrayify'
function matchPath(
node: Node,
path: string
): {
[key: string]: string
} | null {
function matchPath(node: RouterNode, path: string): Record<string, string> | null {
const parts = path.split('/').filter(Boolean)
const segmentsLength = node.route.segments.length
if (parts.length < segmentsLength) {

@@ -17,3 +13,3 @@ return null

const state = {}
const state: Record<string, unknown> = {}
const isMatching = node.route.segments.every((segment, i) => {

@@ -23,4 +19,7 @@ if (segment.type === 'dir') {

}
const transform = node.transform && node.transform[segment.name]
state[segment.name] = transform ? transform.toState(parts[i]) : parts[i]
return true

@@ -34,7 +33,10 @@ })

const rest = parts.slice(segmentsLength)
let childState: {
[key: string]: string
} | null = null
const children =
typeof node.children === 'function' ? arrayify(node.children(state)) : node.children
children.some((childNode) => {

@@ -44,4 +46,10 @@ // console.log('----childNode')

// console.log('----childNode')
childState = matchPath(childNode, rest.join('/'))
return childState
if (childNode) {
childState = matchPath(childNode, rest.join('/'))
return childState
}
return undefined
})

@@ -54,6 +62,10 @@

const mergedState = {...state, ...(childState || {})}
return node.scope ? {[node.scope]: mergedState} : mergedState
}
export default function resolveStateFromPath(node: Node, path: string): Record<string, any> | null {
/**
* @public
*/
export function resolveStateFromPath(node: RouterNode, path: string): Record<string, any> | null {
debug('resolving state from path %s', path)

@@ -64,3 +76,4 @@

debug('resolved: %o', pathMatch || null)
return pathMatch || null
}

@@ -0,10 +1,12 @@

import {parseRoute} from './parseRoute'
import {resolveStateFromPath} from './resolveStateFromPath'
import {resolvePathFromState} from './resolvePathFromState'
import {Transform, Router, RouteChildren} from './types'
import parseRoute from './parseRoute'
import resolveStateFromPath from './resolveStateFromPath'
import resolvePathFromState from './resolvePathFromState'
import {decodeJsonParams, encodeJsonParams} from './utils/jsonParamsEncoding'
import {decodeParams, encodeParams} from './utils/paramsEncoding'
import {decodeJsonParams, encodeJsonParams} from './utils/jsonParamsEncoding'
type NodeOptions = {
/**
* @public
*/
export type NodeOptions = {
path?: string

@@ -38,2 +40,3 @@ children?: RouteChildren

}
if (

@@ -46,10 +49,25 @@ Array.isArray(childrenOrOpts) ||

}
if (children) {
return {path, ...childrenOrOpts, children: normalizeChildren(children)}
}
return {path, ...childrenOrOpts}
}
export default function route(
routeOrOpts: string | NodeOptions,
/**
* @public
*/
export const route: {
create: (
routeOrOpts: NodeOptions | string,
childrenOrOpts?: NodeOptions | RouteChildren | null,
children?: Router | RouteChildren
) => Router
intents: (base: string) => Router
scope: (scopeName: string, ...rest: any[]) => Router
} = {create: createRoute, scope: routeScope, intents: routeIntents}
function createRoute(
routeOrOpts: NodeOptions | string,
childrenOrOpts?: NodeOptions | RouteChildren | null,

@@ -61,3 +79,3 @@ children?: Router | RouteChildren

route.scope = function scope(scopeName: string, ...rest: any[]): Router {
function routeScope(scopeName: string, ...rest: any[]): Router {
const options = normalizeArgs(...rest)

@@ -71,10 +89,11 @@

function normalize(...paths) {
return paths.reduce((acc, path) => acc.concat(path.split('/')), []).filter(Boolean)
function normalize(...paths: string[]) {
return paths.reduce<string[]>((acc, path) => acc.concat(path.split('/')), []).filter(Boolean)
}
route.intents = function intents(base) {
function routeIntents(base: string): Router {
const basePath = normalize(base).join('/')
return route(`${basePath}/:intent`, [
route(
return route.create(`${basePath}/:intent`, [
route.create(
':params',

@@ -90,3 +109,3 @@ {

[
route(':payload', {
route.create(':payload', {
transform: {

@@ -105,4 +124,6 @@ payload: {

const EMPTY_STATE = {}
function isRoot(pathname: string): boolean {
const parts = pathname.split('/')
for (let i = 0; i < parts.length; i++) {

@@ -113,2 +134,3 @@ if (parts[i]) {

}
return true

@@ -119,5 +141,7 @@ }

const {path, scope, transform, children} = options
if (!path) {
throw new TypeError('Missing path')
}
const parsedRoute = parseRoute(path)

@@ -124,0 +148,0 @@

import React from 'react'
import {InternalRouter} from './components/types'
import {RouterContextValue} from './types'
const missingContext = () => {
throw new Error('No router context provider found')
}
export const RouterContext = React.createContext<InternalRouter>({
channel: {subscribe: missingContext, publish: missingContext},
getState: missingContext,
navigate: missingContext,
navigateIntent: missingContext,
navigateUrl: missingContext,
resolveIntentLink: missingContext,
resolvePathFromState: missingContext,
})
/**
* @public
*/
export const RouterContext = React.createContext<RouterContextValue | null>(null)

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

export type Segment = {
/**
* @public
*/
export interface Segment {
name: string

@@ -6,3 +9,6 @@ type: 'dir' | 'param'

export type Transform<T> = {
/**
* @public
*/
export interface Transform<T> {
toState: (value: string) => T

@@ -12,18 +18,28 @@ toPath: (value: T) => string

export type Route = {
/**
* @public
*/
export interface Route {
raw: string
segments: Segment[]
transform?: {
[key: string]: Transform<any>
[key: string]: Transform<RouterState>
}
}
// eslint-disable-next-line no-use-before-define
export type RouteChildren = Node[] | ((state: Record<string, unknown>) => Node[])
/**
* @public
*/
export type RouteChildren =
| RouterNode[]
| ((state: RouterState) => Router | RouterNode | RouterNode[] | undefined | false)
export type Node = {
/**
* @public
*/
export interface RouterNode {
route: Route
scope?: string
transform?: {
[key: string]: Transform<any>
[key: string]: Transform<RouterState>
}

@@ -33,6 +49,9 @@ children: RouteChildren

export type Router = Node & {
/**
* @public
*/
export interface Router extends RouterNode {
_isRoute: boolean
encode: (state: Record<string, unknown>) => string
decode: (path: string) => Record<string, unknown> | null
encode: (state: RouterState) => string
decode: (path: string) => RouterState | null
isNotFound: (path: string) => boolean

@@ -43,6 +62,37 @@ getBasePath: () => string

}
export type MatchResult = {
nodes: Node[]
export interface MatchResult {
nodes: RouterNode[]
missing: string[]
remaining: string[]
}
/**
* @public
*/
export type NavigateOptions = {
replace?: boolean
}
/**
* @public
*/
export type IntentParameters =
| Record<string, unknown>
| [Record<string, unknown>, Record<string, unknown>]
/**
* @public
*/
export type RouterState = Record<string, unknown>
/**
* @public
*/
export type RouterContextValue = {
resolvePathFromState: (nextState: RouterState) => string
resolveIntentLink: (intentName: string, params?: IntentParameters) => string
navigateUrl: (opts: {path: string; replace?: boolean}) => void
navigate: (nextState: RouterState, options?: NavigateOptions) => void
navigateIntent: (intentName: string, params?: IntentParameters, options?: NavigateOptions) => void
state: RouterState
}
import {useContext} from 'react'
import {InternalRouter} from './components/types'
import {RouterContext} from './RouterContext'
import {RouterContextValue} from './types'
export function useRouter(): InternalRouter {
return useContext(RouterContext)
/**
* @public
*/
export function useRouter(): RouterContextValue {
const router = useContext(RouterContext)
if (!router) {
throw new Error('Router: missing context value')
}
return router
}
import {identity} from 'lodash'
import {useContext, useEffect, useState} from 'react'
import {RouterState} from './components/types'
import {RouterContext} from './RouterContext'
import {useEffect, useState} from 'react'
import {RouterState} from './types'
import {useRouter} from './useRouter'
/**
* @public
*/
export function useRouterState<R = RouterState>(selector: (routerState: RouterState) => R): R
/**
* @public
*/
export function useRouterState(): RouterState
/**
* @public
*/
export function useRouterState(
selector: (routerState: RouterState) => unknown = identity
): unknown {
const {channel, getState} = useContext(RouterContext)
const [selectedState, setState] = useState(() => selector(getState()))
const {state} = useRouter()
const [selectedState, setState] = useState(() => selector(state))
// reset the state when the `selector` prop changes
useEffect(() => setState(selector(getState())), [selector, getState])
useEffect(() => setState(selector(state)), [selector, state])
// update the state via a subscription
useEffect(() => {
// prevents "Can't perform a React state update on an unmounted component."
const mounted = {current: true}
// useEffect(() => {
// // prevents "Can't perform a React state update on an unmounted component."
// const mounted = {current: true}
const unsubscribe = channel.subscribe(() => {
if (mounted.current) {
setState(selector(getState()))
}
})
// const unsubscribe = channel.subscribe(() => {
// if (mounted.current) {
// setState(selector(state))
// }
// })
return () => {
mounted.current = false
unsubscribe()
}
}, [channel, selector, getState])
// return () => {
// mounted.current = false
// unsubscribe()
// }
// }, [channel, selector, getState])
return selectedState
}

@@ -1,6 +0,7 @@

export default function arrayify<T>(val: Array<T> | T): Array<T> {
export function arrayify<T>(val: Array<T> | T): Array<T> {
if (Array.isArray(val)) {
return val
}
return typeof val === 'undefined' ? [] : [val]
return val ? [val] : []
}
const _hasOWn = Object.prototype.hasOwnProperty
export default _hasOWn.call.bind(_hasOWn)
export const hasOwn = _hasOWn.call.bind(_hasOWn)

@@ -1,4 +0,4 @@

import hasOwn from './hasOwn'
import {hasOwn} from './hasOwn'
export default function isEmpty(object: Object): boolean {
export function isEmpty(object: Record<string, unknown>): boolean {
for (const key in object) {

@@ -9,3 +9,4 @@ if (hasOwn(object, key)) {

}
return true
}

@@ -1,3 +0,4 @@

export function decodeJsonParams(pathsegment = '') {
export function decodeJsonParams(pathsegment = ''): Record<string, unknown> {
const segment = decodeURIComponent(pathsegment)
if (!segment) {

@@ -16,3 +17,2 @@ return {}

} catch (err) {
// eslint-disable-next-line no-console
console.warn('Failed to parse JSON parameters')

@@ -24,4 +24,4 @@ }

export function encodeJsonParams(params) {
return params === null || typeof params === 'undefined' ? '' : btoa(JSON.stringify(params))
export function encodeJsonParams(params?: Record<string, unknown>): string {
return params ? btoa(JSON.stringify(params)) : ''
}
export function decodeParams(pathSegment: string): Record<string, string> {
return pathSegment.split(';').reduce<Record<string, string>>((params, pair) => {
const [key, value] = pair.split('=')
params[decodeURIComponent(key)] = decodeURIComponent(value)
return params

@@ -9,6 +11,7 @@ }, {})

export function encodeParams(params: Record<string, string>): string {
return Object.keys(params)
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
export function encodeParams(params: Record<string, string | undefined | null>): string {
return Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
.join(';')
}
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