Suite of AT Protocol TypeScript libraries built on web standards
1import { join } from "node:path";
2import {
3 type LexiconDocument,
4 lexiconDocumentSchema,
5 LexiconIterableIndexer,
6} from "../document/mod.ts";
7
8export type LexiconDirectoryIndexerOptions = {
9 lexicons: string;
10 ignoreInvalidLexicons?: boolean;
11};
12
13export class LexiconDirectoryIndexer extends LexiconIterableIndexer {
14 constructor(options: LexiconDirectoryIndexerOptions) {
15 super(readLexicons(options));
16 }
17}
18
19async function* readLexicons(
20 options: LexiconDirectoryIndexerOptions,
21): AsyncGenerator<LexiconDocument, void, unknown> {
22 for await (const filePath of listFiles(options.lexicons)) {
23 if (filePath.endsWith(".json")) {
24 try {
25 const data = await Deno.readTextFile(filePath);
26 yield lexiconDocumentSchema.parse(JSON.parse(data));
27 } catch (cause) {
28 const message = `Error parsing lexicon document ${filePath}`;
29 if (options.ignoreInvalidLexicons) console.error(`${message}:`, cause);
30 else throw new Error(message, { cause });
31 }
32 }
33 }
34}
35
36async function* listFiles(dir: string): AsyncGenerator<string> {
37 let entries: Deno.DirEntry[];
38 try {
39 entries = [];
40 for await (const entry of Deno.readDir(dir)) {
41 entries.push(entry);
42 }
43 } catch (err) {
44 if (err instanceof Deno.errors.NotFound) return;
45 throw err;
46 }
47 for (const entry of entries) {
48 const res = join(dir, entry.name);
49 if (entry.isDirectory) {
50 yield* listFiles(res);
51 } else if (entry.isFile || entry.isSymlink) {
52 yield res;
53 }
54 }
55}