(options: StatusOptions)
| 46 | sources: SourceEntry[]; |
| 47 | } |
| 48 | |
| 49 | async function run(options: StatusOptions): Promise<void> { |
| 50 | const rows: Row[] = []; |
| 51 | |
| 52 | const entries = await readdir(options.sources, { withFileTypes: true }); |
| 53 | const sourceFiles = entries |
| 54 | .filter((entry) => entry.isFile() && /\.ya?ml$/i.test(entry.name)) |
| 55 | .map((entry) => entry.name) |
| 56 | .sort(); |
| 57 | |
| 58 | for (const fileName of sourceFiles) { |
| 59 | const sourcePath = path.join(options.sources, fileName); |
| 60 | const sourceParsed = parse(await readFile(sourcePath, "utf8")) as { |
| 61 | provider?: string; |
| 62 | name?: string; |
| 63 | scope?: string; |
| 64 | categories?: string[]; |
| 65 | aliases?: string[]; |
| 66 | sources?: Array<{ name?: string; url?: string; type?: string; selector?: string }>; |
| 67 | }; |
| 68 | const id = sourceParsed.provider ?? fileName.replace(/\.ya?ml$/i, ""); |
| 69 | const name = sourceParsed.name ?? id; |
| 70 | const scope = sourceParsed.scope ?? "global"; |
| 71 | const categories = sourceParsed.categories ?? []; |
| 72 | const aliases = sourceParsed.aliases ?? []; |
| 73 | |
| 74 | const providerPath = path.join(options.providers, `${id}.yaml`); |
| 75 | const providerRaw = await readFile(providerPath, "utf8").catch(() => null); |
| 76 | const rules = providerRaw ? countRulesFromText(providerRaw) : 0; |
| 77 | |
| 78 | const sourceEntries: SourceEntry[] = []; |
| 79 | for (const source of sourceParsed.sources ?? []) { |
| 80 | const cacheKey = source.url |
| 81 | ? source.type === "remote-html" |
| 82 | ? `html-extract:${source.url}#${source.selector ?? ""}` |
| 83 | : source.url |
| 84 | : undefined; |
| 85 | const entry: SourceEntry = { name: source.name ?? "?", url: source.url, staleFlag: false }; |
| 86 | if (cacheKey) { |
| 87 | const stats = await statCacheFile(options.cache, cacheKey); |
| 88 | if (stats) { |
| 89 | entry.lastFetched = stats.mtime.toISOString().slice(0, 10); |
| 90 | entry.ageDays = Math.floor((Date.now() - stats.mtime.getTime()) / 86_400_000); |
| 91 | entry.staleFlag = entry.ageDays > 30; |
| 92 | } |
| 93 | } |
| 94 | sourceEntries.push(entry); |
| 95 | } |
| 96 | |
| 97 | rows.push({ id, name, scope, categories, aliases, rules, sources: sourceEntries }); |
| 98 | } |
| 99 | |
| 100 | await writeFile(options.output, renderMarkdown(rows), "utf8"); |
| 101 | console.log(`[status] wrote ${options.output} (${rows.length} providers)`); |
| 102 | } |
no test coverage detected