@principal-ai/codebase-composition 0.2.8 → 0.2.10
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.
- package/package.json +5 -4
- package/dist/adapters/NodeFileSystemAdapter.d.ts +0 -28
- package/dist/adapters/NodeFileSystemAdapter.d.ts.map +0 -1
- package/dist/adapters/NodeFileSystemAdapter.js +0 -128
- package/dist/adapters/NodeFileSystemAdapter.js.map +0 -1
- package/dist/helpers/QualityMetricsCalculator.d.ts +0 -46
- package/dist/helpers/QualityMetricsCalculator.d.ts.map +0 -1
- package/dist/helpers/QualityMetricsCalculator.js +0 -161
- package/dist/helpers/QualityMetricsCalculator.js.map +0 -1
- package/dist/helpers/packageLayerHelpers.d.ts +0 -30
- package/dist/helpers/packageLayerHelpers.d.ts.map +0 -1
- package/dist/helpers/packageLayerHelpers.js +0 -88
- package/dist/helpers/packageLayerHelpers.js.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -14
- package/dist/index.js.map +0 -1
- package/dist/modules/DependencyLayerModule.d.ts +0 -44
- package/dist/modules/DependencyLayerModule.d.ts.map +0 -1
- package/dist/modules/DependencyLayerModule.js +0 -282
- package/dist/modules/DependencyLayerModule.js.map +0 -1
- package/dist/modules/FileSystemModule.d.ts +0 -30
- package/dist/modules/FileSystemModule.d.ts.map +0 -1
- package/dist/modules/FileSystemModule.js +0 -43
- package/dist/modules/FileSystemModule.js.map +0 -1
- package/dist/modules/FileTypeLayerModule.d.ts +0 -34
- package/dist/modules/FileTypeLayerModule.d.ts.map +0 -1
- package/dist/modules/FileTypeLayerModule.js +0 -165
- package/dist/modules/FileTypeLayerModule.js.map +0 -1
- package/dist/modules/FrameworkLayerModule.d.ts +0 -22
- package/dist/modules/FrameworkLayerModule.d.ts.map +0 -1
- package/dist/modules/FrameworkLayerModule.js +0 -384
- package/dist/modules/FrameworkLayerModule.js.map +0 -1
- package/dist/modules/PackageLayerModule.d.ts +0 -23
- package/dist/modules/PackageLayerModule.d.ts.map +0 -1
- package/dist/modules/PackageLayerModule.js +0 -775
- package/dist/modules/PackageLayerModule.js.map +0 -1
- package/dist/modules/TypeExtractionModule.d.ts +0 -37
- package/dist/modules/TypeExtractionModule.d.ts.map +0 -1
- package/dist/modules/TypeExtractionModule.js +0 -141
- package/dist/modules/TypeExtractionModule.js.map +0 -1
- package/dist/modules/VersionControlLayerModule.d.ts +0 -10
- package/dist/modules/VersionControlLayerModule.d.ts.map +0 -1
- package/dist/modules/VersionControlLayerModule.js +0 -26
- package/dist/modules/VersionControlLayerModule.js.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts +0 -4
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.d.ts.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js +0 -3
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/index.js.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts +0 -15
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.d.ts.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js +0 -2
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/category.js.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts +0 -34
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.d.ts.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js +0 -19
- package/dist/modules/__fixtures__/typescript-packages/complex-types/src/models/product.js.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts +0 -39
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.d.ts.map +0 -1
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js +0 -36
- package/dist/modules/__fixtures__/typescript-packages/simple-types/index.js.map +0 -1
- package/dist/modules/extractors/TypeScriptExtractor.d.ts +0 -18
- package/dist/modules/extractors/TypeScriptExtractor.d.ts.map +0 -1
- package/dist/modules/extractors/TypeScriptExtractor.js +0 -322
- package/dist/modules/extractors/TypeScriptExtractor.js.map +0 -1
- package/dist/modules/index.d.ts +0 -13
- package/dist/modules/index.d.ts.map +0 -1
- package/dist/modules/index.js +0 -10
- package/dist/modules/index.js.map +0 -1
- package/dist/providers/GitVersionControlProvider.d.ts +0 -108
- package/dist/providers/GitVersionControlProvider.d.ts.map +0 -1
- package/dist/providers/GitVersionControlProvider.js +0 -375
- package/dist/providers/GitVersionControlProvider.js.map +0 -1
- package/dist/providers/PackageManagerApiProvider.d.ts +0 -78
- package/dist/providers/PackageManagerApiProvider.d.ts.map +0 -1
- package/dist/providers/PackageManagerApiProvider.js +0 -10
- package/dist/providers/PackageManagerApiProvider.js.map +0 -1
- package/dist/providers/index.d.ts +0 -4
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -5
- package/dist/providers/index.js.map +0 -1
- package/dist/services/FilesystemService.d.ts +0 -59
- package/dist/services/FilesystemService.d.ts.map +0 -1
- package/dist/services/FilesystemService.js +0 -394
- package/dist/services/FilesystemService.js.map +0 -1
- package/dist/services/index.d.ts +0 -2
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/index.js +0 -3
- package/dist/services/index.js.map +0 -1
- package/dist/types/file-system.d.ts +0 -6
- package/dist/types/file-system.d.ts.map +0 -1
- package/dist/types/file-system.js +0 -6
- package/dist/types/file-system.js.map +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/types/layer-types.d.ts +0 -205
- package/dist/types/layer-types.d.ts.map +0 -1
- package/dist/types/layer-types.js +0 -6
- package/dist/types/layer-types.js.map +0 -1
- package/dist/types/version-control-layer.d.ts +0 -53
- package/dist/types/version-control-layer.d.ts.map +0 -1
- package/dist/types/version-control-layer.js +0 -2
- package/dist/types/version-control-layer.js.map +0 -1
- package/dist/types/workspace-boundaries.d.ts +0 -17
- package/dist/types/workspace-boundaries.d.ts.map +0 -1
- package/dist/types/workspace-boundaries.js +0 -6
- package/dist/types/workspace-boundaries.js.map +0 -1
|
@@ -1,775 +0,0 @@
|
|
|
1
|
-
import * as TOML from 'js-toml';
|
|
2
|
-
import { parsePipRequirementsFile } from 'pip-requirements-js';
|
|
3
|
-
import { QualityMetricsCalculator } from '../helpers/QualityMetricsCalculator.js';
|
|
4
|
-
// Node.js package.json parser
|
|
5
|
-
class NodePackageParser {
|
|
6
|
-
manifestFileName = 'package.json';
|
|
7
|
-
packageType = 'node';
|
|
8
|
-
canParse(filename) {
|
|
9
|
-
return filename.endsWith('package.json');
|
|
10
|
-
}
|
|
11
|
-
parseContent(rawContent) {
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(rawContent);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
extractPackageData(content, path) {
|
|
20
|
-
// Handle cases where path is just "package.json" (root package)
|
|
21
|
-
// or a proper path like "packages/foo/package.json"
|
|
22
|
-
let packagePath;
|
|
23
|
-
if (path === 'package.json') {
|
|
24
|
-
packagePath = ''; // Root directory - empty string for root
|
|
25
|
-
}
|
|
26
|
-
else if (path.endsWith('/package.json')) {
|
|
27
|
-
packagePath = path.slice(0, -13); // Remove '/package.json'
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
// Shouldn't happen, but handle gracefully
|
|
31
|
-
packagePath = path;
|
|
32
|
-
}
|
|
33
|
-
const availableCommands = this.extractCommands(content, packagePath);
|
|
34
|
-
return {
|
|
35
|
-
name: content.name || 'unnamed',
|
|
36
|
-
version: content.version,
|
|
37
|
-
path: packagePath,
|
|
38
|
-
manifestPath: path, // The full relative path to package.json
|
|
39
|
-
packageManager: 'unknown',
|
|
40
|
-
dependencies: content.dependencies || {},
|
|
41
|
-
devDependencies: content.devDependencies || {},
|
|
42
|
-
peerDependencies: content.peerDependencies || {},
|
|
43
|
-
isMonorepoRoot: !!content.workspaces,
|
|
44
|
-
isWorkspace: false, // Will be determined by context
|
|
45
|
-
parentPackage: undefined,
|
|
46
|
-
availableCommands,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
extractCommands(content, packagePath) {
|
|
50
|
-
const commands = [];
|
|
51
|
-
// Extract npm scripts
|
|
52
|
-
if (content.scripts) {
|
|
53
|
-
Object.entries(content.scripts).forEach(([name, script]) => {
|
|
54
|
-
// Use npm as default, will be updated with correct package manager later
|
|
55
|
-
commands.push({
|
|
56
|
-
name,
|
|
57
|
-
command: `npm run ${name}`,
|
|
58
|
-
description: script.length > 50 ? script.substring(0, 47) + '...' : script,
|
|
59
|
-
type: 'script',
|
|
60
|
-
workingDirectory: packagePath,
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
// Add standard npm commands - these will also be updated with correct package manager
|
|
65
|
-
const standardCommands = [
|
|
66
|
-
{ name: 'install', command: 'npm install', description: 'Install dependencies' },
|
|
67
|
-
{ name: 'ci', command: 'npm ci', description: 'Clean install dependencies' },
|
|
68
|
-
{ name: 'update', command: 'npm update', description: 'Update dependencies' },
|
|
69
|
-
];
|
|
70
|
-
standardCommands.forEach(cmd => {
|
|
71
|
-
commands.push({
|
|
72
|
-
...cmd,
|
|
73
|
-
type: 'standard',
|
|
74
|
-
workingDirectory: packagePath,
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
return commands;
|
|
78
|
-
}
|
|
79
|
-
detectWorkspaces(content) {
|
|
80
|
-
if (Array.isArray(content.workspaces)) {
|
|
81
|
-
return content.workspaces;
|
|
82
|
-
}
|
|
83
|
-
if (content.workspaces &&
|
|
84
|
-
typeof content.workspaces === 'object' &&
|
|
85
|
-
'packages' in content.workspaces) {
|
|
86
|
-
return content.workspaces.packages || null;
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
detectPackageManager(content, lockFiles) {
|
|
91
|
-
// Check for packageManager field (corepack)
|
|
92
|
-
if (content.packageManager) {
|
|
93
|
-
const pm = content.packageManager.split('@')[0];
|
|
94
|
-
if (['npm', 'yarn', 'pnpm'].includes(pm)) {
|
|
95
|
-
return pm;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Check lock files
|
|
99
|
-
if (lockFiles.some((f) => f.endsWith('yarn.lock')))
|
|
100
|
-
return 'yarn';
|
|
101
|
-
if (lockFiles.some((f) => f.endsWith('pnpm-lock.yaml')))
|
|
102
|
-
return 'pnpm';
|
|
103
|
-
if (lockFiles.some((f) => f.endsWith('package-lock.json')))
|
|
104
|
-
return 'npm';
|
|
105
|
-
// Default to npm instead of unknown for Node packages
|
|
106
|
-
return 'npm';
|
|
107
|
-
}
|
|
108
|
-
detectConfigs(packagePath, fileTree, manifestContent) {
|
|
109
|
-
const configs = {};
|
|
110
|
-
// Define config patterns for Node/JavaScript projects
|
|
111
|
-
const configPatterns = {
|
|
112
|
-
knip: [
|
|
113
|
-
'knip.json',
|
|
114
|
-
'knip.jsonc',
|
|
115
|
-
'.knip.json',
|
|
116
|
-
'knip.config.js',
|
|
117
|
-
'knip.config.ts',
|
|
118
|
-
'knip.config.mjs',
|
|
119
|
-
],
|
|
120
|
-
eslint: [
|
|
121
|
-
'.eslintrc',
|
|
122
|
-
'.eslintrc.js',
|
|
123
|
-
'.eslintrc.json',
|
|
124
|
-
'.eslintrc.yml',
|
|
125
|
-
'.eslintrc.yaml',
|
|
126
|
-
'eslint.config.js',
|
|
127
|
-
'eslint.config.mjs',
|
|
128
|
-
'.eslintrc.cjs',
|
|
129
|
-
],
|
|
130
|
-
prettier: [
|
|
131
|
-
'.prettierrc',
|
|
132
|
-
'.prettierrc.js',
|
|
133
|
-
'.prettierrc.json',
|
|
134
|
-
'.prettierrc.yml',
|
|
135
|
-
'.prettierrc.yaml',
|
|
136
|
-
'prettier.config.js',
|
|
137
|
-
'.prettierrc.toml',
|
|
138
|
-
],
|
|
139
|
-
typescript: ['tsconfig.json', 'tsconfig.base.json'],
|
|
140
|
-
jest: [
|
|
141
|
-
'jest.config.js',
|
|
142
|
-
'jest.config.ts',
|
|
143
|
-
'jest.config.mjs',
|
|
144
|
-
'jest.config.cjs',
|
|
145
|
-
'jest.config.json',
|
|
146
|
-
],
|
|
147
|
-
vitest: ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs'],
|
|
148
|
-
webpack: ['webpack.config.js', 'webpack.config.ts', 'webpack.config.mjs'],
|
|
149
|
-
vite: ['vite.config.js', 'vite.config.ts', 'vite.config.mjs'],
|
|
150
|
-
rollup: ['rollup.config.js', 'rollup.config.ts', 'rollup.config.mjs'],
|
|
151
|
-
babel: ['.babelrc', '.babelrc.js', '.babelrc.json', 'babel.config.js', 'babel.config.json'],
|
|
152
|
-
dockerfile: ['Dockerfile', 'dockerfile', 'Dockerfile.dev', 'Dockerfile.prod'],
|
|
153
|
-
gitignore: ['.gitignore'],
|
|
154
|
-
editorconfig: ['.editorconfig'],
|
|
155
|
-
};
|
|
156
|
-
// Normalize package path
|
|
157
|
-
const normalizedPath = packagePath === '.' || packagePath === ''
|
|
158
|
-
? ''
|
|
159
|
-
: packagePath.endsWith('/')
|
|
160
|
-
? packagePath
|
|
161
|
-
: packagePath + '/';
|
|
162
|
-
// Check files in tree
|
|
163
|
-
if (fileTree.allFiles) {
|
|
164
|
-
for (const [tool, patterns] of Object.entries(configPatterns)) {
|
|
165
|
-
for (const pattern of patterns) {
|
|
166
|
-
const fullPath = normalizedPath + pattern;
|
|
167
|
-
const fileExists = fileTree.allFiles.some(f => f.path === fullPath || f.relativePath === fullPath);
|
|
168
|
-
if (fileExists) {
|
|
169
|
-
configs[tool] = {
|
|
170
|
-
path: fullPath,
|
|
171
|
-
exists: true,
|
|
172
|
-
type: this.getConfigFileType(pattern),
|
|
173
|
-
};
|
|
174
|
-
break; // Found config for this tool
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// Check for inline configs in package.json
|
|
180
|
-
if (manifestContent) {
|
|
181
|
-
if (manifestContent.eslintConfig) {
|
|
182
|
-
configs.eslint = {
|
|
183
|
-
path: normalizedPath + 'package.json',
|
|
184
|
-
exists: true,
|
|
185
|
-
type: 'json',
|
|
186
|
-
isInline: true,
|
|
187
|
-
inlineField: 'eslintConfig',
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
if (manifestContent.prettier) {
|
|
191
|
-
configs.prettier = {
|
|
192
|
-
path: normalizedPath + 'package.json',
|
|
193
|
-
exists: true,
|
|
194
|
-
type: 'json',
|
|
195
|
-
isInline: true,
|
|
196
|
-
inlineField: 'prettier',
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (manifestContent.jest) {
|
|
200
|
-
configs.jest = {
|
|
201
|
-
path: normalizedPath + 'package.json',
|
|
202
|
-
exists: true,
|
|
203
|
-
type: 'json',
|
|
204
|
-
isInline: true,
|
|
205
|
-
inlineField: 'jest',
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
if (manifestContent.babel) {
|
|
209
|
-
configs.babel = {
|
|
210
|
-
path: normalizedPath + 'package.json',
|
|
211
|
-
exists: true,
|
|
212
|
-
type: 'json',
|
|
213
|
-
isInline: true,
|
|
214
|
-
inlineField: 'babel',
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return configs;
|
|
219
|
-
}
|
|
220
|
-
getConfigFileType(filename) {
|
|
221
|
-
if (filename.endsWith('.json') || filename.endsWith('.jsonc'))
|
|
222
|
-
return 'json';
|
|
223
|
-
if (filename.endsWith('.yml') || filename.endsWith('.yaml'))
|
|
224
|
-
return 'yaml';
|
|
225
|
-
if (filename.endsWith('.toml'))
|
|
226
|
-
return 'toml';
|
|
227
|
-
if (filename.endsWith('.js') || filename.endsWith('.mjs') || filename.endsWith('.cjs'))
|
|
228
|
-
return 'js';
|
|
229
|
-
if (filename.endsWith('.ts') || filename.endsWith('.mts') || filename.endsWith('.cts'))
|
|
230
|
-
return 'ts';
|
|
231
|
-
if (filename.endsWith('.ini'))
|
|
232
|
-
return 'ini';
|
|
233
|
-
return 'custom';
|
|
234
|
-
}
|
|
235
|
-
detectDocsFolder(packagePath, fileTree, _manifestContent) {
|
|
236
|
-
// Look for common documentation folder names
|
|
237
|
-
const docsFolderNames = ['docs', 'documentation', 'doc'];
|
|
238
|
-
// Normalize package path
|
|
239
|
-
let packagePrefix = packagePath === '.' || packagePath === '' ? '' : packagePath;
|
|
240
|
-
if (packagePrefix && !packagePrefix.endsWith('/')) {
|
|
241
|
-
packagePrefix += '/';
|
|
242
|
-
}
|
|
243
|
-
if (!fileTree.allFiles)
|
|
244
|
-
return undefined;
|
|
245
|
-
// Look for root-level docs folders first (preferred)
|
|
246
|
-
for (const folderName of docsFolderNames) {
|
|
247
|
-
const expectedPath = packagePrefix + folderName;
|
|
248
|
-
const hasFiles = fileTree.allFiles.some(file => file.path?.toLowerCase().startsWith(expectedPath.toLowerCase() + '/'));
|
|
249
|
-
if (hasFiles) {
|
|
250
|
-
// Find the actual folder name with correct case
|
|
251
|
-
const actualPath = fileTree.allFiles.find(file => file.path?.toLowerCase().startsWith(expectedPath.toLowerCase() + '/'))?.path;
|
|
252
|
-
if (actualPath) {
|
|
253
|
-
const actualFolderPath = actualPath.substring(0, actualPath.indexOf('/', packagePrefix.length));
|
|
254
|
-
return actualFolderPath.substring(packagePrefix.length);
|
|
255
|
-
}
|
|
256
|
-
return folderName; // Fallback to lowercase version
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
// If no explicit docs folder found, look for directories with high concentration of docs
|
|
260
|
-
const potentialFolders = new Map();
|
|
261
|
-
for (const file of fileTree.allFiles) {
|
|
262
|
-
if (!file.path)
|
|
263
|
-
continue;
|
|
264
|
-
// Only consider files within this package
|
|
265
|
-
if (packagePrefix !== '' && !file.path.startsWith(packagePrefix)) {
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
const relativePath = packagePrefix === '' ? file.path : file.path.substring(packagePrefix.length);
|
|
269
|
-
const pathParts = relativePath.split('/');
|
|
270
|
-
// Only look at direct subdirectories (depth 1)
|
|
271
|
-
if (pathParts.length === 2) {
|
|
272
|
-
const dirName = pathParts[0];
|
|
273
|
-
const fileName = pathParts[1];
|
|
274
|
-
if (!potentialFolders.has(dirName)) {
|
|
275
|
-
potentialFolders.set(dirName, { docFiles: 0, totalFiles: 0 });
|
|
276
|
-
}
|
|
277
|
-
const stats = potentialFolders.get(dirName);
|
|
278
|
-
stats.totalFiles++;
|
|
279
|
-
if (this.isDocumentationFile(fileName)) {
|
|
280
|
-
stats.docFiles++;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// Find the best candidate folder (highest percentage of docs, minimum 2 doc files)
|
|
285
|
-
let bestFolder;
|
|
286
|
-
let bestScore = 0.5; // Must be at least 50% docs
|
|
287
|
-
for (const [folderName, stats] of Array.from(potentialFolders.entries())) {
|
|
288
|
-
if (stats.docFiles >= 2) {
|
|
289
|
-
const score = stats.docFiles / stats.totalFiles;
|
|
290
|
-
if (score > bestScore) {
|
|
291
|
-
bestScore = score;
|
|
292
|
-
bestFolder = folderName;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return bestFolder;
|
|
297
|
-
}
|
|
298
|
-
isDocumentationFile(fileName) {
|
|
299
|
-
const lowerName = fileName.toLowerCase();
|
|
300
|
-
// Documentation file extensions
|
|
301
|
-
const docExtensions = ['.md', '.mdx', '.rst', '.txt', '.adoc', '.asciidoc'];
|
|
302
|
-
const hasDocExtension = docExtensions.some(ext => lowerName.endsWith(ext));
|
|
303
|
-
if (hasDocExtension)
|
|
304
|
-
return true;
|
|
305
|
-
// Documentation file name patterns
|
|
306
|
-
const docPatterns = [
|
|
307
|
-
/^readme/i,
|
|
308
|
-
/^changelog/i,
|
|
309
|
-
/^changes/i,
|
|
310
|
-
/^history/i,
|
|
311
|
-
/^license/i,
|
|
312
|
-
/^copying/i,
|
|
313
|
-
/^install/i,
|
|
314
|
-
/^usage/i,
|
|
315
|
-
/^guide/i,
|
|
316
|
-
/^tutorial/i,
|
|
317
|
-
/^manual/i,
|
|
318
|
-
/^faq/i,
|
|
319
|
-
/^api/i,
|
|
320
|
-
/^reference/i,
|
|
321
|
-
/^spec/i,
|
|
322
|
-
/^specification/i,
|
|
323
|
-
];
|
|
324
|
-
return docPatterns.some(pattern => pattern.test(fileName));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Python pyproject.toml parser - simplified version
|
|
328
|
-
class PythonPackageParser {
|
|
329
|
-
manifestFileName = 'pyproject.toml';
|
|
330
|
-
packageType = 'python';
|
|
331
|
-
canParse(filename) {
|
|
332
|
-
return (filename.endsWith('pyproject.toml') ||
|
|
333
|
-
filename.endsWith('setup.py') ||
|
|
334
|
-
filename.endsWith('requirements.txt'));
|
|
335
|
-
}
|
|
336
|
-
parseContent(rawContent) {
|
|
337
|
-
try {
|
|
338
|
-
return TOML.load(rawContent);
|
|
339
|
-
}
|
|
340
|
-
catch {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
extractPackageData(content, path) {
|
|
345
|
-
// Extract package directory path from manifest path
|
|
346
|
-
let packagePath;
|
|
347
|
-
const manifestName = path.split('/').pop() || '';
|
|
348
|
-
if (path === manifestName) {
|
|
349
|
-
// Just the filename, package is at root
|
|
350
|
-
packagePath = ''; // Empty string for root
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
// Remove the manifest filename from path
|
|
354
|
-
packagePath = path.substring(0, path.lastIndexOf('/'));
|
|
355
|
-
}
|
|
356
|
-
// Handle pyproject.toml structure
|
|
357
|
-
const poetryData = content.tool?.poetry;
|
|
358
|
-
const projectData = content.project;
|
|
359
|
-
const name = poetryData?.name || projectData?.name || 'unnamed';
|
|
360
|
-
const version = poetryData?.version || projectData?.version;
|
|
361
|
-
// Extract dependencies
|
|
362
|
-
const dependencies = {};
|
|
363
|
-
const devDependencies = {};
|
|
364
|
-
// Poetry format
|
|
365
|
-
if (poetryData?.dependencies) {
|
|
366
|
-
Object.entries(poetryData.dependencies).forEach(([key, value]) => {
|
|
367
|
-
if (key !== 'python') {
|
|
368
|
-
dependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
if (poetryData?.['dev-dependencies']) {
|
|
373
|
-
Object.entries(poetryData['dev-dependencies']).forEach(([key, value]) => {
|
|
374
|
-
devDependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
// PEP 621 format
|
|
378
|
-
if (projectData?.dependencies && Array.isArray(projectData.dependencies)) {
|
|
379
|
-
try {
|
|
380
|
-
// Use pip-requirements-js to properly parse PEP 508 dependency specifications
|
|
381
|
-
const depString = projectData.dependencies.join('\n');
|
|
382
|
-
const requirements = parsePipRequirementsFile(depString);
|
|
383
|
-
requirements.forEach((req) => {
|
|
384
|
-
if (req.type === 'ProjectName' && req.name) {
|
|
385
|
-
let version = '*';
|
|
386
|
-
if (req.versionSpec && req.versionSpec.length > 0) {
|
|
387
|
-
// Use the first version spec for simplicity
|
|
388
|
-
const firstSpec = req.versionSpec[0];
|
|
389
|
-
version = firstSpec.version || '*';
|
|
390
|
-
}
|
|
391
|
-
dependencies[req.name] = version;
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
catch (error) {
|
|
396
|
-
console.warn('Failed to parse PEP 621 dependencies with pip-requirements-js, falling back to simple parsing:', error);
|
|
397
|
-
// Fallback to simple parsing if pip-requirements-js fails
|
|
398
|
-
projectData.dependencies.forEach((dep) => {
|
|
399
|
-
const cleanDep = dep.trim();
|
|
400
|
-
const spaceIndex = cleanDep.indexOf(' ');
|
|
401
|
-
if (spaceIndex > 0) {
|
|
402
|
-
dependencies[cleanDep.substring(0, spaceIndex)] = '*';
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
dependencies[cleanDep] = '*';
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
const packageManager = this.detectPythonPackageManager(content);
|
|
411
|
-
return {
|
|
412
|
-
name,
|
|
413
|
-
version,
|
|
414
|
-
path: packagePath,
|
|
415
|
-
manifestPath: path, // The full relative path to pyproject.toml/requirements.txt/setup.py
|
|
416
|
-
packageManager,
|
|
417
|
-
dependencies,
|
|
418
|
-
devDependencies,
|
|
419
|
-
peerDependencies: {},
|
|
420
|
-
isMonorepoRoot: false,
|
|
421
|
-
isWorkspace: false,
|
|
422
|
-
parentPackage: undefined,
|
|
423
|
-
availableCommands: [],
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
detectWorkspaces(_content) {
|
|
427
|
-
// Python doesn't have built-in workspace support like npm
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
detectPackageManager(content, _lockFiles) {
|
|
431
|
-
return this.detectPythonPackageManager(content);
|
|
432
|
-
}
|
|
433
|
-
detectPythonPackageManager(content) {
|
|
434
|
-
// Check for Poetry
|
|
435
|
-
if (content.tool?.poetry)
|
|
436
|
-
return 'poetry';
|
|
437
|
-
// Default to pip
|
|
438
|
-
return 'pip';
|
|
439
|
-
}
|
|
440
|
-
detectDocsFolder(packagePath, fileTree, _manifestContent) {
|
|
441
|
-
// Reuse the same logic as NodePackageParser
|
|
442
|
-
const nodeParser = new NodePackageParser();
|
|
443
|
-
return nodeParser.detectDocsFolder(packagePath, fileTree, {});
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
// Rust Cargo.toml parser - simplified version
|
|
447
|
-
class CargoPackageParser {
|
|
448
|
-
manifestFileName = 'Cargo.toml';
|
|
449
|
-
packageType = 'cargo';
|
|
450
|
-
canParse(filename) {
|
|
451
|
-
return filename.endsWith('Cargo.toml');
|
|
452
|
-
}
|
|
453
|
-
parseContent(rawContent) {
|
|
454
|
-
try {
|
|
455
|
-
return TOML.load(rawContent);
|
|
456
|
-
}
|
|
457
|
-
catch {
|
|
458
|
-
return null;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
extractPackageData(content, path) {
|
|
462
|
-
const packageData = content.package;
|
|
463
|
-
if (!packageData)
|
|
464
|
-
return null;
|
|
465
|
-
// Extract package directory path from manifest path
|
|
466
|
-
let packagePath;
|
|
467
|
-
if (path === 'Cargo.toml') {
|
|
468
|
-
packagePath = ''; // Root directory - empty string for root
|
|
469
|
-
}
|
|
470
|
-
else if (path.endsWith('/Cargo.toml')) {
|
|
471
|
-
packagePath = path.slice(0, -11); // Remove '/Cargo.toml'
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
// Shouldn't happen, but handle gracefully
|
|
475
|
-
packagePath = path;
|
|
476
|
-
}
|
|
477
|
-
// Extract dependencies
|
|
478
|
-
const dependencies = {};
|
|
479
|
-
const devDependencies = {};
|
|
480
|
-
if (content.dependencies) {
|
|
481
|
-
Object.entries(content.dependencies).forEach(([key, value]) => {
|
|
482
|
-
dependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
if (content['dev-dependencies']) {
|
|
486
|
-
Object.entries(content['dev-dependencies']).forEach(([key, value]) => {
|
|
487
|
-
devDependencies[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
return {
|
|
491
|
-
name: packageData.name || 'unnamed',
|
|
492
|
-
version: packageData.version,
|
|
493
|
-
path: packagePath,
|
|
494
|
-
manifestPath: path, // The full relative path to Cargo.toml
|
|
495
|
-
packageManager: 'cargo',
|
|
496
|
-
dependencies,
|
|
497
|
-
devDependencies,
|
|
498
|
-
peerDependencies: {},
|
|
499
|
-
isMonorepoRoot: !!content.workspace,
|
|
500
|
-
isWorkspace: false,
|
|
501
|
-
parentPackage: undefined,
|
|
502
|
-
availableCommands: [],
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
detectWorkspaces(content) {
|
|
506
|
-
return content.workspace?.members || null;
|
|
507
|
-
}
|
|
508
|
-
detectPackageManager() {
|
|
509
|
-
return 'cargo';
|
|
510
|
-
}
|
|
511
|
-
detectDocsFolder(packagePath, fileTree, _manifestContent) {
|
|
512
|
-
// Reuse the same logic as NodePackageParser
|
|
513
|
-
const nodeParser = new NodePackageParser();
|
|
514
|
-
return nodeParser.detectDocsFolder(packagePath, fileTree, {});
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
export class PackageLayerModule {
|
|
518
|
-
parsers = [
|
|
519
|
-
new NodePackageParser(),
|
|
520
|
-
new PythonPackageParser(),
|
|
521
|
-
new CargoPackageParser(),
|
|
522
|
-
];
|
|
523
|
-
/**
|
|
524
|
-
* Extract package information from the file tree
|
|
525
|
-
* @param fileTree - The file tree to analyze
|
|
526
|
-
* @param fileReader - Optional function to read file contents. If not provided, manifest detection only.
|
|
527
|
-
*/
|
|
528
|
-
async discoverPackages(fileTree, fileReader) {
|
|
529
|
-
const packages = [];
|
|
530
|
-
if (!fileTree.allFiles)
|
|
531
|
-
return packages;
|
|
532
|
-
// Find all package manifest files
|
|
533
|
-
const manifestFiles = fileTree.allFiles.filter(file => {
|
|
534
|
-
if (!file.path)
|
|
535
|
-
return false;
|
|
536
|
-
return this.parsers.some(parser => parser.canParse(file.path));
|
|
537
|
-
});
|
|
538
|
-
// Get all files for lock file detection
|
|
539
|
-
const allFilePaths = fileTree.allFiles.map(f => f.path).filter(Boolean);
|
|
540
|
-
for (const manifestFile of manifestFiles) {
|
|
541
|
-
if (!manifestFile.path)
|
|
542
|
-
continue;
|
|
543
|
-
// Find appropriate parser
|
|
544
|
-
const parser = this.parsers.find(p => p.canParse(manifestFile.path));
|
|
545
|
-
if (!parser)
|
|
546
|
-
continue;
|
|
547
|
-
// Get manifest content
|
|
548
|
-
let content = null;
|
|
549
|
-
if (fileReader) {
|
|
550
|
-
try {
|
|
551
|
-
const fileContent = await fileReader(manifestFile.path);
|
|
552
|
-
if (fileContent) {
|
|
553
|
-
// Use the parser's parseContent method for type-safe parsing
|
|
554
|
-
content = parser.parseContent(fileContent);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
catch (error) {
|
|
558
|
-
console.warn(`Could not read or parse ${manifestFile.path}:`, error);
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
// If no file reader provided, create minimal package info from path
|
|
564
|
-
const packageName = manifestFile.path.split('/').slice(-2, -1)[0] || 'unnamed';
|
|
565
|
-
content = this.createMinimalManifest(parser.packageType, packageName);
|
|
566
|
-
}
|
|
567
|
-
if (!content)
|
|
568
|
-
continue;
|
|
569
|
-
// Extract package data
|
|
570
|
-
const packageData = parser.extractPackageData(content, manifestFile.path);
|
|
571
|
-
if (!packageData) {
|
|
572
|
-
console.warn(`[PackageLayerModule] extractPackageData returned null for ${manifestFile.path}`, { content, parser: parser.packageType });
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
// Detect package manager
|
|
576
|
-
const dirPath = manifestFile.path.substring(0, manifestFile.path.lastIndexOf('/'));
|
|
577
|
-
const lockFilesInDir = allFilePaths.filter(f => f.startsWith(dirPath) && this.isLockFile(f));
|
|
578
|
-
packageData.packageManager = parser.detectPackageManager(content, lockFilesInDir);
|
|
579
|
-
// Update commands with correct package manager for Node packages
|
|
580
|
-
if (parser.packageType === 'node' && packageData.availableCommands) {
|
|
581
|
-
// Only replace if we detected a valid package manager
|
|
582
|
-
if (packageData.packageManager &&
|
|
583
|
-
packageData.packageManager !== 'unknown' &&
|
|
584
|
-
packageData.packageManager !== 'npm') {
|
|
585
|
-
packageData.availableCommands = packageData.availableCommands.map(cmd => {
|
|
586
|
-
// Update both script and standard commands
|
|
587
|
-
if (cmd.command.includes('npm ')) {
|
|
588
|
-
let pmCommand = cmd.command;
|
|
589
|
-
// Handle different package manager syntaxes
|
|
590
|
-
if (packageData.packageManager === 'yarn') {
|
|
591
|
-
pmCommand = pmCommand
|
|
592
|
-
.replace('npm run', 'yarn')
|
|
593
|
-
.replace('npm install', 'yarn install')
|
|
594
|
-
.replace('npm ci', 'yarn install --frozen-lockfile')
|
|
595
|
-
.replace('npm update', 'yarn upgrade');
|
|
596
|
-
}
|
|
597
|
-
else if (packageData.packageManager === 'pnpm') {
|
|
598
|
-
pmCommand = pmCommand.replace('npm', 'pnpm');
|
|
599
|
-
}
|
|
600
|
-
return { ...cmd, command: pmCommand };
|
|
601
|
-
}
|
|
602
|
-
return cmd;
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
// Create file set for the manifest
|
|
607
|
-
const fileSet = {
|
|
608
|
-
id: `package-manifest-${packageData.path}`,
|
|
609
|
-
name: parser.manifestFileName,
|
|
610
|
-
patterns: [
|
|
611
|
-
{
|
|
612
|
-
type: 'exact',
|
|
613
|
-
pattern: manifestFile.path,
|
|
614
|
-
description: `${parser.packageType} package manifest`,
|
|
615
|
-
},
|
|
616
|
-
],
|
|
617
|
-
matchedFiles: [manifestFile.path],
|
|
618
|
-
fileCount: 1,
|
|
619
|
-
};
|
|
620
|
-
// Detect configuration files if the parser supports it
|
|
621
|
-
let configFiles;
|
|
622
|
-
if (parser.detectConfigs) {
|
|
623
|
-
configFiles = parser.detectConfigs(packageData.path, fileTree, content);
|
|
624
|
-
}
|
|
625
|
-
// Detect documentation folder if the parser supports it
|
|
626
|
-
let docsFolder;
|
|
627
|
-
if (parser.detectDocsFolder) {
|
|
628
|
-
docsFolder = parser.detectDocsFolder(packageData.path, fileTree, content);
|
|
629
|
-
}
|
|
630
|
-
// Calculate quality metrics from lens commands
|
|
631
|
-
const qualityMetrics = packageData.availableCommands
|
|
632
|
-
? QualityMetricsCalculator.calculateQualityProfile(packageData.availableCommands)
|
|
633
|
-
: undefined;
|
|
634
|
-
// Create package layer
|
|
635
|
-
const layer = {
|
|
636
|
-
id: `package-${parser.packageType}-${packageData.path.replace(/[^a-zA-Z0-9-]/g, '-')}`,
|
|
637
|
-
name: packageData.name,
|
|
638
|
-
type: parser.packageType,
|
|
639
|
-
enabled: true,
|
|
640
|
-
derivedFrom: {
|
|
641
|
-
fileSets: [fileSet],
|
|
642
|
-
derivationType: 'content',
|
|
643
|
-
description: `${parser.packageType} package defined in ${parser.manifestFileName}`,
|
|
644
|
-
contentExtraction: {
|
|
645
|
-
method: 'parse',
|
|
646
|
-
parser: {
|
|
647
|
-
format: parser.manifestFileName.endsWith('.json') ? 'json' : 'toml',
|
|
648
|
-
paths: parser.packageType === 'node'
|
|
649
|
-
? ['']
|
|
650
|
-
: parser.packageType === 'python'
|
|
651
|
-
? ['tool.poetry', 'project']
|
|
652
|
-
: ['package'],
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
},
|
|
656
|
-
packageData,
|
|
657
|
-
configFiles,
|
|
658
|
-
docsFolder,
|
|
659
|
-
qualityMetrics,
|
|
660
|
-
pillar: 'foundationHealth',
|
|
661
|
-
};
|
|
662
|
-
packages.push(layer);
|
|
663
|
-
}
|
|
664
|
-
// Determine workspace relationships
|
|
665
|
-
this.resolveWorkspaceRelationships(packages);
|
|
666
|
-
return packages;
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Create package layers from workspace boundaries (backwards compatibility)
|
|
670
|
-
*/
|
|
671
|
-
createPackageLayersFromBoundaries(boundaries) {
|
|
672
|
-
return boundaries.map(boundary => {
|
|
673
|
-
const fileSet = {
|
|
674
|
-
id: `package-manifest-${boundary.rootPath || 'root'}`,
|
|
675
|
-
name: 'package.json',
|
|
676
|
-
patterns: [
|
|
677
|
-
{
|
|
678
|
-
type: 'exact',
|
|
679
|
-
pattern: boundary.packageJsonPath,
|
|
680
|
-
description: 'Node.js package manifest',
|
|
681
|
-
},
|
|
682
|
-
],
|
|
683
|
-
matchedFiles: [boundary.packageJsonPath],
|
|
684
|
-
fileCount: 1,
|
|
685
|
-
};
|
|
686
|
-
const packageData = {
|
|
687
|
-
name: boundary.packageData?.name || boundary.name,
|
|
688
|
-
version: boundary.packageData?.version,
|
|
689
|
-
path: boundary.rootPath || '.',
|
|
690
|
-
manifestPath: boundary.packageJsonPath,
|
|
691
|
-
packageManager: 'unknown',
|
|
692
|
-
dependencies: {},
|
|
693
|
-
devDependencies: {},
|
|
694
|
-
peerDependencies: {},
|
|
695
|
-
isMonorepoRoot: !!boundary.packageData?.workspaces,
|
|
696
|
-
isWorkspace: !boundary.isRoot && boundaries.length > 1,
|
|
697
|
-
parentPackage: boundary.isRoot ? undefined : 'root',
|
|
698
|
-
};
|
|
699
|
-
const layer = {
|
|
700
|
-
id: `package-node-${boundary.id}`,
|
|
701
|
-
name: packageData.name,
|
|
702
|
-
type: 'node',
|
|
703
|
-
enabled: true,
|
|
704
|
-
derivedFrom: {
|
|
705
|
-
fileSets: [fileSet],
|
|
706
|
-
derivationType: 'content',
|
|
707
|
-
description: 'Node.js package defined in package.json',
|
|
708
|
-
},
|
|
709
|
-
packageData,
|
|
710
|
-
pillar: 'foundationHealth',
|
|
711
|
-
};
|
|
712
|
-
return layer;
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
isLockFile(path) {
|
|
716
|
-
const lockFiles = [
|
|
717
|
-
'package-lock.json',
|
|
718
|
-
'yarn.lock',
|
|
719
|
-
'pnpm-lock.yaml',
|
|
720
|
-
'poetry.lock',
|
|
721
|
-
'Pipfile.lock',
|
|
722
|
-
'Cargo.lock',
|
|
723
|
-
];
|
|
724
|
-
return lockFiles.some(lock => path.endsWith(lock));
|
|
725
|
-
}
|
|
726
|
-
resolveWorkspaceRelationships(packages) {
|
|
727
|
-
// Find root packages (typically at the repository root)
|
|
728
|
-
const _rootPackages = packages.filter(p => p.packageData.path === '.' || p.packageData.path === '');
|
|
729
|
-
// Mark workspace packages
|
|
730
|
-
packages.forEach(pkg => {
|
|
731
|
-
if (pkg.packageData.path !== '.' && pkg.packageData.path !== '') {
|
|
732
|
-
pkg.packageData.isWorkspace = true;
|
|
733
|
-
// Find parent package
|
|
734
|
-
const parentPath = pkg.packageData.path.includes('/')
|
|
735
|
-
? pkg.packageData.path.substring(0, pkg.packageData.path.lastIndexOf('/'))
|
|
736
|
-
: '.';
|
|
737
|
-
const parent = packages.find(p => p.packageData.path === parentPath);
|
|
738
|
-
if (parent) {
|
|
739
|
-
pkg.packageData.parentPackage = parent.packageData.name;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Create a minimal manifest for when file content isn't available
|
|
746
|
-
*/
|
|
747
|
-
createMinimalManifest(packageType, packageName) {
|
|
748
|
-
switch (packageType) {
|
|
749
|
-
case 'node':
|
|
750
|
-
return {
|
|
751
|
-
name: packageName,
|
|
752
|
-
version: '0.0.0',
|
|
753
|
-
dependencies: {},
|
|
754
|
-
devDependencies: {},
|
|
755
|
-
};
|
|
756
|
-
case 'python':
|
|
757
|
-
return {
|
|
758
|
-
project: {
|
|
759
|
-
name: packageName,
|
|
760
|
-
version: '0.0.0',
|
|
761
|
-
},
|
|
762
|
-
};
|
|
763
|
-
case 'cargo':
|
|
764
|
-
return {
|
|
765
|
-
package: {
|
|
766
|
-
name: packageName,
|
|
767
|
-
version: '0.0.0',
|
|
768
|
-
},
|
|
769
|
-
};
|
|
770
|
-
default:
|
|
771
|
-
return {};
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
//# sourceMappingURL=PackageLayerModule.js.map
|