scaffy-tool 0.2.0 → 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 +20 -25
- package/cli.js +14 -35
- package/core/detector.js +5 -31
- package/core/executor.js +5 -27
- package/core/interviewer.js +4 -11
- package/core/plugin-validator.js +1 -1
- package/core/registry.js +37 -25
- package/core/utils.js +2 -19
- package/package.json +11 -11
- package/registry/go/gin/plugin.json +23 -0
- package/registry/go/gin/v1/questions.js +19 -0
- package/registry/go/gin/v1/scaffold.js +196 -0
- package/registry/index.json +2 -2
- package/registry/javascript/expressjs/plugin.json +44 -0
- package/registry/javascript/expressjs/v4/questions.js +43 -0
- package/registry/javascript/expressjs/v4/scaffold.js +236 -0
- package/registry/javascript/nestjs/plugin.json +2 -2
- package/registry/javascript/nestjs/v10/questions.js +4 -2
- package/registry/javascript/nestjs/v10/scaffold.js +1 -1
- package/registry/javascript/nestjs/v11/questions.js +61 -0
- package/registry/javascript/nestjs/v11/scaffold.js +94 -0
- package/registry/javascript/nextjs/plugin.json +44 -0
- package/registry/javascript/nextjs/v14/questions.js +44 -0
- package/registry/javascript/nextjs/v14/scaffold.js +57 -0
- package/registry/javascript/vuejs/v3/questions.js +4 -2
- package/registry/javascript/vuejs/v3/scaffold.js +3 -10
- package/registry/php/laravel/plugin.json +2 -2
- package/registry/php/laravel/v11/questions.js +1 -1
- package/registry/php/laravel/v11/scaffold.js +1 -1
- package/registry/php/laravel/v12/questions.js +45 -0
- package/registry/php/laravel/v12/scaffold.js +46 -0
- package/registry/php/laravel/v13/questions.js +28 -0
- package/registry/php/laravel/v13/scaffold.js +46 -0
- package/registry/php/laravel/v13/test/.gitkeep +0 -0
- package/registry/php/laravel/v13/test/questions.test.js +48 -0
- package/registry/php/laravel/v13/test/scaffold.test.js +105 -0
- package/registry/php/symfony/plugin.json +35 -0
- package/registry/php/symfony/v7/questions.js +32 -0
- package/registry/php/symfony/v7/scaffold.js +86 -0
- package/registry/python/django/plugin.json +35 -0
- package/registry/python/django/v5/questions.js +24 -0
- package/registry/python/django/v5/scaffold.js +107 -0
- package/registry/python/fastapi/plugin.json +35 -0
- package/registry/python/fastapi/v1/questions.js +25 -0
- package/registry/python/fastapi/v1/scaffold.js +180 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
describe('Laravel v13 scaffold', () => {
|
|
4
|
+
let scaffold;
|
|
5
|
+
let mockUtils;
|
|
6
|
+
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
const mod = await import('../scaffold.js');
|
|
9
|
+
scaffold = mod.default;
|
|
10
|
+
|
|
11
|
+
mockUtils = {
|
|
12
|
+
run: jest.fn().mockResolvedValue(),
|
|
13
|
+
runInProject: jest.fn().mockResolvedValue(),
|
|
14
|
+
setEnv: jest.fn(),
|
|
15
|
+
createFile: jest.fn(),
|
|
16
|
+
appendToFile: jest.fn(),
|
|
17
|
+
log: jest.fn(),
|
|
18
|
+
success: jest.fn(),
|
|
19
|
+
warn: jest.fn(),
|
|
20
|
+
error: jest.fn(),
|
|
21
|
+
title: jest.fn(),
|
|
22
|
+
step: jest.fn(),
|
|
23
|
+
divider: jest.fn(),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('runs the laravel v13 installer', async () => {
|
|
28
|
+
await scaffold(
|
|
29
|
+
{
|
|
30
|
+
projectName: 'my-app',
|
|
31
|
+
starterKit: 'none',
|
|
32
|
+
database: 'sqlite',
|
|
33
|
+
docker: false,
|
|
34
|
+
},
|
|
35
|
+
mockUtils
|
|
36
|
+
);
|
|
37
|
+
expect(mockUtils.run).toHaveBeenCalledWith(
|
|
38
|
+
expect.stringContaining('laravel/laravel:^13.0 my-app')
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('installs breeze when selected', async () => {
|
|
43
|
+
await scaffold(
|
|
44
|
+
{
|
|
45
|
+
projectName: 'my-app',
|
|
46
|
+
starterKit: 'breeze',
|
|
47
|
+
database: 'sqlite',
|
|
48
|
+
docker: false,
|
|
49
|
+
},
|
|
50
|
+
mockUtils
|
|
51
|
+
);
|
|
52
|
+
expect(mockUtils.runInProject).toHaveBeenCalledWith(
|
|
53
|
+
'my-app',
|
|
54
|
+
expect.stringContaining('breeze')
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('installs jetstream when selected', async () => {
|
|
59
|
+
await scaffold(
|
|
60
|
+
{
|
|
61
|
+
projectName: 'my-app',
|
|
62
|
+
starterKit: 'jetstream',
|
|
63
|
+
database: 'sqlite',
|
|
64
|
+
docker: false,
|
|
65
|
+
},
|
|
66
|
+
mockUtils
|
|
67
|
+
);
|
|
68
|
+
expect(mockUtils.runInProject).toHaveBeenCalledWith(
|
|
69
|
+
'my-app',
|
|
70
|
+
expect.stringContaining('jetstream')
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('sets database env vars', async () => {
|
|
75
|
+
await scaffold(
|
|
76
|
+
{
|
|
77
|
+
projectName: 'my-app',
|
|
78
|
+
starterKit: 'none',
|
|
79
|
+
database: 'mysql',
|
|
80
|
+
docker: false,
|
|
81
|
+
},
|
|
82
|
+
mockUtils
|
|
83
|
+
);
|
|
84
|
+
expect(mockUtils.setEnv).toHaveBeenCalledWith(
|
|
85
|
+
'my-app',
|
|
86
|
+
expect.objectContaining({ DB_CONNECTION: 'mysql' })
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('installs sail when docker is true', async () => {
|
|
91
|
+
await scaffold(
|
|
92
|
+
{
|
|
93
|
+
projectName: 'my-app',
|
|
94
|
+
starterKit: 'none',
|
|
95
|
+
database: 'sqlite',
|
|
96
|
+
docker: true,
|
|
97
|
+
},
|
|
98
|
+
mockUtils
|
|
99
|
+
);
|
|
100
|
+
expect(mockUtils.runInProject).toHaveBeenCalledWith(
|
|
101
|
+
'my-app',
|
|
102
|
+
expect.stringContaining('sail')
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Symfony",
|
|
3
|
+
"alias": ["symfony"],
|
|
4
|
+
"language": "php",
|
|
5
|
+
"latest": "v7",
|
|
6
|
+
"versions": ["v7"],
|
|
7
|
+
"description": "High-performance PHP framework for web applications and APIs",
|
|
8
|
+
"requires": [
|
|
9
|
+
{
|
|
10
|
+
"tool": "php",
|
|
11
|
+
"checkCommand": "php --version",
|
|
12
|
+
"parseVersion": "PHP ([0-9]+\\.[0-9]+\\.[0-9]+)",
|
|
13
|
+
"minVersion": "8.2.0",
|
|
14
|
+
"installGuide": {
|
|
15
|
+
"mac": "brew install php",
|
|
16
|
+
"linux": "sudo apt install php8.2",
|
|
17
|
+
"windows": "https://windows.php.net",
|
|
18
|
+
"docs": "https://php.net"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"tool": "composer",
|
|
23
|
+
"checkCommand": "composer --version",
|
|
24
|
+
"parseVersion": "Composer version ([0-9]+\\.[0-9]+\\.[0-9]+)",
|
|
25
|
+
"minVersion": "2.0.0",
|
|
26
|
+
"installGuide": {
|
|
27
|
+
"mac": "brew install composer",
|
|
28
|
+
"linux": "sudo apt install composer",
|
|
29
|
+
"windows": "https://getcomposer.org/download",
|
|
30
|
+
"docs": "https://getcomposer.org"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"maintainer": "community"
|
|
35
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
type: 'list',
|
|
4
|
+
name: 'projectType',
|
|
5
|
+
message: 'Project type?',
|
|
6
|
+
choices: [
|
|
7
|
+
{ name: 'Web App — full stack with Twig, forms, ORM', value: 'webapp' },
|
|
8
|
+
{ name: 'API — REST API with Symfony skeleton', value: 'api' },
|
|
9
|
+
{
|
|
10
|
+
name: 'Microservice — minimal Symfony skeleton',
|
|
11
|
+
value: 'microservice',
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
type: 'list',
|
|
17
|
+
name: 'database',
|
|
18
|
+
message: 'Database?',
|
|
19
|
+
choices: [
|
|
20
|
+
{ name: 'MySQL', value: 'mysql' },
|
|
21
|
+
{ name: 'PostgreSQL', value: 'postgresql' },
|
|
22
|
+
{ name: 'SQLite', value: 'sqlite' },
|
|
23
|
+
{ name: 'None', value: 'none' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'confirm',
|
|
28
|
+
name: 'docker',
|
|
29
|
+
message: 'Include Docker config?',
|
|
30
|
+
default: false,
|
|
31
|
+
},
|
|
32
|
+
];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export default async (answers, utils) => {
|
|
2
|
+
const { projectName, projectType, database, docker } = answers;
|
|
3
|
+
|
|
4
|
+
utils.title('Creating Symfony v7 Project');
|
|
5
|
+
|
|
6
|
+
// ─── Step 1: Install ───────────────────────────────────
|
|
7
|
+
utils.step(1, 'Running Symfony installer');
|
|
8
|
+
|
|
9
|
+
if (projectType === 'webapp') {
|
|
10
|
+
await utils.run(
|
|
11
|
+
`composer create-project symfony/website-skeleton:"7.*" ${projectName}`
|
|
12
|
+
);
|
|
13
|
+
} else {
|
|
14
|
+
await utils.run(
|
|
15
|
+
`composer create-project symfony/skeleton:"7.*" ${projectName}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── Step 2: API Platform ──────────────────────────────
|
|
20
|
+
if (projectType === 'api') {
|
|
21
|
+
utils.step(2, 'Installing API Platform');
|
|
22
|
+
await utils.runInProject(projectName, 'composer require api-platform/core');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── Step 3: Database ──────────────────────────────────
|
|
26
|
+
if (database !== 'none') {
|
|
27
|
+
utils.step(3, 'Configuring database');
|
|
28
|
+
await utils.runInProject(projectName, 'composer require symfony/orm-pack');
|
|
29
|
+
await utils.runInProject(
|
|
30
|
+
projectName,
|
|
31
|
+
'composer require --dev symfony/maker-bundle'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const dsnMap = {
|
|
35
|
+
mysql: `mysql://root:password@127.0.0.1:3306/${projectName}?serverVersion=8.0`,
|
|
36
|
+
postgresql: `postgresql://root:password@127.0.0.1:5432/${projectName}?serverVersion=15`,
|
|
37
|
+
sqlite: `sqlite:///%kernel.project_dir%/var/data.db`,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
await utils.setEnv(projectName, {
|
|
41
|
+
DATABASE_URL: dsnMap[database],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Step 4: Docker ────────────────────────────────────
|
|
46
|
+
if (docker) {
|
|
47
|
+
utils.step(4, 'Creating Docker config');
|
|
48
|
+
|
|
49
|
+
const dockerCompose = `version: '3.8'
|
|
50
|
+
services:
|
|
51
|
+
php:
|
|
52
|
+
image: php:8.2-fpm
|
|
53
|
+
volumes:
|
|
54
|
+
- .:/var/www/html
|
|
55
|
+
ports:
|
|
56
|
+
- "9000:9000"
|
|
57
|
+
${
|
|
58
|
+
database === 'mysql'
|
|
59
|
+
? ` db:
|
|
60
|
+
image: mysql:8.0
|
|
61
|
+
environment:
|
|
62
|
+
MYSQL_ROOT_PASSWORD: password
|
|
63
|
+
MYSQL_DATABASE: ${projectName}
|
|
64
|
+
ports:
|
|
65
|
+
- "3306:3306"`
|
|
66
|
+
: ''
|
|
67
|
+
}
|
|
68
|
+
${
|
|
69
|
+
database === 'postgresql'
|
|
70
|
+
? ` db:
|
|
71
|
+
image: postgres:15
|
|
72
|
+
environment:
|
|
73
|
+
POSTGRES_PASSWORD: password
|
|
74
|
+
POSTGRES_DB: ${projectName}
|
|
75
|
+
ports:
|
|
76
|
+
- "5432:5432"`
|
|
77
|
+
: ''
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
await utils.createFile(`${projectName}/docker-compose.yml`, dockerCompose);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
utils.success(`Symfony v7 project "${projectName}" created successfully!`);
|
|
84
|
+
utils.log(` cd ${projectName}`);
|
|
85
|
+
utils.log(` symfony server:start`);
|
|
86
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Django",
|
|
3
|
+
"alias": ["django"],
|
|
4
|
+
"language": "python",
|
|
5
|
+
"latest": "v5",
|
|
6
|
+
"versions": ["v5"],
|
|
7
|
+
"description": "High-level Python web framework for rapid development",
|
|
8
|
+
"requires": [
|
|
9
|
+
{
|
|
10
|
+
"tool": "python",
|
|
11
|
+
"checkCommand": "python3 --version",
|
|
12
|
+
"parseVersion": "Python ([0-9]+\\.[0-9]+\\.[0-9]+)",
|
|
13
|
+
"minVersion": "3.10.0",
|
|
14
|
+
"installGuide": {
|
|
15
|
+
"mac": "brew install python",
|
|
16
|
+
"linux": "sudo apt install python3",
|
|
17
|
+
"windows": "https://python.org/downloads",
|
|
18
|
+
"docs": "https://python.org"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"tool": "pip",
|
|
23
|
+
"checkCommand": "pip3 --version",
|
|
24
|
+
"parseVersion": "pip ([0-9]+\\.[0-9]+)",
|
|
25
|
+
"minVersion": "22.0.0",
|
|
26
|
+
"installGuide": {
|
|
27
|
+
"mac": "brew install python",
|
|
28
|
+
"linux": "sudo apt install python3-pip",
|
|
29
|
+
"windows": "https://pip.pypa.io/en/stable/installation",
|
|
30
|
+
"docs": "https://pip.pypa.io"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"maintainer": "community"
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
type: 'list',
|
|
4
|
+
name: 'database',
|
|
5
|
+
message: 'Database?',
|
|
6
|
+
choices: [
|
|
7
|
+
{ name: 'SQLite — default, no setup needed', value: 'sqlite' },
|
|
8
|
+
{ name: 'PostgreSQL', value: 'postgresql' },
|
|
9
|
+
{ name: 'MySQL', value: 'mysql' },
|
|
10
|
+
],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
type: 'confirm',
|
|
14
|
+
name: 'restFramework',
|
|
15
|
+
message: 'Include Django REST Framework?',
|
|
16
|
+
default: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'confirm',
|
|
20
|
+
name: 'docker',
|
|
21
|
+
message: 'Include Docker config?',
|
|
22
|
+
default: false,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export default async (answers, utils) => {
|
|
2
|
+
const { projectName, database, restFramework, docker } = answers;
|
|
3
|
+
|
|
4
|
+
utils.title('Creating Django v5 Project');
|
|
5
|
+
|
|
6
|
+
// ─── Step 1: Install Django ────────────────────────────
|
|
7
|
+
utils.step(1, 'Installing Django');
|
|
8
|
+
await utils.run('pip3 install "django>=5.0,<6.0"');
|
|
9
|
+
|
|
10
|
+
// ─── Step 2: Create Project ────────────────────────────
|
|
11
|
+
utils.step(2, 'Creating Django project');
|
|
12
|
+
await utils.run(`django-admin startproject ${projectName}`);
|
|
13
|
+
|
|
14
|
+
// ─── Step 3: Database ──────────────────────────────────
|
|
15
|
+
if (database === 'postgresql') {
|
|
16
|
+
utils.step(3, 'Installing PostgreSQL driver');
|
|
17
|
+
await utils.runInProject(projectName, 'pip3 install psycopg2-binary');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (database === 'mysql') {
|
|
21
|
+
utils.step(3, 'Installing MySQL driver');
|
|
22
|
+
await utils.runInProject(projectName, 'pip3 install mysqlclient');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── Step 4: REST Framework ────────────────────────────
|
|
26
|
+
if (restFramework) {
|
|
27
|
+
utils.step(4, 'Installing Django REST Framework');
|
|
28
|
+
await utils.runInProject(projectName, 'pip3 install djangorestframework');
|
|
29
|
+
|
|
30
|
+
const settingsPath = `${projectName}/${projectName}/settings.py`;
|
|
31
|
+
await utils.appendToFile(
|
|
32
|
+
settingsPath,
|
|
33
|
+
`\n# Django REST Framework\nINSTALLED_APPS += ['rest_framework']\n`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── Step 5: requirements.txt ──────────────────────────
|
|
38
|
+
utils.step(5, 'Generating requirements.txt');
|
|
39
|
+
|
|
40
|
+
const requirements = [`django>=5.0,<6.0`];
|
|
41
|
+
if (database === 'postgresql') requirements.push('psycopg2-binary');
|
|
42
|
+
if (database === 'mysql') requirements.push('mysqlclient');
|
|
43
|
+
if (restFramework) requirements.push('djangorestframework');
|
|
44
|
+
|
|
45
|
+
await utils.createFile(
|
|
46
|
+
`${projectName}/requirements.txt`,
|
|
47
|
+
requirements.join('\n') + '\n'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// ─── Step 6: Docker ────────────────────────────────────
|
|
51
|
+
if (docker) {
|
|
52
|
+
utils.step(6, 'Creating Docker config');
|
|
53
|
+
|
|
54
|
+
const dockerfile = `FROM python:3.12-slim
|
|
55
|
+
|
|
56
|
+
WORKDIR /app
|
|
57
|
+
|
|
58
|
+
COPY requirements.txt .
|
|
59
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
60
|
+
|
|
61
|
+
COPY . .
|
|
62
|
+
|
|
63
|
+
EXPOSE 8000
|
|
64
|
+
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const dockerCompose = `version: '3.8'
|
|
68
|
+
services:
|
|
69
|
+
web:
|
|
70
|
+
build: .
|
|
71
|
+
ports:
|
|
72
|
+
- "8000:8000"
|
|
73
|
+
volumes:
|
|
74
|
+
- .:/app
|
|
75
|
+
${
|
|
76
|
+
database === 'postgresql'
|
|
77
|
+
? ` db:
|
|
78
|
+
image: postgres:15
|
|
79
|
+
environment:
|
|
80
|
+
POSTGRES_PASSWORD: password
|
|
81
|
+
POSTGRES_DB: ${projectName}
|
|
82
|
+
ports:
|
|
83
|
+
- "5432:5432"`
|
|
84
|
+
: ''
|
|
85
|
+
}
|
|
86
|
+
${
|
|
87
|
+
database === 'mysql'
|
|
88
|
+
? ` db:
|
|
89
|
+
image: mysql:8.0
|
|
90
|
+
environment:
|
|
91
|
+
MYSQL_ROOT_PASSWORD: password
|
|
92
|
+
MYSQL_DATABASE: ${projectName}
|
|
93
|
+
ports:
|
|
94
|
+
- "3306:3306"`
|
|
95
|
+
: ''
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
await utils.createFile(`${projectName}/Dockerfile`, dockerfile);
|
|
100
|
+
await utils.createFile(`${projectName}/docker-compose.yml`, dockerCompose);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
utils.success(`Django v5 project "${projectName}" created successfully!`);
|
|
104
|
+
utils.log(` cd ${projectName}`);
|
|
105
|
+
utils.log(` python manage.py migrate`);
|
|
106
|
+
utils.log(` python manage.py runserver`);
|
|
107
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "FastAPI",
|
|
3
|
+
"alias": ["fastapi"],
|
|
4
|
+
"language": "python",
|
|
5
|
+
"latest": "v1",
|
|
6
|
+
"versions": ["v1"],
|
|
7
|
+
"description": "Modern, fast Python web framework for building APIs",
|
|
8
|
+
"requires": [
|
|
9
|
+
{
|
|
10
|
+
"tool": "python",
|
|
11
|
+
"checkCommand": "python3 --version",
|
|
12
|
+
"parseVersion": "Python ([0-9]+\\.[0-9]+\\.[0-9]+)",
|
|
13
|
+
"minVersion": "3.10.0",
|
|
14
|
+
"installGuide": {
|
|
15
|
+
"mac": "brew install python",
|
|
16
|
+
"linux": "sudo apt install python3",
|
|
17
|
+
"windows": "https://python.org/downloads",
|
|
18
|
+
"docs": "https://python.org"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"tool": "pip",
|
|
23
|
+
"checkCommand": "pip3 --version",
|
|
24
|
+
"parseVersion": "pip ([0-9]+\\.[0-9]+)",
|
|
25
|
+
"minVersion": "22.0.0",
|
|
26
|
+
"installGuide": {
|
|
27
|
+
"mac": "brew install python",
|
|
28
|
+
"linux": "sudo apt install python3-pip",
|
|
29
|
+
"windows": "https://pip.pypa.io/en/stable/installation",
|
|
30
|
+
"docs": "https://pip.pypa.io"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"maintainer": "community"
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
type: 'list',
|
|
4
|
+
name: 'database',
|
|
5
|
+
message: 'Database?',
|
|
6
|
+
choices: [
|
|
7
|
+
{ name: 'None', value: 'none' },
|
|
8
|
+
{ name: 'PostgreSQL — SQLAlchemy + Alembic', value: 'postgresql' },
|
|
9
|
+
{ name: 'MySQL — SQLAlchemy + Alembic', value: 'mysql' },
|
|
10
|
+
{ name: 'SQLite — SQLAlchemy + Alembic', value: 'sqlite' },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
type: 'confirm',
|
|
15
|
+
name: 'async',
|
|
16
|
+
message: 'Use async database drivers?',
|
|
17
|
+
default: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'confirm',
|
|
21
|
+
name: 'docker',
|
|
22
|
+
message: 'Include Docker config?',
|
|
23
|
+
default: false,
|
|
24
|
+
},
|
|
25
|
+
];
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
export default async (answers, utils) => {
|
|
2
|
+
const { projectName, database, async: useAsync, docker } = answers;
|
|
3
|
+
|
|
4
|
+
utils.title('Creating FastAPI Project');
|
|
5
|
+
|
|
6
|
+
// ─── Step 1: Create project folder ────────────────────
|
|
7
|
+
utils.step(1, 'Creating project structure');
|
|
8
|
+
await utils.run(`mkdir ${projectName}`);
|
|
9
|
+
await utils.run(`mkdir ${projectName}/app`);
|
|
10
|
+
|
|
11
|
+
// ─── Step 2: Create virtual environment ───────────────
|
|
12
|
+
utils.step(2, 'Creating virtual environment');
|
|
13
|
+
await utils.runInProject(projectName, 'python3 -m venv venv');
|
|
14
|
+
|
|
15
|
+
// ─── Step 3: Install FastAPI + Uvicorn ────────────────
|
|
16
|
+
utils.step(3, 'Installing FastAPI and Uvicorn');
|
|
17
|
+
await utils.runInProject(
|
|
18
|
+
projectName,
|
|
19
|
+
'venv/bin/pip install "fastapi>=0.100.0" "uvicorn[standard]"'
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// ─── Step 4: Database drivers ─────────────────────────
|
|
23
|
+
if (database !== 'none') {
|
|
24
|
+
utils.step(4, 'Installing database dependencies');
|
|
25
|
+
await utils.runInProject(
|
|
26
|
+
projectName,
|
|
27
|
+
'venv/bin/pip install sqlalchemy alembic'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (database === 'postgresql') {
|
|
31
|
+
const driver = useAsync ? 'asyncpg' : 'psycopg2-binary';
|
|
32
|
+
await utils.runInProject(projectName, `venv/bin/pip install ${driver}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (database === 'mysql') {
|
|
36
|
+
const driver = useAsync ? 'aiomysql' : 'mysqlclient';
|
|
37
|
+
await utils.runInProject(projectName, `venv/bin/pip install ${driver}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (database === 'sqlite' && useAsync) {
|
|
41
|
+
await utils.runInProject(projectName, 'venv/bin/pip install aiosqlite');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Step 5: Generate main.py ─────────────────────────
|
|
46
|
+
utils.step(5, 'Generating application files');
|
|
47
|
+
|
|
48
|
+
const mainPy = `from fastapi import FastAPI
|
|
49
|
+
|
|
50
|
+
app = FastAPI(title="${projectName}", version="0.1.0")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@app.get("/")
|
|
54
|
+
${useAsync ? 'async ' : ''}def root():
|
|
55
|
+
return {"message": "Hello from ${projectName}"}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@app.get("/health")
|
|
59
|
+
${useAsync ? 'async ' : ''}def health():
|
|
60
|
+
return {"status": "ok"}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
await utils.createFile(`${projectName}/app/main.py`, mainPy);
|
|
64
|
+
|
|
65
|
+
// ─── Step 6: Generate database.py if needed ───────────
|
|
66
|
+
if (database !== 'none') {
|
|
67
|
+
const dsnMap = {
|
|
68
|
+
postgresql: useAsync
|
|
69
|
+
? `postgresql+asyncpg://user:password@localhost/${projectName}`
|
|
70
|
+
: `postgresql+psycopg2://user:password@localhost/${projectName}`,
|
|
71
|
+
mysql: useAsync
|
|
72
|
+
? `mysql+aiomysql://user:password@localhost/${projectName}`
|
|
73
|
+
: `mysql+mysqlclient://user:password@localhost/${projectName}`,
|
|
74
|
+
sqlite: useAsync
|
|
75
|
+
? `sqlite+aiosqlite:///./data.db`
|
|
76
|
+
: `sqlite:///./data.db`,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const databasePy = `from sqlalchemy${useAsync ? '.ext.asyncio' : ''} import create_engine${useAsync ? ', AsyncSession' : ''}, sessionmaker
|
|
80
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
81
|
+
|
|
82
|
+
DATABASE_URL = "${dsnMap[database]}"
|
|
83
|
+
|
|
84
|
+
${
|
|
85
|
+
useAsync
|
|
86
|
+
? `engine = create_engine(DATABASE_URL)
|
|
87
|
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)`
|
|
88
|
+
: `engine = create_engine(DATABASE_URL)
|
|
89
|
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Base(DeclarativeBase):
|
|
94
|
+
pass
|
|
95
|
+
`;
|
|
96
|
+
await utils.createFile(`${projectName}/app/database.py`, databasePy);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─── Step 7: Generate __init__.py ─────────────────────
|
|
100
|
+
await utils.createFile(`${projectName}/app/__init__.py`, '');
|
|
101
|
+
|
|
102
|
+
// ─── Step 8: requirements.txt ─────────────────────────
|
|
103
|
+
utils.step(6, 'Generating requirements.txt');
|
|
104
|
+
|
|
105
|
+
const requirements = ['fastapi>=0.100.0', 'uvicorn[standard]'];
|
|
106
|
+
|
|
107
|
+
if (database !== 'none') {
|
|
108
|
+
requirements.push('sqlalchemy');
|
|
109
|
+
requirements.push('alembic');
|
|
110
|
+
if (database === 'postgresql')
|
|
111
|
+
requirements.push(useAsync ? 'asyncpg' : 'psycopg2-binary');
|
|
112
|
+
if (database === 'mysql')
|
|
113
|
+
requirements.push(useAsync ? 'aiomysql' : 'mysqlclient');
|
|
114
|
+
if (database === 'sqlite' && useAsync) requirements.push('aiosqlite');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await utils.createFile(
|
|
118
|
+
`${projectName}/requirements.txt`,
|
|
119
|
+
requirements.join('\n') + '\n'
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// ─── Step 9: Docker ───────────────────────────────────
|
|
123
|
+
if (docker) {
|
|
124
|
+
utils.step(7, 'Creating Docker config');
|
|
125
|
+
|
|
126
|
+
const dockerfile = `FROM python:3.12-slim
|
|
127
|
+
|
|
128
|
+
WORKDIR /app
|
|
129
|
+
|
|
130
|
+
COPY requirements.txt .
|
|
131
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
132
|
+
|
|
133
|
+
COPY . .
|
|
134
|
+
|
|
135
|
+
EXPOSE 8000
|
|
136
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const dockerCompose = `version: '3.8'
|
|
140
|
+
services:
|
|
141
|
+
web:
|
|
142
|
+
build: .
|
|
143
|
+
ports:
|
|
144
|
+
- "8000:8000"
|
|
145
|
+
volumes:
|
|
146
|
+
- .:/app
|
|
147
|
+
${
|
|
148
|
+
database === 'postgresql'
|
|
149
|
+
? ` db:
|
|
150
|
+
image: postgres:15
|
|
151
|
+
environment:
|
|
152
|
+
POSTGRES_PASSWORD: password
|
|
153
|
+
POSTGRES_DB: ${projectName}
|
|
154
|
+
ports:
|
|
155
|
+
- "5432:5432"`
|
|
156
|
+
: ''
|
|
157
|
+
}
|
|
158
|
+
${
|
|
159
|
+
database === 'mysql'
|
|
160
|
+
? ` db:
|
|
161
|
+
image: mysql:8.0
|
|
162
|
+
environment:
|
|
163
|
+
MYSQL_ROOT_PASSWORD: password
|
|
164
|
+
MYSQL_DATABASE: ${projectName}
|
|
165
|
+
ports:
|
|
166
|
+
- "3306:3306"`
|
|
167
|
+
: ''
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
await utils.createFile(`${projectName}/Dockerfile`, dockerfile);
|
|
172
|
+
await utils.createFile(`${projectName}/docker-compose.yml`, dockerCompose);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
utils.success(`FastAPI project "${projectName}" created successfully!`);
|
|
176
|
+
utils.log(` cd ${projectName}`);
|
|
177
|
+
utils.log(` source venv/bin/activate`);
|
|
178
|
+
utils.log(` uvicorn app.main:app --reload`);
|
|
179
|
+
utils.log(` Docs: http://localhost:8000/docs`);
|
|
180
|
+
};
|