UNPKG

@rayburst/sharity

Version:

Analyze shared package usage across monorepos - calculate symbol sharing percentages, track exclusive imports, and identify unused exports

332 lines (246 loc) โ€ข 11.8 kB
# @rayburst/sharity > Analyze shared package usage across monorepos - calculate symbol sharing percentages, track exclusive imports, and identify unused exports ## Overview `sharity` is a CLI tool and programmatic API that helps you understand how shared packages are used across your monorepo. It answers questions like: - **What percentage of exports are truly shared** (used by 2+ apps)? - **Which symbols are exclusive** to single consumers? - **How much dead code** exists (unused exports)? - **Which apps have high coupling** to shared packages? Perfect for: - ๐Ÿ—๏ธ **Monorepo maintenance** - Identify refactoring opportunities - ๐Ÿ“Š **Architecture decisions** - Understand package coupling - ๐Ÿงน **Code cleanup** - Find unused exports - ๐Ÿš€ **CI/CD integration** - Enforce sharing thresholds ## Installation ### In your monorepo (recommended) ```bash pnpm add -D @rayburst/sharity # or npm install --save-dev @rayburst/sharity # or yarn add -D @rayburst/sharity ``` ### Global installation ```bash npm install -g @rayburst/sharity ``` ## Quick Start ### CLI Usage ```bash # Analyze a package npx sharity packages/ui --consumers "apps/*" "packages/*" # With verbose output npx sharity packages/ui --consumers "apps/*" -v # JSON output npx sharity packages/ui -f json # With CI threshold (fails if shared % below 50%) npx sharity packages/ui --threshold 50 ``` ### As npm script Add to your `package.json`: ```json { "scripts": { "analyze:ui": "sharity packages/ui --consumers 'apps/*' 'packages/*'", "analyze:ui:ci": "sharity packages/ui --threshold 50" } } ``` Then run: ```bash npm run analyze:ui ``` ### Programmatic API ```typescript import { SharedPackageAnalyzer } from '@rayburst/sharity'; const analyzer = new SharedPackageAnalyzer({ packagePath: './packages/ui', consumerGlobs: ['apps/*', 'packages/*'], verbose: true, }); const result = await analyzer.analyze(); console.log(`Shared symbols: ${result.sharedSymbolsPercentage}%`); console.log(`Exclusive symbols: ${result.exclusiveSymbolsPercentage}%`); console.log(`Unused symbols: ${result.unusedSymbolsPercentage}%`); ``` ## CLI Options ``` Usage: sharity [options] <package-path> Analyze shared package usage across monorepos Arguments: package-path Path to the package to analyze Options: -V, --version output the version number -c, --consumers <globs...> Glob patterns for consumer packages (default: ["apps/*","packages/*"]) -e, --exclude <patterns...> Patterns to exclude from analysis (default: []) -t, --threshold <number> Minimum sharing percentage threshold for CI (exit 1 if below) -f, --format <type> Output format: table or json (default: "table") --include-tests Include test files in analysis (default: false) --config <path> Load configuration from file -v, --verbose Verbose output (default: false) -h, --help display help for command ``` ## Example Output ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Shared Package Analysis: @myorg/ui โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Total Symbols โ”‚ 129 โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Shared (2+ apps) โ”‚ 34 (26.4%) โœ“ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Exclusive (1 app) โ”‚ 95 (73.6%) โš ๏ธ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Unused (0 apps) โ”‚ 0 (0%) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Total Lines โ”‚ 23,114 โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Shared lines โ”‚ 17,536 (75.87%) โœ“ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Exclusive lines โ”‚ 3,500 (15.14%) โš ๏ธ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Unused lines โ”‚ 2,078 (8.99%) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Per-Consumer Analysis โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Consumer โ”‚ Total โ”‚ Exclusive โ”‚ Exclusive % โ”‚ Est. Lines โ”‚ Risk โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ web-client โ”‚ 87 โ”‚ 63 โ”‚ 72.4% โ”‚ 2,450 โ”‚ ๐Ÿ”ด โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ customer-funnel โ”‚ 38 โ”‚ 17 โ”‚ 44.7% โ”‚ 550 โ”‚ ๐ŸŸก โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ microsites โ”‚ 37 โ”‚ 8 โ”‚ 21.6% โ”‚ 400 โ”‚ ๐ŸŸข โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ## Configuration File Create `.sharity.json`: ```json { "packagePath": "./packages/ui", "consumerGlobs": ["apps/*", "packages/*"], "excludePatterns": ["**/*.test.ts", "**/__mocks__/**"], "includeTests": false, "sharingThreshold": 50, "outputFormat": "table", "verbose": false } ``` Then use it: ```bash npx sharity --config .sharity.json ``` ## CI/CD Integration ### GitHub Actions ```yaml name: Shared Package Analysis on: [pull_request] jobs: analyze: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm install - name: Analyze shared package run: npx sharity packages/ui --threshold 50 ``` ### GitLab CI ```yaml analyze-shared: script: - npm install - npx sharity packages/ui --threshold 50 ``` ## Understanding the Metrics ### Symbol Categories - **Shared (2+ apps)**: โœ… Correctly placed in shared package - **Exclusive (1 app)**: โš ๏ธ Should likely be moved to that app - **Unused (0 apps)**: ๐Ÿ—‘๏ธ Dead code, can be deleted ### Risk Levels - ๐Ÿ”ด **HIGH** (โ‰ฅ70% exclusive): Strong coupling, consider refactoring - ๐ŸŸก **MEDIUM** (40-69% exclusive): Moderate coupling, review - ๐ŸŸข **LOW** (<40% exclusive): Good separation ### When to Worry **๐Ÿšจ Red flags:** - Shared symbol percentage < 30% - Consumer with >70% exclusive imports - High unused code percentage (>20%) **โœ… Healthy metrics:** - Shared symbol percentage > 50% - Most consumers with <40% exclusive imports - Unused code < 10% ## Use Cases ### 1. Find Refactoring Opportunities ```bash # Identify which apps have exclusive imports to extract npx sharity packages/ui -v ``` ### 2. Enforce Architecture Rules ```bash # Fail CI if sharing drops below 50% npx sharity packages/ui --threshold 50 ``` ### 3. Track Progress ```bash # Before refactoring npx sharity packages/ui -f json > before.json # After refactoring npx sharity packages/ui -f json > after.json # Compare results ``` ### 4. Clean Up Dead Code ```bash # Find unused exports npx sharity packages/ui -v | grep "Unused" ``` ## How It Works 1. **Scans target package** - Extracts all exported symbols using TypeScript Compiler API 2. **Finds consumer packages** - Uses glob patterns to locate all consumers 3. **Analyzes imports** - Tracks which consumers import which symbols 4. **Calculates metrics** - Determines shared vs exclusive vs unused 5. **Generates report** - Pretty-printed tables or JSON output ## Limitations - Only analyzes TypeScript/JavaScript files (.ts, .tsx, .js, .jsx) - Requires static imports (dynamic imports not tracked) - Line counting excludes comments and empty lines - Re-exports may be counted multiple times ## Development ```bash # Clone and install git clone https://github.com/rayburst/sharity.git cd sharity npm install # Build npm run build # Test npm test # Lint npm run lint ``` ## Contributing Contributions welcome! Please: 1. Fork the repo 2. Create a feature branch 3. Add tests 4. Submit a PR ## License MIT ## Related Tools - [Knip](https://github.com/webpro/knip) - Find unused files, dependencies, and exports - [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) - Validate and visualize dependencies - [ts-unused-exports](https://github.com/pzavolinsky/ts-unused-exports) - Find unused exports in TypeScript ## Why This Tool? While other tools find unused exports, **sharity** uniquely: - โœ… Calculates **sharing percentages** - โœ… Tracks **per-consumer exclusive imports** - โœ… Provides **architectural insights** - โœ… Supports **CI thresholds** - โœ… Designed for **monorepo workflows** --- Made with โค๏ธ for better monorepo architecture