First of all, I’m the author of the Babel Playbook, and I have a source code knowledge of Babel, so I’m qualified to discuss this topic.
The following topics would have been discussed:
- How does Babel convert the code
- How does jscodeshift translate code
- Difference between Babel and jscodeshift
- Why not recommend Gogocode
How does Babel convert the code
The Babel compilation process is divided into three steps: parse, Transform, and generate
- Parse: Convert source code to AST, Babel Parser (Babylon) supports esNext syntax, and supports typescript, JSX, flow, and other syntax via plug-ins
- Transform: Transforms the AST, and the different AST is processed by visitor
- Generate: Prints the transformed AST as object code and generates sourcemap
The conversion plug-in reads like this (linter’s case in the booklet) :
const { declare } = require('@babel/helper-plugin-utils');
const noFuncAssignLint = declare((api, options, dirname) = > {
api.assertVersion(7);
return {
pre(file) {
file.set('errors'[]); },visitor: {
AssignmentExpression(path, state) {
const errors = state.file.get('errors');
const assignTarget = path.get('left').toString()
const binding = path.scope.getBinding(assignTarget);
if (binding) {
if (binding.path.isFunctionDeclaration() || binding.path.isFunctionExpression()) {
const tmp = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
errors.push(path.buildCodeFrameError('can not reassign to function'.Error));
Error.stackTraceLimit = tmp; }}}},post(file) {
console.log(file.get('errors')); }}});module.exports = noFuncAssignLint;
Copy the code
Declare the visitor function, which is called during traversal to get the API for path and state:
Path is a relationship between nodes. Each path is associated with the parent node and the current node. The path object forms a path from the current node to the root node. State is the mechanism for sharing data during traversal.
Transform is completed by adding, deleting, changing, and checking the AST API.
For example, the following API:
getSibling(key)
getNextSibling()
getPrevSibling()
getAllPrevSiblings()
getAllNextSiblings()
isXxx(opts)
assertXxx(opts)
insertBefore(nodes)
insertAfter(nodes)
replaceWith(replacement)
replaceWithMultiple(nodes)
replaceWithSourceString(replacement)
remove()
Copy the code
How does jscodeshift translate code
Jscodeshift is also a code-conversion tool, but with a different API style. It actively looks up an AST, modifs it to a new AST, and generates code from it:
module.exports = function(fileInfo, api) {
return api.jscodeshift(fileInfo.source)
.findVariableDeclarators('foo')
.renameTo('bar')
.toSource();
}
Copy the code
Jscodeshift has the advantage of being simpler.
But can jscodeshift replace Babel? Take a look at Daniel’s answer:
Difference between Babel and jscodeshift
Jscodeshift’s Parser is Recast, and there were maintainers of Babel who wanted to combine the two:
Github.com/facebook/js…
Parse with Recast and then convert with Babel Parser.
Here’s a brilliant response to clarify the difference between Babel and jscodeshift:
Let me get this straight:
Babel’s Transform API is visitor style, which declares what to do with an AST and is called during traversal. This writing, which is not coupled to the specific traversal, is a design pattern (visitor pattern) that can handle even complex scenarios. Dealing with simple scenarios is just a little bit more verbose.
Jscodeshift is a collection style, similar to jquery. You can search for an AST, and then convert it to a simple scene. You can process many cases, if the search path is not correct. That might miss something, but it’s harder to be bug-free in complex scenarios than Babel.
Just like the difference between jquery and MVVM, the MVVM way (Babel) works for complex scenes, not missing some DOM processing. (Just an analogy)
So, simple scenarios can use jscodeshift, and all scenarios can use Babel.
The advantage of Babel’s visitor is the advantage of the visitor pattern in the design pattern, which is easy to reuse without coupling to concrete traversal.
Why not recommend Gogocode
Gogocode is the AST modification tool released by Ali’s mother these days. It has made a layer of encapsulation based on Babel, which is said to simplify AST operation.
The API looks something like this:
$(code)
.find('var a = 1')
.attr('declarations.0.id.name'.'c')
.root()
.generate();
Copy the code
Yes, the Visitor style API based on Babel encapsulates jscodeshift’s Collection style API.
Although the original Babel visitor is a bit troublesome to write, all paths can be handled. However, once a path is dropped after collection style is changed, there will be a bug. There are too many cases to handle and it is not suitable for complex scenarios.
Babel’s original visitor pattern was a benefit, but it ended up encapsulating the Collection API on top. If you want to do this, why not build jscodeshift directly… I don’t understand the wave operation.
I am not confident that I can handle all paths without missing cases in complex scenarios. I choose to use Babel’S API directly in complex scenarios.
conclusion
Babel is an implementation of the visitor design pattern, separating the traversal mode from the operation on the AST, making the operation reusable. Jscodeshift is a collection style, similar to jquery, which can easily drop cases from complex scenes.
Gogocode implements collection style based on Babel.
Summary: Jscodeshift can be used for simple scenarios, Babel can be used for all scenarios, gogocode is not recommended.
Finally, I advertised that if you want to learn Babel, you can read my booklet “Babel Plug-in Secrets”. The case code of Babel has been pushed by hand, and the booklet will be updated soon.