在 重构利器 jscodeshift 的末尾,留下了几个问题:
- 怎么做到同时满足
JavaScript
和TypeScript
的解析? jscodeshift
是如何实现链式调用的?registerMethods
怎么实现,怎么使用?- 测试工具
testUtils
做了哪些封装?
上一篇文章中我们举了一个 🌰:
export const sum = (a, b) => {
console.log('计算下面两个数的和:', a, b);
return a + b;
};
export const minus = (a, b) => {
console.log('计算下面两个数的差:' + a + ',' + b);
return a - b;
};
export const multiply = (a, b) => {
console.warn('计算下面两个数的积:', a, b);
return a * b;
};
export const divide = (a, b) => {
console.error(`计算下面两个数相除 ${
a}, ${
b}`);
return a / b;
};
转换器 transform
如下:
module.exports = (fileInfo, api, options) => {
const j = api.jscodeshift;
return j(fileInfo.source)
.find(j.ExpressionStatement, {
expression: {
callee: {
type: 'MemberExpression',
object: {
type: 'Identifier', name: 'console' },
}
},
})
.remove()
.toSource();
};
咱们从入口开始分析,先看看这个 transform
文件的几个参数是什么?这部分代码位于 src/Worker.js
:
function run(data) {
const files = data.files;
const options = data.options || {
};
if (!files.length) {
finish();
return;
}
async.each(
files,
function(file, callback) {
fs.readFile(file, async function(err, source) {
if (err) {
updateStatus('error', file, 'File error: ' + err);
callback();
return;
}
source = source.toString();
try {
// 获取解析器 🛠️
const jscodeshift = prepareJscodeshift(options);
// 这里的transform就是我们写在transform.js中的函数,对应的参数就是从这里来的
const out = await transform(
// 上述 🌰 中的 fileInfo 参数
{
path: file,
source: source,
},
// 上述 🌰 中的 api 参数
{
j: jscodeshift,
jscodeshift: jscodeshift,
stats: options.dry ? stats : empty,
report: msg => report(file, msg),
},
// 上述 🌰 中的 options 参数
options
);
// 无结果或者跟源码一样时
if (!out || out === source) {
updateStatus(out ? 'nochange' : 'skip', file);
callback();
return;
}
// --print 控制台打印结果
if (options.print) {
console.log(out); // eslint-disable-line no-console
}
// --dry 🤪🤪 如果不是陪跑,会将结果写入文件
if (!options.dry) {
writeFileAtomic(file, out, function(err) {
if (err) {
updateStatus('error', file, 'File writer error: ' + err);
} else {
updateStatus('ok', file);
}
callback();
});
} else {
updateStatus('ok', file);
callback();
}
} catch(err) {
updateStatus(
'error',
file,
'Transformation error ('+ err.message.replace(/\n/g, ' ') + ')\n' + trimStackTrace(err.stack)
);
callback();
}
});
},
function(err) {
if (err) {
updateStatus('error', '', 'This should never be shown!');
}
free();
}
);
}
run
的逻辑很简单,遍历文件调用 transform
函数去转换,同时将处理状态通过调用 notify
同步到父进程。先聚焦在获取解析器上:
/**
* 获取 jscodeshift 解析器
*/
f