| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- #!/usr/bin/env bun
- import * as path from "path"
- import * as ts from "typescript"
- const BASE_DIR = "/home/thdxr/dev/projects/anomalyco/kirincode/packages/opencode"
- // Get entry file from command line arg or use default
- const ENTRY_FILE = process.argv[2] || "src/plugin/tui/runtime.ts"
- const visited = new Set<string>()
- function resolveImport(importPath: string, fromFile: string): string | null {
- if (importPath.startsWith("@/")) {
- return path.join(BASE_DIR, "src", importPath.slice(2))
- }
- if (importPath.startsWith("./") || importPath.startsWith("../")) {
- const dir = path.dirname(fromFile)
- return path.resolve(dir, importPath)
- }
- return null
- }
- function isInternalImport(importPath: string): boolean {
- return importPath.startsWith("@/") || importPath.startsWith("./") || importPath.startsWith("../")
- }
- async function tryExtensions(filePath: string): Promise<string | null> {
- const extensions = [".ts", ".tsx", ".js", ".jsx"]
- try {
- const file = Bun.file(filePath)
- const stat = await file.stat()
- if (stat?.isDirectory()) {
- for (const ext of extensions) {
- const indexPath = path.join(filePath, "index" + ext)
- const indexFile = Bun.file(indexPath)
- if (await indexFile.exists()) return indexPath
- }
- return null
- }
- // It's a file
- return filePath
- } catch {
- // Path doesn't exist, try adding extensions
- for (const ext of extensions) {
- const withExt = filePath + ext
- const extFile = Bun.file(withExt)
- if (await extFile.exists()) return withExt
- }
- return null
- }
- }
- function extractImports(sourceFile: ts.SourceFile): string[] {
- const imports: string[] = []
- function visit(node: ts.Node) {
- // import x from "path" or import { x } from "path"
- if (ts.isImportDeclaration(node)) {
- // Skip type-only imports
- if (node.importClause?.isTypeOnly) return
- const moduleSpec = node.moduleSpecifier
- if (ts.isStringLiteral(moduleSpec)) {
- imports.push(moduleSpec.text)
- }
- }
- // export { x } from "path"
- if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
- if (ts.isStringLiteral(node.moduleSpecifier)) {
- imports.push(node.moduleSpecifier.text)
- }
- }
- // Dynamic import: import("path")
- if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
- const arg = node.arguments[0]
- if (arg && ts.isStringLiteral(arg)) {
- imports.push(arg.text)
- }
- }
- ts.forEachChild(node, visit)
- }
- visit(sourceFile)
- return imports
- }
- async function traceFile(filePath: string, depth = 0): Promise<void> {
- const normalizedPath = path.relative(BASE_DIR, filePath)
- if (visited.has(filePath)) {
- return
- }
- // Only trace TypeScript/JavaScript files
- if (!filePath.match(/\.(ts|tsx|js|jsx)$/)) {
- return
- }
- visited.add(filePath)
- console.log("\t".repeat(depth) + normalizedPath)
- let content: string
- try {
- content = await Bun.file(filePath).text()
- } catch {
- return
- }
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true)
- const imports = extractImports(sourceFile)
- const internalImports = imports.filter(isInternalImport)
- const externalImports = imports.filter((imp) => !isInternalImport(imp))
- // Print external imports
- for (const imp of externalImports) {
- console.log("\t".repeat(depth + 1) + `[ext] ${imp}`)
- }
- for (const imp of internalImports) {
- const resolved = resolveImport(imp, filePath)
- if (!resolved) continue
- const actualPath = await tryExtensions(resolved)
- if (!actualPath) continue
- await traceFile(actualPath, depth + 1)
- }
- }
- async function main() {
- const entryPath = path.join(BASE_DIR, ENTRY_FILE)
- // Check if file exists
- const file = Bun.file(entryPath)
- if (!(await file.exists())) {
- console.error(`File not found: ${ENTRY_FILE}`)
- console.error(`Resolved to: ${entryPath}`)
- process.exit(1)
- }
- await traceFile(entryPath)
- }
- main().catch(console.error)
|