"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const debug_1 = __importDefault(require("debug"));
const chalk_1 = __importDefault(require("chalk"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const urllib_1 = __importDefault(require("urllib"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const assert_1 = __importDefault(require("assert"));
const compressing_1 = __importDefault(require("compressing"));
const glob_1 = __importDefault(require("glob"));
const inquirer_1 = __importDefault(require("inquirer"));
const is_type_of_1 = __importDefault(require("is-type-of"));
const istextorbinary_1 = __importDefault(require("istextorbinary"));
const util_1 = require("../util");
const CONFIG_NAME = '@vision-design/cli-init-config';
var CreateType;
(function (CreateType) {
    CreateType["Lib"] = "library";
    CreateType["Component"] = "component";
    CreateType["Plugin"] = "plugin";
    CreateType["Action"] = "action";
    CreateType["Container"] = "container";
})(CreateType = exports.CreateType || (exports.CreateType = {}));
class Creator {
    constructor(name, targetDir, type, options) {
        this.httpClient = urllib_1.default.create();
        this.fileMapping = {
            gitignore: '.gitignore',
            _gitignore: '.gitignore',
            '_.gitignore': '.gitignore',
            '_package.json': 'package.json',
            '_.eslintignore': '.eslintignore',
            '_.npmignore': '.npmignore',
        };
        this.name = name;
        this.targetDir = targetDir;
        this.options = options;
        this.type = type;
        this.registryUrl = Creator.getRegistryByType(options.registry);
    }
    create() {
        return __awaiter(this, void 0, void 0, function* () {
            util_1.logWithSpinner('💫', '正在获取模板信息');
            const boilerplateMap = yield this.fetchBoilerplateMapping(CONFIG_NAME);
            util_1.stopSpinner();
            const boilerplate = boilerplateMap[this.type];
            if (!boilerplate) {
                util_1.error('the boilerplate does not exist');
                return;
            }
            const pkgName = boilerplate.package;
            util_1.logWithSpinner('💫', `正在下载模板 ${chalk_1.default.yellow(pkgName)}.`);
            const templateDir = yield this.downloadBoilerplate(pkgName);
            util_1.stopSpinner();
            // logWithSpinner(`📄`, '正在处理文件:');
            yield this.processFiles(this.targetDir, templateDir);
            // stopSpinner();
            util_1.info(this.targetDir);
        });
    }
    downloadBoilerplate(pkgName) {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.getPackageInfo(pkgName);
            const tgzUrl = result.dist.tarball;
            const saveDir = path_1.default.join(os_1.default.tmpdir(), 'impage-init-boilerplate');
            yield new Promise((resolve, reject) => {
                fs_extra_1.default.remove(saveDir, (err) => {
                    if (err) {
                        reject(err);
                    }
                    resolve();
                });
            });
            util_1.log(`下载中：${tgzUrl}`);
            const response = yield this.curl(tgzUrl, {
                streaming: true,
                followRedirect: true,
            });
            yield compressing_1.default.tgz.uncompress(response.res, saveDir);
            util_1.log(`解压到：${saveDir}`);
            return path_1.default.join(saveDir, '/package');
        });
    }
    fetchBoilerplateMapping(configName) {
        return __awaiter(this, void 0, void 0, function* () {
            const pkgInfo = yield this.getPackageInfo(configName);
            const mapping = pkgInfo.config;
            Object.keys(mapping).forEach((key) => {
                const item = mapping[key];
                item.name = item.name || key;
                item.from = pkgInfo;
            });
            return mapping;
        });
    }
    replaceTemplate(content, scope) {
        return content.toString().replace(/(\\)?{{ *(\w+) *}}/g, (block, skip, key) => {
            if (skip) {
                return block.substring(skip.length);
            }
            return scope.hasOwnProperty(key) ? scope[key] : block;
        });
    }
    processFiles(targetDir, templateDir) {
        return __awaiter(this, void 0, void 0, function* () {
            const src = path_1.default.join(templateDir, 'boilerplate');
            const locals = yield this.askForVariable(targetDir, templateDir);
            debug_1.default('impage:cli')('local variables of boilerplate', locals);
            const files = glob_1.default.sync('**/*.*', { cwd: src, dot: true });
            debug_1.default('impage:cli')('boilerplate files', files);
            files.forEach((file) => {
                debug_1.default('impage:cli')('file', file);
                const from = path_1.default.join(src, file);
                const to = path_1.default.join(targetDir, this.replaceTemplate(this.fileMapping[file] || file, locals));
                const content = fs_extra_1.default.readFileSync(from);
                util_1.log(`write to ${to}`);
                // check if content is a text file
                const result = istextorbinary_1.default.isTextSync(from, content)
                    ? this.replaceTemplate(content.toString('utf8'), locals)
                    : content;
                fs_extra_1.default.ensureDirSync(path_1.default.dirname(to));
                fs_extra_1.default.writeFileSync(to, result);
            });
            return files;
        });
    }
    askForVariable(targetDir, templateDir) {
        return __awaiter(this, void 0, void 0, function* () {
            let questions;
            try {
                // eslint-disable-next-line
                questions = require(templateDir);
                // support function
                if (is_type_of_1.default.function(questions)) {
                    questions = questions(this);
                }
                // use target dir name as `name` default
                if (questions.name && !questions.name.default) {
                    questions.name.default = this.name;
                }
                if (questions.author && this.options.author) {
                    questions.author.default = this.options.author;
                }
            }
            catch (err) {
                console.log(err);
                if (err.code !== 'MODULE_NOT_FOUND') {
                    util_1.error(`load boilerplate config got trouble, skip and use defaults, ${err.message}`);
                }
                return {};
            }
            util_1.log('获取模板配置信息');
            const keys = Object.keys(questions);
            return (yield inquirer_1.default.prompt(keys.map((key) => {
                const question = questions[key];
                return {
                    type: question.type || 'input',
                    name: key,
                    message: question.description || question.desc,
                    default: question.default,
                    filter: question.filter,
                    choices: question.choices,
                };
            })));
        });
    }
    getPackageInfo(pkgName) {
        return __awaiter(this, void 0, void 0, function* () {
            util_1.log(`fetching npm info of ${pkgName}`);
            const result = yield this.curl(`${this.registryUrl}/${pkgName}`, {
                dataType: 'json',
                followRedirect: true,
            });
            assert_1.default(result.status === 200, `npm info ${pkgName} got error: ${result.status}, ${result.data.reason}`);
            const latestVersion = result.data['dist-tags'].latest;
            return result.data.versions[latestVersion];
        });
    }
    curl(url, options) {
        return this.httpClient.request(url, options);
    }
    static getRegistryByType(key) {
        switch (key) {
            case 'taobao':
                return 'https://registry.npm.taobao.org';
            case 'npm':
                return 'https://registry.npmjs.org';
            case 'tnpm':
            default:
                return 'http://r.tnpm.oa.com';
        }
    }
}
exports.default = Creator;
//# sourceMappingURL=creator.js.map