create-unibest
Version:
快速创建 unibest 项目的脚手架工具
1,518 lines (1,494 loc) • 58.6 kB
JavaScript
#!/usr/bin/env node
// src/index.ts
import process6 from "process";
import { green as green5, yellow as yellow4 } from "kolorist";
import minimist from "minimist";
// package.json
var version = "4.0.16";
var package_default = {
name: "create-unibest",
type: "module",
version,
updateTime: "2026-04-25",
packageManager: "pnpm@10.10.0",
description: "\u5FEB\u901F\u521B\u5EFA unibest \u9879\u76EE\u7684\u811A\u624B\u67B6\u5DE5\u5177",
author: "feige996",
license: "MIT",
homepage: "https://unibest.tech",
bin: {
best: "bin/index.js",
"create-unibest": "bin/index.js",
unibest: "bin/index.js"
},
files: [
"bin",
"dist",
"features"
],
scripts: {
dev: "cross-env NODE_ENV=development tsup --watch",
build: "cross-env NODE_ENV=production tsup",
prepare: "pnpm build",
start: "cross-env NODE_ENV=development node bin/index.js"
},
dependencies: {
"@clack/prompts": "^1.0.0",
dayjs: "^1.11.18",
ejs: "^4.0.0",
"fs-extra": "^11.3.0",
kolorist: "^1.8.0",
minimist: "^1.2.8",
"node-fetch": "^3.3.2"
},
devDependencies: {
"@types/ejs": "^3.1.5",
"@types/fs-extra": "^11.0.4",
"@types/minimist": "^1.2.5",
"@types/node": "^24.5.0",
"cross-env": "^7.0.3",
tsup: "^8.5.0",
typescript: "^5.9.0"
}
};
// src/commands/add.ts
import fs4 from "fs";
import path3 from "path";
import process2 from "process";
import { cancel, intro, isCancel, log as log2, multiselect } from "@clack/prompts";
import { bold as bold2, green } from "kolorist";
// src/features/interface.ts
var AVAILABLE_FEATURES = [
{
name: "i18n",
description: "\u591A\u8BED\u8A00\u652F\u6301",
dependencies: {
"vue-i18n": "^9.0.0",
"dayjs": "^1.11.0"
}
},
{
name: "login",
description: "\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09",
dependencies: {}
},
{
name: "lime-echart",
description: "lime-echart \u56FE\u8868\u5E93",
dependencies: {
"echarts": "^5.4.1",
"lodash-es": "^4.17.21"
}
},
{
name: "ucharts",
description: "uCharts \u56FE\u8868\u5E93",
dependencies: {
"@qiun/ucharts": "2.5.0-20230101"
}
}
];
function getFeatureByName(name) {
return AVAILABLE_FEATURES.find((f) => f.name === name);
}
function getSelectedFeatures(options) {
const features = [];
if (options.i18n) {
const feature = getFeatureByName("i18n");
if (feature)
features.push(feature);
}
if (options.loginStrategy) {
const feature = getFeatureByName("login");
if (feature)
features.push(feature);
}
for (const library of options.chartLibraries || []) {
const feature = getFeatureByName(library);
if (feature)
features.push(feature);
}
return features;
}
// src/features/loader.ts
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
var __filename = fileURLToPath(import.meta.url);
var __dirname = path.dirname(__filename);
var FEATURES_DIR = path.resolve(__dirname, "..", "features");
async function loadFeatureHooks(featureName) {
const hooksPath = path.join(FEATURES_DIR, featureName, "hooks.js");
if (!fs.existsSync(hooksPath)) {
return null;
}
const module = await import(hooksPath);
return module;
}
function getAvailableFeatureNames() {
if (!fs.existsSync(FEATURES_DIR)) {
return [];
}
const entries = fs.readdirSync(FEATURES_DIR, { withFileTypes: true });
return entries.filter((e) => e.isDirectory() && fs.existsSync(path.join(FEATURES_DIR, e.name, "files"))).map((e) => e.name);
}
// src/utils/injector.ts
import fs2 from "fs";
import path2, { dirname } from "path";
import { fileURLToPath as fileURLToPath2 } from "url";
var __filename2 = fileURLToPath2(import.meta.url);
var __dirname2 = dirname(__filename2);
var FEATURES_PATH = path2.join(__dirname2, "..", "features");
var FeatureInjector = class {
constructor(projectPath) {
this.projectPath = projectPath;
}
injectFile(targetPath, featureCode, placeholder) {
const fullPath = path2.join(this.projectPath, targetPath);
if (!fs2.existsSync(fullPath)) {
return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
}
const content = fs2.readFileSync(fullPath, "utf-8");
if (!content.includes(placeholder)) {
return { success: false, message: `\u5360\u4F4D\u7B26\u4E0D\u5B58\u5728: ${placeholder}` };
}
const newContent = content.replace(placeholder, featureCode);
fs2.writeFileSync(fullPath, newContent);
return { success: true, message: `\u6CE8\u5165\u6210\u529F: ${targetPath}` };
}
replaceFile(targetPath, featureFilePath) {
const targetFullPath = path2.join(this.projectPath, targetPath);
const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
if (!fs2.existsSync(featureFullPath)) {
return {
success: false,
message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}`
};
}
const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
fs2.writeFileSync(targetFullPath, featureContent);
return { success: true, message: `\u66FF\u6362\u6210\u529F: ${targetPath}` };
}
createFile(relativePath, featureFilePath) {
const targetFullPath = path2.join(this.projectPath, relativePath);
const featureFullPath = path2.join(FEATURES_PATH, featureFilePath);
if (!fs2.existsSync(featureFullPath)) {
return {
success: false,
message: `Feature \u6587\u4EF6\u4E0D\u5B58\u5728: ${featureFilePath}`
};
}
const dir = path2.dirname(targetFullPath);
if (!fs2.existsSync(dir)) {
fs2.mkdirSync(dir, { recursive: true });
}
const featureContent = fs2.readFileSync(featureFullPath, "utf-8");
fs2.writeFileSync(targetFullPath, featureContent);
return { success: true, message: `\u521B\u5EFA\u6210\u529F: ${relativePath}` };
}
appendAfter(targetPath, marker, code) {
const fullPath = path2.join(this.projectPath, targetPath);
if (!fs2.existsSync(fullPath)) {
return { success: false, message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${targetPath}` };
}
const content = fs2.readFileSync(fullPath, "utf-8");
if (!content.includes(marker)) {
return { success: false, message: `\u6807\u8BB0\u4E0D\u5B58\u5728: ${marker}` };
}
const newContent = content.replace(marker, `${marker}
${code}`);
fs2.writeFileSync(fullPath, newContent);
return { success: true, message: `\u8FFD\u52A0\u6210\u529F: ${targetPath}` };
}
};
async function injectI18n(projectPath) {
const results = [];
const injector = new FeatureInjector(projectPath);
results.push(
injector.appendAfter(
"src/main.ts",
`import 'virtual:uno.css'`,
`import i18n from './locale/index'`
)
);
results.push(
injector.appendAfter(
"src/main.ts",
` app.use(requestInterceptor)`,
` app.use(i18n)`
)
);
results.push(
injector.replaceFile(
"src/tabbar/config.ts",
"i18n/files/src/tabbar/config.ts"
)
);
results.push(
injector.replaceFile(
"src/tabbar/index.vue",
"i18n/files/src/tabbar/index.vue"
)
);
results.push(
injector.replaceFile(
"src/tabbar/TabbarItem.vue",
"i18n/files/src/tabbar/TabbarItem.vue"
)
);
results.push(
injector.replaceFile("src/utils/index.ts", "i18n/files/src/utils/index.ts")
);
results.push(
injector.replaceFile("src/store/token.ts", "i18n/files/src/store/token.ts")
);
const i18nFiles = [
"src/locale/index.ts",
"src/locale/en.json",
"src/locale/zh-Hans.json",
"src/locale/README.md",
"src/utils/i18n.ts",
"src/types/i18n.d.ts",
"src/tabbar/i18n.ts",
"src/pages/i18n/index.vue"
];
for (const file of i18nFiles) {
const featurePath = `i18n/files/${file}`;
results.push(injector.createFile(file, featurePath));
}
return results;
}
async function injectLogin(projectPath) {
const results = [];
const injector = new FeatureInjector(projectPath);
results.push(
injector.replaceFile(
"src/router/interceptor.ts",
"login/files/src/router/interceptor.ts"
)
);
results.push(
injector.replaceFile(
"src/router/config.ts",
"login/files/src/router/config.ts"
)
);
results.push(
injector.replaceFile("src/pages/me/me.vue", "login/files/src/pages/me.vue")
);
const loginFiles = [
"src/pages/auth/login.vue",
"src/pages/auth/register.vue",
"src/pages/auth/README.md"
];
for (const file of loginFiles) {
const featurePath = `login/files/${file}`;
results.push(injector.createFile(file, featurePath));
}
return results;
}
function ensurePagesDemoSubPackage(projectPath) {
const configPath = path2.join(projectPath, "pages.config.ts");
if (!fs2.existsSync(configPath)) {
return { success: false, message: "\u6587\u4EF6\u4E0D\u5B58\u5728: pages.config.ts" };
}
let content = fs2.readFileSync(configPath, "utf-8");
if (content.includes("root: 'pages-demo'") || content.includes('root: "pages-demo"')) {
return { success: true, message: "pages-demo \u5DF2\u5B58\u5728" };
}
const entry = `{
root: 'pages-demo',
pages: [],
}`;
if (/subPackages:\s*\[\s*\]/.test(content)) {
content = content.replace(
/subPackages:\s*\[\s*\]/,
`subPackages: [${entry}]`
);
} else if (/subPackages:\s*\[/.test(content)) {
content = content.replace(
/subPackages:\s*\[/,
`subPackages: [
${entry},`
);
} else {
if (content.includes("tabBar:")) {
content = content.replace(
/\n\s*tabBar:\s*/,
`
subPackages: [${entry}],
tabBar: `
);
} else if (content.includes("defineUniPages({")) {
content = content.replace(
/\n\}\)\s*$/,
`
subPackages: [${entry}],
})`
);
} else {
return { success: false, message: "\u672A\u627E\u5230 defineUniPages \u914D\u7F6E" };
}
}
fs2.writeFileSync(configPath, content);
return {
success: true,
message: "\u5DF2\u66F4\u65B0 pages.config.ts \u7684 pages-demo \u914D\u7F6E"
};
}
async function injectLimeEchart(projectPath) {
const results = [];
const injector = new FeatureInjector(projectPath);
results.push(ensurePagesDemoSubPackage(projectPath));
const limeFiles = [
"src/uni_modules/lime-echart/package.json",
"src/uni_modules/lime-echart/static/uvue.html",
"src/uni_modules/lime-echart/static/echarts.min.js",
"src/uni_modules/lime-echart/static/ecStat.min.js",
"src/uni_modules/lime-echart/static/uni.webview.1.5.5.js",
"src/uni_modules/lime-echart/components/lime-echart/lime-echart.vue",
"src/uni_modules/lime-echart/components/lime-echart/lime-echart.nvue",
"src/uni_modules/lime-echart/components/lime-echart/lime-echart.uvue",
"src/uni_modules/lime-echart/components/l-echart/utils.js",
"src/uni_modules/lime-echart/components/l-echart/uvue.uts",
"src/uni_modules/lime-echart/components/l-echart/nvue.js",
"src/uni_modules/lime-echart/components/l-echart/l-echart.vue",
"src/uni_modules/lime-echart/components/l-echart/canvas.js",
"src/uni_modules/lime-echart/components/l-echart/l-echart.uvue",
"src/pages-demo/hooks/useEcharts.ts",
"src/pages-demo/lime-echarts/index.vue",
"src/pages-demo/lime-echarts/index2.vue",
"src/pages-demo/lime-echarts/index2.ts"
];
for (const file of limeFiles) {
const featurePath = `lime-echart/files/${file}`;
results.push(injector.createFile(file, featurePath));
}
return results;
}
async function injectUcharts(projectPath) {
const results = [];
const injector = new FeatureInjector(projectPath);
results.push(ensurePagesDemoSubPackage(projectPath));
const uchartsFiles = [
"src/components/qiun-data-charts/config-ucharts.js",
"src/components/qiun-data-charts/config-echarts.js",
"src/components/qiun-data-charts/qiun-data-charts.vue",
"src/components/qiun-data-charts/u-charts.js",
"src/components/qiun-error/qiun-error.vue",
"src/components/qiun-loading/loading1.vue",
"src/components/qiun-loading/loading2.vue",
"src/components/qiun-loading/loading3.vue",
"src/components/qiun-loading/loading4.vue",
"src/components/qiun-loading/loading5.vue",
"src/components/qiun-loading/qiun-loading.vue",
"src/components/qiun-title-bar/qiun-title-bar.vue",
"src/pages-demo/ucharts/index.vue",
"src/pages-demo/ucharts/data.json"
];
for (const file of uchartsFiles) {
const featurePath = `ucharts/files/${file}`;
results.push(injector.createFile(file, featurePath));
}
return results;
}
// src/utils/logger.ts
import { log, spinner } from "@clack/prompts";
import { bold, red } from "kolorist";
var logger = {
/** 普通信息日志 */
info: (message) => {
log.info(bold(message));
},
/** 成功日志 */
success: (message) => {
log.success(bold(message));
},
/** 错误日志 */
error: (message) => {
log.error(bold(message));
},
/** 警告日志 */
warn: (message) => {
log.warn(bold(message));
},
/** 提示日志 */
tip: (message) => {
log.info(bold(message));
},
/** 开始一个带有 spinner 的任务 */
start: (message) => {
const s = spinner();
s.start(bold(message));
return {
stop: (msg) => s.stop(msg ? bold(msg) : void 0),
fail: (msg) => s.stop(msg ? bold(red(msg)) : void 0)
};
}
};
// src/utils/readPackageJson.ts
import fs3 from "fs";
function readPackageJson(pkgPath) {
if (!fs3.existsSync(pkgPath)) {
throw new Error(`package.json not found: ${pkgPath}`);
}
const content = fs3.readFileSync(pkgPath, "utf-8");
return JSON.parse(content);
}
function writePackageJson(pkgPath, pkg) {
fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
`);
}
// src/commands/add.ts
function getFeatureStatusFromPackageJson(pkgPath) {
try {
const pkg = readPackageJson(pkgPath);
return {
"i18n": pkg.unibest?.i18n === true,
"login": pkg.unibest?.loginStrategy === true,
"lime-echart": pkg.unibest?.charts?.limeEchart === true,
"ucharts": pkg.unibest?.charts?.ucharts === true
};
} catch {
return { "i18n": false, "login": false, "lime-echart": false, "ucharts": false };
}
}
function updatePackageJsonForFeature(pkgPath, featureName) {
const pkg = readPackageJson(pkgPath);
if (!pkg.unibest) {
pkg.unibest = {};
}
switch (featureName) {
case "i18n":
pkg.unibest.i18n = true;
break;
case "login":
pkg.unibest.loginStrategy = true;
break;
case "lime-echart":
pkg.unibest.charts = {
...pkg.unibest.charts,
limeEchart: true
};
break;
case "ucharts":
pkg.unibest.charts = {
...pkg.unibest.charts,
ucharts: true
};
break;
}
writePackageJson(pkgPath, pkg);
}
async function checkFeatureStatus(projectPath) {
const pkgPath = path3.join(projectPath, "package.json");
const statusFromPkg = getFeatureStatusFromPackageJson(pkgPath);
return [
{ name: "i18n", enabled: statusFromPkg.i18n },
{ name: "login", enabled: statusFromPkg.login },
{ name: "lime-echart", enabled: statusFromPkg["lime-echart"] },
{ name: "ucharts", enabled: statusFromPkg.ucharts }
];
}
async function addFeature(featureName, projectPath, options = {}) {
const feature = getFeatureByName(featureName);
if (!feature) {
logger.error(`\u672A\u77E5\u7684 Feature: ${featureName}`);
return false;
}
const pkgPath = path3.join(projectPath, "package.json");
const pkg = readPackageJson(pkgPath);
let alreadyAdded = false;
if (featureName === "i18n" && pkg.unibest?.i18n === true) {
alreadyAdded = true;
} else if (featureName === "login" && pkg.unibest?.loginStrategy === true) {
alreadyAdded = true;
} else if (featureName === "lime-echart" && pkg.unibest?.charts?.limeEchart === true) {
alreadyAdded = true;
} else if (featureName === "ucharts" && pkg.unibest?.charts?.ucharts === true) {
alreadyAdded = true;
}
if (alreadyAdded && !options.force) {
logger.warn(`Feature ${featureName} \u5DF2\u6DFB\u52A0\u8FC7\uFF0C\u5982\u9700\u91CD\u65B0\u6CE8\u5165\u8BF7\u4F7F\u7528 --force \u53C2\u6570`);
return true;
}
log2.info(`\u6B63\u5728\u6DFB\u52A0 Feature: ${green(featureName)} - ${feature.description}`);
try {
let results;
switch (featureName) {
case "i18n":
results = await injectI18n(projectPath);
break;
case "login":
results = await injectLogin(projectPath);
break;
case "lime-echart":
results = await injectLimeEchart(projectPath);
break;
case "ucharts":
results = await injectUcharts(projectPath);
break;
default:
logger.error(`\u4E0D\u652F\u6301\u7684 Feature: ${featureName}`);
return false;
}
for (const result of results) {
if (result.success) {
logger.success(result.message);
} else {
logger.warn(result.message);
}
}
const hooks = await loadFeatureHooks(featureName);
if (hooks?.postApply) {
await hooks.postApply({
options: { projectName: "", platforms: [], uiLibrary: "none", i18n: true, loginStrategy: true },
projectPath,
featureName
});
}
updatePackageJsonForFeature(pkgPath, featureName);
logger.success(`\u5DF2\u66F4\u65B0 package.json \u7684 unibest \u914D\u7F6E`);
if (feature.dependencies && Object.keys(feature.dependencies).length > 0) {
logger.info(`\u5B89\u88C5\u4F9D\u8D56: ${Object.keys(feature.dependencies).join(", ")}`);
}
logger.success(`Feature ${featureName} \u6DFB\u52A0\u6210\u529F\uFF01`);
return true;
} catch (error) {
logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
return false;
}
}
async function addCommand(args) {
const options = {
path: args.path || args.p || ".",
feature: args._[1] || args.feature || args.f,
force: args.force || args.f
};
intro(bold2(green(`create-unibest@v${version} \u6DFB\u52A0 Feature`)));
const projectPath = path3.isAbsolute(options.path) ? options.path : path3.join(process2.cwd(), options.path);
const pkgPath = path3.join(projectPath, "package.json");
if (!fs4.existsSync(pkgPath)) {
logger.error(`\u9879\u76EE\u4E0D\u5B58\u5728: ${projectPath}`);
process2.exit(1);
}
const pkg = readPackageJson(pkgPath);
if (pkg.name !== "unibest") {
logger.warn(`\u5F53\u524D\u9879\u76EE\u53EF\u80FD\u4E0D\u662F unibest \u9879\u76EE: ${pkg.name}`);
}
const availableFeatures = getAvailableFeatureNames();
try {
if (!options.feature) {
const detected = await checkFeatureStatus(projectPath);
const available = availableFeatures.filter((f) => !detected.find((d) => d.name === f && d.enabled));
if (available.length === 0) {
logger.info("\u6240\u6709\u53EF\u7528 Feature \u5DF2\u542F\u7528");
return;
}
const selectedFeatures = await multiselect({
message: `\u8BF7\u9009\u62E9\u8981\u6DFB\u52A0\u7684 Feature`,
options: available.map((name) => {
const feature = getFeatureByName(name);
return {
value: name,
label: feature?.name || name,
hint: feature?.description || ""
};
}),
required: false
});
if (isCancel(selectedFeatures)) {
cancel("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
process2.exit(0);
}
if (!Array.isArray(selectedFeatures) || selectedFeatures.length === 0) {
logger.info("\u672A\u9009\u62E9\u4EFB\u4F55 Feature");
return;
}
for (const featureName of selectedFeatures) {
await addFeature(featureName, projectPath, options);
}
} else {
const features = Array.isArray(options.feature) ? options.feature : [options.feature];
for (const featureName of features) {
await addFeature(featureName, projectPath, options);
}
}
} catch (error) {
logger.error(`\u6DFB\u52A0 Feature \u5931\u8D25: ${error.message}`);
process2.exit(1);
}
}
// src/commands/create.ts
import { intro as intro2, log as log5 } from "@clack/prompts";
import { bold as bold3, green as green3, yellow as yellow2 } from "kolorist";
// src/utils/beacon.ts
import crypto from "crypto";
import os2 from "os";
import dayjs from "dayjs";
import fetch2 from "node-fetch";
// src/utils/debug.ts
import { magenta } from "kolorist";
function debug(...args) {
const isDev = process.env.NODE_ENV === "development";
if (isDev) {
const debugPrefix = magenta("[debug]");
console.log(debugPrefix, ...args);
}
}
// src/utils/unibestVersion.ts
import { promises as fs5 } from "fs";
import os from "os";
import { join } from "path";
import fetch from "node-fetch";
async function getUnibestVersionFromGitee() {
try {
const apiUrl = `https://gitee.com/api/v5/repos/feige996/unibest/contents/package.json?ref=main`;
const response = await fetch(apiUrl, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if (response.ok) {
const data = await response.json();
const { content, encoding } = data;
if (encoding === "base64") {
const decodedContent = Buffer.from(content, "base64").toString("utf8");
const packageJson = JSON.parse(decodedContent);
return packageJson.version || null;
} else {
return null;
}
} else {
return null;
}
} catch (error) {
return null;
}
}
// src/utils/beacon.ts
async function beacon(options) {
try {
const unibestVersion = await getUnibestVersionFromGitee();
const deviceIdentifier = generateDeviceIdentifier();
await fetch2("https://ukw0y1.laf.run/create-unibest-v3/beacon", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
...options,
version: unibestVersion,
cbVersion: package_default.version,
createAt: dayjs().format("YYYY-MM-DD HH:mm:ss"),
nodeVersion: process.version,
osPlatform: process.platform,
cpuModel: os2.cpus()[0]?.model || "unknown",
osRelease: os2.release(),
totalMem: Math.round(os2.totalmem() / (1024 * 1024 * 1024)),
// 四舍五入为整数 GB
cpuArch: process.arch,
uuid: deviceIdentifier
// 添加设备唯一标识符
})
});
debug("Beacon sent successfully");
} catch (error) {
}
}
function generateDeviceIdentifier() {
const deviceInfo = [os2.cpus()[0]?.model || "", os2.totalmem().toString(), os2.platform(), os2.userInfo().username].join(
"|"
);
const hash = crypto.createHash("sha256").update(deviceInfo).digest("hex");
return hash;
}
// src/utils/validate.ts
import { existsSync } from "fs";
import { join as join2 } from "path";
import { yellow } from "kolorist";
function validateProjectName(name) {
const reg = /^[\w-]+$/;
if (!reg.test(name)) {
return false;
}
if (name.startsWith("-") || name.endsWith("-")) {
return false;
}
return true;
}
function checkProjectNameExistAndValidate(_projectName) {
const projectName = _projectName.trim();
if (existsSync(join2(process.cwd(), projectName))) {
return `\u76EE\u5F55 ${yellow(projectName)} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0`;
}
if (!validateProjectName(projectName)) {
return `\u9879\u76EE\u540D\u79F0 ${yellow(projectName)} \u4E0D\u7B26\u5408\u89C4\u8303\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\u6216\u4E0B\u5212\u7EBF`;
}
return "";
}
// src/commands/create/generate.ts
import path4 from "path";
import process4 from "process";
import { log as log4 } from "@clack/prompts";
// src/utils/cloneRepo.ts
import { exec } from "child_process";
import { promises as fsPromises } from "fs";
import { join as join4 } from "path";
import process3 from "process";
import { log as log3 } from "@clack/prompts";
import { red as red2 } from "kolorist";
// src/utils/replacePackageJson.ts
import { readFileSync, writeFileSync } from "fs";
import { join as join3 } from "path";
import dayjs2 from "dayjs";
function replaceContent(filePath, projectName, version2, options) {
const fileContent = JSON.parse(readFileSync(filePath, "utf8"));
const unibestVersion = fileContent["unibest-version"];
const unibestUpdateTime = fileContent["unibest-update-time"];
delete fileContent["unibest-version"];
delete fileContent["unibest-update-time"];
delete fileContent.metadata;
delete fileContent.name;
delete fileContent.version;
const { projectName: _, ...restOptions } = options;
const selectedFeatures = getSelectedFeatures(options);
const featureDeps = {};
for (const feature of selectedFeatures) {
if (feature.dependencies) {
Object.assign(featureDeps, feature.dependencies);
}
}
if (!fileContent.dependencies) {
fileContent.dependencies = {};
}
for (const [pkg, ver] of Object.entries(featureDeps)) {
if (!fileContent.dependencies[pkg]) {
fileContent.dependencies[pkg] = ver;
}
}
const newContent = {
name: projectName,
type: fileContent.type,
// 保持 type 在前(如果存在)
version: version2,
unibest: {
...restOptions,
cliVersion: version,
unibestVersion,
unibestUpdateTime,
createdAt: dayjs2().format("YYYY-MM-DD HH:mm:ss")
},
...fileContent
// 剩余字段
};
writeFileSync(filePath, JSON.stringify(newContent, null, 2));
}
function replacePackageJson(root2, name, version2, options) {
const projectName = name.toLocaleLowerCase().replace(/\s/g, "-");
const pkgPath = join3(root2, "package.json");
replaceContent(pkgPath, projectName, version2, options);
}
// src/utils/cloneRepo.ts
var REPO_URL = "https://gitee.com/feige996/unibest.git";
async function removeGitFolder(localPath) {
const gitFolderPath = join4(localPath, ".git");
await fsPromises.rm(gitFolderPath, { recursive: true, force: true });
}
async function cloneRepo(projectName, branch) {
log3.info("\u4ECE Git \u514B\u9686\u57FA\u7840\u6A21\u677F...");
await new Promise((resolve, reject) => {
const execStr = `git clone --depth=1 -b ${branch} ${REPO_URL} "${projectName}"`;
exec(execStr, async (error) => {
if (error) {
log3.error(`${red2("\u514B\u9686\u6A21\u677F\u5931\u8D25:")} ${error}`);
reject(error);
return;
}
try {
await removeGitFolder(projectName);
resolve();
} catch (error2) {
log3.error(`${red2("\u79FB\u9664 .git \u6587\u4EF6\u5939\u5931\u8D25:")} ${error2}`);
reject(error2);
}
});
});
}
async function cloneRepoByBranch(root2, name, branch, options) {
try {
await cloneRepo(name, "base");
} catch (error) {
log3.error(`${red2(`\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25\uFF01`)} ${error}`);
process3.exit(1);
}
const projectPath = join4(root2, name);
replacePackageJson(projectPath, name, "1.0.0", options);
}
// src/utils/uiLibrary.ts
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
import { join as join5 } from "path";
var UI_LIBRARY_CONFIGS = {
"none": null,
"wot-ui": {
packageName: "wot-design-uni",
easycom: {
pattern: "^wd-(.*)",
path: "wot-design-uni/components/wd-$1/wd-$1.vue"
},
types: ["wot-design-uni/global.d.ts"]
},
/** Wot UI v2,npm 包 @wot-ui/ui,见 https://wot-ui.cn/guide/quick-use.html */
"wot-ui-v2": {
packageName: "@wot-ui/ui",
devDependencies: { sass: "latest" },
easycom: {
pattern: "^wd-(.*)",
path: "@wot-ui/ui/components/wd-$1/wd-$1.vue"
},
types: ["@wot-ui/ui/global"]
},
"sard-uniapp": {
packageName: "sard-uniapp",
easycom: {
pattern: "^sar-(.*)",
path: "sard-uniapp/components/$1/$1.vue"
},
types: ["sard-uniapp/global"],
appVueImport: "@import 'sard-uniapp/index.scss';"
},
"uview-pro": {
packageName: "uview-pro",
easycom: {
pattern: "^u-(.*)",
path: "uview-pro/components/u-$1/u-$1.vue"
},
needMainImport: true,
mainImport: "import uViewPro from 'uview-pro';",
needUniScss: true,
uniScssImport: "@import 'uview-pro/theme.scss';",
needAppVue: true,
appVueImport: "@import 'uview-pro/index.scss';"
},
"uv-ui": {
packageName: "@climblee/uv-ui",
easycom: {
pattern: "^uv-(.*)",
path: "@climblee/uv-ui/components/uv-$1/uv-$1.vue"
},
types: ["@ttou/uv-typings/shim", "@ttou/uv-typings/v2"]
},
"uview-plus": {
packageName: "uview-plus",
// uview-plus 需要多个 easycom 配置
easycom: {
pattern: "^u--(.*)",
path: "uview-plus/components/u-$1/u-$1.vue"
},
types: ["uview-plus/types"],
needUniScss: true,
uniScssImport: "@import 'uview-plus/theme.scss'; // /* \u884C\u4E3A\u76F8\u5173\u989C\u8272 */"
},
"tdesign": {
packageName: "@tdesign/uniapp",
easycom: {
pattern: "^t-(.*)",
path: "@tdesign/uniapp/$1/$1.vue"
},
mainImport: "import '@tdesign/uniapp/common/style/theme/index.css';",
types: ["@tdesign/uniapp/global.d.ts"]
}
};
function getUILibraryConfig(uiLibrary) {
return UI_LIBRARY_CONFIGS[uiLibrary];
}
async function applyUILibraryConfig(projectPath, uiLibrary) {
if (uiLibrary === "none") {
return;
}
const config = getUILibraryConfig(uiLibrary);
if (!config) {
return;
}
await updatePackageJson(projectPath, config.packageName, config.devDependencies);
if (config.easycom) {
await updatePagesConfig(projectPath, config.easycom);
if (uiLibrary === "uview-plus") {
await updatePagesConfig(projectPath, {
pattern: "^up-(.*)",
path: "uview-plus/components/u-$1/u-$1.vue"
});
await updatePagesConfig(projectPath, {
pattern: "^u-([^-].*)",
path: "uview-plus/components/u-$1/u-$1.vue"
});
}
}
if (config.types && config.types.length > 0) {
await updateTsConfig(projectPath, config.types);
}
if (config.needMainImport && config.mainImport) {
await updateMainTs(projectPath, config.mainImport);
}
if (config.needUniScss && config.uniScssImport) {
await updateUniScss(projectPath, config.uniScssImport);
}
if (config.needAppVue && config.appVueImport) {
await updateAppVue(projectPath, config.appVueImport);
}
if (uiLibrary === "wot-ui-v2") {
await ensureWotUiV2Resolver(projectPath);
}
}
var WOT_UI_V2_RESOLVER_FILENAME = "wot-ui-resolver.ts";
var WOT_UI_V2_RESOLVER_CONTENT = `import type { ComponentResolver } from '@uni-helper/vite-plugin-uni-components'
import { kebabCase } from '@uni-helper/vite-plugin-uni-components'
/**
* npm \u5B89\u88C5\u7684 Wot UI \u7EC4\u4EF6\u5728 H5 \u7AEF\u9700\u901A\u8FC7 vite-plugin-uni-components \u89E3\u6790\uFF0C
* \u624D\u80FD\u6B63\u786E\u6302\u8F7D\u7EC4\u4EF6\u6837\u5F0F\u3002
*/
export function WotResolver(): ComponentResolver {
return {
type: 'component',
resolve: (name: string) => {
if (name.match(/^Wd[A-Z]/)) {
const compName = kebabCase(name)
return {
name,
from: \`@wot-ui/ui/components/\${compName}/\${compName}.vue\`,
}
}
if (name.startsWith('wd-')) {
return {
name,
from: \`@wot-ui/ui/components/\${name}/\${name}.vue\`,
}
}
},
}
}
`;
async function ensureWotUiV2Resolver(projectPath) {
const resolverPath = join5(projectPath, WOT_UI_V2_RESOLVER_FILENAME);
const viteConfigPath = join5(projectPath, "vite.config.ts");
if (!existsSync2(resolverPath)) {
writeFileSync2(resolverPath, WOT_UI_V2_RESOLVER_CONTENT);
}
if (existsSync2(viteConfigPath)) {
updateViteConfigForWotUiV2(viteConfigPath);
}
}
function updateViteConfigForWotUiV2(viteConfigPath) {
const original = readFileSync2(viteConfigPath, "utf8");
let content = original;
if (!content.includes(`from './${WOT_UI_V2_RESOLVER_FILENAME.replace(".ts", "")}'`)) {
const importTarget = `import { WotResolver } from './${WOT_UI_V2_RESOLVER_FILENAME.replace(".ts", "")}'`;
const uniPagesImportRegex = /(import\s+UniPages\s+from\s+['"]@uni-helper\/vite-plugin-uni-pages['"]\s*\n)/;
if (uniPagesImportRegex.test(content)) {
content = content.replace(uniPagesImportRegex, `$1${importTarget}
`);
} else {
content = `${importTarget}
${content}`;
}
}
const uniComponentsRegex = /UniComponents\(\{[\s\S]*?\}\)/;
const matched = content.match(uniComponentsRegex);
if (matched && !matched[0].includes("resolvers:")) {
const block = matched[0];
const updatedBlock = block.replace(/\}\)$/, ` resolvers: [WotResolver()],
})`);
content = content.replace(block, updatedBlock);
}
if (content !== original) {
writeFileSync2(viteConfigPath, ensureTrailingNewline(content));
}
}
async function updatePackageJson(projectPath, packageName, devDependencies) {
const packageJsonPath = join5(projectPath, "package.json");
if (!existsSync2(packageJsonPath)) {
return;
}
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
if (!packageJson.dependencies) {
packageJson.dependencies = {};
}
if (!packageJson.dependencies[packageName]) {
packageJson.dependencies[packageName] = "latest";
}
if (devDependencies && Object.keys(devDependencies).length > 0) {
if (!packageJson.devDependencies) {
packageJson.devDependencies = {};
}
for (const [name, version2] of Object.entries(devDependencies)) {
if (!packageJson.devDependencies[name]) {
packageJson.devDependencies[name] = version2;
}
}
}
writeFileSync2(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
`);
}
async function updatePagesConfig(projectPath, easycom) {
const pagesConfigPath = join5(projectPath, "pages.config.ts");
if (!existsSync2(pagesConfigPath)) {
return;
}
const originalContent = readFileSync2(pagesConfigPath, "utf8");
let content = originalContent;
const patternLiteral = escapeSingleQuotes(easycom.pattern);
const pathLiteral = escapeSingleQuotes(easycom.path);
const existingEntryRegex = new RegExp(
`["']${escapeForRegExp(patternLiteral)}["']\\s*:\\s*["']${escapeForRegExp(pathLiteral)}["']`
);
if (existingEntryRegex.test(content)) {
return;
}
const entry = { pattern: patternLiteral, path: pathLiteral };
content = addToExistingCustomBlock(content, entry) ?? addCustomBlock(content, entry) ?? addEasycomBlock(content, entry) ?? appendEasycomBlock(content, entry);
if (content !== originalContent) {
writeFileSync2(pagesConfigPath, ensureTrailingNewline(content));
}
}
async function updateTsConfig(projectPath, types) {
const tsConfigPath = join5(projectPath, "tsconfig.json");
if (!existsSync2(tsConfigPath)) {
return;
}
const tsConfig = JSON.parse(readFileSync2(tsConfigPath, "utf8"));
if (!tsConfig.compilerOptions) {
tsConfig.compilerOptions = {};
}
if (!tsConfig.compilerOptions.types) {
tsConfig.compilerOptions.types = [];
}
const existingTypes = tsConfig.compilerOptions.types;
for (const type of types) {
if (!existingTypes.includes(type)) {
existingTypes.push(type);
}
}
writeFileSync2(tsConfigPath, `${JSON.stringify(tsConfig, null, 2)}
`);
}
async function updateMainTs(projectPath, importCode) {
const mainTsPath = join5(projectPath, "src", "main.ts");
if (!existsSync2(mainTsPath)) {
return;
}
let content = readFileSync2(mainTsPath, "utf8");
const firstLine = importCode.split("\n")[0];
if (content.includes(firstLine)) {
return;
}
const vueImportRegex = /(import\s+(?:\S.*)?from\s+['"]vue['"];?\s*\n)/;
const match = content.match(vueImportRegex);
if (match) {
content = content.replace(vueImportRegex, `$1${importCode}
`);
} else {
content = `${importCode}
${content}`;
}
if (importCode.includes("uview-pro")) {
const appUseRegex = /(const\s+app\s*=\s*createSSRApp\(App\);?\s*\n)/;
const appUseMatch = content.match(appUseRegex);
if (appUseMatch && !content.includes("app.use(uViewPro)")) {
content = content.replace(appUseRegex, `$1 app.use(uViewPro);
`);
}
}
writeFileSync2(mainTsPath, content);
}
async function updateUniScss(projectPath, importCode) {
const uniScssPath = join5(projectPath, "src", "uni.scss");
if (!existsSync2(uniScssPath)) {
const altPath = join5(projectPath, "uni.scss");
if (existsSync2(altPath)) {
await updateScssFile(altPath, importCode);
}
return;
}
await updateScssFile(uniScssPath, importCode);
}
async function updateScssFile(filePath, importCode) {
let content = readFileSync2(filePath, "utf8");
if (content.includes(importCode)) {
return;
}
content = `${content.trim()}
${importCode}
`;
writeFileSync2(filePath, content);
}
async function updateAppVue(projectPath, importCode) {
const appVuePath = join5(projectPath, "src", "App.vue");
if (!existsSync2(appVuePath)) {
return;
}
let content = readFileSync2(appVuePath, "utf8");
if (content.includes(importCode)) {
return;
}
const styleRegex = /(<style[^>]*>)/;
const match = content.match(styleRegex);
if (match) {
content = content.replace(styleRegex, `$1
${importCode}`);
} else {
content = `${content}
<style lang="scss">
${importCode}</style>`;
}
writeFileSync2(appVuePath, content);
}
function addToExistingCustomBlock(content, entry) {
const customIndex = content.indexOf("custom:");
if (customIndex === -1) {
return null;
}
const braceIndex = content.indexOf("{", customIndex);
if (braceIndex === -1) {
return null;
}
const closingIndex = findMatchingBrace(content, braceIndex);
if (closingIndex === -1) {
return null;
}
const inside = content.slice(braceIndex + 1, closingIndex);
const hasEntries = inside.trim().length > 0;
const entryIndent = `${getLineIndent(content, braceIndex)} `;
const entryLine = `${entryIndent}'${entry.pattern}': '${entry.path}',`;
if (!hasEntries) {
const closingIndent = getLineIndent(content, closingIndex);
return `${content.slice(0, braceIndex + 1)}
${entryLine}
${closingIndent}${content.slice(closingIndex)}`;
}
const beforeRaw = content.slice(0, closingIndex).replace(/\s*$/, "");
const separator = beforeRaw.endsWith("\n") ? "" : "\n";
const after = content.slice(closingIndex);
return `${beforeRaw}${separator}${entryLine}
${after}`;
}
function addCustomBlock(content, entry) {
const easycomIndex = content.indexOf("easycom:");
if (easycomIndex === -1) {
return null;
}
const braceIndex = content.indexOf("{", easycomIndex);
if (braceIndex === -1) {
return null;
}
const closingIndex = findMatchingBrace(content, braceIndex);
if (closingIndex === -1) {
return null;
}
const easycomIndent = getLineIndent(content, braceIndex);
const customIndent = `${easycomIndent} `;
const entryIndent = `${customIndent} `;
const customBlock = `${customIndent}custom: {
${entryIndent}'${entry.pattern}': '${entry.path}',
${customIndent}},`;
const beforeRaw = content.slice(0, closingIndex).replace(/\s*$/, "");
const separator = beforeRaw.endsWith("\n") ? "" : "\n";
const after = content.slice(closingIndex);
return `${beforeRaw}${separator}${customBlock}
${after}`;
}
function addEasycomBlock(content, entry) {
const exportIndex = content.indexOf("export default");
if (exportIndex === -1) {
return null;
}
const braceIndex = content.indexOf("{", exportIndex);
if (braceIndex === -1) {
return null;
}
const closingIndex = findMatchingBrace(content, braceIndex);
if (closingIndex === -1) {
return null;
}
const blockIndent = `${getLineIndent(content, braceIndex)} `;
const entryIndent = `${blockIndent} `;
const block = `${blockIndent}easycom: {
${blockIndent} autoscan: true,
${blockIndent} custom: {
${entryIndent}'${entry.pattern}': '${entry.path}',
${blockIndent} },
${blockIndent}},`;
const before = content.slice(0, braceIndex + 1);
const after = content.slice(braceIndex + 1);
const prefix = before.endsWith("\n") ? before : `${before}
`;
return `${prefix}${block}
${after}`;
}
function appendEasycomBlock(content, entry) {
const block = `
export const easycom = {
autoscan: true,
custom: {
'${entry.pattern}': '${entry.path}',
},
}
`;
return `${content}${block}`;
}
function findMatchingBrace(content, startIndex) {
let depth = 0;
for (let i = startIndex; i < content.length; i += 1) {
const char = content[i];
if (char === "{") {
depth += 1;
} else if (char === "}") {
depth -= 1;
if (depth === 0) {
return i;
}
}
}
return -1;
}
function getLineIndent(content, index) {
const lineStart = content.lastIndexOf("\n", index) + 1;
const line = content.slice(lineStart, index);
const match = line.match(/^\s*/);
return match ? match[0] : "";
}
function escapeSingleQuotes(value) {
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}
function escapeForRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function ensureTrailingNewline(value) {
return value.endsWith("\n") ? value : `${value}
`;
}
// src/commands/create/generate.ts
var root = process4.cwd();
async function generateProject(options) {
debug("generateProject options", options);
const {
projectName,
platforms,
uiLibrary,
loginStrategy,
i18n,
chartLibraries
} = options;
const projectPath = path4.join(root, projectName);
debug("\u62C9\u53D6 base \u5206\u652F");
await cloneRepoByBranch(root, projectName, "base", options);
if (i18n) {
debug("\u6CE8\u5165 i18n feature");
const results = await injectI18n(projectPath);
for (const result of results) {
if (result.success) {
debug(result.message);
} else {
logger.warn(result.message);
}
}
}
if (loginStrategy) {
debug("\u6CE8\u5165 login feature");
const results = await injectLogin(projectPath);
for (const result of results) {
if (result.success) {
debug(result.message);
} else {
logger.warn(result.message);
}
}
}
for (const chartLibrary of chartLibraries || []) {
if (chartLibrary === "lime-echart") {
debug("\u6CE8\u5165 lime-echart feature");
const results = await injectLimeEchart(projectPath);
for (const result of results) {
if (result.success) {
debug(result.message);
} else {
logger.warn(result.message);
}
}
}
if (chartLibrary === "ucharts") {
debug("\u6CE8\u5165 ucharts feature");
const results = await injectUcharts(projectPath);
for (const result of results) {
if (result.success) {
debug(result.message);
} else {
logger.warn(result.message);
}
}
}
}
if (uiLibrary === "none") {
debug("\u4E0D\u5F15\u5165\u4EFB\u4F55UI\u5E93");
} else {
debug(`\u914D\u7F6E UI \u5E93: ${uiLibrary}`);
try {
await applyUILibraryConfig(projectPath, uiLibrary);
logger.success(`UI \u5E93 ${uiLibrary} \u914D\u7F6E\u5B8C\u6210`);
} catch (error) {
logger.warn(`UI \u5E93 ${uiLibrary} \u914D\u7F6E\u5931\u8D25: ${error.message}`);
logger.info("\u60A8\u53EF\u4EE5\u5728\u9879\u76EE\u521B\u5EFA\u540E\u624B\u52A8\u914D\u7F6E UI \u5E93");
}
}
const selectedFeatures = getSelectedFeatures(options);
const allDeps = {};
for (const feature of selectedFeatures) {
if (feature.dependencies) {
Object.assign(allDeps, feature.dependencies);
}
}
if (Object.keys(allDeps).length > 0) {
log4.info(`Feature \u4F9D\u8D56: ${Object.keys(allDeps).join(", ")}`);
}
try {
log4.success(`\u9879\u76EE${projectName}\u521B\u5EFA\u6210\u529F\uFF01`);
logger.info("\u4E0B\u4E00\u6B65:");
logger.info(` cd ${projectName}`);
logger.info(" pnpm i");
logger.info(" pnpm dev");
logger.info(" \u8FD0\u884C\u5B8C\u4EE5\u4E0A\u547D\u4EE4\u540E\uFF0C\u518D\u8FD0\u884C\u5176\u4ED6\u5E73\u53F0");
logger.info(" \u5982\uFF1Apnpm dev:mp, pnpm dev:app \u7B49");
} catch (error) {
logger.error(`\u751F\u6210\u9879\u76EE\u5931\u8D25: ${error.message}`);
throw error;
}
}
// src/commands/create/prompts.ts
import process5 from "process";
import { cancel as cancel2, confirm, isCancel as isCancel2, multiselect as multiselect2, select, text } from "@clack/prompts";
import { green as green2, red as red3 } from "kolorist";
var VALID_PLATFORMS = ["h5", "mp-weixin", "app", "mp-alipay", "mp-toutiao"];
var VALID_UI_LIBRARIES = ["none", "wot-ui-v2", "wot-ui", "uview-pro", "sard-uniapp", "uv-ui", "uview-plus", "tdesign"];
var VALID_CHART_LIBRARIES = ["lime-echart", "ucharts"];
async function promptUser(projectName, argv = {}) {
let platforms;
let uiLibrary;
let loginStrategy;
let i18n;
let chartLibraries;
const platformArg = argv.p || argv.platform;
if (platformArg) {
let parsedPlatforms = [];
if (Array.isArray(platformArg)) {
parsedPlatforms = platformArg;
} else if (typeof platformArg === "string") {
parsedPlatforms = platformArg.split(",");
}
const invalidPlatforms = parsedPlatforms.filter((p) => !VALID_PLATFORMS.includes(p));
if (invalidPlatforms.length > 0) {
console.error(red3(`\u65E0\u6548\u7684\u5E73\u53F0\u53C2\u6570: ${invalidPlatforms.join(", ")}`));
console.error(red3(`\u53EF\u9009\u503C: ${VALID_PLATFORMS.join(", ")}`));
process5.exit(1);
}
platforms = parsedPlatforms;
}
const uiArg = argv.u || argv.ui;
if (uiArg) {
if (!VALID_UI_LIBRARIES.includes(uiArg)) {
console.error(red3(`\u65E0\u6548\u7684UI\u5E93\u53C2\u6570: ${uiArg}`));
console.error(red3(`\u53EF\u9009\u503C: ${VALID_UI_LIBRARIES.join(", ")}`));
process5.exit(1);
}
uiLibrary = uiArg;
}
const loginArg = argv.l ?? argv.login;
if (loginArg === true || loginArg === "true") {
loginStrategy = true;
} else if (loginArg === false || loginArg === "false") {
loginStrategy = false;
}
const i18nArg = argv.i ?? argv.i18n;
if (i18nArg === true || i18nArg === "true") {
i18n = true;
} else if (i18nArg === false || i18nArg === "false") {
i18n = false;
}
const chartArgs = [];
if (argv["lime-echart"] === true || argv["lime-echart"] === "true") {
chartArgs.push("lime-echart");
}
if (argv.ucharts === true || argv.ucharts === "true" || argv.uchart === true || argv.uchart === "true") {
chartArgs.push("ucharts");
}
if (chartArgs.length > 0) {
const invalidCharts = chartArgs.filter((c) => !VALID_CHART_LIBRARIES.includes(c));
if (invalidCharts.length > 0) {
console.error(red3(`\u65E0\u6548\u7684\u56FE\u8868\u5E93\u53C2\u6570: ${invalidCharts.join(", ")}`));
console.error(red3(`\u53EF\u9009\u503C: ${VALID_CHART_LIBRARIES.join(", ")}`));
process5.exit(1);
}
chartLibraries = Array.from(new Set(chartArgs));
}
try {
if (!projectName) {
const inputProjectName = await text({
message: `\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0${green2("[\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u77ED\u6A2A\u7EBF\uFF0C\u5343\u4E07\u522B\u5199\u4E2D\u6587]")}`,
initialValue: "",
validate: (value) => {
if (!value?.trim())
return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
const errorMessage = checkProjectNameExistAndValidate(value);
if (errorMessage)
return errorMessage;
return;
}
});
if (isCancel2(inputProjectName)) {
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
process5.exit(0);
}
projectName = inputProjectName;
}
if (!platforms) {
const selectedPlatforms = await multiselect2({
message: `\u8BF7\u9009\u62E9\u9700\u8981\u652F\u6301\u7684\u5E73\u53F0\uFF08\u591A\u9009\uFF09${green2("[\u811A\u624B\u67B6\u5C06\u6839\u636E\u6240\u9009\u5E73\u53F0\u751F\u6210\u5BF9\u5E94\u7684\u5E73\u53F0\u4EE3\u7801\uFF0C\u8BF7\u6839\u636E\u5B9E\u9645\u60C5\u51B5\u9009\u62E9]")}`,
options: [
{ value: "h5", label: "H5" },
{ value: "mp-weixin", label: "\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F" },
{ value: "app", label: "APP" },
{ value: "mp-alipay", label: "\u652F\u4ED8\u5B9D\u5C0F\u7A0B\u5E8F\uFF08\u5305\u542B\u9489\u9489\uFF09" },
{ value: "mp-toutiao", label: "\u6296\u97F3\u5C0F\u7A0B\u5E8F" }
],
initialValues: ["h5"],
// 默认选择 H5
required: true
});
if (isCancel2(selectedPlatforms)) {
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
process5.exit(0);
}
platforms = selectedPlatforms;
}
if (!uiLibrary) {
const selectedUiLibrary = await select({
message: "\u8BF7\u9009\u62E9UI\u5E93",
options: [
{ value: "none", label: "\u65E0UI\u5E93" },
{ value: "wot-ui-v2", label: "wot-ui V2\uFF08@wot-ui/ui\uFF09" },
{ value: "wot-ui", label: "wot-ui\uFF08v1 / wot-design-uni\uFF09" },
{ value: "uview-pro", label: "uview-pro" },
{ value: "sard-uniapp", label: "sard-uniapp" },
{ value: "uv-ui", label: "uv-ui" },
{ value: "uview-plus", label: "uview-plus" },
{ value: "tdesign", label: "tdesign" }
],
initialValue: "none"
});
if (isCancel2(selectedUiLibrary)) {
cancel2("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
process5.exit(0);
}
uiLibrary = selectedUiLibrary;
}
if (loginStrategy === void 0) {
const selectedLoginStrategy = await confirm({
message: `\u662F\u5426\u9700\u8981\u767B\u5F55\u7B56\u7565\uFF08\u9ED1\u767D\u540D\u5355\u3001\u767B\u5F55\u62E6\u622A\u7B49\uFF09\uFF1F${green2("[\u6682\u4E0D\u77E5\u9053\u7684\uFF0C\u900