Skip to content

Commit

Permalink
feat: ✨ added simple router and test
Browse files Browse the repository at this point in the history
  • Loading branch information
rabelgm committed Aug 13, 2023
1 parent 584591b commit 4025551
Show file tree
Hide file tree
Showing 8 changed files with 1,779 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-dryers-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rabelgm/route-matcher": patch
---

added simple router functionality and tests
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
dist
dist
coverage
14 changes: 14 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
rootDir: "./",
coverageDirectory: "<rootDir>/coverage",
collectCoverageFrom: [
"<rootDir>/lib/**/*.ts",
"!<rootDir>/lib/**/constant.ts",
],
testPathIgnorePatterns: ["<rootDir>/node_modules"],
coverageReporters: ["json", "html"],
testMatch: ["<rootDir>/test/**/*.test.ts"],
};
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Router } from "./router";
69 changes: 69 additions & 0 deletions lib/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const optionalParam = /\((.*?)\)/g;
const namedParam = /(\(\?)?:\w+/g;
const splatParam = /\*\w+/g;
const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;

interface Route {
path: string;
regex: RegExp;
callback: (params: (string | null)[]) => void;
}

export class Router {
private routes: Route[];

constructor() {
this.routes = [];
}

exec(path: string) {
for (const route of this.routes) {
const isMatch = route.regex.test(path);

if (isMatch) {
const params = this.extractParameters(route.regex, path);
route.callback(params);
break;
}
}
}

add(segment: string, callback: (params: (string | null)[]) => void) {
const regex = this.routeToRegExp(segment);

this.routes.push({ path: segment, regex, callback });
}

/**
* Convert a route string into a regular expression, suitable for matching.
*/
routeToRegExp(route: string): RegExp {
route = route
.replace(escapeRegExp, "\\$&")
.replace(optionalParam, "(?:$1)?")
.replace(namedParam, function (match, optional) {
return optional ? match : "([^/?]+)";
})
.replace(splatParam, "([^?]*?)");
return new RegExp("^" + route + "(?:\\?([\\s\\S]*))?$");
}

/**
* Given a route, and a URL fragment that it matches, return the array of
* extracted decoded parameters. Empty or unmatched parameters will be treated as `null`
* to normalize cross-browser behavior.
*/
extractParameters(route: RegExp, fragment: string): Array<string | null> {
const result = route.exec(fragment);
if (!result) return [];

// Removes the first item from the result array because its the input text executed by the regex.
const params = result.slice(1);

return params.map((param, i) => {
// Don't decode the search params.
if (i === params.length - 1) return param || null;
return param ? decodeURIComponent(param) : null;
});
}
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"scripts": {
"build": "tsup lib/index.ts --format cjs,esm --dts",
"release": "pnpm run build && changeset publish",
"test": "jest --passWithNoTests --updateSnapshot --coverage",
"lint": "tsc"
},
"keywords": [
Expand All @@ -23,6 +24,9 @@
],
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@types/jest": "^29.5.3",
"jest": "^29.6.2",
"ts-jest": "^29.1.1",
"tsup": "^7.2.0",
"typescript": "^5.1.6"
}
Expand Down
Loading

0 comments on commit 4025551

Please sign in to comment.