@bfra.me/workspace-analyzer 0.1.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.
Files changed (73) hide show
  1. package/README.md +402 -0
  2. package/lib/chunk-4LSFAAZW.js +1 -0
  3. package/lib/chunk-JDF7DQ4V.js +27 -0
  4. package/lib/chunk-WOJ4C7N7.js +7122 -0
  5. package/lib/cli.d.ts +1 -0
  6. package/lib/cli.js +318 -0
  7. package/lib/index.d.ts +3701 -0
  8. package/lib/index.js +1262 -0
  9. package/lib/types/index.d.ts +146 -0
  10. package/lib/types/index.js +28 -0
  11. package/package.json +89 -0
  12. package/src/analyzers/analyzer.ts +201 -0
  13. package/src/analyzers/architectural-analyzer.ts +304 -0
  14. package/src/analyzers/build-config-analyzer.ts +334 -0
  15. package/src/analyzers/circular-import-analyzer.ts +463 -0
  16. package/src/analyzers/config-consistency-analyzer.ts +335 -0
  17. package/src/analyzers/dead-code-analyzer.ts +565 -0
  18. package/src/analyzers/duplicate-code-analyzer.ts +626 -0
  19. package/src/analyzers/duplicate-dependency-analyzer.ts +381 -0
  20. package/src/analyzers/eslint-config-analyzer.ts +281 -0
  21. package/src/analyzers/exports-field-analyzer.ts +324 -0
  22. package/src/analyzers/index.ts +388 -0
  23. package/src/analyzers/large-dependency-analyzer.ts +535 -0
  24. package/src/analyzers/package-json-analyzer.ts +349 -0
  25. package/src/analyzers/peer-dependency-analyzer.ts +275 -0
  26. package/src/analyzers/tree-shaking-analyzer.ts +623 -0
  27. package/src/analyzers/tsconfig-analyzer.ts +382 -0
  28. package/src/analyzers/unused-dependency-analyzer.ts +356 -0
  29. package/src/analyzers/version-alignment-analyzer.ts +308 -0
  30. package/src/api/analyze-workspace.ts +245 -0
  31. package/src/api/index.ts +11 -0
  32. package/src/cache/cache-manager.ts +495 -0
  33. package/src/cache/cache-schema.ts +247 -0
  34. package/src/cache/change-detector.ts +169 -0
  35. package/src/cache/file-hasher.ts +65 -0
  36. package/src/cache/index.ts +47 -0
  37. package/src/cli/commands/analyze.ts +240 -0
  38. package/src/cli/commands/index.ts +5 -0
  39. package/src/cli/index.ts +61 -0
  40. package/src/cli/types.ts +65 -0
  41. package/src/cli/ui.ts +213 -0
  42. package/src/cli.ts +9 -0
  43. package/src/config/defaults.ts +183 -0
  44. package/src/config/index.ts +81 -0
  45. package/src/config/loader.ts +270 -0
  46. package/src/config/merger.ts +229 -0
  47. package/src/config/schema.ts +263 -0
  48. package/src/core/incremental-analyzer.ts +462 -0
  49. package/src/core/index.ts +34 -0
  50. package/src/core/orchestrator.ts +416 -0
  51. package/src/graph/dependency-graph.ts +408 -0
  52. package/src/graph/index.ts +19 -0
  53. package/src/index.ts +417 -0
  54. package/src/parser/config-parser.ts +491 -0
  55. package/src/parser/import-extractor.ts +340 -0
  56. package/src/parser/index.ts +54 -0
  57. package/src/parser/typescript-parser.ts +95 -0
  58. package/src/performance/bundle-estimator.ts +444 -0
  59. package/src/performance/index.ts +27 -0
  60. package/src/reporters/console-reporter.ts +355 -0
  61. package/src/reporters/index.ts +49 -0
  62. package/src/reporters/json-reporter.ts +273 -0
  63. package/src/reporters/markdown-reporter.ts +349 -0
  64. package/src/reporters/reporter.ts +399 -0
  65. package/src/rules/builtin-rules.ts +709 -0
  66. package/src/rules/index.ts +52 -0
  67. package/src/rules/rule-engine.ts +409 -0
  68. package/src/scanner/index.ts +18 -0
  69. package/src/scanner/workspace-scanner.ts +403 -0
  70. package/src/types/index.ts +176 -0
  71. package/src/types/result.ts +19 -0
  72. package/src/utils/index.ts +7 -0
  73. package/src/utils/pattern-matcher.ts +48 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Cache schema types for workspace analyzer incremental analysis.
