Skip to content

Commit 6b9fb92

Browse files
committed
feat: add node API
1 parent 2f0a554 commit 6b9fb92

File tree

9 files changed

+186
-113
lines changed

9 files changed

+186
-113
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Found 2 dependencies with mismatching versions across the workspace. Fix with `-
113113
## Options
114114

115115
| Name | Description |
116-
| --- | --- |
116+
| :-- | :-- |
117117
| `--fix` | Whether to autofix inconsistencies (using latest version present). |
118118
| `--ignore-dep` | Dependency to ignore mismatches for (option can be repeated). |
119119
| `--ignore-dep-pattern` | RegExp of dependency names to ignore mismatches for (option can be repeated). |
@@ -122,6 +122,24 @@ Found 2 dependencies with mismatching versions across the workspace. Fix with `-
122122
| `--ignore-path` | Workspace-relative path of packages to ignore mismatches for (option can be repeated). |
123123
| `--ignore-path-pattern` | RegExp of workspace-relative path of packages to ignore mismatches for (option can be repeated). |
124124

125+
## Node API
126+
127+
| Function | Description |
128+
| :-- | :-- |
129+
| `check()` | Checks for inconsistencies across a workspace. Optionally fixes them. Returns lists of inconsistencies: a complete list, a fixable list, and a not fixable list. |
130+
| `mismatchingVersionsToDetailedSummary()` | Returns a string of human-readable tables describing mismatching dependency versions. |
131+
| `mismatchingVersionsToFixedSummary()` | Returns a string summary of the mismatching dependency versions that were fixed. |
132+
133+
More information about parameters and return values can be found in the types and JSDoc comments.
134+
135+
Import example:
136+
137+
```ts
138+
import { check } from 'check-dependency-version-consistency';
139+
```
140+
141+
See a usage example in [`lib/cli.ts`](./lib/cli.ts).
142+
125143
## Related
126144

127145
* [npm-package-json-lint](https://github.com/tclindner/npm-package-json-lint) — use this complementary tool to enforce that your dependency versions use consistent range types (i.e. [prefer-caret-version-dependencies](https://npmpackagejsonlint.org/docs/rules/dependencies/prefer-caret-version-dependencies), [prefer-caret-version-devDependencies](https://npmpackagejsonlint.org/docs/rules/dependencies/prefer-caret-version-devDependencies))

lib/check.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
calculateVersionsForEachDependency,
3+
calculateMismatchingVersions,
4+
filterOutIgnoredDependencies,
5+
fixMismatchingVersions,
6+
} from './dependency-versions.js';
7+
import { getPackages } from './workspace.js';
8+
9+
/**
10+
* Checks for inconsistencies across a workspace. Optionally fixes them.
11+
* @param path - path to the workspace root
12+
* @param options
13+
* @returns an object with the following properties:
14+
* - `mismatchingVersions`: All the mismatching versions found.
15+
* - `mismatchingVersionsFixable`: The mismatching versions that are fixable (these will be fixed in `fix` mode)
16+
* - `mismatchingVersionsNotFixable`: The mismatching versions that are not fixable (these will be skipped in `fix` mode).
17+
*/
18+
export function check(
19+
path: string,
20+
options: {
21+
ignoreDep: string[];
22+
ignoreDepPattern: string[];
23+
ignorePackage: string[];
24+
ignorePackagePattern: string[];
25+
ignorePath: string[];
26+
ignorePathPattern: string[];
27+
fix: boolean;
28+
}
29+
) {
30+
// Calculate.
31+
const packages = getPackages(
32+
path,
33+
options.ignorePackage,
34+
options.ignorePackagePattern.map((s) => new RegExp(s)),
35+
options.ignorePath,
36+
options.ignorePathPattern.map((s) => new RegExp(s))
37+
);
38+
39+
const dependencyVersions = calculateVersionsForEachDependency(packages);
40+
41+
const mismatchingVersions = filterOutIgnoredDependencies(
42+
calculateMismatchingVersions(dependencyVersions),
43+
options.ignoreDep,
44+
options.ignoreDepPattern.map((s) => new RegExp(s))
45+
);
46+
47+
const resultsAfterFix = fixMismatchingVersions(
48+
packages,
49+
mismatchingVersions,
50+
!options.fix // Do dryrun if not fixing.
51+
);
52+
const mismatchingVersionsFixable = resultsAfterFix.fixable;
53+
const mismatchingVersionsNotFixable = resultsAfterFix.notFixable;
54+
55+
return {
56+
mismatchingVersions,
57+
mismatchingVersionsFixable,
58+
mismatchingVersionsNotFixable,
59+
};
60+
}

