UNPKG

14.2 kBJavaScriptView Raw
1const fs = require('fs');
2const fse = require("fs-extra");
3const path = require('path');
4const utils = require('../utils');
5const logger = require('../utils/logger');
6const inquirer = require('inquirer');
7const rimraf = require('rimraf');
8const shelljs = require('shelljs');
9const ora = require('ora');
10const decompress = require('decompress');
11const tmp = require('tmp');
12const request = require('request');
13const isWin = /^win/.test(process.platform);
14
15/**
16 * 添加插件
17 * @param op
18 */
19function add(op) {
20 let outputPath = path.resolve(op.rootDir, 'plugins', op.name);
21 if (fs.existsSync(outputPath)) {
22 inquirer.prompt([{
23 type: 'confirm',
24 message: `已存在名为${op.name}的插件,是否覆盖安装?`,
25 name: 'ok'
26 }]).then(answers => {
27 if (answers.ok) {
28 rimraf(outputPath, () => {
29 download(op)
30 })
31 } else {
32 logger.error(`放弃安装${op.name}!`);
33 typeof op.callback === 'function' && op.callback(op);
34 }
35 }).catch(console.error);
36 } else {
37 download(op)
38 }
39}
40
41/**
42 * 移除插件(不删除目录)
43 * @param op
44 */
45function remove(op) {
46 changeSetting(op, false);
47 changeGradle(op, false);
48 cleanIdea(op);
49 invokeAndroid(op, () => {
50 changeProfile(op, false);
51 invokeIos(op, () => {
52 utils.pluginsJson(false, op);
53 logger.success('插件' + op.name + '移除完毕!');
54 typeof op.callback === 'function' && op.callback(op);
55 request(op.baseUrl + op.name + '?act=uninstall', () => {});
56 });
57 });
58}
59
60function download(op) {
61 let outputPath = path.resolve(op.rootDir, 'plugins', op.name);
62 let downPath = tmp.tmpNameSync({dir: require('os').tmpdir()}) + ".zip";
63 let downUrl = null;
64 let downUrls = [];
65 let startDownload = () => {
66 let writeStream = fs.createWriteStream(downPath);
67 writeStream.on("close", () => {
68 decompress(downPath, outputPath).then(() => {
69 fs.unlinkSync(downPath);
70 utils.removeRubbish(outputPath);
71 utils.onlyOneDirMoveToParent(outputPath);
72 //
73 changeSetting(op, true);
74 changeGradle(op, true);
75 cleanIdea(op);
76 invokeAndroid(op, () => {
77 changeProfile(op, true);
78 invokeIos(op, () => {
79 let configFile = path.resolve(outputPath, 'config.json');
80 let configInfo = utils.jsonParse(!fs.existsSync(configFile) ? {} : fs.readFileSync(configFile, 'utf8'));
81 op.requireName = configInfo.requireName;
82 op.url = downUrl;
83 utils.pluginsJson(true, op);
84 logger.success('插件' + op.name + '添加成功!');
85 typeof op.callback === 'function' && op.callback(op);
86 request(op.baseUrl + op.name + '?act=install', () => {});
87 });
88 });
89 }).catch((err) => {
90 if (downUrls.length > 0) {
91 logger.warn(`资源似乎出了点问题...`);
92 logger.info(`正在重新尝试...`);
93 downUrl = downUrls[0];
94 downUrls = downUrls.filter(t => t !== downUrl);
95 setTimeout(startDownload, 1000);
96 } else {
97 logger.fatal(`插件${op.name}添加失败: ${err}!`);
98 }
99 });
100 }).on("error", (err) => {
101 if (downUrls.length > 0) {
102 op.__endExit = true;
103 logger.warn(`连接似乎出了点问题...`);
104 logger.info(`正在重新尝试...`);
105 downUrl = downUrls[0];
106 downUrls = downUrls.filter(t => t !== downUrl);
107 setTimeout(startDownload, 1000);
108 } else {
109 logger.fatal(`插件${op.name}下载失败: ${err}!`);
110 }
111 });
112 //
113 let receivedBytes = 0;
114 let totalBytes = 0;
115 let speedBytes = 0;
116 let speedPer = "0B/S";
117 let speedInt = setInterval(() => {
118 speedPer = utils.renderSize(Math.max(0, receivedBytes - speedBytes)) + "/S";
119 speedBytes = receivedBytes;
120 }, 1000);
121 let spinText = '插件' + op.name + '正在下载...';
122 let spinFetch = ora(spinText);
123 spinFetch.start();
124 request.get(downUrl).on("error", function (err) {
125 spinFetch.stop();
126 writeStream.emit('error', err);
127 }).on("response", function (res) {
128 if (res.statusCode !== 200) {
129 logger.fatal(`插件${op.name}下载失败: Get zipUrl return a non-200 response!`);
130 }
131 totalBytes = parseInt(res.headers['content-length'], 10);
132 if (isNaN(totalBytes)) totalBytes = 0;
133 }).on('data', (chunk) => {
134 receivedBytes += chunk.length;
135 let progress = "0%";
136 if (totalBytes > 0) {
137 progress = parseFloat(Math.max(0, receivedBytes / totalBytes * 100).toFixed(2)) + "%";
138 } else {
139 progress = utils.renderSize(receivedBytes);
140 }
141 spinFetch.text = spinText + `(${progress}, ${speedPer})`;
142 }).on("end", function () {
143 clearInterval(speedInt);
144 spinFetch.stop();
145 logger.info('插件' + op.name + '下载成功,开始添加...');
146 }).pipe(writeStream);
147 };
148 //
149 if (utils.count(op.fileinfo) <= 0) {
150 logger.fatal(`插件${op.name}没有下载地址!`);
151 }
152 if (utils.count(op.fileinfo) === 1 || op.simple === true) {
153 downUrl = op.fileinfo[0]['path'];
154 downUrls = (utils.likeArray(op.fileinfo[0]['paths']) ? op.fileinfo[0]['paths'] : []).filter(t => t !== downUrl);
155 logger.info('开始添加插件');
156 startDownload();
157 return;
158 }
159 let lists = [];
160 op.fileinfo.forEach(t => {
161 let name = t.name;
162 if (name.substr(-4, 4) === '.zip') name = name.substr(0, name.length - 4);
163 lists.push({
164 name: (lists.length + 1) + ". " + name + (t.desc ? " (" + t.desc + ")" : ""),
165 value: t
166 });
167 });
168 let array = [{
169 type: 'list',
170 name: 'release',
171 message: `选择插件${op.name}版本:`,
172 choices: lists
173 }];
174 inquirer.prompt(array).then(function(answers) {
175 downUrl = answers.release.path;
176 downUrls = (utils.likeArray(answers.release['paths']) ? answers.release['paths'] : []).filter(t => t !== downUrl);
177 logger.info('开始添加插件');
178 startDownload();
179 });
180}
181
182function changeSetting(op, isInstall) {
183 let gradleName = findGradleName(op);
184 if (utils.count(gradleName) === 0) {
185 return;
186 }
187 let tmpPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/settings.gradle');
188 let result = fs.readFileSync(tmpPath, 'utf8');
189 let temp = result.split('\n');
190 if (new RegExp("include\\s*('|\"):" + utils.spritUpperCase(op.name) + "\\1", "g").test(temp[0])) {
191 logger.fatal(utils.spritUpperCase(op.name) + '与项目同名,无法安装!');
192 }
193 let out = [];
194 for (let t in temp) {
195 if (temp.hasOwnProperty(t)) {
196 if (temp[t].indexOf(`":${utils.spritUpperCase(op.name)}"`) === -1 && temp[t]) {
197 if (temp[t].indexOf('include ') === 0) {
198 out.push('');
199 }
200 out.push(temp[t])
201 }
202 }
203 }
204 if (isInstall) {
205 out.push('');
206 out.push(`include ":${utils.spritUpperCase(op.name)}"`);
207 out.push(`project (":${utils.spritUpperCase(op.name)}").projectDir = new File("../../../plugins/${op.name}/android")`);
208 }
209 let s = '';
210 out.forEach((item) => {
211 s += item + '\n'
212 });
213 fs.writeFileSync(tmpPath, s.replace(/^\n+|\n+$/g, ""), 'utf8')
214}
215
216function changeGradle(op, isInstall) {
217 let gradleName = findGradleName(op);
218 if (utils.count(gradleName) === 0) {
219 return;
220 }
221 let tmpPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/app/build.gradle');
222 let result = fs.readFileSync(tmpPath, 'utf8');
223 let res = result.substr(result.indexOf('dependencies'), result.length);
224 let temp = res.split('\n');
225 let out = [];
226 temp.forEach((item) => {
227 if (!(item.indexOf('implementation') !== -1 &&
228 (item.indexOf(`":${utils.spritUpperCase(op.name)}"`) !== -1))) {
229 out.push(item)
230 }
231 });
232 if (isInstall) {
233 let temp = [];
234 let pos = 0;
235 let i = 0;
236 out.forEach((item) => {
237 i++;
238 temp.push(item);
239 if (item.indexOf("implementation") !== -1) {
240 pos = i;
241 }
242 });
243 temp.splice(pos, 0, ` implementation project(":${utils.spritUpperCase(op.name)}")`);
244 out = temp;
245 }
246 let string = '';
247 out.forEach((item) => { string += item + '\n' });
248 result = result.replace(res, string);
249 fs.writeFileSync(tmpPath, result.replace(/^\n+|\n+$/g, ""), 'utf8');
250}
251
252function cleanIdea(op) {
253 let ideGradlePath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/.idea/gradle.xml');
254 if (fs.existsSync(ideGradlePath)) {
255 let ideGradleResult = fs.readFileSync(ideGradlePath, 'utf8');
256 let ideGradleRege = new RegExp(`<option value="(.*?)/plugins/${op.name}/android"(.*?)/>\n*`, "g");
257 ideGradleResult = ideGradleResult.replace(ideGradleRege, "");
258 fs.writeFileSync(ideGradlePath, ideGradleResult, 'utf8');
259 }
260 //
261 let ideModulesPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp/.idea/modules.xml');
262 if (fs.existsSync(ideModulesPath)) {
263 let ideModulesResult = fs.readFileSync(ideModulesPath, 'utf8');
264 let ideModulesRege = new RegExp(`<module fileurl="(.*?)/plugins/${op.name}/android/(.*?).iml"(.*?)/>\n*`, "g");
265 ideModulesResult = ideModulesResult.replace(ideModulesRege, "");
266 fs.writeFileSync(ideModulesPath, ideModulesResult, 'utf8');
267 }
268}
269
270function changeProfile(op, isInstall) {
271 let xcodeprojName = findXcodeprojName(op);
272 if (utils.count(xcodeprojName) === 0) {
273 return;
274 }
275 let eeuiPath = path.resolve(op.rootDir, 'platforms/ios/eeuiApp');
276 let podPath = path.resolve(eeuiPath, 'Podfile');
277 let result = fs.readFileSync(podPath, 'utf8');
278 let temp = result.split('\n');
279 let out = [];
280 let weg = [];
281 let hasEnd = false;
282 temp.forEach((item) => {
283 if (item.trim() === 'end') {
284 hasEnd = true
285 }
286 if (!hasEnd) {
287 if (item.indexOf('\'' + xcodeprojName + '\'') === -1) {
288 out.push(item)
289 }
290 } else {
291 weg.push(item)
292 }
293 });
294 if (isInstall) {
295 out.push(' pod \'' + xcodeprojName + '\', :path => \'../../../plugins/' + op.name + '/ios\'');
296 }
297 weg.forEach((item) => {
298 out.push(item)
299 });
300 let px = '';
301 out.forEach((item) => {
302 px += item + '\n'
303 });
304 fs.writeFileSync(podPath, px.replace(/^\n+|\n+$/g, ""), 'utf8');
305}
306
307function invokeAndroid(op, callback) {
308 if (op.simple === true) {
309 typeof callback === 'function' && callback();
310 return;
311 }
312 let eeuiPath = path.resolve(op.rootDir, 'platforms/android/eeuiApp');
313 let tempPath = process.cwd();
314 let spinPod = ora('gradlew clean...');
315 spinPod.start();
316 try {
317 shelljs.cd(eeuiPath);
318 shelljs.exec('./gradlew clean', {silent: true}, () => {
319 shelljs.cd(tempPath);
320 spinPod.stop();
321 typeof callback === 'function' && callback();
322 });
323 } catch (e) {
324 shelljs.cd(tempPath);
325 spinPod.stop();
326 typeof callback === 'function' && callback();
327 }
328}
329
330function invokeIos(op, callback) {
331 if (op.simple === true) {
332 typeof callback === 'function' && callback();
333 return;
334 }
335 if (!shelljs.which('pod')) {
336 if (!isWin) {
337 logger.info('未检测到系统安装CocoaPods,请安装后手动执行pod install!');
338 }
339 typeof callback === 'function' && callback();
340 return;
341 }
342 let eeuiPath = path.resolve(op.rootDir, 'platforms/ios/eeuiApp');
343 let tempPath = process.cwd();
344 let spinPod = ora('pod install...');
345 spinPod.start();
346 try {
347 shelljs.cd(eeuiPath);
348 shelljs.exec('pod install', {silent: true}, () => {
349 shelljs.cd(tempPath);
350 spinPod.stop();
351 typeof callback === 'function' && callback();
352 });
353 } catch (e) {
354 shelljs.cd(tempPath);
355 spinPod.stop();
356 typeof callback === 'function' && callback();
357 }
358}
359
360function findXcodeprojName(op) {
361 let dirPath = path.resolve(op.rootDir, 'plugins', op.name, 'ios');
362 if (!fs.existsSync(dirPath)) {
363 return "";
364 }
365 let files = fs.readdirSync(dirPath);
366 let name = "";
367 files.some((filename) => {
368 let stats = utils.pathType(path.join(dirPath, filename));
369 if (stats === 2) {
370 if (utils.rightExists(filename, ".xcodeproj")) {
371 name = utils.rightDelete(filename, ".xcodeproj");
372 return true;
373 }
374 }
375 });
376 return name;
377}
378
379function findGradleName(op) {
380 let dirPath = path.resolve(op.rootDir, 'plugins', op.name, 'android');
381 if (!fs.existsSync(dirPath)) {
382 return "";
383 }
384 let files = fs.readdirSync(dirPath);
385 let name = "";
386 files.some((filename) => {
387 let stats = utils.pathType(path.join(dirPath, filename));
388 if (stats === 1) {
389 if (utils.rightExists(filename, ".gradle")) {
390 name = utils.rightDelete(filename, ".gradle");
391 return true;
392 }
393 }
394 });
395 return name;
396}
397
398module.exports = {add, remove, changeSetting, changeGradle, cleanIdea, changeProfile, invokeAndroid, invokeIos};
399
400
401
402