next-typed-paths 0.1.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/README.md +249 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +45 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +16 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +22 -0
- package/dist/constants.js.map +1 -0
- package/dist/file.d.ts +7 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +13 -0
- package/dist/file.js.map +1 -0
- package/dist/generator.d.ts +9 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +80 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime.d.ts +12 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +64 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scanner.d.ts +13 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +78 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/watcher.d.ts +10 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +66 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Next Typed Routes
|
|
2
|
+
|
|
3
|
+
Type-safe Next.js App Router route builder with automatic generation from your file system.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- š **Fully Type-Safe**: Get autocomplete and type checking for all your routes
|
|
8
|
+
- š **Auto-Generated**: Scans your Next.js app directory and generates routes automatically
|
|
9
|
+
- š **Live Updates**: Watch mode regenerates routes when files change
|
|
10
|
+
- āļø **Configurable**: Support for config files and CLI options
|
|
11
|
+
- š¦ **Zero Runtime Cost**: All types are compile-time only
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install next-typed-paths
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Generate Routes
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx next-typed-paths generate --input ./src/app/api --output ./src/generated/routes.ts
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Use in Your Code
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { routes } from "./generated/routes";
|
|
31
|
+
|
|
32
|
+
// Type-safe route building
|
|
33
|
+
const userRoute = routes.api.users.$userId("123"); // "/api/users/123"
|
|
34
|
+
const listRoute = routes.api.users.$(); // "/api/users"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
Create a `routes.config.ts` file in your project root:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import type { RouteConfig } from "next-typed-paths";
|
|
43
|
+
|
|
44
|
+
const routeConfig: RouteConfig = {
|
|
45
|
+
input: "./src/app/api",
|
|
46
|
+
output: "./src/generated/routes.ts",
|
|
47
|
+
watch: false,
|
|
48
|
+
basePrefix: "/api",
|
|
49
|
+
paramTypes: {
|
|
50
|
+
userId: "string",
|
|
51
|
+
postId: "number",
|
|
52
|
+
},
|
|
53
|
+
imports: ["import { z } from 'zod';"],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default routeConfig;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Configuration Options
|
|
60
|
+
|
|
61
|
+
- **`input`** (`string`, required): The directory path to scan for route files. This should point to your Next.js API routes directory (e.g., `./src/app/api` or `./src/app`). You can use next-typed-paths for just your REST API backend or also for any page routes that return UI.
|
|
62
|
+
|
|
63
|
+
- **`output`** (`string`, required): The file path where the generated TypeScript routes file will be written. This file will contain all your type-safe route builders.
|
|
64
|
+
|
|
65
|
+
- **`watch`** (`boolean`, optional): When set to `true`, the generator will run in watch mode and automatically regenerate routes whenever files change in the input directory. Defaults to `false`.
|
|
66
|
+
|
|
67
|
+
- **`basePrefix`** (`string`, optional): A prefix that will be prepended to all generated routes. For example, if your API routes are under `/api`, set this to `"/api"` so generated routes include this prefix. Defaults to `""`.
|
|
68
|
+
|
|
69
|
+
- **`paramTypes`** (`Record<string, string>`, optional): A mapping of parameter names to their TypeScript types. This allows you to specify custom types for dynamic route parameters instead of the default `string` type. For example, `{ userId: "number", postId: "string" }` will type the `userId` parameter as a number. Defaults to `{}`. You can even specify custom stricter types such as database table, user type, etc. Any unspecified route params will have their type defaulted to `string`.
|
|
70
|
+
|
|
71
|
+
- **`imports`** (`string[]`, optional): An array of import statements to include at the top of the generated routes file. Useful if your route builders need to reference custom types or utilities. For example, `["import { z } from 'zod';", "import type { User } from './types';"]`. Defaults to `[]`.
|
|
72
|
+
|
|
73
|
+
## CLI Commands
|
|
74
|
+
|
|
75
|
+
### Generate Routes
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx next-typed-paths generate [options]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
|
|
83
|
+
- `-i, --input <path>`: Input directory to scan (default: "./app/api")
|
|
84
|
+
- `-o, --output <path>`: Output file path (default: "./generated/routes.ts")
|
|
85
|
+
- `-w, --watch`: Watch for changes and regenerate
|
|
86
|
+
- `-c, --config <path>`: Path to config file
|
|
87
|
+
|
|
88
|
+
### Watch Mode
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx next-typed-paths generate --watch
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This will watch your app directory and automatically regenerate routes when files change.
|
|
95
|
+
|
|
96
|
+
### Integration with Development Workflow
|
|
97
|
+
|
|
98
|
+
You can integrate the route generator into your development workflow to automatically regenerate routes alongside your dev server. For example, if you are using [Nx](https://github.com/nrwl/nx), you can run both the Next.js dev server and the route generator in parallel:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
// project.json
|
|
102
|
+
{
|
|
103
|
+
"name": "your-next-app",
|
|
104
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
105
|
+
"sourceRoot": "apps/your-next-app",
|
|
106
|
+
"projectType": "application",
|
|
107
|
+
"targets": {
|
|
108
|
+
"dev": {
|
|
109
|
+
"executor": "nx:run-commands",
|
|
110
|
+
"options": {
|
|
111
|
+
"commands": ["next dev", "npx next-typed-paths generate --watch"],
|
|
112
|
+
"parallel": true
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"build": {
|
|
116
|
+
"executor": "@nx/next:build",
|
|
117
|
+
"outputs": ["{options.outputPath}"],
|
|
118
|
+
"options": {
|
|
119
|
+
"outputPath": "dist/apps/your-next-app"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
With `"parallel": true`, both commands run simultaneously:
|
|
127
|
+
|
|
128
|
+
- `next dev` starts your Next.js development server
|
|
129
|
+
- `npx next-typed-paths generate --watch` watches for route file changes and regenerates types
|
|
130
|
+
|
|
131
|
+
This ensures your route types stay in sync with your file system as you develop.
|
|
132
|
+
|
|
133
|
+
## How It Works
|
|
134
|
+
|
|
135
|
+
The generator scans your Next.js app directory structure:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
app/api/
|
|
139
|
+
āāā users/
|
|
140
|
+
ā āāā route.ts ā routes.api.users.$()
|
|
141
|
+
ā āāā [userId]/
|
|
142
|
+
ā āāā route.ts ā routes.api.users.$userId(id)
|
|
143
|
+
āāā posts/
|
|
144
|
+
āāā route.ts ā routes.api.posts.$()
|
|
145
|
+
āāā [postId]/
|
|
146
|
+
āāā route.ts ā routes.api.posts.$postId(id)
|
|
147
|
+
āāā comments/
|
|
148
|
+
āāā route.ts ā routes.api.posts.$postId(id).comments()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
It uses the directory structure to generate in realtime a typed schema of the available routes in your Next.Js application. You are still responsible for ensuring you use the route in the correct way (i.e. correct HTTP method and query params), however, the route and path params are typed for you.
|
|
152
|
+
|
|
153
|
+
## Examples
|
|
154
|
+
|
|
155
|
+
### Basic Usage
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { routes } from "./generated/routes";
|
|
159
|
+
|
|
160
|
+
// Static routes
|
|
161
|
+
routes.api.auth.login(); // "/api/auth/login"
|
|
162
|
+
|
|
163
|
+
// Dynamic routes
|
|
164
|
+
routes.api.users.$userId("123"); // "/api/users/123"
|
|
165
|
+
|
|
166
|
+
// Nested dynamic routes
|
|
167
|
+
routes.api.posts.$postId("456").comments(); // "/api/posts/456/comments"
|
|
168
|
+
|
|
169
|
+
// Access parent route
|
|
170
|
+
routes.api.users.$userId("123").$(); // "/api/users/123"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### With Next.js
|
|
174
|
+
|
|
175
|
+
By no means are the following examples an indication you are pinned to using certain libraries (e.g. Axios, Tanstack Query). Rather I provide some examples within the context of some common patterns.
|
|
176
|
+
|
|
177
|
+
#### In Client Components
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
'use client';
|
|
181
|
+
|
|
182
|
+
import { useState } from "react";
|
|
183
|
+
|
|
184
|
+
import axios from "axios";
|
|
185
|
+
|
|
186
|
+
import { User } from "@/common/types";
|
|
187
|
+
import { routes } from '@/generated/routes';
|
|
188
|
+
|
|
189
|
+
export const UsersList = () => {
|
|
190
|
+
const [users, setUsers] = useState<User[]>([]);
|
|
191
|
+
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
// Type-safe API calls from the client
|
|
194
|
+
axios.get(routes.api.users.$())
|
|
195
|
+
.then(res => res.data)
|
|
196
|
+
.then(setUsers);
|
|
197
|
+
}, []);
|
|
198
|
+
|
|
199
|
+
return <div>{/* render users */}</div>;
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### In Server Components
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const UserProfile = async ({ userId }: { userId: string }) => {
|
|
207
|
+
// Call your API with type-safe routes
|
|
208
|
+
const { data: user } = await axios.get(routes.api.users.$userId(userId));
|
|
209
|
+
return <div>{user.name}</div>;
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### For redirects
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { redirect } from "next/navigation";
|
|
217
|
+
|
|
218
|
+
const handleLogin = (userId: string) => {
|
|
219
|
+
redirect(routes.api.auth.callback.$());
|
|
220
|
+
};
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Building URLs for links
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
const UserLink = ({ userId }: { userId: string }) => {
|
|
227
|
+
return <a href={routes.api.users.$userId(userId)}>View Profile</a>;
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### With [TanStack Query](https://github.com/TanStack/query)
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { useQuery } from '@tanstack/react-query';
|
|
235
|
+
|
|
236
|
+
const UserProfile = ({ userId }: { userId: string }) => {
|
|
237
|
+
const { data: user, isLoading } = useQuery({
|
|
238
|
+
queryKey: ['user', userId],
|
|
239
|
+
queryFn: () => axios.get(routes.api.users.$userId(userId)).then(res => res.data),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (isLoading) return <div>Loading...</div>;
|
|
243
|
+
return <div>{user?.name}</div>;
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;GAEG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for Next.js route generation
|
|
4
|
+
*/
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { writeFile } from "fs/promises";
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
import { loadConfig, mergeConfig } from "./config";
|
|
10
|
+
import { CLI_NAME, CONFIG_FILE_NAME, DEFAULT_BASE_PREFIX, DEFAULT_INPUT_DIR, DEFAULT_OUTPUT_FILE, PACKAGE_NAME, PACKAGE_VERSION, } from "./constants";
|
|
11
|
+
import { mkdirIfNotExists } from "./file";
|
|
12
|
+
import { generateRouteFile } from "./generator";
|
|
13
|
+
import { generateRouteStructure } from "./scanner";
|
|
14
|
+
import { startWatcher } from "./watcher";
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name(CLI_NAME)
|
|
18
|
+
.description("Generate type-safe routes from Next.js app directory structure")
|
|
19
|
+
.version(PACKAGE_VERSION);
|
|
20
|
+
/**
|
|
21
|
+
* Generate routes from directory structure
|
|
22
|
+
*/
|
|
23
|
+
const generateRoutes = async (config) => {
|
|
24
|
+
try {
|
|
25
|
+
console.log("š Scanning directory:", config.input);
|
|
26
|
+
// Scan directory structure
|
|
27
|
+
const structure = await generateRouteStructure(config.input);
|
|
28
|
+
// Generate TypeScript code
|
|
29
|
+
const code = generateRouteFile(structure, config);
|
|
30
|
+
// Ensure output directory exists
|
|
31
|
+
const outputDir = dirname(config.output);
|
|
32
|
+
await mkdirIfNotExists(outputDir);
|
|
33
|
+
// Write output file
|
|
34
|
+
await writeFile(config.output, code, "utf-8");
|
|
35
|
+
console.log("ā
Routes generated successfully:");
|
|
36
|
+
console.log(" š Input:", config.input);
|
|
37
|
+
console.log(" š Output:", config.output);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error("ā Error generating routes:", error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Generate command
|
|
46
|
+
*/
|
|
47
|
+
program
|
|
48
|
+
.command("generate")
|
|
49
|
+
.description("Generate route file from Next.js app directory")
|
|
50
|
+
.option("-i, --input <path>", "Input directory to scan")
|
|
51
|
+
.option("-o, --output <path>", "Output file path")
|
|
52
|
+
.option("-w, --watch", "Watch for changes and regenerate")
|
|
53
|
+
.option("-c, --config <path>", "Path to config file")
|
|
54
|
+
.option("-p, --prefix <prefix>", "Base prefix for all routes")
|
|
55
|
+
.action(async (options) => {
|
|
56
|
+
try {
|
|
57
|
+
// Load base config from file
|
|
58
|
+
const baseConfig = await loadConfig(options.config);
|
|
59
|
+
// Merge with CLI options
|
|
60
|
+
const config = mergeConfig(baseConfig, {
|
|
61
|
+
input: options.input,
|
|
62
|
+
output: options.output,
|
|
63
|
+
watch: options.watch,
|
|
64
|
+
basePrefix: options.prefix,
|
|
65
|
+
});
|
|
66
|
+
// Generate initial routes
|
|
67
|
+
await generateRoutes(config);
|
|
68
|
+
// Start watch mode if requested
|
|
69
|
+
if (config.watch) {
|
|
70
|
+
startWatcher(config, async () => {
|
|
71
|
+
await generateRoutes(config);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error(error);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* Init command - create example config file
|
|
82
|
+
*/
|
|
83
|
+
program
|
|
84
|
+
.command("init")
|
|
85
|
+
.description("Create a routes.config.ts file with defaults")
|
|
86
|
+
.action(async () => {
|
|
87
|
+
const configContent = `import type { RouteConfig } from "${PACKAGE_NAME}";
|
|
88
|
+
|
|
89
|
+
const config: RouteConfig = {
|
|
90
|
+
input: "${DEFAULT_INPUT_DIR}",
|
|
91
|
+
output: "${DEFAULT_OUTPUT_FILE}",
|
|
92
|
+
watch: false,
|
|
93
|
+
basePrefix: "${DEFAULT_BASE_PREFIX}",
|
|
94
|
+
paramTypes: {
|
|
95
|
+
// Add custom parameter types here
|
|
96
|
+
// Example: userId: 'string', postId: 'number'
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default config;
|
|
101
|
+
`;
|
|
102
|
+
const configPath = join(process.cwd(), `${CONFIG_FILE_NAME}.ts`);
|
|
103
|
+
if (existsSync(configPath)) {
|
|
104
|
+
console.log(`ā ļø ${CONFIG_FILE_NAME}.ts already exists`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await writeFile(configPath, configContent, "utf-8");
|
|
108
|
+
console.log(`ā
Created ${CONFIG_FILE_NAME}.ts`);
|
|
109
|
+
});
|
|
110
|
+
program.parse();
|
|
111
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader using cosmiconfig
|
|
3
|
+
*/
|
|
4
|
+
import { RouteConfig } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Default configuration
|
|
7
|
+
*/
|
|
8
|
+
export declare const defaultConfig: RouteConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Load configuration from file or use defaults
|
|
11
|
+
*/
|
|
12
|
+
export declare const loadConfig: (configPath?: string) => Promise<RouteConfig>;
|
|
13
|
+
/**
|
|
14
|
+
* Merge config with CLI options
|
|
15
|
+
*/
|
|
16
|
+
export declare const mergeConfig: (baseConfig: RouteConfig, options: Partial<RouteConfig>) => RouteConfig;
|
|
17
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAKtC;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,WAM3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,WAAW,CAgBzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,YAAY,WAAW,EAAE,SAAS,OAAO,CAAC,WAAW,CAAC,KAAG,WAKpF,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader using cosmiconfig
|
|
3
|
+
*/
|
|
4
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
5
|
+
import { DEFAULT_INPUT_DIR, DEFAULT_OUTPUT_FILE, DEFAULT_BASE_PREFIX, CONFIG_MODULE_NAME } from "./constants";
|
|
6
|
+
const explorer = cosmiconfig(CONFIG_MODULE_NAME);
|
|
7
|
+
/**
|
|
8
|
+
* Default configuration
|
|
9
|
+
*/
|
|
10
|
+
export const defaultConfig = {
|
|
11
|
+
input: DEFAULT_INPUT_DIR,
|
|
12
|
+
output: DEFAULT_OUTPUT_FILE,
|
|
13
|
+
watch: false,
|
|
14
|
+
basePrefix: DEFAULT_BASE_PREFIX,
|
|
15
|
+
paramTypes: {},
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Load configuration from file or use defaults
|
|
19
|
+
*/
|
|
20
|
+
export const loadConfig = async (configPath) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = configPath ? await explorer.load(configPath) : await explorer.search();
|
|
23
|
+
if (result && result.config) {
|
|
24
|
+
return {
|
|
25
|
+
...defaultConfig,
|
|
26
|
+
...result.config,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
// Config file not found or invalid, use defaults
|
|
32
|
+
console.warn("No config file found, using defaults");
|
|
33
|
+
}
|
|
34
|
+
return defaultConfig;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Merge config with CLI options
|
|
38
|
+
*/
|
|
39
|
+
export const mergeConfig = (baseConfig, options) => {
|
|
40
|
+
return {
|
|
41
|
+
...baseConfig,
|
|
42
|
+
...Object.fromEntries(Object.entries(options).filter(([_, v]) => v !== undefined)),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9G,MAAM,QAAQ,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;AAEjD;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,KAAK,EAAE,iBAAiB;IACxB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,KAAK;IACZ,UAAU,EAAE,mBAAmB;IAC/B,UAAU,EAAE,EAAE;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,UAAmB,EAAwB,EAAE;IAC5E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QAEtF,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAC5B,OAAO;gBACL,GAAG,aAAa;gBAChB,GAAG,MAAM,CAAC,MAAM;aACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iDAAiD;QACjD,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAuB,EAAE,OAA6B,EAAe,EAAE;IACjG,OAAO;QACL,GAAG,UAAU;QACb,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;KACnF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants used throughout the package
|
|
3
|
+
*/
|
|
4
|
+
export declare const PACKAGE_NAME = "next-typed-paths";
|
|
5
|
+
export declare const PACKAGE_VERSION: any;
|
|
6
|
+
export declare const CLI_NAME = "next-typed-paths";
|
|
7
|
+
export declare const CONFIG_MODULE_NAME = "routes";
|
|
8
|
+
export declare const CONFIG_FILE_NAME = "routes.config";
|
|
9
|
+
export declare const DEFAULT_INPUT_DIR = "./app/api";
|
|
10
|
+
export declare const DEFAULT_OUTPUT_FILE = "./generated/routes.ts";
|
|
11
|
+
export declare const DEFAULT_BASE_PREFIX = "/api";
|
|
12
|
+
export declare const WATCH_DEBOUNCE_MS = 300;
|
|
13
|
+
export declare const ROUTE_FILE_EXTENSIONS: string[];
|
|
14
|
+
export declare const ROUTE_FILE_NAME = "route";
|
|
15
|
+
export declare const PAGE_FILE_NAME = "page";
|
|
16
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,eAAO,MAAM,YAAY,qBAAqB,CAAC;AAC/C,eAAO,MAAM,eAAe,KAAiB,CAAC;AAC9C,eAAO,MAAM,QAAQ,qBAAe,CAAC;AACrC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAC3C,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAC3D,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAE1C,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,qBAAqB,UAAiC,CAAC;AACpE,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants used throughout the package
|
|
3
|
+
*/
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
9
|
+
const packageVersion = packageJson.version;
|
|
10
|
+
export const PACKAGE_NAME = "next-typed-paths";
|
|
11
|
+
export const PACKAGE_VERSION = packageVersion;
|
|
12
|
+
export const CLI_NAME = PACKAGE_NAME;
|
|
13
|
+
export const CONFIG_MODULE_NAME = "routes";
|
|
14
|
+
export const CONFIG_FILE_NAME = "routes.config";
|
|
15
|
+
export const DEFAULT_INPUT_DIR = "./app/api";
|
|
16
|
+
export const DEFAULT_OUTPUT_FILE = "./generated/routes.ts";
|
|
17
|
+
export const DEFAULT_BASE_PREFIX = "/api";
|
|
18
|
+
export const WATCH_DEBOUNCE_MS = 300;
|
|
19
|
+
export const ROUTE_FILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
20
|
+
export const ROUTE_FILE_NAME = "route";
|
|
21
|
+
export const PAGE_FILE_NAME = "page";
|
|
22
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAElC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1F,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,CAAC;AAE3C,MAAM,CAAC,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,CAAC;AAC9C,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAC3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAEhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AACvC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC"}
|
package/dist/file.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAIpE,CAAC"}
|
package/dist/file.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { mkdir } from "fs/promises";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
/**
|
|
4
|
+
* Make sure a directory exists, creating it if necessary
|
|
5
|
+
*
|
|
6
|
+
* @param dirPath - The path of the directory to ensure exists
|
|
7
|
+
*/
|
|
8
|
+
export const mkdirIfNotExists = async (dirPath) => {
|
|
9
|
+
if (!existsSync(dirPath)) {
|
|
10
|
+
await mkdir(dirPath, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=file.js.map
|
package/dist/file.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAEhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAe,EAAiB,EAAE;IACvE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code generator for route files
|
|
3
|
+
*/
|
|
4
|
+
import { RouteNode, RouteConfig } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Generate complete route file content
|
|
7
|
+
*/
|
|
8
|
+
export declare const generateRouteFile: (structure: RouteNode, config: RouteConfig) => string;
|
|
9
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAkDjD;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,SAAS,EAAE,QAAQ,WAAW,KAAG,MA8B7E,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code generator for route files
|
|
3
|
+
*/
|
|
4
|
+
import { PACKAGE_NAME } from "./constants";
|
|
5
|
+
/**
|
|
6
|
+
* Generate TypeScript code for route structure constant
|
|
7
|
+
*/
|
|
8
|
+
const generateStructureCode = (structure, indent = 2) => {
|
|
9
|
+
const indentStr = " ".repeat(indent);
|
|
10
|
+
const lines = [];
|
|
11
|
+
// Sort entries to put $param and $route first
|
|
12
|
+
const entries = Object.entries(structure);
|
|
13
|
+
const metadataEntries = entries.filter(([key]) => key.startsWith("$"));
|
|
14
|
+
const regularEntries = entries.filter(([key]) => !key.startsWith("$"));
|
|
15
|
+
const sortedEntries = [...metadataEntries, ...regularEntries];
|
|
16
|
+
for (const [key, value] of sortedEntries) {
|
|
17
|
+
// Check if key needs to be quoted (contains special characters)
|
|
18
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(key);
|
|
19
|
+
const quotedKey = needsQuotes ? `"${key}"` : key;
|
|
20
|
+
if (typeof value === "object" && value !== null) {
|
|
21
|
+
const childLines = generateStructureCode(value, indent + 2);
|
|
22
|
+
if (childLines) {
|
|
23
|
+
lines.push(`${indentStr}${quotedKey}: {`);
|
|
24
|
+
lines.push(childLines);
|
|
25
|
+
lines.push(`${indentStr}},`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else if (typeof value === "boolean") {
|
|
29
|
+
lines.push(`${indentStr}${quotedKey}: ${value},`);
|
|
30
|
+
}
|
|
31
|
+
else if (typeof value === "string") {
|
|
32
|
+
lines.push(`${indentStr}${quotedKey}: "${value}",`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return lines.join("\n");
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Generate parameter type map from config
|
|
39
|
+
*/
|
|
40
|
+
const generateParamTypeMap = (paramTypes) => {
|
|
41
|
+
if (!paramTypes || Object.keys(paramTypes).length === 0) {
|
|
42
|
+
return "{}";
|
|
43
|
+
}
|
|
44
|
+
const entries = Object.entries(paramTypes).map(([key, type]) => ` ${key}: ${type};`);
|
|
45
|
+
return `{\n${entries.join("\n")}\n}`;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Generate complete route file content
|
|
49
|
+
*/
|
|
50
|
+
export const generateRouteFile = (structure, config) => {
|
|
51
|
+
const structureCode = generateStructureCode(structure);
|
|
52
|
+
const paramTypeMap = generateParamTypeMap(config.paramTypes);
|
|
53
|
+
const basePrefix = config.basePrefix || "";
|
|
54
|
+
const customImports = config.imports?.join("\n") || "";
|
|
55
|
+
return `/**
|
|
56
|
+
* Auto-generated Next.js route builder
|
|
57
|
+
* Generated from: ${config.input}
|
|
58
|
+
*
|
|
59
|
+
* DO NOT EDIT THIS FILE MANUALLY - it will be regenerated
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
import { createRouteBuilder } from "${PACKAGE_NAME}/runtime";
|
|
63
|
+
import type { RouteBuilderObject, GetParamType, HasChildren, RouteBuilder } from "${PACKAGE_NAME}/types";
|
|
64
|
+
${customImports ? "\n" + customImports + "\n" : ""}
|
|
65
|
+
// Route structure definition
|
|
66
|
+
const ROUTE_STRUCTURE = {
|
|
67
|
+
${structureCode}
|
|
68
|
+
} as const;
|
|
69
|
+
|
|
70
|
+
// Parameter type mappings
|
|
71
|
+
type ParamTypeMap = ${paramTypeMap};
|
|
72
|
+
|
|
73
|
+
// Type-safe route builder with parameter types
|
|
74
|
+
export type Routes = RouteBuilderObject<typeof ROUTE_STRUCTURE, ParamTypeMap>;
|
|
75
|
+
|
|
76
|
+
// Route builder instance
|
|
77
|
+
export const routes = createRouteBuilder(ROUTE_STRUCTURE, [], "${basePrefix}") as Routes;
|
|
78
|
+
`;
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the package
|
|
3
|
+
*/
|
|
4
|
+
export { defaultConfig, loadConfig, mergeConfig } from "./config";
|
|
5
|
+
export * from "./constants";
|
|
6
|
+
export { generateRouteFile } from "./generator";
|
|
7
|
+
export { buildRoutePath, createRouteBuilder } from "./runtime";
|
|
8
|
+
export { generateRouteStructure, scanDirectory } from "./scanner";
|
|
9
|
+
export type { GetParamType, HasChildren, RouteBuilder, RouteBuilderObject, RouteConfig, RouteNode } from "./types";
|
|
10
|
+
export { startWatcher } from "./watcher";
|
|
11
|
+
export type { RegenerateCallback } from "./watcher";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClE,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the package
|
|
3
|
+
*/
|
|
4
|
+
export { defaultConfig, loadConfig, mergeConfig } from "./config";
|
|
5
|
+
export * from "./constants";
|
|
6
|
+
export { generateRouteFile } from "./generator";
|
|
7
|
+
export { buildRoutePath, createRouteBuilder } from "./runtime";
|
|
8
|
+
export { generateRouteStructure, scanDirectory } from "./scanner";
|
|
9
|
+
export { startWatcher } from "./watcher";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClE,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core route builder runtime
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Build a typed API route path from segments
|
|
6
|
+
*/
|
|
7
|
+
export declare const buildRoutePath: (segments: (string | number)[], basePrefix?: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Recursively build route builder functions from route structure
|
|
10
|
+
*/
|
|
11
|
+
export declare const createRouteBuilder: <T extends Record<string, any>>(structure: T, basePath?: (string | number)[], basePrefix?: string) => any;
|
|
12
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,aAAY,MAAW,KAAG,MAGvF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9D,WAAW,CAAC,EACZ,WAAU,CAAC,MAAM,GAAG,MAAM,CAAC,EAAO,EAClC,aAAY,MAAW,KACtB,GAuDF,CAAC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core route builder runtime
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Build a typed API route path from segments
|
|
6
|
+
*/
|
|
7
|
+
export const buildRoutePath = (segments, basePrefix = "") => {
|
|
8
|
+
const path = segments.map((s) => String(s)).join("/");
|
|
9
|
+
return basePrefix ? `${basePrefix}/${path}` : `/${path}`;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Recursively build route builder functions from route structure
|
|
13
|
+
*/
|
|
14
|
+
export const createRouteBuilder = (structure, basePath = [], basePrefix = "") => {
|
|
15
|
+
const builder = {};
|
|
16
|
+
for (const [key, value] of Object.entries(structure)) {
|
|
17
|
+
// Skip metadata keys, but not route segment keys like $documentId
|
|
18
|
+
if (key === "$param" || key === "$route")
|
|
19
|
+
continue;
|
|
20
|
+
const currentPath = [...basePath, key];
|
|
21
|
+
if (typeof value === "object") {
|
|
22
|
+
const hasRoute = value.$route === true;
|
|
23
|
+
const hasParam = "$param" in value;
|
|
24
|
+
// Check if there are children (non-metadata keys)
|
|
25
|
+
const childKeys = Object.keys(value).filter((k) => k !== "$param" && k !== "$route");
|
|
26
|
+
const hasChildren = childKeys.length > 0;
|
|
27
|
+
if (hasParam) {
|
|
28
|
+
// This level has a parameter
|
|
29
|
+
builder[key] = (param) => {
|
|
30
|
+
const paramPath = [...currentPath.slice(0, -1), param];
|
|
31
|
+
if (hasChildren) {
|
|
32
|
+
// Has children, build them with the parameter in the path
|
|
33
|
+
const children = createRouteBuilder(value, paramPath, basePrefix);
|
|
34
|
+
// If this level is also a route, add a $ method
|
|
35
|
+
if (hasRoute) {
|
|
36
|
+
Object.assign(children, { $: () => buildRoutePath(paramPath, basePrefix) });
|
|
37
|
+
}
|
|
38
|
+
return children;
|
|
39
|
+
}
|
|
40
|
+
else if (hasRoute) {
|
|
41
|
+
// Leaf route with parameter
|
|
42
|
+
return buildRoutePath(paramPath, basePrefix);
|
|
43
|
+
}
|
|
44
|
+
return buildRoutePath(paramPath, basePrefix);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else if (hasRoute && !hasChildren) {
|
|
48
|
+
// Leaf route with no children or params
|
|
49
|
+
builder[key] = () => buildRoutePath(currentPath, basePrefix);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Has children, recurse
|
|
53
|
+
const children = createRouteBuilder(value, currentPath, basePrefix);
|
|
54
|
+
if (hasRoute) {
|
|
55
|
+
// Also a route itself
|
|
56
|
+
Object.assign(children, { $: () => buildRoutePath(currentPath, basePrefix) });
|
|
57
|
+
}
|
|
58
|
+
builder[key] = children;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return builder;
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner for Next.js app directory structure
|
|
3
|
+
*/
|
|
4
|
+
import { RouteNode } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Recursively scan a directory and build route structure
|
|
7
|
+
*/
|
|
8
|
+
export declare const scanDirectory: (dirPath: string, basePath?: string) => Promise<RouteNode>;
|
|
9
|
+
/**
|
|
10
|
+
* Scan Next.js app directory and generate route structure
|
|
11
|
+
*/
|
|
12
|
+
export declare const generateRouteStructure: (inputDir: string) => Promise<RouteNode>;
|
|
13
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAoCpC;;GAEG;AACH,eAAO,MAAM,aAAa,GAAU,SAAS,MAAM,EAAE,WAAU,MAAW,KAAG,OAAO,CAAC,SAAS,CAsC7F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,SAAS,CAGhF,CAAC"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scanner for Next.js app directory structure
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { readdir } from "fs/promises";
|
|
6
|
+
import { join, resolve } from "path";
|
|
7
|
+
import { ROUTE_FILE_EXTENSIONS, ROUTE_FILE_NAME, PAGE_FILE_NAME } from "./constants";
|
|
8
|
+
/**
|
|
9
|
+
* Check if a directory contains a route.ts or page.ts file
|
|
10
|
+
*/
|
|
11
|
+
const hasRouteFile = async (dirPath) => {
|
|
12
|
+
const fileNames = [ROUTE_FILE_NAME, PAGE_FILE_NAME];
|
|
13
|
+
const checks = await Promise.all(fileNames.flatMap((fileName) => ROUTE_FILE_EXTENSIONS.map(async (ext) => {
|
|
14
|
+
const filePath = join(dirPath, `${fileName}${ext}`);
|
|
15
|
+
return existsSync(filePath);
|
|
16
|
+
})));
|
|
17
|
+
return checks.some((exists) => exists);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Extract parameter name from Next.js dynamic segment [paramName]
|
|
21
|
+
*/
|
|
22
|
+
const extractParamName = (segment) => {
|
|
23
|
+
const match = segment.match(/^\[(.+)\]$/);
|
|
24
|
+
return match ? (match[1] ?? null) : null;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Convert parameter name from Next.js format to camelCase with $ prefix
|
|
28
|
+
* e.g., [userId] -> $userId, [user-id] -> $userId
|
|
29
|
+
*/
|
|
30
|
+
const formatParamName = (paramName) => {
|
|
31
|
+
// Convert kebab-case to camelCase
|
|
32
|
+
const camelCase = paramName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
33
|
+
return `$${camelCase}`;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Recursively scan a directory and build route structure
|
|
37
|
+
*/
|
|
38
|
+
export const scanDirectory = async (dirPath, basePath = "") => {
|
|
39
|
+
const node = {};
|
|
40
|
+
if (!existsSync(dirPath)) {
|
|
41
|
+
throw new Error(`Directory does not exist: ${dirPath}`);
|
|
42
|
+
}
|
|
43
|
+
// Check if this directory itself has a route
|
|
44
|
+
if (await hasRouteFile(dirPath)) {
|
|
45
|
+
node.$route = true;
|
|
46
|
+
}
|
|
47
|
+
// Read directory contents
|
|
48
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
// Skip non-directories and special directories
|
|
51
|
+
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const entryPath = join(dirPath, entry.name);
|
|
55
|
+
const paramName = extractParamName(entry.name);
|
|
56
|
+
if (paramName) {
|
|
57
|
+
// Dynamic segment [paramName]
|
|
58
|
+
const formattedName = formatParamName(paramName);
|
|
59
|
+
const childNode = await scanDirectory(entryPath, `${basePath}/${entry.name}`);
|
|
60
|
+
childNode.$param = paramName;
|
|
61
|
+
node[formattedName] = childNode;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Static segment
|
|
65
|
+
const childNode = await scanDirectory(entryPath, `${basePath}/${entry.name}`);
|
|
66
|
+
node[entry.name] = childNode;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return node;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Scan Next.js app directory and generate route structure
|
|
73
|
+
*/
|
|
74
|
+
export const generateRouteStructure = async (inputDir) => {
|
|
75
|
+
const resolvedPath = resolve(inputDir);
|
|
76
|
+
return scanDirectory(resolvedPath);
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for route builder
|
|
3
|
+
*/
|
|
4
|
+
export type HasChildren<T> = keyof Omit<T, "$param" | "$route"> extends never ? false : true;
|
|
5
|
+
export type GetParamType<P extends string, TMap = {}> = P extends keyof TMap ? TMap[P] : string;
|
|
6
|
+
export type RouteBuilder<T, TMap = {}> = T extends {
|
|
7
|
+
$param: infer P extends string;
|
|
8
|
+
} ? HasChildren<T> extends true ? (param: GetParamType<P, TMap>) => RouteBuilderObject<Omit<T, "$param">, TMap> & (T extends {
|
|
9
|
+
$route: true;
|
|
10
|
+
} ? {
|
|
11
|
+
$: () => string;
|
|
12
|
+
} : {}) : T extends {
|
|
13
|
+
$route: true;
|
|
14
|
+
} ? (param: GetParamType<P, TMap>) => string : (param: GetParamType<P, TMap>) => RouteBuilderObject<Omit<T, "$param">, TMap> : T extends {
|
|
15
|
+
$route: true;
|
|
16
|
+
} ? () => string : T extends object ? RouteBuilderObject<T, TMap> : never;
|
|
17
|
+
export type RouteBuilderObject<T, TMap = {}> = {
|
|
18
|
+
[K in keyof T as K extends "$param" | "$route" ? never : K]: RouteBuilder<T[K], TMap>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Route structure node
|
|
22
|
+
*/
|
|
23
|
+
export interface RouteNode {
|
|
24
|
+
$param?: string;
|
|
25
|
+
$route?: boolean;
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configuration options for route generation
|
|
30
|
+
*/
|
|
31
|
+
export interface RouteConfig {
|
|
32
|
+
/** Input directory to scan (e.g., "./app/api") */
|
|
33
|
+
input: string;
|
|
34
|
+
/** Output file path for generated routes */
|
|
35
|
+
output: string;
|
|
36
|
+
/** Watch for changes and regenerate */
|
|
37
|
+
watch?: boolean;
|
|
38
|
+
/** Base prefix for all routes (e.g., "/api") */
|
|
39
|
+
basePrefix?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Parameter type mappings
|
|
42
|
+
* Allows for defining custom types for route parameters,
|
|
43
|
+
* which will be used in the generated route builder for
|
|
44
|
+
* type safety.
|
|
45
|
+
*
|
|
46
|
+
* Any parameter not defined here will default to string type.
|
|
47
|
+
*/
|
|
48
|
+
paramTypes?: Record<string, string>;
|
|
49
|
+
/** Additional imports to include in generated file */
|
|
50
|
+
imports?: string[];
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;AAG7F,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,GAAG,EAAE,IAAI,CAAC,SAAS,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;AAGhG,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,IAAI,CAAC,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GACjF,WAAW,CAAC,CAAC,CAAC,SAAS,IAAI,GACzB,CACE,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,KACzB,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,CAAC,EAAE,MAAM,MAAM,CAAA;CAAE,GAAG,EAAE,CAAC,GAC1G,CAAC,SAAS;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GACxB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,MAAM,GACxC,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,GACjF,CAAC,SAAS;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GACxB,MAAM,MAAM,GACZ,CAAC,SAAS,MAAM,GACd,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,GAC3B,KAAK,CAAC;AAEd,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,IAAI,GAAG,EAAE,IAAI;KAC5C,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;CACtF,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher for live route regeneration
|
|
3
|
+
*/
|
|
4
|
+
import { RouteConfig } from "./types";
|
|
5
|
+
export type RegenerateCallback = () => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Start watching the input directory for changes
|
|
8
|
+
*/
|
|
9
|
+
export declare const startWatcher: (config: RouteConfig, onRegenerate: RegenerateCallback) => void;
|
|
10
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAErD;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,WAAW,EAAE,cAAc,kBAAkB,KAAG,IA6DpF,CAAC"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher for live route regeneration
|
|
3
|
+
*/
|
|
4
|
+
import { watch } from "chokidar";
|
|
5
|
+
import { basename, relative, resolve } from "path";
|
|
6
|
+
import { ROUTE_FILE_NAME, WATCH_DEBOUNCE_MS } from "./constants";
|
|
7
|
+
/**
|
|
8
|
+
* Start watching the input directory for changes
|
|
9
|
+
*/
|
|
10
|
+
export const startWatcher = (config, onRegenerate) => {
|
|
11
|
+
const inputPath = resolve(config.input);
|
|
12
|
+
console.log(`š Watching for changes in: ${inputPath}`);
|
|
13
|
+
const watcher = watch(inputPath, {
|
|
14
|
+
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
|
15
|
+
persistent: true,
|
|
16
|
+
ignoreInitial: true,
|
|
17
|
+
});
|
|
18
|
+
let regenerateTimer = null;
|
|
19
|
+
const scheduleRegenerate = () => {
|
|
20
|
+
// Debounce rapid changes
|
|
21
|
+
if (regenerateTimer) {
|
|
22
|
+
clearTimeout(regenerateTimer);
|
|
23
|
+
}
|
|
24
|
+
regenerateTimer = setTimeout(async () => {
|
|
25
|
+
console.log("\nš Changes detected, regenerating routes...");
|
|
26
|
+
try {
|
|
27
|
+
await onRegenerate();
|
|
28
|
+
console.log("ā
Routes regenerated successfully");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("ā Failed to regenerate routes:", error);
|
|
32
|
+
}
|
|
33
|
+
}, WATCH_DEBOUNCE_MS);
|
|
34
|
+
};
|
|
35
|
+
watcher
|
|
36
|
+
.on("add", (filePath) => {
|
|
37
|
+
if (basename(filePath).startsWith(`${ROUTE_FILE_NAME}.`)) {
|
|
38
|
+
console.log(`š New route file: ${relative(inputPath, filePath)}`);
|
|
39
|
+
scheduleRegenerate();
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
.on("unlink", (filePath) => {
|
|
43
|
+
if (basename(filePath).startsWith(`${ROUTE_FILE_NAME}.`)) {
|
|
44
|
+
console.log(`šļø Removed route file: ${relative(inputPath, filePath)}`);
|
|
45
|
+
scheduleRegenerate();
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.on("addDir", (dirPath) => {
|
|
49
|
+
console.log(`š New directory: ${relative(inputPath, dirPath)}`);
|
|
50
|
+
scheduleRegenerate();
|
|
51
|
+
})
|
|
52
|
+
.on("unlinkDir", (dirPath) => {
|
|
53
|
+
console.log(`šļø Removed directory: ${relative(inputPath, dirPath)}`);
|
|
54
|
+
scheduleRegenerate();
|
|
55
|
+
})
|
|
56
|
+
.on("error", (error) => {
|
|
57
|
+
console.error("ā Watcher error:", error);
|
|
58
|
+
});
|
|
59
|
+
// Handle graceful shutdown
|
|
60
|
+
process.on("SIGINT", () => {
|
|
61
|
+
console.log("\nš Stopping watcher...");
|
|
62
|
+
watcher.close();
|
|
63
|
+
process.exit(0);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-typed-paths",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A realtime typesafe Next.Js route generation tool",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nextjs",
|
|
7
|
+
"routes",
|
|
8
|
+
"typescript",
|
|
9
|
+
"type-safe",
|
|
10
|
+
"app-router"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/hugs7/next-typed-paths#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/hugs7/next-typed-paths/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/hugs7/next-typed-paths.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Hugo Burton",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"bin": {
|
|
26
|
+
"next-typed-paths": "./dist/cli.js"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
42
|
+
"test": "vitest"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"chokidar": "^5.0.0",
|
|
46
|
+
"commander": "^14.0.3",
|
|
47
|
+
"cosmiconfig": "^9.0.0",
|
|
48
|
+
"zod": "^4.3.6"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^25.2.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"vitest": "^4.0.18"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"typescript": ">=5.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|