Source

src/compiler/core.ts

import * as fs from "fs";
import * as Terser from "terser";
import * as path from "path";
import slash from "slash";
import * as JavaScriptObfuscator from "javascript-obfuscator";
import log from "./log";
import * as uglifycss from "uglifycss";
import * as sass from "sass";
import { exec } from "child_process";
import "../node-localstorage/src/index";
import { config } from "./config";
import * as framework from "./framework";
import filemanager from "./filemanager";
import less from "less";

/**
 * Determine framework is loaded in browser
 */
const isAppLoaded = true;

/**
 * @class Core compiler
 * @author Dimas Lanjaka <dimaslanjaka@gmail.com>
 */
class core {
    static log = log;
    log = log;

    /**
     * config.json
     */
    static config() {
        return config;
    }

    /**
     * filter array after deletion
     * @param arr
     */
    static array_filter(arr: any[]) {
        return arr.filter(function (el) {
            return el != null;
        });
    }

    /**
     * return Asynchronous function (Promise)
     * @param callback
     */
    static async(callback: Function) {
        return new Promise(function (resolve) {
            if (typeof callback == "function") {
                callback();
            }
            resolve(true);
        });
    }

    /**
     * Composer
     * @param dir directory has composer.json
     * @param type
     */
    static composer(dir: string, type: "update" | "install" | "validate" | "upgrade" | "self-update") {
        if (type) {
            exec(
                `cd ${dir} && php libs/bin/composer/composer.phar ${type}`,
                (error: { message: any }, stdout: any, stderr: any) => {
                    if (error) {
                        log.log(log.error(`error: ${error.message}`));
                        return;
                    }
                    if (stderr) {
                        log.log(`stderr: ${stderr}`);
                        return;
                    }
                    log.log(`stdout: ${stdout}`);
                }
            );
        }
    }

    /**
     * Is Node or CommonJS Browser
     */
    static isNode() {
        let isNode = false;
        if (typeof module !== "undefined" && module.exports) {
            isNode = true;
        }
        return isNode;
    }

    /**
     * File Log Output Console
     * @param file
     */
    static filelog(file: string) {
        return path.join(
            core.normalize(path.dirname(file)).replace(core.normalize(process.cwd()), ""),
            path.basename(file)
        );
    }

    /**
     * transform *.browserify to .js
     * @param filename filepath
     */
    static browserify(filename: string) {
        const self = this;
        const exists = fs.existsSync(filename);
        if (exists) {
            const output = filename.toString().replace(/\.browserify\.js/s, ".js");
            exec(`browserify -t [ babelify --presets [ es2015 ] ] ${filename} -o ${output}`);
            log.log(
                `${self.filelog(filename.toString())} > ${self.filelog(output.toString())} ${log.success("success")}`
            );
        }
    }

    /**
     * Compile filename.scss to filename.css and filename.min.css
     * @param filename
     */
    static scss(filename: string) {
        const self = this;
        const exists = fs.existsSync(filename);
        if (exists) {
            if (exists) {
                const output = filename.toString().replace(/\.scss/s, ".css");
                const outputcss = output;
                if (/\.scss$/s.test(filename.toString()) && !/\.min\.scss$/s.test(filename.toString())) {
                    sass.render(
                        {
                            file: filename.toString(),
                            outputStyle: "expanded",
                            outFile: output,
                        },
                        function (err, result) {
                            if (!err) {
                                fs.writeFile(outputcss, result.css.toString(), function (err) {
                                    if (!err) {
                                        log.log(
                                            `${log.chalk().red(self.filelog(filename.toString()))} > ${log
                                                .chalk()
                                                .blueBright(self.filelog(outputcss))} ${log.success("success")}`
                                        );
                                        core.minCSS(output, null);
                                    } else {
                                        log.log(log.error(err.message));
                                    }
                                });
                            }
                        }
                    );
                }
            } else {
                console.error(`${filename} not found`);
            }
        }
    }

