investigate_esm.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /* eslint-disable */
  2. import { extractImports, findJSAndImports } from './esm-check-migration.mjs'
  3. import path from 'node:path'
  4. import fs from 'node:fs'
  5. const imports = await findJSAndImports(
  6. ['app', 'modules'].map(dir => path.resolve(dir))
  7. )
  8. const entryPoint = fs.existsSync('app.js') ? 'app.js' : 'app.mjs'
  9. imports.set(path.resolve(entryPoint), extractImports(entryPoint))
  10. const moduleImports = new Map()
  11. imports.forEach((deps, module) => {
  12. for (const dep of deps) {
  13. if (!moduleImports.has(dep)) {
  14. moduleImports.set(dep, new Set())
  15. }
  16. moduleImports.set(dep, moduleImports.get(dep).add(module))
  17. }
  18. })
  19. const soloImports = new Map()
  20. moduleImports.forEach((importedBy, module) => {
  21. if (importedBy.size === 1) {
  22. soloImports.set(module, [...importedBy][0])
  23. }
  24. })
  25. console.log(soloImports)
  26. for (const [module, importedBy] of soloImports) {
  27. if (!moduleImports.has(importedBy)) {
  28. console.log(
  29. `${module} is only imported by ${importedBy}, which has no other imports`
  30. )
  31. }
  32. if (soloImports.has(importedBy)) {
  33. console.log(
  34. `${module} is only imported by ${importedBy}, which is only imported by ${soloImports.get(importedBy)}`
  35. )
  36. }
  37. const chains = findDependencyChainsToTarget(imports, module)
  38. const conversionsToMake = chains.reduce((conversions, chain) => {
  39. chain.forEach(dep => {
  40. conversions.add(dep)
  41. })
  42. return conversions
  43. }, new Set())
  44. if (conversionsToMake.length < 10) {
  45. console.log(
  46. `To convert ${module}, would need to convert: ${[...conversionsToMake].length}`
  47. )
  48. }
  49. }
  50. // --- Circular dependency detection ---
  51. function findCircularDependencies(importsMap) {
  52. const cycles = []
  53. const visited = new Set()
  54. const stack = []
  55. function dfs(file, pathStack) {
  56. if (pathStack.includes(file)) {
  57. // Cycle detected
  58. const cycleStart = pathStack.indexOf(file)
  59. cycles.push(pathStack.slice(cycleStart).concat(file))
  60. return
  61. }
  62. if (!importsMap.has(file)) return
  63. pathStack.push(file)
  64. for (const imp of importsMap.get(file)) {
  65. const resolvedImp = path.resolve(imp)
  66. dfs(resolvedImp, pathStack)
  67. }
  68. pathStack.pop()
  69. }
  70. for (const file of importsMap.keys()) {
  71. dfs(file, [])
  72. }
  73. return cycles
  74. }
  75. const cycles = findCircularDependencies(imports)
  76. if (cycles.length > 0) {
  77. console.log('Circular dependencies found:')
  78. for (const cycle of cycles) {
  79. console.log(' ' + cycle.join(' -> '))
  80. }
  81. } else {
  82. console.log('No circular dependencies detected.')
  83. }
  84. // --- Find all chains of dependencies to a target file ---
  85. function findDependencyChainsToTarget(importsMap, targetPath) {
  86. const chains = []
  87. const resolvedTarget = path.resolve(targetPath)
  88. function dfs(current, pathStack) {
  89. if (pathStack.includes(current)) return // avoid cycles
  90. pathStack.push(current)
  91. if (current === resolvedTarget) {
  92. chains.push([...pathStack])
  93. pathStack.pop()
  94. return
  95. }
  96. if (!importsMap.has(current)) {
  97. pathStack.pop()
  98. return
  99. }
  100. for (const imp of importsMap.get(current)) {
  101. const resolvedImp = path.resolve(imp)
  102. dfs(resolvedImp, pathStack)
  103. }
  104. pathStack.pop()
  105. }
  106. for (const file of importsMap.keys()) {
  107. if (file === resolvedTarget) continue // skip self
  108. dfs(file, [])
  109. }
  110. return chains
  111. }
  112. // Example usage: set your target file path here
  113. const targetFile =
  114. '/Users/arumble/Documents/Projects/internal/services/web/app/src/Features/Analytics/AnalyticsManager.js' // <-- change to your target
  115. const chains = findDependencyChainsToTarget(imports, targetFile)
  116. if (chains.length > 0) {
  117. console.log(`Dependency chains leading to ${targetFile}:`)
  118. for (const chain of chains) {
  119. console.log(' ' + chain.join(' -> '))
  120. }
  121. } else {
  122. console.log(`No dependency chains found leading to ${targetFile}.`)
  123. }
  124. const conversionsToMake = chains.reduce((conversions, chain) => {
  125. chain.forEach(dep => {
  126. conversions.add(dep)
  127. })
  128. return conversions
  129. }, new Set())
  130. console.log([...conversionsToMake].join(' '))