JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator
—
Automatic source map generation for tracking original source locations through transformations with character-level precision.
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 objectCompose 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 finalThe 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[];
}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
// }Recast generates character-by-character mappings for maximum debugging precision.
Characteristics:
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 fileConfigure 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 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 sourceUsing 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'
};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'));
});Install with Tessl CLI
npx tessl i tessl/npm-recast