rollup-plugin-circular-dependencies
Version:
Detect circular dependencies in Rollup projects
221 lines (220 loc) • 6.36 kB
text/typescript
import { FilterPattern } from "@rollup/pluginutils";
import { Plugin, PluginContext } from "rollup";
//#region src/module/types.d.ts
/**
* Duplicates Rollup's `ModuleInfo` interface for backwards compatibility
*/
interface ModuleInfo {
id: string;
importedIds: string[] | readonly string[];
dynamicallyImportedIds: string[] | readonly string[];
}
//#endregion
//#region src/module/ModuleNode.d.ts
declare class ModuleNode {
readonly id: string;
readonly importedIds: readonly string[];
readonly children: Set<ModuleNode>;
constructor(moduleInfo: ModuleInfo);
}
//#endregion
//#region src/types.d.ts
type CircularDependenciesData = Record<string, Array<Array<ModuleNode['id']>>>;
/** Formatted output data produced by formatters */
type FormattedData = string | CircularDependenciesData | null | undefined;
/**
* Metrics collected during circular dependency detection.
* Provided in the `onEnd` callback for analysis and monitoring.
*/
interface Metrics {
/** Total number of modules checked for circular dependencies */
readonly modulesChecked: number;
/** Number of unique cycles found */
readonly cyclesFound: number;
/** Size of the largest cycle (number of modules) */
readonly largestCycleSize: number;
/** Time taken for detection in milliseconds */
readonly detectionTimeMs: number;
}
interface OptionsOnEndArgs {
rawOutput: CircularDependenciesData;
formattedOutput: unknown;
metrics: Metrics;
}
interface Options {
/**
* Enable plugin
*
* @default true
*/
enabled?: boolean;
/**
* Include specific files based on a RegExp or a glob pattern
*
* @default [/\.(c|m)?[jt]s(x)?$/]
*/
include?: FilterPattern;
/**
* Exclude specific files based on a RegExp or a glob pattern
*
* @default [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/]
*/
exclude?: FilterPattern;
/**
* Throw Rollup/Vite error instead of warning
*
* @default true
*/
throwOnError?: boolean;
/**
* Path to the file with scan results. By default, the result is output to the console
*
* @default ""
*/
outputFilePath?: string;
/**
* Enable debug logging for troubleshooting
*
* @default false
*/
debug?: boolean;
/**
* Formats the output module path
*
* _Default_: `""`
*
* @param {string} path - The module path to be formatted
* @returns {string} - The formatted module path
*/
formatOutModulePath?: (path: string) => string;
/**
* Formats the given data into a specific output format
*
* _Default (console)_: `DefaultFormatters.Pretty({ colors: true })`
*
* _Default (file)_: `DefaultFormatters.JSON()`
*
* @param {CircularDependenciesData} data - The input data to be formatted
* @returns {unknown} - The formatted output
*/
formatOut?: (data: CircularDependenciesData) => unknown;
/**
* Filter function to ignore specific circular dependency cycles.
* Return `true` to ignore the cycle, `false` to report it.
*
* @param {string[]} cyclePaths - Array of module paths forming the cycle
* @returns {boolean} - Whether to ignore this cycle
*
* @example
* ```ts
* ignoreCycle: (paths) => paths.some(p => p.includes('generated'))
* ```
*/
ignoreCycle?: (cyclePaths: string[]) => boolean;
/**
* Called before the cycle detection starts
*
* @returns {void}
*/
onStart?: (pluginContext: PluginContext) => void;
/**
* Called for each cyclical module
*
* @returns {void}
*/
onDetected?: (modulePath: ModuleNode['id'], pluginContext: PluginContext) => void;
/**
* Called after the cycle detection ends
*
* @returns {void}
*/
onEnd?: (params: OptionsOnEndArgs, pluginContext: PluginContext) => void;
}
//#endregion
//#region src/utils/formatters.d.ts
/** Formatter function that transforms circular dependencies data into a string */
type Formatter = (data: CircularDependenciesData) => string;
interface PrettyFormatterConfig {
colors?: boolean;
}
/**
* Creates a JSON formatter that serializes data with 2-space indentation.
*
* @returns Formatter function producing JSON string output
*
* @example
* ```ts
* circularDependencies({
* formatOut: DefaultFormatters.JSON()
* })
* ```
*/
declare function JSONFormatter(): Formatter;
/**
* Creates a human-readable pretty formatter with optional color support.
* Colors are enabled by default unless disabled via environment variables
* (`NO_COLOR`, `NODE_DISABLE_COLORS`, `FORCE_COLOR=0`).
*
* @param config - Optional configuration for color output
* @returns Formatter function producing styled string output
*
* @example
* ```ts
* circularDependencies({
* formatOut: DefaultFormatters.Pretty({ colors: false })
* })
* ```
*/
declare function PrettyFormatter(config?: PrettyFormatterConfig): Formatter;
declare const DefaultFormatters: {
JSON: typeof JSONFormatter;
Pretty: typeof PrettyFormatter;
};
//#endregion
//#region src/index.d.ts
/**
* Creates a Rollup plugin that detects circular dependencies in your project.
* Works with both Rollup and Vite.
*
* @param options - Plugin configuration options
* @returns Rollup plugin instance
*
* @example
* ```ts
* // Basic usage
*
* import circularDependencies from 'rollup-plugin-circular-dependencies';
*
* export default {
* plugins: [circularDependencies()]
* }
* ```
*
* @example
* ```ts
* // Advanced usage
*
* import circularDependencies, { DefaultFormatters } from 'rollup-plugin-circular-dependencies';
*
* export default {
* plugins: [
* circularDependencies({
* enabled: true,
* exclude: [/node_modules/],
* throwOnError: true,
* debug: false,
* formatOut: DefaultFormatters.Pretty({ colors: false }),
* ignoreCycle: (paths) => paths.some(p => p.includes('generated')),
* onStart: (pluginContext) => {},
* onDetected: (modulePath, pluginContext) => {},
* onEnd: ({ rawOutput, formattedOutput, metrics }, pluginContext) => {
* console.log(`Found ${metrics.cyclesFound} cycle(s)`);
* },
* })
* ]
* }
* ```
*/
declare function circularDependencies(options?: Options): Plugin;
//#endregion
export { type CircularDependenciesData, DefaultFormatters, type FormattedData, type Metrics, type Options, circularDependencies, circularDependencies as default };