@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/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "@hono-filebased-route/core",
3
- "version": "0.2.1",
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
- for await (const method of methods) {
40
- const fileUrl = pathToFileURL(file).href
41
- const module = await import(fileUrl)
42
- if (typeof module[method] === 'function') {
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
- if (routePath === '/') {
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
- await writeFile(output, fileContent.trimStart())
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
- "index.ts"
12
- ],
13
- "exclude": [
14
- "node_modules",
15
- "dist",
16
- "**/*.test.ts"
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
@@ -0,0 +1,6 @@
1
+ export const METHODS = ['GET', 'POST'] as const
2
+ export type Method = (typeof METHODS)[number]
3
+
4
+ export type ExportedMethods = {
5
+ [key in Method]: boolean;
6
+ }
@@ -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
+ }