Detect circular dependencies in your TypeScript projects.
TypeScript
912
63 commits
updated Jul 29, 2026
A robust static dependency analyzer for your JavaScript and TypeScript projects.
Highlights | Install | Usage | Options | API
CommonJS, ESM.JavaScript and TypeScript completely.
tsconfig.json files and TypeScript project references.dpdm.config.ts and JavaScript/JSON config files.madge, whose results are completely inconclusive when analyze TypeScript.For command line
npm i -g dpdm
# or via yarn
yarn global add dpdm
As a module
npm i -D dpdm
# or via yarn
yarn add -D dpdm
Simple usage
dpdm ./src/index.ts
Print circular dependencies only
dpdm --no-warning --no-tree ./src/index.ts
Exit with a non-zero code if a circular dependency is found.
dpdm --exit-code circular:1 ./src/index.ts
Ignore type dependencies for TypeScript modules
dpdm -T ./src/index.ts
Find unused files by index.js in src directory:
dpdm --no-tree --no-warning --no-circular --detect-unused-files-from 'src/**/*.*' 'index.js'
Skip dynamic imports:
# The value circular will only ignore the dynamic imports
# when parse circular references.
# You can set it as tree to ignore the dynamic imports
# when parse source files.
dpdm --skip-dynamic-imports circular index.js
Ignore specified imports when finding circular dependencies:
dpdm ./src/index.js --skip-imports 'src/a.js:.*' src/c.js:src/d.js
Analyze files from another working directory:
dpdm --cwd ../other-project ./src/index.ts
Show dependencies and circular dependencies grouped by package:
dpdm --group-by-package './packages/*/src/index.ts'
Use a config file:
// dpdm.config.ts
import { defineConfig } from 'dpdm';
export default defineConfig({
files: ['./src/index.ts'],
exitCode: 'circular:1',
transform: true,
warning: false,
});
dpdm
dpdm searches for dpdm.config.ts, dpdm.config.mts,
dpdm.config.cts, dpdm.config.mjs, dpdm.config.cjs,
dpdm.config.js, or dpdm.config.json in the working directory. CLI
options override config file values.
Ignore imports from source comments:
// @dpdm-ignore
import './intentional-cycle';
The @dpdm-ignore comment can be placed before an import, export,
require(), or dynamic import() dependency.
Ignore selected warnings:
dpdm ./src/index.ts --ignore-miss-warning '^vscode$' --ignore-skip-warning '^node_modules/'
dpdm [files...]
Analyze the files' dependencies.
Positionals:
files The file paths or globs [string]
Options:
--version Show version number [boolean]
--config the config file path, default searches dpdm.config.* in cwd
[string]
--context the context directory to shorten path, default is cwd [string]
--cwd the working directory used to match files and resolve relative
paths, default is current directory [string]
--extensions, --ext comma separated extensions to resolve
[string] [default: ".ts,.tsx,.mjs,.js,.jsx,.json"]
--js comma separated extensions indicate the file is js like
[string] [default: ".ts,.tsx,.mjs,.js,.jsx"]
--include included filenames regexp in string, default includes all files
[string] [default: ".*"]
--exclude excluded filenames regexp in string, set as empty string to
include all files [string] [default: "node_modules"]
-o, --output output json to file [string]
--tree print tree to stdout [boolean] [default: true]
--circular print circular to stdout [boolean] [default: true]
--warning print warning to stdout [boolean] [default: true]
--tsconfig the tsconfig path, which is used for resolve path alias, default
is tsconfig.json if it exists in context directory. Project
references are supported. When omitted, dpdm can use the nearest
package tsconfig.json for each source file. [string]
-T, --transform transform typescript modules to javascript before analyze, it
allows you to omit types dependency in typescript
[boolean] [default: false]
--exit-code exit with specified code, the value format is CASE:CODE,
`circular` is the only supported CASE, CODE should be a integer
between 0 and 128. For example: `dpdm --exit-code circular:1` the
program will exit with code 1 if circular dependency found.
[string]
--progress show progress bar [boolean] [default: true]
--detect-unused-files-from this file is a glob, used for finding unused files. [string]
--skip-dynamic-imports Skip parse import(...) statement.
[string] [choices: "tree", "circular"]
--skip-imports Skip import edges from circular checks. Values are regexp
ISSUER:DEPENDENCY pairs. [array]
--ignore-miss-warning ignore miss warnings whose import request matches any specified
regexp [array]
--ignore-skip-warning ignore skip warnings whose skipped file matches any specified
regexp [array]
--group-by-package print dependencies and circulars grouped by nearest package.json
[boolean]
-h, --help Show help [boolean]

