UNPKG

create-moost

Version:
277 lines (269 loc) 8.23 kB
import { Cli, CliApp, Param } from "@moostjs/event-cli"; import { useAutoHelp, useCliOption } from "@wooksjs/event-cli"; import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmdirSync, unlinkSync } from "fs"; import prompts from "prompts"; import { ProstoRewrite } from "@prostojs/rewrite"; import { join } from "path"; //#region packages/create-moost/src/prompts.ts const defaultProjectName = "moost-app"; async function getPrompts(inputs) { const predefined = { targetDir: inputs.name || "", projectName: inputs.name || "", packageName: inputs.name || "" }; try { const results = await prompts([ { name: "projectName", type: predefined.targetDir ? null : "text", message: "Project name:", initial: defaultProjectName, onState: (state) => predefined.targetDir = String(state.value).trim() || defaultProjectName }, { name: "overwrite", type: () => canSkipEmptying(predefined.targetDir) || inputs.force ? null : "confirm", message: () => { const dirForPrompt = predefined.targetDir === "." ? "Current directory" : `Target directory "${predefined.targetDir}"`; return `${dirForPrompt} is not empty. Remove existing files and continue?`; } }, { name: "overwriteChecker", type: (prev, values) => { if (values.overwrite === false) throw new Error("Operation cancelled"); return null; } }, { name: "type", type: () => { if (inputs.cli && !inputs.http) { predefined.type = "cli"; return null; } if (!inputs.cli && inputs.http) { predefined.type = "http"; return null; } return "select"; }, message: "Moost Adapter:", choices: [{ title: "HTTP (Web) Application", value: "http" }, { title: "CLI Application", value: "cli" }] }, { name: "packageName", type: () => isValidPackageName(predefined.targetDir) ? null : "text", message: "Package name:", initial: () => toValidPackageName(predefined.targetDir), validate: (dir) => isValidPackageName(dir) || "Invalid package.json name" }, { name: "wf", type: (prev, values) => inputs.wf || values.type === "cli" || inputs.cli ? null : "toggle", message: "Add Moost Workflows Example?", initial: false, active: "Yes", inactive: "No" }, { name: "domelint", type: () => inputs.domelint ? null : "toggle", message: "Add do-me-lint (smart eslint installer)?", initial: false, active: "Yes", inactive: "No" } ], { onCancel: () => { throw new Error("Operation cancelled"); } }); return { ...predefined, ...results, packageName: results.packageName || results.targetDir || predefined.targetDir }; } catch (error) { console.log(error.message); process.exit(1); } } function canSkipEmptying(dir) { if (!existsSync(dir)) return true; const files = readdirSync(dir); if (files.length === 0) return true; if (files.length === 1 && files[0] === ".git") return true; return false; } function isValidPackageName(projectName) { return /^(?:@[\d*a-z~-][\d*._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*$/.test(projectName); } function toValidPackageName(projectName) { return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^\da-z~-]+/g, "-"); } //#endregion //#region packages/create-moost/src/scaffold.ts const rw = new ProstoRewrite({ textPattern: [ "*.{js,jsx,ts,tsx,txt,json,jsonc,yml,yaml,md,ini,css,html}", "Dockerfile", "*config", ".gitignore", ".eslintrc.json", ".prettierignore", ".prettierrc" ] }); const root = process.cwd(); const { version } = JSON.parse(readFileSync(join(__dirname, "../package.json")).toString()); async function scaffold(data) { const projectDir = join(root, data.targetDir); if (existsSync(projectDir)) { if (data.overwrite) emptyDirectorySync(projectDir); } else mkdirSync(projectDir); const templatePath = join(__dirname, "../templates", data.type); const commonPath = join(__dirname, "../templates/common"); const wfPath = join(__dirname, "../templates/wf"); const context = { ...data, version }; const excludeCommon = []; if (!data.domelint) excludeCommon.push(".domelintrc.yml"); await rw.rewriteDir({ baseDir: templatePath, output: projectDir, renameFile(filename) { if (filename.endsWith(".jsonc")) return filename.replace(/c$/, ""); return filename; } }, context); await rw.rewriteDir({ baseDir: commonPath, output: projectDir, exclude: excludeCommon, renameFile(filename) { if (filename.endsWith(".jsonc")) return filename.replace(/c$/, ""); return filename; } }, context); if (data.wf) await rw.rewriteDir({ baseDir: wfPath, output: projectDir, renameFile(filename) { if (filename.endsWith(".jsonc")) return filename.replace(/c$/, ""); return filename; } }, context); } function emptyDirectorySync(directory) { if (existsSync(directory)) readdirSync(directory).forEach((file) => { const currentPath = join(directory, file); if (lstatSync(currentPath).isDirectory()) { emptyDirectorySync(currentPath); rmdirSync(currentPath); } else unlinkSync(currentPath); }); } //#endregion //#region packages/create-moost/src/index.ts function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } function _ts_param(paramIndex, decorator) { return function(target, key) { decorator(target, key, paramIndex); }; } let Commands = class Commands$1 extends CliApp { root() { return this.execute(); } withName(name) { return this.execute(name); } async execute(name) { useAutoHelp() && process.exit(0); const prompts$1 = await getPrompts({ name, http: !!useCliOption("http"), cli: !!useCliOption("cli"), wf: !!useCliOption("wf"), domelint: !!useCliOption("domelint"), force: !!useCliOption("force") }); console.log("\nScaffolding a new project..."); await scaffold(prompts$1); const cli = prompts$1.type === "cli"; return ` ${"\x1B[97m\x1B[1m"}Success! ${"\x1B[22m"}Your new "${prompts$1.projectName}" project has been created successfully. ${"\x1B[39m"} Follow these next steps to start your development server: 1. Navigate to your new project: ${"\x1B[36m"}cd ${prompts$1.targetDir} ${"\x1B[39m"} 2. Install the dependencies: ${"\x1B[36m"}npm install ${"\x1B[39m"} ${cli ? ` 3. Make bin.js executable: ${"\x1B[36m"}chmod +x ./bin.js ${"\x1B[39m"} ` : ""} ${cli ? "4" : "3"}. Start the development server: ${"\x1B[36m"}npm run dev${cli ? " -- hello World" : ""}${"\x1B[39m"} ${"\x1B[32m"}You're all set! The development server will help you in building your application. Enjoy coding, and build something amazing!${"\x1B[39m"} `; } }; _ts_decorate([ Cli(""), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", []), _ts_metadata("design:returntype", void 0) ], Commands.prototype, "root", null); _ts_decorate([ Cli(":name"), _ts_param(0, Param("name")), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [String]), _ts_metadata("design:returntype", void 0) ], Commands.prototype, "withName", null); function run() { new Commands().useOptions([ { keys: ["http"], description: "Use Moost HTTP", type: Boolean }, { keys: ["cli"], description: "Use Moost CLI", type: Boolean }, { keys: ["wf"], description: "Add Workflow Adapter", type: Boolean }, { keys: ["domelint"], description: "Add do-me-lint (smart eslint installer)", type: Boolean }, { keys: ["force"], description: "Force Overwrite", type: Boolean } ]).start(); } //#endregion export { run };