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

boring-router

Package Overview
Dependencies
Maintainers
1
Versions
108
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

boring-router - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0-alpha.0

1

bld/library/@utils.d.ts

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

export declare function then(handler: () => void): void;
export declare function isPathPrefix(path: string, prefix: string): boolean;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const FULFILLED_PROMISE = Promise.resolve();
function then(handler) {
// tslint:disable-next-line:no-floating-promises
FULFILLED_PROMISE.then(handler);
}
exports.then = then;
function isPathPrefix(path, prefix) {

@@ -4,0 +10,0 @@ return (path.startsWith(prefix) &&

3

bld/library/redirect.js

@@ -6,2 +6,3 @@ "use strict";

const react_1 = tslib_1.__importStar(require("react"));
const _utils_1 = require("./@utils");
let Redirect = class Redirect extends react_1.Component {

@@ -19,3 +20,3 @@ render() {

if (matched) {
requestAnimationFrame(() => this.redirect());
_utils_1.then(() => this.redirect());
}

@@ -22,0 +23,0 @@ return react_1.default.createElement(react_1.default.Fragment, null);

import { History } from 'history';
import { Dict } from 'tslang';
export declare type RouteMatchAction = () => void;
/**
* Route match interception callback.
* @return Return `true` or `undefined` to do nothing; return `false` to ignore
* this match; return a full path to redirect.
*/
export declare type RouteMatchInterception = () => string | boolean | void;
export declare type RouteMatchReaction = () => void;
export declare type GeneralFragmentDict = Dict<string | undefined>;

@@ -10,2 +16,3 @@ export declare type GeneralQueryDict = Dict<string | undefined>;

query: Dict<boolean> | undefined;
exact: boolean;
}

@@ -18,3 +25,3 @@ export declare class RouteMatch<TParamDict extends GeneralParamDict = GeneralParamDict> {

readonly $name: string;
constructor(name: string, history: History, { match, query }: RouteMatchOptions);
constructor(name: string, history: History, { match, query, exact }: RouteMatchOptions);
/**

@@ -48,9 +55,15 @@ * A reactive value indicates whether this route is matched.

/**
* Perform an action if this `RouteMatch` matches.
* @param action A callback to perform this action.
* @param exact Perform this action only if it's an exact match.
* Intercept route matching if this `RouteMatch` matches.
* @param interception The interception callback.
* @param exact Intercept only if it's an exact match.
*/
$action(action: RouteMatchAction, exact?: boolean): void;
$intercept(interception: RouteMatchInterception, exact?: boolean): void;
/**
* Perform a reaction if this `RouteMatch` matches.
* @param reaction A callback to perform this reaction.
* @param exact Perform this reaction only if it's an exact match.
*/
$react(reaction: RouteMatchReaction, exact?: boolean): void;
static fragment: RegExp;
static rest: RegExp;
}

@@ -7,4 +7,6 @@ "use strict";

class RouteMatch {
constructor(name, history, { match, query }) {
constructor(name, history, { match, query, exact }) {
/** @internal */
this._interceptionEntries = [];
/** @internal */
this._matched = false;

@@ -22,2 +24,3 @@ /** @internal */

}
this._allowExact = exact;
}

@@ -86,10 +89,21 @@ /**

/**
* Perform an action if this `RouteMatch` matches.
* @param action A callback to perform this action.
* @param exact Perform this action only if it's an exact match.
* Intercept route matching if this `RouteMatch` matches.
* @param interception The interception callback.
* @param exact Intercept only if it's an exact match.
*/
$action(action, exact = false) {
$intercept(interception, exact = false) {
this._interceptionEntries.push({
interception,
exact,
});
}
/**
* Perform a reaction if this `RouteMatch` matches.
* @param reaction A callback to perform this reaction.
* @param exact Perform this reaction only if it's an exact match.
*/
$react(reaction, exact = false) {
mobx_1.autorun(() => {
if (exact ? this.$exact : this.$matched) {
requestAnimationFrame(() => action());
_utils_1.then(() => reaction());
}

@@ -99,39 +113,6 @@ });

/** @internal */
_update(skipped, upperRest, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict) {
let { current, rest } = this._match(skipped, upperRest);
let name = this.$name;
let matched = current !== undefined;
let exact = matched && rest === '';
let matchPattern = this._matchPattern;
let pathFragmentDict = Object.assign({}, upperPathFragmentDict, { [name]: typeof matchPattern === 'string' ? matchPattern : current });
let paramFragmentDict = Object.assign({}, upperParamFragmentDict, (typeof matchPattern === 'string' ? undefined : { [name]: current }));
this._pathFragments = pathFragmentDict;
let queryKeys = this._queryKeys;
let queryDict = queryKeys
? matched
? queryKeys.reduce((dict, key) => {
let value = sourceQueryDict[key];
if (value !== undefined) {
dict[key] = sourceQueryDict[key];
}
return dict;
}, {})
: {}
: undefined;
this._sourceQuery = sourceQueryDict;
this._params = Object.assign({}, paramFragmentDict, queryDict);
this._matched = matched;
this._exact = exact;
return {
matched,
rest,
pathFragmentDict,
paramFragmentDict,
};
}
/** @internal */
_match(skipped, rest) {
if (skipped || !rest) {
_match(rest) {
if (!rest) {
return {
current: undefined,
fragment: undefined,
rest: '',

@@ -148,3 +129,3 @@ };

return {
current: pattern,
fragment: pattern,
rest: rest.slice(pattern.length),

@@ -155,3 +136,3 @@ };

return {
current: undefined,
fragment: undefined,
rest: '',

@@ -169,3 +150,3 @@ };

return {
current: matched,
fragment: matched,
rest: rest.slice(matched.length),

@@ -176,3 +157,3 @@ };

return {
current: undefined,
fragment: undefined,
rest: '',

@@ -183,2 +164,51 @@ };

}
/** @internal */
_intercept(exact) {
let entries = this._interceptionEntries;
if (!exact) {
entries = entries.filter(entry => !entry.exact);
}
for (let { interception } of entries) {
let result = interception();
if (result === true || result === undefined) {
continue;
}
if (result === false) {
return false;
}
if (typeof result === 'string') {
return result;
}
throw new Error('Invalid interception result');
}
return undefined;
}
/** @internal */
_update(matched, exact, fragment, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict) {
let name = this.$name;
let matchPattern = this._matchPattern;
let pathFragmentDict = Object.assign({}, upperPathFragmentDict, { [name]: typeof matchPattern === 'string' ? matchPattern : fragment });
let paramFragmentDict = Object.assign({}, upperParamFragmentDict, (typeof matchPattern === 'string' ? undefined : { [name]: fragment }));
this._pathFragments = pathFragmentDict;
let queryKeys = this._queryKeys;
let queryDict = queryKeys
? matched
? queryKeys.reduce((dict, key) => {
let value = sourceQueryDict[key];
if (value !== undefined) {
dict[key] = sourceQueryDict[key];
}
return dict;
}, {})
: {}
: undefined;
this._sourceQuery = sourceQueryDict;
this._params = Object.assign({}, paramFragmentDict, queryDict);
this._matched = matched;
this._exact = exact;
return {
pathFragmentDict,
paramFragmentDict,
};
}
}

@@ -185,0 +215,0 @@ RouteMatch.fragment = /[^/]+/;

@@ -21,2 +21,7 @@ import { History } from 'history';

export declare type RouterType<TRouteSchemaDict> = Router & RouteMatchFragmentType<TRouteSchemaDict, never>;
export interface RouteMatchEntry {
match: RouteMatch;
exact: boolean;
fragment: string;
}
export interface RouterOptions {

@@ -23,0 +28,0 @@ /**

@@ -5,2 +5,3 @@ "use strict";

const hyphenate_1 = tslib_1.__importDefault(require("hyphenate"));
const _utils_1 = require("./@utils");
const route_match_1 = require("./route-match");

@@ -17,3 +18,11 @@ const DEFAULT_FRAGMENT_MATCHER_CALLBACK = key => hyphenate_1.default(key, { lowerCase: true });

}, {});
this._pushRouteChange(this, false, pathname, {}, {}, queryDict);
let matchResult = this._match(this, pathname);
if (typeof matchResult === 'string') {
this._history.replace(matchResult);
return;
}
let routeMatchEntryMap = new Map(matchResult
? matchResult.map((entry) => [entry.match, entry])
: undefined);
this._update(this, routeMatchEntryMap, {}, {}, queryDict);
};

@@ -23,21 +32,81 @@ this._history = history;

fragmentMatcher || DEFAULT_FRAGMENT_MATCHER_CALLBACK;
this._children = this._buildRouteMatches(this, schema);
history.listen(this._onLocationChange);
this._onLocationChange(history.location);
this._children = this._build(this, schema);
this._update(this, new Map(), {}, {}, {});
_utils_1.then(() => {
history.listen(this._onLocationChange);
this._onLocationChange(history.location);
});
}
/** @internal */
_pushRouteChange(target, skipped, upperRest, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict) {
if (!target._children) {
return;
}
for (let routeMatch of target._children) {
let { matched, rest, pathFragmentDict, paramFragmentDict, } = routeMatch._update(skipped, upperRest, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict);
_match(target, upperRest) {
for (let routeMatch of target._children || []) {
let { fragment, rest } = routeMatch._match(upperRest);
let matched = fragment !== undefined;
let exact = matched && rest === '';
if (matched) {
skipped = true;
let interceptionResult = routeMatch._intercept(exact);
if (typeof interceptionResult === 'string') {
return interceptionResult;
}
else if (interceptionResult === false) {
matched = false;
exact = false;
}
}
this._pushRouteChange(routeMatch, !matched, rest, pathFragmentDict, paramFragmentDict, sourceQueryDict);
if (!matched) {
continue;
}
if (exact) {
if (!routeMatch._children || routeMatch._allowExact) {
return [
{
match: routeMatch,
fragment: fragment,
exact: true,
},
];
}
else {
continue;
}
}
let result = this._match(routeMatch, rest);
if (typeof result === 'string') {
return result;
}
else if (result) {
return [
{
match: routeMatch,
fragment: fragment,
exact: false,
},
...result,
];
}
}
return undefined;
}
/** @internal */
_buildRouteMatches(target, schemaDict) {
_update(target, routeMatchEntryMap, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict) {
for (let routeMatch of target._children || []) {
let entry = routeMatchEntryMap.get(routeMatch);
let matched;
let exact;
let fragment;
if (entry) {
matched = true;
exact = entry.exact;
fragment = entry.fragment;
}
else {
matched = false;
exact = false;
}
let { pathFragmentDict, paramFragmentDict } = routeMatch._update(matched, exact, fragment, upperPathFragmentDict, upperParamFragmentDict, sourceQueryDict);
this._update(routeMatch, routeMatchEntryMap, pathFragmentDict, paramFragmentDict, sourceQueryDict);
}
}
/** @internal */
_build(target, schemaDict) {
let routeMatches = [];

@@ -48,4 +117,8 @@ for (let [key, schema] of Object.entries(schemaDict)) {

}
let { $match: match = this._fragmentMatcher(key), $query: query, $children: children, } = schema;
let routeMatch = new route_match_1.RouteMatch(key, this._history, { match, query });
let { $match: match = this._fragmentMatcher(key), $query: query, $exact: exact = false, $children: children, } = schema;
let routeMatch = new route_match_1.RouteMatch(key, this._history, {
match,
query,
exact,
});
routeMatches.push(routeMatch);

@@ -56,3 +129,3 @@ target[key] = routeMatch;

}
routeMatch._children = this._buildRouteMatches(routeMatch, children);
routeMatch._children = this._build(routeMatch, children);
}

@@ -59,0 +132,0 @@ return routeMatches;

@@ -5,2 +5,7 @@ import { Dict } from 'tslang';

$query?: Dict<boolean>;
/**
* Whether to allow exact match while if this route has children. Only
* applies if this route has children.
*/
$exact?: boolean;
$children?: RouteSchemaDict;

@@ -7,0 +12,0 @@ }

# Changelog
## 0.3.0 - unreleased
### Breaking changes
- The router will no longer match parent if it has no matching child.
- By default, the router will no longer match a route if it has children but the path is ended at the route itself. For example, if the path is `/account`, and route `account` has `$children`, `account` will not be matched by default. An option `$exact` is added for this scenario, and applies only to route schema with `$children`.
- `RouteMatch#$action` has been replaced with `RouteMatch#$react`.
## [0.2.1] - 2018-9-5

@@ -4,0 +12,0 @@

{
"name": "boring-router",
"version": "0.2.1",
"version": "0.3.0-alpha.0",
"description": "A light-weight, type-safe, yet reactive router service using MobX.",

@@ -37,2 +37,3 @@ "repository": {

"@types/history": "^4.7.0",
"@types/jest": "^23.3.1",
"@types/react": "^16.4.12",

@@ -55,3 +56,2 @@ "@types/react-dom": "^16.0.7",

"dependencies": {
"@types/jest": "^23.3.1",
"classnames": "^2.2.6",

@@ -58,0 +58,0 @@ "hyphenate": "^0.2.4",

@@ -101,3 +101,3 @@ [![NPM Package](https://badge.fury.io/js/boring-router.svg)](https://www.npmjs.com/package/boring-router)

$replace(params?: Partial<TParamDict>, preserveQuery?: boolean): void;
$action(action: RouteMatchAction, exact?: boolean): void;
$react(reaction: RouteMatchReaction, exact?: boolean): void;
}

@@ -213,9 +213,9 @@ ```

- [Action](examples/action/main.tsx)
- [Reaction](examples/reaction/main.tsx)
Take an action on route match.
Take a reaction on route match.
```tsx
router.account.$action(() => {
router.about.$replace({source: 'action'});
router.account.$react(() => {
router.about.$replace({source: 'reaction'});
});

@@ -222,0 +222,0 @@ ```

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