JavaScript Formatter Implementation: From String Parsing to AST Beautification#

Author: JsonKit Team Date: 2026-05-10 15:31 Tool: JavaScript Formatter

Introduction#

In collaborative development, consistent code style is a persistent challenge. Have you ever received code from a colleague with messy indentation and random line breaks that reads like gibberish? A reliable JavaScript formatter becomes a lifesaver in such situations.

Today, let’s dive deep into the implementation principles of JavaScript formatting, from simple string processing to professional AST parsing, and see how to build a practical code beautification tool.

Two Technical Approaches Compared#

1. String Traversal Method (Lightweight Implementation)#

The string traversal approach is the most intuitive implementation. The core idea is to scan code character by character and apply line breaks and indentation based on specific symbols.

function beautifyJs(js, spaces = 2) {
  let formatted = ''
  let indentLevel = 0
  const indentStr = ' '.repeat(spaces)
  let inString = false
  let stringChar = ''

  for (let i = 0; i < js.length; i++) {
    const char = js[i]

    // Handle string boundaries (avoid misjudging symbols inside strings)
    if ((char === '"' || char === "'" || char === '`') && !inString) {
      inString = true
      stringChar = char
      formatted += char
      continue
    } else if (inString && char === stringChar && js[i - 1] !== '\\') {
      inString = false
      formatted += char
      continue
    }

    if (inString) {
      formatted += char
      continue
    }

    // Handle indentation-critical symbols
    if (char === '{') {
      formatted += ' {\n' + indentStr.repeat(indentLevel + 1)
      indentLevel++
    } else if (char === '}') {
      indentLevel = Math.max(0, indentLevel - 1)
      formatted += '\n' + indentStr.repeat(indentLevel) + '}\n'
    } else if (char === ';') {
      formatted += ';\n' + indentStr.repeat(indentLevel)
    }
  }

  return formatted.trim()
}

Pros: Small footprint, fast execution, no dependencies, ideal for real-time browser preview.

Cons: Cannot handle complex syntax (arrow functions, nested template strings), regex misjudgment, difficult comment handling.

2. AST Parsing Method (Professional Implementation)#

AST (Abstract Syntax Tree) parsing uses lexical and syntax analysis to parse code into a tree structure, then re-outputs according to unified rules.

import { parse } from '@babel/parser'
import generate from '@babel/generator'

function formatWithAST(code, options = {}) {
  const ast = parse(code, {
    sourceType: 'module',
    plugins: ['jsx', 'typescript']
  })

  const output = generate(ast, {
    indent: { style: '  ' },
    compact: false,
    retainLines: false
  })

  return output.code
}

Pros: Guaranteed syntax correctness, supports ES6+/TypeScript/JSX, rich configurable rules.

Cons: Large library size (Babel ecosystem 1MB+), parsing overhead.

Practical Pitfalls and Solutions#

Pitfall 1: Misjudging Symbols Inside Strings#

const obj = {
  message: "This is a string with { curly braces } inside"
}

Without string boundary detection, curly braces would be mistakenly identified as code block starts.

Solution: Maintain an inString state machine, track string delimiter characters (', ", `), and handle escape characters.

Pitfall 2: Comment Handling#

// This is a single-line comment, should not be formatted
/* This is a multi-line comment
   preserve original line breaks */

Content inside comments should be preserved as-is, not re-indented according to code rules.

Solution: Single-line comments detect // to \n, multi-line comments detect /* to */, skip formatting logic in these ranges.

Pitfall 3: Regex During Minification#

// Simple regex replacement during minification can cause issues
code.replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments

If code contains strings with /*, the regex would mistakenly delete string content.

Solution: Use AST parser’s comments property to get actual comment positions, or use state machine character-by-character analysis.

Performance Comparison: Minification vs Beautification#

Operation 1KB Code 100KB Code 1MB Code
String Beautify < 1ms 15ms 180ms
AST Beautify 5ms 120ms 1.2s
String Minify < 1ms 8ms 95ms
AST Minify 3ms 80ms 800ms

For browser-side real-time preview scenarios, the string method handles most needs adequately. For production deployment, mature tools like Prettier are recommended.

Real-World Use Cases#

  1. Code Review: Standardize team code style, reduce formatting debates during reviews
  2. Reverse Engineering: Beautify minified JavaScript for easier analysis of third-party libraries
  3. Teaching Demos: Expand single-line code to multiple lines for explaining execution flow
  4. Bundle Optimization: Minification reduces 30-50% file size, speeds up network transfer

Best Practice Recommendations#

  • Use beautification in development, minification in production
  • Team projects should standardize on Prettier + ESLint configuration
  • Browser-side tools should prioritize lightweight string methods
  • Complex projects (TS/JSX) must use AST parsing

This article covers the core implementation principles of JavaScript formatters. For more developer tools, visit JsonKit.