create-projx 1.6.1 → 1.6.3

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.
@@ -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-FTHX7ILT.js";
36
+ } from "./chunk-LYPPFXGK.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.1",
3
+ "version": "1.6.3",
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** | Keycloak (OIDC / JWT) |
27
+ | **Identity** | OIDC / JWT |
28
28
  | **Containers** | Docker, Docker Compose |
29
29
 
30
30
  ## Getting Started
@@ -13,23 +13,23 @@ jobs:
13
13
  permissions:
14
14
  pull-requests: read
15
15
  outputs:
16
- <% if (components.includes('fastapi')) { %>
17
- <%= paths.fastapi %>: ${{ steps.filter.outputs.<%= paths.fastapi %> }}
16
+ <% for (const inst of fastapiInstances) { %>
17
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
18
18
  <% } %>
19
- <% if (components.includes('fastify')) { %>
20
- <%= paths.fastify %>: ${{ steps.filter.outputs.<%= paths.fastify %> }}
19
+ <% for (const inst of fastifyInstances) { %>
20
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
21
21
  <% } %>
22
- <% if (components.includes('frontend')) { %>
23
- <%= paths.frontend %>: ${{ steps.filter.outputs.<%= paths.frontend %> }}
22
+ <% for (const inst of frontendInstances) { %>
23
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
24
24
  <% } %>
25
- <% if (components.includes('mobile')) { %>
26
- <%= paths.mobile %>: ${{ steps.filter.outputs.<%= paths.mobile %> }}
25
+ <% for (const inst of mobileInstances) { %>
26
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
27
27
  <% } %>
28
- <% if (components.includes('e2e')) { %>
29
- <%= paths.e2e %>: ${{ steps.filter.outputs.<%= paths.e2e %> }}
28
+ <% for (const inst of e2eInstances) { %>
29
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
30
30
  <% } %>
31
- <% if (components.includes('infra')) { %>
32
- <%= paths.infra %>: ${{ steps.filter.outputs.<%= paths.infra %> }}
31
+ <% for (const inst of infraInstances) { %>
32
+ <%= inst.path %>: ${{ steps.filter.outputs.<%= inst.path %> }}
33
33
  <% } %>
34
34
  steps:
35
35
  - uses: actions/checkout@v5
@@ -37,63 +37,77 @@ jobs:
37
37
  id: filter
38
38
  with:
39
39
  filters: |
40
- <% if (components.includes('fastapi')) { %>
41
- <%= paths.fastapi %>:
42
- - '<%= paths.fastapi %>/**'
40
+ <% for (const inst of fastapiInstances) { %>
41
+ <%= inst.path %>:
42
+ - '<%= inst.path %>/**'
43
43
  <% } %>
44
- <% if (components.includes('fastify')) { %>
45
- <%= paths.fastify %>:
46
- - '<%= paths.fastify %>/**'
44
+ <% for (const inst of fastifyInstances) { %>
45
+ <%= inst.path %>:
46
+ - '<%= inst.path %>/**'
47
47
  <% } %>
48
- <% if (components.includes('frontend')) { %>
49
- <%= paths.frontend %>:
50
- - '<%= paths.frontend %>/**'
48
+ <% for (const inst of frontendInstances) { %>
49
+ <%= inst.path %>:
50
+ - '<%= inst.path %>/**'
51
51
  <% } %>
52
- <% if (components.includes('mobile')) { %>
53
- <%= paths.mobile %>:
54
- - '<%= paths.mobile %>/**'
52
+ <% for (const inst of mobileInstances) { %>
53
+ <%= inst.path %>:
54
+ - '<%= inst.path %>/**'
55
55
  <% } %>
56
- <% if (components.includes('e2e')) { %>
57
- <%= paths.e2e %>:
58
- - '<%= paths.e2e %>/**'
56
+ <% for (const inst of e2eInstances) { %>
57
+ <%= inst.path %>:
58
+ - '<%= inst.path %>/**'
59
59
  <% } %>
60
- <% if (components.includes('infra')) { %>
61
- <%= paths.infra %>:
62
- - '<%= paths.infra %>/**'
60
+ <% for (const inst of infraInstances) { %>
61
+ <%= inst.path %>:
62
+ - '<%= inst.path %>/**'
63
63
  <% } %>
64
- <% if (components.includes('fastapi')) { %>
65
64
 
66
- <%= paths.fastapi %>:
67
- name: <%= displayNames.fastapi %> (format + lint)
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 }}
75
+ <% for (const inst of fastapiInstances) { %>
76
+
77
+ <%= inst.path %>:
78
+ name: <%= inst.display %> (format + lint + typecheck + test + audit)
68
79
  needs: changes
69
- if: needs.changes.outputs.<%= paths.fastapi %> == 'true'
80
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
70
81
  runs-on: ubuntu-latest
71
82
  defaults:
72
83
  run:
73
- working-directory: <%= paths.fastapi %>
84
+ working-directory: <%= inst.path %>
74
85
  steps:
75
86
  - uses: actions/checkout@v5
76
87
  - uses: astral-sh/setup-uv@v4
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
- <% if (components.includes('fastify')) { %>
95
+ <% for (const inst of fastifyInstances) { %>
82
96
 
83
- <%= paths.fastify %>:
84
- name: <%= displayNames.fastify %> (format + lint + typecheck)
97
+ <%= inst.path %>:
98
+ name: <%= inst.display %> (format + lint + typecheck + audit)
85
99
  needs: changes
86
- if: needs.changes.outputs.<%= paths.fastify %> == 'true'
100
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
87
101
  runs-on: ubuntu-latest
88
102
  defaults:
89
103
  run:
90
- working-directory: <%= paths.fastify %>
104
+ working-directory: <%= inst.path %>
91
105
  steps:
92
106
  - uses: actions/checkout@v5
93
107
  <% if (pm === 'pnpm') { %>
94
108
  - uses: pnpm/action-setup@v4
95
109
  with:
96
- version: 9
110
+ version: 10
97
111
  <% } %>
98
112
  <% if (pm === 'bun') { %>
99
113
  - uses: oven-sh/setup-bun@v2
@@ -103,30 +117,31 @@ jobs:
103
117
  with:
104
118
  node-version: 20
105
119
  cache: <%= pm.name %>
106
- cache-dependency-path: <%= paths.fastify %>/<%= pm.lockfile %>
120
+ cache-dependency-path: <%= inst.path %>/<%= pm.lockfile %>
107
121
  <% } %>
108
122
  - run: <%= pm.ci %>
109
123
  - run: <%= pm.prismaExec %> generate
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
- <% if (components.includes('frontend')) { %>
129
+ <% for (const inst of frontendInstances) { %>
115
130
 
116
- <%= paths.frontend %>:
117
- name: <%= displayNames.frontend %> (format + lint + typecheck)
131
+ <%= inst.path %>:
132
+ name: <%= inst.display %> (format + lint + typecheck + audit)
118
133
  needs: changes
119
- if: needs.changes.outputs.<%= paths.frontend %> == 'true'
134
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
120
135
  runs-on: ubuntu-latest
121
136
  defaults:
122
137
  run:
123
- working-directory: <%= paths.frontend %>
138
+ working-directory: <%= inst.path %>
124
139
  steps:
125
140
  - uses: actions/checkout@v5
126
141
  <% if (pm === 'pnpm') { %>
127
142
  - uses: pnpm/action-setup@v4
128
143
  with:
129
- version: 9
144
+ version: 10
130
145
  <% } %>
131
146
  <% if (pm === 'bun') { %>
132
147
  - uses: oven-sh/setup-bun@v2
@@ -136,23 +151,24 @@ jobs:
136
151
  with:
137
152
  node-version: 22
138
153
  cache: <%= pm.name %>
139
- cache-dependency-path: <%= paths.frontend %>/<%= pm.lockfile %>
154
+ cache-dependency-path: <%= inst.path %>/<%= pm.lockfile %>
140
155
  <% } %>
141
156
  - run: <%= pm.ci %>
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
- <% if (components.includes('mobile')) { %>
162
+ <% for (const inst of mobileInstances) { %>
147
163
 
148
- <%= paths.mobile %>:
149
- name: <%= displayNames.mobile %> (format + analyze)
164
+ <%= inst.path %>:
165
+ name: <%= inst.display %> (format + analyze)
150
166
  needs: changes
151
- if: needs.changes.outputs.<%= paths.mobile %> == 'true'
167
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
152
168
  runs-on: ubuntu-latest
153
169
  defaults:
154
170
  run:
155
- working-directory: <%= paths.mobile %>
171
+ working-directory: <%= inst.path %>
156
172
  steps:
157
173
  - uses: actions/checkout@v5
158
174
  - uses: subosito/flutter-action@v2
@@ -163,22 +179,22 @@ jobs:
163
179
  - run: dart format --set-exit-if-changed .
164
180
  - run: dart analyze --fatal-infos
165
181
  <% } %>
166
- <% if (components.includes('e2e')) { %>
182
+ <% for (const inst of e2eInstances) { %>
167
183
 
168
- <%= paths.e2e %>:
169
- name: <%= displayNames.e2e %> (format + lint + typecheck)
184
+ <%= inst.path %>:
185
+ name: <%= inst.display %> (format + lint + typecheck + audit)
170
186
  needs: changes
171
- if: needs.changes.outputs.<%= paths.e2e %> == 'true'
187
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
172
188
  runs-on: ubuntu-latest
173
189
  defaults:
174
190
  run:
175
- working-directory: <%= paths.e2e %>
191
+ working-directory: <%= inst.path %>
176
192
  steps:
177
193
  - uses: actions/checkout@v5
178
194
  <% if (pm === 'pnpm') { %>
179
195
  - uses: pnpm/action-setup@v4
180
196
  with:
181
- version: 9
197
+ version: 10
182
198
  <% } %>
183
199
  <% if (pm === 'bun') { %>
184
200
  - uses: oven-sh/setup-bun@v2
@@ -188,23 +204,24 @@ jobs:
188
204
  with:
189
205
  node-version: 22
190
206
  cache: <%= pm.name %>
191
- cache-dependency-path: <%= paths.e2e %>/<%= pm.lockfile %>
207
+ cache-dependency-path: <%= inst.path %>/<%= pm.lockfile %>
192
208
  <% } %>
193
209
  - run: <%= pm.ci %>
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
- <% if (components.includes('infra')) { %>
215
+ <% for (const inst of infraInstances) { %>
199
216
 
200
- <%= paths.infra %>:
201
- name: <%= displayNames.infra %> (fmt + validate)
217
+ <%= inst.path %>:
218
+ name: <%= inst.display %> (fmt + validate)
202
219
  needs: changes
203
- if: needs.changes.outputs.<%= paths.infra %> == 'true'
220
+ if: needs.changes.outputs.<%= inst.path %> == 'true'
204
221
  runs-on: ubuntu-latest
205
222
  defaults:
206
223
  run:
207
- working-directory: <%= paths.infra %>/stack
224
+ working-directory: <%= inst.path %>/stack
208
225
  steps:
209
226
  - uses: actions/checkout@v5
210
227
  - uses: hashicorp/setup-terraform@v3
@@ -27,94 +27,95 @@ if [ -n "$STAGED_ENV_FILES" ]; then
27
27
  fi
28
28
 
29
29
  STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
30
- <% if (components.includes('fastapi')) { %>
30
+ <% for (const inst of fastapiInstances) { %>
31
31
 
32
- <%= pathsUpper.fastapi %>_PY=$(echo "$STAGED_FILES" | grep '^<%= paths.fastapi %>/.*\.py$' || true)
33
- if [ -n "$<%= pathsUpper.fastapi %>_PY" ]; then
34
- echo "Formatting & linting <%= paths.fastapi %>..."
35
- cd <%= paths.fastapi %>
36
- echo "$<%= pathsUpper.fastapi %>_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff format
37
- echo "$<%= pathsUpper.fastapi %>_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff check --fix
32
+ <%= inst.upper %>_PY=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.py$' || true)
33
+ if [ -n "$<%= inst.upper %>_PY" ]; then
34
+ echo "Running quality gates for <%= inst.path %>..."
35
+ cd <%= inst.path %>
36
+ echo "$<%= inst.upper %>_PY" | sed 's|^<%= inst.path %>/||' | xargs uv run ruff format
37
+ echo "$<%= inst.upper %>_PY" | sed 's|^<%= inst.path %>/||' | xargs uv run ruff check --fix
38
+ uv run mypy
38
39
  cd ..
39
- echo "$<%= pathsUpper.fastapi %>_PY" | xargs git add
40
+ echo "$<%= inst.upper %>_PY" | xargs git add
40
41
  fi
41
42
  <% } %>
42
- <% if (components.includes('fastify')) { %>
43
+ <% for (const inst of fastifyInstances) { %>
43
44
 
44
- <%= pathsUpper.fastify %>_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.fastify %>/.*\.ts$' || true)
45
- <%= pathsUpper.fastify %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.fastify %>/' || true)
46
- if [ -n "$<%= pathsUpper.fastify %>_ALL" ]; then
47
- echo "Formatting <%= paths.fastify %>..."
48
- cd <%= paths.fastify %>
49
- echo "$<%= pathsUpper.fastify %>_ALL" | sed 's|^<%= paths.fastify %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
50
- if [ -n "$<%= pathsUpper.fastify %>_TS" ]; then
51
- echo "$<%= pathsUpper.fastify %>_TS" | sed 's|^<%= paths.fastify %>/||' | xargs <%= pm.exec %> eslint --fix
45
+ <%= inst.upper %>_TS=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.ts$' || true)
46
+ <%= inst.upper %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/' || true)
47
+ if [ -n "$<%= inst.upper %>_ALL" ]; then
48
+ echo "Formatting <%= inst.path %>..."
49
+ cd <%= inst.path %>
50
+ echo "$<%= inst.upper %>_ALL" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
51
+ if [ -n "$<%= inst.upper %>_TS" ]; then
52
+ echo "$<%= inst.upper %>_TS" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> eslint --fix
52
53
  <%= pm.exec %> tsc --noEmit
53
54
  fi
54
55
  cd ..
55
- echo "$<%= pathsUpper.fastify %>_ALL" | xargs git add
56
+ echo "$<%= inst.upper %>_ALL" | xargs git add
56
57
  fi
57
58
  <% } %>
58
- <% if (components.includes('frontend')) { %>
59
+ <% for (const inst of frontendInstances) { %>
59
60
 
60
- <%= pathsUpper.frontend %>_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.frontend %>/.*\.tsx\?$' || true)
61
- <%= pathsUpper.frontend %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.frontend %>/' || true)
62
- if [ -n "$<%= pathsUpper.frontend %>_ALL" ]; then
63
- echo "Formatting <%= paths.frontend %>..."
64
- cd <%= paths.frontend %>
65
- echo "$<%= pathsUpper.frontend %>_ALL" | sed 's|^<%= paths.frontend %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
66
- if [ -n "$<%= pathsUpper.frontend %>_TS" ]; then
67
- echo "$<%= pathsUpper.frontend %>_TS" | sed 's|^<%= paths.frontend %>/||' | xargs <%= pm.exec %> eslint --fix
61
+ <%= inst.upper %>_TS=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.tsx\?$' || true)
62
+ <%= inst.upper %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/' || true)
63
+ if [ -n "$<%= inst.upper %>_ALL" ]; then
64
+ echo "Formatting <%= inst.path %>..."
65
+ cd <%= inst.path %>
66
+ echo "$<%= inst.upper %>_ALL" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
67
+ if [ -n "$<%= inst.upper %>_TS" ]; then
68
+ echo "$<%= inst.upper %>_TS" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> eslint --fix
68
69
  <%= pm.exec %> tsc --noEmit
69
70
  fi
70
71
  cd ..
71
- echo "$<%= pathsUpper.frontend %>_ALL" | xargs git add
72
+ echo "$<%= inst.upper %>_ALL" | xargs git add
72
73
  fi
73
74
  <% } %>
74
- <% if (components.includes('e2e')) { %>
75
+ <% for (const inst of e2eInstances) { %>
75
76
 
76
- <%= pathsUpper.e2e %>_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.e2e %>/.*\.ts$' || true)
77
- <%= pathsUpper.e2e %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.e2e %>/' || true)
78
- if [ -n "$<%= pathsUpper.e2e %>_ALL" ]; then
79
- echo "Formatting <%= paths.e2e %>..."
80
- cd <%= paths.e2e %>
81
- echo "$<%= pathsUpper.e2e %>_ALL" | sed 's|^<%= paths.e2e %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
82
- if [ -n "$<%= pathsUpper.e2e %>_TS" ]; then
83
- echo "$<%= pathsUpper.e2e %>_TS" | sed 's|^<%= paths.e2e %>/||' | xargs <%= pm.exec %> eslint --fix
77
+ <%= inst.upper %>_TS=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.ts$' || true)
78
+ <%= inst.upper %>_ALL=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/' || true)
79
+ if [ -n "$<%= inst.upper %>_ALL" ]; then
80
+ echo "Formatting <%= inst.path %>..."
81
+ cd <%= inst.path %>
82
+ echo "$<%= inst.upper %>_ALL" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> prettier --write --ignore-unknown
83
+ if [ -n "$<%= inst.upper %>_TS" ]; then
84
+ echo "$<%= inst.upper %>_TS" | sed 's|^<%= inst.path %>/||' | xargs <%= pm.exec %> eslint --fix
84
85
  <%= pm.exec %> tsc --noEmit
85
86
  fi
86
87
  cd ..
87
- echo "$<%= pathsUpper.e2e %>_ALL" | xargs git add
88
+ echo "$<%= inst.upper %>_ALL" | xargs git add
88
89
  fi
89
90
  <% } %>
90
- <% if (components.includes('mobile')) { %>
91
+ <% for (const inst of mobileInstances) { %>
91
92
 
92
- <%= pathsUpper.mobile %>_DART=$(echo "$STAGED_FILES" | grep '^<%= paths.mobile %>/.*\.dart$' || true)
93
- if [ -n "$<%= pathsUpper.mobile %>_DART" ]; then
93
+ <%= inst.upper %>_DART=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.dart$' || true)
94
+ if [ -n "$<%= inst.upper %>_DART" ]; then
94
95
  if command -v dart &> /dev/null; then
95
- echo "Formatting <%= paths.mobile %>..."
96
- cd <%= paths.mobile %>
97
- echo "$<%= pathsUpper.mobile %>_DART" | sed 's|^<%= paths.mobile %>/||' | xargs dart format
96
+ echo "Formatting <%= inst.path %>..."
97
+ cd <%= inst.path %>
98
+ echo "$<%= inst.upper %>_DART" | sed 's|^<%= inst.path %>/||' | xargs dart format
98
99
  if command -v flutter &> /dev/null; then
99
100
  dart analyze --fatal-infos
100
101
  fi
101
102
  cd ..
102
- echo "$<%= pathsUpper.mobile %>_DART" | xargs git add
103
+ echo "$<%= inst.upper %>_DART" | xargs git add
103
104
  else
104
105
  echo "Skipping mobile lint (dart not installed)"
105
106
  fi
106
107
  fi
107
108
  <% } %>
108
- <% if (components.includes('infra')) { %>
109
+ <% for (const inst of infraInstances) { %>
109
110
 
110
- <%= pathsUpper.infra %>_TF=$(echo "$STAGED_FILES" | grep '^<%= paths.infra %>/.*\.tf$' || true)
111
- if [ -n "$<%= pathsUpper.infra %>_TF" ]; then
111
+ <%= inst.upper %>_TF=$(echo "$STAGED_FILES" | grep '^<%= inst.path %>/.*\.tf$' || true)
112
+ if [ -n "$<%= inst.upper %>_TF" ]; then
112
113
  if command -v terraform &> /dev/null; then
113
- echo "Formatting <%= paths.infra %>..."
114
- cd <%= paths.infra %>/stack
115
- echo "$<%= pathsUpper.infra %>_TF" | sed 's|^<%= paths.infra %>/stack/||' | xargs terraform fmt
114
+ echo "Formatting <%= inst.path %>..."
115
+ cd <%= inst.path %>/stack
116
+ echo "$<%= inst.upper %>_TF" | sed 's|^<%= inst.path %>/stack/||' | xargs terraform fmt
116
117
  cd ../..
117
- echo "$<%= pathsUpper.infra %>_TF" | xargs git add
118
+ echo "$<%= inst.upper %>_TF" | xargs git add
118
119
  else
119
120
  echo "Skipping infra lint (terraform not installed)"
120
121
  fi
@@ -3,33 +3,33 @@ set -e
3
3
 
4
4
  git config core.hooksPath .githooks
5
5
  echo "Git hooks configured."
6
- <% if (components.includes('fastapi')) { %>
6
+ <% for (const inst of fastapiInstances) { %>
7
7
 
8
- cd <%= paths.fastapi %> && uv sync --all-extras && cd ..
9
- echo "<%= displayNames.fastapi %> dependencies installed."
8
+ cd <%= inst.path %> && uv sync --all-extras && cd ..
9
+ echo "<%= inst.display %> dependencies installed."
10
10
  <% } %>
11
- <% if (components.includes('fastify')) { %>
11
+ <% for (const inst of fastifyInstances) { %>
12
12
 
13
- cd <%= paths.fastify %> && <%= pm.ci %> && cd ..
14
- echo "<%= displayNames.fastify %> dependencies installed."
13
+ cd <%= inst.path %> && <%= pm.ci %> && cd ..
14
+ echo "<%= inst.display %> dependencies installed."
15
15
  <% } %>
16
- <% if (components.includes('frontend')) { %>
16
+ <% for (const inst of frontendInstances) { %>
17
17
 
18
- cd <%= paths.frontend %> && <%= pm.ci %> && cd ..
19
- echo "<%= displayNames.frontend %> dependencies installed."
18
+ cd <%= inst.path %> && <%= pm.ci %> && cd ..
19
+ echo "<%= inst.display %> dependencies installed."
20
20
  <% } %>
21
- <% if (components.includes('e2e')) { %>
21
+ <% for (const inst of e2eInstances) { %>
22
22
 
23
- cd <%= paths.e2e %> && <%= pm.ci %> && cd ..
24
- echo "<%= displayNames.e2e %> dependencies installed."
23
+ cd <%= inst.path %> && <%= pm.ci %> && cd ..
24
+ echo "<%= inst.display %> dependencies installed."
25
25
  <% } %>
26
- <% if (components.includes('mobile')) { %>
26
+ <% for (const inst of mobileInstances) { %>
27
27
 
28
28
  if command -v flutter &>/dev/null; then
29
- cd <%= paths.mobile %> && flutter pub get && cd ..
30
- echo "<%= displayNames.mobile %> dependencies installed."
29
+ cd <%= inst.path %> && flutter pub get && cd ..
30
+ echo "<%= inst.display %> dependencies installed."
31
31
  else
32
- echo "<%= displayNames.mobile %> skipped (SDK not installed)."
32
+ echo "<%= inst.display %> skipped (SDK not installed)."
33
33
  fi
34
34
  <% } %>
35
35