@holic512/slothtool 1.0.2 → 1.0.3

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.
@@ -1,122 +1,320 @@
1
- const { execSync } = require('child_process');
1
+ const {execSync} = require('child_process');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
4
  const registry = require('./registry');
5
- const { extractPluginAlias, getPluginDir, ensureDir, getPluginsDir } = require('./utils');
5
+ const {extractPluginAlias, getPluginDir, ensureDir, getPluginsDir, getPluginConfigPath} = require('./utils');
6
+ const {t} = require('./i18n');
6
7
 
7
8
  /**
8
9
  * 安装插件
9
10
  * @param {string} packageName - npm 包名 (例如: @slothtool/plugin-loc 或 plugin-loc)
10
11
  */
11
12
  function installPlugin(packageName) {
12
- console.log(`Installing plugin: ${packageName}...`);
13
+ console.log(t('installing'), packageName + '...');
13
14
 
14
- // 提取插件别名
15
- const alias = extractPluginAlias(packageName);
15
+ // 提取插件别名
16
+ const alias = extractPluginAlias(packageName);
16
17
 
17
- // 检查是否已安装
18
- if (registry.hasPlugin(alias)) {
19
- console.log(`Plugin "${alias}" is already installed.`);
20
- console.log(`Run "slothtool uninstall ${alias}" first if you want to reinstall.`);
21
- return;
22
- }
18
+ // 检查是否已安装
19
+ if (registry.hasPlugin(alias)) {
20
+ console.log(t('alreadyInstalled', {alias}));
21
+ console.log(t('uninstallFirst', {alias}));
22
+ return;
23
+ }
23
24
 
24
- // 确保插件目录存在
25
- ensureDir(getPluginsDir());
25
+ // 确保插件目录存在
26
+ ensureDir(getPluginsDir());
26
27
 
27
- const pluginDir = getPluginDir(alias);
28
- ensureDir(pluginDir);
28
+ const pluginDir = getPluginDir(alias);
29
+ ensureDir(pluginDir);
29
30
 
30
- try {
31
- // 使用 npm install 安装插件到指定目录
32
- console.log(`Installing to: ${pluginDir}`);
33
- execSync(`npm install ${packageName} --prefix "${pluginDir}"`, {
34
- stdio: 'inherit',
35
- encoding: 'utf8'
36
- });
31
+ try {
32
+ // 使用 npm install 安装插件到指定目录
33
+ console.log(t('installingTo'), pluginDir);
34
+ execSync(`npm install ${packageName} --prefix "${pluginDir}"`, {
35
+ stdio: 'inherit',
36
+ encoding: 'utf8'
37
+ });
37
38
 
38
- // 读取插件的 package.json
39
- const pkgPath = path.join(pluginDir, 'node_modules', packageName, 'package.json');
39
+ // 读取插件的 package.json
40
+ const pkgPath = path.join(pluginDir, 'node_modules', packageName, 'package.json');
40
41
 
41
- if (!fs.existsSync(pkgPath)) {
42
- throw new Error(`Package.json not found at: ${pkgPath}`);
43
- }
42
+ if (!fs.existsSync(pkgPath)) {
43
+ throw new Error(`Package.json not found at: ${pkgPath}`);
44
+ }
44
45
 
45
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
46
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
46
47
 
47
- // 获取 bin 路径
48
- let binPath;
49
- if (typeof pkg.bin === 'string') {
50
- binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin);
51
- } else if (typeof pkg.bin === 'object') {
52
- // 如果 bin 是对象,取第一个值
53
- const binKey = Object.keys(pkg.bin)[0];
54
- binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin[binKey]);
55
- } else {
56
- throw new Error(`No bin field found in package.json for ${packageName}`);
48
+ // 获取 bin 路径
49
+ let binPath;
50
+ if (typeof pkg.bin === 'string') {
51
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin);
52
+ } else if (typeof pkg.bin === 'object') {
53
+ // 如果 bin 是对象,取第一个值
54
+ const binKey = Object.keys(pkg.bin)[0];
55
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin[binKey]);
56
+ } else {
57
+ throw new Error(`No bin field found in package.json for ${packageName}`);
58
+ }
59
+
60
+ // 验证 bin 文件存在
61
+ if (!fs.existsSync(binPath)) {
62
+ throw new Error(`Bin file not found at: ${binPath}`);
63
+ }
64
+
65
+ // 添加到注册表
66
+ registry.addPlugin(alias, {
67
+ name: packageName,
68
+ version: pkg.version,
69
+ binPath: binPath,
70
+ installedAt: new Date().toISOString()
71
+ });
72
+
73
+ console.log(t('installSuccess', {alias}));
74
+ console.log(t('installRun', {alias}));
75
+
76
+ } catch (error) {
77
+ console.error(t('installFailed', {packageName}), error.message);
78
+
79
+ // 清理失败的安装
80
+ if (fs.existsSync(pluginDir)) {
81
+ fs.rmSync(pluginDir, {recursive: true, force: true});
82
+ }
83
+
84
+ process.exit(1);
57
85
  }
