pm-auto 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/build_command.d.ts +7 -0
- package/dist/build_command.d.ts.map +1 -0
- package/dist/build_command.js +97 -0
- package/dist/build_command.js.map +1 -0
- package/dist/config_path.d.ts +4 -0
- package/dist/config_path.d.ts.map +1 -0
- package/dist/config_path.js +43 -0
- package/dist/config_path.js.map +1 -0
- package/dist/config_reader.d.ts +13 -0
- package/dist/config_reader.d.ts.map +1 -0
- package/dist/config_reader.js +96 -0
- package/dist/config_reader.js.map +1 -0
- package/dist/display.d.ts +8 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +29 -0
- package/dist/display.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +6 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +38 -0
- package/dist/install.js.map +1 -0
- package/dist/orchestrator.d.ts +2 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +48 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/nodemon.json +6 -0
- package/package.json +38 -0
- package/src/build_command.ts +119 -0
- package/src/config_path.ts +54 -0
- package/src/config_reader.ts +119 -0
- package/src/display.ts +33 -0
- package/src/index.ts +45 -0
- package/src/install.ts +40 -0
- package/src/orchestrator.ts +71 -0
- package/src/types/index.ts +16 -0
- package/test.json +83 -0
- package/tsconfig.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { saveConfigPath } from "./config_path.js";
|
|
4
|
+
import { orchestrator } from "./orchestrator.js";
|
|
5
|
+
const program = new Command();
|
|
6
|
+
program
|
|
7
|
+
.name("pm-auto")
|
|
8
|
+
.version("1.0.0")
|
|
9
|
+
.description("CLI for automated npm,yarn,pnpm package installation");
|
|
10
|
+
program
|
|
11
|
+
.command("config <path>")
|
|
12
|
+
.description("Set the config file path")
|
|
13
|
+
.action((path) => {
|
|
14
|
+
saveConfigPath(path);
|
|
15
|
+
});
|
|
16
|
+
program
|
|
17
|
+
.command("install [packages...]")
|
|
18
|
+
.description("Install packages")
|
|
19
|
+
.option("-p, --pkg-json", "Install packages from package.json")
|
|
20
|
+
.option("-A, --add-command <command>", "Add a custom command to all installation commands from config file")
|
|
21
|
+
.option("-D, --dry-run", "Dry run - Display commands before execution")
|
|
22
|
+
.action((packages, options) => {
|
|
23
|
+
orchestrator("install", packages, options);
|
|
24
|
+
});
|
|
25
|
+
program
|
|
26
|
+
.command("uninstall <packages...>")
|
|
27
|
+
.description("Uninstall packages")
|
|
28
|
+
.option("-A, --add-command <command>", "Add a custom command to all installation commands from config file")
|
|
29
|
+
.action((packages, options) => {
|
|
30
|
+
orchestrator("uninstall", packages, options);
|
|
31
|
+
});
|
|
32
|
+
program.parse();
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,sDAAsD,CAAC,CAAC;AAEvE,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACf,cAAc,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,uBAAuB,CAAC;KAChC,WAAW,CAAC,kBAAkB,CAAC;KAC/B,MAAM,CAAC,gBAAgB,EAAE,oCAAoC,CAAC;KAC9D,MAAM,CACL,6BAA6B,EAC7B,oEAAoE,CACrE;KACA,MAAM,CAAC,eAAe,EAAE,6CAA6C,CAAC;KACtE,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;IAC5B,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,yBAAyB,CAAC;KAClC,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CACL,6BAA6B,EAC7B,oEAAoE,CACrE;KACA,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;IAC5B,YAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CommandResult } from "./types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Takes the interactive and non interactive command from each item in the CommandResult Array
|
|
4
|
+
*/
|
|
5
|
+
export declare function install(commands: CommandResult[]): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAetD;;GAEG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,aAAa,EAAE,iBAoBtD"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import { display } from "./display.js";
|
|
3
|
+
async function runCommand(command) {
|
|
4
|
+
try {
|
|
5
|
+
const [commandName, ...args] = command.split(" ");
|
|
6
|
+
await execa(commandName, args, {
|
|
7
|
+
stdio: "inherit",
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
display(`Error:, ${error.message}`, "error");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Takes the interactive and non interactive command from each item in the CommandResult Array
|
|
16
|
+
*/
|
|
17
|
+
export async function install(commands) {
|
|
18
|
+
try {
|
|
19
|
+
for (const command of commands) {
|
|
20
|
+
// Wait for all interactive commands to finish first
|
|
21
|
+
if (command.interactive) {
|
|
22
|
+
for (const interactiveCommand of command.interactive) {
|
|
23
|
+
display(`Running command: ${interactiveCommand}`, "loading");
|
|
24
|
+
await runCommand(interactiveCommand);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Then run non-interactive
|
|
28
|
+
if (command.nonInteractive) {
|
|
29
|
+
display(`Running command: ${command.nonInteractive[0]}`, "loading");
|
|
30
|
+
await runCommand(command.nonInteractive[0]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
display(`Error: ${error.message}`, "error");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,KAAK,UAAU,UAAU,CAAC,OAAe;IACvC,IAAI,CAAC;QACH,MAAM,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAElD,MAAM,KAAK,CAAC,WAAqB,EAAE,IAAI,EAAE;YACvC,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,WAAW,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,QAAyB;IACrD,IAAI,CAAC;QACH,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,oDAAoD;YACpD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;gBACxB,KAAK,MAAM,kBAAkB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;oBACrD,OAAO,CAAC,oBAAoB,kBAAkB,EAAE,EAAE,SAAS,CAAC,CAAC;oBAC7D,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;gBAC3B,OAAO,CAAC,oBAAoB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;gBACpE,MAAM,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAW,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,YAAY,GACvB,SAAS,MAAM,EACf,UAAU,MAAM,EAAE,EAClB,UAAU,GAAG,SA+Cd,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { buildCommands, buildUninstallCommands } from "./build_command.js";
|
|
2
|
+
import { getConfigObject } from "./config_reader.js";
|
|
3
|
+
import { display } from "./display.js";
|
|
4
|
+
import { install } from "./install.js";
|
|
5
|
+
//Check if the value is an array of ConfigType objects
|
|
6
|
+
function isConfigTypeArray(value) {
|
|
7
|
+
return (Array.isArray(value) &&
|
|
8
|
+
value.every((item) => typeof item === "object" &&
|
|
9
|
+
item !== null &&
|
|
10
|
+
"packages" in item &&
|
|
11
|
+
Array.isArray(item.packages)));
|
|
12
|
+
}
|
|
13
|
+
export const orchestrator = (command, packages, options) => {
|
|
14
|
+
if (command === "install") {
|
|
15
|
+
display(`Installing packages... ${options.pkgJson ? "from package.json" : packages.join(", ")}`, "info");
|
|
16
|
+
getConfigObject(packages, options).then(async (config) => {
|
|
17
|
+
if (config.length === 0) {
|
|
18
|
+
display("No configuration found", "error");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (isConfigTypeArray(config)) {
|
|
22
|
+
const commands = buildCommands(config);
|
|
23
|
+
await install(commands);
|
|
24
|
+
display("✅ Packages installed successfully", "success");
|
|
25
|
+
process.stdout.write("\x07");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
await install(config);
|
|
29
|
+
display("✅ Packages from package.json installed successfully", "success");
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
display(`Uninstalling packages... ${packages.join(", ")}`, "info");
|
|
35
|
+
getConfigObject(packages, options).then(async (config) => {
|
|
36
|
+
if (config.length === 0) {
|
|
37
|
+
display("No configuration found", "error");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (isConfigTypeArray(config)) {
|
|
41
|
+
const commands = buildUninstallCommands(config);
|
|
42
|
+
await install(commands);
|
|
43
|
+
display("✅ Packages uninstalled successfully", "success");
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,sDAAsD;AACtD,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAAK,CAAC,KAAK,CACT,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,IAAI,KAAK,QAAQ;YACxB,IAAI,KAAK,IAAI;YACb,UAAU,IAAI,IAAI;YAClB,KAAK,CAAC,OAAO,CAAE,IAAY,CAAC,QAAQ,CAAC,CACxC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,OAAe,EACf,QAAkB,EAClB,OAAa,EACb,EAAE;IACF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,CACL,0BAA0B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAE,QAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACrG,MAAM,CACP,CAAC;QAEF,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACvD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YAED,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACxB,OAAO,CAAC,mCAAmC,EAAE,SAAS,CAAC,CAAC;gBACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;gBAEtB,OAAO,CACL,qDAAqD,EACrD,SAAS,CACV,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO,CACL,4BAA6B,QAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC/D,MAAM,CACP,CAAC;QAEF,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACvD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YAED,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;gBAChD,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACxB,OAAO,CAAC,qCAAqC,EAAE,SAAS,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ConfigType {
|
|
2
|
+
name: string;
|
|
3
|
+
packageManager: string;
|
|
4
|
+
packages: {
|
|
5
|
+
command: string;
|
|
6
|
+
interactive: boolean;
|
|
7
|
+
}[];
|
|
8
|
+
}
|
|
9
|
+
export interface PackageType {
|
|
10
|
+
command: string;
|
|
11
|
+
interactive: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface CommandResult {
|
|
14
|
+
name: string;
|
|
15
|
+
interactive: string[];
|
|
16
|
+
nonInteractive: string[];
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
package/nodemon.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pm-auto",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for automated npm,yarn,pnpm package installation",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pm-auto",
|
|
7
|
+
"yarn-auto",
|
|
8
|
+
"pnpm-auto",
|
|
9
|
+
"auto"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "Elliot Otoijagha",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"bin": {
|
|
16
|
+
"pm-auto": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "nodemon --exec tsx src/index.ts",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"test": "jest --watch"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@inquirer/prompts": "^7.10.1",
|
|
26
|
+
"chalk": "^5.6.2",
|
|
27
|
+
"commander": "^14.0.2",
|
|
28
|
+
"execa": "^9.6.0",
|
|
29
|
+
"ora": "^9.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.10.1",
|
|
33
|
+
"nodemon": "^3.1.11",
|
|
34
|
+
"ts-node": "^10.9.2",
|
|
35
|
+
"tsx": "^4.20.6",
|
|
36
|
+
"typescript": "^5.9.3"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ConfigType, PackageType, CommandResult } from "./types/index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build commands from project configurations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function buildCommands(projects: ConfigType[]) {
|
|
8
|
+
// Initialize arrays properly
|
|
9
|
+
|
|
10
|
+
const commandArray: CommandResult[] = [];
|
|
11
|
+
for (const project of projects) {
|
|
12
|
+
const { packageManager, packages } = project;
|
|
13
|
+
|
|
14
|
+
const commandPrefixes = {
|
|
15
|
+
npm: {
|
|
16
|
+
install: "npm install",
|
|
17
|
+
run: "npx",
|
|
18
|
+
},
|
|
19
|
+
pnpm: {
|
|
20
|
+
install: "pnpm add",
|
|
21
|
+
run: "pnpm dlx",
|
|
22
|
+
},
|
|
23
|
+
yarn: {
|
|
24
|
+
install: "yarn add",
|
|
25
|
+
run: "yarn dlx",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const manager =
|
|
30
|
+
commandPrefixes[packageManager as keyof typeof commandPrefixes] ||
|
|
31
|
+
commandPrefixes.npm;
|
|
32
|
+
|
|
33
|
+
const result: CommandResult = {
|
|
34
|
+
name: project.name,
|
|
35
|
+
interactive: [],
|
|
36
|
+
nonInteractive: [],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Separate interactive from non-interactive packages
|
|
40
|
+
const nonInteractive: PackageType[] = [];
|
|
41
|
+
const interactive: PackageType[] = [];
|
|
42
|
+
|
|
43
|
+
if (packages) {
|
|
44
|
+
packages.forEach((pkg) => {
|
|
45
|
+
if (pkg.interactive) {
|
|
46
|
+
interactive.push(pkg);
|
|
47
|
+
} else {
|
|
48
|
+
nonInteractive.push(pkg);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add interactive packages as separate commands (sequential)
|
|
54
|
+
interactive.forEach((pkg) => {
|
|
55
|
+
result.interactive.push(`${manager.run} ${pkg.command}`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Batch all non-interactive packages into ONE command
|
|
59
|
+
if (nonInteractive.length > 0) {
|
|
60
|
+
const packageNames = nonInteractive.map((pkg) => pkg.command).join(" ");
|
|
61
|
+
result.nonInteractive.push(`${manager.install} ${packageNames}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
commandArray.push(result);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return commandArray;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function buildUninstallCommands(projects: ConfigType[]) {
|
|
71
|
+
const commandArray: CommandResult[] = [];
|
|
72
|
+
for (const project of projects) {
|
|
73
|
+
const { packageManager, packages } = project;
|
|
74
|
+
|
|
75
|
+
const commandPrefixes = {
|
|
76
|
+
npm: {
|
|
77
|
+
install: "npm uninstall",
|
|
78
|
+
},
|
|
79
|
+
pnpm: {
|
|
80
|
+
install: "pnpm uninstall",
|
|
81
|
+
},
|
|
82
|
+
yarn: {
|
|
83
|
+
install: "yarn remove",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const manager =
|
|
88
|
+
commandPrefixes[packageManager as keyof typeof commandPrefixes] ||
|
|
89
|
+
commandPrefixes.npm;
|
|
90
|
+
|
|
91
|
+
const result: CommandResult = {
|
|
92
|
+
name: project.name,
|
|
93
|
+
interactive: [],
|
|
94
|
+
nonInteractive: [],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Separate interactive from non-interactive packages
|
|
98
|
+
const nonInteractive: PackageType[] = [];
|
|
99
|
+
const interactive: PackageType[] = [];
|
|
100
|
+
|
|
101
|
+
if (packages) {
|
|
102
|
+
packages.forEach((pkg) => {
|
|
103
|
+
if (!pkg.interactive) {
|
|
104
|
+
nonInteractive.push(pkg);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Batch all non-interactive packages into ONE command
|
|
110
|
+
if (nonInteractive.length > 0) {
|
|
111
|
+
const packageNames = nonInteractive.map((pkg) => pkg.command).join(" ");
|
|
112
|
+
result.nonInteractive.push(`${manager.install} ${packageNames}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
commandArray.push(result);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return commandArray;
|
|
119
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import { display } from "./display.js";
|
|
5
|
+
|
|
6
|
+
const SETTINGS_DIR = path.join(os.homedir(), ".pm-auto");
|
|
7
|
+
const SETTINGS_FILE = path.join(SETTINGS_DIR, "settings.json");
|
|
8
|
+
|
|
9
|
+
interface Settings {
|
|
10
|
+
configPath?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function saveConfigPath(configPath: string): void {
|
|
14
|
+
// Create directory if it doesn't exist
|
|
15
|
+
if (!fs.existsSync(SETTINGS_DIR)) {
|
|
16
|
+
fs.mkdirSync(SETTINGS_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//check if file exists
|
|
20
|
+
try {
|
|
21
|
+
const real = fs.realpathSync(configPath);
|
|
22
|
+
const settings: Settings = { configPath: real };
|
|
23
|
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
|
|
24
|
+
|
|
25
|
+
display(`Config file path saved: ${configPath}`, "success");
|
|
26
|
+
} catch (err: any) {
|
|
27
|
+
display(`Error saving config file: ${err.message}`, "error");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getConfigPath(): string | void {
|
|
32
|
+
//check if settings exists
|
|
33
|
+
if (!fs.existsSync(SETTINGS_FILE)) {
|
|
34
|
+
display(
|
|
35
|
+
"Run `pm-auto config <path>`, where <path> is the path to your config file",
|
|
36
|
+
"info",
|
|
37
|
+
);
|
|
38
|
+
display("Config file path not set", "error");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const data = fs.readFileSync(SETTINGS_FILE, "utf8");
|
|
43
|
+
const settings: Settings = JSON.parse(data);
|
|
44
|
+
return settings.configPath || "";
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
display(`Error reading config file path: ${error.message}`, "error");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function clearConfigPath(): void {
|
|
51
|
+
if (fs.existsSync(SETTINGS_FILE)) {
|
|
52
|
+
fs.unlinkSync(SETTINGS_FILE);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import { getConfigPath } from "./config_path.js";
|
|
3
|
+
|
|
4
|
+
import * as fsd from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import type { CommandResult, ConfigType } from "./types/index.js";
|
|
7
|
+
import { display } from "./display.js";
|
|
8
|
+
import { confirm } from "@inquirer/prompts";
|
|
9
|
+
|
|
10
|
+
type PackageManager = "npm" | "yarn" | "pnpm";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect the package manager used in the project.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export function detectPackageManager(
|
|
17
|
+
projectPath: string = process.cwd(),
|
|
18
|
+
): PackageManager | void {
|
|
19
|
+
// Check for lock files in order of specificity
|
|
20
|
+
if (fsd.existsSync(path.join(projectPath, "pnpm-lock.yaml"))) {
|
|
21
|
+
return "pnpm";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (fsd.existsSync(path.join(projectPath, "yarn.lock"))) {
|
|
25
|
+
return "yarn";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (fsd.existsSync(path.join(projectPath, "package-lock.json"))) {
|
|
29
|
+
return "npm";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default to npm if no lock file found
|
|
33
|
+
display("No Lock File Found", "error");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the installation commands from the config file, transforms into a js object and with the options given
|
|
38
|
+
* it modifies the object and returns it
|
|
39
|
+
*/
|
|
40
|
+
export const getConfigObject = async (
|
|
41
|
+
packages: string[],
|
|
42
|
+
options?: any,
|
|
43
|
+
): Promise<ConfigType[] | CommandResult[]> => {
|
|
44
|
+
if (!options.pkgJson) {
|
|
45
|
+
const configPath = getConfigPath();
|
|
46
|
+
|
|
47
|
+
//read config file content
|
|
48
|
+
let configContent = "";
|
|
49
|
+
try {
|
|
50
|
+
configContent = await fs.readFile(configPath as string, "utf8");
|
|
51
|
+
} catch (error) {
|
|
52
|
+
display(`File not found ${error}`, "error");
|
|
53
|
+
}
|
|
54
|
+
const configObject = JSON.parse(configContent);
|
|
55
|
+
let result: ConfigType[] = Object.values(configObject);
|
|
56
|
+
|
|
57
|
+
//filter the packages the user wants to install
|
|
58
|
+
if (packages.length > 0) {
|
|
59
|
+
result = packages.map((pkg) => {
|
|
60
|
+
if (!configObject[pkg]) {
|
|
61
|
+
display(
|
|
62
|
+
`Package ${pkg} not found in the configuration file`,
|
|
63
|
+
"warning",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return configObject[pkg];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/*
|
|
70
|
+
* Config object modification with the options given
|
|
71
|
+
*/
|
|
72
|
+
//Add command to previous configured commands (-A/-add-command)
|
|
73
|
+
if (options.addCommand) {
|
|
74
|
+
result.forEach((config) => {
|
|
75
|
+
config.packages.forEach((pkg) => {
|
|
76
|
+
pkg.command = pkg.interactive
|
|
77
|
+
? pkg.command
|
|
78
|
+
: pkg.command + " " + options.addCommand;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//Dry run - Display commands before execution
|
|
84
|
+
if (options.dryRun) {
|
|
85
|
+
display("Dry Run:", "info");
|
|
86
|
+
result.forEach((config) => {
|
|
87
|
+
display(`Package name -> ${config.name}`, "info");
|
|
88
|
+
config.packages.forEach((pkg) => {
|
|
89
|
+
display(`add ${pkg.command}`, "info");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
const continueWithInstall = await confirm({
|
|
93
|
+
message: "Continue with installation?",
|
|
94
|
+
default: true,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (!continueWithInstall) {
|
|
98
|
+
display("Installation cancelled ", "success");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return result;
|
|
103
|
+
} else {
|
|
104
|
+
//generate command for package.json
|
|
105
|
+
const pm = detectPackageManager();
|
|
106
|
+
|
|
107
|
+
const command = pm + " install";
|
|
108
|
+
|
|
109
|
+
const result: CommandResult[] = [
|
|
110
|
+
{
|
|
111
|
+
name: "package.json",
|
|
112
|
+
interactive: [],
|
|
113
|
+
nonInteractive: [command],
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
};
|
package/src/display.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display a message with a specified type.
|
|
3
|
+
*
|
|
4
|
+
* @param text - The message to display.
|
|
5
|
+
* @param type - The type of message to display to determine the color.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
|
|
10
|
+
export const display = (
|
|
11
|
+
text: string,
|
|
12
|
+
type: "error" | "success" | "warning" | "info" | "loading" | "",
|
|
13
|
+
) => {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case "error":
|
|
16
|
+
console.error(chalk.red(text));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
case "success":
|
|
19
|
+
console.log(chalk.green(text));
|
|
20
|
+
process.exit(0);
|
|
21
|
+
case "warning":
|
|
22
|
+
console.warn(chalk.yellow(text));
|
|
23
|
+
break;
|
|
24
|
+
case "info":
|
|
25
|
+
console.info(chalk.blue(text));
|
|
26
|
+
break;
|
|
27
|
+
case "loading":
|
|
28
|
+
console.log(`Loading... ${text}`);
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
console.log(text);
|
|
32
|
+
}
|
|
33
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { saveConfigPath } from "./config_path.js";
|
|
5
|
+
import { orchestrator } from "./orchestrator.js";
|
|
6
|
+
|
|
7
|
+
const program = new Command();
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.name("pm-auto")
|
|
11
|
+
.version("1.0.0")
|
|
12
|
+
.description("CLI for automated npm,yarn,pnpm package installation");
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.command("config <path>")
|
|
16
|
+
.description("Set the config file path")
|
|
17
|
+
.action((path) => {
|
|
18
|
+
saveConfigPath(path);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.command("install [packages...]")
|
|
23
|
+
.description("Install packages")
|
|
24
|
+
.option("-p, --pkg-json", "Install packages from package.json")
|
|
25
|
+
.option(
|
|
26
|
+
"-A, --add-command <command>",
|
|
27
|
+
"Add a custom command to all installation commands from config file",
|
|
28
|
+
)
|
|
29
|
+
.option("-D, --dry-run", "Dry run - Display commands before execution")
|
|
30
|
+
.action((packages, options) => {
|
|
31
|
+
orchestrator("install", packages, options);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command("uninstall <packages...>")
|
|
36
|
+
.description("Uninstall packages")
|
|
37
|
+
.option(
|
|
38
|
+
"-A, --add-command <command>",
|
|
39
|
+
"Add a custom command to all installation commands from config file",
|
|
40
|
+
)
|
|
41
|
+
.action((packages, options) => {
|
|
42
|
+
orchestrator("uninstall", packages, options);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
program.parse();
|
package/src/install.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import type { CommandResult } from "./types/index.js";
|
|
3
|
+
import { display } from "./display.js";
|
|
4
|
+
|
|
5
|
+
async function runCommand(command: string) {
|
|
6
|
+
try {
|
|
7
|
+
const [commandName, ...args] = command.split(" ");
|
|
8
|
+
|
|
9
|
+
await execa(commandName as string, args, {
|
|
10
|
+
stdio: "inherit",
|
|
11
|
+
});
|
|
12
|
+
} catch (error: any) {
|
|
13
|
+
display(`Error:, ${error.message}`, "error");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Takes the interactive and non interactive command from each item in the CommandResult Array
|
|
19
|
+
*/
|
|
20
|
+
export async function install(commands: CommandResult[]) {
|
|
21
|
+
try {
|
|
22
|
+
for (const command of commands) {
|
|
23
|
+
// Wait for all interactive commands to finish first
|
|
24
|
+
if (command.interactive) {
|
|
25
|
+
for (const interactiveCommand of command.interactive) {
|
|
26
|
+
display(`Running command: ${interactiveCommand}`, "loading");
|
|
27
|
+
await runCommand(interactiveCommand);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Then run non-interactive
|
|
32
|
+
if (command.nonInteractive) {
|
|
33
|
+
display(`Running command: ${command.nonInteractive[0]}`, "loading");
|
|
34
|
+
await runCommand(command.nonInteractive[0] as string);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
display(`Error: ${error.message}`, "error");
|
|
39
|
+
}
|
|
40
|
+
}
|