UNPKG

dio-cli

Version:

CLI for scaffolding dio-cli projects , 前端项目, 构建工具

234 lines (198 loc) 6.39 kB
const fs = require("fs-extra"); const path = require("path"); const { exec, spawn } = require("child_process"); const cwd = process.cwd(); const program = require("commander"); const inquirer = require("inquirer"); const ora = require("ora"); const semver = require("semver"); const klaw = require("klaw-sync"); const { log } = require("../lib/log"); const registerLogger = require("../lib/register-logger"); const merge = require("../lib/merge"); const pacote = require("pacote"); const getConfig = require("../lib/get-config"); const { resolveCwd } = require("../lib/utils"); program.option("-a, --all", "更新全部").parse(process.argv); if (program.all) { updateAll(); } else { updateFiles(); } registerLogger("update", process); process.on("exit", () => { fs.removeSync(resolveCwd("temp")); }); async function updateAll() { updateFiles(); await updateDeps("--save", "i-tofu"); await updateDeps("-g", "tofu-cli"); } async function updateFiles() { const spinner = ora("正在做更新前检查...").start(); await checkGit(); try { await pacote.extract("pitaya", "./temp", { registry: "http://registry.npm.taobao.org" }); } catch (err) { console.error("下载模板出错,请重试"); console.error(err); } // Load need update list let needUpdateFiles; needUpdateFiles = loadList(); if (!needUpdateFiles.length) { spinner.stop(); console.info("没有需要更新的文件"); return; } spinner.stop(); const { ignoreFiles } = await inquirer .prompt([ { type: "checkbox", message: "确认需要更新的文件(勾选取消更新)", name: "ignoreFiles", choices: needUpdateFiles.map(file => { return { name: file }; }), pageSize: 20 } ]) .catch(err => { console.err(err); }); needUpdateFiles = reduceList(needUpdateFiles, ignoreFiles); let needDeleteFiles = []; let fatalList = []; needUpdateFiles = needUpdateFiles.reduce((rst, file) => { // 处理需要删除的文件 if (/^\!/.test(file)) { needDeleteFiles.push(file); return rst; } const target = resolveCwd(file); const src = resolveCwd("temp", file); try { fs.lstatSync(src).isFile() ? rst.push(file) : (rst = rst.concat( klaw(target).map(obj => { return path.relative(cwd, obj.path).replace(/^temp\//, ""); }) )); } catch (err) { fatalList.push(target); } return rst; }, []); // traverse, merge and detect conflict spinner.start("正在更新文件..."); let conflictFiles = []; needUpdateFiles.forEach(file => { try { const src = resolveCwd("temp", file); const target = resolveCwd(file); const empty = resolveCwd("temp", "any"); fs.ensureFileSync(empty); const result = merge(target, empty, src); if (!result) conflictFiles.push(file); } catch (err) { fatalList.push(file); } }); needDeleteFiles.forEach(file => { fs.removeSync(resolveCwd(file)); }); spinner.succeed("更新文件完成").stop(); if (conflictFiles.length) log("\n以下文件有冲突,请手动修复:", "red"); conflictFiles.forEach(f => log(` ${f}`, "red")); if (fatalList.length) log("\n以下文件更新失败,请手动更新:", "red"); fatalList.forEach(f => log(` ${f}`, "red")); /** * Check working tree if is clear */ function checkGit() { return new Promise((resolve, reject) => { const child = spawn("git", ["status"]); child.stdout.on("data", data => { if (!~data.toString().indexOf("working tree clean")) { throw new Error("请先提交尚未提交的修改"); } }); child.stderr.on("data", data => { console.error(data); }); child.on("close", code => { resolve(); }); }); } /** * Load needed files list. */ function loadList() { let rst; let tofurc; try { tofurc = require(resolveCwd("temp/.tofurc.js")); } catch (err) { console.error("读取更新文件列表出错,请重试"); console.error(err); } const config = getConfig(); if (!config) return tofurc.updateList; const newUpdateList = tofurc.updateList; const oldUpdateList = config.updateList; if (oldUpdateList && !Array.isArray(oldUpdateList)) throw new Error(".tofurc 中的 ignore 选项必须是数组类型"); if (tofurc && newUpdateList && Array.isArray(newUpdateList)) { if (tofurc._meta && config._meta) { if (tofurc._meta.type !== config._meta.type) return []; const newVersion = semver.valid(tofurc._meta.version); const oldVersion = semver.valid(config._meta.version); if (!newVersion || !oldVersion) throw new Error("配置文件版本信息无效"); if (semver.gt(newVersion, oldVersion)) { if ( semver.minor(newVersion) > semver.minor(oldVersion) || semver.major(newVersion) > semver.major(oldVersion) ) { return newUpdateList; } else { return oldUpdateList; } } else { return []; } } else { throw new Error("配置文件缺少 _meta"); } } else { return []; } } function reduceList(origin, ignore) { console.assert(Array.isArray(origin) && Array.isArray(ignore), "It must be array type"); return origin.reduce((pv, cv) => { if (!~ignore.indexOf(cv)) pv.push(cv); return pv; }, []); } } function updateDeps(dep, option) { const spinner = ora(`正在更新 ${dep} ...`).start(); return Promise((resolve, reject) => { const child = proc.spawn( process.platform === "win32" ? "npm.cmd" : "npm"[("install", option, dep)] ); child.stdout.on("data", data => { console.log(`stdout: ${data}`); }); child.stderr.on("data", data => { console.error(data); }); child.on("close", code => { spinner.succeed(`更新 ${dep} 成功`).stop(); resolve(); }); }); }