create-acmekit-app 2.13.1
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/.turbo/turbo-build.log +1 -0
- package/CHANGELOG.md +3 -0
- package/README.md +58 -0
- package/dist/commands/create.js +8 -0
- package/dist/index.js +31 -0
- package/dist/utils/clone-repo.js +77 -0
- package/dist/utils/create-abort-controller.js +11 -0
- package/dist/utils/create-db.js +172 -0
- package/dist/utils/execute.js +51 -0
- package/dist/utils/facts.js +87 -0
- package/dist/utils/format-connection-string.js +17 -0
- package/dist/utils/get-config-store.js +10 -0
- package/dist/utils/get-current-os.js +10 -0
- package/dist/utils/log-message.js +21 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/nextjs-utils.js +85 -0
- package/dist/utils/node-version.js +5 -0
- package/dist/utils/package-manager.js +266 -0
- package/dist/utils/postgres-client.js +9 -0
- package/dist/utils/prepare-project.js +184 -0
- package/dist/utils/process-manager.js +50 -0
- package/dist/utils/project-creator/acmekit-plugin-creator.js +93 -0
- package/dist/utils/project-creator/acmekit-project-creator.js +204 -0
- package/dist/utils/project-creator/creator.js +46 -0
- package/dist/utils/project-creator/index.js +4 -0
- package/dist/utils/project-creator/project-creator-factory.js +83 -0
- package/dist/utils/start-acmekit.js +13 -0
- package/dist/utils/update-package-versions.js +28 -0
- package/jest.config.cjs +14 -0
- package/package.json +48 -0
- package/src/commands/create.ts +12 -0
- package/src/index.ts +65 -0
- package/src/types.d.ts +1 -0
- package/src/utils/__tests__/create-abort-controller.test.ts +166 -0
- package/src/utils/__tests__/package-manager.test.ts +637 -0
- package/src/utils/clone-repo.ts +117 -0
- package/src/utils/create-abort-controller.ts +16 -0
- package/src/utils/create-db.ts +245 -0
- package/src/utils/execute.ts +86 -0
- package/src/utils/facts.ts +148 -0
- package/src/utils/format-connection-string.ts +29 -0
- package/src/utils/get-config-store.ts +17 -0
- package/src/utils/get-current-os.ts +10 -0
- package/src/utils/log-message.ts +28 -0
- package/src/utils/logger.ts +10 -0
- package/src/utils/nextjs-utils.ts +139 -0
- package/src/utils/node-version.ts +7 -0
- package/src/utils/package-manager.ts +334 -0
- package/src/utils/postgres-client.ts +23 -0
- package/src/utils/prepare-project.ts +325 -0
- package/src/utils/process-manager.ts +60 -0
- package/src/utils/project-creator/acmekit-plugin-creator.ts +127 -0
- package/src/utils/project-creator/acmekit-project-creator.ts +272 -0
- package/src/utils/project-creator/creator.ts +77 -0
- package/src/utils/project-creator/index.ts +4 -0
- package/src/utils/project-creator/project-creator-factory.ts +119 -0
- package/src/utils/start-acmekit.ts +26 -0
- package/src/utils/update-package-versions.ts +37 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
import PackageManager from "../package-manager"
|
|
2
|
+
import ProcessManager from "../process-manager"
|
|
3
|
+
import execute from "../execute"
|
|
4
|
+
import { existsSync, rmSync } from "fs"
|
|
5
|
+
import logMessage from "../log-message"
|
|
6
|
+
|
|
7
|
+
// Mock dependencies
|
|
8
|
+
jest.mock("../execute")
|
|
9
|
+
jest.mock("fs")
|
|
10
|
+
jest.mock("../log-message")
|
|
11
|
+
|
|
12
|
+
const mockExecute = execute as jest.MockedFunction<typeof execute>
|
|
13
|
+
const mockExistsSync = existsSync as jest.MockedFunction<typeof existsSync>
|
|
14
|
+
const mockRmSync = rmSync as jest.MockedFunction<typeof rmSync>
|
|
15
|
+
const mockLogMessage = logMessage as jest.MockedFunction<typeof logMessage>
|
|
16
|
+
|
|
17
|
+
describe("PackageManager", () => {
|
|
18
|
+
let processManager: ProcessManager
|
|
19
|
+
let originalEnv: NodeJS.ProcessEnv
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
processManager = new ProcessManager()
|
|
23
|
+
originalEnv = { ...process.env }
|
|
24
|
+
jest.clearAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
process.env = originalEnv
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe("constructor", () => {
|
|
32
|
+
it("should initialize with default options", () => {
|
|
33
|
+
const pm = new PackageManager(processManager)
|
|
34
|
+
expect(pm.getPackageManager()).toBeUndefined()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should set npm as chosen package manager when useNpm is true", () => {
|
|
38
|
+
const pm = new PackageManager(processManager, { useNpm: true })
|
|
39
|
+
expect(pm["chosenPackageManager"]).toBe("npm")
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("should set yarn as chosen package manager when useYarn is true", () => {
|
|
43
|
+
const pm = new PackageManager(processManager, { useYarn: true })
|
|
44
|
+
expect(pm["chosenPackageManager"]).toBe("yarn")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("should set pnpm as chosen package manager when usePnpm is true", () => {
|
|
48
|
+
const pm = new PackageManager(processManager, { usePnpm: true })
|
|
49
|
+
expect(pm["chosenPackageManager"]).toBe("pnpm")
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("should respect verbose option", () => {
|
|
53
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
54
|
+
expect(pm["verbose"]).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("should prioritize npm over other options", () => {
|
|
58
|
+
const pm = new PackageManager(processManager, {
|
|
59
|
+
useNpm: true,
|
|
60
|
+
useYarn: true,
|
|
61
|
+
usePnpm: true,
|
|
62
|
+
})
|
|
63
|
+
expect(pm["chosenPackageManager"]).toBe("npm")
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe("detectFromUserAgent", () => {
|
|
68
|
+
it("should detect pnpm from user agent with version", () => {
|
|
69
|
+
process.env.npm_config_user_agent = "pnpm/8.0.0"
|
|
70
|
+
const pm = new PackageManager(processManager)
|
|
71
|
+
const result = pm["detectFromUserAgent"]()
|
|
72
|
+
expect(result.manager).toBe("pnpm")
|
|
73
|
+
expect(result.version).toBe("8.0.0")
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("should detect pnpm from pnpx with version", () => {
|
|
77
|
+
process.env.npm_config_user_agent = "pnpx/8.0.0"
|
|
78
|
+
const pm = new PackageManager(processManager)
|
|
79
|
+
const result = pm["detectFromUserAgent"]()
|
|
80
|
+
expect(result.manager).toBe("pnpm")
|
|
81
|
+
expect(result.version).toBe("8.0.0")
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it("should detect yarn from user agent with version", () => {
|
|
85
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
86
|
+
const pm = new PackageManager(processManager)
|
|
87
|
+
const result = pm["detectFromUserAgent"]()
|
|
88
|
+
expect(result.manager).toBe("yarn")
|
|
89
|
+
expect(result.version).toBe("1.22.0")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("should default to npm for unknown user agent", () => {
|
|
93
|
+
process.env.npm_config_user_agent = "some-unknown-manager/1.0.0"
|
|
94
|
+
const pm = new PackageManager(processManager)
|
|
95
|
+
const result = pm["detectFromUserAgent"]()
|
|
96
|
+
expect(result.manager).toBe("npm")
|
|
97
|
+
expect(result.version).toBeUndefined()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it("should default to npm when user agent is undefined", () => {
|
|
101
|
+
delete process.env.npm_config_user_agent
|
|
102
|
+
const pm = new PackageManager(processManager)
|
|
103
|
+
const result = pm["detectFromUserAgent"]()
|
|
104
|
+
expect(result.manager).toBe("npm")
|
|
105
|
+
expect(result.version).toBeUndefined()
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe("setPackageManager", () => {
|
|
110
|
+
it("should not run if package manager is already set", async () => {
|
|
111
|
+
const pm = new PackageManager(processManager)
|
|
112
|
+
pm["packageManager"] = "yarn"
|
|
113
|
+
|
|
114
|
+
await pm.setPackageManager({})
|
|
115
|
+
|
|
116
|
+
expect(mockExecute).not.toHaveBeenCalled()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("should set chosen package manager if available", async () => {
|
|
120
|
+
mockExecute.mockResolvedValue({ stdout: "8.0.0", stderr: "" })
|
|
121
|
+
const pm = new PackageManager(processManager, { usePnpm: true })
|
|
122
|
+
|
|
123
|
+
await pm.setPackageManager({})
|
|
124
|
+
|
|
125
|
+
expect(pm.getPackageManager()).toBe("pnpm")
|
|
126
|
+
expect(await pm.getPackageManagerString()).toBe("pnpm@8.0.0")
|
|
127
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
128
|
+
["pnpm -v", {}],
|
|
129
|
+
{ verbose: false }
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("should throw error when chosen package manager is not available", async () => {
|
|
134
|
+
mockExecute.mockRejectedValueOnce(new Error("Command not found"))
|
|
135
|
+
|
|
136
|
+
const pm = new PackageManager(processManager, { usePnpm: true })
|
|
137
|
+
|
|
138
|
+
await pm.setPackageManager({})
|
|
139
|
+
|
|
140
|
+
expect(mockLogMessage).toHaveBeenCalledWith({
|
|
141
|
+
type: "error",
|
|
142
|
+
message: expect.stringContaining('"pnpm" is not available'),
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it("should detect from user agent when no package manager chosen", async () => {
|
|
147
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
148
|
+
|
|
149
|
+
const pm = new PackageManager(processManager)
|
|
150
|
+
await pm.setPackageManager({})
|
|
151
|
+
|
|
152
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
153
|
+
expect(await pm.getPackageManagerString()).toBe("yarn@1.22.0")
|
|
154
|
+
// Detection message should not be logged in non-verbose mode
|
|
155
|
+
expect(mockLogMessage).not.toHaveBeenCalledWith({
|
|
156
|
+
type: "info",
|
|
157
|
+
message: expect.stringContaining('Using detected package manager "yarn"'),
|
|
158
|
+
})
|
|
159
|
+
// Should not call getVersion since version is in user agent
|
|
160
|
+
expect(mockExecute).not.toHaveBeenCalled()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it("should use detected package manager even without version in user agent", async () => {
|
|
164
|
+
mockExecute.mockResolvedValue({ stdout: "1.22.0", stderr: "" })
|
|
165
|
+
process.env.npm_config_user_agent = "yarn"
|
|
166
|
+
|
|
167
|
+
const pm = new PackageManager(processManager)
|
|
168
|
+
await pm.setPackageManager({})
|
|
169
|
+
|
|
170
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
171
|
+
expect(await pm.getPackageManagerString()).toBe("yarn@1.22.0")
|
|
172
|
+
// Should call getVersion since version is not in user agent
|
|
173
|
+
expect(mockExecute).toHaveBeenCalledWith(["yarn -v", {}], {
|
|
174
|
+
verbose: false,
|
|
175
|
+
})
|
|
176
|
+
// Fallback message should not be logged in non-verbose mode
|
|
177
|
+
expect(mockLogMessage).not.toHaveBeenCalledWith({
|
|
178
|
+
type: "info",
|
|
179
|
+
message: expect.stringContaining("Falling back to"),
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it("should log detection messages in verbose mode", async () => {
|
|
184
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
185
|
+
|
|
186
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
187
|
+
await pm.setPackageManager({})
|
|
188
|
+
|
|
189
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
190
|
+
expect(mockLogMessage).toHaveBeenCalledWith({
|
|
191
|
+
type: "info",
|
|
192
|
+
message: expect.stringContaining('Using detected package manager "yarn"'),
|
|
193
|
+
})
|
|
194
|
+
// Should not call getVersion since version is in user agent
|
|
195
|
+
expect(mockExecute).not.toHaveBeenCalled()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it("should log fallback messages in verbose mode when no version in user agent", async () => {
|
|
199
|
+
mockExecute.mockResolvedValue({ stdout: "1.22.0", stderr: "" })
|
|
200
|
+
process.env.npm_config_user_agent = "yarn"
|
|
201
|
+
|
|
202
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
203
|
+
await pm.setPackageManager({})
|
|
204
|
+
|
|
205
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
206
|
+
expect(mockLogMessage).toHaveBeenCalledWith({
|
|
207
|
+
type: "info",
|
|
208
|
+
message: expect.stringContaining("Falling back to yarn"),
|
|
209
|
+
})
|
|
210
|
+
// Should call getVersion to get the version
|
|
211
|
+
expect(mockExecute).toHaveBeenCalledWith(["yarn -v", {}], {
|
|
212
|
+
verbose: false,
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
describe("removeLockFiles", () => {
|
|
218
|
+
it("should not remove files if package manager is not set", async () => {
|
|
219
|
+
const pm = new PackageManager(processManager)
|
|
220
|
+
|
|
221
|
+
await pm.removeLockFiles("/test/path")
|
|
222
|
+
|
|
223
|
+
expect(mockExistsSync).not.toHaveBeenCalled()
|
|
224
|
+
expect(mockRmSync).not.toHaveBeenCalled()
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it("should remove yarn.lock, pnpm-lock.yaml, and .yarn when using npm", async () => {
|
|
228
|
+
const pm = new PackageManager(processManager)
|
|
229
|
+
pm["packageManager"] = "npm"
|
|
230
|
+
mockExistsSync.mockReturnValue(true)
|
|
231
|
+
|
|
232
|
+
await pm.removeLockFiles("/test/path")
|
|
233
|
+
|
|
234
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/yarn.lock")
|
|
235
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/pnpm-lock.yaml")
|
|
236
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/.yarn")
|
|
237
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/yarn.lock", {
|
|
238
|
+
force: true,
|
|
239
|
+
recursive: true,
|
|
240
|
+
})
|
|
241
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/pnpm-lock.yaml", {
|
|
242
|
+
force: true,
|
|
243
|
+
recursive: true,
|
|
244
|
+
})
|
|
245
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/.yarn", {
|
|
246
|
+
force: true,
|
|
247
|
+
recursive: true,
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it("should remove package-lock.json and pnpm-lock.yaml when using yarn", async () => {
|
|
252
|
+
const pm = new PackageManager(processManager)
|
|
253
|
+
pm["packageManager"] = "yarn"
|
|
254
|
+
mockExistsSync.mockReturnValue(true)
|
|
255
|
+
|
|
256
|
+
await pm.removeLockFiles("/test/path")
|
|
257
|
+
|
|
258
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/package-lock.json")
|
|
259
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/pnpm-lock.yaml")
|
|
260
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/package-lock.json", {
|
|
261
|
+
force: true,
|
|
262
|
+
recursive: true,
|
|
263
|
+
})
|
|
264
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/pnpm-lock.yaml", {
|
|
265
|
+
force: true,
|
|
266
|
+
recursive: true,
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it("should remove yarn.lock, package-lock.json, and .yarn when using pnpm", async () => {
|
|
271
|
+
const pm = new PackageManager(processManager)
|
|
272
|
+
pm["packageManager"] = "pnpm"
|
|
273
|
+
mockExistsSync.mockReturnValue(true)
|
|
274
|
+
|
|
275
|
+
await pm.removeLockFiles("/test/path")
|
|
276
|
+
|
|
277
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/yarn.lock")
|
|
278
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/package-lock.json")
|
|
279
|
+
expect(mockExistsSync).toHaveBeenCalledWith("/test/path/.yarn")
|
|
280
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/yarn.lock", {
|
|
281
|
+
force: true,
|
|
282
|
+
recursive: true,
|
|
283
|
+
})
|
|
284
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/package-lock.json", {
|
|
285
|
+
force: true,
|
|
286
|
+
recursive: true,
|
|
287
|
+
})
|
|
288
|
+
expect(mockRmSync).toHaveBeenCalledWith("/test/path/.yarn", {
|
|
289
|
+
force: true,
|
|
290
|
+
recursive: true,
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it("should not remove files that don't exist", async () => {
|
|
295
|
+
const pm = new PackageManager(processManager)
|
|
296
|
+
pm["packageManager"] = "npm"
|
|
297
|
+
mockExistsSync.mockReturnValue(false)
|
|
298
|
+
|
|
299
|
+
await pm.removeLockFiles("/test/path")
|
|
300
|
+
|
|
301
|
+
expect(mockExistsSync).toHaveBeenCalled()
|
|
302
|
+
expect(mockRmSync).not.toHaveBeenCalled()
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
describe("installDependencies", () => {
|
|
307
|
+
it("should set package manager before installing if not set", async () => {
|
|
308
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
309
|
+
|
|
310
|
+
const pm = new PackageManager(processManager)
|
|
311
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
312
|
+
|
|
313
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it("should remove lock files before installing", async () => {
|
|
317
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
318
|
+
mockExistsSync.mockReturnValue(true)
|
|
319
|
+
|
|
320
|
+
const pm = new PackageManager(processManager, { useNpm: true })
|
|
321
|
+
pm["packageManager"] = "npm"
|
|
322
|
+
|
|
323
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
324
|
+
|
|
325
|
+
expect(mockRmSync).toHaveBeenCalled()
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it("should execute yarn command when using yarn", async () => {
|
|
329
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
330
|
+
|
|
331
|
+
const pm = new PackageManager(processManager)
|
|
332
|
+
pm["packageManager"] = "yarn"
|
|
333
|
+
|
|
334
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
335
|
+
|
|
336
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
337
|
+
["yarn", { cwd: "/test/path" }],
|
|
338
|
+
{ verbose: false }
|
|
339
|
+
)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it("should execute pnpm install when using pnpm", async () => {
|
|
343
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
344
|
+
|
|
345
|
+
const pm = new PackageManager(processManager)
|
|
346
|
+
pm["packageManager"] = "pnpm"
|
|
347
|
+
|
|
348
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
349
|
+
|
|
350
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
351
|
+
["pnpm install", { cwd: "/test/path" }],
|
|
352
|
+
{ verbose: false }
|
|
353
|
+
)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it("should execute npm install then npm ci when using npm", async () => {
|
|
357
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
358
|
+
|
|
359
|
+
const pm = new PackageManager(processManager)
|
|
360
|
+
pm["packageManager"] = "npm"
|
|
361
|
+
|
|
362
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
363
|
+
|
|
364
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
365
|
+
["npm install", { cwd: "/test/path" }],
|
|
366
|
+
{ verbose: false }
|
|
367
|
+
)
|
|
368
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
369
|
+
["npm ci", { cwd: "/test/path" }],
|
|
370
|
+
{ verbose: false }
|
|
371
|
+
)
|
|
372
|
+
expect(mockExecute).toHaveBeenCalledTimes(2)
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
it("should re-run npm install if npm ci fails", async () => {
|
|
376
|
+
mockExecute
|
|
377
|
+
.mockResolvedValueOnce({ stdout: "", stderr: "" }) // npm install succeeds
|
|
378
|
+
.mockRejectedValueOnce(new Error("npm ci failed")) // npm ci fails
|
|
379
|
+
.mockResolvedValueOnce({ stdout: "", stderr: "" }) // npm install retry succeeds
|
|
380
|
+
|
|
381
|
+
const pm = new PackageManager(processManager)
|
|
382
|
+
pm["packageManager"] = "npm"
|
|
383
|
+
|
|
384
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
385
|
+
|
|
386
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
387
|
+
["npm install", { cwd: "/test/path" }],
|
|
388
|
+
{ verbose: false }
|
|
389
|
+
)
|
|
390
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
391
|
+
["npm ci", { cwd: "/test/path" }],
|
|
392
|
+
{ verbose: false }
|
|
393
|
+
)
|
|
394
|
+
// Second npm install call
|
|
395
|
+
expect(mockExecute).toHaveBeenCalledTimes(3)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
it("should respect verbose option", async () => {
|
|
399
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
400
|
+
|
|
401
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
402
|
+
pm["packageManager"] = "yarn"
|
|
403
|
+
|
|
404
|
+
await pm.installDependencies({ cwd: "/test/path" })
|
|
405
|
+
|
|
406
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
407
|
+
expect.anything(),
|
|
408
|
+
{ verbose: true }
|
|
409
|
+
)
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
describe("getCommandStr", () => {
|
|
414
|
+
it("should throw error if package manager is not set", () => {
|
|
415
|
+
const pm = new PackageManager(processManager)
|
|
416
|
+
|
|
417
|
+
expect(() => pm.getCommandStr("dev")).toThrow("Package manager not set")
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it("should return yarn command format", () => {
|
|
421
|
+
const pm = new PackageManager(processManager)
|
|
422
|
+
pm["packageManager"] = "yarn"
|
|
423
|
+
|
|
424
|
+
expect(pm.getCommandStr("dev")).toBe("yarn dev")
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
it("should return pnpm command format", () => {
|
|
428
|
+
const pm = new PackageManager(processManager)
|
|
429
|
+
pm["packageManager"] = "pnpm"
|
|
430
|
+
|
|
431
|
+
expect(pm.getCommandStr("build")).toBe("pnpm build")
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it("should return npm command format with 'run'", () => {
|
|
435
|
+
const pm = new PackageManager(processManager)
|
|
436
|
+
pm["packageManager"] = "npm"
|
|
437
|
+
|
|
438
|
+
expect(pm.getCommandStr("test")).toBe("npm run test")
|
|
439
|
+
})
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
describe("runCommand", () => {
|
|
443
|
+
it("should set package manager before running if not set", async () => {
|
|
444
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
445
|
+
|
|
446
|
+
const pm = new PackageManager(processManager)
|
|
447
|
+
await pm.runCommand("dev", { cwd: "/test/path" })
|
|
448
|
+
|
|
449
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
it("should execute command with correct format for yarn", async () => {
|
|
453
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
454
|
+
|
|
455
|
+
const pm = new PackageManager(processManager)
|
|
456
|
+
pm["packageManager"] = "yarn"
|
|
457
|
+
|
|
458
|
+
await pm.runCommand("dev", { cwd: "/test/path" })
|
|
459
|
+
|
|
460
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
461
|
+
["yarn dev", { cwd: "/test/path" }],
|
|
462
|
+
{ verbose: false }
|
|
463
|
+
)
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it("should execute command with correct format for npm", async () => {
|
|
467
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
468
|
+
|
|
469
|
+
const pm = new PackageManager(processManager)
|
|
470
|
+
pm["packageManager"] = "npm"
|
|
471
|
+
|
|
472
|
+
await pm.runCommand("build", { cwd: "/test/path" })
|
|
473
|
+
|
|
474
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
475
|
+
["npm run build", { cwd: "/test/path" }],
|
|
476
|
+
{ verbose: false }
|
|
477
|
+
)
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it("should pass through verbose options", async () => {
|
|
481
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
482
|
+
|
|
483
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
484
|
+
pm["packageManager"] = "yarn"
|
|
485
|
+
|
|
486
|
+
await pm.runCommand("dev", { cwd: "/test/path" }, { needOutput: true })
|
|
487
|
+
|
|
488
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
489
|
+
expect.anything(),
|
|
490
|
+
{ verbose: true, needOutput: true }
|
|
491
|
+
)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
it("should return execution result", async () => {
|
|
495
|
+
const result = { stdout: "success", stderr: "" }
|
|
496
|
+
mockExecute.mockResolvedValue(result)
|
|
497
|
+
|
|
498
|
+
const pm = new PackageManager(processManager)
|
|
499
|
+
pm["packageManager"] = "yarn"
|
|
500
|
+
|
|
501
|
+
const output = await pm.runCommand("dev", {})
|
|
502
|
+
|
|
503
|
+
expect(output).toEqual(result)
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
describe("runAcmeKitCommand", () => {
|
|
508
|
+
it("should set package manager before running if not set", async () => {
|
|
509
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
510
|
+
|
|
511
|
+
const pm = new PackageManager(processManager)
|
|
512
|
+
await pm.runAcmeKitCommand("migrate", { cwd: "/test/path" })
|
|
513
|
+
|
|
514
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
it("should execute yarn acmekit command for yarn", async () => {
|
|
518
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
519
|
+
|
|
520
|
+
const pm = new PackageManager(processManager)
|
|
521
|
+
pm["packageManager"] = "yarn"
|
|
522
|
+
|
|
523
|
+
await pm.runAcmeKitCommand("migrate", { cwd: "/test/path" })
|
|
524
|
+
|
|
525
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
526
|
+
["yarn acmekit migrate", { cwd: "/test/path" }],
|
|
527
|
+
{ verbose: false }
|
|
528
|
+
)
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
it("should execute pnpm acmekit command for pnpm", async () => {
|
|
532
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
533
|
+
|
|
534
|
+
const pm = new PackageManager(processManager)
|
|
535
|
+
pm["packageManager"] = "pnpm"
|
|
536
|
+
|
|
537
|
+
await pm.runAcmeKitCommand("seed", { cwd: "/test/path" })
|
|
538
|
+
|
|
539
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
540
|
+
["pnpm acmekit seed", { cwd: "/test/path" }],
|
|
541
|
+
{ verbose: false }
|
|
542
|
+
)
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
it("should execute npx acmekit command for npm", async () => {
|
|
546
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
547
|
+
|
|
548
|
+
const pm = new PackageManager(processManager)
|
|
549
|
+
pm["packageManager"] = "npm"
|
|
550
|
+
|
|
551
|
+
await pm.runAcmeKitCommand("start", { cwd: "/test/path" })
|
|
552
|
+
|
|
553
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
554
|
+
["npx acmekit start", { cwd: "/test/path" }],
|
|
555
|
+
{ verbose: false }
|
|
556
|
+
)
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
it("should pass through verbose options", async () => {
|
|
560
|
+
mockExecute.mockResolvedValue({ stdout: "", stderr: "" })
|
|
561
|
+
|
|
562
|
+
const pm = new PackageManager(processManager, { verbose: true })
|
|
563
|
+
pm["packageManager"] = "yarn"
|
|
564
|
+
|
|
565
|
+
await pm.runAcmeKitCommand("migrate", { cwd: "/test/path" }, { needOutput: true })
|
|
566
|
+
|
|
567
|
+
expect(mockExecute).toHaveBeenCalledWith(
|
|
568
|
+
expect.anything(),
|
|
569
|
+
{ verbose: true, needOutput: true }
|
|
570
|
+
)
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
it("should return execution result", async () => {
|
|
574
|
+
const result = { stdout: "migration complete", stderr: "" }
|
|
575
|
+
mockExecute.mockResolvedValue(result)
|
|
576
|
+
|
|
577
|
+
const pm = new PackageManager(processManager)
|
|
578
|
+
pm["packageManager"] = "yarn"
|
|
579
|
+
|
|
580
|
+
const output = await pm.runAcmeKitCommand("migrate", {})
|
|
581
|
+
|
|
582
|
+
expect(output).toEqual(result)
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
describe("getPackageManager", () => {
|
|
587
|
+
it("should return undefined when not set", () => {
|
|
588
|
+
const pm = new PackageManager(processManager)
|
|
589
|
+
expect(pm.getPackageManager()).toBeUndefined()
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
it("should return the current package manager", () => {
|
|
593
|
+
const pm = new PackageManager(processManager)
|
|
594
|
+
pm["packageManager"] = "yarn"
|
|
595
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
596
|
+
})
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
describe("getPackageManagerString", () => {
|
|
600
|
+
it("should return undefined when version is not set", async () => {
|
|
601
|
+
const pm = new PackageManager(processManager)
|
|
602
|
+
pm["packageManager"] = "yarn"
|
|
603
|
+
expect(await pm.getPackageManagerString()).toBeUndefined()
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
it("should return packageManager@version format", async () => {
|
|
607
|
+
const pm = new PackageManager(processManager)
|
|
608
|
+
pm["packageManager"] = "yarn"
|
|
609
|
+
pm["packageManagerVersion"] = "4.9.0"
|
|
610
|
+
expect(await pm.getPackageManagerString()).toBe("yarn@4.9.0")
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
it("should work with pnpm", async () => {
|
|
614
|
+
const pm = new PackageManager(processManager)
|
|
615
|
+
pm["packageManager"] = "pnpm"
|
|
616
|
+
pm["packageManagerVersion"] = "8.15.0"
|
|
617
|
+
expect(await pm.getPackageManagerString()).toBe("pnpm@8.15.0")
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
it("should work with npm", async () => {
|
|
621
|
+
const pm = new PackageManager(processManager)
|
|
622
|
+
pm["packageManager"] = "npm"
|
|
623
|
+
pm["packageManagerVersion"] = "10.0.0"
|
|
624
|
+
expect(await pm.getPackageManagerString()).toBe("npm@10.0.0")
|
|
625
|
+
})
|
|
626
|
+
|
|
627
|
+
it("should call setPackageManager if package manager is not set", async () => {
|
|
628
|
+
process.env.npm_config_user_agent = "yarn/1.22.0"
|
|
629
|
+
|
|
630
|
+
const pm = new PackageManager(processManager)
|
|
631
|
+
const result = await pm.getPackageManagerString()
|
|
632
|
+
|
|
633
|
+
expect(pm.getPackageManager()).toBe("yarn")
|
|
634
|
+
expect(result).toBe("yarn@1.22.0")
|
|
635
|
+
})
|
|
636
|
+
})
|
|
637
|
+
})
|