libs/src/compiler/func.ts   F
last analyzed

Complexity

Total Complexity 92
Complexity/F 4.6

Size

Lines of Code 630
Function Count 20

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 425
dl 0
loc 630
rs 2
c 0
b 0
f 0
wmc 92
mnd 72
bc 72
fnc 20
bpm 3.6
cpm 4.6
noi 0

19 Functions

Rating   Name   Duplication   Size   Complexity  
C func.ts ➔ config_builder 0 56 9
A func.ts ➔ random_rgba 0 18 1
A func.ts ➔ asset 0 13 4
B func.ts ➔ random_hex 0 53 2
A func.ts ➔ async 0 12 2
A func.ts ➔ trycatch 0 14 3
A func.ts ➔ readFile 0 10 2
B func.ts ➔ writeFile 0 32 6
B func.ts ➔ fixDeps 0 30 7
A func.ts ➔ array_remove 0 16 1
C func.ts ➔ module_exists 0 45 9
B func.ts ➔ async_exec 0 32 5
A func.ts ➔ getLatestVersion 0 16 3
A func.ts ➔ isOffline 0 14 2
A func.ts ➔ resolve_dir 0 12 3
A func.ts ➔ count 0 10 3
F func.ts ➔ execute 0 58 14
A func.ts ➔ writenow 0 31 3
F func.ts ➔ list_package 0 100 13

How to fix   Complexity   

Complexity

Complex classes like libs/src/compiler/func.ts often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import * as fs from "fs";
2
import { exec, spawn, spawnSync, execSync } from "child_process";
3
import * as path from "path";
4
import * as Process from "process";
5
import * as http from "http";
6
import { dirname } from "path";
7
import { localStorage } from "../node-localstorage/index";
8
//const { promisify } = require("util");
9
import { promisify } from "util";
10
import observatory from "../observatory/lib/observatory";
11
import chalk from "chalk";
12
import dns from "dns";
13
import log from "./log";
14
import sorter from "./sorter";
15
require("./consoler");
16
17
/**
18
 * Check connectivity
19
 */
20
export function isOffline() {
21
  var res = null;
22
  dns.resolve("www.google.com", function (err) {
23
    if (err) {
24
      res = true;
25
    } else {
26
      res = false;
27
    }
28
  });
29
  return res;
30
}
31
32
/**
33
 * Locate asset file
34
 * @param file
35
 */
