pm-auto 1.0.5 → 1.0.7

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.
Files changed (46) hide show
  1. package/README.md +6 -3
  2. package/config.json +45 -0
  3. package/dist/build_command.d.ts +6 -1
  4. package/dist/build_command.d.ts.map +1 -1
  5. package/dist/build_command.js +66 -13
  6. package/dist/build_command.js.map +1 -1
  7. package/dist/config_path.d.ts +1 -0
  8. package/dist/config_path.d.ts.map +1 -1
  9. package/dist/config_path.js +60 -2
  10. package/dist/config_path.js.map +1 -1
  11. package/dist/config_reader.d.ts +2 -12
  12. package/dist/config_reader.d.ts.map +1 -1
  13. package/dist/config_reader.js +76 -92
  14. package/dist/config_reader.js.map +1 -1
  15. package/dist/display.d.ts +5 -0
  16. package/dist/display.d.ts.map +1 -1
  17. package/dist/display.js +2 -1
  18. package/dist/display.js.map +1 -1
  19. package/dist/index.js +6 -7
  20. package/dist/index.js.map +1 -1
  21. package/dist/orchestrator.d.ts.map +1 -1
  22. package/dist/orchestrator.js +18 -28
  23. package/dist/orchestrator.js.map +1 -1
  24. package/dist/run_commands.d.ts +6 -0
  25. package/dist/run_commands.d.ts.map +1 -0
  26. package/dist/{install.js → run_commands.js} +10 -7
  27. package/dist/run_commands.js.map +1 -0
  28. package/dist/types/index.d.ts +8 -2
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/package.json +4 -2
  31. package/src/build_command.ts +78 -13
  32. package/src/config_path.ts +63 -2
  33. package/src/config_reader.ts +90 -110
  34. package/src/display.ts +2 -2
  35. package/src/index.ts +8 -13
  36. package/src/orchestrator.ts +21 -34
  37. package/src/{install.ts → run_commands.ts} +10 -6
  38. package/src/types/index.ts +12 -3
  39. package/tests/build_command.test.ts +240 -30
  40. package/tests/config_reader.test.ts +51 -92
  41. package/tests/display.test.ts +42 -34
  42. package/tests/{install.test.ts → run_command.test.ts} +23 -23
  43. package/dist/install.d.ts +0 -6
  44. package/dist/install.d.ts.map +0 -1
  45. package/dist/install.js.map +0 -1
  46. package/test.json +0 -87
@@ -1,37 +1,247 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { buildCommands, buildUninstallCommands } from "../src/build_command.js";
3
- import type { ConfigType } from "../src/types/index.js";
2
+ import {
3
+ buildInstallCommands,
4
+ buildUninstallCommands,
5
+ cleanCommand,
6
+ } from "../src/build_command.js";
7
+ import type { CommandResult, ConfigType } from "../src/types/index.js";
4
8
 