3
+ *
4
+ * These types define the structure for storing and retrieving analysis results
5
+ * to enable efficient incremental analysis on large codebases.
6
+ */
7
+
8
+ import type {Issue} from '../types/index'
9
+
10
+ /**
11
+ * Schema version for cache format migration.
12
+ * Increment when making breaking changes to cache structure.
13
+ */
14
+ export const CACHE_SCHEMA_VERSION = 1
15
+
16
+ /**
17
+ * Metadata about a cached file's state at analysis time.
18
+ */
19
+ export interface CachedFileState {
20
+ /** Absolute file path */
21
+ readonly path: string
22
+ /** Content hash (SHA-256) at time of analysis */
23
+ readonly contentHash: string
24
+ /** File modification timestamp (ISO 8601) */
25
+ readonly modifiedAt: string
26
+ /** File size in bytes */
27
+ readonly size: number
28
+ }
29
+
30
+ /**
31
+ * Metadata about a cached analysis run.
32
+ */
33
+ export interface CacheMetadata {
34
+ /** Cache schema version for migration support */
35
+ readonly version: number
36
+ /** Workspace root path that was analyzed */
37
+ readonly workspacePath: string
38
+ /** Timestamp when the cache was created (ISO 8601) */
39
+ readonly createdAt: string
40
+ /** Timestamp when the cache was last updated (ISO 8601) */
41
+ readonly updatedAt: string
42
+ /** Hash of the analyzer configuration */
43
+ readonly configHash: string
44
+ /** Workspace analyzer package version */
45
+ readonly analyzerVersion: string
46
+ }
47
+
48
+ /**
49
+ * A cached analysis result for a single file.
50
+ */
51
+ export interface CachedFileAnalysis {
52
+ /** File state at time of analysis */
53
+ readonly fileState: CachedFileState
54
+ /** Issues found in this file */
55
+ readonly issues: readonly Issue[]
56
+ /** Analyzer IDs that processed this file */
57
+ readonly analyzersRun: readonly string[]
58
+ /** Analysis timestamp (ISO 8601) */
59
+ readonly analyzedAt: string
60
+ }
61
+
62
+ /**
63
+ * A cached package-level analysis result.
64
+ */
65
+ export interface CachedPackageAnalysis {
66
+ /** Package name */
67
+ readonly packageName: string
68
+ /** Package path relative to workspace root */
69
+ readonly packagePath: string
70
+ /** Hash of package.json content */
71
+ readonly packageJsonHash: string
72
+ /** Issues found at the package level */
73
+ readonly issues: readonly Issue[]
74
+ /** Analyzer IDs that processed this package */
75
+ readonly analyzersRun: readonly string[]
76
+ /** Analysis timestamp (ISO 8601) */
77
+ readonly analyzedAt: string
78
+ }
79
+
80
+ /**
81
+ * Complete analysis cache for a workspace.
82
+ */
83
+ export interface AnalysisCache {
84
+ /** Cache metadata */
85
+ readonly metadata: CacheMetadata
86
+ /** Per-file analysis results indexed by file path */
87
+ readonly files: Readonly<Record<string, CachedFileAnalysis>>
88
+ /** Per-package analysis results indexed by package name */
89
+ readonly packages: Readonly<Record<string, CachedPackageAnalysis>>
90
+ /** Workspace-level issues not tied to specific files */
91
+ readonly workspaceIssues: readonly Issue[]
92
+ /** Configuration files state for invalidation detection */
93
+ readonly configFiles: readonly CachedFileState[]
94
+ }
95
+
96
+ /**
97
+ * Result of cache validation check.
98
+ */
99
+ export interface CacheValidationResult {
100
+ /** Whether the cache is valid and can be used */
101
+ readonly isValid: boolean
102
+ /** Files that have changed since last analysis */
103
+ readonly changedFiles: readonly string[]
104
+ /** Files that are new since last analysis */
105
+ readonly newFiles: readonly string[]
106
+ /** Files that were deleted since last analysis */
107
+ readonly deletedFiles: readonly string[]
108
+ /** Packages that need re-analysis */
109
+ readonly invalidatedPackages: readonly string[]
110
+ /** Configuration files that changed (triggers full invalidation) */
111
+ readonly changedConfigFiles: readonly string[]
112
+ /** Reason for invalidation if not valid */
113
+ readonly invalidationReason?: string
114
+ }
115
+
116
+ /**
117
+ * Options for cache operations.
118
+ */
119
+ export interface CacheOptions {
120
+ /** Directory path for cache storage (default: .workspace-analyzer-cache) */
121
+ readonly cacheDir?: string
122
+ /** Maximum cache age in milliseconds (default: 7 days) */
123
+ readonly maxAge?: number
124
+ /** Whether to compress cache files (default: true) */
125
+ readonly compress?: boolean
126
+ /** Hash algorithm for file content (default: sha256) */
127
+ readonly hashAlgorithm?: 'sha256' | 'md5'
128
+ }
129
+
130
+ /**
131
+ * Statistics about cache usage.
132
+ */
133
+ export interface CacheStatistics {
134
+ /** Number of cached files */
135
+ readonly cachedFiles: number
136
+ /** Number of cached packages */
137
+ readonly cachedPackages: number
138
+ /** Total cache size in bytes */
139
+ readonly totalSizeBytes: number
140
+ /** Cache age in milliseconds */
141
+ readonly ageMs: number
142
+ /** Number of cache hits during last analysis */
143
+ readonly hitCount: number
144
+ /** Number of cache misses during last analysis */
145
+ readonly missCount: number
146
+ /** Cache hit rate (0-1) */
147
+ readonly hitRate: number
148
+ }
149
+
150
+ /**
151
+ * Configuration file patterns for cache invalidation.
152
+ * Changes to these files trigger re-analysis of affected areas.
153
+ */
154
+ export const CONFIG_FILE_PATTERNS: readonly string[] = [
155
+ 'package.json',
156
+ 'tsconfig.json',
157
+ 'tsconfig.*.json',
158
+ 'eslint.config.ts',
159
+ 'eslint.config.js',
160
+ 'eslint.config.mjs',
161
+ '.eslintrc.*',
162
+ 'tsup.config.ts',
163
+ 'tsup.config.js',
164
+ 'vitest.config.ts',
165
+ 'vitest.config.js',
166
+ 'workspace-analyzer.config.ts',
167
+ 'workspace-analyzer.config.js',
168
+ ]
169
+
170
+ /**
171
+ * Default cache options.
172
+ */
173
+ export const DEFAULT_CACHE_OPTIONS: Required<CacheOptions> = {
174
+ cacheDir: '.workspace-analyzer-cache',
175
+ maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
176
+ compress: true,
177
+ hashAlgorithm: 'sha256',
178
+ } as const
179
+
180
+ /**
181
+ * Creates an empty analysis cache with default metadata.
182
+ */
183
+ export function createEmptyCache(
184
+ workspacePath: string,
185
+ configHash: string,
186
+ analyzerVersion: string,
187
+ ): AnalysisCache {
188
+ const now = new Date().toISOString()
189
+ return {
190
+ metadata: {
191
+ version: CACHE_SCHEMA_VERSION,
192
+ workspacePath,
193
+ createdAt: now,
194
+ updatedAt: now,
195
+ configHash,
196
+ analyzerVersion,
197
+ },
198
+ files: {},
199
+ packages: {},
200
+ workspaceIssues: [],
201
+ configFiles: [],
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Creates a cache entry for a file analysis result.
207
+ */
208
+ export function createFileAnalysisEntry(
209
+ path: string,
210
+ contentHash: string,
211
+ modifiedAt: Date,
212
+ size: number,
213
+ issues: readonly Issue[],
214
+ analyzersRun: readonly string[],
215
+ ): CachedFileAnalysis {
216
+ return {
217
+ fileState: {
218
+ path,
219
+ contentHash,
220
+ modifiedAt: modifiedAt.toISOString(),
221
+ size,
222
+ },
223
+ issues,
224
+ analyzersRun,
225
+ analyzedAt: new Date().toISOString(),
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Creates a cache entry for a package analysis result.
231
+ */
232
+ export function createPackageAnalysisEntry(
233
+ packageName: string,
234
+ packagePath: string,
235
+ packageJsonHash: string,
236
+ issues: readonly Issue[],
237
+ analyzersRun: readonly string[],
238
+ ): CachedPackageAnalysis {
239
+ return {
240
+ packageName,
241
+ packagePath,
242
+ packageJsonHash,
243
+ issues,
244
+ analyzersRun,
245
+ analyzedAt: new Date().toISOString(),
246
+ }
247
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Change detector for incremental analysis.
3
+ *
4
+ * Re-exports from @bfra.me/es/watcher with workspace-analyzer-specific utilities.
5
+ */
6
+
7
+ import type {ChangeDetector, ChangeDetectorOptions} from '@bfra.me/es/watcher'
8
+ import type {CachedFileState, CacheValidationResult} from './cache-schema'
9
+ import {createChangeDetector} from '@bfra.me/es/watcher'
10
+
11
+ import {createWorkspaceHasher} from './file-hasher'
12
+
13
+ // Re-export the core change detector
14
+ export {createChangeDetector}
15
+ export type {ChangeDetector, ChangeDetectorOptions}
16
+
17
+ /**
18
+ * Options for creating an analysis change detector.
19
+ */
20
+ export interface AnalysisChangeDetectorOptions extends ChangeDetectorOptions {
21
+ /** Configuration file patterns to monitor for invalidation */
22
+ readonly configFilePatterns?: readonly string[]
23
+ }
24
+
25
+ /**
26
+ * Extended change detector for workspace analysis.
27
+ */
28
+ export interface AnalysisChangeDetector extends ChangeDetector {
29
+ /** Validate cache against current file states */
30
+ readonly validateCache: (
31
+ cachedFiles: readonly CachedFileState[],
32
+ currentFiles: readonly string[],
33
+ ) => Promise<CacheValidationResult>
34
+ /** Get all recorded file paths */
35
+ readonly getRecordedPaths: () => readonly string[]
36
+ /** Check if configuration files have changed */
37
+ readonly hasConfigChanged: (configFiles: readonly CachedFileState[]) => Promise<boolean>
38
+ }
39
+
40
+ /**
41
+ * Creates an extended change detector for workspace analysis.
42
+ *
43
+ * @param options - Change detector configuration options
44
+ * @returns An AnalysisChangeDetector instance
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const detector = createAnalysisChangeDetector()
49
+ *
50
+ * // Record initial file state
51
+ * await detector.record('src/index.ts')
52
+ *
53
+ * // Later, validate cache
54
+ * const validation = await detector.validateCache(
55
+ * cachedFileStates,
56
+ * currentFilePaths
57
+ * )
58
+ *
59
+ * if (!validation.isValid) {
60
+ * console.log('Cache invalid:', validation.invalidationReason)
61
+ * }
62
+ * ```
63
+ */
64
+ export function createAnalysisChangeDetector(
65
+ options: AnalysisChangeDetectorOptions = {},
66
+ ): AnalysisChangeDetector {
67
+ const baseDetector = createChangeDetector(options)
68
+ const hasher = createWorkspaceHasher({algorithm: options.algorithm})
69
+ const recordedPaths = new Set<string>()
70
+
71
+ return {
72
+ async hasChanged(path: string): Promise<boolean> {
73
+ return baseDetector.hasChanged(path)
74
+ },
75
+
76
+ async record(path: string): Promise<void> {
77
+ recordedPaths.add(path)
78
+ return baseDetector.record(path)
79
+ },
80
+
81
+ clear(path: string): void {
82
+ recordedPaths.delete(path)
83
+ baseDetector.clear(path)
84
+ },
85
+
86
+ clearAll(): void {
87
+ recordedPaths.clear()
88
+ baseDetector.clearAll()
89
+ },
90
+
91
+ getRecordedPaths(): readonly string[] {
92
+ return Array.from(recordedPaths)
93
+ },
94
+
95
+ async validateCache(
96
+ cachedFiles: readonly CachedFileState[],
97
+ currentFiles: readonly string[],
98
+ ): Promise<CacheValidationResult> {
99
+ const cachedPaths = new Set(cachedFiles.map(f => f.path))
100
+ const currentPaths = new Set(currentFiles)
101
+
102
+ const changedFiles: string[] = []
103
+ const newFiles: string[] = []
104
+ const deletedFiles: string[] = []
105
+
106
+ // Check for new files
107
+ for (const path of currentPaths) {
108
+ if (!cachedPaths.has(path)) {
109
+ newFiles.push(path)
110
+ }
111
+ }
112
+
113
+ // Check for deleted and changed files
114
+ for (const cached of cachedFiles) {
115
+ if (!currentPaths.has(cached.path)) {
116
+ deletedFiles.push(cached.path)
117
+ continue
118
+ }
119
+
120
+ // Check if file content has changed
121
+ try {
122
+ const currentHash = await hasher.hash(cached.path)
123
+ if (currentHash !== cached.contentHash) {
124
+ changedFiles.push(cached.path)
125
+ }
126
+ } catch {
127
+ // File might have been deleted or inaccessible
128
+ deletedFiles.push(cached.path)
129
+ }
130
+ }
131
+
132
+ const hasChanges = changedFiles.length > 0 || newFiles.length > 0 || deletedFiles.length > 0
133
+
134
+ let invalidationReason: string | undefined
135
+ if (hasChanges) {
136
+ const reasons: string[] = []
137
+ if (changedFiles.length > 0) reasons.push(`${changedFiles.length} files changed`)
138
+ if (newFiles.length > 0) reasons.push(`${newFiles.length} new files`)
139
+ if (deletedFiles.length > 0) reasons.push(`${deletedFiles.length} files deleted`)
140
+ invalidationReason = reasons.join(', ')
141
+ }
142
+
143
+ return {
144
+ isValid: !hasChanges,
145
+ changedFiles,
146
+ newFiles,
147
+ deletedFiles,
148
+ invalidatedPackages: [], // Computed by cache manager based on file paths
149
+ changedConfigFiles: [],
150
+ invalidationReason,
151
+ }
152
+ },
153
+
154
+ async hasConfigChanged(configFiles: readonly CachedFileState[]): Promise<boolean> {
155
+ for (const config of configFiles) {
156
+ try {
157
+ const currentHash = await hasher.hash(config.path)
158
+ if (currentHash !== config.contentHash) {
159
+ return true
160
+ }
161
+ } catch {
162
+ // Config file was deleted or inaccessible = changed
163
+ return true
164
+ }
165
+ }
166
+ return false
167
+ },
168
+ }
169
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * File hasher for cache invalidation and change detection.
3
+ *
4
+ * Re-exports from @bfra.me/es/watcher with workspace-analyzer-specific utilities.
5
+ */
6
+
7
+ import type {FileHasher} from '@bfra.me/es/watcher'
8
+ import {createFileHasher} from '@bfra.me/es/watcher'
9
+
10
+ // Re-export the core hasher factory
11
+ export {createFileHasher}
12
+ export type {FileHasher}
13
+
14
+ /**
15
+ * Options for creating a workspace file hasher.
16
+ */
17
+ export interface WorkspaceHasherOptions {
18
+ /** Hash algorithm to use (default: sha256) */
19
+ readonly algorithm?: 'sha256' | 'md5'
20
+ /** Whether to normalize line endings before hashing (default: true) */
21
+ readonly normalizeLineEndings?: boolean
22
+ }
23
+
24
+ /**
25
+ * Extended file hasher with workspace-specific utilities.
26
+ */
27
+ export interface WorkspaceFileHasher extends FileHasher {
28
+ /** Hash a JSON object consistently (keys sorted) */
29
+ readonly hashJson: (obj: unknown) => string
30
+ /** Hash multiple files and return combined hash */
31
+ readonly hashFiles: (paths: readonly string[]) => Promise<string>
32
+ }
33
+
34
+ /**
35
+ * Creates an extended file hasher with workspace-specific utilities.
36
+ *
37
+ * @param options - Hasher configuration options
38
+ * @returns A WorkspaceFileHasher instance
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const hasher = createWorkspaceHasher()
43
+ * const configHash = hasher.hashJson(analyzerConfig)
44
+ * const filesHash = await hasher.hashFiles(['package.json', 'tsconfig.json'])
45
+ * ```
46
+ */
47
+ export function createWorkspaceHasher(options: WorkspaceHasherOptions = {}): WorkspaceFileHasher {
48
+ const {algorithm = 'sha256'} = options
49
+ const baseHasher = createFileHasher(algorithm)
50
+
51
+ return {
52
+ ...baseHasher,
53
+
54
+ hashJson(obj: unknown): string {
55
+ const normalized = JSON.stringify(obj, Object.keys(obj as object).sort(), 0)
56
+ return baseHasher.hashContent(normalized)
57
+ },
58
+
59
+ async hashFiles(paths: readonly string[]): Promise<string> {
60
+ const hashes = await Promise.all(paths.map(async path => baseHasher.hash(path)))
61
+ const combined = hashes.join(':')
62
+ return baseHasher.hashContent(combined)
63
+ },
64
+ }
65
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Cache module exports for workspace analyzer.
3
+ *
4
+ * Provides caching infrastructure for incremental analysis including
5
+ * file hashing, change detection, and cache management.
6
+ */
7
+
8
+ // Cache manager
9
+ export {collectConfigFileStates, createCacheManager, initializeCache} from './cache-manager'
10
+
11
+ export type {CacheError, CacheErrorCode, CacheManager, CacheManagerOptions} from './cache-manager'
12
+
13
+ // Cache schema types and utilities
14
+ export {
15
+ CACHE_SCHEMA_VERSION,
16
+ CONFIG_FILE_PATTERNS,
17
+ createEmptyCache,
18
+ createFileAnalysisEntry,
19
+ createPackageAnalysisEntry,
20
+ DEFAULT_CACHE_OPTIONS,
21
+ } from './cache-schema'
22
+
23
+ export type {
24
+ AnalysisCache,
25
+ CachedFileAnalysis,
26
+ CachedFileState,
27
+ CachedPackageAnalysis,
28
+ CacheMetadata,
29
+ CacheOptions,
30
+ CacheStatistics,
31
+ CacheValidationResult,
32
+ } from './cache-schema'
33
+
34
+ // Change detector utilities
35
+ export {createAnalysisChangeDetector, createChangeDetector} from './change-detector'
36
+
37
+ export type {
38
+ AnalysisChangeDetector,
39
+ AnalysisChangeDetectorOptions,
40
+ ChangeDetector,
41
+ ChangeDetectorOptions,
42
+ } from './change-detector'
43
+
44
+ // File hasher utilities
45
+ export {createFileHasher, createWorkspaceHasher} from './file-hasher'
46
+
47
+ export type {FileHasher, WorkspaceFileHasher, WorkspaceHasherOptions} from './file-hasher'