core/analyzers/package.analyzer.ts
core/analyzers/package.analyzer.ts is a file in GovLab Docs. 79 lines of code and 23 definitions.
import type { InspectContext, PackageInfo } from "#types/index.types";
import { PACKAGE_FILE, README_FILE } from "#configuration/constants/document.constants";
import { join, relative, sep } from "node:path";
import { readDirSafe, readJsonSafe, readTextSafe } from "#core/loaders/base.loader";
import { PACKAGE_SCOPE } from "#configuration/constants/manifest.constants";
import { ROOT } from "@ssot/paths";
import { collectSourceStats } from "#core/counters/source.counter";
import { excludeMatcher } from "@govlab/quality/config";
import { existsSync } from "node:fs";
import { exportedNames } from "#core/parsers/export.parser";
import { isPlainRecord } from "#core/predicates/record.predicate";
const BARREL_FILE = "index.ts";
const PURPOSE_HEADING = "## What is it";
const NEXT_HEADING = "\n## ";
const LINE_BREAK = "\n";
const WORD_JOIN = " ";
const POSIX_SEPARATOR = "/";
const DEFAULT_VERSION = "0.0.0";
const isExcluded = await excludeMatcher(ROOT);
const stringOr = function stringOr(value: unknown, fallback: string): string {
return typeof value === "string" ? value : fallback;
};
const barrelCount = function barrelCount(barrel: string): number {
return existsSync(barrel) ? exportedNames(barrel).size : 0;
};
const collectBarrelExports = function collectBarrelExports(packageDir: string): number {
const rootBarrel = join(packageDir, BARREL_FILE);
if (existsSync(rootBarrel)) {
return barrelCount(rootBarrel);
}
return readDirSafe(packageDir)
.filter((entry) => entry.isDirectory())
.map((entry) => join(packageDir, entry.name))
.filter((dir) => !isExcluded(dir))
.reduce((total, dir) => total + barrelCount(join(dir, BARREL_FILE)), 0);
};
const extractPurpose = function extractPurpose(readme: string | null): string | null {
const at = readme === null ? -1 : readme.indexOf(PURPOSE_HEADING);
if (readme === null || at === -1) {
return null;
}
const after = readme.slice(at + PURPOSE_HEADING.length).trimStart();
const next = after.indexOf(NEXT_HEADING);
return (next === -1 ? after : after.slice(0, next))
.trim()
.split(LINE_BREAK)
.filter((line) => line.trim().length > 0)
.join(WORD_JOIN);
};
const scopedDeps = function scopedDeps(dependencies: Readonly<Record<string, unknown>>, wantScoped: boolean): string[] {
return Object.keys(dependencies)
.filter((dep) => dep.startsWith(PACKAGE_SCOPE) === wantScoped)
.toSorted((left, right) => left.localeCompare(right));
};
export const inspectPackage = function inspectPackage(packageDir: string, context: InspectContext): PackageInfo | null {
const manifest = readJsonSafe(join(packageDir, PACKAGE_FILE));
if (!isPlainRecord(manifest) || typeof manifest["name"] !== "string") {
return null;
}
const readme = readTextSafe(join(packageDir, README_FILE));
const stats = collectSourceStats(packageDir, isExcluded);
const declared = manifest["dependencies"];
const dependencies = isPlainRecord(declared) ? declared : {};
return {
barrelExports: collectBarrelExports(packageDir),
description: stringOr(manifest["description"], ""),
externalDeps: scopedDeps(dependencies, false),
group: context.group,
hasReadme: readme !== null,
name: manifest["name"],
path: relative(context.repoRoot, packageDir).split(sep).join(POSIX_SEPARATOR),
purpose: extractPurpose(readme),
siblingDeps: scopedDeps(dependencies, true),
slug: context.packageName,
sourceFiles: stats.fileCount,
sourceLoc: stats.loc,
version: stringOr(manifest["version"], DEFAULT_VERSION),
};
};