repo-tools/type-deps: support nested declaration files

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-10 16:01:35 +01:00
parent ee8d999aac
commit 31c34ddef8
2 changed files with 47 additions and 6 deletions
@@ -2,4 +2,4 @@
'@backstage/repo-tools': patch
---
The `type-deps` command now detects ambient global types from the `jest` namespace in declaration files, rather than only looking for explicit imports and reference directives.
The `type-deps` command now follows relative imports and re-exports into declaration chunk files, and detects ambient global types such as the `jest` namespace.
@@ -78,6 +78,50 @@ function shouldCheckTypes(pkg: Package & PackageJsonWithTypes) {
);
}
/**
* Read a declaration file and recursively follow all relative imports and
* re-exports, collecting the source content of each file encountered.
*/
function collectDeclSources(entryPath: string): string[] {
const visited = new Set<string>();
const sources: string[] = [];
function visit(filePath: string) {
const resolved = resolvePath(filePath);
if (visited.has(resolved)) {
return;
}
visited.add(resolved);
// Handle .js -> .d.ts extension mapping used by declaration chunk files
let actualPath = resolved;
if (!fs.existsSync(actualPath)) {
actualPath = resolved.replace(/\.js$/, '.d.ts');
}
if (!fs.existsSync(actualPath)) {
return;
}
const src = fs.readFileSync(actualPath, 'utf8');
sources.push(src);
// Follow relative imports and re-exports into chunk files
const relativeRefs = (
src.match(/^(?:import|export) .* from '\..*';$/gm) || []
)
.map(match => match.match(/from '(.*)'/)?.[1] ?? '')
.filter(n => n.startsWith('.'));
const dir = resolvePath(actualPath, '..');
for (const ref of relativeRefs) {
visit(resolvePath(dir, ref));
}
}
visit(entryPath);
return sources;
}
function findAllDeps(declSrc: string) {
const importedDeps = (declSrc.match(/^import .* from '.*';$/gm) || [])
.map(match => match.match(/from '(.*)'/)?.[1] ?? '')
@@ -112,11 +156,8 @@ function findAllDeps(declSrc: string) {
*/
function checkTypes(pkg: Package) {
const allDeps = getPackageExportDetails(pkg.packageJson).flatMap(exp => {
const typeDecl = fs.readFileSync(
resolvePath(pkg.dir, 'dist', exp.distPath),
'utf8',
);
return findAllDeps(typeDecl);
const entryPath = resolvePath(pkg.dir, 'dist', exp.distPath);
return collectDeclSources(entryPath).flatMap(src => findAllDeps(src));
});
const deps = Array.from(new Set(allDeps));