ESTree-compatible AST printer designed for JavaScript/TypeScript language tooling.
- Source preservation — untouched nodes (configurable) are printed verbatim from the original source, preserving whitespace, comments, formatting, and ASI safety.
- Volar.js mappings — generates
Mapping<Data>[]mapping source ranges to generated ranges, with automatic merging of adjacent mappings. - Full TypeScript support — handles all
@typescript-eslint/typesAST node types (TSESTree), compatible with acorn-typescript parsed ASTs. - Extensible — every aspect is customizable: untouched detection, mapping data, comments injection, printer overrides per node type.
- Zero runtime dependencies — keeps the library minimal, only 10 kB gzipped.
pnpm add espolarRequires Node.js >= 26.
import { print } from "espolar";
import type { PrintOptions } from "espolar";
const ast = /* parse to AST.Program with loc/range */;
const result = print(ast, {
source: originalCode,
});
console.log(result.code); // generated source string
console.log(result.mappings); // Volar.js Mapping<{}>[]import { print } from "espolar";
const result = print(ast, {
source,
printers: {
Literal(node, ctx) {
ctx.write(`"overridden"`);
},
},
});import { print } from "espolar";
const result = print(ast, {
source,
getMappingData: (node) => (node ? [node.type] : []),
combineMappingData: (left, right) => {
return [...left, ...right];
},
});Main entry point. Returns PrintResult<Data>.
| Option | Type | Description |
|---|---|---|
source |
string |
The original source code (required) |
isUntouched |
(node) => boolean | SourceRange |
Determine if a node should be preserved from source. Default: checks range/start/end |
getMappingData |
(node?) => Data |
Extract data for each mapping entry. Default: () => undefined |
combineMappingData |
(left, right) => Data |
Merge data when adjacent mappings are combined. Default: throws if left !== right |
printers |
Printers<Data> |
Override printers for specific AST_NODE_TYPES |
getLeadingComments |
(node) => Comment[] | undefined |
Return comments to print before a touched node |
getTrailingComments |
(node) => Comment[] | undefined |
Return comments to print after a touched node |
| Field | Type | Description |
|---|---|---|
code |
string |
The generated source code |
mappings |
Mapping<Data>[] |
Volar.js source mappings (range-to-range) |
The context object passed to each printer function.
| Method/Property | Description |
|---|---|
readonly source: string |
The original source string |
readonly generatedOffset: number |
Current output position |
write(text: string) |
Emit text to the output |
writeMapped(text: string, srcStart, srcEnd, data?) |
Emit text to the output with mappings |
writeNode(node) |
Dispatch to a printer (preserving if untouched) |
writeNodeList(nodes, sep) |
Print a list with explicit separator |
writeNodeListWithSourceGaps(nodes, fallbackSep) |
Print a list, copying source gaps between nodes |
writeSource(start, end, data?) |
Copy source range and add mapping |
writePreservedNode(node) |
Force-preserve a node from source |
appendMapping(srcRange, genStart, genEnd, data) |
Append a mapping manually |
| Export | Description |
|---|---|
defaultIsUntouched |
Default untouched detection function |
defaultGetMappingData |
Default mapping data extractor |
defaultCombineMappingData |
Default mapping data combiner |
The printer works in a single pass:
- If a node is untouched (has source range and
isUntouchedreturns truthy), its original source text is copied verbatim with a 1:1 mapping. - Otherwise, the node is dispatched to its registered printer function, which recursively calls
writeNodeon children. - Adjacent untouched siblings have their mappings merged into a single combined range.
This design preserves original formatting for unmodified code while only reconstructing the parts that changed.
pnpm install # Install dependencies
pnpm test # Run tests (vitest)
pnpm coverage # Run tests with V8 coverage
pnpm typecheck # TypeScript type checking
pnpm build # Build via tsdownTests use acorn + @sveltejs/acorn-typescript to parse TypeScript source into AST inputs.
MIT