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

Complexity

Total Complexity 60
Complexity/F 2.86

Size

Lines of Code 549
Function Count 21

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 343
dl 0
loc 549
rs 3.6
c 0
b 0
f 0
wmc 60
mnd 39
bc 39
fnc 21
bpm 1.8571
cpm 2.8571
noi 0

21 Functions

Rating   Name   Duplication   Size   Complexity  
A core.localStorage 0 5 1
A core.isNode 0 10 2
A core.config 0 5 1
B core.readdir 0 42 6
A core.array_filter 0 7 1
A core.filelog 0 11 1
A core.async 0 10 2
A core.composer 0 22 4
A core.browserify 0 15 2
A core.normalize 0 10 2
A core.compileLESS 0 13 2
A core.exists 0 3 1
C core.minJS 0 102 8
A core.unlink 0 7 1
A core.obfuscate 0 30 4
B core.scss 0 48 6
A core.less 0 29 2
B core.minCSS 0 56 6
A core.root 0 13 2
A core.isWin 0 6 1
A core.minify_folder 0 22 5

How to fix   Complexity   

Complexity

Complex classes like libs/src/compiler/core.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 * as Terser from "terser";
3
import * as path from "path";
4
import slash from "slash";
5
import * as JavaScriptObfuscator from "javascript-obfuscator";
6
import log from "./log";
7
import * as uglifycss from "uglifycss";
8
import * as sass from "sass";
9
import { exec } from "child_process";
10
import { LocalStorage } from "../node-localstorage/index";
11
import configuration from "./config";
12
import * as framework from "./framework";
13
import filemanager from "./filemanager";
14
import less from "less";
15
16
/**
17
 * @class Core compiler
18
 * @author Dimas Lanjaka <[email protected]>
19
 */
