create-theta-code 1.0.1
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/bin/create.js +9 -0
- package/package.json +34 -0
- package/src/cli.js +21 -0
- package/src/generators/scaffoldProject.js +46 -0
- package/src/prompts/getProjectName.js +30 -0
- package/src/prompts/getTemplateChoice.js +39 -0
- package/src/utils/logger.js +29 -0
- package/templates/mongo-js/.env +12 -0
- package/templates/mongo-js/.env.example +13 -0
- package/templates/mongo-js/.eslintrc.json +24 -0
- package/templates/mongo-js/.prettierrc +9 -0
- package/templates/mongo-js/README.md +429 -0
- package/templates/mongo-js/_env.example +13 -0
- package/templates/mongo-js/_gitignore +22 -0
- package/templates/mongo-js/package-lock.json +4671 -0
- package/templates/mongo-js/package.json +48 -0
- package/templates/mongo-js/server.js +67 -0
- package/templates/mongo-js/src/config/app.config.js +72 -0
- package/templates/mongo-js/src/config/db.config.js +32 -0
- package/templates/mongo-js/src/config/env.config.js +49 -0
- package/templates/mongo-js/src/config/rateLimiter.config.js +32 -0
- package/templates/mongo-js/src/middlewares/auth.middleware.js +20 -0
- package/templates/mongo-js/src/middlewares/error.middleware.js +61 -0
- package/templates/mongo-js/src/middlewares/notFound.middleware.js +11 -0
- package/templates/mongo-js/src/middlewares/requestId.middleware.js +10 -0
- package/templates/mongo-js/src/middlewares/requireRole.middleware.js +13 -0
- package/templates/mongo-js/src/middlewares/validate.middleware.js +21 -0
- package/templates/mongo-js/src/modules/user/user.controller.js +88 -0
- package/templates/mongo-js/src/modules/user/user.model.js +45 -0
- package/templates/mongo-js/src/modules/user/user.repository.js +47 -0
- package/templates/mongo-js/src/modules/user/user.routes.js +32 -0
- package/templates/mongo-js/src/modules/user/user.service.js +87 -0
- package/templates/mongo-js/src/modules/user/user.validator.js +28 -0
- package/templates/mongo-js/src/utils/AppError.js +15 -0
- package/templates/mongo-js/src/utils/apiResponse.js +23 -0
- package/templates/mongo-js/src/utils/asyncHandler.js +7 -0
- package/templates/mongo-js/src/utils/constants.js +16 -0
- package/templates/mongo-js/src/utils/jwt.utils.js +40 -0
- package/templates/mongo-js/tests/integration/user.routes.test.js +111 -0
- package/templates/mongo-js/tests/unit/user.service.test.js +96 -0
- package/templates/pg-js/.eslintrc.json +24 -0
- package/templates/pg-js/.prettierrc +9 -0
- package/templates/pg-js/_env.example +7 -0
- package/templates/pg-js/_gitignore +20 -0
- package/templates/pg-js/package.json +50 -0
- package/templates/pg-js/prisma/schema.prisma +23 -0
- package/templates/pg-js/server.js +63 -0
- package/templates/pg-js/src/config/app.config.js +48 -0
- package/templates/pg-js/src/config/db.config.js +30 -0
- package/templates/pg-js/src/config/env.config.js +36 -0
- package/templates/pg-js/src/config/rateLimiter.config.js +22 -0
- package/templates/pg-js/src/middlewares/auth.middleware.js +32 -0
- package/templates/pg-js/src/middlewares/error.middleware.js +50 -0
- package/templates/pg-js/src/middlewares/notFound.middleware.js +11 -0
- package/templates/pg-js/src/middlewares/validate.middleware.js +21 -0
- package/templates/pg-js/src/modules/user/user.controller.js +57 -0
- package/templates/pg-js/src/modules/user/user.model.js +20 -0
- package/templates/pg-js/src/modules/user/user.repository.js +105 -0
- package/templates/pg-js/src/modules/user/user.routes.js +27 -0
- package/templates/pg-js/src/modules/user/user.service.js +81 -0
- package/templates/pg-js/src/modules/user/user.validator.js +22 -0
- package/templates/pg-js/src/utils/AppError.js +14 -0
- package/templates/pg-js/src/utils/apiResponse.js +23 -0
- package/templates/pg-js/src/utils/asyncHandler.js +7 -0
- package/templates/pg-js/src/utils/constants.js +24 -0
- package/templates/pg-js/src/utils/jwt.utils.js +39 -0
- package/templates/pg-js/tests/integration/user.routes.test.js +95 -0
- package/templates/pg-js/tests/unit/user.service.test.js +96 -0
package/bin/create.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/create.js — Entry point for create-theta-code CLI. Routes to runCLI orchestrator only.
|
|
3
|
+
|
|
4
|
+
import { runCLI } from '../src/cli.js';
|
|
5
|
+
|
|
6
|
+
runCLI(process.argv.slice(2)).catch((err) => {
|
|
7
|
+
console.error(err.message);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-theta-code",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Production-grade Node.js + Express scaffolding CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-theta-code": "bin/create.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "node bin/create.js",
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"scaffolding",
|
|
21
|
+
"cli",
|
|
22
|
+
"nodejs",
|
|
23
|
+
"express",
|
|
24
|
+
"boilerplate"
|
|
25
|
+
],
|
|
26
|
+
"author": "Theta Labs",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@clack/prompts": "^0.7.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/cli.js — CLI orchestrator. Collects prompts, calls scaffoldProject, manages flow.
|
|
2
|
+
|
|
3
|
+
import { printBanner, printSuccess } from './utils/logger.js';
|
|
4
|
+
import { getProjectName } from './prompts/getProjectName.js';
|
|
5
|
+
import { getTemplateChoice } from './prompts/getTemplateChoice.js';
|
|
6
|
+
import { scaffoldProject } from './generators/scaffoldProject.js';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
async function runCLI(argv) {
|
|
10
|
+
printBanner();
|
|
11
|
+
|
|
12
|
+
const projectName = await getProjectName(argv);
|
|
13
|
+
const templateType = await getTemplateChoice();
|
|
14
|
+
|
|
15
|
+
const currentDir = process.cwd();
|
|
16
|
+
const projectPath = await scaffoldProject(projectName, templateType, currentDir);
|
|
17
|
+
|
|
18
|
+
printSuccess(projectName, templateType, projectPath);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { runCLI };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/generators/scaffoldProject.js — Copies template, injects project name, renames hidden files.
|
|
2
|
+
|
|
3
|
+
import { cp, readFile, writeFile, rename } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { dirname } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
|
|
11
|
+
async function scaffoldProject(projectName, templateType, targetDir) {
|
|
12
|
+
const templatePath = join(__dirname, '..', '..', 'templates', templateType);
|
|
13
|
+
const projectPath = join(targetDir, projectName);
|
|
14
|
+
|
|
15
|
+
// Copy entire template directory
|
|
16
|
+
await cp(templatePath, projectPath, { recursive: true });
|
|
17
|
+
|
|
18
|
+
// Rename _gitignore to .gitignore
|
|
19
|
+
const gitignorePath = join(projectPath, '_gitignore');
|
|
20
|
+
const realGitignorePath = join(projectPath, '.gitignore');
|
|
21
|
+
try {
|
|
22
|
+
await rename(gitignorePath, realGitignorePath);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
// File might not exist, continue
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Rename _env.example to .env.example
|
|
28
|
+
const envExamplePath = join(projectPath, '_env.example');
|
|
29
|
+
const realEnvExamplePath = join(projectPath, '.env.example');
|
|
30
|
+
try {
|
|
31
|
+
await rename(envExamplePath, realEnvExamplePath);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
// File might not exist, continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Update package.json with project name
|
|
37
|
+
const packageJsonPath = join(projectPath, 'package.json');
|
|
38
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
39
|
+
packageJson.name = projectName;
|
|
40
|
+
packageJson.description = `${projectName} - Production-grade Node.js API built with create-theta-code`;
|
|
41
|
+
await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
42
|
+
|
|
43
|
+
return projectPath;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { scaffoldProject };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/prompts/getProjectName.js — Reads project name from argv[0] or prompts interactively.
|
|
2
|
+
|
|
3
|
+
import { text } from '@clack/prompts';
|
|
4
|
+
import { isCancel } from '@clack/prompts';
|
|
5
|
+
|
|
6
|
+
async function getProjectName(argv) {
|
|
7
|
+
if (argv.length > 0 && argv[0].trim()) {
|
|
8
|
+
return argv[0].trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const name = await text({
|
|
12
|
+
message: 'Enter project name:',
|
|
13
|
+
placeholder: 'theta-code-app',
|
|
14
|
+
validate: (input) => {
|
|
15
|
+
if (!input.trim()) return 'Project name is required';
|
|
16
|
+
if (!/^[a-z0-9-]+$/.test(input.trim())) {
|
|
17
|
+
return 'Project name must contain only lowercase letters, numbers, and hyphens';
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (isCancel(name)) {
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return name;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { getProjectName };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/prompts/getTemplateChoice.js — Prompts user to choose DB and language. Returns template type.
|
|
2
|
+
|
|
3
|
+
import { select } from '@clack/prompts';
|
|
4
|
+
import { isCancel } from '@clack/prompts';
|
|
5
|
+
|
|
6
|
+
async function getTemplateChoice() {
|
|
7
|
+
const dbChoice = await select({
|
|
8
|
+
message: 'Choose database:',
|
|
9
|
+
options: [
|
|
10
|
+
{ value: 'mongo', label: 'MongoDB + Mongoose' },
|
|
11
|
+
{ value: 'pg', label: 'PostgreSQL + Prisma' },
|
|
12
|
+
],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (isCancel(dbChoice)) {
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const langChoice = await select({
|
|
20
|
+
message: 'Choose language:',
|
|
21
|
+
options: [
|
|
22
|
+
{ value: 'js', label: 'JavaScript' },
|
|
23
|
+
{ value: 'ts', label: 'TypeScript (coming soon)' },
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (isCancel(langChoice)) {
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (langChoice === 'ts') {
|
|
32
|
+
console.log('TypeScript templates coming soon! Defaulting to JavaScript.');
|
|
33
|
+
return `${dbChoice}-js`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return `${dbChoice}-${langChoice}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { getTemplateChoice };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/utils/logger.js — Display banner and success messages only. No logging infrastructure.
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
function printBanner() {
|
|
10
|
+
console.log('\n╔════════════════════════════════════════╗');
|
|
11
|
+
console.log('║ 🚀 create-theta-code ║');
|
|
12
|
+
console.log('║ Production-Grade Node.js Scaffolding ║');
|
|
13
|
+
console.log('╚════════════════════════════════════════╝\n');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function printSuccess(projectName, templateType, projectPath) {
|
|
17
|
+
console.log('\n✅ Project created successfully!\n');
|
|
18
|
+
console.log(`📁 Name: ${projectName}`);
|
|
19
|
+
console.log(`🛠️ Template: ${templateType}`);
|
|
20
|
+
console.log(`📂 Path: ${projectPath}\n`);
|
|
21
|
+
console.log('Next steps:');
|
|
22
|
+
console.log(` 1. cd ${projectName}`);
|
|
23
|
+
console.log(' 2. npm install');
|
|
24
|
+
console.log(' 3. cp .env.example .env');
|
|
25
|
+
console.log(' 4. Fill .env with your configuration');
|
|
26
|
+
console.log(' 5. npm start\n');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { printBanner, printSuccess };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
NODE_ENV=test
|
|
2
|
+
MONGODB_URI=mongodb://localhost:27017/test
|
|
3
|
+
JWT_SECRET=this_is_a_test_secret_that_is_long_enough
|
|
4
|
+
JWT_REFRESH_SECRET=this_is_a_test_refresh_secret_long_enough
|
|
5
|
+
JWT_EXPIRES_IN=7d
|
|
6
|
+
JWT_REFRESH_EXPIRES_IN=30d
|
|
7
|
+
BCRYPT_SALT_ROUNDS=12
|
|
8
|
+
RATE_LIMIT_WINDOW_MS=900000
|
|
9
|
+
RATE_LIMIT_MAX=100
|
|
10
|
+
AUTH_RATE_LIMIT_MAX=10
|
|
11
|
+
CORS_ORIGIN=http://localhost:3000
|
|
12
|
+
LOG_LEVEL=debug
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
NODE_ENV=development
|
|
2
|
+
PORT=3000
|
|
3
|
+
MONGODB_URI=
|
|
4
|
+
JWT_SECRET=
|
|
5
|
+
JWT_REFRESH_SECRET=
|
|
6
|
+
JWT_EXPIRES_IN=7d
|
|
7
|
+
JWT_REFRESH_EXPIRES_IN=30d
|
|
8
|
+
BCRYPT_SALT_ROUNDS=12
|
|
9
|
+
RATE_LIMIT_WINDOW_MS=900000
|
|
10
|
+
RATE_LIMIT_MAX=100
|
|
11
|
+
AUTH_RATE_LIMIT_MAX=10
|
|
12
|
+
CORS_ORIGIN=http://localhost:3000
|
|
13
|
+
LOG_LEVEL=info
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["eslint:recommended"],
|
|
3
|
+
"env": {
|
|
4
|
+
"node": true,
|
|
5
|
+
"es2022": true
|
|
6
|
+
},
|
|
7
|
+
"parserOptions": {
|
|
8
|
+
"ecmaVersion": 2022,
|
|
9
|
+
"sourceType": "module"
|
|
10
|
+
},
|
|
11
|
+
"rules": {
|
|
12
|
+
"no-console": "off",
|
|
13
|
+
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
14
|
+
"prefer-const": "error",
|
|
15
|
+
"no-var": "error",
|
|
16
|
+
"eqeqeq": ["error", "always"],
|
|
17
|
+
"curly": "error",
|
|
18
|
+
"brace-style": ["error", "1tbs"],
|
|
19
|
+
"quotes": ["error", "single", { "avoidEscape": true }],
|
|
20
|
+
"semi": ["error", "always"],
|
|
21
|
+
"comma-dangle": ["error", "always-multiline"],
|
|
22
|
+
"indent": ["error", 2]
|
|
23
|
+
}
|
|
24
|
+
}
|