@ordojs/dev-tools
Version:
Advanced developer tools for OrdoJS with component inspector, AST explorer, and performance profiling
276 lines (274 loc) • 6.7 kB
JavaScript
import { EventEmitter } from 'events';
// src/ast-explorer/index.ts
var ASTExplorer = class extends EventEmitter {
astNodes;
isRunning;
port;
/**
* Create a new ASTExplorer instance
*
* @param port - WebSocket port for AST explorer
*/
constructor(port = 24681) {
super();
this.astNodes = /* @__PURE__ */ new Map();
this.isRunning = false;
this.port = port;
}
/**
* Start the AST explorer
*/
async start() {
if (this.isRunning) {
console.warn("AST explorer is already running");
return;
}
try {
await this.startWebSocketServer();
this.isRunning = true;
console.log(`AST explorer started on port ${this.port}`);
this.emit("started");
} catch (error) {
console.error("Failed to start AST explorer:", error);
this.emit("error", error);
throw error;
}
}
/**
* Stop the AST explorer
*/
async stop() {
if (!this.isRunning) {
console.warn("AST explorer is not running");
return;
}
try {
await this.stopWebSocketServer();
this.isRunning = false;
console.log("AST explorer stopped");
this.emit("stopped");
} catch (error) {
console.error("Failed to stop AST explorer:", error);
this.emit("error", error);
throw error;
}
}
/**
* Parse source code and create AST
*
* @param sourceCode - Source code to parse
* @param fileName - File name for the AST
* @returns AST root node
*/
parseSourceCode(sourceCode, fileName) {
try {
const astNode = {
type: "Program",
position: {
start: { line: 1, column: 1 },
end: { line: 1, column: sourceCode.length }
},
children: [],
value: sourceCode
};
this.astNodes.set(fileName, astNode);
this.emit("astParsed", { fileName, astNode });
return astNode;
} catch (error) {
console.error("Failed to parse source code:", error);
this.emit("error", error);
throw error;
}
}
/**
* Get AST for a file
*
* @param fileName - File name
* @returns AST node or undefined
*/
getAST(fileName) {
return this.astNodes.get(fileName);
}
/**
* Get all ASTs
*
* @returns Map of all ASTs
*/
getAllASTs() {
return new Map(this.astNodes);
}
/**
* Find nodes by type
*
* @param fileName - File name
* @param nodeType - Node type to search for
* @returns Array of matching nodes
*/
findNodesByType(fileName, nodeType) {
const ast = this.astNodes.get(fileName);
if (!ast) {
return [];
}
return this.findNodesRecursive(ast, nodeType);
}
/**
* Find nodes by value
*
* @param fileName - File name
* @param value - Value to search for
* @returns Array of matching nodes
*/
findNodesByValue(fileName, value) {
const ast = this.astNodes.get(fileName);
if (!ast) {
return [];
}
return this.findNodesByValueRecursive(ast, value);
}
/**
* Get node path from root
*
* @param fileName - File name
* @param targetNode - Target node
* @returns Array of nodes from root to target
*/
getNodePath(fileName, targetNode) {
const ast = this.astNodes.get(fileName);
if (!ast) {
return [];
}
return this.findNodePath(ast, targetNode);
}
/**
* Get AST statistics
*
* @param fileName - File name
* @returns AST statistics
*/
getASTStats(fileName) {
const ast = this.astNodes.get(fileName);
if (!ast) {
return {
totalNodes: 0,
nodeTypes: {},
maxDepth: 0,
averageDepth: 0
};
}
const stats = this.calculateASTStats(ast);
return stats;
}
/**
* Clear all ASTs
*/
clearASTs() {
this.astNodes.clear();
this.emit("astsCleared");
}
/**
* Recursively find nodes by type
*
* @param node - Current node
* @param nodeType - Node type to search for
* @returns Array of matching nodes
*/
findNodesRecursive(node, nodeType) {
const results = [];
if (node.type === nodeType) {
results.push(node);
}
if (node.children) {
for (const child of node.children) {
results.push(...this.findNodesRecursive(child, nodeType));
}
}
return results;
}
/**
* Recursively find nodes by value
*
* @param node - Current node
* @param value - Value to search for
* @returns Array of matching nodes
*/
findNodesByValueRecursive(node, value) {
const results = [];
if (node.value && node.value.includes(value)) {
results.push(node);
}
if (node.children) {
for (const child of node.children) {
results.push(...this.findNodesByValueRecursive(child, value));
}
}
return results;
}
/**
* Find path from root to target node
*
* @param node - Current node
* @param targetNode - Target node
* @param path - Current path
* @returns Path to target node or empty array
*/
findNodePath(node, targetNode, path = []) {
const currentPath = [...path, node];
if (node === targetNode) {
return currentPath;
}
if (node.children) {
for (const child of node.children) {
const result = this.findNodePath(child, targetNode, currentPath);
if (result.length > 0) {
return result;
}
}
}
return [];
}
/**
* Calculate AST statistics
*
* @param node - Root node
* @param depth - Current depth
* @returns AST statistics
*/
calculateASTStats(node, depth = 0) {
let totalNodes = 1;
const nodeTypes = { [node.type]: 1 };
let maxDepth = depth;
let totalDepth = depth;
if (node.children) {
for (const child of node.children) {
const childStats = this.calculateASTStats(child, depth + 1);
totalNodes += childStats.totalNodes;
maxDepth = Math.max(maxDepth, childStats.maxDepth);
totalDepth += childStats.totalNodes * (depth + 1);
for (const [type, count] of Object.entries(childStats.nodeTypes)) {
nodeTypes[type] = (nodeTypes[type] || 0) + count;
}
}
}
return {
totalNodes,
nodeTypes,
maxDepth,
averageDepth: totalDepth / totalNodes
};
}
/**
* Start WebSocket server for AST explorer communication
*/
async startWebSocketServer() {
console.log("Starting WebSocket server for AST explorer...");
}
/**
* Stop WebSocket server
*/
async stopWebSocketServer() {
console.log("Stopping WebSocket server for AST explorer...");
}
};
export { ASTExplorer };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map