create-projx 1.0.1 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-projx",
3
- "version": "1.0.1",
3
+ "version": "1.1.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": {
@@ -13,6 +13,8 @@
13
13
  "scripts": {
14
14
  "build": "tsup src/index.ts --format esm --target node18 --clean",
15
15
  "dev": "tsup src/index.ts --format esm --target node18 --watch",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
16
18
  "prepublishOnly": "npm run build"
17
19
  },
18
20
  "keywords": [
@@ -26,6 +28,15 @@
26
28
  "fullstack",
27
29
  "create"
28
30
  ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/ukanhaupa/projx.git",
34
+ "directory": "cli"
35
+ },
36
+ "homepage": "https://github.com/ukanhaupa/projx#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/ukanhaupa/projx/issues"
39
+ },
29
40
  "license": "MIT",
30
41
  "engines": {
31
42
  "node": ">=18"
@@ -39,6 +50,7 @@
39
50
  "eslint": "^10.2.0",
40
51
  "tsup": "^8",
41
52
  "typescript": "^5",
42
- "typescript-eslint": "^8.58.0"
53
+ "typescript-eslint": "^8.58.0",
54
+ "vitest": "^4.1.2"
43
55
  }
44
56
  }
@@ -38,7 +38,7 @@ docker compose -f docker-compose.dev.yml up # Start with Docker (dev mode)
38
38
  ### FastAPI
39
39
 
40
40
  ```bash
41
- cd fastapi && cp .env.example .env && uv sync && uv run main.py
41
+ cd <%= paths.fastapi %> && cp .env.example .env && uv sync && uv run main.py
42
42
  ```
43
43
 
44
44
  API docs at `http://localhost:7860/docs`.
@@ -48,7 +48,7 @@ API docs at `http://localhost:7860/docs`.
48
48
  ### Fastify
49
49
 
50
50
  ```bash
51
- cd fastify && cp .env.example .env && pnpm install && npx prisma migrate dev && pnpm dev
51
+ cd <%= paths.fastify %> && cp .env.example .env && pnpm install && npx prisma migrate dev && pnpm dev
52
52
  ```
53
53
 
54
54
  API docs at `http://localhost:3000/docs`.
@@ -58,7 +58,7 @@ API docs at `http://localhost:3000/docs`.
58
58
  ### Frontend
59
59
 
60
60
  ```bash
61
- cd frontend && cp .env.example .env && npm install && npm run dev
61
+ cd <%= paths.frontend %> && cp .env.example .env && npm install && npm run dev
62
62
  ```
63
63
  <% } %>
64
64
  <% if (components.includes('mobile')) { %>
@@ -66,7 +66,7 @@ cd frontend && cp .env.example .env && npm install && npm run dev
66
66
  ### Mobile
67
67
 
68
68
  ```bash
69
- cd mobile && cp .env.example .env && flutter pub get && flutter run
69
+ cd <%= paths.mobile %> && cp .env.example .env && flutter pub get && flutter run
70
70
  ```
71
71
  <% } %>
72
72
 
@@ -74,19 +74,19 @@ cd mobile && cp .env.example .env && flutter pub get && flutter run
74
74
 
75
75
  ```bash
76
76
  <% if (components.includes('fastapi')) { %>
77
- cd fastapi && uv run pytest
77
+ cd <%= paths.fastapi %> && uv run pytest
78
78
  <% } %>
79
79
  <% if (components.includes('fastify')) { %>
80
- cd fastify && pnpm test
80
+ cd <%= paths.fastify %> && pnpm test
81
81
  <% } %>
82
82
  <% if (components.includes('frontend')) { %>
83
- cd frontend && npx vitest run
83
+ cd <%= paths.frontend %> && npx vitest run
84
84
  <% } %>
85
85
  <% if (components.includes('mobile')) { %>
86
- cd mobile && flutter test
86
+ cd <%= paths.mobile %> && flutter test
87
87
  <% } %>
88
88
  <% if (components.includes('e2e')) { %>
89
- cd e2e && npx playwright test
89
+ cd <%= paths.e2e %> && npx playwright test
90
90
  <% } %>
91
91
  ```
92
92
 
@@ -95,3 +95,7 @@ cd e2e && npx playwright test
95
95
  ```bash
96
96
  npx create-projx@latest update
97
97
  ```
