"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.strToBuf = exports.fileToZip = exports.getFiles = exports.copyRecursiveSync = exports.copyFileSync = exports.copy = exports.copyFiles = exports.cleanDir = exports.removeFile = exports.writeFile = exports.generatedFileContents = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const lodash_1 = require("lodash");
const junk = __importStar(require("../util/junk"));
const glob_1 = __importDefault(require("glob"));
const jszip_1 = __importDefault(require("jszip"));
const buffer_1 = require("buffer");
exports.generatedFileContents = {}; // generated files for incrmental build
async function generateFiles(appFileData, srcDir, dstDir, ctx) {
    const filesGenerated = [];
    // Generating file by template and data
    for (const file in appFileData) {
        const fileNameList = file.split('|');
        const srcFileName = fileNameList[0];
        const outFileName = fileNameList[1] || fileNameList[0];
        const tplStr = await fs_extra_1.default.readFile(path_1.default.join(srcDir, srcFileName), {
            encoding: 'utf8',
        });
        let generatedCode = (0, lodash_1.template)(tplStr, { interpolate: /<%=([\s\S]+?)%>/ })(appFileData[file]);
        // 混合模式下，引用公共路径要多增加一层，并加多一层命名
        // 混合子包直接在对应目录上生成，而不采用替换的方式 @royhyang
        // if (ctx?.isMixMode && ctx?.rootPath) {
        //   generatedCode = generatedCode.replace(
        //     /..\/..\/..\/common\//g,
        //     '../../../../common/'
        //   )
        //   generatedCode = generatedCode.replace(
        //     /..\/..\/..\/app\//g,
        //     '../../../../app/'
        //   )
        // }
        const outFile = path_1.default.resolve(dstDir, outFileName.replace(/\.tpl$/, ''));
        if (await writeFile(outFile, generatedCode)) {
            filesGenerated.push(outFileName);
        }
    }
    return filesGenerated;
}
exports.default = generateFiles;
async function writeFile(outFile, content) {
    var _a;
    const generated = (_a = exports.generatedFileContents[outFile]) === null || _a === void 0 ? void 0 : _a.content;
    if (generated === content && fs_extra_1.default.existsSync(outFile)) {
        exports.generatedFileContents[outFile].modify = false;
        return false;
    }
    // console.log(outFile);
    await fs_extra_1.default.ensureFile(outFile);
    await fs_extra_1.default.writeFile(outFile, content);
    exports.generatedFileContents[outFile] = { content, modify: true };
    return true;
}
exports.writeFile = writeFile;
function removeFile(file) {
    console.log(`Removing ${file}`);
    Object.keys(exports.generatedFileContents).map((cachedFile) => {
        if (cachedFile.indexOf(file) === 0) {
            delete exports.generatedFileContents[cachedFile];
        }
    });
    return fs_extra_1.default.removeSync(file);
}
exports.removeFile = removeFile;
/**
 * Remove file in the dir but not in the allowedFiles
 */
function cleanDir(dir, allowedFiles) {
    if (!fs_extra_1.default.existsSync(dir))
        return;
    const allFiles = fs_extra_1.default.readdirSync(dir).filter(junk.not);
    allFiles.map((file) => {
        if (allowedFiles.indexOf(file) < 0) {
            removeFile(path_1.default.join(dir, file));
        }
    });
}
exports.cleanDir = cleanDir;
/**
 * 用 writeFile 模拟 copyFile
 * @param srcFiles 相对路径文件
 * @param srcDir 文件目录
 * @param dstDir 目标目录
 */
async function copyFiles(copyFiles, srcDir, dstDir) {
    return Promise.all(copyFiles.map(async (filePath) => {
        const srcFile = path_1.default.join(srcDir, filePath);
        const dstFile = path_1.default.join(dstDir, filePath);
        // await fs.copy(path.join(appTemplateDir, 'src', entry), dstFile)
        // 用带 cache 功能的 writeFile 来替换 copy
        const srcContent = await fs_extra_1.default.readFile(srcFile, {
            encoding: 'utf8',
        });
        await writeFile(dstFile, srcContent);
    }));
}
exports.copyFiles = copyFiles;
async function copy(src, dest) {
    const needCopyFiles = glob_1.default.sync('**/*', { cwd: src, nodir: true });
    await copyFiles(needCopyFiles, src, dest);
}
exports.copy = copy;
/**
 * 复制文件
 * @param source 源文件
 * @param target 目标文件
 */
