UNPKG

hataraku

Version:

An autonomous coding agent for building AI-powered development tools. The name "Hataraku" (働く) means "to work" in Japanese.

719 lines 30.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.findExportsInFile = findExportsInFile; exports.findExportsInDirectory = findExportsInDirectory; exports.findAllExportsInFile = findAllExportsInFile; exports.findAllExportsInDirectory = findAllExportsInDirectory; exports.findNestedExports = findNestedExports; exports.findNestedExportsInDirectory = findNestedExportsInDirectory; const ts = __importStar(require("typescript")); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const glob = __importStar(require("fast-glob")); /** * Finds all exported functions in a TypeScript/JavaScript file * @param filePath Path to the file to analyze * @returns Array of exported functions */ function findExportsInFile(filePath) { // Read the file const fileContent = fs.readFileSync(filePath, 'utf-8'); // Create a SourceFile const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); const exportedFunctions = []; // Helper function to extract JSDoc comment if available const getJSDocComment = (node) => { const jsDocComments = node.jsDoc || []; if (jsDocComments.length > 0) { return jsDocComments[0].comment; } return undefined; }; // Helper function to get function signature const getFunctionSignature = (node) => { let signature = ''; // Get function name if (ts.isFunctionDeclaration(node) && node.name) { signature = node.name.text; } // Get parameters signature += '('; if (node.parameters) { signature += node.parameters.map(param => { let paramText = ''; if (param.name) { paramText = ts.isIdentifier(param.name) ? param.name.text : param.name.getText(sourceFile); } if (param.type) { paramText += ': ' + param.type.getText(sourceFile); } if (param.questionToken) { paramText += '?'; } if (param.initializer) { paramText += ' = ' + param.initializer.getText(sourceFile); } return paramText; }).join(', '); } signature += ')'; // Get return type if (node.type) { signature += ': ' + node.type.getText(sourceFile); } return signature; }; // Visit each node in the source file function visit(node) { // Check for export declarations if (ts.isExportDeclaration(node)) { // Handle named exports like: export { foo, bar } if (node.exportClause && ts.isNamedExports(node.exportClause)) { node.exportClause.elements.forEach(element => { // We need to find the actual declaration of this exported item const name = element.name.text; const propertyName = element.propertyName?.text || name; // Find the declaration in the file ts.forEachChild(sourceFile, innerNode => { if (ts.isFunctionDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { exportedFunctions.push({ name, filePath, signature: getFunctionSignature(innerNode), isDefault: false, documentation: getJSDocComment(innerNode) }); } }); }); } } // Check for exported function declarations if (ts.isFunctionDeclaration(node) && node.name && node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) { const isDefault = node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword); exportedFunctions.push({ name: node.name.text, filePath, signature: getFunctionSignature(node), isDefault, documentation: getJSDocComment(node) }); } // Check for variable declarations that export functions if (ts.isVariableStatement(node) && node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) { node.declarationList.declarations.forEach(declaration => { if (declaration.initializer) { if (ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer)) { const isDefault = node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword); if (ts.isIdentifier(declaration.name)) { exportedFunctions.push({ name: declaration.name.text, filePath, signature: declaration.name.text + getFunctionSignature(declaration.initializer), isDefault, documentation: getJSDocComment(node) }); } } } }); } // Continue traversing the AST ts.forEachChild(node, visit); } // Start the traversal visit(sourceFile); return exportedFunctions; } /** * Finds all exported functions in a directory * @param directory Directory to search in * @param pattern Glob pattern for files to include (default: **\/*.{ts,js}) * @returns Array of exported functions */ function findExportsInDirectory(directory, pattern = '**/*.{ts,js}') { const files = glob.sync(pattern, { cwd: directory, absolute: true }); let allExports = []; files.forEach(file => { try { const exports = findExportsInFile(file); allExports = [...allExports, ...exports]; } catch (error) { console.error(`Error processing file ${file}:`, error); } }); return allExports; } /** * Finds all exported items (functions, classes, variables, etc.) in a file * @param filePath Path to the file to analyze * @returns Array of exported items */ function findAllExportsInFile(filePath) { // Read the file const fileContent = fs.readFileSync(filePath, 'utf-8'); // Create a SourceFile const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); const exportedItems = []; // Helper function to extract JSDoc comment if available const getJSDocComment = (node) => { const jsDocComments = node.jsDoc || []; if (jsDocComments.length > 0) { return jsDocComments[0].comment; } return undefined; }; // Helper function to get node kind as string const getNodeKindName = (node) => { if (ts.isFunctionDeclaration(node)) return 'function'; if (ts.isClassDeclaration(node)) return 'class'; if (ts.isInterfaceDeclaration(node)) return 'interface'; if (ts.isTypeAliasDeclaration(node)) return 'type'; if (ts.isEnumDeclaration(node)) return 'enum'; if (ts.isVariableDeclaration(node)) { if (node.initializer) { if (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) { return 'function'; } if (ts.isObjectLiteralExpression(node.initializer)) { return 'object'; } } return 'variable'; } return ts.SyntaxKind[node.kind] || 'unknown'; }; // Visit each node in the source file function visit(node) { // Check for export declarations if (ts.isExportDeclaration(node)) { // Handle named exports like: export { foo, bar } if (node.exportClause && ts.isNamedExports(node.exportClause)) { node.exportClause.elements.forEach(element => { const name = element.name.text; const propertyName = element.propertyName?.text || name; // Find the declaration in the file ts.forEachChild(sourceFile, innerNode => { let foundNode; if (ts.isFunctionDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isClassDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isInterfaceDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isTypeAliasDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isEnumDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isVariableStatement(innerNode)) { innerNode.declarationList.declarations.forEach(decl => { if (ts.isIdentifier(decl.name) && decl.name.text === propertyName) { foundNode = decl; } }); } if (foundNode) { exportedItems.push({ name, kind: getNodeKindName(foundNode), filePath, isDefault: false, documentation: getJSDocComment(foundNode) }); } }); }); } } // Check for exported declarations if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) { const isDefault = node.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword); if (ts.isFunctionDeclaration(node) && node.name) { exportedItems.push({ name: node.name.text, kind: 'function', filePath, isDefault, documentation: getJSDocComment(node) }); } else if (ts.isClassDeclaration(node) && node.name) { exportedItems.push({ name: node.name.text, kind: 'class', filePath, isDefault, documentation: getJSDocComment(node) }); } else if (ts.isInterfaceDeclaration(node) && node.name) { exportedItems.push({ name: node.name.text, kind: 'interface', filePath, isDefault, documentation: getJSDocComment(node) }); } else if (ts.isTypeAliasDeclaration(node) && node.name) { exportedItems.push({ name: node.name.text, kind: 'type', filePath, isDefault, documentation: getJSDocComment(node) }); } else if (ts.isEnumDeclaration(node) && node.name) { exportedItems.push({ name: node.name.text, kind: 'enum', filePath, isDefault, documentation: getJSDocComment(node) }); } else if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach(declaration => { if (ts.isIdentifier(declaration.name)) { exportedItems.push({ name: declaration.name.text, kind: getNodeKindName(declaration), filePath, isDefault, documentation: getJSDocComment(node) }); } }); } } // Continue traversing the AST ts.forEachChild(node, visit); } // Start the traversal visit(sourceFile); return exportedItems; } /** * Finds all exported items in a directory * @param directory Directory to search in * @param pattern Glob pattern for files to include (default: **\/*.{ts,js}) * @returns Array of exported items */ function findAllExportsInDirectory(directory, pattern = '**/*.{ts,js}') { const files = glob.sync(pattern, { cwd: directory, absolute: true }); let allExports = []; files.forEach(file => { try { const exports = findAllExportsInFile(file); allExports = [...allExports, ...exports]; } catch (error) { console.error(`Error processing file ${file}:`, error); } }); return allExports; } /** * Helper function to resolve a source path * @param baseDir Base directory to resolve from * @param source Source path to resolve * @returns Resolved path or null if not found */ function resolveSourcePath(baseDir, source) { // Handle node_modules imports if (source.startsWith('.') || source.startsWith('/')) { // Resolve relative paths let resolvedPath = path.resolve(baseDir, source); // Check if the path exists if (fs.existsSync(resolvedPath)) { // If it's a directory, look for an index file const stats = fs.statSync(resolvedPath); if (stats.isDirectory()) { for (const ext of ['.ts', '.tsx', '.js', '.jsx']) { const indexPath = path.join(resolvedPath, `index${ext}`); if (fs.existsSync(indexPath)) { return indexPath; } } return null; // Directory exists but no index file found } return resolvedPath; } // Try adding extensions for (const ext of ['.ts', '.tsx', '.js', '.jsx']) { const pathWithExt = `${resolvedPath}${ext}`; if (fs.existsSync(pathWithExt)) { return pathWithExt; } } } // Could not resolve the path return null; } /** * Helper function to detect re-exports in a file * @param filePath Path to the file to analyze * @returns Array of re-exported items */ function findReExportsInFile(filePath) { const reExports = []; const fileContent = fs.readFileSync(filePath, 'utf-8'); // Create a SourceFile const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); // Visit each node in the source file function visit(node) { // Check for export declarations with module specifiers (re-exports) if (ts.isExportDeclaration(node) && node.moduleSpecifier) { const source = node.moduleSpecifier.getText(sourceFile).replace(/['"]/g, ''); // Handle named exports like: export { foo, bar } from './module' if (node.exportClause && ts.isNamedExports(node.exportClause)) { node.exportClause.elements.forEach(element => { const name = element.name.text; const propertyName = element.propertyName?.text || name; reExports.push({ name: propertyName !== name ? `${propertyName} as ${name}` : name, originalSource: source, filePath }); }); } // Handle wildcard exports like: export * from './module' else if (!node.exportClause) { reExports.push({ name: '*', originalSource: source, filePath }); } } // Continue traversing the AST ts.forEachChild(node, visit); } // Start the traversal visit(sourceFile); return reExports; } /** * Helper function to find direct exports in a file (not re-exports) * @param filePath Path to the file to analyze * @returns Array of directly exported items */ function findDirectExportsInFile(filePath) { const directExports = []; const fileContent = fs.readFileSync(filePath, 'utf-8'); const baseDir = path.dirname(filePath); // Create a SourceFile const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); // Track imported items with their resolved paths const importedItems = new Map(); // Helper function to extract JSDoc comment if available const getJSDocComment = (node) => { const jsDocComments = node.jsDoc || []; if (jsDocComments.length > 0) { return jsDocComments[0].comment; } return undefined; }; // First pass: collect imports function collectImports(node) { if (ts.isImportDeclaration(node)) { const source = node.moduleSpecifier.getText(sourceFile).replace(/['"]/g, ''); const resolvedPath = resolveSourcePath(baseDir, source); if (node.importClause) { if (node.importClause.name) { // Default import importedItems.set(node.importClause.name.text, resolvedPath); } if (node.importClause.namedBindings) { if (ts.isNamedImports(node.importClause.namedBindings)) { // Named imports node.importClause.namedBindings.elements.forEach(element => { importedItems.set(element.name.text, resolvedPath); }); } } } } ts.forEachChild(node, collectImports); } // Visit each node in the source file function visit(node) { // Check for exported declarations (classes, interfaces, functions, etc.) if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) { const isDefault = node.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword); let name = ''; if (ts.isFunctionDeclaration(node) && node.name) { name = node.name.text; } else if (ts.isClassDeclaration(node) && node.name) { name = node.name.text; } else if (ts.isInterfaceDeclaration(node) && node.name) { name = node.name.text; } else if (ts.isTypeAliasDeclaration(node) && node.name) { name = node.name.text; } else if (ts.isEnumDeclaration(node) && node.name) { name = node.name.text; } else if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach(declaration => { if (ts.isIdentifier(declaration.name)) { const name = declaration.name.text; const resolvedPath = importedItems.get(name); directExports.push({ name, filePath: resolvedPath || filePath, isDefault, documentation: getJSDocComment(node) }); } }); return; // Already processed variable declarations } if (name) { const resolvedPath = importedItems.get(name); directExports.push({ name, filePath: resolvedPath || filePath, isDefault, documentation: getJSDocComment(node) }); } } // Check for export declarations without module specifiers (direct exports) if (ts.isExportDeclaration(node) && !node.moduleSpecifier) { if (node.exportClause && ts.isNamedExports(node.exportClause)) { node.exportClause.elements.forEach(element => { const name = element.name.text; const propertyName = element.propertyName?.text || name; let documentation; ts.forEachChild(sourceFile, innerNode => { let foundNode; if (ts.isFunctionDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isClassDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isInterfaceDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isTypeAliasDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isEnumDeclaration(innerNode) && innerNode.name && innerNode.name.text === propertyName) { foundNode = innerNode; } else if (ts.isVariableStatement(innerNode)) { innerNode.declarationList.declarations.forEach(decl => { if (ts.isIdentifier(decl.name) && decl.name.text === propertyName) { foundNode = decl; } }); } if (foundNode) { documentation = getJSDocComment(foundNode); } }); const resolvedPath = importedItems.get(propertyName); directExports.push({ name: propertyName !== name ? `${propertyName} as ${name}` : name, filePath: resolvedPath || filePath, documentation }); }); } } // Continue traversing the AST ts.forEachChild(node, visit); } // First collect imports collectImports(sourceFile); // Then process exports visit(sourceFile); return directExports; } /** * Recursively follow re-export chains to find the original source * @param filePath Path to the file to analyze * @param maxDepth Maximum recursion depth (default: 10) * @param visited Set of already visited files to prevent circular dependencies * @returns Array of nested exported items */ async function findNestedExports(filePath, maxDepth = 10, visited = new Set()) { if (maxDepth <= 0) return []; if (visited.has(filePath)) return []; // Prevent circular dependencies visited.add(filePath); const baseDir = path.dirname(filePath); const reExports = findReExportsInFile(filePath); const directExports = findDirectExportsInFile(filePath); // Add direct exports to the result const result = [...directExports]; // Process each re-export for (const reExport of reExports) { // Handle wildcard exports if (reExport.name === '*') { // For wildcard exports, we need to find all exports from the source file const sourcePath = resolveSourcePath(baseDir, reExport.originalSource); if (sourcePath) { try { // Find all exports in the source file const sourceExports = await findNestedExports(sourcePath, maxDepth - 1, new Set(visited)); // Add them to our results with the original source path sourceExports.forEach(item => { if (!item.originalSource) { item.originalSource = reExport.originalSource; } }); result.push(...sourceExports); } catch (error) { console.error(`Error processing wildcard export from ${reExport.originalSource}: ${error}`); } } continue; } let currentItem = { ...reExport }; // Resolve the source path let sourcePath = resolveSourcePath(baseDir, reExport.originalSource); if (!sourcePath) { // If we can't resolve the path, keep the original result.push(currentItem); continue; } // Follow the re-export chain let depth = 0; let currentVisited = new Set(visited); while (depth < maxDepth) { try { // Check if the source file exists if (!fs.existsSync(sourcePath) || currentVisited.has(sourcePath)) { break; } currentVisited.add(sourcePath); // Find re-exports in the source file const nestedExports = findReExportsInFile(sourcePath); // Look for the current export name in the nested exports const nestedExport = nestedExports.find(e => e.name === currentItem.name); if (!nestedExport) { // If not found, we've reached the original source break; } // Update the current item and continue following the chain currentItem.originalSource = currentItem.originalSource || currentItem.filePath; currentItem.originalName = currentItem.originalName || currentItem.name; currentItem.filePath = nestedExport.filePath; // Resolve the new source path const newBaseDir = path.dirname(sourcePath); sourcePath = resolveSourcePath(newBaseDir, nestedExport.originalSource); if (!sourcePath) { break; } depth++; } catch (error) { console.error(`Error following re-export chain: ${error}`); break; } } // Store the final result result.push(currentItem); } return result; } /** * Finds all nested exports in a directory * @param directory Directory to search in * @param pattern Glob pattern for files to include (default: **\/*.{ts,js}) * @returns Array of nested exported items */ async function findNestedExportsInDirectory(directory, pattern = '**/*.{ts,js}') { const files = glob.sync(pattern, { cwd: directory, absolute: true }); let allExports = []; for (const file of files) { try { const exports = await findNestedExports(file); allExports = [...allExports, ...exports]; } catch (error) { console.error(`Error processing file ${file}:`, error); } } return allExports; } //# sourceMappingURL=export-analyzer.js.map