eper-cli
Version:
A simple CLI for scaffolding eeui projects, we provide eeui-template to quickly build small and medium sized app.
403 lines (386 loc) • 14.2 kB
JavaScript
const fs = require('fs');
const fse = require("fs-extra");
const path = require('path');
const utils = require('../utils');
const logger = require('../utils/logger');
const inquirer = require('inquirer');
const rimraf = require('rimraf');
const shelljs = require('shelljs');
const ora = require('ora');
const decompress = require('decompress');
const tmp = require('tmp');
const request = require('request');
const isWin = /^win/.test(process.platform);
/**
* 添加插件
* @param op
*/
function add(op) {
let outputPath = path.resolve(op.rootDir, 'plugins', op.name);
if (fs.existsSync(outputPath)) {
inquirer.prompt([{
type: 'confirm',
message: `已存在名为${op.name}的插件,是否覆盖安装?`,
name: 'ok'
}]).then(answers => {
if (answers.ok) {
rimraf(outputPath, () => {
download(op)
})
} else {
logger.error(`放弃安装${op.name}!`);
typeof op.callback === 'function' && op.callback(op);
}
}).catch(console.error);
} else {
download(op)
}
}
/**
* 移除插件(不删除目录)
* @param op
*/
function remove(op) {
changeSetting(op, false);
changeGradle(op, false);
cleanIdea(op);
invokeAndroid(op, () => {
changeProfile(op, false);
invokeIos(op, () => {
utils.pluginsJson(false, op);
logger.success('插件' + op.name + '移除完毕!');
typeof op.callback === 'function' && op.callback(op);
request(op.baseUrl + op.name + '?act=uninstall', () => {});
});
});
}
function download(op) {
let outputPath = path.resolve(op.rootDir, 'plugins', op.name);
let downPath = tmp.tmpNameSync({dir: require('os').tmpdir()}) + ".zip";
let downUrl = null;
let downUrls = [];
let startDownload = () => {
let writeStream = fs.createWriteStream(downPath);
writeStream.on("close", () => {
decompress(downPath, outputPath).then(() => {
fs.unlinkSync(downPath);
utils.removeRubbish(outputPath);
utils.onlyOneDirMoveToParent(outputPath);
//
changeSetting(op, true);
changeGradle(op, true);
cleanIdea(op);
invokeAndroid(op, () => {
changeProfile(op, true);
invokeIos(op, () => {
let configFile = path.resolve(outputPath, 'config.json');
let configInfo = utils.jsonParse(!fs.existsSync(configFile) ? {} : fs.readFileSync(configFile, 'utf8'));
op.requireName = configInfo.requireName;
op.url = downUrl;
utils.pluginsJson(true, op);
logger.success('插件' + op.name + '添加成功!');
typeof op.callback === 'function' && op.callback(op);
request(op.baseUrl + op.name + '?act=install', () => {});
});
});
}).catch((err) => {
if (downUrls.length > 0) {
logger.warn(`资源似乎出了点问题...`);
logger.info(`正在重新尝试...`);
downUrl = downUrls[0];
downUrls = downUrls.filter(t => t !== downUrl);
setTimeout(startDownload, 1000);
} else {
logger.fatal(`插件${op.name}添加失败: ${err}!`);
}
});
}).on("error", (err) => {
if (downUrls.length > 0) {
op.__endExit = true;
logger.warn(`连接似乎出了点问题...`);
logger.info(`正在重新尝试...`);
downUrl = downUrls[0];
downUrls = downUrls.filter(t => t !== downUrl);
setTimeout(startDownload, 1000);
} else {
logger.fatal(`插件${op.name}下载失败: ${err}!`);
}
});
//
let receivedBytes = 0;
let totalBytes = 0;
let speedBytes = 0;
let speedPer = "0B/S";
let speedInt = setInterval(() => {
speedPer = utils.renderSize(Math.max(0, receivedBytes - speedBytes)) + "/S";
speedBytes = receivedBytes;
}, 1000);
let spinText = '插件' + op.name + '正在下载...';
let spinFetch = ora(spinText);
spinFetch.start();
request.get(downUrl).on("error", function (err) {
spinFetch.stop();
writeStream.emit('error', err);
}).on("response", function (res) {
if (res.statusCode !== 200) {
logger.fatal(`插件${op.name}下载失败: Get zipUrl return a non-200 response!`);
}
totalBytes = parseInt(res.headers['content-length'], 10);
if (isNaN(totalBytes)) totalBytes = 0;
}).on('data', (chunk) => {
receivedBytes += chunk.length;
let progress = "0%";
if (totalBytes > 0) {
progress = parseFloat(Math.max(0, receivedBytes / totalBytes * 100).toFixed(2)) + "%";
} else {
progress = utils.renderSize(receivedBytes);
}
spinFetch.text = spinText + `(${progress}, ${speedPer})`;
}).on("end", function () {
clearInterval(speedInt);
spinFetch.stop();
logger.info('插件' + op.name + '下载成功,开始添加...');
}).pipe(writeStream);
};
//
if (utils.count(op.fileinfo) <= 0) {
logger.fatal(`插件${op.name}没有下载地址!`);
}
if (utils.count(op.fileinfo) === 1 || op.simple === true) {
downUrl = op.fileinfo[0]['path'];
downUrls = (utils.likeArray(op.fileinfo[0]['paths']) ? op.fileinfo[0]['paths'] : []).filter(t => t !== downUrl);
logger.info('开始添加插件');
startDownload();
return;
}
let lists = [];
op.fileinfo.forEach(t => {
let name = t.name;
if (name.substr(-4, 4) === '.zip') name = name.substr(0, name.length - 4);
lists.push({
name: (lists.length + 1) + ". " + name + (t.desc ? " (" + t.desc + ")" : ""),
value: t
});
});
let array = [{
type: 'list',
name: 'release',
message: `选择插件${op.name}版本:`,
choices: lists
}];
inquirer.prompt(array).then(function(answers) {
downUrl = answers.release.path;
downUrls = (utils.likeArray(answers.release['paths']) ? answers.release['paths'] : []).filter(t => t !== downUrl);
logger.info('开始添加插件');
startDownload();
});
}
function changeSetting(op, isInstall) {
let gradleName = findGradleName(op);
if (utils.count(gradleName) === 0) {
return;
}
let tmpPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/settings.gradle');
let result = fs.readFileSync(tmpPath, 'utf8');
let temp = result.split('\n');
if (new RegExp("include\\s*('|\"):" + utils.spritUpperCase(op.name) + "\\1", "g").test(temp[0])) {
logger.fatal(utils.spritUpperCase(op.name) + '与项目同名,无法安装!');
}
let out = [];
for (let t in temp) {
if (temp.hasOwnProperty(t)) {
if (temp[t].indexOf(`":${utils.spritUpperCase(op.name)}"`) === -1 && temp[t]) {
if (temp[t].indexOf('include ') === 0) {
out.push('');
}
out.push(temp[t])
}
}
}
if (isInstall) {
out.push('');
out.push(`include ":${utils.spritUpperCase(op.name)}"`);
out.push(`project (":${utils.spritUpperCase(op.name)}").projectDir = new File("../../../plugins/${op.name}/android")`);
}
let s = '';
out.forEach((item) => {
s += item + '\n'
});
fs.writeFileSync(tmpPath, s.replace(/^\n+|\n+$/g, ""), 'utf8')
}
function changeGradle(op, isInstall) {
let gradleName = findGradleName(op);
if (utils.count(gradleName) === 0) {
return;
}
let tmpPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/app/build.gradle');
let result = fs.readFileSync(tmpPath, 'utf8');
let res = result.substr(result.indexOf('dependencies'), result.length);
let temp = res.split('\n');
let out = [];
temp.forEach((item) => {
if (!(item.indexOf('implementation') !== -1 &&
(item.indexOf(`":${utils.spritUpperCase(op.name)}"`) !== -1))) {
out.push(item)
}
});
if (isInstall) {
let temp = [];
let pos = 0;
let i = 0;
out.forEach((item) => {
i++;
temp.push(item);
if (item.indexOf("implementation") !== -1) {
pos = i;
}
});
temp.splice(pos, 0, ` implementation project(":${utils.spritUpperCase(op.name)}")`);
out = temp;
}
let string = '';
out.forEach((item) => { string += item + '\n' });
result = result.replace(res, string);
fs.writeFileSync(tmpPath, result.replace(/^\n+|\n+$/g, ""), 'utf8');
}
function cleanIdea(op) {
let ideGradlePath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/.idea/gradle.xml');
if (fs.existsSync(ideGradlePath)) {
let ideGradleResult = fs.readFileSync(ideGradlePath, 'utf8');
let ideGradleRege = new RegExp(`<option value="(.*?)/plugins/${op.name}/android"(.*?)/>\n*`, "g");
ideGradleResult = ideGradleResult.replace(ideGradleRege, "");
fs.writeFileSync(ideGradlePath, ideGradleResult, 'utf8');
}
//
let ideModulesPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/.idea/modules.xml');
if (fs.existsSync(ideModulesPath)) {
let ideModulesResult = fs.readFileSync(ideModulesPath, 'utf8');
let ideModulesRege = new RegExp(`<module fileurl="(.*?)/plugins/${op.name}/android/(.*?).iml"(.*?)/>\n*`, "g");
ideModulesResult = ideModulesResult.replace(ideModulesRege, "");
fs.writeFileSync(ideModulesPath, ideModulesResult, 'utf8');
}
}
function changeProfile(op, isInstall) {
let xcodeprojName = findXcodeprojName(op);
if (utils.count(xcodeprojName) === 0) {
return;
}
let eeuiPath = path.resolve(op.rootDir, 'platforms/ios/eeuiApp');
let podPath = path.resolve(eeuiPath, 'Podfile');
let result = fs.readFileSync(podPath, 'utf8');
let temp = result.split('\n');
let out = [];
let weg = [];
let hasEnd = false;
temp.forEach((item) => {
if (item.trim() === 'end') {
hasEnd = true
}
if (!hasEnd) {
if (item.indexOf('\'' + xcodeprojName + '\'') === -1) {
out.push(item)
}
} else {
weg.push(item)
}
});
if (isInstall) {
out.push(' pod \'' + xcodeprojName + '\', :path => \'../../../plugins/' + op.name + '/ios\'');
}
weg.forEach((item) => {
out.push(item)
});
let px = '';
out.forEach((item) => {
px += item + '\n'
});
fs.writeFileSync(podPath, px.replace(/^\n+|\n+$/g, ""), 'utf8');
}
function invokeAndroid(op, callback) {
if (op.simple === true) {
typeof callback === 'function' && callback();
return;
}
let eeuiPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp');
let tempPath = process.cwd();
let spinPod = ora('gradlew clean...');
spinPod.start();
try {
shelljs.cd(eeuiPath);
shelljs.exec('./gradlew clean', {silent: true}, () => {
shelljs.cd(tempPath);
spinPod.stop();
typeof callback === 'function' && callback();
});
} catch (e) {
shelljs.cd(tempPath);
spinPod.stop();
typeof callback === 'function' && callback();
}
}
function invokeIos(op, callback) {
if (op.simple === true) {
typeof callback === 'function' && callback();
return;
}
if (!shelljs.which('pod')) {
if (!isWin) {
logger.info('未检测到系统安装CocoaPods,请安装后手动执行pod install!');
}
typeof callback === 'function' && callback();
return;
}
let eeuiPath = path.resolve(op.rootDir, 'platforms/ios/eeuiApp');
let tempPath = process.cwd();
let spinPod = ora('pod install...');
spinPod.start();
try {
shelljs.cd(eeuiPath);
shelljs.exec('pod install', {silent: true}, () => {
shelljs.cd(tempPath);
spinPod.stop();
typeof callback === 'function' && callback();
});
} catch (e) {
shelljs.cd(tempPath);
spinPod.stop();
typeof callback === 'function' && callback();
}
}
function findXcodeprojName(op) {
let dirPath = path.resolve(op.rootDir, 'plugins', op.name, 'ios');
if (!fs.existsSync(dirPath)) {
return "";
}
let files = fs.readdirSync(dirPath);
let name = "";
files.some((filename) => {
let stats = utils.pathType(path.join(dirPath, filename));
if (stats === 2) {
if (utils.rightExists(filename, ".xcodeproj")) {
name = utils.rightDelete(filename, ".xcodeproj");
return true;
}
}
});
return name;
}
function findGradleName(op) {
let dirPath = path.resolve(op.rootDir, 'plugins', op.name, 'android');
if (!fs.existsSync(dirPath)) {
return "";
}
let files = fs.readdirSync(dirPath);
let name = "";
files.some((filename) => {
let stats = utils.pathType(path.join(dirPath, filename));
if (stats === 1) {
if (utils.rightExists(filename, ".gradle")) {
name = utils.rightDelete(filename, ".gradle");
return true;
}
}
});
return name;
}
module.exports = {add, remove, changeSetting, changeGradle, cleanIdea, changeProfile, invokeAndroid, invokeIos};