import {
defineConfig,
parseDependencyTree,
parseCircular,
prettyCircular,
} from 'dpdm';
parseDependencyTree('./index', {
/* options, see below */
}).then((tree) => {
const circulars = parseCircular(tree);
console.log(prettyCircular(circulars));
});
parseDependencyTree(entries, option, output): parse dependencies for glob entries
/**
* @param entries - the glob entries to match
* @param options - the options, see below
*/
export declare function parseDependencyTree(
entries: string | string[],
options: ParserOptions,
): Promise<DependencyTree>;
/**
* the parse options
*/
export interface ParseOptions {
cwd: string;
context: string;
extensions: string[];
js: string[];
include: RegExp;
exclude: RegExp;
tsconfig: string | undefined;
onProgress: (event: 'start' | 'end', target: string) => void;
transform: boolean;
skipDynamicImports: boolean;
}
export declare function defineConfig(config: Config): Config;
export interface Config {
files?: string | string[];
cwd?: string;
context?: string;
extensions?: string | string[];
js?: string | string[];
include?: string | RegExp;
exclude?: string | RegExp;
output?: string;
tree?: boolean;
circular?: boolean;
warning?: boolean;
tsconfig?: string;
transform?: boolean;
exitCode?: string;
progress?: boolean;
detectUnusedFilesFrom?: string;
skipDynamicImports?: 'tree' | 'circular';
skipImports?: string | string[];
groupByPackage?: boolean;
ignoreMissWarning?: string | string[];
ignoreSkipWarning?: string | string[];
}
export enum DependencyKind {
CommonJS = 'CommonJS', // require
StaticImport = 'StaticImport', // import ... from "foo"
DynamicImport = 'DynamicImport', // import("foo")
StaticExport = 'StaticExport', // export ... from "foo"
}
export interface Dependency {
issuer: string;
request: string;
kind: DependencyKind;
id: string | null; // the shortened, resolved filename, if cannot resolve, it will be null
}
// the parse tree result, key is file id, value is its dependencies
// if file is ignored, it will be null
export type DependencyTree = Record<string, Dependency[] | null>;
groupDependencyTreeByPackage(tree, context): group dependencies by nearest package.json
export declare function groupDependencyTreeByPackage(
tree: DependencyTree,
context: string,
): DependencyTree;
parseCircular(tree): parse circulars in dependency tree
export declare function parseCircular(tree: DependencyTree): string[][];
Overall
Maintained
Code review
npm
Latest release
Releases this year
40,578 followers · starred Sep 2026
165 followers · starred Jan 2024
82 followers · starred Sep 2022
1,775 followers · starred Nov 2024
Detect circular dependencies in your TypeScript projects.
TypeScript
912
63 commits
updated Jul 29, 2026
A robust static dependency analyzer for your JavaScript and TypeScript projects.
Highlights | Install | Usage | Options | API
CommonJS, ESM.JavaScript and TypeScript completely.
tsconfig.json files and TypeScript project references.dpdm.config.ts and JavaScript/JSON config files.madge, whose results are completely inconclusive when analyze TypeScript.For command line
npm i -g dpdm
# or via yarn
yarn global add dpdm
As a module
npm i -D dpdm
# or via yarn
yarn add -D dpdm
Simple usage
dpdm ./src/index.ts
Print circular dependencies only
dpdm --no-warning --no-tree ./src/index.ts
Exit with a non-zero code if a circular dependency is found.
dpdm --exit-code circular:1 ./src/index.ts
Ignore type dependencies for TypeScript modules
dpdm -T ./src/index.ts
Find unused files by index.js in src directory:
dpdm --no-tree --no-warning --no-circular --detect-unused-files-from 'src/**/*.*' 'index.js'
Skip dynamic imports:
# The value circular will only ignore the dynamic imports
# when parse circular references.
# You can set it as tree to ignore the dynamic imports
# when parse source files.
dpdm --skip-dynamic-imports circular index.js
Ignore specified imports when finding circular dependencies:
dpdm ./src/index.js --skip-imports 'src/a.js:.*' src/c.js:src/d.js
Analyze files from another working directory:
dpdm --cwd ../other-project ./src/index.ts
Show dependencies and circular dependencies grouped by package:
dpdm --group-by-package './packages/*/src/index.ts'
Use a config file:
// dpdm.config.ts
import { defineConfig } from 'dpdm';
export default defineConfig({
files: ['./src/index.ts'],
exitCode: 'circular:1',
transform: true,
warning: false,
});
dpdm
dpdm searches for dpdm.config.ts, dpdm.config.mts,
dpdm.config.cts, dpdm.config.mjs, dpdm.config.cjs,
dpdm.config.js, or dpdm.config.json in the working directory. CLI
options override config file values.
Ignore imports from source comments:
// @dpdm-ignore
import './intentional-cycle';
The @dpdm-ignore comment can be placed before an import, export,
require(), or dynamic import() dependency.
Ignore selected warnings:
dpdm ./src/index.ts --ignore-miss-warning '^vscode$' --ignore-skip-warning '^node_modules/'
dpdm [files...]
Analyze the files' dependencies.
Positionals:
files The file paths or globs [string]
Options:
--version Show version number [boolean]
--config the config file path, default searches dpdm.config.* in cwd
[string]
--context the context directory to shorten path, default is cwd [string]
--cwd the working directory used to match files and resolve relative
paths, default is current directory [string]
--extensions, --ext comma separated extensions to resolve
[string] [default: ".ts,.tsx,.mjs,.js,.jsx,.json"]
--js comma separated extensions indicate the file is js like
[string] [default: ".ts,.tsx,.mjs,.js,.jsx"]
--include included filenames regexp in string, default includes all files
[string] [default: ".*"]
--exclude excluded filenames regexp in string, set as empty string to
include all files [string] [default: "node_modules"]
-o, --output output json to file [string]
--tree print tree to stdout [boolean] [default: true]
--circular print circular to stdout [boolean] [default: true]
--warning print warning to stdout [boolean] [default: true]
--tsconfig the tsconfig path, which is used for resolve path alias, default
is tsconfig.json if it exists in context directory. Project
references are supported. When omitted, dpdm can use the nearest
package tsconfig.json for each source file. [string]
-T, --transform transform typescript modules to javascript before analyze, it
allows you to omit types dependency in typescript
[boolean] [default: false]
--exit-code exit with specified code, the value format is CASE:CODE,
`circular` is the only supported CASE, CODE should be a integer
between 0 and 128. For example: `dpdm --exit-code circular:1` the
program will exit with code 1 if circular dependency found.
[string]
--progress show progress bar [boolean] [default: true]
--detect-unused-files-from this file is a glob, used for finding unused files. [string]
--skip-dynamic-imports Skip parse import(...) statement.
[string] [choices: "tree", "circular"]
--skip-imports Skip import edges from circular checks. Values are regexp
ISSUER:DEPENDENCY pairs. [array]
--ignore-miss-warning ignore miss warnings whose import request matches any specified
regexp [array]
--ignore-skip-warning ignore skip warnings whose skipped file matches any specified
regexp [array]
--group-by-package print dependencies and circulars grouped by nearest package.json
[boolean]
-h, --help Show help [boolean]

