create-projx 1.4.0 → 1.4.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 +42 -0
- package/dist/index.js +20 -3
- package/package.json +1 -1
- package/src/templates/docker-compose.dev.yml.ejs +28 -18
- package/src/templates/docker-compose.yml.ejs +18 -12
package/README.md
CHANGED
|
@@ -131,6 +131,8 @@ npx create-projx pin <patterns...>
|
|
|
131
131
|
npx create-projx unpin <patterns...>
|
|
132
132
|
npx create-projx pin --list
|
|
133
133
|
npx create-projx doctor [--fix]
|
|
134
|
+
npx create-projx gen entity <name>
|
|
135
|
+
npx create-projx sync [--url <url>]
|
|
134
136
|
|
|
135
137
|
--components <list> Comma-separated: fastapi,fastify,frontend,mobile,e2e,infra
|
|
136
138
|
--no-git Skip git init
|
|
@@ -174,6 +176,46 @@ npx create-projx doctor --fix # auto-fix what's possible
|
|
|
174
176
|
|
|
175
177
|
Checks: config validity, component markers, baseline ref, stale worktrees, skip pattern coverage.
|
|
176
178
|
|
|
179
|
+
### Generate Entities
|
|
180
|
+
|
|
181
|
+
Scaffold a new entity across all components in your project:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npx create-projx gen entity invoice # interactive
|
|
185
|
+
npx create-projx gen entity invoice --fields "name:string,amount:number" # non-interactive
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Generates based on what's in your `.projx`:
|
|
189
|
+
|
|
190
|
+
| Component | Generated |
|
|
191
|
+
| --------- | --------- |
|
|
192
|
+
| `fastapi` | `src/entities/<name>/_model.py` — auto-discovered by registry |
|
|
193
|
+
| `fastify` | `src/modules/<name>/schemas.ts` + `index.ts` + Prisma model + app.ts import |
|
|
194
|
+
| `frontend` | `src/types/<name>.ts` — TypeScript interface + Create/Update variants |
|
|
195
|
+
| `mobile` | `lib/entities/<name>/model.dart` — Dart class with fromJson/toJson/copyWith |
|
|
196
|
+
|
|
197
|
+
No migrations — run `alembic revision --autogenerate` or `npx prisma migrate dev` when ready.
|
|
198
|
+
|
|
199
|
+
### Sync Types
|
|
200
|
+
|
|
201
|
+
Regenerate all frontend/mobile types from a running backend:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx create-projx sync # auto-detects URL
|
|
205
|
+
npx create-projx sync --url http://localhost:8000/api/v1/_meta # explicit URL
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Fetches `/_meta` from your backend, generates typed interfaces for every entity. Run after any backend change — new field, renamed column, new entity.
|
|
209
|
+
|
|
210
|
+
The generic `api.ts` client accepts type parameters:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import type { Invoice } from '../types/invoice';
|
|
214
|
+
|
|
215
|
+
const { data } = await api.list<Invoice>('/invoices'); // data: Invoice[]
|
|
216
|
+
const item = await api.get<Invoice>('/invoices', id); // item: Invoice
|
|
217
|
+
```
|
|
218
|
+
|
|
177
219
|
## Rename Component Directories
|
|
178
220
|
|
|
179
221
|
Rename `fastapi/` to `backend/`? Just rename the folder — the `.projx-component` marker file moves with it. The `update` command auto-discovers where each component lives by scanning for these markers. No config changes needed.
|
package/dist/index.js
CHANGED
|
@@ -261,12 +261,29 @@ function render(template, vars) {
|
|
|
261
261
|
const ifMatch = line.match(/^<%\s*if\s*\((.+?)\)\s*\{?\s*%>$/);
|
|
262
262
|
if (ifMatch) {
|
|
263
263
|
const fn = new Function("components", "projectName", `return ${ifMatch[1]}`);
|
|
264
|
-
|
|
264
|
+
const result = fn(components, projectName);
|
|
265
|
+
stack.push({ active: result, matched: result });
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const elseIfMatch = line.match(/^<%\s*\}\s*else\s+if\s*\((.+?)\)\s*\{?\s*%>$/);
|
|
269
|
+
if (elseIfMatch) {
|
|
270
|
+
if (stack.length > 0) {
|
|
271
|
+
const top = stack[stack.length - 1];
|
|
272
|
+
if (top.matched) {
|
|
273
|
+
top.active = false;
|
|
274
|
+
} else {
|
|
275
|
+
const fn = new Function("components", "projectName", `return ${elseIfMatch[1]}`);
|
|
276
|
+
const result = fn(components, projectName);
|
|
277
|
+
top.active = result;
|
|
278
|
+
if (result) top.matched = true;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
265
281
|
continue;
|
|
266
282
|
}
|
|
267
283
|
if (/^<%\s*\}\s*else\s*\{?\s*%>$/.test(line)) {
|
|
268
284
|
if (stack.length > 0) {
|
|
269
|
-
|
|
285
|
+
const top = stack[stack.length - 1];
|
|
286
|
+
top.active = !top.matched;
|
|
270
287
|
}
|
|
271
288
|
continue;
|
|
272
289
|
}
|
|
@@ -274,7 +291,7 @@ function render(template, vars) {
|
|
|
274
291
|
stack.pop();
|
|
275
292
|
continue;
|
|
276
293
|
}
|
|
277
|
-
if (stack.length > 0 && stack.some((v) => !v)) continue;
|
|
294
|
+
if (stack.length > 0 && stack.some((v) => !v.active)) continue;
|
|
278
295
|
const replaced = line.replace(
|
|
279
296
|
/<%=\s*([\w.]+)\s*%>/g,
|
|
280
297
|
(_, expr) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-projx",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Scaffold production-grade fullstack projects in seconds. FastAPI, Fastify, React, Flutter, Terraform — with auth, database, CI/CD, E2E tests, and Docker. One command, ready to deploy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@ services:
|
|
|
24
24
|
- app-network
|
|
25
25
|
<% } %>
|
|
26
26
|
<% if (components.includes('fastapi')) { %>
|
|
27
|
-
migrate:
|
|
27
|
+
<%= paths.fastapi %>-migrate:
|
|
28
28
|
build: ./<%= paths.fastapi %>
|
|
29
29
|
command: ['uv', 'run', 'migrate.py']
|
|
30
30
|
environment:
|
|
@@ -39,8 +39,7 @@ services:
|
|
|
39
39
|
cpus: '0.5'
|
|
40
40
|
networks:
|
|
41
41
|
- app-network
|
|
42
|
-
|
|
43
|
-
backend:
|
|
42
|
+
<%= paths.fastapi %>:
|
|
44
43
|
build: ./<%= paths.fastapi %>
|
|
45
44
|
command:
|
|
46
45
|
[
|
|
@@ -61,7 +60,7 @@ services:
|
|
|
61
60
|
- ./<%= paths.fastapi %>/alembic.ini:/app/alembic.ini
|
|
62
61
|
- ./<%= paths.fastapi %>/migrate.py:/app/migrate.py
|
|
63
62
|
depends_on:
|
|
64
|
-
migrate:
|
|
63
|
+
<%= paths.fastapi %>-migrate:
|
|
65
64
|
condition: service_completed_successfully
|
|
66
65
|
restart: unless-stopped
|
|
67
66
|
healthcheck:
|
|
@@ -83,7 +82,22 @@ services:
|
|
|
83
82
|
- app-network
|
|
84
83
|
<% } %>
|
|
85
84
|
<% if (components.includes('fastify')) { %>
|
|
86
|
-
|
|
85
|
+
<%= paths.fastify %>-migrate:
|
|
86
|
+
build: ./<%= paths.fastify %>
|
|
87
|
+
command: ['pnpm', 'prisma', 'migrate', 'deploy']
|
|
88
|
+
environment:
|
|
89
|
+
- DATABASE_URL=postgresql://dev:dev@db:5432/app
|
|
90
|
+
depends_on:
|
|
91
|
+
db:
|
|
92
|
+
condition: service_healthy
|
|
93
|
+
deploy:
|
|
94
|
+
resources:
|
|
95
|
+
limits:
|
|
96
|
+
memory: 256M
|
|
97
|
+
cpus: '0.5'
|
|
98
|
+
networks:
|
|
99
|
+
- app-network
|
|
100
|
+
<%= paths.fastify %>:
|
|
87
101
|
build: ./<%= paths.fastify %>
|
|
88
102
|
command: ['pnpm', 'dev']
|
|
89
103
|
ports:
|
|
@@ -96,8 +110,8 @@ services:
|
|
|
96
110
|
volumes:
|
|
97
111
|
- ./<%= paths.fastify %>/src:/app/src
|
|
98
112
|
depends_on:
|
|
99
|
-
|
|
100
|
-
condition:
|
|
113
|
+
<%= paths.fastify %>-migrate:
|
|
114
|
+
condition: service_completed_successfully
|
|
101
115
|
restart: unless-stopped
|
|
102
116
|
healthcheck:
|
|
103
117
|
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3000/api/health']
|
|
@@ -121,23 +135,21 @@ services:
|
|
|
121
135
|
ports:
|
|
122
136
|
- '5173:5173'
|
|
123
137
|
environment:
|
|
124
|
-
<% if (components.includes('
|
|
125
|
-
- VITE_API_URL=http://localhost:7860
|
|
126
|
-
<% } %>
|
|
127
|
-
<% if (components.includes('fastify') && !components.includes('fastapi')) { %>
|
|
138
|
+
<% if (components.includes('fastify')) { %>
|
|
128
139
|
- VITE_API_URL=http://localhost:3000
|
|
140
|
+
<% } else if (components.includes('fastapi')) { %>
|
|
141
|
+
- VITE_API_URL=http://localhost:7860
|
|
129
142
|
<% } %>
|
|
130
143
|
volumes:
|
|
131
144
|
- ./<%= paths.frontend %>:/app
|
|
132
145
|
- frontend_node_modules:/app/node_modules
|
|
133
|
-
<% if (components.includes('
|
|
146
|
+
<% if (components.includes('fastify')) { %>
|
|
134
147
|
depends_on:
|
|
135
|
-
|
|
148
|
+
<%= paths.fastify %>:
|
|
136
149
|
condition: service_healthy
|
|
137
|
-
<% } %>
|
|
138
|
-
<% if (components.includes('fastify') && !components.includes('fastapi')) { %>
|
|
150
|
+
<% } else if (components.includes('fastapi')) { %>
|
|
139
151
|
depends_on:
|
|
140
|
-
|
|
152
|
+
<%= paths.fastapi %>:
|
|
141
153
|
condition: service_healthy
|
|
142
154
|
<% } %>
|
|
143
155
|
healthcheck:
|
|
@@ -154,7 +166,6 @@ services:
|
|
|
154
166
|
networks:
|
|
155
167
|
- app-network
|
|
156
168
|
<% } %>
|
|
157
|
-
|
|
158
169
|
volumes:
|
|
159
170
|
<% if (components.includes('fastapi') || components.includes('fastify')) { %>
|
|
160
171
|
pgdata:
|
|
@@ -162,7 +173,6 @@ volumes:
|
|
|
162
173
|
<% if (components.includes('frontend')) { %>
|
|
163
174
|
frontend_node_modules:
|
|
164
175
|
<% } %>
|
|
165
|
-
|
|
166
176
|
networks:
|
|
167
177
|
app-network:
|
|
168
178
|
driver: bridge
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
services:
|
|
2
2
|
<% if (components.includes('fastapi')) { %>
|
|
3
|
-
migrate:
|
|
3
|
+
<%= paths.fastapi %>-migrate:
|
|
4
4
|
build: ./<%= paths.fastapi %>
|
|
5
5
|
command: ["uv", "run", "migrate.py"]
|
|
6
6
|
env_file:
|
|
7
7
|
- ./<%= paths.fastapi %>/.env
|
|
8
8
|
networks:
|
|
9
9
|
- app-network
|
|
10
|
-
|
|
11
|
-
backend:
|
|
10
|
+
<%= paths.fastapi %>:
|
|
12
11
|
build: ./<%= paths.fastapi %>
|
|
13
12
|
expose:
|
|
14
13
|
- "7860"
|
|
@@ -16,7 +15,7 @@ services:
|
|
|
16
15
|
- ./<%= paths.fastapi %>/.env
|
|
17
16
|
restart: unless-stopped
|
|
18
17
|
depends_on:
|
|
19
|
-
migrate:
|
|
18
|
+
<%= paths.fastapi %>-migrate:
|
|
20
19
|
condition: service_completed_successfully
|
|
21
20
|
healthcheck:
|
|
22
21
|
test:
|
|
@@ -34,13 +33,23 @@ services:
|
|
|
34
33
|
- app-network
|
|
35
34
|
<% } %>
|
|
36
35
|
<% if (components.includes('fastify')) { %>
|
|
37
|
-
|
|
36
|
+
<%= paths.fastify %>-migrate:
|
|
37
|
+
build: ./<%= paths.fastify %>
|
|
38
|
+
command: ["pnpm", "prisma", "migrate", "deploy"]
|
|
39
|
+
env_file:
|
|
40
|
+
- ./<%= paths.fastify %>/.env
|
|
41
|
+
networks:
|
|
42
|
+
- app-network
|
|
43
|
+
<%= paths.fastify %>:
|
|
38
44
|
build: ./<%= paths.fastify %>
|
|
39
45
|
expose:
|
|
40
46
|
- "3000"
|
|
41
47
|
env_file:
|
|
42
48
|
- ./<%= paths.fastify %>/.env
|
|
43
49
|
restart: unless-stopped
|
|
50
|
+
depends_on:
|
|
51
|
+
<%= paths.fastify %>-migrate:
|
|
52
|
+
condition: service_completed_successfully
|
|
44
53
|
healthcheck:
|
|
45
54
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"]
|
|
46
55
|
interval: 30s
|
|
@@ -62,14 +71,13 @@ services:
|
|
|
62
71
|
volumes:
|
|
63
72
|
- letsencrypt:/etc/letsencrypt
|
|
64
73
|
- certbot-www:/var/www/certbot
|
|
65
|
-
<% if (components.includes('
|
|
74
|
+
<% if (components.includes('fastify')) { %>
|
|
66
75
|
depends_on:
|
|
67
|
-
|
|
76
|
+
<%= paths.fastify %>:
|
|
68
77
|
condition: service_healthy
|
|
69
|
-
<% } %>
|
|
70
|
-
<% if (components.includes('fastify') && !components.includes('fastapi')) { %>
|
|
78
|
+
<% } else if (components.includes('fastapi')) { %>
|
|
71
79
|
depends_on:
|
|
72
|
-
|
|
80
|
+
<%= paths.fastapi %>:
|
|
73
81
|
condition: service_healthy
|
|
74
82
|
<% } %>
|
|
75
83
|
restart: unless-stopped
|
|
@@ -81,7 +89,6 @@ services:
|
|
|
81
89
|
start_period: 10s
|
|
82
90
|
networks:
|
|
83
91
|
- app-network
|
|
84
|
-
|
|
85
92
|
certbot:
|
|
86
93
|
image: certbot/certbot:latest
|
|
87
94
|
volumes:
|
|
@@ -102,7 +109,6 @@ volumes:
|
|
|
102
109
|
letsencrypt:
|
|
103
110
|
certbot-www:
|
|
104
111
|
<% } %>
|
|
105
|
-
|
|
106
112
|
networks:
|
|
107
113
|
app-network:
|
|
108
114
|
driver: bridge
|