pkgviz 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +2 -1
- package/.next/build-manifest.json +15 -24
- package/.next/fallback-build-manifest.json +15 -0
- package/.next/images-manifest.json +14 -3
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +52 -16
- package/.next/required-server-files.js +349 -0
- package/.next/required-server-files.json +106 -75
- package/.next/routes-manifest.json +16 -6
- package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_global-error/page/build-manifest.json +20 -0
- package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
- package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_global-error/page.js +9 -0
- package/.next/server/app/_global-error/page.js.map +5 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +22 -0
- package/.next/server/app/_global-error.rsc +15 -0
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +20 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_not-found/page/build-manifest.json +20 -0
- package/.next/server/app/_not-found/page/next-font-manifest.json +11 -0
- package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_not-found/page.js +13 -2
- package/.next/server/app/_not-found/page.js.map +5 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.meta +19 -0
- package/.next/server/app/_not-found.rsc +18 -19
- package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -0
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +24 -0
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/favicon.ico/route/app-paths-manifest.json +3 -0
- package/.next/server/app/favicon.ico/route/build-manifest.json +12 -0
- package/.next/server/app/favicon.ico/route.js +6 -1
- package/.next/server/app/favicon.ico/route.js.map +5 -0
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/page/app-paths-manifest.json +3 -0
- package/.next/server/app/page/build-manifest.json +20 -0
- package/.next/server/app/page/next-font-manifest.json +11 -0
- package/.next/server/app/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/page/server-reference-manifest.json +93 -0
- package/.next/server/app/page.js +16 -13
- package/.next/server/app/page.js.map +5 -0
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +3 -1
- package/.next/server/app-paths-manifest.json +2 -1
- package/.next/server/chunks/[externals]__0l8ei7u._.js +3 -0
- package/.next/server/chunks/[externals]__0l8ei7u._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__1to-yfd._.js +160 -0
- package/.next/server/chunks/[root-of-the-server]__1to-yfd._.js.map +1 -0
- package/.next/server/chunks/[turbopack]_runtime.js +807 -0
- package/.next/server/chunks/[turbopack]_runtime.js.map +10 -0
- package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_0g2jjls.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_0g2jjls.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0agsrep._.js +28 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0agsrep._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0f_e7c8._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0f_e7c8._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0yqn68e._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0yqn68e._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__112mz-6._.js +14 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__112mz-6._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__11_76b7._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__11_76b7._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__12ovu0c._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__12ovu0c._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__16pmw__._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__16pmw__._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1f2jx51._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1f2jx51._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1rn09vt._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1rn09vt._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__20e4gkp._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__20e4gkp._.js.map +1 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js +807 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +10 -0
- package/.next/server/chunks/ssr/_1acy_2s._.js +158 -0
- package/.next/server/chunks/ssr/_1acy_2s._.js.map +1 -0
- package/.next/server/chunks/ssr/_1rh2tf9._.js +367 -0
- package/.next/server/chunks/ssr/_1rh2tf9._.js.map +1 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0zi5s8-.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0zi5s8-.js.map +1 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0pt47yr.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0pt47yr.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_01xbj_9._.js +33 -0
- package/.next/server/chunks/ssr/node_modules_01xbj_9._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_1c5ky4f._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_1c5ky4f._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js +6 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_1n3w9lb._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0wpq8j3._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0wpq8j3._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0symwr9.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_forbidden_0symwr9.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0q-w892.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0q-w892.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0l_sp0x.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0l_sp0x.js.map +1 -0
- package/.next/server/chunks/ssr/src_app_loading_tsx_01g_-m0._.js +3 -0
- package/.next/server/chunks/ssr/src_app_loading_tsx_01g_-m0._.js.map +1 -0
- package/.next/server/middleware-build-manifest.js +24 -1
- package/.next/server/middleware-manifest.json +2 -2
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +15 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +2 -4
- package/.next/server/prefetch-hints.json +25 -0
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +94 -1
- package/.next/static/chunks/19mx3mg6lkumu.js +1 -0
- package/.next/static/chunks/1las_6j17xa9p.js +1 -0
- package/.next/static/chunks/1xk6snnh6e5_j.css +3 -0
- package/.next/static/chunks/2-rtuqsgzmno4.js +22 -0
- package/.next/static/chunks/27t_qfc-3_lzs.js +1 -0
- package/.next/static/chunks/2sswp23h6zh08.js +345 -0
- package/.next/static/chunks/310vm2bl3xxpt.js +31 -0
- package/.next/static/chunks/3fntmmi971322.js +1 -0
- package/.next/static/chunks/turbopack-1b6uko1cu_h42.js +1 -0
- package/.next/static/media/4fa387ec64143e14-s.2tuy5pz7dlieh.woff2 +0 -0
- package/.next/static/media/53b9e256198e5412-s.390ncx5urfkfu.woff2 +0 -0
- package/.next/static/media/5ce348bf30bf5439-s.31988l_ccedte.woff2 +0 -0
- package/.next/static/media/6306c77e7c8268e4-s.2dbetqa9o8jxf.woff2 +0 -0
- package/.next/static/media/7178b3e590c64307-s.21jp631_3pja2.woff2 +0 -0
- package/.next/static/media/797e433ab948586e-s.p.0r6juujl39pe6.woff2 +0 -0
- package/.next/static/media/7d817b4c03b0c5f1-s.1uyisp29ctx0d.woff2 +0 -0
- package/.next/static/media/8a480f0b521d4e75-s.1qq4vpdcun5oj.woff2 +0 -0
- package/.next/static/media/bbc41e54d2fcbd21-s.1rgnod-3esatf.woff2 +0 -0
- package/.next/static/media/caa3a2e1cccd8315-s.p.0wgildi0cnwt9.woff2 +0 -0
- package/.next/static/media/favicon.2246eezznj2ab.ico +0 -0
- package/.next/static/media/fef07dbb0973bf53-s.3p2_lha1f2xer.woff2 +0 -0
- package/.next/static/rjdzHqzPw2GCNhDbjcu0-/_buildManifest.js +11 -0
- package/.next/static/rjdzHqzPw2GCNhDbjcu0-/_clientMiddlewareManifest.js +1 -0
- package/README.md +176 -4
- package/bin/pkgviz.ts +57 -29
- package/package.json +57 -36
- package/src/app/actions/audit.actions.ts +7 -49
- package/src/app/globals.css +0 -2
- package/src/app/layout.tsx +3 -2
- package/src/app/loading.tsx +8 -0
- package/src/app/page.tsx +16 -2
- package/src/app/utils/buildGraph.ts +99 -100
- package/src/app/utils/getParsedFileStructure.ts +128 -54
- package/src/app/utils/inspectParserLanguageAsync.ts +39 -0
- package/src/app/utils/markCyclicPackages.ts +45 -185
- package/src/app/utils/parser/cpp/parseCppFile.ts +68 -86
- package/src/app/utils/parser/delphi/parseFile.ts +36 -120
- package/src/app/utils/parser/java/findEntryPoint.ts +2 -1
- package/src/app/utils/parser/java/parseJavaFile.ts +33 -45
- package/src/app/utils/parser/kotlin/parseFile.ts +34 -87
- package/src/app/utils/parser/python/parseFile.ts +30 -100
- package/src/app/utils/parser/typescript/parseFile.ts +17 -52
- package/src/app/utils/selectParserLanguage.ts +39 -0
- package/src/cli/formatAuditRuleFailures.ts +21 -0
- package/src/cli/runAuditAsync.ts +41 -0
- package/src/components/Breadcrumb.tsx +5 -4
- package/src/components/ExportPanel.tsx +53 -0
- package/src/components/Header.tsx +1 -0
- package/src/components/Loader.tsx +8 -2
- package/src/components/ProjectLoadError.tsx +15 -0
- package/src/components/SettingsPanel.tsx +184 -0
- package/src/components/Switch.tsx +9 -17
- package/src/components/ThemeToggle.tsx +2 -1
- package/src/components/ToggleSwitch.tsx +36 -0
- package/src/components/sidebar/Sidebar.tsx +14 -0
- package/src/components/sidebar/SidebarBadge.tsx +25 -0
- package/src/components/sidebar/SidebarRow.tsx +14 -0
- package/src/components/sidebar/SidebarSection.tsx +16 -0
- package/src/components/sidebar/SidebarTabs.tsx +58 -0
- package/src/contexts/SettingsContext.tsx +19 -20
- package/src/features/audit/adapters/inbound/react/AuditRuleList.tsx +76 -0
- package/src/features/audit/adapters/inbound/react/AuditRulePanel.tsx +30 -0
- package/src/features/audit/adapters/inbound/react/CycleInspector.tsx +116 -0
- package/src/features/audit/adapters/inbound/react/CyclicDependenciesRuleDetails.tsx +124 -0
- package/src/features/audit/adapters/inbound/react/useCycleSelection.ts +47 -0
- package/src/features/audit/application/use-cases/createAuditFromSnapshot.ts +28 -0
- package/src/features/audit/application/use-cases/resolveSelectedCycleIds.ts +18 -0
- package/src/features/audit/composition/createAuditAsync.ts +11 -0
- package/src/features/audit/domain/evaluateAuditRules.ts +42 -0
- package/src/features/audit/domain/hasBlockingAuditRuleFailure.ts +6 -0
- package/src/features/audit/domain/resolveAuditConfiguration.ts +27 -0
- package/src/features/audit/utils/cycleVisualization.ts +86 -0
- package/src/features/dependency-analysis/adapters/outbound/dependency-graph/analyzeDependencyImportsAsync.ts +172 -0
- package/src/features/dependency-analysis/adapters/outbound/dependency-graph/analyzeTypeScriptImportsAsync.ts +6 -0
- package/src/features/graph-view/adapters/inbound/react/DependencyGraphView.tsx +184 -0
- package/src/features/graph-view/adapters/inbound/react/GraphZoomControls.tsx +48 -0
- package/src/features/graph-view/adapters/inbound/react/applyGraphInteractionPresentation.ts +35 -0
- package/src/features/graph-view/adapters/inbound/react/createGraphViewModel.ts +240 -0
- package/src/features/graph-view/adapters/inbound/react/createGraphViewStyles.ts +37 -0
- package/src/features/graph-view/adapters/inbound/react/useGraphFocus.ts +104 -0
- package/src/features/graph-view/adapters/inbound/react/useGraphInteractions.ts +50 -0
- package/src/features/graph-view/adapters/inbound/react/useGraphProjection.ts +71 -0
- package/src/features/graph-view/adapters/inbound/react/useGraphViewport.ts +48 -0
- package/src/features/graph-view/domain/getDependencyHighlightClasses.ts +39 -0
- package/src/features/graph-view/domain/reduceGraphInteraction.ts +23 -0
- package/src/features/graph-view/utils/createCompoundOpacityIndex.ts +38 -0
- package/src/features/graph-view/utils/projectVisibleGraph.ts +94 -0
- package/src/features/graph-view/utils/readNodeDefinitionId.ts +7 -0
- package/src/features/graph-view/utils/removeEmptyStructuralNodes.ts +27 -0
- package/src/features/project-analysis/adapters/outbound/filesystem/readProjectSnapshotAsync.ts +12 -0
- package/src/features/project-analysis/application/use-cases/createProjectSnapshotReader.ts +29 -0
- package/src/features/project-analysis/composition/loadProjectOverviewAsync.ts +14 -0
- package/src/features/project-analysis/composition/loadProjectSnapshotAsync.ts +12 -0
- package/src/features/project-tree/adapters/inbound/react/ProjectTreePanel.tsx +52 -0
- package/src/features/project-tree/adapters/inbound/react/useProjectTreeExpansion.ts +46 -0
- package/src/features/project-tree/application/use-cases/buildProjectTree.ts +61 -0
- package/src/features/project-tree/application/use-cases/resolveProjectTreeNavigation.ts +13 -0
- package/src/features/project-tree/utils/findProjectTreeNode.ts +14 -0
- package/src/features/project-tree/utils/findProjectTreeNodeByGraphPackage.ts +21 -0
- package/src/features/project-tree/utils/getProjectTreeAncestorIds.ts +26 -0
- package/src/features/settings/utils/readSettingsEnvironment.ts +53 -0
- package/src/i18n/en.ts +21 -2
- package/src/i18n/i18n.ts +2 -3
- package/src/layouts/breadthfirst/style.ts +1 -0
- package/src/layouts/circle/style.ts +1 -0
- package/src/layouts/concentric/style.ts +1 -0
- package/src/layouts/elk/layout.ts +1 -3
- package/src/layouts/elk/style.ts +1 -0
- package/src/layouts/getWeightBuckets.ts +3 -1
- package/src/layouts/grid/style.ts +1 -0
- package/src/layouts/index.ts +0 -4
- package/src/layouts/style.ts +172 -25
- package/src/screens/home/Home.tsx +73 -25
- package/src/screens/home/HomeGraph.tsx +43 -0
- package/src/screens/home/HomeSidebar.tsx +129 -0
- package/src/shared/types/index.ts +0 -9
- package/src/shared/utils/getProjectName.ts +4 -1
- package/src/shared/utils/parseProjectPath.ts +1 -0
- package/src/shared/utils/toPosix.ts +1 -1
- package/src/types/audit.ts +76 -0
- package/src/types/auditVisualization.ts +22 -0
- package/src/types/graphInteraction.ts +9 -0
- package/src/types/parserSelection.ts +11 -0
- package/src/types/projectAnalysis.ts +20 -0
- package/src/types/projectAnalysisActionResult.ts +2 -0
- package/src/types/projectTree.ts +7 -0
- package/src/utils/filter/filterByPackagePrefix.ts +25 -7
- package/src/utils/filter/filterEmptyPackages.ts +28 -29
- package/src/utils/filter/filterSubPackagesFromDepth.ts +20 -17
- package/src/utils/filter/filterVendorPackages.ts +1 -1
- package/src/utils/filter/isIntrinsicGraphNode.ts +8 -0
- package/src/utils/filter/toggleCompoundNodes.ts +41 -31
- package/src/utils/runProjectAnalysisActionAsync.ts +14 -0
- package/tsconfig.json +11 -3
- package/.next/app-build-manifest.json +0 -29
- package/.next/cache/.previewinfo +0 -1
- package/.next/cache/.rscinfo +0 -1
- package/.next/cache/.tsbuildinfo +0 -1
- package/.next/cache/chrome-devtools-workspace-uuid +0 -1
- package/.next/cache/eslint/.cache_6t30dc +0 -1
- package/.next/cache/webpack/client-production/0.pack +0 -4
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/10.pack +0 -0
- package/.next/cache/webpack/client-production/11.pack +0 -0
- package/.next/cache/webpack/client-production/12.pack +0 -0
- package/.next/cache/webpack/client-production/13.pack +0 -0
- package/.next/cache/webpack/client-production/14.pack +0 -0
- package/.next/cache/webpack/client-production/15.pack +0 -0
- package/.next/cache/webpack/client-production/16.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/3.pack +0 -0
- package/.next/cache/webpack/client-production/4.pack +0 -0
- package/.next/cache/webpack/client-production/5.pack +0 -0
- package/.next/cache/webpack/client-production/6.pack +0 -0
- package/.next/cache/webpack/client-production/7.pack +0 -0
- package/.next/cache/webpack/client-production/8.pack +0 -0
- package/.next/cache/webpack/client-production/9.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/1.pack +0 -0
- package/.next/cache/webpack/server-production/10.pack +0 -4
- package/.next/cache/webpack/server-production/11.pack +0 -0
- package/.next/cache/webpack/server-production/12.pack +0 -0
- package/.next/cache/webpack/server-production/13.pack +0 -0
- package/.next/cache/webpack/server-production/14.pack +0 -0
- package/.next/cache/webpack/server-production/15.pack +0 -0
- package/.next/cache/webpack/server-production/16.pack +0 -0
- package/.next/cache/webpack/server-production/17.pack +0 -0
- package/.next/cache/webpack/server-production/18.pack +0 -4
- package/.next/cache/webpack/server-production/2.pack +0 -0
- package/.next/cache/webpack/server-production/3.pack +0 -0
- package/.next/cache/webpack/server-production/4.pack +0 -0
- package/.next/cache/webpack/server-production/5.pack +0 -0
- package/.next/cache/webpack/server-production/6.pack +0 -0
- package/.next/cache/webpack/server-production/7.pack +0 -0
- package/.next/cache/webpack/server-production/8.pack +0 -0
- package/.next/cache/webpack/server-production/9.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/.next/diagnostics/build-diagnostics.json +0 -6
- package/.next/diagnostics/framework.json +0 -1
- package/.next/server/app/index.html +0 -1
- package/.next/server/app/index.meta +0 -7
- package/.next/server/app/index.rsc +0 -20
- package/.next/server/chunks/548.js +0 -6
- package/.next/server/chunks/610.js +0 -7
- package/.next/server/chunks/985.js +0 -22
- package/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/.next/server/pages/_app.js +0 -1
- package/.next/server/pages/_app.js.nft.json +0 -1
- package/.next/server/pages/_document.js +0 -1
- package/.next/server/pages/_document.js.nft.json +0 -1
- package/.next/server/pages/_error.js +0 -19
- package/.next/server/pages/_error.js.nft.json +0 -1
- package/.next/server/webpack-runtime.js +0 -1
- package/.next/static/_YC2p40ZiYT2Gcw-h_tjC/_buildManifest.js +0 -1
- package/.next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js +0 -1
- package/.next/static/chunks/680-55ad17895c8fb70b.js +0 -1
- package/.next/static/chunks/90542734-29e255269788f1bd.js +0 -1
- package/.next/static/chunks/91dbc596-b9cdccd3f5cba95d.js +0 -1
- package/.next/static/chunks/964-b440f7af22c41ca5.js +0 -1
- package/.next/static/chunks/app/_not-found/page-11e4fea8e290f304.js +0 -1
- package/.next/static/chunks/app/layout-8aae7fba5ed8afb3.js +0 -1
- package/.next/static/chunks/app/page-91450b1230da8bca.js +0 -1
- package/.next/static/chunks/framework-bc35dade6ff9684e.js +0 -1
- package/.next/static/chunks/main-9e86d333c1e02dd1.js +0 -1
- package/.next/static/chunks/main-app-69a9ff75ff3465ca.js +0 -1
- package/.next/static/chunks/pages/_app-0a0020ddd67f79cf.js +0 -1
- package/.next/static/chunks/pages/_error-03529f2c21436739.js +0 -1
- package/.next/static/chunks/webpack-7adfafc806738767.js +0 -1
- package/.next/static/css/ec31779168107a11.css +0 -3
- package/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/.next/trace +0 -2
- package/.next/types/app/layout.ts +0 -84
- package/.next/types/app/page.ts +0 -84
- package/.next/types/cache-life.d.ts +0 -141
- package/.next/types/package.json +0 -1
- package/src/app/actions/graph.actions.ts +0 -25
- package/src/app/utils/parser/cpp/extractCppPackageFromImport.ts +0 -18
- package/src/app/utils/parser/delphi/extractPackageFromImport.ts +0 -21
- package/src/app/utils/parser/java/extractJavaPackageFromImport.ts +0 -39
- package/src/app/utils/parser/java/getIntrinsicPackagesRecursive.ts +0 -33
- package/src/app/utils/parser/kotlin/extractPackageFromImport.ts +0 -19
- package/src/app/utils/parser/python/extractPythonPackageFromImport.ts +0 -18
- package/src/app/utils/parser/typescript/extractTypeScriptPackageFromImport.ts +0 -18
- package/src/components/Cytoscape.tsx +0 -23
- package/src/components/Setting.tsx +0 -17
- package/src/components/Settings.tsx +0 -189
- package/src/components/ZoomInput.tsx +0 -94
- package/src/components/useCytoscape.ts +0 -343
- package/src/layouts/getLayoutStyle.ts +0 -19
- package/src/shared/utils/detectLanguage.ts +0 -255
- package/src/shared/utils/getJsonAsync.ts +0 -13
- package/src/shared/utils/parseEnv.ts +0 -91
- package/src/store/useLocalStorage.ts +0 -29
- package/src/utils/hasChildren.ts +0 -7
- /package/.next/{react-loadable-manifest.json → server/app/_global-error/page/react-loadable-manifest.json} +0 -0
- /package/.next/static/chunks/{polyfills-42372ed130431b0a.js → 0cz1d0mv5g_q7.js} +0 -0
- /package/.next/static/{_YC2p40ZiYT2Gcw-h_tjC → rjdzHqzPw2GCNhDbjcu0-}/_ssgManifest.js +0 -0
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
'use server';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
5
3
|
import path from 'node:path';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
readDirectoryWithinRoot,
|
|
7
|
+
resolveFileSystemPathWithinRoot,
|
|
8
|
+
} from '@ankhorage/utility/node/fs';
|
|
9
|
+
|
|
10
10
|
import { parseCppFile } from '@/app/utils/parser/cpp/parseCppFile';
|
|
11
|
-
import { parsePythonFile } from '@/app/utils/parser/python/parseFile';
|
|
12
11
|
import { parseDelphiFile } from '@/app/utils/parser/delphi/parseFile';
|
|
12
|
+
import { parseJavaFile } from '@/app/utils/parser/java/parseJavaFile';
|
|
13
13
|
import { parseKotlinFile } from '@/app/utils/parser/kotlin/parseFile';
|
|
14
|
-
import {
|
|
14
|
+
import { parsePythonFile } from '@/app/utils/parser/python/parseFile';
|
|
15
|
+
import { parseFile as parseTypeScriptFile } from '@/app/utils/parser/typescript/parseFile';
|
|
16
|
+
import { analyzeDependencyImportsAsync } from '@/features/dependency-analysis/adapters/outbound/dependency-graph/analyzeDependencyImportsAsync';
|
|
15
17
|
import { JAVA_ROOT } from '@/shared/constants';
|
|
18
|
+
import { type ImportDefinition, Language, type ParsedDirectory } from '@/shared/types';
|
|
19
|
+
import { parseProjectPath } from '@/shared/utils/parseProjectPath';
|
|
20
|
+
import { toPosix } from '@/shared/utils/toPosix';
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
import { inspectParserLanguageAsync } from './inspectParserLanguageAsync';
|
|
23
|
+
|
|
24
|
+
/***
|
|
18
25
|
* Returns resolved root
|
|
19
26
|
*/
|
|
20
|
-
|
|
27
|
+
async function resolveRoot(dir: string, detectedLanguage: Language) {
|
|
28
|
+
const projectRoot = resolveFileSystemPathWithinRoot(dir, '.', { allowRoot: true });
|
|
29
|
+
|
|
21
30
|
switch (detectedLanguage) {
|
|
22
31
|
case Language.Java: {
|
|
23
|
-
const javaRoot = toPosix(
|
|
32
|
+
const javaRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, JAVA_ROOT));
|
|
24
33
|
if (!existsSync(javaRoot)) {
|
|
25
34
|
console.error('Failed to find:', JAVA_ROOT);
|
|
26
35
|
throw new Error(`Invalid Java project structure. Missing ${JAVA_ROOT}`);
|
|
@@ -30,93 +39,91 @@ export async function resolveRoot(dir: string, detectedLanguage: Language) {
|
|
|
30
39
|
|
|
31
40
|
case Language.TypeScript:
|
|
32
41
|
// Normalize to an absolute project root
|
|
33
|
-
return toPosix(
|
|
42
|
+
return toPosix(projectRoot);
|
|
34
43
|
|
|
35
44
|
case Language.Cpp:
|
|
36
45
|
// For C++, look for src directory or use project root
|
|
37
|
-
const cppSrcRoot = toPosix(
|
|
46
|
+
const cppSrcRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'src'));
|
|
38
47
|
if (existsSync(cppSrcRoot)) {
|
|
39
48
|
return cppSrcRoot;
|
|
40
49
|
}
|
|
41
|
-
return toPosix(
|
|
50
|
+
return toPosix(projectRoot);
|
|
42
51
|
|
|
43
52
|
case Language.Python:
|
|
44
53
|
// For Python, look for src directory or use project root
|
|
45
|
-
const pythonSrcRoot = toPosix(
|
|
54
|
+
const pythonSrcRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'src'));
|
|
46
55
|
if (existsSync(pythonSrcRoot)) {
|
|
47
56
|
return pythonSrcRoot;
|
|
48
57
|
}
|
|
49
58
|
// Also check for common Python app structure
|
|
50
|
-
const appRoot = toPosix(
|
|
59
|
+
const appRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'app'));
|
|
51
60
|
if (existsSync(appRoot)) {
|
|
52
61
|
return appRoot;
|
|
53
62
|
}
|
|
54
|
-
return toPosix(
|
|
63
|
+
return toPosix(projectRoot);
|
|
55
64
|
|
|
56
65
|
case Language.Delphi:
|
|
57
66
|
// For Delphi, look for common source directories
|
|
58
|
-
const delphiSrcRoot = toPosix(
|
|
67
|
+
const delphiSrcRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'src'));
|
|
59
68
|
if (existsSync(delphiSrcRoot)) {
|
|
60
69
|
return delphiSrcRoot;
|
|
61
70
|
}
|
|
62
71
|
// Also check for Source directory (common in Delphi projects)
|
|
63
|
-
const sourceRoot = toPosix(
|
|
72
|
+
const sourceRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'Source'));
|
|
64
73
|
if (existsSync(sourceRoot)) {
|
|
65
74
|
return sourceRoot;
|
|
66
75
|
}
|
|
67
|
-
return toPosix(
|
|
76
|
+
return toPosix(projectRoot);
|
|
68
77
|
|
|
69
78
|
case Language.Kotlin:
|
|
70
79
|
// For Kotlin, look for src/main/kotlin directory (Gradle/Maven structure)
|
|
71
|
-
const kotlinSrcRoot = toPosix(
|
|
80
|
+
const kotlinSrcRoot = toPosix(
|
|
81
|
+
resolveFileSystemPathWithinRoot(projectRoot, 'src/main/kotlin')
|
|
82
|
+
);
|
|
72
83
|
if (existsSync(kotlinSrcRoot)) {
|
|
73
84
|
return kotlinSrcRoot;
|
|
74
85
|
}
|
|
75
86
|
// Fallback to src directory
|
|
76
|
-
const kotlinAltSrcRoot = toPosix(
|
|
87
|
+
const kotlinAltSrcRoot = toPosix(resolveFileSystemPathWithinRoot(projectRoot, 'src'));
|
|
77
88
|
if (existsSync(kotlinAltSrcRoot)) {
|
|
78
89
|
return kotlinAltSrcRoot;
|
|
79
90
|
}
|
|
80
|
-
return toPosix(
|
|
91
|
+
return toPosix(projectRoot);
|
|
81
92
|
|
|
82
93
|
default:
|
|
83
94
|
throw new Error(`Invalid file structure for ${detectedLanguage}`);
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
/***
|
|
88
99
|
* Read directory recursively
|
|
89
100
|
*/
|
|
90
|
-
|
|
101
|
+
async function readDirRecursively(
|
|
91
102
|
dir: string,
|
|
92
|
-
result: ParsedDirectory = {},
|
|
93
103
|
projectRoot: string,
|
|
94
|
-
language: Language
|
|
104
|
+
language: Language,
|
|
105
|
+
analysisRoot: string,
|
|
106
|
+
importsByFile?: ReadonlyMap<string, readonly ImportDefinition[]>
|
|
95
107
|
): Promise<ParsedDirectory> {
|
|
96
|
-
const
|
|
97
|
-
const resolvedDir = path.resolve(dir);
|
|
108
|
+
const result: ParsedDirectory = Object.create(null);
|
|
98
109
|
|
|
99
|
-
//
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 1. Read current directory
|
|
106
|
-
const entries = readdirSync(resolvedDir, { withFileTypes: true });
|
|
110
|
+
// 1. Read the current directory through the shared rooted-filesystem boundary.
|
|
111
|
+
const { entries, path: resolvedDir } = readDirectoryWithinRoot({
|
|
112
|
+
rootPath: projectRoot,
|
|
113
|
+
directoryPath: dir,
|
|
114
|
+
});
|
|
107
115
|
|
|
108
116
|
const ignores = [
|
|
109
117
|
'@types',
|
|
110
118
|
'.cache',
|
|
111
119
|
'.git',
|
|
112
120
|
'.github',
|
|
113
|
-
'.
|
|
121
|
+
'.generated',
|
|
114
122
|
'.next',
|
|
115
|
-
'
|
|
123
|
+
'dist',
|
|
116
124
|
'coverage',
|
|
117
125
|
'examples',
|
|
118
126
|
'node_modules',
|
|
119
|
-
'packages',
|
|
120
127
|
'test',
|
|
121
128
|
];
|
|
122
129
|
|
|
@@ -127,7 +134,13 @@ export async function readDirRecursively(
|
|
|
127
134
|
|
|
128
135
|
// Directory: Recursively continue to read
|
|
129
136
|
if (entry.isDirectory()) {
|
|
130
|
-
result[entry.name] = await readDirRecursively(
|
|
137
|
+
result[entry.name] = await readDirRecursively(
|
|
138
|
+
fullPath,
|
|
139
|
+
projectRoot,
|
|
140
|
+
language,
|
|
141
|
+
analysisRoot,
|
|
142
|
+
importsByFile
|
|
143
|
+
);
|
|
131
144
|
continue;
|
|
132
145
|
}
|
|
133
146
|
|
|
@@ -136,14 +149,24 @@ export async function readDirRecursively(
|
|
|
136
149
|
// Java
|
|
137
150
|
case Language.Java:
|
|
138
151
|
if (entry.name.endsWith('.java')) {
|
|
139
|
-
|
|
152
|
+
if (importsByFile === undefined) {
|
|
153
|
+
throw new Error('Missing canonical Java dependency analysis.');
|
|
154
|
+
}
|
|
155
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
156
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
157
|
+
result[entry.name] = await parseJavaFile(fullPath, projectRoot, imports);
|
|
140
158
|
}
|
|
141
159
|
break;
|
|
142
160
|
|
|
143
161
|
// TypeScript
|
|
144
162
|
case Language.TypeScript:
|
|
145
163
|
if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {
|
|
146
|
-
|
|
164
|
+
if (importsByFile === undefined) {
|
|
165
|
+
throw new Error('Missing canonical TypeScript dependency analysis.');
|
|
166
|
+
}
|
|
167
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
168
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
169
|
+
result[entry.name] = await parseTypeScriptFile(fullPath, projectRoot, imports);
|
|
147
170
|
}
|
|
148
171
|
break;
|
|
149
172
|
|
|
@@ -157,14 +180,24 @@ export async function readDirRecursively(
|
|
|
157
180
|
entry.name.endsWith('.hpp') ||
|
|
158
181
|
entry.name.endsWith('.hxx')
|
|
159
182
|
) {
|
|
160
|
-
|
|
183
|
+
if (importsByFile === undefined) {
|
|
184
|
+
throw new Error('Missing canonical C++ dependency analysis.');
|
|
185
|
+
}
|
|
186
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
187
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
188
|
+
result[entry.name] = await parseCppFile(fullPath, projectRoot, imports);
|
|
161
189
|
}
|
|
162
190
|
break;
|
|
163
191
|
|
|
164
192
|
// Python
|
|
165
193
|
case Language.Python:
|
|
166
194
|
if (entry.name.endsWith('.py')) {
|
|
167
|
-
|
|
195
|
+
if (importsByFile === undefined) {
|
|
196
|
+
throw new Error('Missing canonical Python dependency analysis.');
|
|
197
|
+
}
|
|
198
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
199
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
200
|
+
result[entry.name] = await parsePythonFile(fullPath, projectRoot, imports);
|
|
168
201
|
}
|
|
169
202
|
break;
|
|
170
203
|
|
|
@@ -175,14 +208,24 @@ export async function readDirRecursively(
|
|
|
175
208
|
entry.name.endsWith('.pp') ||
|
|
176
209
|
entry.name.endsWith('.dpr')
|
|
177
210
|
) {
|
|
178
|
-
|
|
211
|
+
if (importsByFile === undefined) {
|
|
212
|
+
throw new Error('Missing canonical Delphi dependency analysis.');
|
|
213
|
+
}
|
|
214
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
215
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
216
|
+
result[entry.name] = await parseDelphiFile(fullPath, projectRoot, imports);
|
|
179
217
|
}
|
|
180
218
|
break;
|
|
181
219
|
|
|
182
220
|
// Kotlin
|
|
183
221
|
case Language.Kotlin:
|
|
184
222
|
if (entry.name.endsWith('.kt') || entry.name.endsWith('.kts')) {
|
|
185
|
-
|
|
223
|
+
if (importsByFile === undefined) {
|
|
224
|
+
throw new Error('Missing canonical Kotlin dependency analysis.');
|
|
225
|
+
}
|
|
226
|
+
const relativeFile = toPosix(path.relative(projectRoot, fullPath));
|
|
227
|
+
const imports = importsByFile.get(relativeFile) ?? [];
|
|
228
|
+
result[entry.name] = await parseKotlinFile(fullPath, projectRoot, imports);
|
|
186
229
|
}
|
|
187
230
|
break;
|
|
188
231
|
}
|
|
@@ -191,14 +234,15 @@ export async function readDirRecursively(
|
|
|
191
234
|
return result;
|
|
192
235
|
}
|
|
193
236
|
|
|
194
|
-
|
|
237
|
+
/***
|
|
195
238
|
* Entrypoint
|
|
196
239
|
*/
|
|
197
|
-
export async function getParsedFileStructure(
|
|
198
|
-
|
|
199
|
-
|
|
240
|
+
export async function getParsedFileStructure(
|
|
241
|
+
language?: Language,
|
|
242
|
+
projectPath: string = parseProjectPath()
|
|
243
|
+
) {
|
|
200
244
|
// 1. Detect language & filter non-supported
|
|
201
|
-
const detectedLanguage = (await
|
|
245
|
+
const detectedLanguage = language ?? (await inspectParserLanguageAsync(projectPath)).language;
|
|
202
246
|
console.log('1. Detected language:', detectedLanguage);
|
|
203
247
|
|
|
204
248
|
if (
|
|
@@ -220,6 +264,36 @@ export async function getParsedFileStructure() {
|
|
|
220
264
|
const rootDir = await resolveRoot(projectPath, detectedLanguage);
|
|
221
265
|
console.log('2. rootDir:', rootDir);
|
|
222
266
|
|
|
267
|
+
const importsByFile =
|
|
268
|
+
detectedLanguage === Language.TypeScript
|
|
269
|
+
? await analyzeDependencyImportsAsync(projectPath)
|
|
270
|
+
: detectedLanguage === Language.Java
|
|
271
|
+
? await analyzeDependencyImportsAsync(projectPath, rootDir, 'specifier')
|
|
272
|
+
: detectedLanguage === Language.Kotlin
|
|
273
|
+
? await analyzeDependencyImportsAsync(
|
|
274
|
+
projectPath,
|
|
275
|
+
rootDir,
|
|
276
|
+
'specifier',
|
|
277
|
+
'kotlin-standard-library'
|
|
278
|
+
)
|
|
279
|
+
: detectedLanguage === Language.Python
|
|
280
|
+
? await analyzeDependencyImportsAsync(
|
|
281
|
+
projectPath,
|
|
282
|
+
rootDir,
|
|
283
|
+
'specifier',
|
|
284
|
+
'python-legacy'
|
|
285
|
+
)
|
|
286
|
+
: detectedLanguage === Language.Cpp
|
|
287
|
+
? await analyzeDependencyImportsAsync(projectPath, rootDir, 'specifier')
|
|
288
|
+
: detectedLanguage === Language.Delphi
|
|
289
|
+
? await analyzeDependencyImportsAsync(
|
|
290
|
+
projectPath,
|
|
291
|
+
rootDir,
|
|
292
|
+
'specifier',
|
|
293
|
+
'delphi-standard-library'
|
|
294
|
+
)
|
|
295
|
+
: undefined;
|
|
296
|
+
|
|
223
297
|
// 3. Read directory recursively (pass resolved root as both dir and projectRoot)
|
|
224
|
-
return await readDirRecursively(rootDir,
|
|
298
|
+
return await readDirRecursively(rootDir, rootDir, detectedLanguage, projectPath, importsByFile);
|
|
225
299
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { inspectProjectAsync } from '@ankhorage/project-detector/node';
|
|
2
|
+
|
|
3
|
+
import type { ParserSelection } from '../../types/parserSelection';
|
|
4
|
+
import { selectParserLanguage } from './selectParserLanguage';
|
|
5
|
+
|
|
6
|
+
const PROJECT_PATH_ERROR_CODES: ReadonlySet<string> = new Set(['EACCES', 'ENOENT', 'ENOTDIR']);
|
|
7
|
+
|
|
8
|
+
/*** Inspect safely through the canonical detector, then apply pkgviz's parser selection policy. */
|
|
9
|
+
export async function inspectParserLanguageAsync(projectPath: string): Promise<ParserSelection> {
|
|
10
|
+
try {
|
|
11
|
+
const inspection = await inspectProjectAsync(projectPath, {
|
|
12
|
+
excludeDirectories: ['@types', '.github', 'examples', 'test'],
|
|
13
|
+
});
|
|
14
|
+
if (!inspection.complete) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Project inspection is incomplete: ${inspection.diagnostics.map(diagnostic => diagnostic.message).join('; ')}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return selectParserLanguage(inspection.detection);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (!isProjectPathUnavailableError(error)) throw error;
|
|
22
|
+
|
|
23
|
+
const projectPathError = new Error(`Invalid or unavailable project path: ${projectPath}`, {
|
|
24
|
+
cause: error,
|
|
25
|
+
});
|
|
26
|
+
projectPathError.name = 'ProjectPathUnavailableError';
|
|
27
|
+
throw projectPathError;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*** Identify filesystem errors that mean the configured inspection root cannot be used. */
|
|
32
|
+
function isProjectPathUnavailableError(error: unknown): boolean {
|
|
33
|
+
return (
|
|
34
|
+
error instanceof Error &&
|
|
35
|
+
'code' in error &&
|
|
36
|
+
typeof error.code === 'string' &&
|
|
37
|
+
PROJECT_PATH_ERROR_CODES.has(error.code)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { findCyclePath, findCyclicComponents, type Graph } from '@ankhorage/graph';
|
|
1
2
|
import type { ElementsDefinition } from 'cytoscape';
|
|
3
|
+
|
|
2
4
|
import type { ParsedDirectory, ParsedFile } from '@/shared/types';
|
|
5
|
+
import type { CycleEdgeEvidence, ImportEvidence, PackageCycleDetail } from '@/types/audit';
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
/*** Collect all files from your ParsedDirectory tree. */
|
|
5
8
|
function collectFiles(root: ParsedDirectory): ParsedFile[] {
|
|
6
9
|
const files: ParsedFile[] = [];
|
|
10
|
+
/*** Collects parsed files by traversing the parsed directory tree. */
|
|
7
11
|
const walk = (dir: ParsedDirectory) => {
|
|
8
12
|
for (const key in dir) {
|
|
9
13
|
const entry = (dir as Record<string, unknown>)[key];
|
|
@@ -18,15 +22,15 @@ function collectFiles(root: ParsedDirectory): ParsedFile[] {
|
|
|
18
22
|
return files;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
/*** Build per-edge evidence: key "from->to" → list of ImportEvidence. */
|
|
22
26
|
function buildEdgeEvidence(dir: ParsedDirectory): Map<string, ImportEvidence[]> {
|
|
23
27
|
const files = collectFiles(dir);
|
|
24
28
|
const map = new Map<string, ImportEvidence[]>();
|
|
25
29
|
|
|
26
30
|
for (const f of files) {
|
|
27
|
-
const from = f.package
|
|
31
|
+
const from = f.package;
|
|
28
32
|
for (const imp of f.imports ?? []) {
|
|
29
|
-
const to = imp.pkg
|
|
33
|
+
const to = imp.pkg;
|
|
30
34
|
if (!to || to === from) continue;
|
|
31
35
|
const key = `${from}->${to}`;
|
|
32
36
|
if (!map.has(key)) map.set(key, []);
|
|
@@ -41,16 +45,16 @@ function buildEdgeEvidence(dir: ParsedDirectory): Map<string, ImportEvidence[]>
|
|
|
41
45
|
return map;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
/*** Convert ElementsDefinition (from buildGraph) to adjacency map. */
|
|
45
49
|
function elementsToAdj(elements: ElementsDefinition) {
|
|
46
50
|
const adj = new Map<TUniquePackageName, Set<TUniquePackageName>>();
|
|
47
51
|
for (const n of elements.nodes) {
|
|
48
|
-
const id = String(n.data.id)
|
|
52
|
+
const id = String(n.data.id);
|
|
49
53
|
if (!adj.has(id)) adj.set(id, new Set());
|
|
50
54
|
}
|
|
51
55
|
for (const e of elements.edges) {
|
|
52
|
-
const s = String(e.data.source)
|
|
53
|
-
const t = String(e.data.target)
|
|
56
|
+
const s = String(e.data.source);
|
|
57
|
+
const t = String(e.data.target);
|
|
54
58
|
if (!adj.has(s)) adj.set(s, new Set());
|
|
55
59
|
if (!adj.has(t)) adj.set(t, new Set());
|
|
56
60
|
adj.get(s)!.add(t);
|
|
@@ -58,79 +62,24 @@ function elementsToAdj(elements: ElementsDefinition) {
|
|
|
58
62
|
return adj;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
stack.push(v);
|
|
77
|
-
onStack.add(v);
|
|
78
|
-
|
|
79
|
-
for (const w of graph.get(v) ?? []) {
|
|
80
|
-
if (!idx.has(w)) {
|
|
81
|
-
strong(w);
|
|
82
|
-
low.set(v, Math.min(low.get(v)!, low.get(w)!));
|
|
83
|
-
} else if (onStack.has(w)) {
|
|
84
|
-
low.set(v, Math.min(low.get(v)!, idx.get(w)!));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (low.get(v) === idx.get(v)) {
|
|
89
|
-
const comp: TUniquePackageName[] = [];
|
|
90
|
-
let w: TUniquePackageName;
|
|
91
|
-
do {
|
|
92
|
-
w = stack.pop()!;
|
|
93
|
-
onStack.delete(w);
|
|
94
|
-
comp.push(w);
|
|
95
|
-
} while (w !== v);
|
|
96
|
-
out.push(comp);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
for (const v of graph.keys()) if (!idx.has(v)) strong(v);
|
|
101
|
-
return out;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Find one simple cycle ordering inside a given SCC. */
|
|
105
|
-
function findOneCycleInScc(
|
|
106
|
-
graph: Map<TUniquePackageName, Set<TUniquePackageName>>,
|
|
107
|
-
sccSet: Set<TUniquePackageName>
|
|
108
|
-
): TUniquePackageName[] | null {
|
|
109
|
-
const nodes = Array.from(sccSet);
|
|
110
|
-
for (const start of nodes) {
|
|
111
|
-
const path: TUniquePackageName[] = [];
|
|
112
|
-
const seen = new Set<TUniquePackageName>();
|
|
113
|
-
function dfs(v: TUniquePackageName): TUniquePackageName[] | null {
|
|
114
|
-
path.push(v);
|
|
115
|
-
seen.add(v);
|
|
116
|
-
for (const w of graph.get(v) ?? []) {
|
|
117
|
-
if (!sccSet.has(w)) continue;
|
|
118
|
-
if (w === start && path.length > 1) return [...path, start];
|
|
119
|
-
if (!seen.has(w)) {
|
|
120
|
-
const r = dfs(w);
|
|
121
|
-
if (r) return r;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
path.pop();
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
const cyc = dfs(start);
|
|
128
|
-
if (cyc) return cyc;
|
|
129
|
-
}
|
|
130
|
-
return null;
|
|
65
|
+
/*** Convert adjacency to the canonical Ankhorage graph model for shared algorithms. */
|
|
66
|
+
function adjacencyToGraph(
|
|
67
|
+
adjacency: ReadonlyMap<TUniquePackageName, ReadonlySet<TUniquePackageName>>
|
|
68
|
+
): Graph<undefined, undefined> {
|
|
69
|
+
return {
|
|
70
|
+
nodes: Array.from(adjacency.keys(), id => ({ id, data: undefined })),
|
|
71
|
+
edges: Array.from(adjacency.entries()).flatMap(([source, targets]) =>
|
|
72
|
+
Array.from(targets, target => ({
|
|
73
|
+
id: `${source}->${target}`,
|
|
74
|
+
source,
|
|
75
|
+
target,
|
|
76
|
+
data: undefined,
|
|
77
|
+
}))
|
|
78
|
+
),
|
|
79
|
+
};
|
|
131
80
|
}
|
|
132
81
|
|
|
133
|
-
|
|
82
|
+
/***
|
|
134
83
|
* High-level API: build graph via `buildGraph`, detect package cycles,
|
|
135
84
|
* and attach **member evidence** (files/imports) per cycle edge.
|
|
136
85
|
*/
|
|
@@ -143,107 +92,36 @@ export function getPackageCyclesWithMembers(
|
|
|
143
92
|
graph: ElementsDefinition; // for convenience (already built)
|
|
144
93
|
} {
|
|
145
94
|
const adj = elementsToAdj(graph);
|
|
146
|
-
const
|
|
95
|
+
const canonicalGraph = adjacencyToGraph(adj);
|
|
96
|
+
const sccs = findCyclicComponents(canonicalGraph);
|
|
147
97
|
const evidence = buildEdgeEvidence(dir);
|
|
148
98
|
|
|
149
99
|
const cycles: PackageCycleDetail[] = [];
|
|
150
100
|
const packageSet = new Set<TUniquePackageName>();
|
|
151
101
|
|
|
152
102
|
for (const scc of sccs) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
scc.forEach(p => packageSet.add(p));
|
|
156
|
-
const sccSet = new Set(scc);
|
|
157
|
-
const cycle = findOneCycleInScc(adj, sccSet) ?? [...scc, scc[0]];
|
|
103
|
+
scc.forEach(p => packageSet.add(p));
|
|
104
|
+
const cycle = [...(findCyclePath(canonicalGraph, scc) ?? [...scc, scc[0]])];
|
|
158
105
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
cycles.push({ packages: cycle, edges });
|
|
106
|
+
const edges: CycleEdgeEvidence[] = [];
|
|
107
|
+
for (let i = 0; i < cycle.length - 1; i++) {
|
|
108
|
+
const from = cycle[i];
|
|
109
|
+
const to = cycle[i + 1];
|
|
110
|
+
const key = `${from}->${to}`;
|
|
111
|
+
edges.push({
|
|
112
|
+
from,
|
|
113
|
+
to,
|
|
114
|
+
via: evidence.get(key) ?? [],
|
|
115
|
+
});
|
|
172
116
|
}
|
|
173
|
-
}
|
|
174
117
|
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Annotate Cytoscape nodes:
|
|
180
|
-
* - adds class "packageCycle" for cyclic packages
|
|
181
|
-
* - sets data.packageCycle (boolean)
|
|
182
|
-
* - sets data.cycleEvidence (edges where this pkg is the "from" side)
|
|
183
|
-
*/
|
|
184
|
-
export function markCyclicPackagesWithEvidence(
|
|
185
|
-
elements: ElementsDefinition,
|
|
186
|
-
dir: ParsedDirectory
|
|
187
|
-
): ElementsDefinition {
|
|
188
|
-
const { cycles, packageSet } = getPackageCyclesWithMembers(dir, elements);
|
|
189
|
-
|
|
190
|
-
// pkg → list of outgoing cycle edges (evidence)
|
|
191
|
-
const pkgToEdges = new Map<TUniquePackageName, CycleEdgeEvidence[]>();
|
|
192
|
-
|
|
193
|
-
// Track cycle edges by (from → to) for quick lookup when mapping Cytoscape edges
|
|
194
|
-
const cycleEdgeKeys = new Set<string>();
|
|
195
|
-
|
|
196
|
-
for (const c of cycles) {
|
|
197
|
-
for (const e of c.edges) {
|
|
198
|
-
if (!pkgToEdges.has(e.from)) pkgToEdges.set(e.from, []);
|
|
199
|
-
pkgToEdges.get(e.from)!.push(e);
|
|
200
|
-
cycleEdgeKeys.add(`${e.from}→${e.to}`);
|
|
201
|
-
}
|
|
118
|
+
cycles.push({ packages: cycle, edges });
|
|
202
119
|
}
|
|
203
120
|
|
|
204
|
-
|
|
205
|
-
const id = String(n.data.id) as TUniquePackageName;
|
|
206
|
-
const isCyclic = packageSet.has(id);
|
|
207
|
-
const existing = n.classes ? String(n.classes) : '';
|
|
208
|
-
const classes = isCyclic ? (existing ? `${existing} packageCycle` : 'packageCycle') : existing;
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
...n,
|
|
212
|
-
classes,
|
|
213
|
-
data: {
|
|
214
|
-
...n.data,
|
|
215
|
-
packageCycle: isCyclic,
|
|
216
|
-
cycleEvidence: isCyclic ? (pkgToEdges.get(id) ?? []) : undefined,
|
|
217
|
-
},
|
|
218
|
-
};
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const edges = (elements.edges ?? []).map(e => {
|
|
222
|
-
const source = String(e.data.source) as TUniquePackageName;
|
|
223
|
-
const target = String(e.data.target) as TUniquePackageName;
|
|
224
|
-
const isCycleEdge = cycleEdgeKeys.has(`${source}→${target}`);
|
|
225
|
-
|
|
226
|
-
const existing = e.classes ? String(e.classes) : '';
|
|
227
|
-
const classes = isCycleEdge
|
|
228
|
-
? existing
|
|
229
|
-
? `${existing} packageCycle`
|
|
230
|
-
: 'packageCycle'
|
|
231
|
-
: existing;
|
|
232
|
-
|
|
233
|
-
return {
|
|
234
|
-
...e,
|
|
235
|
-
classes,
|
|
236
|
-
data: {
|
|
237
|
-
...e.data,
|
|
238
|
-
packageCycle: isCycleEdge,
|
|
239
|
-
},
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
return { ...elements, nodes, edges };
|
|
121
|
+
return { cycles, packageSet, graph };
|
|
244
122
|
}
|
|
245
123
|
|
|
246
|
-
|
|
124
|
+
/***
|
|
247
125
|
* Convenience that only returns the set of cyclic packages (no evidence)
|
|
248
126
|
*/
|
|
249
127
|
export function getCyclicPackageSet(
|
|
@@ -255,21 +133,3 @@ export function getCyclicPackageSet(
|
|
|
255
133
|
}
|
|
256
134
|
|
|
257
135
|
type TUniquePackageName = string;
|
|
258
|
-
|
|
259
|
-
interface ImportEvidence {
|
|
260
|
-
readonly filePath: string; // ParsedFile.path (relative to project)
|
|
261
|
-
readonly fileClass: string; // ParsedFile.className
|
|
262
|
-
readonly importName: string; // IJavaImport.name
|
|
263
|
-
readonly isIntrinsic?: boolean; // IJavaImport.isIntrinsic
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
interface CycleEdgeEvidence {
|
|
267
|
-
readonly from: TUniquePackageName;
|
|
268
|
-
readonly to: TUniquePackageName;
|
|
269
|
-
readonly via: ImportEvidence[]; // files in `from` that import `to`
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export interface PackageCycleDetail {
|
|
273
|
-
readonly packages: TUniquePackageName[]; // ordered cycle incl. closing node, e.g. ["A","B","A"]
|
|
274
|
-
readonly edges: CycleEdgeEvidence[]; // evidence aligned with packages[i] -> packages[i+1]
|
|
275
|
-
}
|