uipool
v0.3.0
Published
CLI tool to scan repositories for UI pattern annotations and generate usage reports
Maintainers
Readme
UiPool CLI
A CLI tool to scan repositories for UI pattern and component annotations and generate usage reports. Supports React, Angular, Vue, and other modern web frameworks.
Installation
# Install globally
npm install -g uipool
# Or use with npx
npx uipool scanUsage
Basic Scan (v0.3.0 — "Magical" Defaults)
Scan the current directory and generate a full insights report:
uipool scanAs of v0.3.0, scan includes auto-detection and usage analysis by default. This will:
- Scan all supported files in the current directory
- Extract pattern annotations from comments (
// uipool-pattern: ...) - Auto-detect components by folder structure (no annotations required)
- Analyze usage to find where components are imported
- Generate rich insights: top used, unused, pattern suggestions, clusters
- Write
uipool-report.jsonto the current directory
To disable auto-detection or usage analysis, use --no-auto or --no-usage.
Options
uipool scan [options]
Options:
-r, --root <path> Directory to scan (default: current directory)
-o, --out <path> Output JSON file path (default: <root>/uipool-report.json)
-p, --project <name> Override project name (default: inferred from package.json)
-e, --ext <list> Comma-separated file extensions to scan
-i, --ignore <list> Comma-separated ignore globs to append to defaults
--strict Only accept namespaced markers (uipool-pattern, uipool-component)
--warn-legacy Warn about legacy pattern: annotations (default: true)
--no-warn-legacy Suppress warnings about legacy pattern: annotations
--auto Auto-scan mode: discover components by folder name (default: true)
--no-auto Disable auto-scan mode (only detect tagged annotations)
--usage Detect component usage across the codebase (default: true)
--no-usage Disable usage detection
--fail-on-unused Exit with code 1 if unused components are found
--summary-json Output a one-line JSON summary to stdout (CI-friendly)
--max-depth <n> Maximum directory depth for scanning (auto mode)
--cluster-min-components <n> Min components per cluster (default: 1)
--cluster-min-files <n> Min files per cluster (default: 1)
--cluster-max <n> Max clusters in output (default: 200)
--top-used <n> Number of top used components to include (default: 20)Examples
# Scan a specific directory
uipool scan --root ./src
# Custom output path
uipool scan --out ./reports/patterns.json
# Override project name
uipool scan --project "My App"
# Scan only TypeScript and Vue files
uipool scan --ext .ts,.vue
# Add custom ignore patterns
uipool scan --ignore "**/legacy/**,**/deprecated/**"
# Strict mode - only namespaced annotations
uipool scan --strict
# Suppress legacy warnings
uipool scan --no-warn-legacyAuto-Scan Mode (Default in v0.3.0)
Auto-scan discovers components automatically by folder structure, without requiring annotations. Combined with usage detection, it generates rich insights.
As of v0.3.0, auto-scan and usage detection are enabled by default. Simply run:
uipool scan -r ./appTo customize or tune the output:
# Full insights with cluster tuning
uipool scan \
--cluster-min-components 2 \
--cluster-min-files 2 \
--top-used 10 \
-r ./app
# Fail CI if unused components exist
uipool scan --fail-on-unused -r ./app
# Disable auto-scan (only tagged patterns/components)
uipool scan --no-auto --no-usage -r ./appWhat auto-scan provides:
- Components: Discovers Vue/React/Angular components by folder naming conventions
- Usage detection: Tracks where each component is imported/used across your codebase
- Candidate patterns: Suggests pattern groupings based on folder structure
- Usage clusters: Groups components by where they're used together (route-based)
- Top used: Ranks components by usage frequency
- Unused detection: Identifies components with no detected imports
- Risk signals: Flags potential issues (e.g., high unused ratio)
CI-Friendly JSON Summary
Use --summary-json to output a compact one-line JSON summary for CI pipelines:
# Auto-scan with usage detection and JSON summary
uipool scan --auto --usage --summary-json -r ./app -o report.jsonThis prints a single JSON line to stdout containing:
{
"version": "0.3.0",
"mode": "auto",
"root": "./app",
"projectName": "app",
"generatedAt": "2026-01-13T12:00:00.000Z",
"filesScanned": 83,
"totalComponents": 15,
"unusedComponents": 2,
"unusedPercent": 13.3,
"totalCandidatePatterns": 3,
"totalUsageClusters": 9,
"topClusters": [
{ "clusterId": "pages/docs", "componentCount": 4, "fileCount": 14 }
],
"topUnused": ["copy-code", "preview-mode-banner"],
"riskSignals": ["unused-components-found"],
"reportPath": "./app/uipool-report.json",
"reportWritten": true,
"exitCodeHint": 0,
"flags": {
"auto": true,
"usage": true,
"failOnUnused": false,
"summaryJson": true
}
}Summary fields:
| Field | Description |
| --------------- | ------------------------------------------------------------------------ |
| reportPath | Normalized path to the written report file (forward slashes) |
| reportWritten | true if report was written successfully, false if write failed |
| exitCodeHint | Expected exit code: 1 if --fail-on-unused and unused exist, else 0 |
When --summary-json is enabled:
- Normal verbose console output is suppressed
- Exactly one line of JSON is printed to stdout
- The full report is still written to the output file
With --fail-on-unused: The summary is printed before exiting with code 1 if unused components exist:
uipool scan --auto --usage --summary-json --fail-on-unused -r ./app
# Prints JSON summary, then exits with code 1 if unused components foundAnnotation Formats
UiPool supports two types of annotations: patterns and components.
Pattern Annotations (Recommended)
Use the namespaced uipool-pattern: annotation for future-proof scanning:
// uipool-pattern: login-flow
function LoginPage() { ... }
/* uipool-pattern: data-table */
const TableComponent = ...# uipool-pattern: user-dashboard
class DashboardView:
passLegacy Pattern Annotations
For backward compatibility, the original pattern: annotation is still supported:
// pattern: login-flow
function LoginPage() { ... }Note: We recommend migrating to
uipool-pattern:for future-proof scanning. Use--strictmode to enforce namespaced annotations only.
Component Annotations (Optional)
Track component usage with uipool-component::
// uipool-component: button-primary
export const PrimaryButton = () => { ... }
/* uipool-component: modal-dialog */
export function ModalDialog() { ... }Components will appear in a separate components array in the report, enabling pattern-to-component mapping in UiPool.
HTML/Angular Template Annotations
For Angular templates and HTML files, use HTML comments:
<!-- uipool-pattern: login-flow -->
<div class="login-container">
<!-- uipool-component: input-field -->
<input type="email" />
<!-- uipool-component: button-primary -->
<button type="submit">Sign In</button>
</div>Supported Comment Syntaxes
| Syntax | Languages |
| -------------- | ------------------------------------------------- |
| // ... | JavaScript, TypeScript, JSX, TSX, CSS, SCSS, LESS |
| /* ... */ | JavaScript, TypeScript, CSS, SCSS, LESS, Vue |
| # ... | Markdown, Python, YAML |
| <!-- ... --> | HTML, Vue templates, Angular templates, MDX |
Flexible Whitespace
Extra spaces are allowed in all formats:
// uipool-pattern: login-flow
/* uipool-component: button-primary */<!-- uipool-pattern: login-flow -->Case Insensitive
The annotation keywords are case-insensitive:
// uipool-pattern: login-flow
// UIPOOL-PATTERN: login-flow
// UiPool-Pattern: login-flowID Format
Pattern and component IDs can contain:
- Letters (a-z, A-Z)
- Numbers (0-9)
- Dashes (-)
- Underscores (_)
Examples: login-flow, data_table, UserDashboard, form-wizard-v2, button-primary
Output Format
The generated report follows this structure:
{
"generatedAt": "2026-01-13T21:43:49.381Z",
"projectName": "MyApp",
"entries": [
{
"patternId": "login-flow",
"count": 4,
"files": ["app/pages/auth/login.vue", "app/pages/index.vue"]
}
],
"components": [
{
"componentId": "button-primary",
"count": 2,
"files": ["src/components/Button.tsx"],
"usedCount": 5,
"usedInFiles": ["src/App.tsx", "src/Dashboard.tsx"],
"unused": false,
"usageSignals": {
"importRefs": 3,
"templateTagRefs": 5,
"dynamicImportRefs": 0,
"registryRefs": 0,
"globalRegRefs": 0
}
},
{
"componentId": "deprecated-widget",
"count": 1,
"files": ["src/components/DeprecatedWidget.tsx"],
"usedCount": 0,
"usedInFiles": [],
"unused": true,
"unusedReason": "No imports, template usage, or dynamic references detected",
"usageSignals": {
"importRefs": 0,
"templateTagRefs": 0,
"dynamicImportRefs": 0,
"registryRefs": 0,
"globalRegRefs": 0
}
}
],
"candidatePatterns": [
{
"patternId": "auth",
"componentIds": ["login-form", "signup-form"],
"files": [
"components/auth/LoginForm.vue",
"components/auth/SignupForm.vue"
],
"count": 2,
"source": "folder",
"rationale": "Components grouped by auth/ folder"
},
{
"patternId": "co-usage-1",
"componentIds": ["data-table", "pagination", "filter-bar"],
"files": ["pages/users.vue", "pages/orders.vue"],
"count": 3,
"source": "coUsage",
"confidence": 0.85,
"rationale": "Strong correlation: components frequently co-occur"
}
],
"usageClusters": [
{
"clusterId": "pages/auth",
"componentIds": ["login-form", "auth-header"],
"files": ["pages/auth/login.vue", "pages/auth/signup.vue"],
"componentCount": 2,
"fileCount": 2
}
],
"topUsedComponents": [
{
"componentId": "base-button",
"usedCount": 42,
"usedInFiles": ["src/App.vue", "src/Dashboard.vue"]
}
],
"unusedSummary": {
"totalComponents": 15,
"unusedCount": 2,
"unusedPercent": 13.3
},
"unusedByGroup": [
{
"groupId": "legacy",
"unusedCount": 2,
"componentIds": ["old-button", "old-card"]
}
],
"highlightClusters": [
{
"clusterId": "pages/docs",
"componentIds": ["docs-header", "docs-section"],
"componentCount": 4,
"fileCount": 14,
"rationale": "Shared set powering a route group"
}
],
"riskSignals": [
{
"type": "unused-components-found",
"message": "2 unused components detected",
"componentIds": ["old-button", "old-card"]
}
]
}Core Fields
| Field | Description |
| --------------------------- | ---------------------------------------------------------------- |
| generatedAt | ISO 8601 timestamp of when the report was generated |
| projectName | Name of the project (from package.json or folder name) |
| entries | Array of pattern entries, sorted alphabetically by patternId |
| entries[].patternId | The pattern identifier |
| entries[].count | Total number of occurrences across all files |
| entries[].files | Unique list of files containing the pattern |
| components | Array of component entries (from --auto mode or annotations) |
| components[].componentId | The component identifier |
| components[].source | "tagged" (via comment) or "auto" (folder detection) (v0.3.0) |
| components[].count | Total number of occurrences across all files |
| components[].files | Unique list of files containing the component |
| components[].usedCount | Number of files where component is used (with --usage) |
| components[].usedInFiles | Files where component is imported/used (with --usage) |
| components[].unused | true if component has no detected usage (with --usage) |
| components[].usageSignals | Detailed usage signal counts (v0.3.1) |
| components[].unusedReason | Human-readable explanation when unused=true (v0.3.1) |
Usage Signals (v0.3.1)
Each component includes a usageSignals object with detailed detection counts:
| Signal | Description |
| ------------------- | ------------------------------------------------------------------ |
| importRefs | Explicit import statements (default/named imports) |
| templateTagRefs | Template usage (<MyComp> or <my-comp>) |
| dynamicImportRefs | Dynamic imports (import('./MyComp.vue'), defineAsyncComponent) |
| registryRefs | Component registry references (components: { MyComp }) |
| globalRegRefs | Global registrations (app.component(), resolveComponent()) |
Unused detection logic: A component is marked unused=true only if ALL signals are 0 AND usedInFiles is empty. This multi-signal approach reduces false positives from dynamic components, auto-imports, or global registrations.
Insight Fields (v0.3.0)
These fields are included when using --auto --usage mode:
| Field | Description |
| ---------------------------------- | ------------------------------------------------------------------ |
| candidatePatterns | Suggested pattern groupings based on folder structure and co-usage |
| candidatePatterns[].patternId | Suggested pattern name (from folder or cluster ID) |
| candidatePatterns[].componentIds | Components that would belong to this pattern |
| candidatePatterns[].source | "folder" or "coUsage" (v0.3.1) |
| candidatePatterns[].confidence | Confidence score 0-1 for coUsage patterns (v0.3.1) |
| candidatePatterns[].rationale | Human-readable explanation (v0.3.1) |
| usageClusters | Groups of components used together in the same files |
| usageClusters[].clusterId | Cluster identifier (typically a route/page path) |
| usageClusters[].componentIds | Components used in this cluster |
| usageClusters[].componentCount | Number of components in the cluster |
| usageClusters[].fileCount | Number of files in the cluster |
| topUsedComponents | Most frequently used components (limit: --top-used) |
| unusedSummary | Summary of unused component statistics |
| unusedSummary.totalComponents | Total number of components scanned |
| unusedSummary.unusedCount | Number of unused components |
| unusedSummary.unusedPercent | Percentage of components that are unused |
| unusedByGroup | Unused components grouped by folder |
| highlightClusters | Notable clusters with high component reuse |
| highlightClusters[].rationale | Why this cluster is highlighted |
| riskSignals | Actionable warnings about codebase health |
| riskSignals[].type | Signal type (e.g., unused-components-found) |
| riskSignals[].message | Human-readable description |
Note: Insight fields are only populated when using
--auto --usagemode. They enable the Insights tab in the UiPool web dashboard.
Default Behavior
File Extensions
By default, these extensions are scanned:
.js- JavaScript.ts- TypeScript.vue- Vue components.jsx- React JSX.tsx- React TypeScript.md- Markdown.html- HTML / Angular templates.css- CSS.scss- SCSS.sass- Sass.less- Less.mdx- MDX
Ignored Directories
These directories are ignored by default:
node_modulesdistbuild.output.nuxt.next.gitcoverage.turbo.cache
Framework Examples
React
// uipool-pattern: user-profile
// uipool-component: avatar
export function UserProfile({ user }) {
return (
<div className="profile">
<Avatar src={user.avatar} />
<h1>{user.name}</h1>
</div>
);
}Angular
<!-- user-list.component.html -->
<!-- uipool-pattern: user-list -->
<div class="user-list">
<!-- uipool-component: user-card -->
<app-user-card *ngFor="let user of users" [user]="user" />
</div>Vue
<template>
<!-- uipool-pattern: dashboard -->
<div class="dashboard">
<!-- uipool-component: stats-widget -->
<StatsWidget :data="stats" />
</div>
</template>
<script setup>
// uipool-pattern: dashboard
import StatsWidget from "./StatsWidget.vue";
</script>Integration with UiPool
Upload the generated uipool-report.json to UiPool to:
- Track pattern usage across your codebase
- Map components to patterns
- Identify unused or deprecated patterns
- Generate documentation coverage reports
- Share usage analytics with your team
License
MIT
