@pecometer/peco-cli
Version:
Pecometer Command Line Interface Application with monorepo support
5 lines • 14.2 kB
JavaScript
"use strict";
/**
* @author Sean Hutchinson
* @copyright Pecometer Software Ltd
*/Object.defineProperty(exports,"__esModule",{value:!0}),exports.CreateApp=void 0;const _=require("lodash"),npm=require("npm"),path=require("node:path"),backend_app_config_1=require("../helpers/backend-app-config"),base_command_1=require("../classes/base-command"),file_1=require("../classes/file"),JSON_OUTPUT=4,ESLINT_CONFIG="import { defineConfig, globalIgnores } from 'eslint/config';\nimport config from '@pecometer/eslint-config';\n\nexport default defineConfig([\n config,\n globalIgnores(['eslint.config.mjs', '**/*.js', '**/tests/**','**/*jest.config.ts']),\n {\n languageOptions: {\n parserOptions: {\n tsconfigRootDir: import.meta.dirname,\n },\n },\n },\n]);";class CreateApp extends base_command_1.CBaseCommand{constructor(){super(...arguments),this.description="Create a PecoTS and/or Pecular app basic application",this._fileHelper=new file_1.CFileHelper,this._folderPath=process.cwd(),this._cwdPath=process.cwd(),this._backend=!1,this._frontend=!1,this._backendStructure=backend_app_config_1.BACKEND_STRUCTURE,this._backendFiles=backend_app_config_1.BACKEND_FILES,this._backendDependencies=backend_app_config_1.BACKEND_DEPENDENCIES,this._frontendStructure=[],this._frontendFiles=[],this._frontendDependencies={dev:{},main:{}},this._commonFiles=[{content:"gitignore.data",location:[""],name:".gitignore"},{content:"gitattributes.data",location:[""],name:".gitattributes"},{content:"license.data",location:[""],name:"license"},{content:"readme.data",location:[""],name:"README.md"},{content:"prettierrc.data",location:[""],name:".prettierrc"},{content:"prettierignore.data",location:[""],name:".prettierignore"}],this._commonFolders=[["public"]],this._npmRcFile=[{backend:!0,data:"@pecometer:registry=https://npm.pecometer.co.uk\n//npm.pecometer.co.uk/:_authToken={{pecots_npm_token}}",name:"pecots_npm_token",pecular:!0,prompt:"Auth Token For @pecometer NPM Server"},{data:"@fortawesome:registry=https://npm.fontawesome.com/\n//npm.fontawesome.com/:_authToken={{font_awesome_token}}",name:"font_awesome_token",pecular:!0,prompt:"Auth Token for FontAwesome"}],this._packageJson={author:"",dependencies:{},description:"",devDependencies:{},license:"SEE LICENSE IN licence",main:"index.js",name:"",scripts:{},version:"1.0.0"},this._commonDependencies={dev:{"@pecometer/eslint-config":"",terser:"",typescript:""},main:{}},this._ifBothDependencies={dev:{concurrently:""},main:{}},this._frontEndPlaceholders=[{default:"127.0.0.1",name:"ip-address",prompt:"Development IP Address (127.0.0.1)"}]}async execute(name){if(name&&!this._validateDeprecatedOptions(name))return!1;if(!name&&(this.outputNewLine(),this.info("Please enter a name for your project/application: "),name=await this.waitForPrompt(),!this._validateDeprecatedOptions(name)))return!1;if(this._folderName=name,this._name=_.kebabCase(name),this.outputNewLine(),this.info("Would you like to install a PecoTS backend? Y/N (N): ",!1),this._backend=await this.waitForYesNo(),this.info("Would you like to install a Pecular frontend? Y/N (N): ",!1),this._frontend=await this.waitForYesNo(),!this._backend&&!this._frontend)return this.error("Please select either a backend or frontend app, or both."),!1;if(await this._setupFrontendConfig(),this.notice("Creating folder structure."),!await this._createAppFolders(name))return!1;if(this._backend&&(this.notice("Creating basic backend application."),this.outputNewLine(),!await this._createBackendApp()))return!1;if(this._frontend){this.notice("Creating basic frontend application."),this.outputNewLine();for(const element of this._frontEndPlaceholders){const placeholder=element;if(placeholder.prompt){this.info(`${placeholder.prompt}: `,!1);const value=await this.waitForPrompt();placeholder.value=()=>value||placeholder.default}}if(this.outputNewLine(),!await this._createFrontendApp())return!1}return this.notice("Adding common files."),!(!await this._addCommonFiles()||(this.notice("Preparing npmrc file."),this.outputNewLine(),!await this._prepareNpmRcFile()||(this.notice("Creating package.json."),this.outputNewLine(),!await this._writePackageJson()||(this.notice("Installing packages..."),this.outputNewLine(),!await this._installPackages()||(this.outputNewLine(),this.notice("Adding debug config files for Visual Code..."),!await this._addVsCodeDebug()||(this.outputNewLine(),this.notice("Adding eslint config..."),!await this._addEslintConfig()||(this.notice(`Application setup, enter the directory using \`cd ${this._folderName}\` and run the command \`npm run dev\` to begin development.`),0)))))))}_validateDeprecatedOptions(input){const deprecatedTerms=["angular","ng-","@angular","angular-cli"],lowerInput=input.toLowerCase();for(const term of deprecatedTerms)if(lowerInput.includes(term))return this.error(`Deprecated Angular option detected: "${input}"`),this.error("Angular frontend support has been removed from this CLI."),this.info("Please use Pecular frontend instead."),this.info("Supported combinations:"),this.info(" - PecoTS backend only"),this.info(" - Pecular frontend only"),this.info(" - PecoTS backend + Pecular frontend"),!1;return!0}async _setupFrontendConfig(){if(this._frontend){const{FRONTEND_STRUCTURE,FRONTEND_FILES,FRONTEND_DEPENDENCIES}=await Promise.resolve().then(()=>require("../helpers/pecular-frontend-app-config"));this._frontendStructure=FRONTEND_STRUCTURE,this._frontendDependencies=FRONTEND_DEPENDENCIES,this._frontendFiles=FRONTEND_FILES}}async _prepareNpmRcFile(){let npmrc="";for(let i=0,n=this._npmRcFile.length;i<n;i++)if(this._npmRcFile[i].both||this._backend&&this._npmRcFile[i].backend||this._frontend&&this._npmRcFile[i].pecular){this.info(`${this._npmRcFile[i].prompt}: `,!1);const result=await this.waitForPrompt(),regex=new RegExp(`{{${this._npmRcFile[i].name}}}`,"gi");npmrc+=this._npmRcFile[i].data.replace(regex,""===result?"":result),npmrc+="\n"}return!!await this._fileHelper.writeFile(path.join(this._folderPath,".npmrc"),npmrc)}async _installPackages(){return new Promise((resolve,reject)=>{this.outputNewLine(),process.chdir(this._folderPath),npm.load(async err=>{err&&(process.chdir(this._cwdPath),reject(!1)),await this._installDependencies(this._prepareModulesToInstall()),await this._installDependencies(this._prepareDevModulesToInstall(),!0),resolve(!0)})})}_installDependencies(modules,dev=!1){return new Promise((resolve,reject)=>{dev?(npm.config.set("save-dev",!0),npm.config.set("save",!1)):(npm.config.set("save-dev",!1),npm.config.set("save",!0)),npm.commands.install(modules,err=>{process.chdir(this._cwdPath),err?(this.error(err.message),reject(!1)):resolve(!0)})})}_prepareModulesToInstall(){const modules=[];return this._backend&&modules.push(..._.map(this._backendDependencies.main,(o,k)=>`${k}${o?`@${o}`:""}`)),this._frontend&&modules.push(..._.map(this._frontendDependencies.main,(o,k)=>`${k}${o?`@${o}`:""}`)),this._backend&&this._frontend&&modules.push(..._.map(this._ifBothDependencies.main,(o,k)=>`${k}${o?`@${o}`:""}`)),modules.push(..._.map(this._commonDependencies.main,(o,k)=>`${k}${o?`@${o}`:""}`)),modules}_prepareDevModulesToInstall(){const modules=[];return this._backend&&modules.push(..._.map(this._backendDependencies.dev,(o,k)=>`${k}${o?`@${o}`:""}`)),this._frontend&&modules.push(..._.map(this._frontendDependencies.dev,(o,k)=>`${k}${o?`@${o}`:""}`)),this._backend&&this._frontend&&modules.push(..._.map(this._ifBothDependencies.dev,(o,k)=>`${k}${o?`@${o}`:""}`)),modules.push(..._.map(this._commonDependencies.dev,(o,k)=>`${k}${o?`@${o}`:""}`)),modules}async _writePackageJson(){return!!await this._fileHelper.writeFile(path.join(this._folderPath,"package.json"),JSON.stringify(await this._preparePackageJson(),null,4))}async _preparePackageJson(){const packageJson=this._packageJson;return this.info("Package Author: ",!1),packageJson.author=await this.waitForPrompt(),packageJson.name=this._name,packageJson.description=this._name,this._frontend&&this._backend?packageJson.scripts={build:"npm run build:backend && npm run build:frontend","build:backend":"gulp","build:frontend":"cd frontend && webpack --config config/webpack.prod.js --progress --profile --bail",cli:"cd backend && ts-node -r tsconfig-paths/register index.ts",dev:'concurrently --kill-others "npm run dev:backend" "npm run dev:frontend"',"dev:backend":"cd backend && ts-node-dev --ignore-watch node_modules --poll --inspect --pretty --respawn --watch .env -r tsconfig-paths/register server.ts","dev:frontend":"cd frontend && webpack serve",tsc:"tsc"}:this._frontend?packageJson.scripts={build:"webpack --config config/webpack.prod.js --progress --profile --bail",dev:"webpack serve"}:this._backend&&(packageJson.scripts={build:"gulp",cli:"ts-node -r tsconfig-paths/register index.ts",dev:"ts-node-dev --ignore-watch node_modules --pretty --inspect --respawn --watch .env -r tsconfig-paths/register server.ts"}),this.info("Package Version (1.0.0): ",!1),packageJson.version=await this.waitForPrompt()||packageJson.version,packageJson}async _addCommonFiles(){for(let i=0,n=this._commonFiles.length;i<n;i++)if(!await this._processFile(this._commonFiles[i],this._folderPath))return!1;return!0}async _createFrontendApp(){for(let i=0,n=this._frontendFiles.length;this._frontend&&i<n;i++)if((this._backend&&!1!==this._frontendFiles[i].withBackend||!this._backend&&!0!==this._frontendFiles[i].withBackend)&&!await this._processFile(this._frontendFiles[i],this._frontendFolder,!0))return!1;return!0}async _createBackendApp(){for(let i=0,n=this._backendFiles.length;this._backend&&i<n;i++)if((this._frontend&&!1!==this._backendFiles[i].withFrontend||!this._frontend&&!0!==this._backendFiles[i].withFrontend)&&!await this._processFile(this._backendFiles[i],this._backendFolder))return!1;return!0}async _processFile(file,basePath,frontend=!1){let dataToWrite=(await this._fileHelper.readFile(path.join(__dirname,"..","data",file.content))).toString();if(file.placeholders)for(let i=0,n=file.placeholders.length;i<n;i++){const placeholder=file.placeholders[i];let result=null;if(frontend){const completed=_.find(this._frontEndPlaceholders,["name",placeholder.name]);completed&&(result=completed.value(placeholder.name))}!result&&placeholder.prompt?(this.info(`${placeholder.prompt}: `,!1),result=await this.waitForPrompt()):!result&&placeholder.value&&(result=await placeholder.value(this._name));const regex=new RegExp(`{{${placeholder.name}}}`,"gi");dataToWrite=dataToWrite.replace(regex,""!==result?result:void 0!==placeholder.default?placeholder.default:"")}return!!await this._fileHelper.writeFile(path.join(basePath,...file.location,file.name),dataToWrite)||(this.error(`An error has occurred trying to create the file ${path.join(__dirname,this._folderPath,...file.location)}.`),!1)}async _createAppFolders(name){return this._folderPath=path.join(this._folderPath,name),!0===await this._fileHelper.exists(this._folderPath)?(this.error(`Folder ${name} already exists, please use another name.`),!1):!!await this._createBackendStructure()&&!!await this._createFrontendStructure()&&!!await this._createCommonFolders()}async _createBackendStructure(){this._backendFolder=this._frontend?path.join(this._folderPath,"backend"):this._folderPath;for(let i=0,n=this._backendStructure.length;this._backend&&i<n;i++)if(!await this._fileHelper.createFolder(path.join(this._backendFolder,...this._backendStructure[i])))return this.error(`An error has occurred trying to create folder ${path.join(this._backendFolder,...this._backendStructure[i])}.`),!1;return!0}async _createFrontendStructure(){this._frontendFolder=this._backend?path.join(this._folderPath,"frontend"):this._folderPath;for(let i=0,n=this._frontendStructure.length;this._frontend&&i<n;i++)if(!await this._fileHelper.createFolder(path.join(this._frontendFolder,...this._frontendStructure[i])))return this.error(`An error has occurred trying to create folder ${path.join(this._frontendFolder,...this._frontendStructure[i])}.`),!1;return!0}async _createCommonFolders(){for(let i=0,n=this._commonFolders.length;i<n;i++)if(!await this._fileHelper.createFolder(path.join(this._folderPath,...this._commonFolders[i])))return this.error(`An error has occurred trying to create folder ${path.join(this._folderPath,...this._commonFolders[i])}.`),!1;return!0}async _addVsCodeDebug(){if(!await this._fileHelper.createFolder(path.join(this._folderPath,".vscode")))return this.error(`An error has occurred trying to create folder ${path.join(this._folderPath,".vscode")}.`),!1;if(!await this._fileHelper.writeFile(path.join(this._folderPath,".vscode","settings.json"),JSON.stringify({"codemetrics.basics.CodeLensHiddenUnder":5,"codemetrics.basics.ComplexityLevelExtreme":40,"codemetrics.basics.ComplexityLevelHigh":30,"codemetrics.basics.ComplexityLevelLow":5,"codemetrics.basics.ComplexityLevelNormal":20},null,4)))return this.error(`An error has occurred trying to write ${path.join(this._folderPath,".vscode","settings.json")}.`),!1;const launch={configurations:[],version:"0.2.0"};if(this._backend&&launch.configurations.push({cwd:"${workspaceRoot}"+(this._frontend?"/backend":""),name:"Attach to Server Debug",port:9229,protocol:"inspector",request:"attach",restart:!0,type:"node"}),this._frontend){const ip=_.find(this._frontEndPlaceholders,["name","ip-address"]);launch.configurations.push({disableNetworkCache:!0,name:"Launch Chrome for Frontend",request:"launch",sourceMapPathOverrides:{"webpack:///./src/*":"${webRoot}/*"},sourceMaps:!0,trace:!0,type:"chrome",url:`http://${ip&&ip.value?ip.value("ip-address"):"127.0.0.1"}:5555`,webRoot:this._backend?"${workspaceRoot}/frontend/src":"${workspaceRoot}/src"})}return!!await this._fileHelper.writeFile(path.join(this._folderPath,".vscode","launch.json"),JSON.stringify(launch,null,4))||(this.error(`An error has occurred trying to write ${path.join(this._folderPath,".vscode","launch.json")}.`),!1)}async _addEslintConfig(){return!!await this._fileHelper.writeFile(path.join(this._folderPath,"eslint.config.mjs"),ESLINT_CONFIG)||(this.error("An error has occurred trying to write eslint.config.mjs."),!1)}}exports.CreateApp=CreateApp;