Socket
Socket
Sign inDemoInstall

@sanity/state-router

Package Overview
Dependencies
Maintainers
6
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 0.1.1 to 0.2.0-beta.0

example.js

10

lib/components/Link.js

@@ -50,7 +50,7 @@ 'use strict';

value: function handleClick(e) {
var _props = this.props;
var onClick = _props.onClick;
var href = _props.href;
var target = _props.target;
var replace = _props.replace;
var _props = this.props,
onClick = _props.onClick,
href = _props.href,
target = _props.target,
replace = _props.replace;

@@ -57,0 +57,0 @@

@@ -36,5 +36,5 @@ 'use strict';

return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = RouterProvider.__proto__ || Object.getPrototypeOf(RouterProvider)).call.apply(_ref, [this].concat(args))), _this), _this.navigateUrl = function (url) {
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
replace = _ref2.replace;
var replace = _ref2.replace;
var onNavigate = _this.props.onNavigate;

@@ -44,6 +44,5 @@

}, _this.navigateState = function (nextState) {
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
replace = _ref3.replace;
var replace = _ref3.replace;
_this.navigateUrl(_this.resolvePathFromState(nextState), { replace: replace });

@@ -58,6 +57,3 @@ }, _this.resolvePathFromState = function (state) {

value: function getChildContext() {
var _props = this.props;
var router = _props.router;
var location = _props.location;
var state = _props.state;
var state = this.props.state;

@@ -71,3 +67,3 @@ return {

navigate: this.navigateState,
state: state || router.decode(location.pathname)
state: state
}

@@ -89,5 +85,2 @@ };

router: _react.PropTypes.object,
location: _react.PropTypes.shape({
pathname: _react.PropTypes.string
}),
state: _react.PropTypes.object,

@@ -94,0 +87,0 @@ children: _react.PropTypes.node

@@ -36,5 +36,5 @@ 'use strict';

var scope = this.props.scope;
var _context = this.context;
var router = _context.router;
var __internalRouter = _context.__internalRouter;
var _context = this.context,
router = _context.router,
__internalRouter = _context.__internalRouter;

@@ -41,0 +41,0 @@ return {

@@ -45,5 +45,5 @@ 'use strict';

value: function resolveUrl() {
var _props = this.props;
var toIndex = _props.toIndex;
var state = _props.state;
var _props = this.props,
toIndex = _props.toIndex,
state = _props.state;

@@ -50,0 +50,0 @@

@@ -32,9 +32,6 @@ 'use strict';

function parseRoute(route) {
var _route$split = route.split('?');
var _route$split = route.split('?'),
_route$split2 = _slicedToArray(_route$split, 1),
pathname = _route$split2[0];
var _route$split2 = _slicedToArray(_route$split, 1);
var pathname = _route$split2[0];
var segments = pathname.split('/').map(createSegment).filter(Boolean);

@@ -41,0 +38,0 @@

@@ -15,2 +15,9 @@ 'use strict';

function arrayify(val) {
if (Array.isArray(val)) {
return val;
}
return typeof val === 'undefined' ? [] : [val];
}
function matchPath(node, path) {

@@ -39,9 +46,10 @@ var parts = path.split('/').filter(Boolean);

var childState = null;
var children = typeof node.children === 'function' ? node.children(state) : node.children;
if (children) {
children.some(function (childNode) {
childState = matchPath(childNode, rest.join('/'));
return childState;
});
}
var children = typeof node.children === 'function' ? arrayify(node.children(state)) : node.children;
children.some(function (childNode) {
// console.log('----childNode')
// console.log(childNode)
// console.log('----childNode')
childState = matchPath(childNode, rest.join('/'));
return childState;
});

@@ -48,0 +56,0 @@ if (rest.length > 0 && !childState) {

@@ -29,2 +29,13 @@ 'use strict';

function normalizeChildren(children) {
if (Array.isArray(children) || typeof children === 'function') {
return children;
}
return children ? [children] : [];
}
function isRoute(val) {
return val && '_isRoute' in val;
}
function normalizeArgs(path, childrenOrOpts, children) {

@@ -34,7 +45,7 @@ if ((typeof path === 'undefined' ? 'undefined' : _typeof(path)) === 'object') {

}
if (Array.isArray(childrenOrOpts) || typeof childrenOrOpts === 'function') {
return { path: path, children: childrenOrOpts };
if (Array.isArray(childrenOrOpts) || typeof childrenOrOpts === 'function' || isRoute(childrenOrOpts)) {
return { path: path, children: normalizeChildren(childrenOrOpts) };
}
if (children) {
return _extends({ path: path }, childrenOrOpts, { children: children });
return _extends({ path: path }, childrenOrOpts, { children: normalizeChildren(children) });
}

@@ -60,7 +71,18 @@ return _extends({ path: path }, childrenOrOpts);

var EMPTY_STATE = {};
function isRoot(pathname) {
var parts = pathname.split('/');
for (var i = 0; i < parts.length; i++) {
if (parts[i]) {
return false;
}
}
return true;
}
function createNode(options) {
var path = options.path;
var scope = options.scope;
var transform = options.transform;
var children = options.children;
var path = options.path,
scope = options.scope,
transform = options.transform,
children = options.children;

@@ -71,3 +93,5 @@ if (!path) {

var parsedRoute = (0, _parseRoute2.default)(path);
return {
_isRoute: true, // todo: make a Router class instead
scope: scope,

@@ -82,4 +106,22 @@ route: parsedRoute,

return (0, _resolveStateFromPath2.default)(this, _path);
},
isRoot: isRoot,
getBasePath: function getBasePath() {
return this.encode(EMPTY_STATE);
},
isNotFound: function isNotFound(pathname) {
return this.decode(pathname) === null;
},
getRedirectBase: function getRedirectBase(pathname) {
if (isRoot(pathname)) {
var basePath = this.getBasePath();
// Check if basepath is something different than given
if (pathname !== basePath) {
return basePath;
}
}
return null;
}
};
}
{
"name": "@sanity/state-router",
"version": "0.1.1",
"version": "0.2.0-beta.0",
"description": "A path pattern => state object bidirectional mapper",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -12,31 +12,31 @@ ## @sanity/state-router

```js
import {createRoute, resolvePathFromState, resolveStateFromPath} from 'xroute'
import {route} from 'xroute'
const route = createRoute('/*', [
createRoute('/products/:productId'),
createRoute('/users/:userId'),
createRoute('/:page'),
const router = route('/', [
route('/products/:productId'),
route('/users/:userId'),
route('/:page'),
])
resolvePathFromState(route, {})
router.encode(route, {})
// => '/'
resolveStateFromPath(route, '/')
router.decode(route, '/')
// => {}
resolvePathFromState(route, {productId: 54})
router.encode(route, {productId: 54})
// => '/products/54'
resolveStateFromPath(route, '/products/54')
router.decode(route, '/products/54')
// => {productId: 54}
resolvePathFromState(route, {userId: 22})
router.encode(route, {userId: 22})
// => '/users/22'
resolveStateFromPath(route, '/users/54')
router.decode(route, '/users/54')
// => {userId: 54}
resolvePathFromState(route, {page: 'about'})
router.encode(route, {page: 'about'})
// => '/about'
resolveStateFromPath(route, '/about')
router.decode(route, '/about')
// => {page: about}

@@ -46,3 +46,31 @@

## Restrictions
## API
- `route(path : string, ?options : Options, ?children : ) : Router`
- `route.scope(name : string, path : string, ?options : Options, ?children : ) : Router`
- `Router`:
- `encode(state : object) : string`
- `decode(path : string) : object`
- `isRoot(path : string) : boolean`
- `getBasePath() : string`,
- `isNotFound(pathname: string): boolean`
- `getRedirectBase(pathname : string) : ?string`
- `RouteChildren`:
```
Router | [Router] | ((state) => Router | [Router])
```
- `Options`:
```
{
path?: string,
children?: RouteChildren,
transform?: {[key: string] : Transform<*>},
scope?: string
}
```
- `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
## Limitations
- Parameterized paths *only*. Each route must have at least one unique parameter. If not, there's no way of unambiguously resolve a path from an empty state.

@@ -52,13 +80,92 @@

```js
const rootRoute = createRoute('/', [
createRoute('/about'),
createRoute('/contact')
const router = route('/', [
route('/about'),
route('/contact')
])
```
What route should be resolved from an empty state? Since both `/about` and `/contact` above resolves to an empty state object, there's no way to resolve an empty state object back to either of them. The solution to this would be to introduce the page name as a parameter instead:
What route should be resolved from an empty state? Since both `/about` and `/contact` above resolves to an empty state object, there's no way to encode an empty state unambiguously back to either of them. The solution to this would be to introduce the page name as a parameter instead:
```js
const rootRoute = createRoute('/:page')
const router = route('/', route('/:page'))
```
Now, `/about` would resolve to the state `{page: 'about'}` which unambiguously can map back to `/page`, and an empty state can map to `/`. To figure out if you are on the index page, you can check for `state.page == null`, (and set the state.page to null to navigate back to the index)
### No query params
Query parameters doesn't work too well with router scopes as they operate in a global namespace. A possible workaround is to "fake" query params in a path segment using transforms:
Now, `/about` would resolve to the state `{page: 'about'}` which unambiguously can map back to `/page`, and an empty state can map to `/`. To figure out if you are on the index page, you can check for `state.page == null`, (and set the state.page to null to navigate back to the index)
## Scopes
A scope is a separate router state space, allowing different parts of an application to be completely agnostic about the overall routing schema is like. Let's illustrate:
```js
import {route} from './src'
function findAppByName(name) {
return name === 'pokemon' && {
name: 'pokemon',
router: route('/:section', route('/:pokemonName'))
}
}
const router = route('/', [
route('/users/:username'),
route('/apps/:appName', params => {
const app = findAppByName(params.appName)
return app && route.scope(app.name, '/', app.router)
})
])
```
Decoding the following path...
```js
router.decode('/apps/pokemon/stats/bulbasaur')
```
...will give us the state:
```js
{
appName: 'pokemon',
pokemon: {
section: 'stats',
pokemonName: 'bulbasaur'
}
}
```
## 404s
To check whether a path name matches, you can use the isNotFound method on the returned router instance:
```js
const router = route('/pages/:page')
router.isNotFound('/some/invalid/path')
// => true
```
## Base paths
Using a base path is as simple as adding a toplevel route with no params:
```js
const router = route('/some/basepath', [
route('/:foo'),
route('/:bar')
])
```
Any empty router state will resolve to `/some/basepath`. To check if you should redirect to the base path on app init, you can use the `router.isRoot(path)` and `router.getBasePath()` method:
```js
if (router.isRoot(location.pathname)) {
const basePath = router.getBasePath()
if (basePath !== location.pathname) {
history.replaceState(null, null, basePath)
}
}
```
For convenience, this check is combined in the method `router.getRedirectBase()`, that if a redirect is needed, will return the base path, otherwise `null`
```
const redirectTo = router.getRedirectBase(location.pathname)
if (redirectTo) {
history.replaceState(null, null, redirectTo)
}
```

@@ -7,5 +7,2 @@ import React, {PropTypes} from 'react'

router: PropTypes.object,
location: PropTypes.shape({
pathname: PropTypes.string
}),
state: PropTypes.object,

@@ -29,3 +26,3 @@ children: PropTypes.node

getChildContext() {
const {router, location, state} = this.props
const {state} = this.props
return {

@@ -38,3 +35,3 @@ __internalRouter: {

navigate: this.navigateState,
state: state || router.decode(location.pathname)
state: state
}

@@ -41,0 +38,0 @@ }

@@ -5,2 +5,9 @@ // @flow

function arrayify<T>(val : Array<T> | T) : Array<T> {
if (Array.isArray(val)) {
return val
}
return typeof val === 'undefined' ? [] : [val]
}
function matchPath(node : Node, path : string) : ?{[key: string]: string} {

@@ -29,9 +36,10 @@ const parts = path.split('/').filter(Boolean)

let childState = null
const children = typeof node.children === 'function' ? node.children(state) : node.children
if (children) {
children.some(childNode => {
childState = matchPath(childNode, rest.join('/'))
return childState
})
}
const children = typeof node.children === 'function' ? arrayify(node.children(state)) : node.children
children.some(childNode => {
// console.log('----childNode')
// console.log(childNode)
// console.log('----childNode')
childState = matchPath(childNode, rest.join('/'))
return childState
})

@@ -38,0 +46,0 @@ if (rest.length > 0 && !childState) {

@@ -15,11 +15,26 @@ // @flow

function normalizeArgs(path, childrenOrOpts, children) : NodeOptions {
function normalizeChildren(children : any) : RouteChildren {
if (Array.isArray(children) || typeof children === 'function') {
return children
}
return children ? [children] : []
}
function isRoute(val? : NodeOptions | Router | RouteChildren) {
return val && '_isRoute' in val
}
function normalizeArgs(
path : string | NodeOptions,
childrenOrOpts? : NodeOptions | Router | RouteChildren,
children? : Router | RouteChildren
) : NodeOptions {
if (typeof path === 'object') {
return path
}
if (Array.isArray(childrenOrOpts) || typeof childrenOrOpts === 'function') {
return {path, children: childrenOrOpts}
if (Array.isArray(childrenOrOpts) || typeof childrenOrOpts === 'function' || isRoute(childrenOrOpts)) {
return {path, children: normalizeChildren(childrenOrOpts)}
}
if (children) {
return {path, ...childrenOrOpts, children}
return {path, ...childrenOrOpts, children: normalizeChildren(children)}
}

@@ -29,3 +44,7 @@ return {path, ...childrenOrOpts}

export default function route(routeOrOpts : string | NodeOptions, childrenOrOpts? : NodeOptions | RouteChildren, children? : RouteChildren) : Router {
export default function route(
routeOrOpts : string | NodeOptions,
childrenOrOpts? : NodeOptions | RouteChildren,
children? : Router | RouteChildren
) : Router {
return createNode(normalizeArgs(routeOrOpts, childrenOrOpts, children))

@@ -44,2 +63,13 @@ }

const EMPTY_STATE = {}
function isRoot(pathname: string): boolean {
const parts = pathname.split('/')
for (let i = 0; i < parts.length; i++) {
if (parts[i]) {
return false
}
}
return true
}
function createNode(options : NodeOptions) : Router {

@@ -51,3 +81,5 @@ const {path, scope, transform, children} = options

const parsedRoute = parseRoute(path)
return {
_isRoute: true, // todo: make a Router class instead
scope,

@@ -62,4 +94,21 @@ route: parsedRoute,

return resolveStateFromPath(this, _path)
},
isRoot: isRoot,
getBasePath(): boolean {
return this.encode(EMPTY_STATE)
},
isNotFound(pathname: string): boolean {
return this.decode(pathname) === null
},
getRedirectBase(pathname : string) : ?string {
if (isRoot(pathname)) {
const basePath = this.getBasePath()
// Check if basepath is something different than given
if (pathname !== basePath) {
return basePath
}
}
return null
}
}
}

@@ -7,4 +7,6 @@ // @flow

const original = obj[method]
// $FlowIgnore
obj[method] = mockFn
return function restore() {
// $FlowIgnore
obj[method] = original

@@ -11,0 +13,0 @@ }

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