Mastering AST: Transform JavaScript Code with Babel and Esprima

This article explains how to use AST in JavaScript by introducing three essential components—a parser, a traverser, and a code generator—showcasing practical examples with esprima and Babel to parse, modify, and regenerate code, and exploring advanced use cases such as removing console statements, variable obfuscation, arrow‑function conversion, tree‑shaking, and generating SVG flowcharts, while also highlighting AST applications in other languages.

WecTeam
WecTeam
WecTeam
Mastering AST: Transform JavaScript Code with Babel and Esprima

Three Key Points of AST Application

Need a parser to convert code to AST.

Need a traverser to walk and manipulate AST nodes.

Need a code generator to convert AST back to code.

esprima and babel

Common tools that satisfy the three points are esprima and babel.

esprima related packages and usage:

const esprima = require('esprima'); // code => ast
const estraverse = require('estraverse'); // ast traversal
const escodegen = require('escodegen'); // ast => code
let code = 'const a = 1';
const ast = esprima.parseScript(code);
// traversal example...
const transformCode = escodegen.generate(ast);

babel related packages and usage:

const parser = require('@babel/parser'); // code => ast
const traverse = require('@babel/traverse').default; // ast traversal, node CRUD, scope handling
const generate = require('@babel/generator').default; // ast => code
const t = require('@babel/types'); // utility library for AST nodes
let code = `function square(n) {
  console.log(n);
  console.warn(n);
  return n * n;
}`;
let ast = parser.parse(code);
// traversal example...

Babel is recommended over esprima due to better ecosystem and documentation.

Using Babel Tools to Operate AST

@babel/parser

– parse code to AST @babel/traverse – traverse AST, modify nodes, handle scopes @babel/generator – generate code from AST @babel/types – Lodash‑style utilities for AST node construction and validation

More APIs can be found in the Babel handbook.

Case 1: Remove console.log()

Implementation code:

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
let sourceCode = `function square(n) {
  console.log(n);
  console.warn(n);
  return n * n;
}`;
let ast = parser.parse(sourceCode);
traverse(ast, {
  CallExpression(path) {
    const { callee } = path.node;
    if (callee.type === 'MemberExpression' &&
        callee.object.name === 'console' &&
        callee.property.name === 'log') {
      path.remove(); // beware of global.console.log()
    }
  }
});
console.log(generate(ast).code);

Result:

function square(n) {
  // console.log removed
  console.warn(n);
  return n * n;
}

Key points: how to traverse specific nodes, identify CallExpression, and use path.remove().

Case 2: Variable obfuscation

Implementation code:

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');
let sourceCode = `function square(number) {
  console.warn(number);
  return number * number;
}`;
let ast = parser.parse(sourceCode);
traverse(ast, {
  FunctionDeclaration(path) {
    const uid = path.scope.generateUidIdentifier('a');
    path.scope.rename('number', uid.name);
  }
});
console.log(generate(ast).code);

Result:

function square(_a) {
  console.warn(_a);
  return _a * _a;
}

Key points: path.scope provides scope information, you can generate unique identifiers and rename variables.

Case 3: Convert arrow functions and drop unused parameters

Implementation code:

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');
let sourceCode = `new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(1); },200) })`;
let ast = parser.parse(sourceCode);
traverse(ast, {
  ArrowFunctionExpression(path) {
    const { id, params, body } = path.node;
    const usedParams = params.filter(p => path.scope.bindings[p.name].referenced);
    path.replaceWith(t.functionExpression(id, usedParams, body));
  }
});
console.log(generate(ast).code);

Result shows the arrow function turned into a regular function with only the used parameters.

Case 4: Tree‑shaking in a JD mini‑program

Implementation code removes unused exported functions:

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');
let sourceCode = `export function square(x){ return x*x; }
export function cube(x){ return x*x*x; }`;
let ast = parser.parse(sourceCode);
traverse(ast, {
  ExportNamedDeclaration(path) {
    const decl = path.node.declaration;
    if (decl.type === 'FunctionDeclaration') {
      const name = decl.id.name;
      const binding = path.scope.bindings[name];
      if (binding.references === 1) { // only the export itself
        path.remove();
      }
    }
  }
});
console.log(generate(ast).code);

Result keeps only the used export.

Case 5: Convert code to SVG flowchart

Uses the open‑source project js-code-to-svg-flowchart to transform JavaScript code into an SVG diagram. A live demo is available.

AST in other languages

Beyond JavaScript, AST is used for HTML, CSS, SQL, and other languages. Parsers for these languages are listed on the AST Explorer site.

AST language support
AST language support

Conclusion

AST is everywhere: Babel, Webpack, ESLint, Taro, etc. Understanding and leveraging AST can help you build powerful tools and improve your projects.

References [1] Babel handbook: https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md [2] js-code-to-svg-flowchart: https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart [3] Demo: https://bogdan-lyashenko.github.io/js-code-to-svg-flowchart/docs/live-editor/index.html

JavaScriptASTBabelcode transformationAST TraversalEsprima
WecTeam
Written by

WecTeam

WecTeam (维C团) is the front‑end technology team of JD.com’s Jingxi business unit, focusing on front‑end engineering, web performance optimization, mini‑program and app development, serverless, multi‑platform reuse, and visual building.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.