create-projx 1.6.1 → 1.6.2
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 +5 -3
- package/dist/{baseline-5XAJJ457.js → baseline-KTCFW2FK.js} +2 -2
- package/dist/{chunk-TNI4XBVS.js → chunk-D33FXCNT.js} +8 -4
- package/dist/{chunk-FTHX7ILT.js → chunk-LTIJPVRZ.js} +21 -4
- package/dist/index.js +3 -3
- package/dist/{utils-OOY5OZDX.js → utils-VY5BBJBQ.js} +3 -1
- package/package.json +8 -9
- package/src/templates/README.md.ejs +1 -1
- package/src/templates/ci.yml.ejs +24 -7
- package/src/templates/pre-commit.ejs +2 -1
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
**Go from blank folder to production-ready project in 30 seconds.** Backend-only API, AI/ML app, mobile, full-stack, infra setup — pick what you need and get it wired with auth, database, Docker, CI/CD, hooks, and tests. All optional. All yours.
|
|
9
9
|
|
|
10
|
+

|
|
11
|
+
|
|
10
12
|
```bash
|
|
11
13
|
npx create-projx my-app
|
|
12
14
|
```
|
|
@@ -289,7 +291,7 @@ Override with `--ai` (fastapi) or `--backend` (fastify).
|
|
|
289
291
|
| `frontend` | `src/types/<name>.ts` — TypeScript interface + Create/Update variants |
|
|
290
292
|
| `mobile` | `lib/entities/<name>/model.dart` — Dart class with fromJson/toJson/copyWith |
|
|
291
293
|
|
|
292
|
-
**Tests included**: every `gen entity` writes a working integration test file alongside the model — 11 tests for FastAPI (extending `BaseEntityApiTest`), 11 tests for Fastify (via `describeCrudEntity`). Both run against a real database (Postgres
|
|
294
|
+
**Tests included**: every `gen entity` writes a working integration test file alongside the model — 11 tests for FastAPI (extending `BaseEntityApiTest`), 11 tests for Fastify (via `describeCrudEntity`). Both run against a real database (Postgres). New entities ship green from day one — no scrambling to bolt on tests at go-live.
|
|
293
295
|
|
|
294
296
|
No migrations — run `alembic revision --autogenerate` or `prisma migrate dev` (via your package manager) when ready.
|
|
295
297
|
|
|
@@ -371,8 +373,8 @@ The CLI lives in `cli/`. Templates are the root-level component directories (`fa
|
|
|
371
373
|
|
|
372
374
|
```bash
|
|
373
375
|
cd cli
|
|
374
|
-
|
|
375
|
-
|
|
376
|
+
pnpm test # run tests
|
|
377
|
+
pnpm build # build CLI
|
|
376
378
|
```
|
|
377
379
|
|
|
378
380
|
## Try it now
|
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
readComponentMarker,
|
|
7
7
|
readProjxConfig,
|
|
8
8
|
render,
|
|
9
|
+
renderEjsInDir,
|
|
9
10
|
replaceInDir,
|
|
10
11
|
replaceInFile,
|
|
11
12
|
sharedTemplateDir,
|
|
12
13
|
toSnake,
|
|
13
14
|
upsertComponentMarker,
|
|
14
15
|
writeProjxConfig
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-LTIJPVRZ.js";
|
|
16
17
|
|
|
17
18
|
// src/baseline.ts
|
|
18
19
|
import { existsSync, writeFileSync, unlinkSync } from "fs";
|
|
@@ -106,7 +107,7 @@ function buildDisplayNames(paths) {
|
|
|
106
107
|
}
|
|
107
108
|
var BASELINE_REF = "refs/projx/baseline";
|
|
108
109
|
async function migrateComponentMarkers(cwd, components, componentPaths, applyDefaults) {
|
|
109
|
-
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-
|
|
110
|
+
const { readComponentMarker: readComponentMarker2, writeComponentMarker } = await import("./utils-VY5BBJBQ.js");
|
|
110
111
|
for (const component of components) {
|
|
111
112
|
const dir = componentPaths[component];
|
|
112
113
|
const markerDir = join2(cwd, dir);
|
|
@@ -329,8 +330,10 @@ async function removeSkippedFiles(dir, skipPatterns, realDir) {
|
|
|
329
330
|
const rel = full.slice(base.length + 1);
|
|
330
331
|
if (entry.isDirectory()) {
|
|
331
332
|
await walk(full, base);
|
|
332
|
-
} else if (entry.name !== ".projx-component"
|
|
333
|
-
|
|
333
|
+
} else if (entry.name !== ".projx-component") {
|
|
334
|
+
const targetRel = rel.endsWith(".ejs") ? rel.slice(0, -".ejs".length) : rel;
|
|
335
|
+
if (!matchesSkip(targetRel, skipPatterns) && !matchesSkip(rel, skipPatterns)) continue;
|
|
336
|
+
if (realDir && !existsSync(join2(realDir, targetRel))) continue;
|
|
334
337
|
await unlink(full);
|
|
335
338
|
}
|
|
336
339
|
}
|
|
@@ -363,6 +366,7 @@ async function writeTemplateToDir(dest, repoDir, components, componentPaths, var
|
|
|
363
366
|
await cp(srcDir, outDir, { recursive: true, force: true });
|
|
364
367
|
}
|
|
365
368
|
await rm(tmpDir, { recursive: true, force: true });
|
|
369
|
+
await renderEjsInDir(outDir, vars);
|
|
366
370
|
await upsertComponentMarker(join2(dest, targetDir), component, skipPatterns.length > 0 ? skipPatterns : void 0);
|
|
367
371
|
}
|
|
368
372
|
if (!vars.pathsUpper) {
|
|
@@ -19,13 +19,13 @@ var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
|
|
|
19
19
|
function pmCommands(pm) {
|
|
20
20
|
switch (pm) {
|
|
21
21
|
case "npm":
|
|
22
|
-
return { name: "npm", install: "npm install", ci: "npm ci", run: "npm run", exec: "npx", dlx: "npx", lockfile: "package-lock.json", prismaExec: "npx prisma", runDev: "npm run dev" };
|
|
22
|
+
return { name: "npm", install: "npm install", ci: "npm ci", run: "npm run", exec: "npx", dlx: "npx", lockfile: "package-lock.json", prismaExec: "npx prisma", runDev: "npm run dev", audit: "npm audit --omit=dev" };
|
|
23
23
|
case "pnpm":
|
|
24
|
-
return { name: "pnpm", install: "pnpm install", ci: "pnpm install --frozen-lockfile", run: "pnpm", exec: "pnpm exec", dlx: "pnpm dlx", lockfile: "pnpm-lock.yaml", prismaExec: "pnpm prisma", runDev: "pnpm dev" };
|
|
24
|
+
return { name: "pnpm", install: "pnpm install", ci: "pnpm install --frozen-lockfile", run: "pnpm", exec: "pnpm exec", dlx: "pnpm dlx", lockfile: "pnpm-lock.yaml", prismaExec: "pnpm prisma", runDev: "pnpm dev", audit: "pnpm audit --prod" };
|
|
25
25
|
case "yarn":
|
|
26
|
-
return { name: "yarn", install: "yarn", ci: "yarn --frozen-lockfile", run: "yarn", exec: "yarn", dlx: "yarn dlx", lockfile: "yarn.lock", prismaExec: "yarn prisma", runDev: "yarn dev" };
|
|
26
|
+
return { name: "yarn", install: "yarn", ci: "yarn --frozen-lockfile", run: "yarn", exec: "yarn", dlx: "yarn dlx", lockfile: "yarn.lock", prismaExec: "yarn prisma", runDev: "yarn dev", audit: "yarn npm audit --environment production" };
|
|
27
27
|
case "bun":
|
|
28
|
-
return { name: "bun", install: "bun install", ci: "bun install --frozen-lockfile", run: "bun run", exec: "bunx", dlx: "bunx", lockfile: "bun.lockb", prismaExec: "bunx prisma", runDev: "bun run dev" };
|
|
28
|
+
return { name: "bun", install: "bun install", ci: "bun install --frozen-lockfile", run: "bun run", exec: "bunx", dlx: "bunx", lockfile: "bun.lockb", prismaExec: "bunx prisma", runDev: "bun run dev", audit: "bun audit --prod" };
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
function detectPackageManager(cwd) {
|
|
@@ -369,6 +369,22 @@ function render(template, vars) {
|
|
|
369
369
|
}
|
|
370
370
|
return output.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
371
371
|
}
|
|
372
|
+
async function renderEjsInDir(dir, vars) {
|
|
373
|
+
if (!existsSync(dir)) return;
|
|
374
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
const full = join(dir, entry.name);
|
|
377
|
+
if (entry.isDirectory()) {
|
|
378
|
+
await renderEjsInDir(full, vars);
|
|
379
|
+
} else if (entry.name.endsWith(".ejs")) {
|
|
380
|
+
const content = await readFile(full, "utf-8");
|
|
381
|
+
const rendered = render(content, vars);
|
|
382
|
+
const out = full.slice(0, -".ejs".length);
|
|
383
|
+
await writeFile(out, rendered);
|
|
384
|
+
await rm(full);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
372
388
|
function detectProjectName(cwd, components, componentPaths) {
|
|
373
389
|
for (const component of components) {
|
|
374
390
|
const dir = componentPaths[component] ?? component;
|
|
@@ -420,5 +436,6 @@ export {
|
|
|
420
436
|
discoverComponentPaths,
|
|
421
437
|
discoverComponentsFromMarkers,
|
|
422
438
|
render,
|
|
439
|
+
renderEjsInDir,
|
|
423
440
|
detectProjectName
|
|
424
441
|
};
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
matchesSkip,
|
|
10
10
|
saveBaselineRef,
|
|
11
11
|
writeTemplateToDir
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-D33FXCNT.js";
|
|
13
13
|
import {
|
|
14
14
|
COMPONENTS,
|
|
15
15
|
COMPONENT_MARKER,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
toTitle,
|
|
34
34
|
writeComponentMarker,
|
|
35
35
|
writeProjxConfig
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-LTIJPVRZ.js";
|
|
37
37
|
|
|
38
38
|
// src/index.ts
|
|
39
39
|
import { existsSync as existsSync11 } from "fs";
|
|
@@ -371,7 +371,7 @@ function hasUncommittedChanges(cwd) {
|
|
|
371
371
|
async function findPinnedFilesWithUpdates(cwd, repoDir, components, componentPaths, vars, version, componentSkips, rootSkip) {
|
|
372
372
|
const { mkdir: mkdir5, rm: rm2, readFile: readFile7 } = await import("fs/promises");
|
|
373
373
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
374
|
-
const { writeTemplateToDir: writeTemplateToDir2 } = await import("./baseline-
|
|
374
|
+
const { writeTemplateToDir: writeTemplateToDir2 } = await import("./baseline-KTCFW2FK.js");
|
|
375
375
|
const config = await readProjxConfig(cwd);
|
|
376
376
|
const rootPinned = Array.isArray(config.skip) ? config.skip : [];
|
|
377
377
|
const componentPinned = [];
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
readFileOrNull,
|
|
24
24
|
readProjxConfig,
|
|
25
25
|
render,
|
|
26
|
+
renderEjsInDir,
|
|
26
27
|
replaceInDir,
|
|
27
28
|
replaceInFile,
|
|
28
29
|
sharedTemplateDir,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
upsertComponentMarker,
|
|
33
34
|
writeComponentMarker,
|
|
34
35
|
writeProjxConfig
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-LTIJPVRZ.js";
|
|
36
37
|
export {
|
|
37
38
|
COMPONENTS,
|
|
38
39
|
COMPONENT_MARKER,
|
|
@@ -58,6 +59,7 @@ export {
|
|
|
58
59
|
readFileOrNull,
|
|
59
60
|
readProjxConfig,
|
|
60
61
|
render,
|
|
62
|
+
renderEjsInDir,
|
|
61
63
|
replaceInDir,
|
|
62
64
|
replaceInFile,
|
|
63
65
|
sharedTemplateDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-projx",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
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": {
|
|
@@ -10,13 +10,6 @@
|
|
|
10
10
|
"dist",
|
|
11
11
|
"src/templates"
|
|
12
12
|
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "tsup src/index.ts --format esm --target node18 --clean",
|
|
15
|
-
"dev": "tsup src/index.ts --format esm --target node18 --watch",
|
|
16
|
-
"test": "vitest run",
|
|
17
|
-
"test:watch": "vitest",
|
|
18
|
-
"prepublishOnly": "npm run build"
|
|
19
|
-
},
|
|
20
13
|
"keywords": [
|
|
21
14
|
"projx",
|
|
22
15
|
"scaffold",
|
|
@@ -61,5 +54,11 @@
|
|
|
61
54
|
"typescript": "^5",
|
|
62
55
|
"typescript-eslint": "^8.58.0",
|
|
63
56
|
"vitest": "^4.1.2"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsup src/index.ts --format esm --target node18 --clean",
|
|
60
|
+
"dev": "tsup src/index.ts --format esm --target node18 --watch",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"test:watch": "vitest"
|
|
64
63
|
}
|
|
65
|
-
}
|
|
64
|
+
}
|
|
@@ -24,7 +24,7 @@ Scaffolded with [Projx](https://github.com/ukanhaupa/projx).
|
|
|
24
24
|
<% if (components.includes('infra')) { %>
|
|
25
25
|
| **<%= paths.infra %>/** | Terraform, AWS (EKS, RDS, VPC, CodePipeline) |
|
|
26
26
|
<% } %>
|
|
27
|
-
| **Identity** |
|
|
27
|
+
| **Identity** | OIDC / JWT |
|
|
28
28
|
| **Containers** | Docker, Docker Compose |
|
|
29
29
|
|
|
30
30
|
## Getting Started
|
package/src/templates/ci.yml.ejs
CHANGED
|
@@ -61,10 +61,21 @@ jobs:
|
|
|
61
61
|
<%= paths.infra %>:
|
|
62
62
|
- '<%= paths.infra %>/**'
|
|
63
63
|
<% } %>
|
|
64
|
+
|
|
65
|
+
secrets:
|
|
66
|
+
name: Secret scan
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/checkout@v5
|
|
70
|
+
with:
|
|
71
|
+
fetch-depth: 0
|
|
72
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
73
|
+
env:
|
|
74
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
64
75
|
<% if (components.includes('fastapi')) { %>
|
|
65
76
|
|
|
66
77
|
<%= paths.fastapi %>:
|
|
67
|
-
name: <%= displayNames.fastapi %> (format + lint)
|
|
78
|
+
name: <%= displayNames.fastapi %> (format + lint + typecheck + test + audit)
|
|
68
79
|
needs: changes
|
|
69
80
|
if: needs.changes.outputs.<%= paths.fastapi %> == 'true'
|
|
70
81
|
runs-on: ubuntu-latest
|
|
@@ -77,11 +88,14 @@ jobs:
|
|
|
77
88
|
- run: uv sync --group dev
|
|
78
89
|
- run: uv run ruff format --check src tests
|
|
79
90
|
- run: uv run ruff check src tests
|
|
91
|
+
- run: uv run mypy
|
|
92
|
+
- run: uv run pytest
|
|
93
|
+
- run: uv run pip-audit
|
|
80
94
|
<% } %>
|
|
81
95
|
<% if (components.includes('fastify')) { %>
|
|
82
96
|
|
|
83
97
|
<%= paths.fastify %>:
|
|
84
|
-
name: <%= displayNames.fastify %> (format + lint + typecheck)
|
|
98
|
+
name: <%= displayNames.fastify %> (format + lint + typecheck + audit)
|
|
85
99
|
needs: changes
|
|
86
100
|
if: needs.changes.outputs.<%= paths.fastify %> == 'true'
|
|
87
101
|
runs-on: ubuntu-latest
|
|
@@ -93,7 +107,7 @@ jobs:
|
|
|
93
107
|
<% if (pm === 'pnpm') { %>
|
|
94
108
|
- uses: pnpm/action-setup@v4
|
|
95
109
|
with:
|
|
96
|
-
version:
|
|
110
|
+
version: 10
|
|
97
111
|
<% } %>
|
|
98
112
|
<% if (pm === 'bun') { %>
|
|
99
113
|
- uses: oven-sh/setup-bun@v2
|
|
@@ -110,11 +124,12 @@ jobs:
|
|
|
110
124
|
- run: <%= pm.exec %> prettier --check .
|
|
111
125
|
- run: <%= pm.exec %> eslint .
|
|
112
126
|
- run: <%= pm.exec %> tsc --noEmit
|
|
127
|
+
- run: <%= pm.audit %>
|
|
113
128
|
<% } %>
|
|
114
129
|
<% if (components.includes('frontend')) { %>
|
|
115
130
|
|
|
116
131
|
<%= paths.frontend %>:
|
|
117
|
-
name: <%= displayNames.frontend %> (format + lint + typecheck)
|
|
132
|
+
name: <%= displayNames.frontend %> (format + lint + typecheck + audit)
|
|
118
133
|
needs: changes
|
|
119
134
|
if: needs.changes.outputs.<%= paths.frontend %> == 'true'
|
|
120
135
|
runs-on: ubuntu-latest
|
|
@@ -126,7 +141,7 @@ jobs:
|
|
|
126
141
|
<% if (pm === 'pnpm') { %>
|
|
127
142
|
- uses: pnpm/action-setup@v4
|
|
128
143
|
with:
|
|
129
|
-
version:
|
|
144
|
+
version: 10
|
|
130
145
|
<% } %>
|
|
131
146
|
<% if (pm === 'bun') { %>
|
|
132
147
|
- uses: oven-sh/setup-bun@v2
|
|
@@ -142,6 +157,7 @@ jobs:
|
|
|
142
157
|
- run: <%= pm.exec %> prettier --check .
|
|
143
158
|
- run: <%= pm.exec %> eslint 'src/**/*.{ts,tsx}'
|
|
144
159
|
- run: <%= pm.exec %> tsc --noEmit
|
|
160
|
+
- run: <%= pm.audit %>
|
|
145
161
|
<% } %>
|
|
146
162
|
<% if (components.includes('mobile')) { %>
|
|
147
163
|
|
|
@@ -166,7 +182,7 @@ jobs:
|
|
|
166
182
|
<% if (components.includes('e2e')) { %>
|
|
167
183
|
|
|
168
184
|
<%= paths.e2e %>:
|
|
169
|
-
name: <%= displayNames.e2e %> (format + lint + typecheck)
|
|
185
|
+
name: <%= displayNames.e2e %> (format + lint + typecheck + audit)
|
|
170
186
|
needs: changes
|
|
171
187
|
if: needs.changes.outputs.<%= paths.e2e %> == 'true'
|
|
172
188
|
runs-on: ubuntu-latest
|
|
@@ -178,7 +194,7 @@ jobs:
|
|
|
178
194
|
<% if (pm === 'pnpm') { %>
|
|
179
195
|
- uses: pnpm/action-setup@v4
|
|
180
196
|
with:
|
|
181
|
-
version:
|
|
197
|
+
version: 10
|
|
182
198
|
<% } %>
|
|
183
199
|
<% if (pm === 'bun') { %>
|
|
184
200
|
- uses: oven-sh/setup-bun@v2
|
|
@@ -194,6 +210,7 @@ jobs:
|
|
|
194
210
|
- run: <%= pm.exec %> prettier --check .
|
|
195
211
|
- run: <%= pm.exec %> eslint '**/*.ts'
|
|
196
212
|
- run: <%= pm.exec %> tsc --noEmit
|
|
213
|
+
- run: <%= pm.audit %>
|
|
197
214
|
<% } %>
|
|
198
215
|
<% if (components.includes('infra')) { %>
|
|
199
216
|
|
|
@@ -31,10 +31,11 @@ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
|
|
|
31
31
|
|
|
32
32
|
<%= pathsUpper.fastapi %>_PY=$(echo "$STAGED_FILES" | grep '^<%= paths.fastapi %>/.*\.py$' || true)
|
|
33
33
|
if [ -n "$<%= pathsUpper.fastapi %>_PY" ]; then
|
|
34
|
-
echo "
|
|
34
|
+
echo "Running quality gates for <%= paths.fastapi %>..."
|
|
35
35
|
cd <%= paths.fastapi %>
|
|
36
36
|
echo "$<%= pathsUpper.fastapi %>_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff format
|
|
37
37
|
echo "$<%= pathsUpper.fastapi %>_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff check --fix
|
|
38
|
+
uv run mypy
|
|
38
39
|
cd ..
|
|
39
40
|
echo "$<%= pathsUpper.fastapi %>_PY" | xargs git add
|
|
40
41
|
fi
|