boring-router
Advanced tools
Comparing version 0.3.0-alpha.45 to 0.3.0-alpha.46
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.tolerate = exports.testPathPrefix = exports.parseSearch = exports.parseRef = exports.buildRef = exports.buildPath = exports.hasOwnProperty = void 0; | ||
const _hasOwnProperty = Object.prototype.hasOwnProperty; | ||
@@ -4,0 +5,0 @@ function hasOwnProperty(object, name) { |
@@ -14,7 +14,7 @@ export declare type HistoryChangeCallbackRemovalHandler = () => void; | ||
private changeCallbackSet; | ||
protected abstract readonly snapshot: HistorySnapshot<TEntryId, TData>; | ||
protected abstract get snapshot(): HistorySnapshot<TEntryId, TData>; | ||
listen(callback: HistoryChangeCallback<TEntryId, TData>): HistoryChangeCallbackRemovalHandler; | ||
readonly ref: string; | ||
readonly index: number; | ||
readonly length: number; | ||
get ref(): string; | ||
get index(): number; | ||
get length(): number; | ||
abstract getHRefByRef(ref: string): string; | ||
@@ -21,0 +21,0 @@ abstract getRefByHRef(href: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getActiveHistoryEntry = exports.getActiveHistoryEntryIndex = exports.isHistoryEntryEqual = exports.AbstractHistory = void 0; | ||
class History { | ||
@@ -4,0 +5,0 @@ constructor() { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.MemoryHistory = void 0; | ||
const history_1 = require("./history"); | ||
@@ -4,0 +5,0 @@ const SNAP_PROMISE = Promise.resolve(); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ReadOnlyHistory = void 0; | ||
const history_1 = require("./history"); | ||
@@ -4,0 +5,0 @@ class ReadOnlyHistory extends history_1.AbstractHistory { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RouteBuilder = void 0; | ||
const _utils_1 = require("./@utils"); | ||
@@ -4,0 +5,0 @@ class RouteBuilder { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NextRouteMatch = void 0; | ||
const route_match_shared_1 = require("./route-match-shared"); | ||
@@ -4,0 +5,0 @@ class NextRouteMatch extends route_match_shared_1.RouteMatchShared { |
@@ -47,15 +47,15 @@ import { Dict, EmptyObjectPatch } from 'tslang'; | ||
*/ | ||
readonly $params: TParamDict; | ||
get $params(): TParamDict; | ||
/** | ||
* A reactive value indicates whether this route is exactly matched. | ||
*/ | ||
readonly $exact: boolean; | ||
get $exact(): boolean; | ||
/** | ||
* A reactive value indicates whether this route is matched. | ||
*/ | ||
readonly $matched: boolean; | ||
get $matched(): boolean; | ||
/** | ||
* Get the deepest matching descendant. | ||
*/ | ||
readonly $rest: this; | ||
get $rest(): this; | ||
$(params?: Partial<TParamDict> & EmptyObjectPatch): RouteBuilder<TGroupName>; | ||
@@ -62,0 +62,0 @@ $ref(params?: Partial<TParamDict> & EmptyObjectPatch): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RouteMatchShared = exports.ROUTE_MATCH_START_ANCHOR_PATTERN = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,167 +8,170 @@ const mobx_1 = require("mobx"); | ||
exports.ROUTE_MATCH_START_ANCHOR_PATTERN = Symbol('^'); | ||
class RouteMatchShared { | ||
constructor(name, router, source, parent, history, { match, query, group }) { | ||
this.$name = name; | ||
this.$group = group; | ||
this.$parent = parent; | ||
this.$router = router; | ||
this._source = source; | ||
this._history = history; | ||
if (match instanceof RegExp && match.global) { | ||
throw new Error('Expecting a non-global regular expression as match pattern'); | ||
let RouteMatchShared = /** @class */ (() => { | ||
class RouteMatchShared { | ||
constructor(name, router, source, parent, history, { match, query, group }) { | ||
this.$name = name; | ||
this.$group = group; | ||
this.$parent = parent; | ||
this.$router = router; | ||
this._source = source; | ||
this._history = history; | ||
if (match instanceof RegExp && match.global) { | ||
throw new Error('Expecting a non-global regular expression as match pattern'); | ||
} | ||
this._matchPattern = match; | ||
this._queryKeySet = new Set([ | ||
...(query ? Object.keys(query) : []), | ||
...(parent ? parent._queryKeySet : []), | ||
]); | ||
} | ||
this._matchPattern = match; | ||
this._queryKeySet = new Set([ | ||
...(query ? Object.keys(query) : []), | ||
...(parent ? parent._queryKeySet : []), | ||
]); | ||
} | ||
/** | ||
* A dictionary of the combination of query string and segments. | ||
*/ | ||
get $params() { | ||
return { | ||
...this._paramSegments, | ||
...this._query, | ||
}; | ||
} | ||
/** | ||
* A reactive value indicates whether this route is exactly matched. | ||
*/ | ||
get $exact() { | ||
let entry = this._matchEntry; | ||
return !!entry && entry.exact; | ||
} | ||
/** | ||
* A reactive value indicates whether this route is matched. | ||
*/ | ||
get $matched() { | ||
return !!this._matchEntry; | ||
} | ||
/** | ||
* Get the deepest matching descendant. | ||
*/ | ||
get $rest() { | ||
if (!this.$matched || this.$exact) { | ||
return this; | ||
/** | ||
* A dictionary of the combination of query string and segments. | ||
*/ | ||
get $params() { | ||
return { | ||
...this._paramSegments, | ||
...this._query, | ||
}; | ||
} | ||
let children = this._children; | ||
let matchingChild = children && children.find(match => match.$matched); | ||
return matchingChild ? matchingChild.$rest : this; | ||
} | ||
/** @internal */ | ||
get _matchEntry() { | ||
return this._getMatchEntry(this._source); | ||
} | ||
/** @internal */ | ||
get _segment() { | ||
let entry = this._matchEntry; | ||
return entry && entry.segment; | ||
} | ||
/** @internal */ | ||
get _paramSegments() { | ||
let parent = this.$parent; | ||
let upperSegmentDict = parent && parent._paramSegments; | ||
let matchPattern = this._matchPattern; | ||
let segment = this._segment; | ||
if (typeof matchPattern === 'string') { | ||
/** | ||
* A reactive value indicates whether this route is exactly matched. | ||
*/ | ||
get $exact() { | ||
let entry = this._matchEntry; | ||
return !!entry && entry.exact; | ||
} | ||
/** | ||
* A reactive value indicates whether this route is matched. | ||
*/ | ||
get $matched() { | ||
return !!this._matchEntry; | ||
} | ||
/** | ||
* Get the deepest matching descendant. | ||
*/ | ||
get $rest() { | ||
if (!this.$matched || this.$exact) { | ||
return this; | ||
} | ||
let children = this._children; | ||
let matchingChild = children && children.find(match => match.$matched); | ||
return matchingChild ? matchingChild.$rest : this; | ||
} | ||
/** @internal */ | ||
get _matchEntry() { | ||
return this._getMatchEntry(this._source); | ||
} | ||
/** @internal */ | ||
get _segment() { | ||
let entry = this._matchEntry; | ||
return entry && entry.segment; | ||
} | ||
/** @internal */ | ||
get _paramSegments() { | ||
let parent = this.$parent; | ||
let upperSegmentDict = parent && parent._paramSegments; | ||
let matchPattern = this._matchPattern; | ||
let segment = this._segment; | ||
if (typeof matchPattern === 'string') { | ||
return { | ||
...upperSegmentDict, | ||
}; | ||
} | ||
return { | ||
...upperSegmentDict, | ||
[this.$name]: segment, | ||
}; | ||
} | ||
return { | ||
...upperSegmentDict, | ||
[this.$name]: segment, | ||
}; | ||
} | ||
/** @internal */ | ||
get _pathSegments() { | ||
let parent = this.$parent; | ||
let upperSegmentDict = parent && parent._pathSegments; | ||
let matchPattern = this._matchPattern; | ||
let segment = this._segment; | ||
if (typeof matchPattern === 'symbol' && | ||
matchPattern === exports.ROUTE_MATCH_START_ANCHOR_PATTERN) { | ||
/** @internal */ | ||
get _pathSegments() { | ||
let parent = this.$parent; | ||
let upperSegmentDict = parent && parent._pathSegments; | ||
let matchPattern = this._matchPattern; | ||
let segment = this._segment; | ||
if (typeof matchPattern === 'symbol' && | ||
matchPattern === exports.ROUTE_MATCH_START_ANCHOR_PATTERN) { | ||
return { | ||
...upperSegmentDict, | ||
}; | ||
} | ||
return { | ||
...upperSegmentDict, | ||
[this.$name]: typeof matchPattern === 'string' ? matchPattern : segment, | ||
}; | ||
} | ||
return { | ||
...upperSegmentDict, | ||
[this.$name]: typeof matchPattern === 'string' ? matchPattern : segment, | ||
}; | ||
} | ||
/** @internal */ | ||
get _rest() { | ||
let entry = this._matchEntry; | ||
return entry ? entry.rest : ''; | ||
} | ||
/** @internal */ | ||
get _query() { | ||
let sourceQueryDict = this._source.queryDict; | ||
return Array.from(this._queryKeySet).reduce((dict, key) => { | ||
let value = sourceQueryDict[key]; | ||
if (value !== undefined) { | ||
dict[key] = sourceQueryDict[key]; | ||
} | ||
return dict; | ||
}, {}); | ||
} | ||
$(params) { | ||
return new route_builder_1.RouteBuilder(new Map(), this._source.queryDict, this.$router, [{ match: this, params }]); | ||
} | ||
$ref(params) { | ||
return this.$(params).$ref(); | ||
} | ||
$href(params) { | ||
return this.$(params).$href(); | ||
} | ||
$push(params, { onComplete, ...options } = {}) { | ||
this._build(params, options).$push({ onComplete }); | ||
} | ||
$replace(params, { onComplete, ...options } = {}) { | ||
this._build(params, options).$replace({ onComplete }); | ||
} | ||
/** @internal */ | ||
_build(params = {}, { leave = false, leaves = [] } = {}) { | ||
if (typeof leaves === 'string') { | ||
leaves = [leaves]; | ||
/** @internal */ | ||
get _rest() { | ||
let entry = this._matchEntry; | ||
return entry ? entry.rest : ''; | ||
} | ||
if (leave) { | ||
let group = this.$group; | ||
if (group === undefined) { | ||
throw new Error('Cannot leave primary route'); | ||
/** @internal */ | ||
get _query() { | ||
let sourceQueryDict = this._source.queryDict; | ||
return Array.from(this._queryKeySet).reduce((dict, key) => { | ||
let value = sourceQueryDict[key]; | ||
if (value !== undefined) { | ||
dict[key] = sourceQueryDict[key]; | ||
} | ||
return dict; | ||
}, {}); | ||
} | ||
$(params) { | ||
return new route_builder_1.RouteBuilder(new Map(), this._source.queryDict, this.$router, [{ match: this, params }]); | ||
} | ||
$ref(params) { | ||
return this.$(params).$ref(); | ||
} | ||
$href(params) { | ||
return this.$(params).$href(); | ||
} | ||
$push(params, { onComplete, ...options } = {}) { | ||
this._build(params, options).$push({ onComplete }); | ||
} | ||
$replace(params, { onComplete, ...options } = {}) { | ||
this._build(params, options).$replace({ onComplete }); | ||
} | ||
/** @internal */ | ||
_build(params = {}, { leave = false, leaves = [] } = {}) { | ||
if (typeof leaves === 'string') { | ||
leaves = [leaves]; | ||
} | ||
leaves.push(group); | ||
if (leave) { | ||
let group = this.$group; | ||
if (group === undefined) { | ||
throw new Error('Cannot leave primary route'); | ||
} | ||
leaves.push(group); | ||
} | ||
return this._getBuilder() | ||
.$(this, params) | ||
.$leave(leaves); | ||
} | ||
return this._getBuilder() | ||
.$(this, params) | ||
.$leave(leaves); | ||
} | ||
} | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "$params", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "$rest", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_matchEntry", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_segment", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_paramSegments", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_pathSegments", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_rest", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_query", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "$params", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "$rest", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_matchEntry", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_segment", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_paramSegments", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_pathSegments", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_rest", null); | ||
tslib_1.__decorate([ | ||
mobx_1.computed | ||
], RouteMatchShared.prototype, "_query", null); | ||
return RouteMatchShared; | ||
})(); | ||
exports.RouteMatchShared = RouteMatchShared; | ||
//# sourceMappingURL=route-match-shared.js.map |
@@ -91,7 +91,9 @@ import { IAutorunOptions, IReactionDisposer, IReactionPublic } from 'mobx'; | ||
exact: boolean | string; | ||
metadata: object | undefined; | ||
} | ||
export declare class RouteMatch<TParamDict extends GeneralParamDict = GeneralParamDict, TNextRouteMatch extends NextRouteMatch<TParamDict> = NextRouteMatch<TParamDict>, TSpecificGroupName extends string | undefined = string | undefined, TGroupName extends string = string> extends RouteMatchShared<TParamDict, TSpecificGroupName, TGroupName> { | ||
export declare class RouteMatch<TParamDict extends GeneralParamDict = GeneralParamDict, TNextRouteMatch extends NextRouteMatch<TParamDict> = NextRouteMatch<TParamDict>, TSpecificGroupName extends string | undefined = string | undefined, TGroupName extends string = string, TMetadata extends object = object> extends RouteMatchShared<TParamDict, TSpecificGroupName, TGroupName> { | ||
readonly $parent: RouteMatch | undefined; | ||
readonly $next: TNextRouteMatch; | ||
constructor(name: string, router: Router<TGroupName>, source: RouteSource, parent: RouteMatch | undefined, extension: object | undefined, history: IHistory, { exact, ...sharedOptions }: RouteMatchOptions); | ||
get $metadata(): TMetadata; | ||
constructor(name: string, router: Router<TGroupName>, source: RouteSource, parent: RouteMatch | undefined, extension: object | undefined, history: IHistory, { exact, metadata, ...sharedOptions }: RouteMatchOptions); | ||
$beforeEnter(callback: RouteBeforeEnterCallback<this>): RouteHookRemovalCallback; | ||
@@ -98,0 +100,0 @@ $beforeUpdate(callback: RouteBeforeUpdateCallback<this>, options?: RouteBeforeUpdateOptions): RouteHookRemovalCallback; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RouteMatch = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -7,202 +8,195 @@ const mobx_1 = require("mobx"); | ||
const route_match_shared_1 = require("./route-match-shared"); | ||
class RouteMatch extends route_match_shared_1.RouteMatchShared { | ||
constructor(name, router, source, parent, extension, history, { exact, ...sharedOptions }) { | ||
super(name, router, source, parent, history, sharedOptions); | ||
/** @internal */ | ||
this._beforeEnterCallbackSet = new Set(); | ||
/** @internal */ | ||
this._beforeUpdateEntrySet = new Set(); | ||
/** @internal */ | ||
this._beforeLeaveCallbackSet = new Set(); | ||
/** @internal */ | ||
this._afterEnterCallbackSet = new Set(); | ||
/** @internal */ | ||
this._afterUpdateEntrySet = new Set(); | ||
/** @internal */ | ||
this._afterLeaveCallbackSet = new Set(); | ||
/** @internal */ | ||
this._autorunEntrySet = new Set(); | ||
/** @internal */ | ||
this._autorunDisposers = []; | ||
if (extension) { | ||
for (let [key, defaultValue] of Object.entries(extension)) { | ||
Object.defineProperty(this, key, { | ||
get() { | ||
let service = this.$matched | ||
? this._service | ||
: undefined; | ||
return service ? service[key] : defaultValue; | ||
}, | ||
let RouteMatch = /** @class */ (() => { | ||
class RouteMatch extends route_match_shared_1.RouteMatchShared { | ||
constructor(name, router, source, parent, extension, history, { exact, metadata, ...sharedOptions }) { | ||
super(name, router, source, parent, history, sharedOptions); | ||
/** @internal */ | ||
this._beforeEnterCallbackSet = new Set(); | ||
/** @internal */ | ||
this._beforeUpdateEntrySet = new Set(); | ||
/** @internal */ | ||
this._beforeLeaveCallbackSet = new Set(); | ||
/** @internal */ | ||
this._afterEnterCallbackSet = new Set(); | ||
/** @internal */ | ||
this._afterUpdateEntrySet = new Set(); | ||
/** @internal */ | ||
this._afterLeaveCallbackSet = new Set(); | ||
/** @internal */ | ||
this._autorunEntrySet = new Set(); | ||
/** @internal */ | ||
this._autorunDisposers = []; | ||
if (extension) { | ||
for (let [key, defaultValue] of Object.entries(extension)) { | ||
Object.defineProperty(this, key, { | ||
get() { | ||
let service = this.$matched | ||
? this._service | ||
: undefined; | ||
return service ? service[key] : defaultValue; | ||
}, | ||
}); | ||
} | ||
} | ||
this._metadata = metadata; | ||
this._allowExact = exact; | ||
} | ||
get $metadata() { | ||
var _a, _b; | ||
return Object.assign((_b = (_a = this.$parent) === null || _a === void 0 ? void 0 : _a.$metadata) !== null && _b !== void 0 ? _b : {}, this._metadata); | ||
} | ||
$beforeEnter(callback) { | ||
this._beforeEnterCallbackSet.add(callback); | ||
return () => { | ||
this._beforeEnterCallbackSet.delete(callback); | ||
}; | ||
} | ||
$beforeUpdate(callback, options) { | ||
let entry = { | ||
callback, | ||
options, | ||
}; | ||
this._beforeUpdateEntrySet.add(entry); | ||
return () => { | ||
this._beforeUpdateEntrySet.delete(entry); | ||
}; | ||
} | ||
$beforeLeave(callback) { | ||
this._beforeLeaveCallbackSet.add(callback); | ||
return () => { | ||
this._beforeLeaveCallbackSet.delete(callback); | ||
}; | ||
} | ||
$afterEnter(callback) { | ||
this._afterEnterCallbackSet.add(callback); | ||
return () => { | ||
this._afterEnterCallbackSet.delete(callback); | ||
}; | ||
} | ||
$afterUpdate(callback, options) { | ||
let afterUpdateEntry = { | ||
callback, | ||
options, | ||
}; | ||
this._afterUpdateEntrySet.add(afterUpdateEntry); | ||
return () => { | ||
this._afterUpdateEntrySet.delete(afterUpdateEntry); | ||
}; | ||
} | ||
$afterLeave(callback) { | ||
this._afterLeaveCallbackSet.add(callback); | ||
return () => { | ||
this._afterLeaveCallbackSet.delete(callback); | ||
}; | ||
} | ||
$autorun(view, options) { | ||
let autorunEntry = { view, options }; | ||
this._autorunEntrySet.add(autorunEntry); | ||
if (this.$matched) { | ||
_utils_1.tolerate(() => { | ||
this._autorunDisposers.push(mobx_1.autorun(view, options)); | ||
}); | ||
} | ||
return () => { | ||
this._autorunEntrySet.delete(autorunEntry); | ||
}; | ||
} | ||
this._allowExact = exact; | ||
} | ||
$beforeEnter(callback) { | ||
this._beforeEnterCallbackSet.add(callback); | ||
return () => { | ||
this._beforeEnterCallbackSet.delete(callback); | ||
}; | ||
} | ||
$beforeUpdate(callback, options) { | ||
let entry = { | ||
callback, | ||
options, | ||
}; | ||
this._beforeUpdateEntrySet.add(entry); | ||
return () => { | ||
this._beforeUpdateEntrySet.delete(entry); | ||
}; | ||
} | ||
$beforeLeave(callback) { | ||
this._beforeLeaveCallbackSet.add(callback); | ||
return () => { | ||
this._beforeLeaveCallbackSet.delete(callback); | ||
}; | ||
} | ||
$afterEnter(callback) { | ||
this._afterEnterCallbackSet.add(callback); | ||
return () => { | ||
this._afterEnterCallbackSet.delete(callback); | ||
}; | ||
} | ||
$afterUpdate(callback, options) { | ||
let afterUpdateEntry = { | ||
callback, | ||
options, | ||
}; | ||
this._afterUpdateEntrySet.add(afterUpdateEntry); | ||
return () => { | ||
this._afterUpdateEntrySet.delete(afterUpdateEntry); | ||
}; | ||
} | ||
$afterLeave(callback) { | ||
this._afterLeaveCallbackSet.add(callback); | ||
return () => { | ||
this._afterLeaveCallbackSet.delete(callback); | ||
}; | ||
} | ||
$autorun(view, options) { | ||
let autorunEntry = { view, options }; | ||
this._autorunEntrySet.add(autorunEntry); | ||
if (this.$matched) { | ||
_utils_1.tolerate(() => { | ||
this._autorunDisposers.push(mobx_1.autorun(view, options)); | ||
}); | ||
$intercept(callback, options) { | ||
let beforeUpdateEntry = { | ||
callback, | ||
options, | ||
}; | ||
this._beforeEnterCallbackSet.add(callback); | ||
this._beforeUpdateEntrySet.add(beforeUpdateEntry); | ||
return () => { | ||
this._beforeEnterCallbackSet.delete(callback); | ||
this._beforeUpdateEntrySet.delete(beforeUpdateEntry); | ||
}; | ||
} | ||
return () => { | ||
this._autorunEntrySet.delete(autorunEntry); | ||
}; | ||
} | ||
$intercept(callback, options) { | ||
let beforeUpdateEntry = { | ||
callback, | ||
options, | ||
}; | ||
this._beforeEnterCallbackSet.add(callback); | ||
this._beforeUpdateEntrySet.add(beforeUpdateEntry); | ||
return () => { | ||
this._beforeEnterCallbackSet.delete(callback); | ||
this._beforeUpdateEntrySet.delete(beforeUpdateEntry); | ||
}; | ||
} | ||
$react(callback, afterUpdateOptions) { | ||
let afterUpdateEntry = { | ||
callback, | ||
options: afterUpdateOptions, | ||
}; | ||
this._afterEnterCallbackSet.add(callback); | ||
this._afterUpdateEntrySet.add(afterUpdateEntry); | ||
return () => { | ||
this._afterEnterCallbackSet.delete(callback); | ||
this._afterUpdateEntrySet.delete(afterUpdateEntry); | ||
}; | ||
} | ||
$service(factory) { | ||
if (this._serviceFactory) { | ||
throw new Error(`Service has already been defined for "${this.$name}"`); | ||
$react(callback, afterUpdateOptions) { | ||
let afterUpdateEntry = { | ||
callback, | ||
options: afterUpdateOptions, | ||
}; | ||
this._afterEnterCallbackSet.add(callback); | ||
this._afterUpdateEntrySet.add(afterUpdateEntry); | ||
return () => { | ||
this._afterEnterCallbackSet.delete(callback); | ||
this._afterUpdateEntrySet.delete(afterUpdateEntry); | ||
}; | ||
} | ||
this._serviceFactory = factory; | ||
return this; | ||
} | ||
$parallel(options) { | ||
if (this.$group) { | ||
throw new Error('Parallel whitelist can only be set on primary routes'); | ||
$service(factory) { | ||
if (this._serviceFactory) { | ||
throw new Error(`Service has already been defined for "${this.$name}"`); | ||
} | ||
this._serviceFactory = factory; | ||
return this; | ||
} | ||
let { groups = [], matches = [] } = options; | ||
let parent = this.$parent; | ||
if (parent instanceof RouteMatch && parent._parallel) { | ||
let { groups: parentGroups = [], matches: parentMatches = [], } = parent._parallel; | ||
let parentGroupSet = new Set(parentGroups); | ||
let parentMatchSet = new Set(parentMatches); | ||
let groupsBeingSubsetOfParents = groups.every(group => parentGroupSet.has(group)); | ||
if (!groupsBeingSubsetOfParents) { | ||
throw new Error("Parallel group can only be a subset of its parent's groups"); | ||
$parallel(options) { | ||
if (this.$group) { | ||
throw new Error('Parallel whitelist can only be set on primary routes'); | ||
} | ||
let matchesBeingSubsetOfParents = matches.every(match => { | ||
if (typeof match.$group === 'string' && | ||
parentGroupSet.has(match.$group)) { | ||
return true; | ||
let { groups = [], matches = [] } = options; | ||
let parent = this.$parent; | ||
if (parent instanceof RouteMatch && parent._parallel) { | ||
let { groups: parentGroups = [], matches: parentMatches = [], } = parent._parallel; | ||
let parentGroupSet = new Set(parentGroups); | ||
let parentMatchSet = new Set(parentMatches); | ||
let groupsBeingSubsetOfParents = groups.every(group => parentGroupSet.has(group)); | ||
if (!groupsBeingSubsetOfParents) { | ||
throw new Error("Parallel group can only be a subset of its parent's groups"); | ||
} | ||
let current = match; | ||
while (current) { | ||
if (parentMatchSet.has(current)) { | ||
let matchesBeingSubsetOfParents = matches.every(match => { | ||
if (typeof match.$group === 'string' && | ||
parentGroupSet.has(match.$group)) { | ||
return true; | ||
} | ||
current = current.$parent; | ||
let current = match; | ||
while (current) { | ||
if (parentMatchSet.has(current)) { | ||
return true; | ||
} | ||
current = current.$parent; | ||
} | ||
return false; | ||
}); | ||
if (!matchesBeingSubsetOfParents) { | ||
throw new Error("Parallel match can only be a subset of its parent's matches"); | ||
} | ||
return false; | ||
}); | ||
if (!matchesBeingSubsetOfParents) { | ||
throw new Error("Parallel match can only be a subset of its parent's matches"); | ||
} | ||
} | ||
this._parallel = options; | ||
let children = this._children || []; | ||
for (let child of children) { | ||
if (child._parallel && | ||
(!parent || parent._parallel !== child._parallel)) { | ||
throw new Error('Parallel options can only be specified in a top-down fashion'); | ||
this._parallel = options; | ||
let children = this._children || []; | ||
for (let child of children) { | ||
if (child._parallel && | ||
(!parent || parent._parallel !== child._parallel)) { | ||
throw new Error('Parallel options can only be specified in a top-down fashion'); | ||
} | ||
child.$parallel(options); | ||
} | ||
child.$parallel(options); | ||
} | ||
} | ||
/** @internal */ | ||
_match(upperRest) { | ||
let pattern = this._matchPattern; | ||
if (typeof pattern === 'symbol') { | ||
if (pattern === route_match_shared_1.ROUTE_MATCH_START_ANCHOR_PATTERN) { | ||
return { | ||
matched: true, | ||
exactlyMatched: false, | ||
segment: undefined, | ||
rest: upperRest, | ||
}; | ||
/** @internal */ | ||
_match(upperRest) { | ||
let pattern = this._matchPattern; | ||
if (typeof pattern === 'symbol') { | ||
if (pattern === route_match_shared_1.ROUTE_MATCH_START_ANCHOR_PATTERN) { | ||
return { | ||
matched: true, | ||
exactlyMatched: false, | ||
segment: undefined, | ||
rest: upperRest, | ||
}; | ||
} | ||
throw new Error(`Unrecognized symbol pattern [${pattern.description}]`); | ||
} | ||
throw new Error(`Unrecognized symbol pattern [${pattern.description}]`); | ||
} | ||
let segment; | ||
let rest; | ||
if (upperRest) { | ||
if (!upperRest.startsWith('/')) { | ||
throw new Error(`Expecting rest of path to be started with "/", but got ${JSON.stringify(upperRest)} instead`); | ||
} | ||
upperRest = upperRest.slice(1); | ||
if (typeof pattern === 'string') { | ||
if (_utils_1.testPathPrefix(upperRest, pattern)) { | ||
segment = pattern; | ||
rest = upperRest.slice(pattern.length); | ||
let segment; | ||
let rest; | ||
if (upperRest) { | ||
if (!upperRest.startsWith('/')) { | ||
throw new Error(`Expecting rest of path to be started with "/", but got ${JSON.stringify(upperRest)} instead`); | ||
} | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
} | ||
} | ||
else { | ||
let groups = pattern.exec(upperRest); | ||
if (groups) { | ||
let matched = groups[0]; | ||
if (_utils_1.testPathPrefix(upperRest, matched)) { | ||
segment = matched; | ||
rest = upperRest.slice(matched.length); | ||
upperRest = upperRest.slice(1); | ||
if (typeof pattern === 'string') { | ||
if (_utils_1.testPathPrefix(upperRest, pattern)) { | ||
segment = pattern; | ||
rest = upperRest.slice(pattern.length); | ||
} | ||
@@ -215,151 +209,166 @@ else { | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
let groups = pattern.exec(upperRest); | ||
if (groups) { | ||
let matched = groups[0]; | ||
if (_utils_1.testPathPrefix(upperRest, matched)) { | ||
segment = matched; | ||
rest = upperRest.slice(matched.length); | ||
} | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
} | ||
} | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
} | ||
let matched = segment !== undefined; | ||
let exactlyMatched = matched && rest === ''; | ||
if (exactlyMatched) { | ||
let allowExact = this._allowExact; | ||
if (typeof allowExact === 'string') { | ||
rest = `/${allowExact}`; | ||
else { | ||
segment = undefined; | ||
rest = ''; | ||
} | ||
else if (this._children && !allowExact) { | ||
matched = false; | ||
exactlyMatched = false; | ||
} | ||
} | ||
return { | ||
matched, | ||
exactlyMatched, | ||
segment, | ||
rest, | ||
}; | ||
} | ||
/** @internal */ | ||
async _beforeLeave() { | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeLeaveCallbackSet).map(callback => _utils_1.tolerate(callback)), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeLeave) { | ||
return _utils_1.tolerate(() => service.beforeLeave()); | ||
let matched = segment !== undefined; | ||
let exactlyMatched = matched && rest === ''; | ||
if (exactlyMatched) { | ||
let allowExact = this._allowExact; | ||
if (typeof allowExact === 'string') { | ||
rest = `/${allowExact}`; | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
/** @internal */ | ||
async _beforeEnter() { | ||
let next = this.$next; | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeEnterCallbackSet).map(callback => _utils_1.tolerate(callback, next)), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeEnter) { | ||
return _utils_1.tolerate(() => service.beforeEnter(next)); | ||
else if (this._children && !allowExact) { | ||
matched = false; | ||
exactlyMatched = false; | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
/** @internal */ | ||
async _beforeUpdate(triggeredByDescendants) { | ||
let next = this.$next; | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeUpdateEntrySet) | ||
.filter(({ options }) => triggeredByDescendants ? options && options.traceDescendants : true) | ||
.map(({ callback }) => _utils_1.tolerate(callback, next, { descendants: triggeredByDescendants })), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeUpdate) { | ||
return _utils_1.tolerate(() => service.beforeUpdate(next, { descendants: triggeredByDescendants })); | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
/** @internal */ | ||
async _afterLeave() { | ||
for (let autorunDisposer of this._autorunDisposers) { | ||
autorunDisposer(); | ||
} | ||
return { | ||
matched, | ||
exactlyMatched, | ||
segment, | ||
rest, | ||
}; | ||
} | ||
for (let callback of this._afterLeaveCallbackSet) { | ||
_utils_1.tolerate(callback); | ||
/** @internal */ | ||
async _beforeLeave() { | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeLeaveCallbackSet).map(callback => _utils_1.tolerate(callback)), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeLeave) { | ||
return _utils_1.tolerate(() => service.beforeLeave()); | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterLeave) { | ||
_utils_1.tolerate(() => service.afterLeave()); | ||
/** @internal */ | ||
async _beforeEnter() { | ||
let next = this.$next; | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeEnterCallbackSet).map(callback => _utils_1.tolerate(callback, next)), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeEnter) { | ||
return _utils_1.tolerate(() => service.beforeEnter(next)); | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
} | ||
/** @internal */ | ||
async _afterEnter() { | ||
for (let callback of this._afterEnterCallbackSet) { | ||
_utils_1.tolerate(callback); | ||
/** @internal */ | ||
async _beforeUpdate(triggeredByDescendants) { | ||
let next = this.$next; | ||
let results = await Promise.all([ | ||
...Array.from(this._beforeUpdateEntrySet) | ||
.filter(({ options }) => triggeredByDescendants ? options && options.traceDescendants : true) | ||
.map(({ callback }) => _utils_1.tolerate(callback, next, { descendants: triggeredByDescendants })), | ||
(async () => { | ||
let service = await this._getService(); | ||
if (service && service.beforeUpdate) { | ||
return _utils_1.tolerate(() => service.beforeUpdate(next, { descendants: triggeredByDescendants })); | ||
} | ||
})(), | ||
]); | ||
return !results.some(result => result === false); | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterEnter) { | ||
_utils_1.tolerate(() => service.afterEnter()); | ||
/** @internal */ | ||
async _afterLeave() { | ||
for (let autorunDisposer of this._autorunDisposers) { | ||
autorunDisposer(); | ||
} | ||
for (let callback of this._afterLeaveCallbackSet) { | ||
_utils_1.tolerate(callback); | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterLeave) { | ||
_utils_1.tolerate(() => service.afterLeave()); | ||
} | ||
} | ||
for (let autorunEntry of this._autorunEntrySet) { | ||
_utils_1.tolerate(() => { | ||
this._autorunDisposers.push(mobx_1.autorun(autorunEntry.view, autorunEntry.options)); | ||
}); | ||
/** @internal */ | ||
async _afterEnter() { | ||
for (let callback of this._afterEnterCallbackSet) { | ||
_utils_1.tolerate(callback); | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterEnter) { | ||
_utils_1.tolerate(() => service.afterEnter()); | ||
} | ||
for (let autorunEntry of this._autorunEntrySet) { | ||
_utils_1.tolerate(() => { | ||
this._autorunDisposers.push(mobx_1.autorun(autorunEntry.view, autorunEntry.options)); | ||
}); | ||
} | ||
} | ||
} | ||
/** @internal */ | ||
async _afterUpdate(triggeredByDescendants) { | ||
for (let { callback, options } of this._afterUpdateEntrySet) { | ||
if (triggeredByDescendants ? options && options.traceDescendants : true) { | ||
_utils_1.tolerate(callback, { descendants: triggeredByDescendants }); | ||
/** @internal */ | ||
async _afterUpdate(triggeredByDescendants) { | ||
for (let { callback, options } of this._afterUpdateEntrySet) { | ||
if (triggeredByDescendants ? options && options.traceDescendants : true) { | ||
_utils_1.tolerate(callback, { descendants: triggeredByDescendants }); | ||
} | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterUpdate) { | ||
_utils_1.tolerate(() => service.afterUpdate({ descendants: triggeredByDescendants })); | ||
} | ||
} | ||
let service = await this._getService(); | ||
if (service && service.afterUpdate) { | ||
_utils_1.tolerate(() => service.afterUpdate({ descendants: triggeredByDescendants })); | ||
/** @internal */ | ||
_getMatchEntry(source) { | ||
let matchToMatchEntryMap = source.groupToMatchToMatchEntryMapMap.get(this.$group); | ||
return matchToMatchEntryMap && matchToMatchEntryMap.get(this); | ||
} | ||
} | ||
/** @internal */ | ||
_getMatchEntry(source) { | ||
let matchToMatchEntryMap = source.groupToMatchToMatchEntryMapMap.get(this.$group); | ||
return matchToMatchEntryMap && matchToMatchEntryMap.get(this); | ||
} | ||
/** @internal */ | ||
_getBuilder() { | ||
return this.$router.$current; | ||
} | ||
/** @internal */ | ||
async _getService() { | ||
let serviceOrServicePromise = this._service || this._servicePromise; | ||
if (serviceOrServicePromise) { | ||
return serviceOrServicePromise; | ||
/** @internal */ | ||
_getBuilder() { | ||
return this.$router.$current; | ||
} | ||
let factory = this._serviceFactory; | ||
if (!factory) { | ||
return undefined; | ||
/** @internal */ | ||
async _getService() { | ||
let serviceOrServicePromise = this._service || this._servicePromise; | ||
if (serviceOrServicePromise) { | ||
return serviceOrServicePromise; | ||
} | ||
let factory = this._serviceFactory; | ||
if (!factory) { | ||
return undefined; | ||
} | ||
let output = _utils_1.tolerate(factory, this); | ||
if (output instanceof Promise) { | ||
return (this._servicePromise = output.then(service => { | ||
this._service = service; | ||
return service; | ||
})); | ||
} | ||
else { | ||
this._service = output; | ||
return output; | ||
} | ||
} | ||
let output = _utils_1.tolerate(factory, this); | ||
if (output instanceof Promise) { | ||
return (this._servicePromise = output.then(service => { | ||
this._service = service; | ||
return service; | ||
})); | ||
} | ||
else { | ||
this._service = output; | ||
return output; | ||
} | ||
} | ||
} | ||
RouteMatch.segment = /[^/]+/; | ||
RouteMatch.rest = /.+/; | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], RouteMatch.prototype, "_service", void 0); | ||
RouteMatch.segment = /[^/]+/; | ||
RouteMatch.rest = /.+/; | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], RouteMatch.prototype, "_service", void 0); | ||
return RouteMatch; | ||
})(); | ||
exports.RouteMatch = RouteMatch; | ||
//# sourceMappingURL=route-match.js.map |
@@ -20,8 +20,12 @@ import { Dict, EmptyObjectPatch } from 'tslang'; | ||
} | ||
interface RouteSchemaMetadataSection<TMetadata> { | ||
$metadata: TMetadata; | ||
} | ||
declare type RouteMatchMetadataType<TRouteSchema, TUpperMetadata> = TRouteSchema extends RouteSchemaMetadataSection<infer TMetadata> ? TMetadata & TUpperMetadata : TUpperMetadata; | ||
declare type RouteMatchExtensionType<TRouteSchema> = TRouteSchema extends RouteSchemaExtensionSection<infer TRouteMatchExtension> ? TRouteMatchExtension : {}; | ||
declare type RouteMatchSegmentType<TRouteSchemaDict, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string> = { | ||
[K in Extract<keyof TRouteSchemaDict, string>]: RouteMatchType<TRouteSchemaDict[K], TSegmentKey | FilterRouteMatchNonStringSegment<TRouteSchemaDict[K], K>, TQueryKey | Extract<keyof RouteQuerySchemaType<TRouteSchemaDict[K]>, string>, TSpecificGroupName, TGroupName>; | ||
declare type RouteMatchSegmentType<TRouteSchemaDict, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string, TMetadata extends object> = { | ||
[K in Extract<keyof TRouteSchemaDict, string>]: RouteMatchType<TRouteSchemaDict[K], TSegmentKey | FilterRouteMatchNonStringSegment<TRouteSchemaDict[K], K>, TQueryKey | Extract<keyof RouteQuerySchemaType<TRouteSchemaDict[K]>, string>, TSpecificGroupName, TGroupName, TMetadata>; | ||
}; | ||
declare type __RouteMatchType<TRouteSchema, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string, TParamDict extends Dict<string | undefined>> = RouteMatch<TParamDict, __NextRouteMatchType<TRouteSchema, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, TParamDict>, TSpecificGroupName, TGroupName> & RouteMatchSegmentType<NestedRouteSchemaDictType<TRouteSchema>, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName> & RouteMatchExtensionType<TRouteSchema>; | ||
export declare type RouteMatchType<TRouteSchema, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string> = __RouteMatchType<TRouteSchema, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, Record<TQueryKey, string | undefined> & Record<TSegmentKey, string>>; | ||
declare type __RouteMatchType<TRouteSchema, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string, TParamDict extends Dict<string | undefined>, TMetadata extends object> = RouteMatch<TParamDict, __NextRouteMatchType<TRouteSchema, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, TParamDict>, TSpecificGroupName, TGroupName, RouteMatchMetadataType<TRouteSchema, TMetadata>> & RouteMatchSegmentType<NestedRouteSchemaDictType<TRouteSchema>, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, RouteMatchMetadataType<TRouteSchema, TMetadata>> & RouteMatchExtensionType<TRouteSchema>; | ||
export declare type RouteMatchType<TRouteSchema, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string, TMetadata extends object> = __RouteMatchType<TRouteSchema, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, Record<TQueryKey, string | undefined> & Record<TSegmentKey, string>, TMetadata>; | ||
declare type NextRouteMatchSegmentType<TRouteSchemaDict, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string> = { | ||
@@ -32,5 +36,5 @@ [K in Extract<keyof TRouteSchemaDict, string>]: NextRouteMatchType<TRouteSchemaDict[K], TSegmentKey | FilterRouteMatchNonStringSegment<TRouteSchemaDict[K], K>, TQueryKey | Extract<keyof RouteQuerySchemaType<TRouteSchemaDict[K]>, string>, TSpecificGroupName, TGroupName>; | ||
declare type NextRouteMatchType<TRouteSchema, TSegmentKey extends string, TQueryKey extends string, TSpecificGroupName extends string | undefined, TGroupName extends string> = __NextRouteMatchType<TRouteSchema, TSegmentKey, TQueryKey, TSpecificGroupName, TGroupName, Record<TQueryKey, string | undefined> & Record<TSegmentKey, string>>; | ||
export declare type RootRouteMatchType<TRouteSchemaDict, TSpecificGroupName extends string | undefined, TGroupName extends string> = RouteMatchType<{ | ||
export declare type RootRouteMatchType<TRouteSchemaDict, TSpecificGroupName extends string | undefined, TGroupName extends string, TMetadata extends object = {}> = RouteMatchType<{ | ||
$children: TRouteSchemaDict; | ||
}, never, never, TSpecificGroupName, TGroupName>; | ||
}, never, never, TSpecificGroupName, TGroupName, TMetadata>; | ||
export declare type RouterOnLeave = (path: string) => void; | ||
@@ -57,6 +61,6 @@ export declare type RouterOnNavigateComplete = () => void; | ||
constructor(history: RouterHistory, { segmentMatcher }?: RouterOptions); | ||
readonly $current: RouteBuilder<TGroupName>; | ||
readonly $routing: boolean; | ||
readonly $next: RouteBuilder<TGroupName>; | ||
readonly $groups: TGroupName[]; | ||
get $current(): RouteBuilder<TGroupName>; | ||
get $routing(): boolean; | ||
get $next(): RouteBuilder<TGroupName>; | ||
get $groups(): TGroupName[]; | ||
$route<TPrimaryRouteSchemaDict extends RouteSchemaDict>(schema: TPrimaryRouteSchemaDict): RootRouteMatchType<TPrimaryRouteSchemaDict, undefined, TGroupName>; | ||
@@ -63,0 +67,0 @@ $route<TRouteSchemaDict extends RouteSchemaDict, TSpecificGroupName extends TGroupName>(group: TSpecificGroupName, schema: TRouteSchemaDict): RootRouteMatchType<TRouteSchemaDict, TSpecificGroupName, TGroupName>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Router = void 0; | ||
const tslib_1 = require("tslib"); | ||
@@ -12,313 +13,328 @@ const hyphenate_1 = tslib_1.__importDefault(require("hyphenate")); | ||
const DEFAULT_SEGMENT_MATCHER_CALLBACK = key => hyphenate_1.default(key, { lowerCase: true }); | ||
class Router { | ||
constructor(history, { segmentMatcher } = {}) { | ||
/** @internal */ | ||
this._source = mobx_1.observable({ | ||
groupToMatchToMatchEntryMapMap: new Map(), | ||
queryDict: {}, | ||
pathMap: new Map(), | ||
}); | ||
/** @internal */ | ||
this._matchingSource = mobx_1.observable({ | ||
groupToMatchToMatchEntryMapMap: new Map(), | ||
queryDict: {}, | ||
pathMap: new Map(), | ||
}); | ||
/** @internal */ | ||
this._changing = Promise.resolve(); | ||
/** @internal */ | ||
this._routing = false; | ||
/** @internal */ | ||
this._groupToRouteMatchMap = new Map(); | ||
/** @internal */ | ||
this._onHistoryChange = (snapshot) => { | ||
this._nextSnapshot = snapshot; | ||
this._changing = this._changing | ||
.then(() => this._asyncOnHistoryChange(snapshot)) | ||
.catch(console.error); | ||
}; | ||
/** @internal */ | ||
this._asyncOnHistoryChange = async (nextSnapshot) => { | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return; | ||
} | ||
let { ref, data } = history_1.getActiveHistoryEntry(nextSnapshot); | ||
let navigateCompleteListener = data && data.navigateCompleteListener; | ||
let { pathname, search } = _utils_1.parseRef(ref); | ||
let snapshot = this._snapshot; | ||
if (snapshot && lodash_1.default.isEqual(snapshot, nextSnapshot)) { | ||
return; | ||
} | ||
let queryDict = _utils_1.parseSearch(search); | ||
let pathMap = new Map(); | ||
pathMap.set(undefined, pathname || '/'); | ||
let groups = this.$groups; | ||
// Extract group route paths in query | ||
for (let group of groups) { | ||
let key = `_${group}`; | ||
if (!_utils_1.hasOwnProperty(queryDict, key)) { | ||
continue; | ||
let Router = /** @class */ (() => { | ||
class Router { | ||
constructor(history, { segmentMatcher } = {}) { | ||
/** @internal */ | ||
this._source = mobx_1.observable({ | ||
groupToMatchToMatchEntryMapMap: new Map(), | ||
queryDict: {}, | ||
pathMap: new Map(), | ||
}); | ||
/** @internal */ | ||
this._matchingSource = mobx_1.observable({ | ||
groupToMatchToMatchEntryMapMap: new Map(), | ||
queryDict: {}, | ||
pathMap: new Map(), | ||
}); | ||
/** @internal */ | ||
this._changing = Promise.resolve(); | ||
/** @internal */ | ||
this._routing = false; | ||
/** @internal */ | ||
this._groupToRouteMatchMap = new Map(); | ||
/** @internal */ | ||
this._onHistoryChange = (snapshot) => { | ||
this._nextSnapshot = snapshot; | ||
this._changing = this._changing | ||
.then(() => this._asyncOnHistoryChange(snapshot)) | ||
.catch(console.error); | ||
}; | ||
/** @internal */ | ||
this._asyncOnHistoryChange = async (nextSnapshot) => { | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return; | ||
} | ||
let path = queryDict[key]; | ||
if (path) { | ||
pathMap.set(group, path); | ||
let { ref, data } = history_1.getActiveHistoryEntry(nextSnapshot); | ||
let navigateCompleteListener = data && data.navigateCompleteListener; | ||
let { pathname, search } = _utils_1.parseRef(ref); | ||
let snapshot = this._snapshot; | ||
if (snapshot && lodash_1.default.isEqual(snapshot, nextSnapshot)) { | ||
return; | ||
} | ||
delete queryDict[key]; | ||
} | ||
// Match parallel routes | ||
let groupToMatchEntriesMap = new Map(); | ||
let groupToRouteMatchMap = this._groupToRouteMatchMap; | ||
for (let [group, path] of pathMap) { | ||
let routeMatch = groupToRouteMatchMap.get(group); | ||
let routeMatchEntries = this._match(routeMatch ? [routeMatch] : [], path) || []; | ||
if (!routeMatchEntries.length) { | ||
continue; | ||
let queryDict = _utils_1.parseSearch(search); | ||
let pathMap = new Map(); | ||
pathMap.set(undefined, pathname || '/'); | ||
let groups = this.$groups; | ||
// Extract group route paths in query | ||
for (let group of groups) { | ||
let key = `_${group}`; | ||
if (!_utils_1.hasOwnProperty(queryDict, key)) { | ||
continue; | ||
} | ||
let path = queryDict[key]; | ||
if (path) { | ||
pathMap.set(group, path); | ||
} | ||
delete queryDict[key]; | ||
} | ||
let [{ match }] = routeMatchEntries; | ||
if (match.$group !== group) { | ||
continue; | ||
// Match parallel routes | ||
let groupToMatchEntriesMap = new Map(); | ||
let groupToRouteMatchMap = this._groupToRouteMatchMap; | ||
for (let [group, path] of pathMap) { | ||
let routeMatch = groupToRouteMatchMap.get(group); | ||
let routeMatchEntries = this._match(routeMatch ? [routeMatch] : [], path) || []; | ||
if (!routeMatchEntries.length) { | ||
continue; | ||
} | ||
let [{ match }] = routeMatchEntries; | ||
if (match.$group !== group) { | ||
continue; | ||
} | ||
groupToMatchEntriesMap.set(group, routeMatchEntries); | ||
} | ||
groupToMatchEntriesMap.set(group, routeMatchEntries); | ||
} | ||
// Check primary match parallel options | ||
let groupToMatchToMatchEntryMapMap = new Map(); | ||
let primaryMatchEntries = groupToMatchEntriesMap.get(undefined); | ||
if (primaryMatchEntries) { | ||
let primaryMatch = primaryMatchEntries[primaryMatchEntries.length - 1].match; | ||
let options = primaryMatch._parallel; | ||
let { groups = [], matches = [] } = options || {}; | ||
for (let [group, entries] of groupToMatchEntriesMap) { | ||
if (!group || | ||
!options || | ||
groups.includes(group) || | ||
entries.some(({ match }) => matches.includes(match))) { | ||
groupToMatchToMatchEntryMapMap.set(group, new Map(entries.map((entry) => [ | ||
entry.match, | ||
entry, | ||
]))); | ||
// Check primary match parallel options | ||
let groupToMatchToMatchEntryMapMap = new Map(); | ||
let primaryMatchEntries = groupToMatchEntriesMap.get(undefined); | ||
if (primaryMatchEntries) { | ||
let primaryMatch = primaryMatchEntries[primaryMatchEntries.length - 1].match; | ||
let options = primaryMatch._parallel; | ||
let { groups = [], matches = [] } = options || {}; | ||
for (let [group, entries] of groupToMatchEntriesMap) { | ||
if (!group || | ||
!options || | ||
groups.includes(group) || | ||
entries.some(({ match }) => matches.includes(match))) { | ||
groupToMatchToMatchEntryMapMap.set(group, new Map(entries.map((entry) => [ | ||
entry.match, | ||
entry, | ||
]))); | ||
} | ||
} | ||
} | ||
} | ||
mobx_1.runInAction(() => { | ||
Object.assign(this._matchingSource, { | ||
groupToMatchToMatchEntryMapMap, | ||
queryDict, | ||
pathMap, | ||
mobx_1.runInAction(() => { | ||
Object.assign(this._matchingSource, { | ||
groupToMatchToMatchEntryMapMap, | ||
queryDict, | ||
pathMap, | ||
}); | ||
}); | ||
}); | ||
let generalGroups = [undefined, ...groups]; | ||
mobx_1.runInAction(() => { | ||
this._routing = true; | ||
}); | ||
let interUpdateDataArray = await Promise.all(generalGroups.map(async (group) => this._beforeUpdate(nextSnapshot, group, groupToMatchToMatchEntryMapMap.get(group)))); | ||
mobx_1.runInAction(() => { | ||
this._routing = false; | ||
}); | ||
if (interUpdateDataArray.some(data => !data)) { | ||
return; | ||
let generalGroups = [undefined, ...groups]; | ||
mobx_1.runInAction(() => { | ||
this._routing = true; | ||
}); | ||
let interUpdateDataArray = await Promise.all(generalGroups.map(async (group) => this._beforeUpdate(nextSnapshot, group, groupToMatchToMatchEntryMapMap.get(group)))); | ||
mobx_1.runInAction(() => { | ||
this._routing = false; | ||
}); | ||
if (interUpdateDataArray.some(data => !data)) { | ||
return; | ||
} | ||
this._update(generalGroups); | ||
this._snapshot = nextSnapshot; | ||
await Promise.all(interUpdateDataArray.map(data => this._afterUpdate(data))); | ||
if (navigateCompleteListener) { | ||
navigateCompleteListener(); | ||
} | ||
}; | ||
this._history = history; | ||
this._segmentMatcher = segmentMatcher || DEFAULT_SEGMENT_MATCHER_CALLBACK; | ||
history.listen(this._onHistoryChange); | ||
} | ||
get $current() { | ||
let { pathMap, queryDict } = this._source; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this); | ||
} | ||
get $routing() { | ||
return this._routing; | ||
} | ||
get $next() { | ||
let { pathMap, queryDict } = this._matchingSource; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this); | ||
} | ||
get $groups() { | ||
return Array.from(this._groupToRouteMatchMap.keys()).filter((group) => !!group); | ||
} | ||
$route(groupOrSchema, schemaOrUndefined) { | ||
let group; | ||
let schema; | ||
if (typeof groupOrSchema === 'string') { | ||
group = groupOrSchema; | ||
schema = schemaOrUndefined; | ||
} | ||
this._update(generalGroups); | ||
this._snapshot = nextSnapshot; | ||
await Promise.all(interUpdateDataArray.map(data => this._afterUpdate(data))); | ||
if (navigateCompleteListener) { | ||
navigateCompleteListener(); | ||
else { | ||
group = undefined; | ||
schema = groupOrSchema; | ||
} | ||
}; | ||
this._history = history; | ||
this._segmentMatcher = segmentMatcher || DEFAULT_SEGMENT_MATCHER_CALLBACK; | ||
history.listen(this._onHistoryChange); | ||
} | ||
get $current() { | ||
let { pathMap, queryDict } = this._source; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this); | ||
} | ||
get $routing() { | ||
return this._routing; | ||
} | ||
get $next() { | ||
let { pathMap, queryDict } = this._matchingSource; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this); | ||
} | ||
get $groups() { | ||
return Array.from(this._groupToRouteMatchMap.keys()).filter((group) => !!group); | ||
} | ||
$route(groupOrSchema, schemaOrUndefined) { | ||
let group; | ||
let schema; | ||
if (typeof groupOrSchema === 'string') { | ||
group = groupOrSchema; | ||
schema = schemaOrUndefined; | ||
let [routeMatch] = this._buildRouteMatch(group, '', undefined, undefined, { | ||
match: route_match_1.ROUTE_MATCH_START_ANCHOR_PATTERN, | ||
exact: false, | ||
query: undefined, | ||
children: schema, | ||
extension: undefined, | ||
metadata: undefined, | ||
}); | ||
this._groupToRouteMatchMap.set(group, routeMatch); | ||
return routeMatch; | ||
} | ||
else { | ||
group = undefined; | ||
schema = groupOrSchema; | ||
$ref() { | ||
return this.$current.$ref(); | ||
} | ||
let [routeMatch] = this._buildRouteMatch(group, '', undefined, undefined, { | ||
match: route_match_1.ROUTE_MATCH_START_ANCHOR_PATTERN, | ||
exact: false, | ||
query: undefined, | ||
children: schema, | ||
extension: undefined, | ||
}); | ||
this._groupToRouteMatchMap.set(group, routeMatch); | ||
return routeMatch; | ||
} | ||
$ref() { | ||
return this.$current.$ref(); | ||
} | ||
$href() { | ||
return this.$current.$href(); | ||
} | ||
$(match, params) { | ||
let { pathMap, queryDict } = this._source; | ||
let buildingPart = typeof match === 'string' | ||
? match | ||
: { | ||
match, | ||
params, | ||
}; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this, [buildingPart]); | ||
} | ||
$scratch() { | ||
return new route_builder_1.RouteBuilder(new Map(), {}, this); | ||
} | ||
$push(ref, options) { | ||
this.$current.$(ref).$push(options); | ||
} | ||
$replace(ref, options) { | ||
this.$current.$(ref).$replace(options); | ||
} | ||
/** @internal */ | ||
_push(ref, { onComplete } = {}) { | ||
this._history | ||
.push(ref, { navigateCompleteListener: onComplete }) | ||
.catch(console.error); | ||
} | ||
/** @internal */ | ||
_replace(ref, { onComplete } = {}) { | ||
this._history | ||
.replace(ref, { navigateCompleteListener: onComplete }) | ||
.catch(console.error); | ||
} | ||
/** @internal */ | ||
async _beforeUpdate(nextSnapshot, group, matchToMatchEntryMap) { | ||
if (!matchToMatchEntryMap) { | ||
matchToMatchEntryMap = new Map(); | ||
$href() { | ||
return this.$current.$href(); | ||
} | ||
// Prepare previous/next match set | ||
let previousMatchToMatchEntryMap = this._source.groupToMatchToMatchEntryMapMap.get(group); | ||
if (!previousMatchToMatchEntryMap) { | ||
previousMatchToMatchEntryMap = new Map(); | ||
mobx_1.runInAction(() => { | ||
this._source.groupToMatchToMatchEntryMapMap.set(group, previousMatchToMatchEntryMap); | ||
}); | ||
$(match, params) { | ||
let { pathMap, queryDict } = this._source; | ||
let buildingPart = typeof match === 'string' | ||
? match | ||
: { | ||
match, | ||
params, | ||
}; | ||
return new route_builder_1.RouteBuilder(pathMap, queryDict, this, [buildingPart]); | ||
} | ||
let previousMatchSet = new Set(previousMatchToMatchEntryMap.keys()); | ||
let matchSet = new Set(matchToMatchEntryMap.keys()); | ||
let leavingMatchSet = new Set(previousMatchSet); | ||
for (let match of matchSet) { | ||
leavingMatchSet.delete(match); | ||
$scratch() { | ||
return new route_builder_1.RouteBuilder(new Map(), {}, this); | ||
} | ||
let reversedLeavingMatches = Array.from(leavingMatchSet).reverse(); | ||
let enteringAndUpdatingMatchSet = new Set(matchSet); | ||
let descendantUpdatingMatchSet = new Set(); | ||
for (let match of previousMatchSet) { | ||
if (!enteringAndUpdatingMatchSet.has(match)) { | ||
continue; | ||
$push(ref, options) { | ||
this.$current.$(ref).$push(options); | ||
} | ||
$replace(ref, options) { | ||
this.$current.$(ref).$replace(options); | ||
} | ||
/** @internal */ | ||
_push(ref, { onComplete } = {}) { | ||
this._history | ||
.push(ref, { navigateCompleteListener: onComplete }) | ||
.catch(console.error); | ||
} | ||
/** @internal */ | ||
_replace(ref, { onComplete } = {}) { | ||
this._history | ||
.replace(ref, { navigateCompleteListener: onComplete }) | ||
.catch(console.error); | ||
} | ||
/** @internal */ | ||
async _beforeUpdate(nextSnapshot, group, matchToMatchEntryMap) { | ||
if (!matchToMatchEntryMap) { | ||
matchToMatchEntryMap = new Map(); | ||
} | ||
let nextMatch = match.$next; | ||
if (lodash_1.default.isEqual(match._pathSegments, nextMatch._pathSegments) && | ||
match.$exact === nextMatch.$exact) { | ||
if (match._rest === nextMatch._rest) { | ||
enteringAndUpdatingMatchSet.delete(match); | ||
// Prepare previous/next match set | ||
let previousMatchToMatchEntryMap = this._source.groupToMatchToMatchEntryMapMap.get(group); | ||
if (!previousMatchToMatchEntryMap) { | ||
previousMatchToMatchEntryMap = new Map(); | ||
mobx_1.runInAction(() => { | ||
this._source.groupToMatchToMatchEntryMapMap.set(group, previousMatchToMatchEntryMap); | ||
}); | ||
} | ||
let previousMatchSet = new Set(previousMatchToMatchEntryMap.keys()); | ||
let matchSet = new Set(matchToMatchEntryMap.keys()); | ||
let leavingMatchSet = new Set(previousMatchSet); | ||
for (let match of matchSet) { | ||
leavingMatchSet.delete(match); | ||
} | ||
let reversedLeavingMatches = Array.from(leavingMatchSet).reverse(); | ||
let enteringAndUpdatingMatchSet = new Set(matchSet); | ||
let descendantUpdatingMatchSet = new Set(); | ||
for (let match of previousMatchSet) { | ||
if (!enteringAndUpdatingMatchSet.has(match)) { | ||
continue; | ||
} | ||
else { | ||
descendantUpdatingMatchSet.add(match); | ||
let nextMatch = match.$next; | ||
if (lodash_1.default.isEqual(match._pathSegments, nextMatch._pathSegments) && | ||
match.$exact === nextMatch.$exact) { | ||
if (match._rest === nextMatch._rest) { | ||
enteringAndUpdatingMatchSet.delete(match); | ||
} | ||
else { | ||
descendantUpdatingMatchSet.add(match); | ||
} | ||
} | ||
} | ||
} | ||
for (let match of reversedLeavingMatches) { | ||
let result = await match._beforeLeave(); | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return undefined; | ||
for (let match of reversedLeavingMatches) { | ||
let result = await match._beforeLeave(); | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return undefined; | ||
} | ||
if (!result) { | ||
this._revert(); | ||
return undefined; | ||
} | ||
} | ||
if (!result) { | ||
this._revert(); | ||
return undefined; | ||
for (let match of enteringAndUpdatingMatchSet) { | ||
let update = previousMatchSet.has(match); | ||
let result = update | ||
? await match._beforeUpdate(descendantUpdatingMatchSet.has(match)) | ||
: await match._beforeEnter(); | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return undefined; | ||
} | ||
if (!result) { | ||
this._revert(); | ||
return undefined; | ||
} | ||
} | ||
return { | ||
reversedLeavingMatches, | ||
enteringAndUpdatingMatchSet, | ||
previousMatchSet, | ||
descendantUpdatingMatchSet, | ||
}; | ||
} | ||
for (let match of enteringAndUpdatingMatchSet) { | ||
let update = previousMatchSet.has(match); | ||
let result = update | ||
? await match._beforeUpdate(descendantUpdatingMatchSet.has(match)) | ||
: await match._beforeEnter(); | ||
if (this._isNextSnapshotOutDated(nextSnapshot)) { | ||
return undefined; | ||
/** @internal */ | ||
_update(generalGroups) { | ||
let source = this._source; | ||
let matchingSource = this._matchingSource; | ||
source.queryDict = matchingSource.queryDict; | ||
for (let group of generalGroups) { | ||
let path = matchingSource.pathMap.get(group); | ||
if (path) { | ||
source.pathMap.set(group, path); | ||
} | ||
else { | ||
source.pathMap.delete(group); | ||
} | ||
let matchToMatchEntryMap = matchingSource.groupToMatchToMatchEntryMapMap.get(group); | ||
source.groupToMatchToMatchEntryMapMap.set(group, matchToMatchEntryMap); | ||
} | ||
if (!result) { | ||
this._revert(); | ||
return undefined; | ||
} | ||
} | ||
return { | ||
reversedLeavingMatches, | ||
enteringAndUpdatingMatchSet, | ||
previousMatchSet, | ||
descendantUpdatingMatchSet, | ||
}; | ||
} | ||
/** @internal */ | ||
_update(generalGroups) { | ||
let source = this._source; | ||
let matchingSource = this._matchingSource; | ||
source.queryDict = matchingSource.queryDict; | ||
for (let group of generalGroups) { | ||
let path = matchingSource.pathMap.get(group); | ||
if (path) { | ||
source.pathMap.set(group, path); | ||
/** @internal */ | ||
async _afterUpdate({ reversedLeavingMatches, enteringAndUpdatingMatchSet, previousMatchSet, descendantUpdatingMatchSet, }) { | ||
for (let match of reversedLeavingMatches) { | ||
await match._afterLeave(); | ||
} | ||
else { | ||
source.pathMap.delete(group); | ||
for (let match of enteringAndUpdatingMatchSet) { | ||
let update = previousMatchSet.has(match); | ||
if (update) { | ||
await match._afterUpdate(descendantUpdatingMatchSet.has(match)); | ||
} | ||
else { | ||
await match._afterEnter(); | ||
} | ||
} | ||
let matchToMatchEntryMap = matchingSource.groupToMatchToMatchEntryMapMap.get(group); | ||
source.groupToMatchToMatchEntryMapMap.set(group, matchToMatchEntryMap); | ||
} | ||
} | ||
/** @internal */ | ||
async _afterUpdate({ reversedLeavingMatches, enteringAndUpdatingMatchSet, previousMatchSet, descendantUpdatingMatchSet, }) { | ||
for (let match of reversedLeavingMatches) { | ||
await match._afterLeave(); | ||
/** @internal */ | ||
_isNextSnapshotOutDated(snapshot) { | ||
return this._nextSnapshot !== snapshot; | ||
} | ||
for (let match of enteringAndUpdatingMatchSet) { | ||
let update = previousMatchSet.has(match); | ||
if (update) { | ||
await match._afterUpdate(descendantUpdatingMatchSet.has(match)); | ||
/** @internal */ | ||
_revert() { | ||
let snapshot = this._snapshot; | ||
if (snapshot) { | ||
this._history.restore(snapshot).catch(console.error); | ||
} | ||
else { | ||
await match._afterEnter(); | ||
this._history.replace('/').catch(console.error); | ||
} | ||
} | ||
} | ||
/** @internal */ | ||
_isNextSnapshotOutDated(snapshot) { | ||
return this._nextSnapshot !== snapshot; | ||
} | ||
/** @internal */ | ||
_revert() { | ||
let snapshot = this._snapshot; | ||
if (snapshot) { | ||
this._history.restore(snapshot).catch(console.error); | ||
} | ||
else { | ||
this._history.replace('/').catch(console.error); | ||
} | ||
} | ||
/** @internal */ | ||
_match(routeMatches, upperRest) { | ||
for (let routeMatch of routeMatches) { | ||
let { matched, exactlyMatched, segment, rest } = routeMatch._match(upperRest); | ||
if (!matched) { | ||
continue; | ||
} | ||
if (rest === '') { | ||
/** @internal */ | ||
_match(routeMatches, upperRest) { | ||
for (let routeMatch of routeMatches) { | ||
let { matched, exactlyMatched, segment, rest } = routeMatch._match(upperRest); | ||
if (!matched) { | ||
continue; | ||
} | ||
if (rest === '') { | ||
return [ | ||
{ | ||
match: routeMatch, | ||
segment: segment, | ||
exact: exactlyMatched, | ||
rest, | ||
}, | ||
]; | ||
} | ||
let result = this._match(routeMatch._children || [], rest); | ||
if (!result) { | ||
continue; | ||
} | ||
return [ | ||
@@ -331,74 +347,65 @@ { | ||
}, | ||
...result, | ||
]; | ||
} | ||
let result = this._match(routeMatch._children || [], rest); | ||
if (!result) { | ||
continue; | ||
} | ||
return [ | ||
{ | ||
match: routeMatch, | ||
segment: segment, | ||
exact: exactlyMatched, | ||
rest, | ||
}, | ||
...result, | ||
]; | ||
return undefined; | ||
} | ||
return undefined; | ||
} | ||
/** @internal */ | ||
_buildRouteMatches(group, schemaDict, parent, matchingParent) { | ||
return Object.entries(schemaDict).reduce(([routeMatches, nextRouteMatches], [routeName, schema]) => { | ||
if (typeof schema === 'boolean') { | ||
schema = {}; | ||
} | ||
let { $match: match = this._segmentMatcher(routeName), $exact: exact = false, $query: query, $children: children, $extension: extension, } = schema; | ||
let [routeMatch, nextRouteMatch] = this._buildRouteMatch(group, routeName, parent, matchingParent, { | ||
/** @internal */ | ||
_buildRouteMatches(group, schemaDict, parent, matchingParent) { | ||
return Object.entries(schemaDict).reduce(([routeMatches, nextRouteMatches], [routeName, schema]) => { | ||
if (typeof schema === 'boolean') { | ||
schema = {}; | ||
} | ||
let { $match: match = this._segmentMatcher(routeName), $exact: exact = false, $query: query, $children: children, $extension: extension, $metadata: metadata, } = schema; | ||
let [routeMatch, nextRouteMatch] = this._buildRouteMatch(group, routeName, parent, matchingParent, { | ||
match, | ||
exact, | ||
query, | ||
children, | ||
extension, | ||
metadata, | ||
}); | ||
parent[routeName] = routeMatch; | ||
matchingParent[routeName] = nextRouteMatch; | ||
return [ | ||
[...routeMatches, routeMatch], | ||
[...nextRouteMatches, nextRouteMatch], | ||
]; | ||
}, [[], []]); | ||
} | ||
/** @internal */ | ||
_buildRouteMatch(group, routeName, parent, matchingParent, { match, exact, query, children, extension, metadata, }) { | ||
let source = this._source; | ||
let matchingSource = this._matchingSource; | ||
let history = this._history; | ||
let options = { | ||
match, | ||
query, | ||
exact, | ||
query, | ||
children, | ||
extension, | ||
}); | ||
parent[routeName] = routeMatch; | ||
matchingParent[routeName] = nextRouteMatch; | ||
return [ | ||
[...routeMatches, routeMatch], | ||
[...nextRouteMatches, nextRouteMatch], | ||
]; | ||
}, [[], []]); | ||
} | ||
/** @internal */ | ||
_buildRouteMatch(group, routeName, parent, matchingParent, { match, exact, query, children, extension }) { | ||
let source = this._source; | ||
let matchingSource = this._matchingSource; | ||
let history = this._history; | ||
let options = { | ||
match, | ||
query, | ||
exact, | ||
group, | ||
}; | ||
let routeMatch = new route_match_1.RouteMatch(routeName, this, source, parent, extension, history, options); | ||
let nextRouteMatch = new route_match_1.NextRouteMatch(routeName, this, matchingSource, matchingParent, routeMatch, history, options); | ||
routeMatch.$next = nextRouteMatch; | ||
if (children) { | ||
let [childRouteMatches, childNextRouteMatches] = this._buildRouteMatches(group, children, routeMatch, nextRouteMatch); | ||
routeMatch._children = childRouteMatches; | ||
nextRouteMatch._children = childNextRouteMatches; | ||
group, | ||
metadata, | ||
}; | ||
let routeMatch = new route_match_1.RouteMatch(routeName, this, source, parent, extension, history, options); | ||
let nextRouteMatch = new route_match_1.NextRouteMatch(routeName, this, matchingSource, matchingParent, routeMatch, history, options); | ||
routeMatch.$next = nextRouteMatch; | ||
if (children) { | ||
let [childRouteMatches, childNextRouteMatches] = this._buildRouteMatches(group, children, routeMatch, nextRouteMatch); | ||
routeMatch._children = childRouteMatches; | ||
nextRouteMatch._children = childNextRouteMatches; | ||
} | ||
return [routeMatch, nextRouteMatch]; | ||
} | ||
return [routeMatch, nextRouteMatch]; | ||
} | ||
} | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], Router.prototype, "_matchingSource", void 0); | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], Router.prototype, "_routing", void 0); | ||
tslib_1.__decorate([ | ||
mobx_1.action | ||
], Router.prototype, "_update", null); | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], Router.prototype, "_matchingSource", void 0); | ||
tslib_1.__decorate([ | ||
mobx_1.observable | ||
], Router.prototype, "_routing", void 0); | ||
tslib_1.__decorate([ | ||
mobx_1.action | ||
], Router.prototype, "_update", null); | ||
return Router; | ||
})(); | ||
exports.Router = Router; | ||
//# sourceMappingURL=router.js.map |
@@ -15,2 +15,3 @@ import { Dict } from 'tslang'; | ||
$extension?: object; | ||
$metadata?: object; | ||
} | ||
@@ -17,0 +18,0 @@ export declare type RouteSchemaDict = Dict<RouteSchema | boolean>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.schema = void 0; | ||
function schema(schema) { | ||
@@ -4,0 +5,0 @@ return schema; |
{ | ||
"name": "boring-router", | ||
"version": "0.3.0-alpha.45", | ||
"version": "0.3.0-alpha.46", | ||
"description": "A light-weight, type-safe, yet reactive router service using MobX.", | ||
@@ -31,6 +31,6 @@ "repository": { | ||
"@types/history": "^4.7.3", | ||
"@types/jest": "^24.0.18", | ||
"@types/jest": "^25.2.2", | ||
"@types/lodash": "^4.14.141", | ||
"jest": "^24.9.0", | ||
"ts-jest": "^24.1.0" | ||
"jest": "^26.0.1", | ||
"ts-jest": "^26.0.0" | ||
}, | ||
@@ -42,3 +42,3 @@ "dependencies": { | ||
}, | ||
"gitHead": "38d6c5edb95fcddbd7d5713a8419a3bd3da2a856" | ||
"gitHead": "af5f8836b8cd7ff0e777a00c9f93eb229ab20912" | ||
} |
@@ -89,9 +89,6 @@ import {Dict} from 'tslang'; | ||
return Array.from(searchParams).reduce( | ||
(dict, [key, value]) => { | ||
dict[key] = value; | ||
return dict; | ||
}, | ||
{} as GeneralQueryDict, | ||
); | ||
return Array.from(searchParams).reduce((dict, [key, value]) => { | ||
dict[key] = value; | ||
return dict; | ||
}, {} as GeneralQueryDict); | ||
} | ||
@@ -98,0 +95,0 @@ |
@@ -222,14 +222,11 @@ import {computed} from 'mobx'; | ||
return Array.from(this._queryKeySet).reduce( | ||
(dict, key) => { | ||
let value = sourceQueryDict[key]; | ||
return Array.from(this._queryKeySet).reduce((dict, key) => { | ||
let value = sourceQueryDict[key]; | ||
if (value !== undefined) { | ||
dict[key] = sourceQueryDict[key]; | ||
} | ||
if (value !== undefined) { | ||
dict[key] = sourceQueryDict[key]; | ||
} | ||
return dict; | ||
}, | ||
{} as GeneralQueryDict, | ||
); | ||
return dict; | ||
}, {} as GeneralQueryDict); | ||
} | ||
@@ -236,0 +233,0 @@ |
@@ -193,2 +193,3 @@ import { | ||
exact: boolean | string; | ||
metadata: object | undefined; | ||
} | ||
@@ -202,3 +203,4 @@ | ||
TSpecificGroupName extends string | undefined = string | undefined, | ||
TGroupName extends string = string | ||
TGroupName extends string = string, | ||
TMetadata extends object = object | ||
> extends RouteMatchShared<TParamDict, TSpecificGroupName, TGroupName> { | ||
@@ -247,4 +249,14 @@ readonly $parent: RouteMatch | undefined; | ||
/** @internal */ | ||
private readonly _metadata: object | undefined; | ||
/** @internal */ | ||
_parallel: RouteMatchParallelOptions<TGroupName> | undefined; | ||
get $metadata(): TMetadata { | ||
return Object.assign( | ||
this.$parent?.$metadata ?? {}, | ||
this._metadata, | ||
) as TMetadata; | ||
} | ||
constructor( | ||
@@ -257,3 +269,3 @@ name: string, | ||
history: IHistory, | ||
{exact, ...sharedOptions}: RouteMatchOptions, | ||
{exact, metadata, ...sharedOptions}: RouteMatchOptions, | ||
) { | ||
@@ -275,2 +287,4 @@ super(name, router, source, parent, history, sharedOptions); | ||
this._metadata = metadata; | ||
this._allowExact = exact; | ||
@@ -277,0 +291,0 @@ } |
@@ -57,2 +57,13 @@ import hyphenate from 'hyphenate'; | ||
interface RouteSchemaMetadataSection<TMetadata> { | ||
$metadata: TMetadata; | ||
} | ||
type RouteMatchMetadataType< | ||
TRouteSchema, | ||
TUpperMetadata | ||
> = TRouteSchema extends RouteSchemaMetadataSection<infer TMetadata> | ||
? TMetadata & TUpperMetadata | ||
: TUpperMetadata; | ||
type RouteMatchExtensionType< | ||
@@ -69,3 +80,4 @@ TRouteSchema | ||
TSpecificGroupName extends string | undefined, | ||
TGroupName extends string | ||
TGroupName extends string, | ||
TMetadata extends object | ||
> = { | ||
@@ -78,3 +90,4 @@ [K in Extract<keyof TRouteSchemaDict, string>]: RouteMatchType< | ||
TSpecificGroupName, | ||
TGroupName | ||
TGroupName, | ||
TMetadata | ||
>; | ||
@@ -89,3 +102,4 @@ }; | ||
TGroupName extends string, | ||
TParamDict extends Dict<string | undefined> | ||
TParamDict extends Dict<string | undefined>, | ||
TMetadata extends object | ||
> = RouteMatch< | ||
@@ -102,3 +116,4 @@ TParamDict, | ||
TSpecificGroupName, | ||
TGroupName | ||
TGroupName, | ||
RouteMatchMetadataType<TRouteSchema, TMetadata> | ||
> & | ||
@@ -110,3 +125,4 @@ RouteMatchSegmentType< | ||
TSpecificGroupName, | ||
TGroupName | ||
TGroupName, | ||
RouteMatchMetadataType<TRouteSchema, TMetadata> | ||
> & | ||
@@ -120,3 +136,4 @@ RouteMatchExtensionType<TRouteSchema>; | ||
TSpecificGroupName extends string | undefined, | ||
TGroupName extends string | ||
TGroupName extends string, | ||
TMetadata extends object | ||
> = __RouteMatchType< | ||
@@ -128,3 +145,4 @@ TRouteSchema, | ||
TGroupName, | ||
Record<TQueryKey, string | undefined> & Record<TSegmentKey, string> | ||
Record<TQueryKey, string | undefined> & Record<TSegmentKey, string>, | ||
TMetadata | ||
>; | ||
@@ -183,3 +201,4 @@ | ||
TSpecificGroupName extends string | undefined, | ||
TGroupName extends string | ||
TGroupName extends string, | ||
TMetadata extends object = {} | ||
> = RouteMatchType< | ||
@@ -190,3 +209,4 @@ {$children: TRouteSchemaDict}, | ||
TSpecificGroupName, | ||
TGroupName | ||
TGroupName, | ||
TMetadata | ||
>; | ||
@@ -216,2 +236,3 @@ | ||
extension: object | undefined; | ||
metadata: object | undefined; | ||
} | ||
@@ -336,2 +357,3 @@ | ||
extension: undefined, | ||
metadata: undefined, | ||
}); | ||
@@ -786,2 +808,3 @@ | ||
$extension: extension, | ||
$metadata: metadata, | ||
} = schema; | ||
@@ -800,2 +823,3 @@ | ||
extension, | ||
metadata, | ||
}, | ||
@@ -822,3 +846,10 @@ ); | ||
matchingParent: NextRouteMatch | undefined, | ||
{match, exact, query, children, extension}: BuildRouteMatchOptions, | ||
{ | ||
match, | ||
exact, | ||
query, | ||
children, | ||
extension, | ||
metadata, | ||
}: BuildRouteMatchOptions, | ||
): [RouteMatch, NextRouteMatch] { | ||
@@ -834,2 +865,3 @@ let source = this._source; | ||
group, | ||
metadata, | ||
}; | ||
@@ -836,0 +868,0 @@ |
@@ -16,2 +16,3 @@ import {Dict} from 'tslang'; | ||
$extension?: object; | ||
$metadata?: object; | ||
} | ||
@@ -18,0 +19,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
185127
3959