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 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
- stack.push(fn(components, projectName));
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
- stack[stack.length - 1] = !stack[stack.length - 1];
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.0",
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
- backend-fastify:
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
- db:
100
- condition: service_healthy
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('fastapi')) { %>
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('fastapi')) { %>
146
+ <% if (components.includes('fastify')) { %>
134
147
  depends_on:
135
- backend:
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
- backend-fastify:
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
- backend-fastify:
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('fastapi')) { %>
74
+ <% if (components.includes('fastify')) { %>
66
75
  depends_on:
67
- backend:
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
- backend-fastify:
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