98
+
99
+ ---
100
+
101
+ Scaffolded with [create-projx](https://github.com/ukanhaupa/projx)
@@ -7,13 +7,70 @@ on:
7
7
  branches: [main]
8
8
 
9
9
  jobs:
10
+ changes:
11
+ name: Detect changes
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ pull-requests: read
15
+ outputs:
16
+ <% if (components.includes('fastapi')) { %>
17
+ fastapi: ${{ steps.filter.outputs.<%= paths.fastapi %> }}
18
+ <% } %>
19
+ <% if (components.includes('fastify')) { %>
20
+ fastify: ${{ steps.filter.outputs.<%= paths.fastify %> }}
21
+ <% } %>
22
+ <% if (components.includes('frontend')) { %>
23
+ frontend: ${{ steps.filter.outputs.<%= paths.frontend %> }}
24
+ <% } %>
25
+ <% if (components.includes('mobile')) { %>
26
+ mobile: ${{ steps.filter.outputs.<%= paths.mobile %> }}
27
+ <% } %>
28
+ <% if (components.includes('e2e')) { %>
29
+ e2e: ${{ steps.filter.outputs.<%= paths.e2e %> }}
30
+ <% } %>
31
+ <% if (components.includes('infra')) { %>
32
+ infra: ${{ steps.filter.outputs.<%= paths.infra %> }}
33
+ <% } %>
34
+ steps:
35
+ - uses: actions/checkout@v5
36
+ - uses: dorny/paths-filter@v3
37
+ id: filter
38
+ with:
39
+ filters: |
40
+ <% if (components.includes('fastapi')) { %>
41
+ <%= paths.fastapi %>:
42
+ - '<%= paths.fastapi %>/**'
43
+ <% } %>
44
+ <% if (components.includes('fastify')) { %>
45
+ <%= paths.fastify %>:
46
+ - '<%= paths.fastify %>/**'
47
+ <% } %>
48
+ <% if (components.includes('frontend')) { %>
49
+ <%= paths.frontend %>:
50
+ - '<%= paths.frontend %>/**'
51
+ <% } %>
52
+ <% if (components.includes('mobile')) { %>
53
+ <%= paths.mobile %>:
54
+ - '<%= paths.mobile %>/**'
55
+ <% } %>
56
+ <% if (components.includes('e2e')) { %>
57
+ <%= paths.e2e %>:
58
+ - '<%= paths.e2e %>/**'
59
+ <% } %>
60
+ <% if (components.includes('infra')) { %>
61
+ <%= paths.infra %>:
62
+ - '<%= paths.infra %>/**'
63
+ <% } %>
10
64
  <% if (components.includes('fastapi')) { %>
65
+
11
66
  fastapi:
12
67
  name: FastAPI (format + lint)
68
+ needs: changes
69
+ if: needs.changes.outputs.fastapi == 'true'
13
70
  runs-on: ubuntu-latest
14
71
  defaults:
15
72
  run:
16
- working-directory: fastapi
73
+ working-directory: <%= paths.fastapi %>
17
74
  steps:
18
75
  - uses: actions/checkout@v5
19
76
  - uses: astral-sh/setup-uv@v4
@@ -22,12 +79,15 @@ jobs:
22
79
  - run: uv run ruff check src tests
23
80
  <% } %>
24
81
  <% if (components.includes('fastify')) { %>
82
+
25
83
  fastify:
26
84
  name: Fastify (format + lint + typecheck)
85
+ needs: changes
86
+ if: needs.changes.outputs.fastify == 'true'
27
87
  runs-on: ubuntu-latest
28
88
  defaults:
29
89
  run:
30
- working-directory: fastify
90
+ working-directory: <%= paths.fastify %>
31
91
  steps:
32
92
  - uses: actions/checkout@v5
33
93
  - uses: pnpm/action-setup@v4
@@ -37,7 +97,7 @@ jobs:
37
97
  with:
38
98
  node-version: 20
39
99
  cache: pnpm
40
- cache-dependency-path: fastify/pnpm-lock.yaml
100
+ cache-dependency-path: <%= paths.fastify %>/pnpm-lock.yaml
41
101
  - run: pnpm install --frozen-lockfile
42
102
  - run: npx prisma generate
43
103
  - run: npx prettier --check .
@@ -45,31 +105,37 @@ jobs:
45
105
  - run: npx tsc --noEmit
46
106
  <% } %>
47
107
  <% if (components.includes('frontend')) { %>
108
+
48
109
  frontend:
49
110
  name: Frontend (format + lint + typecheck)
111
+ needs: changes
112
+ if: needs.changes.outputs.frontend == 'true'
50
113
  runs-on: ubuntu-latest
51
114
  defaults:
52
115
  run:
53
- working-directory: frontend
116
+ working-directory: <%= paths.frontend %>
54
117
  steps:
55
118
  - uses: actions/checkout@v5
56
119
  - uses: actions/setup-node@v5
57
120
  with:
58
121
  node-version: 22
59
122
  cache: npm
60
- cache-dependency-path: frontend/package-lock.json
123
+ cache-dependency-path: <%= paths.frontend %>/package-lock.json
61
124
  - run: npm ci
62
125
  - run: npx prettier --check .
63
126
  - run: npx eslint 'src/**/*.{ts,tsx}'
64
127
  - run: npx tsc --noEmit
65
128
  <% } %>
66
129
  <% if (components.includes('mobile')) { %>
130
+
67
131
  mobile:
68
132
  name: Flutter (format + analyze)
133
+ needs: changes
134
+ if: needs.changes.outputs.mobile == 'true'
69
135
  runs-on: ubuntu-latest
70
136
  defaults:
71
137
  run:
72
- working-directory: mobile
138
+ working-directory: <%= paths.mobile %>
73
139
  steps:
74
140
  - uses: actions/checkout@v5
75
141
  - uses: subosito/flutter-action@v2
@@ -81,31 +147,37 @@ jobs:
81
147
  - run: dart analyze --fatal-infos
82
148
  <% } %>
83
149
  <% if (components.includes('e2e')) { %>
150
+
84
151
  e2e:
85
152
  name: E2E (format + lint + typecheck)
153
+ needs: changes
154
+ if: needs.changes.outputs.e2e == 'true'
86
155
  runs-on: ubuntu-latest
87
156
  defaults:
88
157
  run:
89
- working-directory: e2e
158
+ working-directory: <%= paths.e2e %>
90
159
  steps:
91
160
  - uses: actions/checkout@v5
92
161
  - uses: actions/setup-node@v5
93
162
  with:
94
163
  node-version: 22
95
164
  cache: npm
96
- cache-dependency-path: e2e/package-lock.json
165
+ cache-dependency-path: <%= paths.e2e %>/package-lock.json
97
166
  - run: npm ci
98
167
  - run: npx prettier --check .
99
168
  - run: npx eslint '**/*.ts'
100
169
  - run: npx tsc --noEmit
101
170
  <% } %>
102
171
  <% if (components.includes('infra')) { %>
172
+
103
173
  infra:
104
174
  name: Terraform (fmt + validate)
175
+ needs: changes
176
+ if: needs.changes.outputs.infra == 'true'
105
177
  runs-on: ubuntu-latest
106
178
  defaults:
107
179
  run:
108
- working-directory: infra/stack
180
+ working-directory: <%= paths.infra %>/stack
109
181
  steps:
110
182
  - uses: actions/checkout@v5
111
183
  - uses: hashicorp/setup-terraform@v3
@@ -25,7 +25,7 @@ services:
25
25
  <% } %>
26
26
  <% if (components.includes('fastapi')) { %>
27
27
  migrate:
28
- build: ./fastapi
28
+ build: ./<%= paths.fastapi %>
29
29
  command: ['uv', 'run', 'migrate.py']
30
30
  environment:
31
31
  - SQLALCHEMY_DATABASE_URI=postgresql+asyncpg://dev:dev@db:5432/app
@@ -41,7 +41,7 @@ services:
41
41
  - app-network
42
42
 
43
43
  backend:
44
- build: ./fastapi
44
+ build: ./<%= paths.fastapi %>
45
45
  command:
46
46
  [
47
47
  'uv', 'run', 'uvicorn', 'src.app:app',
@@ -57,9 +57,9 @@ services:
57
57
  - JWT_SECRET=dev-secret-that-is-at-least-32-bytes-long
58
58
  - JWT_ALGORITHMS=HS256
59
59
  volumes:
60
- - ./fastapi/src:/app/src
61
- - ./fastapi/alembic.ini:/app/alembic.ini
62
- - ./fastapi/migrate.py:/app/migrate.py
60
+ - ./<%= paths.fastapi %>/src:/app/src
61
+ - ./<%= paths.fastapi %>/alembic.ini:/app/alembic.ini
62
+ - ./<%= paths.fastapi %>/migrate.py:/app/migrate.py
63
63
  depends_on:
64
64
  migrate:
65
65
  condition: service_completed_successfully
@@ -84,7 +84,7 @@ services:
84
84
  <% } %>
85
85
  <% if (components.includes('fastify')) { %>
86
86
  backend-fastify:
87
- build: ./fastify
87
+ build: ./<%= paths.fastify %>
88
88
  command: ['pnpm', 'dev']
89
89
  ports:
90
90
  - '3000:3000'
@@ -94,7 +94,7 @@ services:
94
94
  - JWT_PROVIDER=shared_secret
95
95
  - JWT_SECRET=dev-secret-that-is-at-least-32-bytes-long
96
96
  volumes:
97
- - ./fastify/src:/app/src
97
+ - ./<%= paths.fastify %>/src:/app/src
98
98
  depends_on:
99
99
  db:
100
100
  condition: service_healthy
@@ -128,7 +128,7 @@ services:
128
128
  - VITE_API_URL=http://localhost:3000
129
129
  <% } %>
130
130
  volumes:
131
- - ./frontend:/app
131
+ - ./<%= paths.frontend %>:/app
132
132
  - frontend_node_modules:/app/node_modules
133
133
  <% if (components.includes('fastapi')) { %>
134
134
  depends_on:
@@ -1,10 +1,10 @@
1
1
  services:
2
2
  <% if (components.includes('fastapi')) { %>
3
3
  migrate:
4
- build: ./fastapi
4
+ build: ./<%= paths.fastapi %>
5
5
  command: ['uv', 'run', 'migrate.py']
6
6
  env_file:
7
- - ./fastapi/.env
7
+ - ./<%= paths.fastapi %>/.env
8
8
  deploy:
9
9
  resources:
10
10
  limits:
@@ -14,11 +14,11 @@ services:
14
14
  - app-network
15
15
 
16
16
  backend:
17
- build: ./fastapi
17
+ build: ./<%= paths.fastapi %>
18
18
  expose:
19
19
  - '7860'
20
20
  env_file:
21
- - ./fastapi/.env
21
+ - ./<%= paths.fastapi %>/.env
22
22
  restart: unless-stopped
23
23
  depends_on:
24
24
  migrate:
@@ -52,11 +52,11 @@ services:
52
52
  <% } %>
53
53
  <% if (components.includes('fastify')) { %>
54
54
  backend-fastify:
55
- build: ./fastify
55
+ build: ./<%= paths.fastify %>
56
56
  expose:
57
57
  - '3000'
58
58
  env_file:
59
- - ./fastify/.env
59
+ - ./<%= paths.fastify %>/.env
60
60
  restart: unless-stopped
61
61
  healthcheck:
62
62
  test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3000/api/health']
@@ -77,7 +77,7 @@ services:
77
77
  <% if (components.includes('frontend')) { %>
78
78
  frontend:
79
79
  build:
80
- context: ./frontend
80
+ context: ./<%= paths.frontend %>
81
81
  args:
82
82
  VITE_API_URL: ''
83
83
  ports:
@@ -29,26 +29,26 @@ fi
29
29
  STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
30
30
  <% if (components.includes('fastapi')) { %>
31
31
 
32
- FASTAPI_PY=$(echo "$STAGED_FILES" | grep '^fastapi/.*\.py$' || true)
32
+ FASTAPI_PY=$(echo "$STAGED_FILES" | grep '^<%= paths.fastapi %>/.*\.py$' || true)
33
33
  if [ -n "$FASTAPI_PY" ]; then
34
- echo "Formatting & linting fastapi..."
35
- cd fastapi
36
- echo "$FASTAPI_PY" | sed 's|^fastapi/||' | xargs uv run ruff format
37
- echo "$FASTAPI_PY" | sed 's|^fastapi/||' | xargs uv run ruff check --fix
34
+ echo "Formatting & linting <%= paths.fastapi %>..."
35
+ cd <%= paths.fastapi %>
36
+ echo "$FASTAPI_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff format
37
+ echo "$FASTAPI_PY" | sed 's|^<%= paths.fastapi %>/||' | xargs uv run ruff check --fix
38
38
  cd ..
39
39
  echo "$FASTAPI_PY" | xargs git add
40
40
  fi
41
41
  <% } %>
42
42
  <% if (components.includes('fastify')) { %>
43
43
 
44
- FASTIFY_TS=$(echo "$STAGED_FILES" | grep '^fastify/.*\.ts$' || true)
45
- FASTIFY_ALL=$(echo "$STAGED_FILES" | grep '^fastify/' || true)
44
+ FASTIFY_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.fastify %>/.*\.ts$' || true)
45
+ FASTIFY_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.fastify %>/' || true)
46
46
  if [ -n "$FASTIFY_ALL" ]; then
47
- echo "Formatting fastify..."
48
- cd fastify
49
- echo "$FASTIFY_ALL" | sed 's|^fastify/||' | xargs npx prettier --write --ignore-unknown
47
+ echo "Formatting <%= paths.fastify %>..."
48
+ cd <%= paths.fastify %>
49
+ echo "$FASTIFY_ALL" | sed 's|^<%= paths.fastify %>/||' | xargs npx prettier --write --ignore-unknown
50
50
  if [ -n "$FASTIFY_TS" ]; then
51
- echo "$FASTIFY_TS" | sed 's|^fastify/||' | xargs npx eslint --fix
51
+ echo "$FASTIFY_TS" | sed 's|^<%= paths.fastify %>/||' | xargs npx eslint --fix
52
52
  npx tsc --noEmit
53
53
  fi
54
54
  cd ..
@@ -57,14 +57,14 @@ fi
57
57
  <% } %>
58
58
  <% if (components.includes('frontend')) { %>
59
59
 
60
- FRONTEND_TS=$(echo "$STAGED_FILES" | grep '^frontend/.*\.tsx\?$' || true)
61
- FRONTEND_ALL=$(echo "$STAGED_FILES" | grep '^frontend/' || true)
60
+ FRONTEND_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.frontend %>/.*\.tsx\?$' || true)
61
+ FRONTEND_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.frontend %>/' || true)
62
62
  if [ -n "$FRONTEND_ALL" ]; then
63
- echo "Formatting frontend..."
64
- cd frontend
65
- echo "$FRONTEND_ALL" | sed 's|^frontend/||' | xargs npx prettier --write --ignore-unknown
63
+ echo "Formatting <%= paths.frontend %>..."
64
+ cd <%= paths.frontend %>
65
+ echo "$FRONTEND_ALL" | sed 's|^<%= paths.frontend %>/||' | xargs npx prettier --write --ignore-unknown
66
66
  if [ -n "$FRONTEND_TS" ]; then
67
- echo "$FRONTEND_TS" | sed 's|^frontend/||' | xargs npx eslint --fix
67
+ echo "$FRONTEND_TS" | sed 's|^<%= paths.frontend %>/||' | xargs npx eslint --fix
68
68
  npx tsc --noEmit
69
69
  fi
70
70
  cd ..
@@ -73,14 +73,14 @@ fi
73
73
  <% } %>
74
74
  <% if (components.includes('e2e')) { %>
75
75
 
76
- E2E_TS=$(echo "$STAGED_FILES" | grep '^e2e/.*\.ts$' || true)
77
- E2E_ALL=$(echo "$STAGED_FILES" | grep '^e2e/' || true)
76
+ E2E_TS=$(echo "$STAGED_FILES" | grep '^<%= paths.e2e %>/.*\.ts$' || true)
77
+ E2E_ALL=$(echo "$STAGED_FILES" | grep '^<%= paths.e2e %>/' || true)
78
78
  if [ -n "$E2E_ALL" ]; then
79
- echo "Formatting e2e..."
80
- cd e2e
81
- echo "$E2E_ALL" | sed 's|^e2e/||' | xargs npx prettier --write --ignore-unknown
79
+ echo "Formatting <%= paths.e2e %>..."
80
+ cd <%= paths.e2e %>
81
+ echo "$E2E_ALL" | sed 's|^<%= paths.e2e %>/||' | xargs npx prettier --write --ignore-unknown
82
82
  if [ -n "$E2E_TS" ]; then
83
- echo "$E2E_TS" | sed 's|^e2e/||' | xargs npx eslint --fix
83
+ echo "$E2E_TS" | sed 's|^<%= paths.e2e %>/||' | xargs npx eslint --fix
84
84
  npx tsc --noEmit
85
85
  fi
86
86
  cd ..
@@ -89,12 +89,12 @@ fi
89
89
  <% } %>
90
90
  <% if (components.includes('mobile')) { %>
91
91
 
92
- MOBILE_DART=$(echo "$STAGED_FILES" | grep '^mobile/.*\.dart$' || true)
92
+ MOBILE_DART=$(echo "$STAGED_FILES" | grep '^<%= paths.mobile %>/.*\.dart$' || true)
93
93
  if [ -n "$MOBILE_DART" ]; then
94
94
  if command -v dart &> /dev/null; then
95
- echo "Formatting mobile..."
96
- cd mobile
97
- echo "$MOBILE_DART" | sed 's|^mobile/||' | xargs dart format
95
+ echo "Formatting <%= paths.mobile %>..."
96
+ cd <%= paths.mobile %>
97
+ echo "$MOBILE_DART" | sed 's|^<%= paths.mobile %>/||' | xargs dart format
98
98
  if command -v flutter &> /dev/null; then
99
99
  dart analyze --fatal-infos
100
100
  fi
@@ -107,12 +107,12 @@ fi
107
107
  <% } %>
108
108
  <% if (components.includes('infra')) { %>
109
109
 
110
- INFRA_TF=$(echo "$STAGED_FILES" | grep '^infra/.*\.tf$' || true)
110
+ INFRA_TF=$(echo "$STAGED_FILES" | grep '^<%= paths.infra %>/.*\.tf$' || true)
111
111
  if [ -n "$INFRA_TF" ]; then
112
112
  if command -v terraform &> /dev/null; then
113
- echo "Formatting infra..."
114
- cd infra/stack
115
- echo "$INFRA_TF" | sed 's|^infra/stack/||' | xargs terraform fmt
113
+ echo "Formatting <%= paths.infra %>..."
114
+ cd <%= paths.infra %>/stack
115
+ echo "$INFRA_TF" | sed 's|^<%= paths.infra %>/stack/||' | xargs terraform fmt
116
116
  cd ../..
117
117
  echo "$INFRA_TF" | xargs git add
118
118
  else
@@ -5,28 +5,28 @@ git config core.hooksPath .githooks
5
5
  echo "Git hooks configured."
6
6
  <% if (components.includes('fastapi')) { %>
7
7
 
8
- cd fastapi && uv sync --all-extras && cd ..
8
+ cd <%= paths.fastapi %> && uv sync --all-extras && cd ..
9
9
  echo "FastAPI dependencies installed."
10
10
  <% } %>
11
11
  <% if (components.includes('fastify')) { %>
12
12
 
13
- cd fastify && pnpm install --frozen-lockfile && cd ..
13
+ cd <%= paths.fastify %> && pnpm install --frozen-lockfile && cd ..
14
14
  echo "Fastify dependencies installed."
15
15
  <% } %>
16
16
  <% if (components.includes('frontend')) { %>
17
17
 
18
- cd frontend && npm ci && cd ..
18
+ cd <%= paths.frontend %> && npm ci && cd ..
19
19
  echo "Frontend dependencies installed."
20
20
  <% } %>
21
21
  <% if (components.includes('e2e')) { %>
22
22
 
23
- cd e2e && npm ci && cd ..
23
+ cd <%= paths.e2e %> && npm ci && cd ..
24
24
  echo "E2E dependencies installed."
25
25
  <% } %>
26
26
  <% if (components.includes('mobile')) { %>
27
27
 
28
28
  if command -v flutter &>/dev/null; then
29
- cd mobile && flutter pub get && cd ..
29
+ cd <%= paths.mobile %> && flutter pub get && cd ..
30
30
  echo "Flutter dependencies installed."
31
31
  else
32
32
  echo "Flutter SDK not installed — skipping mobile."