@practica/create-node-app
Version:
Create Node.js app that is packed with best practices AND strive for simplicity
230 lines (229 loc) • 13.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderWizard = void 0;
const react_1 = __importDefault(require("react"));
const ink_1 = require("ink");
const ink_multi_select_1 = __importDefault(require("ink-multi-select"));
const ink_select_input_1 = __importDefault(require("ink-select-input"));
const ink_text_input_1 = require("ink-text-input");
const figlet_1 = __importDefault(require("figlet"));
const generateService = __importStar(require("../generation-logic/generate-service"));
const generation_options_1 = require("../generation-logic/generation-options");
const QuestionsWizard = () => {
const initialQuestionsWizard = {
isOver: false,
chosenName: "",
finalMessage: "",
chosenFramework: "",
chosenDB: "",
showWarningMessage: true,
showNameQuestion: false,
showFrameworkQuestion: false,
showDBTypeQuestion: false,
showFeatures: false,
advice: "",
title: figlet_1.default.textSync("Practica", {
font: "Big",
horizontalLayout: "full",
verticalLayout: "default",
width: 60,
whitespaceBreak: true,
}),
generationStatusMessage: "",
};
const [questionsWizard, setQuestionsWizard] = react_1.default.useState(initialQuestionsWizard);
const features = [
{ label: "Logger", value: "logger" },
{ label: "Request-ID - Correlation-ID", value: "request-id" },
{ label: "Error-Handler", value: "error-handling" },
];
const flavours = [
{
label: "Minimal",
value: "minimal",
advice: "Configuration only things such as linters. When you want to code everything yourself",
},
{
label: "Full-flow",
value: "full-flow",
advice: "Demonstrates full request flow. Packs common best practices. \n \n ✅ 82/120 Best Practices ",
},
{
label: "Fully featured",
value: "fully-featured",
advice: "All our best practices are packed inside. Might be an overkill for some apps",
},
{
label: "Cherry pick",
value: "cherry-pick",
advice: "Specifically choose the features that you need",
},
];
const databases = [
{
label: "Postgres",
value: "pg",
advice: "Strikes great balance between popularity and flexibility. Can handle both relational workload and light noSQL/JSON workload",
},
{
label: "mySQL",
value: "my-sql",
advice: "Classic DB that mostly leans toward relational and structured data",
},
{
label: "Mongo",
value: "mongo",
advice: "Great DB for scenarios when a flexible schema is needed",
},
];
const frameworks = [
{
label: "Express",
value: "express",
advice: "A minimalist web library that is easy to learn. A great choice when in a need to have full-control. \n \n ⭐️ 91,000 stars \n \n ⬇️ 1,200,000 downloads/week",
},
{
label: "Fastify",
value: "my-fastify",
advice: "A minimalist web library that is easy to learn. A great choice when in a need to have full-control. \n \n ⭐️ 91,000 stars \n \n ⬇️ 1,200,000 downloads/week",
},
{
label: "Nest.JS",
value: "nestjs",
advice: "A minimalist web library that is easy to learn. A great choice when in a need to have full-control. \n \n ⭐️ 91,000 stars \n \n ⬇️ 1,200,000 downloads/week",
},
];
const handleNameChoose = (name) => {
setQuestionsWizard({
...questionsWizard,
chosenName: name,
showDBTypeQuestion: false,
showNameQuestion: false,
showFrameworkQuestion: true,
});
};
const warningWasConfirmed = () => {
setQuestionsWizard({
...questionsWizard,
showWarningMessage: false,
advice: "Determines the root folder and the libraries scope name. For example, @your-org/logger",
showNameQuestion: true,
});
};
const handleFrameworkChoose = (chosenOption) => {
setQuestionsWizard({
...questionsWizard,
chosenFramework: chosenOption.value,
showFrameworkQuestion: false,
showDBTypeQuestion: true,
});
};
const handleFeaturesChoose = (selected) => { };
const handleFlavourChoose = async (selected) => {
setQuestionsWizard({
...questionsWizard,
finalMessage: "🔁 Creating your app now. This might take a few seconds...",
isOver: true,
});
const generationOptions = (0, generation_options_1.factorDefaultOptions)({
installDependencies: true,
targetDirectory: process.cwd(),
webFramework: questionsWizard.chosenFramework,
});
await generateService.generateApp(generationOptions);
setQuestionsWizard({
...questionsWizard,
isOver: true,
finalMessage: "✅ Your app is ready and packed with great practices inside",
});
};
const handleDBChoose = async (chosenOption) => {
setQuestionsWizard({
...questionsWizard,
chosenDB: chosenOption.value,
showDBTypeQuestion: false,
showFlavourQuestion: true,
});
};
const onSelectItemChange = (selectedItem) => {
const allOptions = [...databases, ...frameworks, ...flavours];
const chosenItem = allOptions.find((option) => option.value === selectedItem.value)?.advice;
const activeAdvice = chosenItem ? chosenItem : "";
setQuestionsWizard({ ...questionsWizard, advice: activeAdvice });
};
react_1.default.useEffect(() => { }, []);
return (react_1.default.createElement(ink_1.Box, { width: "100%", alignSelf: "center", flexDirection: "column" },
react_1.default.createElement(ink_1.Box, { flexDirection: "row", width: "100%", flexBasis: "100%" },
react_1.default.createElement(ink_1.Text, { wrap: "wrap" }, questionsWizard.title)),
!questionsWizard.isOver ? (react_1.default.createElement(ink_1.Box, { flexDirection: "row" },
react_1.default.createElement(ink_1.Box, { width: "50%", alignSelf: "flex-start", borderStyle: "classic", borderColor: "grey", height: 15, paddingX: 3, marginX: 3, alignItems: "flex-start" },
react_1.default.createElement(ink_1.Box, { flexDirection: "column" },
react_1.default.createElement(ink_1.Box, { paddingY: 1, alignSelf: "flex-start" },
react_1.default.createElement(ink_1.Text, { color: "white", bold: true }, "\u29BE Just a few questions first"),
react_1.default.createElement(ink_1.Newline, null),
react_1.default.createElement(ink_1.Spacer, null)),
react_1.default.createElement(ink_1.Box, null,
react_1.default.createElement(ink_1.Box, { display: questionsWizard.showFeaturesQuestion ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "green" }, "Cherry-pick features:"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_multi_select_1.default, { items: features, onSelect: handleFeaturesChoose })),
questionsWizard.showWarningMessage ? (react_1.default.createElement(ink_1.Box, { display: questionsWizard.showWarningMessage ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "grey" }, "\uD83D\uDD16 This is an alpha version of this wizard which is meant for demo purposes. Whatever technologies you'll choose, for now the generated app will be based on Express + Postgres. Enter to continue or CTRL + C to exit"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_text_input_1.UncontrolledTextInput, { initialValue: "", onSubmit: warningWasConfirmed }))) : (react_1.default.createElement(react_1.default.Fragment, null)),
questionsWizard.showFlavourQuestion ? (react_1.default.createElement(ink_1.Box, { display: questionsWizard.showFlavourQuestion ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "green" }, "Which level of starter:"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_select_input_1.default, { items: flavours, onHighlight: onSelectItemChange, onSelect: handleFlavourChoose }))) : (react_1.default.createElement(react_1.default.Fragment, null)),
questionsWizard.showNameQuestion ? (react_1.default.createElement(ink_1.Box, { display: questionsWizard.showNameQuestion ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "green" }, "Name of your app or organization:"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_text_input_1.UncontrolledTextInput, { initialValue: "", onSubmit: handleNameChoose }))) : (react_1.default.createElement(react_1.default.Fragment, null)),
questionsWizard.showDBTypeQuestion ? (react_1.default.createElement(ink_1.Box, { display: questionsWizard.showDBTypeQuestion ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "green" }, "Which is your preferred DB:"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_select_input_1.default, { items: databases, onSelect: handleDBChoose, onHighlight: onSelectItemChange }))) : (react_1.default.createElement(react_1.default.Fragment, null)),
questionsWizard.showFrameworkQuestion ? (react_1.default.createElement(ink_1.Box, { display: questionsWizard.showFrameworkQuestion ? "flex" : "none" },
react_1.default.createElement(ink_1.Text, { color: "green" }, "Your preferred framework:"),
react_1.default.createElement(ink_1.Spacer, null),
react_1.default.createElement(ink_select_input_1.default, { items: frameworks, onSelect: handleFrameworkChoose, onHighlight: onSelectItemChange }))) : (react_1.default.createElement(react_1.default.Fragment, null))))),
react_1.default.createElement(ink_1.Box, { width: "30%", borderStyle: "classic", borderColor: "grey", height: 15, paddingX: 5, marginX: 3, alignItems: "flex-start", alignSelf: "flex-end" },
react_1.default.createElement(ink_1.Box, { flexDirection: "column" },
react_1.default.createElement(ink_1.Box, { paddingY: 1, alignSelf: "flex-start" },
react_1.default.createElement(ink_1.Text, { color: "white", bold: true }, "\u29BE More Info")),
react_1.default.createElement(ink_1.Box, null,
react_1.default.createElement(ink_1.Text, null, questionsWizard.advice)))))) : (react_1.default.createElement(react_1.default.Fragment, null)),
questionsWizard.isOver ? (react_1.default.createElement(ink_1.Box, { flexDirection: "row", width: "100%", flexBasis: "100%" },
react_1.default.createElement(ink_1.Text, { wrap: "wrap", color: "white", bold: true }, questionsWizard.finalMessage))) : (react_1.default.createElement(react_1.default.Fragment, null))));
};
//<SelectInput items={items} onSelect={handleSubmit} />
const renderWizard = () => {
(0, ink_1.render)(react_1.default.createElement(QuestionsWizard, null));
};
exports.renderWizard = renderWizard;