86
+ }
58
87
 
59
- // 验证 bin 文件存在
60
- if (!fs.existsSync(binPath)) {
61
- throw new Error(`Bin file not found at: ${binPath}`);
88
+ /**
89
+ * 卸载插件
90
+ * @param {string} alias - 插件别名
91
+ */
92
+ function uninstallPlugin(alias) {
93
+ const plugin = registry.getPlugin(alias);
94
+
95
+ if (!plugin) {
96
+ console.error(t('notInstalled', {alias}));
97
+ process.exit(1);
62
98
  }
63
99
 
64
- // 添加到注册表
65
- registry.addPlugin(alias, {
66
- name: packageName,
67
- version: pkg.version,
68
- binPath: binPath,
69
- installedAt: new Date().toISOString()
70
- });
100
+ console.log(t('uninstalling'), alias + '...');
71
101
 
72
- console.log(`\n✓ Plugin "${alias}" installed successfully!`);
73
- console.log(` Run: slothtool ${alias} --help`);
102
+ // 显示将要删除的内容
103
+ const pluginDir = getPluginDir(alias);
104
+ const configPath = getPluginConfigPath(alias);
105
+ const hasConfig = fs.existsSync(configPath);
74
106
 
75
- } catch (error) {
76
- console.error(`\n✗ Failed to install plugin "${packageName}":`, error.message);
107
+ console.log(t('uninstallWillRemove'));
108
+ console.log(t('uninstallPluginDir', {dir: pluginDir}));
77
109
 
78
- // 清理失败的安装
79
- if (fs.existsSync(pluginDir)) {
80
- fs.rmSync(pluginDir, { recursive: true, force: true });
110
+ if (hasConfig) {
111
+ console.log(t('uninstallConfigFile', {file: configPath}));
112
+ } else {
113
+ console.log(t('uninstallNoConfig'));
81
114
  }
82
115
 
83
- process.exit(1);
84
- }
116
+ console.log(t('uninstallRegistryEntry'));
117
+ console.log('');
118
+
119
+ try {
120
+ // 删除插件目录
121
+ if (fs.existsSync(pluginDir)) {
122
+ fs.rmSync(pluginDir, {recursive: true, force: true});
123
+ }
124
+
125
+ // 删除插件配置文件(如果存在)
126
+ if (hasConfig) {
127
+ fs.rmSync(configPath, {force: true});
128
+ }
129
+
130
+ // 从注册表移除
131
+ registry.removePlugin(alias);
132
+
133
+ console.log(t('uninstallSuccess', {alias}));
134
+
135
+ } catch (error) {
136
+ console.error(t('uninstallFailed', {alias}), error.message);
137
+ process.exit(1);
138
+ }
85
139
  }
86
140
 
87
141
  /**
88
- * 卸载插件
142
+ * 更新插件
89
143
  * @param {string} alias - 插件别名
90
144
  */
91
- function uninstallPlugin(alias) {
92
- const plugin = registry.getPlugin(alias);
145
+ function updatePlugin(alias) {
146
+ const plugin = registry.getPlugin(alias);
93
147
 
94
- if (!plugin) {
95
- console.error(`Plugin "${alias}" is not installed.`);
96
- process.exit(1);
97
- }
148
+ if (!plugin) {
149
+ console.error(t('notInstalled', {alias}));
150
+ process.exit(1);
151
+ }
98
152
 
99
- console.log(`Uninstalling plugin: ${alias}...`);
153
+ console.log(t('updating'), `${alias} (${plugin.name})...`);
154
+ console.log(t('currentVersion'), plugin.version);
100
155
 
101
- try {
102
- // 删除插件目录
103
156
  const pluginDir = getPluginDir(alias);
104
- if (fs.existsSync(pluginDir)) {
105
- fs.rmSync(pluginDir, { recursive: true, force: true });
157
+ const packageName = plugin.name;
158
+
159
+ try {
160
+ // 使用 npm update 更新插件
161
+ console.log(t('checkingUpdates'));
162
+ execSync(`npm update ${packageName} --prefix "${pluginDir}"`, {
163
+ stdio: 'inherit',
164
+ encoding: 'utf8'
165
+ });
166
+
167
+ // 读取更新后的 package.json
168
+ const pkgPath = path.join(pluginDir, 'node_modules', packageName, 'package.json');
169
+
170
+ if (!fs.existsSync(pkgPath)) {
171
+ throw new Error(`Package.json not found at: ${pkgPath}`);
172
+ }
173
+
174
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
175
+
176
+ // 检查版本是否有变化
177
+ if (pkg.version === plugin.version) {
178
+ console.log(t('alreadyLatest', {alias, version: pkg.version}));
179
+ return;
180
+ }
181
+
182
+ // 获取 bin 路径
183
+ let binPath;
184
+ if (typeof pkg.bin === 'string') {
185
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin);
186
+ } else if (typeof pkg.bin === 'object') {
187
+ const binKey = Object.keys(pkg.bin)[0];
188
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin[binKey]);
189
+ } else {
190
+ throw new Error(`No bin field found in package.json for ${packageName}`);
191
+ }
192
+
193
+ // 验证 bin 文件存在
194
+ if (!fs.existsSync(binPath)) {
195
+ throw new Error(`Bin file not found at: ${binPath}`);
196
+ }
197
+
198
+ // 更新注册表
199
+ registry.addPlugin(alias, {
200
+ name: packageName,
201
+ version: pkg.version,
202
+ binPath: binPath,
203
+ installedAt: plugin.installedAt,
204
+ updatedAt: new Date().toISOString()
205
+ });
206
+
207
+ console.log(t('updateSuccess', {alias, oldVersion: plugin.version, newVersion: pkg.version}));
208
+
209
+ } catch (error) {
210
+ console.error(t('updateFailed', {alias}), error.message);
211
+ process.exit(1);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * 更新所有插件
217
+ */
218
+ function updateAllPlugins() {
219
+ const plugins = registry.getAllPlugins();
220
+ const pluginList = Object.keys(plugins);
221
+
222
+ if (pluginList.length === 0) {
223
+ console.log(t('noPlugins'));
224
+ return;
106
225
  }
107
226
 
108
- // 从注册表移除
109
- registry.removePlugin(alias);
227
+ console.log(t('updateAll.title'));
228
+ console.log(t('updateAll.foundPlugins', {count: pluginList.length}));
229
+ console.log('');
230
+
231
+ let updatedCount = 0;
232
+ let alreadyLatestCount = 0;
233
+ let failedCount = 0;
234
+ const results = [];
235
+
236
+ for (const alias of pluginList) {
237
+ const plugin = plugins[alias];
238
+ console.log('─'.repeat(50));
239
+ console.log(t('updating'), `${alias} (${plugin.name})...`);
240
+ console.log(t('currentVersion'), plugin.version);
241
+
242
+ const pluginDir = getPluginDir(alias);
243
+ const packageName = plugin.name;
244
+
245
+ try {
246
+ // 使用 npm update 更新插件
247
+ execSync(`npm update ${packageName} --prefix "${pluginDir}"`, {
248
+ stdio: 'pipe',
249
+ encoding: 'utf8'
250
+ });
251
+
252
+ // 读取更新后的 package.json
253
+ const pkgPath = path.join(pluginDir, 'node_modules', packageName, 'package.json');
254
+
255
+ if (!fs.existsSync(pkgPath)) {
256
+ throw new Error(`Package.json not found at: ${pkgPath}`);
257
+ }
110
258
 
111
- console.log(`✓ Plugin "${alias}" uninstalled successfully!`);
259
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
260
+
261
+ // 检查版本是否有变化
262
+ if (pkg.version === plugin.version) {
263
+ console.log(t('alreadyLatest', {alias, version: pkg.version}));
264
+ alreadyLatestCount++;
265
+ results.push({alias, status: 'latest', version: pkg.version});
266
+ } else {
267
+ // 获取 bin 路径
268
+ let binPath;
269
+ if (typeof pkg.bin === 'string') {
270
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin);
271
+ } else if (typeof pkg.bin === 'object') {
272
+ const binKey = Object.keys(pkg.bin)[0];
273
+ binPath = path.join(pluginDir, 'node_modules', packageName, pkg.bin[binKey]);
274
+ } else {
275
+ throw new Error(`No bin field found in package.json for ${packageName}`);
276
+ }
277
+
278
+ // 验证 bin 文件存在
279
+ if (!fs.existsSync(binPath)) {
280
+ throw new Error(`Bin file not found at: ${binPath}`);
281
+ }
282
+
283
+ // 更新注册表
284
+ registry.addPlugin(alias, {
285
+ name: packageName,
286
+ version: pkg.version,
287
+ binPath: binPath,
288
+ installedAt: plugin.installedAt,
289
+ updatedAt: new Date().toISOString()
290
+ });
291
+
292
+ console.log(t('updateSuccess', {alias, oldVersion: plugin.version, newVersion: pkg.version}));
293
+ updatedCount++;
294
+ results.push({alias, status: 'updated', oldVersion: plugin.version, newVersion: pkg.version});
295
+ }
296
+
297
+ } catch (error) {
298
+ console.error(t('updateFailed', {alias}), error.message);
299
+ failedCount++;
300
+ results.push({alias, status: 'failed', error: error.message});
301
+ }
302
+
303
+ console.log('');
304
+ }
112
305
 
113
- } catch (error) {
114
- console.error(`✗ Failed to uninstall plugin "${alias}":`, error.message);
115
- process.exit(1);
116
- }
306
+ // 显示总结
307
+ console.log('═'.repeat(50));
308
+ console.log(t('updateAll.summary'));
309
+ console.log(t('updateAll.totalPlugins', {count: pluginList.length}));
310
+ console.log(t('updateAll.updated', {count: updatedCount}));
311
+ console.log(t('updateAll.alreadyLatest', {count: alreadyLatestCount}));
312
+ console.log(t('updateAll.failed', {count: failedCount}));
117
313
  }
118
314
 
119
315
  module.exports = {
120
- installPlugin,
121
- uninstallPlugin
316
+ installPlugin,
317
+ uninstallPlugin,
318
+ updatePlugin,
319
+ updateAllPlugins
122
320
  };
package/lib/registry.js CHANGED
@@ -1,28 +1,28 @@
1
1
  const fs = require('fs');
2
- const { getRegistryPath, ensureDir, getSlothToolHome } = require('./utils');
2
+ const {getRegistryPath, ensureDir, getSlothToolHome} = require('./utils');
3
3
 
4
4
  /**
5
5
  * 读取注册表
6
6
  * @returns {Object} 注册表对象
7
7
  */
8
8
  function readRegistry() {
9
- const registryPath = getRegistryPath();
9
+ const registryPath = getRegistryPath();
10
10
 
11
- // 确保 .slothtool 目录存在
12
- ensureDir(getSlothToolHome());
11
+ // 确保 .slothtool 目录存在
12
+ ensureDir(getSlothToolHome());
13
13
 
14
- // 如果注册表文件不存在,返回空对象
15
- if (!fs.existsSync(registryPath)) {
16
- return { plugins: {} };
17
- }
14
+ // 如果注册表文件不存在,返回空对象
15
+ if (!fs.existsSync(registryPath)) {
16
+ return {plugins: {}};
17
+ }
18
18
 
19
- try {
20
- const content = fs.readFileSync(registryPath, 'utf8');
21
- return JSON.parse(content);
22
- } catch (error) {
23
- console.error('Failed to read registry:', error.message);
24
- return { plugins: {} };
25
- }
19
+ try {
20
+ const content = fs.readFileSync(registryPath, 'utf8');
21
+ return JSON.parse(content);
22
+ } catch (error) {
23
+ console.error('Failed to read registry:', error.message);
24
+ return {plugins: {}};
25
+ }
26
26
  }
27
27
 
28
28
  /**
@@ -30,17 +30,17 @@ function readRegistry() {
30
30
  * @param {Object} registry - 注册表对象
31
31
  */
32
32
  function writeRegistry(registry) {
33
- const registryPath = getRegistryPath();
33
+ const registryPath = getRegistryPath();
34
34
 
35
- // 确保 .slothtool 目录存在
36
- ensureDir(getSlothToolHome());
35
+ // 确保 .slothtool 目录存在
36
+ ensureDir(getSlothToolHome());
37
37
 
38
- try {
39
- fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2), 'utf8');
40
- } catch (error) {
41
- console.error('Failed to write registry:', error.message);
42
- throw error;
43
- }
38
+ try {
39
+ fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2), 'utf8');
40
+ } catch (error) {
41
+ console.error('Failed to write registry:', error.message);
42
+ throw error;
43
+ }
44
44
  }
