nextjs-route-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +25 -0
- package/README.md +36 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +29 -0
- package/index.test.ts +40 -0
- package/index.ts +39 -0
- package/package.json +32 -0
- package/tsconfig.json +14 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
{
|
2
|
+
"editor.formatOnSave": true,
|
3
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
4
|
+
"[javascript]": {
|
5
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
6
|
+
},
|
7
|
+
"[javascriptreact]": {
|
8
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
9
|
+
},
|
10
|
+
"[typescript]": {
|
11
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
12
|
+
},
|
13
|
+
"[typescriptreact]": {
|
14
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
15
|
+
},
|
16
|
+
"[css]": {
|
17
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
18
|
+
},
|
19
|
+
"[html]": {
|
20
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
21
|
+
},
|
22
|
+
"[jsonc]": {
|
23
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
24
|
+
}
|
25
|
+
}
|
package/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# nextjs-route-utils
|
2
|
+
|
3
|
+
> A lightweight, type-safe route builder utility for Next.js
|
4
|
+
> Build dynamic and optional routes with ease and full TypeScript support.
|
5
|
+
|
6
|
+
<p>
|
7
|
+
<a href="https://github.com/wolfdev1337" style="padding-left:5px"><img alt="X platform" src="https://img.shields.io/badge/Github-25292f"></a>
|
8
|
+
<a href="https://x.com/WolfDev1337"><img alt="Github platform" src="https://img.shields.io/badge/Twitter-1DA1F2"></a>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
## Why use `nextjs-route-utils`?
|
14
|
+
|
15
|
+
In Next.js projects, especially with dynamic routes, managing route strings manually can lead to typos, inconsistencies, and bugs.
|
16
|
+
This package lets you **define your route patterns once** and then **build URLs with typed parameters**, ensuring correctness across your app.
|
17
|
+
|
18
|
+
---
|
19
|
+
|
20
|
+
## Features
|
21
|
+
|
22
|
+
- Define routes with dynamic (`:param`) and optional (`:param?`) segments
|
23
|
+
- Type-safe parameter enforcement in TypeScript
|
24
|
+
- Automatic URL encoding of parameter values
|
25
|
+
- Removes optional params when not provided
|
26
|
+
- Lightweight and zero dependencies
|
27
|
+
|
28
|
+
---
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
```bash
|
33
|
+
npm install nextjs-route-utils
|
34
|
+
# or
|
35
|
+
yarn add nextjs-route-utils
|
36
|
+
```
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
type Params = Record<string, string | number | undefined>;
|
2
|
+
/**
|
3
|
+
* Creates a type-safe route builder for dynamic and optional segments
|
4
|
+
* @param pattern A path pattern like "/users/:userId/posts/:postId?"
|
5
|
+
* @returns A function that builds the URL with given params
|
6
|
+
*/
|
7
|
+
export declare function createRoute<T extends Params>(pattern: string): (params: T) => string;
|
8
|
+
export {};
|
package/dist/index.js
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Creates a type-safe route builder for dynamic and optional segments
|
3
|
+
* @param pattern A path pattern like "/users/:userId/posts/:postId?"
|
4
|
+
* @returns A function that builds the URL with given params
|
5
|
+
*/
|
6
|
+
export function createRoute(pattern) {
|
7
|
+
const keys = Array.from(pattern.matchAll(/:([a-zA-Z0-9_]+)\??/g)).map((match) => match[1]);
|
8
|
+
return (params) => {
|
9
|
+
let result = pattern;
|
10
|
+
keys.forEach((key) => {
|
11
|
+
const value = params[key];
|
12
|
+
const optional = result.includes(`:${key}?`);
|
13
|
+
if (value !== undefined) {
|
14
|
+
result = result.replace(`:${key}${optional ? "?" : ""}`, encodeURIComponent(String(value)));
|
15
|
+
}
|
16
|
+
else if (optional) {
|
17
|
+
// Remove optional segment (including optional slash)
|
18
|
+
result = result.replace(new RegExp(`/?:${key}\?`), "");
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
throw new Error(`Missing required route parameter: ${key}`);
|
22
|
+
}
|
23
|
+
});
|
24
|
+
// Remove leftover optional markers (e.g., '/posts?')
|
25
|
+
result = result.replace(/\?(?=\/|$)/g, "");
|
26
|
+
// Cleanup extra slashes
|
27
|
+
return result.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
28
|
+
};
|
29
|
+
}
|
package/index.test.ts
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
2
|
+
import { createRoute } from "./index";
|
3
|
+
|
4
|
+
describe("createRoute", () => {
|
5
|
+
it("builds static routes correctly", () => {
|
6
|
+
const home = createRoute<{}>("/");
|
7
|
+
expect(home({})).toBe("/");
|
8
|
+
});
|
9
|
+
|
10
|
+
it("builds dynamic routes correctly", () => {
|
11
|
+
const user = createRoute<{ userId: string }>("/users/:userId");
|
12
|
+
expect(user({ userId: "abc" })).toBe("/users/abc");
|
13
|
+
});
|
14
|
+
|
15
|
+
it("builds multiple dynamic segments", () => {
|
16
|
+
const post = createRoute<{ userId: string; postId: number }>(
|
17
|
+
"/users/:userId/posts/:postId"
|
18
|
+
);
|
19
|
+
expect(post({ userId: "john", postId: 42 })).toBe("/users/john/posts/42");
|
20
|
+
});
|
21
|
+
|
22
|
+
it("supports optional params", () => {
|
23
|
+
const post = createRoute<{ postId?: string }>("/posts/:postId?");
|
24
|
+
expect(post({})).toBe("/posts");
|
25
|
+
expect(post({ postId: "123" })).toBe("/posts/123");
|
26
|
+
});
|
27
|
+
|
28
|
+
it("throws if required param is missing", () => {
|
29
|
+
const user = createRoute<{ userId: string }>("/users/:userId");
|
30
|
+
// @ts-expect-error intentionally missing param
|
31
|
+
expect(() => user({})).toThrowError(
|
32
|
+
"Missing required route parameter: userId"
|
33
|
+
);
|
34
|
+
});
|
35
|
+
|
36
|
+
it("encodes URI components", () => {
|
37
|
+
const encoded = createRoute<{ name: string }>("/hello/:name");
|
38
|
+
expect(encoded({ name: "John Doe" })).toBe("/hello/John%20Doe");
|
39
|
+
});
|
40
|
+
});
|
package/index.ts
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
type Params = Record<string, string | number | undefined>;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Creates a type-safe route builder for dynamic and optional segments
|
5
|
+
* @param pattern A path pattern like "/users/:userId/posts/:postId?"
|
6
|
+
* @returns A function that builds the URL with given params
|
7
|
+
*/
|
8
|
+
export function createRoute<T extends Params>(pattern: string) {
|
9
|
+
const keys = Array.from(pattern.matchAll(/:([a-zA-Z0-9_]+)\??/g)).map(
|
10
|
+
(match) => match[1]
|
11
|
+
);
|
12
|
+
|
13
|
+
return (params: T): string => {
|
14
|
+
let result = pattern;
|
15
|
+
|
16
|
+
keys.forEach((key) => {
|
17
|
+
const value = params[key];
|
18
|
+
const optional = result.includes(`:${key}?`);
|
19
|
+
|
20
|
+
if (value !== undefined) {
|
21
|
+
result = result.replace(
|
22
|
+
`:${key}${optional ? "?" : ""}`,
|
23
|
+
encodeURIComponent(String(value))
|
24
|
+
);
|
25
|
+
} else if (optional) {
|
26
|
+
// Remove optional segment (including optional slash)
|
27
|
+
result = result.replace(new RegExp(`/?:${key}\?`), "");
|
28
|
+
} else {
|
29
|
+
throw new Error(`Missing required route parameter: ${key}`);
|
30
|
+
}
|
31
|
+
});
|
32
|
+
|
33
|
+
// Remove leftover optional markers (e.g., '/posts?')
|
34
|
+
result = result.replace(/\?(?=\/|$)/g, "");
|
35
|
+
|
36
|
+
// Cleanup extra slashes
|
37
|
+
return result.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
38
|
+
};
|
39
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"name": "nextjs-route-utils",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"main": "dist/index.js",
|
5
|
+
"types": "dist/index.d.ts",
|
6
|
+
"scripts": {
|
7
|
+
"build": "tsc",
|
8
|
+
"test": "vitest run",
|
9
|
+
"prebuild": "npm run test",
|
10
|
+
"prepublishOnly": "npm run build",
|
11
|
+
"dev": "vitest"
|
12
|
+
},
|
13
|
+
"repository": {
|
14
|
+
"type": "git",
|
15
|
+
"url": "git+https://github.com/wolfdev1337/nextjs-route-utils.git"
|
16
|
+
},
|
17
|
+
"keywords": [
|
18
|
+
"nextjs",
|
19
|
+
"routes",
|
20
|
+
"utility",
|
21
|
+
"typescript"
|
22
|
+
],
|
23
|
+
"author": "Wolf Developer",
|
24
|
+
"license": "ISC",
|
25
|
+
"description": "",
|
26
|
+
"devDependencies": {
|
27
|
+
"@types/node": "^24.0.10",
|
28
|
+
"tsx": "^4.20.3",
|
29
|
+
"typescript": "^5.8.3",
|
30
|
+
"vitest": "^3.2.4"
|
31
|
+
}
|
32
|
+
}
|
package/tsconfig.json
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"target": "ES2020",
|
4
|
+
"module": "ESNext",
|
5
|
+
"moduleResolution": "Node",
|
6
|
+
"declaration": true,
|
7
|
+
"outDir": "dist",
|
8
|
+
"strict": true,
|
9
|
+
"esModuleInterop": true,
|
10
|
+
"skipLibCheck": true,
|
11
|
+
"forceConsistentCasingInFileNames": true
|
12
|
+
},
|
13
|
+
"include": ["index.ts"]
|
14
|
+
}
|