react-tp-cli
Version:
🧵Utilities for creating React projects | React 模板项目脚手架
325 lines (312 loc) • 13.3 kB
JavaScript
import { program } from 'commander';
import inquirer from 'inquirer';
import fse from 'fs-extra';
import dayjs from 'dayjs';
import path from 'path';
import boxen from 'boxen';
import chalk from 'chalk';
import { fileURLToPath } from 'url';
import { exec, execSync } from 'child_process';
import StreamZip from 'node-stream-zip';
import ora from 'ora';
import axios from 'axios';
var name = "react-tp-cli";
var version = "3.4.2";
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var __filename = fileURLToPath(import.meta.url);
var __dirname = path.dirname(__filename);
var tpList = [
"🌟react-ts-rsbuild (推荐/recommend)",
"react-ts-vite",
"react-ts-webpack",
];
var prompt = [
{
type: "input",
message: "请输入项目名称(project name):",
name: "projectName",
default: "react-app",
},
{
type: "list",
message: "选择模板(choose template):",
name: "type",
default: tpList[0],
choices: tpList,
},
{
type: "list",
message: "安装依赖(download dependencies):",
name: "install",
default: "N",
choices: ["Y", "N"],
},
];
var downloadConf = {
tempDir: path.resolve(__dirname, "../temp"),
templateDir: path.join(__dirname, "../template"),
};
var logFile = path.resolve(__dirname, "../versionLog.json");
var repo = {
"react-ts-vite": {
url: "https://codeload.github.com/wanpan11/react-admin-tp/zip/refs/heads/vite",
dirName: "react-admin-tp-vite",
},
"react-ts-webpack": {
url: "https://codeload.github.com/wanpan11/react-admin-tp/zip/refs/heads/webpack",
dirName: "react-admin-tp-webpack",
},
"🌟react-ts-rsbuild (推荐/recommend)": {
url: "https://codeload.github.com/wanpan11/react-admin-tp/zip/refs/heads/rsbuild",
dirName: "react-admin-tp-rsbuild",
},
};
var printUpdateMsg = function (version, lastVer) {
console.log(boxen("package update from ".concat(version, " to ").concat(lastVer, "\n\n run ").concat(chalk.blue("npm i ".concat(name, " -g")), "\n\n Please upgrade before using\n\n \u8BF7\u5347\u7EA7\u540E\u4F7F\u7528"), {
padding: 1,
borderColor: "blue",
borderStyle: "double",
}));
};
function checkVersion(info) {
return __awaiter(this, void 0, void 0, function () {
var name, version, lastVer, _a, remoteVersion, date;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
name = info.name, version = info.version;
lastVer = "";
_b.label = 1;
case 1:
_b.trys.push([1, 7, , 8]);
if (!fse.pathExistsSync(logFile)) return [3 /*break*/, 4];
_a = fse.readJSONSync(logFile), remoteVersion = _a.remoteVersion, date = _a.date;
lastVer = remoteVersion;
if (!dayjs().isAfter(date)) return [3 /*break*/, 3];
return [4 /*yield*/, requestRemote(name)];
case 2:
lastVer = _b.sent();
_b.label = 3;
case 3: return [3 /*break*/, 6];
case 4: return [4 /*yield*/, requestRemote(name)];
case 5:
lastVer = _b.sent();
_b.label = 6;
case 6: return [2 /*return*/, { isUpdate: !!lastVer && version !== lastVer, lastVer: lastVer }];
case 7:
_b.sent();
return [2 /*return*/, { isUpdate: false, lastVer: lastVer }];
case 8: return [2 /*return*/];
}
});
});
}
function requestRemote(name) {
return new Promise(function (resolve, reject) {
exec("npm view ".concat(name, " version"), function (err, stdout) {
if (err) {
reject(err);
}
else {
var remoteVersion = stdout.trim();
fse.writeJsonSync(logFile, {
remoteVersion: remoteVersion,
date: dayjs().add(3, "day"),
});
resolve(remoteVersion);
}
});
});
}
var tempDir = downloadConf.tempDir;
var spinner = ora("");
var projectNameGlobal = "";
var selectTypeGlobal = "🌟react-ts-rsbuild (推荐/recommend)";
var userDirGlobal = "";
var needInstall = "N";
function getTemplate(_a) {
var projectName = _a.projectName, type = _a.type, install = _a.install;
projectNameGlobal = projectName;
selectTypeGlobal = type;
userDirGlobal = path.resolve(process.cwd(), "./".concat(projectName));
needInstall = install;
spinner.start();
spinner.color = "yellow";
spinner.text = "template downloading ~~";
if (!fse.existsSync(tempDir))
fse.mkdirSync(tempDir);
download();
}
function download() {
return __awaiter(this, void 0, void 0, function () {
var fileName, zipPath, stream, _a, status, data, error_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
fileName = "".concat(Date.now(), ".zip");
zipPath = path.resolve(tempDir, "./" + fileName);
stream = fse.createWriteStream(zipPath);
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, axios.get(repo[selectTypeGlobal].url, {
responseType: "stream",
})];
case 2:
_a = _b.sent(), status = _a.status, data = _a.data;
if (status === 200) {
// spinner.succeed("模板下载成功");
data.pipe(stream);
data.on("end", function () {
// spinner.start("模板解压中~~");
var zip = new StreamZip({
file: zipPath,
storeEntries: true,
});
zip.on("ready", function () {
zip.extract(repo[selectTypeGlobal].dirName, userDirGlobal, function (extractErr) {
if (extractErr) {
errStop("decompression failed ===>" + extractErr);
}
else {
install();
}
zip.close();
});
});
zip.on("error", function (zipErr) {
errStop("decompression failed ===>" + zipErr);
});
});
}
else {
errStop("network error!");
}
return [3 /*break*/, 4];
case 3:
error_1 = _b.sent();
errStop("download error!" + error_1);
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
}
function install() {
try {
var packageObj = fse.readJsonSync("".concat(userDirGlobal, "/package.json"));
packageObj.name = projectNameGlobal;
// 格式化 package.json
fse.outputFileSync("".concat(userDirGlobal, "/package.json"), JSON.stringify(packageObj, null, " "));
spinner.succeed("template create success!");
clearTemp();
try {
// 切换到项目目录
process.chdir(userDirGlobal);
// git
var git = execSync("git init", { encoding: "utf8" });
spinner.info("git init: ".concat(git.trim()));
// 检查是否需要安装依赖
if (needInstall === "N")
return;
// 检查pnpm版本
var version = execSync("pnpm -v", { encoding: "utf8" });
spinner.info("pnpm version: ".concat(version.trim()));
// 安装依赖
spinner.start("installing dependencies ~~");
execSync("pnpm i", { stdio: "inherit" });
spinner.succeed("react template created. ~~");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (error) {
console.error((error === null || error === void 0 ? void 0 : error.message) || error);
spinner.fail("dependency installation failed.");
return;
}
}
catch (error) {
errStop(error);
}
}
function errStop(msg) {
spinner.fail(msg);
spinner.stop();
}
function clearTemp() {
if (fse.existsSync(tempDir)) {
fse.removeSync(tempDir);
}
}
checkVersion({ name: name, version: version })
.then(function (_a) {
var isUpdate = _a.isUpdate, lastVer = _a.lastVer;
if (isUpdate)
return printUpdateMsg(version, lastVer);
program.version(version, "-v, --version");
program.description("React project template initialization tool");
program
.usage("<command>")
.command("init")
.description("create project")
.action(function () {
inquirer.prompt(prompt).then(function (answers) {
getTemplate(answers);
});
});
program.parse(process.argv);
})
.catch(function (e) {
console.error(e);
});