@principal-ai/codebase-composition 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 (129) hide show
  1. package/README.md +67 -0
  2. package/dist/index.d.ts +9 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +23 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/modules/DependencyLayerModule.d.ts +44 -0
  7. package/dist/modules/DependencyLayerModule.d.ts.map +1 -0
  8. package/dist/modules/DependencyLayerModule.js +286 -0
  9. package/dist/modules/DependencyLayerModule.js.map +1 -0
  10. package/dist/modules/FileSystemModule.d.ts +30 -0
  11. package/dist/modules/FileSystemModule.d.ts.map +1 -0
  12. package/dist/modules/FileSystemModule.js +46 -0
  13. package/dist/modules/FileSystemModule.js.map +1 -0
  14. package/dist/modules/FileTypeLayerModule.d.ts +34 -0
  15. package/dist/modules/FileTypeLayerModule.d.ts.map +1 -0
  16. package/dist/modules/FileTypeLayerModule.js +169 -0
  17. package/dist/modules/FileTypeLayerModule.js.map +1 -0
  18. package/dist/modules/FrameworkLayerModule.d.ts +22 -0
  19. package/dist/modules/FrameworkLayerModule.d.ts.map +1 -0
  20. package/dist/modules/FrameworkLayerModule.js +388 -0
  21. package/dist/modules/FrameworkLayerModule.js.map +1 -0
  22. package/dist/modules/PackageLayerModule.d.ts +23 -0
  23. package/dist/modules/PackageLayerModule.d.ts.map +1 -0
  24. package/dist/modules/PackageLayerModule.js +810 -0
  25. package/dist/modules/PackageLayerModule.js.map +1 -0
  26. package/dist/modules/TypeExtractionModule.d.ts +37 -0
  27. package/dist/modules/TypeExtractionModule.d.ts.map +1 -0
  28. package/dist/modules/TypeExtractionModule.js +180 -0
  29. package/dist/modules/TypeExtractionModule.js.map +1 -0
  30. package/dist/modules/VersionControlLayerModule.d.ts +10 -0
  31. package/dist/modules/VersionControlLayerModule.d.ts.map +1 -0
  32. package/dist/modules/VersionControlLayerModule.js +32 -0
  33. package/dist/modules/VersionControlLayerModule.js.map +1 -0
  34. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts +4 -0
  35. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts.map +1 -0
  36. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js +7 -0
  37. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js.map +1 -0
  38. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts +15 -0
  39. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts.map +1 -0
  40. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js +3 -0
  41. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js.map +1 -0
  42. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts +34 -0
  43. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts.map +1 -0
  44. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js +23 -0
  45. package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js.map +1 -0
  46. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts +39 -0
  47. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts.map +1 -0
  48. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js +39 -0
  49. package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js.map +1 -0
  50. package/dist/modules/extractors/TypeScriptExtractor.d.ts +18 -0
  51. package/dist/modules/extractors/TypeScriptExtractor.d.ts.map +1 -0
  52. package/dist/modules/extractors/TypeScriptExtractor.js +361 -0
  53. package/dist/modules/extractors/TypeScriptExtractor.js.map +1 -0
  54. package/dist/modules/index.d.ts +13 -0
  55. package/dist/modules/index.d.ts.map +1 -0
  56. package/dist/modules/index.js +21 -0
  57. package/dist/modules/index.js.map +1 -0
  58. package/dist/providers/GitVersionControlProvider.d.ts +108 -0
  59. package/dist/providers/GitVersionControlProvider.d.ts.map +1 -0
  60. package/dist/providers/GitVersionControlProvider.js +380 -0
  61. package/dist/providers/GitVersionControlProvider.js.map +1 -0
  62. package/dist/providers/PackageManagerApiProvider.d.ts +78 -0
  63. package/dist/providers/PackageManagerApiProvider.d.ts.map +1 -0
  64. package/dist/providers/PackageManagerApiProvider.js +14 -0
  65. package/dist/providers/PackageManagerApiProvider.js.map +1 -0
  66. package/dist/providers/index.d.ts +4 -0
  67. package/dist/providers/index.d.ts.map +1 -0
  68. package/dist/providers/index.js +10 -0
  69. package/dist/providers/index.js.map +1 -0
  70. package/dist/services/FilesystemService.d.ts +59 -0
  71. package/dist/services/FilesystemService.d.ts.map +1 -0
  72. package/dist/services/FilesystemService.js +391 -0
  73. package/dist/services/FilesystemService.js.map +1 -0
  74. package/dist/services/index.d.ts +2 -0
  75. package/dist/services/index.d.ts.map +1 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/services/index.js.map +1 -0
  78. package/dist/types/file-system.d.ts +7 -0
  79. package/dist/types/file-system.d.ts.map +1 -0
  80. package/dist/types/file-system.js +7 -0
  81. package/dist/types/file-system.js.map +1 -0
  82. package/dist/types/index.d.ts +4 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +3 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/dist/types/layer-types.d.ts +187 -0
  87. package/dist/types/layer-types.d.ts.map +1 -0
  88. package/dist/types/layer-types.js +7 -0
  89. package/dist/types/layer-types.js.map +1 -0
  90. package/dist/types/version-control-layer.d.ts +53 -0
  91. package/dist/types/version-control-layer.d.ts.map +1 -0
  92. package/dist/types/version-control-layer.js +3 -0
  93. package/dist/types/version-control-layer.js.map +1 -0
  94. package/dist/types/workspace-boundaries.d.ts +17 -0
  95. package/dist/types/workspace-boundaries.d.ts.map +1 -0
  96. package/dist/types/workspace-boundaries.js +7 -0
  97. package/dist/types/workspace-boundaries.js.map +1 -0
  98. package/package.json +42 -0
  99. package/src/index.ts +62 -0
  100. package/src/modules/DependencyLayerModule.ts +329 -0
  101. package/src/modules/FileSystemModule.ts +65 -0
  102. package/src/modules/FileTypeLayerModule.ts +199 -0
  103. package/src/modules/FrameworkLayerModule.ts +437 -0
  104. package/src/modules/PackageLayerModule.ts +979 -0
  105. package/src/modules/TypeExtractionModule.test.ts +340 -0
  106. package/src/modules/TypeExtractionModule.ts +180 -0
  107. package/src/modules/VersionControlLayerModule.ts +31 -0
  108. package/src/modules/__fixtures__/typescript-packages/complex-types/package.json +6 -0
  109. package/src/modules/__fixtures__/typescript-packages/complex-types/src/index.ts +6 -0
  110. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/category.ts +15 -0
  111. package/src/modules/__fixtures__/typescript-packages/complex-types/src/models/product.ts +48 -0
  112. package/src/modules/__fixtures__/typescript-packages/javascript-only/index.js +18 -0
  113. package/src/modules/__fixtures__/typescript-packages/javascript-only/package.json +5 -0
  114. package/src/modules/__fixtures__/typescript-packages/simple-types/index.ts +53 -0
  115. package/src/modules/__fixtures__/typescript-packages/simple-types/package.json +6 -0
  116. package/src/modules/extractors/README.md +55 -0
  117. package/src/modules/extractors/TypeScriptExtractor.ts +409 -0
  118. package/src/modules/index.ts +13 -0
  119. package/src/providers/GitVersionControlProvider.ts +500 -0
  120. package/src/providers/PackageManagerApiProvider.ts +108 -0
  121. package/src/providers/README.md +88 -0
  122. package/src/providers/index.ts +17 -0
  123. package/src/services/FilesystemService.ts +530 -0
  124. package/src/services/index.ts +2 -0
  125. package/src/types/file-system.ts +11 -0
  126. package/src/types/index.ts +24 -0
  127. package/src/types/layer-types.ts +264 -0
  128. package/src/types/version-control-layer.ts +87 -0
  129. package/src/types/workspace-boundaries.ts +17 -0
