create-scafty 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/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # ๐Ÿš€ create-scafty
2
+
3
+ A modern CLI to scaffold a backend API with **Express**, **TypeScript**, **ESLint**, and **Prettier** โ€” instantly.
4
+
5
+ ---
6
+
7
+ ## โœจ Features
8
+
9
+ - โšก Instant backend setup
10
+ - ๐ŸŸฆ Optional TypeScript support
11
+ - ๐Ÿงน ESLint (with import sorting)
12
+ - ๐Ÿ’… Prettier configuration
13
+ - ๐Ÿ“ Optional `src/` structure
14
+ - ๐Ÿ“ฆ Choose your package manager (npm / pnpm / yarn)
15
+ - ๐ŸŒ Express API starter included
16
+ - ๐Ÿงฑ Modular and scalable architecture
17
+
18
+ ---
19
+
20
+ ## ๐Ÿ“ฆ Usage
21
+
22
+ ### 1. Create a new project
23
+
24
+ ```bash
25
+ npm create scafty@latest
26
+ ```
27
+
28
+ or
29
+
30
+ ```bash
31
+ npx create-scafty
32
+ ```
33
+
34
+ ---
35
+
36
+ ### 2. Follow the prompts
37
+
38
+ Youโ€™ll be asked to configure:
39
+
40
+ - Project name
41
+ - TypeScript support
42
+ - ESLint setup
43
+ - Import sorting
44
+ - Prettier
45
+ - `src/` directory
46
+ - Package manager
47
+
48
+ ---
49
+
50
+ ### 3. Start development
51
+
52
+ ```bash
53
+ cd your-project
54
+ npm run dev
55
+ ```
56
+
57
+ ---
58
+
59
+ ## ๐Ÿ“ Generated Structure
60
+
61
+ Example output:
62
+
63
+ ```bash
64
+ your-project/
65
+ โ”œโ”€โ”€ src/
66
+ โ”‚ โ””โ”€โ”€ index.ts / index.js
67
+ โ”œโ”€โ”€ package.json
68
+ โ”œโ”€โ”€ tsconfig.json (if TS selected)
69
+ โ”œโ”€โ”€ eslint.config.js (if enabled)
70
+ โ”œโ”€โ”€ .prettierrc (if enabled)
71
+ ```
72
+
73
+ ---
74
+
75
+ ## ๐Ÿš€ Express Starter
76
+
77
+ The generated project includes a minimal Express server:
78
+
79
+ ```ts
80
+ app.get('/', (req, res) => {
81
+ res.json({ message: 'API running ๐Ÿš€' });
82
+ });
83
+ ```
84
+
85
+ ---
86
+
87
+ ## โš™๏ธ Requirements
88
+
89
+ - Node.js >= 18
90
+ - npm / pnpm / yarn
91
+
92
+ ---
93
+
94
+ ## ๐Ÿ›  Development (for contributors)
95
+
96
+ Clone the repo:
97
+
98
+ ```bash
99
+ git clone <your-repo-url>
100
+ cd create-scafty
101
+ ```
102
+
103
+ Install dependencies:
104
+
105
+ ```bash
106
+ npm install
107
+ ```
108
+
109
+ Build the CLI:
110
+
111
+ ```bash
112
+ npm run build
113
+ ```
114
+
115
+ Test locally:
116
+
117
+ ```bash
118
+ npm link
119
+ create-scafty
120
+ ```
121
+
122
+ ---
123
+
124
+ ## ๐Ÿ“ฆ Publishing
125
+
126
+ ```bash
127
+ npm run build
128
+ npm publish --access public
129
+ ```
130
+
131
+ ---
132
+
133
+ ## ๐Ÿง  Roadmap
134
+
135
+ - [ ] CLI flags (`--ts`, `--eslint`)
136
+ - [ ] Multiple templates (MVC, Clean Architecture)
137
+ - [ ] Git initialization
138
+ - [ ] Environment file generator
139
+ - [ ] Plugin system
140
+
141
+ ---
142
+
143
+ ## ๐Ÿค Contributing
144
+
145
+ Pull requests are welcome!
146
+ Feel free to open issues for suggestions or bugs.
147
+
148
+ ---
149
+
150
+ ## ๐Ÿ“„ License
151
+
152
+ MIT
153
+
154
+ ---
155
+
156
+ ## ๐Ÿ’ก Inspiration
157
+
158
+ Inspired by modern scaffolding tools like:
159
+
160
+ - create-vite
161
+ - create-next-app
162
+
163
+ ---
164
+
165
+ ## โญ Support
166
+
167
+ If you like this project, give it a โญ on GitHub!
package/dist/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ import path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { askQuestions } from './prompts.js';
4
+ import { createProject } from './generators/project.js';
5
+ import { setupTS } from './generators/ts.js';
6
+ import { setupESLint } from './generators/eslint.js';
7
+ import { setupPrettier } from './generators/prettier.js';
8
+ import { createExpressApp } from './generators/express.js';
9
+ import { installDeps } from './install.js';
10
+ import { logSuccess, logError, logInfo } from './utils/logger.js';
11
+ import { createManager } from './utils/createManager.js';
12
+ const rootDir = process.cwd();
13
+ async function main() {
14
+ try {
15
+ logInfo('\n๐Ÿš€ Backend Project Setup\n');
16
+ const answers = await askQuestions();
17
+ const projectName = answers.name?.trim().replace(/\s+/g, '-').toLowerCase() || 'backend';
18
+ const projectDir = path.join(rootDir, projectName);
19
+ if (await fs.pathExists(projectDir)) {
20
+ logError('Folder already exists. Aborting.');
21
+ process.exit(1);
22
+ }
23
+ await createProject(projectDir, projectName, answers);
24
+ const depsKeyName = 'deps';
25
+ const devDepsKeyName = 'devDeps';
26
+ const depsManager = createManager(depsKeyName);
27
+ const devDepsManager = createManager(devDepsKeyName);
28
+ if (answers.typescript) {
29
+ await setupTS(projectDir, answers, depsManager, devDepsManager);
30
+ }
31
+ if (answers.eslint) {
32
+ await setupESLint(projectDir, answers, depsManager, devDepsManager);
33
+ }
34
+ if (answers.prettier) {
35
+ await setupPrettier(projectDir, answers, depsManager, devDepsManager);
36
+ }
37
+ await createExpressApp(projectDir, answers, depsManager, devDepsManager);
38
+ await installDeps(projectDir, answers, depsManager, devDepsManager);
39
+ logSuccess(`\nโœ… Project created.\n`);
40
+ }
41
+ catch (err) {
42
+ logError(err.message);
43
+ }
44
+ }
45
+ main();
@@ -0,0 +1,66 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function setupESLint(projectDir, answers, deps, devDeps) {
4
+ devDeps.add('eslint');
5
+ devDeps.add('globals');
6
+ let config = `import js from "@eslint/js";
7
+ import globals from "globals";
8
+ import { defineConfig } from "eslint/config";`;
9
+ // TS support
10
+ if (answers.typescript) {
11
+ devDeps.add('typescript-eslint');
12
+ config += `
13
+ import tseslint from "typescript-eslint";`;
14
+ }
15
+ // Import sort plugin
16
+ if (answers.importSort) {
17
+ devDeps.add('eslint-plugin-simple-import-sort');
18
+ config += `
19
+ import simpleImportSort from "eslint-plugin-simple-import-sort";`;
20
+ }
21
+ config += `
22
+
23
+ export default defineConfig([
24
+ js.configs.recommended,`;
25
+ // TS config
26
+ if (answers.typescript) {
27
+ config += `
28
+ ...tseslint.configs.recommended,`;
29
+ }
30
+ config += `
31
+ {
32
+ files: ["**/*.{js,ts}"],
33
+ languageOptions: {
34
+ globals: globals.node,
35
+ },
36
+ plugins: {`;
37
+ if (answers.importSort) {
38
+ config += `
39
+ "simple-import-sort": simpleImportSort,`;
40
+ }
41
+ config += `
42
+ },
43
+ rules: {`;
44
+ // TS vs JS rules
45
+ if (answers.typescript) {
46
+ config += `
47
+ "no-unused-vars": "off",
48
+ "@typescript-eslint/no-unused-vars": "warn",`;
49
+ }
50
+ else {
51
+ config += `
52
+ "no-unused-vars": "warn",`;
53
+ }
54
+ // import sort rules
55
+ if (answers.importSort) {
56
+ config += `
57
+
58
+ "simple-import-sort/imports": "warn",
59
+ "simple-import-sort/exports": "warn",`;
60
+ }
61
+ config += `
62
+ }
63
+ }
64
+ ]);`;
65
+ await fs.writeFile(path.join(projectDir, 'eslint.config.js'), config);
66
+ }
@@ -0,0 +1,25 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function createExpressApp(projectDir, answers, deps, devDeps) {
4
+ const srcPath = answers.srcDir ? path.join(projectDir, 'src') : projectDir;
5
+ await fs.mkdirp(srcPath);
6
+ const ext = answers.typescript ? 'ts' : 'js';
7
+ deps.add('express');
8
+ devDeps.add('@types/node');
9
+ devDeps.add('@types/express');
10
+ await fs.writeFile(path.join(srcPath, `index.${ext}`), `import express from 'express';
11
+
12
+ const app = express();
13
+ const PORT = process.env["PORT"] || 3586;
14
+
15
+ app.use(express.json());
16
+
17
+ app.get('/', (_, res) => {
18
+ res.json({ message: 'API running ๐Ÿš€' });
19
+ });
20
+
21
+ app.listen(PORT, () => {
22
+ console.log(\`Server running at http://localhost:\${PORT}\`);
23
+ });
24
+ `);
25
+ }
@@ -0,0 +1,27 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function setupPrettier(projectDir, answers, deps, devDeps) {
4
+ devDeps.add('prettier');
5
+ await fs.writeJSON(path.join(projectDir, '.prettierrc'), {
6
+ singleQuote: true,
7
+ semi: true,
8
+ tabWidth: 2,
9
+ useTabs: false,
10
+ trailingComma: 'es5',
11
+ printWidth: 80,
12
+ bracketSpacing: true,
13
+ bracketSameLine: false,
14
+ }, { spaces: 2 });
15
+ await fs.writeFile(path.join(projectDir, '.prettierignore'), `node_modules
16
+ dist
17
+ build
18
+ *.min.js
19
+
20
+ .next
21
+ out
22
+
23
+ *.config.ts
24
+ *.config.js
25
+ *.config.mjs
26
+ `);
27
+ }
@@ -0,0 +1,14 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function createProject(projectDir, name, answers) {
4
+ await fs.mkdirp(projectDir);
5
+ await fs.writeJSON(path.join(projectDir, 'package.json'), {
6
+ name,
7
+ version: '1.0.0',
8
+ type: 'module',
9
+ private: true,
10
+ scripts: {
11
+ dev: answers.typescript ? 'tsx src/index.ts' : 'node src/index.js',
12
+ },
13
+ }, { spaces: 2 });
14
+ }
@@ -0,0 +1,41 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function setupTS(projectDir, answers, deps, devDeps) {
4
+ devDeps.add('typescript');
5
+ devDeps.add('tsx');
6
+ await fs.writeFile(path.join(projectDir, 'tsconfig.json'), `{
7
+ "compilerOptions": {
8
+ /* Language and Environment */
9
+ "target": "ESNext",
10
+ ${answers.srcDir ? `"rootDir": "src",` : ''}
11
+ "outDir": "dist",
12
+ "types": ["node"],
13
+
14
+ /* Modules */
15
+ "module": "NodeNext",
16
+ "moduleResolution": "NodeNext",
17
+ "verbatimModuleSyntax": true,
18
+
19
+ /* Interop */
20
+ "esModuleInterop": true,
21
+ "forceConsistentCasingInFileNames": true,
22
+
23
+ /* Type Checking */
24
+ "strict": true,
25
+ "noUnusedLocals": true,
26
+ "noUnusedParameters": true,
27
+ "noFallthroughCasesInSwitch": true,
28
+ "noUncheckedIndexedAccess": true,
29
+ "noImplicitOverride": true,
30
+
31
+ /* Linting */
32
+ "erasableSyntaxOnly": true,
33
+ "noPropertyAccessFromIndexSignature": true,
34
+
35
+ /* Performance */
36
+ "skipLibCheck": true,
37
+ },
38
+ ${answers.srcDir ? `"include": ["src"],` : ''}
39
+ "exclude": ["node_modules", "dist"]
40
+ }`);
41
+ }
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import './cli.js';
@@ -0,0 +1,19 @@
1
+ import {} from './prompts.js';
2
+ import { Manager } from './utils/createManager.js';
3
+ import { run } from './utils/run.js';
4
+ const registry = {
5
+ pnpm: 'pnpm add',
6
+ yarn: 'yarn add',
7
+ npm: 'npm i',
8
+ };
9
+ export async function installDeps(projectDir, answers, deps, devDeps) {
10
+ const cmd = registry[answers['packageManager']];
11
+ if (!cmd)
12
+ throw new Error(`Unknown package manager: ${answers['packageManager']}`);
13
+ const depsManager = deps.get();
14
+ const devDepsManager = devDeps.get();
15
+ if (depsManager.length)
16
+ run(`${cmd} ${depsManager.join(' ')}`, projectDir);
17
+ if (devDepsManager.length)
18
+ run(`${cmd} -D ${devDepsManager.join(' ')}`, projectDir);
19
+ }
@@ -0,0 +1,49 @@
1
+ import inquirer, {} from 'inquirer';
2
+ export const packageManagerList = ['pnpm', 'yarn', 'npm'];
3
+ const questions = [
4
+ {
5
+ name: 'name',
6
+ type: 'input',
7
+ message: 'Project name:',
8
+ default: 'backend',
9
+ },
10
+ {
11
+ name: 'typescript',
12
+ type: 'confirm',
13
+ message: 'Use TypeScript?',
14
+ default: true,
15
+ },
16
+ {
17
+ name: 'eslint',
18
+ type: 'confirm',
19
+ message: 'Add ESLint?',
20
+ default: true,
21
+ },
22
+ {
23
+ name: 'importSort',
24
+ type: 'confirm',
25
+ message: 'Add import sorting?',
26
+ default: true,
27
+ },
28
+ {
29
+ name: 'prettier',
30
+ type: 'confirm',
31
+ message: 'Add Prettier?',
32
+ default: true,
33
+ },
34
+ {
35
+ name: 'srcDir',
36
+ type: 'confirm',
37
+ message: 'Create src folder?',
38
+ default: true,
39
+ },
40
+ {
41
+ name: 'packageManager',
42
+ type: 'list',
43
+ message: 'Select package manager:',
44
+ choices: packageManagerList,
45
+ },
46
+ ];
47
+ export async function askQuestions() {
48
+ return inquirer.prompt(questions);
49
+ }
@@ -0,0 +1,22 @@
1
+ export class Manager {
2
+ manager = {};
3
+ key;
4
+ constructor(key) {
5
+ this.key = key;
6
+ this.manager[key] = [];
7
+ }
8
+ get() {
9
+ return this.manager[this.key] ?? [];
10
+ }
11
+ add(str) {
12
+ this.get().push(str);
13
+ }
14
+ remove(str) {
15
+ const arr = this.get();
16
+ const index = arr.indexOf(str);
17
+ if (index !== -1) {
18
+ arr.splice(index, 1);
19
+ }
20
+ }
21
+ }
22
+ export const createManager = (key) => new Manager(key);
@@ -0,0 +1,5 @@
1
+ import chalk from 'chalk';
2
+ export const logInfo = (msg) => console.log(chalk.cyan(msg));
3
+ export const logSuccess = (msg) => console.log(chalk.green(msg));
4
+ export const logError = (msg) => console.log(chalk.red(msg));
5
+ export const logWarning = (msg) => console.log(chalk.yellow(msg));
@@ -0,0 +1,4 @@
1
+ import { execSync } from 'child_process';
2
+ export function run(cmd, cwd) {
3
+ execSync(cmd, { stdio: 'inherit', cwd });
4
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "create-scafty",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Scaffold Express backend with TS, ESLint, Prettier",
6
+ "bin": {
7
+ "create-scafty": "./dist/index.js",
8
+ "scafty": "./dist/index.js"
9
+ },
10
+ "keywords": [
11
+ "cli",
12
+ "scaffold",
13
+ "express",
14
+ "typescript"
15
+ ],
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.build.json",
21
+ "dev": "tsx src/cli.ts"
22
+ },
23
+ "dependencies": {
24
+ "chalk": "^5.6.2",
25
+ "fs-extra": "^11.3.4",
26
+ "inquirer": "^9.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/fs-extra": "^11.0.4",
30
+ "@types/inquirer": "^9.0.9",
31
+ "@types/node": "^25.6.0",
32
+ "eslint": "^10.2.0",
33
+ "eslint-plugin-simple-import-sort": "^13.0.0",
34
+ "globals": "^17.5.0",
35
+ "prettier": "^3.8.2",
36
+ "tsx": "^4.21.0",
37
+ "typescript": "^6.0.2",
38
+ "typescript-eslint": "^8.58.1"
39
+ }
40
+ }