meocli 0.1.6 → 0.1.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/README.md CHANGED
@@ -48,56 +48,48 @@ $ me prettier ./test.svg --verbose # 使用prettier格式化文件
48
48
  3. 配置 `.vscode/settings.json` 添加 `"emeraldwalk.runonsave"` 节点,以下为参考配置⤵︎
49
49
 
50
50
  ```json
51
- {
52
- "emeraldwalk.runonsave": {
51
+ "emeraldwalk.runonsave": {
53
52
  "commands": [
54
53
  {
55
54
  // prettier
56
- "match": "\\.(ts|js|json|html|css|graphql|gql|yaml|yml|md)$",
55
+ "match": "\\.(txt|ts|js|jsx|tsx|json|html|css|graphql|gql|yaml|yml|md)$",
57
56
  "notMatch": "node_modules/*$",
58
57
  "isAsync": true,
59
- "cmd": "me prettier ${file}"
58
+ "cmd": "me prettier \"${file}\""
60
59
  },
61
60
  {
62
61
  // @prettier/plugin-xml
63
62
  "match": "\\.(xml|svg)$",
64
63
  "isAsync": true,
65
- "cmd": "me prettier ${file}"
64
+ "cmd": "me prettier \"${file}\""
66
65
  },
67
66
  {
68
67
  // prettier-plugin-toml
69
68
  "match": "\\.(toml)$",
70
69
  "isAsync": true,
71
- "cmd": "me prettier ${file}"
70
+ "cmd": "me prettier \"${file}\""
72
71
  },
73
72
  {
74
73
  // prettier-plugin-nginx
75
74
  "match": "\\.(nginx)$",
76
75
  "isAsync": true,
77
- "cmd": "me prettier ${file}"
76
+ "cmd": "me prettier \"${file}\""
78
77
  },
79
78
  {
80
79
  // prettier-plugin-sh
81
80
  "match": "\\.(sh|env|Dockerfile|properties|gitignore|dockerignore|prettierignore)$",
82
81
  "notMatch": "\\.(nu)$",
83
82
  "isAsync": true,
84
- "cmd": "me prettier ${file}"
83
+ "cmd": "me prettier \"${file}\""
85
84
  },
86
85
  {
87
86
  // no-dot-ext
88
- "match": "Dockerfile$",
87
+ "match": "Dockerfile$|web.config",
89
88
  "isAsync": true,
90
- "cmd": "me prettier ${file}"
91
- },
92
- {
93
- // nushell
94
- "match": "\\.(nu)$",
95
- "isAsync": true,
96
- "cmd": "topiary format ${file}"
89
+ "cmd": "me prettier \"${file}\""
97
90
  }
98
91
  ]
99
92
  }
100
- }
101
93
  ```
102
94
 
103
95
  # Publish
@@ -116,7 +108,7 @@ $ npm install -g meocli
116
108
  $ me COMMAND
117
109
  running command...
118
110
  $ me (--version)
119
- meocli/0.1.6 win32-x64 node-v24.13.1
111
+ meocli/0.1.7 win32-x64 node-v24.14.0
120
112
  $ me --help [COMMAND]
121
113
  USAGE
122
114
  $ me COMMAND
