create-projx 1.0.0 → 1.0.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 ADDED
@@ -0,0 +1,144 @@
1
+ # Projx
2
+
3
+ Production-grade project scaffolder. Pick your stack, get a fully wired project with auth, database, CI/CD, and E2E tests — ready to deploy.
4
+
5
+ ## The Problem
6
+
7
+ Starting a new project means days of boilerplate: setting up auth, database migrations, CI/CD pipelines, Docker configs, linting, pre-commit hooks, test infrastructure. Every team does this from scratch, every time.
8
+
9
+ ## The Solution
10
+
11
+ ```bash
12
+ npx create-projx my-app
13
+ ```
14
+
15
+ Pick the components you need. Get a production-ready project in seconds.
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # Interactive — pick your stack
21
+ npx create-projx my-app
22
+
23
+ # Non-interactive — specify components
24
+ npx create-projx my-app --components fastify,frontend,e2e
25
+
26
+ # Accept defaults (Fastify + Frontend + E2E)
27
+ npx create-projx my-app -y
28
+ ```
29
+
30
+ ## Components
31
+
32
+ | Component | Stack | What You Get |
33
+ | --------- | ----- | ------------ |
34
+ | `fastapi` | Python, SQLAlchemy, Alembic | Auto-entity CRUD, JWT auth, migrations, OpenAPI docs |
35
+ | `fastify` | Node.js, Prisma, TypeBox | Auto-entity CRUD, JWT auth, typed schemas, OpenAPI docs |
36
+ | `frontend` | React 19, TypeScript, Vite | Auto-entity UI from metadata, design tokens, light/dark mode |
37
+ | `mobile` | Flutter, Riverpod, GoRouter | Auto-entity screens, offline-first with Isar, biometric auth |
38
+ | `e2e` | Playwright | Page object model, auth fixtures, accessibility scans |
39
+ | `infra` | Terraform, AWS | EKS, RDS, VPC, ALB, CodePipeline, multi-environment |
40
+
41
+ All optional. Pick any combination.
42
+
43
+ ## Commands
44
+
45
+ ### Create a Project
46
+
47
+ ```bash
48
+ npx create-projx my-app
49
+ ```
50
+
51
+ Interactive prompt lets you pick components. Or specify them directly:
52
+
53
+ ```bash
54
+ npx create-projx my-app --components fastapi,fastify,frontend,mobile,e2e,infra
55
+ ```
56
+
57
+ ### Add Components Later
58
+
59
+ Already have a project? Add more components anytime:
60
+
61
+ ```bash
62
+ cd my-app
63
+ npx create-projx add frontend mobile
64
+ ```
65
+
66
+ This copies the new component directories, regenerates shared files (docker-compose, CI, pre-commit hooks) to include them, and installs dependencies.
67
+
68
+ ### Update Scaffolding
69
+
70
+ When we improve templates, update your project's scaffolding without touching your code:
71
+
72
+ ```bash
73
+ cd my-app
74
+ npx create-projx@latest update
75
+ ```
76
+
77
+ This updates template files (base models, middleware, configs, Dockerfiles, CI) tracked in `.projx` manifest. Files you created (new entities, pages, features) are never touched.
78
+
79
+ ## Options
80
+
81
+ ```
82
+ npx create-projx <name> [options]
83
+ npx create-projx add <components...>
84
+ npx create-projx update
85
+
86
+ --components <list> Comma-separated: fastapi,fastify,frontend,mobile,e2e,infra
87
+ --no-git Skip git init
88
+ --no-install Skip dependency installation
89
+ -y, --yes Accept defaults (fastify + frontend + e2e)
90
+ -h, --help Show help
91
+ ```
92
+
93
+ ## What a Scaffolded Project Looks Like
94
+
95
+ ```
96
+ my-app/
97
+ ├── fastapi/ # Auto-entity CRUD backend
98
+ ├── frontend/ # Auto-entity UI from /_meta
99
+ ├── e2e/ # Playwright E2E tests
100
+ ├── docker-compose.yml # Production (backend + frontend + SSL)
101
+ ├── docker-compose.dev.yml # Development (PostgreSQL + hot reload)
102
+ ├── .github/workflows/ # CI per component
103
+ ├── .githooks/pre-commit # Format + lint on commit
104
+ ├── setup.sh # Install all deps
105
+ └── .projx # Manifest (tracks template files for updates)
106
+ ```
107
+
108
+ Only the components you selected appear. Shared files (docker-compose, CI, hooks) are generated to match your selection.
109
+
110
+ ## Auto-Entity Pattern
111
+
112
+ The core idea: define a data model, get everything else for free.
113
+
114
+ **Backend** — Drop a model file. The registry auto-discovers it and generates CRUD routes, schemas, pagination, filtering, sorting, search, FK expansion, and OpenAPI docs.
115
+
116
+ **Frontend** — Fetches metadata from `GET /api/v1/_meta`, renders table + form UI automatically. Customize with overrides.
117
+
118
+ **Mobile** — Same metadata endpoint, generates list/detail/form screens. Offline-first with local DB and sync queue.
119
+
120
+ ## What's Included
121
+
122
+ - JWT auth with Keycloak (pluggable providers)
123
+ - Docker Compose for dev and prod
124
+ - GitHub Actions CI per component
125
+ - Pre-commit hooks (format + lint + typecheck)
126
+ - Secret detection in pre-commit
127
+ - 80% test coverage enforced
128
+ - Auto-entity discovery across all stacks
129
+
130
+ ## Development
131
+
132
+ Contributing to Projx itself:
133
+
134
+ ```bash
135
+ git clone https://github.com/ukanhaupa/projx.git
136
+ cd projx
137
+ ./setup.sh
138
+ ```
139
+
140
+ The CLI lives in `cli/`. Templates are the root-level component directories (`fastapi/`, `frontend/`, etc.).
141
+
142
+ ## License
143
+
144
+ MIT
package/dist/index.js CHANGED
@@ -271,9 +271,6 @@ async function generateDockerCompose(vars) {
271
271
  async function generateDockerComposeDev(vars) {
272
272
  return renderShared("docker-compose.dev.yml.ejs", vars);
273
273
  }
274
- async function generateMakefile(vars) {
275
- return renderShared("Makefile.ejs", vars);
276
- }
277
274
  async function generatePreCommit(vars) {
278
275
  return renderShared("pre-commit.ejs", vars);
279
276
  }
@@ -328,9 +325,6 @@ async function doScaffold(opts, dest, repoDir, name, nameSnake, vars) {
328
325
  await writeFile2(join3(dest, "docker-compose.dev.yml"), dcDev);
329
326
  manifest.push("docker-compose.dev.yml");
330
327
  }
331
- const makefile = await generateMakefile(vars);
332
- await writeFile2(join3(dest, "Makefile"), makefile);
333
- manifest.push("Makefile");
334
328
  const readme = await generateReadme(vars);
335
329
  await writeFile2(join3(dest, "README.md"), readme);
336
330
  manifest.push("README.md");
@@ -383,7 +377,7 @@ async function doScaffold(opts, dest, repoDir, name, nameSnake, vars) {
383
377
  p2.outro(`Done! Next steps:
