-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
1,408 additions
and
882 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,155 +1,42 @@ | ||
#!/usr/bin/env node | ||
import { program } from "commander"; | ||
import inquirer from "inquirer"; | ||
import download from "download-git-repo"; | ||
import ora from "ora"; | ||
import chalk from "chalk"; | ||
import path from "path"; | ||
import _ from "lodash"; | ||
import { promises as fs } from "fs"; // Import promises version of fs for async file operations | ||
|
||
import templates from "./utils/data/templates.mjs"; | ||
import { replaceInDirectory } from "./utils/cookie-cutting/replacer.mjs"; | ||
import { sync } from "./utils/commands/sync.mjs"; | ||
import { init, initAction } from "./utils/commands/init.mjs"; | ||
|
||
import packageJson from "../package.json" with { type: "json" }; | ||
import { logger } from "./utils/logger"; | ||
const { version } = packageJson | ||
|
||
let DEFAULT_CONFIG_NAME = "create-multiplayer-game.config.json"; | ||
const cmds = { | ||
init, | ||
sync | ||
} | ||
|
||
const { version } = packageJson | ||
const isArgumentACommand = !!cmds[process.argv[2]] | ||
const isContainsHelp = process.argv.includes(a => a === "--help") | ||
|
||
program | ||
.version( | ||
version, | ||
"-v, --version", | ||
"Display the version number" | ||
) | ||
.arguments("[project-name]") | ||
.option("-t, --template <template>", "Specify the template") | ||
.option("-c, --config <config>", "Specify the configuration file") | ||
.parse(process.argv); | ||
|
||
async function main() { | ||
let projectName = program.args[0]; | ||
let gameName = projectName; | ||
let templateChoice = program.template; | ||
let configPath = program.config || DEFAULT_CONFIG_NAME; // Default config file path | ||
let currentRun = Date.now(); | ||
let generatedAt = currentRun; | ||
let lastRunAt = currentRun; | ||
|
||
// Check if config file exists | ||
let configExists = false; | ||
try { | ||
await fs.access(configPath); | ||
configExists = true; | ||
} catch (err) { | ||
// Config file doesn't exist, continue without it | ||
} | ||
|
||
// If config file exists, read configuration from it | ||
if (configExists) { | ||
const spinner = ora( | ||
`Reading config file at ${path.join(process.cwd(), configPath)}...` | ||
).start(); | ||
const configData = await fs.readFile(configPath, "utf-8"); | ||
const config = JSON.parse(configData); | ||
if (!projectName && config.projectName) projectName = config.projectName; | ||
if (!gameName && config.gameName) gameName = config.gameName; | ||
if (!templateChoice && config.templateChoice) templateChoice = config.templateChoice; | ||
if (config.generatedAt) generatedAt = config.generatedAt; | ||
spinner.succeed(); | ||
} | ||
|
||
// If config file doesn't exist, prompt user for input | ||
if (!projectName) { | ||
const answersGameName = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "gameName", | ||
message: "What will you call your game?", | ||
validate: (input) => !!input.trim(), | ||
}, | ||
]); | ||
gameName = answersGameName.gameName; | ||
|
||
const kebabCaseGameName = _.kebabCase(gameName); | ||
|
||
const answers = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "projectName", | ||
message: "Enter the project name:", | ||
default: kebabCaseGameName, | ||
validate: (input) => !!input.trim(), | ||
}, | ||
]); | ||
projectName = answers.projectName; | ||
.argument("[command]") | ||
.argument("[project-name]") | ||
|
||
if (isArgumentACommand || isContainsHelp) { | ||
Object.values(cmds).forEach(cmd => { | ||
program.addCommand(cmd) | ||
}) | ||
} | ||
|
||
if (!templateChoice) { | ||
const answers = await inquirer.prompt([ | ||
{ | ||
type: "list", | ||
name: "templateChoice", | ||
message: "Which template would you like to use?", | ||
choices: templates.map((template) => template.id + (template.source === "community" ? " (community)" : "")), | ||
}, | ||
]); | ||
templateChoice = answers.templateChoice.split(" ")[0]; // <-- remove any addons to the ID | ||
else { | ||
program | ||
.option("-t, --template <template>", "Specify the template") | ||
.option("-c, --config <config>", "Specify the configuration file") | ||
.action(async (command, projectName, options) => { | ||
// If no command is provided, run the default command (init) | ||
await initAction(projectName, options, program); | ||
}); | ||
} | ||
|
||
const chosenTemplate = templates.find( | ||
(template) => template.id === templateChoice | ||
) | ||
|
||
const templateRepoUrl = chosenTemplate.url; | ||
const targetPath = path.join(process.cwd(), projectName); | ||
const spinner = ora("Downloading project template...").start(); | ||
|
||
download(templateRepoUrl, targetPath, { clone: true }, (err) => { | ||
if (err) { | ||
spinner.fail(chalk.red("Failed to download project template.")); | ||
if (chosenTemplate.price === "premium") { | ||
logger.warn("This template is 👑 Premium. We're releasing Premium purchases soon at https://grayhat.studio/games/pricing. Until then, sit tight!") | ||
} | ||
else { | ||
logger.error("Couldn't clone your project. Please check your git configuration, or check if a folder with a similar name to the project you want to create already exists."); | ||
logger.log("Detailed error log:"); | ||
} | ||
console.error(err); | ||
} else { | ||
|
||
if (chosenTemplate.editable) { | ||
// Cookie cutting | ||
replaceInDirectory(targetPath, new RegExp("%GAME_NAME%", "g"), gameName); | ||
} | ||
|
||
spinner.succeed(chalk.green("Project template downloaded successfully.")); | ||
logger.warn(`\nProject initialized at ${targetPath}`); | ||
|
||
logger.info("\nHappy coding!"); | ||
|
||
// Write configuration to file | ||
const configData = { | ||
projectName, | ||
gameName, | ||
templateChoice, | ||
generatedAt, | ||
lastRunAt, | ||
version | ||
}; | ||
|
||
let configFileWritePath = path.join(targetPath, DEFAULT_CONFIG_NAME); | ||
fs.writeFile(configFileWritePath, JSON.stringify(configData, null, 2)) | ||
.then(() => | ||
logger.success(`Configuration saved to ${configFileWritePath}`) | ||
) | ||
.catch((err) => | ||
logger.error(`Error writing configuration file: ${err.message}`) | ||
); | ||
} | ||
}); | ||
} | ||
|
||
main(); | ||
program.parse(process.argv); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { logger } from '../logger.mjs' | ||
import { Command } from "commander" | ||
import inquirer from "inquirer"; | ||
// import download from "download-git-repo"; | ||
import _ from "lodash"; | ||
import ora from "ora"; | ||
import chalk from "chalk"; | ||
import path from "path"; | ||
import { promises as fs } from "fs"; // Import promises version of fs for async file operations | ||
|
||
import templates from "../data/templates.mjs"; | ||
import { replaceInDirectory } from "../cookie-cutting/replacer.mjs"; | ||
import { addTemplateUpstream, cloneTemplate } from "../git-actions.mjs"; | ||
|
||
import packageJson from "../../../package.json" with { type: "json" }; | ||
import { DEFAULT_CONFIG_NAME } from '../data/cmg-config.mjs'; | ||
const { version } = packageJson | ||
|
||
export const initAction = async (name, options, program) => { | ||
|
||
let projectName = program.args[0]; | ||
let gameName = projectName; | ||
let templateId = program.template; | ||
let configPath = program.config || DEFAULT_CONFIG_NAME; // Default config file path | ||
let currentRun = Date.now(); | ||
let generatedAt = currentRun; | ||
let lastRunAt = currentRun; | ||
|
||
// Check if config file exists | ||
let configExists = false; | ||
try { | ||
await fs.access(configPath); | ||
configExists = true; | ||
} catch (err) { | ||
// Config file doesn't exist, continue without it | ||
} | ||
|
||
// If config file exists, read configuration from it | ||
if (configExists) { | ||
const spinner = ora( | ||
`Reading config file at ${path.join(process.cwd(), configPath)}...` | ||
).start(); | ||
const configData = await fs.readFile(configPath, "utf-8"); | ||
const config = JSON.parse(configData); | ||
if (!projectName && config.projectName) projectName = config.projectName; | ||
if (!gameName && config.gameName) gameName = config.gameName; | ||
if (!templateId && config.templateId) templateId = config.templateId; | ||
if (config.generatedAt) generatedAt = config.generatedAt; | ||
spinner.succeed(); | ||
} | ||
|
||
// If config file doesn't exist, prompt user for input | ||
if (!projectName) { | ||
const answersGameName = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "gameName", | ||
message: "What will you call your game?", | ||
validate: (input) => !!input.trim(), | ||
}, | ||
]); | ||
gameName = answersGameName.gameName; | ||
|
||
const kebabCaseGameName = _.kebabCase(gameName); | ||
|
||
const answers = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "projectName", | ||
message: "Enter the project name:", | ||
default: kebabCaseGameName, | ||
validate: (input) => !!input.trim(), | ||
}, | ||
]); | ||
projectName = answers.projectName; | ||
} | ||
|
||
if (!templateId) { | ||
const answers = await inquirer.prompt([ | ||
{ | ||
type: "list", | ||
name: "templateId", | ||
message: "Which template would you like to use?", | ||
choices: templates.map((template) => template.id + (template.source === "community" ? " (community)" : "")), | ||
}, | ||
]); | ||
templateId = answers.templateId.split(" ")[0]; // <-- remove any addons to the ID | ||
} | ||
|
||
const chosenTemplate = templates.find( | ||
(template) => template.id === templateId | ||
) | ||
|
||
const templateRepoUrl = chosenTemplate.repository.url; | ||
const cwd = process.cwd() | ||
const targetPath = path.join(cwd, projectName); | ||
const spinner = ora("Downloading project template...").start(); | ||
|
||
try { | ||
await cloneTemplate(templateRepoUrl, projectName, cwd) | ||
} | ||
catch (err) { | ||
spinner.fail(chalk.red("Failed to download project template.")); | ||
if (chosenTemplate.price === "premium") { | ||
logger.warn("This template is 👑 Premium. We're releasing Premium purchases soon at https://grayhat.studio/games/pricing. Until then, sit tight!") | ||
} | ||
else { | ||
logger.error("Couldn't clone your project. Please check your git configuration, or check if a folder with a similar name to the project you want to create already exists."); | ||
logger.log("Detailed error log:"); | ||
} | ||
console.error(err); | ||
} | ||
|
||
await addTemplateUpstream(templateRepoUrl, targetPath) | ||
|
||
if (chosenTemplate.editable) { | ||
// Cookie cutting | ||
replaceInDirectory(targetPath, new RegExp("%GAME_NAME%", "g"), gameName); | ||
} | ||
|
||
spinner.succeed(chalk.green("Project template downloaded successfully.")); | ||
logger.warn(`\nProject initialized at ${targetPath}`); | ||
|
||
logger.info("\nHappy coding!"); | ||
|
||
// Write configuration to file | ||
const configData = { | ||
projectName, | ||
gameName, | ||
templateId, | ||
generatedAt, | ||
lastRunAt, | ||
version | ||
}; | ||
|
||
let configFileWritePath = path.join(targetPath, DEFAULT_CONFIG_NAME); | ||
fs.writeFile(configFileWritePath, JSON.stringify(configData, null, 2)) | ||
.then(() => | ||
logger.success(`Configuration saved to ${configFileWritePath}`) | ||
) | ||
.catch((err) => | ||
logger.error(`Error writing configuration file: ${err.message}`) | ||
); | ||
|
||
} | ||
|
||
export const init = new Command() | ||
.name("init") | ||
.arguments("[project-name]") | ||
.option("-t, --template <template>", "Specify the template") | ||
.option("-c, --config <config>", "Specify the configuration file") | ||
.description("Scaffold a game") | ||
.action(initAction) |
Oops, something went wrong.