jsir 2.2.1 → 2.2.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.
package/deps/util.js CHANGED
@@ -4,31 +4,85 @@ const home = os.homedir()
4
4
  const {exec, spawnSync} = require('child_process');
5
5
  const readline = require('readline')
6
6
  const fs = require('fs')
7
- const http = require("http");
8
- const querystring = require('querystring');
7
+ const fp = require('fs').promises
9
8
  const BigNumber = require('bignumber.js');
10
9
  const pad = require('pad');
11
10
  const crypto = require('crypto');
12
- const emptyFn = ()=>{}
13
11
  const dayJs = require('dayjs')
14
12
  const table = require('console.table')
15
13
  const initModulePaths = Object.assign([], module.paths)
16
14
 
17
- let _globalLog = createLimitLogger2(`${setting.name}.log`, null, false)
18
- const $log = str => {
19
- str = trim(str)
20
- if (str) {
21
- _globalLog(`[${timeStr()}] ${str}`)
15
+ global.$newInput = false
16
+ global.$workspaceMap = {}
17
+ global.$defaultSpace = 'local'
18
+ global.$newError = false
19
+ global.$tips = {}
20
+
21
+ let libDataDir;
22
+ let lockDir;
23
+ let logDir;
24
+ let configDir;
25
+
26
+ class SyncQueue {
27
+ constructor() {
28
+ this.queue = [];
29
+ this.isProcessing = false;
30
+ }
31
+ // 添加任务到队列
32
+ enqueue(task) {
33
+ this.queue.push(task);
34
+ this.processQueue();
35
+ }
36
+ // 处理队列
37
+ async processQueue() {
38
+ if (this.isProcessing) return;
39
+ this.isProcessing = true;
40
+ while (this.queue.length > 0) {
41
+ const task = this.queue.shift();
42
+ try {
43
+ await task();
44
+ } catch (error) {
45
+ console.error('Error during SyncQueue operation:', error);
46
+ }
47
+ }
48
+ this.isProcessing = false;
22
49
  }
23
50
  }
24
- let _globalDraft= createLimitLogger2(`draft.log`, null, false)
25
- const $draft = str => {
51
+
52
+ const syncQueues = {}
53
+ function syncQueue(task, key) {
54
+ `
55
+ 同步队列分发器
56
+ task: function
57
+ key: 根据key值区分不同队列, 可选
58
+ return void
59
+ `
60
+ let queue = getOrFn(syncQueues, trim(key), () => new SyncQueue())
61
+ queue.enqueue(task)
62
+ }
63
+
64
+ const $log = createLimitLogger(`${setting.name}.log`, {
65
+ logInfo: false
66
+ });
67
+ const $error = createLimitLogger(`${setting.name}.error`, {
68
+ logInfo: false,
69
+ error: true
70
+ });
71
+ let _globalDraft= createLimitLogger(`draft.log`, {
72
+ logInfo: false,
73
+ time: false
74
+ })
75
+ const draftLog = str => {
76
+ `
77
+ (/regStr/) => query from draft
78
+ (str) => save to draft
79
+ `
26
80
  if (getType(str) === 'RegExp') {
27
81
  let results = draftQuery(trim(str.source))
28
82
  return results.map(i => i.split(/\n/).slice(1).join('\n'))
29
83
  } else if (trim(str)) {
30
84
  let now = new Date();
31
- _globalDraft(`---------${now.getTime()} ${timeStr(null, now)}\n${str.split("\n").filter(i => trim(i)).join("\n")}`)
85
+ _globalDraft(`---------${now.getTime()} ${timeStr(null, now)}\n${trimEmptyLine(str)}`)
32
86
  }
33
87
  }
34
88
  const $fnCache = {}
@@ -51,30 +105,25 @@ function errorStr(str) {
51
105
  }
52
106
 
53
107
  function tableStr(...args) {
54
- return trim(table.getTable(...args))
55
- }
56
- function nableStr(rows) {
57
- return tableStr(wrapRows(rows))
58
- }
59
- const _console = Object.assign({}, global.console);
60
- global.console.table = (...args) => {
61
- _console.log(tableStr(...args))
62
- }
63
- global.console.nable = (rows) => {
64
- _console.log(nableStr(rows))
65
- }
66
- global.console.info = (...args) => {
67
- _console.log(infoStr('[info]'), ...dealLevelArgs(infoStr, args))
68
- }
69
- global.console.msg = (...args) => {
70
- _console.log(msgStr('[msg]'), ...dealLevelArgs(msgStr, args))
71
- }
72
- global.console.warn = (...args) => {
73
- _console.warn(warnStr('[warn]'), ...dealLevelArgs(warnStr, args))
108
+ return args.map(i => {
109
+ if (typeof i === 'object') {
110
+ return trim(table.getTable(i))
111
+ } else {
112
+ return i;
113
+ }
114
+ }).join("\n")
74
115
  }
75
- global.console.error = (...args) => {
76
- _console.error(errorStr('[error]'), ...dealLevelArgs(errorStr, args))
116
+
117
+ function nableStr(...args) {
118
+ return tableStr(...args.map(i => {
119
+ if (Array.isArray(i)) {
120
+ return wrapRows(i)
121
+ } else {
122
+ return i
123
+ }
124
+ }))
77
125
  }
126
+
78
127
  function dealLevelArgs(levelStrFn, args) {
79
128
  if (args.length <= 0) {
80
129
  return args;
@@ -90,22 +139,192 @@ function dealLevelArgs(levelStrFn, args) {
90
139
  return args
91
140
  }
92
141
 
93
- const console = Object.assign({}, global.console);
142
+ const _console =Object.assign({}, global.console);
143
+ const _consoleFns= {
144
+ log: {
145
+ fn: _console.log,
146
+ args: args => args
147
+ },
148
+ table: {
149
+ fn: _console.log,
150
+ args: args => [tableStr(...args)]
151
+ },
152
+ nable: {
153
+ fn: _console.log,
154
+ args: args => [nableStr(...args)]
155
+ },
156
+ info: {
157
+ fn: _console.info,
158
+ args: args => [infoStr('[info]'), ...dealLevelArgs(infoStr, args)]
159
+ },
160
+ msg: {
161
+ fn: _console.log,
162
+ args: args => [msgStr('[msg]'), ...dealLevelArgs(msgStr, args)]
163
+ },
164
+ warn: {
165
+ fn: _console.warn,
166
+ args: args => [warnStr('[warn]'), ...dealLevelArgs(warnStr, args)],
167
+ },
168
+ error: {
169
+ fn: _console.error,
170
+ args: args => [errorStr('[error]'), ...dealLevelArgs(errorStr, args)]
171
+ },
172
+ dir: {
173
+ fn: _console.dir,
174
+ args: args => args
175
+ },
176
+ debug: {
177
+ fn: _console.debug,
178
+ args: args => args
179
+ }
180
+ }
181
+
182
+ function createConfig(uniqueName) {
183
+ let result = {
184
+ get: getConfig,
185
+ local: getConfig
186
+ }
187
+ if (uniqueName) {
188
+ result.local = (key, defaultVal) => {
189
+ return _getConfig(key, defaultVal, uniqueName);
190
+ }
191
+ }
192
+ return result;
193
+ }
194
+
195
+ function createConsole(uniqueName) {
196
+ let result = Object.assign({}, _console);
197
+ result.$log = $log;
198
+ result.$error = $error;
199
+ result.$draft = draftLog;
200
+ if (uniqueName) {
201
+ result.$log = createLimitLogger(md5(uniqueName) + '.log', {
202
+ logInfo: false
203
+ });
204
+ result.$error = createLimitLogger(md5(uniqueName) + '.error', {
205
+ logInfo: false,
206
+ error: true
207
+ });
208
+ }
209
+ let quite = false
210
+ if (uniqueName) {
211
+ global.$newInput = false;
212
+ }
213
+ for (let key of Object.keys(_consoleFns)) {
214
+ result[key] = (...args) => {
215
+ if (global.$newInput) {
216
+ quite = true;
217
+ }
218
+ if (uniqueName && quite) {
219
+ let _args = _consoleFns[key].args(args);
220
+ if ("error" === key) {
221
+ result.$log(..._args)
222
+ result.$error(..._args)
223
+ } else {
224
+ result.$log(..._args)
225
+ }
226
+ } else {
227
+ let _args = _consoleFns[key].args(args);
228
+ _consoleFns[key].fn(..._args);
229
+ }
230
+ }
231
+ }
232
+ return result;
233
+ }
234
+
235
+ const console = createConsole();
94
236
  // fix console done
95
237
 
96
- function isArgsMatch(text, args, callback, useMd5) {
238
+
239
+ function getFullPath(name) {
240
+ name = trim(name)
241
+ if (!name) {
242
+ return name;
243
+ }
244
+ let uniqueName = toUniqueName(name)
245
+ let pair = parseUniqueName(uniqueName)
246
+ let space = $workspaceMap[pair[0]]
247
+ if (!space) {
248
+ return ''
249
+ }
250
+ return `${space}/${pair[1]}`
251
+ }
252
+
253
+ function parseUniqueName(uniqueName) {
254
+ let pair = uniqueName.split('/').map(trim).filter(i => i)
255
+ let len = pair.length;
256
+ return [pair[len - 2], pair[len - 1]]
257
+ }
258
+
259
+
260
+ function toUniqueName(name, space) {
261
+ name = trim(name)
262
+ if (!name) {
263
+ throw `invalid name '${name}'`
264
+ }
265
+ let uniqueName
266
+ if (name.startsWith('/')) {
267
+ let items = name.split('/').map(trim).filter(i => i).reverse()
268
+ uniqueName = items[1] + '/' + items[0]
269
+ } else if (name.indexOf('/') !== -1) {
270
+ uniqueName = name;
271
+ } else {
272
+ uniqueName = (space || $defaultSpace) + '/' + name;
273
+ }
274
+ let pair = parseUniqueName(uniqueName);
275
+ return (pair[0] || space || $defaultSpace) + '/' + toJsirFileName(pair[1])
276
+ }
277
+
278
+ function toJsirFileName(name) {
279
+ name = trim(name)
280
+ if (!name) {
281
+ throw `invalid name '${name}'`
282
+ }
283
+ if (isJsirFileName(name)) {
284
+ return name;
285
+ }
286
+ name = name + '.js'
287
+ if (!isJsirFileName(name)) {
288
+ throw `invalid name '${name}'`
289
+ }
290
+ return name
291
+ }
292
+
293
+ function isJsirFileName(name) {
294
+ return /^[^./]*[^./\s]\.[^./\s]+$/.test(name)
295
+ }
296
+
297
+ function isMatch(text,
298
+ matchStr, // reg,reg;reg
299
+ callback, // invoke when matched, no args
300
+ useMd5 // '0x' + md5(text).substring(0, 8)) === matchStr
301
+ ) {
97
302
  let match = false
98
- for (let arg of args) {
303
+ let orStrs = matchStr.split(';').map(trim).filter(i => i);
304
+ for (let arg of orStrs) {
99
305
  let r = true
100
306
  for (let str of arg.split(',').map(trim).filter(i => i)) {
307
+ let not = false;
308
+ if (str.startsWith("!")) {
309
+ not = true;
310
+ str = str.substring(1);
311
+ }
101
312
  let reg = new RegExp(str.split(/\s+/).join('\\s+'), 'i')
102
313
  if (useMd5 && /^0x[a-z\d]{8}$/.test(str)) {
103
- if (('0x' + md5(text).substr(0, 8)) !== str) {
314
+ let flag = ('0x' + md5(text).substring(0, 8)) !== str;
315
+ if (not) {
316
+ flag = !flag;
317
+ }
318
+ if (flag) {
104
319
  r = false
105
320
  break
106
321
  }
107
322
  } else {
108
- if (!reg.test(text)) {
323
+ let flag = !reg.test(text);
324
+ if (not) {
325
+ flag = !flag;
326
+ }
327
+ if (flag) {
109
328
  r = false
110
329
  break
111
330
  }
@@ -116,6 +335,9 @@ function isArgsMatch(text, args, callback, useMd5) {
116
335
  break
117
336
  }
118
337
  }
338
+ if (orStrs.length === 0) {
339
+ match = true;
340
+ }
119
341
  if (match && callback) {
120
342
  callback()
121
343
  }
@@ -129,26 +351,27 @@ function parseDraftLog(draftPath) {
129
351
  let allLines = text.split("\n");
130
352
  let temp = [];
131
353
  let startIndex = 0;
354
+ let head;
355
+ let splitReg = /^---------\d+/;
132
356
 
133
357
  allLines.forEach((line, i) => {
134
- if (!line.trim()) return;
135
-
136
- if (line.startsWith("---------")) {
358
+ if (splitReg.test(line)) {
137
359
  if (temp.length > 0) {
138
360
  let fullText = temp.join('\n');
139
361
  lines.push(fullText);
140
- lineRanges.set(fullText, { start: startIndex, end: i - 1 });
362
+ lineRanges.set(fullText, { start: startIndex, end: i - 1, head });
141
363
  }
142
364
  startIndex = i;
143
365
  temp = [];
366
+ head = line;
144
367
  }
145
- temp.push(line.startsWith("---------") ? warnStr(line) : line);
368
+ temp.push(splitReg.test(line) ? warnStr(line) : line);
146
369
  });
147
370
 
148
371
  if (temp.length > 0) {
149
- let fullText = temp.join('\n');
372
+ let fullText = temp.join('\n').replace(/\n$/, '');
150
373
  lines.push(fullText);
151
- lineRanges.set(fullText, { start: startIndex, end: allLines.length - 1 });
374
+ lineRanges.set(fullText, { start: startIndex, end: allLines.length - 1, head });
152
375
  }
153
376
 
154
377
  return { lines, lineRanges, allLines };
@@ -168,12 +391,17 @@ function draftQuery(fLine) {
168
391
  let limit = 0;
169
392
  let filterKeywords = '';
170
393
  let isDelete = false;
394
+ let isEdit = false;
171
395
 
172
396
  // 解析 fLine 来确定过滤关键词和操作
173
397
  if (/\s+-$/.test(fLine)) {
174
398
  fLine = fLine.replace(/\s+-$/, '');
175
399
  isDelete = true;
176
400
  }
401
+ if (/\s+\+$/.test(fLine)) {
402
+ fLine = fLine.replace(/\s+\+$/, '');
403
+ isEdit = true;
404
+ }
177
405
  if (/\s+\d+$/.test(fLine)) {
178
406
  filterKeywords = fLine.replace(/\s+\d+$/, '');
179
407
  limit = parseInt(fLine.match(/\d+$/)[0]);
@@ -185,7 +413,7 @@ function draftQuery(fLine) {
185
413
  results = lines.slice(- parseInt(filterKeywords));
186
414
  } else {
187
415
  lines.forEach(line => {
188
- if (isArgsMatch(line, [filterKeywords])) {
416
+ if (isMatch(line, filterKeywords)) {
189
417
  results.push(line);
190
418
  }
191
419
  });
@@ -209,16 +437,63 @@ function draftQuery(fLine) {
209
437
  fs.writeFileSync(draftPath, allLines.join("\n") + "\n");
210
438
  console.info("removed")
211
439
  }
440
+ if (isEdit && results.length === 1) {
441
+ let tempPath = getLibDataDir() + "/log/draft.temp";
442
+ fs.writeFileSync(tempPath, results[0].split(/\n/).slice(1).join('\n'))
443
+ ei(getEditor(), [tempPath])
444
+ let tempText = String(fs.readFileSync(tempPath))
445
+ let lineRange = lineRanges.get(results[0]);
446
+ let before = allLines.filter((_, index) => {
447
+ return index < lineRange.start;
448
+ }).join("\n");
449
+ let after = allLines.filter((_, index) => {
450
+ return index > lineRange.end;
451
+ }).join("\n");
452
+ if (trim(tempText)) {
453
+ tempText = trimEmptyLine(tempText)
454
+ results = [warnStr(lineRange.head) + '\n' + tempText]
455
+ let newText = [before, lineRange.head + '\n' + tempText, after].filter(i => i).join('\n')
456
+ if (!newText.endsWith('\n')) {
457
+ newText += '\n';
458
+ }
459
+ fs.writeFileSync(draftPath, newText);
460
+ }
461
+ }
212
462
  return results;
213
463
  }
214
464
 
465
+ function trimEmptyLine(text) {
466
+ `
467
+ 去除开头和末尾的空白行
468
+ `
469
+ // 将字符串按行分割成数组
470
+ const lines = text.split('\n');
471
+ let start = 0;
472
+ let end = lines.length - 1;
473
+ // 循环查找开头的空白行
474
+ while (start < end && lines[start].trim() === '') {
475
+ start++;
476
+ }
477
+ // 循环查找末尾的空白行
478
+ while (end > start && lines[end].trim() === '') {
479
+ end--;
480
+ }
481
+ // 返回去除空白行后的字符串
482
+ return lines.slice(start, end + 1).join('\n');
483
+ }
484
+
485
+ function getEditor() {
486
+ return getConfig("defaultEditor", "vi")
487
+ }
488
+
215
489
  function timeStr(fmt, date) {
216
- return dayJs(date || new Date()).format(fmt || 'YYYY/MM/DD HH:mm:ss')
490
+ return dayJs(date || new Date()).format(fmt || 'YYYY-MM-DD HH:mm:ss')
217
491
  }
218
492
 
219
493
  function vl(obj) {
220
- if (typeof obj === 'number') {
221
- return !Number.isNaN(obj)
494
+ let type= typeof obj;
495
+ if (type === 'number') {
496
+ return !Number.isNaN(obj) && Number.isFinite(obj);
222
497
  }
223
498
  if (typeof obj === 'string') {
224
499
  obj = obj.trim()
@@ -310,53 +585,51 @@ function clearConsole(cleanType = 0) {
310
585
  }
311
586
  }
312
587
 
313
- function appendLog(fileName, text) {
314
- let logDir = getLibDataDir() + "/log"
588
+ function getLogDir() {
589
+ if (logDir) {
590
+ return logDir;
591
+ }
592
+ logDir = getLibDataDir() + "/log"
315
593
  mkdir(logDir)
316
- let logPath = logDir + "/" + fileName
317
- fs.appendFile(logPath, text + '\n', emptyFn)
594
+ return logDir;
318
595
  }
319
596
 
320
- function createLimitLogger(fileName, maxChars, logInfo = true) {
321
- let logDir = getLibDataDir() + "/log"
322
- mkdir(logDir)
323
- let logPath = logDir + "/" + fileName
324
- fileCleaner(logPath, maxChars)
325
-
326
- if (logInfo) {
327
- console.info(`log: ${logPath}`)
328
- }
329
-
330
- return (text) => {
331
- text = trim(text)
332
- if (!text) {
333
- return
334
- }
335
- fs.appendFile(logPath, text + '\n', emptyFn)
597
+ function createLimitLogger(fileName, {
598
+ maxChars = 49 * 1024 * 1024,
599
+ logInfo = true,
600
+ time = true,
601
+ error = false
602
+ } = {}) {
603
+ fileName = trim(fileName)
604
+ if (!fileName) {
605
+ throw 'log fileName missing';
336
606
  }
337
- }
338
-
339
- function createLimitLogger2(fileName, maxChars, logInfo = true) {
340
- let logDir = getLibDataDir() + "/log"
341
- mkdir(logDir)
607
+ let logDir = getLogDir()
342
608
  let logPath = logDir + "/" + fileName
343
-
344
609
  if (logInfo) {
345
610
  console.info(`log: ${logPath}`)
346
611
  }
347
-
612
+ if (fs.existsSync(logPath)) {
613
+ cleanFile(logPath, maxChars)
614
+ }
348
615
  let minNum = (Date.now()/(1000 * 60 * 10)).toFixed(0)
349
- return (text) => {
350
- text = trim(text)
616
+ return (...args) => {
617
+ let text = args.join(" ")
351
618
  if (!text) {
352
619
  return
353
620
  }
621
+ if (error) {
622
+ global.$newError = true;
623
+ }
624
+ if (time) {
625
+ text = `${timeStr('YYYY-MM-DD HH:mm:ss.SSS')} ${text}`
626
+ }
627
+ syncQueue(() => fp.appendFile(logPath, text + '\n'), logPath)
354
628
  let _minNum = (Date.now()/(1000 * 60 * 10)).toFixed(0)
355
629
  if (_minNum !== minNum) {
356
630
  minNum = _minNum
357
631
  cleanFile(logPath, maxChars)
358
632
  }
359
- fs.appendFile(logPath, text + '\n', emptyFn)
360
633
  }
361
634
  }
362
635
 
@@ -403,7 +676,7 @@ function dataFile(fileName, fn, fmt, defaultObj = {}, returnStr = false) {
403
676
 
404
677
  let text = String(fs.readFileSync(path));
405
678
  if (returnStr) {
406
- text = text.replace(/return\s+/, '')
679
+ text = text.replace(/^return\s+/, '')
407
680
  }
408
681
  let obj = JSON.parse(text)
409
682
  if (!fn) {
@@ -416,24 +689,68 @@ function dataFile(fileName, fn, fmt, defaultObj = {}, returnStr = false) {
416
689
  if (result && typeof result === 'object') {
417
690
  obj = result
418
691
  }
419
- fileLock(fileName, () => fs.writeFileSync(path, prefixStr + JSON.stringify(obj, null, fmt ? 2:null)))
692
+ fs.writeFileSync(path, prefixStr + JSON.stringify(obj, null, fmt ? 2:null))
420
693
  resolve(obj)
421
694
  })
422
695
  } else {
423
696
  if (val && typeof val === 'object') {
424
697
  obj = val
425
698
  }
426
- fileLock(fileName, () => fs.writeFileSync(path, prefixStr + JSON.stringify(obj, null, fmt ? 2:null)))
699
+ fs.writeFileSync(path, prefixStr + JSON.stringify(obj, null, fmt ? 2:null))
427
700
  return obj
428
701
  }
429
702
  }
430
703
 
704
+ async function fileExist(path) {
705
+ try {
706
+ await fp.access(path);
707
+ return true;
708
+ } catch (_){
709
+ return false;
710
+ }
711
+ }
712
+
713
+ async function fileJson(key, fn, fmt = true) {
714
+ `
715
+ 多进程安全文件读写
716
+ `
717
+ let dataDir = getLibDataDir() + "/data"
718
+ let fileName = trim(key)
719
+ let path = dataDir + "/" + fileName;
720
+ let homeDir = global.$workspaceMap[$defaultSpace]
721
+ let isInit = fileName.startsWith("i ");
722
+ if (isInit) {
723
+ path = homeDir + '/' + toJsirFileName(fileName);
724
+ }
725
+ let prefixStr = isInit ? 'return ' : '';
726
+ let result = null;
727
+ await fileLock(path, async () => {
728
+ if (!await fileExist(path)) {
729
+ await fp.writeFile(path, prefixStr + '{}')
730
+ }
731
+ let text = String(await fp.readFile(path));
732
+ if (isInit) {
733
+ text = text.replace(/^return\s+/, '')
734
+ }
735
+ result = JSON.parse(text)
736
+ if (!fn) {
737
+ return;
738
+ }
739
+ let val = await fn(result)
740
+ if (val && typeof val === 'object') {
741
+ result = val
742
+ }
743
+ await fp.writeFile(path, prefixStr + JSON.stringify(result, null, fmt ? 2:null))
744
+ });
745
+ return result;
746
+ }
747
+
431
748
  function requireG(moduleName){
432
749
  let moduleDir = $workspaceMap[global.$defaultSpace] + '/node_modules'
433
750
  if (module.paths.indexOf(moduleDir) === -1) {
434
751
  module.paths.splice(0, module.paths.length)
435
- module.paths.push(...initModulePaths)
436
752
  module.paths.push(moduleDir)
753
+ module.paths.push(...initModulePaths)
437
754
  }
438
755
  try {
439
756
  let result = require(moduleName);
@@ -453,8 +770,8 @@ async function importG(moduleName) {
453
770
  let moduleDir = $workspaceMap[global.$defaultSpace] + '/node_modules'
454
771
  if (module.paths.indexOf(moduleDir) === -1) {
455
772
  module.paths.splice(0, module.paths.length)
456
- module.paths.push(...initModulePaths)
457
773
  module.paths.push(moduleDir)
774
+ module.paths.push(...initModulePaths)
458
775
  }
459
776
  try {
460
777
  let result = await import(moduleName);
@@ -467,7 +784,7 @@ async function importG(moduleName) {
467
784
  console.log(warnStr(`.npm install ${moduleName}`))
468
785
  throw `${moduleName} not found, use .npm install to add it`;
469
786
  }
470
- return await import(path);
787
+ return await import(require.resolve(path));
471
788
  }
472
789
 
473
790
  function validStr(str, name) {
@@ -511,8 +828,24 @@ function aesDecipher(str, key){
511
828
  }
512
829
 
513
830
  function getConfig(key, defaultVal) {
831
+ return _getConfig(key, defaultVal)
832
+ }
833
+
834
+ function getConfigDir() {
835
+ if (configDir) {
836
+ return configDir
837
+ }
838
+ configDir = getLibDataDir() + '/config';
839
+ mkdir(configDir);
840
+ return configDir;
841
+ }
842
+
843
+ function _getConfig(key, defaultVal, uniqueName) {
514
844
  let configInit = {}
515
845
  let configFile = getLibDataDir() + '/config.json';
846
+ if (uniqueName) {
847
+ configFile = getConfigDir() + "/" + md5(uniqueName) + ".json"
848
+ }
516
849
  if (!fs.existsSync(configFile)) {
517
850
  fs.writeFileSync(configFile, JSON.stringify(configInit, null, 2));
518
851
  }
@@ -525,7 +858,7 @@ function getConfig(key, defaultVal) {
525
858
  if (!(key in config)) {
526
859
  writeFlag = true
527
860
  config[key] = null
528
- console.warn(`require config "${key}" in ${configFile}`)
861
+ console.warn(`require config "${key}", use .setting`)
529
862
  }
530
863
  if (writeFlag) {
531
864
  fs.writeFileSync(configFile, JSON.stringify(config, null, 2))
@@ -614,67 +947,68 @@ function eFn(fn, ...args) {
614
947
  return fn(...args);
615
948
  }
616
949
 
617
- function globalLock(key, expiredMs) {
618
- let lockKeys = global.$lockKeys || {}
619
- if (!global.$lockKeys) {
620
- global.$lockKeys = lockKeys
621
- }
622
- if (lockKeys[key] && (Date.now() <= lockKeys[key])) {
623
- return false;
624
- } else {
625
- lockKeys[key] = Date.now() + (expiredMs || 1000 * 60 * 60 * 24 * 1000)
626
- return true
950
+ function getLockDir() {
951
+ if (lockDir) {
952
+ return lockDir;
627
953
  }
954
+ lockDir = getLibDataDir() + "/lock";
955
+ mkdir(lockDir);
956
+ return lockDir;
628
957
  }
629
958
 
630
- function globalUnLock(key) {
631
- if (global.$lockKeys) {
632
- delete global.$lockKeys[key]
959
+ function getLockFile(key) {
960
+ if (!key) {
961
+ throw "invalid args"
633
962
  }
963
+ key = md5(key.trim());
964
+ let lockDir = getLockDir();
965
+ return lockDir + "/" + key;
634
966
  }
635
967
 
636
- function fileLock(key, fn) {
637
- if (!(fn && key)) {
638
- return;
639
- }
640
- key = md5(trim(key))
641
- let lockDir = getLibDataDir() + "/lock"
642
- mkdir(lockDir)
643
- let file = lockDir + "/" + key
644
- if (fs.existsSync(file)) {
645
- return;
968
+ async function _fileLock(key, fn) {
969
+ `
970
+ 文件锁,返回true/false,
971
+ 如果没锁住则不执行fn
972
+ `
973
+ if (!key || !fn) {
974
+ throw new Error('invalid args')
646
975
  }
976
+ let file = getLockFile(key)
647
977
  try {
648
- fs.mkdirSync(file)
978
+ await fp.mkdir(file);
649
979
  } catch (e) {
650
- return;
980
+ return false;
651
981
  }
652
-
653
- let obj
982
+ let fileLockMap = getOr(global, `$fileLock`, {})
983
+ fileLockMap[file] = true;
654
984
  try {
655
- obj = fn()
985
+ await fn();
986
+ return true;
656
987
  } catch (e) {
657
- rmDir(file)
658
- }
659
- if (getType(obj)=== 'Promise') {
660
- return obj.then(result => {
661
- rmDir(file)
662
- return result
663
- }).catch(e => {
664
- rmDir(file)
665
- throw e
666
- })
667
- } else {
668
- rmDir(file)
669
- return obj
988
+ throw e;
989
+ } finally {
990
+ try {
991
+ await fp.rmdir(file)
992
+ delete fileLockMap[file]
993
+ } catch (_){}
670
994
  }
671
995
  }
672
996
 
673
- function rmDir(dir) {
674
- if (fs.existsSync(dir)) {
675
- try {
676
- fs.rmdirSync(dir)
677
- } catch (e) {}
997
+ async function fileLock(key, fn, wait = true) {
998
+ `
999
+ 文件锁, 默认一直等待,直到加锁成功执行fn
1000
+ wait = false, 加锁失败则不执行fn
1001
+ return void
1002
+ `
1003
+ if (wait) {
1004
+ while (true) {
1005
+ if (await _fileLock(key, fn)) {
1006
+ break;
1007
+ }
1008
+ await sleep(49);
1009
+ }
1010
+ } else {
1011
+ await _fileLock(key, fn);
678
1012
  }
679
1013
  }
680
1014
 
@@ -689,52 +1023,135 @@ function removeFirst(array, obj) {
689
1023
  return array.splice(deleteIndex, 1)
690
1024
  }
691
1025
 
692
- function fileCleaner(path, maxChars) {
693
- if (!globalLock("cleaner: " + path)) {
694
- return
1026
+ const _tipsOnRm = {}
1027
+ function setTips(key, value, onRm) {
1028
+ `
1029
+ 可以设置相同的key, value值,会在左侧提示符体现出来
1030
+ `
1031
+ getOr(global.$tips, key, []).push(value);
1032
+ if (onRm) {
1033
+ getOr(_tipsOnRm, key, []).push(onRm)
1034
+ }
1035
+ }
1036
+
1037
+ function delTips(...keys) {
1038
+ for (let key of Object.keys($tips)) {
1039
+ if (keys.length === 0) {
1040
+ delete $tips[key]
1041
+ tipsOnRmCallback(key)
1042
+ } else if (keys.indexOf(key) !== -1) {
1043
+ delete $tips[key]
1044
+ tipsOnRmCallback(key)
1045
+ }
695
1046
  }
696
- if (!global.$cleanFiles) {
697
- global.$cleanFiles = {}
1047
+ if (keys.length === 0) {
1048
+ for (let key of Object.keys(_tipsOnRm)) {
1049
+ tipsOnRmCallback(key)
1050
+ }
698
1051
  }
699
- if (!global.$cleanFiles[path]) {
700
- global.$cleanFiles[path] = 0
1052
+ }
1053
+
1054
+ function tipsOnRmCallback(key) {
1055
+ let callbacks = _tipsOnRm[key]
1056
+ delete _tipsOnRm[key]
1057
+ if (callbacks && callbacks.length > 0) {
1058
+ for (let callback of callbacks) {
1059
+ try {
1060
+ callback()
1061
+ } catch (e) {
1062
+ $log(errorStack(e))
1063
+ console.error(`[${key}] OnRmCallback: ${String(e)}`)
1064
+ }
1065
+ }
701
1066
  }
1067
+ }
702
1068
 
703
- if (!global.$fileCleanerOn) {
704
- timeLoop(() => {
705
- for (let aPath of Object.keys(global.$cleanFiles)) {
1069
+ async function timer(key, ms, fn, label, printInfo = true){
1070
+ `
1071
+ 定时控制器
1072
+ clear tips退出
1073
+ fn方法返回true退出
1074
+ return void;
1075
+ `
1076
+ if (!(key && ms && fn)) {
1077
+ throw 'invalid args'
1078
+ }
1079
+ let ids = []
1080
+ let showLabel = vl(label) ? label : key;
1081
+ let flag = [...new Set([key, showLabel].map(trim))].filter(i => i).join(' ')
1082
+ setTips(key, showLabel, () => {
1083
+ clearTimeout(ids[0])
1084
+ ids[0] = false
1085
+ if (printInfo) {
1086
+ console.info(`${flag} stop`)
1087
+ }
1088
+ })
1089
+ if (printInfo) {
1090
+ console.info(`${flag} start`)
1091
+ }
1092
+ await timeLoop(fn, ms, ids)
1093
+ }
1094
+
1095
+ const cleanFiles = {}
1096
+ function fileCleaner(path, maxChars) {
1097
+ `
1098
+ 文件清理器,提示符为sys f
1099
+ `
1100
+ cleanFiles[path] = Date.now() + (1000 * 60)
1101
+ let flag = "sys"
1102
+ if (!$tips[flag]) {
1103
+ timer(flag, 1000 * 9, () => {
1104
+ for (let aPath of Object.keys(cleanFiles)) {
1105
+ if (cleanFiles[aPath] < Date.now()) {
1106
+ delete cleanFiles[path]
1107
+ continue;
1108
+ }
706
1109
  try {
707
1110
  cleanFile(path, maxChars)
708
1111
  } catch (e) {
709
- console.log(String(e))
1112
+ console.error(e)
710
1113
  }
711
- global.$cleanFiles[path] ++
712
1114
  }
713
- }, 1000 * 60 * 10)
714
- global.$fileCleanerOn = true
1115
+ }, "f", false);
715
1116
  }
716
1117
  }
717
1118
 
718
- function cleanFile(path, maxChars) {
719
- if (!fs.existsSync(path)) {
1119
+ function cleanFile(path, maxChars = 9 * 1024 * 1024) {
1120
+ `
1121
+ 进入文件操作队列,不会同步去做
1122
+ `
1123
+ path = trim(path);
1124
+ if (!path) {
720
1125
  return
721
1126
  }
722
- maxChars = maxChars || 1024 * 1024
723
-
724
- // 获取文件信息
725
- const stats = fs.statSync(path);
726
- const fileSize = stats.size;
727
- // 只有当文件大小超过限制时才需要清理
728
- if (fileSize <= maxChars) {
729
- return;
730
- }
731
-
732
- let content = String(fs.readFileSync(path))
733
- let newContext = content.substr(- (maxChars * 0.9))
734
- fs.writeFileSync(path, newContext)
1127
+ syncQueue(async () => {
1128
+ maxChars = maxChars || 1024 * 1024
1129
+ // 获取文件信息
1130
+ let stats;
1131
+ try {
1132
+ stats = await fp.stat(path);
1133
+ } catch (e) {
1134
+ console.$error('cleanFile stat failed', errorMsg(e));
1135
+ return;
1136
+ }
1137
+ const fileSize = stats.size;
1138
+ // 只有当文件大小超过限制时才需要清理
1139
+ if (fileSize <= maxChars) {
1140
+ return;
1141
+ }
1142
+ await fileLock(path, async () => {
1143
+ let content = String(await fp.readFile(path))
1144
+ let newContext = content.slice(-Math.floor(maxChars * 0.7));
1145
+ await fp.writeFile(path, newContext)
1146
+ }, false)
1147
+ }, path)
735
1148
  }
736
1149
 
737
1150
  function lisPid(name) {
1151
+ `
1152
+ 调用此方法后,将会创建一个 ${dir}/${name}.pid 文件
1153
+ 删除该文件,当前进程将会结束
1154
+ `
738
1155
  let dir = getLisPidDir()
739
1156
  let pidFile = `${dir}/${name}.pid`
740
1157
  fs.writeFileSync(pidFile, String(process.pid))
@@ -760,34 +1177,6 @@ function getLisPidDir() {
760
1177
  return dir
761
1178
  }
762
1179
 
763
- function fanyi(content) {
764
- return new Promise(resolve => {
765
- let params = querystring.stringify({
766
- word: content
767
- })
768
- let req = http.request({
769
- host: 'api.bejson.com',
770
- port: 80,
771
- path: '/Bejson/Api/Zh2en/toEnglish',
772
- method: 'post',
773
- headers: {
774
- 'Content-Type':'application/x-www-form-urlencoded',
775
- 'Content-Length': params.length
776
- }},function(res){
777
- let str="";
778
- res.on("data",function(chunk){
779
- str+=chunk;
780
- });
781
- res.on("end",function(){
782
- resolve(JSON.parse(str)['data']['target_text'])
783
- })
784
- })
785
-
786
- req.write(params + "\n");
787
- req.end();
788
- })
789
- }
790
-
791
1180
  function linuxAskAndKill(...keys){
792
1181
  let rl = readline.createInterface({
793
1182
  input: process.stdin,
@@ -814,6 +1203,11 @@ function linuxAskAndKill(...keys){
814
1203
  }
815
1204
 
816
1205
  function timeLoop(fn, timestamp, lastIds = []){
1206
+ `
1207
+ fn的结果===true, 则退出;
1208
+ lastIds[0]===false, 则退出;
1209
+ return lastIds;
1210
+ `
817
1211
  let val
818
1212
  try {
819
1213
  val = fn()
@@ -852,13 +1246,19 @@ function _linuxAskAndKill(rl, keys){
852
1246
  }
853
1247
 
854
1248
  function trim(obj) {
1249
+ `
1250
+ 返回值一定是string
1251
+ `
855
1252
  return vl(obj) ? String(obj).trim():""
856
1253
  }
857
1254
 
858
1255
  function getLibDataDir() {
859
- let dir = `${home}/${setting.libDataDir}`
860
- mkdir(dir)
861
- return dir
1256
+ if (libDataDir) {
1257
+ return libDataDir;
1258
+ }
1259
+ libDataDir = `${home}/${setting.libDataDir}`
1260
+ mkdir(libDataDir)
1261
+ return libDataDir
862
1262
  }
863
1263
 
864
1264
  function mkdir(dir) {
@@ -868,11 +1268,14 @@ function mkdir(dir) {
868
1268
  try {
869
1269
  fs.mkdirSync(dir)
870
1270
  } catch (e) {
871
-
1271
+ console.$error(errorStack(e))
872
1272
  }
873
1273
  }
874
1274
 
875
1275
  function e(cmd, mbNum = 3, doTrim = true){
1276
+ `
1277
+ return Promise(result)
1278
+ `
876
1279
  return new Promise((resolve, reject) => {
877
1280
  exec(`${cmd}`, {
878
1281
  maxBuffer: 1024 * 1024 * (mbNum || 3) //quick fix
@@ -887,6 +1290,9 @@ function e(cmd, mbNum = 3, doTrim = true){
887
1290
  }
888
1291
 
889
1292
  function ee(cmd, mbNum = 3, doTrim = true){
1293
+ `
1294
+ return Promise([stdout, stderr])
1295
+ `
890
1296
  return new Promise((resolve, reject) => {
891
1297
  exec(`${cmd}`, {
892
1298
  maxBuffer: 1024 * 1024 * (mbNum || 3) //quick fix
@@ -898,11 +1304,14 @@ function ee(cmd, mbNum = 3, doTrim = true){
898
1304
  }
899
1305
 
900
1306
  function ei(cmd, args = [], shell = false) {
1307
+ `
1308
+ 当前进程会卡住,输入输出由cmd进程持有
1309
+ `
901
1310
  spawnSync(cmd, args, {stdio:"inherit", shell});
902
1311
  }
903
1312
 
904
- async function getCbText() {
905
- return await e(`pbpaste`, null, false)
1313
+ async function getCbText(mbNum) {
1314
+ return await e(`pbpaste`, mbNum, false)
906
1315
  }
907
1316
 
908
1317
  async function setCbText(str) {
@@ -943,7 +1352,7 @@ function splitArray(items, size) {
943
1352
  }
944
1353
 
945
1354
  function isError(obj) {
946
- return getType(obj).endsWith('Error')
1355
+ return obj instanceof Error;
947
1356
  }
948
1357
 
949
1358
  class Range {
@@ -976,14 +1385,14 @@ function range(start, end, step) {
976
1385
  }
977
1386
 
978
1387
  function getOr(obj, key, defaultVal) {
979
- if (!obj[key]) {
1388
+ if (!vl(obj[key])) {
980
1389
  obj[key] = defaultVal
981
1390
  }
982
1391
  return obj[key]
983
1392
  }
984
1393
 
985
1394
  function getOrFn(obj, key, defaultValFn) {
986
- if (!obj[key]) {
1395
+ if (!vl(obj[key])) {
987
1396
  obj[key] = defaultValFn()
988
1397
  }
989
1398
  return obj[key]
@@ -1119,14 +1528,16 @@ function getType(obj) {
1119
1528
  return typeof obj
1120
1529
  }
1121
1530
 
1122
- function getInfo(obj, key, type) {
1531
+ function getInfo(obj, name, type) {
1532
+ `
1533
+ return {key, type, value}
1534
+ `
1123
1535
  let info = {}
1124
- if (key) {
1125
- info.key = key
1536
+ if (name) {
1537
+ info.name = name
1126
1538
  }
1127
1539
  info.type = type || getType(obj)
1128
1540
  if (!vl(obj)) {
1129
- info.type = type
1130
1541
  info.value = String(obj);
1131
1542
  return info;
1132
1543
  }
@@ -1135,15 +1546,20 @@ function getInfo(obj, key, type) {
1135
1546
  let fnName
1136
1547
  if (info.type === 'Function' || info.type === 'AsyncFunction') {
1137
1548
  fnName = obj.name
1138
- let fnStr = trim(str.split(/=\s*>|(?<=\))\s*{/)[0])
1139
- if (/[()]/.test(fnStr)) {
1140
- value = fnStr.split(/[()]/)[1]
1141
- } else {
1142
- value = fnStr
1143
- }
1144
- value = warnStr('args: ' + trim(value.split('\n').map(trim).join('\n ')))
1145
-
1146
- let docRegResult = /\n(\s*`[^`]+`)/.exec(str)
1549
+ value = getFnArgsStr(str).split('\n').map(i => {
1550
+ let index = i.indexOf('//');
1551
+ let begin = i;
1552
+ let end = '';
1553
+ if (index !== -1) {
1554
+ begin = i.substring(0, index);
1555
+ end = i.substring(index);
1556
+ }
1557
+ if (begin) {
1558
+ return warnStr(begin) + end;
1559
+ }
1560
+ return end;
1561
+ }).join('\n')
1562
+ let docRegResult = /\{\s*\n(\s*`[^`]+`)/.exec(str)
1147
1563
  let doc = docRegResult ? docRegResult[1]: ""
1148
1564
  if (doc) {
1149
1565
  value += `\n` + trimText(doc)
@@ -1154,15 +1570,29 @@ function getInfo(obj, key, type) {
1154
1570
  let len = Object.keys(obj).length;
1155
1571
  value = len > 0 ? ('keys: ' + len) : (str.length <= 64 ? str : `${str.substring(0, 64)}...[length: ${str.length}]`);
1156
1572
  }
1157
- key = key || fnName
1158
- if (key) {
1159
- info.key = key;
1573
+ name = name || fnName
1574
+ if (name) {
1575
+ info.name = name;
1160
1576
  }
1161
1577
  info.value = value;
1162
1578
  return info;
1163
1579
  }
1164
1580
 
1581
+ function getFnArgsStr(str){
1582
+ let fnStr = trim(str.split(/=\s*>|(?<=\))\s*{/)[0])
1583
+ let argsStr
1584
+ if (fnStr.endsWith(')')) {
1585
+ argsStr = fnStr.substring(fnStr.indexOf("(") + 1, fnStr.length - 1)
1586
+ } else {
1587
+ argsStr = fnStr
1588
+ }
1589
+ return trim(argsStr.split('\n').map(trim).filter(i => i).join('\n'))
1590
+ }
1591
+
1165
1592
  function trimText(str) {
1593
+ `
1594
+ 对多行文本进行trim,相当于列操作去除空格
1595
+ `
1166
1596
  if (!trim(str)) {
1167
1597
  return ""
1168
1598
  }
@@ -1258,12 +1688,14 @@ function errorTag(e, tag) {
1258
1688
  if (!tag) {
1259
1689
  return e;
1260
1690
  }
1261
- if (!isError(e)) {
1262
- e = new Error(e)
1263
- e.stack = e.stack.split(/\n/)[0]
1691
+ let newError = new Error(getVl(e.message, e))
1692
+ let stack = newError.stack
1693
+ if (isError(e)) {
1694
+ newError.name = e.name;
1695
+ stack = e.stack;
1264
1696
  }
1265
- e.stack = e.stack + `\n at ${tag}`
1266
- return e
1697
+ newError.stack = stack + `\n at ${tag}`
1698
+ return newError
1267
1699
  }
1268
1700
 
1269
1701
  function getTextComments(text) {
@@ -1315,7 +1747,6 @@ module.exports = {
1315
1747
  regEach,
1316
1748
  runSync,
1317
1749
  linuxAskAndKill,
1318
- fanyi,
1319
1750
  cleanFile,
1320
1751
  fileCleaner,
1321
1752
  getLibDataDir,
@@ -1336,13 +1767,9 @@ module.exports = {
1336
1767
  importG,
1337
1768
  toBigNum,
1338
1769
  objDataFile,
1339
- appendLog,
1340
1770
  createLimitLogger,
1341
1771
  clearConsole,
1342
1772
  fileLock,
1343
- globalLock,
1344
- globalUnLock,
1345
- rmDir,
1346
1773
  eFn,
1347
1774
  removeFirst,
1348
1775
  sleep,
@@ -1357,21 +1784,15 @@ module.exports = {
1357
1784
  splitArray,
1358
1785
  BigNumber,
1359
1786
  range,
1360
- Range,
1361
1787
  getOr,
1362
- $log,
1363
- createLimitLogger2,
1364
1788
  cacheFn,
1365
1789
  isError,
1366
1790
  getOrFn,
1367
- emptyFn,
1368
- setConfig,
1369
1791
  arrayDataFile,
1370
- dataFile,
1371
1792
  strEq,
1372
1793
  wrapRows,
1373
1794
  parseSteps,
1374
- $draft,
1795
+ draftLog,
1375
1796
  infoStr,
1376
1797
  warnStr,
1377
1798
  errorStr,
@@ -1389,8 +1810,26 @@ module.exports = {
1389
1810
  errorTag,
1390
1811
  iobjDataFile,
1391
1812
  iarrayDataFile,
1392
- isArgsMatch,
1813
+ isMatch,
1393
1814
  draftQuery,
1394
1815
  getTextComments,
1395
- trimText
1816
+ trimText,
1817
+ getFnArgsStr,
1818
+ createConsole,
1819
+ setTips,
1820
+ delTips,
1821
+ timer,
1822
+ createConfig,
1823
+ getEditor,
1824
+ setConfig,
1825
+ trimEmptyLine,
1826
+ syncQueue,
1827
+ getLogDir,
1828
+ getConfigDir,
1829
+ getFullPath,
1830
+ parseUniqueName,
1831
+ toUniqueName,
1832
+ isJsirFileName,
1833
+ toJsirFileName,
1834
+ fileJson
1396
1835
  }