create-xpress-backend 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +43 -0
- package/src/generator.js +50 -0
- package/src/index.js +154 -0
- package/src/templates/index.js +15 -0
- package/src/templates/js.js +519 -0
- package/src/templates/shared.js +283 -0
- package/src/templates/ts.js +587 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-xpress-backend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scaffold a production-ready Express + Prisma + PostgreSQL backend in seconds",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-xpress-backend": "src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node src/index.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"express",
|
|
14
|
+
"backend",
|
|
15
|
+
"scaffold",
|
|
16
|
+
"prisma",
|
|
17
|
+
"postgresql",
|
|
18
|
+
"cli",
|
|
19
|
+
"boilerplate",
|
|
20
|
+
"starter",
|
|
21
|
+
"generator",
|
|
22
|
+
"node"
|
|
23
|
+
],
|
|
24
|
+
"author": "ayushsolanki29",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/ayushsolanki29/create-xpress-backend.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/ayushsolanki29/create-xpress-backend/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/ayushsolanki29/create-xpress-backend#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"chalk": "^4.1.2",
|
|
39
|
+
"fs-extra": "^11.2.0",
|
|
40
|
+
"inquirer": "^8.2.6",
|
|
41
|
+
"ora": "^5.4.1"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/generator.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const { buildTemplates } = require('./templates');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Main generator — writes every file into the new project directory.
|
|
10
|
+
* @param {object} answers - Inquirer answers
|
|
11
|
+
* @param {object} spinner - Ora spinner instance
|
|
12
|
+
*/
|
|
13
|
+
async function generateProject(answers, spinner) {
|
|
14
|
+
const { projectName, language, port, includeFileUpload, includeAuth, initGit } = answers;
|
|
15
|
+
const isTs = language === 'ts';
|
|
16
|
+
const destDir = path.resolve(process.cwd(), projectName);
|
|
17
|
+
|
|
18
|
+
// 1. Create root folder
|
|
19
|
+
spinner.text = 'Creating project folder...';
|
|
20
|
+
await fs.ensureDir(destDir);
|
|
21
|
+
|
|
22
|
+
// 2. Build all file contents from templates
|
|
23
|
+
const files = buildTemplates({ projectName, language, port, includeFileUpload, includeAuth });
|
|
24
|
+
|
|
25
|
+
// 3. Write every file
|
|
26
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
27
|
+
spinner.text = `Writing ${relPath}`;
|
|
28
|
+
const absPath = path.join(destDir, relPath);
|
|
29
|
+
await fs.ensureDir(path.dirname(absPath));
|
|
30
|
+
await fs.writeFile(absPath, content, 'utf8');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 4. Create uploads dir placeholder if file upload included
|
|
34
|
+
if (includeFileUpload) {
|
|
35
|
+
await fs.ensureDir(path.join(destDir, 'uploads'));
|
|
36
|
+
await fs.writeFile(path.join(destDir, 'uploads', '.gitkeep'), '', 'utf8');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 5. Git init
|
|
40
|
+
if (initGit) {
|
|
41
|
+
spinner.text = 'Initialising git...';
|
|
42
|
+
try {
|
|
43
|
+
execSync('git init', { cwd: destDir, stdio: 'ignore' });
|
|
44
|
+
} catch {
|
|
45
|
+
// git not available — not fatal
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { generateProject };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
|
|
11
|
+
const { generateProject } = require('./generator');
|
|
12
|
+
|
|
13
|
+
const VERSION = require('../package.json').version;
|
|
14
|
+
|
|
15
|
+
// ─── Banner ───────────────────────────────────────────────────────────────────
|
|
16
|
+
function printBanner() {
|
|
17
|
+
console.log('');
|
|
18
|
+
console.log(chalk.bold.cyan(' ┌─────────────────────────────────────────────┐'));
|
|
19
|
+
console.log(
|
|
20
|
+
chalk.bold.cyan(' │') +
|
|
21
|
+
chalk.bold.white(' 🚀 create-xpress-backend') +
|
|
22
|
+
chalk.gray(` v${VERSION}`) +
|
|
23
|
+
chalk.bold.cyan(' │')
|
|
24
|
+
);
|
|
25
|
+
console.log(
|
|
26
|
+
chalk.bold.cyan(' │') +
|
|
27
|
+
chalk.dim(' Express · Prisma · PostgreSQL · Node.js ') +
|
|
28
|
+
chalk.bold.cyan('│')
|
|
29
|
+
);
|
|
30
|
+
console.log(chalk.bold.cyan(' └─────────────────────────────────────────────┘'));
|
|
31
|
+
console.log('');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ─── Prompts ──────────────────────────────────────────────────────────────────
|
|
35
|
+
async function askQuestions() {
|
|
36
|
+
return inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'projectName',
|
|
40
|
+
message: chalk.cyan('project_name') + chalk.gray(' : '),
|
|
41
|
+
validate(input) {
|
|
42
|
+
const name = input.trim();
|
|
43
|
+
if (!name) return 'Project name cannot be empty.';
|
|
44
|
+
if (!/^[a-z0-9-_]+$/i.test(name))
|
|
45
|
+
return 'Use only letters, numbers, hyphens or underscores.';
|
|
46
|
+
if (fs.existsSync(path.resolve(process.cwd(), name)))
|
|
47
|
+
return `Folder "${name}" already exists — pick a different name.`;
|
|
48
|
+
return true;
|
|
49
|
+
},
|
|
50
|
+
filter: (v) => v.trim(),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'list',
|
|
54
|
+
name: 'language',
|
|
55
|
+
message: chalk.cyan('language') + chalk.gray(' : '),
|
|
56
|
+
choices: [
|
|
57
|
+
{ name: 'TypeScript (recommended)', value: 'ts' },
|
|
58
|
+
{ name: 'JavaScript', value: 'js' },
|
|
59
|
+
],
|
|
60
|
+
default: 'ts',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'input',
|
|
64
|
+
name: 'port',
|
|
65
|
+
message: chalk.cyan('port') + chalk.gray(' : '),
|
|
66
|
+
default: '5050',
|
|
67
|
+
validate: (v) => (/^\d+$/.test(v.trim()) ? true : 'Port must be a number.'),
|
|
68
|
+
filter: (v) => v.trim(),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'confirm',
|
|
72
|
+
name: 'includeFileUpload',
|
|
73
|
+
message: chalk.cyan('file upload system') + chalk.gray(' : include?'),
|
|
74
|
+
default: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'confirm',
|
|
78
|
+
name: 'includeAuth',
|
|
79
|
+
message: chalk.cyan('JWT auth module') + chalk.gray(' : include?'),
|
|
80
|
+
default: false,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'confirm',
|
|
84
|
+
name: 'initGit',
|
|
85
|
+
message: chalk.cyan('git init') + chalk.gray(' : initialize?'),
|
|
86
|
+
default: true,
|
|
87
|
+
},
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── Success output ───────────────────────────────────────────────────────────
|
|
92
|
+
function printSuccess(projectName, port, language) {
|
|
93
|
+
const isTs = language === 'ts';
|
|
94
|
+
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log(chalk.bold.green(' ✔ Done! Your project is ready.\n'));
|
|
97
|
+
|
|
98
|
+
console.log(chalk.bold(' Get started:'));
|
|
99
|
+
console.log(chalk.white(` cd ${projectName}`));
|
|
100
|
+
console.log(chalk.white(' npm install'));
|
|
101
|
+
console.log(chalk.dim(' # configure DATABASE_URL in .env, then:'));
|
|
102
|
+
console.log(chalk.white(' npm run db:push'));
|
|
103
|
+
console.log(chalk.white(' npm run dev\n'));
|
|
104
|
+
|
|
105
|
+
if (isTs) {
|
|
106
|
+
console.log(chalk.dim(' Build for production:'));
|
|
107
|
+
console.log(chalk.white(' npm run build'));
|
|
108
|
+
console.log(chalk.white(' npm start\n'));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(chalk.dim(' Health check → ') + chalk.cyan(`http://localhost:${port}/health`));
|
|
112
|
+
console.log(chalk.dim(' Prisma Studio → ') + chalk.cyan('npm run db:studio'));
|
|
113
|
+
console.log('');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
117
|
+
async function main() {
|
|
118
|
+
printBanner();
|
|
119
|
+
|
|
120
|
+
let answers;
|
|
121
|
+
try {
|
|
122
|
+
answers = await askQuestions();
|
|
123
|
+
} catch {
|
|
124
|
+
console.log('\n' + chalk.yellow(' Cancelled.'));
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log('');
|
|
129
|
+
|
|
130
|
+
const isTs = answers.language === 'ts';
|
|
131
|
+
const langLabel = isTs
|
|
132
|
+
? chalk.blue('TypeScript')
|
|
133
|
+
: chalk.yellow('JavaScript');
|
|
134
|
+
|
|
135
|
+
const spinner = ora({
|
|
136
|
+
text: chalk.cyan(`Scaffolding ${chalk.bold(answers.projectName)} `) + chalk.gray(`[${langLabel}${chalk.gray(']')}...`),
|
|
137
|
+
color: 'cyan',
|
|
138
|
+
}).start();
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
await generateProject(answers, spinner);
|
|
142
|
+
spinner.succeed(
|
|
143
|
+
chalk.green(`Project "${chalk.bold(answers.projectName)}" created! `) +
|
|
144
|
+
chalk.gray(`(${isTs ? 'TypeScript' : 'JavaScript'})`)
|
|
145
|
+
);
|
|
146
|
+
printSuccess(answers.projectName, answers.port, answers.language);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
spinner.fail(chalk.red('Something went wrong.'));
|
|
149
|
+
console.error(chalk.red(`\n ${err.message}\n`));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
main();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { buildJsTemplates } = require('./js');
|
|
4
|
+
const { buildTsTemplates } = require('./ts');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Routes to the correct template set based on chosen language.
|
|
8
|
+
*/
|
|
9
|
+
function buildTemplates(opts) {
|
|
10
|
+
return opts.language === 'ts'
|
|
11
|
+
? buildTsTemplates(opts)
|
|
12
|
+
: buildJsTemplates(opts);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { buildTemplates };
|