jax-hono 1.0.1 → 1.0.2
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/bin/jax.js +66 -0
- package/bin/jax.ts +82 -24
- package/build/generate-config.ts +4 -5
- package/build/generate-registry.ts +4 -5
- package/build/generated-path.ts +57 -0
- package/docs/jax.md +7 -7
- package/package.json +3 -2
- package/plugins/mongoose/model.ts +1 -1
package/README.md
CHANGED
package/bin/jax.js
ADDED
|
@@ -0,0 +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
|
+
});
|
package/bin/jax.ts
CHANGED
|
@@ -4,18 +4,40 @@ import { cac } from "cac";
|
|
|
4
4
|
import { $ } from "bun";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
|
-
import { mkdir } from "node:fs/promises";
|
|
7
|
+
import { lstat, mkdir, rm } from "node:fs/promises";
|
|
8
8
|
|
|
9
9
|
const cli = cac("jax");
|
|
10
10
|
|
|
11
|
+
async function removeExistingTarget(
|
|
12
|
+
targetDir: string,
|
|
13
|
+
allowRegularDirectory: boolean,
|
|
14
|
+
) {
|
|
15
|
+
if (!existsSync(targetDir)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const stats = await lstat(targetDir);
|
|
20
|
+
if (stats.isSymbolicLink() || allowRegularDirectory) {
|
|
21
|
+
await rm(targetDir, { recursive: true, force: true });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.error(
|
|
26
|
+
`目标已存在且不是符号链接:${targetDir}\n请手动删除后再执行 jax link。`,
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
11
31
|
cli
|
|
12
|
-
.command(
|
|
13
|
-
"link",
|
|
14
|
-
"Create a symlink to the jax-hono installation directory",
|
|
15
|
-
)
|
|
32
|
+
.command("link", "创建指向 jax-hono 安装目录的链接")
|
|
16
33
|
.action(async () => {
|
|
17
34
|
const cwd = process.cwd();
|
|
18
35
|
const sourceDir = path.resolve(import.meta.dir, "..");
|
|
36
|
+
if (path.resolve(cwd) === sourceDir) {
|
|
37
|
+
console.error("不能在 jax-hono 安装目录内执行 jax link。");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
19
41
|
const shouldUseNodeModules =
|
|
20
42
|
existsSync(path.join(cwd, "package.json")) ||
|
|
21
43
|
existsSync(path.join(cwd, "node_modules"));
|
|
@@ -29,54 +51,90 @@ cli
|
|
|
29
51
|
await mkdir(targetRoot, { recursive: true });
|
|
30
52
|
}
|
|
31
53
|
|
|
54
|
+
await removeExistingTarget(targetDir, shouldUseNodeModules);
|
|
55
|
+
|
|
32
56
|
if (process.platform === "win32") {
|
|
33
57
|
const result =
|
|
34
|
-
await $`cmd /c mklink /
|
|
58
|
+
await $`cmd /c mklink /J ${targetDir} ${sourceDir}`.nothrow().quiet();
|
|
35
59
|
if (result.exitCode !== 0) {
|
|
36
60
|
console.error(
|
|
37
|
-
|
|
61
|
+
`创建目录联接失败:${result.stderr.toString()}`,
|
|
38
62
|
);
|
|
39
63
|
process.exit(1);
|
|
40
64
|
}
|
|
41
|
-
console.log(
|
|
65
|
+
console.log(`目录联接已创建:${targetDir} <<===>> ${sourceDir}`);
|
|
42
66
|
} else {
|
|
43
67
|
const relativeSource = path.relative(path.dirname(targetDir), sourceDir);
|
|
44
|
-
const result = await $`ln -sf ${relativeSource} ${targetDir}`.nothrow();
|
|
68
|
+
const result = await $`ln -sf ${relativeSource} ${targetDir}`.nothrow().quiet();
|
|
45
69
|
if (result.exitCode !== 0) {
|
|
46
|
-
console.error(
|
|
70
|
+
console.error(`创建符号链接失败:${result.stderr.toString()}`);
|
|
47
71
|
process.exit(1);
|
|
48
72
|
}
|
|
49
|
-
console.log(
|
|
73
|
+
console.log(`符号链接已创建:${targetDir} -> ${relativeSource}`);
|
|
50
74
|
}
|
|
51
75
|
});
|
|
52
76
|
|
|
53
77
|
cli
|
|
54
|
-
.command("open", "
|
|
78
|
+
.command("open", "在文件管理器中打开 jax-hono 安装目录")
|
|
55
79
|
.action(async () => {
|
|
56
80
|
const sourceDir = path.resolve(import.meta.dir, "..");
|
|
57
81
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
if (process.platform === "win32") {
|
|
83
|
+
const escapedSourceDir = sourceDir.replace(/'/g, "''");
|
|
84
|
+
const proc = Bun.spawn([
|
|
85
|
+
"powershell",
|
|
86
|
+
"-NoProfile",
|
|
87
|
+
"-Command",
|
|
88
|
+
`Start-Process -FilePath explorer.exe -ArgumentList '${escapedSourceDir}'`,
|
|
89
|
+
], {
|
|
90
|
+
stdout: "pipe",
|
|
91
|
+
stderr: "pipe",
|
|
92
|
+
windowsHide: true,
|
|
93
|
+
});
|
|
94
|
+
const exitCode = await proc.exited;
|
|
95
|
+
const stderr = await new Response(proc.stderr).text();
|
|
96
|
+
|
|
97
|
+
if (exitCode !== 0) {
|
|
98
|
+
console.error(`打开目录失败:${stderr || "未知错误"}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`已打开:${sourceDir}`);
|
|
103
|
+
return;
|
|
65
104
|
}
|
|
66
105
|
|
|
106
|
+
const command =
|
|
107
|
+
process.platform === "darwin" ? ["open", sourceDir] : ["xdg-open", sourceDir];
|
|
67
108
|
const [cmd, ...args] = command;
|
|
68
|
-
const result = await $`${cmd} ${args}`.nothrow();
|
|
109
|
+
const result = await $`${cmd} ${args}`.nothrow().quiet();
|
|
69
110
|
if (result.exitCode !== 0) {
|
|
70
|
-
console.error(
|
|
111
|
+
console.error(`打开目录失败:${result.stderr.toString() || "未知错误"}`);
|
|
71
112
|
process.exit(1);
|
|
72
113
|
}
|
|
73
|
-
console.log(
|
|
114
|
+
console.log(`已打开:${sourceDir}`);
|
|
74
115
|
});
|
|
75
116
|
|
|
76
|
-
cli.help()
|
|
117
|
+
cli.help((sections) =>
|
|
118
|
+
sections.map((section) => {
|
|
119
|
+
const titleMap: Record<string, string> = {
|
|
120
|
+
Usage: "用法",
|
|
121
|
+
Commands: "命令",
|
|
122
|
+
"For more info, run any command with the `--help` flag":
|
|
123
|
+
"查看更多信息,请在任意命令后添加 `--help`",
|
|
124
|
+
Options: "选项",
|
|
125
|
+
Examples: "示例",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
...section,
|
|
130
|
+
title: section.title ? (titleMap[section.title] ?? section.title) : section.title,
|
|
131
|
+
body: section.body.replace("Display this message", "显示帮助信息"),
|
|
132
|
+
};
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
77
135
|
const parsed = cli.parse();
|
|
78
136
|
|
|
79
|
-
//
|
|
137
|
+
// 未匹配到命令时显示帮助,例如直接执行 `jax` 或输入未知命令。
|
|
80
138
|
if (!cli.matchedCommand && !parsed.options.help) {
|
|
81
139
|
cli.outputHelp();
|
|
82
140
|
}
|
package/build/generate-config.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import { cwd } from 'node:process'
|
|
3
3
|
import { join, parse, relative } from 'node:path'
|
|
4
|
+
import { ensureGeneratedDir, generatedDir, projectDir } from './generated-path'
|
|
4
5
|
|
|
5
|
-
const packageDir = join(import.meta.dir, '..')
|
|
6
6
|
const srcDir = join(cwd(), 'src')
|
|
7
7
|
const configDir = join(srcDir, 'config')
|
|
8
8
|
const typesFile = join(srcDir, 'types/index.ts')
|
|
9
|
-
const generatedDir = join(packageDir, 'generated')
|
|
10
9
|
const outputFile = join(generatedDir, 'config.ts')
|
|
11
10
|
const envStages = new Set([
|
|
12
11
|
'default',
|
|
@@ -102,14 +101,14 @@ ${projectConfigType}
|
|
|
102
101
|
export default configModules
|
|
103
102
|
`
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
ensureGeneratedDir()
|
|
106
105
|
|
|
107
106
|
if (existsSync(outputFile) && readFileSync(outputFile, 'utf8') === content) {
|
|
108
107
|
return
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
writeFileSync(outputFile, content)
|
|
112
|
-
console.log(`Generated ${relative(
|
|
111
|
+
console.log(`Generated ${relative(projectDir, outputFile)} with ${files.length} config file(s).`)
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
if (import.meta.main) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import { cwd } from 'node:process'
|
|
3
3
|
import { basename, join, parse, relative } from 'node:path'
|
|
4
|
+
import { ensureGeneratedDir, generatedDir, projectDir } from './generated-path'
|
|
4
5
|
|
|
5
|
-
const packageDir = join(import.meta.dir, '..')
|
|
6
6
|
const srcDir = join(cwd(), 'src')
|
|
7
|
-
const generatedDir = join(packageDir, 'generated')
|
|
8
7
|
const moduleExtensions = new Set(['.ts', '.js', '.mjs', '.cjs'])
|
|
9
8
|
const generatedHeader = `// This file is generated by jax-hono/build/generate-registry.ts.
|
|
10
9
|
// Do not edit it directly.
|
|
@@ -66,14 +65,14 @@ function walkFiles(dir: string): string[] {
|
|
|
66
65
|
function writeGeneratedFile(fileName: string, content: string) {
|
|
67
66
|
const outputFile = join(generatedDir, fileName)
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
ensureGeneratedDir()
|
|
70
69
|
|
|
71
70
|
if (existsSync(outputFile) && readFileSync(outputFile, 'utf8') === content) {
|
|
72
71
|
return
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
writeFileSync(outputFile, content)
|
|
76
|
-
console.log(`Generated ${relative(
|
|
75
|
+
console.log(`Generated ${relative(projectDir, outputFile)}.`)
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
function toImportPath(filePath: string) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
symlinkSync,
|
|
8
|
+
} from 'node:fs'
|
|
9
|
+
import { cwd } from 'node:process'
|
|
10
|
+
import { join, resolve } from 'node:path'
|
|
11
|
+
|
|
12
|
+
export const packageDir = join(import.meta.dir, '..')
|
|
13
|
+
export const projectDir = cwd()
|
|
14
|
+
|
|
15
|
+
export function getGeneratedDir() {
|
|
16
|
+
const configuredDir = Bun.env.JAX_HONO_GENERATED_DIR
|
|
17
|
+
|
|
18
|
+
return configuredDir
|
|
19
|
+
? resolve(projectDir, configuredDir)
|
|
20
|
+
: join(projectDir, 'generated')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const generatedDir = getGeneratedDir()
|
|
24
|
+
|
|
25
|
+
export function ensureGeneratedDir() {
|
|
26
|
+
mkdirSync(generatedDir, { recursive: true })
|
|
27
|
+
ensurePackageGeneratedLink()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function ensurePackageGeneratedLink() {
|
|
31
|
+
const packageGeneratedDir = join(packageDir, 'generated')
|
|
32
|
+
|
|
33
|
+
if (resolve(packageGeneratedDir) === resolve(generatedDir)) {
|
|
34
|
+
if (existsSync(packageGeneratedDir) && lstatSync(packageGeneratedDir).isSymbolicLink()) {
|
|
35
|
+
rmSync(packageGeneratedDir, { recursive: true, force: true })
|
|
36
|
+
mkdirSync(generatedDir, { recursive: true })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (existsSync(packageGeneratedDir)) {
|
|
43
|
+
const stats = lstatSync(packageGeneratedDir)
|
|
44
|
+
|
|
45
|
+
if (stats.isSymbolicLink()) {
|
|
46
|
+
const currentTarget = realpathSync(packageGeneratedDir)
|
|
47
|
+
|
|
48
|
+
if (resolve(currentTarget) === resolve(generatedDir)) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
rmSync(packageGeneratedDir, { recursive: true, force: true })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
symlinkSync(generatedDir, packageGeneratedDir, 'junction')
|
|
57
|
+
}
|
package/docs/jax.md
CHANGED
|
@@ -80,7 +80,7 @@ export default definePlugin({
|
|
|
80
80
|
});
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
未启用的插件不会写入调用项目的 `generated/plugins.generated.ts`,因此 `bun build` 不会解析它的运行时依赖。
|
|
84
84
|
|
|
85
85
|
### `build`
|
|
86
86
|
|
|
@@ -88,12 +88,12 @@ export default definePlugin({
|
|
|
88
88
|
|
|
89
89
|
当前 `bun run setup` 会生成:
|
|
90
90
|
|
|
91
|
-
- `
|
|
92
|
-
- `
|
|
93
|
-
- `
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
96
|
-
- `
|
|
91
|
+
- `generated/config.ts`
|
|
92
|
+
- `generated/controllers.generated.ts`
|
|
93
|
+
- `generated/middlewares.generated.ts`
|
|
94
|
+
- `generated/models.generated.ts`
|
|
95
|
+
- `generated/plugins.generated.ts`
|
|
96
|
+
- `generated/services.generated.ts`
|
|
97
97
|
|
|
98
98
|
业务代码不要手动编辑 `generated` 目录。
|
|
99
99
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jax-hono",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Lightweight framework layer on top of Hono, built for Bun",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
"bin": {
|
|
63
|
-
"jax": "./bin/jax.
|
|
63
|
+
"jax": "./bin/jax.js"
|
|
64
64
|
},
|
|
65
65
|
"files": [
|
|
66
66
|
"index.ts",
|
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
"mongoose": "*"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
|
+
"@types/bun": "^1.3.14",
|
|
97
98
|
"dayjs": "^1.11.21",
|
|
98
99
|
"deepmerge": "^4.3.1",
|
|
99
100
|
"dotenv": "^17.4.2",
|