lib/cli.ts

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
import { Command, Argument } from 'commander';
22
import { readFileSync } from 'node:fs';
3-
import {
4-
calculateVersionsForEachDependency,
5-
calculateMismatchingVersions,
6-
filterOutIgnoredDependencies,
7-
fixMismatchingVersions,
8-
MismatchingDependencyVersions,
9-
} from '../lib/dependency-versions.js';
10-
import { getPackages } from '../lib/workspace.js';
11-
import {
12-
mismatchingVersionsToOutput,
13-
mismatchingVersionsFixedToOutput,
14-
} from '../lib/output.js';
153
import { join, dirname } from 'node:path';
164
import type { PackageJson } from 'type-fest';
175
import { fileURLToPath } from 'node:url';
6+
import {
7+
mismatchingVersionsToDetailedSummary,
8+
mismatchingVersionsToFixedSummary,
9+
} from './output.js';
10+
import { check } from './check.js';
1811

1912
const __dirname = dirname(fileURLToPath(import.meta.url));
2013

@@ -81,53 +74,31 @@ export function run() {
8174
collect,
8275
[]
8376
)
84-
.action(function (
85-
path,
86-
options: {
87-
ignoreDep: string[];
88-
ignoreDepPattern: string[];
89-
ignorePackage: string[];
90-
ignorePackagePattern: string[];
91-
ignorePath: string[];
92-
ignorePathPattern: string[];
93-
fix: boolean;
94-
}
95-
) {
96-
// Calculate.
97-
const packages = getPackages(
98-
path,
99-
options.ignorePackage,
100-
options.ignorePackagePattern.map((s) => new RegExp(s)),
101-
options.ignorePath,
102-
options.ignorePathPattern.map((s) => new RegExp(s))
103-
);
104-
105-
const dependencyVersions = calculateVersionsForEachDependency(packages);
106-
107-
let mismatchingVersions = filterOutIgnoredDependencies(
108-
calculateMismatchingVersions(dependencyVersions),
109-
options.ignoreDep,
110-
options.ignoreDepPattern.map((s) => new RegExp(s))
111-
);
112-
let mismatchingVersionsFixed: MismatchingDependencyVersions = [];
77+
.action((path, options) => {
78+
const {
79+
mismatchingVersions,
80+
mismatchingVersionsFixable,
81+
mismatchingVersionsNotFixable,
82+
} = check(path, options);
11383

11484
if (options.fix) {
115-
const resultsAfterFix = fixMismatchingVersions(
116-
packages,
117-
mismatchingVersions
118-
);
119-
mismatchingVersions = resultsAfterFix.notFixed;
120-
mismatchingVersionsFixed = resultsAfterFix.fixed;
121-
}
122-
123-
// Show output for dependencies we fixed.
124-
if (mismatchingVersionsFixed.length > 0) {
125-
console.log(mismatchingVersionsFixedToOutput(mismatchingVersionsFixed));
126-
}
85+
// Show output for dependencies we fixed.
86+
if (mismatchingVersionsFixable.length > 0) {
87+
console.log(
88+
mismatchingVersionsToFixedSummary(mismatchingVersionsFixable)
89+
);
90+
}
12791

128-
// Show output for dependencies that still have mismatches.
129-
if (mismatchingVersions.length > 0) {
130-
console.log(mismatchingVersionsToOutput(mismatchingVersions));
92+
// Show output for dependencies that still have mismatches.
93+
if (mismatchingVersionsNotFixable.length > 0) {
94+
console.log(
95+
mismatchingVersionsToDetailedSummary(mismatchingVersionsNotFixable)
96+
);
97+
process.exitCode = 1;
98+
}
99+
} else if (mismatchingVersions.length > 0) {
100+
// Show output for dependencies that have mismatches.
101+
console.log(mismatchingVersionsToDetailedSummary(mismatchingVersions));
131102
process.exitCode = 1;
132103
}
133104
})

lib/dependency-versions.ts

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,17 @@ function writeDependencyVersion(
287287
);
288288
}
289289

290+
// eslint-disable-next-line complexity
290291
export function fixMismatchingVersions(
291292
packages: Package[],
292-
mismatchingVersions: MismatchingDependencyVersions
293+
mismatchingVersions: MismatchingDependencyVersions,
294+
dryrun = false
293295
): {
294-
fixed: MismatchingDependencyVersions;
295-
notFixed: MismatchingDependencyVersions;
296+
fixable: MismatchingDependencyVersions;
297+
notFixable: MismatchingDependencyVersions;
296298
} {
297-
const fixed = [];
298-
const notFixed = [];
299+
const fixable: MismatchingDependencyVersions = [];
300+
const notFixable: MismatchingDependencyVersions = [];
299301
// Loop through each dependency that has a mismatching versions.
300302
for (const mismatchingVersion of mismatchingVersions) {
301303
// Decide what version we should fix to.
@@ -307,7 +309,7 @@ export function fixMismatchingVersions(
307309
fixedVersion = getIncreasedLatestVersion(versions);
308310
} catch {
309311
// Skip this dependency.
310-
notFixed.push(mismatchingVersion);
312+
notFixable.push(mismatchingVersion);
311313
continue;
312314
}
313315

@@ -321,7 +323,7 @@ export function fixMismatchingVersions(
321323
compareVersionRanges(fixedVersion, localPackage.packageJson.version) > 0
322324
) {
323325
// Skip this dependency.
324-
notFixed.push(mismatchingVersion);
326+
notFixable.push(mismatchingVersion);
325327
continue;
326328
}
327329

@@ -342,13 +344,15 @@ export function fixMismatchingVersions(
342344
package_.packageJson.devDependencies[mismatchingVersion.dependency] !==
343345
fixedVersion
344346
) {
345-
writeDependencyVersion(
346-
package_.pathPackageJson,
347-
package_.packageJsonEndsInNewline,
348-
'devDependencies',
349-
mismatchingVersion.dependency,
350-
fixedVersion
351-
);
347+
if (!dryrun) {
348+
writeDependencyVersion(
349+
package_.pathPackageJson,
350+
package_.packageJsonEndsInNewline,
351+
'devDependencies',
352+
mismatchingVersion.dependency,
353+
fixedVersion
354+
);
355+
}
352356
isFixed = true;
353357
}
354358

@@ -358,13 +362,15 @@ export function fixMismatchingVersions(
358362
package_.packageJson.dependencies[mismatchingVersion.dependency] !==
359363
fixedVersion
360364
) {
361-
writeDependencyVersion(
362-
package_.pathPackageJson,
363-
package_.packageJsonEndsInNewline,
364-
'dependencies',
365-
mismatchingVersion.dependency,
366-
fixedVersion
367-
);
365+
if (!dryrun) {
366+
writeDependencyVersion(
367+
package_.pathPackageJson,
368+
package_.packageJsonEndsInNewline,
369+
'dependencies',
370+
mismatchingVersion.dependency,
371+
fixedVersion
372+
);
373+
}
368374
isFixed = true;
369375
}
370376

@@ -374,24 +380,26 @@ export function fixMismatchingVersions(
374380
package_.packageJson.resolutions[mismatchingVersion.dependency] !==
375381
fixedVersion
376382
) {
377-
writeDependencyVersion(
378-
package_.pathPackageJson,
379-
package_.packageJsonEndsInNewline,
380-
'resolutions',
381-
mismatchingVersion.dependency,
382-
fixedVersion
383-
);
383+
if (!dryrun) {
384+
writeDependencyVersion(
385+
package_.pathPackageJson,
386+
package_.packageJsonEndsInNewline,
387+
'resolutions',
388+
mismatchingVersion.dependency,
389+
fixedVersion
390+
);
391+
}
384392
isFixed = true;
385393
}
386394
}
387395

388396
if (isFixed) {
389-
fixed.push(mismatchingVersion);
397+
fixable.push(mismatchingVersion);
390398
}
391399
}
392400

393401
return {
394-
fixed,
395-
notFixed,
402+
fixable,
403+
notFixable,
396404
};
397405
}

