babylon
babylon is the JavaScript parser Babel 6 used. You give it source text and it returns a Babel-flavoured ESTree AST: a File node wrapping a Program, plus comments and tokens arrays. It handles JSX and Flow through plugins and was, in 2017, the parser with the best coverage of in-flight TC39 proposals. It is also finished. The project moved into the Babel monorepo as @babel/parser in 2018, the repository was archived, and the npm dist-tag still points at 6.18.0 from August 2017. Anything you install today parses the language as it stood at ES2017.
babylon is a well-built parser that stopped in August 2017 and became @babel/parser. Keep it only to service Babel 6 code you have not migrated yet; for anything you will still be running next year, install @babel/parser instead.
Use it if
- You maintain a Babel 6 toolchain that already depends on babylon and you need to understand or patch what it parses before migrating
- You are reading or writing a codebase that is frozen at ES2017 syntax and you want a parser whose behaviour will never shift under you
- You need Babel's AST shape specifically, with StringLiteral and NumericLiteral instead of ESTree's Literal, and ClassMethod instead of MethodDefinition, for tooling written against Babel 6
- You are tracing why an old build step fails on modern syntax and want to confirm the parser, not the transform, is the thing rejecting the code
- Your source contains any syntax newer than ES2017: optional chaining, nullish coalescing, BigInt literals, numeric separators, optional catch binding, logical assignment, private class fields and top-level await all throw here, confirmed by running 6.18.0
- You want dynamic import(), object rest and spread, or class fields without opting into plugins, because in 6.18.0 all three need an explicit plugins entry and the decorators plugin is documented as an outdated version of the proposal
- You expect maintenance: the repository has been archived since May 2018, the last stable publish was August 2017, and every fix since then has landed in @babel/parser instead
- You are starting anything new, since @babel/parser is the same lineage with the same AST, actively released, and the migration is mostly a package rename
- You need ESM or TypeScript types, because 6.18.0 ships CommonJS only with no bundled declarations
- You want an ESTree-shaped AST by default for tools like eslint or escodegen, since Babel's deviations require the estree plugin or downstream translation
Setup reality
npm install babylon gives you a 33 KB gzipped CommonJS module with zero runtime dependencies and a babylon CLI binary. Nothing to configure, no native build. The friction is entirely about what it will and will not accept. Two API entry points exist: parse(code, options) for a whole program and parseExpression(code, options) for a single expression. Options that matter are sourceType ('script' or 'module'), plugins (an array of strings), allowImportExportEverywhere, allowReturnOutsideFunction, sourceFilename and startLine. The default plugin set is empty, so JSX, Flow, class properties, object rest and spread, and dynamic import all fail until you name them. Running 6.18.0 against modern syntax gives a clear picture: async functions and the ** operator parse, but a?.b, a ?? b, 1n, 1_000, try {} catch {}, a ||= b, class A { #x }, import.meta and top-level await all throw SyntaxError. The output is a File node, not a Program node, so ast.program.body is where statements live; ast.comments and ast.tokens are populated by default, which surprises people used to acorn where tokens are opt-in. Error objects carry pos, loc.line and loc.column, so reporting failures is easy. There is one more trap in the version list: npm has 7.0.0-beta.47 published in May 2018, but the latest tag points at 6.18.0, so a range like ^6.18.0 is fine while a wildcard can quietly pick up an abandoned beta. If any of this is annoying, that is the signal to move: @babel/parser is a rename plus plugin-name updates for most callers.
Patterns
Parse ES module sourceparse-a-module
const babylon = require('babylon')
const ast = babylon.parse('import a from "a"; export default a', {
sourceType: 'module',
})
console.log(ast.type) // 'File'
console.log(ast.program.type) // 'Program'
console.log(ast.program.body.length)The root is a File node, not Program. Statements live at ast.program.body, which trips up code written for acorn.
Turn on JSX and Flow syntaxenable-jsx-and-flow
const babylon = require('babylon')
const ast = babylon.parse(source, {
sourceType: 'module',
plugins: ['jsx', 'flow'],
})The default plugin list is empty, so <div/> and type aliases are syntax errors until you name the plugins. Both are verified working in 6.18.0.
Parse one expression instead of a programparse-single-expression
const babylon = require('babylon')
const node = babylon.parseExpression('a + b * c')
console.log(node.type) // 'BinaryExpression'parseExpression skips program-level setup and returns the expression node directly. Use parse() whenever the input might be more than one expression.
Report a parse failure with positionhandle-syntax-errors
const babylon = require('babylon')
try {
babylon.parse(source, { sourceType: 'module' })
} catch (err) {
console.error(`${err.message} at ${err.loc.line}:${err.loc.column}`)
console.error(`char offset ${err.pos}`)
}Errors carry pos and loc. The message already includes a position suffix, so printing both duplicates it unless you strip the suffix first.
Confirm which modern syntax it rejectsdetect-unsupported-syntax
const babylon = require('babylon')
for (const src of ['a?.b', 'a ?? b', '1n', '1_000', 'try{}catch{}', 'a ||= b', 'class A{#x=1}', 'await x']) {
try {
babylon.parse(src, { sourceType: 'module' })
console.log('OK ', src)
} catch (e) {
console.log('FAIL', src, e.message)
}
}Every one of those fails on 6.18.0. If your source has any of it, no option or plugin helps; you need @babel/parser.
Enable the proposal plugins it does haveopt-into-proposals
const babylon = require('babylon')
const ast = babylon.parse(source, {
sourceType: 'module',
plugins: [
'classProperties',
'objectRestSpread',
'dynamicImport',
'asyncGenerators',
],
})class A { x = 1 }, ({...a}) and import('x') all parse only with these on. The decorators plugin tracks an outdated proposal and produces nodes current tools will not recognise.
Use the comments and tokens it already collectedread-comments-and-tokens
const babylon = require('babylon')
const ast = babylon.parse('// note\nconst x = 1', { sourceType: 'module' })
console.log(ast.comments.map((c) => c.type)) // [ 'CommentLine' ]
console.log(ast.tokens.length)Both arrays are populated by default with no option to request them, unlike acorn. That costs time and memory on large files you only wanted an AST for.
Get an ESTree-shaped AST for other toolsestree-compatible-output
const babylon = require('babylon')
const ast = babylon.parse(source, {
sourceType: 'module',
plugins: ['estree'],
})
// Literal instead of StringLiteral/NumericLiteral,
// Property instead of ObjectProperty/ObjectMethodAdd estree when feeding escodegen or eslint-style consumers. Babel's own babel-traverse and babel-types expect the deviations, so do not mix the two in one pipeline.
Parse code that breaks the usual rulesparse-script-fragments
const babylon = require('babylon')
const ast = babylon.parse(snippet, {
allowReturnOutsideFunction: true,
allowImportExportEverywhere: true,
sourceFilename: 'inline.js',
startLine: 42,
})startLine and sourceFilename make error positions line up with the host file when you extract snippets out of templates or markdown.
Avoid the abandoned 7.0.0 betaspin-away-from-beta
// package.json
{
"dependencies": {
"babylon": "^6.18.0"
}
}
// npm dist-tag latest is 6.18.0;
// 7.0.0-beta.47 (May 2018) exists but never shipped stableA caret on 6.18.0 stays inside the 6.x line. A wildcard or a "next" tag can drag in a beta that was abandoned mid-migration.
Dump an AST from the shellcli-parse
npx babylon@6.18.0 --plugins jsx,flow src/component.js > ast.jsonThe package ships a babylon binary. It is useful for one-off inspection; it has no watch mode and no pretty-printing beyond raw JSON.
Move to the package this becamemigrate-to-babel-parser
// npm uninstall babylon && npm install @babel/parser
-const babylon = require('babylon')
-const ast = babylon.parse(code, { plugins: ['objectRestSpread'] })
+const parser = require('@babel/parser')
+const ast = parser.parse(code, { plugins: ['jsx'] })
// object rest/spread and dynamic import are now standard, no plugin neededparse and parseExpression keep their signatures. The work is dropping plugin names that became standard syntax and renaming ones Babel 7 changed.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @babel/parser | npm | You want this exact parser and AST shape, still released, with current syntax support; this is where the project moved in 2018 |
| acorn | npm | You want a small, fast, plain ESTree parser and do not need Babel's node types or its proposal plugins |
| espree | npm | You are building on eslint's parser contract and want the AST that eslint rules already expect |
| meriyah | npm | You want an ESTree parser tuned for speed and are willing to trade a smaller ecosystem for it |