45
45
 
46
46
  /**
@@ -49,9 +49,9 @@ function writeRegistry(registry) {
49
49
  * @param {Object} pluginInfo - 插件信息
50
50
  */
51
51
  function addPlugin(alias, pluginInfo) {
52
- const registry = readRegistry();
53
- registry.plugins[alias] = pluginInfo;
54
- writeRegistry(registry);
52
+ const registry = readRegistry();
53
+ registry.plugins[alias] = pluginInfo;
54
+ writeRegistry(registry);
55
55
  }
56
56
 
57
57
  /**
@@ -59,9 +59,9 @@ function addPlugin(alias, pluginInfo) {
59
59
  * @param {string} alias - 插件别名
60
60
  */
61
61
  function removePlugin(alias) {
62
- const registry = readRegistry();
63
- delete registry.plugins[alias];
64
- writeRegistry(registry);
62
+ const registry = readRegistry();
63
+ delete registry.plugins[alias];
64
+ writeRegistry(registry);
65
65
  }
66
66
 
67
67
  /**
@@ -70,8 +70,8 @@ function removePlugin(alias) {
70
70
  * @returns {Object|null} 插件信息,如果不存在返回 null
71
71
  */
72
72
  function getPlugin(alias) {
73
- const registry = readRegistry();
74
- return registry.plugins[alias] || null;
73
+ const registry = readRegistry();
74
+ return registry.plugins[alias] || null;
75
75
  }
