( language: string, )
| 123 | * degrading to the regex fallback. |
| 124 | */ |
| 125 | export async function diagnoseGrammar( |
| 126 | language: string, |
| 127 | ): Promise<{ ok: boolean; path?: string; error?: string }> { |
| 128 | try { |
| 129 | await initTreeSitter(); |
| 130 | } catch (e: any) { |
| 131 | return { ok: false, error: e?.message ?? 'tree-sitter init failed' }; |
| 132 | } |
| 133 | const grammarFile = `tree-sitter-${language}.wasm`; |
| 134 | const candidates: string[] = []; |
| 135 | try { |
| 136 | const here = path.dirname(url.fileURLToPath(import.meta.url)); |
| 137 | candidates.push(path.join(here, '..', '..', '..', 'grammars', grammarFile)); |
| 138 | candidates.push(path.join(here, '..', '..', '..', '..', 'grammars', grammarFile)); |
| 139 | candidates.push(path.join(process.cwd(), 'grammars', grammarFile)); |
| 140 | } catch {} |
| 141 | |
| 142 | let lastError: string | undefined; |
| 143 | let foundButUnloadable = false; |
| 144 | for (const candidate of candidates) { |
| 145 | try { |
| 146 | await access(candidate); |
| 147 | } catch { |
| 148 | continue; // file not present at this candidate |
| 149 | } |
| 150 | foundButUnloadable = true; |
| 151 | try { |
| 152 | await TSLanguage.load(candidate); |
| 153 | return { ok: true, path: candidate }; |
| 154 | } catch (e: any) { |
| 155 | lastError = e?.message ?? String(e); |
| 156 | } |
| 157 | } |
| 158 | return { |
| 159 | ok: false, |
| 160 | error: foundButUnloadable |
| 161 | ? (lastError ?? 'grammar present but load failed') |
| 162 | : 'no grammar file found in any candidate path', |
| 163 | }; |
| 164 | } |
| 165 | |
| 166 | export interface FoundSymbol { |
| 167 | name: string; |
no test coverage detected