@search-docs/server
Version:
search-docs サーバ実装
110 lines • 4.02 kB
JavaScript
import fg from 'fast-glob';
import * as fs from 'fs/promises';
import * as path from 'path';
import { minimatch } from 'minimatch';
// ignoreパッケージのファクトリ関数をdynamic importで使用
let ignoreFactory = null;
/**
* ファイル検索クラス
* Globパターンと.gitignoreを使用してMarkdownファイルを検索
*/
export class FileDiscovery {
rootDir;
config;
ignoreFilter = null;
constructor(options) {
this.rootDir = path.resolve(options.rootDir);
this.config = options.config;
}
/**
* ファイルを検索
* @returns 見つかったファイルのパス一覧(プロジェクトルートからの相対パス)
*/
async findFiles() {
// .gitignoreを読み込む
if (this.config.ignoreGitignore) {
await this.loadGitignore();
}
// fast-globで検索
const files = await fg(this.config.include, {
cwd: this.rootDir,
ignore: this.config.exclude,
absolute: false, // 相対パスを返す
onlyFiles: true,
dot: false, // ドットファイルを除外
});
// .gitignoreフィルタを適用
if (this.ignoreFilter) {
return files.filter((file) => !this.ignoreFilter.ignores(file));
}
return files;
}
/**
* パスがパターンにマッチするか判定
* @param filePath ファイルパス(相対パス)
* @returns マッチする場合true
*/
matchesPattern(filePath) {
// includeパターンにマッチするか
const matchesInclude = this.config.include.some((pattern) => {
// minimatchはfast-globと異なり、**/patternがルートレベルにマッチしない
// fast-globの挙動に合わせるため、**/で始まるパターンは
// ルートレベルとネストレベルの両方をチェック
if (pattern.startsWith('**/')) {
const rootPattern = pattern.slice(3); // '**/'を除去
return minimatch(filePath, pattern) || minimatch(filePath, rootPattern);
}
return minimatch(filePath, pattern);
});
if (!matchesInclude) {
return false;
}
// excludeパターンにマッチしないか
const matchesExclude = this.config.exclude.some((pattern) => {
// exclude側も同様の処理
if (pattern.startsWith('**/')) {
const rootPattern = pattern.slice(3);
return minimatch(filePath, pattern) || minimatch(filePath, rootPattern);
}
return minimatch(filePath, pattern);
});
return !matchesExclude;
}
/**
* パスを除外すべきか判定
* @param filePath ファイルパス(相対パス)
* @returns 除外する場合true
*/
shouldIgnore(filePath) {
// .gitignoreフィルタ
if (this.config.ignoreGitignore && this.ignoreFilter) {
if (this.ignoreFilter.ignores(filePath)) {
return true;
}
}
// パターンマッチング
return !this.matchesPattern(filePath);
}
/**
* .gitignoreを読み込む
*/
async loadGitignore() {
try {
// ignoreパッケージを動的にロード
if (!ignoreFactory) {
const ignoreModule = await import('ignore');
ignoreFactory = ignoreModule.default;
}
const gitignorePath = path.join(this.rootDir, '.gitignore');
const content = await fs.readFile(gitignorePath, 'utf-8');
this.ignoreFilter = ignoreFactory().add(content);
}
catch (error) {
// .gitignoreが存在しない場合は無視
if (error.code !== 'ENOENT') {
throw error;
}
}
}
}
//# sourceMappingURL=file-discovery.js.map