gitnexus 1.6.10-rc.220 → 1.6.10-rc.221
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/dist/config/ignore-service.d.ts +2 -0
- package/dist/config/ignore-service.js +29 -7
- package/dist/core/group/extractors/python-workspace-extractor.js +5 -2
- package/dist/core/ingestion/filesystem-walker.js +18 -2
- package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +4 -3
- package/dist/core/ingestion/languages/typescript/tsconfig.js +4 -3
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ import type { Path } from 'path-scurry';
|
|
|
3
3
|
export declare const shouldIgnorePath: (filePath: string) => boolean;
|
|
4
4
|
/** Check if a directory name is in the hardcoded ignore list */
|
|
5
5
|
export declare const isHardcodedIgnoredDirectory: (name: string) => boolean;
|
|
6
|
+
/** Apply directory ignore rules that depend on repository-relative depth. */
|
|
7
|
+
export declare const isHardcodedIgnoredDirectoryAtPath: (repoRoot: string, directoryPath: string) => boolean;
|
|
6
8
|
/**
|
|
7
9
|
* Load .gitignore and .gitnexusignore rules from the repo root.
|
|
8
10
|
* Returns an `ignore` instance with all patterns, or null if no files found.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ignore from 'ignore';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
2
3
|
import fs from 'fs/promises';
|
|
3
4
|
import nodePath from 'path';
|
|
4
5
|
import { logger } from '../core/logger.js';
|
|
@@ -27,12 +28,15 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|
|
27
28
|
// 'packages' removed - commonly used for monorepo source code (lerna, pnpm, yarn workspaces)
|
|
28
29
|
'venv',
|
|
29
30
|
'.venv',
|
|
30
|
-
'env',
|
|
31
31
|
'.env',
|
|
32
|
+
// Bare `env/` can be application source or a Python virtual environment.
|
|
33
|
+
// Path-aware rules below prune it at the root and wherever pyvenv.cfg marks
|
|
34
|
+
// a virtual environment, while preserving ordinary nested source folders.
|
|
32
35
|
'__pycache__',
|
|
33
36
|
'.pytest_cache',
|
|
34
37
|
'.mypy_cache',
|
|
35
38
|
'site-packages',
|
|
39
|
+
'dist-packages',
|
|
36
40
|
'.tox',
|
|
37
41
|
'eggs',
|
|
38
42
|
'.eggs',
|
|
@@ -78,8 +82,9 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|
|
78
82
|
'.temp',
|
|
79
83
|
// Generated/Compiled
|
|
80
84
|
'.generated',
|
|
81
|
-
'generated',
|
|
82
85
|
'auto-generated',
|
|
86
|
+
// Bare `generated/` can contain tracked source-of-truth code. Build output
|
|
87
|
+
// remains covered by .gitignore/.gitnexusignore and the unambiguous names.
|
|
83
88
|
'monaco-workers', // Monaco editor web-worker bundles generated for browser runtime
|
|
84
89
|
'.terraform',
|
|
85
90
|
'.serverless',
|
|
@@ -95,6 +100,11 @@ const DEFAULT_IGNORE_LIST = new Set([
|
|
|
95
100
|
'snapshots', // Jest snapshots
|
|
96
101
|
'__snapshots__',
|
|
97
102
|
]);
|
|
103
|
+
// Ambiguous names that conventionally denote generated artifacts only at the
|
|
104
|
+
// repository root. Nested directories with these names are frequently source
|
|
105
|
+
// modules (for example apps/web/src/env or packages/api/generated).
|
|
106
|
+
const ROOT_ARTIFACT_DIRECTORIES = new Set(['env', 'generated']);
|
|
107
|
+
const isRootArtifactDirectory = (relativePath, name) => !relativePath.includes('/') && ROOT_ARTIFACT_DIRECTORIES.has(name);
|
|
98
108
|
const IGNORED_EXTENSIONS = new Set([
|
|
99
109
|
// Images
|
|
100
110
|
'.png',
|
|
@@ -264,6 +274,9 @@ export const shouldIgnorePath = (filePath) => {
|
|
|
264
274
|
const parts = normalizedPath.split('/');
|
|
265
275
|
const fileName = parts[parts.length - 1];
|
|
266
276
|
const fileNameLower = fileName.toLowerCase();
|
|
277
|
+
if (parts.length > 0 && isRootArtifactDirectory(parts[0], parts[0])) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
267
280
|
// Laravel compiles Blade templates into generated PHP cache files under
|
|
268
281
|
// storage/framework/views. Source templates live in resources/views and are
|
|
269
282
|
// handled separately; compiled cache should not become source-of-truth. Keep
|
|
@@ -299,9 +312,7 @@ export const shouldIgnorePath = (filePath) => {
|
|
|
299
312
|
// Ignore files that look like generated/bundled code
|
|
300
313
|
if (fileNameLower.includes('.bundle.') ||
|
|
301
314
|
fileNameLower.includes('.chunk.') ||
|
|
302
|
-
fileNameLower.includes('.generated.')
|
|
303
|
-
fileNameLower.endsWith('.d.ts')) {
|
|
304
|
-
// TypeScript declaration files
|
|
315
|
+
fileNameLower.includes('.generated.')) {
|
|
305
316
|
return true;
|
|
306
317
|
}
|
|
307
318
|
return false;
|
|
@@ -310,6 +321,16 @@ export const shouldIgnorePath = (filePath) => {
|
|
|
310
321
|
export const isHardcodedIgnoredDirectory = (name) => {
|
|
311
322
|
return DEFAULT_IGNORE_LIST.has(name);
|
|
312
323
|
};
|
|
324
|
+
/** Apply directory ignore rules that depend on repository-relative depth. */
|
|
325
|
+
export const isHardcodedIgnoredDirectoryAtPath = (repoRoot, directoryPath) => {
|
|
326
|
+
const name = nodePath.basename(directoryPath);
|
|
327
|
+
if (isHardcodedIgnoredDirectory(name))
|
|
328
|
+
return true;
|
|
329
|
+
const relative = nodePath.relative(repoRoot, directoryPath).replace(/\\/g, '/');
|
|
330
|
+
if (isRootArtifactDirectory(relative, name))
|
|
331
|
+
return true;
|
|
332
|
+
return name === 'env' && existsSync(nodePath.join(directoryPath, 'pyvenv.cfg'));
|
|
333
|
+
};
|
|
313
334
|
export const loadIgnoreRules = async (repoPath, options) => {
|
|
314
335
|
const ig = ignore();
|
|
315
336
|
let hasRules = false;
|
|
@@ -450,9 +471,10 @@ export const createIgnoreFilter = async (repoPath, options) => {
|
|
|
450
471
|
// blocks descent into `__tests__/generated/`.
|
|
451
472
|
if (ig && rel && hasExplicitUnignore(ig, rel) && !ig.ignores(rel + '/'))
|
|
452
473
|
return false;
|
|
453
|
-
// Hardcoded
|
|
454
|
-
if (
|
|
474
|
+
// Hardcoded and path-aware rules prune whole trees before glob walks them.
|
|
475
|
+
if (rel && isHardcodedIgnoredDirectoryAtPath(repoPath, nodePath.join(repoPath, rel))) {
|
|
455
476
|
return true;
|
|
477
|
+
}
|
|
456
478
|
// Check against .gitignore / .gitnexusignore patterns.
|
|
457
479
|
// Since childrenIgnored is only called for directories, always test with
|
|
458
480
|
// a trailing slash. This ensures directory-only negation patterns (e.g.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { shouldIgnorePath, loadIgnoreRules } from '../../../config/ignore-service.js';
|
|
3
|
+
import { shouldIgnorePath, loadIgnoreRules, isHardcodedIgnoredDirectoryAtPath, } from '../../../config/ignore-service.js';
|
|
4
4
|
import { logger } from '../../logger.js';
|
|
5
5
|
async function parsePythonManifest(repoPath) {
|
|
6
6
|
const pyprojectPath = path.join(repoPath, 'pyproject.toml');
|
|
@@ -124,11 +124,14 @@ async function findPythonFiles(repoPath) {
|
|
|
124
124
|
for (const entry of entries) {
|
|
125
125
|
const childRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
126
126
|
if (entry.isDirectory()) {
|
|
127
|
+
const childPath = path.join(dir, entry.name);
|
|
127
128
|
if (shouldIgnorePath(childRel))
|
|
128
129
|
continue;
|
|
130
|
+
if (isHardcodedIgnoredDirectoryAtPath(repoPath, childPath))
|
|
131
|
+
continue;
|
|
129
132
|
if (ig && ig.ignores(childRel + '/'))
|
|
130
133
|
continue;
|
|
131
|
-
await walk(
|
|
134
|
+
await walk(childPath, childRel);
|
|
132
135
|
}
|
|
133
136
|
else if (entry.name.endsWith('.py')) {
|
|
134
137
|
if (shouldIgnorePath(childRel))
|
|
@@ -8,6 +8,20 @@ import { mapConcurrent } from '../../lib/utils.js';
|
|
|
8
8
|
import { logger } from '../logger.js';
|
|
9
9
|
const READ_CONCURRENCY = 32;
|
|
10
10
|
const ANALYZE_PROGRESS_ACTIVE_ENV = 'GITNEXUS_ANALYZE_PROGRESS_ACTIVE';
|
|
11
|
+
const DECLARATION_COMPANION_SUFFIXES = [
|
|
12
|
+
{ declaration: '.d.ts', implementations: ['.ts', '.tsx'] },
|
|
13
|
+
{ declaration: '.d.mts', implementations: ['.mts'] },
|
|
14
|
+
{ declaration: '.d.cts', implementations: ['.cts'] },
|
|
15
|
+
];
|
|
16
|
+
const hasImplementationSibling = (declarationPath, scannedPaths) => {
|
|
17
|
+
const companion = DECLARATION_COMPANION_SUFFIXES.find(({ declaration }) => declarationPath.endsWith(declaration));
|
|
18
|
+
if (!companion)
|
|
19
|
+
return false;
|
|
20
|
+
// Keep standalone declarations. Only suppress declaration output that sits
|
|
21
|
+
// beside an implementation with the corresponding module suffix.
|
|
22
|
+
const stem = declarationPath.slice(0, -companion.declaration.length);
|
|
23
|
+
return companion.implementations.some((suffix) => scannedPaths.has(`${stem}${suffix}`));
|
|
24
|
+
};
|
|
11
25
|
const warnLargeFileSkip = (message) => {
|
|
12
26
|
if (process.env[ANALYZE_PROGRESS_ACTIVE_ENV] === '1') {
|
|
13
27
|
// analyze.ts routes console.warn through the progress bar logger while
|
|
@@ -61,10 +75,12 @@ export const walkRepositoryPaths = async (repoPath, onProgress) => {
|
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
77
|
}
|
|
78
|
+
const scannedPaths = new Set(entries.map((entry) => entry.path));
|
|
79
|
+
const deduplicatedEntries = entries.filter((entry) => !hasImplementationSibling(entry.path, scannedPaths));
|
|
64
80
|
// Filesystem/glob traversal order is not stable across filesystems or repeated
|
|
65
81
|
// scans. Canonicalize once at the scan boundary so every downstream phase sees
|
|
66
82
|
// the same repository order.
|
|
67
|
-
|
|
83
|
+
deduplicatedEntries.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
68
84
|
if (skippedLarge > 0) {
|
|
69
85
|
const isDefault = maxFileSizeBytes === DEFAULT_MAX_FILE_SIZE_BYTES;
|
|
70
86
|
const isOverrideUnset = !process.env.GITNEXUS_MAX_FILE_SIZE;
|
|
@@ -93,7 +109,7 @@ export const walkRepositoryPaths = async (repoPath, onProgress) => {
|
|
|
93
109
|
warnLargeFileSkip(` Set GITNEXUS_MAX_FILE_SIZE=<KB> to include files above the default cap.`);
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
|
-
return
|
|
112
|
+
return deduplicatedEntries;
|
|
97
113
|
};
|
|
98
114
|
/**
|
|
99
115
|
* Phase 2: Read file contents for a specific set of relative paths.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import fs from 'fs/promises';
|
|
18
18
|
import path from 'path';
|
|
19
19
|
import { createRequire } from 'node:module';
|
|
20
|
-
import {
|
|
20
|
+
import { isHardcodedIgnoredDirectoryAtPath } from '../../../config/ignore-service.js';
|
|
21
21
|
import { logger } from '../../logger.js';
|
|
22
22
|
import { resolveFile } from '../languages/typescript/file-candidates.js';
|
|
23
23
|
// `js-yaml` is CJS; the rest of this repository reaches it the same way
|
|
@@ -285,10 +285,11 @@ export async function loadNodeWorkspacePackages(repoRoot) {
|
|
|
285
285
|
}
|
|
286
286
|
for (const entry of entries) {
|
|
287
287
|
if (entry.isDirectory()) {
|
|
288
|
-
|
|
288
|
+
const childDir = path.join(dir, entry.name);
|
|
289
|
+
if (isHardcodedIgnoredDirectoryAtPath(repoRoot, childDir))
|
|
289
290
|
continue;
|
|
290
291
|
if (depth < SCAN_MAX_DEPTH) {
|
|
291
|
-
queue.push({ dir:
|
|
292
|
+
queue.push({ dir: childDir, depth: depth + 1 });
|
|
292
293
|
}
|
|
293
294
|
continue;
|
|
294
295
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import fs from 'fs/promises';
|
|
23
23
|
import path from 'path';
|
|
24
|
-
import {
|
|
24
|
+
import { isHardcodedIgnoredDirectoryAtPath } from '../../../../config/ignore-service.js';
|
|
25
25
|
import { logger } from '../../../logger.js';
|
|
26
26
|
const SCAN_MAX_DIRS = 20_000;
|
|
27
27
|
const SCAN_MAX_DEPTH = 24;
|
|
@@ -247,10 +247,11 @@ async function findTsconfigFiles(repoRoot) {
|
|
|
247
247
|
}
|
|
248
248
|
for (const entry of entries) {
|
|
249
249
|
if (entry.isDirectory()) {
|
|
250
|
-
|
|
250
|
+
const childDir = path.join(dir, entry.name);
|
|
251
|
+
if (isHardcodedIgnoredDirectoryAtPath(repoRoot, childDir))
|
|
251
252
|
continue;
|
|
252
253
|
if (depth < SCAN_MAX_DEPTH)
|
|
253
|
-
queue.push({ dir:
|
|
254
|
+
queue.push({ dir: childDir, depth: depth + 1 });
|
|
254
255
|
continue;
|
|
255
256
|
}
|
|
256
257
|
if (!entry.isFile())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitnexus",
|
|
3
|
-
"version": "1.6.10-rc.
|
|
3
|
+
"version": "1.6.10-rc.221",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|