CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-recast

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

Pending
Overview
Eval results
Files

source-maps.mddocs/

Source Maps

Automatic source map generation for tracking original source locations through transformations with character-level precision.

Capabilities

Automatic Source Map Generation

Recast automatically generates high-resolution source maps when reprinting modified ASTs, tracking which parts of the output correspond to which parts of the original source.

/**
 * Enable source map generation by providing source file information
 */
interface SourceMapOptions {
  /** Original source file name */
  sourceFileName?: string | null;
  /** Output source map name */
  sourceMapName?: string | null;
  /** Root directory for relative source paths */
  sourceRoot?: string | null;
  /** Input source map for composition */
  inputSourceMap?: string | null;
}

Usage Examples:

import { parse, print } from "recast";

// Parse with source file information
const ast = parse(sourceCode, {
  sourceFileName: "input.js"
});

// Transform AST
// ... modify ast ...

// Generate source map during printing
const result = print(ast, {
  sourceMapName: "output.js"
});

console.log(result.code); // Generated code
console.log(result.map);  // Source map object

Source Map Composition

Compose multiple source maps when chaining transformations.

Usage Example:

import { parse, print } from "recast";

// First transformation
const ast1 = parse(originalCode, {
  sourceFileName: "original.js"
});
// ... transform ast1 ...
const result1 = print(ast1, {
  sourceMapName: "intermediate.js"
});

// Second transformation using previous source map
const ast2 = parse(result1.code, {
  sourceFileName: "intermediate.js"
});
// ... transform ast2 ...
const result2 = print(ast2, {
  sourceMapName: "final.js",
  inputSourceMap: JSON.stringify(result1.map)
});

// result2.map now contains composed source map from original to final

Print Result with Source Maps

The result object returned by print operations when source maps are enabled.

interface PrintResultType {
  /** The generated source code */
  code: string;
  /** Source map object (when sourceMapName provided) */
  map?: SourceMap;
  /** Deprecated string conversion */
  toString(): string;
}

interface SourceMap {
  /** Source map version (always 3) */
  version: number;
  /** Generated file name */
  file: string;
  /** Root directory for source paths */
  sourceRoot?: string;
  /** Array of source file names */
  sources: string[];
  /** Array of source content (optional) */
  sourcesContent?: Array<string | null>;
  /** Encoded mapping data */
  mappings: string;
  /** Array of symbol names */
  names: string[];
}

Source Map Consumer Usage

Using generated source maps with the Mozilla source-map library.

Usage Example:

import { parse, print } from "recast";
import { SourceMapConsumer } from "source-map";

const ast = parse(sourceCode, {
  sourceFileName: "input.js"
});

// Transform code
// ... modify ast ...

const result = print(ast, {
  sourceMapName: "output.js"
});

// Use source map to find original positions
const consumer = new SourceMapConsumer(result.map);

// Find original position for generated position
const originalPosition = consumer.originalPositionFor({
  line: 5,
  column: 10
});

console.log(originalPosition);
// {
//   source: 'input.js',
//   line: 3,
//   column: 15,
//   name: null
// }

High-Resolution Mapping

Recast generates character-by-character mappings for maximum debugging precision.

Characteristics:

  • Character-level precision: Maps individual characters rather than just lines
  • Preserve unchanged code: Original source locations maintained for unmodified code
  • Automatic tracking: No manual mapping required - handled during reprinting
  • Multiple source support: Can track code from multiple source files in one output

Example mapping data:

// Original code spans multiple files
const file1Code = "function add(a, b) { return a + b; }";
const file2Code = "const multiply = (x, y) => x * y;";

// Parse and combine
const ast1 = parse(file1Code, { sourceFileName: "math1.js" });
const ast2 = parse(file2Code, { sourceFileName: "math2.js" });

// Combine ASTs
ast1.program.body.push(...ast2.program.body);

// Generate combined source map
const result = print(ast1, {
  sourceMapName: "combined.js"
});

// result.map.sources = ["math1.js", "math2.js"]
// Mappings track which output characters came from which source file

Source Map Configuration

File Path Configuration

Configure how source paths are handled in generated source maps.

interface SourceMapConfig {
  /** Name of the original source file */
  sourceFileName?: string | null;
  /** Name for the generated source map */
  sourceMapName?: string | null;
  /** Root directory for resolving relative source paths */
  sourceRoot?: string | null;
}

Usage Examples:

// Absolute paths
const result = print(ast, {
  sourceFileName: "/project/src/input.js",
  sourceMapName: "/project/dist/output.js",
  sourceRoot: "/project"
});

// Relative paths (recommended)
const result = print(ast, {
  sourceFileName: "src/input.js",
  sourceMapName: "dist/output.js",
  sourceRoot: ".."
});

Source Content Embedding

Source maps can optionally include the original source content.

// Source map will include sourcesContent array
// with original source code embedded
const result = print(ast, {
  sourceFileName: "input.js",
  sourceMapName: "output.js"
});

// result.map.sourcesContent[0] contains original source

Integration with Build Tools

Webpack Integration

Using recast-generated source maps with Webpack.

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'recast-loader', // hypothetical loader
          options: {
            sourceMap: true,
            sourceRoot: path.resolve(__dirname, 'src')
          }
        }
      }
    ]
  },
  devtool: 'source-map'
};

Gulp Integration

Composing recast transformations with Gulp source maps.

const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const through2 = require('through2');
const { parse, print } = require('recast');

function recastTransform() {
  return through2.obj(function(file, enc, callback) {
    const ast = parse(file.contents.toString(), {
      sourceFileName: file.relative
    });
    
    // Transform AST
    // ... modify ast ...
    
    const result = print(ast, {
      sourceMapName: file.relative
    });
    
    file.contents = Buffer.from(result.code);
    file.sourceMap = result.map;
    
    callback(null, file);
  });
}

gulp.task('transform', () => {
  return gulp.src('src/*.js')
    .pipe(sourcemaps.init())
    .pipe(recastTransform())
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('dist'));
});

Important Notes

Source Map Accuracy

  • Format preservation: Source maps accurately reflect preserved original formatting
  • Transformation tracking: Modified code sections get new mappings while preserved sections maintain original mappings
  • Multi-file support: Can track transformations across multiple input files
  • Character precision: Maps individual characters for precise debugging

Performance Considerations

  • Memory usage: Source maps add memory overhead proportional to source size
  • Generation time: Source map creation adds processing time during printing
  • File size: Generated source maps can be large for heavily transformed code

Debugging Benefits

  • Stack traces: Error stack traces point to original source locations
  • Breakpoints: Debugger breakpoints work on original source
  • Variable names: Original variable names preserved in debugging info
  • Code navigation: IDE navigation works with original source structure

Install with Tessl CLI

npx tessl i tessl/npm-recast

docs

ast-manipulation.md

cli-interface.md

core-operations.md

index.md

parser-configuration.md

source-maps.md

tile.json