
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
ember-estree
Advanced tools
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-estree
toTree 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 + ClassDeclaration
parse 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>"
print also accepts the File node returned by toTree, printing the whole program with file.comments woven back into the output (each comment is emitted before the nearest node that follows it in the original source). Placement is approximate — a trailing same-line comment becomes a leading comment of the next node — so pair the output with a formatter (e.g. prettier) when exact layout matters.
import { toTree, print } from "ember-estree";
let tree = toTree(`// greet the user\nlet greeting = "hello";`);
print(tree);
// => '// greet the user\nlet greeting = "hello";'
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 order
For .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 |
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.
.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 |
.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) |
.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
FAQs
ESTree generator for gjs and gts file used by ember
The npm package ember-estree receives a total of 76,748 weekly downloads. As such, ember-estree popularity was classified as popular.
We found that ember-estree demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.