@@ -132,6 +124,7 @@ USAGE
132
124
  * [`me hello PERSON`](#me-hello-person)
133
125
  * [`me hello world`](#me-hello-world)
134
126
  * [`me help [COMMAND]`](#me-help-command)
127
+ * [`me js clash FILEPATH`](#me-js-clash-filepath)
135
128
  * [`me plugins`](#me-plugins)
136
129
  * [`me plugins add PLUGIN`](#me-plugins-add-plugin)
137
130
  * [`me plugins:inspect PLUGIN...`](#me-pluginsinspect-plugin)
@@ -166,7 +159,7 @@ EXAMPLES
166
159
  me env .env
167
160
  ```
168
161
 
169
- _See code: [src/commands/env/index.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/env/index.ts)_
162
+ _See code: [src/commands/env/index.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/env/index.ts)_
170
163
 
171
164
  ## `me env apifox [FILEPATH]`
172
165
 
@@ -189,7 +182,7 @@ EXAMPLES
189
182
  me env apifox .env
190
183
  ```
191
184
 
192
- _See code: [src/commands/env/apifox.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/env/apifox.ts)_
185
+ _See code: [src/commands/env/apifox.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/env/apifox.ts)_
193
186
 
194
187
  ## `me hello PERSON`
195
188
 
@@ -213,7 +206,7 @@ EXAMPLES
213
206
  hello friend --from oclif (./src/commands/hello/index.ts)
214
207
  ```
215
208
 
216
- _See code: [src/commands/hello/index.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/hello/index.ts)_
209
+ _See code: [src/commands/hello/index.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/hello/index.ts)_
217
210
 
218
211
  ## `me hello world`
219
212
 
@@ -231,7 +224,7 @@ EXAMPLES
231
224
  hello world! (./src/commands/hello/world.ts)
232
225
  ```
233
226
 
234
- _See code: [src/commands/hello/world.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/hello/world.ts)_
227
+ _See code: [src/commands/hello/world.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/hello/world.ts)_
235
228
 
236
229
  ## `me help [COMMAND]`
237
230
 
@@ -253,6 +246,29 @@ DESCRIPTION
253
246
 
254
247
  _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.37/src/commands/help.ts)_
255
248
 
249
+ ## `me js clash FILEPATH`
250
+
251
+ 修改Clash脚本
252
+
253
+ ```
254
+ USAGE
255
+ $ me js clash FILEPATH [-v]
256
+
257
+ ARGUMENTS
258
+ FILEPATH 目标js文件路径
259
+
260
+ FLAGS
261
+ -v, --verbose Show verbose output
262
+
263
+ DESCRIPTION
264
+ 修改Clash脚本
265
+
266
+ EXAMPLES
267
+ $ me js clash ./tests/test.js
268
+ ```
269
+
270
+ _See code: [src/commands/js/clash.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/js/clash.ts)_
271
+
256
272
  ## `me plugins`
257
273
 
258
274
  List installed plugins.
@@ -571,7 +587,7 @@ EXAMPLES
571
587
  $ me prettier ./src/file.ts --config ./.prettierrc.yaml
572
588
  ```
573
589
 
574
- _See code: [src/commands/prettier/index.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/prettier/index.ts)_
590
+ _See code: [src/commands/prettier/index.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/prettier/index.ts)_
575
591
 
576
592
  ## `me prettier reset`
577
593
 
@@ -591,5 +607,5 @@ EXAMPLES
591
607
  $ me prettier reset --verbose
592
608
  ```
593
609
 
594
- _See code: [src/commands/prettier/reset.ts](https://github.com/meme2046/meocli/blob/v0.1.6/src/commands/prettier/reset.ts)_
610
+ _See code: [src/commands/prettier/reset.ts](https://github.com/meme2046/meocli/blob/v0.1.7/src/commands/prettier/reset.ts)_
595
611
  <!-- commandsstop -->
@@ -0,0 +1,12 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class ClashModify extends Command {
3
+ static args: {
4
+ filePath: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,491 @@
1
+ import traverse from "@babel/traverse";
2
+ import { Args, Command, Flags } from "@oclif/core";
3
+ import { existsSync } from "node:fs";
4
+ import { extname } from "node:path";
5
+ import { objToAst, readJsAst, writeAst } from "../../lib/ast.js";
6
+ import { require } from "../../lib/commonjs.js";
7
+ export default class ClashModify extends Command {
8
+ static args = {
9
+ filePath: Args.string({
10
+ description: "目标js文件路径",
11
+ required: true,
12
+ }),
13
+ };
14
+ static description = "修改Clash脚本";
15
+ static examples = ["<%= config.bin %> <%= command.id %> ./tests/test.js"];
16
+ static flags = {
17
+ verbose: Flags.boolean({
18
+ char: "v",
19
+ default: false,
20
+ description: "Show verbose output",
21
+ }),
22
+ };
23
+ // https://github.com/IvanSolis1989/Smart-Config-Kit/tree/main/Clash%20Party
24
+ async run() {
25
+ // 目标 myCustomRules
26
+ const customRules = [
27
+ "RULE-SET,my-direct,DIRECT",
28
+ "RULE-SET,my-reject,REJECT",
29
+ "RULE-SET,my-proxy,🌍 全球节点",
30
+ ];
31
+ // 目标 rule-providers 配置
32
+ const ruleProviders = {
33
+ "my-direct": {
34
+ behavior: "classical",
35
+ format: "yaml",
36
+ interval: 3600,
37
+ path: "./ruleset/my-direct.yaml",
38
+ type: "http",
39
+ url: "https://raw.githubusercontent.com/meme2046/data/main/clash/direct.yaml?_t={{timestamp}}",
40
+ },
41
+ "my-proxy": {
42
+ behavior: "classical",
43
+ format: "yaml",
44
+ interval: 3600,
45
+ path: "./ruleset/my-proxy.yaml",
46
+ type: "http",
47
+ url: "https://raw.githubusercontent.com/meme2046/data/main/clash/proxy.yaml?_t={{timestamp}}",
48
+ },
49
+ "my-reject": {
50
+ behavior: "classical",
51
+ format: "yaml",
52
+ interval: 3600,
53
+ path: "./ruleset/my-reject.yaml",
54
+ type: "http",
55
+ url: "https://raw.githubusercontent.com/meme2046/data/main/clash/reject.yaml?_t={{timestamp}}",
56
+ },
57
+ };
58
+ const { args, flags } = await this.parse(ClashModify);
59
+ const { filePath } = args;
60
+ const { verbose } = flags;
61
+ if (verbose) {
62
+ process.env.DEBUG = "oclif:me:js";
63
+ require("debug").enable(process.env.DEBUG);
64
+ }
65
+ // 检查文件是否存在
66
+ if (!existsSync(filePath)) {
67
+ this.error(`file『${filePath}』not found`);
68
+ return;
69
+ }
70
+ // 1. 读取并解析AST
71
+ const ast = await readJsAst(filePath);
72
+ traverse(ast, {
73
+ // 修改 injectRuleProviders、injectRules 和 overwriteGeneral 函数
74
+ FunctionDeclaration: (path) => {
75
+ // 修改 injectRuleProviders 函数内的 rule-providers
76
+ if (path.node.id?.name === "injectRuleProviders") {
77
+ // 找到 if (!config["rule-providers"]) config["rule-providers"] = {}; 的位置
78
+ let insertIndex = 0;
79
+ for (let i = 0; i < path.node.body.body.length; i++) {
80
+ const stmt = path.node.body.body[i];
81
+ // 匹配 if 语句
82
+ if (stmt.type === "IfStatement" &&
83
+ stmt.test.type === "UnaryExpression" &&
84
+ stmt.test.operator === "!" &&
85
+ stmt.test.argument.type === "MemberExpression" &&
86
+ stmt.test.argument.object.type === "Identifier" &&
87
+ stmt.test.argument.object.name === "config" &&
88
+ stmt.test.argument.property.type === "StringLiteral" &&
89
+ stmt.test.argument.property.value === "rule-providers") {
90
+ insertIndex = i + 1; // 在 if 语句之后插入
91
+ break;
92
+ }
93
+ }
94
+ // 在找到的位置之后插入新的赋值语句
95
+ for (const [name, cfg] of Object.entries(ruleProviders)) {
96
+ const newAssignment = {
97
+ expression: {
98
+ left: {
99
+ computed: true,
100
+ object: {
101
+ computed: true,
102
+ object: { name: "config", type: "Identifier" },
103
+ property: {
104
+ type: "StringLiteral",
105
+ value: "rule-providers",
106
+ },
107
+ type: "MemberExpression",
108
+ },
109
+ property: { type: "StringLiteral", value: name },
110
+ type: "MemberExpression",
111
+ },
112
+ operator: "=",
113
+ right: objToAst(cfg),
114
+ type: "AssignmentExpression",
115
+ },
116
+ type: "ExpressionStatement",
117
+ };
118
+ // 使用 splice 在指定位置插入
119
+ path.node.body.body.splice(insertIndex, 0, newAssignment);
120
+ insertIndex++; // 下一个插入位置后移
121
+ this.log(`✔ rule-provider '${name}' 已添加/更新:`);
122
+ this.log(`${JSON.stringify(cfg, null, 2)}\n`);
123
+ }
124
+ }
125
+ // 修改 injectRules 函数
126
+ if (path.node.id?.name === "injectRules") {
127
+ // 第一遍:删除已存在的 myCustomRules 声明
128
+ path.traverse({
129
+ VariableDeclarator: (innerPath) => {
130
+ const { id } = innerPath.node;
131
+ if (id.type === "Identifier" && id.name === "myCustomRules") {
132
+ innerPath.remove();
133
+ this.log("✔ 已删除原有的 myCustomRules\n");
134
+ }
135
+ },
136
+ });
137
+ // 添加新的 myCustomRules 到函数开头
138
+ const varDecl = {
139
+ declarations: [
140
+ {
141
+ id: { name: "myCustomRules", type: "Identifier" },
142
+ init: objToAst(customRules),
143
+ type: "VariableDeclarator",
144
+ },
145
+ ],
146
+ kind: "const",
147
+ type: "VariableDeclaration",
148
+ };
149
+ path.node.body.body.unshift(varDecl);
150
+ this.log("✔ 已添加 myCustomRules:");
151
+ this.log(`${JSON.stringify(customRules, null, 2)}\n`);
152
+ // 在 config.rules 数组前面添加 ...myCustomRules
153
+ path.traverse({
154
+ AssignmentExpression: (innerPath) => {
155
+ const { left } = innerPath.node;
156
+ // 匹配 config.rules = [...] 赋值
157
+ if (left.type === "MemberExpression" &&
158
+ left.object.type === "Identifier" &&
159
+ left.object.name === "config" &&
160
+ left.property.type === "Identifier" &&
161
+ left.property.name === "rules" &&
162
+ innerPath.node.right.type === "ArrayExpression") {
163
+ const arrayExp = innerPath.node.right;
164
+ // 在数组开头插入展开表达式 ...myCustomRules
165
+ arrayExp.elements.unshift({
166
+ argument: { name: "myCustomRules", type: "Identifier" },
167
+ type: "SpreadElement",
168
+ });
169
+ this.log("✔ 已在 config.rules 前面添加 ...myCustomRules\n");
170
+ }
171
+ },
172
+ });
173
+ }
174
+ // 修改 overwriteGeneral 函数
175
+ if (path.node.id?.name === "overwriteGeneral") {
176
+ const targetDomains = ["api.memeniu.xyz", "meme.us.kg"];
177
+ // 1. 声明 targetDomains 变量
178
+ const targetDomainsDecl = {
179
+ declarations: [
180
+ {
181
+ id: { name: "targetDomains", type: "Identifier" },
182
+ init: objToAst(targetDomains),
183
+ type: "VariableDeclarator",
184
+ },
185
+ ],
186
+ kind: "const",
187
+ type: "VariableDeclaration",
188
+ };
189
+ // 2. 设置 config.ipv6 = true
190
+ const ipv6Assign1 = {
191
+ expression: {
192
+ left: {
193
+ object: { name: "config", type: "Identifier" },
194
+ property: { name: "ipv6", type: "Identifier" },
195
+ type: "MemberExpression",
196
+ },
197
+ operator: "=",
198
+ right: { type: "BooleanLiteral", value: true },
199
+ type: "AssignmentExpression",
200
+ },
201
+ type: "ExpressionStatement",
202
+ };
203
+ // 3. 设置 config.dns.ipv6 = true
204
+ const ipv6Assign2 = {
205
+ expression: {
206
+ left: {
207
+ object: {
208
+ object: { name: "config", type: "Identifier" },
209
+ property: { name: "dns", type: "Identifier" },
210
+ type: "MemberExpression",
211
+ },
212
+ property: { name: "ipv6", type: "Identifier" },
213
+ type: "MemberExpression",
214
+ },
215
+ operator: "=",
216
+ right: { type: "BooleanLiteral", value: true },
217
+ type: "AssignmentExpression",
218
+ },
219
+ type: "ExpressionStatement",
220
+ };
221
+ // 4. 声明 ipv6Doh 数组
222
+ const ipv6DohDecl = {
223
+ declarations: [
224
+ {
225
+ id: { name: "ipv6Doh", type: "Identifier" },
226
+ init: {
227
+ elements: [
228
+ {
229
+ type: "StringLiteral",
230
+ value: "https://[2402:4e00::]/dns-query",
231
+ },
232
+ {
233
+ type: "StringLiteral",
234
+ value: "https://[2400:3200::1]/dns-query",
235
+ },
236
+ ],
237
+ type: "ArrayExpression",
238
+ },
239
+ type: "VariableDeclarator",
240
+ },
241
+ ],
242
+ kind: "const",
243
+ type: "VariableDeclaration",
244
+ };
245
+ // 5. 声明 mixedDns 数组
246
+ const mixedDnsDecl = {
247
+ declarations: [
248
+ {
249
+ id: { name: "mixedDns", type: "Identifier" },
250
+ init: {
251
+ elements: [
252
+ {
253
+ argument: { name: "domesticDoH", type: "Identifier" },
254
+ type: "SpreadElement",
255
+ },
256
+ {
257
+ argument: { name: "ipv6Doh", type: "Identifier" },
258
+ type: "SpreadElement",
259
+ },
260
+ ],
261
+ type: "ArrayExpression",
262
+ },
263
+ type: "VariableDeclarator",
264
+ },
265
+ ],
266
+ kind: "const",
267
+ type: "VariableDeclaration",
268
+ };
269
+ // 6. 循环给每个域名绑定混合DNS池
270
+ const forEachDnsPolicy = {
271
+ expression: {
272
+ arguments: [
273
+ {
274
+ body: {
275
+ body: [
276
+ {
277
+ consequent: {
278
+ body: [
279
+ {
280
+ expression: {
281
+ left: {
282
+ computed: true,
283
+ object: {
284
+ computed: true,
285
+ object: {
286
+ object: {
287
+ name: "config",
288
+ type: "Identifier",
289
+ },
290
+ property: {
291
+ name: "dns",
292
+ type: "Identifier",
293
+ },
294
+ type: "MemberExpression",
295
+ },
296
+ property: {
297
+ type: "StringLiteral",
298
+ value: "nameserver-policy",
299
+ },
300
+ type: "MemberExpression",
301
+ },
302
+ property: {
303
+ name: "host",
304
+ type: "Identifier",
305
+ },
306
+ type: "MemberExpression",
307
+ },
308
+ operator: "=",
309
+ right: {
310
+ arguments: [],
311
+ callee: {
312
+ object: {
313
+ name: "mixedDns",
314
+ type: "Identifier",
315
+ },
316
+ property: {
317
+ name: "slice",
318
+ type: "Identifier",
319
+ },
320
+ type: "MemberExpression",
321
+ },
322
+ type: "CallExpression",
323
+ },
324
+ type: "AssignmentExpression",
325
+ },
326
+ type: "ExpressionStatement",
327
+ },
328
+ ],
329
+ type: "BlockStatement",
330
+ },
331
+ test: {
332
+ argument: {
333
+ computed: true,
334
+ object: {
335
+ computed: true,
336
+ object: {
337
+ object: { name: "config", type: "Identifier" },
338
+ property: { name: "dns", type: "Identifier" },
339
+ type: "MemberExpression",
340
+ },
341
+ property: {
342
+ type: "StringLiteral",
343
+ value: "nameserver-policy",
344
+ },
345
+ type: "MemberExpression",
346
+ },
347
+ property: { name: "host", type: "Identifier" },
348
+ type: "MemberExpression",
349
+ },
350
+ operator: "!",
351
+ type: "UnaryExpression",
352
+ },
353
+ type: "IfStatement",
354
+ },
355
+ ],
356
+ type: "BlockStatement",
357
+ },
358
+ params: [{ name: "host", type: "Identifier" }],
359
+ type: "FunctionExpression",
360
+ },
361
+ ],
362
+ callee: {
363
+ object: { name: "targetDomains", type: "Identifier" },
364
+ property: { name: "forEach", type: "Identifier" },
365
+ type: "MemberExpression",
366
+ },
367
+ type: "CallExpression",
368
+ },
369
+ type: "ExpressionStatement",
370
+ };
371
+ // 7. 加入fake-ip白名单
372
+ const forEachFakeIpFilter = {
373
+ expression: {
374
+ arguments: [
375
+ {
376
+ body: {
377
+ body: [
378
+ {
379
+ consequent: {
380
+ body: [
381
+ {
382
+ expression: {
383
+ arguments: [
384
+ { name: "domain", type: "Identifier" },
385
+ ],
386
+ callee: {
387
+ object: {
388
+ computed: true,
389
+ object: {
390
+ object: {
391
+ name: "config",
392
+ type: "Identifier",
393
+ },
394
+ property: {
395
+ name: "dns",
396
+ type: "Identifier",
397
+ },
398
+ type: "MemberExpression",
399
+ },
400
+ property: {
401
+ type: "StringLiteral",
402
+ value: "fake-ip-filter",
403
+ },
404
+ type: "MemberExpression",
405
+ },
406
+ property: {
407
+ name: "push",
408
+ type: "Identifier",
409
+ },
410
+ type: "MemberExpression",
411
+ },
412
+ type: "CallExpression",
413
+ },
414
+ type: "ExpressionStatement",
415
+ },
416
+ ],
417
+ type: "BlockStatement",
418
+ },
419
+ test: {
420
+ argument: {
421
+ arguments: [{ name: "domain", type: "Identifier" }],
422
+ callee: {
423
+ object: {
424
+ computed: true,
425
+ object: {
426
+ object: {
427
+ name: "config",
428
+ type: "Identifier",
429
+ },
430
+ property: { name: "dns", type: "Identifier" },
431
+ type: "MemberExpression",
432
+ },
433
+ property: {
434
+ type: "StringLiteral",
435
+ value: "fake-ip-filter",
436
+ },
437
+ type: "MemberExpression",
438
+ },
439
+ property: {
440
+ name: "includes",
441
+ type: "Identifier",
442
+ },
443
+ type: "MemberExpression",
444
+ },
445
+ type: "CallExpression",
446
+ },
447
+ operator: "!",
448
+ type: "UnaryExpression",
449
+ },
450
+ type: "IfStatement",
451
+ },
452
+ ],
453
+ type: "BlockStatement",
454
+ },
455
+ params: [{ name: "domain", type: "Identifier" }],
456
+ type: "FunctionExpression",
457
+ },
458
+ ],
459
+ callee: {
460
+ object: { name: "targetDomains", type: "Identifier" },
461
+ property: { name: "forEach", type: "Identifier" },
462
+ type: "MemberExpression",
463
+ },
464
+ type: "CallExpression",
465
+ },
466
+ type: "ExpressionStatement",
467
+ };
468
+ // 将所有新语句插入到函数末尾
469
+ const newStatements = [
470
+ targetDomainsDecl,
471
+ ipv6Assign1,
472
+ ipv6Assign2,
473
+ ipv6DohDecl,
474
+ mixedDnsDecl,
475
+ forEachDnsPolicy,
476
+ forEachFakeIpFilter,
477
+ ];
478
+ path.node.body.body.push(...newStatements);
479
+ this.log("✔ 已在 overwriteGeneral 函数末尾添加 IPv6 配置代码\n");
480
+ }
481
+ },
482
+ });
483
+ // 生成新文件名
484
+ const ext = extname(filePath); // 获取扩展名(如 .js)
485
+ const baseName = filePath.slice(0, -ext.length); // 获取去掉扩展名的文件名
486
+ const outputPath = `${baseName}_update${ext}`; // 拼接新文件名
487
+ await writeAst(ast, outputPath); // 保存到新文件
488
+ // 3. 写回文件
489
+ this.log("✔ 自定义clash配置添加完成");
490
+ }
491
+ }
@@ -0,0 +1,8 @@
1
+ import * as t from "@babel/types";
2
+ /** JS对象转babel ObjectExpression AST节点 */
3
+ /** JS值转babel AST节点(支持对象和数组) */
4
+ export declare function objToAst(value: Record<string, unknown> | unknown[]): t.Expression;
5
+ /** 读取JS文件并返回AST */
6
+ export declare function readJsAst(filePath: string): Promise<import("@babel/parser").ParseResult<t.File>>;
7
+ /** AST 生成并覆盖写入文件 */
8
+ export declare function writeAst(ast: t.Node, filePath: string): Promise<void>;
@@ -0,0 +1,63 @@
1
+ import { generate } from "@babel/generator";
2
+ import { parse } from "@babel/parser";
3
+ import * as t from "@babel/types";
4
+ import fs from "node:fs/promises";
5
+ /** 创建字面量节点 */
6
+ function createLiteralNode(value) {
7
+ if (typeof value === "string")
8
+ return t.stringLiteral(value);
9
+ if (typeof value === "number")
10
+ return t.numericLiteral(value);
11
+ if (typeof value === "boolean")
12
+ return t.booleanLiteral(value);
13
+ return t.stringLiteral(String(value));
14
+ }
15
+ /** JS对象转babel ObjectExpression AST节点 */
16
+ /** JS值转babel AST节点(支持对象和数组) */
17
+ export function objToAst(value) {
18
+ // 如果是数组
19
+ if (Array.isArray(value)) {
20
+ const elements = value.map((item) => {
21
+ if (typeof item === "string" ||
22
+ typeof item === "number" ||
23
+ typeof item === "boolean") {
24
+ return createLiteralNode(item);
25
+ }
26
+ // 递归处理嵌套对象/数组
27
+ return objToAst(item);
28
+ });
29
+ return t.arrayExpression(elements);
30
+ }
31
+ // 如果是对象
32
+ const props = [];
33
+ for (const [k, v] of Object.entries(value)) {
34
+ let valNode;
35
+ if (typeof v === "string" ||
36
+ typeof v === "number" ||
37
+ typeof v === "boolean") {
38
+ valNode = createLiteralNode(v);
39
+ }
40
+ else if (Array.isArray(v)) {
41
+ // 递归处理数组
42
+ valNode = objToAst(v);
43
+ }
44
+ else {
45
+ // 递归处理嵌套对象
46
+ valNode = objToAst(v);
47
+ }
48
+ props.push(t.objectProperty(t.identifier(k), valNode));
49
+ }
50
+ return t.objectExpression(props);
51
+ }
52
+ /** 读取JS文件并返回AST */
53
+ export async function readJsAst(filePath) {
54
+ const source = await fs.readFile(filePath, "utf8");
55
+ return parse(source, {
56
+ sourceType: "script",
57
+ });
58
+ }
59
+ /** AST 生成并覆盖写入文件 */
60
+ export async function writeAst(ast, filePath) {
61
+ const output = generate(ast, { compact: false, retainLines: true }).code;
62
+ await fs.writeFile(filePath, output, "utf8");
63
+ }
@@ -12,4 +12,4 @@ console.log(formatNumberWithUnit(1));
12
12
  console.log(formatNumberWithUnit(150_123)); // 输出: 150.1k
13
13
  console.log(formatNumberWithUnit(23_200_000)); // 输出: 23.2M
14
14
  console.log(formatNumberWithUnit(1_600_000_000)); // 输出: 1.6B
15
- console.log(formatNumberWithUnit(123_456, 2)); // 输出: 123.46k(自定义精度)
15
+ console.log(formatNumberWithUnit(123_456, 2)); // 输出: 124.46k(自定义精度)
@@ -233,7 +233,45 @@
233
233
  "prettier",
234
234
  "reset.js"
235
235
  ]
236
+ },
237
+ "js:clash": {
238
+ "aliases": [],
239
+ "args": {
240
+ "filePath": {
241
+ "description": "目标js文件路径",
242
+ "name": "filePath",
243
+ "required": true
244
+ }
245
+ },
246
+ "description": "修改Clash脚本",
247
+ "examples": [
248
+ "<%= config.bin %> <%= command.id %> ./tests/test.js"
249
+ ],
250
+ "flags": {
251
+ "verbose": {
252
+ "char": "v",
253
+ "description": "Show verbose output",
254
+ "name": "verbose",
255
+ "allowNo": false,
256
+ "type": "boolean"
257
+ }
258
+ },
259
+ "hasDynamicHelp": false,
260
+ "hiddenAliases": [],
261
+ "id": "js:clash",
262
+ "pluginAlias": "meocli",
263
+ "pluginName": "meocli",
264
+ "pluginType": "core",
265
+ "strict": true,
266
+ "enableJsonFlag": false,
267
+ "isESM": true,
268
+ "relativePath": [
269
+ "dist",
270
+ "commands",
271
+ "js",
272
+ "clash.js"
273
+ ]
236
274
  }
237
275
  },
238
- "version": "0.1.6"
276
+ "version": "0.1.7"
239
277
  }
package/package.json CHANGED
@@ -1,27 +1,31 @@
1
1
  {
2
2
  "name": "meocli",
3
3
  "description": "Node CLI generated with oclif, Integrate Prettier",
4
- "version": "0.1.6",
4
+ "version": "0.1.7",
5
5
  "author": "meme2046",
6
6
  "bin": {
7
7
  "me": "./bin/run.js"
8
8
  },
9
9
  "bugs": "https://github.com/meme2046/meocli/issues",
10
10
  "dependencies": {
11
- "@oclif/core": "^4.8.0",
11
+ "@babel/generator": "^8.0.0",
12
+ "@babel/parser": "^8.0.0",
13
+ "@babel/traverse": "^8.0.0",
14
+ "@babel/types": "^8.0.0",
15
+ "@oclif/core": "^4.11.14",
12
16
  "@oclif/plugin-help": "^6",
13
17
  "@oclif/plugin-plugins": "^5",
14
18
  "@prettier/plugin-xml": "^3.4.2",
15
19
  "d3-format": "^3.1.2",
16
- "dayjs": "^1.11.19",
20
+ "dayjs": "^1.11.21",
17
21
  "debug": "^4.4.3",
18
- "dotenv": "^17.2.3",
22
+ "dotenv": "^17.4.2",
19
23
  "execa": "^9.6.1",
20
- "js-yaml": "^4.1.1",
21
- "lodash": "^4.17.23",
22
- "prettier": "^3.8.1",
24
+ "js-yaml": "^4.3.0",
25
+ "lodash": "^4.18.1",
26
+ "prettier": "^3.9.4",
23
27
  "prettier-plugin-nginx": "^1.0.3",
24
- "prettier-plugin-sh": "^0.18.0",
28
+ "prettier-plugin-sh": "^0.18.1",
25
29
  "prettier-plugin-toml": "^2.0.6",
26
30
  "trading-signals": "^7.4.3"
27
31
  },
@@ -32,7 +36,7 @@
32
36
  "@types/chai": "^4",
33
37
  "@types/d3-format": "^3.0.4",
34
38
  "@types/js-yaml": "^4.0.9",
35
- "@types/lodash": "^4.17.23",
39
+ "@types/lodash": "^4.17.24",
36
40
  "@types/mocha": "^10",
37
41
  "@types/node": "^18",
38
42
  "chai": "^4",
@@ -40,7 +44,7 @@
40
44
  "eslint-config-oclif": "^6",
41
45
  "eslint-config-prettier": "^10",
42
46
  "mocha": "^10",
43
- "npm-check-updates": "^19.3.2",
47
+ "npm-check-updates": "^19.6.6",
44
48
  "oclif": "^4",
45
49
  "shx": "^0.3.3",
46
50
  "ts-node": "^10",
@@ -78,7 +82,10 @@
78
82
  }
79
83
  }
80
84
  },
81
- "repository": "meme2046/meocli",
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "meme2046/meocli"
88
+ },
82
89
  "types": "dist/index.d.ts",
83
90
  "scripts": {
84
91
  "ncu": "ncu --target minor --reject webpack,shx",