143 lines
3.6 KiB
TypeScript
143 lines
3.6 KiB
TypeScript
import * as depcheck from 'depcheck';
|
|
import { readFileSync, readdirSync } from 'fs';
|
|
import { join } from 'path';
|
|
import chalk from 'chalk';
|
|
import { satisfies } from 'semver';
|
|
|
|
const argv = require('yargs')
|
|
.usage('Check projects for dependency discrepancies.')
|
|
.option('projects', {
|
|
alias: 'p',
|
|
type: 'array',
|
|
description: 'Projects to check',
|
|
})
|
|
.option('missing', {
|
|
alias: 'm',
|
|
type: 'boolean',
|
|
default: false,
|
|
description: 'Check for missing dependencies',
|
|
})
|
|
.option('discrepancies', {
|
|
alias: 'd',
|
|
type: 'boolean',
|
|
default: true,
|
|
description: 'Check for discrepancies between package and dev dependencies',
|
|
})
|
|
.option('verbose', {
|
|
alias: 'v',
|
|
type: 'boolean',
|
|
description: 'Run with verbose logging',
|
|
}).argv;
|
|
|
|
(async () => {
|
|
const { devDependencies } = JSON.parse(
|
|
readFileSync(`./package.json`).toString()
|
|
);
|
|
|
|
const packagesDirectory = join(__dirname, '..', 'packages');
|
|
|
|
const projects =
|
|
argv.projects ||
|
|
readdirSync(packagesDirectory, { withFileTypes: true })
|
|
.filter((dirent) => dirent.isDirectory())
|
|
.map((dirent) => dirent.name);
|
|
|
|
const results = await Promise.all(
|
|
projects
|
|
.sort()
|
|
.map((name) => ({ name }))
|
|
.map(async (project) => {
|
|
const projectPath = join(packagesDirectory, project.name);
|
|
const { dependencies } = JSON.parse(
|
|
readFileSync(`${projectPath}/package.json`).toString()
|
|
);
|
|
|
|
const missing = argv.missing
|
|
? await getMissingDependencies(
|
|
project.name,
|
|
projectPath,
|
|
dependencies
|
|
)
|
|
: [];
|
|
|
|
const discrepancies = argv.discrepancies
|
|
? getDiscrepancies(dependencies, devDependencies)
|
|
: [];
|
|
|
|
return { ...project, missing, discrepancies };
|
|
})
|
|
);
|
|
|
|
const total = { missing: 0, discrepancies: 0 };
|
|
|
|
results.forEach(({ name, missing, discrepancies }) => {
|
|
if (!missing.length && !discrepancies.length) {
|
|
return;
|
|
}
|
|
|
|
console.log(`${chalk.inverse.bold.cyan(` ${name.toUpperCase()} `)}`);
|
|
|
|
if (missing.length > 0) {
|
|
total.missing += missing.length;
|
|
console.log(
|
|
`⚠️ ${chalk.bold.inverse(` Missing `)}\n${missing
|
|
.sort()
|
|
.map(
|
|
(p) => ` ${devDependencies[p] ? `${p}@${devDependencies[p]}` : p}`
|
|
)
|
|
.join(`\n`)}\n`
|
|
);
|
|
}
|
|
|
|
if (discrepancies.length > 0) {
|
|
total.discrepancies += discrepancies.length;
|
|
console.log(
|
|
`⛔ ${chalk.bold.inverse(` Discrepancies `)}\n${discrepancies
|
|
.map((d) => ` ${d}`)
|
|
.join(`\n`)}\n`
|
|
);
|
|
}
|
|
});
|
|
|
|
if (total.discrepancies > 0) {
|
|
process.exit(1);
|
|
}
|
|
|
|
process.exit(0);
|
|
})().catch((err) => console.log(err));
|
|
|
|
async function getMissingDependencies(
|
|
name: string,
|
|
path: string,
|
|
dependencies: JSON
|
|
) {
|
|
const options: any = {
|
|
skipMissing: false, // skip calculation of missing dependencies
|
|
ignorePatterns: ['*.spec*'],
|
|
};
|
|
const { missing } = await depcheck(path, {
|
|
...options,
|
|
package: { dependencies },
|
|
});
|
|
|
|
if (argv.verbose) {
|
|
console.log(name, missing);
|
|
}
|
|
|
|
return Object.keys(missing).filter((p) => !p.startsWith('@nrwl/'));
|
|
}
|
|
|
|
function getDiscrepancies(projectDependencies: JSON, devDependencies: JSON) {
|
|
return Object.keys(projectDependencies)
|
|
.filter((p) => !p.startsWith('@nrwl/'))
|
|
.filter(
|
|
(p) =>
|
|
devDependencies[p] &&
|
|
projectDependencies[p] !== devDependencies[p] &&
|
|
!satisfies(devDependencies[p], projectDependencies[p])
|
|
)
|
|
.map(
|
|
(p) => `${p}@${devDependencies[p]} ${chalk.dim(projectDependencies[p])}`
|
|
);
|
|
}
|