jax-hono 1.0.24 → 1.0.25
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 +32 -32
- package/bin/jax.js +66 -66
- package/bin/jax.ts +8 -8
- package/build/build.ts +19 -19
- package/build/generate-config.ts +122 -122
- package/build/generate-registry.ts +942 -942
- package/build/generated-path.ts +20 -20
- package/config.ts +94 -94
- package/core/api-controller.ts +342 -342
- package/core/app.ts +226 -226
- package/core/controller.ts +109 -109
- package/core/cron.ts +162 -162
- package/core/generated.ts +73 -73
- package/core/hono.ts +65 -65
- package/core/service.ts +44 -44
- package/docs/agent.md +172 -172
- package/docs/jax.md +332 -332
- package/helpers/config.ts +23 -23
- package/helpers/crud.ts +27 -27
- package/helpers/index.ts +4 -4
- package/helpers/route.ts +7 -7
- package/index.ts +121 -121
- package/middleware/api-response.ts +44 -44
- package/middleware/jwt.ts +90 -90
- package/middleware/logger.ts +3 -3
- package/middleware/request-log.ts +114 -114
- package/package.json +129 -129
- package/plugins/config.ts +8 -8
- package/plugins/define.ts +5 -5
- package/plugins/mongoose/filter.ts +132 -132
- package/plugins/mongoose/index.ts +58 -58
- package/plugins/mongoose/model.ts +322 -322
- package/plugins/mongoose/schema.ts +30 -30
- package/plugins/session/index.ts +86 -86
- package/setup.ts +105 -105
- package/types/config.ts +9 -9
- package/types/context.ts +10 -10
- package/types/index.ts +2 -2
- package/utils/array.ts +7 -7
- package/utils/async.ts +47 -47
- package/utils/crypto.ts +6 -6
- package/utils/http.ts +106 -106
- package/utils/index.ts +8 -8
- package/utils/regexp.ts +14 -14
- package/utils/transform.ts +3 -3
package/README.md
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
# Jax-hono
|
|
2
|
-
|
|
3
|
-
Jax 是本项目的轻量框架层。完整说明见 [docs/jax.md](docs/jax.md)。
|
|
4
|
-
|
|
5
|
-
给 Codex、Claude 等代码代理看的维护说明见 [docs/agent.md](docs/agent.md)。
|
|
6
|
-
|
|
7
|
-
维护 Jax 内部代码时按目录职责放置文件:
|
|
8
|
-
|
|
9
|
-
```txt
|
|
10
|
-
core/ Hono runtime、生命周期、Controller/Service
|
|
11
|
-
middleware/ 框架中间件
|
|
12
|
-
plugins/ 插件定义、内置插件配置、内置插件实现
|
|
13
|
-
build/ 代码生成/扫描注册
|
|
14
|
-
helpers/ 框架辅助函数
|
|
15
|
-
utils/ 纯工具函数
|
|
16
|
-
errors/ 统一错误类型
|
|
17
|
-
types/ 框架公共类型
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
`.jax-hono` 目录只存在于调用项目中,不放在 `jax-hono` 包内,也不会从包内链接到某个项目。
|
|
21
|
-
|
|
22
|
-
需要 `bun --compile` 的项目可以用 setup 包裹 Bun 构建命令:
|
|
23
|
-
|
|
24
|
-
```powershell
|
|
25
|
-
bun node_modules/jax-hono/setup.ts src/index.ts -- bun build src/index.ts --compile --production --target=bun-windows-x64 --outfile dist/app.exe
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
setup 会在执行命令期间临时把入口里对 `jax-hono` 的导入切到项目生成入口,命令结束后恢复源码。业务入口仍然写:
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
import { config, createApp } from 'jax-hono';
|
|
32
|
-
```
|
|
1
|
+
# Jax-hono
|
|
2
|
+
|
|
3
|
+
Jax 是本项目的轻量框架层。完整说明见 [docs/jax.md](docs/jax.md)。
|
|
4
|
+
|
|
5
|
+
给 Codex、Claude 等代码代理看的维护说明见 [docs/agent.md](docs/agent.md)。
|
|
6
|
+
|
|
7
|
+
维护 Jax 内部代码时按目录职责放置文件:
|
|
8
|
+
|
|
9
|
+
```txt
|
|
10
|
+
core/ Hono runtime、生命周期、Controller/Service
|
|
11
|
+
middleware/ 框架中间件
|
|
12
|
+
plugins/ 插件定义、内置插件配置、内置插件实现
|
|
13
|
+
build/ 代码生成/扫描注册
|
|
14
|
+
helpers/ 框架辅助函数
|
|
15
|
+
utils/ 纯工具函数
|
|
16
|
+
errors/ 统一错误类型
|
|
17
|
+
types/ 框架公共类型
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`.jax-hono` 目录只存在于调用项目中,不放在 `jax-hono` 包内,也不会从包内链接到某个项目。
|
|
21
|
+
|
|
22
|
+
需要 `bun --compile` 的项目可以用 setup 包裹 Bun 构建命令:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
bun node_modules/jax-hono/setup.ts src/index.ts -- bun build src/index.ts --compile --production --target=bun-windows-x64 --outfile dist/app.exe
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
setup 会在执行命令期间临时把入口里对 `jax-hono` 的导入切到项目生成入口,命令结束后恢复源码。业务入口仍然写:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { config, createApp } from 'jax-hono';
|
|
32
|
+
```
|
package/bin/jax.js
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
const cliPath = path.join(__dirname, "jax.ts");
|
|
11
|
-
|
|
12
|
-
function getBunCommand() {
|
|
13
|
-
const candidates = [];
|
|
14
|
-
|
|
15
|
-
if (process.env.BUN_INSTALL) {
|
|
16
|
-
candidates.push(
|
|
17
|
-
path.join(
|
|
18
|
-
process.env.BUN_INSTALL,
|
|
19
|
-
"bin",
|
|
20
|
-
process.platform === "win32" ? "bun.exe" : "bun",
|
|
21
|
-
),
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (process.platform === "win32") {
|
|
26
|
-
const nodeDir = path.dirname(process.execPath);
|
|
27
|
-
candidates.push(
|
|
28
|
-
path.join(nodeDir, "bun.exe"),
|
|
29
|
-
path.join(nodeDir, "node_modules", "bun", "bin", "bun.exe"),
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const candidate of candidates) {
|
|
34
|
-
if (existsSync(candidate)) {
|
|
35
|
-
return candidate;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return "bun";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const child = spawn(getBunCommand(), [cliPath, ...process.argv.slice(2)], {
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
windowsHide: true,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
child.on("error", (error) => {
|
|
48
|
-
if (error.code === "ENOENT") {
|
|
49
|
-
console.error(
|
|
50
|
-
"未找到 Bun。请从 https://bun.sh 安装 Bun,或确认 bun 已加入 PATH。",
|
|
51
|
-
);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.error(error.message);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
child.on("exit", (code, signal) => {
|
|
60
|
-
if (signal) {
|
|
61
|
-
process.kill(process.pid, signal);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
process.exit(code ?? 0);
|
|
66
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const cliPath = path.join(__dirname, "jax.ts");
|
|
11
|
+
|
|
12
|
+
function getBunCommand() {
|
|
13
|
+
const candidates = [];
|
|
14
|
+
|
|
15
|
+
if (process.env.BUN_INSTALL) {
|
|
16
|
+
candidates.push(
|
|
17
|
+
path.join(
|
|
18
|
+
process.env.BUN_INSTALL,
|
|
19
|
+
"bin",
|
|
20
|
+
process.platform === "win32" ? "bun.exe" : "bun",
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (process.platform === "win32") {
|
|
26
|
+
const nodeDir = path.dirname(process.execPath);
|
|
27
|
+
candidates.push(
|
|
28
|
+
path.join(nodeDir, "bun.exe"),
|
|
29
|
+
path.join(nodeDir, "node_modules", "bun", "bin", "bun.exe"),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
if (existsSync(candidate)) {
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return "bun";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const child = spawn(getBunCommand(), [cliPath, ...process.argv.slice(2)], {
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
windowsHide: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
child.on("error", (error) => {
|
|
48
|
+
if (error.code === "ENOENT") {
|
|
49
|
+
console.error(
|
|
50
|
+
"未找到 Bun。请从 https://bun.sh 安装 Bun,或确认 bun 已加入 PATH。",
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.error(error.message);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
child.on("exit", (code, signal) => {
|
|
60
|
+
if (signal) {
|
|
61
|
+
process.kill(process.pid, signal);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
process.exit(code ?? 0);
|
|
66
|
+
});
|
package/bin/jax.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
3
|
import { cac } from "cac";
|
|
4
4
|
import { $ } from "bun";
|
|
5
5
|
import path from "node:path";
|
|
@@ -41,12 +41,12 @@ cli
|
|
|
41
41
|
const shouldUseNodeModules =
|
|
42
42
|
existsSync(path.join(cwd, "package.json")) ||
|
|
43
43
|
existsSync(path.join(cwd, "node_modules"));
|
|
44
|
-
const targetRoot = shouldUseNodeModules
|
|
45
|
-
? path.join(cwd, "node_modules")
|
|
46
|
-
: cwd;
|
|
47
|
-
const targetName = shouldUseNodeModules ? "jax-hono" : "jax";
|
|
48
|
-
const targetDir = path.resolve(targetRoot, targetName);
|
|
49
|
-
|
|
44
|
+
const targetRoot = shouldUseNodeModules
|
|
45
|
+
? path.join(cwd, "node_modules")
|
|
46
|
+
: cwd;
|
|
47
|
+
const targetName = shouldUseNodeModules ? "jax-hono" : "jax";
|
|
48
|
+
const targetDir = path.resolve(targetRoot, targetName);
|
|
49
|
+
|
|
50
50
|
if (shouldUseNodeModules) {
|
|
51
51
|
await mkdir(targetRoot, { recursive: true });
|
|
52
52
|
}
|
package/build/build.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { setupJax } from '../setup'
|
|
2
|
-
|
|
3
|
-
setupJax()
|
|
4
|
-
|
|
5
|
-
const result = await Bun.build({
|
|
6
|
-
entrypoints: ['src/index.ts'],
|
|
7
|
-
outdir: 'dist',
|
|
8
|
-
target: 'bun',
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
if (!result.success) {
|
|
12
|
-
for (const log of result.logs) {
|
|
13
|
-
console.error(log)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
process.exit(1)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
console.log('Build completed.')
|
|
1
|
+
import { setupJax } from '../setup'
|
|
2
|
+
|
|
3
|
+
setupJax()
|
|
4
|
+
|
|
5
|
+
const result = await Bun.build({
|
|
6
|
+
entrypoints: ['src/index.ts'],
|
|
7
|
+
outdir: 'dist',
|
|
8
|
+
target: 'bun',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
if (!result.success) {
|
|
12
|
+
for (const log of result.logs) {
|
|
13
|
+
console.error(log)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log('Build completed.')
|
package/build/generate-config.ts
CHANGED
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import { cwd } from 'node:process'
|
|
3
|
-
import { join, parse, relative } from 'node:path'
|
|
4
|
-
import { ensureGeneratedDir, generatedDir, projectDir } from './generated-path'
|
|
5
|
-
|
|
6
|
-
const srcDir = join(cwd(), 'src')
|
|
7
|
-
const configDir = join(srcDir, 'config')
|
|
8
|
-
const typesFile = join(srcDir, 'types/index.ts')
|
|
9
|
-
const outputFile = join(generatedDir, 'config.ts')
|
|
10
|
-
const envStages = new Set([
|
|
11
|
-
'default',
|
|
12
|
-
'local',
|
|
13
|
-
'dev',
|
|
14
|
-
'development',
|
|
15
|
-
'test',
|
|
16
|
-
'staging',
|
|
17
|
-
'pre',
|
|
18
|
-
'uat',
|
|
19
|
-
'prod',
|
|
20
|
-
'production',
|
|
21
|
-
])
|
|
22
|
-
|
|
23
|
-
function toImportName(fileName: string, index: number) {
|
|
24
|
-
return `config${index}_${parse(fileName).name.replace(/[^a-zA-Z0-9_$]/g, '_')}`
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getStage(fileName: string) {
|
|
28
|
-
const stage = parse(fileName).name.split('.').at(-1)
|
|
29
|
-
|
|
30
|
-
if (stage && envStages.has(stage)) {
|
|
31
|
-
return stage
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return 'base'
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getImportPath(fileName: string) {
|
|
38
|
-
const { dir, name } = parse(fileName)
|
|
39
|
-
const path = join(configDir, dir, name).replaceAll('\\', '/')
|
|
40
|
-
|
|
41
|
-
return path
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getConfigFiles() {
|
|
45
|
-
if (!existsSync(configDir)) {
|
|
46
|
-
return []
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return readdirSync(configDir)
|
|
50
|
-
.filter((fileName) => /^config\.[^.]+\.ts$/.test(fileName))
|
|
51
|
-
.sort((left, right) => {
|
|
52
|
-
const stageWeight: Record<string, number> = {
|
|
53
|
-
base: 0,
|
|
54
|
-
default: 1,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const diff = (stageWeight[getStage(left)] ?? 2) - (stageWeight[getStage(right)] ?? 2)
|
|
58
|
-
|
|
59
|
-
if (diff !== 0) {
|
|
60
|
-
return diff
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return left.localeCompare(right)
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function hasProjectConfigType() {
|
|
68
|
-
if (!existsSync(typesFile)) {
|
|
69
|
-
return false
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const source = readFileSync(typesFile, 'utf8')
|
|
73
|
-
|
|
74
|
-
return /\bexport\s+(?:type|interface)\s+ProjectConfig\b/.test(source)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function generateConfig() {
|
|
78
|
-
const files = getConfigFiles()
|
|
79
|
-
const defaultConfigIndex = files.findIndex((fileName) => getStage(fileName) === 'default')
|
|
80
|
-
const imports = files.map((fileName, index) => {
|
|
81
|
-
return `import ${toImportName(fileName, index)} from '${getImportPath(fileName)}'`
|
|
82
|
-
})
|
|
83
|
-
const modules = files.map((fileName, index) => {
|
|
84
|
-
return ` { name: '${fileName}', stage: '${getStage(fileName)}', config: ${toImportName(fileName, index)} },`
|
|
85
|
-
})
|
|
86
|
-
const hasProjectTypes = hasProjectConfigType()
|
|
87
|
-
const typeImports = hasProjectTypes ? [`import type { ProjectConfig } from '${join(srcDir, 'types').replaceAll('\\', '/')}'`] : []
|
|
88
|
-
const projectConfigType = hasProjectTypes
|
|
89
|
-
? 'export type { ProjectConfig }'
|
|
90
|
-
: defaultConfigIndex === -1
|
|
91
|
-
? 'export type ProjectConfig = Record<string, unknown>'
|
|
92
|
-
: `export type ProjectConfig = typeof ${toImportName(files[defaultConfigIndex], defaultConfigIndex)}`
|
|
93
|
-
const content = `${[...typeImports, ...imports].join('\n')}
|
|
94
|
-
|
|
95
|
-
export const configModules = [
|
|
96
|
-
${modules.join('\n')}
|
|
97
|
-
] as const
|
|
98
|
-
|
|
99
|
-
${projectConfigType}
|
|
100
|
-
|
|
101
|
-
declare module 'jax-hono/core/generated' {
|
|
102
|
-
interface JaxGenerated {
|
|
103
|
-
config: ProjectConfig
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export default configModules
|
|
108
|
-
`
|
|
109
|
-
|
|
110
|
-
ensureGeneratedDir()
|
|
111
|
-
|
|
112
|
-
if (existsSync(outputFile) && readFileSync(outputFile, 'utf8') === content) {
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
writeFileSync(outputFile, content)
|
|
117
|
-
console.log(`Generated ${relative(projectDir, outputFile)} with ${files.length} config file(s).`)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (import.meta.main) {
|
|
121
|
-
generateConfig()
|
|
122
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { cwd } from 'node:process'
|
|
3
|
+
import { join, parse, relative } from 'node:path'
|
|
4
|
+
import { ensureGeneratedDir, generatedDir, projectDir } from './generated-path'
|
|
5
|
+
|
|
6
|
+
const srcDir = join(cwd(), 'src')
|
|
7
|
+
const configDir = join(srcDir, 'config')
|
|
8
|
+
const typesFile = join(srcDir, 'types/index.ts')
|
|
9
|
+
const outputFile = join(generatedDir, 'config.ts')
|
|
10
|
+
const envStages = new Set([
|
|
11
|
+
'default',
|
|
12
|
+
'local',
|
|
13
|
+
'dev',
|
|
14
|
+
'development',
|
|
15
|
+
'test',
|
|
16
|
+
'staging',
|
|
17
|
+
'pre',
|
|
18
|
+
'uat',
|
|
19
|
+
'prod',
|
|
20
|
+
'production',
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
function toImportName(fileName: string, index: number) {
|
|
24
|
+
return `config${index}_${parse(fileName).name.replace(/[^a-zA-Z0-9_$]/g, '_')}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getStage(fileName: string) {
|
|
28
|
+
const stage = parse(fileName).name.split('.').at(-1)
|
|
29
|
+
|
|
30
|
+
if (stage && envStages.has(stage)) {
|
|
31
|
+
return stage
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return 'base'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getImportPath(fileName: string) {
|
|
38
|
+
const { dir, name } = parse(fileName)
|
|
39
|
+
const path = join(configDir, dir, name).replaceAll('\\', '/')
|
|
40
|
+
|
|
41
|
+
return path
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getConfigFiles() {
|
|
45
|
+
if (!existsSync(configDir)) {
|
|
46
|
+
return []
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return readdirSync(configDir)
|
|
50
|
+
.filter((fileName) => /^config\.[^.]+\.ts$/.test(fileName))
|
|
51
|
+
.sort((left, right) => {
|
|
52
|
+
const stageWeight: Record<string, number> = {
|
|
53
|
+
base: 0,
|
|
54
|
+
default: 1,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const diff = (stageWeight[getStage(left)] ?? 2) - (stageWeight[getStage(right)] ?? 2)
|
|
58
|
+
|
|
59
|
+
if (diff !== 0) {
|
|
60
|
+
return diff
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return left.localeCompare(right)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasProjectConfigType() {
|
|
68
|
+
if (!existsSync(typesFile)) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const source = readFileSync(typesFile, 'utf8')
|
|
73
|
+
|
|
74
|
+
return /\bexport\s+(?:type|interface)\s+ProjectConfig\b/.test(source)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function generateConfig() {
|
|
78
|
+
const files = getConfigFiles()
|
|
79
|
+
const defaultConfigIndex = files.findIndex((fileName) => getStage(fileName) === 'default')
|
|
80
|
+
const imports = files.map((fileName, index) => {
|
|
81
|
+
return `import ${toImportName(fileName, index)} from '${getImportPath(fileName)}'`
|
|
82
|
+
})
|
|
83
|
+
const modules = files.map((fileName, index) => {
|
|
84
|
+
return ` { name: '${fileName}', stage: '${getStage(fileName)}', config: ${toImportName(fileName, index)} },`
|
|
85
|
+
})
|
|
86
|
+
const hasProjectTypes = hasProjectConfigType()
|
|
87
|
+
const typeImports = hasProjectTypes ? [`import type { ProjectConfig } from '${join(srcDir, 'types').replaceAll('\\', '/')}'`] : []
|
|
88
|
+
const projectConfigType = hasProjectTypes
|
|
89
|
+
? 'export type { ProjectConfig }'
|
|
90
|
+
: defaultConfigIndex === -1
|
|
91
|
+
? 'export type ProjectConfig = Record<string, unknown>'
|
|
92
|
+
: `export type ProjectConfig = typeof ${toImportName(files[defaultConfigIndex], defaultConfigIndex)}`
|
|
93
|
+
const content = `${[...typeImports, ...imports].join('\n')}
|
|
94
|
+
|
|
95
|
+
export const configModules = [
|
|
96
|
+
${modules.join('\n')}
|
|
97
|
+
] as const
|
|
98
|
+
|
|
99
|
+
${projectConfigType}
|
|
100
|
+
|
|
101
|
+
declare module 'jax-hono/core/generated' {
|
|
102
|
+
interface JaxGenerated {
|
|
103
|
+
config: ProjectConfig
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export default configModules
|
|
108
|
+
`
|
|
109
|
+
|
|
110
|
+
ensureGeneratedDir()
|
|
111
|
+
|
|
112
|
+
if (existsSync(outputFile) && readFileSync(outputFile, 'utf8') === content) {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
writeFileSync(outputFile, content)
|
|
117
|
+
console.log(`Generated ${relative(projectDir, outputFile)} with ${files.length} config file(s).`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (import.meta.main) {
|
|
121
|
+
generateConfig()
|
|
122
|
+
}
|