UNPKG

@do-while-for-each/path-resolver

Version:

Resolving the path into some kind of expected result

241 lines (240 loc) 9.31 kB
"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;