Skip to content

Commit

Permalink
Merge pull request #3011 from dependabot/feelepxyz/use-npm7
Browse files Browse the repository at this point in the history
Add support for npm 7 lockfiles
  • Loading branch information
feelepxyz authored Feb 8, 2021
2 parents 1138e3e + 7971b82 commit 19fef63
Show file tree
Hide file tree
Showing 25 changed files with 6,883 additions and 1,513 deletions.
1 change: 1 addition & 0 deletions npm_and_yarn/helpers/lib/npm6/updater.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function flattenAllDependencies(manifest) {
);
}

// NOTE: Re-used in npm 7 updater
function installArgs(
depName,
desiredVersion,
Expand Down
5 changes: 5 additions & 0 deletions npm_and_yarn/helpers/lib/npm7/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const peerDependencyChecker = require("./peer-dependency-checker");

module.exports = {
checkPeerDependencies: peerDependencyChecker.checkPeerDependencies,
};
77 changes: 77 additions & 0 deletions npm_and_yarn/helpers/lib/npm7/peer-dependency-checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* PEER DEPENDENCY CHECKER
*
* Inputs:
* - directory containing a package.json and a yarn.lock
* - dependency name
* - new dependency version
* - requirements for this dependency
*
* Outputs:
* - successful completion, or an error if there are peer dependency warnings
*/

const npm = require("npm7");
const Arborist = require("@npmcli/arborist");
const { promisify } = require("util");

async function checkPeerDependencies(
directory,
depName,
desiredVersion,
requirements,
_topLevelDependencies // included for compatibility with npm 6 implementation
) {
await promisify(npm.load).bind(npm)();

// `ignoreScripts` is used to disable prepare and prepack scripts which are
// run when installing git dependencies
const arb = new Arborist({
...npm.flatOptions,
path: directory,
packageLockOnly: true,
dryRun: true,
save: false,
ignoreScripts: true,
engineStrict: false,
// NOTE: there seems to be no way to disable platform checks in arborist
// without force installing invalid peer dependencies
//
// TODO: ignore platform checks
force: false,
});

// Returns dep name and version for npm install, example: ["react@16.6.0"]
let args = installArgsWithVersion(depName, desiredVersion, requirements);

return await arb
.buildIdealTree({
add: args,
})
.catch((er) => {
if (er.code === "ERESOLVE") {
// NOTE: Emulate the error message in npm 6 for compatibility with the
// version resolver
const conflictingDependencies = [
`${er.edge.from.name}@${er.edge.from.version} requires a peer of ${er.current.name}@${er.edge.spec} but none is installed.`,
];
throw new Error(conflictingDependencies.join("\n"));
} else {
// NOTE: Hand over exception handling to the file updater. This is
// consistent with npm6 behaviour.
return [];
}
})
.then(() => []);
}

function installArgsWithVersion(depName, desiredVersion, reqs) {
const source = (reqs.find((req) => req.source) || {}).source;

if (source && source.type === "git") {
return [`${depName}@${source.url}#${desiredVersion}`];
} else {
return [`${depName}@${desiredVersion}`];
}
}

module.exports = { checkPeerDependencies };
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const path = require("path");
const os = require("os");
const fs = require("fs");
const rimraf = require("rimraf");
const {
findConflictingDependencies,
} = require("../../lib/npm/conflicting-dependency-parser");
const helpers = require("./helpers");

describe("findConflictingDependencies", () => {
let tempDir;
beforeEach(() => {
tempDir = fs.mkdtempSync(os.tmpdir() + path.sep);
});
afterEach(() => rimraf.sync(tempDir));

it("finds conflicting dependencies", async () => {
helpers.copyDependencies("conflicting-dependency-parser/simple", tempDir);

const result = await findConflictingDependencies(tempDir, "abind", "2.0.0");
expect(result).toEqual([
{
explanation: "objnest@4.1.2 requires abind@^1.0.0",
name: "objnest",
version: "4.1.2",
requirement: "^1.0.0",
},
]);
});

it("finds the top-level conflicting dependency", async () => {
helpers.copyDependencies("conflicting-dependency-parser/nested", tempDir);

const result = await findConflictingDependencies(tempDir, "abind", "2.0.0");
expect(result).toEqual([
{
explanation: "askconfig@4.0.4 requires abind@^1.0.5 via objnest@5.1.0",
name: "objnest",
version: "5.1.0",
requirement: "^1.0.5",
},
]);
});

it("explains a deeply nested dependency", async () => {
helpers.copyDependencies(
"conflicting-dependency-parser/deeply-nested",
tempDir
);

const result = await findConflictingDependencies(tempDir, "abind", "2.0.0");
expect(result).toEqual([
{
explanation: "apass@1.1.0 requires abind@^1.0.0 via cipherjson@2.1.0",
name: "cipherjson",
version: "2.1.0",
requirement: "^1.0.0",
},
{
explanation: `apass@1.1.0 requires abind@^1.0.0 via a transitive dependency on objnest@3.0.9`,
name: "objnest",
version: "3.0.9",
requirement: "^1.0.0",
},
]);
});
});
Loading

0 comments on commit 19fef63

Please sign in to comment.