alemonjs 2.1.86 → 2.1.88
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/bin/README.md +21 -0
- package/bin/alemonc.js +20 -1
- package/bin/publish.js +405 -0
- package/lib/application/define-router.js +1 -0
- package/lib/application/runtime/client-runtime.d.ts +9 -1
- package/lib/application/runtime/client-runtime.js +9 -0
- package/lib/application/runtime/http/routers/router.js +7 -7
- package/lib/application/runtime/load-modules/loadChild.js +67 -2
- package/lib/application/runtime/store.js +2 -2
- package/lib/global.d.ts +6 -1
- package/package.json +2 -2
package/bin/README.md
CHANGED
|
@@ -100,6 +100,27 @@ alemonc version update
|
|
|
100
100
|
|
|
101
101
|
读取本地 `package.json`,查找所有 `alemonjs` 和 `@alemonjs/*` 依赖,检查并更新到最新版本。
|
|
102
102
|
|
|
103
|
+
#### publish — 智能发布当前包
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
alemonc publish
|
|
107
|
+
alemonc publish patch
|
|
108
|
+
alemonc publish --dry-run
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
行为说明:
|
|
112
|
+
|
|
113
|
+
- 默认会查询 git tag 历史作为版本基线
|
|
114
|
+
- 不传参数时:
|
|
115
|
+
- 如果本地版本高于最新 tag,直接发布本地版本
|
|
116
|
+
- 否则自动按最新 tag `patch +1`
|
|
117
|
+
- 传 `patch/minor/major/prepatch/preminor/premajor/prerelease` 时会自动递增
|
|
118
|
+
- 传具体版本号时会直接以该版本发布
|
|
119
|
+
- 默认先执行 `npm run build`
|
|
120
|
+
- 文件选择规则直接复用 npm 机制,也就是 `package.json.files`、`.npmignore`、`.gitignore` 和 npm 默认规则
|
|
121
|
+
- 最终把产物提交到 git `release` 分支,并推送对应 tag
|
|
122
|
+
- 默认要求 git 工作区干净,发布成功后会自动提交源码中的 `package.json` 版本变更
|
|
123
|
+
|
|
103
124
|
---
|
|
104
125
|
|
|
105
126
|
### 项目诊断
|
package/bin/alemonc.js
CHANGED
|
@@ -6,6 +6,7 @@ import { versionUpdate } from './versionUpdate.js';
|
|
|
6
6
|
import { info } from './info.js';
|
|
7
7
|
import { platformAdd, platformRemove, platformList } from './platform.js';
|
|
8
8
|
import { login } from './login.js';
|
|
9
|
+
import { publish } from './publish.js';
|
|
9
10
|
import { Command } from 'commander';
|
|
10
11
|
const program = new Command();
|
|
11
12
|
|
|
@@ -74,6 +75,24 @@ program
|
|
|
74
75
|
versionUpdate();
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
program
|
|
79
|
+
.command('publish [release]')
|
|
80
|
+
.description('智能发布当前包到 git release 仓库,支持 patch/minor/major 或直接指定版本号')
|
|
81
|
+
.option('--tag <tag>', 'git 标签通道,默认 stable 为 latest,预发布为 next')
|
|
82
|
+
.option('--preid <preid>', '预发布标识,默认 beta', 'beta')
|
|
83
|
+
.option('--branch <branch>', '发布目标分支,默认 release')
|
|
84
|
+
.option('--dry-run', '只执行检查和打包,不真正发布')
|
|
85
|
+
.option('--skip-build', '跳过构建')
|
|
86
|
+
.option('--no-git-checks', '跳过 git 干净工作区检查及发布后自动提交源码版本')
|
|
87
|
+
.action(async (release, options) => {
|
|
88
|
+
try {
|
|
89
|
+
await publish(release, options);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(error instanceof Error ? error.message : error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
77
96
|
program
|
|
78
97
|
.command('info')
|
|
79
98
|
.description('输出项目诊断信息')
|
|
@@ -118,4 +137,4 @@ program
|
|
|
118
137
|
program.help();
|
|
119
138
|
});
|
|
120
139
|
|
|
121
|
-
program.
|
|
140
|
+
program.parseAsync(process.argv);
|
package/bin/publish.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { execSync, spawnSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const RELEASE_TYPES = new Set(['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease']);
|
|
8
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
9
|
+
|
|
10
|
+
function readPackageJson() {
|
|
11
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(pkgPath)) {
|
|
14
|
+
throw new Error('未找到 package.json');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
pkgPath,
|
|
19
|
+
pkg: JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function runCommand(command, args, options = {}) {
|
|
24
|
+
const result = spawnSync(command, args, {
|
|
25
|
+
cwd: process.cwd(),
|
|
26
|
+
stdio: 'inherit',
|
|
27
|
+
shell: false,
|
|
28
|
+
...options
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (result.error) {
|
|
32
|
+
throw result.error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (result.status !== 0) {
|
|
36
|
+
throw new Error(`命令执行失败: ${command} ${args.join(' ')}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getCommandOutput(command) {
|
|
41
|
+
return execSync(command, {
|
|
42
|
+
cwd: process.cwd(),
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
45
|
+
}).trim();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasCommand(command) {
|
|
49
|
+
const result = spawnSync(command, ['--version'], {
|
|
50
|
+
cwd: process.cwd(),
|
|
51
|
+
stdio: 'ignore'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return !result.error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isGitRepo() {
|
|
58
|
+
try {
|
|
59
|
+
return getCommandOutput('git rev-parse --is-inside-work-tree') === 'true';
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ensureCleanGit() {
|
|
66
|
+
const status = getCommandOutput('git status --porcelain');
|
|
67
|
+
if (status) {
|
|
68
|
+
throw new Error('工作区存在未提交改动,请先提交或使用 --no-git-checks 跳过检查');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseVersion(version) {
|
|
73
|
+
const matched = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
74
|
+
if (!matched) {
|
|
75
|
+
throw new Error(`非法版本号: ${version}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
major: Number(matched[1]),
|
|
80
|
+
minor: Number(matched[2]),
|
|
81
|
+
patch: Number(matched[3]),
|
|
82
|
+
prerelease: matched[4] ?? null
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function compareVersions(a, b) {
|
|
87
|
+
const av = parseVersion(a);
|
|
88
|
+
const bv = parseVersion(b);
|
|
89
|
+
|
|
90
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
91
|
+
if (av[key] > bv[key]) return 1;
|
|
92
|
+
if (av[key] < bv[key]) return -1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (av.prerelease === bv.prerelease) return 0;
|
|
96
|
+
if (av.prerelease === null) return 1;
|
|
97
|
+
if (bv.prerelease === null) return -1;
|
|
98
|
+
|
|
99
|
+
return av.prerelease.localeCompare(bv.prerelease);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function incrementVersion(baseVersion, releaseType, preid = 'beta') {
|
|
103
|
+
const parsed = parseVersion(baseVersion);
|
|
104
|
+
|
|
105
|
+
if (releaseType === 'patch') {
|
|
106
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (releaseType === 'minor') {
|
|
110
|
+
return `${parsed.major}.${parsed.minor + 1}.0`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (releaseType === 'major') {
|
|
114
|
+
return `${parsed.major + 1}.0.0`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (releaseType === 'prepatch') {
|
|
118
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}-${preid}.0`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (releaseType === 'preminor') {
|
|
122
|
+
return `${parsed.major}.${parsed.minor + 1}.0-${preid}.0`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (releaseType === 'premajor') {
|
|
126
|
+
return `${parsed.major + 1}.0.0-${preid}.0`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (releaseType === 'prerelease') {
|
|
130
|
+
if (parsed.prerelease) {
|
|
131
|
+
const next = parsed.prerelease.match(/^(.*?)(\d+)$/);
|
|
132
|
+
if (next) {
|
|
133
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch}-${next[1]}${Number(next[2]) + 1}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch}-${parsed.prerelease}.1`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}-${preid}.0`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
throw new Error(`不支持的发布类型: ${releaseType}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function resolveTargetVersion(localVersion, remoteVersion, release, preid) {
|
|
146
|
+
if (!release) {
|
|
147
|
+
if (!remoteVersion) {
|
|
148
|
+
return localVersion;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (compareVersions(localVersion, remoteVersion) > 0) {
|
|
152
|
+
return localVersion;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return incrementVersion(remoteVersion, 'patch', preid);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (RELEASE_TYPES.has(release)) {
|
|
159
|
+
const base = remoteVersion && compareVersions(remoteVersion, localVersion) > 0 ? remoteVersion : localVersion;
|
|
160
|
+
return incrementVersion(base, release, preid);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (SEMVER_RE.test(release)) {
|
|
164
|
+
return release;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw new Error(`无法识别的发布参数: ${release}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function updateVersion(pkgPath, pkg, version) {
|
|
171
|
+
pkg.version = version;
|
|
172
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getPackResult(cwd = process.cwd()) {
|
|
176
|
+
const output = execSync('npm pack --json --dry-run', {
|
|
177
|
+
cwd,
|
|
178
|
+
encoding: 'utf8',
|
|
179
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
180
|
+
maxBuffer: 20 * 1024 * 1024
|
|
181
|
+
}).trim();
|
|
182
|
+
|
|
183
|
+
const result = JSON.parse(output);
|
|
184
|
+
if (!Array.isArray(result) || result.length === 0 || !Array.isArray(result[0]?.files)) {
|
|
185
|
+
throw new Error('无法解析 npm pack 文件清单');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result[0];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function copyPublishFiles(files) {
|
|
192
|
+
const stageDir = fs.mkdtempSync(join(os.tmpdir(), 'alemon-publish-'));
|
|
193
|
+
|
|
194
|
+
for (const item of files) {
|
|
195
|
+
const relativePath = item.path;
|
|
196
|
+
const sourcePath = join(process.cwd(), relativePath);
|
|
197
|
+
const targetPath = join(stageDir, relativePath);
|
|
198
|
+
|
|
199
|
+
fs.mkdirSync(join(targetPath, '..'), { recursive: true });
|
|
200
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return stageDir;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function materializePublishDir(sourceDir, files) {
|
|
207
|
+
const publishDir = fs.mkdtempSync(join(os.tmpdir(), 'alemon-release-'));
|
|
208
|
+
|
|
209
|
+
for (const item of files) {
|
|
210
|
+
const relativePath = item.path;
|
|
211
|
+
const sourcePath = join(sourceDir, relativePath);
|
|
212
|
+
const targetPath = join(publishDir, relativePath);
|
|
213
|
+
|
|
214
|
+
fs.mkdirSync(join(targetPath, '..'), { recursive: true });
|
|
215
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return publishDir;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function removeDirContents(dirPath) {
|
|
222
|
+
for (const entry of fs.readdirSync(dirPath)) {
|
|
223
|
+
if (entry === '.git') {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
fs.rmSync(join(dirPath, entry), { recursive: true, force: true });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function copyDirContents(sourceDir, targetDir) {
|
|
232
|
+
for (const entry of fs.readdirSync(sourceDir)) {
|
|
233
|
+
fs.cpSync(join(sourceDir, entry), join(targetDir, entry), { recursive: true });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function ensureGitRepo() {
|
|
238
|
+
if (!isGitRepo()) {
|
|
239
|
+
throw new Error('当前目录不是 git 仓库,无法执行 git 发布');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function remoteBranchExists(branch) {
|
|
244
|
+
const result = spawnSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', branch], {
|
|
245
|
+
cwd: process.cwd(),
|
|
246
|
+
stdio: 'ignore'
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return result.status === 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function localBranchExists(branch) {
|
|
253
|
+
const result = spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], {
|
|
254
|
+
cwd: process.cwd(),
|
|
255
|
+
stdio: 'ignore'
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return result.status === 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function createReleaseWorktree(branch) {
|
|
262
|
+
const worktreeDir = fs.mkdtempSync(join(os.tmpdir(), 'alemon-worktree-'));
|
|
263
|
+
|
|
264
|
+
if (remoteBranchExists(branch)) {
|
|
265
|
+
runCommand('git', ['fetch', 'origin', branch]);
|
|
266
|
+
runCommand('git', ['worktree', 'add', '-B', branch, worktreeDir, `origin/${branch}`]);
|
|
267
|
+
} else if (localBranchExists(branch)) {
|
|
268
|
+
runCommand('git', ['worktree', 'add', worktreeDir, branch]);
|
|
269
|
+
} else {
|
|
270
|
+
runCommand('git', ['worktree', 'add', '-b', branch, worktreeDir]);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return worktreeDir;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function cleanupWorktree(worktreeDir) {
|
|
277
|
+
if (!worktreeDir) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
spawnSync('git', ['worktree', 'remove', '--force', worktreeDir], {
|
|
282
|
+
cwd: process.cwd(),
|
|
283
|
+
stdio: 'ignore'
|
|
284
|
+
});
|
|
285
|
+
fs.rmSync(worktreeDir, { recursive: true, force: true });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function publish(release, options = {}) {
|
|
289
|
+
if (!hasCommand('git')) {
|
|
290
|
+
throw new Error('未找到 git,请先安装 git');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!hasCommand('npm')) {
|
|
294
|
+
throw new Error('未找到 npm,请先安装 Node.js/npm');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const { pkgPath, pkg } = readPackageJson();
|
|
298
|
+
const packageName = pkg.name;
|
|
299
|
+
const localVersion = String(pkg.version || '').trim();
|
|
300
|
+
|
|
301
|
+
if (!packageName) {
|
|
302
|
+
throw new Error('package.json 缺少 name');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!SEMVER_RE.test(localVersion)) {
|
|
306
|
+
throw new Error(`当前 package.json 版本号非法: ${localVersion}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log(`发布包: ${packageName}`);
|
|
310
|
+
console.log(`本地版本: ${localVersion}`);
|
|
311
|
+
|
|
312
|
+
ensureGitRepo();
|
|
313
|
+
|
|
314
|
+
const releaseBranch = options.branch || 'release';
|
|
315
|
+
const remoteVersion = getCommandOutput(`git tag --list "v*" --sort=-v:refname | head -n 1`).replace(/^v/, '');
|
|
316
|
+
if (remoteVersion) {
|
|
317
|
+
console.log(`最新 git tag: v${remoteVersion}`);
|
|
318
|
+
} else {
|
|
319
|
+
console.log('最新 git tag: 无,将按首次发布处理');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (options.gitChecks !== false && isGitRepo()) {
|
|
323
|
+
ensureCleanGit();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const targetVersion = resolveTargetVersion(localVersion, remoteVersion, release, options.preid);
|
|
327
|
+
const distTag = options.tag || (targetVersion.includes('-') ? 'next' : 'latest');
|
|
328
|
+
const gitTagName = distTag === 'latest' ? `v${targetVersion}` : `v${targetVersion}-${distTag}`;
|
|
329
|
+
console.log(`目标版本: ${targetVersion}`);
|
|
330
|
+
console.log(`发布分支: ${releaseBranch}`);
|
|
331
|
+
console.log(`git 标签: ${gitTagName}`);
|
|
332
|
+
|
|
333
|
+
if (targetVersion !== localVersion) {
|
|
334
|
+
updateVersion(pkgPath, pkg, targetVersion);
|
|
335
|
+
console.log(`已更新 package.json 版本: ${localVersion} -> ${targetVersion}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let stageDir = null;
|
|
339
|
+
let publishDir = null;
|
|
340
|
+
let worktreeDir = null;
|
|
341
|
+
try {
|
|
342
|
+
if (!options.skipBuild) {
|
|
343
|
+
if (pkg.scripts?.build) {
|
|
344
|
+
console.log('执行构建...');
|
|
345
|
+
runCommand('npm', ['run', 'build']);
|
|
346
|
+
} else {
|
|
347
|
+
console.log('未定义 build 脚本,跳过构建');
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
console.log('已跳过构建');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const packResult = getPackResult();
|
|
354
|
+
stageDir = copyPublishFiles(packResult.files);
|
|
355
|
+
|
|
356
|
+
const filteredPackResult = getPackResult(stageDir);
|
|
357
|
+
publishDir = materializePublishDir(stageDir, filteredPackResult.files);
|
|
358
|
+
|
|
359
|
+
console.log(`发布文件数: ${filteredPackResult.files.length}`);
|
|
360
|
+
|
|
361
|
+
if (options.dryRun) {
|
|
362
|
+
console.log('dry-run 模式,不会真正推送到 git');
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
console.log('准备 release worktree...');
|
|
367
|
+
worktreeDir = createReleaseWorktree(releaseBranch);
|
|
368
|
+
removeDirContents(worktreeDir);
|
|
369
|
+
copyDirContents(publishDir, worktreeDir);
|
|
370
|
+
|
|
371
|
+
runCommand('git', ['-C', worktreeDir, 'add', '-A']);
|
|
372
|
+
const hasChanges = spawnSync('git', ['-C', worktreeDir, 'diff', '--cached', '--quiet']).status !== 0;
|
|
373
|
+
if (!hasChanges) {
|
|
374
|
+
console.log('release 分支无文件变化,跳过提交');
|
|
375
|
+
} else {
|
|
376
|
+
runCommand('git', ['-C', worktreeDir, 'commit', '-m', `release: ${gitTagName}`]);
|
|
377
|
+
runCommand('git', ['-C', worktreeDir, 'push', 'origin', `HEAD:${releaseBranch}`]);
|
|
378
|
+
console.log(`已推送到分支: ${releaseBranch}`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
runCommand('git', ['tag', '-f', gitTagName]);
|
|
382
|
+
runCommand('git', ['push', 'origin', gitTagName, '--force']);
|
|
383
|
+
console.log(`发布完成: ${packageName}@${targetVersion}`);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (targetVersion !== localVersion) {
|
|
386
|
+
updateVersion(pkgPath, pkg, localVersion);
|
|
387
|
+
console.log(`已回滚 package.json 版本到 ${localVersion}`);
|
|
388
|
+
}
|
|
389
|
+
throw error;
|
|
390
|
+
} finally {
|
|
391
|
+
if (stageDir) {
|
|
392
|
+
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
393
|
+
}
|
|
394
|
+
if (publishDir) {
|
|
395
|
+
fs.rmSync(publishDir, { recursive: true, force: true });
|
|
396
|
+
}
|
|
397
|
+
cleanupWorktree(worktreeDir);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (options.gitChecks !== false && isGitRepo()) {
|
|
401
|
+
runCommand('git', ['add', 'package.json']);
|
|
402
|
+
runCommand('git', ['commit', '-m', `release: ${gitTagName}`]);
|
|
403
|
+
console.log(`已记录源码版本变更: ${gitTagName}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import '../define-children.js';
|
|
2
|
+
import '../define-response.js';
|
|
3
|
+
import '../define-middleware.js';
|
|
4
|
+
import '../define-router.js';
|
|
5
|
+
import '../format/message-api.js';
|
|
6
|
+
import './event-response.js';
|
|
7
|
+
import './event-middleware.js';
|
|
8
|
+
import './event-utils.js';
|
|
9
|
+
import './event-group.js';
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import '../define-children.js';
|
|
2
|
+
import '../define-response.js';
|
|
3
|
+
import '../define-middleware.js';
|
|
4
|
+
import '../define-router.js';
|
|
5
|
+
import '../format/message-api.js';
|
|
6
|
+
import './event-response.js';
|
|
7
|
+
import './event-middleware.js';
|
|
8
|
+
import './event-utils.js';
|
|
9
|
+
import './event-group.js';
|
|
1
10
|
import { cbpClient } from './cbp/connects/client.js';
|
|
2
11
|
import 'flatted';
|
|
3
12
|
import 'fs';
|
|
@@ -177,19 +177,19 @@ const dispatchAppKoaRouters = async (ctx, appName) => {
|
|
|
177
177
|
const beforeRouterPath = ctx.routerPath;
|
|
178
178
|
let fallthrough = false;
|
|
179
179
|
await rewriteCtxPath(ctx, rewrittenPath, async () => {
|
|
180
|
-
await koaRouter.routes()(ctx, () => {
|
|
180
|
+
await koaRouter.routes()(ctx, async () => {
|
|
181
181
|
fallthrough = true;
|
|
182
182
|
});
|
|
183
183
|
});
|
|
184
184
|
const afterMatched = Array.isArray(matchedContext.matched) ? matchedContext.matched.length : 0;
|
|
185
185
|
const afterMatchedRoute = ctx._matchedRoute;
|
|
186
186
|
const afterRouterPath = ctx.routerPath;
|
|
187
|
-
const handled = afterMatched > beforeMatched
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
const handled = afterMatched > beforeMatched ||
|
|
188
|
+
afterMatchedRoute !== beforeMatchedRoute ||
|
|
189
|
+
afterRouterPath !== beforeRouterPath ||
|
|
190
|
+
ctx.status !== beforeStatus ||
|
|
191
|
+
ctx.body !== beforeBody ||
|
|
192
|
+
!fallthrough;
|
|
193
193
|
if (!handled) {
|
|
194
194
|
continue;
|
|
195
195
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dirname, join } from 'path';
|
|
2
|
-
import { existsSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
import { showErrorModule, getRecursiveDirFiles, createEventName } from '../../../common/utils.js';
|
|
4
4
|
import { registerRuntimeApp, updateRuntimeAppStatus, ChildrenApp, clearRuntimeAppKoaRouters, setRuntimeAppKoaRouters, updateRuntimeAppCapabilities } from '../store.js';
|
|
5
5
|
import { registerExpose } from '../../expose.js';
|
|
@@ -11,6 +11,50 @@ import { dispatchRuntimeStatusChange, dispatchAppDispose, dispatchAppReady } fro
|
|
|
11
11
|
const initRequire = () => { };
|
|
12
12
|
initRequire.resolve = () => '';
|
|
13
13
|
const require$1 = module$1?.createRequire?.(import.meta.url) ?? initRequire;
|
|
14
|
+
const resolvePackageDir = (appName) => {
|
|
15
|
+
const resolveWithPaths = require$1.resolve;
|
|
16
|
+
const candidatePaths = resolveWithPaths?.paths?.(appName) ?? [];
|
|
17
|
+
for (const basePath of candidatePaths) {
|
|
18
|
+
const packageDir = join(basePath, appName);
|
|
19
|
+
if (existsSync(join(packageDir, 'package.json'))) {
|
|
20
|
+
return packageDir;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
const resolvePackageEntryFromPackageJson = (packageDir) => {
|
|
26
|
+
const packageJsonPath = join(packageDir, 'package.json');
|
|
27
|
+
if (!existsSync(packageJsonPath)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) ?? {};
|
|
32
|
+
const exportsField = pkg?.exports;
|
|
33
|
+
let entry = null;
|
|
34
|
+
if (typeof exportsField === 'string') {
|
|
35
|
+
entry = exportsField;
|
|
36
|
+
}
|
|
37
|
+
else if (exportsField && typeof exportsField === 'object') {
|
|
38
|
+
const rootExport = exportsField['.'];
|
|
39
|
+
if (typeof rootExport === 'string') {
|
|
40
|
+
entry = rootExport;
|
|
41
|
+
}
|
|
42
|
+
else if (rootExport && typeof rootExport === 'object') {
|
|
43
|
+
entry = rootExport.import ?? rootExport.default ?? rootExport.require ?? null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!entry) {
|
|
47
|
+
entry = pkg?.module ?? pkg?.main ?? 'index.js';
|
|
48
|
+
}
|
|
49
|
+
if (typeof entry !== 'string' || !entry.trim()) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return join(packageDir, entry);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
14
58
|
const resolvePackageRoot = (startDir) => {
|
|
15
59
|
let currentDir = startDir;
|
|
16
60
|
while (currentDir && currentDir !== dirname(currentDir)) {
|
|
@@ -253,7 +297,14 @@ const loadChildrenFile = (appName) => {
|
|
|
253
297
|
return;
|
|
254
298
|
}
|
|
255
299
|
try {
|
|
256
|
-
|
|
300
|
+
let mainPath = require$1.resolve(appName);
|
|
301
|
+
if (!existsSync(mainPath)) {
|
|
302
|
+
const packageDir = resolvePackageDir(appName);
|
|
303
|
+
const fallbackMainPath = packageDir ? resolvePackageEntryFromPackageJson(packageDir) : null;
|
|
304
|
+
if (fallbackMainPath) {
|
|
305
|
+
mainPath = fallbackMainPath;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
257
308
|
if (!existsSync(mainPath)) {
|
|
258
309
|
updateRuntimeAppStatus(appName, 'failed', new Error('The main file does not exist,' + mainPath));
|
|
259
310
|
logger.error({
|
|
@@ -274,6 +325,20 @@ const loadChildrenFile = (appName) => {
|
|
|
274
325
|
void loadChildren(mainPath, appName);
|
|
275
326
|
}
|
|
276
327
|
catch (e) {
|
|
328
|
+
const packageDir = resolvePackageDir(appName);
|
|
329
|
+
const fallbackMainPath = packageDir ? resolvePackageEntryFromPackageJson(packageDir) : null;
|
|
330
|
+
if (fallbackMainPath && existsSync(fallbackMainPath)) {
|
|
331
|
+
registerRuntimeApp({
|
|
332
|
+
name: appName,
|
|
333
|
+
kind: 'plugin',
|
|
334
|
+
enabled: true,
|
|
335
|
+
status: 'discovered',
|
|
336
|
+
rootDir: dirname(fallbackMainPath),
|
|
337
|
+
mainPath: fallbackMainPath
|
|
338
|
+
});
|
|
339
|
+
void loadChildren(fallbackMainPath, appName);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
277
342
|
updateRuntimeAppStatus(appName, 'failed', e);
|
|
278
343
|
showErrorModule(e);
|
|
279
344
|
}
|
|
@@ -107,8 +107,8 @@ const registerRuntimeApp = (record) => {
|
|
|
107
107
|
createdAt: current?.createdAt ?? now,
|
|
108
108
|
updatedAt: now
|
|
109
109
|
};
|
|
110
|
-
if (current?.status !== record.status) {
|
|
111
|
-
logRuntimeAppStatus(
|
|
110
|
+
if (current?.status !== record.status && process.env.NODE_ENV === 'development') {
|
|
111
|
+
logRuntimeAppStatus('debug', runtimeApps[record.name]);
|
|
112
112
|
}
|
|
113
113
|
return runtimeApps[record.name];
|
|
114
114
|
};
|
package/lib/global.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DefineChildrenFunc, OnResponseReversalFunc, OnMiddlewareReversalFunc, OnSelectsFunc, OnDataFormatFunc, OnResponseReversalFuncBack, OnGroupFunc, OnMiddlewareReversalFuncBack, DefineResponseFunc, defineMiddlewareFunc, StoreChildrenApp, StateSubscribeMap, SubscribeKeysMap, LoggerUtils, ResponseState, StartOptions } from './types';
|
|
1
|
+
import type { DefineChildrenFunc, OnResponseReversalFunc, OnMiddlewareReversalFunc, OnSelectsFunc, OnDataFormatFunc, OnResponseReversalFuncBack, OnGroupFunc, OnMiddlewareReversalFuncBack, DefineResponseFunc, defineMiddlewareFunc, DefineRouterFunc, StoreChildrenApp, StateSubscribeMap, SubscribeKeysMap, LoggerUtils, ResponseState, StartOptions } from './types';
|
|
2
2
|
import { type Server } from 'ws';
|
|
3
3
|
import type WebSocket from 'ws';
|
|
4
4
|
import type { IncomingMessage } from 'http';
|
|
@@ -7,6 +7,9 @@ import type { RuntimeAppRecord } from './application/runtime/store.js';
|
|
|
7
7
|
declare global {
|
|
8
8
|
var __config: any;
|
|
9
9
|
var __options: StartOptions;
|
|
10
|
+
var __sandbox: boolean | undefined;
|
|
11
|
+
var __client_loaded: boolean | undefined;
|
|
12
|
+
var __publicIp: string | undefined;
|
|
10
13
|
var logger: LoggerUtils;
|
|
11
14
|
var alemonjsCore: {
|
|
12
15
|
storeState: ResponseState;
|
|
@@ -25,6 +28,7 @@ declare global {
|
|
|
25
28
|
var chatbotServer: Server<typeof WebSocket, typeof IncomingMessage>;
|
|
26
29
|
var chatbotPlatform: WebSocket;
|
|
27
30
|
var chatbotClient: WebSocket;
|
|
31
|
+
var testoneClient: WebSocket | undefined;
|
|
28
32
|
var onResponse: OnResponseReversalFunc;
|
|
29
33
|
var OnResponse: OnResponseReversalFuncBack;
|
|
30
34
|
var onMiddleware: OnMiddlewareReversalFunc;
|
|
@@ -32,6 +36,7 @@ declare global {
|
|
|
32
36
|
var defineChildren: DefineChildrenFunc;
|
|
33
37
|
var defineResponse: DefineResponseFunc;
|
|
34
38
|
var defineMiddleware: defineMiddlewareFunc;
|
|
39
|
+
var defineRouter: DefineRouterFunc;
|
|
35
40
|
var onSelects: OnSelectsFunc;
|
|
36
41
|
var format: OnDataFormatFunc;
|
|
37
42
|
var onGroup: OnGroupFunc;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alemonjs",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.88",
|
|
4
4
|
"description": "bot script",
|
|
5
5
|
"author": "lemonade",
|
|
6
6
|
"license": "MIT",
|
|
@@ -93,4 +93,4 @@
|
|
|
93
93
|
"url": "https://github.com/lemonade-lab/alemonjs.git"
|
|
94
94
|
},
|
|
95
95
|
"gitHead": "c6aa5616afe091a37610dad22fbb2d2618d943b8"
|
|
96
|
-
}
|
|
96
|
+
}
|