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 parent
4264302f21
commit
d363da2b72
11 files changed
+373
-223
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-medusa-app": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
chore(create-medusa-app): Cleanup the main script for readability and maintanability
|
||||||
@@ -28,7 +28,8 @@
|
|||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"validator": "^13.9.0",
|
"validator": "^13.9.0",
|
||||||
"wait-on": "^7.0.1"
|
"wait-on": "^7.0.1",
|
||||||
|
"winston": "^3.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chalk": "^2.2.0",
|
"@types/chalk": "^2.2.0",
|
||||||
|
|||||||
@@ -1,33 +1,31 @@
|
|||||||
import inquirer from "inquirer"
|
import inquirer from "inquirer"
|
||||||
import slugifyType from "slugify"
|
import slugifyType from "slugify"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import pg from "pg"
|
import { getDbClientAndCredentials, runCreateDb } from "../utils/create-db.js"
|
||||||
import createDb from "../utils/create-db.js"
|
|
||||||
import postgresClient from "../utils/postgres-client.js"
|
|
||||||
import cloneRepo from "../utils/clone-repo.js"
|
|
||||||
import prepareProject from "../utils/prepare-project.js"
|
import prepareProject from "../utils/prepare-project.js"
|
||||||
import startMedusa from "../utils/start-medusa.js"
|
import startMedusa from "../utils/start-medusa.js"
|
||||||
import open from "open"
|
import open from "open"
|
||||||
import waitOn from "wait-on"
|
import waitOn from "wait-on"
|
||||||
import formatConnectionString from "../utils/format-connection-string.js"
|
import ora, { Ora } from "ora"
|
||||||
import ora from "ora"
|
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import { nanoid } from "nanoid"
|
|
||||||
import isEmailImported from "validator/lib/isEmail.js"
|
import isEmailImported from "validator/lib/isEmail.js"
|
||||||
import logMessage from "../utils/log-message.js"
|
import logMessage from "../utils/log-message.js"
|
||||||
import createAbortController, {
|
import createAbortController, {
|
||||||
isAbortError,
|
isAbortError,
|
||||||
} from "../utils/create-abort-controller.js"
|
} from "../utils/create-abort-controller.js"
|
||||||
import { track } from "medusa-telemetry"
|
import { track } from "medusa-telemetry"
|
||||||
import { createFactBox, resetFactBox } from "../utils/facts.js"
|
|
||||||
import boxen from "boxen"
|
import boxen from "boxen"
|
||||||
import { emojify } from "node-emoji"
|
import { emojify } from "node-emoji"
|
||||||
import ProcessManager from "../utils/process-manager.js"
|
import ProcessManager from "../utils/process-manager.js"
|
||||||
|
import { nanoid } from "nanoid"
|
||||||
|
import { displayFactBox, FactBoxOptions } from "../utils/facts.js"
|
||||||
|
import { EOL } from "os"
|
||||||
|
import { runCloneRepo } from "../utils/clone-repo.js"
|
||||||
|
|
||||||
const slugify = slugifyType.default
|
const slugify = slugifyType.default
|
||||||
const isEmail = isEmailImported.default
|
const isEmail = isEmailImported.default
|
||||||
|
|
||||||
type CreateOptions = {
|
export type CreateOptions = {
|
||||||
repoUrl?: string
|
repoUrl?: string
|
||||||
seed?: boolean
|
seed?: boolean
|
||||||
// commander passed --no-boilerplate as boilerplate
|
// commander passed --no-boilerplate as boilerplate
|
||||||
@@ -42,90 +40,53 @@ export default async ({ repoUrl = "", seed, boilerplate }: CreateOptions) => {
|
|||||||
if (seed) {
|
if (seed) {
|
||||||
track("SEED_SELECTED", { seed })
|
track("SEED_SELECTED", { seed })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const spinner: Ora = ora()
|
||||||
const processManager = new ProcessManager()
|
const processManager = new ProcessManager()
|
||||||
const abortController = createAbortController(processManager)
|
const abortController = createAbortController(processManager)
|
||||||
|
const factBoxOptions: FactBoxOptions = {
|
||||||
const { projectName } = await inquirer.prompt([
|
interval: null,
|
||||||
{
|
spinner,
|
||||||
type: "input",
|
processManager,
|
||||||
name: "projectName",
|
message: "",
|
||||||
message: "What's the name of your project?",
|
title: "",
|
||||||
default: "my-medusa-store",
|
|
||||||
filter: (input) => {
|
|
||||||
return slugify(input)
|
|
||||||
},
|
|
||||||
validate: (input) => {
|
|
||||||
if (!input.length) {
|
|
||||||
return "Please enter a project name"
|
|
||||||
}
|
}
|
||||||
return fs.existsSync(input) && fs.lstatSync(input).isDirectory()
|
const dbName = `medusa-${nanoid(4)}`
|
||||||
? "A directory already exists with the same name. Please enter a different project name."
|
let isProjectCreated = false
|
||||||
: true
|
let printedMessage = false
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
let client: pg.Client | undefined
|
processManager.onTerminated(async () => {
|
||||||
let dbConnectionString = ""
|
spinner.stop()
|
||||||
let postgresUsername = "postgres"
|
if (client) {
|
||||||
let postgresPassword = ""
|
await client.end()
|
||||||
|
}
|
||||||
|
|
||||||
// try to log in with default db username and password
|
// the SIGINT event is triggered twice once the backend runs
|
||||||
try {
|
// this ensures that the message isn't printed twice to the user
|
||||||
client = await postgresClient({
|
if (!printedMessage && isProjectCreated) {
|
||||||
user: postgresUsername,
|
printedMessage = true
|
||||||
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({
|
logMessage({
|
||||||
message:
|
message: boxen(
|
||||||
"Couldn't connect to PostgreSQL. Make sure you have PostgreSQL installed and the credentials you provided are correct.\n\n" +
|
chalk.green(
|
||||||
"You can learn how to install PostgreSQL here: https://docs.medusajs.com/development/backend/prepare-environment#postgresql",
|
`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}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`
|
||||||
type: "error",
|
),
|
||||||
|
{
|
||||||
|
titleAlignment: "center",
|
||||||
|
textAlignment: "center",
|
||||||
|
padding: 1,
|
||||||
|
margin: 1,
|
||||||
|
float: "center",
|
||||||
|
}
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const { adminEmail } = await inquirer.prompt([
|
return
|
||||||
{
|
})
|
||||||
type: "input",
|
|
||||||
name: "adminEmail",
|
const projectName = await askForProjectName()
|
||||||
message: "Enter an email for your admin dashboard user",
|
const { client, dbConnectionString } = await getDbClientAndCredentials(dbName)
|
||||||
default: !seed && boilerplate ? "admin@medusa-test.com" : undefined,
|
const adminEmail = await askForAdminEmail(seed, boilerplate)
|
||||||
validate: (input) => {
|
|
||||||
return typeof input === "string" && input.length > 0 && isEmail(input)
|
|
||||||
? true
|
|
||||||
: "Please enter a valid email"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
logMessage({
|
logMessage({
|
||||||
message: `${emojify(
|
message: `${emojify(
|
||||||
@@ -133,74 +94,36 @@ export default async ({ repoUrl = "", seed, boilerplate }: CreateOptions) => {
|
|||||||
)} Starting project setup, this may take a few minutes.`,
|
)} Starting project setup, this may take a few minutes.`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const spinner = ora().start()
|
spinner.start()
|
||||||
|
|
||||||
processManager.onTerminated(() => spinner.stop())
|
factBoxOptions.interval = displayFactBox({
|
||||||
|
...factBoxOptions,
|
||||||
|
title: "Setting up project...",
|
||||||
|
})
|
||||||
|
|
||||||
let interval: NodeJS.Timer | null = createFactBox(
|
|
||||||
spinner,
|
|
||||||
"Setting up project...",
|
|
||||||
processManager
|
|
||||||
)
|
|
||||||
|
|
||||||
// clone repository
|
|
||||||
try {
|
try {
|
||||||
await cloneRepo({
|
await runCloneRepo({
|
||||||
directoryName: projectName,
|
projectName,
|
||||||
repoUrl,
|
repoUrl,
|
||||||
abortController,
|
abortController,
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
if (isAbortError(e)) {
|
|
||||||
process.exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner.stop()
|
|
||||||
logMessage({
|
|
||||||
message: `An error occurred while setting up your project: ${e}`,
|
|
||||||
type: "error",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
interval = resetFactBox(
|
|
||||||
interval,
|
|
||||||
spinner,
|
spinner,
|
||||||
"Created project directory",
|
|
||||||
processManager,
|
|
||||||
"Creating database..."
|
|
||||||
)
|
|
||||||
|
|
||||||
if (client) {
|
|
||||||
const dbName = `medusa-${nanoid(4)}`
|
|
||||||
// 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",
|
|
||||||
})
|
})
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// format connection string
|
factBoxOptions.interval = displayFactBox({
|
||||||
dbConnectionString = formatConnectionString({
|
...factBoxOptions,
|
||||||
user: postgresUsername,
|
message: "Created project directory",
|
||||||
password: postgresPassword,
|
title: "Creating database...",
|
||||||
host: client.host,
|
|
||||||
db: dbName,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
resetFactBox(
|
await runCreateDb({ client, dbName, spinner })
|
||||||
interval,
|
|
||||||
spinner,
|
factBoxOptions.interval = displayFactBox({
|
||||||
`Database ${dbName} created`,
|
...factBoxOptions,
|
||||||
processManager
|
message: `Database ${dbName} created`,
|
||||||
)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// prepare project
|
// prepare project
|
||||||
let inviteToken: string | undefined = undefined
|
let inviteToken: string | undefined = undefined
|
||||||
@@ -227,13 +150,15 @@ export default async ({ repoUrl = "", seed, boilerplate }: CreateOptions) => {
|
|||||||
message: `An error occurred while preparing project: ${e}`,
|
message: `An error occurred while preparing project: ${e}`,
|
||||||
type: "error",
|
type: "error",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
} finally {
|
||||||
|
// close db connection
|
||||||
|
await client?.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.succeed(chalk.green("Project Prepared"))
|
spinner.succeed(chalk.green("Project Prepared"))
|
||||||
|
|
||||||
// close db connection
|
|
||||||
await client?.end()
|
|
||||||
|
|
||||||
// start backend
|
// start backend
|
||||||
logMessage({
|
logMessage({
|
||||||
message: "Starting Medusa...",
|
message: "Starting Medusa...",
|
||||||
@@ -253,31 +178,11 @@ export default async ({ repoUrl = "", seed, boilerplate }: CreateOptions) => {
|
|||||||
message: `An error occurred while starting Medusa`,
|
message: `An error occurred while starting Medusa`,
|
||||||
type: "error",
|
type: "error",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// the SIGINT event is triggered twice once the backend runs
|
isProjectCreated = true
|
||||||
// this ensures that the message isn't printed twice to the user
|
|
||||||
let printedMessage = false
|
|
||||||
|
|
||||||
processManager.onTerminated(() => {
|
|
||||||
if (!printedMessage) {
|
|
||||||
printedMessage = true
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.green(
|
|
||||||
`Change to the \`${projectName}\` directory to explore your Medusa project.\n\nStart your Medusa app again with the following command:\n\nnpx @medusajs/medusa-cli develop\n\nCheck out the Medusa documentation to start your development:\n\nhttps://docs.medusajs.com/\n\nStar us on GitHub if you like what we're building:\n\nhttps://github.com/medusajs/medusa/stargazers`
|
|
||||||
),
|
|
||||||
{
|
|
||||||
titleAlignment: "center",
|
|
||||||
textAlignment: "center",
|
|
||||||
padding: 1,
|
|
||||||
margin: 1,
|
|
||||||
float: "center",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await waitOn({
|
await waitOn({
|
||||||
resources: ["http://localhost:9000/health"],
|
resources: ["http://localhost:9000/health"],
|
||||||
@@ -289,3 +194,47 @@ export default async ({ repoUrl = "", seed, boilerplate }: CreateOptions) => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function askForProjectName(): Promise<string> {
|
||||||
|
const { projectName } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "projectName",
|
||||||
|
message: "What's the name of your project?",
|
||||||
|
default: "my-medusa-store",
|
||||||
|
filter: (input) => {
|
||||||
|
return slugify(input)
|
||||||
|
},
|
||||||
|
validate: (input) => {
|
||||||
|
if (!input.length) {
|
||||||
|
return "Please enter a project name"
|
||||||
|
}
|
||||||
|
return fs.existsSync(input) && fs.lstatSync(input).isDirectory()
|
||||||
|
? "A directory already exists with the same name. Please enter a different project name."
|
||||||
|
: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
return projectName
|
||||||
|
}
|
||||||
|
|
||||||
|
async function askForAdminEmail(
|
||||||
|
seed?: boolean,
|
||||||
|
boilerplate?: boolean
|
||||||
|
): Promise<string> {
|
||||||
|
const { adminEmail } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "adminEmail",
|
||||||
|
message: "Enter an email for your admin dashboard user",
|
||||||
|
default: !seed && boilerplate ? "admin@medusa-test.com" : undefined,
|
||||||
|
validate: (input) => {
|
||||||
|
return typeof input === "string" && input.length > 0 && isEmail(input)
|
||||||
|
? true
|
||||||
|
: "Please enter a valid email"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return adminEmail
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import promiseExec from "./promise-exec.js"
|
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 = {
|
type CloneRepoOptions = {
|
||||||
directoryName?: string
|
directoryName?: string
|
||||||
@@ -9,12 +12,42 @@ type CloneRepoOptions = {
|
|||||||
const DEFAULT_REPO =
|
const DEFAULT_REPO =
|
||||||
"https://github.com/medusajs/medusa-starter-default -b feat/onboarding"
|
"https://github.com/medusajs/medusa-starter-default -b feat/onboarding"
|
||||||
|
|
||||||
export default async ({
|
export default async function cloneRepo({
|
||||||
directoryName = "",
|
directoryName = "",
|
||||||
repoUrl,
|
repoUrl,
|
||||||
abortController,
|
abortController,
|
||||||
}: CloneRepoOptions) => {
|
}: CloneRepoOptions) {
|
||||||
await promiseExec(`git clone ${repoUrl || DEFAULT_REPO} ${directoryName}`, {
|
await promiseExec(`git clone ${repoUrl || DEFAULT_REPO} ${directoryName}`, {
|
||||||
signal: abortController?.signal,
|
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 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 = {
|
type CreateDbOptions = {
|
||||||
client: pg.Client
|
client: pg.Client
|
||||||
db: string
|
db: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async ({ client, db }: CreateDbOptions) => {
|
export default async function createDb({ client, db }: CreateDbOptions) {
|
||||||
await client.query(`CREATE DATABASE "${db}"`)
|
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 { emojify } from "node-emoji"
|
||||||
import ProcessManager from "./process-manager.js"
|
import ProcessManager from "./process-manager.js"
|
||||||
|
|
||||||
|
export type FactBoxOptions = {
|
||||||
|
interval: NodeJS.Timer | null
|
||||||
|
spinner: Ora
|
||||||
|
processManager: ProcessManager
|
||||||
|
message?: string
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
const facts = [
|
const facts = [
|
||||||
"Plugins allow you to integrate third-party services for payment, fulfillment, notifications, and more.",
|
"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.",
|
"You can specify a product's availability in one or more sales channels.",
|
||||||
@@ -79,3 +87,17 @@ export const resetFactBox = (
|
|||||||
|
|
||||||
return newInterval
|
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 chalk from "chalk"
|
||||||
import { program } from "commander"
|
import { program } from "commander"
|
||||||
|
import { logger } from "./logger.js"
|
||||||
|
|
||||||
type LogOptions = {
|
type LogOptions = {
|
||||||
message: string
|
message: string
|
||||||
@@ -9,13 +10,13 @@ type LogOptions = {
|
|||||||
export default ({ message, type = "info" }: LogOptions) => {
|
export default ({ message, type = "info" }: LogOptions) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "info":
|
case "info":
|
||||||
console.log(chalk.white(message))
|
logger.info(chalk.white(message))
|
||||||
break
|
break
|
||||||
case "success":
|
case "success":
|
||||||
console.log(chalk.green(message))
|
logger.info(chalk.green(message))
|
||||||
break
|
break
|
||||||
case "warning":
|
case "warning":
|
||||||
console.log(chalk.yellow(message))
|
logger.warning(chalk.yellow(message))
|
||||||
break
|
break
|
||||||
case "error":
|
case "error":
|
||||||
program.error(chalk.bold.red(message))
|
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 { Ora } from "ora"
|
||||||
import promiseExec from "./promise-exec.js"
|
import promiseExec from "./promise-exec.js"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { createFactBox, resetFactBox } from "./facts.js"
|
import { displayFactBox, FactBoxOptions } from "./facts.js"
|
||||||
import { clearProject } from "@medusajs/utils"
|
import { clearProject } from "@medusajs/utils"
|
||||||
import ProcessManager from "./process-manager.js"
|
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
|
// initialize the invite token to return
|
||||||
let inviteToken: string | undefined = undefined
|
let inviteToken: string | undefined = undefined
|
||||||
|
|
||||||
@@ -54,11 +62,12 @@ export default async ({
|
|||||||
`DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}`
|
`DATABASE_TYPE=postgres${EOL}DATABASE_URL=${dbConnectionString}`
|
||||||
)
|
)
|
||||||
|
|
||||||
let interval: NodeJS.Timer | null = createFactBox(
|
factBoxOptions.interval = displayFactBox({
|
||||||
|
...factBoxOptions,
|
||||||
spinner,
|
spinner,
|
||||||
"Installing dependencies...",
|
title: "Installing dependencies...",
|
||||||
processManager
|
processManager,
|
||||||
)
|
})
|
||||||
|
|
||||||
await processManager.runProcess({
|
await processManager.runProcess({
|
||||||
process: async () => {
|
process: async () => {
|
||||||
@@ -73,30 +82,29 @@ export default async ({
|
|||||||
ignoreERESOLVE: true,
|
ignoreERESOLVE: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
interval = resetFactBox(
|
factBoxOptions.interval = displayFactBox({
|
||||||
interval,
|
...factBoxOptions,
|
||||||
spinner,
|
message: "Installed Dependencies",
|
||||||
"Installed Dependencies",
|
})
|
||||||
processManager
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!boilerplate) {
|
if (!boilerplate) {
|
||||||
interval = createFactBox(
|
factBoxOptions.interval = displayFactBox({
|
||||||
spinner,
|
...factBoxOptions,
|
||||||
"Preparing Project Directory...",
|
title: "Preparing Project Directory...",
|
||||||
processManager
|
})
|
||||||
)
|
|
||||||
// delete files and directories related to onboarding
|
// delete files and directories related to onboarding
|
||||||
clearProject(directory)
|
clearProject(directory)
|
||||||
interval = resetFactBox(
|
displayFactBox({
|
||||||
interval,
|
...factBoxOptions,
|
||||||
spinner,
|
message: "Prepared Project Directory",
|
||||||
"Prepared Project Directory",
|
})
|
||||||
processManager
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interval = createFactBox(spinner, "Building Project...", processManager)
|
factBoxOptions.interval = displayFactBox({
|
||||||
|
...factBoxOptions,
|
||||||
|
title: "Building Project...",
|
||||||
|
})
|
||||||
|
|
||||||
await processManager.runProcess({
|
await processManager.runProcess({
|
||||||
process: async () => {
|
process: async () => {
|
||||||
try {
|
try {
|
||||||
@@ -110,9 +118,11 @@ export default async ({
|
|||||||
ignoreERESOLVE: true,
|
ignoreERESOLVE: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
interval = resetFactBox(interval, spinner, "Project Built", processManager)
|
displayFactBox({ ...factBoxOptions, message: "Project Built" })
|
||||||
|
factBoxOptions.interval = displayFactBox({
|
||||||
interval = createFactBox(spinner, "Running Migrations...", processManager)
|
...factBoxOptions,
|
||||||
|
title: "Running Migrations...",
|
||||||
|
})
|
||||||
|
|
||||||
// run migrations
|
// run migrations
|
||||||
await processManager.runProcess({
|
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) {
|
if (admin) {
|
||||||
// create admin user
|
// create admin user
|
||||||
interval = createFactBox(
|
factBoxOptions.interval = displayFactBox({
|
||||||
spinner,
|
...factBoxOptions,
|
||||||
"Creating an admin user...",
|
title: "Creating an admin user...",
|
||||||
processManager
|
})
|
||||||
)
|
|
||||||
|
|
||||||
await processManager.runProcess({
|
await processManager.runProcess({
|
||||||
process: async () => {
|
process: async () => {
|
||||||
@@ -155,16 +167,17 @@ export default async ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
interval = resetFactBox(
|
factBoxOptions.interval = displayFactBox({
|
||||||
interval,
|
...factBoxOptions,
|
||||||
spinner,
|
message: "Created admin user",
|
||||||
"Created admin user",
|
})
|
||||||
processManager
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seed || !boilerplate) {
|
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
|
// check if a seed file exists in the project
|
||||||
if (!fs.existsSync(path.join(directory, "data", "seed.json"))) {
|
if (!fs.existsSync(path.join(directory, "data", "seed.json"))) {
|
||||||
@@ -189,17 +202,19 @@ export default async ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
resetFactBox(
|
|
||||||
interval,
|
displayFactBox({
|
||||||
spinner,
|
...factBoxOptions,
|
||||||
"Seeded database with demo data",
|
message: "Seeded database with demo data",
|
||||||
processManager
|
})
|
||||||
)
|
|
||||||
} else if (
|
} else if (
|
||||||
fs.existsSync(path.join(directory, "data", "seed-onboarding.json"))
|
fs.existsSync(path.join(directory, "data", "seed-onboarding.json"))
|
||||||
) {
|
) {
|
||||||
// seed the database with onboarding seed
|
// seed the database with onboarding seed
|
||||||
interval = createFactBox(spinner, "Finish preparation...", processManager)
|
factBoxOptions.interval = displayFactBox({
|
||||||
|
...factBoxOptions,
|
||||||
|
title: "Finish preparation...",
|
||||||
|
})
|
||||||
|
|
||||||
await processManager.runProcess({
|
await processManager.runProcess({
|
||||||
process: async () => {
|
process: async () => {
|
||||||
@@ -212,7 +227,8 @@ export default async ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
resetFactBox(interval, spinner, "Finished Preparation", processManager)
|
|
||||||
|
displayFactBox({ ...factBoxOptions, message: "Finished Preparation" })
|
||||||
}
|
}
|
||||||
|
|
||||||
return inviteToken
|
return inviteToken
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default class ProcessManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onTerminated(fn: Function) {
|
onTerminated(fn: () => Promise<void> | void) {
|
||||||
process.on("SIGTERM", () => fn())
|
process.on("SIGTERM", () => fn())
|
||||||
process.on("SIGINT", () => fn())
|
process.on("SIGINT", () => fn())
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ export default class ProcessManager {
|
|||||||
let processError = false
|
let processError = false
|
||||||
let retries = 0
|
let retries = 0
|
||||||
do {
|
do {
|
||||||
retries++
|
++retries
|
||||||
try {
|
try {
|
||||||
await process()
|
await process()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -17878,6 +17878,7 @@ __metadata:
|
|||||||
uuid: ^9.0.0
|
uuid: ^9.0.0
|
||||||
validator: ^13.9.0
|
validator: ^13.9.0
|
||||||
wait-on: ^7.0.1
|
wait-on: ^7.0.1
|
||||||
|
winston: ^3.9.0
|
||||||
bin:
|
bin:
|
||||||
create-medusa-app: dist/index.js
|
create-medusa-app: dist/index.js
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
@@ -43151,6 +43152,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"winston@npm:^3.9.0":
|
||||||
|
version: 3.9.0
|
||||||
|
resolution: "winston@npm:3.9.0"
|
||||||
|
dependencies:
|
||||||
|
"@colors/colors": 1.5.0
|
||||||
|
"@dabh/diagnostics": ^2.0.2
|
||||||
|
async: ^3.2.3
|
||||||
|
is-stream: ^2.0.0
|
||||||
|
logform: ^2.4.0
|
||||||
|
one-time: ^1.0.0
|
||||||
|
readable-stream: ^3.4.0
|
||||||
|
safe-stable-stringify: ^2.3.1
|
||||||
|
stack-trace: 0.0.x
|
||||||
|
triple-beam: ^1.3.0
|
||||||
|
winston-transport: ^4.5.0
|
||||||
|
checksum: e789b0c57bc3b0173c6fbd7caa4d60816a1a45cd796e5643e1bea65161d44c82a139b69f6ffc738a2919d80b5afc6df46c01947972173fc847ebcc668285fab3
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3":
|
"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3":
|
||||||
version: 1.2.3
|
version: 1.2.3
|
||||||
resolution: "word-wrap@npm:1.2.3"
|
resolution: "word-wrap@npm:1.2.3"
|
||||||
|
|||||||
Reference in new issue
Block a user