UNPKG

@manypkg/cli

Version:

Manypkg is a linter for `package.json` files in Yarn, npm, Lerna, pnpm or Rush monorepos.

195 lines (172 loc) 5.21 kB
import type { Package } from "@manypkg/get-packages"; import * as semver from "semver"; import { highest } from "sembear"; export const NORMAL_DEPENDENCY_TYPES = [ "dependencies", "devDependencies", "optionalDependencies", ] as const; export const DEPENDENCY_TYPES = [ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies", ] as const; export type Options = { defaultBranch?: string; ignoredRules?: string[]; workspaceProtocol?: "allow" | "require"; }; type RootCheck<ErrorType> = { type: "root"; validate: ( rootPackage: Package, allPackages: Map<string, Package>, options: Options ) => ErrorType[]; fix?: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type RootCheckWithFix<ErrorType> = { type: "root"; validate: ( rootPackage: Package, allPackages: Map<string, Package>, options: Options ) => ErrorType[]; fix: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type AllCheck<ErrorType> = { type: "all"; validate: ( workspace: Package, allWorkspaces: Map<string, Package>, rootWorkspace: Package | undefined, options: Options ) => ErrorType[]; fix?: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type AllCheckWithFix<ErrorType> = { type: "all"; validate: ( workspace: Package, allWorkspaces: Map<string, Package>, rootWorkspace: Package | undefined, options: Options ) => ErrorType[]; fix: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; export function sortObject(prevObj: { [key: string]: string }) { let newObj: { [key: string]: string } = {}; for (let key of Object.keys(prevObj).sort()) { newObj[key] = prevObj[key]; } return newObj; } export function sortDeps(pkg: Package) { for (let depType of DEPENDENCY_TYPES) { let prevDeps = pkg.packageJson[depType]; if (prevDeps) { pkg.packageJson[depType] = sortObject(prevDeps); } } } function weakMemoize<Arg, Ret>(func: (arg: Arg) => Ret): (arg: Arg) => Ret { let cache = new WeakMap<any, any>(); return (arg) => { if (cache.has(arg)) { return cache.get(arg); } let ret = func(arg); cache.set(arg, ret); return ret; }; } export let getMostCommonRangeMap = weakMemoize(function getMostCommonRanges( allPackages: Map<string, Package> ) { let dependencyRangesMapping = new Map<string, { [key: string]: number }>(); for (let [pkgName, pkg] of allPackages) { for (let depType of NORMAL_DEPENDENCY_TYPES) { let deps = pkg.packageJson[depType]; if (deps) { for (let depName in deps) { const depSpecifier = deps[depName]; if (!allPackages.has(depName)) { if (!semver.validRange(deps[depName])) { continue; } let dependencyRanges = dependencyRangesMapping.get(depName) || {}; const specifierCount = dependencyRanges[depSpecifier] || 0; dependencyRanges[depSpecifier] = specifierCount + 1; dependencyRangesMapping.set(depName, dependencyRanges); } } } } } let mostCommonRangeMap = new Map<string, string>(); for (let [depName, specifierMap] of dependencyRangesMapping) { const specifierMapEntryArray = Object.entries(specifierMap); const [first] = specifierMapEntryArray; const maxValue = specifierMapEntryArray.reduce((acc, value) => { if (acc[1] === value[1]) { // If all dependency ranges occurances are equal, pick the highest. // It's impossible to infer intention of the developer // when all ranges occur an equal amount of times const highestRange = highest([acc[0], value[0]]); return [highestRange, acc[1]]; } if (acc[1] > value[1]) { return acc; } return value; }, first); mostCommonRangeMap.set(depName, maxValue[0]); } return mostCommonRangeMap; }); export function versionRangeToRangeType(versionRange: string) { if (versionRange.charAt(0) === "^") return "^"; if (versionRange.charAt(0) === "~") return "~"; return ""; } export function isArrayEqual(arrA: Array<string>, arrB: Array<string>) { for (var i = 0; i < arrA.length; i++) { if (arrA[i] !== arrB[i]) { return false; } } return true; } function makeCheck<ErrorType>( check: RootCheckWithFix<ErrorType> ): RootCheckWithFix<ErrorType>; function makeCheck<ErrorType>( check: AllCheckWithFix<ErrorType> ): AllCheckWithFix<ErrorType>; function makeCheck<ErrorType>( check: RootCheck<ErrorType> ): RootCheck<ErrorType>; function makeCheck<ErrorType>(check: AllCheck<ErrorType>): AllCheck<ErrorType>; function makeCheck<ErrorType>( check: RootCheck<ErrorType> | AllCheck<ErrorType> ): RootCheck<ErrorType> | AllCheck<ErrorType> { return check; } export { makeCheck };