Convert Figma logo to code with AI

benjamn logorecast

JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator

5,016
350
5,016
204

Top Related Projects

A JavaScript codemod toolkit.

43,312

🐠 Babel is a compiler for writing next generation JavaScript.

49,268

Prettier is an opinionated code formatter.

10,671

A small, fast, JavaScript-based JavaScript parser

ECMAScript code generator

8,757

🗜 JavaScript parser, mangler and compressor toolkit for ES6+

Quick Overview

Recast is a powerful JavaScript syntax tree transformer, parser, and printer. It allows you to parse JavaScript code into an abstract syntax tree (AST), manipulate the AST, and then regenerate the code, preserving the original formatting and comments as much as possible.

Pros

  • Preserves code formatting and comments during transformation
  • Provides a flexible and powerful API for AST manipulation
  • Supports modern JavaScript syntax, including ES6+ features
  • Integrates well with other tools in the JavaScript ecosystem

Cons

  • Learning curve for working with ASTs can be steep for beginners
  • Performance may be slower compared to simpler string-based transformations
  • Documentation could be more comprehensive for advanced use cases
  • Limited support for TypeScript and other JavaScript supersets

Code Examples

  1. Parsing JavaScript code into an AST:
const recast = require('recast');

const code = 'function add(a, b) { return a + b; }';
const ast = recast.parse(code);
console.log(ast);
  1. Modifying an AST and regenerating code:
const recast = require('recast');
const { visit } = recast.types;

const code = 'const x = 5; const y = 10;';
const ast = recast.parse(code);

visit(ast, {
  visitVariableDeclaration(path) {
    if (path.node.kind === 'const') {
      path.node.kind = 'let';
    }
    this.traverse(path);
  }
});

const output = recast.print(ast).code;
console.log(output); // Output: let x = 5; let y = 10;
  1. Adding a new import statement to existing code:
const recast = require('recast');
const { builders: b } = recast.types;

const code = 'const x = 5;';
const ast = recast.parse(code);

const importDeclaration = b.importDeclaration(
  [b.importSpecifier(b.identifier('useState'))],
  b.literal('react')
);

ast.program.body.unshift(importDeclaration);

const output = recast.print(ast).code;
console.log(output);
// Output:
// import { useState } from 'react';
// const x = 5;

Getting Started

To start using Recast in your project, follow these steps:

  1. Install Recast using npm:

    npm install recast
    
  2. Import Recast in your JavaScript file:

    const recast = require('recast');
    
  3. Parse your code into an AST:

    const code = 'your JavaScript code here';
    const ast = recast.parse(code);
    
  4. Manipulate the AST using Recast's API and the visit function.

  5. Generate the modified code:

    const output = recast.print(ast).code;
    console.log(output);
    

Competitor Comparisons

A JavaScript codemod toolkit.

Pros of jscodeshift

  • More powerful and flexible API for large-scale codebase transformations
  • Built-in support for running codemods in parallel, improving performance
  • Extensive documentation and examples for common use cases

Cons of jscodeshift

  • Steeper learning curve due to its more complex API
  • Requires more setup and configuration for simple transformations
  • Less focus on preserving original code formatting

Code Comparison

recast:

const recast = require('recast');

const ast = recast.parse(code);
recast.visit(ast, {
  visitIdentifier(path) {
    // Transform identifiers
    this.traverse(path);
  }
});

jscodeshift:

module.exports = function(fileInfo, api) {
  const j = api.jscodeshift;
  const root = j(fileInfo.source);

  return root
    .find(j.Identifier)
    .forEach(path => {
      // Transform identifiers
    })
    .toSource();
};

Summary

recast is simpler and focuses on preserving code style, making it ideal for smaller transformations. jscodeshift offers more power and scalability for large-scale refactoring but requires more setup. Choose based on your project's complexity and transformation needs.

43,312

🐠 Babel is a compiler for writing next generation JavaScript.

Pros of Babel

  • Broader scope: Babel is a full-featured JavaScript compiler with extensive plugin ecosystem
  • Active development: Regularly updated with new features and improvements
  • Widely adopted: Used in many large-scale projects and has strong community support

Cons of Babel

  • Heavier and more complex: Larger codebase and more dependencies
  • Steeper learning curve: Requires more configuration and setup for basic use cases
  • Potentially slower for simple transformations due to its comprehensive nature