5
- describe("buildCommand", () => {
6
- const mockConfig: ConfigType[] = [
9
+ describe("cleanCommand", () => {
10
+ it("should return a string of a command stripped of its flags", () => {
11
+ const commands = [
12
+ "lodash@latest",
13
+ "@react-three/fiber",
14
+ "@three/types -D",
15
+ "three --force",
16
+ ];
17
+ const results = [];
18
+ for (const command of commands) {
19
+ results.push(cleanCommand(command));
20
+ }
21
+ expect(results).toEqual([
22
+ "lodash",
23
+ "@react-three/fiber",
24
+ "@three/types",
25
+ "three",
26
+ ]);
27
+ });
28
+ });
29
+
30
+ const mockConfigs: Record<string, ConfigType[]> = {
31
+ pnpm: [
32
+ {
33
+ presetName: "vite",
34
+ packageManager: "pnpm",
35
+ packages: [
36
+ { command: "vite", interactive: true, version: "latest", flags: ["."] },
37
+ { command: "gsap", interactive: false, version: "latest" },
38
+ {
39
+ command: "react-dom",
40
+ interactive: false,
41
+ version: "latest",
42
+ dev: true,
43
+ flags: ["--legacy-peer-deps"],
44
+ },
45
+ {
46
+ command: "@react-three/fiber",
47
+ interactive: false,
48
+ version: "1.0.0",
49
+ dev: true,
50
+ },
51
+ {
52
+ command: "typescript",
53
+ interactive: false,
54
+ version: "latest",
55
+ dev: true,
56
+ },
57
+ { command: "clsx", interactive: false },
58
+ { command: "shadcn", interactive: true, version: "latest" },
59
+ ],
60
+ },
61
+ ],
62
+ yarn: [
63
+ {
64
+ presetName: "vite",
65
+ packageManager: "yarn",
66
+ packages: [
67
+ { command: "vite", interactive: true, version: "latest", flags: ["."] },
68
+ { command: "gsap", interactive: false, version: "latest" },
69
+ {
70
+ command: "react-dom",
71
+ interactive: false,
72
+ version: "latest",
73
+ dev: true,
74
+ flags: ["--peer-deps"],
75
+ },
76
+ {
77
+ command: "@react-three/fiber",
78
+ interactive: false,
79
+ version: "1.0.0",
80
+ dev: true,
81
+ },
82
+ {
83
+ command: "typescript",
84
+ interactive: false,
85
+ version: "latest",
86
+ dev: true,
87
+ },
88
+ { command: "clsx", interactive: false },
89
+ { command: "shadcn", interactive: true, version: "latest" },
90
+ ],
91
+ },
92
+ ],
93
+ npm: [
7
94
  {
8
- name: "vite",
95
+ presetName: "vite",
9
96
  packageManager: "npm",
10
97
  packages: [
11
- { command: "vite@latest", interactive: true },
12
- { command: "gsap@latest", interactive: false },
13
- ],
14
- },
15
- ];
16
- it("builds an installation command", () => {
17
- const command = buildCommands(mockConfig);
18
- expect(command).toEqual([
19
- {
20
- name: "vite",
21
- interactive: ["npx vite@latest"],
22
- nonInteractive: ["npm install gsap@latest"],
23
- },
24
- ]);
25
- });
98
+ { command: "vite", interactive: true, version: "latest", flags: ["."] },
99
+ { command: "gsap", interactive: false, version: "latest" },
100
+ {
101
+ command: "react-dom",
102
+ interactive: false,
103
+ version: "latest",
104
+ dev: true,
105
+ flags: ["--legacy-peer-deps"],
106
+ },
107
+ {
108
+ command: "@react-three/fiber",
109
+ interactive: false,
110
+ version: "1.0.0",
111
+ dev: true,
112
+ },
113
+ {
114
+ command: "typescript",
115
+ interactive: false,
116
+ version: "latest",
117
+ dev: true,
118
+ },
119
+ { command: "clsx", interactive: false },
120
+ { command: "shadcn", interactive: true, version: "latest" },
121
+ ],
122
+ },
123
+ ],
124
+ bun: [
125
+ {
126
+ presetName: "vite",
127
+ packageManager: "bun",
128
+ packages: [
129
+ { command: "vite", interactive: true, version: "latest", flags: ["."] },
130
+ { command: "gsap", interactive: false, version: "latest" },
131
+ {
132
+ command: "react-dom",
133
+ interactive: false,
134
+ version: "latest",
135
+ dev: true,
136
+ flags: ["--peer-deps"],
137
+ },
138
+ {
139
+ command: "@react-three/fiber",
140
+ interactive: false,
141
+ version: "1.0.0",
142
+ dev: true,
143
+ },
144
+ {
145
+ command: "typescript",
146
+ interactive: false,
147
+ version: "latest",
148
+ dev: true,
149
+ },
150
+ { command: "clsx", interactive: false },
151
+ { command: "shadcn", interactive: true, version: "latest" },
152
+ ],
153
+ },
154
+ ],
155
+ };
26
156
 
