jscan uses a layered architecture inspired by Clean Architecture. Core analysis logic stays isolated from CLI/output concerns, while command handlers can call application use cases or services directly for pragmatic orchestration.
┌──────────────────────────────────────────────┐
│ CLI (cmd/) │
│ cobra commands, arg parsing, I/O │
├──────────────────────────────────────────────┤
│ Application (app/) │
│ reusable use cases / file orchestration │
├──────────────────────────────────────────────┤
│ Service (service/) │
│ analysis orchestration, formatting, output │
├──────────────────────────────────────────────┤
│ Internal (internal/) │
│ parser, analyzers, config, reporter │
├──────────────────────────────────────────────┤
│ Domain (domain/) │
│ pure models and service interfaces │
└──────────────────────────────────────────────┘
Typical runtime flows:
cmd -> service -> internal -> domaincmd -> app -> service -> internal -> domain
All layers depend on domain for shared types; domain depends on nothing.
Entry point using cobra. Handles command-line argument parsing, flag configuration, and output rendering. Commands include:
analyze- Run full project analysischeck- Run health checks against thresholdsdeps- Analyze module dependenciesinit- Initialize a jscan configuration file
For performance-sensitive commands, CLI handlers may orchestrate services directly.
Provides reusable orchestration/use-case logic that can be used by CLI handlers and tests. Examples:
analyze_usecase.go- Full analysis pipelinecomplexity_usecase.go- Complexity-focused analysisdead_code_usecase.go- Dead code workflow delegating todomain.DeadCodeService
Business logic services that operate between the CLI and core analyzers:
- complexity_service - Orchestrates complexity analysis
- dead_code_service - Orchestrates dead code detection
- dead_code_aggregate - Cross-file dead code aggregation (unused imports/exports, orphan files)
- clone_service - Orchestrates clone detection
- cbo_service - Orchestrates coupling metrics
- dependency_graph_service - Orchestrates dependency graph construction
- output_formatter - Formats results as text, JSON, HTML, or CSV
- dot_formatter - Generates DOT graph output for dependency visualization
- parallel_executor - Manages concurrent file analysis
- progress_manager - Terminal progress bar rendering
- config_loader - Loads and validates jscan configuration
- browser - Opens HTML reports in the system browser
Wraps go-tree-sitter to parse JavaScript and TypeScript source files into concrete syntax trees (CSTs). Provides the foundation for all downstream analysis.
The heart of jscan. Contains all static analysis algorithms:
- CFG construction (
cfg.go,cfg_builder.go) - Builds control flow graphs from parsed ASTs - Reachability analysis (
reachability.go) - Determines reachable/unreachable code paths from CFG - Cyclomatic complexity (
complexity.go) - McCabe cyclomatic complexity calculation - Dead code detection (
dead_code.go,unused_code.go) - Detects unreachable code, unused imports/exports, and orphan files - Clone detection (
clone_detector.go) - Identifies duplicate code using APTED tree edit distance combined with MinHash/LSH for candidate selectionapted.go/apted_tree.go/apted_cost.go- APTED tree edit distance algorithmminhash.go- MinHash fingerprinting for approximate similaritylsh_index.go- Locality-sensitive hashing index for fast candidate retrievalast_features.go- AST feature extraction for fingerprintinggrouping_strategy.go- Strategies for grouping code fragments
- Module analysis (
module_analyzer.go) - ESM and CommonJS import/export resolution - Dependency graph (
dependency_graph.go) - Builds the full module dependency graph - CBO metrics (
cbo.go,coupling_metrics.go) - Coupling Between Objects measurement - Circular dependency detection (
circular_detector.go) - Finds circular dependencies using Tarjan's strongly connected components algorithm
Formats complexity analysis results for different output targets.
Reads and manages jscan configuration (thresholds, ignore patterns, output settings).
Pure data structures with no external dependencies:
complexity.go- Complexity measurement modelsdead_code.go- Dead code finding typesclone.go- Clone detection result typescbo.go- Coupling metric typesdependency_graph.go- Dependency graph typesmodule.go- Module/import/export typesoutput.go- Output configuration typessystem_analysis.go- Top-level analysis result typeserrors.go- Domain error types
The layered split keeps analysis engines independent of CLI/output concerns, but still allows direct service orchestration from command handlers where it simplifies concurrency and UX behavior. This keeps critical analysis logic testable while reducing command-level duplication.
tree-sitter provides fast, incremental, error-tolerant parsing. Unlike regex-based approaches, it produces a full concrete syntax tree, enabling accurate structural analysis. It handles malformed files gracefully, which is important when scanning real-world codebases.
Pure tree edit distance (APTED) is accurate but O(n^3) per pair comparison, making it impractical for large codebases. MinHash fingerprinting with LSH indexing provides O(1) approximate similarity lookups to narrow candidates before running the expensive APTED comparison. This two-phase approach balances accuracy with performance.
Tarjan's algorithm finds all strongly connected components in a directed graph in O(V+E) time. Each strongly connected component with more than one node represents a circular dependency. This is more efficient and complete than naive cycle detection approaches.