jax-hono 1.0.14 → 1.0.15

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # Jax-hono
2
2
 
3
- Jax 是本项目的轻量框架层。完整说明见 [docs/jax.md](docs/jax.md)。
3
+ Jax 是本项目的轻量框架层。完整说明见 [docs/jax.md](docs/jax.md)。
4
+
5
+ 给 Codex、Claude 等代码代理看的维护说明见 [docs/agent.md](docs/agent.md)。
4
6
 
5
7
  维护 Jax 内部代码时按目录职责放置文件:
6
8
 
@@ -30,7 +30,7 @@ const controllerGroups: ControllerGroup[] = [
30
30
  { name: 'backend', dirs: ['backend', 'controllers/backend', 'controller/backend', 'modules'], suffix: '.backend' },
31
31
  { name: 'open', dirs: ['open', 'controllers/open', 'controller/open', 'modules'], suffix: '.open' },
32
32
  { name: 'client', dirs: ['client', 'controllers/client', 'controller/client', 'modules'], suffix: '.client' },
33
- { name: 'controller', dirs: ['controllers', 'controller'] },
33
+ { name: 'controller', dirs: ['controllers', 'controller', 'modules'], suffix: '.controller' },
34
34
  ]
35
35
 
36
36
  const scopedControllerGroups = new Set(
@@ -126,15 +126,25 @@ function stripKnownSuffix(modulePath: string, suffixes: string[]) {
126
126
  return modulePath
127
127
  }
128
128
 
129
- function normalizeModulePath(modulePath: string) {
130
- const parts = modulePath.split('/')
131
- const last = parts.at(-1)
132
- const parent = parts.at(-2)
133
-
134
- return last && parent && last === parent
135
- ? parts.slice(0, -1).join('/')
136
- : modulePath
137
- }
129
+ function normalizeModulePath(modulePath: string) {
130
+ const parts = modulePath.split('/')
131
+ const last = parts.at(-1)
132
+ const parent = parts.at(-2)
133
+
134
+ if (!last || !parent) {
135
+ return modulePath
136
+ }
137
+
138
+ if (last === parent) {
139
+ return parts.slice(0, -1).join('/')
140
+ }
141
+
142
+ if (last.startsWith(`${parent}-`) || last.startsWith(`${parent}_`)) {
143
+ return [...parts.slice(0, -2), last].join('/')
144
+ }
145
+
146
+ return modulePath
147
+ }
138
148
 
139
149
  function toModulePath(baseDir: string, filePath: string, suffixes: string[] = []) {
140
150
  const modulePath = relative(join(srcDir, baseDir), filePath)
@@ -170,13 +180,13 @@ function isSuffixedModuleFile(filePath: string, suffix: string) {
170
180
  .endsWith(suffix)
171
181
  }
172
182
 
173
- function isModuleFeatureFile(filePath: string, suffix: string) {
174
- return !isFileInDir(filePath, 'modules') || isSuffixedModuleFile(filePath, suffix)
175
- }
176
-
177
- function hasDefaultExport(filePath: string) {
178
- return /\bexport\s+default\b/.test(readFileSync(filePath, 'utf8'))
179
- }
183
+ function isModuleFeatureFile(filePath: string, suffix: string) {
184
+ return !isFileInDir(filePath, 'modules') || isSuffixedModuleFile(filePath, suffix)
185
+ }
186
+
187
+ function hasDefaultExport(filePath: string) {
188
+ return /\bexport\s+default\b/.test(readFileSync(filePath, 'utf8'))
189
+ }
180
190
 
181
191
  function indentObject(content: string, spaces = 2) {
182
192
  if (!content) {
@@ -226,18 +236,18 @@ function generateControllersRegistry() {
226
236
  const groupLines: string[] = []
227
237
 
228
238
  for (const group of controllerGroups) {
229
- const entries = getFilesFromDirs(group.dirs)
230
- .filter((filePath) => basename(filePath) !== 'index.ts')
231
- .filter((filePath) => !group.suffix || isModuleFeatureFile(filePath, group.suffix))
232
- .filter(hasDefaultExport)
233
- .map((filePath, index) => {
234
- const baseDir = group.dirs.find((dirName) => {
235
- return isFileInDir(filePath, dirName)
236
- }) ?? group.dirs[0]
237
- const modulePath = toModulePath(baseDir, filePath, group.suffix ? [group.suffix] : [])
238
- const importName = toIdentifier([group.name, modulePath, String(index), 'Controller'])
239
-
240
- return { modulePath, importName, filePath }
239
+ const entries = getFilesFromDirs(group.dirs)
240
+ .filter((filePath) => basename(filePath) !== 'index.ts')
241
+ .filter((filePath) => !group.suffix || isModuleFeatureFile(filePath, group.suffix))
242
+ .filter(hasDefaultExport)
243
+ .map((filePath, index) => {
244
+ const baseDir = group.dirs.find((dirName) => {
245
+ return isFileInDir(filePath, dirName)
246
+ }) ?? group.dirs[0]
247
+ const modulePath = toModulePath(baseDir, filePath, group.suffix ? [group.suffix] : [])
248
+ const importName = toIdentifier([group.name, modulePath, String(index), 'Controller'])
249
+
250
+ return { modulePath, importName, filePath }
241
251
  })
242
252
  .filter((entry) => {
243
253
  if (group.name !== 'controller') {
@@ -274,8 +284,12 @@ function generateControllersRegistry() {
274
284
  4,
275
285
  )
276
286
 
277
- groupLines.push(` ${group.name}: {\n${objectBody}\n },`)
278
- }
287
+ if (group.name === 'controller') {
288
+ groupLines.push(objectBody)
289
+ } else {
290
+ groupLines.push(` ${group.name}: {\n${objectBody}\n },`)
291
+ }
292
+ }
279
293
 
280
294
  const helperImport = imports.length > 0
281
295
  ? `import { createController } from 'jax-hono/core/controller'\n`
package/docs/agent.md ADDED
@@ -0,0 +1,105 @@
1
+ # Agent 维护说明
2
+
3
+ 这份文档给 Codex、Claude 或其他代码代理阅读,用来说明维护 `jax-hono` 时的默认规则。
4
+
5
+ ## 本地 link 规则
6
+
7
+ `jax-hono` 是用户自己维护的框架,常通过 `jax link` 链接到业务工程的 `node_modules/jax-hono`。
8
+
9
+ 如果在业务工程中需要修改 `jax-hono` 行为,先检查依赖是否已经 link 到本地源码目录。不要把它当成一次性的第三方 `node_modules` 临时补丁。
10
+
11
+ 在 Windows PowerShell 中,可以在业务仓库根目录查找所有 `jax-hono` 依赖:
12
+
13
+ ```powershell
14
+ Get-ChildItem -Path . -Directory -Recurse -Filter jax-hono |
15
+ Where-Object { $_.FullName -match '\\node_modules\\jax-hono$' } |
16
+ Format-List LinkType,Target,FullName
17
+ ```
18
+
19
+ 如果 `LinkType` 是 `Junction` 或 `SymbolicLink`,直接修改 `Target` 指向的 `jax-hono` 源码目录。
20
+
21
+ 如果没有 link,先执行:
22
+
23
+ ```powershell
24
+ jax link
25
+ ```
26
+
27
+ 然后再次确认 link 状态,再修改框架源码。
28
+
29
+ ## 修改生成器后的验证
30
+
31
+ 修改 `build/generate-registry.ts`、`build/generate-config.ts` 等生成逻辑后,需要在使用方工程里重新生成。
32
+
33
+ 常见命令:
34
+
35
+ ```powershell
36
+ bun run setup
37
+ ```
38
+
39
+ 然后检查业务工程中的生成结果,例如:
40
+
41
+ ```text
42
+ generated/controllers.generated.ts
43
+ generated/models.generated.ts
44
+ generated/services.generated.ts
45
+ generated/middlewares.generated.ts
46
+ generated/plugins.generated.ts
47
+ ```
48
+
49
+ 如果业务仓库里有多个工程都依赖 `jax-hono`,分别查看各工程的 `package.json`,运行它们自己的 setup/build 命令。
50
+
51
+ ## Controller 注册约定
52
+
53
+ 生成器需要保持不同命名空间的边界清晰:
54
+
55
+ - `*.backend.ts` 注册到 `controllers.backend`
56
+ - `*.api.ts` 注册到 `controllers.api`
57
+ - `*.open.ts` 注册到 `controllers.open`
58
+ - `*.client.ts` 注册到 `controllers.client`
59
+ - `*.controller.ts` 注册到 `controllers` 根对象
60
+
61
+ `.controller.ts` 表示通用 Controller,供 backend/api/open/client 等不同路由复用,不应自动归入 `controllers.backend`。
62
+
63
+ 例如:
64
+
65
+ ```text
66
+ src/modules/oss/oss.controller.ts
67
+ src/modules/oss/oss-bucket.controller.ts
68
+ ```
69
+
70
+ 应生成:
71
+
72
+ ```ts
73
+ controllers.oss
74
+ controllers.ossBucket
75
+ ```
76
+
77
+ 如果需要后台专用 Controller,使用:
78
+
79
+ ```text
80
+ src/modules/foo/foo.backend.ts
81
+ ```
82
+
83
+ 应生成:
84
+
85
+ ```ts
86
+ controllers.backend.foo
87
+ ```
88
+
89
+ ## 模块路径命名
90
+
91
+ 模块内文件名如果以模块目录名开头,生成 key 时可以去掉重复前缀:
92
+
93
+ ```text
94
+ src/modules/oss/oss.controller.ts -> controllers.oss
95
+ src/modules/oss/oss-bucket.controller.ts -> controllers.ossBucket
96
+ ```
97
+
98
+ 不要生成成:
99
+
100
+ ```ts
101
+ controllers.oss.oss
102
+ controllers.oss.ossBucket
103
+ ```
104
+
105
+ 这样业务侧调用会更自然,也更适合在不同路由层复用。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jax-hono",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Lightweight framework layer on top of Hono, built for Bun",
5
5
  "type": "module",
6
6
  "main": "./index.ts",