@zhin.js/cli 1.0.5 → 1.0.7
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/CHANGELOG.md +14 -0
- package/README.md +125 -57
- package/lib/cli.js +2 -2
- package/lib/cli.js.map +1 -1
- package/lib/commands/build.d.ts.map +1 -1
- package/lib/commands/build.js +72 -26
- package/lib/commands/build.js.map +1 -1
- package/lib/commands/new.d.ts +3 -0
- package/lib/commands/new.d.ts.map +1 -0
- package/lib/commands/new.js +351 -0
- package/lib/commands/new.js.map +1 -0
- package/lib/commands/start.d.ts.map +1 -1
- package/lib/commands/start.js +10 -4
- package/lib/commands/start.js.map +1 -1
- package/lib/utils/process.d.ts +2 -2
- package/lib/utils/process.d.ts.map +1 -1
- package/lib/utils/process.js +4 -10
- package/lib/utils/process.js.map +1 -1
- package/package.json +4 -4
- package/src/cli.ts +2 -2
- package/src/commands/build.ts +87 -32
- package/src/commands/new.ts +392 -0
- package/src/commands/start.ts +10 -5
- package/src/utils/process.ts +5 -11
- package/lib/commands/init.d.ts +0 -3
- package/lib/commands/init.d.ts.map +0 -1
- package/lib/commands/init.js +0 -845
- package/lib/commands/init.js.map +0 -1
- package/src/commands/init.ts +0 -893
package/src/commands/build.ts
CHANGED
|
@@ -5,48 +5,57 @@ import fs from 'fs-extra';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
|
|
7
7
|
export const buildCommand = new Command('build')
|
|
8
|
-
.description('
|
|
8
|
+
.description('构建插件项目')
|
|
9
|
+
.argument('[plugin]', '插件名称(默认构建所有插件)')
|
|
9
10
|
.option('--clean', '清理输出目录', false)
|
|
10
|
-
.action(async (options: { clean: boolean }) => {
|
|
11
|
+
.action(async (pluginName: string | undefined, options: { clean: boolean }) => {
|
|
11
12
|
try {
|
|
12
13
|
const cwd = process.cwd();
|
|
13
14
|
|
|
14
|
-
// 检查是否是
|
|
15
|
-
if (!
|
|
16
|
-
logger.error('当前目录不是Zhin项目');
|
|
15
|
+
// 检查是否是 workspace 项目
|
|
16
|
+
if (!isWorkspaceProject(cwd)) {
|
|
17
|
+
logger.error('当前目录不是 Zhin workspace 项目');
|
|
17
18
|
process.exit(1);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const pluginsDir = path.join(cwd, 'plugins');
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await fs.remove(distPath);
|
|
23
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
24
|
+
logger.error('未找到 plugins 目录');
|
|
25
|
+
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
shell:true,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
child.on('close', (code) => {
|
|
38
|
-
if (code === 0) {
|
|
39
|
-
logger.info('构建完成!');
|
|
40
|
-
} else {
|
|
41
|
-
logger.error(`构建失败,退出码: ${code}`);
|
|
28
|
+
// 如果指定了插件名称
|
|
29
|
+
if (pluginName) {
|
|
30
|
+
const pluginPath = path.join(pluginsDir, pluginName);
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(pluginPath)) {
|
|
33
|
+
logger.error(`未找到插件: ${pluginName}`);
|
|
42
34
|
process.exit(1);
|
|
43
35
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
|
|
37
|
+
await buildPlugin(pluginPath, pluginName, options.clean);
|
|
38
|
+
} else {
|
|
39
|
+
// 构建所有插件
|
|
40
|
+
const plugins = await fs.readdir(pluginsDir);
|
|
41
|
+
const validPlugins = plugins.filter(p => {
|
|
42
|
+
const pluginPath = path.join(pluginsDir, p);
|
|
43
|
+
const stat = fs.statSync(pluginPath);
|
|
44
|
+
return stat.isDirectory() && fs.existsSync(path.join(pluginPath, 'package.json'));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (validPlugins.length === 0) {
|
|
48
|
+
logger.warn('未找到任何插件');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
logger.info(`找到 ${validPlugins.length} 个插件,开始构建...`);
|
|
53
|
+
|
|
54
|
+
for (const plugin of validPlugins) {
|
|
55
|
+
const pluginPath = path.join(pluginsDir, plugin);
|
|
56
|
+
await buildPlugin(pluginPath, plugin, options.clean);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
50
59
|
|
|
51
60
|
} catch (error) {
|
|
52
61
|
logger.error(`构建失败: ${error}`);
|
|
@@ -54,9 +63,55 @@ export const buildCommand = new Command('build')
|
|
|
54
63
|
}
|
|
55
64
|
});
|
|
56
65
|
|
|
57
|
-
function
|
|
66
|
+
async function buildPlugin(pluginPath: string, pluginName: string, clean: boolean): Promise<void> {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
logger.info(`正在构建插件: ${pluginName}...`);
|
|
69
|
+
|
|
70
|
+
// 清理输出目录
|
|
71
|
+
if (clean) {
|
|
72
|
+
const libPath = path.join(pluginPath, 'lib');
|
|
73
|
+
const distPath = path.join(pluginPath, 'dist');
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(libPath)) {
|
|
76
|
+
fs.removeSync(libPath);
|
|
77
|
+
logger.info(`✓ 已清理 ${pluginName}/lib`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (fs.existsSync(distPath)) {
|
|
81
|
+
fs.removeSync(distPath);
|
|
82
|
+
logger.info(`✓ 已清理 ${pluginName}/dist`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 使用 pnpm build 构建插件
|
|
87
|
+
const child = spawn('pnpm', ['build'], {
|
|
88
|
+
cwd: pluginPath,
|
|
89
|
+
stdio: 'inherit',
|
|
90
|
+
shell: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
child.on('close', (code) => {
|
|
94
|
+
if (code === 0) {
|
|
95
|
+
logger.success(`✓ ${pluginName} 构建完成`);
|
|
96
|
+
resolve();
|
|
97
|
+
} else {
|
|
98
|
+
logger.error(`✗ ${pluginName} 构建失败,退出码: ${code}`);
|
|
99
|
+
reject(new Error(`Build failed with code ${code}`));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
child.on('error', (error) => {
|
|
104
|
+
logger.error(`✗ ${pluginName} 构建失败: ${error.message}`);
|
|
105
|
+
reject(error);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isWorkspaceProject(cwd: string): boolean {
|
|
111
|
+
const workspaceYamlPath = path.join(cwd, 'pnpm-workspace.yaml');
|
|
58
112
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
59
|
-
|
|
113
|
+
|
|
114
|
+
if (!fs.existsSync(workspaceYamlPath) || !fs.existsSync(packageJsonPath)) {
|
|
60
115
|
return false;
|
|
61
116
|
}
|
|
62
117
|
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
interface NewPluginOptions {
|
|
9
|
+
skipInstall?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const newCommand = new Command('new')
|
|
13
|
+
.description('创建插件包模板')
|
|
14
|
+
.argument('[plugin-name]', '插件名称(如: my-plugin)')
|
|
15
|
+
.option('--skip-install', '跳过依赖安装', false)
|
|
16
|
+
.action(async (pluginName: string, options: NewPluginOptions) => {
|
|
17
|
+
try {
|
|
18
|
+
let name = pluginName;
|
|
19
|
+
|
|
20
|
+
if (!name) {
|
|
21
|
+
const { pluginName: inputName } = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'input',
|
|
24
|
+
name: 'pluginName',
|
|
25
|
+
message: '请输入插件名称:',
|
|
26
|
+
default: 'my-plugin',
|
|
27
|
+
validate: (input: string) => {
|
|
28
|
+
if (!input.trim()) {
|
|
29
|
+
return '插件名称不能为空';
|
|
30
|
+
}
|
|
31
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(input)) {
|
|
32
|
+
return '插件名称只能包含字母、数字、横线和下划线';
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
name = inputName;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 确定插件目录
|
|
42
|
+
const pluginDir = path.resolve(process.cwd(), 'plugins', name);
|
|
43
|
+
|
|
44
|
+
// 检查目录是否已存在
|
|
45
|
+
if (fs.existsSync(pluginDir)) {
|
|
46
|
+
logger.error(`插件目录已存在: ${pluginDir}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.info(`正在创建插件包 ${name}...`);
|
|
51
|
+
|
|
52
|
+
// 创建插件包结构
|
|
53
|
+
await createPluginPackage(pluginDir, name, options);
|
|
54
|
+
|
|
55
|
+
// 自动添加到 app/package.json
|
|
56
|
+
await addPluginToApp(name);
|
|
57
|
+
|
|
58
|
+
logger.success(`✓ 插件包 ${name} 创建成功!`);
|
|
59
|
+
logger.log('');
|
|
60
|
+
logger.log('📝 下一步操作:');
|
|
61
|
+
logger.log(` cd plugins/${name}`);
|
|
62
|
+
if (options.skipInstall) {
|
|
63
|
+
logger.log(` pnpm install`);
|
|
64
|
+
}
|
|
65
|
+
logger.log(` pnpm build`);
|
|
66
|
+
logger.log(` pnpm dev # 开发模式(监听文件变化)`);
|
|
67
|
+
logger.log('');
|
|
68
|
+
logger.log('📦 发布到 npm:');
|
|
69
|
+
logger.log(` pnpm publish`);
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error(`创建插件失败: ${error}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
async function createPluginPackage(pluginDir: string, pluginName: string, options: NewPluginOptions) {
|
|
78
|
+
const capitalizedName = pluginName.charAt(0).toUpperCase() + pluginName.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
79
|
+
const packageName = `@zhin.js/${pluginName}`;
|
|
80
|
+
|
|
81
|
+
// 创建目录结构
|
|
82
|
+
await fs.ensureDir(pluginDir);
|
|
83
|
+
await fs.ensureDir(path.join(pluginDir, 'app'));
|
|
84
|
+
await fs.ensureDir(path.join(pluginDir, 'client'));
|
|
85
|
+
await fs.ensureDir(path.join(pluginDir, 'lib'));
|
|
86
|
+
await fs.ensureDir(path.join(pluginDir, 'dist'));
|
|
87
|
+
|
|
88
|
+
// 创建 package.json
|
|
89
|
+
const packageJson = {
|
|
90
|
+
name: packageName,
|
|
91
|
+
version: '0.1.0',
|
|
92
|
+
description: `Zhin.js ${capitalizedName} 插件`,
|
|
93
|
+
type: 'module',
|
|
94
|
+
main: './lib/index.js',
|
|
95
|
+
types: './lib/index.d.ts',
|
|
96
|
+
exports: {
|
|
97
|
+
'.': {
|
|
98
|
+
types: './lib/index.d.ts',
|
|
99
|
+
import: './lib/index.js'
|
|
100
|
+
},
|
|
101
|
+
'./client': {
|
|
102
|
+
import: './dist/index.js'
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
files: [
|
|
106
|
+
'lib',
|
|
107
|
+
'app',
|
|
108
|
+
'dist',
|
|
109
|
+
'client',
|
|
110
|
+
'README.md',
|
|
111
|
+
'CHANGELOG.md'
|
|
112
|
+
],
|
|
113
|
+
scripts: {
|
|
114
|
+
build: 'pnpm build:app && pnpm build:client',
|
|
115
|
+
'build:app': 'tsc --project tsconfig.app.json',
|
|
116
|
+
'build:client': 'tsc --project tsconfig.client.json',
|
|
117
|
+
dev: 'tsc --project tsconfig.app.json --watch',
|
|
118
|
+
clean: 'rm -rf lib dist',
|
|
119
|
+
prepublishOnly: 'pnpm build'
|
|
120
|
+
},
|
|
121
|
+
keywords: [
|
|
122
|
+
'zhin',
|
|
123
|
+
'zhin-plugin',
|
|
124
|
+
pluginName
|
|
125
|
+
],
|
|
126
|
+
author: '',
|
|
127
|
+
license: 'MIT',
|
|
128
|
+
peerDependencies: {
|
|
129
|
+
'zhin.js': 'workspace:*'
|
|
130
|
+
},
|
|
131
|
+
dependencies: {
|
|
132
|
+
'@zhin.js/client': 'workspace:*'
|
|
133
|
+
},
|
|
134
|
+
devDependencies: {
|
|
135
|
+
'@zhin.js/types': 'workspace:*',
|
|
136
|
+
'@types/node': 'latest',
|
|
137
|
+
'@types/react': 'latest',
|
|
138
|
+
'typescript': 'latest',
|
|
139
|
+
'react': 'latest',
|
|
140
|
+
'react-dom': 'latest',
|
|
141
|
+
'lucide-react': 'latest'
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await fs.writeJson(path.join(pluginDir, 'package.json'), packageJson, { spaces: 2 });
|
|
146
|
+
|
|
147
|
+
// 创建 tsconfig.app.json (插件代码)
|
|
148
|
+
const tsConfigApp = {
|
|
149
|
+
extends: '../../tsconfig.json',
|
|
150
|
+
compilerOptions: {
|
|
151
|
+
rootDir: './app',
|
|
152
|
+
outDir: './lib',
|
|
153
|
+
declaration: true,
|
|
154
|
+
noEmit: false
|
|
155
|
+
},
|
|
156
|
+
include: ['app/**/*'],
|
|
157
|
+
exclude: ['node_modules', 'lib', 'dist']
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
await fs.writeJson(path.join(pluginDir, 'tsconfig.app.json'), tsConfigApp, { spaces: 2 });
|
|
161
|
+
|
|
162
|
+
// 创建 tsconfig.client.json (客户端代码)
|
|
163
|
+
const tsConfigClient = {
|
|
164
|
+
compilerOptions: {
|
|
165
|
+
target: 'ES2022',
|
|
166
|
+
module: 'ESNext',
|
|
167
|
+
moduleResolution: 'bundler',
|
|
168
|
+
rootDir: './client',
|
|
169
|
+
outDir: './dist',
|
|
170
|
+
declaration: false,
|
|
171
|
+
jsx: 'react-jsx',
|
|
172
|
+
baseUrl: '.',
|
|
173
|
+
skipLibCheck: true,
|
|
174
|
+
esModuleInterop: true,
|
|
175
|
+
allowSyntheticDefaultImports: true
|
|
176
|
+
},
|
|
177
|
+
include: ['client/**/*'],
|
|
178
|
+
exclude: ['node_modules', 'lib', 'dist']
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
await fs.writeJson(path.join(pluginDir, 'tsconfig.client.json'), tsConfigClient, { spaces: 2 });
|
|
182
|
+
|
|
183
|
+
// 创建 tsconfig.json (编辑器支持)
|
|
184
|
+
const tsConfig = {
|
|
185
|
+
extends: '../../tsconfig.json',
|
|
186
|
+
include: ['app/**/*', 'client/**/*'],
|
|
187
|
+
exclude: ['node_modules', 'lib', 'dist']
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
await fs.writeJson(path.join(pluginDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
|
|
191
|
+
|
|
192
|
+
// 创建插件主入口文件 app/index.ts
|
|
193
|
+
const appContent = `import {
|
|
194
|
+
useLogger,
|
|
195
|
+
useContext,
|
|
196
|
+
onDispose,
|
|
197
|
+
} from 'zhin.js';
|
|
198
|
+
import path from 'node:path';
|
|
199
|
+
|
|
200
|
+
const logger = useLogger();
|
|
201
|
+
|
|
202
|
+
// 注册客户端入口(如果有客户端代码)
|
|
203
|
+
useContext('web', (web) => {
|
|
204
|
+
const dispose = web.addEntry(
|
|
205
|
+
path.resolve(import.meta.dirname, '../client/index.tsx')
|
|
206
|
+
);
|
|
207
|
+
return dispose;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// 插件销毁时的清理
|
|
211
|
+
onDispose(() => {
|
|
212
|
+
logger.info('${capitalizedName} 插件已销毁');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
logger.info('${capitalizedName} 插件已加载');
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
await fs.writeFile(path.join(pluginDir, 'app', 'index.ts'), appContent);
|
|
219
|
+
|
|
220
|
+
// 创建客户端入口文件 client/index.tsx
|
|
221
|
+
const clientContent = `import { addPage } from '@zhin.js/client';
|
|
222
|
+
import { Component } from 'lucide-react';
|
|
223
|
+
import ${capitalizedName}Page from './pages/${capitalizedName}Page';
|
|
224
|
+
|
|
225
|
+
addPage({
|
|
226
|
+
key: '${pluginName}-page',
|
|
227
|
+
path: '/plugins/${pluginName}',
|
|
228
|
+
title: '${capitalizedName}',
|
|
229
|
+
icon: <Component className="w-5 h-5" />,
|
|
230
|
+
element: <${capitalizedName}Page />
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
export { ${capitalizedName}Page };
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
await fs.writeFile(path.join(pluginDir, 'client', 'index.tsx'), clientContent);
|
|
237
|
+
|
|
238
|
+
// 创建 client/tsconfig.json
|
|
239
|
+
const clientTsConfig = {
|
|
240
|
+
extends: '@zhin.js/console/browser.tsconfig.json',
|
|
241
|
+
compilerOptions: {
|
|
242
|
+
target: 'ES2022',
|
|
243
|
+
module: 'ESNext',
|
|
244
|
+
moduleResolution: 'bundler',
|
|
245
|
+
jsx: 'react-jsx',
|
|
246
|
+
baseUrl: '.'
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
await fs.writeJson(path.join(pluginDir, 'client', 'tsconfig.json'), clientTsConfig, { spaces: 2 });
|
|
251
|
+
|
|
252
|
+
// 创建客户端页面组件
|
|
253
|
+
await fs.ensureDir(path.join(pluginDir, 'client', 'pages'));
|
|
254
|
+
const pageContent = `import { useEffect } from 'react';
|
|
255
|
+
|
|
256
|
+
export default function ${capitalizedName}Page() {
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
console.log('${capitalizedName} 页面已挂载');
|
|
260
|
+
}, []);
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<div className="p-6">
|
|
264
|
+
<h1 className="text-2xl font-bold mb-4">${capitalizedName}</h1>
|
|
265
|
+
</div>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
await fs.writeFile(path.join(pluginDir, 'client', 'pages', `${capitalizedName}Page.tsx`), pageContent);
|
|
271
|
+
|
|
272
|
+
// 创建 README.md
|
|
273
|
+
const readmeContent = `# ${packageName}
|
|
274
|
+
|
|
275
|
+
${capitalizedName} 插件 for Zhin.js
|
|
276
|
+
|
|
277
|
+
## 安装
|
|
278
|
+
|
|
279
|
+
\`\`\`bash
|
|
280
|
+
pnpm add ${packageName}
|
|
281
|
+
\`\`\`
|
|
282
|
+
|
|
283
|
+
## 使用
|
|
284
|
+
|
|
285
|
+
在 \`zhin.config.ts\` 中添加插件:
|
|
286
|
+
|
|
287
|
+
\`\`\`typescript
|
|
288
|
+
export default defineConfig({
|
|
289
|
+
plugins: [
|
|
290
|
+
'${pluginName}'
|
|
291
|
+
]
|
|
292
|
+
});
|
|
293
|
+
\`\`\`
|
|
294
|
+
|
|
295
|
+
## 开发
|
|
296
|
+
|
|
297
|
+
\`\`\`bash
|
|
298
|
+
# 安装依赖
|
|
299
|
+
pnpm install
|
|
300
|
+
|
|
301
|
+
# 构建
|
|
302
|
+
pnpm build
|
|
303
|
+
|
|
304
|
+
# 开发模式
|
|
305
|
+
pnpm dev
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
## 许可证
|
|
309
|
+
|
|
310
|
+
MIT
|
|
311
|
+
`;
|
|
312
|
+
|
|
313
|
+
await fs.writeFile(path.join(pluginDir, 'README.md'), readmeContent);
|
|
314
|
+
|
|
315
|
+
// 创建 CHANGELOG.md
|
|
316
|
+
const changelogContent = `# ${packageName}
|
|
317
|
+
|
|
318
|
+
## 0.1.0
|
|
319
|
+
|
|
320
|
+
### Features
|
|
321
|
+
|
|
322
|
+
- 初始版本
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
await fs.writeFile(path.join(pluginDir, 'CHANGELOG.md'), changelogContent);
|
|
326
|
+
|
|
327
|
+
// 创建 .gitignore
|
|
328
|
+
const gitignoreContent = `node_modules/
|
|
329
|
+
lib/
|
|
330
|
+
dist/
|
|
331
|
+
*.log
|
|
332
|
+
.DS_Store
|
|
333
|
+
`;
|
|
334
|
+
|
|
335
|
+
await fs.writeFile(path.join(pluginDir, '.gitignore'), gitignoreContent);
|
|
336
|
+
|
|
337
|
+
// 安装依赖
|
|
338
|
+
if (!options.skipInstall) {
|
|
339
|
+
logger.info('正在安装依赖...');
|
|
340
|
+
try {
|
|
341
|
+
execSync('pnpm install', {
|
|
342
|
+
cwd: pluginDir,
|
|
343
|
+
stdio: 'inherit'
|
|
344
|
+
});
|
|
345
|
+
logger.success('✓ 依赖安装成功');
|
|
346
|
+
} catch (error) {
|
|
347
|
+
logger.warn('⚠ 依赖安装失败,请手动执行 pnpm install');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function addPluginToApp(pluginName: string) {
|
|
353
|
+
try {
|
|
354
|
+
const rootPackageJsonPath = path.resolve(process.cwd(), 'package.json');
|
|
355
|
+
|
|
356
|
+
// 检查根 package.json 是否存在
|
|
357
|
+
if (!fs.existsSync(rootPackageJsonPath)) {
|
|
358
|
+
logger.warn('⚠ 未找到根目录 package.json,跳过依赖添加');
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const packageJson = await fs.readJson(rootPackageJsonPath);
|
|
363
|
+
const packageName = `@zhin.js/${pluginName}`;
|
|
364
|
+
|
|
365
|
+
// 初始化 dependencies
|
|
366
|
+
if (!packageJson.dependencies) {
|
|
367
|
+
packageJson.dependencies = {};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 添加 workspace 依赖
|
|
371
|
+
packageJson.dependencies[packageName] = 'workspace:*';
|
|
372
|
+
|
|
373
|
+
// 写回文件
|
|
374
|
+
await fs.writeJson(rootPackageJsonPath, packageJson, { spaces: 2 });
|
|
375
|
+
|
|
376
|
+
logger.success(`✓ 已将 ${packageName} 添加到 package.json`);
|
|
377
|
+
|
|
378
|
+
// 重新安装依赖
|
|
379
|
+
logger.info('正在更新依赖...');
|
|
380
|
+
try {
|
|
381
|
+
execSync('pnpm install', {
|
|
382
|
+
cwd: process.cwd(),
|
|
383
|
+
stdio: 'inherit'
|
|
384
|
+
});
|
|
385
|
+
logger.success('✓ 依赖更新成功');
|
|
386
|
+
} catch (error) {
|
|
387
|
+
logger.warn('⚠ 依赖更新失败,请手动执行 pnpm install');
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
logger.warn(`⚠ 添加到 package.json 失败: ${error}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
package/src/commands/start.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const startCommand = new Command('start')
|
|
|
10
10
|
.description('生产模式启动机器人')
|
|
11
11
|
.option('-d, --daemon', '后台运行', false)
|
|
12
12
|
.option('--log-file [file]', '日志文件路径')
|
|
13
|
-
.option('--bun', '使用 bun 运行(默认使用
|
|
13
|
+
.option('--bun', '使用 bun 运行(默认使用 tsx)', false)
|
|
14
14
|
.action(async (options: { daemon: boolean; logFile?: string; bun: boolean }) => {
|
|
15
15
|
try {
|
|
16
16
|
const cwd = process.cwd();
|
|
@@ -27,7 +27,7 @@ export const startCommand = new Command('start')
|
|
|
27
27
|
loadEnvFiles(cwd, 'production');
|
|
28
28
|
|
|
29
29
|
// 检查构建产物
|
|
30
|
-
const distPath = path.join(cwd, '
|
|
30
|
+
const distPath = path.join(cwd, 'lib');
|
|
31
31
|
const sourcePath = path.join(cwd, 'src');
|
|
32
32
|
const sourceFile = path.join(sourcePath, 'index.ts');
|
|
33
33
|
const distFile = path.join(distPath, 'index.js');
|
|
@@ -47,7 +47,6 @@ export const startCommand = new Command('start')
|
|
|
47
47
|
...process.env,
|
|
48
48
|
NODE_ENV: 'production'
|
|
49
49
|
};
|
|
50
|
-
|
|
51
50
|
// 配置stdio
|
|
52
51
|
let stdio: any = 'inherit';
|
|
53
52
|
if (options.daemon) {
|
|
@@ -57,11 +56,17 @@ export const startCommand = new Command('start')
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
// 选择运行时
|
|
60
|
-
const runtime = options.bun ? 'bun' : '
|
|
59
|
+
const runtime = options.bun ? 'bun' : 'tsx';
|
|
61
60
|
const args = options.bun ? [entryFile] : ['--expose-gc', entryFile];
|
|
62
61
|
|
|
63
62
|
logger.info(`📦 启动命令: ${runtime} ${args.join(' ')}`);
|
|
64
|
-
return startProcess(runtime, args,
|
|
63
|
+
return startProcess(runtime, args, {
|
|
64
|
+
cwd,
|
|
65
|
+
env,
|
|
66
|
+
stdio,
|
|
67
|
+
shell:true,
|
|
68
|
+
detached: options.daemon,
|
|
69
|
+
});
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
let child = await startBot();
|
package/src/utils/process.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn, ChildProcess, exec } from 'child_process';
|
|
1
|
+
import { spawn, ChildProcess, exec, SpawnOptions } from 'child_process';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { logger } from './logger.js';
|
|
@@ -60,21 +60,15 @@ async function killProcess(pid: number, signal: string = 'SIGTERM'): Promise<boo
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export async function startProcess(command: string, args: string[],
|
|
63
|
+
export async function startProcess(command: string, args: string[], options:SpawnOptions): Promise<ChildProcess> {
|
|
64
64
|
return new Promise((resolve, reject) => {
|
|
65
|
-
const child = spawn(command, args,
|
|
66
|
-
cwd,
|
|
67
|
-
detached: daemon,
|
|
68
|
-
stdio: 'inherit',
|
|
69
|
-
shell:true,
|
|
70
|
-
env: { ...process.env }
|
|
71
|
-
});
|
|
65
|
+
const child = spawn(command, args, options);
|
|
72
66
|
|
|
73
|
-
if(
|
|
67
|
+
if(options.detached) child.unref();
|
|
74
68
|
|
|
75
69
|
child.on('spawn', () => {
|
|
76
70
|
// 保存进程ID
|
|
77
|
-
const pidFile = path.join(cwd, PID_FILE);
|
|
71
|
+
const pidFile = path.join((options.cwd!).toString(), PID_FILE);
|
|
78
72
|
fs.writeFileSync(pidFile, child.pid!.toString());
|
|
79
73
|
logger.success(`机器人已启动,PID: ${child.pid}`);
|
|
80
74
|
resolve(child);
|
package/lib/commands/init.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,eAAO,MAAM,WAAW,SA0HpB,CAAC"}
|