gorig-cli 1.0.23 → 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.
Files changed (73) hide show
  1. package/README.md +112 -22
  2. package/commands/create.js +267 -112
  3. package/commands/doc.js +21 -8
  4. package/commands/init.js +316 -255
  5. package/commands/skill.js +55 -17
  6. package/package.json +1 -1
  7. package/templates/config.go.ejs +6 -5
  8. package/templates/config.yaml.ejs +3 -38
  9. package/templates/controller.go.ejs +11 -42
  10. package/templates/crud.controller.go.ejs +71 -0
  11. package/templates/crud.doc.md.ejs +60 -0
  12. package/templates/crud.dto.go.ejs +103 -0
  13. package/templates/crud.integration.init_test.go.ejs +17 -0
  14. package/templates/crud.integration_test.go.ejs +66 -0
  15. package/templates/crud.model.go.ejs +25 -0
  16. package/templates/crud.module.README.md.ejs +70 -0
  17. package/templates/crud.mongo.config.yaml.ejs +19 -0
  18. package/templates/crud.mysql.config.yaml.ejs +14 -0
  19. package/templates/crud.router.go.ejs +18 -0
  20. package/templates/crud.service.go.ejs +179 -0
  21. package/templates/crud.test.go.ejs +52 -0
  22. package/templates/dto.go.ejs +16 -0
  23. package/templates/gitignore.ejs +18 -49
  24. package/templates/hello.controller.go.ejs +19 -0
  25. package/templates/hello.router.go.ejs +12 -0
  26. package/templates/hello.service.go.ejs +31 -0
  27. package/templates/hello.test.go.ejs +34 -0
  28. package/templates/init.go.ejs +1 -27
  29. package/templates/main.go.ejs +2 -5
  30. package/templates/model.go.ejs +3 -4
  31. package/templates/project.README.md.ejs +35 -0
  32. package/templates/router.go.ejs +4 -8
  33. package/templates/service.go.ejs +25 -77
  34. package/templates/skills/gorig-backend/SKILL.md +89 -0
  35. package/templates/skills/gorig-backend/agents/openai.yaml +4 -0
  36. package/templates/skills/gorig-backend/assets/api-doc-template.md +59 -0
  37. package/templates/skills/gorig-backend/assets/gorig.gitignore +65 -0
  38. package/templates/skills/gorig-backend/assets/module-readme-template.md +58 -0
  39. package/templates/skills/gorig-backend/references/advanced-data-access.md +275 -0
  40. package/templates/skills/gorig-backend/references/auth-security.md +194 -0
  41. package/templates/skills/gorig-backend/references/business-scenarios.md +155 -0
  42. package/templates/skills/gorig-backend/references/cache.md +301 -0
  43. package/templates/skills/gorig-backend/references/capability-matrix.md +37 -0
  44. package/templates/skills/gorig-backend/references/configuration.md +48 -0
  45. package/templates/skills/gorig-backend/references/framework-api.md +190 -0
  46. package/templates/skills/gorig-backend/references/messaging.md +143 -0
  47. package/templates/skills/gorig-backend/references/onboarding-files.md +46 -0
  48. package/templates/skills/gorig-backend/references/outbound-http.md +162 -0
  49. package/templates/skills/gorig-backend/references/persistent-crud.md +332 -0
  50. package/templates/skills/gorig-backend/references/project-bootstrap.md +128 -0
  51. package/templates/skills/gorig-backend/references/scheduled-tasks.md +231 -0
  52. package/templates/skills/gorig-backend/references/service-lifecycle.md +51 -0
  53. package/templates/skills/gorig-backend/references/source-map.md +43 -0
  54. package/templates/skills/gorig-backend/references/source-policy.md +58 -0
  55. package/templates/skills/gorig-backend/references/sse.md +121 -0
  56. package/templates/skills/gorig-backend/references/testing.md +171 -0
  57. package/templates/skills/gorig-backend/scripts/check-source-links.sh +48 -0
  58. package/templates/skills/gorig-backend/scripts/detect-gorig-context.sh +67 -0
  59. package/templates/skills/gorig-backend/scripts/verify-basic-project.sh +108 -0
  60. package/npm_publish copy.sh +0 -65
  61. package/templates/internal.go.ejs +0 -49
  62. package/templates/req.go.ejs +0 -8
  63. package/templates/resp.go.ejs +0 -8
  64. package/templates/service.pub.go.ejs +0 -22
  65. package/templates/skills/claude/gorig-backend/SKILL.md +0 -189
  66. package/templates/skills/claude/gorig-backend/assets/api-doc-template.md +0 -50
  67. package/templates/skills/claude/gorig-backend/assets/module-readme-template.md +0 -46
  68. package/templates/skills/claude/gorig-backend/references/onboarding-files.md +0 -38
  69. package/templates/skills/codex/gorig-backend/SKILL.md +0 -189
  70. package/templates/skills/codex/gorig-backend/agents/openai.yaml +0 -4
  71. package/templates/skills/codex/gorig-backend/assets/api-doc-template.md +0 -50
  72. package/templates/skills/codex/gorig-backend/assets/module-readme-template.md +0 -46
  73. package/templates/skills/codex/gorig-backend/references/onboarding-files.md +0 -38
