create-astra 0.1.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/README.md +120 -0
- package/dist/generator.js +144 -0
- package/dist/index.js +13 -0
- package/dist/plugins/base/index.js +79 -0
- package/dist/plugins/ci/index.js +67 -0
- package/dist/plugins/docker/index.js +69 -0
- package/dist/plugins/prisma/index.js +71 -0
- package/dist/plugins/types.js +2 -0
- package/dist/prompts.js +105 -0
- package/dist/utils/env.js +18 -0
- package/dist/utils/files.js +17 -0
- package/dist/utils/packages.js +11 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# create-astra
|
|
2
|
+
|
|
3
|
+
A CLI tool that scaffolds production-ready backend projects with everything wired up automatically. No more manually editing config files, no more copy-pasting boilerplate.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx create-astra
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The problem
|
|
12
|
+
|
|
13
|
+
Every time you start a backend project you repeat the same steps. Install dependencies, write a tsconfig, connect Prisma to a database adapter, write a docker-compose, set up a CI pipeline. It works but it's tedious and easy to get wrong.
|
|
14
|
+
|
|
15
|
+
create-astra does all of that in one command.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What it sets up
|
|
20
|
+
|
|
21
|
+
Depending on your selections, create-astra generates a fully wired project with:
|
|
22
|
+
|
|
23
|
+
- Express app with a working entry point
|
|
24
|
+
- TypeScript configured correctly for Node.js
|
|
25
|
+
- Prisma with schema, config, adapter, and generated client
|
|
26
|
+
- Docker Compose with a postgres service wired to your env vars
|
|
27
|
+
- GitHub Actions CI pipeline that matches your stack
|
|
28
|
+
- `.env` and `.env.example` with all required variables
|
|
29
|
+
- `.gitignore` with sensible defaults
|
|
30
|
+
- `package.json` with all scripts ready to use
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx create-astra
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Follow the interactive prompts:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
What is your project name? my-app
|
|
44
|
+
Select a language: Node.js
|
|
45
|
+
Select a framework: Express (TypeScript)
|
|
46
|
+
Select extra features: Prisma, Docker, CI/CD
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd my-app
|
|
53
|
+
npm run dev
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Generated scripts
|
|
59
|
+
|
|
60
|
+
| Script | What it does |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `npm run dev` | starts the dev server |
|
|
63
|
+
| `npm run build` | compiles TypeScript |
|
|
64
|
+
| `npm run start` | runs the compiled app |
|
|
65
|
+
| `npm run db:generate` | generates the Prisma client |
|
|
66
|
+
| `npm run db:migrate` | runs database migrations |
|
|
67
|
+
| `npm run db:studio` | opens Prisma Studio |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## After setup
|
|
72
|
+
|
|
73
|
+
If you selected Prisma, you need to:
|
|
74
|
+
|
|
75
|
+
1. Set up a postgres database
|
|
76
|
+
2. Update `DATABASE_URL` in your `.env` with real credentials
|
|
77
|
+
3. Run `npm run db:migrate` to create your tables
|
|
78
|
+
|
|
79
|
+
The Prisma client is already generated during setup so TypeScript types resolve immediately.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Supported stacks
|
|
84
|
+
|
|
85
|
+
| Language | Framework | Status |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Node.js | Express (JavaScript) | available |
|
|
88
|
+
| Node.js | Express (TypeScript) | available |
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Want to add support for a new language or framework? See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Supported plugins
|
|
96
|
+
|
|
97
|
+
| Plugin | What it adds |
|
|
98
|
+
|---|---|
|
|
99
|
+
| Prisma | schema, config, adapter, client, db scripts |
|
|
100
|
+
| Docker | Dockerfile, docker-compose, .dockerignore |
|
|
101
|
+
| CI/CD | GitHub Actions pipeline |
|
|
102
|
+
|
|
103
|
+
Want to add a new plugin? See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Local development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
git clone https://github.com/your-username/create-astra
|
|
111
|
+
cd create-astra
|
|
112
|
+
npm install
|
|
113
|
+
npm run dev
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.generateProject = generateProject;
|
|
40
|
+
const clack = __importStar(require("@clack/prompts"));
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
const execa_1 = require("execa");
|
|
43
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
44
|
+
const base_1 = require("./plugins/base");
|
|
45
|
+
const prisma_1 = require("./plugins/prisma");
|
|
46
|
+
const docker_1 = require("./plugins/docker");
|
|
47
|
+
const ci_1 = require("./plugins/ci");
|
|
48
|
+
const files_1 = require("./utils/files");
|
|
49
|
+
const packages_1 = require("./utils/packages");
|
|
50
|
+
const env_1 = require("./utils/env");
|
|
51
|
+
async function generateProject(options) {
|
|
52
|
+
if (options.language === 'node') {
|
|
53
|
+
await generateNodeProject(options);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
clack.outro('This language is coming soon. Want to contribute? github.com/you/create-astra');
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function generateNodeProject(options) {
|
|
61
|
+
const { projectName, extras } = options;
|
|
62
|
+
const projectPath = path_1.default.join(process.cwd(), projectName);
|
|
63
|
+
const spinner = clack.spinner();
|
|
64
|
+
const plugins = [
|
|
65
|
+
...(options.language === 'node' ? [(0, base_1.createBasePlugin)(options)] : []),
|
|
66
|
+
...(extras.includes('prisma') ? [(0, prisma_1.createPrismaPlugin)(options)] : []),
|
|
67
|
+
...(extras.includes('docker') ? [(0, docker_1.createDockerPlugin)(options)] : []),
|
|
68
|
+
...(extras.includes('ci') ? [(0, ci_1.createCiPlugin)(options)] : []),
|
|
69
|
+
];
|
|
70
|
+
const dependencies = plugins.flatMap(p => p.dependencies);
|
|
71
|
+
const devDependencies = plugins.flatMap(p => p.devDependencies);
|
|
72
|
+
const envVars = plugins.reduce((acc, p) => ({ ...acc, ...p.envVars }), {});
|
|
73
|
+
const packageScripts = plugins.reduce((acc, p) => ({ ...acc, ...p.packageScripts }), {});
|
|
74
|
+
const files = plugins.flatMap(p => p.files);
|
|
75
|
+
spinner.start('Initialising project...');
|
|
76
|
+
try {
|
|
77
|
+
await (0, files_1.createFolder)(projectPath);
|
|
78
|
+
await (0, execa_1.execa)('npm', ['init', '-y'], { cwd: projectPath });
|
|
79
|
+
spinner.stop('Project initialised!');
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
spinner.stop('Failed.');
|
|
83
|
+
clack.log.error('Could not initialise project.');
|
|
84
|
+
console.error(err);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
spinner.start('Creating files...');
|
|
88
|
+
try {
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
await (0, files_1.createFile)(file.path, file.content);
|
|
91
|
+
}
|
|
92
|
+
await writeGitignore(projectPath);
|
|
93
|
+
spinner.stop('Files created!');
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
spinner.stop('Failed.');
|
|
97
|
+
clack.log.error('Could not create files.');
|
|
98
|
+
console.error(err);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
spinner.start('Writing environment variables...');
|
|
102
|
+
try {
|
|
103
|
+
await (0, env_1.writeEnvFile)(projectPath, envVars);
|
|
104
|
+
spinner.stop('Environment variables written!');
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
spinner.stop('Failed.');
|
|
108
|
+
clack.log.error('Could not write env files.');
|
|
109
|
+
console.error(err);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
spinner.start('Installing dependencies...');
|
|
113
|
+
try {
|
|
114
|
+
await (0, packages_1.installPackages)(dependencies, projectPath);
|
|
115
|
+
await (0, packages_1.installPackages)(devDependencies, projectPath, true);
|
|
116
|
+
spinner.stop('Dependencies installed!');
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
spinner.stop('Failed.');
|
|
120
|
+
clack.log.error('Could not install dependencies.');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
spinner.start('Configuring package.json...');
|
|
124
|
+
try {
|
|
125
|
+
await patchPackageJson(projectPath, packageScripts);
|
|
126
|
+
spinner.stop('package.json configured!');
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
spinner.stop('Failed.');
|
|
130
|
+
clack.log.error('Could not configure package.json.');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
clack.outro(`Your project is ready! cd ${projectName} and npm run dev to start.`);
|
|
134
|
+
}
|
|
135
|
+
async function writeGitignore(projectPath) {
|
|
136
|
+
await (0, files_1.createFile)(path_1.default.join(projectPath, '.gitignore'), `node_modules\ndist\n.env\ngenerated\n`);
|
|
137
|
+
}
|
|
138
|
+
async function patchPackageJson(projectPath, scripts) {
|
|
139
|
+
const packageJsonPath = path_1.default.join(projectPath, 'package.json');
|
|
140
|
+
const raw = await promises_1.default.readFile(packageJsonPath, 'utf-8');
|
|
141
|
+
const packageJson = JSON.parse(raw);
|
|
142
|
+
packageJson.scripts = { ...packageJson.scripts, ...scripts };
|
|
143
|
+
await promises_1.default.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
144
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const prompts_1 = require("./prompts");
|
|
5
|
+
const generator_1 = require("./generator");
|
|
6
|
+
async function main() {
|
|
7
|
+
const options = await (0, prompts_1.getProjectOptions)();
|
|
8
|
+
await (0, generator_1.generateProject)(options);
|
|
9
|
+
}
|
|
10
|
+
main().catch(err => {
|
|
11
|
+
console.error(err);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBasePlugin = void 0;
|
|
4
|
+
const createBasePlugin = (options) => {
|
|
5
|
+
const { projectName, useTypescript } = options;
|
|
6
|
+
const ext = useTypescript ? 'ts' : 'js';
|
|
7
|
+
const srcEntry = useTypescript ?
|
|
8
|
+
`import express from 'express'
|
|
9
|
+
|
|
10
|
+
const app = express()
|
|
11
|
+
const PORT = process.env.PORT || 3000
|
|
12
|
+
|
|
13
|
+
app.use(express.json())
|
|
14
|
+
|
|
15
|
+
app.get('/', (req,res) => {
|
|
16
|
+
res.json({message: 'Hello from ${projectName}'})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
app.listen(PORT, () => {
|
|
20
|
+
console.log(\`Server running on port \${PORT}\`)
|
|
21
|
+
})
|
|
22
|
+
`
|
|
23
|
+
:
|
|
24
|
+
`const express = require('express')
|
|
25
|
+
const app = express()
|
|
26
|
+
const PORT = process.env.PORT || 3000
|
|
27
|
+
|
|
28
|
+
app.use(express.json())
|
|
29
|
+
|
|
30
|
+
app.get('/', (req, res) => {
|
|
31
|
+
res.json({ message: 'Hello from ${projectName}!' })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
app.listen(PORT, () => {
|
|
35
|
+
console.log(\`Server running on port \${PORT}\`)
|
|
36
|
+
})
|
|
37
|
+
`;
|
|
38
|
+
return {
|
|
39
|
+
name: 'base',
|
|
40
|
+
dependencies: ['express'],
|
|
41
|
+
devDependencies: useTypescript
|
|
42
|
+
? ['typescript', 'ts-node', '@types/node', '@types/express']
|
|
43
|
+
: [],
|
|
44
|
+
files: [
|
|
45
|
+
{
|
|
46
|
+
path: `${projectName}/src/index.${ext}`,
|
|
47
|
+
content: srcEntry
|
|
48
|
+
},
|
|
49
|
+
...(useTypescript ? [{
|
|
50
|
+
path: `${projectName}/tsconfig.json`,
|
|
51
|
+
content: JSON.stringify({
|
|
52
|
+
compilerOptions: {
|
|
53
|
+
target: 'es2020',
|
|
54
|
+
module: 'commonjs',
|
|
55
|
+
rootDir: 'src',
|
|
56
|
+
outDir: 'dist',
|
|
57
|
+
strict: true,
|
|
58
|
+
esModuleInterop: true,
|
|
59
|
+
types: ['node']
|
|
60
|
+
},
|
|
61
|
+
include: ['src/**/*']
|
|
62
|
+
}, null, 2)
|
|
63
|
+
}] : []),
|
|
64
|
+
],
|
|
65
|
+
envVars: {
|
|
66
|
+
PORT: '3000',
|
|
67
|
+
NODE_ENV: 'development',
|
|
68
|
+
},
|
|
69
|
+
packageScripts: useTypescript ? {
|
|
70
|
+
dev: 'ts-node src/index.ts',
|
|
71
|
+
build: 'tsc',
|
|
72
|
+
start: 'node dist/index.js',
|
|
73
|
+
} : {
|
|
74
|
+
dev: 'node src/index.js',
|
|
75
|
+
start: 'node src/index.js',
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
exports.createBasePlugin = createBasePlugin;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCiPlugin = createCiPlugin;
|
|
4
|
+
function createCiPlugin(options) {
|
|
5
|
+
const { projectName, useTypescript, extras } = options;
|
|
6
|
+
const hasPrisma = extras.includes('prisma');
|
|
7
|
+
const ciContent = `name: CI
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
pull_request:
|
|
13
|
+
branches: [main]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
${hasPrisma ? `services:
|
|
20
|
+
postgres:
|
|
21
|
+
image: postgres:15
|
|
22
|
+
env:
|
|
23
|
+
POSTGRES_USER: user
|
|
24
|
+
POSTGRES_PASSWORD: password
|
|
25
|
+
POSTGRES_DB: mydb
|
|
26
|
+
ports:
|
|
27
|
+
- 5432:5432
|
|
28
|
+
options: >-
|
|
29
|
+
--health-cmd pg_isready
|
|
30
|
+
--health-interval 10s
|
|
31
|
+
--health-timeout 5s
|
|
32
|
+
--health-retries 5` : ''}
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- name: Setup Node.js
|
|
38
|
+
uses: actions/setup-node@v4
|
|
39
|
+
with:
|
|
40
|
+
node-version: 20
|
|
41
|
+
cache: 'npm'
|
|
42
|
+
|
|
43
|
+
- name: Install dependencies
|
|
44
|
+
run: npm install
|
|
45
|
+
|
|
46
|
+
${hasPrisma ? `- name: Generate Prisma client
|
|
47
|
+
run: npm run db:generate
|
|
48
|
+
env:
|
|
49
|
+
DATABASE_URL: postgresql://user:password@localhost:5432/mydb` : ''}
|
|
50
|
+
|
|
51
|
+
${useTypescript ? `- name: Build
|
|
52
|
+
run: npm run build` : ''}
|
|
53
|
+
`;
|
|
54
|
+
return {
|
|
55
|
+
name: 'ci',
|
|
56
|
+
dependencies: [],
|
|
57
|
+
devDependencies: [],
|
|
58
|
+
files: [
|
|
59
|
+
{
|
|
60
|
+
path: `${projectName}/.github/workflows/ci.yml`,
|
|
61
|
+
content: ciContent,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
envVars: {},
|
|
65
|
+
packageScripts: {},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDockerPlugin = createDockerPlugin;
|
|
4
|
+
function createDockerPlugin(options) {
|
|
5
|
+
const { projectName, extras } = options;
|
|
6
|
+
const hasPrisma = extras.includes('prisma');
|
|
7
|
+
const dockerComposeContent = `services:
|
|
8
|
+
app:
|
|
9
|
+
build: .
|
|
10
|
+
ports:
|
|
11
|
+
- "\${PORT}:3000"
|
|
12
|
+
env_file:
|
|
13
|
+
- .env
|
|
14
|
+
depends_on:
|
|
15
|
+
${hasPrisma ? '- postgres' : ''}
|
|
16
|
+
|
|
17
|
+
${hasPrisma ? ` postgres:
|
|
18
|
+
image: postgres:15
|
|
19
|
+
restart: always
|
|
20
|
+
environment:
|
|
21
|
+
POSTGRES_USER: user
|
|
22
|
+
POSTGRES_PASSWORD: password
|
|
23
|
+
POSTGRES_DB: mydb
|
|
24
|
+
ports:
|
|
25
|
+
- "5432:5432"
|
|
26
|
+
volumes:
|
|
27
|
+
- postgres_data:/var/lib/postgresql/data` : ''}
|
|
28
|
+
|
|
29
|
+
${hasPrisma ? `volumes:
|
|
30
|
+
postgres_data:` : ''}
|
|
31
|
+
`;
|
|
32
|
+
const dockerfileContent = `FROM node:20-alpine
|
|
33
|
+
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
|
|
36
|
+
COPY package*.json ./
|
|
37
|
+
RUN npm install
|
|
38
|
+
|
|
39
|
+
COPY . .
|
|
40
|
+
|
|
41
|
+
${options.useTypescript ? 'RUN npm run build\nCMD ["node", "dist/index.js"]' : 'CMD ["node", "src/index.js"]'}
|
|
42
|
+
`;
|
|
43
|
+
const dockerignoreContent = `node_modules
|
|
44
|
+
dist
|
|
45
|
+
.env
|
|
46
|
+
generated
|
|
47
|
+
`;
|
|
48
|
+
return {
|
|
49
|
+
name: 'docker',
|
|
50
|
+
dependencies: [],
|
|
51
|
+
devDependencies: [],
|
|
52
|
+
files: [
|
|
53
|
+
{
|
|
54
|
+
path: `${projectName}/docker-compose.yml`,
|
|
55
|
+
content: dockerComposeContent,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
path: `${projectName}/Dockerfile`,
|
|
59
|
+
content: dockerfileContent,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
path: `${projectName}/.dockerignore`,
|
|
63
|
+
content: dockerignoreContent,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
envVars: {},
|
|
67
|
+
packageScripts: {},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPrismaPlugin = void 0;
|
|
4
|
+
const createPrismaPlugin = (options) => {
|
|
5
|
+
const { projectName, useTypescript } = options;
|
|
6
|
+
const prismaConfig = `prisma.config.ts`;
|
|
7
|
+
const schemaContent = `generator client {
|
|
8
|
+
provider = "prisma-client"
|
|
9
|
+
output = "../generated/prisma"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
datasource db {
|
|
13
|
+
provider = "postgresql"
|
|
14
|
+
}`;
|
|
15
|
+
const prismaConfigContent = `import path from 'path'
|
|
16
|
+
import { defineConfig } from 'prisma/config'
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
earlyAccess: true,
|
|
20
|
+
schema: path.join('prisma', 'schema.prisma'),
|
|
21
|
+
migrate: {
|
|
22
|
+
async adapter() {
|
|
23
|
+
const { PrismaPg } = await import('@prisma/adapter-pg')
|
|
24
|
+
return new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
})`;
|
|
28
|
+
const prismaClientContent = useTypescript
|
|
29
|
+
? `import { PrismaClient } from '../../generated/prisma'
|
|
30
|
+
import { PrismaPg } from '@prisma/adapter-pg'
|
|
31
|
+
|
|
32
|
+
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! })
|
|
33
|
+
const prisma = new PrismaClient({ adapter })
|
|
34
|
+
|
|
35
|
+
export default prisma`
|
|
36
|
+
: `const { PrismaClient } = require('../../generated/prisma')
|
|
37
|
+
const { PrismaPg } = require('@prisma/adapter-pg')
|
|
38
|
+
|
|
39
|
+
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
40
|
+
const prisma = new PrismaClient({ adapter })
|
|
41
|
+
|
|
42
|
+
module.exports = prisma`;
|
|
43
|
+
return {
|
|
44
|
+
name: 'prisma',
|
|
45
|
+
dependencies: ['@prisma/client', '@prisma/adapter-pg', 'pg'],
|
|
46
|
+
devDependencies: ['prisma'],
|
|
47
|
+
files: [
|
|
48
|
+
{
|
|
49
|
+
path: `${projectName}/prisma/schema.prisma`,
|
|
50
|
+
content: schemaContent
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
path: `${projectName}/${prismaConfig}`,
|
|
54
|
+
content: prismaConfigContent
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
path: `${projectName}/src/lib/prisma.${useTypescript ? 'ts' : 'js'}`,
|
|
58
|
+
content: prismaClientContent
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
envVars: {
|
|
62
|
+
DATABASE_URL: 'postgresql://user:password@localhost:5432/mydb'
|
|
63
|
+
},
|
|
64
|
+
packageScripts: {
|
|
65
|
+
'db:generate': 'prisma generate',
|
|
66
|
+
'db:migrate': 'prisma migrate dev',
|
|
67
|
+
'db:studio': 'prisma studio'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.createPrismaPlugin = createPrismaPlugin;
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getProjectOptions = void 0;
|
|
37
|
+
const clack = __importStar(require("@clack/prompts"));
|
|
38
|
+
const getProjectOptions = async () => {
|
|
39
|
+
clack.intro('Welcome to create-astra!');
|
|
40
|
+
const projectName = await clack.text({
|
|
41
|
+
message: 'What is your project name?',
|
|
42
|
+
placeholder: 'my-app',
|
|
43
|
+
validate(value) {
|
|
44
|
+
if (!value || value.trim() === '') {
|
|
45
|
+
return 'Project name cannot be empty.';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
if (clack.isCancel(projectName)) {
|
|
50
|
+
clack.cancel('Operation cancelled.');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
const language = await clack.select({
|
|
54
|
+
message: 'Select a language:',
|
|
55
|
+
options: [
|
|
56
|
+
{ value: 'node', label: 'Js(node)' }
|
|
57
|
+
]
|
|
58
|
+
});
|
|
59
|
+
if (clack.isCancel(language)) {
|
|
60
|
+
clack.cancel('Operation cancelled.');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
const frameworkOptions = {
|
|
64
|
+
node: [
|
|
65
|
+
{ value: 'express', label: 'Express (JavaScript)' },
|
|
66
|
+
{ value: 'express-ts', label: 'Express (TypeScript)' },
|
|
67
|
+
],
|
|
68
|
+
python: [
|
|
69
|
+
{ value: 'fastapi', label: 'FastAPI' },
|
|
70
|
+
{ value: 'flask', label: 'Flask' },
|
|
71
|
+
],
|
|
72
|
+
go: [
|
|
73
|
+
{ value: 'gin', label: 'Gin' },
|
|
74
|
+
{ value: 'fiber', label: 'Fiber' },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
const framework = await clack.select({
|
|
78
|
+
message: 'Select a framework:',
|
|
79
|
+
options: frameworkOptions[language]
|
|
80
|
+
});
|
|
81
|
+
if (clack.isCancel(framework)) {
|
|
82
|
+
clack.cancel('Operation cancelled.');
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
const extras = await clack.multiselect({
|
|
86
|
+
message: 'Select extra features:',
|
|
87
|
+
options: [
|
|
88
|
+
{ value: 'prisma', label: 'Prisma' },
|
|
89
|
+
{ value: 'docker', label: 'Docker' },
|
|
90
|
+
{ value: 'ci', label: 'CI/CD' },
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
if (clack.isCancel(extras)) {
|
|
94
|
+
clack.cancel('Operation cancelled.');
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
projectName: projectName,
|
|
99
|
+
language: language,
|
|
100
|
+
framework: framework,
|
|
101
|
+
extras: extras,
|
|
102
|
+
useTypescript: framework === 'express-ts'
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
exports.getProjectOptions = getProjectOptions;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeEnvFile = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const files_1 = require("./files");
|
|
9
|
+
const writeEnvFile = async (projectPath, vars) => {
|
|
10
|
+
const lines = Object.entries(vars)
|
|
11
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
12
|
+
.join('\n');
|
|
13
|
+
await (0, files_1.createFile)(path_1.default.join(projectPath, '.env'), lines);
|
|
14
|
+
await (0, files_1.createFile)(path_1.default.join(projectPath, '.env.example'), Object.entries(vars)
|
|
15
|
+
.map(([key]) => `${key}=`)
|
|
16
|
+
.join('\n'));
|
|
17
|
+
};
|
|
18
|
+
exports.writeEnvFile = writeEnvFile;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createFolder = createFolder;
|
|
7
|
+
exports.createFile = createFile;
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
async function createFolder(folderPath) {
|
|
11
|
+
await promises_1.default.mkdir(folderPath, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
async function createFile(filePath, content) {
|
|
14
|
+
const dir = path_1.default.dirname(filePath);
|
|
15
|
+
await promises_1.default.mkdir(dir, { recursive: true });
|
|
16
|
+
await promises_1.default.writeFile(filePath, content, 'utf-8');
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installPackages = void 0;
|
|
4
|
+
const execa_1 = require("execa");
|
|
5
|
+
const installPackages = async (packages, projectPath, dev = false) => {
|
|
6
|
+
if (packages.length === 0)
|
|
7
|
+
return;
|
|
8
|
+
const flag = dev ? ['--save-dev'] : [];
|
|
9
|
+
await (0, execa_1.execa)('npm', ['install', ...flag, ...packages], { cwd: projectPath });
|
|
10
|
+
};
|
|
11
|
+
exports.installPackages = installPackages;
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-astra",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A CLI that scaffolds production-ready backend projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-astra": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "ts-node src/index.ts",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepare": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"scaffold",
|
|
17
|
+
"express",
|
|
18
|
+
"prisma",
|
|
19
|
+
"typescript",
|
|
20
|
+
"backend"
|
|
21
|
+
],
|
|
22
|
+
"author": "Njong Remy",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"type": "commonjs",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@clack/prompts": "^1.6.0",
|
|
30
|
+
"execa": "^9.6.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^26.0.1",
|
|
34
|
+
"ts-node": "^10.9.2",
|
|
35
|
+
"typescript": "^6.0.3"
|
|
36
|
+
}
|
|
37
|
+
}
|