Skip to content

Commit

Permalink
fix: update patch deps during check-project (#1460)
Browse files Browse the repository at this point in the history
To ensure people with lockfiles get the latest version of deps, update
patch versions during `check-project`.
  • Loading branch information
achingbrain committed Feb 6, 2024
1 parent 72d2fcd commit 505ada6
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 30 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@
"globby": "^14.0.0",
"is-plain-obj": "^4.1.0",
"kleur": "^4.1.4",
"latest-version": "^8.0.0",
"lilconfig": "^3.0.0",
"listr": "~0.14.2",
"mdast-util-from-markdown": "^2.0.0",
Expand Down
69 changes: 39 additions & 30 deletions src/check-project/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from 'path'
import { fileURLToPath } from 'url'
import { execa } from 'execa'
import fs from 'fs-extra'
import latestVersion from 'latest-version'
import Listr from 'listr'
import prompt from 'prompt'
import semver from 'semver'
Expand Down Expand Up @@ -161,38 +162,33 @@ async function processMonorepo (projectDir, manifest, branchName, repoUrl, ciFil
* @param {string[]} projectDirs
*/
async function alignMonorepoProjectDependencies (projectDirs) {
console.info('Align monorepo project dependencies')

/** @type {Record<string, string>} */
const siblingVersions = {}
/** @type {Record<string, string>} */
const deps = {}
/** @type {Record<string, string>} */
const devDeps = {}
/** @type {Record<string, string>} */
const optionalDeps = {}
/** @type {Record<string, string>} */
const peerDeps = {}

// first loop over every project and choose the most recent version of a given dep
for (const projectDir of projectDirs) {
const pkg = fs.readJSONSync(path.join(projectDir, 'package.json'))
siblingVersions[pkg.name] = calculateSiblingVersion(pkg.version)

chooseVersions(pkg.dependencies || {}, deps)
chooseVersions(pkg.devDependencies || {}, devDeps)
chooseVersions(pkg.optionalDependencies || {}, optionalDeps)
chooseVersions(pkg.peerDependencies || {}, peerDeps)
chooseVersions(pkg.dependencies ?? {}, deps)
chooseVersions(pkg.devDependencies ?? {}, deps)
chooseVersions(pkg.optionalDependencies ?? {}, deps)
chooseVersions(pkg.peerDependencies ?? {}, deps)
}

// get the latest patch release of every dep from npm
await findLatestVersions(deps)

// now propose the most recent version of a dep for all projects
for (const projectDir of projectDirs) {
const pkg = fs.readJSONSync(path.join(projectDir, 'package.json'))

selectVersions(pkg.dependencies || {}, deps, siblingVersions)
selectVersions(pkg.devDependencies || {}, devDeps, siblingVersions)
selectVersions(pkg.optionalDependencies || {}, optionalDeps, siblingVersions)
selectVersions(pkg.peerDependencies || {}, peerDeps, siblingVersions)
selectVersions(pkg.dependencies ?? {}, deps, siblingVersions)
selectVersions(pkg.devDependencies ?? {}, deps, siblingVersions)
selectVersions(pkg.optionalDependencies ?? {}, deps, siblingVersions)
selectVersions(pkg.peerDependencies ?? {}, deps, siblingVersions)

await ensureFileHasContents(projectDir, 'package.json', JSON.stringify(pkg, null, 2))
}
Expand All @@ -203,37 +199,50 @@ async function alignMonorepoProjectDependencies (projectDirs) {
* @param {Record<string, string>} list
*/
function chooseVersions (deps, list) {
Object.entries(deps).forEach(([key, value]) => {
for (const [dep, version] of Object.entries(deps)) {
// not seen this dep before
if (!list[key]) {
list[key] = value
return
if (list[dep] == null) {
list[dep] = version
continue
}

// test for later version
if (semver.gt(version.replace(/\^|~/, ''), list[dep].replace(/\^|~/, ''))) {
list[dep] = version
}
}
}

const existingVersion = semver.minVersion(list[key])
const moduleVersion = semver.minVersion(value)
/**
* @param {Record<string, string>} deps
*/
async function findLatestVersions (deps) {
// find the latest semver-compatible release from npm
for (const [key, value] of Object.entries(deps)) {
try {
const npmVersion = `^${await latestVersion(key, { version: value })}`

// take the most recent range or version
const res = semver.compare(existingVersion ?? '0.0.0', moduleVersion ?? '0.0.0')
console.info(key, 'local version:', value, 'npm version:', npmVersion)

if (res === -1) {
list[key] = value
deps[key] = npmVersion
} catch (err) {
console.error(`Could not load latest npm version of "${key}"`, err)
}
})
}
}

/**
* @param {Record<string, string>} deps
* @param {Record<string, string>} list
* @param {Record<string, string>} siblingVersions
*/
function selectVersions (deps, list, siblingVersions) {
async function selectVersions (deps, list, siblingVersions) {
// release-please updates sibling versions to the latest patch releases but
// we try to update to the latest minor so skip that if release please is
// in use
const ignoreSiblingDeps = usesReleasePlease()

Object.entries(list).forEach(([key, value]) => {
for (const [key, value] of Object.entries(list)) {
if (deps[key] != null) {
if (siblingVersions[key] != null && !ignoreSiblingDeps) {
// take sibling version if available
Expand All @@ -243,7 +252,7 @@ function selectVersions (deps, list, siblingVersions) {
deps[key] = value
}
}
})
}
}

/**
Expand Down

0 comments on commit 505ada6

Please sign in to comment.