Code Comparison

Recast:

const recast = require('recast');

const ast = recast.parse(code);
ast.program.body[0].expression.right = newNode;
const output = recast.print(ast).code;

Babel:

const babel = require('@babel/core');

const result = babel.transform(code, {
  plugins: ['@babel/plugin-transform-arrow-functions']
});
const output = result.code;

Summary

Recast focuses on AST manipulation and code generation with minimal changes, while Babel is a comprehensive JavaScript compiler. Recast is lighter and simpler for specific AST transformations, whereas Babel offers a wider range of features and transformations but with added complexity. Choose based on your project's specific needs and scale.

49,268

Prettier is an opinionated code formatter.

Pros of Prettier

  • Opinionated formatting with minimal configuration options, leading to consistent code style across projects
  • Supports a wide range of languages and file types beyond JavaScript
  • Integrates seamlessly with popular editors and IDEs

Cons of Prettier

  • Less flexibility in customizing code output format
  • May not preserve all original code structure and comments

Code Comparison

Recast:

function example(a, b) {
    return a + b;
}

Prettier:

function example(a, b) {
  return a + b;
}

Key Differences

  • Recast focuses on preserving the structure of existing code while making targeted modifications, while Prettier enforces a consistent style across the entire codebase
  • Recast is primarily designed for AST manipulation and code transformation, whereas Prettier is primarily a code formatter
  • Recast offers more fine-grained control over code output, while Prettier aims for simplicity and consistency

Use Cases

  • Use Recast for precise code transformations, refactoring, and maintaining existing code structure
  • Use Prettier for enforcing a consistent code style across projects and teams, especially in multi-language environments

Community and Ecosystem

  • Prettier has a larger community and more widespread adoption
  • Recast is often used as a foundation for other tools and libraries focused on code transformation
10,671

A small, fast, JavaScript-based JavaScript parser

Pros of Acorn

  • Faster parsing speed, especially for large codebases
  • Smaller footprint and fewer dependencies
  • More widely adopted in the JavaScript ecosystem

Cons of Acorn

  • Less focus on code generation and manipulation
  • Fewer built-in utilities for AST traversal and modification
  • Limited support for preserving original code formatting

Code Comparison

Acorn parsing example:

import * as acorn from "acorn";

const ast = acorn.parse("const x = 42;", {
  ecmaVersion: 2020
});

Recast parsing example:

import * as recast from "recast";

const ast = recast.parse("const x = 42;", {
  parser: require("recast/parsers/babel")
});

Both Acorn and Recast are popular JavaScript parsing libraries, but they serve different purposes. Acorn excels in fast and lightweight parsing, making it ideal for static analysis tools and linters. Recast, on the other hand, focuses on code transformation and generation, preserving original formatting and comments.

While Acorn provides a simpler API for parsing, Recast offers more comprehensive tools for manipulating and regenerating code. The choice between the two depends on the specific requirements of your project, such as parsing speed, code manipulation needs, and preservation of original formatting.

ECMAScript code generator

Pros of escodegen

  • Lightweight and focused solely on code generation
  • Supports a wider range of ECMAScript versions (up to ES2020)
  • Faster performance for code generation tasks

Cons of escodegen

  • Limited to code generation; doesn't provide parsing capabilities
  • Less flexibility in preserving original code formatting and comments
  • Fewer options for customizing output formatting

Code Comparison

escodegen:

escodegen.generate(ast, {
  format: {
    indent: {
      style: '  ',
      base: 0
    }
  }
});

recast:

recast.print(ast, {
  tabWidth: 2,
  useTabs: false,
  reuseWhitespace: true
}).code;

Key Differences

  • recast offers both parsing and printing capabilities, while escodegen focuses solely on code generation
  • recast aims to preserve original formatting and comments, whereas escodegen generates code from scratch
  • escodegen provides more granular control over output formatting, while recast emphasizes maintaining the original code structure

Use Cases

  • Choose escodegen for lightweight, fast code generation tasks
  • Opt for recast when working with existing code and preserving its structure is important
  • Use recast for projects requiring both parsing and printing capabilities
8,757

🗜 JavaScript parser, mangler and compressor toolkit for ES6+

