前言
中台化的过程中往往会遇到包依赖包的问题,比如A和B2个工程都需要a包,a包又依赖b包,往往更新b包的一个小功能,就需要手动修改b包/a包/A工程/B工程里面package.json的版本。手动修改极其痛苦,特别在开发阶段需要频繁修改。虽然可以通过lerna在本地进行配置。但是lerna需要将这些工程/包放在一个目录下,管理起来非常麻烦。
解决方案
- 记录工程和包之间的依赖关系。这个过程可以放在jenkins打包的过程中,动态获取项目的包依赖。可以从package.json中获取包名字,从package-lock.json文件中获取包的具体版本,具体实现如下
#!/usr/bin/env node
const http = require('http');
const packageJson = require('./package.json');
const packageLockJson = require('./package-lock.json');
const { dependencies = {}, devDependencies = {} } = packageJson;
const dependenciesKeys = Object.keys(dependencies);
const devDependenciesKey = Object.keys(devDependencies);
// 获取项目中的所有依赖包,包含了dependencies和devDependencies
const packageKeys = [...dependenciesKeys, ...devDependenciesKey];
// 获取package-lock.json中安装的依赖包
const packageLockDependencies = packageLockJson && packageLockJson.dependencies;
// 获取依赖包的信息
const npmList = packageKeys.reduce((total, it) => {
total.push({
name: it,
version: packageLockDependencies[it] && packageLockDependencies[it].version
});
return total;
}, []);
const appName = process.argv[2];
const env = process.argv[3];
const contents = JSON.stringify({
appName,
npmList
});
/** 请求配置 */
const option = {
method: 'post',
host: xx,
prot: '80',
path: '/xx',
headers: {
'Content-Type': 'application/json',
'Content-Length': contents.length,
}
};
const dispose = function(response) {
let body = '';
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
console.log(body);
});
};
const req = http.request(option, dispose);
req.write(contents);// 发送内容
req.end();
-
包和包之间的依赖关系也和第一步类似,在发布到npm的时候动态记录。如果比较简单的化就是手动登记就可以了。
-
中台发布的过程中将需要发布的包和项目拉进来,前2步做的关联关系主要是为了这一步。有了关联关系后,我们只需要打包最底层的包,通过脚本自动修改package.json的版本就可以一层一层往外打,省去了手动修改的麻烦。
- 调用gitlab api获取当前包的package.json文件(本文使用egg作为服务端框架)
/**
* 获取修改后的package.json的内容
* @param {*} ctx ctx
* @param {*} projectId 仓库id
* @param {*} npmName 包的名称
* @param {*} npmVersion 包的version
*/
const getNewPackageJson = async (ctx, projectId, npmName, npmVersion, isCurrent = false, ref = 'master') => {
const result = await ctx.curl(`http://git.xx.cn/api/v4/projects/${projectId}/repository/files/package.json?ref=${ref}`, {
method: 'GET',
headers: config.headers,
});
// 如果没有获取到package.json内容
if (result.status !== 200) {
ctx.throw('获取不到package.json内容,请确认工程名以及工程下是否存在package.json文件');
}
const { content } = JSON.parse(result.data.toString('utf8'));
const packageJson = utils.decodeBase64(content); // package.json的内容
console.log(packageJson)
};
// util.js
const decodeBase64 = base64 => {
let buf;
if (Buffer.from && Buffer.from !== Uint8Array.from) {
buf = Buffer.from(base64, 'base64');
} else {
if (typeof notNumber === 'number') {
throw new Error('The "size" argument must be not of type number.');
}
buf = new Buffer(base64, 'base64');
}
const decodeValue = new Buffer(buf, 'base64').toString();
// 编码转字符串
return decodeValue;
};
- 动态修改依赖信息
const fs = require('fs');
const path = require('path');
const packageJson = fs.readFileSync(path.resolve(__dirname, './package.json'), 'utf-8');
let JsonParse = JSON.parse(packageJson)
JsonParse.version = '1.0.0'
JsonParse.dependencies.lodash = '5.0.0'
// json.stringify的第三个参数表示前面空几格
fs.writeFileSync(path.resolve(__dirname, './package.json'), JSON.stringify(JsonParse, null, 4));
- 至此,中台包的发布就已经完成了。
AST解决package.json前面空格问题
在实际使用中发现package.json文件有些包/文件是空4格,有些则是2格。部分同学还是喜欢手动修改,从而导致package.json因为空格的问题经常冲突。
解决方案: 将package.json文件转为ast树,然后获取version字段前面的空格数a,然后JSON.stringify的第三个参数就用a,具体实现如下
const fs = require('fs');
const path = require('path');
const { parse } = require('@babel/parser');
const { default: traverse } = require("@babel/traverse");
const packageJson = fs.readFileSync(path.resolve(__dirname, './package.json'), 'utf-8');
/**
* 获取空格数
* @param {*}} packageJson
* @param {*} callback
*/
const getSpace = (packageJson, callback) => {
let space = 2;
const obj = "const a = " + packageJson;
const visitor = {
ObjectProperty(_path){
const { node } = _path;
const key = node.key.value;
if(key === 'version'){
try{
space = node.key.loc.start.column;
}catch(e){
space = 2;
}
callback(space);
}
}
}
const ast = parse(obj);
traverse(ast, visitor);
}
getSpace(packageJson, (space) => {
const JsonParse = JSON.parse(packageJson);
// 修改对应的版本
JsonParse.version = '1.0.0';
JsonParse.age = '2.0.0';
// JSON.stringify的第三个参数为动态获取
fs.writeFileSync(path.resolve(__dirname, './package.json'), JSON.stringify(JsonParse, null, space));
})