
Heute werde ich Ihnen eine Schritt-für-Schritt-Anleitung zum Schreiben Ihres Babel- Plugins geben. Mit diesem Wissen können Sie Änderungen, Refactoring oder Codegenerierung automatisieren.
Was ist Babel?
Babel ist ein JavaScript-Compiler, der hauptsächlich zum Konvertieren von ECMAScript 2015+ Code verwendet wird, der abwärtskompatibel mit aktuellen und älteren Browsern oder Umgebungen ist. Babel verwendet ein Plugin-System, um Code zu konvertieren, sodass jeder sein eigenes Plugin schreiben kann.
, AST( ).
AST?
, :
, AST . JavaScript, AST JavaScript estree.
AST . AST babel, .
, AST, .
babel ?
, babel :
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
const code = 'const n = 1';
const ast = parse(code);
traverse(ast, {
  enter(path) {
    
    if (path.isIdentifier({ name: 'n' })) {
      path.node.name = 'x';
    }
  },
});
const output = generate(ast, code);
console.log(output.code); 
@babel/core . @babel/parser, @babel/traverse, @babel/generator @babel/core .
AST, AST .
 -> AST ->  AST ->  
, babel API :
import babel from '@babel/core';
const code = 'const n = 1';
const output = babel.transformSync(code, {
  plugins: [
    
    function myCustomPlugin() {
      return {
        visitor: {
          Identifier(path) {
            
            if (path.isIdentifier({ name: 'n' })) {
              path.node.name = 'x';
            }
          },
        },
      };
    },
  ],
});
console.log(output.code); 
babel n x, ?
myCustomPlugin . npm babel !
: ", babel , ...". , .
1.
:
:
function greet(name) {
  return 'Hello ' + name;
}
console.log(greet('tanhauhau')); 
:
function teerg(eman) {
      return 'H' + 'e' + 'l' + 'l' + 'o' + ' ' + eman;
    }
console.log(teerg('t' + 'a' + 'n' + 'h' + 'a' + 'u' + 'h' + 'a' + 'u')); 
console.log, , . ( !)
2. AST
babel AST explorer, AST:

AST, , .
, :
- Identifier 
- StringLiteral  .
3. AST
babel AST explorer, :

AST .
'H' + 'e' + 'l' + 'l' + 'o' + ' ' + eman (BinaryExpression) (StringLiteral)
4.
:
function myCustomPlugin() {
  return {
    visitor: {
      Identifier(path) {
        
      },
    },
  };
}
visitor.
, babel AST . visitor, , babel .
visitor :
function myCustomPlugin() {
  return {
    visitor: {
      Identifier(path) {
        console.log('');
      },
      StringLiteral(path) {
        console.log(' ');
      },
    },
  };
}
, 
 
 
, Identifer(path) {}. path node, ?
babel, path , , , , .. path , replaceWith, insertBefore, remove , AST .
path babel Jamie Kyle
.
babel AST explorer, name, , :
Identifier(path) {
  path.node.name = path.node.name
    .split('')
    .reverse()
    .join('');
}
:
function teerg(eman) {
  return 'Hello ' + eman;
}
elosnoc.gol(teerg('tanhauhau')); 
console.log . ?
AST:

console.log MemberExpression, c object — "console" property — "log"
, MemberExpression, :
if (
  !(
    path.parentPath.isMemberExpression() &&
    path.parentPath
      .get('object')
      .isIdentifier({ name: 'console' }) &&
    path.parentPath.get('property').isIdentifier({ name: 'log' })
  )
) {
 path.node.name = path.node.name
   .split('')
   .reverse()
   .join('');
}
}
, !
function teerg(eman) {
  return 'Hello ' + eman;
}
console.log(teerg('tanhauhau')); 
, Identifier console.log MemberExpression? Identifier.name === 'console' || Identifier.name === 'log'?
, console or log :
const log = 1;
isMemberExpression isIdentifier? @babel/types isXxxx, anyTypeAnnotation isAnyTypeAnnotation. , 
BinaryExpression .
AST, [@babel/types](https://babeljs.io/docs/en/babel-types):
StringLiteral(path) {
  const newNode = path.node.value
    .split('')
    .map(c => babel.types.stringLiteral(c))
    .reduce((prev, curr) => {
      return babel.types.binaryExpression('+', prev, curr);
    });
  path.replaceWith(newNode);
}
StringLiteral, path.node.value, c StringLiteral BinaryExpression. StringLiteral .
… ! , :
RangeError: Maximum call stack size exceeded
?
, StringLiteral StringLiteral, StringLiteral. StringLiteral StringLiteral, babel , , .
, babel StringLiteral newNode, ?
path.skip() :
StringLiteral(path) {
  const newNode = path.node.value
    .split('')
    .map(c => babel.types.stringLiteral(c))
    .reduce((prev, curr) => {
      return babel.types.binaryExpression('+', prev, curr);
    });
  path.replaceWith(newNode);
  path.skip();
}
… !
babel:
const babel = require('@babel/core');
const code = `
function greet(name) {
  return 'Hello ' + name;
}
console.log(greet('tanhauhau')); // Hello tanhauhau
`;
const output = babel.transformSync(code, {
  plugins: [
    function myCustomPlugin() {
      return {
        visitor: {
          StringLiteral(path) {
            const concat = path.node.value
              .split('')
              .map(c => babel.types.stringLiteral(c))
              .reduce((prev, curr) => {
                return babel.types.binaryExpression('+', prev, curr);
              });
            path.replaceWith(concat);
            path.skip();
          },
          Identifier(path) {
            if (
              !(
                path.parentPath.isMemberExpression() &&
                path.parentPath
                  .get('object')
                  .isIdentifier({ name: 'console' }) &&
                path.parentPath.get('property').isIdentifier({ name: 'log' })
              )
            ) {
              path.node.name = path.node.name
                .split('')
                .reverse()
                .join('');
            }
          },
        },
      };
    },
  ],
});
console.log(output.code);
, :
- AST
- AST
, babel GitHub babel.
https://github.com/babel/babel, babel-plugin-transform-* babel-plugin-proposal-*, -, babel nullish coalescing operator, optional chaining .