27
- it("builds an uninstallation command", () => {
28
- const command = buildUninstallCommands(mockConfig);
29
- expect(command).toEqual([
30
- {
31
- name: "vite",
32
- interactive: [],
33
- nonInteractive: ["npm uninstall gsap@latest"],
34
- },
35
- ]);
36
- });
157
+ const expectedInstall: Record<string, CommandResult[]> = {
158
+ pnpm: [
159
+ {
160
+ presetName: "vite",
161
+ interactive: ["pnpm dlx vite@latest .", "pnpm dlx shadcn@latest"],
162
+ nonInteractive: [
163
+ "pnpm add gsap@latest react-dom@latest -D --legacy-peer-deps @react-three/fiber@1.0.0 -D typescript@latest -D clsx",
164
+ ],
165
+ },
166
+ ],
167
+ yarn: [
168
+ {
169
+ presetName: "vite",
170
+ interactive: ["yarn dlx vite@latest .", "yarn dlx shadcn@latest"],
171
+ nonInteractive: [
172
+ "yarn add gsap@latest react-dom@latest -D --peer-deps @react-three/fiber@1.0.0 -D typescript@latest -D clsx",
173
+ ],
174
+ },
175
+ ],
176
+ npm: [
177
+ {
178
+ presetName: "vite",
179
+ interactive: ["npx vite@latest .", "npx shadcn@latest"],
180
+ nonInteractive: [
181
+ "npm install gsap@latest react-dom@latest -D --legacy-peer-deps @react-three/fiber@1.0.0 -D typescript@latest -D clsx",
182
+ ],
183
+ },
184
+ ],
185
+ bun: [
186
+ {
187
+ presetName: "vite",
188
+ interactive: ["bunx vite@latest .", "bunx shadcn@latest"],
189
+ nonInteractive: [
190
+ "bun add gsap@latest react-dom@latest -d --peer-deps @react-three/fiber@1.0.0 -d typescript@latest -d clsx",
191
+ ],
192
+ },
193
+ ],
194
+ };
195
+
196
+ const expectedUninstall: Record<string, CommandResult[]> = {
197
+ pnpm: [
198
+ {
199
+ presetName: "vite",
200
+ interactive: [],
201
+ nonInteractive: [
202
+ "pnpm remove gsap react-dom @react-three/fiber typescript clsx",
203
+ ],
204
+ },
205
+ ],
206
+ yarn: [
207
+ {
208
+ presetName: "vite",
209
+ interactive: [],
210
+ nonInteractive: [
211
+ "yarn remove gsap react-dom @react-three/fiber typescript clsx",
212
+ ],
213
+ },
214
+ ],
215
+ npm: [
216
+ {
217
+ presetName: "vite",
218
+ interactive: [],
219
+ nonInteractive: [
220
+ "npm uninstall gsap react-dom @react-three/fiber typescript clsx",
221
+ ],
222
+ },
223
+ ],
224
+ bun: [
225
+ {
226
+ presetName: "vite",
227
+ interactive: [],
228
+ nonInteractive: [
229
+ "bun remove gsap react-dom @react-three/fiber typescript clsx",
230
+ ],
231
+ },
232
+ ],
233
+ };
234
+
235
+ describe("buildCommand", () => {
236
+ for (const pm of Object.keys(mockConfigs)) {
237
+ it(`builds ${pm} installation command`, () => {
238
+ const command = buildInstallCommands(mockConfigs[pm]);
239
+ expect(command).toEqual(expectedInstall[pm]);
240
+ });
241
+
242
+ it(`builds ${pm} uninstallation command`, () => {
243
+ const command = buildUninstallCommands(mockConfigs[pm]);
244
+ expect(command).toEqual(expectedUninstall[pm]);
245
+ });
246
+ }
37
247
  });
@@ -1,11 +1,9 @@
1
1
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import fs from "fs/promises";
3
- import * as fsd from "fs";
4
- import * as path from "path";
5
3
  import { display } from "../src/display.js";
