builder-isv
Version:
ISV 模块本地预览与云构建器
341 lines (274 loc) • 9.25 kB
JavaScript
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
}