Skip to main content

escodegen-python

Generate JavaScript from canonical ESTree ASTs or native esprima-fork nodes. This Python port of escodegen targets ECMAScript 2026 ASTs and requires Python 3.11+. Runtime generation is pure Python with esutils; Node and Acorn are development test oracles only.

python -m pip install "escodegen>=2,<3"
python -m pip install "escodegen[integration]>=2,<3"  # optional parser/walker

The 2.x release requires Python 3.11+ and esutils 2.x. Its integration extra adds esprima-fork 5.x (imported as esprima) and acornwalk 2.x. For source development, use sibling checkouts as described in the contributor instructions. See the migration notes for changes from 1.x.

import escodegen
ast = {'type': 'BinaryExpression', 'operator': '+',
       'left': {'type': 'Literal', 'value': 20},
       'right': {'type': 'Literal', 'value': 2}}
assert escodegen.generate(ast) == '20 + 2'

import esprima  # distribution: esprima-fork
tree = esprima.parseScript('const x = 1;')
print(escodegen.generate(tree))
print(escodegen.generate(tree.toDict(), {'format': escodegen.FORMAT_MINIFY}))

AST and language contract

Canonical async/await and native isAsync/allowAwait fields are accepted; conflicts raise ValueError. Legacy FieldDefinition adapts to PropertyDefinition. Nulls are retained. An attribute adapter copies inputs without JSON conversion or mutating ASTs/options. Formatting state belongs to each operation, including nested callbacks and concurrent generation.

Emitters cover ES5–ES2017 syntax, object rest/spread, async generators and iteration, optional catch bindings/chains, nullish/logical assignments, BigInt, public/private fields/methods, private brand checks, static blocks, namespace exports, quoted module names, import/export attributes, dynamic import options, top-level await and current regex descriptors. JSX has element, fragment, namespace, attribute and spread-child emitters. A parser must first validate grammar/early errors in its script/module/extension context. TypeScript, Flow, decorators and unknown AST nodes are unsupported. Legacy Mozilla comprehensions/guards are compatibility extensions rather than part of the published ECMAScript target. Resource declarations (using/await using) require extensions.resourceManagement=True; they are outside the published target.

Number literals apply JavaScript binary64 rounding to Python/JSON integers. Integers beyond the finite domain and NaN raise ValueError; infinity emits 1e400 (JSON null). Negative values/zero are preserved with parentheses where required. Exact integers use {'type':'Literal','value':None,'bigint':'9007199254740993'} with canonical nonnegative decimal digits; negative BigInts use UnaryExpression. Separators and raw numeric spellings may normalize away. Regex descriptors contain JS pattern/flags source. Flag validation rejects duplicates, unknown flags and combined u/v; pattern grammar requires a JS-aware parser, not Python re.

For strict JSON interchange, nonfinite numbers use the small ESTree extension {'type':'Literal','value':None,'number':'Infinity','raw':'1e400'} (or number:'-Infinity'). This distinguishes infinity from actual null while allowing json.dumps(ast, allow_nan=False). Tags require a null value and cannot conflict with BigInt/regex descriptors or raw numeric spelling. Generation restores 1e400/-1e400; JSON literal formatting emits null. The serializer can also describe number:'NaN', but generation retains its explicit NaN rejection policy. Ordinary native float infinities remain supported.

Options

Formatting options belong under format, including escapeless:

code = escodegen.generate(tree, {
    'format': {'quotes': 'double', 'escapeless': True,
               'indent': {'style': '  ', 'base': 0}},
    'comment': True,
})

format keys: indent (style, base, adjustMultilineComment), newline, space, json, renumber, hexadecimal, quotes (single, double, auto), escapeless, compact, parentheses, semicolons, safeConcatenation, preserveBlankLines. FORMAT_DEFAULTS and FORMAT_MINIFY are ready-made format objects. json controls literal syntax, not AST JSON serialization.

Top-level keys: comment, directive, raw, parse, verbatim, sourceCode, sourceMap, sourceMapRoot, sourceMapWithCode, sourceContent, file, plus legacy indent/base. Raw reuse requires an equivalent literal from parse. verbatim names an optional AST field containing a string or {content,precedence} object; absent fields emit normally. Custom/verbatim text is trusted JavaScript. Escaped strict directives retain non-strict semantics. Inputs are not modified.