6
- import { detectPackageManager, getConfigObject } from "../src/config_reader.js";
4
+ import { getConfigObject } from "../src/config_reader.js";
7
5
  import { getConfigPath } from "../src/config_path.js";
8
- import type { CommandResult, ConfigType } from "../src/types/index.js";
6
+ import type { ConfigType } from "../src/types/index.js";
9
7
  import * as clack from "@clack/prompts";
10
8
 
11
9
  vi.mock("fs/promises");
@@ -15,55 +13,25 @@ vi.mock("../src/display.js");
15
13
  vi.mock("@clack/prompts");
16
14
  vi.mock("../src/config_path.js");
17
15
 
18
- describe("detect package manager", () => {
19
- beforeEach(() => {
20
- vi.resetAllMocks();
21
- });
22
- it("should detects pnpm from lock file", () => {
23
- vi.mocked(fsd.existsSync).mockImplementation((filePath) =>
24
- filePath.toString().includes("pnpm-lock.yaml"),
25
- );
26
- const result = detectPackageManager("/test/path");
27
-
28
- expect(result).toBe("pnpm");
29
- });
30
- it("should detects yarn from lock file", () => {
31
- vi.mocked(fsd.existsSync).mockImplementation((filePath) =>
32
- filePath.toString().includes("yarn.lock"),
33
- );
34
- const result = detectPackageManager("/test/path");
35
-
36
- expect(result).toBe("yarn");
37
- });
38
- it("should detects npm from lock file", () => {
39
- vi.mocked(fsd.existsSync).mockImplementation((filePath) =>
40
- filePath.toString().includes("package-lock.json"),
41
- );
42
- const result = detectPackageManager("/test/path");
43
-
44
- expect(result).toBe("npm");
45
- });
46
- it("should display error message when no lock file found", () => {
47
- vi.mocked(fsd.existsSync).mockImplementation(() => false);
48
- const result = detectPackageManager("/test/path");
49
-
50
- expect(display).toHaveBeenCalledWith(
51
- expect.stringContaining("No Lock File Found"),
52
- "error",
53
- );
54
- expect(result).toBeUndefined();
55
- });
56
- });
57
-
58
16
  describe("get Config Object", () => {
59
- const mockConfig = {
17
+ const mockConfig: Record<string, ConfigType> = {
60
18
  react: {
61
- name: "react",
62
- packages: [{ command: "npm install react", interactive: false }],
19
+ presetName: "react",
20
+ packageManager: "pnpm",
21
+ packages: [
22
+ {
23
+ command: "npm install react",
24
+ interactive: false,
25
+ flags: ["--peers-deps"],
26
+ },
27
+ ],
63
28
  },
64
29
  vue: {
65
- name: "vue",
66
- packages: [{ command: "npm install vue", interactive: false }],
30
+ presetName: "vue",
31
+ packageManager: "npm",
32
+ packages: [
33
+ { command: "npm install vue", interactive: false, version: "latest" },
34
+ ],
67
35
  },
68
36
  };
69
37
 
@@ -79,41 +47,61 @@ describe("get Config Object", () => {
79
47
 
80
48
  expect(config).toEqual([
81
49
  {
82
- name: "react",
83
- packages: [{ command: "npm install react", interactive: false }],
50
+ presetName: "react",
51
+ packageManager: "pnpm",
52
+ packages: [
53
+ {
54
+ command: "npm install react",
55
+ interactive: false,
56
+ flags: ["--peers-deps"],
57
+ },
58
+ ],
84
59
  },
85
60
  {
86
- name: "vue",
87
- packages: [{ command: "npm install vue", interactive: false }],
61
+ presetName: "vue",
62
+ packageManager: "npm",
63
+ packages: [
64
+ { command: "npm install vue", interactive: false, version: "latest" },
65
+ ],
88
66
  },
89
- ]);
67
+ ] as ConfigType[]);
90
68
  expect(display).toHaveBeenCalledWith(
91
69
  expect.stringContaining("Package 'next' not found in config"),
92
70
  "warning",
93
71
  );
94
72
  });
95
73
 
74
+ it("should display an error on invalid config file format", async () => {
75
+ vi.mocked(getConfigPath).mockReturnValue("/mock/config.json");
76
+ vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({ name: "help" }));
77
+
78
+ await getConfigObject(["react"], {});
79
+ expect(display).toHaveBeenCalledWith(
80
+ expect.stringContaining("Invalid config file format"),
81
+ "error",
82
+ );
83
+ });
84
+
96
85
  // it("should display an error on readFile error", async () => {
