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 = { type: "root"; validate: ( rootPackage: Package, allPackages: Map, options: Options ) => ErrorType[]; fix?: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type RootCheckWithFix = { type: "root"; validate: ( rootPackage: Package, allPackages: Map, options: Options ) => ErrorType[]; fix: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type AllCheck = { type: "all"; validate: ( workspace: Package, allWorkspaces: Map, rootWorkspace: Package | undefined, options: Options ) => ErrorType[]; fix?: ( error: ErrorType, options: Options ) => void | { requiresInstall: boolean }; print: (error: ErrorType, options: Options) => string; }; type AllCheckWithFix = { type: "all"; validate: ( workspace: Package, allWorkspaces: Map, 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(func: (arg: Arg) => Ret): (arg: Arg) => Ret { let cache = new WeakMap(); 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 ) { let dependencyRangesMapping = new Map(); 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(); 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, arrB: Array) { for (var i = 0; i < arrA.length; i++) { if (arrA[i] !== arrB[i]) { return false; } } return true; } function makeCheck( check: RootCheckWithFix ): RootCheckWithFix; function makeCheck( check: AllCheckWithFix ): AllCheckWithFix; function makeCheck( check: RootCheck ): RootCheck; function makeCheck(check: AllCheck): AllCheck; function makeCheck( check: RootCheck | AllCheck ): RootCheck | AllCheck { return check; } export { makeCheck };