import {
defineConfig,
parseDependencyTree,
parseCircular,
prettyCircular,
} from 'dpdm';
parseDependencyTree('./index', {
/* options, see below */
}).then((tree) => {
const circulars = parseCircular(tree);
console.log(prettyCircular(circulars));
});
parseDependencyTree(entries, option, output): parse dependencies for glob entries
/**
* @param entries - the glob entries to match
* @param options - the options, see below
*/
export declare function parseDependencyTree(
entries: string | string[],
options: ParserOptions,
): Promise<DependencyTree>;
/**
* the parse options
*/
export interface ParseOptions {
cwd: string;
context: string;
extensions: string[];
js: string[];
include: RegExp;
exclude: RegExp;
tsconfig: string | undefined;
onProgress: (event: 'start' | 'end', target: string) => void;
transform: boolean;
skipDynamicImports: boolean;
}
export declare function defineConfig(config: Config): Config;
export interface Config {
files?: string | string[];
cwd?: string;
context?: string;
extensions?: string | string[];
js?: string | string[];
include?: string | RegExp;
exclude?: string | RegExp;
output?: string;
tree?: boolean;
circular?: boolean;
warning?: boolean;
tsconfig?: string;
transform?: boolean;
exitCode?: string;
progress?: boolean;
detectUnusedFilesFrom?: string;
skipDynamicImports?: 'tree' | 'circular';
skipImports?: string | string[];
groupByPackage?: boolean;
ignoreMissWarning?: string | string[];
ignoreSkipWarning?: string | string[];
}
export enum DependencyKind {
CommonJS = 'CommonJS', // require
StaticImport = 'StaticImport', // import ... from "foo"
DynamicImport = 'DynamicImport', // import("foo")
StaticExport = 'StaticExport', // export ... from "foo"
}
export interface Dependency {
issuer: string;
request: string;
kind: DependencyKind;
id: string | null; // the shortened, resolved filename, if cannot resolve, it will be null
}
// the parse tree result, key is file id, value is its dependencies
// if file is ignored, it will be null
export type DependencyTree = Record<string, Dependency[] | null>;
groupDependencyTreeByPackage(tree, context): group dependencies by nearest package.json
export declare function groupDependencyTreeByPackage(
tree: DependencyTree,
context: string,
): DependencyTree;
parseCircular(tree): parse circulars in dependency tree
export declare function parseCircular(tree: DependencyTree): string[][];
Overall
Maintained
Code review
npm
Latest release
Releases this year
40,578 followers · starred Sep 2026
165 followers · starred Jan 2024
82 followers · starred Sep 2022
1,775 followers · starred Nov 2024