97
- // vi.mocked(getConfigPath).mockReturnValue("/path/to/test");
98
- // vi.mocked(fs.readFile).mockRejectedValue(new Error("File not found"));
99
- // await getConfigObject([], {});
86
+ // vi.mocked(getConfigPath).mockReturnValue("/");
87
+ // vi.mocked(fs.readFile).mockRejectedValue("File not found");
88
+ // await getConfigObject(["react"], {});
100
89
  // expect(display).toHaveBeenCalledWith(
101
- // expect.stringContaining("Try updating the config file"),
90
+ // expect.stringContaining("Try updating the config file path"),
102
91
  // "error",
103
92
  // );
104
93
  // });
105
- //
106
94
 
107
- it("adds command with addCommand option", async () => {
95
+ it("adds flags with addFlags option", async () => {
108
96
  vi.mocked(getConfigPath).mockReturnValue("/mock/config.json");
109
97
  vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockConfig));
110
98
 
111
99
  const result = (await getConfigObject(["react"], {
112
- addCommand: "--save-dev",
100
+ addFlags: "--force",
113
101
  })) as ConfigType[];
114
102
 
115
- if (result[0]?.packages[0]?.command) {
116
- expect(result[0].packages[0].command).toContain("--save-dev");
103
+ if (result[0]?.packages[0]?.flags) {
104
+ expect(result[0].packages[0].flags).toContain("--force");
117
105
  }
118
106
  });
119
107
 
@@ -147,33 +135,4 @@ describe("get Config Object", () => {
147
135
 
148
136
  mockExit.mockRestore();
149
137
  });
150
-
151
- it("generates command for package.json install", async () => {
152
- vi.mocked(fsd.existsSync).mockImplementation((filePath) =>
153
- filePath.toString().includes("package-lock.json"),
154
- );
155
-
156
- const result = await getConfigObject([], { pkgJson: true });
157
-
158
- expect(result).toEqual([
159
- {
160
- name: "package.json",
161
- interactive: [],
162
- nonInteractive: ["npm install"],
163
- },
164
- ]);
165
- });
166
-
167
- it("uses correct package manager for package.json", async () => {
168
- vi.mocked(fsd.existsSync).mockImplementation((filePath) =>
169
- filePath.toString().includes("pnpm-lock.yaml"),
170
- );
171
-
172
- const result = (await getConfigObject([], {
173
- pkgJson: true,
174
- })) as CommandResult[];
175
- if (result[0]) {
176
- expect(result[0].nonInteractive[0]).toBe("pnpm install");
177
- }
178
- });
179
138
  });
@@ -1,8 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { display } from "../src/display.js";
2
+ import { display, s } from "../src/display.js";
3
3
  import { log, spinner } from "@clack/prompts";
4
+ import chalk from "chalk";
4
5
 
