core/producers/tool.phpmd.producer.ts

core/producers/tool.phpmd.producer.ts is a file in GovLab Quality. 91 lines of code and 32 definitions.

import type { CatalogProduct, CatalogRule, KnobEntry } from "#types/catalog.types";
import { INTEGER_TYPE } from "#configuration/constants/knob.constants";
import { byRuleId } from "#core/comparators/rule.comparator";
import { defineProducer } from "#core/registries/producer.registry";
import { intAfter } from "#core/parsers/knob.parser";
import { remoteText } from "#core/adapters/remote.adapter";
import { tallyBy } from "#core/aggregators/catalog.aggregator";

const TOOL = "phpmd";
const VERSION = "master";
const SOURCE_ROOT = "https://raw.githubusercontent.com/phpmd/phpmd/master/src/main/resources/rulesets";
const DOCS = "https://phpmd.org/rules";
const RULESETS = ["cleancode", "codesize", "controversial", "design", "naming", "unusedcode"];
const RULE_OPEN = '<rule name="';
const RULE_CLOSE = "</rule>";
const DESCRIPTION_OPEN = "<description>";
const DESCRIPTION_CLOSE = "</description>";
const KNOB_PROPERTIES = ["reportLevel", "minimum", "maximum"];
const PROPERTY_WINDOW = 200;

const attr = function attr(tag: string, key: string): string | null {
    const needle = `${key}="`;
    const at = tag.indexOf(needle);
    const end = at === -1 ? -1 : tag.indexOf('"', at + needle.length);
    return end === -1 ? null : tag.slice(at + needle.length, end);
};

const segments = function segments(text: string): string[] {
    const found: string[] = [];
    let at = text.indexOf(RULE_OPEN);
    while (at >= 0) {
        const end = text.indexOf(RULE_CLOSE, at);
        found.push(text.slice(at, end === -1 ? text.length : end));
        at = text.indexOf(RULE_OPEN, at + RULE_OPEN.length);
    }
    return found;
};

const descriptionOf = function descriptionOf(segment: string): string | null {
    const open = segment.indexOf(DESCRIPTION_OPEN);
    const close = open === -1 ? -1 : segment.indexOf(DESCRIPTION_CLOSE, open);
    const text = close === -1 ? "" : segment.slice(open + DESCRIPTION_OPEN.length, close).trim();
    return text === "" ? null : text;
};

const ruleOf = function ruleOf(segment: string, ruleset: string): CatalogRule[] {
    const tag = segment.slice(0, segment.indexOf(">"));
    const name = attr(tag, "name");
    const className = attr(tag, "class");
    return name === null
        ? []
        : [
              {
                  canonical: null,
                  category: ruleset,
                  class: className === "" ? null : className,
                  description: descriptionOf(segment),
                  ecosystem: "php",
                  name,
                  ruleId: name,
                  tool: TOOL,
                  toolVersion: VERSION,
                  url: `${DOCS}/${ruleset}.html#${name.toLowerCase()}`,
              },
          ];
};

const rulesets = async function rulesets(): Promise<[string, string][]> {
    const texts = await Promise.all(RULESETS.map(async (ruleset) => remoteText(`${SOURCE_ROOT}/${ruleset}.xml`)));
    return RULESETS.map((ruleset, index): [string, string] => [ruleset, texts[index] ?? ""]);
};

const produce = async function produce(): Promise<CatalogProduct[]> {
    const rules = (await rulesets())
        .flatMap(([ruleset, text]) => segments(text).flatMap((segment) => ruleOf(segment, ruleset)))
        .toSorted(byRuleId);
    return [
        {
            rules,
            source: TOOL,
            summary: { byCategory: tallyBy(rules, "category"), tool: TOOL, toolVersion: VERSION, total: rules.length },
        },
    ];
};

const knobsOf = function knobsOf(segment: string): KnobEntry[] {
    const name = segment.slice(RULE_OPEN.length, segment.indexOf('"', RULE_OPEN.length));
    return KNOB_PROPERTIES.flatMap((property): KnobEntry[] => {
        const at = segment.indexOf(`name="${property}"`);
        const value = at === -1 ? null : intAfter(segment, 'value="', { from: at, window: PROPERTY_WINDOW });
        return value === null
            ? []
            : [[TOOL, name, { default: value, knob: property, threshold: true, type: INTEGER_TYPE }]];
    });
};

const knobs = async function knobs(): Promise<KnobEntry[]> {
    return (await rulesets()).flatMap(([, text]) => segments(text).flatMap(knobsOf));
};

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