package/commands/init.js CHANGED
@@ -2,292 +2,353 @@ import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import ejs from 'ejs';
5
- import { exec, spawn } from 'child_process';
5
+ import { execFile, spawn } from 'child_process';
6
+ import { promisify } from 'util';
6
7
  import { fileURLToPath } from 'url';
7
8
  import readline from 'readline';
8
9
 
9
- // 获取 __dirname 的替代方案
10
+ const execFileAsync = promisify(execFile);
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
 
13
- // 检查系统是否安装了 Go
14
- const checkGoEnvironment = () => {
15
- return new Promise((resolve, reject) => {
16
- exec('go version', (error, stdout, stderr) => {
17
- if (error) {
18
- reject(new Error('Go environment not detected, please install Go language environment first, visit https://golang.org/dl/ for installation.'));
19
- } else {
20
- resolve(stdout);
21
- }
22
- });
23
- });
14
+ const DEFAULT_GORIG_VERSION = 'latest';
15
+ const DEFAULT_PORT = 9527;
16
+ const ENVIRONMENTS = ['local', 'dev', 'prod'];
17
+
18
+ const printUsage = () => {
19
+ console.log(chalk.yellow(
20
+ 'Usage: gorig-cli init <project-name> [--module <go-module>] [--gorig-version <version>] ' +
21
+ '[--gorig-replace <path>] [--port <port>] [--force] [--start|--no-start] [--no-git]',
22
+ ));
24
23
  };
25
24
 
26
- // 添加 gorig 依赖到项目中
27
- const addGorigDependency = (projectDir) => {
28
- return new Promise((resolve, reject) => {
29
- exec('go get github.com/jom-io/gorig@latest', { cwd: projectDir }, (error, stdout, stderr) => {
30
- if (error) {
31
- reject(new Error(`Failed to add gorig dependency: ${stderr}`));
32
- } else {
33
- resolve(stdout);
34
- }
35
- });
36
- });
25
+ const readOptionValue = (args, index, name) => {
26
+ const arg = args[index];
27
+ const prefix = `${name}=`;
28
+ if (arg.startsWith(prefix)) {
29
+ return { value: arg.slice(prefix.length), nextIndex: index };
30
+ }
31
+ if (index + 1 >= args.length || args[index + 1].startsWith('--')) {
32
+ throw new Error(`Missing value for ${name}`);
33
+ }
34
+ return { value: args[index + 1], nextIndex: index + 1 };
37
35
  };
38
36
 
39
- // 执行 go mod tidy
40
- const runGoModTidy = (projectDir) => {
41
- return new Promise((resolve, reject) => {
42
- exec('go mod tidy', { cwd: projectDir }, (error, stdout, stderr) => {
43
- if (error) {
44
- reject(new Error(`Failed to run go mod tidy: ${stderr}`));
45
- } else {
46
- resolve(stdout);
47
- }
48
- });
49
- });
37
+ export const parseInitArgs = (args) => {
38
+ if (args.length === 0 || args[0].startsWith('--')) {
39
+ throw new Error('Project name is required');
40
+ }
41
+
42
+ const projectName = args[0];
43
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(projectName)) {
44
+ throw new Error('Project name may contain only letters, digits, dot, underscore, and hyphen');
45
+ }
46
+
47
+ const options = {
48
+ projectName,
49
+ moduleName: projectName,
50
+ gorigVersion: DEFAULT_GORIG_VERSION,
51
+ gorigReplace: '',
52
+ port: DEFAULT_PORT,
53
+ force: false,
54
+ start: false,
55
+ git: true,
56
+ };
57
+
58
+ let startFlag = '';
59
+
60
+ for (let i = 1; i < args.length; i += 1) {
61
+ const arg = args[i];
62
+ if (arg === '--force') {
63
+ options.force = true;
64
+ continue;
65
+ }
66
+ if (arg === '--start') {
67
+ options.start = true;
68
+ startFlag = startFlag && startFlag !== arg ? 'conflict' : arg;
69
+ continue;
70
+ }
71
+ if (arg === '--no-start') {
72
+ options.start = false;
73
+ startFlag = startFlag && startFlag !== arg ? 'conflict' : arg;
74
+ continue;
75
+ }
76
+ if (arg === '--no-git') {
77
+ options.git = false;
78
+ continue;
79
+ }
80
+
81
+ const valueOptions = ['--module', '--gorig-version', '--gorig-replace', '--port'];
82
+ const optionName = valueOptions.find((name) => arg === name || arg.startsWith(`${name}=`));
83
+ if (!optionName) {
84
+ throw new Error(`Unknown init option: ${arg}`);
85
+ }
86
+
87
+ const { value, nextIndex } = readOptionValue(args, i, optionName);
88
+ i = nextIndex;
89
+
90
+ if (optionName === '--module') {
91
+ options.moduleName = value;
92
+ } else if (optionName === '--gorig-version') {
93
+ options.gorigVersion = value;
94
+ } else if (optionName === '--gorig-replace') {
95
+ options.gorigReplace = value;
96
+ } else if (optionName === '--port') {
97
+ options.port = Number(value);
98
+ }
99
+ }
100
+
101
+ if (startFlag === 'conflict') {
102
+ throw new Error('Use only one of --start or --no-start');
103
+ }
104
+ if (!options.moduleName || /\s/.test(options.moduleName)) {
105
+ throw new Error('Go module name must be non-empty and contain no whitespace');
106
+ }
107
+ if (!options.gorigVersion || options.gorigVersion.startsWith('-')) {
108
+ throw new Error('Gorig version must be a version, commit, branch, or latest');
109
+ }
110
+ if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65533) {
111
+ throw new Error('Port must be an integer between 1 and 65533');
112
+ }
113
+
114
+ return options;
50
115
  };
51
116
 
52
- // 询问用户是否覆盖已有目录
53
- const askOverwriteConfirmation = (projectDir) => {
54
- return new Promise((resolve) => {
55
- const rl = readline.createInterface({
56
- input: process.stdin,
57
- output: process.stdout
117
+ const run = async (command, args, cwd) => {
118
+ try {
119
+ return await execFileAsync(command, args, {
120
+ cwd,
121
+ env: process.env,
122
+ maxBuffer: 10 * 1024 * 1024,
58
123
  });
59
- rl.question(
60
- chalk.yellow(`Directory ${chalk.bold(projectDir)} already exists, do you want to overwrite? (y/N): `),
61
- (answer) => {
62
- rl.close();
63
- if (answer.toLowerCase() === 'y') {
64
- resolve(true);
65
- } else {
66
- resolve(false);
67
- }
68
- }
69
- );
70
- });
124
+ } catch (error) {
125
+ const detail = error.stderr?.trim() || error.stdout?.trim() || error.message;
126
+ throw new Error(`${command} ${args.join(' ')} failed: ${detail}`);
127
+ }
71
128
  };
72
129
 
73
- // 询问用户是否启动项目,超时5秒
74
- const askStartConfirmation = () => {
75
- return new Promise((resolve) => {
76
- const rl = readline.createInterface({
77
- input: process.stdin,
78
- output: process.stdout
79
- });
130
+ const getGoVersion = async () => {
131
+ const { stdout } = await run('go', ['version'], process.cwd());
132
+ const match = stdout.match(/go version go(\d+)\.(\d+)(?:\.(\d+))?/);
133
+ if (!match) {
134
+ throw new Error(`Unable to parse Go version: ${stdout.trim()}`);
135
+ }
136
+ return {
137
+ raw: stdout.trim(),
138
+ major: Number(match[1]),
139
+ minor: Number(match[2]),
140
+ };
141
+ };
80
142
 
81
- // 设置5秒超时
82
- const timeout = setTimeout(() => {
83
- rl.close();
84
- resolve(true);
85
- }, 6000);
143
+ const readGoDirective = async (goModPath) => {
144
+ const content = await fs.readFile(goModPath, 'utf8');
145
+ const match = content.match(/^go\s+(\d+)\.(\d+)(?:\.\d+)?$/m);
146
+ if (!match) {
147
+ return null;
148
+ }
149
+ return { major: Number(match[1]), minor: Number(match[2]), value: `${match[1]}.${match[2]}` };
150
+ };
86
151
 
87
- rl.question('\nDo you want to start the project now? (y/N): ', (answer) => {
88
- clearTimeout(timeout);
89
- rl.close();
90
- if (answer.toLowerCase() === 'y') {
91
- resolve(true);
92
- } else {
93
- resolve(false);
94
- }
95
- });
96
- });
152
+ const assertCompatibleGo = (installed, required) => {
153
+ if (!required) {
154
+ return;
155
+ }
156
+ if (installed.major < required.major ||
157
+ (installed.major === required.major && installed.minor < required.minor)) {
158
+ throw new Error(`Gorig requires Go ${required.value}+, but ${installed.raw} is installed`);
159
+ }
97
160
  };
98
161
 
99
- // 初始化项目的主函数
100
- const initModule = async (args) => {
101
- if (args.length < 1) {
102
- console.error(chalk.yellow('Please use the correct command format: npx <your package name> init <project name>'));
103
- process.exit(1);
162
+ const configureGorig = async (projectDir, invocationDir, options, installedGo) => {
163
+ if (options.gorigReplace) {
164
+ const source = path.resolve(invocationDir, options.gorigReplace);
165
+ const sourceGoMod = path.join(source, 'go.mod');
166
+ if (!await fs.pathExists(sourceGoMod)) {
167
+ throw new Error(`Gorig replacement does not contain go.mod: ${source}`);
168
+ }
169
+
170
+ const requiredGo = await readGoDirective(sourceGoMod);
171
+ assertCompatibleGo(installedGo, requiredGo);
172
+
173
+ const requiredVersion = options.gorigVersion === 'latest' ? 'v0.0.0' : options.gorigVersion;
174
+ await run('go', ['mod', 'edit', `-require=github.com/jom-io/gorig@${requiredVersion}`], projectDir);
175
+ await run('go', ['mod', 'edit', `-replace=github.com/jom-io/gorig=${source}`], projectDir);
176
+ } else {
177
+ await run('go', ['get', `github.com/jom-io/gorig@${options.gorigVersion}`], projectDir);
104
178
  }
105
179
 
106
- const projectName = args[0];
107
- const projectDir = path.join(process.cwd(), projectName);
108
- const projectNameUpper = projectName.toUpperCase();
109
- const projectPrefix = projectName.toLowerCase().replace(/-/g, '_');
180
+ const { stdout } = await run(
181
+ 'go',
182
+ ['list', '-m', '-json', 'github.com/jom-io/gorig'],
183
+ projectDir,
184
+ );
185
+ const moduleInfo = JSON.parse(stdout);
186
+ return {
187
+ version: moduleInfo.Version || options.gorigVersion,
188
+ replacement: moduleInfo.Replace?.Dir || '',
189
+ };
190
+ };
110
191
 
111
- // 定义需要创建的子目录
112
- const subDirs = ['_bin', '_cmd', 'domain', 'global', 'cron'];
192
+ const askOverwriteConfirmation = (projectDir) => new Promise((resolve) => {
193
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
194
+ rl.question(
195
+ chalk.yellow(`Directory ${chalk.bold(projectDir)} is not empty. Overwrite it? (y/N): `),
196
+ (answer) => {
197
+ rl.close();
198
+ resolve(answer.toLowerCase() === 'y');
199
+ },
200
+ );
201
+ });
113
202
 
114
- console.log(chalk.blue(`\nStarting project initialization: ${chalk.bold(projectName)}`));
203
+ const prepareProjectDirectory = async (projectDir, force) => {
204
+ if (!await fs.pathExists(projectDir)) {
205
+ await fs.ensureDir(projectDir);
206
+ return;
207
+ }
208
+
209
+ const entries = await fs.readdir(projectDir);
210
+ if (entries.length === 0) {
211
+ return;
212
+ }
213
+
214
+ let overwrite = force;
215
+ if (!overwrite && process.stdin.isTTY) {
216
+ overwrite = await askOverwriteConfirmation(projectDir);
217
+ }
218
+ if (!overwrite) {
219
+ throw new Error(`Directory is not empty: ${projectDir}. Use --force to replace it.`);
220
+ }
221
+
222
+ await fs.remove(projectDir);
223
+ await fs.ensureDir(projectDir);
224
+ };
225
+
226
+ const render = async (templateName, destination, data) => {
227
+ const templatePath = path.join(__dirname, '../templates', templateName);
228
+ const content = await ejs.renderFile(templatePath, data);
229
+ await fs.ensureDir(path.dirname(destination));
230
+ await fs.writeFile(destination, content);
231
+ };
115
232
 
233
+ const initializeGit = async (projectDir) => {
116
234
  try {
117
- // 检查本地是否存在 Go 环境
118
- await checkGoEnvironment();
119
- console.log(chalk.green('Go environment detected, continuing project initialization...'));
120
-
121
- // 检查项目目录是否已存在
122
- if (await fs.pathExists(projectDir)) {
123
- const shouldOverwrite = await askOverwriteConfirmation(projectDir);
124
- if (!shouldOverwrite) {
125
- console.log(chalk.red('Project initialization has been canceled.'));
126
- process.exit(0);
127
- } else {
128
- await fs.remove(projectDir); // 删除已有的目录
129
- console.log(chalk.yellow(`Deleted existing directory: ${chalk.bold(projectDir)}`));
130
- }
235
+ await run('git', ['--version'], projectDir);
236
+ await run('git', ['init'], projectDir);
237
+ console.log(chalk.green('Git repository initialized.'));
238
+ } catch (error) {
239
+ console.log(chalk.yellow(`Skipping Git initialization: ${error.message}`));
240
+ }
241
+ };
242
+
243
+ const startProject = async (projectDir, mode) => new Promise((resolve, reject) => {
244
+ console.log(chalk.blue(`Starting project with GORIG_SYS_MODE=${mode}...`));
245
+ const child = spawn('go', ['run', './_cmd'], {
246
+ cwd: projectDir,
247
+ env: { ...process.env, GORIG_SYS_MODE: mode },
248
+ stdio: 'inherit',
249
+ });
250
+ child.once('error', reject);
251
+ child.once('close', (code, signal) => {
252
+ if (code === 0 || signal === 'SIGINT') {
253
+ resolve();
254
+ return;
131
255
  }
256
+ reject(new Error(`Project exited with code ${code ?? 'unknown'}`));
257
+ });
258
+ });
259
+
260
+ export const initProject = async (options, runtime = {}) => {
261
+ const invocationDir = runtime.cwd || process.cwd();
262
+ const projectDir = path.join(invocationDir, options.projectName);
263
+ const projectNameUpper = options.projectName.toUpperCase().replace(/[^A-Z0-9]+/g, '_');
264
+ const projectPrefix = options.projectName.toLowerCase().replace(/[^a-z0-9]+/g, '_');
265
+ const installedGo = await getGoVersion();
266
+
267
+ console.log(chalk.blue(`Starting project initialization: ${chalk.bold(options.projectName)}`));
268
+ console.log(chalk.green(installedGo.raw));
269
+
270
+ await prepareProjectDirectory(projectDir, options.force);
271
+ await Promise.all([
272
+ '_bin',
273
+ '_cmd',
274
+ 'domain/hello',
275
+ 'global',
276
+ 'test/_bin',
277
+ ].map((dir) => fs.ensureDir(path.join(projectDir, dir))));
278
+
279
+ await run('go', ['mod', 'init', options.moduleName], projectDir);
280
+ const dependency = await configureGorig(projectDir, invocationDir, options, installedGo);
281
+
282
+ for (let index = 0; index < ENVIRONMENTS.length; index += 1) {
283
+ const mode = ENVIRONMENTS[index];
284
+ await render('config.yaml.ejs', path.join(projectDir, '_bin', `${mode}.yaml`), {
285
+ projectNameUpper,
286
+ projectPrefix,
287
+ mode,
288
+ port: options.port + index,
289
+ });
290
+ }
132
291
 
133
- // 创建项目目录
134
- await fs.ensureDir(projectDir);
292
+ const commonData = {
293
+ projectName: options.projectName,
294
+ projectNameUpper,
295
+ moduleName: options.moduleName,
296
+ localPort: options.port,
297
+ devPort: options.port + 1,
298
+ prodPort: options.port + 2,
299
+ gorigVersion: dependency.version,
300
+ };
301
+
302
+ await Promise.all([
303
+ render('main.go.ejs', path.join(projectDir, '_cmd/main.go'), commonData),
304
+ render('init.go.ejs', path.join(projectDir, 'domain/init.go'), commonData),
305
+ render('hello.router.go.ejs', path.join(projectDir, 'domain/hello/router.go'), commonData),
306
+ render('hello.controller.go.ejs', path.join(projectDir, 'domain/hello/controller.go'), commonData),
307
+ render('hello.service.go.ejs', path.join(projectDir, 'domain/hello/service.go'), commonData),
308
+ render('config.go.ejs', path.join(projectDir, 'global/config.go'), commonData),
309
+ render('hello.test.go.ejs', path.join(projectDir, 'test/hello_test.go'), commonData),
310
+ render('project.README.md.ejs', path.join(projectDir, 'README.md'), commonData),
311
+ render('gitignore.ejs', path.join(projectDir, '.gitignore'), commonData),
312
+ render('config.yaml.ejs', path.join(projectDir, 'test/_bin/local.yaml'), {
313
+ projectNameUpper,
314
+ projectPrefix,
315
+ mode: 'local',
316
+ port: options.port,
317
+ }),
318
+ ]);
319
+
320
+ await run('go', ['mod', 'tidy'], projectDir);
321
+ await run('go', ['fmt', './...'], projectDir);
322
+ if (options.git) {
323
+ await initializeGit(projectDir);
324
+ }
135
325
 
136
- // 创建子目录
137
- const createSubDirs = subDirs.map((subDir) => {
138
- const subDirPath = path.join(projectDir, subDir);
139
- return fs.ensureDir(subDirPath);
140
- });
141
- await Promise.all(createSubDirs);
142
-
143
- // 创建并写入 go.mod 文件
144
- const goModPath = path.join(projectDir, 'go.mod');
145
- const goModContent = `module ${projectName}\n\ngo 1.20\n`;
146
- await fs.writeFile(goModPath, goModContent);
147
- console.log(chalk.green(`Successfully created ${chalk.bold('go.mod')} file`));
148
-
149
- // 添加 gorig 依赖
150
- console.log(chalk.blue('Adding gorig dependency, please wait...'));
151
- await addGorigDependency(projectDir);
152
- console.log(chalk.green(`Successfully added the latest version of github.com/jom-io/gorig dependency`));
153
-
154
- // 创建 _bin 目录下的配置文件
155
- const binDir = path.join(projectDir, '_bin');
156
- const configTemplatePath = path.join(__dirname, '../templates/config.yaml.ejs');
157
-
158
- const environments = ['dev', 'local', 'prod'];
159
- for (const env of environments) {
160
- const configFilePath = path.join(binDir, `${env}.yaml`);
161
- const configContent = await ejs.renderFile(configTemplatePath, {
162
- projectNameUpper,
163
- projectPrefix,
164
- mode: env,
165
- });
166
- await fs.writeFile(configFilePath, configContent);
167
- console.log(chalk.green(`Successfully created ${chalk.bold(`${env}.yaml`)} file`));
168
- }
326
+ console.log(chalk.green(`Project created at ${projectDir}`));
327
+ console.log(chalk.green(`Gorig version: ${dependency.version}`));
328
+ if (dependency.replacement) {
329
+ console.log(chalk.green(`Gorig replacement: ${dependency.replacement}`));
330
+ }
331
+ console.log(chalk.cyan(`Run: cd ${options.projectName} && GORIG_SYS_MODE=local go run ./_cmd`));
332
+ console.log(chalk.cyan(`Verify: curl 'http://127.0.0.1:${options.port}/ping'`));
333
+ console.log(chalk.cyan(`Verify: curl 'http://127.0.0.1:${options.port}/hello?name=Gorig'`));
169
334
 
170
- // 创建 domain/init.go 文件
171
- const domainDir = path.join(projectDir, 'domain');
172
- const initGoPath = path.join(domainDir, 'init.go');
173
- const initGoTemplatePath = path.join(__dirname, '../templates/init.go.ejs');
174
- const initGoContent = await ejs.renderFile(initGoTemplatePath, { projectNameUpper });
175
- await fs.writeFile(initGoPath, initGoContent);
176
- console.log(chalk.green(`Successfully created ${chalk.bold('init.go')} file`));
177
-
178
- // 创建 _cmd/main.go 文件
179
- const cmdDir = path.join(projectDir, '_cmd');
180
- const mainGoPath = path.join(cmdDir, 'main.go');
181
- const mainGoTemplatePath = path.join(__dirname, '../templates/main.go.ejs');
182
- const mainGoContent = await ejs.renderFile(mainGoTemplatePath, { projectName });
183
- await fs.writeFile(mainGoPath, mainGoContent);
184
- console.log(chalk.green(`Successfully created ${chalk.bold('main.go')} file`));
185
-
186
- // 创建 global/config.go 文件
187
- const globalDir = path.join(projectDir, 'global');
188
- const configGoPath = path.join(globalDir, 'config.go');
189
- const configGoTemplatePath = path.join(__dirname, '../templates/config.go.ejs');
190
- const configGoContent = await ejs.renderFile(configGoTemplatePath, { projectNameUpper });
191
- await fs.writeFile(configGoPath, configGoContent);
192
- console.log(chalk.green(`Successfully created ${chalk.bold('config.go')} file`));
193
-
194
- // 创建 cron/cron.go 文件
195
- const cronDir = path.join(projectDir, 'cron');
196
- const cronGoPath = path.join(cronDir, 'cron.go');
197
- const cronGoTemplatePath = path.join(__dirname, '../templates/cron.go.ejs');
198
- const cronGoContent = await ejs.renderFile(cronGoTemplatePath, {});
199
- await fs.writeFile(cronGoPath, cronGoContent);
200
- console.log(chalk.green(`Successfully created ${chalk.bold('cron.go')} file`));
201
-
202
- // 创建.gitignore 文件 从 templates 目录中复制 gitignore.ejs 文件
203
- const gitignoreTemplatePath = path.join(__dirname, '../templates/gitignore.ejs');
204
- const gitignorePath = path.join(projectDir, '.gitignore');
205
- await fs.copyFile(gitignoreTemplatePath, gitignorePath);
206
-
207
- // 检测如果本机存在git,则初始化git仓库
208
- try {
209
- exec('git', ['--version']);
210
- exec('git init', { cwd: projectDir }, (err) => {
211
- if (err) {
212
- console.error(chalk.red('Git initialization failed:'), err);
213
- } else {
214
- console.log(chalk.green('Git repository initialized.'));
215
- }
216
- });
217
- } catch {
218
- console.log(chalk.yellow('Git is not installed on this system. Skipping Git initialization.'));
219
- }
220
- // 运行 go mod tidy
221
- console.log(chalk.blue('Organizing Go module dependencies (go mod tidy), please wait...'));
222
- await runGoModTidy(projectDir);
223
- console.log(chalk.green('Successfully organized Go module dependencies'));
224
-
225
- // 提示用户项目创建成功
226
- console.log(chalk.blue(`\nProject ${chalk.bold(projectName)} has been successfully created in ${chalk.bold(projectDir)} directory`));
227
- console.log(chalk.yellow('\nHow to run the project:'));
228
- console.log(chalk.green(`1. Enter the project directory: cd ${projectName}`));
229
- console.log(chalk.green(`2. Use Go command to run the project:`));
230
- console.log(chalk.cyan(` go run _cmd/main.go`));
231
- console.log(chalk.green(`\nOr compile and run directly:`));
232
- console.log(chalk.cyan(` go build -o ${projectName} _cmd/main.go && ./${projectName}`));
233
-
234
- // 询问用户是否启动项目
235
- const shouldStart = await askStartConfirmation();
236
- if (shouldStart) {
237
- // 启动项目
238
- console.log(chalk.blue('\nStarting the project...'));
239
- try {
240
- const goProcess = spawn('go', ['run', '_cmd/main.go'], {
241
- cwd: projectDir,
242
- stdio: ['inherit', 'pipe', 'pipe'] // 继承 stdin 和 stdout,pipe stderr
243
- });
244
-
245
- goProcess.on('error', (err) => {
246
- console.error(chalk.red('Error starting project:'), chalk.redBright(err.message));
247
- });
248
-
249
- goProcess.on('close', (code) => {
250
- if (code !== 0) {
251
- console.error(chalk.red(`Project process exited with code ${code}`));
252
- }
253
- });
254
-
255
- // 检查 goProcess.stdout 是否存在
256
- if (goProcess.stdout) {
257
- goProcess.stdout.on('data', (data) => { // 监听 stdout
258
- const output = data.toString();
259
- process.stdout.write(output); // 将输出写入控制台
260
- if (output.includes('System startup successful')) {
261
- console.log(chalk.green(`\nVisit ${chalk.bold('http://localhost:9527')} to view the project`));
262
- }
263
- });
264
- } else {
265
- console.error(chalk.red('stdout is not available.'));
266
- }
267
-
268
- // 检查 goProcess.stderr 是否存在
269
- // if (goProcess.stderr) {
270
- // goProcess.stderr.on('data', (data) => { // 监听 stderr
271
- // const errorOutput = data.toString();
272
- // if (errorOutput.includes('System startup successful')) {
273
- // console.log(chalk.green(`\nVisit ${chalk.bold('http://localhost:9527')} to view the project`));
274
- // } else {
275
- // console.error(chalk.red(`Error: ${errorOutput}`));
276
- // }
277
- // });
278
- // } else {
279
- // console.error(chalk.red('stderr is not available.'));
280
- // }
281
-
282
- } catch (error) {
283
- console.error(chalk.red('Error starting project:'), chalk.redBright(error.message));
284
- }
285
- } else {
286
- console.log(chalk.blue('\nProject initialization completed without starting the project.'));
287
- }
335
+ if (options.start) {
336
+ await startProject(projectDir, 'local');
337
+ }
338
+
339
+ return { projectDir, dependency };
340
+ };
288
341
 
342
+ const initModule = async (args) => {
343
+ try {
344
+ const options = parseInitArgs(args);
345
+ await initProject(options);
346
+ return 0;
289
347
  } catch (error) {
290
- console.error(chalk.red('Error during project initialization:'), chalk.redBright(error.message));
348
+ console.error(chalk.red('Failed to initialize project:'), chalk.redBright(error.message));
349
+ printUsage();
350
+ process.exitCode = 1;
351
+ return 1;
291
352
  }
292
353
  };
293
354