const path = require(`path`) const resolveCwd = require(`resolve-cwd`) const yargs = require(`yargs`) const existsSync = require(`fs-exists-cached`).sync const { getLocalMedusaVersion } = require(`./util/version`) const { didYouMean } = require(`./did-you-mean`) const { whoami } = require("./commands/whoami") const { login } = require("./commands/login") const { link } = require("./commands/link") const handlerP = fn => (...args) => { Promise.resolve(fn(...args)).then( () => process.exit(0), err => console.log(err) ) } function buildLocalCommands(cli, isLocalProject) { const defaultHost = `localhost` const defaultPort = `9000` const directory = path.resolve(`.`) const projectInfo = { directory } const useYarn = existsSync(path.join(directory, `yarn.lock`)) if (isLocalProject) { const json = require(path.join(directory, `package.json`)) projectInfo.sitePackageJson = json } function getLocalMedusaMajorVersion() { let version = getLocalMedusaVersion() if (version) { version = Number(version.split(`.`)[0]) } return version } function resolveLocalCommand(command) { if (!isLocalProject) { cli.showHelp() } try { const cmdPath = resolveCwd.silent( `@medusajs/medusa/dist/commands/${command}` ) return require(cmdPath).default } catch (err) { cli.showHelp() } } function getCommandHandler(command, handler) { return argv => { const localCmd = resolveLocalCommand(command) const args = { ...argv, ...projectInfo, useYarn } // report.verbose(`running command: ${command}`) return handler ? handler(args, localCmd) : localCmd(args) } } cli .command({ command: `seed`, desc: `Migrates and populates the database with the provided file.`, builder: _ => _.option(`f`, { alias: `seed-file`, type: `string`, describe: `Path to the file where the seed is defined.`, }).option(`m`, { alias: `migrate`, type: `boolean`, default: true, describe: `Flag to indicate if migrations should be run prior to seeding the database`, }), handler: handlerP( getCommandHandler(`seed`, (args, cmd) => { process.env.NODE_ENV = process.env.NODE_ENV || `development` return cmd(args) }) ), }) .command({ command: `migrations [action]`, desc: `Migrate the database to the most recent version.`, builder: { action: { demand: true, choices: ["run", "show"], }, }, handler: handlerP( getCommandHandler(`migrate`, (args, cmd) => { process.env.NODE_ENV = process.env.NODE_ENV || `development` return cmd(args) }) ), }) .command({ command: `whoami`, desc: `View the details of the currently logged in user.`, handler: handlerP(whoami), }) .command({ command: `link`, desc: `Creates your Medusa Cloud user in your local database for local testing.`, builder: _ => _.option(`skip-local-user`, { alias: `skipLocalUser`, type: `boolean`, default: false, describe: `If set a user will not be created in the database.`, }), handler: handlerP(argv => { if (!isLocalProject) { console.log("must be a local project") cli.showHelp() } const args = { ...argv, ...projectInfo, useYarn } return link(args) }), }) .command({ command: `login`, desc: `Logs you into Medusa Cloud.`, handler: handlerP(login), }) .command({ command: `develop`, desc: `Start development server. Watches file and rebuilds when something changes`, builder: _ => _.option(`H`, { alias: `host`, type: `string`, default: defaultHost, describe: `Set host. Defaults to ${defaultHost}`, }).option(`p`, { alias: `port`, type: `string`, default: process.env.PORT || defaultPort, describe: process.env.PORT ? `Set port. Defaults to ${process.env.PORT} (set by env.PORT) (otherwise defaults ${defaultPort})` : `Set port. Defaults to ${defaultPort}`, }), handler: handlerP( getCommandHandler(`develop`, (args, cmd) => { process.env.NODE_ENV = process.env.NODE_ENV || `development` cmd(args) // Return an empty promise to prevent handlerP from exiting early. // The development server shouldn't ever exit until the user directly // kills it so this is fine. return new Promise(resolve => {}) }) ), }) .command({ command: `start`, desc: `Start development server.`, builder: _ => _.option(`H`, { alias: `host`, type: `string`, default: defaultHost, describe: `Set host. Defaults to ${defaultHost}`, }).option(`p`, { alias: `port`, type: `string`, default: process.env.PORT || defaultPort, describe: process.env.PORT ? `Set port. Defaults to ${process.env.PORT} (set by env.PORT) (otherwise defaults ${defaultPort})` : `Set port. Defaults to ${defaultPort}`, }), handler: handlerP( getCommandHandler(`start`, (args, cmd) => { process.env.NODE_ENV = process.env.NODE_ENV || `development` cmd(args) // Return an empty promise to prevent handlerP from exiting early. // The development server shouldn't ever exit until the user directly // kills it so this is fine. return new Promise(resolve => {}) }) ), }) .command({ command: `user`, desc: `Create a user`, builder: _ => _.option(`e`, { alias: `email`, type: `string`, describe: `User's email.`, }).option(`p`, { alias: `password`, type: `string`, describe: `User's password.`, }), handler: handlerP( getCommandHandler(`user`, (args, cmd) => { cmd(args) // Return an empty promise to prevent handlerP from exiting early. // The development server shouldn't ever exit until the user directly // kills it so this is fine. return new Promise(resolve => {}) }) ), }) } function isLocalMedusaProject() { let inMedusaProject = false try { const { dependencies, devDependencies } = require(path.resolve( `./package.json` )) inMedusaProject = (dependencies && dependencies["@medusajs/medusa"]) || (devDependencies && devDependencies["@medusajs/medusa"]) } catch (err) { /* ignore */ } return !!inMedusaProject } function getVersionInfo() { const { version } = require(`../package.json`) const isMedusaProject = isLocalMedusaProject() if (isMedusaProject) { let medusaVersion = getLocalMedusaVersion() if (!medusaVersion) { medusaVersion = `unknown` } return `Medusa CLI version: ${version} Medusa version: ${medusaVersion} Note: this is the Medusa version for the site at: ${process.cwd()}` } else { return `Medusa CLI version: ${version}` } } module.exports = argv => { const cli = yargs() const isLocalProject = isLocalMedusaProject() cli .scriptName(`medusa`) .usage(`Usage: $0 [options]`) .alias(`h`, `help`) .alias(`v`, `version`) .option(`verbose`, { default: false, type: `boolean`, describe: `Turn on verbose output`, global: true, }) .option(`no-color`, { alias: `no-colors`, default: false, type: `boolean`, describe: `Turn off the color in output`, global: true, }) .option(`json`, { describe: `Turn on the JSON logger`, default: false, type: `boolean`, global: true, }) buildLocalCommands(cli, isLocalProject) try { cli.version( `version`, `Show the version of the Medusa CLI and the Medusa package in the current project`, getVersionInfo() ) } catch (e) { // ignore } return cli .wrap(cli.terminalWidth()) .demandCommand(1, `Pass --help to see all available commands and options.`) .strict() .fail((msg, err, yargs) => { const availableCommands = yargs.getCommands().map(commandDescription => { const [command] = commandDescription return command.split(` `)[0] }) const arg = argv.slice(2)[0] const suggestion = arg ? didYouMean(arg, availableCommands) : `` cli.showHelp() // report.log(suggestion) // report.log(msg) }) .parse(argv.slice(2)) }