create-moost
Version:
277 lines (269 loc) • 8.23 kB
JavaScript
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 };