Skip to content

NullVoxPopuli/ember-estree

Repository files navigation

ember-estree

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.

Install

pnpm add ember-estree

Usage

Parsing

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"

Printing

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>"

Options

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.

Token stream

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 });

Custom parser

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.

Visitors

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);
    },
  }),
});

Examples

The examples/ directory contains ready-to-run integrations:

Example Description
eslint-parser Custom ESLint parser that understands <template>
zmod Codemod toolkit using zmod

AST node reference

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)

License

MIT

About

ESTree generator for gjs and gts file used by ember

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors