ESTree-compatible AST parser for Ember's .gjs and .gts files.
Parses <template> tags into Glimmer AST nodes that are embedded directly in the ESTree, so tools like linters and codemods can work with both the JavaScript/TypeScript and template portions of a single file.
pnpm add ember-estreetoTree returns a File node whose .program is a standard ESTree Program, with any <template> regions represented as Glimmer* AST nodes.
import { toTree } from "ember-estree";
let ast = toTree(`
import Component from "@glimmer/component";
export default class Demo extends Component {
<template>Hello, {{this.name}}!</template>
}
`);
console.log(ast.type); // "File"
console.log(ast.program.body.length); // 2 — ImportDeclaration + ClassDeclarationparse is a lower-level alternative that returns the Program node directly.
import { parse } from "ember-estree";
let program = parse(`const x = <template>hi</template>;`);
console.log(program.type); // "Program"print converts an AST node (ESTree or Glimmer) back to source code.
import { print } from "ember-estree";
print({ type: "Identifier", name: "foo" });
// => "foo"
print({
type: "GlimmerTemplate",
body: [{ type: "GlimmerTextNode", chars: "Hello" }],
});
// => "<template>Hello</template>"Both toTree and parse accept an options object as their second argument.
All options are optional.
| Option | Type | Description |
|---|---|---|
filePath |
string |
Used for language detection. |
tokens |
boolean |
Generate a flat ast.tokens array. Required by ESLint; skipped by default so codemods and type-checkers pay nothing. |
templateOnly |
boolean |
Parse the source as a raw Glimmer template. Use for .hbs files. |
parser |
(placeholderJS: string) => { ast, ... } |
Use a custom JS/TS parser instead of the default oxc-parser. See Custom parser. |
visitors |
VisitorMap or (outerAst) => VisitorMap |
Callbacks fired on every node during traversal — JS/TS and Glimmer — in a single pass. See Visitors. |
Handler signature is (node, path) => void, where path = { node, parent, parentPath } — a linked list that walks all the way back through the JS/TS root, so visitors can locate the enclosing scope or class from within a Glimmer subtree.
Pass tokens: true to populate ast.tokens with a flat, position-sorted array of lexemes spanning the full file — including Glimmer tokens spliced in place of each <template> region. This is what ESLint's SourceCode needs; omit it for codemods or type-checkers that don't use the token stream.
import { toTree } from "ember-estree";
const result = toTree(source, {
tokens: true,
parser: myTsParser,
});
// result.ast.program.tokens now contains JS + Glimmer tokens in source orderFor .hbs files via templateOnly, pass both flags:
toTree(hbsSource, { templateOnly: true, tokens: true });Pass any JS/TS parser that returns an ESTree-compatible AST. ember-estree handles template splicing and Glimmer traversal on top of it.
import { parseSync } from "oxc-parser";
import { toTree } from "ember-estree";
const result = toTree(source, {
parser: (js) => ({
ast: parseSync("input.ts", js).program,
visitorKeys: {
/* ...parser's visitor keys... */
},
}),
});The parser receives a placeholder-JS string (templates replaced with backtick expressions of equal length) and must return at least { ast }. Additional fields like scopeManager, visitorKeys, or services are preserved on the returned result.
Pass visitors to observe or rewrite the tree in a single traversal. Handlers fire on both outer JS/TS nodes and spliced Glimmer subtrees, and a single node is never dispatched twice — safe to relocate nodes mid-walk.
The pseudo-type GlimmerBlockParams fires on any node that carries a blockParams array.
Plain-object form — use when you only need the type → handler map:
import { toTree } from "ember-estree";
const identifiers = [];
toTree(source, {
visitors: {
Identifier: (node) => identifiers.push(node.name),
GlimmerPathExpression: (node) => identifiers.push(node.original),
},
});Factory form — use when you need the outer JS/TS AST up front (for example, to attach state to it before the walk):
import { toTree, print } from "ember-estree";
const ast = toTree(`const world = "🌍"; const X = <template>{{world}}</template>;`, {
visitors: () => ({
Identifier: (node) => (node.name = node.name.toUpperCase()),
GlimmerPathExpression(node) {
node.original = node.original.toUpperCase();
if (node.head) node.head.name = node.original;
},
}),
});
print(ast.program);
// => 'const WORLD = "🌍";\nconst X = <template>{{WORLD}}</template>;'Collecting Glimmer comments into program.comments — useful when adapting the AST for ESLint, which reads comments from the Program node:
const ast = toTree(source, {
visitors: (outerAst) => {
outerAst.program.comments = [...(outerAst.comments ?? [])];
const push = (node) => outerAst.program.comments.push(node);
return {
GlimmerCommentStatement: push,
GlimmerMustacheCommentStatement: push,
};
},
});Removing nodes mid-traversal — siblings are splice-safe:
toTree(source, {
visitors: () => ({
GlimmerMustacheCommentStatement(node, path) {
const siblings = path.parent?.body ?? path.parent?.children;
const idx = siblings?.indexOf(node) ?? -1;
if (idx >= 0) siblings.splice(idx, 1);
},
}),
});The examples/ directory contains ready-to-run integrations:
| Example | Description |
|---|---|
eslint-parser |
Custom ESLint parser that understands <template> |
zmod |
Codemod toolkit using zmod |
Every AST node ember-estree may emit (171 total) — grouped by which files they appear in
Generated from oxc-parser's and @glimmer/syntax's visitor-key maps. Re-run node scripts/generate-ast-node-reference.mjs after bumping either dependency to keep this in sync.
Core ESTree — in .gjs and .gts (76 nodes)
Standard JavaScript node types. Present in both .gjs and .gts — TypeScript is a superset of JavaScript, so .gts files may contain all of these too.
| Node | Child keys |
|---|---|
AccessorProperty |
decorators, key, typeAnnotation, value |
ArrayExpression |
elements |
ArrayPattern |
decorators, elements, typeAnnotation |
ArrowFunctionExpression |
typeParameters, params, returnType, body |
AssignmentExpression |
left, right |
AssignmentPattern |
decorators, left, right, typeAnnotation |
AwaitExpression |
argument |
BinaryExpression |
left, right |
BlockStatement |
body |
BreakStatement |
label |
CallExpression |
callee, typeArguments, arguments |
CatchClause |
param, body |
ChainExpression |
expression |
ClassBody |
body |
ClassDeclaration |
decorators, id, typeParameters, superClass, superTypeArguments, implements, body |
ClassExpression |
decorators, id, typeParameters, superClass, superTypeArguments, implements, body |
ConditionalExpression |
test, consequent, alternate |
ContinueStatement |
label |
DebuggerStatement |
(leaf) |
Decorator |
expression |
DoWhileStatement |
body, test |
EmptyStatement |
(leaf) |
ExportAllDeclaration |
exported, source, attributes |
ExportDefaultDeclaration |
declaration |
ExportNamedDeclaration |
declaration, specifiers, source, attributes |
ExportSpecifier |
local, exported |
ExpressionStatement |
expression |
ForInStatement |
left, right, body |
ForOfStatement |
left, right, body |
ForStatement |
init, test, update, body |
FunctionDeclaration |
id, typeParameters, params, returnType, body |
FunctionExpression |
id, typeParameters, params, returnType, body |
Identifier |
decorators, typeAnnotation |
IfStatement |
test, consequent, alternate |
ImportAttribute |
key, value |
ImportDeclaration |
specifiers, source, attributes |
ImportDefaultSpecifier |
local |
ImportExpression |
source, options |
ImportNamespaceSpecifier |
local |
ImportSpecifier |
imported, local |
LabeledStatement |
label, body |
Literal |
(leaf) |
LogicalExpression |
left, right |
MemberExpression |
object, property |
MetaProperty |
meta, property |
MethodDefinition |
decorators, key, value |
NewExpression |
callee, typeArguments, arguments |
ObjectExpression |
properties |
ObjectPattern |
decorators, properties, typeAnnotation |
ParenthesizedExpression |
expression |
PrivateIdentifier |
(leaf) |
Program |
body |
Property |
key, value |
PropertyDefinition |
decorators, key, typeAnnotation, value |
RestElement |
decorators, argument, typeAnnotation |
ReturnStatement |
argument |
SequenceExpression |
expressions |
SpreadElement |
argument |
StaticBlock |
body |
Super |
(leaf) |
SwitchCase |
test, consequent |
SwitchStatement |
discriminant, cases |
TaggedTemplateExpression |
tag, typeArguments, quasi |
TemplateElement |
(leaf) |
TemplateLiteral |
quasis, expressions |
ThisExpression |
(leaf) |
ThrowStatement |
argument |
TryStatement |
block, handler, finalizer |
UnaryExpression |
argument |
UpdateExpression |
argument |
V8IntrinsicExpression |
name, arguments |
VariableDeclaration |
declarations |
VariableDeclarator |
id, init |
WhileStatement |
test, body |
WithStatement |
object, body |
YieldExpression |
argument |
TypeScript — .gts only (74 nodes)
TypeScript-specific nodes. Can only appear in .gts files.
| Node | Child keys |
|---|---|
TSAbstractAccessorProperty |
decorators, key, typeAnnotation |
TSAbstractMethodDefinition |
key, value |
TSAbstractPropertyDefinition |
decorators, key, typeAnnotation |
TSAnyKeyword |
(leaf) |
TSArrayType |
elementType |
TSAsExpression |
expression, typeAnnotation |
TSBigIntKeyword |
(leaf) |
TSBooleanKeyword |
(leaf) |
TSCallSignatureDeclaration |
typeParameters, params, returnType |
TSClassImplements |
expression, typeArguments |
TSConditionalType |
checkType, extendsType, trueType, falseType |
TSConstructorType |
typeParameters, params, returnType |
TSConstructSignatureDeclaration |
typeParameters, params, returnType |
TSDeclareFunction |
id, typeParameters, params, returnType, body |
TSEmptyBodyFunctionExpression |
id, typeParameters, params, returnType |
TSEnumBody |
members |
TSEnumDeclaration |
id, body |
TSEnumMember |
id, initializer |
TSExportAssignment |
expression |
TSExternalModuleReference |
expression |
TSFunctionType |
typeParameters, params, returnType |
TSImportEqualsDeclaration |
id, moduleReference |
TSImportType |
source, options, qualifier, typeArguments |
TSIndexedAccessType |
objectType, indexType |
TSIndexSignature |
parameters, typeAnnotation |
TSInferType |
typeParameter |
TSInstantiationExpression |
expression, typeArguments |
TSInterfaceBody |
body |
TSInterfaceDeclaration |
id, typeParameters, extends, body |
TSInterfaceHeritage |
expression, typeArguments |
TSIntersectionType |
types |
TSIntrinsicKeyword |
(leaf) |
TSJSDocNonNullableType |
typeAnnotation |
TSJSDocNullableType |
typeAnnotation |
TSJSDocUnknownType |
(leaf) |
TSLiteralType |
literal |
TSMappedType |
key, constraint, nameType, typeAnnotation |
TSMethodSignature |
key, typeParameters, params, returnType |
TSModuleBlock |
body |
TSModuleDeclaration |
id, body |
TSNamedTupleMember |
label, elementType |
TSNamespaceExportDeclaration |
id |
TSNeverKeyword |
(leaf) |
TSNonNullExpression |
expression |
TSNullKeyword |
(leaf) |
TSNumberKeyword |
(leaf) |
TSObjectKeyword |
(leaf) |
TSOptionalType |
typeAnnotation |
TSParameterProperty |
decorators, parameter |
TSParenthesizedType |
typeAnnotation |
TSPropertySignature |
key, typeAnnotation |
TSQualifiedName |
left, right |
TSRestType |
typeAnnotation |
TSSatisfiesExpression |
expression, typeAnnotation |
TSStringKeyword |
(leaf) |
TSSymbolKeyword |
(leaf) |
TSTemplateLiteralType |
quasis, types |
TSThisType |
(leaf) |
TSTupleType |
elementTypes |
TSTypeAliasDeclaration |
id, typeParameters, typeAnnotation |
TSTypeAnnotation |
typeAnnotation |
TSTypeAssertion |
typeAnnotation, expression |
TSTypeLiteral |
members |
TSTypeOperator |
typeAnnotation |
TSTypeParameter |
name, constraint, default |
TSTypeParameterDeclaration |
params |
TSTypeParameterInstantiation |
params |
TSTypePredicate |
parameterName, typeAnnotation |
TSTypeQuery |
exprName, typeArguments |
TSTypeReference |
typeName, typeArguments |
TSUndefinedKeyword |
(leaf) |
TSUnionType |
types |
TSUnknownKeyword |
(leaf) |
TSVoidKeyword |
(leaf) |
Glimmer template — in .gjs and .gts (21 nodes)
Nodes produced inside <template>...</template> regions by @glimmer/syntax, prefixed with Glimmer when spliced into the ESTree.
| Node | Child keys |
|---|---|
GlimmerAttrNode |
value |
GlimmerBlock |
body |
GlimmerBlockStatement |
path, params, hash, program, inverse |
GlimmerBooleanLiteral |
(leaf) |
GlimmerCommentStatement |
(leaf) |
GlimmerConcatStatement |
parts |
GlimmerElementModifierStatement |
path, params, hash |
GlimmerElementNode |
attributes, modifiers, children, comments, blockParamNodes, parts |
GlimmerHash |
pairs |
GlimmerHashPair |
value |
GlimmerMustacheCommentStatement |
(leaf) |
GlimmerMustacheStatement |
path, params, hash |
GlimmerNullLiteral |
(leaf) |
GlimmerNumberLiteral |
(leaf) |
GlimmerPathExpression |
(leaf) |
GlimmerProgram |
body, blockParamNodes |
GlimmerStringLiteral |
(leaf) |
GlimmerSubExpression |
path, params, hash |
GlimmerTemplate |
body |
GlimmerTextNode |
(leaf) |
GlimmerUndefinedLiteral |
(leaf) |
MIT