UNPKG

builder-isv

Version:

ISV 模块本地预览与云构建器

341 lines (274 loc) 9.25 kB
var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var Debug = require('debug'); var execSync = require('child_process').execSync; var os = require('os'); var request = require('request'); var isWin = os.platform() === 'win32'; // 判断是否是window系统 var Promise = require('bluebird'); var Client = require('isv-module-info'); var debug = Debug('page'); var debugMini = Debug('page:mini'); var debugError = Debug('page:error'); var debugExtra = Debug('page:extra'); // var CACHE_SOURCE = '.source'; // 不包含source map // var CACHE_SOURCE_MAP = '.source.map'; // 包含source map var regWeb = /\.web(\.js)?$/; var regNative = /\.native(\.js)?$/; var config = require('../config'); // 一些配置项 var upx = Client.upxClient(config.upxEnv); // def 工具统计 var TOKEN_PAGE = 'acf6fe8073ec73fb160d53ce69d83165'; var API_COUNT = 'http://time.labs.taobao.net/api/time'; /** * 用于统计启动时间 * @param item:类型,'config'是1, 'compile'是2 * @param startTime */ function countDevTime(item, startTime) { var time = new Date().getTime() - startTime; debugMini('=====> 统计dev时间:' + item + ':' + time + ':' + startTime); // 容错 try { request.post({ url: API_COUNT, form: { token: TOKEN_PAGE, item: item, time: time } }, function(err) { if (err) { console.log(err); debugError('=====> 发送请求出现错误:', err); } }); } catch (err) { debugError('=====> 调用 request.post 出现错误:', err); } } function readFileOrEmpty(path) { try { debugMini('=====> 读取 ' + path + '文件内容'); return fs.readFileSync(path, 'utf8'); } catch (e) { debugError('=====> 读取 ' + path + '文件内容失败'); return ""; } } function writeFileOrNone(path, data) { fs.writeFile(path, data, 'utf8', function(err) { if (err) { console.log('=====> 写入数据到' + path + '失败'); debugError('=====> 想要写入的数据:' + data); return; } debugMini('=====> 已将数据写入到:' + path); debug('=====> 文件内容:' + data); }); } function writeFileOrNoneSync(path, data) { try { fs.writeFileSync(path, data, 'utf8'); console.log('文件' + path + '写入成功'); } catch (e) { console.log('文件写入失败'); } } // 计算同步执行callback所花费的时间 function calcTime(fn, msg) { var oStart = new Date(); fn(); var oEnd = new Date(); console.log(msg, ':', (oEnd - oStart) / 1000, 's'); } // 切换到指定目录进行tnpm ii操作 function tnpmInstall(curPath, msg, skipTnpm) { var command = isWin ? 'tnpm.cmd ii' : 'rm -rf node_modules/ && tnpm ii'; // 兼容windows var cmdName = 'tnpm ii'; // 如果指定不tnpm ii if (skipTnpm === 'true' || skipTnpm === true) { command = isWin ? 'tnpm.cmd update' : 'tnpm update'; // 使用 tnpm update 代替 ii cmdName = 'tnpm update' } debugMini('=====> 执行' + cmdName + '的根目录:' + curPath); // 同时创建缓存目录 mkdirSyncOrNone(path.join(curPath, config.cacheDir)); calcTime(function() { execSync(command, {cwd: curPath}); }, msg); } function isExist(path) { var exist = true; try { var stat = fs.statSync(path); } catch (e) { debugError('=====> 判断 ' + path + ' 目录是否存在失败'); debugError(e); exist = false; } return exist; } function parseOrFalse(str) { var json = {} try { if (str.length) { json = JSON.parse(str); debugMini('=====> 将字符串解析成 JSON 成功'); debug('=====> 获得的 JSON 的内容:' + json); return json; } else { return false; } } catch (err) { debugError('=====> 将字符串解析成 JSON 失败:', err); debugError('=====> 原字符串内容:' + str); return false; } } // 将资源写入到缓存文件中, // 这里的assets 一般来自于 done 阶段的 stats.compilation.assets // modDir 就是 mod 目录 function writeCachedSource(assets, modDir) { var REG_SOURCE_MAP = /\/\/#\s+sourceMappingURL\S+/g; // 匹配sourceMap // 将打包之后的代码缓存到文件中 _.forOwn(assets, function(asset, modName) { // 写入到指定缓存文件中 var info = modName.split('/'); var type = getEndType(modName); // 获取终端类型 var sourceFileName = [config.sourceFile, type, 'js'].join('.'); var sourceWithMapFileName = [config.sourceFile, type, 'map', 'js'].join('.'); var targetPath = path.join(modDir, info[0], config.cacheDir, sourceFileName); var targetMapPath = path.join(modDir, info[0], config.cacheDir, sourceWithMapFileName); debugMini('=====> 打包之后的资源写入:', targetPath); var source = asset.source(); // 需要去掉其中的sourceCode // 写两份数据,一份包含sourcemap writeFileOrNone(targetMapPath, source); // 为了兼容之前 light 调试的代码,需要将原始文件的源码放在 .cache.source.map // 如果想要去掉这层兼容,需要联系 龙喜 ,知会他去 .cachefile 找文件即可; if (type === 'origin') { var backSourceMapPath = path.join(modDir, info[0], '.cache.source.map'); writeFileOrNone(backSourceMapPath, source); } // 另一份不包含sourcemap source = source.replace(REG_SOURCE_MAP, ''); writeFileOrNone(targetPath, source); }); } // 将内容添加到 .gitignore 文件中 // repo:仓库本地路径 // rules:数组,规则数组字符串 function addRuleToIgnore(repo, rules) { var filePath = path.join(repo, '.gitignore'); var content = readFileOrEmpty(filePath); // 遍历rules var str = ''; _.forEach(rules, function(rule) { // 如果没有规则,则添加 if (content.indexOf(rule) === -1) { console.log('已将', rule, '添加到 ', repo, '/.gitignore 文件中'); str += rule + os.EOL; } }); if (str.length) { // 如果有内容,需要重新写到 .gitignore writeFileOrNone(filePath, content + os.EOL + str); } } // 新建文件夹 function mkdirSyncOrNone(path) { try { fs.mkdirSync(path); debugMini('=====> 创建文件夹成功:' + path); } catch (e) { if (e.code == 'EEXIST') { debugMini('=====> 文件夹已存在不需要创建'); } else { // 如果不是文件已存在的案例,则直接抛出错误 debugError('=====> 创建文件夹失败:', e); console.log('文件夹:', path, '创建失败'); throw e; } } } // 获取终端类型 // 入参:entry名字,比如 'tb-countdown-1111/index.web' // 出参:枚举,origin, web, native function getEndType(entryName) { var type = 'origin'; // 容错 if (typeof entryName === 'undefined' || entryName === null) { return type; } if (regWeb.test(entryName)) { type = 'web'; } else if (regNative.test(entryName)) { type = 'native'; } debugExtra('=====> 终端类型:', type); return type; } // 从 upx 获取版本信息 function getVersionFromUpx(dependencies) { // 如果是空的,制造标准的空白格式 if (_.isEmpty(dependencies)) { // 直接返回空白依赖,不需要查询 upx return new Promise.resolve({}); } /* ---------------------------------------------------- 如果有依赖,需要去 upx 查找 ----------------------------------------------------- */ return upx.request('npmService.getNpmVersionBySemVersion', [dependencies]).then(function(result) { // var result = util.parseOrFalse(data); debugExtra('======> 从 upx 检索获得的 deps:', result); if (!result) { // console.log('从 upx 检索依赖的数据格式有误,解析json出错'); throw new Error('从 upx 检索依赖的数据格式有误,解析json出错'); } // 组装成 version 格式 var verJSON = {}; _.forOwn(result, function(value, key) { verJSON[key] = value && value.version; }); return verJSON; }).catch(function(err) { console.log('处理请求发生错误:', err); throw new Error('从 upx 检索依赖失败,请检查网络状况 或 返回的数据格式有误'); }); } // 从upx同步规范信息 function* getGuideName(name, type) { try { var modInfo = yield upx.request('componentService.getComponentByInfo', [ { name: name, app: 'tb', type: 'mod' } ]); var guideId = modInfo.guideId; var guideInfo = yield upx.request('guideService.getGuideById', [guideId]); var guideName = guideInfo.name; return { guideName: guideName, guideId: guideId }; } catch (e) { console.log('该模块不是规范模块'); } } module.exports = { readFileOrEmpty: readFileOrEmpty, writeFileOrNone: writeFileOrNone, mkdirSyncOrNone: mkdirSyncOrNone, writeFileOrNoneSync: writeFileOrNoneSync, calcTime: calcTime, tnpmInstall: tnpmInstall, isExist: isExist, writeCachedSource: writeCachedSource, addRuleToIgnore: addRuleToIgnore, parseOrFalse: parseOrFalse, getEndType: getEndType, getVersionFromUpx: getVersionFromUpx, countDevTime: countDevTime, getGuideName: getGuideName }