@do-while-for-each/path-resolver
Version:
Resolving the path into some kind of expected result
241 lines (240 loc) • 9.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Entry = void 0;
const common_1 = require("@do-while-for-each/common");
const path_to_regexp_1 = require("path-to-regexp");
const cmmn_1 = require("./cmmn");
/**
* The Entry is an instruction on what to do when matching for this segment of the pathname:
*
* {
* segment,
*
* component?,
* redirectTo?,
* customTo?,
* action?,
* children?,
*
* canActivate?, canDeactivate?,
*
* note?, name?
* }
*
*/
class Entry {
orig;
parent;
hasResult;
resultIsOnlyChildren;
pathTemplate; // e.g. "/control/:user"
match; // https://github.com/pillarjs/path-to-regexp#match
get segment() {
return this.orig.segment;
}
isWildcard; // if segment === '**'
isParam; // if segment start with ':', e.g. ':user'
isSimpleSegment; // !isWildcard && !isParam
component;
redirectTo;
customTo;
action;
children;
canActivate;
canDeactivate;
note;
name;
constructor(orig, parent) {
this.orig = orig;
this.parent = parent;
this.hasResult = Entry.hasResult(this.orig);
if (!this.hasResult) {
console.error('The entry must have at least one of: component, redirectTo, customTo, action or children', this.orig);
throw new Error('The resulting field is missing. Fill one of: component, redirectTo, customTo, action or children');
}
if (Entry.isMultiResult(this.orig)) {
console.error('Only one of the following can be specified at a time: component, redirectTo, customTo or action', this.orig);
throw new Error('Only one of the following can be specified at a time: component, redirectTo, customTo or action');
}
this.resultIsOnlyChildren = Entry.resultIsOnlyChildren(this.orig);
this.pathTemplate = Entry.normalizePathTemplate(this.orig, parent);
this.match = (0, path_to_regexp_1.match)(this.pathTemplate, { decode: decodeURIComponent });
this.isWildcard = this.segment === cmmn_1.WILDCARD_SEGMENT;
this.isParam = this.segment[0] === ':';
this.isSimpleSegment = !this.isWildcard && !this.isParam;
this.component = this.orig.component;
this.redirectTo = Entry.normalizeRedirectTo(this.orig.redirectTo);
this.customTo = Entry.normalizeCustomTo(this.orig.customTo);
this.action = this.orig.action;
this.canActivate = this.orig.canActivate;
this.canDeactivate = this.orig.canDeactivate;
this.note = Entry.cloneObj(this.orig.note);
this.name = this.orig.name;
this.children = Entry.normalizeChildren(this.orig, this);
}
flat() {
return Entry.flat(this.cloneFull());
}
clone() {
let parent;
if (this.parent) {
const parentOrig = Entry.cloneOrig(this.parent.orig, { skipChildren: true });
parent = Entry.of(parentOrig);
parent.pathTemplate = this.parent.pathTemplate;
}
const orig = Entry.cloneOrig(this.orig, { skipChildren: true });
const cloned = Entry.of(orig, parent);
cloned.pathTemplate = this.pathTemplate;
return cloned;
}
cloneFull() {
return Entry.of(Entry.cloneOrig(this.orig), this.parent);
}
//region Normalization & Validation
static cloneOrig(orig, { skipChildren } = { skipChildren: false }) {
const result = { ...orig };
result.customTo = Entry.cloneObj(orig.customTo);
if (orig.children && !skipChildren) {
result.children = orig.children.map(x => Entry.cloneOrig(x));
}
result.note = Entry.cloneObj(orig.note);
return result;
}
static normalizePathTemplate(orig, parent) {
let { segment } = orig;
const hasParent = !!parent;
if (!(0, common_1.isString)(segment)) {
console.error('The segment must be a string', orig);
throw new Error('The segment must be a string');
}
if (segment[0] === '/') {
console.error(`Invalid entry, because segment "${segment}" cannot start with a slash:`, orig);
throw new Error('Invalid segment [cannot start with a slash]');
}
if (segment.includes('*') && segment !== cmmn_1.WILDCARD_SEGMENT) {
console.error(`Incorrect wildcard "${segment}". Use "${cmmn_1.WILDCARD_SEGMENT}"`, orig);
throw new Error(`Incorrect wildcard. Use "${cmmn_1.WILDCARD_SEGMENT}"`);
}
switch (segment) {
case '':
if (orig.children) {
console.error('An entry with segment "" cannot have children:', orig);
throw new Error('An entry with segment "" cannot have children');
}
if (hasParent) {
console.error('A non-root entry cannot have an empty "segment":', orig);
throw new Error('Invalid segment [non-root empty]');
}
return '/';
case cmmn_1.WILDCARD_SEGMENT:
segment = cmmn_1.INNER_WILDCARD_SEGMENT;
break;
}
return (hasParent && parent.pathTemplate || '') + '/' + segment;
}
static normalizeRedirectTo(redirectTo) {
if (redirectTo === undefined)
return;
if (!(0, common_1.isString)(redirectTo)) {
console.error('"redirectTo" must be a string:', redirectTo);
throw new Error('"redirectTo" must be a string');
}
if (redirectTo[0] !== '/') {
console.error('"redirectTo" must start with the "/" character:', redirectTo);
throw new Error('"redirectTo" must start with the "/" character');
}
return redirectTo;
}
static normalizeCustomTo(customTo) {
if (customTo === undefined)
return;
if ((0, common_1.isNotJustObject)(customTo)) {
console.error('"customTo" must be an object:', customTo);
throw new Error('Incorrect "customTo". It must be an object');
}
const { pathname, search, hash } = customTo;
if (typeof pathname !== 'string') {
console.error('"pathname" must exist and be a string', customTo);
throw new Error('Incorrect "pathname", must be a string');
}
if (pathname[0] !== '/') {
console.error('"pathname" must start with "/"', customTo);
throw new Error('"pathname" must start with "/"');
}
if (search !== undefined) {
if (!(0, common_1.isString)(search)) {
console.error('"search" must be a string', customTo);
throw new Error('Incorrect "search", must be a string');
}
if (search[0] !== '?') {
console.error('"search" must start with "?"', customTo);
throw new Error('"search" must start with "?"');
}
}
if (hash !== undefined) {
if (!(0, common_1.isString)(hash)) {
console.error('"hash" must be a string', customTo);
throw new Error('Incorrect "hash", must be a string');
}
if (hash[0] !== '#') {
console.error('"hash" must start with "#"', customTo);
throw new Error('"hash" must start with "#"');
}
}
return { ...customTo };
}
static normalizeChildren({ children }, parent) {
return children?.map(orig => Entry.of(Entry.cloneOrig(orig), parent));
}
static cloneObj(data) {
return (0, common_1.isJustObject)(data) && { ...data } || data;
}
//endregion Normalization & Validation
//region Support
static flat(entry) {
if (!entry.children)
return [entry];
const result = [];
result.push(entry);
for (const child of entry.children) {
result.push(...Entry.flat(child));
}
delete entry.parent;
delete entry.children;
return result.flat();
}
static hasResult(orig) {
return (orig.component !== undefined ||
orig.redirectTo !== undefined ||
orig.customTo !== undefined ||
orig.action !== undefined ||
orig.children !== undefined);
}
static resultIsOnlyChildren(orig) {
if (!Entry.hasResult(orig))
return false;
return (orig.children !== undefined &&
orig.component === undefined &&
orig.redirectTo === undefined &&
orig.customTo === undefined &&
orig.action === undefined);
}
static isMultiResult(orig) {
let count = 0;
if (orig.component !== undefined)
count++;
if (orig.redirectTo !== undefined)
count++;
if (orig.customTo !== undefined)
count++;
if (orig.action !== undefined)
count++;
// if (orig.children !== undefined) <-- it's been commented out,
// count++; and it's not a mistake
return count > 1;
}
static of(orig, parent) {
return new Entry(orig, parent);
}
}
exports.Entry = Entry;