20
class core {
21
  static log = log;
22
  log = log;
23
  /**
24
   * config.json
25
   */
26
  static config() {
27
    return configuration;
28
  }
29
  /**
30
   * filter array after deletion
31
   * @param arr
32
   */
33
  static array_filter(arr: any[]) {
34
    return arr.filter(function (el) {
35
      return el != null;
36
    });
37
  }
38
  /**
39
   * return Asynchronous function (Promise)
40
   * @param callback
41
   */
42
  static async(callback: Function) {
43
    return new Promise(function (resolve) {
44
      if (typeof callback == "function") {
45
        callback();
46
      }
47
      resolve(true);
48
    });
49
  }
50
  /**
51
   * localStorage NodeJS Version
52
   */
53
  static localStorage() {
54
    return new LocalStorage(`${this.root()}/tmp/storage`);
55
  }
56
  /**
57
   * Composer
58
   * @param dir directory has composer.json
59
   * @param type
60
   */
61
  static composer(
62
    dir: string,
63
    type: "update" | "install" | "validate" | "upgrade" | "self-update"
64
  ) {
65
    if (type) {
66
      exec(
67
        `cd ${dir} && php libs/bin/composer/composer.phar ${type}`,
68
        (error: { message: any }, stdout: any, stderr: any) => {
69
          if (error) {
70
            log.log(log.error(`error: ${error.message}`));
71
            return;
72
          }
73
          if (stderr) {
74
            log.log(`stderr: ${stderr}`);
75
            return;
76
          }
77
          log.log(`stdout: ${stdout}`);
78
        }
79
      );
80
    }
81
  }
82
83
  /**
84
   * @param dir
85
   * @param [filelist]
86
   * @return
87
   */
88
  static readdir(
89
    dir: string,
90
    filelist: string[] = null,
91
    exclude: Array<string | RegExp> = null
92
  ): Array<any> {
93
    if (!dir) return null;
94
    var self = this;
95
    if (!dir.toString().endsWith("/")) {
96
      dir += "/";
97
    }
98
    var files = fs.readdirSync(dir);
99
    filelist = filelist || [];
100
    files.forEach(function (file) {
101
      if (fs.statSync(dir + file).isDirectory()) {
102
        filelist = self.readdir(dir + file + "/", filelist, exclude);
103
      } else {
104
        filelist.push(path.resolve(dir + file));
105
      }
106
    });
107
    if (exclude && exclude.length) {
108
      exclude.forEach(function (ex) {
109
        filelist = filelist.filter(function (item) {
110
          var allow = null;
111
          if (ex instanceof RegExp) {
112
            allow = !ex.test(item);
113
          } else {
114
            var matches = item.indexOf(ex) !== -1;
115
            allow = !matches;
116
          }
117
          //console.log(allow, ex);
118
          return allow;
119
        });
120
      });
121
    }
122
123
    return filelist;
124
  }
125
126
  /**
127
   * Is Node or CommonJS Browser
128
   */
129
  static isNode() {
130
    var isNode = false;
131
    if (typeof module !== "undefined" && module.exports) {
132
      isNode = true;
133
    }
134
    return isNode;
135
  }
136
137
  /**
138
   * File Log Output Console
139
   * @param file
140
   */
141
  static filelog(file: string) {
142
    return path.join(
143
      core
144
        .normalize(path.dirname(file))
145
        .replace(core.normalize(process.cwd()), ""),
146
      path.basename(file)
147
    );
148
  }
149
150
  /**
151
   * transform *.browserify to .js
152
   * @param filename filepath
153
   */
154
  static browserify(filename: string) {
155
    const self = this;
156
    const exists = fs.existsSync(filename);
157
    if (exists) {
158
      const output = filename.toString().replace(/\.browserify/s, ".js");
159
      exec(`browserify ${filename} -o ${output}`);
160
      log.log(
161
        `${self.filelog(filename.toString())} > ${self.filelog(
162
          output.toString()
163
        )} ${log.success("success")}`
164
      );
165
    }
166
  }
167
168
  /**
169
   * Compile filename.scss to filename.css and filename.min.css
170
   * @param filename
171
   */
172
  static scss(filename: string) {
173
    const self = this;
174
    const exists = fs.existsSync(filename);
175
    if (exists) {
176
      if (exists) {
177
        var output = filename.toString().replace(/\.scss/s, ".css");
178
        var outputcss = output;
179
        if (
180
          /\.scss$/s.test(filename.toString()) &&
181
          !/\.min\.scss$/s.test(filename.toString())
182
        ) {
183
          sass.render(
184
            {
185
              file: filename.toString(),
186
              outputStyle: "expanded",
187
              outFile: output,
188
            },
189
            function (err, result) {
190
              if (!err) {
191
                fs.writeFile(outputcss, result.css.toString(), function (err) {
192
                  if (!err) {
193
                    log.log(
194
                      `${log
195
                        .chalk()
196
                        .red(
197
                          self.filelog(filename.toString())
198
                        )} > ${log
199
                        .chalk()
200
                        .blueBright(self.filelog(outputcss))} ${log.success(
201
                        "success"
202
                      )}`
203
                    );
204
                    core.minCSS(output, null);
205
                  } else {
206
                    log.log(log.error(err.message));
207
                  }
208
                });
209
              }
210
            }
211
          );
212
        }
213
      } else {
214
        console.error(`${filename} not found`);
215
      }
216
    }
217
  }
218
219
  static exists(filename: string): boolean {
220
    return fs.existsSync(filename);
221
  }
222
223
  static less(filename: string) {
224
    const self = this;
225
    const exists = fs.existsSync(filename);
226
    if (exists) {
227
      var outputcss = filename.toString().replace(/\.less/s, ".css");
228
      var source = fs.readFileSync(filename).toString();
229
      less
230
        .render(source, { sourceMap: { sourceMapFileInline: true } })
231
        .then(function (output) {
232
          fs.writeFileSync(outputcss, output.css, { encoding: "utf-8" });
233
          log.log(
234
            `${log
235
              .chalk()
236
              .hex("#1d365d")
237
              .bgWhite(self.filelog(filename))} > ${log
238
              .chalk()
239
              .blueBright(self.filelog(outputcss))} ${log.success("success")}`
240
          );
241
        })
242
        .catch(function (e) {
243
          console.log(
244
            `${log.chalk().hex("#1d365d")(
245
              self.filelog(filename)
246
            )} > ${log
247
              .chalk()
248
              .blueBright(self.filelog(outputcss))} ${log
249
              .chalk()
250
              .redBright("failed")}`
251
          );
252
        });
253
    }
254
  }
255
256
  /**
257
   * Compile LESS to CSS
258
   * @param from less path file
259
   * @param to to css path file
260
   * @example compileLESS('src/test.less', 'dist/test.css')
261
   */
262
  static compileLESS(from: string, to: string) {
263
    from = path.join(__dirname, from);
264
    to = path.join(__dirname, to);
265
    var self = this;
266
    fs.readFile(from, function (err, data) {
267
      if (err) return;
268
    });
269
  }
270
271
  /**
272
   * Get root path
273
   * @returns {string} posix/unix path format
274
   */
275
  static root(): string {
276
    var appDir = slash(path.dirname(require.main.filename)).toString();
277
    if (/\/libs\/compiler$/s.test(appDir)) {
278
      var split = appDir.split("/");
279
      split = split.slice(0, -2);
280
      appDir = split.join("/");
281
    }
282
    return appDir;
283
  }
284
285
  /**
286
   * Minify all js file to format *.min.js
287
   * @param {string} folder
288
   */
289
  static minify_folder(folder: string) {
290
    var self = this;
291
    var js = new Array();
292
    fs.exists(folder, function (exists) {
293
      if (exists && fs.lstatSync(folder).isDirectory()) {
294
        var read = self.readdir(folder, [], []);
295
        if (Array.isArray(read)) {
296
          read.forEach((file) => {
297
            if (!/\.min\.js$/s.test(file) && /\.js$/s.test(file)) {
298
              js.push(file);
299
              //log(file);
300
            }
301
          });
302
          js.filter(function (el) {
303
            return el != null;
304
          }).forEach(function (file: string) {
305
            if (file) self.minJS(file);
306
          });
307
        }
308
      }
309
    });
310
  }
311
312
  /**
313
   * Obfuscate Javascript
314
   * @param {string} filejs
315
   */
316
  static obfuscate(filejs: string) {
317
    const self = this;
318
    if (!/\.obfuscated\.js$/s.test(filejs) && filejs.endsWith(".js")) {
319
      var output = filejs.replace(/\.js/s, ".obfuscated.js");
320
      fs.readFile(
321
        filejs,
322
        {
323
          encoding: "utf-8",
324
        },
325
        function (err, data) {
326
          if (!err) {
327
            var obfuscationResult = JavaScriptObfuscator.obfuscate(data, {
328
              compact: true,
329
              controlFlowFlattening: true,
330
            });
331
332
            fs.writeFile(
333
              output,
334
              obfuscationResult.getObfuscatedCode(),
335
              function (err) {
336
                if (!err) {
337
                  log.log(
338
                    `${self.filelog(filejs)} > ${self.filelog(
339
                      output
340
                    )} ${log.success("success")}`
341
                  );
342
                }
343
              }
344
            );
345
          }
346
        }
347
      );
348
    }
349
  }
350
351
  /**
352
   * Minify JS into *.min.js version
353
   * @param {string} file
354
   */
355
  static minJS(file: string) {
356
    const self = this;
357
    if (!file) {
358
      return;
359
    }
360
361
    if (/\.min\.js$/s.test(file) || !/\.js$/s.test(file)) {
362
      log.log(log.error(`${file} minJS Not Allowed`));
363
      return;
364
    }
365
    var min = file.replace(/\.js$/s, ".min.js");
366
    //log(min);
367
    if (!fs.existsSync(file)) {
368
      log.log(log.random(file) + log.error(" not found"));
369
      return null;
370
    }
371
    fs.readFile(
372
      file,
373
      {
374
        encoding: "utf-8",
375
      },
376
      function (err, data) {
377
        if (!err) {
378
          fs.writeFile(min, data, function (err) {
379
            if (err) {
380
              console.error(err);
381
            } else {
382
              const terserResult = Terser.minify(fs.readFileSync(min, "utf8"), {
383
                parse: {
384
                  ecma: 8,
385
                },
386
                compress: {
387
                  ecma: 5,
388
                  warnings: false,
389
                  arrows: false,
390
                  collapse_vars: false,
391
                  comparisons: false,
392
                  computed_props: false,
393
                  hoist_funs: false,
394
                  hoist_props: false,
395
                  hoist_vars: false,
396
                  inline: false,
397
                  loops: false,
398
                  negate_iife: false,
399
                  properties: false,
400
                  reduce_funcs: false,
401
                  reduce_vars: false,
402
                  switches: false,
403
                  toplevel: false,
404
                  typeofs: false,
405
                  booleans: true,
406
                  if_return: true,
407
                  sequences: true,
408
                  unused: true,
409
                  conditionals: true,
410
                  dead_code: true,
411
                  evaluate: true,
412
                },
413
                mangle: {
414
                  safari10: true,
415
                },
416
                output: {
417
                  ecma: 5,
418
                  comments: false,
419
                  ascii_only: true,
420
                },
421
              });
422
423
              var input = self.filelog(file);
424
              var output = self.filelog(min);
425
              if (terserResult.error) {
426
                log.log(
427
                  `${log.chalk().yellow(input)} > ${log
428
                    .chalk()
429
                    .yellowBright(output)} ${log.chalk().red("fail")}`
430
                );
431
                fs.exists(min, function (ex) {
432
                  if (ex) {
433
                    filemanager.unlink(min, false);
434
                    log.log(
435
                      log.chalk().yellowBright(core.filelog(min)) +
436
                        log.chalk().redBright(" deleted")
437
                    );
438
                  }
439
                });
440
              } else {
441
                fs.writeFileSync(min, terserResult.code, "utf8");
442
                log.log(
443
                  `${log.chalk().yellow(input)} > ${log
444
                    .chalk()
445
                    .yellowBright(output)} ${log.success("success")}`
446
                );
447
              }
448
            }
449
          });
450
        } else {
451
          log.log(err);
452
        }
453
      }
454
    );
455
  }
456
457
  /**
458
   * smart delete file
459
   * @param {string} file
460
   */
461
  static unlink(file: string) {
462
    return filemanager.unlink(file, false);
463
  }
464
465
  /**
466
   * format path to unix path
467
   * @param {string} path
468
   * @returns {string|null}
469
   */
470
  static normalize(path: string): string | null {
471
    return typeof slash(path) == "string"
472
      ? slash(path).replace(/\/{2,99}/s, "/")
473
      : null;
474
  }
475
476
  /**
477
   * Determine OS is windows
478
   */
479
  static isWin() {
480
    return process.platform === "win32";
481
  }
482
483
  /**
484
   * minify css to *.min.css version
485
   * @param file
486
   * @param callback
487
   */
488
  static minCSS(file: string, callback: Function | null = null) {
489
    const self = this;
490
    fs.exists(file, function (exists) {
491
      if (exists && !/\.min\.css$/s.test(file) && /\.css$/s.test(file)) {
492
        var min = file.replace(/\.css/s, ".min.css");
493
        fs.readFile(
494
          file,
495
          {
496
            encoding: "utf-8",
497
          },
498
          function (err, data) {
499
            if (!err) {
500
              fs.writeFile(min, data, function (err) {
501
                if (!err) {
502
                  var minified = uglifycss.processFiles([min], {
503
                    maxLineLen: 500,
504
                    expandVars: true,
505
                  });
506
                  fs.writeFile(
507
                    min,
508
                    minified,
509
                    {
510
                      encoding: "utf-8",
511
                    },
512
                    function (err) {
513
                      if (!err) {
514
                        if (typeof callback != "function") {
515
                          log.log(
516
                            `${log
517
                              .chalk()
518
                              .blueBright(
519
                                self.filelog(file)
520
                              )} > ${log
521
                              .chalk()
522
                              .blueBright(
523
                                self.filelog(min)
524
                              )} ${log.chalk().green("success")}`
525
                          );
526
                        } else {
527
                          callback(true, file, min);
528
                        }
529
                      }
530
                    }
531
                  );
532
                } else {
533
                  log.log(log.chalk().red(err));
534
                }
535
              });
536
            } else {
537
              log.log(log.chalk().red(err));
538
            }
539
          }
540
        );
541
      }
542
    });
543
  }
544
}
545
546
Object.assign(core, filemanager, framework.dimas);
547
548
export = core;
549