core/quality/eslint/no-wildcard-exports.eslint.rule.ts
core/quality/eslint/no-wildcard-exports.eslint.rule.ts is a file in GovLab Quality. 59 lines of code and 12 definitions.
import type { Rule } from "eslint";
import { packageRelOf } from "#core/resolvers/package.resolver";
const EXPORT_ALL_MESSAGE =
"Re-export-all (export * from '...') is forbidden in workspace packages — every export must be explicit so the public surface is grep-able and the barrel is the package's exhaustive index. " +
"Replace with a named re-export listing every public symbol. [explicit_exports_only]";
const NAMESPACE_IMPORT_MESSAGE =
"Wildcard imports (import * as ns from './...') from a relative/sibling source are forbidden in workspace packages — every in-package symbol is an explicit dependency surface, named one at a time. " +
"Replace with a named import listing every symbol you actually use. " +
"Namespace imports from a bare third-party specifier (e.g. import * as THREE from 'three') are permitted — that is the vendor's own consumption contract. [explicit_imports_only]";
const SELECTORS = ["ExportAllDeclaration", "ImportNamespaceSpecifier"];
interface WithSource {
source?: { value?: unknown };
}
const hasSource = function hasSource(node: unknown): node is WithSource {
return typeof node === "object" && node !== null;
};
const sourceValueOf = function sourceValueOf(node: unknown): unknown {
return hasSource(node) ? node.source?.value : null;
};
const isRelativeSource = function isRelativeSource(source: unknown): boolean {
return typeof source === "string" && source.length > 0 && source.startsWith(".");
};
const isInWorkspacePackage = function isInWorkspacePackage(filename: string): boolean {
return packageRelOf(filename) !== null;
};
const onNamespaceImport = function onNamespaceImport(context: Rule.RuleContext, node: Rule.Node): void {
if (isRelativeSource(sourceValueOf(node.parent))) {
context.report({ messageId: "noNamespaceImport", node });
}
};
export default {
create(context): Rule.RuleListener {
if (!isInWorkspacePackage(context.filename)) {
return {};
}
const handlers: ((node: Rule.Node) => void)[] = [
(node): void => {
context.report({ messageId: "noExportAll", node });
},
(node): void => {
onNamespaceImport(context, node);
},
];
const listeners: Rule.RuleListener = {};
SELECTORS.forEach((selector, i) => {
listeners[selector] = handlers[i];
});
return listeners;
},
meta: {
docs: {
description:
"Ban wildcard exports (export *) and relative-source namespace imports (import * as ns from './...') in workspace packages — the barrel must enumerate every public symbol and every in-package import must name what it consumes. Bare third-party namespace imports are exempt.",
},
messages: { noExportAll: EXPORT_ALL_MESSAGE, noNamespaceImport: NAMESPACE_IMPORT_MESSAGE },
schema: [],
type: "problem",
},
} satisfies Rule.RuleModule;