Ordinary native attribute ASTs are read directly after a compatibility check. Custom properties, mixed mappings, alias normalization and parser callbacks retain per-call snapshot adaptation. Both paths preserve shared references and leave inputs unchanged. Public SimpleObj.fromDict always copies native values. Identifier validation and adapter type information are cached only within each call.

The final performance pass reduced medium native generation by 39.5% and peak allocation by 75.9% from the modernization starting checkpoint. Mapping generation retains its gains over the original port; native generation remains slower than original in the measured legacy cases. See performance status for exact revisions, workloads and limits.

Comments must be attached as leadingComments/trailingComments or block innerComments; this package does not expose upstream attachComments. preserveBlankLines requires sourceCode, statement/comment range and comment extendedRange in UTF-16 units; invalid or split-surrogate ranges raise ValueError. Program.hashbang excludes #!; Line comments with hashbang=True are deduplicated. Hashbangs emit at byte zero, including compact output.

Source maps

Source Map v3 uses Base64 VLQ and UTF-16 generated columns. Mappings describe located node starts rather than every character; punctuation inherits node context.

tree = esprima.parseScript('const x = 1;', {'loc': True})
result = escodegen.generate(tree, {'sourceMap': 'input.js',
    'sourceMapWithCode': True, 'sourceContent': 'const x = 1;', 'file':'output.js'})
print(result.code)
print(result.map.toString())  # JSON; .toJSON() returns a mapping

sourceMap=True uses loc.source, falling back to source.js. A string uses that filename. Without sourceMapWithCode, mapping returns JSON; normal generation returns code. Sources, names, sourceRoot, file and sourcesContent are supported. Original locations use one-based lines, zero-based UTF-16 columns.

Tests and contributor reports

Tests use pinned Acorn 8.19.0/JSX 5.3.2, Node semantic checks and source-map 0.8.0. Coverage includes the upstream 2.1.0 corpus, every nested binary-operator pair, Unicode/controls, directives/comments and concurrency. This is a tested feature contract, not complete Test262 conformance. Generation currently traverses nested expressions recursively. Very deep ASTs can exceed Python's stack capacity and raise an actionable ValueError; the exact threshold depends on the tree and interpreter recursion limit. Generation does not change the process-wide recursion limit.

Reports by wjsw1781 (PR #4) and AugustineFulgur (PR #5) are regression-tested. General ECMAScript escaping supersedes the proposed punctuation exception and Python repr replacements while preserving quote, JSON and UTF-16 values. No PR was merged/closed automatically. Upstream BSD copyrights and fixture notices remain preserved.

Release automation and version bumps are documented in RELEASING.md.

Metadata

Release files for escodegen 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for escodegen 2.0.0
File Size Uploaded
escodegen-2.0.0.tar.gz 74.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for escodegen 2.0.0
File Interpreter ABI Platform
escodegen-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 110.1 kB

Release files / escodegen-2.0.0.tar.gz

Download URL escodegen-2.0.0.tar.gz
Size 74.2 kB
Tags Source
SHA-256 checksum
How to use checksums
341e636acb7552890be615c03c26cf5fe5a44f02dc132e19b91b96ff27e8ae8e
BLAKE2b-256 checksum
How to use checksums
9ab7ff2c3b392951284a601c3161ceb2a2f6b777a2a11498df0f75141cdecd18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / escodegen-2.0.0-py3-none-any.whl

Download URL escodegen-2.0.0-py3-none-any.whl
Size 35.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1882f7add268548c4c01a477df01c7d19c460bc0e3182ce29cbbef14030e7d60
BLAKE2b-256 checksum
How to use checksums
5811bdd0123fc3fb469abd28d4839422efe2a83bcce3022e0995dacb911009da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.0.11

1 release file

1.0.10

1 release file

1.0.9

1 release file

1.0.8

1 release file

1.0.7

1 release file

1.0.6

1 release file

1.0.5

1 release file

1.0.4

1 release file

1.0.3

1 release file

1.0.2

1 release file

1.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page