@oinone/cli 7.2.4 → 7.2.5

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 (3) hide show
  1. package/bin/cli.js +47 -46
  2. package/bin/i18n.js +134 -0
  3. package/package.json +5 -2
package/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ const os = require('os');
11
11
  const AdmZip = require('adm-zip');
12
12
  const cliProgress = require('cli-progress');
13
13
  const packageJson = require('../package.json');
14
+ const { t } = require('./i18n');
14
15
 
15
16
  const downloadAndExtractZip = (url, targetDir) => {
16
17
  return new Promise((resolve, reject) => {
@@ -19,7 +20,7 @@ const downloadAndExtractZip = (url, targetDir) => {
19
20
  headRes.resume();
20
21
 
21
22
  if (headRes.statusCode !== 200) {
22
- reject(new Error(`Failed to fetch file info: ${headRes.statusCode}`));
23
+ reject(new Error(t('fetchFileInfoFailed', { status: headRes.statusCode })));
23
24
  return;
24
25
  }
25
26
 
@@ -36,7 +37,7 @@ const downloadAndExtractZip = (url, targetDir) => {
36
37
  const cachedZipPath = path.join(cacheDir, `static-resources-${etag}.zip`);
37
38
 
38
39
  if (fs.existsSync(cachedZipPath)) {
39
- console.log(chalk.cyan('⚡ Zap! Using cached static resources...'));
40
+ console.log(chalk.cyan(t('usingCached')));
40
41
  try {
41
42
  const zip = new AdmZip(cachedZipPath);
42
43
  zip.extractAllTo(targetDir, true);
@@ -53,7 +54,7 @@ const downloadAndExtractZip = (url, targetDir) => {
53
54
  https.get(url, (response) => {
54
55
  if (response.statusCode !== 200) {
55
56
  response.resume(); // 消费响应数据释放 socket
56
- reject(new Error(`Failed to download: ${response.statusCode}`));
57
+ reject(new Error(t('downloadFailed', { status: response.statusCode })));
57
58
  return;
58
59
  }
59
60
 
@@ -74,7 +75,7 @@ const downloadAndExtractZip = (url, targetDir) => {
74
75
  let startTime = Date.now();
75
76
 
76
77
  const progressBar = new cliProgress.SingleBar({
77
- format: chalk.cyan('🚀 Fetching awesomeness ') + `[{bar}] {percentage}% | ⏳ {eta}s left | 📦 {value}/{total} ${unit} | 💨 {speed} {speedUnit}/s`,
78
+ format: chalk.cyan(t('fetchingAwesomeness')) + `[{bar}] {percentage}% | ⏳ {eta}s ${t('progressLeft')} | 📦 {value}/{total} ${unit} | 💨 {speed} {speedUnit}/s`,
78
79
  barCompleteChar: '\u2588',
79
80
  barIncompleteChar: '\u2591',
80
81
  hideCursor: true,
@@ -139,14 +140,14 @@ const downloadAndExtractZip = (url, targetDir) => {
139
140
  };
140
141
 
141
142
  program
142
- .version(packageJson.version)
143
- .description('Oinone Frontend Template CLI')
144
- .helpOption('-h, --help', 'display help for command');
143
+ .version(packageJson.version, '-V, --version', t('versionOption'))
144
+ .description(t('description'))
145
+ .helpOption('-h, --help', t('helpOption'));
145
146
 
146
147
  // Cache Command
147
148
  const cacheCmd = program
148
149
  .command('cache')
149
- .description('Manage the local static resources cache')
150
+ .description(t('cacheCmdDesc'))
150
151
  .action(() => {
151
152
  // If just "cache" is typed without subcommands, show cache help
152
153
  cacheCmd.help();
@@ -154,29 +155,29 @@ const cacheCmd = program
154
155
 
155
156
  cacheCmd
156
157
  .command('clean')
157
- .description('Clean the local static resources cache')
158
+ .description(t('cacheCleanDesc'))
158
159
  .action(() => {
159
160
  const cacheDir = path.join(os.tmpdir(), 'oinone-cli-cache');
160
161
  if (fs.existsSync(cacheDir)) {
161
162
  fs.removeSync(cacheDir);
162
- console.log(chalk.green(`✨ Whoosh! Cache swept clean at ${cacheDir}`));
163
+ console.log(chalk.green(t('cacheCleaned', { dir: cacheDir })));
163
164
  } else {
164
- console.log(chalk.yellow(`🤷 Cache directory already vanished (or never existed): ${cacheDir}`));
165
+ console.log(chalk.yellow(t('cacheAlreadyEmpty', { dir: cacheDir })));
165
166
  }
166
167
  });
167
168
 
168
169
  cacheCmd
169
170
  .command('info')
170
- .description('Print cache directory information')
171
+ .description(t('cacheInfoDesc'))
171
172
  .action(async () => {
172
173
  const cacheDir = path.join(os.tmpdir(), 'oinone-cli-cache');
173
- console.log(chalk.cyan('📂 Cache Directory: ') + cacheDir);
174
+ console.log(chalk.cyan(t('cacheDir')) + cacheDir);
174
175
 
175
176
  if (fs.existsSync(cacheDir)) {
176
177
  const files = await fs.readdir(cacheDir);
177
178
  let totalSize = 0;
178
179
 
179
- console.log(chalk.cyan('📦 Cached Files:'));
180
+ console.log(chalk.cyan(t('cachedFiles')));
180
181
  for (const file of files) {
181
182
  const filePath = path.join(cacheDir, file);
182
183
  const stat = await fs.stat(filePath);
@@ -199,18 +200,18 @@ cacheCmd
199
200
  totalDisplaySize = `${Math.round(totalSize / 1024)} KB`;
200
201
  }
201
202
 
202
- console.log(chalk.cyan(`\n📊 Total Cache Size: `) + totalDisplaySize);
203
+ console.log(chalk.cyan(t('totalCacheSize')) + totalDisplaySize);
203
204
  } else {
204
- console.log(chalk.yellow('🍃 Cache is currently as empty as space.'));
205
+ console.log(chalk.yellow(t('cacheEmpty')));
205
206
  }
206
207
  });
207
208
 
208
209
  // Create Command (Default Behavior)
209
210
  program
210
- .argument('[project-name]', 'Name of the project to create silently')
211
- .option('--download-static-resource <value>', 'Download static resources (true/false)', 'true')
212
- .option('--oinone-version <value>', 'Oinone version to use (e.g. 7.2.0, 6.4.0)', '7.2.0')
213
- .option('--edition <value>', 'Oinone edition to use (community/enterprise)', 'community')
211
+ .argument('[project-name]', t('projectNameArg'))
212
+ .option('--download-static-resource <value>', t('downloadOpt'), 'true')
213
+ .option('--oinone-version <value>', t('versionOpt'), '7.2.0')
214
+ .option('--edition <value>', t('editionOpt'), 'community')
214
215
  .action(async (projectNameArg, options) => {
215
216
  // If command is 'cache', skip create logic
216
217
  if (projectNameArg === 'cache') {
@@ -266,47 +267,47 @@ program
266
267
  {
267
268
  type: 'input',
268
269
  name: 'companyName',
269
- message: '🏢 What is your awesome company/org name? (e.g. ss):',
270
+ message: t('promptCompany'),
270
271
  default: 'ss',
271
272
  validate: (input) => {
272
- if (input.trim() === '') return 'Oops! Company name cannot be empty 🙈';
273
+ if (input.trim() === '') return t('errorCompanyEmpty');
273
274
  return true;
274
275
  },
275
276
  },
276
277
  {
277
278
  type: 'input',
278
279
  name: 'projectName',
279
- message: '🚀 Name your epic project (e.g. oms):',
280
+ message: t('promptProject'),
280
281
  default: 'oms',
281
282
  validate: (input) => {
282
- if (input.trim() === '') return 'Hold on! Project name cannot be empty 🛑';
283
+ if (input.trim() === '') return t('errorProjectEmpty');
283
284
  return true;
284
285
  },
285
286
  },
286
287
  {
287
288
  type: 'list',
288
289
  name: 'edition',
289
- message: '💎 Choose your Oinone flavor:',
290
+ message: t('promptEdition'),
290
291
  choices: [
291
- { name: '🌱 Community Edition (Default)', value: 'community' },
292
- { name: '👑 Enterprise Edition', value: 'enterprise' }
292
+ { name: t('editionCommunity'), value: 'community' },
293
+ { name: t('editionEnterprise'), value: 'enterprise' }
293
294
  ],
294
295
  default: 'community'
295
296
  },
296
297
  {
297
298
  type: 'list',
298
299
  name: 'oinoneVersion',
299
- message: '📦 Which Oinone version are we rocking today?',
300
+ message: t('promptVersion'),
300
301
  choices: [
301
- { name: '✨ 7.2.0 (Default)', value: '~7.2.0' },
302
- { name: '🕰️ 6.4.0', value: '~6.4.0' }
302
+ { name: t('version720'), value: '~7.2.0' },
303
+ { name: t('version640'), value: '~6.4.0' }
303
304
  ],
304
305
  default: '~7.2.0'
305
306
  },
306
307
  {
307
308
  type: 'confirm',
308
309
  name: 'downloadStaticResources',
309
- message: '📥 Shall we fetch the latest static resources for you? (Highly recommended! 🌟)',
310
+ message: t('promptDownload'),
310
311
  default: true
311
312
  }
312
313
  ]);
@@ -321,17 +322,17 @@ program
321
322
  const targetPath = path.resolve(process.cwd(), fullProjectName);
322
323
 
323
324
  if (fs.existsSync(targetPath)) {
324
- console.log(chalk.red(`\n🚫 Yikes! The directory ${fullProjectName} already exists. Try a different name or clear the path.\n`));
325
+ console.log(chalk.red(t('errorDirExists', { name: fullProjectName })));
325
326
  process.exit(1);
326
327
  }
327
328
 
328
- const spinner = ora('✨ Weaving magic to create your project...').start();
329
+ const spinner = ora(t('weavingMagic')).start();
329
330
 
330
331
  const versionDir = oinoneVersion.replace(/[^0-9.]/g, ''); // Extract '7.2.0' or '6.4.0'
331
332
  const templatePath = path.resolve(__dirname, `../template-${versionDir}-${edition}`);
332
333
 
333
334
  if (!fs.existsSync(templatePath)) {
334
- spinner.fail(chalk.red(`😢 Uh-oh! Template for version ${oinoneVersion} (${edition} edition) went missing in action.`));
335
+ spinner.fail(chalk.red(t('errorTemplateMissing', { version: oinoneVersion, edition })));
335
336
  process.exit(1);
336
337
  }
337
338
 
@@ -422,32 +423,32 @@ program
422
423
  }
423
424
 
424
425
  if (downloadStaticResources) {
425
- spinner.succeed(chalk.green('🎉 Boom! Project structure is ready!'));
426
- console.log(chalk.cyan('🚚 Hauling in the static resources...'));
426
+ spinner.succeed(chalk.green(t('structureReady')));
427
+ console.log(chalk.cyan(t('haulingStatic')));
427
428
  try {
428
429
  const ossDir = path.join(targetPath, 'packages', `${prefix}-boot`, 'public', 'oss');
429
430
  await fs.ensureDir(ossDir);
430
431
  await downloadAndExtractZip('https://pamirs.oss-cn-hangzhou.aliyuncs.com/oinone/installer/static-resources.zip', ossDir);
431
- console.log(chalk.green('🎯 Bullseye! Static resources downloaded and ready to roll!'));
432
+ console.log(chalk.green(t('staticDownloaded')));
432
433
  } catch (err) {
433
- console.log(chalk.yellow(`⚠️ Project is ready, but the static resources had a hiccup: ${err.message}`));
434
+ console.log(chalk.yellow(t('staticHiccup', { msg: err.message })));
434
435
  }
435
436
  } else {
436
- spinner.succeed(chalk.green('🎉 Boom! Project created successfully!'));
437
+ spinner.succeed(chalk.green(t('projectCreated')));
437
438
  const relativeOssDir = `packages/${prefix}-boot/public/oss`;
438
- console.log(chalk.yellow(`\n⚠️ You chose to skip the static resources download.`));
439
- console.log(chalk.yellow(`Don't forget to grab them manually before you fire up the project:`));
439
+ console.log(chalk.yellow(t('skipStaticWarning')));
440
+ console.log(chalk.yellow(t('manualStaticInstructions')));
440
441
  console.log(chalk.cyan(` mkdir -p ${fullProjectName}/${relativeOssDir}`));
441
442
  console.log(chalk.cyan(` curl -L https://pamirs.oss-cn-hangzhou.aliyuncs.com/oinone/installer/static-resources.zip -o static-resources.zip`));
442
443
  console.log(chalk.cyan(` unzip static-resources.zip -d ${fullProjectName}/${relativeOssDir}`));
443
444
  console.log(chalk.cyan(` rm static-resources.zip`));
444
445
  }
445
446
 
446
- console.log(`\n🚀 Let's get this party started:\n`);
447
+ console.log(t('partyStarted'));
447
448
  console.log(chalk.cyan(` cd ${fullProjectName}`));
448
449
  console.log(chalk.cyan(` pnpm install`));
449
450
  console.log(chalk.cyan(` pnpm run dev\n`));
450
- console.log(chalk.magenta(`Happy coding! 💻☕\n`));
451
+ console.log(chalk.magenta(t('happyCoding')));
451
452
 
452
453
  try {
453
454
  const latestVersion = await Promise.race([
@@ -473,8 +474,8 @@ program
473
474
 
474
475
  if (isNewerVersion(packageJson.version, latestVersion)) {
475
476
  console.log(chalk.yellow(`====================================================`));
476
- console.log(chalk.yellow(`📦 Update available! ${chalk.red(packageJson.version)} ${chalk.green(latestVersion)}`));
477
- console.log(chalk.yellow(`Run ${chalk.cyan('npm install -g @oinone/cli')} to update.`));
477
+ console.log(chalk.yellow(t('updateAvailable', { current: chalk.red(packageJson.version), latest: chalk.green(latestVersion) })));
478
+ console.log(chalk.yellow(t('runUpdate', { cmd: chalk.cyan('npm install -g @oinone/cli') })));
478
479
  console.log(chalk.yellow(`====================================================\n`));
479
480
  }
480
481
  }
@@ -483,7 +484,7 @@ program
483
484
  }
484
485
 
485
486
  } catch (error) {
486
- console.error(chalk.red(`\n💥 Oops! Something broke: ${error.message}\n`));
487
+ console.error(chalk.red(t('somethingBroke', { msg: error.message })));
487
488
  process.exit(1);
488
489
  }
489
490
  });
package/bin/i18n.js ADDED
@@ -0,0 +1,134 @@
1
+ const detectLanguage = () => {
2
+ try {
3
+ const locale = new Intl.DateTimeFormat().resolvedOptions().locale;
4
+ if (locale.toLowerCase().startsWith('zh')) {
5
+ return 'zh-CN';
6
+ }
7
+ } catch (e) {
8
+ // Ignore error
9
+ }
10
+
11
+ // Also check environment variables for language
12
+ const envLang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || '';
13
+ if (envLang.toLowerCase().startsWith('zh')) {
14
+ return 'zh-CN';
15
+ }
16
+
17
+ return 'en-US';
18
+ };
19
+
20
+ const currentLang = detectLanguage();
21
+
22
+ const locales = {
23
+ 'en-US': {
24
+ usingCached: '⚡ Zap! Using cached static resources...',
25
+ fetchFileInfoFailed: 'Failed to fetch file info: {status}',
26
+ downloadFailed: 'Failed to download: {status}',
27
+ fetchingAwesomeness: '🚀 Fetching awesomeness ',
28
+ progressLeft: 'left',
29
+ cacheCleaned: '✨ Whoosh! Cache swept clean at {dir}',
30
+ cacheAlreadyEmpty: '🤷 Cache directory already vanished (or never existed): {dir}',
31
+ cacheDir: '📂 Cache Directory: ',
32
+ cachedFiles: '📦 Cached Files:',
33
+ totalCacheSize: '\n📊 Total Cache Size: ',
34
+ cacheEmpty: '🍃 Cache is currently as empty as space.',
35
+ description: 'Oinone Frontend Template CLI',
36
+ cacheCmdDesc: 'Manage the local static resources cache',
37
+ cacheCleanDesc: 'Clean the local static resources cache',
38
+ cacheInfoDesc: 'Print cache directory information',
39
+ versionOption: 'output the version number',
40
+ helpOption: 'display help for command',
41
+ projectNameArg: 'Name of the project to create silently',
42
+ downloadOpt: 'Download static resources (true/false)',
43
+ versionOpt: 'Oinone version to use (e.g. 7.2.0, 6.4.0)',
44
+ editionOpt: 'Oinone edition to use (community/enterprise)',
45
+ promptCompany: '🏢 What is your awesome company/org name? (e.g. ss):',
46
+ errorCompanyEmpty: 'Oops! Company name cannot be empty 🙈',
47
+ promptProject: '🚀 Name your epic project (e.g. oms):',
48
+ errorProjectEmpty: 'Hold on! Project name cannot be empty 🛑',
49
+ promptEdition: '💎 Choose your Oinone flavor:',
50
+ editionCommunity: '🌱 Community Edition (Default)',
51
+ editionEnterprise: '👑 Enterprise Edition',
52
+ promptVersion: '📦 Which Oinone version are we rocking today?',
53
+ version720: '✨ 7.2.0 (Default)',
54
+ version640: '🕰️ 6.4.0',
55
+ promptDownload: '📥 Shall we fetch the latest static resources for you? (Highly recommended! 🌟)',
56
+ errorDirExists: '\n🚫 Yikes! The directory {name} already exists. Try a different name or clear the path.\n',
57
+ weavingMagic: '✨ Weaving magic to create your project...',
58
+ errorTemplateMissing: '😢 Uh-oh! Template for version {version} ({edition} edition) went missing in action.',
59
+ structureReady: '🎉 Boom! Project structure is ready!',
60
+ haulingStatic: '🚚 Hauling in the static resources...',
61
+ staticDownloaded: '🎯 Bullseye! Static resources downloaded and ready to roll!',
62
+ staticHiccup: '⚠️ Project is ready, but the static resources had a hiccup: {msg}',
63
+ projectCreated: '🎉 Boom! Project created successfully!',
64
+ skipStaticWarning: '\n⚠️ You chose to skip the static resources download.',
65
+ manualStaticInstructions: 'Don\'t forget to grab them manually before you fire up the project:',
66
+ partyStarted: '\n🚀 Let\'s get this party started:\n',
67
+ happyCoding: 'Happy coding! 💻☕\n',
68
+ updateAvailable: '📦 Update available! {current} → {latest}',
69
+ runUpdate: 'Run {cmd} to update.',
70
+ somethingBroke: '\n💥 Oops! Something broke: {msg}\n'
71
+ },
72
+ 'zh-CN': {
73
+ usingCached: '⚡ 嗖!正在使用本地缓存的静态资源...',
74
+ fetchFileInfoFailed: '获取文件信息失败: {status}',
75
+ downloadFailed: '下载失败: {status}',
76
+ fetchingAwesomeness: '🚀 正在获取静态资源 ',
77
+ progressLeft: '剩余',
78
+ cacheCleaned: '✨ 呼!缓存清理完毕,位置:{dir}',
79
+ cacheAlreadyEmpty: '🤷 缓存目录已消失(或从未存在):{dir}',
80
+ cacheDir: '📂 缓存目录: ',
81
+ cachedFiles: '📦 缓存文件:',
82
+ totalCacheSize: '\n📊 总缓存大小: ',
83
+ cacheEmpty: '🍃 当前缓存空空如也。',
84
+ description: 'Oinone 前端脚手架',
85
+ cacheCmdDesc: '管理本地静态资源缓存',
86
+ cacheCleanDesc: '清理本地静态资源缓存',
87
+ cacheInfoDesc: '打印缓存目录信息',
88
+ versionOption: '输出版本号',
89
+ helpOption: '显示命令帮助',
90
+ projectNameArg: '静默创建时的项目名称',
91
+ downloadOpt: '是否下载静态资源 (true/false)',
92
+ versionOpt: '使用的 Oinone 版本 (例如 7.2.0, 6.4.0)',
93
+ editionOpt: '使用的 Oinone 版本类型 (community/enterprise)',
94
+ promptCompany: '🏢 您的公司/组织名称是?(例如 ss):',
95
+ errorCompanyEmpty: '哎呀!公司名称不能为空 🙈',
96
+ promptProject: '🚀 给您的项目起个名字吧 (例如 oms):',
97
+ errorProjectEmpty: '稍等!项目名称不能为空 🛑',
98
+ promptEdition: '💎 请选择 Oinone 版本类型:',
99
+ editionCommunity: '🌱 社区版 (默认)',
100
+ editionEnterprise: '👑 企业版',
101
+ promptVersion: '📦 今天我们要使用哪个 Oinone 版本?',
102
+ version720: '✨ 7.2.0 (默认)',
103
+ version640: '🕰️ 6.4.0',
104
+ promptDownload: '📥 是否为您获取最新的静态资源?(强烈推荐!🌟)',
105
+ errorDirExists: '\n🚫 哎呀!目录 {name} 已存在。请尝试其他名称或清理该路径。\n',
106
+ weavingMagic: '✨ 正在施展魔法为您创建项目...',
107
+ errorTemplateMissing: '😢 糟糕!未找到 {version} 版本 ({edition}版) 的模板。',
108
+ structureReady: '🎉 棒!项目结构已准备就绪!',
109
+ haulingStatic: '🚚 正在搬运静态资源...',
110
+ staticDownloaded: '🎯 太准了!静态资源下载完毕,随时可以起飞!',
111
+ staticHiccup: '⚠️ 项目已就绪,但静态资源遇到点小麻烦: {msg}',
112
+ projectCreated: '🎉 棒!项目创建成功!',
113
+ skipStaticWarning: '\n⚠️ 您选择了跳过静态资源下载。',
114
+ manualStaticInstructions: '启动项目前,别忘了手动获取它们:',
115
+ partyStarted: '\n🚀 让我们开始狂欢吧:\n',
116
+ happyCoding: '编码愉快!💻☕\n',
117
+ updateAvailable: '📦 发现新版本!{current} → {latest}',
118
+ runUpdate: '运行 {cmd} 进行更新。',
119
+ somethingBroke: '\n💥 糟糕!出了点问题: {msg}\n'
120
+ }
121
+ };
122
+
123
+ const t = (key, params = {}) => {
124
+ let str = locales[currentLang][key] || locales['en-US'][key] || key;
125
+ Object.keys(params).forEach((k) => {
126
+ str = str.replace(new RegExp(`{${k}}`, 'g'), params[k]);
127
+ });
128
+ return str;
129
+ };
130
+
131
+ module.exports = {
132
+ currentLang,
133
+ t
134
+ };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@oinone/cli",
3
- "version": "7.2.4",
3
+ "version": "7.2.5",
4
4
  "description": "CLI tool to generate Oinone Frontend Template",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "oinone-frontend": "./bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
10
+ "test": "vitest run"
11
11
  },
12
12
  "keywords": [
13
13
  "oinone",
@@ -25,5 +25,8 @@
25
25
  "fs-extra": "^11.3.4",
26
26
  "inquirer": "^8.2.6",
27
27
  "ora": "^5.4.1"
28
+ },
29
+ "devDependencies": {
30
+ "vitest": "^4.1.4"
28
31
  }
29
32
  }