From e443a7be3f07db4e7bc35ca67abdce887e247435 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Tue, 21 May 2024 22:45:43 +0300 Subject: [PATCH] feat(create-medusa-app): install v2 by default (#7381) - Remove `v2` option and install V2 starter branch by default. - Use new `exec` command to seed data - Install v2 branch of next.js starter if the option is provided. - Change the opened browser to `localhost:9000/app`. - Added a bunch of todos for onboarding flows once we have that --- packages/cli/create-medusa-app/package.json | 1 + .../create-medusa-app/src/commands/create.ts | 16 +-- packages/cli/create-medusa-app/src/index.ts | 5 - .../create-medusa-app/src/utils/clone-repo.ts | 9 +- .../cli/create-medusa-app/src/utils/facts.ts | 8 +- .../src/utils/nextjs-utils.ts | 34 +++++- .../src/utils/prepare-project.ts | 110 +++++++----------- .../src/utils/start-medusa.ts | 4 +- 8 files changed, 82 insertions(+), 105 deletions(-) diff --git a/packages/cli/create-medusa-app/package.json b/packages/cli/create-medusa-app/package.json index 1ac65f86c9..8e510312dd 100644 --- a/packages/cli/create-medusa-app/package.json +++ b/packages/cli/create-medusa-app/package.json @@ -8,6 +8,7 @@ "license": "MIT", "scripts": { "dev": "ts-node --esm src/index.ts", + "start": "node dist/index.js", "build": "tsc", "watch": "tsc --watch", "prepublishOnly": "cross-env NODE_ENV=production tsc --build" diff --git a/packages/cli/create-medusa-app/src/commands/create.ts b/packages/cli/create-medusa-app/src/commands/create.ts index a3981ea8d3..14a2587b02 100644 --- a/packages/cli/create-medusa-app/src/commands/create.ts +++ b/packages/cli/create-medusa-app/src/commands/create.ts @@ -34,7 +34,6 @@ const isEmail = isEmailImported.default export type CreateOptions = { repoUrl?: string seed?: boolean - // commander passed --no-boilerplate as boilerplate boilerplate?: boolean skipDb?: boolean dbUrl?: string @@ -43,7 +42,6 @@ export type CreateOptions = { directoryPath?: string withNextjsStarter?: boolean verbose?: boolean - v2?: boolean } export default async ({ @@ -57,7 +55,6 @@ export default async ({ directoryPath, withNextjsStarter = false, verbose = false, - v2 = false, }: CreateOptions) => { track("CREATE_CLI_CMA") @@ -142,7 +139,6 @@ export default async ({ abortController, spinner, verbose, - v2, }) } catch { return @@ -159,6 +155,7 @@ export default async ({ abortController, factBoxOptions, verbose, + processManager }) : "" @@ -195,7 +192,6 @@ export default async ({ nextjsDirectory, client, verbose, - v2, }) } catch (e: any) { if (isAbortError(e)) { @@ -257,14 +253,10 @@ export default async ({ await waitOn({ resources: ["http://localhost:9000/health"], }).then(async () => { - if (v2) { - return - } - open( inviteToken - ? `http://localhost:7001/invite?token=${inviteToken}&first_run=true` - : "http://localhost:7001" + ? `http://localhost:9000/app/invite?token=${inviteToken}&first_run=true` + : "http://localhost:9000/app" ) }) } @@ -324,7 +316,7 @@ function showSuccessMessage( message: boxen( chalk.green( // eslint-disable-next-line prettier/prettier - `Change to the \`${projectName}\` directory to explore your Medusa project.${EOL}${EOL}Start your Medusa app again with the following command:${EOL}${EOL}npx @medusajs/medusa-cli develop${EOL}${EOL}${inviteToken ? `After you start the Medusa app, you can set a password for your admin user with the URL ${getInviteUrl(inviteToken)}${EOL}${EOL}` : ""}${nextjsDirectory?.length ? `The Next.js Starter storefront was installed in the \`${nextjsDirectory}\` directory. Change to that directory and start it with the following command:${EOL}${EOL}npm run dev${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` + `Change to the \`${projectName}\` directory to explore your Medusa project.${EOL}${EOL}Start your Medusa app again with the following command:${EOL}${EOL}yarn dev${EOL}${EOL}${inviteToken ? `After you start the Medusa app, you can set a password for your admin user with the URL ${getInviteUrl(inviteToken)}${EOL}${EOL}` : ""}${nextjsDirectory?.length ? `The Next.js Starter storefront was installed in the \`${nextjsDirectory}\` directory. Change to that directory and start it with the following command:${EOL}${EOL}npm run dev${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` ), { titleAlignment: "center", diff --git a/packages/cli/create-medusa-app/src/index.ts b/packages/cli/create-medusa-app/src/index.ts index c0adc65024..eed6075d03 100644 --- a/packages/cli/create-medusa-app/src/index.ts +++ b/packages/cli/create-medusa-app/src/index.ts @@ -43,11 +43,6 @@ program "Show all logs of underlying commands. Useful for debugging.", false ) - .option( - "--v2", - "Install Medusa with the V2 feature flag enabled. WARNING: Medusa V2 is still in development and shouldn't be used in production.", - false - ) .parse() void create(program.opts()) diff --git a/packages/cli/create-medusa-app/src/utils/clone-repo.ts b/packages/cli/create-medusa-app/src/utils/clone-repo.ts index ddb3b5cd2a..0aa6d78025 100644 --- a/packages/cli/create-medusa-app/src/utils/clone-repo.ts +++ b/packages/cli/create-medusa-app/src/utils/clone-repo.ts @@ -10,7 +10,6 @@ type CloneRepoOptions = { repoUrl?: string abortController?: AbortController verbose?: boolean - v2?: boolean } const DEFAULT_REPO = "https://github.com/medusajs/medusa-starter-default" @@ -21,13 +20,10 @@ export default async function cloneRepo({ repoUrl, abortController, verbose = false, - v2 = false, }: CloneRepoOptions) { await execute( [ - `git clone ${repoUrl || DEFAULT_REPO}${ - v2 ? ` -b ${V2_BRANCH}` : "" - } ${directoryName}`, + `git clone ${repoUrl || DEFAULT_REPO} -b ${V2_BRANCH} ${directoryName}`, { signal: abortController?.signal, }, @@ -42,14 +38,12 @@ export async function runCloneRepo({ abortController, spinner, verbose = false, - v2 = false, }: { projectName: string repoUrl: string abortController: AbortController spinner: Ora verbose?: boolean - v2?: boolean }) { try { await cloneRepo({ @@ -57,7 +51,6 @@ export async function runCloneRepo({ repoUrl, abortController, verbose, - v2, }) deleteGitDirectory(projectName) diff --git a/packages/cli/create-medusa-app/src/utils/facts.ts b/packages/cli/create-medusa-app/src/utils/facts.ts index 3ca96c236a..fda62a0df6 100644 --- a/packages/cli/create-medusa-app/src/utils/facts.ts +++ b/packages/cli/create-medusa-app/src/utils/facts.ts @@ -14,7 +14,6 @@ export type FactBoxOptions = { } const facts = [ - "Plugins allow you to integrate third-party services for payment, fulfillment, notifications, and more.", "You can specify a product's availability in one or more sales channels.", "Payment and shipping options and providers can be configured per region.", "Tax-inclusive pricing allows you to set prices for products, shipping options, and more without having to worry about calculating taxes.", @@ -24,13 +23,16 @@ const facts = [ "Publishable-API Keys allow you to send requests to the backend within a scoped resource.", "You can create custom endpoints by creating a TypeScript file under the src/api directory.", "You can listen to events to perform asynchronous actions using Subscribers.", - "An entity represents a table in the database. You can create a table by creating a custom entity and migration.", + "A data model represents a table in the database. You can create a table by creating a custom data model and migration in a module.", "Medusa's store endpoint paths are prefixed by /store. The admin endpoints are prefixed by /admin.", "Medusa provides a JavaScript client and a React library that you can use to build a storefront or a custom admin.", - "Services are classes with methods related to an entity or functionality. You can create a custom service in a TypeScript file under src/services.", + "Modules hold your custom features and data models. You create them under the src/modules directory. Each module must have a service.", + "A service is a class with methods related to a functionality or a data model. You create a service in a module.", "Modules allow you to replace an entire functionality with your custom logic.", "The event bus module is responsible for triggering events and relaying them to subscribers.", "The cache module is responsible for caching data that requires heavy computation.", + "A workflow is a series of steps that are defined once and executed anywhere. Workflows are created under the src/workflows directory.", + "A workflow's steps can be retried or rolled back in case of an error." ] export const getFact = () => { diff --git a/packages/cli/create-medusa-app/src/utils/nextjs-utils.ts b/packages/cli/create-medusa-app/src/utils/nextjs-utils.ts index e6b2153199..1f63ad71e2 100644 --- a/packages/cli/create-medusa-app/src/utils/nextjs-utils.ts +++ b/packages/cli/create-medusa-app/src/utils/nextjs-utils.ts @@ -7,6 +7,7 @@ import path from "path" import { customAlphabet } from "nanoid" import { isAbortError } from "./create-abort-controller.js" import logMessage from "./log-message.js" +import ProcessManager from "./process-manager.js" const NEXTJS_REPO = "https://github.com/medusajs/nextjs-starter-medusa" @@ -28,6 +29,7 @@ type InstallOptions = { abortController?: AbortController factBoxOptions: FactBoxOptions verbose?: boolean + processManager: ProcessManager } export async function installNextjsStarter({ @@ -35,6 +37,7 @@ export async function installNextjsStarter({ abortController, factBoxOptions, verbose = false, + processManager }: InstallOptions): Promise { factBoxOptions.interval = displayFactBox({ ...factBoxOptions, @@ -56,19 +59,35 @@ export async function installNextjsStarter({ } try { + // TODO change back to use create-next-app once Next.js v2 changes land on the main branch await execute( [ - `npx create-next-app -e ${NEXTJS_REPO} ${nextjsDirectory}`, + `git clone ${NEXTJS_REPO} -b v2 ${nextjsDirectory}`, { signal: abortController?.signal, - env: { - ...process.env, - npm_config_yes: "yes", - }, + env: process.env, }, ], { verbose } ) + const execOptions = { + signal: abortController?.signal, + cwd: nextjsDirectory + } + await processManager.runProcess({ + process: async () => { + try { + await execute([`yarn`, execOptions], { verbose }) + } catch (e) { + // yarn isn't available + // use npm + await execute([`npm install`, execOptions], { + verbose, + }) + } + }, + ignoreERESOLVE: true, + }) } catch (e) { if (isAbortError(e)) { process.exit() @@ -80,6 +99,11 @@ export async function installNextjsStarter({ }) } + fs.rmSync(path.join(nextjsDirectory, ".git"), { + recursive: true, + force: true, + }) + fs.renameSync( path.join(nextjsDirectory, ".env.template"), path.join(nextjsDirectory, ".env.local") 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 ccd144d421..276d44bf5d 100644 --- a/packages/cli/create-medusa-app/src/utils/prepare-project.ts +++ b/packages/cli/create-medusa-app/src/utils/prepare-project.ts @@ -26,7 +26,6 @@ type PrepareOptions = { nextjsDirectory?: string client: Client | null verbose?: boolean - v2?: boolean } export default async ({ @@ -44,7 +43,6 @@ export default async ({ nextjsDirectory = "", client, verbose = false, - v2 = false, }: PrepareOptions) => { // initialize execution options const execOptions = { @@ -73,10 +71,7 @@ export default async ({ let inviteToken: string | undefined = undefined if (!skipDb) { - let env = `DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}${EOL}MEDUSA_ADMIN_ONBOARDING_TYPE=${onboardingType}${EOL}STORE_CORS=http://localhost:8000,http://localhost:7001` - if (v2) { - env += `${EOL}POSTGRES_URL=${dbConnectionString}` - } + let env = `DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}${EOL}MEDUSA_ADMIN_ONBOARDING_TYPE=${onboardingType}${EOL}STORE_CORS=http://localhost:8000,http://localhost:7001${EOL}POSTGRES_URL=${dbConnectionString}` if (nextjsDirectory) { env += `${EOL}MEDUSA_ADMIN_ONBOARDING_NEXTJS_DIRECTORY=${nextjsDirectory}` } @@ -154,7 +149,7 @@ export default async ({ await processManager.runProcess({ process: async () => { const proc = await execute( - ["npx @medusajs/medusa-cli@latest migrations run", npxOptions], + ["npx medusa migrations run", npxOptions], { verbose, needOutput: true } ) @@ -164,7 +159,7 @@ export default async ({ let errorOccurred = false try { const migrations = await client.query( - `SELECT * FROM "${v2 ? "mikro_orm_migrations" : "migrations"}"` + `SELECT * FROM "mikro_orm_migrations"` ) errorOccurred = migrations.rowCount == 0 } catch (e) { @@ -191,7 +186,7 @@ export default async ({ }) } - if (admin && !skipDb && migrations && !v2) { + if (admin && !skipDb && migrations) { // create admin user factBoxOptions.interval = displayFactBox({ ...factBoxOptions, @@ -202,7 +197,7 @@ export default async ({ process: async () => { const proc = await execute( [ - `npx @medusajs/medusa-cli@latest user -e ${admin.email} --invite`, + `npx medusa user -e ${admin.email} --invite`, npxOptions, ], { verbose, needOutput: true } @@ -223,70 +218,47 @@ export default async ({ } if (!skipDb && migrations) { - if (seed || !boilerplate) { - factBoxOptions.interval = displayFactBox({ - ...factBoxOptions, - title: "Seeding database...", - }) + // TODO for now we just seed the default data + // we should add onboarding seeding again if it makes + // since once we re-introduce the onboarding flow. + factBoxOptions.interval = displayFactBox({ + ...factBoxOptions, + title: "Seeding database...", + }) - // check if a seed file exists in the project - if (!fs.existsSync(path.join(directory, "data", "seed.json"))) { - spinner - ?.warn( - chalk.yellow( - "Seed file was not found in the project. Skipping seeding..." - ) + const seedScriptPath = path.join("dist", "helpers", "seed.js") + + // check if a seed file exists in the project + if (!fs.existsSync(path.join(directory, seedScriptPath))) { + spinner + ?.warn( + chalk.yellow( + "Seed file was not found in the project. Skipping seeding..." ) - .start() - return inviteToken - } - - await processManager.runProcess({ - process: async () => { - await execute( - [ - `npx @medusajs/medusa-cli@latest seed --seed-file=${path.join( - "data", - "seed.json" - )}`, - npxOptions, - ], - { verbose } - ) - }, - }) - - displayFactBox({ - ...factBoxOptions, - message: "Seeded database with demo data", - }) - } else if ( - fs.existsSync(path.join(directory, "data", "seed-onboarding.json")) - ) { - // seed the database with onboarding seed - factBoxOptions.interval = displayFactBox({ - ...factBoxOptions, - title: "Finish preparation...", - }) - - await processManager.runProcess({ - process: async () => { - await execute( - [ - `npx @medusajs/medusa-cli@latest seed --seed-file=${path.join( - "data", - "seed-onboarding.json" - )}`, - npxOptions, - ], - { verbose } - ) - }, - }) + ) + .start() + return inviteToken } - displayFactBox({ ...factBoxOptions, message: "Finished Preparation" }) + await processManager.runProcess({ + process: async () => { + await execute( + [ + `npx medusa exec ${seedScriptPath}`, + npxOptions, + ], + { verbose } + ) + }, + }) + + displayFactBox({ + ...factBoxOptions, + message: "Seeded database with demo data", + }) } + displayFactBox({ ...factBoxOptions, message: "Finished Preparation" }) + return inviteToken } diff --git a/packages/cli/create-medusa-app/src/utils/start-medusa.ts b/packages/cli/create-medusa-app/src/utils/start-medusa.ts index 6aa8af83ed..95189b5317 100644 --- a/packages/cli/create-medusa-app/src/utils/start-medusa.ts +++ b/packages/cli/create-medusa-app/src/utils/start-medusa.ts @@ -6,13 +6,11 @@ type StartOptions = { } export default ({ directory, abortController }: StartOptions) => { - const childProcess = exec(`npx @medusajs/medusa-cli@latest develop`, { + const childProcess = exec(`npm run dev`, { cwd: directory, signal: abortController?.signal, env: { ...process.env, - OPEN_BROWSER: "false", - npm_config_yes: "yes", }, })