As a front-end developer, we used console a lot to debug our code, but as the volume of code increased, the log became cluttered and it became difficult to find the key information, especially with multiple people working together. Today we use Babel to regulate logging.
Train of thought
We can prefix each piece of consle. XXX with some key information (below). How to do that, of course, is today’s protagonist babel-loader.
implementation
- Babel iterates through the code to find the
console
Online AST astexplorer.net/
module.exports = () = > {
return {
visitor: {
CallExpression(path, state){
if (
path.node.callee.object &&
path.node.callee.object.name === 'console') {console.log('I found the console object');
}
}
}
}
}
Copy the code
- Gets information on the path of the expression
const basename = require("path").basename;
module.exports = () = > {
return {
visitor: {
CallExpression(path, state) {
if (
path.node.callee.object &&
path.node.callee.object.name === "console"
) {
// The file name
const filename = state.file.opts.filename;
const name = basename(filename);
// Print the location information
const location = ` ${path.node.loc.start.line}:${path.node.loc.start.column}`;
console.log(`name: ${name} \nlocation: ${location}`); ,}}}}; };Copy the code
- Insert the node
const basename = require("path").basename;
module.exports = () = > {
return {
visitor: {
CallExpression(path, state) {
if (
path.node.callee.object &&
path.node.callee.object.name === "console"
) {
// The file name
const filename = state.file.opts.filename;
const name = basename(filename);
// Print the location information
const location = ` ${path.node.loc.start.line}:${path.node.loc.start.column}`;
console.log(`name: ${name} \nlocation: ${location}`);
path.node.arguments.unshift({
type: 'StringLiteral'.value: ` [${name}${location}] `})}},},}; };Copy the code
- Introduce babel-Loader and our plug-in with Webpack as follows
// webpack.config.js
const consolePrefix = require('.. /lib/index')... .module: {
rules: [{test: /\.js$/,
use: [
{
loader: 'babel-loader'.options: {
plugins: [consolePrefix]
}
}
]
}
]
}
Copy the code
To optimize the
The above example has met most of the requirements. To make the plugin more functional and compatible, the complete code is as follows [Github]
const Path = require('path')
const t = require('@babel/types')
/** * convert the target string to a regular, Reference webpack * https://github.com/webpack/webpack/blob/c181294865dca01b28e6e316636fef5f2aad4eb6/lib/ModuleFilenameHelpers.js */
function asRegExp(test) {
if (typeof test === 'string') {
test = new RegExp(test.replace(/[-[\]{}()*+?.,\\^$|#\s]/g.'\ \ $&'))}return test
}
/** * Verify that the file name exists in the exclude list *@param {*} filename
* @param {*} exclude
* @returns boolean* /
function filenameInExclude(filename, exclude) {
if(! exclude)return false
exclude = asRegExp(exclude)
if (Array.isArray(exclude)) {
return exclude.map(asRegExp).some(r= > r.test(filename))
} else {
return exclude.test(filename)
}
}
/** * 1. If -> customPrefix is provided, the prefix is * 2. Else if -> If the print is in node_modules, Else e.g. [index.js 4:13] (showLocation controls whether to display the line number) */
module.exports = () = > {
return {
visitor: {
CallExpression(path, state) {
let {
customPrefix, // Customize the prefix
exclude = [], // Some components have their own specifications that need to be excluded
showLocation = true // Whether to display file location information [index.js 8:13]
} = state.opts || {}
const filename = state.file.opts.filename
if (filenameInExclude(filename, exclude)) return
if (
path.node.callee.object &&
path.node.callee.object.name === 'console'&& path.node.callee.property.name ! = ='table'
) {
let value
// Use the custom prefix passed in by the configuration
if (customPrefix) {
value = customPrefix
} else {
let prefix = ' '
// If node_modules is printed, the component's name is prefixed by default (@mfelibs is private)
const r = filename.match(/node_modules\/(@xxx\/)? / [^ \] +) /)
if (r) {
const componentName = r[2] | |' '
prefix = '📖' + componentName
// The component forces the line number not to be displayed
showLocation = false
} else {
prefix = Path.basename(filename)
}
const location = showLocation
? ` ${path.node.loc.start.line}:${path.node.loc.start.column}`
: ' '
value = ` [${prefix}${location}] `
}
value += ' '
const firstNode = path.node.arguments[0]
// Is a string or template string concatenated before the original character, otherwise %c %s will be handled incorrectly
if (t.isStringLiteral(firstNode)) {
firstNode.value = value + firstNode.value
} else if (t.isTemplateLiteral(firstNode)) {
const _val = firstNode.quasis[0].value
firstNode.quasis[0].value = {
raw: value + _val.raw,
cooked: value + _val.cooked
}
} else {
path.node.arguments.unshift({
type: 'StringLiteral',
value
})
}
}
}
}
}
}
Copy the code