diff --git a/CHANGELOG.md b/CHANGELOG.md index 1815c4150..1beb26a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -201,6 +201,8 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). #### Symbols, tests and the viewer +- Java packages named `build` under standard main and test source roots are now indexed without pulling Gradle or Maven build output into the graph. (#1642) + - **Files under an `e2e/` directory count as tests.** Their calls no longer appear as production callers in Steps, dead-code and test badges. - **Production code under a `samples` or `examples` package path is no longer treated as test code.** A Kotlin or Java project whose package path runs through `com/google/samples/…` (Now in Android, for one) had nearly every file counted as a fixture, so the Map opened on `build-logic`, the entry points hid the app, and dead-code and test badges were wrong. Only the project layout above a `src/` folder decides now; the package path below it never does. diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index ad0ba2374..991309c27 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -7637,6 +7637,39 @@ describe('Nested non-submodule git repos', () => { expect(ig.ignores('dist/')).toBe(true); // valid rule survives expect(ig.ignores('src/app.ts')).toBe(false); }); + + it('keeps Java packages named build while excluding build output (#1642)', () => { + const sourceFile = 'module/src/main/java/com/acme/build/RealtimePlusService.java'; + const testFile = 'module/src/test/java/com/acme/build/RealtimePlusServiceTest.java'; + const outputFile = 'module/build/generated/Generated.java'; + + for (const rel of [sourceFile, testFile, outputFile]) { + const abs = path.join(tempDir, rel); + fs.mkdirSync(path.dirname(abs), { recursive: true }); + fs.writeFileSync(abs, 'class Example {}\n'); + } + + const scope = buildScopeIgnore(tempDir); + expect(scope.ignores(sourceFile)).toBe(false); + expect(scope.ignores(testFile)).toBe(false); + expect(scope.ignores(outputFile)).toBe(true); + + const files = scanDirectory(tempDir); + expect(files).toContain(sourceFile); + expect(files).toContain(testFile); + expect(files).not.toContain(outputFile); + }); + + it('lets an explicit .gitignore exclude a Java package named build (#1642)', () => { + const sourceFile = 'src/main/java/com/acme/build/Hidden.java'; + const abs = path.join(tempDir, sourceFile); + fs.mkdirSync(path.dirname(abs), { recursive: true }); + fs.writeFileSync(abs, 'class Hidden {}\n'); + fs.writeFileSync(path.join(tempDir, '.gitignore'), 'src/main/java/**/build/\n'); + + expect(buildScopeIgnore(tempDir).ignores(sourceFile)).toBe(true); + expect(scanDirectory(tempDir)).not.toContain(sourceFile); + }); }); // ============================================================================= diff --git a/src/extraction/index.ts b/src/extraction/index.ts index 93be48352..a62381414 100644 --- a/src/extraction/index.ts +++ b/src/extraction/index.ts @@ -227,6 +227,12 @@ const DEFAULT_IGNORE_PATTERNS: string[] = [ 'bazel-*/', // Bazel output symlink trees // Android resource dirs at any depth, with their qualifier variants (#1047). ...ANDROID_RES_TYPES.map((t) => `**/res/${t}*/`), + // `build` is also a legal Java package segment. Keep it under conventional + // Java source roots while continuing to exclude module/build output (#1642). + '!**/src/main/java/**/build/', + '!**/src/main/java/**/build/**', + '!**/src/test/java/**/build/', + '!**/src/test/java/**/build/**', ]; /** True if `buf` decodes as strict UTF-8 (no invalid byte sequences). */