    static exists(filename: string): boolean {
        return fs.existsSync(filename);
    }

    static less(filename: string) {
        const self = this;
        const exists = fs.existsSync(filename);
        if (exists) {
            const outputcss = filename.toString().replace(/\.less/s, ".css");
            const source = fs.readFileSync(filename).toString();
            less.render(source, { sourceMap: { sourceMapFileInline: true } })
                .then(function (output) {
                    fs.writeFileSync(outputcss, output.css, { encoding: "utf-8" });
                    log.log(
                        `${log.chalk().hex("#1d365d").bgWhite(self.filelog(filename))} > ${log
                            .chalk()
                            .blueBright(self.filelog(outputcss))} ${log.success("success")}`
                    );
                })
                .catch(function (e) {
                    console.log(
                        `${log.chalk().hex("#1d365d")(self.filelog(filename))} > ${log
                            .chalk()
                            .blueBright(self.filelog(outputcss))} ${log.chalk().redBright("failed")}`
                    );
                });
        }
    }

    /**
     * Compile LESS to CSS
     * @param from less path file
     * @param to to css path file
     * @example compileLESS('src/test.less', 'dist/test.css')
     */
    static compileLESS(from: string, to: string) {
        from = path.join(__dirname, from);
        to = path.join(__dirname, to);
        const self = this;
        fs.readFile(from, function (err, data) {
            if (err) return;
        });
    }

    /**
     * Get root path
     * @returns {string} posix/unix path format
     */
    static root(): string {
        let appDir = slash(path.dirname(require.main.filename)).toString();
        if (/\/libs\/compiler$/s.test(appDir)) {
            let split = appDir.split("/");
            split = split.slice(0, -2);
            appDir = split.join("/");
        }
        return appDir;
    }

    /**
     * Minify all js file to format *.min.js
     * @param {string} folder
     */
    static minify_folder(folder: string) {
        const self = this;
        const js = [];
        const exists = fs.existsSync(folder);

        if (exists && fs.lstatSync(folder).isDirectory()) {
            const read = filemanager.readdir(folder, [], []);
            if (Array.isArray(read)) {
                read.forEach((file) => {
                    if (!/\.min\.js$/s.test(file) && /\.js$/s.test(file)) {
                        js.push(file);
                        //log(file);
                    }
                });
                js.filter(function (el) {
                    return el != null;
                }).forEach(function (file: string) {
                    if (file) self.minJS(file);
                });
            }
        }
    }

    /**
     * Obfuscate Javascript
     * @param {string} filejs
     */
    static obfuscate(filejs: string) {
        const self = this;
        if (!/\.obfuscated\.js$/s.test(filejs) && filejs.endsWith(".js")) {
            const output = filejs.replace(/\.js/s, ".obfuscated.js");
            fs.readFile(
                filejs,
                {
                    encoding: "utf-8",
                },
                function (err, data) {
                    if (!err) {
                        const obfuscationResult = JavaScriptObfuscator.obfuscate(data, {
                            compact: true,
                            controlFlowFlattening: true,
                        });

                        fs.writeFile(output, obfuscationResult.getObfuscatedCode(), function (err) {
                            if (!err) {
                                log.log(`${self.filelog(filejs)} > ${self.filelog(output)} ${log.success("success")}`);
                            }
                        });
                    }
                }
            );
        }
    }

