рдПрдХ рдмрд╛рдмреЗрд▓ рдкреНрд▓рдЧрдЗрди, рд╡реЙрдХрдереНрд░реВ рд▓рд┐рдЦрдирд╛

рдЫрд╡рд┐


рдЖрдЬ рдореИрдВ рдЖрдкрдХреЗ рд╕рд╛рде рдПрдХ рдХрджрдо-рджрд░-рдЪрд░рдг рдорд╛рд░реНрдЧрджрд░реНрд╢рд┐рдХрд╛ рд╕рд╛рдЭрд╛ рдХрд░реВрдБрдЧрд╛ рдХрд┐ рдЖрдк рдЕрдкрдиреЗ рдмреЗрдмреЗрд▓ рдкреНрд▓рдЧрдЗрди рдХреЛ рдХреИрд╕реЗ рд▓рд┐рдЦреЗрдВ ред рдЖрдк рдЗрд╕ рдЬреНрдЮрд╛рди рдХрд╛ рдЙрдкрдпреЛрдЧ рд╕рдВрдкрд╛рджрди, рд░реАрдлрд╝реИрдХреНрдЯрд░рд┐рдВрдЧ рдпрд╛ рдХреЛрдб рдЬреЗрдирд░реЗрд╢рди рдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


рдХреЛрд▓рд╛рд╣рд▓ рдХреНрдпрд╛ рд╣реИ?


рдмреИрдмреЗрд▓ рдПрдХ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрдВрдкрд╛рдЗрд▓рд░ рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдореБрдЦреНрдпрддрдГ ECMAScript 2015+ рдХреЛрдб рдХреЛ рд╡рд░реНрддрдорд╛рди рдФрд░ рдкреБрд░рд╛рдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдпрд╛ рд╡рд╛рддрд╛рд╡рд░рдг рдХреЗ рд╕рд╛рде рдкреАрдЫреЗ рдХреА рдУрд░ рдХрдиреНрд╡рд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдХреЛрдмреЗрд▓ рдХреЛрдб рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреНрд▓рдЧрдЗрди рд╕рд┐рд╕реНрдЯрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдХреЛрдИ рднреА рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рдкреНрд▓рдЧрдЗрди рд▓рд┐рдЦ рд╕рдХрддрд╛ рд╣реИред


, 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';

//    ast
const ast = parse(code);

//  ast
traverse(ast, {
  enter(path) {
    //        `n`  `x`
    if (path.isIdentifier({ name: 'n' })) {
      path.node.name = 'x';
    }
  },
});

//     ast
const output = generate(ast, code);
console.log(output.code); // 'const x = 1;'

@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: [
    //   babel 
    function myCustomPlugin() {
      return {
        visitor: {
          Identifier(path) {
            //        `n`  `x`
            if (path.isIdentifier({ name: 'n' })) {
              path.node.name = 'x';
            }
          },
        },
      };
    },
  ],
});

console.log(output.code); // 'const x = 1;'

babel n x, ?


myCustomPlugin . npm babel !


: ", babel , ...". , .


1.


:



:


function greet(name) {
  return 'Hello ' + name;
}

console.log(greet('tanhauhau')); // Hello tanhauhau

:


function teerg(eman) {
      return 'H' + 'e' + 'l' + 'l' + 'o' + ' ' + eman;
    }

console.log(teerg('t' + 'a' + 'n' + 'h' + 'a' + 'u' + 'h' + 'a' + 'u')); // Hello tanhauhau

console.log, , . ( !)


2. AST


babel AST explorer, AST:


https://lihautan.com/static/0cf3ccd0d3c0a243103feadfdf5c1dd9/bf7ce/targeting.png


AST, , .


, :


  • Identifier 
  • StringLiteral  .

3. AST


babel AST explorer, :


https://lihautan.com/static/fa937b7e3d8c6f72f076f4e86d9b60e1/30a34/output.png


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(' ');
      },
    },
  };
}

, babel :




 




 

, 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')); // Hello tanhauhau

console.log . ?


AST:


https://lihautan.com/static/487252cc6b3641879b1a606c5f7e6263/ae92f/member-expression.png


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')); // Hello 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);

, :


  1. AST
  2. AST


, babel GitHub babel.


https://github.com/babel/babel, babel-plugin-transform-* babel-plugin-proposal-*, -, babel nullish coalescing operator, optional chaining .






All Articles