@@ -0,0 +1,199 @@
1
+ import { FileTree } from '@principal-ai/repository-abstraction';
2
+ import { BaseLayer, FileSet } from '../types/layer-types';
3
+
4
+ export interface FileTypeInfo {
5
+ extension: string;
6
+ name: string;
7
+ count: number;
8
+ files: string[];
9
+ color: string;
10
+ icon: string;
11
+ category?: string;
12
+ }
13
+
14
+ // File type definitions with colors and lucide icon names
15
+ const FILE_TYPE_CONFIG: Record<
16
+ string,
17
+ { name: string; color: string; icon: string; category: string }
18
+ > = {
19
+ '.ts': { name: 'TypeScript', color: '#3178c6', icon: 'file-code', category: 'source' },
20
+ '.tsx': { name: 'TypeScript React', color: '#61dafb', icon: 'file-code-2', category: 'source' },
21
+ '.js': { name: 'JavaScript', color: '#f7df1e', icon: 'file-text', category: 'source' },
22
+ '.jsx': { name: 'JavaScript React', color: '#61dafb', icon: 'file-code-2', category: 'source' },
23
+ '.py': { name: 'Python', color: '#3776ab', icon: 'file-code', category: 'source' },
24
+ '.java': { name: 'Java', color: '#007396', icon: 'coffee', category: 'source' },
25
+ '.go': { name: 'Go', color: '#00add8', icon: 'file-code', category: 'source' },
26
+ '.rs': { name: 'Rust', color: '#dea584', icon: 'file-code', category: 'source' },
27
+ '.cpp': { name: 'C++', color: '#00599c', icon: 'cpu', category: 'source' },
28
+ '.c': { name: 'C', color: '#555555', icon: 'cpu', category: 'source' },
29
+ '.cs': { name: 'C#', color: '#239120', icon: 'file-code', category: 'source' },
30
+ '.php': { name: 'PHP', color: '#777bb4', icon: 'file-code', category: 'source' },
31
+ '.rb': { name: 'Ruby', color: '#cc342d', icon: 'gem', category: 'source' },
32
+ '.swift': { name: 'Swift', color: '#fa7343', icon: 'zap', category: 'source' },
33
+ '.kt': { name: 'Kotlin', color: '#7f52ff', icon: 'file-code', category: 'source' },
34
+ '.vue': { name: 'Vue', color: '#4fc08d', icon: 'triangle', category: 'source' },
35
+ '.svelte': { name: 'Svelte', color: '#ff3e00', icon: 'flame', category: 'source' },
36
+
37
+ // Config files
38
+ '.json': { name: 'JSON', color: '#90a4ae', icon: 'file-json', category: 'config' },
39
+ '.yaml': { name: 'YAML', color: '#cb171e', icon: 'file-text', category: 'config' },
40
+ '.yml': { name: 'YAML', color: '#cb171e', icon: 'file-text', category: 'config' },
41
+ '.toml': { name: 'TOML', color: '#9c4121', icon: 'settings', category: 'config' },
42
+ '.xml': { name: 'XML', color: '#0060ac', icon: 'code', category: 'config' },
43
+
44
+ // Documentation
45
+ '.md': { name: 'Markdown', color: '#083fa1', icon: 'file-text', category: 'docs' },
46
+ '.mdx': { name: 'MDX', color: '#fcb32c', icon: 'file-text', category: 'docs' },
47
+ '.txt': { name: 'Text', color: '#666666', icon: 'file-text', category: 'docs' },
48
+
49
+ // Styles
50
+ '.css': { name: 'CSS', color: '#1572b6', icon: 'palette', category: 'styles' },
51
+ '.scss': { name: 'SCSS', color: '#cc6699', icon: 'palette', category: 'styles' },
52
+ '.sass': { name: 'Sass', color: '#cc6699', icon: 'palette', category: 'styles' },
53
+ '.less': { name: 'Less', color: '#1d365d', icon: 'palette', category: 'styles' },
54
+
55
+ // Other
56
+ '.html': { name: 'HTML', color: '#e34c26', icon: 'globe', category: 'markup' },
57
+ '.svg': { name: 'SVG', color: '#ffb13b', icon: 'image', category: 'assets' },
58
+ '.png': { name: 'PNG', color: '#00bcd4', icon: 'image', category: 'assets' },
59
+ '.jpg': { name: 'JPEG', color: '#00bcd4', icon: 'image', category: 'assets' },
60
+ '.jpeg': { name: 'JPEG', color: '#00bcd4', icon: 'image', category: 'assets' },
61
+ '.gif': { name: 'GIF', color: '#00bcd4', icon: 'image', category: 'assets' },
62
+ '.sh': { name: 'Shell', color: '#4eaa25', icon: 'terminal', category: 'scripts' },
63
+ '.bat': { name: 'Batch', color: '#4d4d4d', icon: 'terminal', category: 'scripts' },
64
+ '.ps1': { name: 'PowerShell', color: '#012456', icon: 'terminal', category: 'scripts' },
65
+ };
66
+
67
+ // Default config for unknown file types
68
+ const DEFAULT_FILE_TYPE = {
69
+ color: '#9e9e9e',
70
+ icon: 'file',
71
+ category: 'other',
72
+ };
73
+
74
+ export class FileTypeLayerModule {
75
+ /**
76
+ * Extract file type information from the file tree
77
+ */
78
+ extractFileTypes(fileTree: FileTree): FileTypeInfo[] {
79
+ const fileTypeMap = new Map<string, FileTypeInfo>();
80
+
81
+ if (!fileTree.allFiles) {
82
+ return [];
83
+ }
84
+
85
+ // Group files by extension
86
+ for (const file of fileTree.allFiles) {
87
+ const extension = this.getFileExtension(file.name);
88
+ if (!extension) continue;
89
+
90
+ const filePath = file.relativePath || file.path;
91
+
92
+ // Skip common ignored paths
93
+ if (this.shouldIgnorePath(filePath)) continue;
94
+
95
+ if (!fileTypeMap.has(extension)) {
96
+ const config = FILE_TYPE_CONFIG[extension] || {
97
+ name: extension.toUpperCase().replace('.', ''),
98
+ ...DEFAULT_FILE_TYPE,
99
+ };
100
+
101
+ fileTypeMap.set(extension, {
102
+ extension,
103
+ name: config.name,
104
+ count: 0,
105
+ files: [],
106
+ color: config.color,
107
+ icon: config.icon,
108
+ category: config.category,
109
+ });
110
+ }
111
+
112
+ const typeInfo = fileTypeMap.get(extension)!;
113
+ typeInfo.count++;
114
+ typeInfo.files.push(filePath);
115
+ }
116
+
117
+ // Convert to array and sort by count
118
+ return Array.from(fileTypeMap.values()).sort((a, b) => b.count - a.count);
119
+ }
120
+
121
+ /**
122
+ * Create BaseLayer objects from file type information
123
+ */
124
+ createFileTypeLayers(fileTypes: FileTypeInfo[]): BaseLayer[] {
125
+ return fileTypes.map(fileType => {
126
+ const fileSet: FileSet = {
127
+ id: `files-${fileType.extension}`,
128
+ name: `${fileType.name} files`,
129
+ patterns: [
130
+ {
131
+ type: 'glob',
132
+ pattern: `**/*${fileType.extension}`,
133
+ description: `All ${fileType.extension} files`,
134
+ },
135
+ ],
136
+ matchedFiles: fileType.files,
137
+ fileCount: fileType.count,
138
+ };
139
+
140
+ const layer: BaseLayer = {
141
+ id: `file-type-${fileType.extension}`,
142
+ name: fileType.name,
143
+ type: 'file-type',
144
+ enabled: false,
145
+ derivedFrom: {
146
+ fileSets: [fileSet],
147
+ derivationType: 'presence',
148
+ description: `Files with ${fileType.extension} extension`,
149
+ },
150
+ color: fileType.color,
151
+ icon: fileType.icon,
152
+ pillar: 'viewLayers',
153
+ };
154
+
155
+ return layer;
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Get file extension from filename
161
+ */
162
+ private getFileExtension(filename: string): string {
163
+ const lastDot = filename.lastIndexOf('.');
164
+ if (lastDot === -1 || lastDot === filename.length - 1) {
165
+ return '';
166
+ }
167
+ return filename.slice(lastDot).toLowerCase();
168
+ }
169
+
170
+ /**
171
+ * Check if a path should be ignored
172
+ */
173
+ private shouldIgnorePath(path: string): boolean {
174
+ const ignoredPaths = [
175
+ 'node_modules',
176
+ '.git',
177
+ 'dist',
178
+ 'build',
179
+ 'coverage',
180
+ '.next',
181
+ '.nuxt',
182
+ 'target',
183
+ '__pycache__',
184
+ '.pytest_cache',
185
+ '.vscode',
186
+ '.idea',
187
+ ];
188
+
189
+ return ignoredPaths.some(ignored => path.includes(ignored));
190
+ }
191
+
192
+ /**
193
+ * Create file type layers from a file tree
194
+ */
195
+ async processFileTree(fileTree: FileTree): Promise<BaseLayer[]> {
196
+ const fileTypes = this.extractFileTypes(fileTree);
197
+ return this.createFileTypeLayers(fileTypes);
198
+ }
199
+ }
@@ -0,0 +1,437 @@
1
+ import { FrameworkLayer, PackageLayer, FileSet } from '../types/layer-types';
2
+
3
+ interface FrameworkDefinition {
4
+ name: string;
5
+ category: 'ui' | 'backend' | 'testing' | 'build' | 'styling';
6
+ deps: string[];
7
+ color: string;
8
+ icon: string; // Lucide icon name
9
+ }
10
+
11
+ // Framework definitions with their identifying dependencies
12
+ const FRAMEWORK_DEFINITIONS: Record<string, FrameworkDefinition> = {
13
+ // UI Frameworks
14
+ react: {
15
+ name: 'React',
16
+ category: 'ui',
17
+ deps: ['react', 'react-dom'],
18
+ color: '#61DAFB',
19
+ icon: 'atom',
20
+ },
21
+ vue: {
22
+ name: 'Vue',
23
+ category: 'ui',
24
+ deps: ['vue'],
25
+ color: '#4FC08D',
26
+ icon: 'triangle',
27
+ },
28
+ angular: {
29
+ name: 'Angular',
30
+ category: 'ui',
31
+ deps: ['@angular/core'],
32
+ color: '#DD0031',
33
+ icon: 'hexagon',
34
+ },
35
+ svelte: {
36
+ name: 'Svelte',
37
+ category: 'ui',
38
+ deps: ['svelte'],
39
+ color: '#FF3E00',
40
+ icon: 'flame',
41
+ },
42
+ solid: {
43
+ name: 'Solid',
44
+ category: 'ui',
45
+ deps: ['solid-js'],
46
+ color: '#2C4F7C',
47
+ icon: 'box',
48
+ },
49
+ preact: {
50
+ name: 'Preact',
51
+ category: 'ui',
52
+ deps: ['preact'],
53
+ color: '#673AB8',
54
+ icon: 'feather',
55
+ },
56
+
57
+ // Backend Frameworks
58
+ express: {
59
+ name: 'Express',
60
+ category: 'backend',
61
+ deps: ['express'],
62
+ color: '#000000',
63
+ icon: 'train',
64
+ },
65
+ fastify: {
66
+ name: 'Fastify',
67
+ category: 'backend',
68
+ deps: ['fastify'],
69
+ color: '#000000',
70
+ icon: 'zap',
71
+ },
72
+ nestjs: {
73
+ name: 'NestJS',
74
+ category: 'backend',
75
+ deps: ['@nestjs/core'],
76
+ color: '#E0234E',
77
+ icon: 'cat',
78
+ },
79
+ koa: {
80
+ name: 'Koa',
81
+ category: 'backend',
82
+ deps: ['koa'],
83
+ color: '#33333D',
84
+ icon: 'cloud',
85
+ },
86
+ hapi: {
87
+ name: 'Hapi',
88
+ category: 'backend',
89
+ deps: ['@hapi/hapi', 'hapi'],
90
+ color: '#F07100',
91
+ icon: 'shield',
92
+ },
93
+
94
+ // Python Backend Frameworks
95
+ django: {
96
+ name: 'Django',
97
+ category: 'backend',
98
+ deps: ['django'],
99
+ color: '#092E20',
100
+ icon: 'layers',
101
+ },
102
+ flask: {
103
+ name: 'Flask',
104
+ category: 'backend',
105
+ deps: ['flask'],
106
+ color: '#000000',
107
+ icon: 'flask-conical',
108
+ },
109
+ fastapi: {
110
+ name: 'FastAPI',
111
+ category: 'backend',
112
+ deps: ['fastapi'],
113
+ color: '#009688',
114
+ icon: 'zap',
115
+ },
116
+
117
+ // Testing Frameworks
118
+ jest: {
119
+ name: 'Jest',
120
+ category: 'testing',
121
+ deps: ['jest'],
122
+ color: '#C21325',
123
+ icon: 'test-tube',
124
+ },
125
+ mocha: {
126
+ name: 'Mocha',
127
+ category: 'testing',
128
+ deps: ['mocha'],
129
+ color: '#8D6748',
130
+ icon: 'coffee',
131
+ },
132
+ vitest: {
133
+ name: 'Vitest',
134
+ category: 'testing',
135
+ deps: ['vitest'],
136
+ color: '#729B1B',
137
+ icon: 'flask-round',
138
+ },
139
+ cypress: {
140
+ name: 'Cypress',
141
+ category: 'testing',
142
+ deps: ['cypress'],
143
+ color: '#17202C',
144
+ icon: 'eye',
145
+ },
146
+ playwright: {
147
+ name: 'Playwright',
148
+ category: 'testing',
149
+ deps: ['@playwright/test', 'playwright'],
150
+ color: '#2EAD33',
151
+ icon: 'play',
152
+ },
153
+ pytest: {
154
+ name: 'Pytest',
155
+ category: 'testing',
156
+ deps: ['pytest'],
157
+ color: '#0A9EDC',
158
+ icon: 'check-circle',
159
+ },
160
+
161
+ // Build Tools
162
+ webpack: {
163
+ name: 'Webpack',
164
+ category: 'build',
165
+ deps: ['webpack'],
166
+ color: '#8DD6F9',
167
+ icon: 'package',
168
+ },
169
+ vite: {
170
+ name: 'Vite',
171
+ category: 'build',
172
+ deps: ['vite'],
173
+ color: '#646CFF',
174
+ icon: 'zap',
175
+ },
176
+ rollup: {
177
+ name: 'Rollup',
178
+ category: 'build',
179
+ deps: ['rollup'],
180
+ color: '#FF6533',
181
+ icon: 'package-2',
182
+ },
183
+ parcel: {
184
+ name: 'Parcel',
185
+ category: 'build',
186
+ deps: ['parcel'],
187
+ color: '#E7BF7E',
188
+ icon: 'package',
189
+ },
190
+ esbuild: {
191
+ name: 'ESBuild',
192
+ category: 'build',
193
+ deps: ['esbuild'],
194
+ color: '#FFCF00',
195
+ icon: 'cpu',
196
+ },
197
+
198
+ // Styling
199
+ tailwind: {
200
+ name: 'Tailwind CSS',
201
+ category: 'styling',
202
+ deps: ['tailwindcss'],
203
+ color: '#06B6D4',
204
+ icon: 'wind',
205
+ },
206
+ styledComponents: {
207
+ name: 'Styled Components',
208
+ category: 'styling',
209
+ deps: ['styled-components'],
210
+ color: '#DB7093',
211
+ icon: 'palette',
212
+ },
213
+ emotion: {
214
+ name: 'Emotion',
215
+ category: 'styling',
216
+ deps: ['@emotion/react', '@emotion/styled'],
217
+ color: '#D36AC7',
218
+ icon: 'heart',
219
+ },
220
+ sass: {
221
+ name: 'Sass/SCSS',
222
+ category: 'styling',
223
+ deps: ['sass', 'node-sass'],
224
+ color: '#CC6699',
225
+ icon: 'palette',
226
+ },
227
+ };
228
+
229
+ // Common Python frameworks that might not be in package.json format
230
+ const PYTHON_FRAMEWORKS = new Set([
231
+ 'django',
232
+ 'flask',
233
+ 'fastapi',
234
+ 'pyramid',
235
+ 'tornado',
236
+ 'aiohttp',
237
+ 'bottle',
238
+ 'cherrypy',
239
+ 'falcon',
240
+ 'sanic',
241
+ ]);
242
+
243
+ export class FrameworkLayerModule {
244
+ /**
245
+ * Detect frameworks from package layers
246
+ */
247
+ detectFrameworks(packageLayers: PackageLayer[]): FrameworkLayer[] {
248
+ const frameworkMap = new Map<
249
+ string,
250
+ {
251
+ definition: FrameworkDefinition;
252
+ usedBy: Set<string>;
253
+ packagePaths: Set<string>;
254
+ }
255
+ >();
256
+
257
+ // Check each package for framework dependencies
258
+ packageLayers.forEach(packageLayer => {
259
+ const allDeps = {
260
+ ...packageLayer.packageData.dependencies,
261
+ ...packageLayer.packageData.devDependencies,
262
+ ...packageLayer.packageData.peerDependencies,
263
+ };
264
+
265
+ // Check each framework definition
266
+ Object.entries(FRAMEWORK_DEFINITIONS).forEach(([key, framework]) => {
267
+ const hasFramework = framework.deps.some(dep => dep in allDeps);
268
+
269
+ if (hasFramework) {
270
+ if (!frameworkMap.has(key)) {
271
+ frameworkMap.set(key, {
272
+ definition: framework,
273
+ usedBy: new Set(),
274
+ packagePaths: new Set(),
275
+ });
276
+ }
277
+
278
+ const entry = frameworkMap.get(key)!;
279
+ entry.usedBy.add(packageLayer.packageData.name);
280
+ entry.packagePaths.add(packageLayer.packageData.path);
281
+ }
282
+ });
283
+
284
+ // Check for Python frameworks in dependencies
285
+ if (packageLayer.type === 'python') {
286
+ Object.keys(allDeps).forEach(dep => {
287
+ const depLower = dep.toLowerCase();
288
+ if (PYTHON_FRAMEWORKS.has(depLower)) {
289
+ // Find if we have a definition for this
290
+ const definitionEntry = Object.entries(FRAMEWORK_DEFINITIONS).find(
291
+ ([_, def]) => def.name.toLowerCase() === depLower,
292
+ );
293
+
294
+ if (definitionEntry) {
295
+ const [key, definition] = definitionEntry;
296
+ if (!frameworkMap.has(key)) {
297
+ frameworkMap.set(key, {
298
+ definition,
299
+ usedBy: new Set(),
300
+ packagePaths: new Set(),
301
+ });
302
+ }
303
+
304
+ const entry = frameworkMap.get(key)!;
305
+ entry.usedBy.add(packageLayer.packageData.name);
306
+ entry.packagePaths.add(packageLayer.packageData.path);
307
+ }
308
+ }
309
+ });
310
+ }
311
+ });
312
+
313
+ // Convert to FrameworkLayer array
314
+ return Array.from(frameworkMap.entries()).map(([key, entry]) => {
315
+ const { definition, usedBy, packagePaths } = entry;
316
+
317
+ // Create file sets for framework detection
318
+ const depFileSets: FileSet[] =
319
+ packagePaths.size > 0
320
+ ? [
321
+ {
322
+ id: `framework-deps-${key}`,
323
+ name: `${definition.name} dependencies`,
324
+ patterns: Array.from(packagePaths).map(path => ({
325
+ type: 'exact' as const,
326
+ pattern: path === '.' ? 'package.json' : `${path}/package.json`,
327
+ description: `Package manifest containing ${definition.name} dependencies`,
328
+ })),
329
+ matchedFiles: Array.from(packagePaths).map(path =>
330
+ path === '.' ? 'package.json' : `${path}/package.json`,
331
+ ),
332
+ fileCount: packagePaths.size,
333
+ },
334
+ ]
335
+ : [];
336
+
337
+ const layer: FrameworkLayer = {
338
+ id: `framework-${key}`,
339
+ name: definition.name,
340
+ type: 'framework',
341
+ enabled: true,
342
+ derivedFrom: {
343
+ fileSets: depFileSets,
344
+ derivationType: 'content',
345
+ description: `${definition.name} framework detected from package dependencies`,
346
+ contentExtraction: {
347
+ method: 'parse',
348
+ parser: {
349
+ format: 'json',
350
+ paths: definition.deps
351
+ .map(dep => `dependencies.${dep}`)
352
+ .concat(definition.deps.map(dep => `devDependencies.${dep}`))
353
+ .concat(definition.deps.map(dep => `peerDependencies.${dep}`)),
354
+ },
355
+ },
356
+ },
357
+ frameworkData: {
358
+ name: definition.name,
359
+ category: definition.category,
360
+ usedBy: Array.from(usedBy),
361
+ },
362
+ color: definition.color,
363
+ icon: definition.icon,
364
+ pillar:
365
+ definition.category === 'testing'
366
+ ? 'validation'
367
+ : definition.category === 'build'
368
+ ? 'foundationHealth'
369
+ : 'viewLayers',
370
+ };
371
+
372
+ return layer;
373
+ });
374
+ }
375
+
376
+ /**
377
+ * Get framework compatibility information
378
+ */
379
+ analyzeCompatibility(frameworks: FrameworkLayer[]): {
380
+ conflicts: Array<{ framework1: string; framework2: string; reason: string }>;
381
+ recommendations: Array<{ message: string; severity: 'info' | 'warning' }>;
382
+ } {
383
+ const compatibility = {
384
+ conflicts: [] as Array<{ framework1: string; framework2: string; reason: string }>,
385
+ recommendations: [] as Array<{ message: string; severity: 'info' | 'warning' }>,
386
+ };
387
+
388
+ // Check for multiple UI frameworks
389
+ const uiFrameworks = frameworks.filter(f => f.frameworkData.category === 'ui');
390
+ if (uiFrameworks.length > 1) {
391
+ // Check if they're used in the same packages
392
+ for (let i = 0; i < uiFrameworks.length - 1; i++) {
393
+ for (let j = i + 1; j < uiFrameworks.length; j++) {
394
+ const sharedPackages = uiFrameworks[i].frameworkData.usedBy.filter((pkg: string) =>
395
+ uiFrameworks[j].frameworkData.usedBy.includes(pkg),
396
+ );
397
+
398
+ if (sharedPackages.length > 0) {
399
+ compatibility.conflicts.push({
400
+ framework1: uiFrameworks[i].name,
401
+ framework2: uiFrameworks[j].name,
402
+ reason: `Both UI frameworks found in: ${sharedPackages.join(', ')}`,
403
+ });
404
+ }
405
+ }
406
+ }
407
+
408
+ if (uiFrameworks.length > 2) {
409
+ compatibility.recommendations.push({
410
+ message: `${uiFrameworks.length} UI frameworks detected. Consider consolidating to reduce complexity.`,
411
+ severity: 'warning',
412
+ });
413
+ }
414
+ }
415
+
416
+ // Check for multiple backend frameworks
417
+ const backendFrameworks = frameworks.filter(f => f.frameworkData.category === 'backend');
418
+ if (backendFrameworks.length > 2) {
419
+ compatibility.recommendations.push({
420
+ message: `Multiple backend frameworks detected: ${backendFrameworks.map(f => f.name).join(', ')}`,
421
+ severity: 'info',
422
+ });
423
+ }
424
+
425
+ // Check for missing testing frameworks
426
+ const hasTestingFramework = frameworks.some(f => f.frameworkData.category === 'testing');
427
+ if (!hasTestingFramework && frameworks.length > 0) {
428
+ compatibility.recommendations.push({
429
+ message:
430
+ 'No testing framework detected. Consider adding Jest, Vitest, or another testing solution.',
431
+ severity: 'info',
432
+ });
433
+ }
434
+
435
+ return compatibility;
436
+ }
437
+ }