├── templates └── express │ ├── js │ ├── README.md │ ├── .vscode │ │ └── extensions.json │ ├── .prettierrc │ ├── package.json │ ├── src │ │ ├── logger.js │ │ ├── index.js │ │ └── config │ │ │ └── headers.js │ ├── .editorconfig │ └── _gitignore │ └── ts │ ├── README.md │ ├── .vscode │ └── extensions.json │ ├── types │ └── enviorment.d.ts │ ├── .prettierrc │ ├── package.json │ ├── src │ ├── logger.ts │ ├── index.ts │ └── config │ │ └── headers.ts │ ├── .editorconfig │ ├── tsconfig.json │ └── _gitignore ├── deprecated ├── setup.cfg ├── .pylintrc ├── pyproject.toml ├── .vscode │ ├── settings.json │ └── extensions.json ├── .github │ ├── ISSUE_TEMPLATE │ │ ├── enhancement_request.md │ │ ├── bug_report.md │ │ └── inconsistency_report.md │ └── pull_request_template.md ├── LICENCE ├── win_update_profile.ps1 ├── .gitignore ├── unix_update_profile.sh ├── README.md ├── without_args.py └── with_args.py ├── index.js ├── options ├── biome-prettier │ ├── extensions.json │ └── biome.json ├── eslint-prettier │ ├── extensions.json │ └── eslint.config.js ├── mongoose │ ├── ts │ │ ├── enviorment.d.ts │ │ ├── db.ts │ │ └── index.ts │ └── js │ │ ├── db.js │ │ └── index.js └── prisma │ ├── ts │ ├── enviorment.d.ts │ └── index.ts │ └── js │ └── index.js ├── .vscode └── extensions.json ├── .prettierrc ├── src ├── types │ ├── dependencyInstallers.d.ts │ └── prompt.d.ts ├── helpers │ ├── envMaker.ts │ ├── handleOptions.ts │ ├── printDependencies.ts │ ├── pkgManager.ts │ ├── fmt&Linters.ts │ └── db&Orms.ts ├── utils │ ├── fileSystem.ts │ ├── errorHandler.ts │ └── cli.ts └── index.ts ├── .github ├── workflows │ └── npmPublish.yml ├── ISSUE_TEMPLATE │ ├── enhancement_request.md │ ├── bug_report.md │ └── inconsistency_report.md └── pull_request_template.md ├── biome.json ├── .editorconfig ├── package.json ├── LICENCE ├── tsconfig.json ├── README.md └── .gitignore /templates/express/js/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/express/ts/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deprecated/setup.cfg: -------------------------------------------------------------------------------- 1 | [pep8] 2 | max-line-length = 120 -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('./dist/index.js'); -------------------------------------------------------------------------------- /deprecated/.pylintrc: -------------------------------------------------------------------------------- 1 | [MAIN] 2 | max-line-length = 120 3 | 4 | [MESSAGES CONTROL] 5 | disable=C0325 -------------------------------------------------------------------------------- /templates/express/js/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "editorconfig.editorconfig"] 3 | } 4 | -------------------------------------------------------------------------------- /templates/express/ts/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "editorconfig.editorconfig"] 3 | } 4 | -------------------------------------------------------------------------------- /deprecated/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "express-nitro" 3 | version = "1.0.0" 4 | 5 | [tool.ruff.lint.pydocstyle] 6 | convention = "google" -------------------------------------------------------------------------------- /options/biome-prettier/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "editorconfig.editorconfig","biomejs.biome"] 3 | } 4 | -------------------------------------------------------------------------------- /deprecated/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.typeCheckingMode": "basic", 3 | "python.analysis.autoImportCompletions": true, 4 | "autopep8.args": ["--max-line-length=120"] 5 | } 6 | -------------------------------------------------------------------------------- /options/eslint-prettier/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "editorconfig.editorconfig", 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "biomejs.biome", 4 | "esbenp.prettier-vscode", 5 | "editorconfig.editorconfig", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /templates/express/ts/types/enviorment.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace NodeJS { 3 | interface ProcessEnv { 4 | NODE_ENV: "development" | "production"; 5 | } 6 | } 7 | } 8 | 9 | export type {}; 10 | -------------------------------------------------------------------------------- /deprecated/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-python.autopep8", 4 | "ms-python.vscode-pylance", 5 | "ms-python.pylint", 6 | "ms-python.python", 7 | "ms-vscode.powershell", 8 | "timonwong.shellcheck" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /options/mongoose/ts/enviorment.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace NodeJS { 3 | interface ProcessEnv { 4 | NODE_ENV: "development" | "production"; 5 | MONGO_URI: string; 6 | } 7 | } 8 | } 9 | 10 | export type {}; 11 | -------------------------------------------------------------------------------- /options/prisma/ts/enviorment.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace NodeJS { 3 | interface ProcessEnv { 4 | NODE_ENV: "development" | "production"; 5 | DATABASE_URL: string; 6 | } 7 | } 8 | } 9 | 10 | export type {}; 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80, 6 | "tabWidth": 4, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /templates/express/js/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80, 6 | "tabWidth": 4, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /templates/express/ts/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 80, 6 | "tabWidth": 4, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /src/types/dependencyInstallers.d.ts: -------------------------------------------------------------------------------- 1 | import type { T_UserInputCli } from "./prompt"; 2 | 3 | export type T_Arg_HandleArgs = { 4 | dependencyCmd: string; 5 | devDependencyCmd: string; 6 | targetPath: string; 7 | }; 8 | 9 | export interface T_Arg_HandleCli extends T_Arg_HandleArgs { 10 | userInput: T_UserInputCli; 11 | } 12 | -------------------------------------------------------------------------------- /templates/express/ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-nitro-app", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "dotenvx run --env-file=.env.development -- tsx watch ./src/index.ts", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "start": "node ./index.js" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /templates/express/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-nitro-app", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "dotenvx run --env-file=.env.development -- nodemon ./src/index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "start": "node ./index.js" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /templates/express/js/src/logger.js: -------------------------------------------------------------------------------- 1 | // import pino & pino-http. 2 | import pino from "pino"; 3 | import pinoHttp from "pino-http"; 4 | 5 | // import pino-pretty. 6 | import pretty from "pino-pretty"; 7 | 8 | const stream = pretty({ 9 | levelFirst: true, 10 | colorize: true, 11 | ignore: "time,hostname,pid", 12 | }); 13 | 14 | export const logger = pino({}, stream); 15 | export const httpLogger = pinoHttp({ logger: logger, autoLogging: false }); 16 | -------------------------------------------------------------------------------- /templates/express/ts/src/logger.ts: -------------------------------------------------------------------------------- 1 | // import pino & pino-http. 2 | import pino from "pino"; 3 | import pinoHttp from "pino-http"; 4 | 5 | // import pino-pretty. 6 | import pretty from "pino-pretty"; 7 | 8 | const stream = pretty({ 9 | levelFirst: true, 10 | colorize: true, 11 | ignore: "time,hostname,pid", 12 | }); 13 | 14 | export const logger = pino({}, stream); 15 | export const httpLogger = pinoHttp({ logger: logger, autoLogging: false }); 16 | -------------------------------------------------------------------------------- /.github/workflows/npmPublish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build-and-publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | cache: 'npm' 16 | registry-url: https://registry.npmjs.org/ 17 | - run: npm ci 18 | - run: npm run pub 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ☀️ Enhancement Request 3 | about: Suggest an enhancement for the project 4 | --- 5 | 6 | 13 | 14 | ## Summary 15 | 16 | 17 | 18 | ## Description 19 | 20 | 21 | -------------------------------------------------------------------------------- /deprecated/.github/ISSUE_TEMPLATE/enhancement_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ☀️ Enhancement Request 3 | about: Suggest an enhancement for the project 4 | --- 5 | 6 | 13 | 14 | ## Summary 15 | 16 | 17 | 18 | ## Description 19 | 20 | 21 | -------------------------------------------------------------------------------- /options/mongoose/js/db.js: -------------------------------------------------------------------------------- 1 | import { logger } from '../logger.js'; 2 | 3 | import mongoose from 'mongoose'; 4 | 5 | async function connectToDb() { 6 | await mongoose 7 | .connect(process.env.MONGO_URI) 8 | .then(() => { 9 | logger.info('db connected !'); 10 | }) 11 | .catch(err => { 12 | logger.error( 13 | err, 14 | '-> an error has occured while connecting to the db!', 15 | ); 16 | 17 | logger.fatal('server is closing... :['); 18 | process.exit(1); 19 | }); 20 | } 21 | 22 | export default connectToDb; 23 | -------------------------------------------------------------------------------- /options/mongoose/ts/db.ts: -------------------------------------------------------------------------------- 1 | import { logger } from "@logger"; 2 | 3 | import mongoose from "mongoose"; 4 | 5 | async function connectToDb() { 6 | await mongoose 7 | .connect(process.env.MONGO_URI as string) 8 | .then(() => { 9 | logger.info("db connected !"); 10 | }) 11 | .catch((err) => { 12 | logger.error( 13 | err, 14 | "-> an error has occured while connecting to the db!", 15 | ); 16 | 17 | logger.fatal("server is closing... :["); 18 | process.exit(1); 19 | }); 20 | } 21 | 22 | export default connectToDb; 23 | -------------------------------------------------------------------------------- /templates/express/ts/src/index.ts: -------------------------------------------------------------------------------- 1 | import setHeaders from "./config/headers"; 2 | import { httpLogger, logger } from "./logger"; 3 | 4 | import express from "express"; 5 | 6 | const PORT = process.env.PORT || 3001; 7 | const app = express(); 8 | 9 | app.use(express.json()); 10 | app.use(express.urlencoded({ extended: true })); 11 | app.use(setHeaders); 12 | app.use(httpLogger); 13 | 14 | if (process.env.NODE_ENV === "development") { 15 | app.get("/", (req, res) => { 16 | res.send("Welcome to api set up by node-nitro ^_+"); 17 | }); 18 | } 19 | 20 | app.listen(PORT, () => { 21 | logger.info(`-> now listening at http://localhost:${PORT}/`); 22 | }); 23 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.6.0/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true 10 | } 11 | }, 12 | "vcs": { 13 | "enabled": true, 14 | "clientKind": "git", 15 | "useIgnoreFile": true 16 | }, 17 | "formatter": { 18 | "enabled": true, 19 | "formatWithErrors": false, 20 | "indentStyle": "space", 21 | "indentWidth": 4, 22 | "lineWidth": 80, 23 | "lineEnding": "lf", 24 | "ignore": [] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /templates/express/js/src/index.js: -------------------------------------------------------------------------------- 1 | import setHeaders from './config/headers.js'; 2 | import { httpLogger, logger } from './logger.js'; 3 | 4 | import express from 'express'; 5 | 6 | const PORT = process.env.PORT || 3001; 7 | const app = express(); 8 | 9 | app.use(express.json()); 10 | app.use(express.urlencoded({ extended: true })); 11 | app.use(setHeaders); 12 | app.use(httpLogger); 13 | 14 | if (process.env.NODE_ENV === 'development') { 15 | app.get('/', (req, res) => { 16 | res.send('Welcome to api set up by node-nitro ^_+'); 17 | }); 18 | } 19 | 20 | app.listen(PORT, () => { 21 | logger.info(`-> now listening at http://localhost:${PORT}/`); 22 | }); 23 | -------------------------------------------------------------------------------- /options/biome-prettier/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.6.0/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true 10 | } 11 | }, 12 | "vcs": { 13 | "enabled": true, 14 | "clientKind": "git", 15 | "useIgnoreFile": true 16 | }, 17 | "formatter": { 18 | "enabled": true, 19 | "formatWithErrors": false, 20 | "indentStyle": "space", 21 | "indentWidth": 4, 22 | "lineWidth": 80, 23 | "lineEnding": "lf", 24 | "ignore": [] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /options/mongoose/js/index.js: -------------------------------------------------------------------------------- 1 | import connectToDb from './config/db.js'; 2 | import setHeaders from './config/headers.js'; 3 | import { httpLogger, logger } from './logger.js'; 4 | 5 | import express from 'express'; 6 | 7 | const PORT = process.env.PORT || 3001; 8 | const app = express(); 9 | 10 | (async () => { 11 | await connectToDb(); 12 | })(); 13 | 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: true })); 16 | app.use(setHeaders); 17 | app.use(httpLogger); 18 | 19 | app.get('/', (req, res) => { 20 | res.send('Welcome to api set up by node-nitro ^_+'); 21 | }); 22 | 23 | app.listen(PORT, () => { 24 | logger.info(`-> now listening at http://localhost:${PORT}/`); 25 | }); 26 | -------------------------------------------------------------------------------- /options/mongoose/ts/index.ts: -------------------------------------------------------------------------------- 1 | import connectToDb from "./config/db"; 2 | import setHeaders from "./config/headers"; 3 | import { httpLogger, logger } from "./logger"; 4 | 5 | import express from "express"; 6 | 7 | const PORT = process.env.PORT || 3001; 8 | const app = express(); 9 | 10 | app.use(express.json()); 11 | app.use(express.urlencoded({ extended: true })); 12 | app.use(setHeaders); 13 | app.use(httpLogger); 14 | 15 | (async () => { 16 | await connectToDb(); 17 | })(); 18 | 19 | if (process.env.NODE_ENV === "development") { 20 | app.get("/", (req, res) => { 21 | res.send("Welcome to api set up by node-nitro ^_+"); 22 | }); 23 | } 24 | 25 | app.listen(PORT, () => { 26 | logger.info(`-> now listening at http://localhost:${PORT}/`); 27 | }); 28 | -------------------------------------------------------------------------------- /src/helpers/envMaker.ts: -------------------------------------------------------------------------------- 1 | // Import necessary node Module(s) 2 | import fs from "node:fs"; 3 | 4 | // Import necessary Module(s) 5 | import handleError from "@utils/errorHandler"; 6 | 7 | export function makeDevEnv(targetPath: string) { 8 | const targetDevEnvPath = `${targetPath}/.env.development`; 9 | const devEnvData = "NODE_ENV=development"; 10 | try { 11 | fs.writeFileSync(targetDevEnvPath, devEnvData); 12 | } catch (err) { 13 | handleError(err); 14 | } 15 | } 16 | 17 | export function makeProdEnv(targetPath: string) { 18 | const targetProdEnvPath = `${targetPath}/.env.production`; 19 | const prodEnvData = "NODE_ENV=production"; 20 | try { 21 | fs.writeFileSync(targetProdEnvPath, prodEnvData); 22 | } catch (err) { 23 | handleError(err); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/types/prompt.d.ts: -------------------------------------------------------------------------------- 1 | declare const templateNames: readonly ["express/js", "express/ts"]; 2 | declare const formatAndLinters: readonly ["eslint-prettier", "biome-prettier"]; 3 | declare const orms: readonly ["prisma", "mongoose"]; 4 | 5 | export type T_TemplateNames = (typeof templateNames)[number]; 6 | export type T_FormatAndLinters = (typeof formatAndLinters)[number]; 7 | export type T_Orms = (typeof orms)[number]; 8 | 9 | export type T_UserInputArgs = { 10 | type: "args"; 11 | template: T_TemplateNames; 12 | dirName: string; 13 | }; 14 | 15 | export type T_UserInputCli = { 16 | type: "cli"; 17 | template: T_TemplateNames; 18 | dirName: string; 19 | formatterAndLinter: T_FormatAndLinters; 20 | wantDb: boolean; 21 | orm: T_Orms; 22 | dsn: string; 23 | }; 24 | 25 | export type T_UserInput = T_UserInputArgs | T_UserInputCli; 26 | -------------------------------------------------------------------------------- /templates/express/js/.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = false 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,ts,css,html}] 14 | charset = utf-8 15 | 16 | # Space indentation 17 | [*.{js,ts,css,html,json}] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Matches the exact files either package.json or .travis.yml 22 | [{package.json,.travis.yml}] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | # Matches the exact file .editorconfig 27 | [.editorconfig] 28 | indent_style = space 29 | indent_size = 4 30 | 31 | # Markdown files often use trailing whitespace to indicate line breaks 32 | [*.md] 33 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /templates/express/ts/.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = false 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,ts,css,html}] 14 | charset = utf-8 15 | 16 | # Space indentation 17 | [*.{js,ts,css,html,json}] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Matches the exact files either package.json or .travis.yml 22 | [{package.json,.travis.yml}] 23 | indent_style = space 24 | indent_size = 4 25 | 26 | # Matches the exact file .editorconfig 27 | [.editorconfig] 28 | indent_style = space 29 | indent_size = 4 30 | 31 | # Markdown files often use trailing whitespace to indicate line breaks 32 | [*.md] 33 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = false 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{js,ts,css,html}] 14 | charset = utf-8 15 | 16 | # Space indentation 17 | [*.{js,ts,css,html,json}] 18 | indent_style = space 19 | indent_size = 4 20 | 21 | # Matches the exact files either package.json or .travis.yml 22 | [{package.json,.travis.yml}] 23 | indent_style = space 24 | 25 | # Indentation override for all JS under lib directory 26 | [*.js] 27 | indent_style = space 28 | indent_size = 4 29 | 30 | # Matches the exact file .editorconfig 31 | [.editorconfig] 32 | indent_style = space 33 | indent_size = 4 34 | 35 | # Markdown files often use trailing whitespace to indicate line breaks 36 | [*.md] 37 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /options/eslint-prettier/eslint.config.js: -------------------------------------------------------------------------------- 1 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 2 | 3 | module.exports = [ 4 | eslintPluginPrettierRecommended, 5 | { 6 | env: { 7 | browser: true, 8 | es2021: true, 9 | }, 10 | extends: 'eslint:recommended', 11 | parserOptions: { 12 | ecmaVersion: 12, 13 | sourceType: 'module', 14 | }, 15 | rules: {}, 16 | ignorePatterns: [], 17 | overrides: [ 18 | { 19 | files: ['**/*.ts', '**/*.tsx'], 20 | parser: '@typescript-eslint/parser', 21 | extends: [ 22 | 'eslint:recommended', 23 | 'plugin:@typescript-eslint/recommended', 24 | 'plugin:prettier/recommended', 25 | ], 26 | rules: {}, 27 | }, 28 | ], 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /templates/express/ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "NodeNext", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src/", 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "typeRoots": ["./types", "./node_modules/@types"], 14 | "paths": { 15 | "@controllers/*": ["./src/controllers/*"], 16 | "@helpers/*": ["./src/helpers/*"], 17 | "@middlewares/*": ["./src/middlewares/*"], 18 | "@models/*": ["./src/models/*"], 19 | "@routes/*": ["./src/routes/*"], 20 | "@utils/*": ["./src/utils/*"], 21 | "@validations/*": ["./src/validations/*"], 22 | "@services/*": ["./src/services/*"], 23 | "@logger": ["./src/logger"] 24 | }, 25 | "plugins": [{ "transform": "typescript-transform-paths" }] 26 | }, 27 | "exclude": ["node_modules", "dist", "coverage", "*.config.js"] 28 | } 29 | -------------------------------------------------------------------------------- /options/prisma/js/index.js: -------------------------------------------------------------------------------- 1 | import setHeaders from './config/headers.js'; 2 | import { httpLogger, logger } from './logger.js'; 3 | 4 | import express from 'express'; 5 | const prisma = new PrismaClient(); 6 | 7 | const PORT = process.env.PORT || 3001; 8 | const app = express(); 9 | 10 | (async () => { 11 | try { 12 | await prisma.$connect(); 13 | logger.info('db connected !'); 14 | } catch (err) { 15 | logger.error( 16 | err, 17 | '-> an error has occured while connecting to the db!', 18 | ); 19 | 20 | logger.fatal('server is closing... :['); 21 | process.exit(1); 22 | } 23 | })(); 24 | 25 | app.use(express.json()); 26 | app.use(express.urlencoded({ extended: true })); 27 | app.use(setHeaders); 28 | app.use(httpLogger); 29 | 30 | app.get('/', (req, res) => { 31 | res.send('Welcome to api set up by node-nitro ^_+'); 32 | }); 33 | 34 | app.listen(PORT, () => { 35 | logger.info(`-> now listening at http://localhost:${PORT}/`); 36 | }); 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report a bug in the project 4 | --- 5 | 6 | 14 | 15 | ## Summary 16 | 17 | 18 | 19 | ## How Found 20 | 21 | 22 | 23 | ## Steps to Replicate 24 | 25 | 26 | 27 | ## Description 28 | 29 | 30 | 31 | ## Proposed Solution 32 | 33 | 34 | -------------------------------------------------------------------------------- /deprecated/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report a bug in the project 4 | --- 5 | 6 | 14 | 15 | ## Summary 16 | 17 | 18 | 19 | ## How Found 20 | 21 | 22 | 23 | ## Steps to Replicate 24 | 25 | 26 | 27 | ## Description 28 | 29 | 30 | 31 | ## Proposed Solution 32 | 33 | 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-nitro", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "tspc", 9 | "dev": "tspc --watch", 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "pub": "npm run build && npm publish" 12 | }, 13 | "bin": { 14 | "create-nitro": "index.js" 15 | }, 16 | "files": [ 17 | "dist/**", 18 | "options/**", 19 | "templates/**" 20 | ], 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "dependencies": { 25 | "commander": "^12.1.0", 26 | "kolorist": "^1.8.0", 27 | "prompts": "^2.4.2" 28 | }, 29 | "devDependencies": { 30 | "@biomejs/biome": "^1.7.3", 31 | "@types/prompts": "^2.4.9", 32 | "ts-patch": "^3.1.2", 33 | "tslib": "^2.6.2", 34 | "tsx": "^4.7.1", 35 | "typescript": "^5.4.3", 36 | "typescript-transform-paths": "^3.4.7" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] Shivansh Khunger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /options/prisma/ts/index.ts: -------------------------------------------------------------------------------- 1 | import setHeaders from "./config/headers"; 2 | import { httpLogger, logger } from "./logger"; 3 | 4 | import { PrismaClient } from "@prisma/client"; 5 | import express from "express"; 6 | 7 | const PORT = process.env.PORT || 3001; 8 | const app = express(); 9 | 10 | const prisma = new PrismaClient(); 11 | 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: true })); 14 | app.use(setHeaders); 15 | app.use(httpLogger); 16 | 17 | (async () => { 18 | try { 19 | await prisma.$connect(); 20 | logger.info("db connected !"); 21 | } catch (err) { 22 | logger.error( 23 | err, 24 | "-> an error has occured while connecting to the db!", 25 | ); 26 | 27 | logger.fatal("server is closing... :["); 28 | process.exit(1); 29 | } 30 | })(); 31 | 32 | if (process.env.NODE_ENV === "development") { 33 | app.get("/", (req, res) => { 34 | res.send("Welcome to api set up by node-nitro ^_+"); 35 | }); 36 | } 37 | 38 | app.listen(PORT, () => { 39 | logger.info(`-> now listening at http://localhost:${PORT}/`); 40 | }); 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/inconsistency_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ⚖️ Inconsistency Report 3 | about: Report an inconsistency in the project 4 | --- 5 | 6 | 14 | 15 | ## Summary 16 | 17 | 18 | 19 | ## How Found 20 | 21 | 22 | 23 | ## Steps to Replicate 24 | 25 | 26 | 27 | ## Description 28 | 29 | 30 | 31 | ## Proposed Solution 32 | 33 | 34 | -------------------------------------------------------------------------------- /deprecated/LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] Shivansh Khunger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /deprecated/.github/ISSUE_TEMPLATE/inconsistency_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ⚖️ Inconsistency Report 3 | about: Report an inconsistency in the project 4 | --- 5 | 6 | 14 | 15 | ## Summary 16 | 17 | 18 | 19 | ## How Found 20 | 21 | 22 | 23 | ## Steps to Replicate 24 | 25 | 26 | 27 | ## Description 28 | 29 | 30 | 31 | ## Proposed Solution 32 | 33 | 34 | -------------------------------------------------------------------------------- /deprecated/win_update_profile.ps1: -------------------------------------------------------------------------------- 1 | # Check if the PowerShell profile exists and store the result in $ifProfile 2 | $ifProfile = Test-Path $PROFILE 3 | 4 | # If the profile does not exist ($ifProfile is false), create it 5 | if (!$ifProfile) { 6 | New-Item -path $PROFILE -type File -Force 7 | } 8 | 9 | # Define a new function mkexp that runs a Python script, and store the function definition in $appendContent 10 | $appendContent = "`n`n# This function 'mkexp' runs a Python script located in the .bin directory of your home directory 11 | 12 | function mkexp(){ 13 | python $HOME/bin_scripts/without_args.py 14 | }" 15 | 16 | # Check if the function definition already exists in the PowerShell profile 17 | $ifExists = Select-String -Path $PROFILE -Pattern 'function mkexp' -Quiet 18 | 19 | # If the function definition does not exist ($ifExists is false), append it 20 | if (!$ifExists) { 21 | Add-Content -Path $PROFILE $appendContent 22 | # Output a success message 23 | Write-Output "The command 'mkexp' has been successfully added to your PowerShell profile." 24 | } else { 25 | Write-Output "The command 'mkexp' already exists in your PowerShell profile." 26 | } -------------------------------------------------------------------------------- /src/helpers/handleOptions.ts: -------------------------------------------------------------------------------- 1 | // Import necessary Type(s) 2 | import type { T_UserInputCli } from "../types/prompt"; 3 | 4 | function handleAdditionalOptions( 5 | userInput: T_UserInputCli, 6 | tempDependency: string, 7 | tempDevDependency: string, 8 | ) { 9 | let dependency = tempDependency; 10 | let devDependency = tempDevDependency; 11 | 12 | if (userInput.formatterAndLinter === "biome-prettier") { 13 | devDependency = `${devDependency} @biomejs/biome`; 14 | } else { 15 | devDependency = `${devDependency} eslint`; 16 | if (userInput.template === "express/ts") { 17 | devDependency = `${devDependency} @eslint/js @types/eslint__js typescript typescript-eslint eslint-plugin-prettier`; 18 | } 19 | } 20 | 21 | if (userInput.wantDb) { 22 | if (userInput.orm === "mongoose") { 23 | dependency = `${dependency} mongoose`; 24 | } else { 25 | dependency = `${dependency} prisma @prisma/client`; 26 | } 27 | } 28 | 29 | return { 30 | updtDependencyCmd: dependency, 31 | updtDevDependencyCmd: devDependency, 32 | }; 33 | } 34 | 35 | export default handleAdditionalOptions; 36 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ## Summary 13 | 14 | 15 | 16 | ## Technical Details 17 | 18 | 19 | 20 | 1. 21 | 2. 22 | 3. 23 | 24 | ## Testing 25 | 26 | 27 | 28 | ## Screenshots 29 | 30 | 31 | 32 | ## Request for Feedback 33 | 34 | 35 | -------------------------------------------------------------------------------- /deprecated/.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ## Summary 13 | 14 | 15 | 16 | ## Technical Details 17 | 18 | 19 | 20 | 1. 21 | 2. 22 | 3. 23 | 24 | ## Testing 25 | 26 | 27 | 28 | ## Screenshots 29 | 30 | 31 | 32 | ## Request for Feedback 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/utils/fileSystem.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | 3 | import handleError from "./errorHandler"; 4 | 5 | export function deleteFile(targetFilePath: string) { 6 | try { 7 | fs.unlinkSync(targetFilePath); 8 | } catch (err) { 9 | handleError(err); 10 | } 11 | } 12 | 13 | export function updateFile(localFilePath: string, targetFilePath: string) { 14 | try { 15 | const localData = fs.readFileSync(localFilePath, "utf-8"); 16 | 17 | fs.writeFileSync(targetFilePath, localData); 18 | } catch (err) { 19 | handleError(err); 20 | } 21 | } 22 | 23 | export function copyFile(localFilePath: string, targetFilePath: string) { 24 | try { 25 | fs.copyFileSync(localFilePath, targetFilePath); 26 | } catch (err) { 27 | handleError(err); 28 | } 29 | } 30 | 31 | export function appendFile(targetFilePath: string, appendData: string) { 32 | try { 33 | fs.appendFileSync(targetFilePath, appendData); 34 | } catch (err) { 35 | handleError(err); 36 | } 37 | } 38 | 39 | export function renameFile(oldName: string, newName: string) { 40 | try { 41 | fs.renameSync(oldName, newName); 42 | } catch (err) { 43 | handleError(err); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/helpers/printDependencies.ts: -------------------------------------------------------------------------------- 1 | import { gray } from "kolorist"; 2 | 3 | import type { T_PkgManagerInfo } from "./pkgManager"; 4 | 5 | function printDependencies( 6 | dependencyCmd: string, 7 | devDependencyCmd: string, 8 | pkgManagerInfo: T_PkgManagerInfo, 9 | ) { 10 | if (pkgManagerInfo.pkgManager === "pnpm") { 11 | // pnpm does this by default 12 | return; 13 | } 14 | 15 | const dependencies = dependencyCmd.replace(pkgManagerInfo.prefix, ""); 16 | const devDependencies = devDependencyCmd.replace( 17 | pkgManagerInfo.devPrefix, 18 | "", 19 | ); 20 | 21 | const dependenciesArr = dependencies.split(" "); 22 | const devDependenciesArr = devDependencies.split(" "); 23 | 24 | console.log( 25 | `\nDownloading dependencies... (${pkgManagerInfo.pkgManager})\n`, 26 | ); 27 | 28 | console.log("Dependencies -:"); 29 | dependenciesArr.map((d: string) => { 30 | console.log(`${gray(d)}`); 31 | }); 32 | 33 | console.log("\nDev-Dependencies -:"); 34 | devDependenciesArr.map((d: string) => { 35 | console.log(`${gray(d)}`); 36 | }); 37 | 38 | if (pkgManagerInfo.pkgManager !== "npm") { 39 | console.log("\n"); 40 | } 41 | } 42 | 43 | export default printDependencies; 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "NodeNext", 5 | "lib": ["dom", "es6", "dom.iterable", "scripthost"], 6 | "allowJs": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src/", 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "typeRoots": ["./src/types", "./node_modules/@types"], 14 | "paths": { 15 | "@controllers/*": ["./src/controllers/*"], 16 | "@helpers/*": ["./src/helpers/*"], 17 | "@middlewares/*": ["./src/middlewares/*"], 18 | "@models/*": ["./src/models/*"], 19 | "@routes/*": ["./src/routes/*"], 20 | "@utils/*": ["./src/utils/*"], 21 | "@validations/*": ["./src/validations/*"], 22 | "@services/*": ["./src/services/*"], 23 | "@logger": ["./src/logger"] 24 | }, 25 | "plugins": [{ "transform": "typescript-transform-paths" }] 26 | }, 27 | "ts-node": { 28 | "require": ["tsconfig-paths/register"] 29 | }, 30 | "exclude": [ 31 | "rollup.config.mjs", 32 | "./index.js", 33 | "node_modules", 34 | "dist", 35 | "coverage", 36 | "*.config.js", 37 | "templates", 38 | "options" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/helpers/pkgManager.ts: -------------------------------------------------------------------------------- 1 | function pkgFromUserAgent(userAgent: string | undefined) { 2 | if (!userAgent) return undefined; 3 | const pkgSpec = userAgent.split(" ")[0]; 4 | const pkgSpecArr = pkgSpec.split("/"); 5 | return { 6 | name: pkgSpecArr[0], 7 | version: pkgSpecArr[1], 8 | }; 9 | } 10 | 11 | export type T_PkgManagerInfo = { 12 | pkgManager: string; 13 | prefix: string; 14 | devPrefix: string; 15 | }; 16 | 17 | const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); 18 | const pkgManager = pkgInfo ? pkgInfo.name : "npm"; 19 | 20 | function selectPkgManager(): T_PkgManagerInfo { 21 | let pkgManagerInfo: T_PkgManagerInfo = { 22 | pkgManager: pkgManager, 23 | prefix: "npm i", 24 | devPrefix: "npm i --save-dev", 25 | }; 26 | 27 | if (pkgManager === "yarn") { 28 | pkgManagerInfo = { 29 | ...pkgManagerInfo, 30 | prefix: "yarn add", 31 | devPrefix: "yarn add --dev", 32 | }; 33 | 34 | return pkgManagerInfo; 35 | } 36 | if (pkgManager === "pnpm") { 37 | pkgManagerInfo = { 38 | ...pkgManagerInfo, 39 | prefix: "pnpm add", 40 | devPrefix: "pnpm add --save-dev", 41 | }; 42 | 43 | return pkgManagerInfo; 44 | } 45 | 46 | return pkgManagerInfo; 47 | } 48 | 49 | export default selectPkgManager; 50 | -------------------------------------------------------------------------------- /src/utils/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | 3 | // Import necessary Module(s) 4 | import { bold, green, lightRed } from "kolorist"; 5 | 6 | const cwd = process.cwd(); 7 | let targetPath: string; 8 | export function makeTargetPath(userInputDirName: string) { 9 | targetPath = `${cwd}${ 10 | process.platform === "win32" ? "\\" : "/" 11 | }${userInputDirName}`; 12 | 13 | return targetPath; 14 | } 15 | 16 | function rollBack() { 17 | try { 18 | fs.rmSync(targetPath, { recursive: true, force: true }); 19 | console.log(` 20 | ${bold( 21 | green( 22 | "\nNo need to worry, we have rolled back anything that was done. Try Again!", 23 | ), 24 | )}\nIf the error presists report on https://github.com/Shivansh-Khunger/node-nitro`); 25 | } catch (err) { 26 | console.log( 27 | lightRed( 28 | "\nAn Error has occured while rolling back changes...\nWe recommend manually deleting the project directory.", 29 | ), 30 | ); 31 | } 32 | 33 | process.exit(1); 34 | } 35 | 36 | // biome-ignore lint/suspicious/noExplicitAny: 37 | function handleError(err: any) { 38 | console.log( 39 | lightRed("An Error has occured while setting up your project..."), 40 | ); 41 | 42 | console.log(err); 43 | 44 | rollBack(); 45 | } 46 | 47 | export default handleError; 48 | -------------------------------------------------------------------------------- /src/helpers/fmt&Linters.ts: -------------------------------------------------------------------------------- 1 | // Import necessary Type(s) 2 | import type { T_FormatAndLinters } from "../types/prompt"; 3 | 4 | // Import necessary Module(s) 5 | import { updateFile } from "@utils/fileSystem"; 6 | import { copyFile } from "@utils/fileSystem"; 7 | 8 | function addFmtAndLinterConfig( 9 | fmtAndLinter: T_FormatAndLinters, 10 | targetPath: string, 11 | projectDirPath: string, 12 | ) { 13 | const optionsDirPath = `${projectDirPath}/options`; 14 | const localConfigPath = `${optionsDirPath}/${fmtAndLinter}/extensions.json`; 15 | const targetConfigPath = `${targetPath}/.vscode/extensions.json`; 16 | switch (fmtAndLinter) { 17 | case "biome-prettier": 18 | { 19 | const biomeConfig = "biome.json"; 20 | const localBiomeConfigPath = `${optionsDirPath}/${fmtAndLinter}/${biomeConfig}`; 21 | const targetBiomeConfigPath = `${targetPath}/${biomeConfig}`; 22 | 23 | copyFile(localBiomeConfigPath, targetBiomeConfigPath); 24 | } 25 | 26 | break; 27 | case "eslint-prettier": 28 | { 29 | const esLintConfig = "eslint.config.js"; 30 | const localEsLintConfigPath = `${optionsDirPath}/${fmtAndLinter}/${esLintConfig}`; 31 | const targetEsLintConfigPath = `${targetPath}/${esLintConfig}`; 32 | 33 | copyFile(localEsLintConfigPath, targetEsLintConfigPath); 34 | } 35 | 36 | break; 37 | default: 38 | break; 39 | } 40 | updateFile(localConfigPath, targetConfigPath); 41 | } 42 | 43 | export default addFmtAndLinterConfig; 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-nitro npm package 2 | 3 |
4 | nitro-logo 5 |
6 | 7 | ## Scaffolding Your Node API's 8 | 9 | > **Compatibility Note:** 10 | > Nitro requires [Node.js](https://nodejs.org/en/) version 18+, 20+. However, some templates require a higher Node.js version to work, please upgrade if your package manager warns about it. 11 | 12 | ### Features 13 | 14 | - **Templates**: Quick setup for Node APIs with industry-standard templates. 15 | - **Database Support**: Easy integration with popular databases. 16 | - **ORM Support**: Support for ORMs like Mongoose and Prisma for easier database operations. 17 | - **Linter Support**: Includes linters like ESLint and Biome for cleaner, consistent code. 18 | 19 | With NPM: 20 | 21 | ```bash 22 | npm create nitro@latest 23 | ``` 24 | 25 | With Yarn: 26 | 27 | ```bash 28 | yarn create nitro 29 | ``` 30 | 31 | With PNPM: 32 | 33 | ```bash 34 | pnpm create nitro 35 | ``` 36 | 37 | Then follow the prompts! 38 | 39 | You can also directly specify the project name and the template you want to use via additional command line options. For example, to scaffold a project, run: 40 | 41 | ```bash 42 | # npm 7+, extra double-dash is needed: 43 | npm create nitro@latest -- -t