├── .babelrc ├── src ├── utils │ ├── data │ │ ├── package-info.mjs │ │ ├── cmg-config.mjs │ │ └── templates.mjs │ ├── logger.mjs │ ├── git-actions.mjs │ ├── cookie-cutting │ │ └── replacer.mjs │ └── commands │ │ ├── sync.mjs │ │ └── init.mjs └── index.mjs ├── package.json ├── .gitignore ├── README.md ├── LICENSE └── pnpm-lock.yaml /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /src/utils/data/package-info.mjs: -------------------------------------------------------------------------------- 1 | import packageJson from '../../../package.json' with { type: "json" }; 2 | 3 | export const getPackageInfo = () => { 4 | return packageJson 5 | } -------------------------------------------------------------------------------- /src/utils/data/cmg-config.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | export const DEFAULT_CONFIG_NAME = "create-multiplayer-game.config.json"; 4 | 5 | export const getCmgConfig = () => { 6 | 7 | const content = fs.readFileSync(DEFAULT_CONFIG_NAME, 'utf-8'); 8 | 9 | return JSON.parse(content) 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/utils/logger.mjs: -------------------------------------------------------------------------------- 1 | import chalk from "chalk" 2 | 3 | export const logger = { 4 | error(...args) { 5 | console.error(chalk.red(...args)) 6 | }, 7 | warn(...args) { 8 | console.warn(chalk.yellow(...args)) 9 | }, 10 | info(...args) { 11 | console.info(chalk.cyan(...args)) 12 | }, 13 | success(...args) { 14 | console.log(chalk.green(...args)) 15 | }, 16 | log(...args) { 17 | console.log(...args) 18 | }, 19 | break() { 20 | console.log("") 21 | }, 22 | } -------------------------------------------------------------------------------- /src/utils/git-actions.mjs: -------------------------------------------------------------------------------- 1 | import {$} from 'execa'; 2 | 3 | export const cloneTemplate = async (url, name = "", path = undefined) => { 4 | if (path) process.chdir(path) 5 | const cloned = await $`git clone ${url} ${name}`; 6 | await $`chmod -R 777 ./${name} `; 7 | return cloned 8 | } 9 | 10 | export const addTemplateUpstream = async (url, path = undefined) => { 11 | if (path) process.chdir(path) 12 | return await $`git remote add upstream ${url}`; 13 | } 14 | 15 | export const isGitDirty = async (path = undefined) => { 16 | if (path) process.chdir(path) 17 | const output = await $`git status --porcelain`; 18 | return !!output 19 | } 20 | 21 | export const syncTemplate = async(path = undefined) => { 22 | if (path) process.chdir(path) 23 | return await $`git pull upstream main`; 24 | } -------------------------------------------------------------------------------- /src/utils/cookie-cutting/replacer.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | function replaceInFile(filePath, search, replace) { 5 | const content = fs.readFileSync(filePath, 'utf-8'); 6 | const updatedContent = content.replace(search, replace); 7 | fs.writeFileSync(filePath, updatedContent, 'utf-8'); 8 | } 9 | 10 | export function replaceInDirectory(directoryPath, search, replace) { 11 | const files = fs.readdirSync(directoryPath); 12 | 13 | files.forEach((file) => { 14 | // Ignore .git folder 15 | if (file === '.git') { 16 | return; 17 | } 18 | 19 | const filePath = path.join(directoryPath, file); 20 | const stat = fs.statSync(filePath); 21 | 22 | if (stat.isDirectory()) { 23 | replaceInDirectory(filePath, search, replace); 24 | } else if (stat.isFile()) { 25 | replaceInFile(filePath, search, replace); 26 | } 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-multiplayer-game", 3 | "version": "0.0.26", 4 | "description": "Create a multiplayer web game in seconds 👾🚀", 5 | "main": "./src/index.mjs", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/grayhatdevelopers/create-multiplayer-game.git" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "start": "node --experimental-json-modules ./src/index.mjs" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bin": { 18 | "create-multiplayer-game": "./src/index.mjs", 19 | "cmg": "./src/index.mjs" 20 | }, 21 | "dependencies": { 22 | "chalk": "^5.3.0", 23 | "commander": "^11.1.0", 24 | "download-git-repo": "^3.0.2", 25 | "execa": "^9.0.2", 26 | "inquirer": "^9.2.12", 27 | "lodash": "^4.17.21", 28 | "ora": "^8.0.1", 29 | "react": "^18.2.0" 30 | }, 31 | "devDependencies": { 32 | "esm": "^3.2.25" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/index.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from "commander"; 3 | 4 | import { sync } from "./utils/commands/sync.mjs"; 5 | import { init, initAction } from "./utils/commands/init.mjs"; 6 | 7 | import { getPackageInfo } from "./utils/data/package-info.mjs"; 8 | const { version } = getPackageInfo() 9 | 10 | const cmds = { 11 | init, 12 | sync 13 | } 14 | 15 | const isArgumentACommand = !!cmds[process.argv[2]] 16 | const isContainsHelp = process.argv.includes(a => a === "--help") 17 | 18 | program 19 | .version( 20 | version, 21 | "-v, --version", 22 | "Display the version number" 23 | ) 24 | .argument("[command]") 25 | .argument("[project-name]") 26 | 27 | if (isArgumentACommand || isContainsHelp) { 28 | Object.values(cmds).forEach(cmd => { 29 | program.addCommand(cmd) 30 | }) 31 | } 32 | else { 33 | program 34 | .option("-t, --template