开发时经常会使用console.log进行调试,如果代码复杂,多个console.log输出往往会分不清其所在位置,因此可以通过一个插件,每次调用console.log时,自动加入其所在文件和代码行数,提升调试效率。
webpack plugin实现
这里是用webpack插件实现的,整体代码如下:
const pluginName = 'ConsoleLogWebpackPlugin';
class ConsoleLogWebpackPlugin {
apply(compiler) {
const wp = compiler.webpack;
compiler.hooks.normalModuleFactory.tap(pluginName, (factory) => {
factory.hooks.parser.for('javascript/auto')
.tap(pluginName, (parser, options) => {
parser.hooks.call.for('console.log').tap(pluginName, (expr) => {
if (!parser.state.module.resource.match(/node_modules/)) {
let filename = parser.state.module.resource.split('/').at(-1);
const dep1 = new wp.dependencies.ConstDependency(
`"${filename + ':' + expr.loc.start.line + ':'}",`,
expr.arguments[0].range[0]
)
dep1.loc = expr.arguments[0].loc
parser.state.module.addPresentationalDependency(dep1)
}
});
});
})
}
}
module.exports = ConsoleLogWebpackPlugin;
开发中的问题
个人感觉实现这个需求最简单的方式是修改ast。通过webpack的js parser,虽然能获取到ast,但并不能修改ast(改了也没效果)。正确的做法是修改webpack编译过程中的dependency。
比如说要删除console.log,则需要在对应位置添加一个空依赖:
const clearDep = new wp.dependencies.ConstDependency("", expr.range);
clearDep.loc = expr.loc;
parser.state.module.addDependency(clearDep);
不过webpack内部对象都没啥api文档,所以还是建议用babel插件来实现。
babel plugin实现
module.exports = function consoleLogPlugin({ types: t }) {
return {
visitor: {
CallExpression(path, state) {
const callee = path.get('callee');
if (
callee.isMemberExpression() &&
callee.get('object').isIdentifier({ name: 'console' }) &&
callee.get('property').isIdentifier({ name: 'log' })
) {
const { line } = path.node.loc.start;
const filename = state.file.opts.filename.split('/').at(-1);
const fileAndLineNode = t.stringLiteral(`${filename}:${line}`);
path.node.arguments.unshift(fileAndLineNode);
}
},
},
};
};