lib/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Public Node API.
2+
3+
export { check } from './check.js';
4+
export {
5+
mismatchingVersionsToDetailedSummary,
6+
mismatchingVersionsToFixedSummary,
7+
} from './output.js';

lib/output.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
} from './semver.js';
77
import { table } from 'table';
88

9-
export function mismatchingVersionsToOutput(
9+
/**
10+
* Returns human-readable tables describing mismatching dependency versions.
11+
*/
12+
export function mismatchingVersionsToDetailedSummary(
1013
mismatchingDependencyVersions: MismatchingDependencyVersions
1114
): string {
1215
if (mismatchingDependencyVersions.length === 0) {
@@ -59,7 +62,10 @@ export function mismatchingVersionsToOutput(
5962
].join('\n');
6063
}
6164

62-
export function mismatchingVersionsFixedToOutput(
65+
/**
66+
* Returns a summary of the mismatching dependency versions that were fixed.
67+
*/
68+
export function mismatchingVersionsToFixedSummary(
6369
mismatchingDependencyVersions: MismatchingDependencyVersions
6470
): string {
6571
if (mismatchingDependencyVersions.length === 0) {

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
},
2020
"license": "MIT",
2121
"type": "module",
22+
"exports": "./dist/lib/index.js",
23+
"main": "./dist/lib/index.js",
24+
"types": "./dist/lib/index.d.ts",
2225
"bin": {
2326
"check-dependency-version-consistency": "dist/bin/check-dependency-version-consistency.js"
2427
},

0 commit comments

Comments
 (0)