Skip to content

Instantly share code, notes, and snippets.

@chenyong
Created June 28, 2018 03:42
Show Gist options
  • Save chenyong/8607355463b5f20cd660d242a02e490a to your computer and use it in GitHub Desktop.
Save chenyong/8607355463b5f20cd660d242a02e490a to your computer and use it in GitHub Desktop.
An url parser
let pageRules = [
{
name: "a",
path: "reports/:reportId/edit",
router: [],
},
{
name: "b",
path: "reports/:reportId",
router: [],
},
{
path: "reports",
router: [
{
name: "analysisId",
path: ":analysisId",
router: [],
},
{
name: "home",
path: "",
router: [],
},
],
},
];
let router: IRouteParseResult = parseRoutePath(this.props.location.pathname, pageRules);
import _ from "lodash";
import produce from "immer";
export interface IRouteRule {
path: string;
name?: string;
router: IRouteRule[];
}
export interface IRouteParseResult {
matches: boolean;
name: string;
data: any;
restPath: string[];
basePath: string[];
next?: IRouteParseResult;
}
let parseRuleIterate = (data: any, segments: string[], ruleSteps: string[], ruleName: string, basePath: string[]): IRouteParseResult => {
if (_.isEmpty(ruleSteps)) {
return { name: ruleName, matches: true, restPath: segments, basePath: basePath, data: data };
}
let s0 = _.first(segments);
let r0 = _.first(ruleSteps);
if (r0[0] === ":") {
let newData = produce(data, (draft) => {
draft[r0.slice(1)] = s0;
});
return parseRuleIterate(newData, segments.slice(1), ruleSteps.slice(1), ruleName, basePath.concat([s0]));
} else if (s0 === r0) {
return parseRuleIterate(data, segments.slice(1), ruleSteps.slice(1), ruleName, basePath.concat([s0]));
} else {
return {
name: ruleName,
matches: false,
restPath: segments,
basePath: basePath,
data: null,
};
}
};
let parseWithRule = (rule: IRouteRule, segments: string[], basePath: string[]): IRouteParseResult => {
let ruleName = rule.name || _.first(rule.path.split("/"));
if (rule.path === "") {
return {
name: ruleName,
matches: true,
restPath: segments,
basePath: basePath,
data: null,
};
}
let ruleSteps = rule.path.split("/");
if (segments.length < ruleSteps.length) {
return { name: ruleName, matches: false, restPath: segments, basePath: basePath, data: null };
}
return parseRuleIterate({}, segments, ruleSteps, ruleName, basePath);
};
var segmentsParsingCaches = {};
let parseSegments = (segments: string[], rules: IRouteRule[], basePath: string[]): IRouteParseResult => {
let cacheKey = segments.join("/");
if (segmentsParsingCaches[cacheKey] != null) {
// console.log("Parser: reusing cache", cacheKey);
return segmentsParsingCaches[cacheKey];
}
if (_.isEmpty(rules)) {
if (_.isEmpty(segments)) {
return null;
} else {
return {
matches: false,
name: "404",
data: null,
restPath: segments,
basePath: basePath,
};
}
} else {
let rule0 = _.first(rules);
let parseResult = parseWithRule(rule0, segments, basePath);
if (parseResult.matches) {
let toReturn = produce(parseResult, (draft: IRouteParseResult) => {
draft.next = parseSegments(parseResult.restPath, rule0.router, basePath);
draft.restPath = null;
});
// console.log("To return", toReturn);
let cacheKey = segments.join("/");
segmentsParsingCaches[cacheKey] = toReturn;
return toReturn;
} else {
return parseSegments(segments, rules.slice(1), basePath);
}
}
};
export let parseRoutePath = (pathString: string, rules: IRouteRule[]): IRouteParseResult => {
let segments = pathString.split("/").filter((x) => x !== "");
return parseSegments(segments, rules, []);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment