diff --git a/README.md b/README.md
index 8ba30839..3ef6f4a6 100644
--- a/README.md
+++ b/README.md
@@ -128,6 +128,36 @@ if the corresponding node manager is not present, this command will install it g
+### `nrm` - uninstall
+
+```bash
+nrm
+
+# npm uninstall axios
+# yarn remove axios
+# pnpm remove axios
+```
+
+```bash
+nrm @types/node -D
+
+# npm uninstall @types/node -D
+# yarn remove @types/node -D
+# pnpm remove @types/node -D
+```
+
+```bash
+nrm -g iroiro
+
+# npm uninstall -g iroiro
+# yarn global remove iroiro
+# pnpm remove -g iroiro
+
+# this uses default agent, regardless your current working directory
+```
+
+
+
### Config
```ini
diff --git a/bin/nrm.js b/bin/nrm.js
new file mode 100644
index 00000000..9f85e433
--- /dev/null
+++ b/bin/nrm.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+'use strict'
+require('../dist/nrm')
diff --git a/package.json b/package.json
index dc8a2da6..a1ad6ff1 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
"nci": "bin/nci.js",
"nr": "bin/nr.js",
"nu": "bin/nu.js",
- "nx": "bin/nx.js"
+ "nx": "bin/nx.js",
+ "nrm": "bin/nrm.js"
},
"bugs": {
"url": "https://github.com/antfu/ni/issues"
@@ -34,8 +35,9 @@
"nr": "esno src/nr.ts",
"nu": "esno src/nu.ts",
"nx": "esno src/nx.ts",
+ "nrm": "esno src/nrm.ts",
"dev": "esno src/ni.ts",
- "build": "rimraf dist && tsup src/ni.ts src/nci.ts src/nr.ts src/nu.ts src/nx.ts src/index.ts --format cjs,esm --dts",
+ "build": "rimraf dist && tsup src/ni.ts src/nci.ts src/nr.ts src/nu.ts src/nx.ts src/nrm.ts src/index.ts --format cjs,esm --dts",
"release": "npx git-ensure && npx bumpp --commit --push --tag",
"lint": "eslint \"**/*.ts\"",
"lint:fix": "npm run lint -- --fix",
diff --git a/src/agents.ts b/src/agents.ts
index 84af1a56..8abaa340 100644
--- a/src/agents.ts
+++ b/src/agents.ts
@@ -14,6 +14,8 @@ export const AGENTS = {
'upgrade': 'npm update {0}',
'upgrade-interactive': null,
'execute': 'npx {0}',
+ 'uninstall': 'npm uninstall {0}',
+ 'global_uninstall': 'npm uninstall -g {0}',
},
yarn: {
'run': 'yarn run {0}',
@@ -24,6 +26,8 @@ export const AGENTS = {
'upgrade': 'yarn upgrade {0}',
'upgrade-interactive': 'yarn upgrade-interactive',
'execute': 'yarn dls {0}',
+ 'uninstall': 'yarn remove {0}',
+ 'global_uninstall': 'yarn global remove {0}',
},
pnpm: {
'run': npmRun('pnpm'),
@@ -34,6 +38,8 @@ export const AGENTS = {
'upgrade': 'pnpm update {0}',
'upgrade-interactive': 'pnpm update -i',
'execute': 'pnpx {0}',
+ 'uninstall': 'pnpm remove {0}',
+ 'global_uninstall': 'pnpm remove -g {0}',
},
}
diff --git a/src/commands.ts b/src/commands.ts
index 4ee24a4b..3119e5a7 100644
--- a/src/commands.ts
+++ b/src/commands.ts
@@ -16,7 +16,6 @@ export function getCommand(
if (!c)
throw new Error(`Command "${command}" is not support by agent "${agent}"`)
-
return c.replace('{0}', args.join(' ')).trim()
}
@@ -61,6 +60,13 @@ export function parseNu(agent: Agent, args: string[]): string {
return getCommand(agent, 'upgrade', args)
}
+
+export function parseNrm(agent: Agent, args: string[]): string {
+ if (args.includes('-g'))
+ return getCommand(agent, 'global_uninstall', exclude(args, '-g'))
+ return getCommand(agent, 'uninstall', args)
+}
+
export function parseNx(agent: Agent, args: string[]): string {
return getCommand(agent, 'execute', args)
}
diff --git a/src/nrm.ts b/src/nrm.ts
new file mode 100644
index 00000000..3a2749e0
--- /dev/null
+++ b/src/nrm.ts
@@ -0,0 +1,4 @@
+import { parseNrm } from './commands'
+import { runCli } from './runner'
+
+runCli(parseNrm)
diff --git a/test/nrm/npm.spec.ts b/test/nrm/npm.spec.ts
new file mode 100644
index 00000000..69b05ddc
--- /dev/null
+++ b/test/nrm/npm.spec.ts
@@ -0,0 +1,18 @@
+import test, { ExecutionContext } from 'ava'
+import { parseNrm } from '../../src/commands'
+
+const agent = 'npm'
+const _ = (arg: string, expected: string) => (t: ExecutionContext) => {
+ t.is(
+ parseNrm(agent, arg.split(' ').filter(Boolean)),
+ expected,
+ )
+}
+
+test('single remove', _('axios', 'npm uninstall axios'))
+
+test('multiple', _('eslint @types/node', 'npm uninstall eslint @types/node'))
+
+test('-D', _('eslint @types/node -D', 'npm uninstall eslint @types/node -D'))
+
+test('global', _('eslint -g', 'npm uninstall -g eslint'))
\ No newline at end of file
diff --git a/test/nrm/pnpm.spec.ts b/test/nrm/pnpm.spec.ts
new file mode 100644
index 00000000..c6548560
--- /dev/null
+++ b/test/nrm/pnpm.spec.ts
@@ -0,0 +1,18 @@
+import test, { ExecutionContext } from 'ava'
+import { parseNrm } from '../../src/commands'
+
+const agent = 'pnpm'
+const _ = (arg: string, expected: string) => (t: ExecutionContext) => {
+ t.is(
+ parseNrm(agent, arg.split(' ').filter(Boolean)),
+ expected,
+ )
+}
+
+test('single remove', _('axios', 'pnpm remove axios'))
+
+test('multiple', _('eslint @types/node', 'pnpm remove eslint @types/node'))
+
+test('-D', _('eslint @types/node -D', 'pnpm remove eslint @types/node -D'))
+
+test('global', _('eslint -g', 'pnpm remove -g eslint'))
diff --git a/test/nrm/yarn.spec.ts b/test/nrm/yarn.spec.ts
new file mode 100644
index 00000000..f3ad3a13
--- /dev/null
+++ b/test/nrm/yarn.spec.ts
@@ -0,0 +1,18 @@
+import test, { ExecutionContext } from 'ava'
+import { parseNrm } from '../../src/commands'
+
+const agent = 'yarn'
+const _ = (arg: string, expected: string) => (t: ExecutionContext) => {
+ t.is(
+ parseNrm(agent, arg.split(' ').filter(Boolean)),
+ expected,
+ )
+}
+
+test('single remove', _('axios', 'yarn remove axios'))
+
+test('multiple', _('eslint @types/node', 'yarn remove eslint @types/node'))
+
+test('-D', _('eslint @types/node -D', 'yarn remove eslint @types/node -D'))
+
+test('global', _('eslint ni -g', 'yarn global remove eslint ni'))
\ No newline at end of file