Pros of Terser

  • Focuses on minification and optimization, resulting in smaller output files
  • Faster processing speed, especially for large codebases
  • More actively maintained with frequent updates and bug fixes

Cons of Terser

  • Limited parsing and transformation capabilities compared to Recast
  • Less flexibility for custom AST manipulations
  • Not designed for code formatting or pretty-printing

Code Comparison

Terser (minification):

function longFunctionName(parameter1, parameter2) {
    return parameter1 + parameter2;
}

Minified output:

function longFunctionName(a,b){return a+b}

Recast (AST manipulation):

import { parse, print } from "recast";

const ast = parse("function add(a, b) { return a + b; }");
ast.program.body[0].id.name = "sum";
console.log(print(ast).code);

Output:

function sum(a, b) { return a + b; }

Terser excels at minification and optimization, while Recast provides powerful tools for AST manipulation and code transformation. Choose Terser for reducing file size and improving performance, and Recast for more complex code modifications and analysis tasks.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

recast, v. CI Join the chat at https://gitter.im/benjamn/recast

  1. to give (a metal object) a different form by melting it down and reshaping it.
  2. to form, fashion, or arrange again.
  3. to remodel or reconstruct (a literary work, document, sentence, etc.).
  4. to supply (a theater or opera work) with a new cast.

Installation

From npm:

npm install recast

From GitHub:

cd path/to/node_modules
git clone git://github.com/benjamn/recast.git
cd recast
npm install .

Import style

Recast is designed to be imported using named imports:

import { parse, print } from "recast";
console.log(print(parse(source)).code);

import * as recast from "recast";
console.log(recast.print(recast.parse(source)).code);

If you're using CommonJS:

const { parse, print } = require("recast");
console.log(print(parse(source)).code);

const recast = require("recast");
console.log(recast.print(recast.parse(source)).code);

Usage

Recast exposes two essential interfaces, one for parsing JavaScript code (require("recast").parse) and the other for reprinting modified syntax trees (require("recast").print).

Here's a simple but non-trivial example of how you might use .parse and .print:

import * as recast from "recast";

// Let's turn this function declaration into a variable declaration.
const code = [
  "function add(a, b) {",
  "  return a +",
  "    // Weird formatting, huh?",
  "    b;",
  "}"
].join("\n");

// Parse the code using an interface similar to require("esprima").parse.
const ast = recast.parse(code);

Now do whatever you want to ast. Really, anything at all!

See ast-types (especially the def/core.ts) module for a thorough overview of the ast API.

// Grab a reference to the function declaration we just parsed.
const add = ast.program.body[0];

// Make sure it's a FunctionDeclaration (optional).
const n = recast.types.namedTypes;
n.FunctionDeclaration.assert(add);

// If you choose to use recast.builders to construct new AST nodes, all builder
// arguments will be dynamically type-checked against the Mozilla Parser API.
const b = recast.types.builders;

// This kind of manipulation should seem familiar if you've used Esprima or the
// Mozilla Parser API before.
ast.program.body[0] = b.variableDeclaration("var", [
  b.variableDeclarator(add.id, b.functionExpression(
    null, // Anonymize the function expression.
    add.params,
    add.body
  ))
]);

// Just for fun, because addition is commutative:
add.params.push(add.params.shift());

When you finish manipulating the AST, let recast.print work its magic:

const output = recast.print(ast).code;

The output string now looks exactly like this, weird formatting and all:

var add = function(b, a) {
  return a +
    // Weird formatting, huh?
    b;
}

The magic of Recast is that it reprints only those parts of the syntax tree that you modify. In other words, the following identity is guaranteed:

recast.print(recast.parse(source)).code === source

Whenever Recast cannot reprint a modified node using the original source code, it falls back to using a generic pretty printer. So the worst that can happen is that your changes trigger some harmless reformatting of your code.

If you really don't care about preserving the original formatting, you can access the pretty printer directly:

var output = recast.prettyPrint(ast, { tabWidth: 2 }).code;

And here's the exact output:

var add = function(b, a) {
  return a + b;
}

Note that the weird formatting was discarded, yet the behavior and abstract structure of the code remain the same.

Using a different parser

By default, Recast uses the Esprima JavaScript parser when you call recast.parse(code). While Esprima supports almost all modern ECMAScript syntax, you may want to use a different parser to enable TypeScript or Flow syntax, or just because you want to match other compilation tools you might be using.

