create-cttq
Version: 
CTTQ大前端脚手架项目
140 lines (133 loc) • 5.17 kB
JavaScript
/**
 *  项目创造器,负责调度
 */
const EventEmitter = require("events");
const fs = require("fs-extra");
const path = require("path");
const ora = require("ora");
const prompts = require("prompts");
const { red, green, bold } = require("kolorist")
const { logWithSpinner, succeedSpinner, failSpinner } = require("./util/spinner");
const { getAllPlugins } = require("./util/plugins")
const Promptor = require("./Promptor");
const Generator = require("./Generator");
const generateReadme = require("./util/generateReadme")
const generateChangelog = require("./util/generateChangelog")
const writeFileTree = require("./util/writeFileTree")
const Package = require("../../package.json");
const TemplateManager = require("./TemplateManager");
module.exports = class Creator extends EventEmitter {
    constructor(context) {
        super();
        this.context = context;
        this.plugins = getAllPlugins();
    }
    async create() {
        // cli问答
        const promptor = new Promptor(this.plugins);
        await promptor.show();
        if (Object.keys(promptor.answers).length <= 0) {
            return;
        }
        // 项目路径创建方式
        const lowerCaseName = promptor.answers.name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
        let targetDir = path.resolve(this.context, lowerCaseName || ".");
        if (path.basename(this.context).toLowerCase() == lowerCaseName) {
            const { context } = await prompts(
                [{
                    name: "context",
                    type: "select",
                    message: `请选择创建项目的目录:`,
                    choices: [
                        {
                            title: "子目录下",
                            value: targetDir,
                            description: targetDir,
                        },
                        {
                            title: "当前目录下",
                            value: this.context,
                            description: this.context,
                        }
                    ],
                    hint: "- ↑ ↓ 选择. 回车 确认",
                }],
                {
                    onCancel: () => {
                        throw new Error(red("✖") + " 取消创建项目");
                    },
                },
            );
            targetDir = context;
        }
        if (targetDir != this.context && fs.existsSync(targetDir)) {
            const { action } = await prompts(
                [{
                    name: "action",
                    type: "select",
                    message: `已经存在该目录 ${green(targetDir)},请选择一种处理方式:`,
                    choices: [
                        {
                            title: "重写",
                            value: "overwrite",
                            description: "删除现有项目所有文件,然后重新创建项目",
                        },
                        {
                            title: "合并",
                            value: "merge",
                            description: "保留现有目录,合并项目下文件,存在同名文件,则替换该文件",
                        },
                        {
                            title: "取消",
                            value: "cancel",
                            description: "取消创建项目",
                            selected: true
                        }
                    ],
                    hint: "- ↑ ↓ 选择. 回车 确认",
                }],
                {
                    onCancel: () => {
                        throw new Error(red("✖") + " 取消创建项目");
                    },
                },
            );
            if ("cancel" == action) {
                throw new Error(red("✖") + " 取消创建项目");
            } else if ("overwrite" == action) {
                await fs.remove(targetDir)
            }
        }
        const pkg = {
            name: lowerCaseName,
            version: "0.0.0",
            description: promptor.answers.description,
            private: true,
            devDependencies: {},
            engines: {
                cli: Package.version,
                template: process.env.CTTQ_TEMPLATE_VERSION,
            }
        }
        logWithSpinner("开始创建项目")
        // 生成项目
        const generator = new Generator(targetDir, this.plugins, pkg, promptor.answers);
        await generator.generate();
        for (const cb of generator.createdCallbacks) {
            await cb()
        }
        //写入CHANGELOG
        if (!generator.files['CHANGELOG.md']) {
            await writeFileTree(targetDir, {
                "CHANGELOG.md": generateChangelog(pkg)
            });
        }
        // 写入README
        if (!generator.files['README.md']) {
            await writeFileTree(targetDir, {
                "README.md": generateReadme(pkg)
            });
        }
        succeedSpinner("项目创建成功:"+green(targetDir));
    }
}