-
Notifications
You must be signed in to change notification settings - Fork 276
/
check-package-versions.js
executable file
·103 lines (89 loc) · 3.33 KB
/
check-package-versions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env node
// Check versions of in-repo packages to make sure they match with the package.json version.
// This is to prevent inadvertent updates of dependency versions for packages which are symlinked
// by lerna. The dependency versions of these packages should only ever be updated in the release branch.
const execa = require('execa');
const path = require('path');
/**
* Create a function which can run lerna commands
* @param lernaPath {string} path to lerna binary
* @returns {function(string, string[], Object.<string, string>): Promise<string>}
*/
function getLernaRunner(lernaPath) {
return async (command, args = [], options = {}) => {
const { stdout } = await execa(lernaPath, [command, ...args], options);
return stdout;
};
}
/**
* Get information on the modules in this repo that are managed by lerna.
* @param lerna {function}
* @returns {Promise<{path: *, name: *, deps: *, version: *}[]>}
*/
async function getLernaManagedModules(lerna) {
const depGraph = JSON.parse(await lerna('list', ['--loglevel', 'silent', '--graph', '--all']));
const managedModules = JSON.parse(await lerna('list', ['--loglevel', 'silent', '--json', '--all']));
const managedModuleNames = managedModules.map(({ name }) => name);
return Object.entries(depGraph).map(([name, deps]) => {
const mod = managedModules.find((mod) => mod.name === name);
return {
name,
deps: deps.filter((d) => managedModuleNames.includes(d)),
path: mod.location,
version: mod.version,
};
});
}
/**
* Build a dictionary from package name to the expected version of that package.
* @param modules
* @returns {Object.<string, string>}
*/
function getExpectedVersions(modules) {
return Object.values(modules).reduce((acc, mod) => {
return Object.assign(acc, { [mod.name]: mod.version });
}, {});
}
/**
* For the module at `modPath`, get the version of the dependency `depName`.
* If the version is prefixed with a carat or tilde, it will be stripped.
* @param modPath {string}
* @param depName {string}
* @returns {string | undefined}
*/
function getDependencyVersion(modPath, depName) {
const packageJsonPath = path.join(modPath, 'package.json');
const {
dependencies = {},
devDependencies = {},
optionalDependencies = {},
peerDependencies = {},
} = require(packageJsonPath);
const deps = { ...dependencies, ...devDependencies, ...optionalDependencies, ...peerDependencies };
if (deps[depName]) {
const matches = deps[depName].match(/^([^~])?(.*)$/);
return matches[matches.length - 1];
}
}
async function main() {
const { stdout: lernaBinary } = await execa('yarn', ['bin', 'lerna'], { cwd: process.cwd() });
const lerna = getLernaRunner(lernaBinary);
const modules = await getLernaManagedModules(lerna);
const expectedVersions = getExpectedVersions(modules);
let exitCode = 0;
for (const mod of modules) {
for (const dep of mod.deps) {
const depVersion = getDependencyVersion(mod.path, dep);
if (depVersion && depVersion !== expectedVersions[dep]) {
console.log(
`error: expected lerna-managed module ${mod.name} to depend on package ${dep} using version ${expectedVersions[dep]}, but found version ${depVersion} instead`
);
exitCode = 1;
}
}
}
return exitCode;
}
main()
.then(process.exit)
.catch((e) => console.error(e));