TypeScript Dependency Detective
detective-typescript is a utility library designed to extract the static dependencies from TypeScript module source code or its Abstract Syntax Tree (AST). It provides a programmatic API to analyze imports and exports, including support for various TypeScript features like type-only imports, dynamic imports, and JSX syntax. The current stable version is 14.1.1, with releases occurring somewhat regularly to support newer TypeScript versions and Node.js environments, and to address bug fixes and performance improvements. It differentiates itself by offering fine-grained control over dependency extraction through options like `skipTypeImports`, `mixedImports`, and `skipAsyncImports`, making it suitable for tools that need to build dependency graphs or perform static analysis on TypeScript projects. Its dependency extraction mechanism is robust, relying on AST parsing provided by `@typescript-eslint/typescript-estree`, and it is often used by build tools, bundlers, and linters for static code analysis.
Common errors
-
Error: Cannot find module 'typescript'
cause The `typescript` package is a peer dependency and was not installed in the project.fixInstall TypeScript in your project: `npm install typescript` or `yarn add typescript`. -
SyntaxError: Unexpected token 'export' (or similar parsing errors for modern TS features)
cause The Node.js version is too old (e.g., <18) or the `typescript` peer dependency installed is incompatible with the TypeScript features used in the source code.fixUpdate Node.js to version 18 or newer. Ensure your installed `typescript` version (`npm view detective-typescript peerDependencies.typescript`) matches the supported range and can parse your source code. -
TypeError: detective is not a function
cause Incorrect import statement. The `detective-typescript` package exports its main function as a default export, not a named export.fixUse `import detective from 'detective-typescript';` for ESM or `const detective = require('detective-typescript');` for CommonJS.
Warnings
- breaking Starting with v13.0.0, the `typescript` package is no longer a direct dependency but a peer dependency. Users must install `typescript` explicitly in their project.
- breaking Version 12.0.0 dropped support for Node.js versions older than 18. Running the package on unsupported Node.js versions will result in errors.
- gotcha The `mixedImports` option defaults to `false`. This means that CommonJS `require()` calls are not included in the dependency list by default. If you need to detect both ESM `import` and CJS `require` statements, you must explicitly set this option to `true`.
- gotcha Options such as `skipAsyncImports`, `onFile`, and `onAfterFile` were incorrectly forwarded to internal parser/walker constructors in versions prior to v14.1.1. This could lead to unexpected behavior or options not being applied correctly.
Install
-
npm install detective-typescript -
yarn add detective-typescript -
pnpm add detective-typescript
Imports
- detective
import { detective } from 'detective-typescript';import detective from 'detective-typescript';
- detective
import detective from 'detective-typescript';
const detective = require('detective-typescript');
Quickstart
import fs from 'node:fs';
import path from 'node:path';
import detective from 'detective-typescript';
// Example TypeScript code with various import types
const tsSourceCode = `
import { SomeType } from './types';
import { utilityFunction } from '@scope/utils';
import defaultExport from 'my-module';
import * as all from './all-exports';
const dynamicImport = import('./lazy-module');
interface MyInterface extends SomeType {}
export class MyClass {
constructor() {
utilityFunction();
console.log(defaultExport);
}
}
`;
// Create a dummy file to simulate reading from disk
const filePath = path.join(process.cwd(), 'temp-file.ts');
fs.writeFileSync(filePath, tsSourceCode, 'utf8');
try {
const mySourceCode = fs.readFileSync(filePath, 'utf8');
// Get all dependencies, including dynamic imports
const dependencies = detective(mySourceCode, { skipAsyncImports: false });
console.log('All dependencies:', dependencies);
// Expected: [ './types', '@scope/utils', 'my-module', './all-exports', './lazy-module' ]
// Get only static dependencies (excluding dynamic imports)
const staticDependencies = detective(mySourceCode, { skipAsyncImports: true });
console.log('Static dependencies:', staticDependencies);
// Expected: [ './types', '@scope/utils', 'my-module', './all-exports' ]
// Skip type-only imports (if 'SomeType' was a type-only import)
const nonTypeDependencies = detective(mySourceCode, { skipTypeImports: true });
console.log('Non-type dependencies:', nonTypeDependencies);
} catch (error) {
console.error('Error processing file:', error);
} finally {
fs.unlinkSync(filePath);
}