type-route
Version:
The flexible, type safe routing library.
119 lines (102 loc) • 3.2 kB
text/typescript
import {
PathFn,
UmbrellaParamDefCollection,
RouteDef,
UmbrellaRouteDef,
} from "./types";
import { TypeRouteError } from "./TypeRouteError";
import { assert } from "./assert";
import { asArray } from "./asArray";
export function defineRoute<TParamDefCollection>(
params: TParamDefCollection,
path: PathFn<TParamDefCollection>
): RouteDef<TParamDefCollection>;
export function defineRoute(path: string | string[]): RouteDef<{}>;
export function defineRoute(...args: any[]): UmbrellaRouteDef {
assertDefineRouteOrExtendArgs("defineRoute", args);
const parent = parseArgs(args);
const routeDef: UmbrellaRouteDef = {
"~internal": {
type: "RouteDef",
params: parent.params,
path: parent.path,
},
extend(...args: any[]) {
assertDefineRouteOrExtendArgs("extend", args);
const { params, path } = parseArgs(args);
const parentParamNames = Object.keys(parent.params);
const extensionParamNames = Object.keys(params);
const duplicateParamNames = parentParamNames.filter(
(name) => extensionParamNames.indexOf(name) >= 0
);
if (__DEV__) {
if (duplicateParamNames.length > 0) {
throw TypeRouteError.Extension_route_definition_parameter_names_may_not_be_the_same_as_base_route_definition_parameter_names.create(
duplicateParamNames
);
}
}
return defineRoute(
{
...params,
...parent.params,
},
(x) => {
const parentPathArray = asArray(
parent.path(filter(parentParamNames))
);
const childPathArray = asArray(path(filter(extensionParamNames)));
return ([] as string[]).concat(
...parentPathArray.map((parentPath) =>
childPathArray.map((childPath) => {
if (parentPath === "/") {
return childPath;
}
return parentPath + (childPath === "/" ? "" : childPath);
})
)
);
function filter(allowedKeys: string[]) {
const filteredX: Record<string, string> = {};
allowedKeys.forEach((key) => {
filteredX[key] = (x as Record<string, string>)[key];
});
return filteredX;
}
}
);
},
};
return routeDef;
}
function assertDefineRouteOrExtendArgs(functionName: string, args: any[]) {
if (__DEV__) {
if (args.length === 1) {
if (Array.isArray(args[0])) {
assert(functionName, [assert.arrayOfType("string", "path", args[0])]);
} else {
assert(functionName, [assert.type("string", "path", args[0])]);
}
} else {
assert(functionName, [
assert.numArgs(args, 1, 2),
assert.collectionOfType("ParamDef", "params", args[0]),
assert.type("function", "path", args[1]),
]);
}
}
}
function parseArgs(args: any[]): {
params: UmbrellaParamDefCollection;
path: PathFn<UmbrellaParamDefCollection>;
} {
return args.length === 1
? {
params: {},
path: () => args[0],
}
: {
params: args[0],
path: args[1],
};
}