core/loaders/icons.loader.ts
core/loaders/icons.loader.ts is a file in Bane's Lab Build Scripts. 66 lines of code and 14 definitions.
import {
EVEN_ODD,
ICONS_CONCERN,
ICONS_SUFFIX,
ICON_PREFIX,
PATH_DATA_CLOSE,
PATH_DATA_OPEN,
PATH_OPEN,
TAG_END,
} from "#configuration/constants/icons.constants";
import { absolutePath, relativePath } from "@ssot/paths";
import { readFileSync, readdirSync } from "node:fs";
import type { IconPath } from "@banes-lab/web/types/learning.types.js";
import { concernFolders } from "@ssot/govlab/shared/resolvers/container.resolver.ts";
import { iconSource } from "#core/resolvers/icons.resolver";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { pathlessIcon } from "#configuration/strings/icons.strings";
import { projectDirs } from "@ssot/govlab/shared/resolvers/anchor.resolver.ts";
const MEMBER = relativePath("app.member");
const MEMBER_BELOW_ROOT = `${MEMBER.slice(MEMBER.indexOf("/") + 1)}/`;
const iconFiles = function iconFiles(): string[] {
return concernFolders(ICONS_CONCERN, projectDirs())
.filter((folder) => folder.startsWith(MEMBER_BELOW_ROOT))
.map((folder) => absolutePath("app.root", folder))
.flatMap((folder) =>
readdirSync(folder)
.filter((name) => name.endsWith(ICONS_SUFFIX))
.sort((a, b) => a.localeCompare(b))
.map((name) => join(folder, name)),
);
};
const loadModule = async function loadModule(file: string): Promise<unknown> {
const loaded: unknown = await import(pathToFileURL(file).href);
return loaded;
};
const iconNamesOf = function iconNamesOf(module: unknown): string[] {
if (typeof module !== "object" || module === null) {
return [];
}
return Object.values(module)
.filter((value): value is string => typeof value === "string" && value.startsWith(ICON_PREFIX))
.map((value) => value.slice(ICON_PREFIX.length));
};
const pathOf = function pathOf(tag: string): IconPath {
const start = tag.indexOf(PATH_DATA_OPEN) + PATH_DATA_OPEN.length;
return { d: tag.slice(start, tag.indexOf(PATH_DATA_CLOSE, start)), evenOdd: tag.includes(EVEN_ODD) };
};
export const iconPathsOf = function iconPathsOf(name: string): readonly IconPath[] {
const svg = readFileSync(iconSource(name), "utf8");
const paths: IconPath[] = [];
let at = svg.indexOf(PATH_OPEN);
while (at !== -1) {
const end = svg.indexOf(TAG_END, at);
paths.push(pathOf(svg.slice(at, end)));
at = svg.indexOf(PATH_OPEN, end);
}
if (paths.length === 0) {
throw new TypeError(pathlessIcon(ICON_PREFIX + name));
}
return paths;
};
export const discoverIcons = async function discoverIcons(): Promise<readonly string[]> {
const loaded = await Promise.all(iconFiles().map(loadModule));
return [...new Set(loaded.flatMap(iconNamesOf))].sort((a, b) => a.localeCompare(b));
};