core/producers/tool.clj-kondo.producer.ts

core/producers/tool.clj-kondo.producer.ts is a file in GovLab Quality. 77 lines of code and 24 definitions.

import type { CatalogProduct, CatalogRule } from "#types/catalog.types";
import { byRuleId } from "#core/comparators/rule.comparator";
import { defineProducer } from "#core/registries/producer.registry";
import { remoteText } from "#core/adapters/remote.adapter";

const TOOL = "clj-kondo";
const VERSION = "master";
const SOURCE = "https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/doc/linters.md";
const DOCS = "https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md";
const HEADER_PREFIX = "### ";

interface Section {
    title: string;
    keyword?: string;
    description?: string;
    level?: string;
}

const backtickAfter = function backtickAfter(line: string, marker: string): string | null {
    const at = line.indexOf(marker);
    const open = at === -1 ? -1 : line.indexOf("`", at + marker.length);
    const close = open === -1 ? -1 : line.indexOf("`", open + 1);
    return close === -1 ? null : line.slice(open + 1, close).trim();
};

const afterMarker = function afterMarker(line: string, marker: string): string | null {
    const at = line.indexOf(marker);
    const rest = at === -1 ? "" : line.slice(at + marker.length).trim();
    return rest === "" ? null : rest;
};

const withMeta = function withMeta(section: Section, line: string): Section {
    const keyword = backtickAfter(line, "*Keyword:*");
    const description = afterMarker(line, "*Description:*");
    const level = backtickAfter(line, "*Default level:*");
    return {
        ...section,
        ...(keyword === null ? {} : { keyword }),
        ...(description === null ? {} : { description }),
        ...(level === null ? {} : { level }),
    };
};

const sectionsOf = function sectionsOf(lines: readonly string[]): Section[] {
    const sections: Section[] = [];
    for (const line of lines) {
        if (line.startsWith(HEADER_PREFIX)) {
            sections.push({ title: line.slice(HEADER_PREFIX.length).trim() });
            continue;
        }
        const last = sections.pop();
        if (last) {
            sections.push(withMeta(last, line));
        }
    }
    return sections;
};

const ruleOf = function ruleOf(section: Section): CatalogRule[] {
    const { description, keyword, level, title } = section;
    if (keyword === undefined || keyword === "") {
        return [];
    }
    return [
        {
            canonical: null,
            category: "linter",
            defaultLevel: level === undefined || level === "" ? null : level,
            description: description === undefined || description === "" ? null : description,
            ecosystem: "clojure",
            name: title === "" ? keyword : title,
            ruleId: keyword,
            tool: TOOL,
            toolVersion: VERSION,
            url: DOCS,
        },
    ];
};

const produce = async function produce(): Promise<CatalogProduct[]> {
    const lines = ((await remoteText(SOURCE)) ?? "").split("\n");
    const rules = sectionsOf(lines).flatMap(ruleOf).toSorted(byRuleId);
    return [{ rules, source: TOOL, summary: { tool: TOOL, toolVersion: VERSION, total: rules.length } }];
};

defineProducer({ name: TOOL, produce, refresh: "manual" });