core/parsers/export.parser.ts
core/parsers/export.parser.ts is a file in GovLab Docs. 83 lines of code and 17 definitions.
import type { BarrelExport } from "#types/code.types";
import { DEFAULT_LABEL } from "#configuration/constants/graph.constants";
import { isCodeFile } from "#core/predicates/source.predicate";
import { readTextSafe } from "#core/loaders/base.loader";
import ts from "typescript";
type NamedDeclaration =
| ts.ClassDeclaration
| ts.EnumDeclaration
| ts.FunctionDeclaration
| ts.InterfaceDeclaration
| ts.TypeAliasDeclaration;
const hasModifier = function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
return ts.canHaveModifiers(node) && (ts.getModifiers(node) ?? []).some((modifier) => modifier.kind === kind);
};
const isExported = function isExported(node: ts.Node): boolean {
return hasModifier(node, ts.SyntaxKind.ExportKeyword);
};
const isNamedDeclaration = function isNamedDeclaration(node: ts.Node): node is NamedDeclaration {
if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {
return true;
}
return ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node);
};
const variableNames = function variableNames(node: ts.VariableStatement): string[] {
return node.declarationList.declarations.flatMap((decl) => (ts.isIdentifier(decl.name) ? [decl.name.text] : []));
};
const clauseNames = function clauseNames(node: ts.ExportDeclaration, valuesOnly: boolean): string[] {
const clause = node.exportClause;
if (!clause || !ts.isNamedExports(clause) || (valuesOnly && node.isTypeOnly)) {
return [];
}
return clause.elements.filter((element) => !valuesOnly || !element.isTypeOnly).map((element) => element.name.text);
};
const exportsFromNode = function exportsFromNode(node: ts.Node): string[] {
if (ts.isExportDeclaration(node)) {
return clauseNames(node, false);
}
if (!isExported(node)) {
return [];
}
if (ts.isVariableStatement(node)) {
return variableNames(node);
}
return isNamedDeclaration(node) && node.name !== undefined ? [node.name.text] : [];
};
const sourceOf = function sourceOf(filePath: string, kind?: ts.ScriptKind): ts.SourceFile | null {
const text = readTextSafe(filePath);
return text === null ? null : ts.createSourceFile(filePath, text, ts.ScriptTarget.ESNext, true, kind);
};
export const exportedNames = function exportedNames(filePath: string): ReadonlySet<string> {
const source = isCodeFile(filePath) ? sourceOf(filePath) : null;
return new Set(source === null ? [] : source.statements.flatMap(exportsFromNode));
};
const declarationPair = function declarationPair(stmt: ts.ClassDeclaration | ts.FunctionDeclaration): BarrelExport {
const label = stmt.name?.text ?? DEFAULT_LABEL;
return { label, name: hasModifier(stmt, ts.SyntaxKind.DefaultKeyword) ? DEFAULT_LABEL : label };
};
const valuePairs = function valuePairs(stmt: ts.Statement): BarrelExport[] {
if (ts.isExportDeclaration(stmt)) {
return clauseNames(stmt, true).map((name) => ({ label: name, name }));
}
if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && isExported(stmt)) {
return [declarationPair(stmt)];
}
if (ts.isVariableStatement(stmt) && isExported(stmt)) {
return variableNames(stmt).map((name) => ({ label: name, name }));
}
if (ts.isExportAssignment(stmt) && stmt.isExportEquals !== true) {
const label = ts.isIdentifier(stmt.expression) ? stmt.expression.text : DEFAULT_LABEL;
return [{ label, name: DEFAULT_LABEL }];
}
return [];
};
export const barrelExports = function barrelExports(barrelPath: string): BarrelExport[] {
const source = sourceOf(barrelPath, ts.ScriptKind.JS);
const byName = new Map<string, string>();
for (const stmt of source?.statements ?? []) {
for (const { name, label } of valuePairs(stmt)) {
byName.set(name, label);
}
}
return [...byName].map(([name, label]) => ({ label, name }));
};