5
- // Mock the dependencies
6
+ // Mock dependencies
6
7
  vi.mock("@clack/prompts", () => ({
7
8
  log: {
8
9
  error: vi.fn(),
@@ -11,7 +12,10 @@ vi.mock("@clack/prompts", () => ({
11
12
  info: vi.fn(),
12
13
  message: vi.fn(),
13
14
  },
14
- spinner: vi.fn(),
15
+ spinner: vi.fn(() => ({
16
+ start: vi.fn(),
17
+ stop: vi.fn(),
18
+ })),
15
19
  }));
16
20
 
17
21
  vi.mock("chalk", () => ({
@@ -23,61 +27,65 @@ vi.mock("chalk", () => ({
23
27
  },
24
28
  }));
25
29
 
26
- describe("display", () => {
27
- let mockSpinner: any;
28
- let exitSpy: any;
30
+ describe("display function", () => {
31
+ let processExitSpy: any;
29
32
 
30
33
  beforeEach(() => {
31
- mockSpinner = {
32
- stop: vi.fn(),
33
- start: vi.fn(),
34
- };
35
- vi.mocked(spinner).mockReturnValue(mockSpinner);
36
- exitSpy = vi
34
+ vi.clearAllMocks();
35
+ processExitSpy = vi
37
36
  .spyOn(process, "exit")
38
- .mockImplementation(() => undefined as never);
37
+ .mockImplementation(() => ({}) as never);
39
38
  });
40
39
 
41
40
  afterEach(() => {
42
- vi.clearAllMocks();
43
- exitSpy.mockRestore();
41
+ processExitSpy.mockRestore();
44
42
  });
45
43
 
46
44
  it("should display error message and exit process", () => {
47
- display("Error text", "error");
48
- expect(log.error).toHaveBeenCalledWith("red:Error text");
49
- expect(exitSpy).toHaveBeenCalledWith(0);
45
+ display("Error occurred", "error");
46
+
47
+ expect(chalk.red).toHaveBeenCalledWith("Error occurred");
48
+ expect(log.error).toHaveBeenCalledWith("red:Error occurred");
49
+ expect(processExitSpy).toHaveBeenCalledWith(0);
50
50
  });
51
51
 
52
52
  it("should display success message", () => {
53
- display("Success text", "success");
54
- expect(log.success).toHaveBeenCalledWith("green:Success text");
55
- expect(exitSpy).not.toHaveBeenCalled();
53
+ display("Operation successful", "success");
54
+
55
+ expect(chalk.green).toHaveBeenCalledWith("Operation successful");
56
+ expect(log.success).toHaveBeenCalledWith("green:Operation successful");
56
57
  });
57
58
 
58
59
  it("should display warning message", () => {
59
- display("Warning text", "warning");
60
- expect(log.warn).toHaveBeenCalledWith("yellow:Warning text");
60
+ display("Warning message", "warning");
61
+
62
+ expect(chalk.yellow).toHaveBeenCalledWith("Warning message");
63
+ expect(log.warn).toHaveBeenCalledWith("yellow:Warning message");
61
64
  });
62
65
 
63
66
  it("should display info message", () => {
64
- display("Info text", "info");
65
- expect(log.info).toHaveBeenCalledWith("blue:Info text");
67
+ display("Info message", "info");
68
+
69
+ expect(chalk.blue).toHaveBeenCalledWith("Info message");
70
+ expect(log.info).toHaveBeenCalledWith("blue:Info message");
66
71
  });
67
72
 
68
73
  it("should start spinner for loading type", () => {
69
- const result = display("Loading text", "loading");
70
- expect(mockSpinner.start).toHaveBeenCalledWith("Loading text");
71
- expect(result).toBe(mockSpinner);
74
+ const result = display("Loading...", "loading");
75
+
76
+ expect(s.start).toHaveBeenCalledWith("Loading...");
77
+ expect(result).toBe(s);
72
78
  });
73
79
 
74
- it("should display plain message for default/empty type", () => {
75
- display("Plain text", "");
76
- expect(log.message).toHaveBeenCalledWith("Plain text");
80
+ it("should display default message for empty type", () => {
81
+ display("Default message", "");
82
+
83
+ expect(log.message).toHaveBeenCalledWith("Default message");
77
84
  });
78
85
 
79
- it("should display plain message when type is not recognized", () => {
80
- display("Unknown text", "unknown" as any);
81
- expect(log.message).toHaveBeenCalledWith("Unknown text");
86
+ it("should display default message for unrecognized type", () => {
87
+ display("Random message", "unknown" as any);
88
+
89
+ expect(log.message).toHaveBeenCalledWith("Random message");
82
90
  });
83
91
  });