UNPKG

madp-cli1

Version:

A simple CLI for scaffolding madp projects, we provide madp-template to quickly build small and medium sized app.

612 lines (584 loc) 19.3 kB
const jsonfile = require('jsonfile'); const fs = require('fs-extra'); const path = require('path'); const ora = require('ora'); const decompress = require('decompress'); const tmp = require('tmp'); const request = require('request').defaults({ headers: { 'User-Agent': 'request', }, }); const utils = require('./lib/utils'); const logger = require('./lib/utils/logger'); class TemplateRelease { /** * 构造函数,必须传入以下参数 * @param {String} name 项目的名称,将用于在家目录创建形如 .eeui 的缓存目录 * @param {String} releaseUrl 形如 https://api.github.com/repos/kuaifan/eeui-template/releases * @return {TemplateRelease} */ constructor(name, releaseUrl, templateUrl, methodsUrl) { if (!name) { throw new Error('Invalid argument'); } // if (!name || !releaseUrl) { // throw new Error('Invalid argument'); // } this.name = name; this.releaseUrl = releaseUrl; this.templateUrl = templateUrl; this.methodsUrl = methodsUrl; this.CACHE_DIR_NAME = '.' + name; this.CACHE_DIR_PATH = path.join(require('os').homedir(), this.CACHE_DIR_NAME); this.CACHE_TEMPLATE_PATH = path.join(this.CACHE_DIR_PATH, 'template'); this.CACHE_METHODS_PATH = path.join(this.CACHE_DIR_PATH, 'methods'); this.RELEASES_JSON_PATH = path.join(this.CACHE_TEMPLATE_PATH, 'release.json'); } /** * 获取所有 release 的版本。只获取版本,不会下载到缓存区。 * @param {Function} cb 接受参数 error 以及无错时的版本数组 []string */ fetchReleaseVersions(cb) { // this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases request.get(this.releaseUrl, function (err, res, body) { if (err) { cb && cb(err); return; } if (res.statusCode !== 200) { cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`); return; } let tags = JSON.parse(body).map(function (e) { return e; }); console('zoulezheli***********') cb && cb(null, tags); }); } fetchTemplateReleaseVersions(cb) { // this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases request.get(this.templateUrl, function (err, res, body) { if (err) { cb && cb(err); return; } if (res.statusCode !== 200) { cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`); return; } let tags = JSON.parse(body).map(function (e) { return e; }); cb && cb(null, tags); }); } /** * 获取指定版本的 release,首先尝试缓存(CACHE_TEMPLATE_PATH),如果未缓存,再尝试下载并缓存 * @param {string} release 指定版本,如果为空,表示最新版本 * @param {string} location 下载服务器 * @param {Function} cb 通过该回调返回错误 error,以及无错时的 release 的路径,一般形如 ~/.eeui/template/0.1.0 */ fetchRelease(release, location, obj, cb) { let type = release.split('|')[0]; let releaseVersion = release.split('|')[1]; let releasesInfo = this._readReleaseJSON(); console.log(releasesInfo, 'relaseinfo') // console.log(release) // if (release) { // let info = releasesInfo[release]; // if (info) { // cb(null, path.join(this.CACHE_TEMPLATE_PATH, info.path)); // return; // } // } // let url = this._getReleaseUrl(release); // let url = this.releaseUrl; // console.log(url) let spinText = `正在下载模板版本: ${releaseVersion ? releaseVersion : 'latest'}...`; let spinDown = ora(spinText); // spinDown.start(); // https://api.github.com/repos/kuaifan/eeui-template/releases/tags/2.4.2 // { // "url": "https://api.github.com/repos/kuaifan/eeui-template/releases/35346901", // "assets_url": "https://api.github.com/repos/kuaifan/eeui-template/releases/35346901/assets", // "upload_url": "https://uploads.github.com/repos/kuaifan/eeui-template/releases/35346901/assets{?name,label}", // "html_url": "https://github.com/kuaifan/eeui-template/releases/tag/2.4.2", // "id": 35346901, // "author": { // "login": "kuaifan", // "id": 36875852, // "node_id": "MDQ6VXNlcjM2ODc1ODUy", // "avatar_url": "https://avatars2.githubusercontent.com/u/36875852?v=4", // "gravatar_id": "", // "url": "https://api.github.com/users/kuaifan", // "html_url": "https://github.com/kuaifan", // "followers_url": "https://api.github.com/users/kuaifan/followers", // "following_url": "https://api.github.com/users/kuaifan/following{/other_user}", // "gists_url": "https://api.github.com/users/kuaifan/gists{/gist_id}", // "starred_url": "https://api.github.com/users/kuaifan/starred{/owner}{/repo}", // "subscriptions_url": "https://api.github.com/users/kuaifan/subscriptions", // "organizations_url": "https://api.github.com/users/kuaifan/orgs", // "repos_url": "https://api.github.com/users/kuaifan/repos", // "events_url": "https://api.github.com/users/kuaifan/events{/privacy}", // "received_events_url": "https://api.github.com/users/kuaifan/received_events", // "type": "User", // "site_admin": false // }, // "node_id": "MDc6UmVsZWFzZTM1MzQ2OTAx", // "tag_name": "2.4.2", // "target_commitish": "master", // "name": "", // "draft": false, // "prerelease": false, // "created_at": "2020-12-16T11:45:25Z", // "published_at": "2020-12-16T12:10:29Z", // "assets": [ // ], // "tarball_url": "https://api.github.com/repos/kuaifan/eeui-template/tarball/2.4.2", // "zipball_url": "https://api.github.com/repos/kuaifan/eeui-template/zipball/2.4.2", // "body": "升级:WeexSDK至0.29.0-RC1\r\n修复:iphone7 iOS14.2系统环境下,获取导航栏高度闪退bug(@xztl 提供)\r\n修复:获取导航栏部分设备和系统主线程卡死问题(@xztl 提供)" // } // request(url, (err, res, body) => { // let body1 = JSON.parse(body); // console.log("=====") // console.log(type+"||"+releaseVersion) console.log("=====155") let info = obj; // let info = {}; // body1.some((t) => { // if (t.type == type && t.tag_version == releaseVersion) { // info = t; // } // }); // console.log(body1,'162') // let info={ // "tag_name": release, // "published_at":"2020-12-16T12:10:29Z", // "zipball_url":'http://localhost:5000/madp-seed-h5.zip' // } // spinDown.stop(); let newInfo = {}; let tag = (newInfo.tag = info['tag_name'] + info['tag_version']); // newInfo.time = info["published_at"]; newInfo.path = newInfo.tag; // console.log("target:"+this.CACHE_TEMPLATE_PATH+":"+newInfo.path) console.log(this.CACHE_TEMPLATE_PATH) let targetPath = path.join(this.CACHE_TEMPLATE_PATH, newInfo.path); console.log("=====177") console.log('info', info) console.log("=====179") console.log('newInfo', newInfo) console.log("=====") console.log('targetPath', targetPath) if (fs.pathExistsSync(targetPath)) { logger.info(`已经缓存的版本。`); cb(null, targetPath); return; } spinDown.start(); console.log('zipball_url:' + info['zipball_url']); // this._downloadAndUnzip('http://192.168.20.24:5000/madp-seed.zip', targetPath, (err) => { this._downloadAndUnzip( info['zipball_url'], targetPath, (err) => { console.log('targetPath cb:' + targetPath); console.log('err:' + err); spinDown.stop(); if (err) { cb && cb(err); return; } releasesInfo[tag] = newInfo; console.log(releasesInfo, 'releasesInfo----!!!!!'); console.log("=====") console.log(this.RELEASES_JSON_PATH, 'this.RELEASES_JSON_PATH'); jsonfile.writeFileSync(this.RELEASES_JSON_PATH, releasesInfo, { spaces: 2 }); cb(null, targetPath); }, (res) => { spinDown.text = spinText + `(${res.progress}, ${res.speed})`; }, ); // }); } /** * 返回缓存里的 release 信息 * @param {string} [release] 指定版本,不指定则返回最新 * @return {Object} release 信息 */ getCachedReleaseInfo(release) { let releasesInfo = this._readReleaseJSON(); if (release) { return releasesInfo[release]; } let latestRleaseInfo = null; for (let tag in releasesInfo) { let info = releasesInfo[tag]; if (!latestRleaseInfo) { latestRleaseInfo = info; } else { if (Date.parse(info.time) > Date.parse(latestRleaseInfo.time)) latestRleaseInfo = info; } } return latestRleaseInfo; } _readReleaseJSON() { fs.ensureFileSync(this.RELEASES_JSON_PATH); try { return jsonfile.readFileSync(this.RELEASES_JSON_PATH); } catch (e) { return {}; } } _getReleaseUrl(tag) { return this.releaseUrl + '/' + (tag ? `tags/${tag}` : 'latest'); } /** * 把 url (zipball_url) 的内容下载并解压到 savePath * @param {string} url * @param {string} savePath * @param {Function} cb 接收参数 error * @param {Function} progressCall 接收进度 */ _downloadAndUnzip(url, savePath, cb, progressCall) { let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip'; console.log('TMP_DOWNLOAD_PATH.', TMP_DOWNLOAD_PATH) let file = fs.createWriteStream(TMP_DOWNLOAD_PATH); file.on('close', () => { console.log(TMP_DOWNLOAD_PATH + "||||" + this.CACHE_TEMPLATE_PATH) decompress(TMP_DOWNLOAD_PATH, this.CACHE_TEMPLATE_PATH, { map: (file) => { if (file.data.length == 0) { file.type = 'directory'; } return file; }, }) .then(() => { console.log('===1'); let origPath = this._getLastReleasePath(savePath); console.log('===2' + origPath + '||' + savePath); if (origPath) { fs.moveSync(origPath, savePath); // 重命名为指定名 } fs.unlinkSync(TMP_DOWNLOAD_PATH); // 删除下载的压缩包 console.log('===3'); cb && cb(); }) .catch((err) => { cb && cb(`下载版本失败: ${err}`); }); }).on('error', (err) => { cb && cb(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); console.log('=====url', url) request .get(url) .on('error', function (err) { cb && cb(`下载版本错误: ${err}`); }) .on('response', function (res) { console.log(url, 'url') if (res.statusCode !== 200) { cb && cb('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); } progressCall && progressCall({ received: receivedBytes, total: totalBytes, speed: speedPer, progress: progress, }); }) .on('end', function () { clearInterval(speedInt); }) .pipe(file); } /** * 获取刚下载解压的 release 的路径 * TODO: 目前无法准确获取 release 解压之后的目录名称,只能根据某种模式推断 * templateReleaseUrl: "https://api.github.com/repos/kuaifan/eeui-template/releases", * [ 'madp', 'release.json' ] [ 'https:', '', 'api.github.com', 'repos', 'kuaifan', 'eeui-template', 'releases' ] */ _getLastReleasePath(savePath) { let files = fs.readdirSync(this.CACHE_TEMPLATE_PATH); console.log('||||||' + this.CACHE_TEMPLATE_PATH); console.log(files); console.log(savePath, '222222@@@'); for (let f of files) { if (savePath.indexOf(f) !== -1) { let ss = path.join(this.CACHE_TEMPLATE_PATH, f); console.log(ss); return ss; } } return null; } /** * 获取所有 方法集release 的版本。只获取版本,不会下载到缓存区。 * @param {Function} cb 接受参数 error 以及无错时的版本数组 []string */ fetchMethodsRelease(cb) { // this.releaseUrl=https://api.github.com/repos/kuaifan/eeui-template/releases request.get(this.methodsUrl, function (err, res, body) { if (err) { cb && cb(err); return; } if (res.statusCode !== 200) { cb && cb(`获取信息失败 - ${res.statusCode}: ${res.body}`); return; } console.log('fetchMethodsRelease 获取方法集列表===', res); let methods = JSON.parse(body).map(function (e) { return e; }); cb && cb(null, methods); }); } /** * 拉取方法集 * @param {*} cb */ fetchMothods(info, cb) { console.log('fetchMothods====', info); let loadText = `正在下载方法集包...`; let loading = ora(loadText).start(); let rundir = path.resolve(process.cwd()); console.log('rundir=====', rundir); let srcDir = rundir + '/src/utils'; let savePath = srcDir; console.log(savePath); this._downloadAndUnzipMthods( info, savePath, (err) => { console.log('targetPath cb:' + savePath); console.log('err:' + err); loading.stop(); if (err) { cb && cb(err); return; } cb(null, savePath); }, (res) => { loading.text = loadText + `(${res.progress}, ${res.speed})`; }, ); } _downloadAndUnzipMthods(info, savePath, cb, progressCall) { let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip';; let rundir = path.resolve(process.cwd()); console.log("rundir====",rundir); // console.log(TMP_DOWNLOAD_PATH) let file = fs.createWriteStream(TMP_DOWNLOAD_PATH); file.on('close', () => { // console.log(TMP_DOWNLOAD_PATH+"||||"+this.CACHE_METHODS_PATH) decompress(TMP_DOWNLOAD_PATH, path.resolve(rundir + '/src/utils'), { map: (file) => { if (file.data.length == 0) { file.type = 'directory'; } return file; }, }) .then(() => { console.log('===1'); console.log(savePath + '/' + info['tag_name']); fs.copySync(path.resolve(savePath + '/' + info['tag_name']), path.resolve(rundir + '/src/utils')); fs.remove(path.resolve(savePath + '/' + info['tag_name'])); // 删除解压后的文件夹 console.log('===3'); cb && cb(); }) .catch((err) => { cb && cb(`下载版本失败: ${err}`); }); }).on('error', (err) => { cb && cb(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); request .get(info['zipball_url']) .on('error', function (err) { cb && cb(`下载版本错误: ${err}`); }) .on('response', function (res) { if (res.statusCode !== 200) { cb && cb('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); } progressCall && progressCall({ received: receivedBytes, total: totalBytes, speed: speedPer, progress: progress, }); }) .on('end', function () { clearInterval(speedInt); }) .pipe(file); } /** * 拉取方法集 * @param {*} cb */ _fetchMothods(info, cb) { // console.log('fetchMothods====', info); let loadText = `正在下载...`; let loading = ora(loadText).start(); let savePath = path.resolve(process.cwd()); savePath=savePath+'/'+info['name'] // console.log(savePath,'savePath402'); this._downloadAndUnzipMthodsNew( info, savePath, (err) => { // console.log('targetPath cb:' + savePath); // console.log('err:' + err); loading.stop(); if (err) { cb && cb(err,savePath); return; } cb('',savePath); }, (res) => { loading.text = loadText + `(${res.progress}, ${res.speed})`; }, ); } _downloadAndUnzipMthodsNew(info, savePath, cb, progressCall) { let TMP_DOWNLOAD_PATH = tmp.tmpNameSync({ dir: require('os').tmpdir() }) + '.zip';; let rundir = path.resolve(process.cwd()); // console.log("rundir====", rundir); // console.log('TMP_DOWNLOAD_PATH',TMP_DOWNLOAD_PATH) fs.readFile(rundir+'/src' , function(err, data) { if(err){ fs.mkdir(rundir+'/src') } // }) let file = fs.createWriteStream(TMP_DOWNLOAD_PATH); file.on('close', () => { // console.log(TMP_DOWNLOAD_PATH+"||||"+this.CACHE_METHODS_PATH) decompress(TMP_DOWNLOAD_PATH, path.resolve(rundir+'/src'), { map: (file) => { if (file.data.length == 0) { file.type = 'directory'; } return file; }, }) .then(() => { // console.log('===11'); // console.log(savePath ,'savePath-222'); fs.copySync(path.resolve(rundir+'/src'),path.resolve(savePath)); fs.remove(path.resolve(rundir+'/src')); // 删除解压后的文件夹 // console.log('===33'); cb && cb(); }) .catch((err) => { cb && cb(`下载版本失败: ${err}`); }); }).on('error', (err) => { cb && cb(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); request .get(info['zipball_url']) .on('error', function (err) { cb && cb(`下载版本错误: ${err}`); }) .on('response', function (res) { // console.log('res',res.statusCode) if (res.statusCode !== 200) { cb && cb('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); } progressCall && progressCall({ received: receivedBytes, total: totalBytes, speed: speedPer, progress: progress, }); }) .on('end', function () { clearInterval(speedInt); }) .pipe(file); }) } } module.exports = TemplateRelease;