Skip to content

Instantly share code, notes, and snippets.

@lxsmnsyc
Last active May 21, 2021 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lxsmnsyc/01c37b847e489c71fd92e5ec8c633394 to your computer and use it in GitHub Desktop.
Save lxsmnsyc/01c37b847e489c71fd92e5ec8c633394 to your computer and use it in GitHub Desktop.
/**
* @license
* MIT License
*
* Copyright (c) 2020 Alexis Munsayac
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
* @author Alexis Munsayac <alexis.munsayac@gmail.com>
* @copyright Alexis Munsayac 2020
*/
class SharedKeyError extends Error {
constructor() {
super('Shared key detected');
}
}
class DuplicateKeyError extends Error {
constructor() {
super('Duplicate key detected');
}
}
function createRouterNode(key, value) {
return {
key,
value,
normal: [],
};
}
function addToChildren(
children,
key,
value,
) {
const node = createRouterNode(key, value);
children.push(node);
return node;
}
function addRoute(
parent,
[lead, ...names],
value,
) {
function addRouteToChildren(children) {
for (let i = 0; i < children.length; i += 1) {
const child = children[i];
if (child.key === lead) {
if (names.length > 0) {
addRoute(child, names, value);
return;
}
throw new DuplicateRouterPathError(child.key);
}
}
const node = addToChildren(children, lead);
if (names.length > 0) {
addRoute(node, names, value);
} else {
node.key = lead;
node.value = value;
}
}
if (lead.startsWith('[')) {
if (lead.endsWith(']')) {
if (lead.startsWith('[...')) {
if (parent.glob) {
throw new SharedRouterPathError(
lead,
parent.glob.key,
);
}
parent.glob = createRouterNode(lead, value);
return;
}
if (parent.named) {
if (lead !== parent.named.key) {
throw new SharedRouterPathError(
lead,
parent.named.key,
);
}
} else {
parent.named = createRouterNode(lead);
if (names.length > 0) {
addRoute(parent.named, names, value);
} else {
parent.named.value = value;
}
}
return;
}
throw new InvalidRouterSyntaxError(lead);
}
addRouteToChildren(parent.normal);
}
function matchRoute(
parent,
[lead, ...names],
params = {},
) {
// Find first if the lead exists in the normal children
for (let i = 0; i < parent.normal.length; i += 1) {
const child = parent.normal[i];
if (child.key === lead) {
if (names.length > 0) {
const matched = matchRoute(child, names, {
...params,
});
if (matched) {
return matched;
}
} else {
return {
value: child.value,
params: {
...params,
},
};
}
}
}
// Check if the parent has a named parameter
if (parent.named) {
if (names.length > 0) {
const namedKey = parent.named.key;
const paramKey = namedKey.substring(1, namedKey.length - 1);
const matched = matchRoute(parent.named, names, {
...params,
[paramKey]: lead,
});
if (matched) {
return matched;
}
} else {
const namedKey = parent.named.key;
const paramKey = namedKey.substring(1, namedKey.length - 1);
return {
value: parent.named.value,
params: {
...params,
[paramKey]: lead,
},
};
}
}
if (parent.glob) {
const globKey = parent.glob.key;
const paramKey = globKey.substring(4, globKey.length - 1);
return {
value: parent.glob.value,
params: {
...params,
[paramKey]: [lead, ...names],
},
};
}
return undefined;
}
const tree = createRouterNode('');
addRoute(tree, ['a'], 'normal');
addRoute(tree, ['[b]'], 'params');
addRoute(tree, ['[...all]'], 'glob');
console.dir(tree, {
depth: null,
});
console.log(matchRoute(tree, ['a', 'b']));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment