Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for pnpm #15

Merged
merged 2 commits into from
Feb 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# narn

Never have to switch between npm and yarn commands ever again.
Never have to switch between yarn, npm, and pnpm commands ever again.

`narn` is a CLI that detects whether your current npm package is using npm or yarn. It then spawns the correct one with the correct arguments. The arguments to narn itself are exactly the same as if you're using yarn. The command will be converted to npm's syntax if the current package is managed by npm.
`narn` is a CLI that detects whether your current npm package is using npm, yarn, or pnpm. It then spawns the correct one with the correct arguments. The arguments to narn itself are exactly the same as if you're using yarn. The command will be converted to npm or pnpm's syntax if the current package is managed by npm / pnpm.

## Installation

Expand Down
27 changes: 23 additions & 4 deletions bin/narn.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#!/usr/bin/env node
const { spawn } = require("child_process");
const { detectYarn, getYarnArgs, getNpmArgs } = require("../lib/narn-lib.js");
const {
detectNpm,
detectPnpm,
getYarnArgs,
getNpmArgs
} = require("../lib/narn-lib.js");
const fs = require("fs");
const path = require("path");

Expand All @@ -10,16 +15,30 @@ const narnPackageJson = JSON.parse(

async function runPackageManager() {
const narnArgs = process.argv.slice(2);
const isYarn = await detectYarn(narnArgs);
let command = isYarn ? "yarn" : "npm";
const [isNpm, isPnpm] = await Promise.all([
detectNpm(narnArgs),
detectPnpm(narnArgs)
]);

let command;

if (isPnpm) {
command = "pnpm";
} else if (isNpm) {
command = "npm";
} else {
command = "yarn";
}

let commandArgs;

const firstArg = narnArgs.length > 0 ? narnArgs[0] : null;
if (firstArg === "--version" || firstArg === "-v") {
console.info(`narn version ${narnPackageJson.version}`);
commandArgs = narnArgs;
} else {
commandArgs = isYarn ? getYarnArgs(narnArgs) : getNpmArgs(narnArgs);
commandArgs =
command === "yarn" ? getYarnArgs(narnArgs) : getNpmArgs(narnArgs, isPnpm);
}

command = commandArgs.command || command;
Expand Down
42 changes: 34 additions & 8 deletions lib/narn-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,34 @@ const path = require("path");
const minimist = require("minimist");
const validateNpmPackageName = require("validate-npm-package-name");

exports.detectYarn = function detectYarn(args) {
exports.detectPnpm = function detectNpm(args) {
if (args && args.length > 0 && args[0] === "global") {
// all global commands go through yarn
return Promise.resolve(true);
return Promise.resolve(false);
} else {
return new Promise((resolve, reject) => {
fs.access(
path.resolve(process.cwd(), "pnpm-lock.yaml"),
fs.constants.F_OK,
noPnpmLock => {
resolve(!Boolean(noPnpmLock));
}
);
});
}
};

exports.detectNpm = function detectNpm(args) {
if (args && args.length > 0 && args[0] === "global") {
// all global commands go through yarn
return Promise.resolve(false);
} else {
return new Promise((resolve, reject) => {
fs.access(
path.resolve(process.cwd(), "package-lock.json"),
fs.constants.F_OK,
noPackageLock => {
resolve(Boolean(noPackageLock));
resolve(!Boolean(noPackageLock));
}
);
});
Expand All @@ -22,8 +39,8 @@ exports.detectYarn = function detectYarn(args) {

exports.getYarnArgs = narnArgs => narnArgs;

exports.getNpmArgs = narnArgs => {
const yarnArgs = minimist(narnArgs, { boolean: ["dev", "D"] });
exports.getNpmArgs = (narnArgs, isPnpm = false) => {
const yarnArgs = minimist(narnArgs, { boolean: ["dev", "D", "--latest"] });
const yarnCommands = yarnArgs._;
const yarnTarget = yarnCommands.length > 0 ? yarnCommands[0] : "install";
const yarnSubCommands = yarnCommands.slice(1);
Expand All @@ -50,9 +67,18 @@ exports.getNpmArgs = narnArgs => {
npmArgs = [];
break;
case "upgrade-interactive":
result = ["-iu", "&&", "npm", "install"];
result.command = "ncu";
return result;
if (isPnpm) {
npmTarget = "update";
npmArgs = ["--interactive"];
if (yarnArgs.latest) {
npmArgs.push("--latest");
}
break;
} else {
result = ["-iu", "&&", "npm", "install"];
result.command = "ncu";
return result;
}
case "publish":
result = [];
result.command = "np";
Expand Down
39 changes: 39 additions & 0 deletions test/detect-package-manager.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { detectNpm, detectPnpm } = require("../lib/narn-lib");
const fs = require("fs");

jest.mock("fs", () => ({
access: jest.fn(),
constants: {
F_OK: 1
}
}));

describe("package manager detection", () => {
beforeEach(() => {
fs.access.mockReset();
});

it("properly detects pnpm packages", async () => {
fs.access.mockImplementationOnce((path, mode, errBack) => {
if (path.includes("pnpm-lock.yaml")) {
errBack(false);
} else {
errBack(true);
}
});
const isPnpm = await detectPnpm(["add", "[email protected]"]);
expect(isPnpm).toBe(true);
});

it("properly detects pnpm packages", async () => {
fs.access.mockImplementationOnce((path, mode, errBack) => {
if (path.includes("package-lock.json")) {
errBack(false);
} else {
errBack(true);
}
});
const isNpm = await detectNpm(["add", "[email protected]"]);
expect(isNpm).toBe(true);
});
});
14 changes: 7 additions & 7 deletions test/global.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { detectYarn } = require("../lib/narn-lib");
const { detectNpm } = require("../lib/narn-lib");
const fs = require("fs");

jest.mock("fs", () => ({
Expand All @@ -14,20 +14,20 @@ describe("narn global commands", () => {
});

it("supports installing global packages", async () => {
const isYarn = await detectYarn(["global", "add", "[email protected]"]);
expect(isYarn).toBe(true);
const isNpm = await detectNpm(["global", "add", "[email protected]"]);
expect(isNpm).toBe(false);
});

it("supports uninstalling global packages", async () => {
const isYarn = await detectYarn(["global", "remove", "lodash"]);
expect(isYarn).toBe(true);
const isNpm = await detectNpm(["global", "remove", "lodash"]);
expect(isNpm).toBe(false);
});

it("doesn't think everything is global", async () => {
fs.access.mockImplementationOnce((path, mode, errBack) => {
errBack(false);
});
const isYarn = await detectYarn(["add", "[email protected]"]);
expect(isYarn).toBe(false);
const isNpm = await detectNpm(["add", "[email protected]"]);
expect(isNpm).toBe(true);
});
});
14 changes: 14 additions & 0 deletions test/upgrade-interactive.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,18 @@ describe("narn upgrade-interactive", () => {
expected.command = "ncu";
expect(getNpmArgs(["upgrade-interactive", "--latest"])).toEqual(expected);
});

it("Runs pnpm update for pnpm projects", () => {
const isPnpm = true;
expect(getNpmArgs(["upgrade-interactive"], isPnpm)).toEqual([
"update",
"--interactive"
]);

expect(getNpmArgs(["upgrade-interactive", "--latest"], isPnpm)).toEqual([
"update",
"--interactive",
"--latest"
]);
});
});