Created
September 12, 2024 12:20
-
-
Save adrianbrowning/4bf5e031e39e69e3d3c668b690bfb7f4 to your computer and use it in GitHub Desktop.
Simple ESM JS file import analyser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as acorn from "acorn"; | |
| import fs from "node:fs"; | |
| import path from "node:path"; | |
| const file = '/path/to/js/file.js'; | |
| const allNodes = new Map<string, TreeNode>(); | |
| class TreeNode { | |
| public path: string; | |
| public parent = new Set<TreeNode>(); | |
| public children = new Set<TreeNode>(); | |
| constructor(parent: TreeNode|null, path: string) { | |
| this.path = path; | |
| if (parent) this.parent.add(parent); | |
| parent?.children.add(this); | |
| allNodes.set(path, this); | |
| if(this.path === "") debugger; | |
| } | |
| static createNode(parent: TreeNode|null, path: string) { | |
| const found = allNodes.get(path); | |
| if (found) { | |
| if (parent) { | |
| found.parent.add(parent); | |
| parent.children.add(found); | |
| } | |
| if(found.path === "") debugger; | |
| return found; | |
| } | |
| return new TreeNode(parent, path); | |
| } | |
| } | |
| const base = new TreeNode(null, file); | |
| const seenImports = new Map<string, TreeNode>(); | |
| const toProcessImports:Array<[string, TreeNode]> = [[file, base]]; | |
| while (toProcessImports.length > 0) { | |
| const [currentFile, parentNode] = toProcessImports.shift()!; | |
| if(seenImports.has(currentFile)) { | |
| continue; | |
| } | |
| seenImports.set(currentFile, parentNode); | |
| processBody(getParsedAST(currentFile).body, currentFile, parentNode); | |
| } | |
| debugger; | |
| function processBody(body: Array<acorn.Statement | acorn.ModuleDeclaration>, currentFile:string, treeNode:TreeNode) { | |
| for (const node of body) { | |
| if (node.type !== "ImportDeclaration") continue; | |
| if (typeof node.source.value !== "string") continue; | |
| const importName = node.source.value.startsWith(".") | |
| ? getCurrentFile(node.source.value, currentFile) | |
| : node.source.value; | |
| const childNode = TreeNode.createNode(treeNode, importName); | |
| if (node.source.value.startsWith(".")) { | |
| toProcessImports.push([importName, childNode]); | |
| } | |
| } | |
| } | |
| function getCurrentFile(file: string, base:string) { | |
| return path.normalize(path.join(path.dirname(base || ""),file)) | |
| } | |
| function getParsedAST(file: string) { | |
| const code = fs.readFileSync(file, 'utf8'); | |
| return acorn.parse(code, {sourceType: "module", ecmaVersion: "latest",}); | |
| } | |
| function printTree(node: TreeNode, prefix: string = "", isLast: boolean = true, depth: number = 0, paths = new Set<string>()) { | |
| // Output the current node | |
| const newPrefix = prefix + (isLast ? " " : "│ "); | |
| if (paths.has(node.path)){ | |
| console.log(newPrefix+ "---- Recursion detected -----"); | |
| return; | |
| } | |
| paths.add(node.path); | |
| console.log(`${prefix}${isLast ? "└── " : "├── "}${node.path}`); | |
| if (depth > 10) { | |
| console.log(newPrefix+ "---- Skipping -----"); | |
| return; | |
| } | |
| const childrenArray = Array.from(node.children); | |
| // Recurse over each child | |
| childrenArray.forEach((child, index) => { | |
| printTree(child, newPrefix, index === childrenArray.length - 1, depth++, paths); | |
| }); | |
| } | |
| printTree(base); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment