core/validators/panel.validator.ts
core/validators/panel.validator.ts is a file in Bane's Lab Content. 81 lines of code and 14 definitions.
import {
CITE_CLOSE,
CITE_OPEN,
LINK_OPEN,
TAG_SEPARATOR,
TAG_WORD_LIMIT,
} from "#configuration/constants/panel.constants";
import {
PANEL_CITE_UNKNOWN,
PANEL_TAG_SEPARATED,
PANEL_TAG_TOO_LONG,
PANEL_TAG_TWICE,
PANEL_UNCITED,
PANEL_UNTAGGED,
} from "#configuration/strings/panel.strings";
import type { PanelSection, PayloadPanel } from "#types/panel.types";
import type { GraphFinding } from "#types/coverage.types";
import { WORD_SEPARATOR } from "#configuration/constants/leak.constants";
const wordCount = function wordCount(tag: string): number {
return tag.split(WORD_SEPARATOR).filter((word) => word.length > 0).length;
};
const tagFindings = function tagFindings(section: PanelSection, panel: PayloadPanel): string[] {
if (panel.tag === null) {
return [PANEL_UNTAGGED];
}
const messages: string[] = [];
if (wordCount(panel.tag) > TAG_WORD_LIMIT) {
messages.push(PANEL_TAG_TOO_LONG + panel.tag);
}
if (panel.tag.includes(TAG_SEPARATOR)) {
messages.push(PANEL_TAG_SEPARATED + panel.tag);
}
if (!section.prose.includes(CITE_OPEN + panel.tag + CITE_CLOSE)) {
messages.push(PANEL_UNCITED + panel.tag);
}
return messages;
};
const duplicateTags = function duplicateTags(section: PanelSection): string[] {
const seen = new Set<string>();
const twice: string[] = [];
for (const panel of section.panels) {
if (panel.tag !== null && seen.has(panel.tag)) {
twice.push(panel.tag);
}
if (panel.tag !== null) {
seen.add(panel.tag);
}
}
return twice;
};
const citesIn = function citesIn(prose: string): readonly string[] {
const cites: string[] = [];
let open = prose.indexOf(CITE_OPEN);
while (open !== -1) {
const close = prose.indexOf(CITE_CLOSE, open + CITE_OPEN.length);
if (close === -1) {
break;
}
if (prose.charAt(close + CITE_CLOSE.length) !== LINK_OPEN) {
cites.push(prose.slice(open + CITE_OPEN.length, close));
}
open = prose.indexOf(CITE_OPEN, close + CITE_CLOSE.length);
}
return cites;
};
const unknownCites = function unknownCites(section: PanelSection): readonly string[] {
const tags = new Set(section.panels.flatMap((panel) => (panel.tag === null ? [] : [panel.tag])));
return [...new Set(citesIn(section.prose))].filter((cite) => !tags.has(cite));
};
export const validatePanels = function validatePanels(page: string, sections: readonly PanelSection[]): GraphFinding[] {
return sections.flatMap((section) => [
...section.panels.flatMap((panel) =>
tagFindings(section, panel).map((message) => ({ message, page, section: section.id })),
),
...duplicateTags(section).map((tag) => ({ message: PANEL_TAG_TWICE + tag, page, section: section.id })),
...unknownCites(section).map((cite) => ({ message: PANEL_CITE_UNKNOWN + cite, page, section: section.id })),
]);
};
export const panelCount = function panelCount(sections: readonly PanelSection[]): number {
return sections.reduce((sum, section) => sum + section.panels.length, 0);
};