servest
Version:
Add optional backend files and features to any project — ESLint, Prettier, Mongoose, and more.
316 lines (284 loc) • 31.5 kB
JavaScript
import{createRequire as e}from"node:module";import{Command as t}from"commander";import n from"fs";import r from"path";import{spawn as i}from"child_process";var a=Object.create,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty,d=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),f=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=c(t),a=0,l=i.length,d;a<l;a++)d=i[a],!u.call(e,d)&&d!==n&&o(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=s(t,d))||r.enumerable});return e},p=(e,t,n)=>(n=e==null?{}:a(l(e)),f(t||!e||!e.__esModule?o(n,`default`,{value:e,enumerable:!0}):n,e)),m=e(import.meta.url),ee=`servest`,te=`0.1.0`,ne=`module`,h=`MIT`,g=`Rashedin Islam <https://www.rashedin.dev>`,_=`Add optional backend files and features to any project — ESLint, Prettier, Mongoose, and more.`,v={node:`>=18`},y={servest:`index.js`,"add-servest":`index.js`},b={dev:`tsdown --watch`,build:`tsdown`,typecheck:`tsc`,prepublishOnly:`npm run build`},x=[`backend`,`cli`,`addons`,`utilities`,`project-tools`],re=[`dist`,`index.js`,`command/**`],ie={type:`git`,url:`git+https://github.com/dev-rashedin/servest.git`,directory:`packages/servest-addons`},ae={url:`https://github.com/dev-rashedin/servest/issues`},oe=`https://github.com/dev-rashedin/servest/tree/main/packages/servest#readme`,S={commander:`^14.0.0`,kleur:`^4.1.5`,ora:`^8.2.0`},C={"@types/node":`^24.3.0`},w={name:ee,version:te,type:ne,license:h,author:g,description:_,engines:v,bin:y,scripts:b,keywords:x,files:re,repository:ie,bugs:ae,homepage:oe,dependencies:S,devDependencies:C};const T={reset:`\x1B[0m`,bold:`\x1B[1m`,red:`\x1B[31m`,green:`\x1B[32m`,yellow:`\x1B[33m`,blue:`\x1B[34m`,magenta:`\x1B[35m`,cyan:`\x1B[36m`,dimGray:`\x1B[2m`};function E(e){return`${T.red}${e}${T.reset}`}function D(e){return`${T.yellow}${e}${T.reset}`}function O(e){return`${T.green}${e}${T.reset}`}function k(e){return`${T.cyan}${e}${T.reset}`}const A=(e=`Operation cancelled`)=>{console.error(E(e)),process.exit(1)};function j(e=process.cwd()){return n.existsSync(r.join(e,`pnpm-lock.yaml`))?`pnpm`:n.existsSync(r.join(e,`yarn.lock`))?`yarn`:n.existsSync(r.join(e,`bun.lockb`))?`bun`:`npm`}const M=[`routes`,`models`,`controllers`,`services`],N=e=>{let t=r.join(e,`servest.config.json`);return n.existsSync(t)?JSON.parse(n.readFileSync(t,`utf-8`)):null},P=(e,t,i)=>{let a=!1,o=!1,s=e=>n.existsSync(e)?(o=!0,!1):(n.writeFileSync(e,``),a=!0,!0);if(i.architecture===`mvc`)M.forEach(a=>{let o=r.join(e,a);n.existsSync(o)||n.mkdirSync(o,{recursive:!0});let c=i.language===`ts`?`ts`:`js`,l=r.join(o,`${t}.${a.slice(0,-1)}.${c}`);s(l)});else if(i.architecture===`modular`){let a=r.join(e,`modules`,t);n.existsSync(a)||n.mkdirSync(a,{recursive:!0});let o=i.language===`ts`?`ts`:`js`;M.forEach(e=>{let n=r.join(a,`${t}.${e.slice(0,-1)}.${o}`);s(n)})}else{let n=i.language===`ts`?`ts`:`js`,a=r.join(e,`${t}.${n}`);s(a)}a&&console.info(O(`✅ Feature "${t}" files created based on ${i.architecture} structure.`)),o&&console.log(D(`👍 Some files for "${t}" already existed. Skipped them.`))},F=(e,t)=>{[`express`,`nest`,`fastify`,`koa`].includes(e)||A(`Cannot add "${t}": detected framework is ${e}. Only Node.js frameworks are supported for this feature.`)},I=(e,t)=>{switch(e){case`pnpm`:case`yarn`:case`bun`:return`${e} add ${t}`;case`npm`:default:return`npm install ${t}`}},L=(e,t)=>{switch(e){case`npm`:return`npm install -D ${t}`;case`yarn`:return`yarn add -D ${t}`;case`pnpm`:return`pnpm add -D ${t}`;case`bun`:return`bun add -d ${t}`;default:throw Error(`Unsupported package manager: ${e}`)}},R=e=>{let t=r.join(e,`src`),i=r.join(t,`app`);return n.existsSync(i)?i:n.existsSync(t)?t:e},z=(e,t)=>{let i=r.join(e,`package.json`);if(!n.existsSync(i))return!1;let a=JSON.parse(n.readFileSync(i,`utf-8`));return a.dependencies&&a.dependencies[t]||a.devDependencies&&a.devDependencies[t]},B=e=>{let t=r.join(e,`package.json`);if(!n.existsSync(t))return!1;let i=JSON.parse(n.readFileSync(t,`utf-8`));return i.type===`module`};var V=d(((exports,t)=>{t.exports=a,a.sync=o;var n=m(`fs`);function r(e,t){var n=t.pathExt===void 0?process.env.PATHEXT:t.pathExt;if(!n||(n=n.split(`;`),n.indexOf(``)!==-1))return!0;for(var r=0;r<n.length;r++){var i=n[r].toLowerCase();if(i&&e.substr(-i.length).toLowerCase()===i)return!0}return!1}function i(e,t,n){return!e.isSymbolicLink()&&!e.isFile()?!1:r(t,n)}function a(e,t,r){n.stat(e,function(n,a){r(n,n?!1:i(a,e,t))})}function o(e,t){return i(n.statSync(e),e,t)}})),H=d(((exports,t)=>{t.exports=r,r.sync=i;var n=m(`fs`);function r(e,t,r){n.stat(e,function(e,n){r(e,e?!1:a(n,t))})}function i(e,t){return a(n.statSync(e),t)}function a(e,t){return e.isFile()&&o(e,t)}function o(e,t){var n=e.mode,r=e.uid,i=e.gid,a=t.uid===void 0?process.getuid&&process.getuid():t.uid,o=t.gid===void 0?process.getgid&&process.getgid():t.gid,s=64,c=8,l=1,u=s|c,d=n&l||n&c&&i===o||n&s&&r===a||n&u&&a===0;return d}})),U=d(((exports,t)=>{m(`fs`);var n;n=process.platform===`win32`||global.TESTING_WINDOWS?V():H(),t.exports=r,r.sync=i;function r(e,t,i){if(typeof t==`function`&&(i=t,t={}),!i){if(typeof Promise!=`function`)throw TypeError(`callback not provided`);return new Promise(function(n,i){r(e,t||{},function(e,t){e?i(e):n(t)})})}n(e,t||{},function(e,n){e&&(e.code===`EACCES`||t&&t.ignoreErrors)&&(e=null,n=!1),i(e,n)})}function i(e,t){try{return n.sync(e,t||{})}catch(e){if(t&&t.ignoreErrors||e.code===`EACCES`)return!1;throw e}}})),W=d(((exports,t)=>{let n=process.platform===`win32`||process.env.OSTYPE===`cygwin`||process.env.OSTYPE===`msys`,r=m(`path`),i=n?`;`:`:`,a=U(),o=e=>Object.assign(Error(`not found: ${e}`),{code:`ENOENT`}),s=(e,t)=>{let r=t.colon||i,a=e.match(/\//)||n&&e.match(/\\/)?[``]:[...n?[process.cwd()]:[],...(t.path||process.env.PATH||``).split(r)],o=n?t.pathExt||process.env.PATHEXT||`.EXE;.CMD;.BAT;.COM`:``,s=n?o.split(r):[``];return n&&e.indexOf(`.`)!==-1&&s[0]!==``&&s.unshift(``),{pathEnv:a,pathExt:s,pathExtExe:o}},c=(e,t,n)=>{typeof t==`function`&&(n=t,t={}),t||(t={});let{pathEnv:i,pathExt:c,pathExtExe:l}=s(e,t),u=[],d=n=>new Promise((a,s)=>{if(n===i.length)return t.all&&u.length?a(u):s(o(e));let c=i[n],l=/^".*"$/.test(c)?c.slice(1,-1):c,d=r.join(l,e),p=!l&&/^\.[\\\/]/.test(e)?e.slice(0,2)+d:d;a(f(p,n,0))}),f=(e,n,r)=>new Promise((i,o)=>{if(r===c.length)return i(d(n+1));let s=c[r];a(e+s,{pathExt:l},(a,o)=>{if(!a&&o)if(t.all)u.push(e+s);else return i(e+s);return i(f(e,n,r+1))})});return n?d(0).then(e=>n(null,e),n):d(0)},l=(e,t)=>{t=t||{};let{pathEnv:n,pathExt:i,pathExtExe:c}=s(e,t),l=[];for(let o=0;o<n.length;o++){let s=n[o],u=/^".*"$/.test(s)?s.slice(1,-1):s,d=r.join(u,e),f=!u&&/^\.[\\\/]/.test(e)?e.slice(0,2)+d:d;for(let e=0;e<i.length;e++){let n=f+i[e];try{let e=a.sync(n,{pathExt:c});if(e)if(t.all)l.push(n);else return n}catch(e){}}}if(t.all&&l.length)return l;if(t.nothrow)return null;throw o(e)};t.exports=c,c.sync=l})),G=d(((exports,t)=>{let n=(e={})=>{let t=e.env||process.env,n=e.platform||process.platform;return n===`win32`?Object.keys(t).reverse().find(e=>e.toUpperCase()===`PATH`)||`Path`:`PATH`};t.exports=n,t.exports.default=n})),K=d(((exports,t)=>{let n=m(`path`),r=W(),i=G();function a(e,t){let a=e.options.env||process.env,o=process.cwd(),s=e.options.cwd!=null,c=s&&process.chdir!==void 0&&!process.chdir.disabled;if(c)try{process.chdir(e.options.cwd)}catch(e){}let l;try{l=r.sync(e.command,{path:a[i({env:a})],pathExt:t?n.delimiter:void 0})}catch(e){}finally{c&&process.chdir(o)}return l&&(l=n.resolve(s?e.options.cwd:``,l)),l}function o(e){return a(e)||a(e,!0)}t.exports=o})),q=d(((exports,t)=>{let n=/([()\][%!^"`<>&|;, *?])/g;function r(e){return e=e.replace(n,`^$1`),e}function i(e,t){return e=`${e}`,e=e.replace(/(?=(\\+?)?)\1"/g,`$1$1\\"`),e=e.replace(/(?=(\\+?)?)\1$/,`$1$1`),e=`"${e}"`,e=e.replace(n,`^$1`),t&&(e=e.replace(n,`^$1`)),e}t.exports.command=r,t.exports.argument=i})),J=d(((exports,t)=>{t.exports=/^#!(.*)/})),se=d(((exports,t)=>{let n=J();t.exports=(e=``)=>{let t=e.match(n);if(!t)return null;let[r,i]=t[0].replace(/#! ?/,``).split(` `),a=r.split(`/`).pop();return a===`env`?i:i?`${a} ${i}`:a}})),ce=d(((exports,t)=>{let n=m(`fs`),r=se();function i(e){let t=Buffer.alloc(150),i;try{i=n.openSync(e,`r`),n.readSync(i,t,0,150,0),n.closeSync(i)}catch(e){}return r(t.toString())}t.exports=i})),le=d(((exports,t)=>{let n=m(`path`),r=K(),i=q(),a=ce(),o=process.platform===`win32`,s=/\.(?:com|exe)$/i,c=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function l(e){e.file=r(e);let t=e.file&&a(e.file);return t?(e.args.unshift(e.file),e.command=t,r(e)):e.file}function u(e){if(!o)return e;let t=l(e),r=!s.test(t);if(e.options.forceShell||r){let r=c.test(t);e.command=n.normalize(e.command),e.command=i.command(e.command),e.args=e.args.map(e=>i.argument(e,r));let a=[e.command].concat(e.args).join(` `);e.args=[`/d`,`/s`,`/c`,`"${a}"`],e.command=process.env.comspec||`cmd.exe`,e.options.windowsVerbatimArguments=!0}return e}function d(e,t,n){t&&!Array.isArray(t)&&(n=t,t=null),t=t?t.slice(0):[],n=Object.assign({},n);let r={command:e,args:t,options:n,file:void 0,original:{command:e,args:t}};return n.shell?r:u(r)}t.exports=d})),ue=d(((exports,t)=>{let n=process.platform===`win32`;function r(e,t){return Object.assign(Error(`${t} ${e.command} ENOENT`),{code:`ENOENT`,errno:`ENOENT`,syscall:`${t} ${e.command}`,path:e.command,spawnargs:e.args})}function i(e,t){if(!n)return;let r=e.emit;e.emit=function(n,i){if(n===`exit`){let n=a(i,t);if(n)return r.call(e,`error`,n)}return r.apply(e,arguments)}}function a(e,t){return n&&e===1&&!t.file?r(t.original,`spawn`):null}function o(e,t){return n&&e===1&&!t.file?r(t.original,`spawnSync`):null}t.exports={hookChildProcess:i,verifyENOENT:a,verifyENOENTSync:o,notFoundError:r}})),Y=d(((exports,t)=>{let n=m(`child_process`),r=le(),i=ue();function a(e,t,a){let o=r(e,t,a),s=n.spawn(o.command,o.args,o.options);return i.hookChildProcess(s,o),s}function o(e,t,a){let o=r(e,t,a),s=n.spawnSync(o.command,o.args,o.options);return s.error=s.error||i.verifyENOENTSync(s.status,o),s}t.exports=a,t.exports.spawn=a,t.exports.sync=o,t.exports._parse=r,t.exports._enoent=i})),de=p(Y(),1);async function fe({cwd:e,baseDir:t,config:i,packageManager:a}){F(i.framework,`mongoose`);let o=i.language===`ts`,s=I(a,`mongoose`),c=B(e);z(e,`mongoose`)?console.log(D(`👍 mongoose already installed.`)):(console.log(k(`⬇️ Installing mongoose...`)),await new Promise((t,n)=>{let r=(0,de.default)(s,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>{e===0?t():n(Error(E(`Installation failed with exit code ${e}`)))}),r.on(`error`,n)}));let l=r.join(t,`config`);n.existsSync(l)||(n.mkdirSync(l,{recursive:!0}),console.log(O(`📁 Created config/ directory.`)));let u=r.join(l,`connectDB.${o?`ts`:`js`}`);if(n.existsSync(u))console.log(D(`👍 connectDB already exists.`));else{let e=o?`
import mongoose from "mongoose";
export async function connectDB(): Promise<void> {
try {
const conn = await mongoose.connect(MONGO_URI!);
console.log(\`MongoDB connected: \${conn.connection.host}\`);
} catch (error) {
console.error("MongoDB connection failed", error);
process.exit(1);
}
}
`:c?`
import mongoose from "mongoose";
export async function connectDB() {
try {
const conn = await mongoose.connect(MONGO_URI);
console.log(\`MongoDB connected: \${conn.connection.host}\`);
} catch (error) {
console.error("MongoDB connection failed", error);
process.exit(1);
}
}
`:`
const mongoose = require("mongoose");
async function connectDB() {
try {
const conn = await mongoose.connect(MONGO_URI);
console.log(\`MongoDB connected: \${conn.connection.host}\`);
} catch (error) {
console.error("MongoDB connection failed", error);
process.exit(1);
}
}
module.exports = { connectDB };
`;n.writeFileSync(u,e,`utf8`),console.log(O(`✅ Created connectDB file.`))}let d=[`src/server`,`src/app`].map(t=>r.join(e,`${t}.${o?`ts`:`js`}`)),f=d.find(e=>n.existsSync(e));if(f){let e=n.readFileSync(f,`utf8`);if(!e.includes(`connectDB`)){let t=c||o?`import { connectDB } from "../src/app/config/connectDB";`:`const { connectDB } = require("../src/app/config/connectDB");`,r=e.indexOf(`app.listen`);if(r!==-1){let t=e.indexOf(`});`,r);e=t===-1?e.slice(0,r).trimEnd():e.slice(0,r).trimEnd()+e.slice(t+3)}let i=e.trimEnd()+`
`,a=`${t}\n${i}(async () => {
try {
await connectDB();
app.listen(config.port, () => {
console.log(\`Server listening on port http://localhost:\${config.port}\`);
});
} catch (err) {
console.error('Failed to connect DB or start server:', err);
process.exit(1);
}
})();`;n.writeFileSync(f,a,`utf8`)}}else console.log(D(`👍 Could not find src/server or src/app to inject connectDB call.`));console.log(O(`🎉 Mongoose setup completed!`))}const pe={printWidth:100,tabWidth:2,useTabs:!1,semi:!0,singleQuote:!0,trailingComma:`all`,bracketSpacing:!0,arrowParens:`avoid`,endOfLine:`lf`},X=(e,t,i)=>{let a=B(e),o=t||a?`eslint.config.mjs`:`eslint.config.cjs`,s=r.join(e,o),c;n.existsSync(s)?console.log(D(`👍 ESLint config already exists.`)):(c=i===`eslint`?t?`import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import { defineConfig } from 'eslint/config';
export default defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
]);
`:a?`import eslint from '@eslint/js';
import globals from 'globals';
import { defineConfig } from 'eslint/config';
export default defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
]);
`:`const eslint = require('@eslint/js');
const globals = require('globals');
const { defineConfig } = require('eslint/config');
module.exports = defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'script',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
]);
`:t?`import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
import { defineConfig } from 'eslint/config';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';
export default defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
// Prettier plugin (runs Prettier as a rule)
eslintPluginPrettierRecommended,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
// Disable conflicting formatting rules (always last)
eslintConfigPrettier,
]);
`:a?`import eslint from '@eslint/js';
import globals from 'globals';
import { defineConfig } from 'eslint/config';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';
export default defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
// Prettier plugin (runs Prettier as a rule)
eslintPluginPrettierRecommended,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
// Disable conflicting formatting rules (always last)
eslintConfigPrettier,
]);
`:`const eslint = require('@eslint/js');
const globals = require('globals');
const { defineConfig } = require('eslint/config');
const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended');
const eslintConfigPrettier = require('eslint-config-prettier');
module.exports = defineConfig([
// Ignore patterns
{
ignores: ['node_modules', 'dist', 'build', 'coverage'],
},
// Base configs
eslint.configs.recommended,
// Prettier plugin (runs Prettier as a rule)
eslintPluginPrettierRecommended,
// Custom overrides
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'script',
},
globals: globals.node,
},
settings: {
node: { version: '>=18.0.0' },
},
rules: {
eqeqeq: 'error',
'no-console': 'warn',
'no-unused-vars': 'warn',
'no-unused-expressions': 'error',
'prefer-const': ['error', { ignoreReadBeforeAssign: true }],
},
},
// Disable conflicting formatting rules (always last)
eslintConfigPrettier,
]);
`,n.writeFileSync(s,c,`utf-8`),console.log(O(`✅ ESLint config created.`)))},Z=e=>{let t=r.join(e,`.prettierrc.json`),i=r.join(e,`.prettierignore`);n.existsSync(t)?console.log(D(`👍 .prettierrc.json already exists.`)):(n.writeFileSync(t,JSON.stringify(pe,null,2)),console.log(O(`✅ Created .prettierrc.json.`))),n.existsSync(i)?console.log(D(`⚠️ .prettierignore already exists.`)):(n.writeFileSync(i,`dist/
node_modules/
pnpm-lock.yaml
yarn.lock
package-lock.json
coverage/
build/
*.log
*.tsbuildinfo`,`utf-8`),console.log(O(`✅ Created .prettierignore.`)))};var me=p(Y(),1);async function he({cwd:e,config:t,packageManager:i}){F(t.framework,`eslint`);let a=t.language===`ts`,o=[`eslint@9.34.0`,`globals@16.3.0`,`@eslint/js@9.34.0`];a&&o.push(`typescript-eslint@8.41.0`);let s=L(i,o.join(` `));z(e,`eslint`)?console.log(D(`👍 ESLint already installed.`)):(console.log(k(`⬇️ Installing ESLint and recommended plugins...`)),await new Promise((t,n)=>{let r=(0,me.default)(s,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)})),X(e,a,`eslint`);let c=r.join(e,`package.json`);if(n.existsSync(c)){let e=JSON.parse(n.readFileSync(c,`utf-8`));if(e.scripts=e.scripts||{},e.scripts.lint&&e.scripts[`lint:fix`])return;e.scripts.lint||(e.scripts.lint=`eslint .`),e.scripts[`lint:fix`]||(e.scripts[`lint:fix`]=`eslint . --fix`),n.writeFileSync(c,JSON.stringify(e,null,2),`utf-8`),console.log(O(`✅ Added lint scripts to package.json.`))}console.log(O(`🎉 ESLint setup completed!`))}var ge=p(Y(),1);async function _e({cwd:e,config:t,packageManager:i}){F(t.framework,`prettier`);let a=[`prettier@3.6.2`],o=L(i,a.join(` `));z(e,`prettier`)?console.log(D(`👍 Prettier already installed`)):(console.log(k(`⬇️ Installing prettier...`)),await new Promise((t,n)=>{let r=(0,ge.default)(o,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)})),Z(e);let s=r.join(e,`package.json`);if(n.existsSync(s)){let e=JSON.parse(n.readFileSync(s,`utf-8`));if(e.scripts=e.scripts||{},e.scripts.prettier&&e.scripts[`prettier:fix`])return;e.scripts.prettier||(e.scripts.prettier=`prettier --ignore-path .gitignore --check "./src/**/*.+(js|ts|json)"`),e.scripts[`prettier:fix`]||(e.scripts[`prettier:fix`]=`prettier --ignore-path .gitignore --write "./src/**/*.+(js|ts|json)"`),n.writeFileSync(s,JSON.stringify(e,null,2),`utf-8`),console.log(O(`✅ Added lint scripts to package.json.`))}console.log(O(`🎉 Prettier setup completed!`))}var ve=p(Y(),1);async function ye({cwd:e,config:t,packageManager:i}){F(t.framework,`eslint-prettier`);let a=t.language===`ts`,o=[`eslint@9.34.0`,`globals@16.3.0`,`@eslint/js@9.34.0`,`eslint-plugin-prettier@5.5.4`,`eslint-config-prettier@10.1.8`];a&&o.push(`typescript-eslint@8.41.0`);let s=L(i,o.join(` `));z(e,`eslint`)&&z(e,`prettier`)?console.log(D(`👍 ESLint & Prettier already installed.`)):(console.log(k(`⬇️ Installing ESLint, Prettier and recommended plugins...`)),await new Promise((t,n)=>{let r=(0,ve.default)(s,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)})),X(e,a,`eslint-prettier`),Z(e);let c=r.join(e,`package.json`);if(n.existsSync(c)){let e=JSON.parse(n.readFileSync(c,`utf-8`));if(e.scripts=e.scripts||{},e.scripts.lint&&e.scripts[`lint:fix`]&&e.scripts.prettier&&e.scripts[`prettier:fix`])return;e.scripts.lint||(e.scripts.lint=`eslint .`),e.scripts[`lint:fix`]||(e.scripts[`lint:fix`]=`eslint . --fix`),e.scripts.prettier||(e.scripts.prettier=`prettier --ignore-path .gitignore --write "./src/**/*.+(js|ts|json)"`),e.scripts[`prettier:fix`]||(e.scripts[`prettier:fix`]=`prettier --write "./src/**/*.+(js|ts|json)"`),n.writeFileSync(c,JSON.stringify(e,null,2),`utf-8`),console.log(O(`✅ Added lint & prettier scripts to package.json.`))}console.log(O(`🎉 ESLint setup completed!`))}var Q=p(Y(),1);async function be({cwd:e,config:t,packageManager:i}){F(t.framework,`mongoose`);let a=[`prisma@6.15.0`],o=[`@prisma/client@6.15.0`],s=L(i,a.join(` `)),c=I(i,o.join(` `));z(e,`prisma`)?console.log(D(`👍 Prisma CLI already installed`)):(console.log(k(`⬇️ Installing Prisma CLI as dev dependency...`)),await new Promise((t,n)=>{let r=(0,Q.default)(s,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)})),z(e,`@prisma/client`)?console.log(D(`👍 @prisma/client already installed`)):(console.log(k(`⬇️ Installing @prisma/client as runtime dependency...`)),await new Promise((t,n)=>{let r=(0,Q.default)(c,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)}));let l=r.join(e,`prisma`);n.existsSync(l)||n.mkdirSync(l);let u=r.join(l,`schema.prisma`);n.existsSync(u)?console.log(D(`⚠️ prisma/schema.prisma already exists`)):(n.writeFileSync(u,`generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}`,`utf-8`),console.log(O(`✅ Created prisma/schema.prisma`)));let d=r.join(e,`.env`);n.existsSync(d)?console.log(D(`⚠️ .env already exists`)):(n.writeFileSync(d,`DATABASE_URL="postgresql://user:password@localhost:5432/dbname"`,`utf-8`),console.log(O(`✅ Created .env for Prisma`)));let f=r.join(e,`package.json`);if(n.existsSync(f)){let e=JSON.parse(n.readFileSync(f,`utf-8`));e.scripts=e.scripts||{},e.scripts[`prisma:generate`]||(e.scripts[`prisma:generate`]=`prisma generate`),e.scripts[`prisma:migrate`]||(e.scripts[`prisma:migrate`]=`prisma migrate dev`),n.writeFileSync(f,JSON.stringify(e,null,2),`utf-8`),console.log(O(`✅ Added Prisma scripts to package.json`))}console.log(O(`🎉 Prisma setup completed!`))}var xe=p(Y(),1);async function Se({cwd:e,config:t,packageManager:i}){F(t.framework,`drizzle`);let a=t.language===`ts`,o=a?`schema.ts`:`schema.js`,s=a?`client.ts`:`client.js`,c=[`drizzle-orm`,`sqlite3`],l=I(i,c.join(` `));z(e,`drizzle-orm`)?console.log(D(`👍 Drizzle ORM already installed`)):(console.log(k(`⬇️ Installing Drizzle ORM and SQLite...`)),await new Promise((t,n)=>{let r=(0,xe.default)(l,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)}));let u=r.join(e,`src`,`db`);n.existsSync(u)||n.mkdirSync(u,{recursive:!0});let d=r.join(u,s);n.existsSync(d)||(n.writeFileSync(d,`import { drizzle } from 'drizzle-orm/libsql';
import sqlite3 from 'sqlite3';
const db = new sqlite3.Database('./dev.db');
export const client = drizzle(db);`,`utf-8`),console.log(O(`✅ Drizzle client created at src/db/client.ts`)));let f=r.join(u,o);n.existsSync(f)||(n.writeFileSync(f,`import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
import { client } from './../../../dist/index';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name'),
});`,`utf-8`),console.log(O(`✅ Drizzle schema created at src/db/schema.ts`)));let p=r.join(e,`package.json`);if(n.existsSync(p)){let e=JSON.parse(n.readFileSync(p,`utf-8`));e.scripts=e.scripts||{},e.scripts[`drizzle:generate`]||(e.scripts[`drizzle:generate`]=`drizzle-kit generate`),n.writeFileSync(p,JSON.stringify(e,null,2),`utf-8`),console.log(O(`✅ Added Drizzle scripts to package.json.`))}console.log(O(`🎉 Drizzle setup completed!`))}async function Ce({cwd:e,config:t,packageManager:a}){F(t.framework,`lint-staged`);let o=j(e),s=[`lint-staged`,`simple-git-hooks`],c=L(a,s.join(` `)),l=r.join(e,`package.json`);if(!n.existsSync(l)){console.log(E(`🚨 package.json not found.`));return}let u=s.filter(t=>!z(e,t));u.length>0?(console.log(k(`⬇️ Installing ${u.join(`, `)}...`)),await new Promise((t,n)=>{let r=i(c,{cwd:e,stdio:`inherit`,shell:!0});r.on(`close`,e=>e===0?t():n(Error(E(`Installation failed.`)))),r.on(`error`,n)})):console.log(D(`👍 lint-staged and simple-git-hooks already installed`));let d=JSON.parse(n.readFileSync(l,`utf-8`));d.scripts=d.scripts||{},(!d.scripts.postinstall||!d.scripts.postinstall.includes(`simple-git-hooks`))&&(d.scripts.postinstall=`simple-git-hooks`,console.log(O(`✅ Added postinstall script.`))),d[`simple-git-hooks`]=d[`simple-git-hooks`]||{"pre-commit":`${o} exec lint-staged -- --concurrent false`},d[`lint-staged`]=d[`lint-staged`]||{"*":[`prettier --write --cache --ignore-unknown`]},n.writeFileSync(l,JSON.stringify(d,null,2)),console.log(O(`✅ Updated package.json with simple-git-hooks and lint-staged configs.`))}const $=j(),we=new t().name(`add`).description(`Add features or generate files/folders for your project`).argument(`<features...>`,`Feature names or f-commands (e.g., mongoose, f-users)`).action(e=>{let t=process.cwd(),n=N(t),r=R(t);n||A(`servest.config.json not found. Please run "npx servest@latest init" first.`);let i={cwd:t,config:n,packageManager:$},a={mongoose:async()=>fe({cwd:t,baseDir:r,config:n,packageManager:$}),eslint:async()=>he(i),prettier:async()=>_e(i),"eslint-prettier":async()=>ye({cwd:t,config:n,packageManager:$}),prisma:async()=>be(i),drizzle:async()=>Se(i),"lint-staged":async()=>Ce(i)},o=async()=>{for(let t of e)try{if(t.startsWith(`f-`)){F(n.framework,t);let e=t.slice(2);await P(r,e,n)}else a[t]?await a[t]():console.log(E(`🚩 Feature "${t}" not recognized.`))}catch(e){console.error(E(`🚨 Failed to process "${t}": ${e}`))}};o().catch(e=>{console.error(E(`🚨 An unexpected error occurred: ${e}`))})});Y();const Te=e=>{let t=r.join(e,`package.json`);if(n.existsSync(t)){let e=JSON.parse(n.readFileSync(t,`utf-8`));if(e.dependencies?.express)return`express`;if(e.dependencies?.nestjs)return`nestjs`;if(e.dependencies?.koa)return`koa`;if(e.dependencies?.fastify)return`fastify`}if(n.existsSync(r.join(e,`manage.py`)))return`django`;if(n.existsSync(r.join(e,`requirements.txt`))){let t=n.readFileSync(r.join(e,`requirements.txt`),`utf-8`);if(t.includes(`Flask`))return`flask`;if(t.includes(`fastapi`))return`fastapi`}if(n.existsSync(r.join(e,`pyproject.toml`))){let t=n.readFileSync(r.join(e,`pyproject.toml`),`utf-8`);if(t.includes(`fastapi`))return`fastapi`}if(n.existsSync(r.join(e,`artisan`)))return`laravel`;if(n.existsSync(r.join(e,`composer.json`))){let t=JSON.parse(n.readFileSync(r.join(e,`composer.json`),`utf-8`));if(t.require&&t.require[`laravel/framework`])return`laravel`}return`unknown`},Ee=e=>n.existsSync(r.join(e,`tsconfig.json`))?`ts`:n.existsSync(r.join(e,`package.json`))?`js`:n.existsSync(r.join(e,`requirements.txt`))||n.existsSync(r.join(e,`pyproject.toml`))||n.existsSync(r.join(e,`manage.py`))?`py`:n.existsSync(r.join(e,`composer.json`))||n.existsSync(r.join(e,`artisan`))?`php`:`unknown`,De=(e,t)=>{let i=[t,r.join(e,`src`,`app`),r.join(e,`src`),e].filter(Boolean);for(let e of i){if(!n.existsSync(e))continue;let t=n.readdirSync(e);if(t.includes(`modules`))return`modular`;if([`routes`,`controllers`,`models`].every(e=>t.includes(e)))return`mvc`;if(t.some(e=>e.endsWith(`.py`)||e===`apps`))return`apps-based`;if(t.includes(`app`)&&t.includes(`routes`))return`laravel-mvc`}return`basic`},Oe=e=>n.existsSync(r.join(e,`src`)),ke=()=>process.env.BUN_INSTALL?`bun`:process.version?`node`:n.existsSync(r.join(process.cwd(),`manage.py`))||n.existsSync(r.join(process.cwd(),`pyproject.toml`))?`python`:n.existsSync(r.join(process.cwd(),`artisan`))||n.existsSync(r.join(process.cwd(),`composer.json`))?`php`:`unknown`,Ae=(e,t)=>{let i=r.join(e,`servest.config.json`);n.writeFileSync(i,JSON.stringify(t,null,2)),console.log(O(`✅ servest.config.json created.`))},je=new t().name(`init`).description(`Detect project structure and create servest.config.json`).option(`--path <path>`,`Base folder for detection`,process.cwd()).action(e=>{let t=r.resolve(e.path),i=r.join(t,`servest.config.json`);if(n.existsSync(i)){console.log(D(`👍 servest.config.json already exists.`));return}let a=Te(t),o=Ee(t),s=De(t),c=Oe(t),l=ke(),u={servestVersion:`1.0.0`,framework:a,language:o,architecture:s,srcDir:c,environment:{runtime:l,nodeVersion:l===`node`?process.version:void 0,bunVersion:l===`bun`?process.env.BUN_VERSION:void 0},features:{},createdAt:new Date().toISOString()};Ae(t,u)});process.on(`SIGINT`,()=>process.exit(0)),process.on(`SIGTERM`,()=>process.exit(0));async function Me(){let e=new t().name(`servest`).description(`Add optional backend features to your project`).version(w.version||`1.0.0`,`-v, --version`,`display the version number`);e.addCommand(je).addCommand(we),e.parse()}Me();export{};