384
378
 
385
379
  cd ${name}
386
- make run-dev`);
380
+ ./setup.sh`);
387
381
  }
388
382
  async function substituteNames(dest, components, name, nameSnake) {
389
383
  if (components.includes("fastapi")) {
@@ -786,7 +780,6 @@ async function doAdd(cwd, config, toAdd, repoDir, skipInstall) {
786
780
  await generateDockerComposeDev(vars)
787
781
  );
788
782
  }
789
- await writeFile4(join5(cwd, "Makefile"), await generateMakefile(vars));
790
783
  await writeFile4(join5(cwd, "README.md"), await generateReadme(vars));
791
784
  await mkdir4(join5(cwd, ".githooks"), { recursive: true });
792
785
  await writeFile4(join5(cwd, ".githooks/pre-commit"), await generatePreCommit(vars));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-projx",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Scaffold production-grade projects. Pick your stack (FastAPI, Fastify, React, Flutter), get a fully wired template with auth, database, CI/CD, and E2E tests.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,8 +30,8 @@ Scaffolded with [Projx](https://github.com/ukanhaupa/projx).
30
30
  ## Getting Started
31
31
 
32
32
  ```bash
33
- make setup # Install all dependencies
34
- make run-dev # Start with Docker (dev mode)
33
+ ./setup.sh # Install all dependencies
34
+ docker compose -f docker-compose.dev.yml up # Start with Docker (dev mode)
35
35
  ```
36
36
  <% if (components.includes('fastapi')) { %>
37
37
 
@@ -73,10 +73,20 @@ cd mobile && cp .env.example .env && flutter pub get && flutter run
73
73
  ## Testing
74
74
 
75
75
  ```bash
76
- make test # Run all tests
77
- make test-coverage # Run with coverage
76
+ <% if (components.includes('fastapi')) { %>
77
+ cd fastapi && uv run pytest
78
+ <% } %>
79
+ <% if (components.includes('fastify')) { %>
80
+ cd fastify && pnpm test
81
+ <% } %>
82
+ <% if (components.includes('frontend')) { %>
83
+ cd frontend && npx vitest run
84
+ <% } %>
85
+ <% if (components.includes('mobile')) { %>
86
+ cd mobile && flutter test
87
+ <% } %>
78
88
  <% if (components.includes('e2e')) { %>
79
- make test-e2e # Playwright E2E tests
89
+ cd e2e && npx playwright test
80
90
  <% } %>
81
91
  ```
82
92
 
@@ -34,4 +34,4 @@ fi
34
34
  <% } %>
35
35
 
36
36
  echo ""
37
- echo "Done. Run 'make run-dev' to start, or 'make help' for all commands."
37
+ echo "Done. Run 'docker compose -f docker-compose.dev.yml up' to start."
@@ -1,286 +0,0 @@
1
- .PHONY: setup install format lint fix check typecheck test test-coverage test-e2e \
2
- migrate migrate-down seed run run-dev stop logs clean
3
-
4
- # ─── Setup ───────────────────────────────────────────────────────────
5
-
6
- setup:
7
- git config core.hooksPath .githooks
8
- @echo "Git hooks configured."
9
- $(MAKE) install
10
-
11
- install:
12
- <% if (components.includes('fastapi')) { %>
13
- cd fastapi && uv sync --all-extras
14
- <% } %>
15
- <% if (components.includes('fastify')) { %>
16
- cd fastify && pnpm install --frozen-lockfile
17
- <% } %>
18
- <% if (components.includes('frontend')) { %>
19
- cd frontend && npm ci
20
- <% } %>
21
- <% if (components.includes('e2e')) { %>
22
- cd e2e && npm ci
23
- <% } %>
24
- <% if (components.includes('mobile')) { %>
25
- cd mobile && flutter pub get 2>/dev/null || echo "Flutter SDK not installed — skipping mobile"
26
- <% } %>
27
- @echo "All dependencies installed."
28
-
29
- # ─── Format ──────────────────────────────────────────────────────────
30
-
31
- format:
32
- <% if (components.includes('fastapi')) { %>
33
- cd fastapi && uv run ruff format src tests
34
- <% } %>
35
- <% if (components.includes('fastify')) { %>
36
- cd fastify && npx prettier --write --ignore-unknown .
37
- <% } %>
38
- <% if (components.includes('frontend')) { %>
39
- cd frontend && npx prettier --write --ignore-unknown .
40
- <% } %>
41
- <% if (components.includes('e2e')) { %>
42
- cd e2e && npx prettier --write --ignore-unknown .
43
- <% } %>
44
- <% if (components.includes('mobile')) { %>
45
- cd mobile && dart format . 2>/dev/null || true
46
- <% } %>
47
- <% if (components.includes('infra')) { %>
48
- cd infra/stack && terraform fmt -recursive 2>/dev/null || true
49
- <% } %>
50
-
51
- # ─── Lint ────────────────────────────────────────────────────────────
52
-
53
- lint:
54
- <% if (components.includes('fastapi')) { %>
55
- cd fastapi && uv run ruff check src tests
56
- <% } %>
57
- <% if (components.includes('fastify')) { %>
58
- cd fastify && npx eslint 'src/**/*.ts' 'tests/**/*.ts'
59
- <% } %>
60
- <% if (components.includes('frontend')) { %>
61
- cd frontend && npx eslint 'src/**/*.{ts,tsx}'
62
- <% } %>
63
- <% if (components.includes('e2e')) { %>
64
- cd e2e && npx eslint '**/*.ts'
65
- <% } %>
66
- <% if (components.includes('mobile')) { %>
67
- cd mobile && dart analyze --fatal-infos 2>/dev/null || true
68
- <% } %>
69
- <% if (components.includes('infra')) { %>
70
- cd infra/stack && terraform validate 2>/dev/null || true
71
- <% } %>
72
-
73
- # ─── Fix (format + lint with auto-fix) ──────────────────────────────
74
-
75
- fix:
76
- <% if (components.includes('fastapi')) { %>
77
- cd fastapi && uv run ruff format src tests && uv run ruff check --fix src tests
78
- <% } %>
79
- <% if (components.includes('fastify')) { %>
80
- cd fastify && npx prettier --write --ignore-unknown . && npx eslint --fix 'src/**/*.ts' 'tests/**/*.ts'
81
- <% } %>
82
- <% if (components.includes('frontend')) { %>
83
- cd frontend && npx prettier --write --ignore-unknown . && npx eslint --fix 'src/**/*.{ts,tsx}'
84
- <% } %>
85
- <% if (components.includes('e2e')) { %>
86
- cd e2e && npx prettier --write --ignore-unknown . && npx eslint --fix '**/*.ts'
87
- <% } %>
88
- <% if (components.includes('mobile')) { %>
89
- cd mobile && dart format . 2>/dev/null && dart analyze --fatal-infos 2>/dev/null || true
90
- <% } %>
91
- <% if (components.includes('infra')) { %>
92
- cd infra/stack && terraform fmt -recursive 2>/dev/null || true
93
- <% } %>
94
-
95
- # ─── Type Check ──────────────────────────────────────────────────────
96
-
97
- typecheck:
98
- <% if (components.includes('fastify')) { %>
99
- cd fastify && npx tsc --noEmit
100
- <% } %>
101
- <% if (components.includes('frontend')) { %>
102
- cd frontend && npx tsc --noEmit
103
- <% } %>
104
- <% if (components.includes('e2e')) { %>
105
- cd e2e && npx tsc --noEmit
106
- <% } %>
107
-
108
- # ─── Check (CI-equivalent) ──────────────────────────────────────────
109
-
110
- check:
111
- <% if (components.includes('fastapi')) { %>
112
- cd fastapi && uv run ruff format --check src tests && uv run ruff check src tests
113
- <% } %>
114
- <% if (components.includes('fastify')) { %>
115
- cd fastify && npx prettier --check . && npx eslint . && npx tsc --noEmit
116
- <% } %>
117
- <% if (components.includes('frontend')) { %>
118
- cd frontend && npx prettier --check . && npx eslint 'src/**/*.{ts,tsx}' && npx tsc --noEmit
119
- <% } %>
120
- <% if (components.includes('e2e')) { %>
121
- cd e2e && npx prettier --check . && npx eslint '**/*.ts' && npx tsc --noEmit
122
- <% } %>
123
- <% if (components.includes('mobile')) { %>
124
- cd mobile && dart format --set-exit-if-changed . 2>/dev/null && dart analyze --fatal-infos 2>/dev/null || true
125
- <% } %>
126
- <% if (components.includes('infra')) { %>
127
- cd infra/stack && terraform fmt -check -recursive 2>/dev/null && terraform validate 2>/dev/null || true
128
- <% } %>
129
-
130
- # ─── Test ────────────────────────────────────────────────────────────
131
-
132
- test:
133
- <% if (components.includes('fastapi')) { %>
134
- cd fastapi && uv run pytest --tb=short -q
135
- <% } %>
136
- <% if (components.includes('fastify')) { %>
137
- cd fastify && pnpm test
138
- <% } %>
139
- <% if (components.includes('frontend')) { %>
140
- cd frontend && npx vitest run
141
- <% } %>
142
- <% if (components.includes('mobile')) { %>
143
- cd mobile && flutter test 2>/dev/null || true
144
- <% } %>
145
-
146
- test-coverage:
147
- <% if (components.includes('fastapi')) { %>
148
- cd fastapi && uv run pytest --cov=src --cov-report=term-missing --cov-fail-under=80
149
- <% } %>
150
- <% if (components.includes('fastify')) { %>
151
- cd fastify && pnpm test -- --coverage
152
- <% } %>
153
- <% if (components.includes('frontend')) { %>
154
- cd frontend && npx vitest run --coverage
155
- <% } %>
156
- <% if (components.includes('mobile')) { %>
157
- cd mobile && flutter test --coverage 2>/dev/null || true
158
- <% } %>
159
- <% if (components.includes('e2e')) { %>
160
-
161
- test-e2e:
162
- cd e2e && npx playwright test
163
- <% } %>
164
- <% if (components.includes('mobile')) { %>
165
-
166
- test-e2e-mobile:
167
- cd mobile && flutter test integration_test/
168
-
169
- mobile-build-apk:
170
- cd mobile && flutter build apk --release
171
-
172
- mobile-build-ios:
173
- cd mobile && flutter build ios --release --no-codesign
174
-
175
- mobile-codegen:
176
- cd mobile && dart run build_runner build --delete-conflicting-outputs
177
- <% } %>
178
- <% if (components.includes('fastapi')) { %>
179
-
180
- # ─── Database ────────────────────────────────────────────────────────
181
-
182
- migrate:
183
- cd fastapi && uv run migrate.py
184
- <% if (components.includes('fastify')) { %>
185
- cd fastify && pnpm prisma:migrate:dev
186
- <% } %>
187
-
188
- migrate-deploy:
189
- cd fastapi && uv run migrate.py
190
- <% if (components.includes('fastify')) { %>
191
- cd fastify && pnpm prisma:migrate:deploy
192
- <% } %>
193
-
194
- migrate-down:
195
- cd fastapi && uv run migrate.py --downgrade -1
196
-
197
- seed:
198
- cd fastapi && uv run seed.py
199
- <% } %>
200
- <% if (components.includes('fastify') && !components.includes('fastapi')) { %>
201
-
202
- migrate:
203
- cd fastify && pnpm prisma:migrate:dev
204
-
205
- migrate-deploy:
206
- cd fastify && pnpm prisma:migrate:deploy
207
- <% } %>
208
-
209
- # ─── Run (local) ─────────────────────────────────────────────────────
210
-
211
- run-local:
212
- <% if (components.includes('fastapi')) { %>
213
- cd fastapi && uv run main.py &
214
- <% } %>
215
- <% if (components.includes('fastify')) { %>
216
- cd fastify && pnpm dev &
217
- <% } %>
218
- <% if (components.includes('frontend')) { %>
219
- cd frontend && npm run dev &
220
- <% } %>
221
- @echo "All services starting. Run 'make stop-local' to stop."
222
- @wait
223
-
224
- stop-local:
225
- @kill $$(lsof -ti:7860,3000,5173 2>/dev/null) 2>/dev/null || true
226
- @echo "All local services stopped."
227
- <% if (components.includes('fastapi')) { %>
228
-
229
- run-fastapi:
230
- cd fastapi && uv run main.py
231
- <% } %>
232
- <% if (components.includes('fastify')) { %>
233
-
234
- run-fastify:
235
- cd fastify && pnpm dev
236
- <% } %>
237
- <% if (components.includes('frontend')) { %>
238
-
239
- run-frontend:
240
- cd frontend && npm run dev
241
- <% } %>
242
- <% if (components.includes('mobile')) { %>
243
-
244
- run-mobile:
245
- cd mobile && flutter run
246
- <% } %>
247
-
248
- # ─── Run (Docker Compose) ───────────────────────────────────────────
249
-
250
- run:
251
- docker compose up -d --build
252
-
253
- run-dev:
254
- docker compose -f docker-compose.dev.yml up -d --build
255
-
256
- stop:
257
- docker compose down
258
- docker compose -f docker-compose.dev.yml down 2>/dev/null || true
259
-
260
- logs:
261
- docker compose logs -f
262
- <% if (components.includes('fastapi')) { %>
263
-
264
- entity:
265
- @test -n "$(name)" || (echo "Usage: make entity name=my_entity" && exit 1)
266
- cd fastapi && uv run scaffold.py $(name)
267
-
268
- entity-custom:
269
- @test -n "$(name)" || (echo "Usage: make entity-custom name=my_entity" && exit 1)
270
- cd fastapi && uv run scaffold.py $(name) --controller
271
- <% } %>
272
-
273
- # ─── Clean ───────────────────────────────────────────────────────────
274
-
275
- clean:
276
- docker compose down -v
277
- docker compose -f docker-compose.dev.yml down -v 2>/dev/null || true
278
- <% if (components.includes('fastapi')) { %>
279
- cd fastapi && rm -rf .pytest_cache __pycache__ .coverage htmlcov
280
- <% } %>
281
- <% if (components.includes('fastify')) { %>
282
- cd fastify && rm -rf dist coverage node_modules/.cache
283
- <% } %>
284
- <% if (components.includes('frontend')) { %>
285
- cd frontend && rm -rf dist coverage node_modules/.cache playwright-report test-results
286
- <% } %>