core/renderers/chapter.renderer.ts
core/renderers/chapter.renderer.ts is a file in Bane's Lab Content. 97 lines of code and 24 definitions.
import {
ATTRIBUTION_SEPARATOR,
CHAPTER_FOOTER_LEAD,
CHAPTER_README_LEAD,
README_LABEL,
} from "#configuration/strings/chapter.strings";
import type { Attribution, Chapter, ChapterSource, LinkResolver, PageSources, ShapeInput } from "#types/chapter.types";
import {
CHAPTER_SEPARATOR,
FOLDER_SEPARATOR,
LIST_ITEM,
PARAGRAPH_BREAK,
PARENT_FOLDER,
README_FILE,
} from "#configuration/constants/chapter.constants";
import { attributionLine, link, tabFileOf } from "#core/renderers/markdown.renderer";
import { LINE_END } from "#configuration/constants/inventory.constants";
import { pageFolderOf } from "#core/normalizers/readme.normalizer";
import { relinked } from "#core/normalizers/link.normalizer";
import { renderReadme } from "#core/renderers/readme.renderer";
const folderOf = function folderOf(page: PageSources): string {
return pageFolderOf(page.page, page.label);
};
export const chapterPathOf = function chapterPathOf(
page: PageSources,
source: ChapterSource,
composed: boolean,
): string {
const folder = folderOf(page);
const file = source.first && !composed && folder.length === 0 ? README_FILE : tabFileOf(source.tab);
return folder.length === 0 ? file : folder + FOLDER_SEPARATOR + file;
};
const relativeTo = function relativeTo(from: string, target: string): string {
if (from.length === 0) {
return target;
}
const prefix = from + FOLDER_SEPARATOR;
return target.startsWith(prefix) ? target.slice(prefix.length) : PARENT_FOLDER + target;
};
export const repositoryResolver = function repositoryResolver(
pages: readonly PageSources[],
composed: boolean,
from: string,
): LinkResolver {
return (page, tab) => {
const found = pages.find((candidate) => candidate.page === page);
const source = found?.sources.find((candidate) => (tab === null ? candidate.first : candidate.tab === tab));
return found === undefined || source === undefined
? null
: relativeTo(from, chapterPathOf(found, source, composed));
};
};
const footerOf = function footerOf(page: PageSources, composed: boolean): string {
const root = folderOf(page).length === 0;
const links = page.sources.map((source) => {
const label = source.first && !composed && root ? README_LABEL : source.label;
return link(label, relativeTo(folderOf(page), chapterPathOf(page, source, composed)));
});
return CHAPTER_FOOTER_LEAD + links.join(ATTRIBUTION_SEPARATOR);
};
const chapterList = function chapterList(page: PageSources): string {
const items = page.sources
.filter((source) => !source.first)
.map((source) => LIST_ITEM + link(source.label, tabFileOf(source.tab)));
return [CHAPTER_README_LEAD, "", ...items].join(LINE_END);
};
const chapterOf = function chapterOf(
page: PageSources,
source: ChapterSource,
attribution: Attribution,
composed: boolean,
): Chapter {
const parts = [attributionLine(attribution), source.markdown.trimEnd()];
const leads = source.first && !composed && folderOf(page).length === 0;
if (leads) {
parts.push(chapterList(page));
}
const body = parts.join(PARAGRAPH_BREAK) + CHAPTER_SEPARATOR + footerOf(page, composed) + LINE_END;
return { body, file: chapterPathOf(page, source, composed), label: source.label };
};
const folderOfFile = function folderOfFile(file: string): string {
const slash = file.lastIndexOf(FOLDER_SEPARATOR);
return slash === -1 ? "" : file.slice(0, slash);
};
export const renderChapters = function renderChapters(input: ShapeInput, attribution: Attribution): Chapter[] {
const composed = input.readme !== null;
const chapters = input.pages.flatMap((page) =>
page.sources.map((source) => chapterOf(page, source, attribution, composed)),
);
if (input.readme !== null) {
chapters.unshift({ body: renderReadme(input.readme, attribution), file: README_FILE, label: README_LABEL });
}
return chapters.map((chapter) => ({
...chapter,
body: relinked(chapter.body, repositoryResolver(input.pages, composed, folderOfFile(chapter.file))),
}));
};