preface
The last article covered the process of compiling Babel, and this one officially starts writing the Babel plug-in. If you are not clear about the process of compiling Babel, see the previous article
@babel/core
As the name suggests, this is the core package of Babel. First of all, this package integrates the @babel/ Parser, @babel/traverse, @babel/ Generator, and @babel/types packages described in the previous article. This means the package has the ability to parse, traverse, generate code, and extend other features.
Let’s take a look at the package.json file for @babel/core. You can see these packages are built into the package as well as other Babel packages.
This article will mainly use Babel /core to demonstrate, so you need to know the common API in advance, please click here @babel/core common API
Write the Babel plug-in
Babel plug-in specification
Writing the Babel plug-in is easy, just follow the rules provided by the official code. The official documentation
As you can see from the figure above, writing your own plug-in requires that the plug-in be provided with a method by default that returns an object containing the visitor attribute. Visitor is also an object whose property supports hook functions for different node types within which operations are performed on nodes of that type. (This is the hook function we provided in the previous article when we deeply traverse the node at the transformation step.)
Try it yourself, according to the official tips.
Create an empty project (same recipe, same taste), create build/index.js, install @babel/core,
Write build/index.js as follows:
const babel = require("@babel/core") const code = ` class Person { constructor(name){ this.name = name } say(){ Console. log(this.name)}} const person = new person; person.say() ` const obj = babel.transformSync(code, { plugins: [ function MyPlugin(babel) { return { visitor: { Identifier(path) { console.log(path.type, path.node.name) } } } } ] }); console.log(obj.code);Copy the code
The second argument to the @babel/core transformSync method can be passed to plugins, where you can specify your own plugins; Isn’t that easy? As for what the plug-in does, you need to decide for yourself.
The above is just a test run (the plug-in is not isolated from a separate file). Here we modify the compiled code to read the source from a file, as follows
const babel = require("@babel/core") const path = require("path"); const file = path.resolve(__dirname, './.. /src/index.js'); const obj = babel.transformFileSync(file, { plugins: [ function MyPlugin(babel) { return { visitor: { Identifier(path) { console.log(path.type, path.node.name) } } } } ] }); console.log(obj.code);Copy the code
TransformFileSync reads compiled code from a file. What is specified above is read from the SRC /index.js file.
From the above, it is clear how the Babel plug-in works. From a code point of view, Babel/Core provides an API with a plugins configuration property that supports passing in custom plugins.
Separate the plug – in
In front-end projects, the Babel plug-in appears as a standalone module. This is where the. Babelrc configuration file comes in handy. The transformFileSync second argument object has a babelrc property, which defaults to true. This property indicates whether presets and plugins are fetched from the babelrc file read from the project root.
Next, create the my-babel-plugin/index.js file in the project root, which is solely responsible for the plug-in functionality. Move the above build/index.js plugin code here as follows:
module.exports = function (babel) {
return {
visitor: {
Identifier(path) {
console.log(path.type, path.node.name)
}
}
}
}
Copy the code
Build /index.js looks like this:
const babel = require("@babel/core") const path = require("path"); const file = path.resolve(__dirname, './.. /src/index.js'); const obj = babel.transformFileSync(file, { babelrc: true }); console.log(obj.code);Copy the code
The.babelrc file is configured as follows:
{
"plugins": [
"./my-babel-plugin/index.js"
]
}
Copy the code
At this point, the Babel plug-in has been isolated. If you want to publish to NPM, all you need to do is follow the publishing specifications and procedures, install it, and configure it in.babelrc.
The source code to be compiled has es6 class syntax, but the compiled code does not change. Babel/Core, as we’ve talked about before, just provides the process for compiling. If you want to work with the code, you also need to provide plug-ins (in the second transform of the compile) to add, delete, change, and review the nodes so that you can modify the nodes to produce the desired executable code. The Babel plugin ecosystem is rich because of the rules that Babel officially provides for custom plugins.
If you want to deal with ES6 and above, use the Babel /preset-env preset package (which is a set of plug-ins), install the package, and change the. Babelrc configuration
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"./my-babel-plugin/index.js"
]
}
Copy the code
On this compilation run, you can see the Babel/PRESET -env output in the console as shown below
From the above compilation results, you can also get a further understanding of what es6 class code looks like when compiled into ES5 code.
Babel plug-in Demo development
The plug-in we wrote above doesn’t have any functionality. It just introduces the first steps of developing a custom plug-in. Now we implement a plug-in that removes the console code.
Step 1: Determine the type of node you want to operate on and understand the characteristics of the node. Here we resolve the AST node corresponding to console.log(123) online. You can see that the yellow section represents this line of console.log(123) nodes. The following
Step 2: Write the custom plug-in code, directly to the code, as follows
module.exports = function (babel) {
return {
visitor: {
Identifier(path) {
console.log(path.type, path.node.name);
},
CallExpression(path) {
if(path.node.callee && babel.types.isIdentifier(path.node.callee.object, {name: 'console'})){
path.remove();
}
}
}
}
}
Copy the code
It can be seen from the above figure that this expression corresponds to a node of the CallExpression type, which has a callee attribute corresponding to MemberExpression node, and a MemberExpression node whose object attribute is Identifier node. The Identifier node is a console expression only if its name is console. Then execute the path.remove method to remove the current node.
Types corresponds to the @babel/types plugin package, which is passed as an argument when the plugin is executed. The functionality of this package was covered in the previous article. (This provides an API that is often used when developing plug-ins, so it is very important).
When you compile again, you’ll notice that console.log/error/warn have been removed from the compiled code accordingly.
This concludes the Babel plugin practice series, thank you!