36
export function asset(file: string) {
37
  file = file.toString().trim().replace(/\.\//gm, "");
38
  if (fs.existsSync(file)) {
39
    return file;
40
  } else if (fs.existsSync(`./${file}`)) {
41
    return `./${file}`;
42
  } else if (fs.existsSync(path.join(Process.cwd(), file))) {
43
    return path.join(Process.cwd(), file);
44
  }
45
}
46
47
/**
48
 * Smart read file
49
 * @param file
50
 */
51
export function readFile(file: string) {
52
  if (fs.existsSync(file)) {
53
    return fs.readFileSync(file).toString();
54
  }
55
  return `${file} not found`;
56
}
57
58
/**
59
 * write package
60
 * @param packageObject
61
 */
62
export function writenow(packageObject: packagejson) {
63
  var sorterFound = trycatch(function () {
64
    return true;
65
  });
66
67
  if (
68
    packageObject &&
69
    typeof packageObject == "object" &&
70
    count(packageObject) > 0
71
  ) {
72
    if (sorterFound) {
73
      fs.writeFileSync(
74
        path.join(__dirname, "package.json"),
75
        JSON.stringify(sorter.reorder(packageObject), null, 4),
76
        { encoding: "utf-8" }
77
      );
78
    } else {
79
      console.log("sorter not found, using default...");
80
      fs.writeFileSync(
81
        path.join(__dirname, "package.json"),
82
        JSON.stringify(packageObject, null, 4),
83
        { encoding: "utf-8" }
84
      );
85
    }
86
  } else {
87
    console.warn("not object", typeof packageObject);
88
  }
89
}
90
91
export function random_hex(familiar?: boolean) {
92
  if (familiar) {
93
    var choose = {
94
      aqua: "#00ffff",
95
      azure: "#f0ffff",
96
      beige: "#f5f5dc",
97
      black: "#000000",
98
      blue: "#0000ff",
99
      brown: "#a52a2a",
100
      cyan: "#00ffff",
101
      darkblue: "#00008b",
102
      darkcyan: "#008b8b",
103
      darkgrey: "#a9a9a9",
104
      darkgreen: "#006400",
105
      darkkhaki: "#bdb76b",
106
      darkmagenta: "#8b008b",
107
      darkolivegreen: "#556b2f",
108
      darkorange: "#ff8c00",
109
      darkorchid: "#9932cc",
110
      darkred: "#8b0000",
111
      darksalmon: "#e9967a",
112
      darkviolet: "#9400d3",
113
      fuchsia: "#ff00ff",
114
      gold: "#ffd700",
115
      green: "#008000",
116
      indigo: "#4b0082",
117
      khaki: "#f0e68c",
118
      lightblue: "#add8e6",
119
      lightcyan: "#e0ffff",
120
      lightgreen: "#90ee90",
121
      lightgrey: "#d3d3d3",
122
      lightpink: "#ffb6c1",
123
      lightyellow: "#ffffe0",
124
      lime: "#00ff00",
125
      magenta: "#ff00ff",
126
      maroon: "#800000",
127
      navy: "#000080",
128
      olive: "#808000",
129
      orange: "#ffa500",
130
      pink: "#ffc0cb",
131
      purple: "#800080",
132
      violet: "#800080",
133
      red: "#ff0000",
134
      silver: "#c0c0c0",
135
      white: "#ffffff",
136
      yellow: "#ffff00",
137
    };
138
    var values = Object.values(choose);
139
    return values[Math.floor(Math.random() * values.length)];
140
  } else {
141
    return (
142
      "#" + (0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6)
143
    );
144
  }
145
}
146
147
/**
148
 * Write file recursive
149
 * @param path
150
 * @param contents
151
 * @param cb
152
 */
153
export function writeFile(
154
  path: string | number | Buffer | import("url").URL,
155
  contents: any,
156
  cb?: { (arg0: any): any; (err: NodeJS.ErrnoException): void }
157
) {
158
  try {
159
    contents = JSON.parse(contents.toString());
160
  } catch (error) {}
161
  if (typeof contents == "object" || Array.isArray(contents)) {
162
    contents = JSON.stringify(contents, null, 2);
163
  }
164
  resolve_dir(dirname(path.toString()));
165
166
  if (typeof contents == "string") {
167
    if (fs.existsSync(dirname(path.toString()))) {
168
      if (typeof cb == "function") {
169
        fs.writeFile(path.toString(), contents.toString(), cb);
170
      } else {
171
        fs.writeFileSync(path.toString(), contents.toString());
172
      }
173
      return true;
174
    }
175
  }
176
  console.error(`contents must be type string, instead of ${typeof contents}`);
177
  return false;
178
}
179
180
/**
181
 * Resolve directory, create if not exists
182
 * @param path
183
 */
184
export function resolve_dir(path: string) {
185
  if (!fs.existsSync(dirname(path.toString()))) {
186
    resolve_dir(dirname(path.toString()));
187
  }
188
189
  if (!fs.existsSync(path.toString())) {
190
    fs.mkdirSync(path.toString());
191
  }
192
}
193
194
/**
195
 * Random RGB color
196
 */
197
export function random_rgba() {
198
  var o = Math.round,
199
    r = Math.random,
200
    s = 255;
201
  return (
202
    "rgba(" +
203
    o(r() * s) +
204
    "," +
205
    o(r() * s) +
206
    "," +
207
    o(r() * s) +
208
    "," +
209
    r().toFixed(1) +
210
    ")"
211
  );
212
}
213
214
/**
215
 * Fix dependencies and devDependencies
216
 * @param pkg
217
 */
218
export async function fixDeps(pkg: packagejson) {
219
  const ori = pkg;
220
  if (!pkg.hasOwnProperty("dependencies")) {
221
    console.error("package.json does not have dependencies");
222
    throw pkg;
223
  }
224
  for (const key in pkg.dependencies) {
225
    if (pkg.dependencies.hasOwnProperty(key)) {
226
      const version = pkg.dependencies[key];
227
      var dups = Object.keys(pkg.devDependencies).includes(key);
228
      if (dups) {
229
        console.warn(`${key} duplicate, removing...`);
230
        delete pkg.dependencies[key];
231
      }
232
      if (key.includes("@types")) {
233
        console.warn(`${key} is typehinting module, moving to dev...`);
234
        pkg.devDependencies[key] = version;
235
        delete pkg.dependencies[key];
236
      }
237
    }
238
  }
239
  if (typeof pkg == "object" && Object.keys(pkg).length) {
240
    return pkg;
241
  }
242
  return ori;
243
}
244
245
var execute_is_running = false;
246
var execute_dump = [];
247
/**
248
 * Execute command schedule
249
 * @param cmds
250
 * @requires NodeJS
251
 * @param callback
252
 */
253
export function execute(
254
  cmd: string | null,
255
  callback: (arg0: boolean, arg1: string | Error) => any = null
256
): any {
257
  if (typeof cmd != "string") {
258
    console.log(typeof cmd + " not string");
259
    return;
260
  }
261
  if (execute_is_running === false) {
262
    execute_is_running = true;
263
    exec(cmd.trim(), (error, stdout, stderr) => {
264
      setTimeout(function () {
265
        execute_is_running = false;
266
      }, 500);
267
      if (error instanceof Error) {
268
        if (error.hasOwnProperty("code")) {
269
          console.error(error.code);
270
        } else if (error.hasOwnProperty("message")) {
271
          console.error(error.message);
272
        } else {
273
          console.error(error);
274
        }
275
        if (typeof callback == "function") {
276
          if (stdout) {
277
            callback(false, new Error(stdout));
278
          } else if (stderr) {
279
            callback(false, new Error(stderr));
280
          } else {
281
            callback(false, new Error("error"));
282
          }
283
        }
284
        console.error(error);
285
      } else {
286
        if (typeof callback != "function") {
287
          console.log("callback is not function", stdout ? stdout : stderr);
288
        } else {
289
          if (stdout) {
290
            callback(true, stdout);
291
          } else if (stderr) {
292
            callback(false, stderr);
293
          }
294
        }
295
      }
296
    });
297
  } else {
298
    if (execute_dump && !execute_dump.includes(cmd)) {
299
      console.warn("executor still running, please wait");
300
      execute_dump.push(cmd);
301
    }
302
    setTimeout(function () {
303
      execute(cmd, callback);
304
    }, 1000);
305
  }
306
}
307
308
/**
309
 * check if module exists
310
 * @param tmodule
311
 * @requires node
312
 */
313
export function module_exists(
314
  tmodule: Array<string> | string,
315
  global: boolean = false,
316
  dump: boolean = false
317
) {
318
  const test = function (tmodule: string, global: boolean) {
319
    var result = null;
320
    execute(
321
      ("npm list -json -depth=0 " + (global ? "-g" : "")).trim(),
322
      function (error, message) {
323
        if (message instanceof Error || !error) {
324
          result = false;
325
        } else {
326
          try {
327
            var json: npmlist = JSON.parse(message);
328
329
            result = Object.keys(json.dependencies).includes(tmodule);
330
            if (dump) {
331
              console.log(
332
                `${tmodule} is ${result ? "installed" : "not installed"}`
333
              );
334
            }
335
          } catch (error) {
336
            result = false;
337
          }
338
        }
339
      }
340
    );
341
    return result;
342
  };
343
  if (typeof tmodule == "string") {
344
    return test(tmodule, global);
345
  }
346
  var tests = [];
347
  if (Array.isArray(tmodule)) {
348
    tmodule.forEach(function (single) {
349
      tests.push(test(single, global));
350
    });
351
    return tests.every(Boolean) ? true : false;
352
  }
353
}
354
355
/**
356
 * Configuration builder
357
 */
358
export function config_builder() {
359
  const config = require(asset("./config.json"));
360
  var parsed = JSON.stringify(parseConfig(config, true), null, 2);
361
362
  if (fs.existsSync(asset("./libs"))) {
363
    console.error("Libs folder not exists, exiting config builder.");
364
    return null;
365
  }
366
367
  fs.writeFileSync(asset("./config-backup.json"), parsed, {
368
    encoding: "utf-8",
369
  });
370
  var str = fs
371
    .readFileSync(asset("./libs/src/compiler/config.ts"), {
372
      encoding: "utf-8",
373
    })
374
    .toString()
375
    .replace(/\s|\t/gm, " ");
376
377
  parsed = JSON.stringify(parseConfig(config, false), null, 2)
378
    .replace(/\"string\"/gm, "string")
379
    .replace(/\"boolean\"/gm, "boolean")
380
    .replace(/\"number\"/gm, "number");
381
382
  var regex = /config\:((.|\n)*)\=\srequire/gm;
383
  var mod = str.replace(regex, `config:${parsed} = require`);
384
  fs.writeFileSync(asset("./libs/src/compiler/config.ts"), mod);
385
386
  function parseConfig(
387
    config: { [key: string]: any },
388
    usingExclude: boolean = null
389
  ) {
390
    const excluded = function (key: string) {
391
      return ["vscode"].indexOf(key) == -1;
392
    };
393
    for (const key in config) {
394
      if (config.hasOwnProperty(key)) {
395
        if (!excluded(key) && usingExclude) {
396
          continue;
397
        }
398
        const element = config[key];
399
        const type = typeof element;
400
        if (["number", "string", "boolean"].indexOf(type) != -1) {
401
          config[key] = type;
402
        } else if (type == "object") {
403
          config[key] = parseConfig(config[key]);
404
        } else if (Array.isArray(config[key])) {
405
          config[key].forEach(parseConfig);
406
        }
407
      }
408
    }
409
    return config;
410
  }
411
}
412
413
/**
414
 * Remove source sub elements array if exists in target array
415
 * ```js
416
 * // If I have this array:
417
 * var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
418
 * // and this one:
419
 * var toRemove = ['b', 'c', 'g'];
420
 * console.log(array_remove(myArray, toRemove)); // => ['a', 'd', 'e', 'f']
421
 * ```
422
 * @param source
423
 * @param target
424
 */
425
export function array_remove(source: Array<string>, target: Array<string>) {
426
  return source.filter(function (el) {
427
    return !target.includes(el);
428
  });
429
}
430
431
/**
432
 * Array object counter
433
 * @param objarr
434
 */
435
export function count(objarr: any[] | object) {
436
  if (Array.isArray(objarr)) {
437
    return objarr.length;
438
  } else if (typeof objarr == "object") {
439
    return Object.keys(objarr).length;
440
  }
441
}
442
443
/**
444
 * Do trycatch
445
 * @param callback
446
 */
447
export function trycatch(callback: () => any) {
448
  try {
449
    var test = callback();
450
    if (typeof test == "boolean") {
451
      return test;
452
    }
453
    return true;
454
  } catch (error) {
455
    return false;
456
  }
457
}
458
459
/**
460
 * Async current function
461
 * @param callback
462
 */
463
export function async(callback: Function) {
464
  return new Promise(async function (resolve, reject) {
465
    if (typeof callback == "function") {
466
      await callback();
467
      resolve(true);
468
    } else {
469
      reject(false);
470
    }
471
  });
472
}
473
474
/**
475
 * Get latest version of packages
476
 * @param key
477
 */
478
export function getLatestVersion(key: string) {
479
  execute(`npm show ${key} version`, function (error, message) {
480
    if (message instanceof Error || !error) {
481
      console.error(error);
482
    }
483
    try {
484
      writeFile(
485
        `./tmp/npm/packages/${key}/latest.json`,
486
        JSON.parse(message.toString())
487
      );
488
    } catch (error) {}
489
  });
490
}
491
492
/**
493
 * Async execute commands
494
 * @param commands
495
 * @param callback callback for each command return
496
 */
497
export function async_exec(
498
  commands: string[],
499
  callback: (stdout: string, stderr: string, isLast: boolean) => any
500
) {
501
  if (!Array.isArray(commands)) {
502
    console.error("commands must be instance of array");
503
    return null;
504
  }
505
  const executor = promisify(exec);
506
  const exec2 = async function () {
507
    let script: string;
508
    let index = 0;
509
    for await (script of commands) {
510
      try {
511
        let { stdout, stderr } = await executor(script);
512
        if (typeof callback == "function") {
513
          callback(stdout, stderr, !--commands.length);
514
        }
515
      } catch (e) {
516
        console.error(e);
517
      }
518
      index++;
519
    }
520
    return true;
521
  };
522
  return exec2;
523
}
524
525
localStorage.removeItem("list_package_latest");
526
localStorage.removeItem("list_package");
527
/**
528
 * Get list packages and fetch latest version
529
 */
530
export function list_package() {
531
  if (isOffline()) {
532
    console.error("Are you offline");
533
    return;
534
  }
535
  if (!localStorage.getItem("list_package")) {
536
    localStorage.setItem("list_package", "true");
537
    var global = exec("npm list -json -depth=0 -g");
538
    global.stdout.on("data", function (data) {
539
      writeFile("./tmp/npm/global.json", data);
540
    });
541
    global.stderr.on("data", function (data) {
542
      writeFile("./tmp/npm/global-error.json", data);
543
    });
544
545
    var local = exec("npm list -json -depth=0");
546
    local.stdout.on("data", function (data) {
547
      try {
548
        data = JSON.parse(data);
549
        if (fs.existsSync("./tmp/npm/local.json")) {
550
          var old = JSON.parse(
551
            fs.readFileSync("./tmp/npm/local.json").toString()
552
          );
553
          if (old) {
554
            data = Object.assign({}, data, old);
555
          }
556
        }
557
        if (
558
          data.hasOwnProperty("dependencies") &&
559
          !localStorage.getItem("list_package_latest")
560
        ) {
561
          localStorage.setItem("list_package_latest", "1");
562
          const executor = promisify(exec);
563
          const dependencies = Object.keys(data.dependencies);
564
          const checkLatest = async function () {
565
            //observatory.settings({ prefix: chalk.cyan("[Buzz] ") });
566
            var task = observatory.add("Fetch latest version");
567
568
            for await (const key of dependencies) {
569
              if (data.dependencies.hasOwnProperty(key)) {
570
                try {
571
                  var { stdout } = await executor(
572
                    "npm show " + key + " version"
573
                  );
574
                  data.dependencies[key].latest = stdout.trim();
575
                  if (!key.includes("@types")) {
576
                    var { stdout } = await executor(
577
                      "npm view @types/" + key + " version -json"
578
                    );
579
                    if (stdout.trim().length) {
580
                      data.dependencies[key].types = {
581
                        name: "@types/" + key,
582
                        version: stdout.trim(),
583
                      };
584
                    } else {
585
                      data.dependencies[key].types = {
586
                        name: null,
587
                        version: null,
588
                      };
589
                    }
590
                  }
591
                  writeFile("./tmp/npm/local.json", data);
592
                  task
593
                    .status(
594
                      (
595
                        (dependencies.length * 100) /
596
                        Object.keys(data.dependencies).length
597
                      )
598
                        .toFixed(3)
599
                        .toString() + "%"
600
                    )
601
                    .details(chalk.hex(random_hex(true)).underline(key));
602
                } catch (error) {
603
                  console.error(error);
604
                }
605
                if (!--dependencies.length) {
606
                  localStorage.removeItem("list_package_latest");
607
                  task.done("Complete");
608
                }
609
              }
610
            }
611
          };
612
613
          checkLatest()
614
            .catch(function (err) {})
615
            .then(function () {
616
              localStorage.removeItem("list_package");
617
            });
618
        }
619
      } catch (error) {}
620
    });
621
    local.stderr.on("data", function (data) {
622
      writeFile("./tmp/npm/local-error.json", data);
623
    });
624
  } else {
625
    console.warn(
626
      "process for list_package is locked, if previous runner got error, please fix it using `node index.js fix`"
627
    );
628
  }
629
}
630
631
/*
632
results.local = data;
633
      if (count(results)) {
634
        for (const key in results.local) {
635
          if (results.local.hasOwnProperty(key)) {
636
            const version = results.local[key];
637
            getLatestVersion(key);
638
          }
639
        }
640
        writeFile("./tmp/npm/installed.json", results);
641
        timer_run = null;
642
      }
643
      */
644