    /**
     * Minify JS into *.min.js version
     * @param {string} file
     */
    static minJS(file: string) {
        const self = this;
        if (!file) {
            return;
        }

        if (/\.min\.js$/s.test(file) || !/\.js$/s.test(file)) {
            log.log(log.error(`${file} minJS Not Allowed`));
            return;
        }
        const min = file.replace(/\.js$/s, ".min.js");
        //log(min);
        if (!fs.existsSync(file)) {
            log.log(log.random(file) + log.error(" not found"));
            return null;
        }
        fs.readFile(
            file,
            {
                encoding: "utf-8",
            },
            function (err, data) {
                if (!err) {
                    fs.writeFile(min, data, function (err) {
                        if (err) {
                            console.error(err);
                        } else {
                            const terserResult = Terser.minify(fs.readFileSync(min, "utf8"), {
                                parse: {
                                    ecma: 8,
                                },
                                compress: {
                                    ecma: 5,
                                    warnings: false,
                                    arrows: false,
                                    collapse_vars: false,
                                    comparisons: false,
                                    computed_props: false,
                                    hoist_funs: false,
                                    hoist_props: false,
                                    hoist_vars: false,
                                    inline: false,
                                    loops: false,
                                    negate_iife: false,
                                    properties: false,
                                    reduce_funcs: false,
                                    reduce_vars: false,
                                    switches: false,
                                    toplevel: false,
                                    typeofs: false,
                                    booleans: true,
                                    if_return: true,
                                    sequences: true,
                                    unused: true,
                                    conditionals: true,
                                    dead_code: true,
                                    evaluate: true,
                                },
                                mangle: {
                                    safari10: true,
                                },
                                output: {
                                    ecma: 5,
                                    comments: false,
                                    ascii_only: true,
                                },
                            });

                            const input = self.filelog(file);
                            const output = self.filelog(min);
                            if (terserResult.error) {
                                log.log(
                                    `${log.chalk().yellow(input)} > ${log.chalk().yellowBright(output)} ${log
                                        .chalk()
                                        .red("fail")}`
                                );

                                if (fs.existsSync(min)) {
                                    filemanager.unlink(min, false);
                                    log.log(
                                        log.chalk().yellowBright(core.filelog(min)) + log.chalk().redBright(" deleted")
                                    );
                                }
                            } else {
                                fs.writeFileSync(min, terserResult.code, "utf8");
                                log.log(
                                    `${log.chalk().yellow(input)} > ${log.chalk().yellowBright(output)} ${log.success(
                                        "success"
                                    )}`
                                );
                            }
                        }
                    });
                } else {
                    log.log(err);
                }
            }
        );
    }

    /**
     * smart delete file
     * @param {string} file
     */
    static unlink(file: string) {
        return filemanager.unlink(file, false);
    }

    /**
     * format path to unix path
     * @param {string} path
     * @returns {string|null}
     */
    static normalize(path: string): string | null {
        return typeof slash(path) == "string" ? slash(path).replace(/\/{2,99}/s, "/") : null;
    }

    /**
     * Determine OS is windows
     */
    static isWin() {
        return process.platform === "win32";
    }

    /**
     * minify css to *.min.css version
     * @param file
     * @param callback
     */
    static minCSS(file: string, callback: Function | null = null) {
        const self = this;
        fs.exists(file, function (exists) {
            if (exists && !/\.min\.css$/s.test(file) && /\.css$/s.test(file)) {
                const min = file.replace(/\.css/s, ".min.css");
                fs.readFile(
                    file,
                    {
                        encoding: "utf-8",
                    },
                    function (err, data) {
                        if (!err) {
                            fs.writeFile(min, data, function (err) {
                                if (!err) {
                                    const minified = uglifycss.processFiles([min], {
                                        maxLineLen: 500,
                                        expandVars: true,
                                    });
                                    fs.writeFile(
                                        min,
                                        minified,
                                        {
                                            encoding: "utf-8",
                                        },
                                        function (err) {
                                            if (!err) {
                                                if (typeof callback != "function") {
                                                    log.log(
                                                        `${log.chalk().blueBright(self.filelog(file))} > ${log
                                                            .chalk()
                                                            .blueBright(self.filelog(min))} ${log
                                                            .chalk()
                                                            .green("success")}`
                                                    );
                                                } else {
                                                    callback(true, file, min);
                                                }
                                            }
                                        }
                                    );
                                } else {
                                    console.error(err);
                                }
                            });
                        } else {
                            console.error(err);
                        }
                    }
                );
            }
        });
    }
}

Object.assign(core, filemanager, framework.dimas);

export = core;