UNPKG

@jsenv/monorepo

Version:

Helpers to manage packages in a monorepo

233 lines (227 loc) 7.74 kB
import { UNICODE } from "@jsenv/humanize"; import { collectWorkspacePackages } from "./internal/collect_workspace_packages.js"; import { compareTwoPackageVersions, VERSION_COMPARE_RESULTS, } from "./internal/compare_two_package_versions.js"; import { buildDependencyGraph, orderByDependencies, } from "./internal/dependency_graph.js"; import { fetchWorkspaceLatests } from "./internal/fetch_workspace_latests.js"; import { increaseVersion } from "./internal/increase_version.js"; import { shouldUpdateVersion } from "./internal/should_update_version.js"; export const syncPackagesVersions = async ({ logs = true, directoryUrl, packagesRelations = {}, }) => { const workspacePackages = await collectWorkspacePackages({ directoryUrl }); const registryLatestVersions = await fetchWorkspaceLatests(workspacePackages); const outdatedPackageNames = []; const toPublishPackageNames = []; for (const packageName of Object.keys(workspacePackages)) { const workspacePackage = workspacePackages[packageName]; const workspacePackageVersion = workspacePackage.packageObject.version; const registryLatestVersion = registryLatestVersions[packageName]; const result = registryLatestVersion === null ? VERSION_COMPARE_RESULTS.GREATER : compareTwoPackageVersions( workspacePackageVersion, registryLatestVersion, ); if (result === VERSION_COMPARE_RESULTS.SMALLER) { outdatedPackageNames.push(packageName); continue; } if (!workspacePackage.packageObject.private) { if ( result === VERSION_COMPARE_RESULTS.GREATER || result === VERSION_COMPARE_RESULTS.DIFF_TAG ) { toPublishPackageNames.push(packageName); } } } const versionUpdates = []; const dependencyUpdates = []; if (outdatedPackageNames.length) { outdatedPackageNames.forEach((outdatedPackageName) => { const workspacePackage = workspacePackages[outdatedPackageName]; workspacePackage.packageObject.version = registryLatestVersions[outdatedPackageName]; workspacePackage.updateFile(workspacePackage.packageObject); }); console.warn( `${UNICODE.WARNING} ${outdatedPackageNames.length} packages modified because they where outdated. Use a tool like "git diff" to see the new versions and ensure this is what you want`, ); return { versionUpdates, dependencyUpdates, }; } if (toPublishPackageNames.length === 0) { console.log(`${UNICODE.OK} packages are published on registry`); } else { console.log( `${UNICODE.INFO} ${ toPublishPackageNames.length } packages could be published - ${toPublishPackageNames.map( (name) => `${name}@${workspacePackages[name].packageObject.version}`, ).join(` - `)}`, ); } const packageFilesToUpdate = {}; const updateDependencyVersion = ({ packageName, dependencyType, dependencyName, from, to, }) => { const packageDeps = workspacePackages[packageName].packageObject[dependencyType]; const version = packageDeps[dependencyName]; if (!shouldUpdateVersion(version)) { return; } dependencyUpdates.push({ packageName, dependencyName, from, to, }); packageDeps[dependencyName] = to; packageFilesToUpdate[packageName] = true; }; const updateVersion = ({ packageName, from, to }) => { const workspacePackage = workspacePackages[packageName]; versionUpdates.push({ packageName, from, to, }); workspacePackage.packageObject.version = to; packageFilesToUpdate[packageName] = true; }; dependencies: { const dependencyGraph = buildDependencyGraph(workspacePackages); const packageNamesOrderedByDependency = orderByDependencies(dependencyGraph); packageNamesOrderedByDependency.forEach((packageName) => { const workspacePackage = workspacePackages[packageName]; const { dependencies = {} } = workspacePackage.packageObject; Object.keys(dependencies).forEach((dependencyName) => { const dependencyAsWorkspacePackage = workspacePackages[dependencyName]; if (!dependencyAsWorkspacePackage) { return; } const versionInDependencies = dependencies[dependencyName]; const version = dependencyAsWorkspacePackage.packageObject.version; if (versionInDependencies === version) { return; } updateDependencyVersion({ packageName, dependencyType: "dependencies", dependencyName, from: versionInDependencies, to: version, }); if (!toPublishPackageNames.includes(packageName)) { if (!workspacePackage.packageObject.private) { updateVersion({ packageName, from: workspacePackage.packageObject.version, to: increaseVersion( workspacePackage.packageObject.version, workspacePackage.packageUrl, ), }); toPublishPackageNames.push(packageName); } } }); }); } dev_dependencies: { Object.keys(workspacePackages).forEach((packageName) => { const workspacePackage = workspacePackages[packageName]; const { devDependencies = {} } = workspacePackage.packageObject; Object.keys(devDependencies).forEach((devDependencyName) => { const devDependencyAsWorkspacePackage = workspacePackages[devDependencyName]; if (!devDependencyAsWorkspacePackage) { return; } const versionInDevDependencies = devDependencies[devDependencyName]; const version = devDependencyAsWorkspacePackage.packageObject.version; if (versionInDevDependencies === version) { return; } updateDependencyVersion({ packageName, dependencyType: "devDependencies", dependencyName: devDependencyName, from: versionInDevDependencies, to: version, }); }); }); } package_relations: { Object.keys(packagesRelations).forEach((packageName) => { const relatedPackageNames = packagesRelations[packageName]; const someRelatedPackageUpdated = relatedPackageNames.some( (relatedPackageName) => { return ( dependencyUpdates.some( (dependencyUpdate) => dependencyUpdate.packageName === relatedPackageName, ) || versionUpdates.some( (versionUpdate) => versionUpdate.packageName === relatedPackageName, ) ); }, ); if (someRelatedPackageUpdated) { const packageInfo = workspacePackages[packageName]; updateVersion({ packageName, from: packageInfo.packageObject.version, to: increaseVersion( packageInfo.packageObject.version, packageInfo.packageUrl, ), }); } }); } Object.keys(packageFilesToUpdate).forEach((packageName) => { const workspacePackage = workspacePackages[packageName]; workspacePackage.updateFile(workspacePackage.packageObject); }); const updateCount = versionUpdates.length + dependencyUpdates.length; if (logs) { if (updateCount === 0) { console.log( `${UNICODE.OK} all versions in package.json files are in sync`, ); } else { console.log( `${UNICODE.INFO} ${updateCount} versions modified in package.json files Use a tool like "git diff" to review these changes`, ); } } return { versionUpdates, dependencyUpdates, }; };