chore(create-medusa-app): Cleanup the main script for readability and maintanability (#4369)
* chore(create-medusa-app): Cleanup the main script for readability and maintanability * update types * cleanup * Create polite-queens-kiss.md
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import promiseExec from "./promise-exec.js"
|
||||
import { Ora } from "ora"
|
||||
import { isAbortError } from "./create-abort-controller.js"
|
||||
import logMessage from "./log-message.js"
|
||||
|
||||
type CloneRepoOptions = {
|
||||
directoryName?: string
|
||||
@@ -9,12 +12,42 @@ type CloneRepoOptions = {
|
||||
const DEFAULT_REPO =
|
||||
"https://github.com/medusajs/medusa-starter-default -b feat/onboarding"
|
||||
|
||||
export default async ({
|
||||
export default async function cloneRepo({
|
||||
directoryName = "",
|
||||
repoUrl,
|
||||
abortController,
|
||||
}: CloneRepoOptions) => {
|
||||
}: CloneRepoOptions) {
|
||||
await promiseExec(`git clone ${repoUrl || DEFAULT_REPO} ${directoryName}`, {
|
||||
signal: abortController?.signal,
|
||||
})
|
||||
}
|
||||
|
||||
export async function runCloneRepo({
|
||||
projectName,
|
||||
repoUrl,
|
||||
abortController,
|
||||
spinner,
|
||||
}: {
|
||||
projectName: string
|
||||
repoUrl: string
|
||||
abortController: AbortController
|
||||
spinner: Ora
|
||||
}) {
|
||||
try {
|
||||
await cloneRepo({
|
||||
directoryName: projectName,
|
||||
repoUrl,
|
||||
abortController,
|
||||
})
|
||||
} catch (e) {
|
||||
if (isAbortError(e)) {
|
||||
process.exit()
|
||||
}
|
||||
|
||||
spinner.stop()
|
||||
logMessage({
|
||||
message: `An error occurred while setting up your project: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,103 @@
|
||||
import pg from "pg"
|
||||
import postgresClient from "./postgres-client.js"
|
||||
import inquirer from "inquirer"
|
||||
import logMessage from "./log-message.js"
|
||||
import formatConnectionString from "./format-connection-string.js"
|
||||
import { Ora } from "ora"
|
||||
|
||||
type CreateDbOptions = {
|
||||
client: pg.Client
|
||||
db: string
|
||||
}
|
||||
|
||||
export default async ({ client, db }: CreateDbOptions) => {
|
||||
export default async function createDb({ client, db }: CreateDbOptions) {
|
||||
await client.query(`CREATE DATABASE "${db}"`)
|
||||
}
|
||||
|
||||
export async function runCreateDb({
|
||||
client,
|
||||
dbName,
|
||||
spinner,
|
||||
}: {
|
||||
client: pg.Client
|
||||
dbName: string
|
||||
spinner: Ora
|
||||
}) {
|
||||
// create postgres database
|
||||
try {
|
||||
await createDb({
|
||||
client,
|
||||
db: dbName,
|
||||
})
|
||||
} catch (e) {
|
||||
spinner.stop()
|
||||
logMessage({
|
||||
message: `An error occurred while trying to create your database: ${e}`,
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDbClientAndCredentials(dbName: string): Promise<{
|
||||
client: pg.Client
|
||||
dbConnectionString: string
|
||||
}> {
|
||||
let client!: pg.Client
|
||||
let postgresUsername = "postgres"
|
||||
let postgresPassword = ""
|
||||
|
||||
try {
|
||||
client = await postgresClient({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
})
|
||||
} catch (e) {
|
||||
// ask for the user's postgres credentials
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "postgresUsername",
|
||||
message: "Enter your Postgres username",
|
||||
default: "postgres",
|
||||
validate: (input) => {
|
||||
return typeof input === "string" && input.length > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "password",
|
||||
name: "postgresPassword",
|
||||
message: "Enter your Postgres password",
|
||||
},
|
||||
])
|
||||
|
||||
postgresUsername = answers.postgresUsername
|
||||
postgresPassword = answers.postgresPassword
|
||||
|
||||
try {
|
||||
client = await postgresClient({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
})
|
||||
} catch (e) {
|
||||
logMessage({
|
||||
message:
|
||||
"Couldn't connect to PostgreSQL. Make sure you have PostgreSQL installed and the credentials you provided are correct.${EOL}${EOL}" +
|
||||
"You can learn how to install PostgreSQL here: https://docs.medusajs.com/development/backend/prepare-environment#postgresql",
|
||||
type: "error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// format connection string
|
||||
const dbConnectionString = formatConnectionString({
|
||||
user: postgresUsername,
|
||||
password: postgresPassword,
|
||||
host: client!.host,
|
||||
db: dbName,
|
||||
})
|
||||
|
||||
return {
|
||||
client,
|
||||
dbConnectionString,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,14 @@ import { Ora } from "ora"
|
||||
import { emojify } from "node-emoji"
|
||||
import ProcessManager from "./process-manager.js"
|
||||
|
||||
export type FactBoxOptions = {
|
||||
interval: NodeJS.Timer | null
|
||||
spinner: Ora
|
||||
processManager: ProcessManager
|
||||
message?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
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.",
|
||||
@@ -79,3 +87,17 @@ export const resetFactBox = (
|
||||
|
||||
return newInterval
|
||||
}
|
||||
|
||||
export function displayFactBox({
|
||||
interval,
|
||||
spinner,
|
||||
processManager,
|
||||
title = "",
|
||||
message = "",
|
||||
}: FactBoxOptions): NodeJS.Timer | null {
|
||||
if (!message) {
|
||||
return createFactBox(spinner, title, processManager)
|
||||
}
|
||||
|
||||
return resetFactBox(interval, spinner, message, processManager, title)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import chalk from "chalk"
|
||||
import { program } from "commander"
|
||||
import { logger } from "./logger.js"
|
||||
|
||||
type LogOptions = {
|
||||
message: string
|
||||
@@ -9,13 +10,13 @@ type LogOptions = {
|
||||
export default ({ message, type = "info" }: LogOptions) => {
|
||||
switch (type) {
|
||||
case "info":
|
||||
console.log(chalk.white(message))
|
||||
logger.info(chalk.white(message))
|
||||
break
|
||||
case "success":
|
||||
console.log(chalk.green(message))
|
||||
logger.info(chalk.green(message))
|
||||
break
|
||||
case "warning":
|
||||
console.log(chalk.yellow(message))
|
||||
logger.warning(chalk.yellow(message))
|
||||
break
|
||||
case "error":
|
||||
program.error(chalk.bold.red(message))
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import winston from "winston"
|
||||
|
||||
const consoleTransport = new winston.transports.Console({
|
||||
format: winston.format.printf((log) => log.message),
|
||||
})
|
||||
const options = {
|
||||
transports: [consoleTransport],
|
||||
}
|
||||
|
||||
export const logger = winston.createLogger(options)
|
||||
@@ -4,7 +4,7 @@ import path from "path"
|
||||
import { Ora } from "ora"
|
||||
import promiseExec from "./promise-exec.js"
|
||||
import { EOL } from "os"
|
||||
import { createFactBox, resetFactBox } from "./facts.js"
|
||||
import { displayFactBox, FactBoxOptions } from "./facts.js"
|
||||
import { clearProject } from "@medusajs/utils"
|
||||
import ProcessManager from "./process-manager.js"
|
||||
|
||||
@@ -45,6 +45,14 @@ export default async ({
|
||||
},
|
||||
}
|
||||
|
||||
const factBoxOptions: FactBoxOptions = {
|
||||
interval: null,
|
||||
spinner,
|
||||
processManager,
|
||||
message: "",
|
||||
title: "",
|
||||
}
|
||||
|
||||
// initialize the invite token to return
|
||||
let inviteToken: string | undefined = undefined
|
||||
|
||||
@@ -54,11 +62,12 @@ export default async ({
|
||||
`DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}`
|
||||
)
|
||||
|
||||
let interval: NodeJS.Timer | null = createFactBox(
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
spinner,
|
||||
"Installing dependencies...",
|
||||
processManager
|
||||
)
|
||||
title: "Installing dependencies...",
|
||||
processManager,
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
@@ -73,30 +82,29 @@ export default async ({
|
||||
ignoreERESOLVE: true,
|
||||
})
|
||||
|
||||
interval = resetFactBox(
|
||||
interval,
|
||||
spinner,
|
||||
"Installed Dependencies",
|
||||
processManager
|
||||
)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Installed Dependencies",
|
||||
})
|
||||
|
||||
if (!boilerplate) {
|
||||
interval = createFactBox(
|
||||
spinner,
|
||||
"Preparing Project Directory...",
|
||||
processManager
|
||||
)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Preparing Project Directory...",
|
||||
})
|
||||
// delete files and directories related to onboarding
|
||||
clearProject(directory)
|
||||
interval = resetFactBox(
|
||||
interval,
|
||||
spinner,
|
||||
"Prepared Project Directory",
|
||||
processManager
|
||||
)
|
||||
displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Prepared Project Directory",
|
||||
})
|
||||
}
|
||||
|
||||
interval = createFactBox(spinner, "Building Project...", processManager)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Building Project...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
try {
|
||||
@@ -110,9 +118,11 @@ export default async ({
|
||||
ignoreERESOLVE: true,
|
||||
})
|
||||
|
||||
interval = resetFactBox(interval, spinner, "Project Built", processManager)
|
||||
|
||||
interval = createFactBox(spinner, "Running Migrations...", processManager)
|
||||
displayFactBox({ ...factBoxOptions, message: "Project Built" })
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Running Migrations...",
|
||||
})
|
||||
|
||||
// run migrations
|
||||
await processManager.runProcess({
|
||||
@@ -133,15 +143,17 @@ export default async ({
|
||||
},
|
||||
})
|
||||
|
||||
interval = resetFactBox(interval, spinner, "Ran Migrations", processManager)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Ran Migrations",
|
||||
})
|
||||
|
||||
if (admin) {
|
||||
// create admin user
|
||||
interval = createFactBox(
|
||||
spinner,
|
||||
"Creating an admin user...",
|
||||
processManager
|
||||
)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Creating an admin user...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
@@ -155,16 +167,17 @@ export default async ({
|
||||
},
|
||||
})
|
||||
|
||||
interval = resetFactBox(
|
||||
interval,
|
||||
spinner,
|
||||
"Created admin user",
|
||||
processManager
|
||||
)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
message: "Created admin user",
|
||||
})
|
||||
}
|
||||
|
||||
if (seed || !boilerplate) {
|
||||
interval = createFactBox(spinner, "Seeding database...", processManager)
|
||||
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"))) {
|
||||
@@ -189,17 +202,19 @@ export default async ({
|
||||
)
|
||||
},
|
||||
})
|
||||
resetFactBox(
|
||||
interval,
|
||||
spinner,
|
||||
"Seeded database with demo data",
|
||||
processManager
|
||||
)
|
||||
|
||||
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
|
||||
interval = createFactBox(spinner, "Finish preparation...", processManager)
|
||||
factBoxOptions.interval = displayFactBox({
|
||||
...factBoxOptions,
|
||||
title: "Finish preparation...",
|
||||
})
|
||||
|
||||
await processManager.runProcess({
|
||||
process: async () => {
|
||||
@@ -212,7 +227,8 @@ export default async ({
|
||||
)
|
||||
},
|
||||
})
|
||||
resetFactBox(interval, spinner, "Finished Preparation", processManager)
|
||||
|
||||
displayFactBox({ ...factBoxOptions, message: "Finished Preparation" })
|
||||
}
|
||||
|
||||
return inviteToken
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class ProcessManager {
|
||||
})
|
||||
}
|
||||
|
||||
onTerminated(fn: Function) {
|
||||
onTerminated(fn: () => Promise<void> | void) {
|
||||
process.on("SIGTERM", () => fn())
|
||||
process.on("SIGINT", () => fn())
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export default class ProcessManager {
|
||||
let processError = false
|
||||
let retries = 0
|
||||
do {
|
||||
retries++
|
||||
++retries
|
||||
try {
|
||||
await process()
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user