@hono-filebased-route/core 0.2.1 → 0.2.3
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 +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +456 -10
- package/package.json +47 -47
- package/scripts/generate-routes.ts +14 -13
- package/tsconfig.json +18 -17
- package/types/index.ts +6 -0
- package/utils/load-routes-utils.ts +34 -0
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hono-filebased-route/core",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "A core utility for file-based routing in Hono applications.",
|
|
6
|
-
"author": "HM Suiji <hmsuiji@gmail.com>",
|
|
7
|
-
"keywords": [
|
|
8
|
-
"hono",
|
|
9
|
-
"router",
|
|
10
|
-
"file-based",
|
|
11
|
-
"routing",
|
|
12
|
-
"backend",
|
|
13
|
-
"framework",
|
|
14
|
-
"typescript",
|
|
15
|
-
"file-based-route"
|
|
16
|
-
],
|
|
17
|
-
"license": "MIT",
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "https://github.com/HM-Suiji/hono-filebased-route.git"
|
|
21
|
-
},
|
|
22
|
-
"main": "dist/index.js",
|
|
23
|
-
"types": "dist/index.d.ts",
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "bun run build.ts",
|
|
26
|
-
"clean": "rm -rf dist"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"fast-glob": "^3.3.3",
|
|
30
|
-
"fs": "0.0.1-security",
|
|
31
|
-
"hono": "^4.9.2",
|
|
32
|
-
"path": "^0.12.7"
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/bun": "^1.2.20",
|
|
36
|
-
"@types/node": "^24.3.0",
|
|
37
|
-
"bun-plugin-dts": "^0.3.0",
|
|
38
|
-
"typescript": "^5.0.0"
|
|
39
|
-
},
|
|
40
|
-
"peerDependenciesMeta": {
|
|
41
|
-
"hono": {
|
|
42
|
-
"optional": false
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public"
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@hono-filebased-route/core",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A core utility for file-based routing in Hono applications.",
|
|
6
|
+
"author": "HM Suiji <hmsuiji@gmail.com>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"hono",
|
|
9
|
+
"router",
|
|
10
|
+
"file-based",
|
|
11
|
+
"routing",
|
|
12
|
+
"backend",
|
|
13
|
+
"framework",
|
|
14
|
+
"typescript",
|
|
15
|
+
"file-based-route"
|
|
16
|
+
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/HM-Suiji/hono-filebased-route.git"
|
|
21
|
+
},
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bun run build.ts",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"fast-glob": "^3.3.3",
|
|
30
|
+
"fs": "0.0.1-security",
|
|
31
|
+
"hono": "^4.9.2",
|
|
32
|
+
"path": "^0.12.7"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/bun": "^1.2.20",
|
|
36
|
+
"@types/node": "^24.3.0",
|
|
37
|
+
"bun-plugin-dts": "^0.3.0",
|
|
38
|
+
"typescript": "^5.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"hono": {
|
|
42
|
+
"optional": false
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
48
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { writeFile } from 'fs/promises'
|
|
2
|
-
import { getFiles, getRoutePath } from '../utils/load-routes-utils'
|
|
2
|
+
import { getExportedHttpMethods, getFiles, getRoutePath } from '../utils/load-routes-utils'
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { pathToFileURL } from 'url'
|
|
5
|
+
import { METHODS } from '../types'
|
|
5
6
|
|
|
6
7
|
const ROUTES_DIR = './src/routes'
|
|
7
8
|
const OUTPUT_FILE = './src/generated-routes.ts'
|
|
8
9
|
|
|
9
10
|
export async function generateRoutesFile(
|
|
10
11
|
dir: string = ROUTES_DIR,
|
|
11
|
-
output: string = OUTPUT_FILE
|
|
12
|
+
output: string = OUTPUT_FILE,
|
|
13
|
+
write: boolean = true
|
|
12
14
|
) {
|
|
13
15
|
console.log('Generating routes file...', dir, output)
|
|
14
16
|
const absoluteRoutesDir = path.resolve(dir)
|
|
@@ -16,7 +18,6 @@ export async function generateRoutesFile(
|
|
|
16
18
|
|
|
17
19
|
const importStatements: string[] = []
|
|
18
20
|
const routeDefinitions: string[] = []
|
|
19
|
-
const methods = ['GET', 'POST']
|
|
20
21
|
|
|
21
22
|
importStatements.push(`import { Hono } from 'hono';`)
|
|
22
23
|
|
|
@@ -36,10 +37,10 @@ export async function generateRoutesFile(
|
|
|
36
37
|
const tempHonoVar = `honoApp${moduleName}`
|
|
37
38
|
routeDefinitions.push(` const ${tempHonoVar} = new Hono();`)
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
40
|
+
const exportedMethods = getExportedHttpMethods(file);
|
|
41
|
+
|
|
42
|
+
for (const method of METHODS) {
|
|
43
|
+
if (exportedMethods[method]) {
|
|
43
44
|
if (routePath.endsWith('/*')) {
|
|
44
45
|
const len = routePath.replace(/\/\*$/g, '').length + 1
|
|
45
46
|
routeDefinitions.push(
|
|
@@ -52,11 +53,7 @@ export async function generateRoutesFile(
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
routeDefinitions.push(` mainApp.route('${routePath}', ${tempHonoVar});`)
|
|
57
|
-
} else {
|
|
58
|
-
routeDefinitions.push(` mainApp.route('${routePath}', ${tempHonoVar});`)
|
|
59
|
-
}
|
|
56
|
+
routeDefinitions.push(` mainApp.route('${routePath}', ${tempHonoVar});`)
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
const fileContent = `
|
|
@@ -73,7 +70,11 @@ ${routeDefinitions.join('\n')}
|
|
|
73
70
|
}
|
|
74
71
|
`
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
if (write) {
|
|
74
|
+
await writeFile(output, fileContent.trimStart())
|
|
75
|
+
}
|
|
77
76
|
|
|
78
77
|
console.log(`Generated routes file: ${output} with ${files.length} routes.`)
|
|
78
|
+
|
|
79
|
+
return fileContent.trimStart()
|
|
79
80
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": ".",
|
|
5
|
-
"outDir": "./dist",
|
|
6
|
-
"composite": true
|
|
7
|
-
},
|
|
8
|
-
"include": [
|
|
9
|
-
"utils/**/*.ts",
|
|
10
|
-
"scripts/**/*.ts",
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"composite": true
|
|
7
|
+
},
|
|
8
|
+
"include": [
|
|
9
|
+
"utils/**/*.ts",
|
|
10
|
+
"scripts/**/*.ts",
|
|
11
|
+
"types/**/*.ts",
|
|
12
|
+
"index.ts"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist",
|
|
17
|
+
"**/*.test.ts"
|
|
18
|
+
]
|
|
18
19
|
}
|
package/types/index.ts
ADDED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fg from 'fast-glob'
|
|
3
|
+
import { readFileSync } from 'fs'
|
|
4
|
+
import { ExportedMethods, Method, METHODS } from '../types'
|
|
5
|
+
import { createSourceFile, ScriptTarget, isVariableStatement, isFunctionDeclaration, SyntaxKind, isIdentifier } from 'typescript'
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* 遍历指定目录并获取所有文件路径
|
|
@@ -39,3 +42,34 @@ export function getRoutePath(filePath: string, baseDir: string): string {
|
|
|
39
42
|
|
|
40
43
|
return `/${routeName}`
|
|
41
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 从文件中提取导出的 HTTP 方法
|
|
48
|
+
* @param filePath 文件的绝对路径
|
|
49
|
+
* @returns 导出的 HTTP 方法对象
|
|
50
|
+
*/
|
|
51
|
+
export function getExportedHttpMethods(filePath: string): ExportedMethods {
|
|
52
|
+
const fileContent = readFileSync(filePath, 'utf8');
|
|
53
|
+
const sourceFile = createSourceFile(
|
|
54
|
+
filePath,
|
|
55
|
+
fileContent,
|
|
56
|
+
ScriptTarget.ESNext,
|
|
57
|
+
true
|
|
58
|
+
);
|
|
59
|
+
const methods: ExportedMethods = {} as ExportedMethods
|
|
60
|
+
sourceFile.forEachChild(node => {
|
|
61
|
+
// 寻找 export const GET = ... 或 export function POST() { ... } 形式
|
|
62
|
+
if (isVariableStatement(node) && node.modifiers && node.modifiers.some(m => m.kind === SyntaxKind.ExportKeyword)) {
|
|
63
|
+
for (const declaration of node.declarationList.declarations) {
|
|
64
|
+
if (isIdentifier(declaration.name) && METHODS.includes(declaration.name.text as Method)) {
|
|
65
|
+
methods[declaration.name.text as Method] = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else if (isFunctionDeclaration(node) && node.modifiers && node.modifiers.some(m => m.kind === SyntaxKind.ExportKeyword)) {
|
|
69
|
+
if (node.name && METHODS.includes(node.name.text as Method)) {
|
|
70
|
+
methods[node.name.text as Method] = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return methods;
|
|
75
|
+
}
|