76
76
 
77
77
  /**
@@ -79,8 +79,8 @@ function getPlugin(alias) {
79
79
  * @returns {Object} 所有插件的映射
80
80
  */
81
81
  function getAllPlugins() {
82
- const registry = readRegistry();
83
- return registry.plugins;
82
+ const registry = readRegistry();
83
+ return registry.plugins;
84
84
  }
85
85
 
86
86
  /**
@@ -89,16 +89,16 @@ function getAllPlugins() {
89
89
  * @returns {boolean} 是否已安装
90
90
  */
91
91
  function hasPlugin(alias) {
92
- const registry = readRegistry();
93
- return alias in registry.plugins;
92
+ const registry = readRegistry();
93
+ return alias in registry.plugins;
94
94
  }
95
95
 
96
96
  module.exports = {
97
- readRegistry,
98
- writeRegistry,
99
- addPlugin,
100
- removePlugin,
101
- getPlugin,
102
- getAllPlugins,
103
- hasPlugin
97
+ readRegistry,
98
+ writeRegistry,
99
+ addPlugin,
100
+ removePlugin,
101
+ getPlugin,
102
+ getAllPlugins,
103
+ hasPlugin
104
104
  };
@@ -0,0 +1,85 @@
1
+ const fs = require('fs');
2
+ const {getSettingsPath, ensureDir, getSlothToolHome} = require('./utils');
3
+
4
+ /**
5
+ * 读取设置
6
+ * @returns {Object} 设置对象
7
+ */
8
+ function readSettings() {
9
+ const settingsPath = getSettingsPath();
10
+
11
+ // 确保 .slothtool 目录存在
12
+ ensureDir(getSlothToolHome());
13
+
14
+ // 如果设置文件不存在,返回默认设置
15
+ if (!fs.existsSync(settingsPath)) {
16
+ return getDefaultSettings();
17
+ }
18
+
19
+ try {
20
+ const content = fs.readFileSync(settingsPath, 'utf8');
21
+ return JSON.parse(content);
22
+ } catch (error) {
23
+ console.error('Failed to read settings:', error.message);
24
+ return getDefaultSettings();
25
+ }
26
+ }
27
+
28
+ /**
29
+ * 写入设置
30
+ * @param {Object} settings - 设置对象
31
+ */
32
+ function writeSettings(settings) {
33
+ const settingsPath = getSettingsPath();
34
+
35
+ // 确保 .slothtool 目录存在
36
+ ensureDir(getSlothToolHome());
37
+
38
+ try {
39
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
40
+ } catch (error) {
41
+ console.error('Failed to write settings:', error.message);
42
+ throw error;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * 获取默认设置
48
+ * @returns {Object} 默认设置对象
49
+ */
50
+ function getDefaultSettings() {
51
+ return {
52
+ language: 'zh' // 默认中文,可选 'zh' 或 'en'
53
+ };
54
+ }
55
+
56
+ /**
57
+ * 获取当前语言设置
58
+ * @returns {string} 语言代码 ('zh' 或 'en')
59
+ */
60
+ function getLanguage() {
61
+ const settings = readSettings();
62
+ return settings.language || 'zh';
63
+ }
64
+
65
+ /**
66
+ * 设置语言
67
+ * @param {string} language - 语言代码 ('zh' 或 'en')
68
+ */
69
+ function setLanguage(language) {
70
+ if (language !== 'zh' && language !== 'en') {
71
+ throw new Error('Language must be "zh" or "en"');
72
+ }
73
+
74
+ const settings = readSettings();
75
+ settings.language = language;
76
+ writeSettings(settings);
77
+ }
78
+
79
+ module.exports = {
80
+ readSettings,
81
+ writeSettings,
82
+ getDefaultSettings,
83
+ getLanguage,
84
+ setLanguage
85
+ };