A JavaScript parser built from the Hermes engine that supports ES6, Flow, and JSX syntax
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Helper functions for AST node manipulation, visitor key management, and compatibility utilities.
All utility functions can be imported individually or through namespace imports:
// Individual imports
import { isNode, getVisitorKeys, nodeWith, shallowCloneNode } from "hermes-parser";
// Namespace imports
import { astNodeMutationHelpers, astArrayMutationHelpers } from "hermes-parser";Utilities for validating and checking AST node types and structures.
/**
* Type guard to check if a value is an AST node
* @param value - Value to test
* @returns True if value is an AST node with type property
*/
function isNode(value: unknown): value is ESNode;Usage Examples:
import { isNode } from "hermes-parser";
// Check various values
console.log(isNode({ type: "Identifier", name: "x" })); // true
console.log(isNode({ type: "Literal", value: 42 })); // true
console.log(isNode("not a node")); // false
console.log(isNode(null)); // false
console.log(isNode({ name: "x" })); // false (no type property)
// Use in traversal logic
function processNode(value) {
if (isNode(value)) {
console.log(`Processing ${value.type} node`);
// Safe to access node properties
} else {
console.log("Skipping non-node value");
}
}Functions for managing visitor keys that control AST traversal behavior.
/**
* Get visitor keys for a specific AST node type
* @param node - AST node to get traversal keys for
* @param visitorKeys - Optional custom visitor keys mapping
* @returns Array of property names that should be traversed
*/
function getVisitorKeys<T extends ESNode>(
node: T,
visitorKeys?: VisitorKeysType
): ReadonlyArray<keyof T>;
/**
* Default Flow ESTree visitor keys mapping
* Maps each AST node type to its traversable properties
*/
const FlowVisitorKeys: VisitorKeysType;
type VisitorKeysType = {
[nodeType: string]: ReadonlyArray<string>;
};Visitor Key Examples:
import { getVisitorKeys, FlowVisitorKeys } from "hermes-parser";
const functionNode = {
type: "FunctionDeclaration",
id: { type: "Identifier", name: "test" },
params: [],
body: { type: "BlockStatement", body: [] }
};
// Get default visitor keys for function
const keys = getVisitorKeys(functionNode);
console.log(keys);
// ["id", "params", "body", "typeParameters", "returnType", "predicate"]
// Check what keys exist for different node types
console.log(FlowVisitorKeys.Identifier); // []
console.log(FlowVisitorKeys.CallExpression); // ["callee", "typeArguments", "arguments"]
console.log(FlowVisitorKeys.MemberExpression); // ["object", "property"]
// Use custom visitor keys
const customKeys = {
...FlowVisitorKeys,
CustomNode: ["customProp1", "customProp2"],
// Override existing keys
Identifier: ["name", "typeAnnotation"]
};
const customTraversalKeys = getVisitorKeys(functionNode, customKeys);Utility for making AST compatible with Prettier formatting tools.
/**
* Mutate ESTree AST to be compatible with Prettier formatting
* Modifies the AST in place to work around Prettier-specific requirements
* @param rootNode - Program node to mutate
* @param visitorKeys - Optional visitor keys for traversal
*/
function mutateESTreeASTForPrettier(
rootNode: Program,
visitorKeys?: VisitorKeysType
): void;Prettier Compatibility Examples:
import { mutateESTreeASTForPrettier, parse } from "hermes-parser";
const code = "const x = obj?.method?.();";
const ast = parse(code);
// Make AST compatible with Prettier V2
mutateESTreeASTForPrettier(ast);
// Now the AST can be formatted with Prettier
// Note: This modifies the AST in placeUtilities for creating copies of AST nodes with proper structure preservation.
/**
* Create shallow clone of AST node with correct parent pointers
* @param node - AST node to clone
* @param visitorKeys - Optional visitor keys for parent management
* @returns Shallow clone with updated parent pointers
*/
function shallowCloneNode<T extends ESNode>(
node: T,
visitorKeys?: VisitorKeysType
): T;
/**
* Create deep clone of AST node and entire subtree
* @param node - AST node to clone
* @param visitorKeys - Optional visitor keys for parent management
* @returns Deep clone with all children and correct parent pointers
*/
function deepCloneNode<T extends ESNode>(
node: T,
visitorKeys?: VisitorKeysType
): T;Cloning Examples:
import { shallowCloneNode, deepCloneNode } from "hermes-parser";
const originalNode = {
type: "FunctionDeclaration",
id: { type: "Identifier", name: "test" },
params: [],
body: { type: "BlockStatement", body: [] }
};
// Shallow clone - children are still shared references
const shallowCopy = shallowCloneNode(originalNode);
console.log(shallowCopy.id === originalNode.id); // true (shared reference)
// Deep clone - entire subtree is copied
const deepCopy = deepCloneNode(originalNode);
console.log(deepCopy.id === originalNode.id); // false (separate copy)
console.log(deepCopy.id.name === originalNode.id.name); // true (same value)
// Parent pointers are correctly set in both cases
console.log(deepCopy.id.parent === deepCopy); // trueUtilities for managing parent relationships in AST nodes.
/**
* Set parent pointers on direct children of a node
* @param node - Parent node to update children for
* @param visitorKeys - Optional visitor keys for traversal
*/
function setParentPointersInDirectChildren(
node: ESNode,
visitorKeys?: VisitorKeysType
): void;
/**
* Update all parent pointers in entire subtree
* @param node - Root node of subtree to update
* @param visitorKeys - Optional visitor keys for traversal
*/
function updateAllParentPointers(
node: ESNode,
visitorKeys?: VisitorKeysType
): void;Parent Pointer Examples:
import {
setParentPointersInDirectChildren,
updateAllParentPointers
} from "hermes-parser";
const ast = parse("function test() { return x + y; }");
// Update only direct children
setParentPointersInDirectChildren(ast);
// Update entire subtree (recommended after manual AST construction)
updateAllParentPointers(ast);
// Verify parent pointers are set
function verifyParents(node, expectedParent = null) {
console.log(`${node.type} parent:`, node.parent?.type || 'null');
// Check each child has correct parent
const keys = getVisitorKeys(node);
keys.forEach(key => {
const child = node[key];
if (Array.isArray(child)) {
child.forEach(item => {
if (isNode(item)) {
verifyParents(item, node);
}
});
} else if (isNode(child)) {
verifyParents(child, node);
}
});
}
verifyParents(ast);Core type definitions used throughout the utility functions.
/**
* Base interface for all AST nodes
*/
interface ESNode {
type: string;
range?: [number, number];
loc?: SourceLocation;
parent?: ESNode;
}
/**
* Source location information
*/
interface SourceLocation {
start: Position;
end: Position;
}
interface Position {
line: number;
column: number;
}
/**
* Visitor keys mapping type
*/
type VisitorKeysType = {
[nodeType: string]: ReadonlyArray<string>;
};
/**
* Program AST node type
*/
interface Program extends ESNode {
type: "Program";
body: Statement[];
sourceType: "script" | "module";
comments?: Comment[];
tokens?: Token[];
}Common patterns for using AST utilities in real applications.
Safe Node Property Access:
function safeGetProperty(node, path) {
const parts = path.split('.');
let current = node;
for (const part of parts) {
if (!isNode(current) && typeof current !== 'object') {
return undefined;
}
current = current[part];
}
return current;
}
// Usage
const memberName = safeGetProperty(callExpression, 'callee.property.name');Custom Visitor Key Creation:
function createCustomVisitorKeys(additionalKeys = {}) {
return {
...FlowVisitorKeys,
...additionalKeys,
// Add custom traversal for specific nodes
CustomWrapper: ['wrappedNode', 'metadata'],
EnhancedIdentifier: ['name', 'typeAnnotation', 'customData']
};
}
const customKeys = createCustomVisitorKeys({
SpecialNode: ['specialProp1', 'specialProp2']
});AST Structure Validation:
function validateASTStructure(node, depth = 0) {
if (depth > 100) {
throw new Error('AST too deep - possible circular reference');
}
if (!isNode(node)) {
throw new Error(`Invalid node at depth ${depth}: ${typeof node}`);
}
const keys = getVisitorKeys(node);
for (const key of keys) {
const child = node[key];
if (Array.isArray(child)) {
child.forEach((item, index) => {
if (isNode(item)) {
if (item.parent !== node) {
console.warn(`Parent pointer mismatch at ${key}[${index}]`);
}
validateASTStructure(item, depth + 1);
}
});
} else if (isNode(child)) {
if (child.parent !== node) {
console.warn(`Parent pointer mismatch at ${key}`);
}
validateASTStructure(child, depth + 1);
}
}
}
// Usage
try {
validateASTStructure(ast);
console.log('AST structure is valid');
} catch (error) {
console.error('AST validation failed:', error.message);
}