diff --git a/.changeset/shy-nails-enjoy.md b/.changeset/shy-nails-enjoy.md new file mode 100644 index 0000000000..c3332f462a --- /dev/null +++ b/.changeset/shy-nails-enjoy.md @@ -0,0 +1,5 @@ +--- +"create-medusa-app": patch +--- + +fix(create-medusa-app): ensure the same package manager is used consistently diff --git a/packages/cli/create-medusa-app/src/utils/execute.ts b/packages/cli/create-medusa-app/src/utils/execute.ts index b5ea20368d..02df8c5e8c 100644 --- a/packages/cli/create-medusa-app/src/utils/execute.ts +++ b/packages/cli/create-medusa-app/src/utils/execute.ts @@ -32,13 +32,15 @@ const execute = async ( const childProcess = spawnSync(commandStr, { ...options, shell: true, - stdio: needOutput - ? "pipe" - : [process.stdin, process.stdout, process.stderr], + stdio: needOutput ? + "pipe" : + [process.stdin, process.stdout, process.stderr], }) - if (childProcess.error) { - throw childProcess.error + if (childProcess.error || childProcess.status !== 0) { + throw childProcess.error || + childProcess.stderr?.toString() || + `${commandStr} failed with status ${childProcess.status}` } if ( diff --git a/packages/cli/create-medusa-app/src/utils/package-manager.ts b/packages/cli/create-medusa-app/src/utils/package-manager.ts new file mode 100644 index 0000000000..5f51fa264e --- /dev/null +++ b/packages/cli/create-medusa-app/src/utils/package-manager.ts @@ -0,0 +1,87 @@ +import execute from "./execute.js" +import ProcessManager from "./process-manager.js" + +export default class PackageManager { + protected packageManager?: "npm" | "yarn" + protected processManager: ProcessManager + protected verbose + + constructor(processManager: ProcessManager, verbose = false) { + this.processManager = processManager + this.verbose = verbose + } + + async setPackageManager(execOptions: Record): Promise { + if (this.packageManager) { + return + } + + // check whether yarn is available + await this.processManager.runProcess({ + process: async () => { + try { + await execute([`yarn -v`, execOptions], { verbose: this.verbose }) + // yarn is available + this.packageManager = "yarn" + } catch (e) { + // yarn isn't available + // use npm + this.packageManager = "npm" + } + }, + ignoreERESOLVE: true, + }) + } + + async installDependencies( + execOptions: Record, + ) { + if (!this.packageManager) { + await this.setPackageManager(execOptions) + } + + const command = this.packageManager === "yarn" ? + `yarn` : `npm install` + + await this.processManager.runProcess({ + process: async () => { + await execute([command, execOptions], { + verbose: this.verbose + }) + }, + ignoreERESOLVE: true, + }) + } + + async runCommand( + command: string, + execOptions: Record, + ) { + if (!this.packageManager) { + await this.setPackageManager(execOptions) + } + + const commandStr = this.getCommandStr(command) + + await this.processManager.runProcess({ + process: async () => { + await execute([commandStr, execOptions], { + verbose: this.verbose + }) + }, + ignoreERESOLVE: true, + }) + } + + getCommandStr( + command: string, + ): string { + if (!this.packageManager) { + throw new Error("Package manager not set") + } + + return this.packageManager === "yarn" + ? `yarn ${command}` + : `npm run ${command}` + } +} \ No newline at end of file diff --git a/packages/cli/create-medusa-app/src/utils/prepare-project.ts b/packages/cli/create-medusa-app/src/utils/prepare-project.ts index 84b9a9f453..b81c136f12 100644 --- a/packages/cli/create-medusa-app/src/utils/prepare-project.ts +++ b/packages/cli/create-medusa-app/src/utils/prepare-project.ts @@ -6,6 +6,7 @@ import { EOL } from "os" import { displayFactBox, FactBoxOptions } from "./facts.js" import ProcessManager from "./process-manager.js" import type { Client } from "pg" +import PackageManager from "./package-manager.js" const ADMIN_EMAIL = "admin@medusa-test.com" let STORE_CORS = "http://localhost:8000" @@ -24,6 +25,7 @@ type PreparePluginOptions = { processManager: ProcessManager abortController?: AbortController verbose?: boolean + packageManager: PackageManager } type PrepareProjectOptions = { @@ -42,6 +44,7 @@ type PrepareProjectOptions = { nextjsDirectory?: string client: Client | null verbose?: boolean + packageManager: PackageManager } type PrepareOptions = PreparePluginOptions | PrepareProjectOptions @@ -66,6 +69,7 @@ async function preparePlugin({ processManager, abortController, verbose = false, + packageManager, }: PreparePluginOptions) { // initialize execution options const execOptions = { @@ -98,20 +102,7 @@ async function preparePlugin({ processManager, }) - await processManager.runProcess({ - process: async () => { - try { - await execute([`yarn`, execOptions], { verbose }) - } catch (e) { - // yarn isn't available - // use npm - await execute([`npm install --legacy-peer-deps`, execOptions], { - verbose, - }) - } - }, - ignoreERESOLVE: true, - }) + await packageManager.installDependencies(execOptions) factBoxOptions.interval = displayFactBox({ ...factBoxOptions, @@ -136,6 +127,7 @@ async function prepareProject({ nextjsDirectory = "", client, verbose = false, + packageManager, }: PrepareProjectOptions) { // initialize execution options const execOptions = { @@ -196,20 +188,7 @@ async function prepareProject({ processManager, }) - await processManager.runProcess({ - process: async () => { - try { - await execute([`yarn`, execOptions], { verbose }) - } catch (e) { - // yarn isn't available - // use npm - await execute([`npm install --legacy-peer-deps`, execOptions], { - verbose, - }) - } - }, - ignoreERESOLVE: true, - }) + await packageManager.installDependencies(execOptions) factBoxOptions.interval = displayFactBox({ ...factBoxOptions, @@ -285,18 +264,7 @@ async function prepareProject({ title: "Seeding database...", }) - await processManager.runProcess({ - process: async () => { - try { - await execute([`yarn seed`, execOptions], { verbose }) - } catch (e) { - // yarn isn't available - // use npm - await execute([`npm run seed`, execOptions], { verbose }) - } - }, - ignoreERESOLVE: true, - }) + await packageManager.runCommand("seed", execOptions) displayFactBox({ ...factBoxOptions, diff --git a/packages/cli/create-medusa-app/src/utils/project-creator/creator.ts b/packages/cli/create-medusa-app/src/utils/project-creator/creator.ts index fc15326b1c..21487edfe5 100644 --- a/packages/cli/create-medusa-app/src/utils/project-creator/creator.ts +++ b/packages/cli/create-medusa-app/src/utils/project-creator/creator.ts @@ -3,6 +3,7 @@ import path from "path" import createAbortController from "../create-abort-controller.js" import { FactBoxOptions } from "../facts.js" import ProcessManager from "../process-manager.js" +import PackageManager from "../package-manager.js" export interface ProjectOptions { repoUrl?: string @@ -25,6 +26,7 @@ export interface ProjectCreator { export abstract class BaseProjectCreator { protected spinner: Ora protected processManager: ProcessManager + protected packageManager: PackageManager protected abortController: AbortController protected factBoxOptions: FactBoxOptions protected projectName: string @@ -39,6 +41,7 @@ export abstract class BaseProjectCreator { ) { this.spinner = ora() this.processManager = new ProcessManager() + this.packageManager = new PackageManager(this.processManager) this.abortController = createAbortController(this.processManager) this.projectName = projectName const basePath = diff --git a/packages/cli/create-medusa-app/src/utils/project-creator/medusa-plugin-creator.ts b/packages/cli/create-medusa-app/src/utils/project-creator/medusa-plugin-creator.ts index 420befc16c..8ae96d49fb 100644 --- a/packages/cli/create-medusa-app/src/utils/project-creator/medusa-plugin-creator.ts +++ b/packages/cli/create-medusa-app/src/utils/project-creator/medusa-plugin-creator.ts @@ -72,6 +72,7 @@ export class PluginProjectCreator processManager: this.processManager, abortController: this.abortController, verbose: this.options.verbose, + packageManager: this.packageManager, }) } diff --git a/packages/cli/create-medusa-app/src/utils/project-creator/medusa-project-creator.ts b/packages/cli/create-medusa-app/src/utils/project-creator/medusa-project-creator.ts index a4f27421ff..edd2f13320 100644 --- a/packages/cli/create-medusa-app/src/utils/project-creator/medusa-project-creator.ts +++ b/packages/cli/create-medusa-app/src/utils/project-creator/medusa-project-creator.ts @@ -153,6 +153,7 @@ export class MedusaProjectCreator nextjsDirectory: this.nextjsDirectory, client: this.client, verbose: this.options.verbose, + packageManager: this.packageManager, }) } finally { await this.client?.end() @@ -214,18 +215,19 @@ export class MedusaProjectCreator } protected showSuccessMessage(): void { + const commandStr = this.packageManager.getCommandStr(`dev`) logMessage({ message: boxen( chalk.green( `Change to the \`${ this.projectName - }\` directory to explore your Medusa project.${EOL}${EOL}Start your Medusa application again with the following command:${EOL}${EOL}yarn dev${EOL}${EOL}${ + }\` directory to explore your Medusa project.${EOL}${EOL}Start your Medusa application again with the following command:${EOL}${EOL}${commandStr}${EOL}${EOL}${ this.inviteToken ? `After you start the Medusa application, you can create an admin user with the URL http://localhost:9000/app/invite?token=${this.inviteToken}&first_run=true${EOL}${EOL}` : "" }${ this.nextjsDirectory?.length - ? `The Next.js Starter Storefront was installed in the \`${this.nextjsDirectory}\` directory. Change to that directory and start it with the following command:${EOL}${EOL}npm run dev${EOL}${EOL}` + ? `The Next.js Starter Storefront was installed in the \`${this.nextjsDirectory}\` directory. Change to that directory and start it with the following command:${EOL}${EOL}${commandStr}${EOL}${EOL}` : "" }Check out the Medusa documentation to start your development:${EOL}${EOL}https://docs.medusajs.com/${EOL}${EOL}Star us on GitHub if you like what we're building:${EOL}${EOL}https://github.com/medusajs/medusa/stargazers` ),