function copyFileSync(source, target) {
    let targetFile = target;
    // If target is a directory, a new file with the same name will be created
    if (fs_extra_1.default.existsSync(target)) {
        if (fs_extra_1.default.lstatSync(target).isDirectory()) {
            targetFile = path_1.default.join(target, path_1.default.basename(source));
        }
    }
    fs_extra_1.default.writeFileSync(targetFile, fs_extra_1.default.readFileSync(source));
}
exports.copyFileSync = copyFileSync;
/**
 * 根据源文件目录，输出目录，递归复制文件
 * @param {string} src  源文件目录
 * @param {string} dest 输出目录
 */
const copyRecursiveSync = function (src, dest, options) {
    const exists = fs_extra_1.default.existsSync(src);
    if (!exists || ((options === null || options === void 0 ? void 0 : options.filter) && !options.filter(src, dest))) {
        return;
    }
    const stats = fs_extra_1.default.statSync(src);
    const isDirectory = stats.isDirectory();
    if (!fs_extra_1.default.existsSync(dest)) {
        mkDirByPathSync(path_1.default.parse(dest).dir);
    }
    if (isDirectory) {
        const files = fs_extra_1.default.readdirSync(src);
        files.forEach((childItemName) => {
            (0, exports.copyRecursiveSync)(path_1.default.join(src, childItemName), path_1.default.join(dest, childItemName), options);
        });
    }
    else {
        copyFileSync(src, dest);
    }
};
exports.copyRecursiveSync = copyRecursiveSync;
/**
 * 参考：https://stackoverflow.com/questions/31645738/how-to-create-full-path-with-nodes-fs-mkdirsync
 * @param targetDir
 * @param param1
 * @returns
 */
function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
    const { sep } = path_1.default;
    const initDir = path_1.default.isAbsolute(targetDir) ? sep : '';
    const baseDir = isRelativeToScript ? __dirname : '.';
    return targetDir.split(sep).reduce((parentDir, childDir) => {
        const curDir = path_1.default.resolve(baseDir, parentDir, childDir);
        try {
            fs_extra_1.default.mkdirSync(curDir);
        }
        catch (err) {
            if (err.code === 'EEXIST') {
                // curDir already exists!
                return curDir;
            }
            // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
            if (err.code === 'ENOENT') {
                // Throw the original parentDir error on curDir `ENOENT` failure.
                throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
            }
            const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
            if (!caughtErr || (caughtErr && curDir === path_1.default.resolve(targetDir))) {
                throw err; // Throw if it's just the last created dir.
            }
        }
        return curDir;
    }, initDir);
}
/**
 * 返回指定目录的文件列表内容，返回map格式
 * @param dir 指定目录
 * @param files_ 文件列表
 * @param replacePath 去掉前面路径
 * @returns
 */
function getFiles(dir, files_, replacePath = '') {
    files_ = files_ || {};
    const files = fs_extra_1.default.readdirSync(dir);
    let mapName;
    for (let i in files) {
        const name = path_1.default.join(dir, files[i]);
        if (fs_extra_1.default.statSync(name).isDirectory()) {
            getFiles(name, files_, replacePath);
        }
        else {
            mapName = name;
            // 替换根路径
            if (name.indexOf(replacePath) === 0) {
                mapName = name.replace(replacePath, '');
            }
            files_[mapName] = fs_extra_1.default.readFileSync(name);
        }
    }
    return files_;
}
exports.getFiles = getFiles;
/**
 * 将文件压缩为zip包
 * 参考JSZip例子：https://stuk.github.io/jszip/documentation/examples/download-zip-file.html
 * @param files
 * @param type
 * @returns
 */
async function fileToZip(files, type) {
    let zip = new jszip_1.default();
    for (let prop in files) {
        zip.file(prop, files[prop]);
    }
    return zip.generateAsync({ type });
}
exports.fileToZip = fileToZip;
function strToBuf(str) {
    return buffer_1.Buffer.from(str);
}
exports.strToBuf = strToBuf;
