mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 11:34:44 -07:00
Add support for an additional exclusion file besides .gitignore and .geminiignore (#16487)
Co-authored-by: Adam Weidman <adamfweidman@google.com>
This commit is contained in:
@@ -12,6 +12,8 @@ import { crawl } from './crawler.js';
|
||||
import { createTmpDir, cleanupTmpDir } from '@google/gemini-cli-test-utils';
|
||||
import type { Ignore } from './ignore.js';
|
||||
import { loadIgnoreRules } from './ignore.js';
|
||||
import { GEMINI_IGNORE_FILE_NAME } from '../../config/constants.js';
|
||||
import { FileDiscoveryService } from '../../services/fileDiscoveryService.js';
|
||||
|
||||
describe('crawler', () => {
|
||||
let tmpDir: string;
|
||||
@@ -24,17 +26,16 @@ describe('crawler', () => {
|
||||
|
||||
it('should use .geminiignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.geminiignore': 'dist/',
|
||||
[GEMINI_IGNORE_FILE_NAME]: 'dist/',
|
||||
dist: ['ignored.js'],
|
||||
src: ['not-ignored.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -48,7 +49,7 @@ describe('crawler', () => {
|
||||
expect.arrayContaining([
|
||||
'.',
|
||||
'src/',
|
||||
'.geminiignore',
|
||||
GEMINI_IGNORE_FILE_NAME,
|
||||
'src/not-ignored.js',
|
||||
]),
|
||||
);
|
||||
@@ -56,19 +57,19 @@ describe('crawler', () => {
|
||||
|
||||
it('should combine .gitignore and .geminiignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': 'dist/',
|
||||
'.geminiignore': 'build/',
|
||||
[GEMINI_IGNORE_FILE_NAME]: 'build/',
|
||||
dist: ['ignored-by-git.js'],
|
||||
build: ['ignored-by-gemini.js'],
|
||||
src: ['not-ignored.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -82,7 +83,7 @@ describe('crawler', () => {
|
||||
expect.arrayContaining([
|
||||
'.',
|
||||
'src/',
|
||||
'.geminiignore',
|
||||
GEMINI_IGNORE_FILE_NAME,
|
||||
'.gitignore',
|
||||
'src/not-ignored.js',
|
||||
]),
|
||||
@@ -95,12 +96,11 @@ describe('crawler', () => {
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: ['logs'],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, ['logs']);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -117,6 +117,7 @@ describe('crawler', () => {
|
||||
|
||||
it('should handle negated directories', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['build/**', '!build/public', '!build/public/**'].join(
|
||||
'\n',
|
||||
),
|
||||
@@ -127,12 +128,11 @@ describe('crawler', () => {
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -157,17 +157,17 @@ describe('crawler', () => {
|
||||
|
||||
it('should handle root-level file negation', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['*.mk', '!Foo.mk'].join('\n'),
|
||||
'bar.mk': '',
|
||||
'Foo.mk': '',
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -184,6 +184,7 @@ describe('crawler', () => {
|
||||
|
||||
it('should handle directory negation with glob', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': [
|
||||
'third_party/**',
|
||||
'!third_party/foo',
|
||||
@@ -200,12 +201,11 @@ describe('crawler', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -229,17 +229,17 @@ describe('crawler', () => {
|
||||
|
||||
it('should correctly handle negated patterns in .gitignore', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['dist/**', '!dist/keep.js'].join('\n'),
|
||||
dist: ['ignore.js', 'keep.js'],
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -266,12 +266,11 @@ describe('crawler', () => {
|
||||
src: ['file1.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -287,16 +286,16 @@ describe('crawler', () => {
|
||||
|
||||
it('should handle empty or commented-only ignore files', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': '# This is a comment\n\n \n',
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -317,12 +316,11 @@ describe('crawler', () => {
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const results = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
@@ -349,12 +347,11 @@ describe('crawler', () => {
|
||||
|
||||
it('should hit the cache for subsequent crawls', async () => {
|
||||
tmpDir = await createTmpDir({ 'file1.js': '' });
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const options = {
|
||||
crawlDirectory: tmpDir,
|
||||
cwd: tmpDir,
|
||||
@@ -382,17 +379,19 @@ describe('crawler', () => {
|
||||
|
||||
it('should miss the cache when ignore rules change', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': 'a.txt',
|
||||
'a.txt': '',
|
||||
'b.txt': '',
|
||||
});
|
||||
const getIgnore = () =>
|
||||
loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
});
|
||||
loadIgnoreRules(
|
||||
new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
const getOptions = (ignore: Ignore) => ({
|
||||
crawlDirectory: tmpDir,
|
||||
cwd: tmpDir,
|
||||
@@ -421,12 +420,11 @@ describe('crawler', () => {
|
||||
|
||||
it('should miss the cache after TTL expires', async () => {
|
||||
tmpDir = await createTmpDir({ 'file1.js': '' });
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const options = {
|
||||
crawlDirectory: tmpDir,
|
||||
cwd: tmpDir,
|
||||
@@ -452,12 +450,11 @@ describe('crawler', () => {
|
||||
|
||||
it('should miss the cache when maxDepth changes', async () => {
|
||||
tmpDir = await createTmpDir({ 'file1.js': '' });
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const getOptions = (maxDepth?: number) => ({
|
||||
crawlDirectory: tmpDir,
|
||||
cwd: tmpDir,
|
||||
@@ -504,12 +501,11 @@ describe('crawler', () => {
|
||||
});
|
||||
|
||||
const getCrawlResults = async (maxDepth?: number) => {
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const paths = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
cwd: tmpDir,
|
||||
@@ -580,12 +576,11 @@ describe('crawler', () => {
|
||||
'file3.js': '',
|
||||
});
|
||||
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
|
||||
const paths = await crawl({
|
||||
crawlDirectory: tmpDir,
|
||||
|
||||
@@ -8,6 +8,8 @@ import { describe, it, expect, afterEach, vi } from 'vitest';
|
||||
import { FileSearchFactory, AbortError, filter } from './fileSearch.js';
|
||||
import { createTmpDir, cleanupTmpDir } from '@google/gemini-cli-test-utils';
|
||||
import * as crawler from './crawler.js';
|
||||
import { GEMINI_IGNORE_FILE_NAME } from '../../config/constants.js';
|
||||
import { FileDiscoveryService } from '../../services/fileDiscoveryService.js';
|
||||
|
||||
describe('FileSearch', () => {
|
||||
let tmpDir: string;
|
||||
@@ -20,41 +22,17 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should use .geminiignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.geminiignore': 'dist/',
|
||||
[GEMINI_IGNORE_FILE_NAME]: 'dist/',
|
||||
dist: ['ignored.js'],
|
||||
src: ['not-ignored.js'],
|
||||
});
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
enableRecursiveFileSearch: true,
|
||||
enableFuzzySearch: true,
|
||||
});
|
||||
|
||||
await fileSearch.initialize();
|
||||
const results = await fileSearch.search('');
|
||||
|
||||
expect(results).toEqual(['src/', '.geminiignore', 'src/not-ignored.js']);
|
||||
});
|
||||
|
||||
it('should combine .gitignore and .geminiignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.gitignore': 'dist/',
|
||||
'.geminiignore': 'build/',
|
||||
dist: ['ignored-by-git.js'],
|
||||
build: ['ignored-by-gemini.js'],
|
||||
src: ['not-ignored.js'],
|
||||
});
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: true,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -67,7 +45,40 @@ describe('FileSearch', () => {
|
||||
|
||||
expect(results).toEqual([
|
||||
'src/',
|
||||
'.geminiignore',
|
||||
GEMINI_IGNORE_FILE_NAME,
|
||||
'src/not-ignored.js',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should combine .gitignore and .geminiignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': 'dist/',
|
||||
[GEMINI_IGNORE_FILE_NAME]: 'build/',
|
||||
dist: ['ignored-by-git.js'],
|
||||
build: ['ignored-by-gemini.js'],
|
||||
src: ['not-ignored.js'],
|
||||
});
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
enableRecursiveFileSearch: true,
|
||||
enableFuzzySearch: true,
|
||||
});
|
||||
|
||||
await fileSearch.initialize();
|
||||
const results = await fileSearch.search('');
|
||||
|
||||
expect(results).toEqual([
|
||||
'src/',
|
||||
GEMINI_IGNORE_FILE_NAME,
|
||||
'.gitignore',
|
||||
'src/not-ignored.js',
|
||||
]);
|
||||
@@ -81,8 +92,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: ['logs'],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -98,6 +111,7 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should handle negated directories', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['build/**', '!build/public', '!build/public/**'].join(
|
||||
'\n',
|
||||
),
|
||||
@@ -110,8 +124,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -143,8 +159,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -160,6 +178,7 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should handle root-level file negation', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['*.mk', '!Foo.mk'].join('\n'),
|
||||
'bar.mk': '',
|
||||
'Foo.mk': '',
|
||||
@@ -167,8 +186,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -184,6 +205,7 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should handle directory negation with glob', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': [
|
||||
'third_party/**',
|
||||
'!third_party/foo',
|
||||
@@ -202,8 +224,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -225,6 +249,7 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should correctly handle negated patterns in .gitignore', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': ['dist/**', '!dist/keep.js'].join('\n'),
|
||||
dist: ['ignore.js', 'keep.js'],
|
||||
src: ['main.js'],
|
||||
@@ -232,8 +257,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -262,8 +289,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -289,8 +318,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -315,8 +346,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -341,8 +374,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -367,8 +402,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -391,8 +428,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -422,8 +461,10 @@ describe('FileSearch', () => {
|
||||
tmpDir = await createTmpDir({});
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -438,14 +479,17 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should handle empty or commented-only ignore files', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': '# This is a comment\n\n \n',
|
||||
src: ['main.js'],
|
||||
});
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -467,8 +511,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false, // Explicitly disable .gitignore to isolate this rule
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false, // Explicitly disable .gitignore to isolate this rule
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -491,8 +537,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -518,8 +566,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -555,8 +605,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: true, // Enable caching for this test
|
||||
cacheTtl: 0,
|
||||
@@ -595,8 +647,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -639,8 +693,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: true, // Ensure caching is enabled
|
||||
cacheTtl: 10000,
|
||||
@@ -677,8 +733,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -707,8 +765,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -732,8 +792,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -757,8 +819,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
@@ -773,6 +837,7 @@ describe('FileSearch', () => {
|
||||
|
||||
it('should respect ignore rules', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': '*.js',
|
||||
'file1.js': '',
|
||||
'file2.ts': '',
|
||||
@@ -780,8 +845,10 @@ describe('FileSearch', () => {
|
||||
|
||||
const fileSearch = FileSearchFactory.create({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
fileDiscoveryService: new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
}),
|
||||
ignoreDirs: [],
|
||||
cache: false,
|
||||
cacheTtl: 0,
|
||||
|
||||
@@ -13,12 +13,12 @@ import { crawl } from './crawler.js';
|
||||
import type { FzfResultItem } from 'fzf';
|
||||
import { AsyncFzf } from 'fzf';
|
||||
import { unescapePath } from '../paths.js';
|
||||
import type { FileDiscoveryService } from '../../services/fileDiscoveryService.js';
|
||||
|
||||
export interface FileSearchOptions {
|
||||
projectRoot: string;
|
||||
ignoreDirs: string[];
|
||||
useGitignore: boolean;
|
||||
useGeminiignore: boolean;
|
||||
fileDiscoveryService: FileDiscoveryService;
|
||||
cache: boolean;
|
||||
cacheTtl: number;
|
||||
enableRecursiveFileSearch: boolean;
|
||||
@@ -101,7 +101,10 @@ class RecursiveFileSearch implements FileSearch {
|
||||
constructor(private readonly options: FileSearchOptions) {}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
this.ignore = loadIgnoreRules(this.options);
|
||||
this.ignore = loadIgnoreRules(
|
||||
this.options.fileDiscoveryService,
|
||||
this.options.ignoreDirs,
|
||||
);
|
||||
|
||||
this.allFiles = await crawl({
|
||||
crawlDirectory: this.options.projectRoot,
|
||||
@@ -200,7 +203,10 @@ class DirectoryFileSearch implements FileSearch {
|
||||
constructor(private readonly options: FileSearchOptions) {}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
this.ignore = loadIgnoreRules(this.options);
|
||||
this.ignore = loadIgnoreRules(
|
||||
this.options.fileDiscoveryService,
|
||||
this.options.ignoreDirs,
|
||||
);
|
||||
}
|
||||
|
||||
async search(
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { Ignore, loadIgnoreRules } from './ignore.js';
|
||||
import { createTmpDir, cleanupTmpDir } from '@google/gemini-cli-test-utils';
|
||||
import { GEMINI_IGNORE_FILE_NAME } from '../../config/constants.js';
|
||||
import { FileDiscoveryService } from '../../services/fileDiscoveryService.js';
|
||||
|
||||
describe('Ignore', () => {
|
||||
describe('getDirectoryFilter', () => {
|
||||
@@ -76,14 +78,14 @@ describe('loadIgnoreRules', () => {
|
||||
|
||||
it('should load rules from .gitignore', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': '*.log',
|
||||
});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const fileFilter = ignore.getFileFilter();
|
||||
expect(fileFilter('test.log')).toBe(true);
|
||||
expect(fileFilter('test.txt')).toBe(false);
|
||||
@@ -91,14 +93,13 @@ describe('loadIgnoreRules', () => {
|
||||
|
||||
it('should load rules from .geminiignore', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.geminiignore': '*.log',
|
||||
[GEMINI_IGNORE_FILE_NAME]: '*.log',
|
||||
});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const fileFilter = ignore.getFileFilter();
|
||||
expect(fileFilter('test.log')).toBe(true);
|
||||
expect(fileFilter('test.txt')).toBe(false);
|
||||
@@ -106,15 +107,15 @@ describe('loadIgnoreRules', () => {
|
||||
|
||||
it('should combine rules from .gitignore and .geminiignore', async () => {
|
||||
tmpDir = await createTmpDir({
|
||||
'.git': {},
|
||||
'.gitignore': '*.log',
|
||||
'.geminiignore': '*.txt',
|
||||
[GEMINI_IGNORE_FILE_NAME]: '*.txt',
|
||||
});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const fileFilter = ignore.getFileFilter();
|
||||
expect(fileFilter('test.log')).toBe(true);
|
||||
expect(fileFilter('test.txt')).toBe(true);
|
||||
@@ -123,12 +124,11 @@ describe('loadIgnoreRules', () => {
|
||||
|
||||
it('should add ignoreDirs', async () => {
|
||||
tmpDir = await createTmpDir({});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: ['logs/'],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, ['logs/']);
|
||||
const dirFilter = ignore.getDirectoryFilter();
|
||||
expect(dirFilter('logs/')).toBe(true);
|
||||
expect(dirFilter('src/')).toBe(false);
|
||||
@@ -136,24 +136,22 @@ describe('loadIgnoreRules', () => {
|
||||
|
||||
it('should handle missing ignore files gracefully', async () => {
|
||||
tmpDir = await createTmpDir({});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: true,
|
||||
useGeminiignore: true,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const fileFilter = ignore.getFileFilter();
|
||||
expect(fileFilter('anyfile.txt')).toBe(false);
|
||||
});
|
||||
|
||||
it('should always add .git to the ignore list', async () => {
|
||||
tmpDir = await createTmpDir({});
|
||||
const ignore = loadIgnoreRules({
|
||||
projectRoot: tmpDir,
|
||||
useGitignore: false,
|
||||
useGeminiignore: false,
|
||||
ignoreDirs: [],
|
||||
const service = new FileDiscoveryService(tmpDir, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
const ignore = loadIgnoreRules(service, []);
|
||||
const dirFilter = ignore.getDirectoryFilter();
|
||||
expect(dirFilter('.git/')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -5,38 +5,28 @@
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import ignore from 'ignore';
|
||||
import picomatch from 'picomatch';
|
||||
import type { FileDiscoveryService } from '../../services/fileDiscoveryService.js';
|
||||
|
||||
const hasFileExtension = picomatch('**/*[*.]*');
|
||||
|
||||
export interface LoadIgnoreRulesOptions {
|
||||
projectRoot: string;
|
||||
useGitignore: boolean;
|
||||
useGeminiignore: boolean;
|
||||
ignoreDirs: string[];
|
||||
}
|
||||
|
||||
export function loadIgnoreRules(options: LoadIgnoreRulesOptions): Ignore {
|
||||
export function loadIgnoreRules(
|
||||
service: FileDiscoveryService,
|
||||
ignoreDirs: string[] = [],
|
||||
): Ignore {
|
||||
const ignorer = new Ignore();
|
||||
if (options.useGitignore) {
|
||||
const gitignorePath = path.join(options.projectRoot, '.gitignore');
|
||||
if (fs.existsSync(gitignorePath)) {
|
||||
ignorer.add(fs.readFileSync(gitignorePath, 'utf8'));
|
||||
const ignoreFiles = service.getAllIgnoreFilePaths();
|
||||
|
||||
for (const filePath of ignoreFiles) {
|
||||
if (fs.existsSync(filePath)) {
|
||||
ignorer.add(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.useGeminiignore) {
|
||||
const geminiignorePath = path.join(options.projectRoot, '.geminiignore');
|
||||
if (fs.existsSync(geminiignorePath)) {
|
||||
ignorer.add(fs.readFileSync(geminiignorePath, 'utf8'));
|
||||
}
|
||||
}
|
||||
|
||||
const ignoreDirs = ['.git', ...options.ignoreDirs];
|
||||
const allIgnoreDirs = ['.git', ...ignoreDirs];
|
||||
ignorer.add(
|
||||
ignoreDirs.map((dir) => {
|
||||
allIgnoreDirs.map((dir) => {
|
||||
if (dir.endsWith('/')) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user