From d353a05980f87144baa4af9edaa56141de03a0a0 Mon Sep 17 00:00:00 2001 From: Devon Powell Date: Fri, 26 Nov 2021 11:07:34 -0500 Subject: [PATCH] Update npm6 to allow for better semver detection --- npm_and_yarn/helpers/lib/npm6/updater.js | 61 ++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/npm_and_yarn/helpers/lib/npm6/updater.js b/npm_and_yarn/helpers/lib/npm6/updater.js index ffbdfc8dcb7..1464bf69af9 100644 --- a/npm_and_yarn/helpers/lib/npm6/updater.js +++ b/npm_and_yarn/helpers/lib/npm6/updater.js @@ -48,15 +48,21 @@ async function updateDependencyFiles(directory, lockfileName, dependencies) { const dryRun = true; const flattenedDependencies = flattenAllDependencies(manifest); - const args = dependencies.map((dependency) => { + const dependencyInfo = dependencies.map((dependency) => { const existingVersionRequirement = flattenedDependencies[dependency.name]; - return installArgs( + const arg = installArgs( dependency.name, dependency.version, dependency.requirements, existingVersionRequirement ); + return { + name: dependency.name, + arg: arg, + requirement: existingVersionRequirement, + }; }); + const args = dependencyInfo.map(info => info.arg); const initialInstaller = new installer.Installer(directory, dryRun, args, { packageLockOnly: true, }); @@ -75,15 +81,20 @@ async function updateDependencyFiles(directory, lockfileName, dependencies) { // This is horrible, but works. const unmute = muteStderr(); try { + const lockfilePath = path.join(directory, lockfileName); + // Fix already present git sub-dependency with invalid "from" and "requires" - updateLockfileWithValidGitUrls(path.join(directory, lockfileName)); + updateLockfileWithValidGitUrls(lockfilePath); await runAsync(initialInstaller, initialInstaller.run, []); // Fix npm5 lockfiles where invalid "from" is introduced after first install - updateLockfileWithValidGitUrls(path.join(directory, lockfileName)); + updateLockfileWithValidGitUrls(lockfilePath); await runAsync(cleanupInstaller, cleanupInstaller.run, []); + + // Reverts semver "from" changes back to existing requirement for "from" + updateLockfileWithCorrectSemverFroms(lockfilePath, dependencyInfo); } finally { unmute(); } @@ -93,6 +104,43 @@ async function updateDependencyFiles(directory, lockfileName, dependencies) { return { [lockfileName]: updatedLockfile }; } +function updateLockfileWithCorrectSemverFroms(lockfilePath, dependencyInfo) { + const lockfile = fs.readFileSync(lockfilePath).toString(); + const indent = detectIndent(lockfile).indent || " "; + const updatedLockfileObject = removeIncorrectSemverFroms(JSON.parse(lockfile), dependencyInfo); + fs.writeFileSync( + lockfilePath, + `${JSON.stringify(updatedLockfileObject, null, indent)}\n` + ); +} + +function removeIncorrectSemverFroms(lockfile, dependencyInfo) { + if (!lockfile || !lockfile.dependencies) return lockfile; + if (!dependencyInfo) return lockfile; + + const dependencies = Object.keys(lockfile.dependencies).reduce((acc, key) => { + const dependency = dependencyInfo.find(info => info.name === key); + const value = lockfile.dependencies[key]; + const validRequirement = dependency && dependency.requirement && dependency.requirement.includes("#semver"); + const validFrom = value && value.from && value.from.includes('#'); + + if (!validFrom || !validRequirement) { + acc[key] = value; + } else { + const requirement = dependency.requirement.split('#')[1]; + const version = value.from.split('#')[1]; + const replaceRegex = new RegExp(`#${version}$`); + const from = value.from.replace(replaceRegex, `#${requirement}`); + + acc[key] = Object.assign({}, value, { from }); + } + + return acc; + }, {}); + + return Object.assign({}, lockfile, { dependencies }); +} + function updateLockfileWithValidGitUrls(lockfilePath) { const lockfile = fs.readFileSync(lockfilePath).toString(); const indent = detectIndent(lockfile).indent || " "; @@ -139,6 +187,11 @@ function installArgs( return `${depName}@${existingVersionRequirement}`; } else if (!existingVersionRequirement.includes("#")) { return `${depName}@${existingVersionRequirement}`; + } else if (existingVersionRequirement.includes("#semver")) { + return `${depName}@${existingVersionRequirement.replace( + /#semver:.*/, + "" + )}#semver:${desiredVersion}`; } else { return `${depName}@${existingVersionRequirement.replace( /#.*/,