In order to get any benefits from Recast's conservative pretty-printing, it is very important that you continue to call recast.parse (rather than parsing the AST yourself using a different parser), and simply instruct recast.parse to use a different parser:

const acornAst = recast.parse(source, {
  parser: require("acorn")
});

Why is this so important? When you call recast.parse, it makes a shadow copy of the AST before returning it to you, giving every copied AST node a reference back to the original through a special .original property. This information is what enables recast.print to detect where the AST has been modified, so that it can preserve formatting for parts of the AST that were not modified.

Any parser object that supports a parser.parse(source) method will work here; however, if your parser requires additional options, you can always implement your own parse method that invokes your parser with custom options:

const acornAst = recast.parse(source, {
  parser: {
    parse(source) {
      return require("acorn").parse(source, {
        // additional options
      });
    }
  }
});

To take some of the guesswork out of configuring common parsers, Recast provides several preconfigured parsers, so you can parse TypeScript (for example) without worrying about the configuration details:

const tsAst = recast.parse(source, {
  parser: require("recast/parsers/typescript")
});

Note: Some of these parsers import npm packages that Recast does not directly depend upon, so please be aware you may have to run npm install @babel/parser to use the typescript, flow, or babel parsers, or npm install acorn to use the acorn parser. Only Esprima is installed by default when Recast is installed.

After calling recast.parse, if you're going to transform the AST, make sure that the .original property is preserved. With Babel, for instance, if you call transformFromAST, you must pass cloneInputAst: false in its options. (More detail).

Source maps

One of the coolest consequences of tracking and reusing original source code during reprinting is that it's pretty easy to generate a high-resolution mapping between the original code and the generated code—completely automatically!

With every slice, join, and re-indent-ation, the reprinting process maintains exact knowledge of which character sequences are original, and where in the original source they came from.

All you have to think about is how to manipulate the syntax tree, and Recast will give you a source map in exchange for specifying the names of your source file(s) and the desired name of the map:

var result = recast.print(transform(recast.parse(source, {
  sourceFileName: "source.js"
})), {
  sourceMapName: "source.min.js"
});

console.log(result.code); // Resulting string of code.
console.log(result.map); // JSON source map.

var SourceMapConsumer = require("source-map").SourceMapConsumer;
var smc = new SourceMapConsumer(result.map);
console.log(smc.originalPositionFor({
  line: 3,
  column: 15
})); // { source: 'source.js',
     //   line: 2,
     //   column: 10,
     //   name: null }

Note that you are free to mix and match syntax trees parsed from different source files, and the resulting source map will automatically keep track of the separate file origins for you.

Note also that the source maps generated by Recast are character-by-character maps, so meaningful identifier names are not recorded at this time. This approach leads to higher-resolution debugging in modern browsers, at the expense of somewhat larger map sizes. Striking the perfect balance here is an area for future exploration, but such improvements will not require any breaking changes to the interface demonstrated above.

Options

All Recast API functions take second parameter with configuration options, documented in options.ts

Motivation

The more code you have, the harder it becomes to make big, sweeping changes quickly and confidently. Even if you trust yourself not to make too many mistakes, and no matter how proficient you are with your text editor, changing tens of thousands of lines of code takes precious, non-refundable time.

Is there a better way? Not always! When a task requires you to alter the semantics of many different pieces of code in subtly different ways, your brain inevitably becomes the bottleneck, and there is little hope of completely automating the process. Your best bet is to plan carefully, buckle down, and get it right the first time. Love it or loathe it, that's the way programming goes sometimes.

What I hope to eliminate are the brain-wasting tasks, the tasks that are bottlenecked by keystrokes, the tasks that can be expressed as operations on the syntactic structure of your code. Specifically, my goal is to make it possible for you to run your code through a parser, manipulate the abstract syntax tree directly, subject only to the constraints of your imagination, and then automatically translate those modifications back into source code, without upsetting the formatting of unmodified code.

And here's the best part: when you're done running a Recast script, if you're not completely satisfied with the results, blow them away with git reset --hard, tweak the script, and just run it again. Change your mind as many times as you like. Instead of typing yourself into a nasty case of RSI, gaze upon your new wells of free time and ask yourself: what next?

NPM DownloadsLast 30 Days