@highbeek/create-rnstarterkit 1.0.2-beta.7 → 1.1.0

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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/dist/bin/create-rnstarterkit.js +205 -7
  4. package/dist/src/generators/appGenerator.js +1944 -127
  5. package/dist/src/generators/codeGenerator.js +289 -0
  6. package/dist/templates/cli-base/App.tsx +199 -21
  7. package/dist/templates/cli-base/assets/images/icon.png +0 -0
  8. package/dist/templates/cli-base/assets/images/partial-react-logo.png +0 -0
  9. package/dist/templates/cli-base/assets/images/react-logo.png +0 -0
  10. package/dist/templates/cli-base/babel.config.js +1 -0
  11. package/dist/templates/cli-base/ios/BaseApp.xcodeproj/project.pbxproj +6 -0
  12. package/dist/templates/cli-base/ios/Podfile +5 -0
  13. package/dist/templates/cli-base/jest.config.js +4 -0
  14. package/dist/templates/cli-base/package.json +7 -4
  15. package/dist/templates/cli-base/tsconfig.json +1 -0
  16. package/dist/templates/expo-base/app/_layout.tsx +7 -3
  17. package/dist/templates/expo-base/app/home.tsx +37 -0
  18. package/dist/templates/expo-base/app/index.tsx +166 -5
  19. package/dist/templates/expo-base/app.json +1 -2
  20. package/dist/templates/expo-base/package.json +5 -2
  21. package/dist/templates/optional/auth-context/components/layout/ScreenLayout.tsx +46 -0
  22. package/dist/templates/optional/auth-context/navigation/ProtectedStack.tsx +33 -5
  23. package/dist/templates/optional/auth-context/screens/HomeScreen.tsx +4 -3
  24. package/dist/templates/optional/auth-context/screens/LoginScreen.tsx +41 -6
  25. package/dist/templates/optional/auth-context/screens/ProfileScreen.tsx +4 -3
  26. package/dist/templates/optional/auth-context/screens/RegisterScreen.tsx +41 -6
  27. package/dist/templates/optional/auth-context/screens/SettingsScreen.tsx +4 -3
  28. package/dist/templates/optional/auth-context/screens/WelcomeScreen.tsx +174 -0
  29. package/dist/templates/optional/auth-redux/components/layout/ScreenLayout.tsx +46 -0
  30. package/dist/templates/optional/auth-redux/navigation/ProtectedStack.tsx +39 -2
  31. package/dist/templates/optional/auth-redux/screens/HomeScreen.tsx +4 -3
  32. package/dist/templates/optional/auth-redux/screens/LoginScreen.tsx +42 -7
  33. package/dist/templates/optional/auth-redux/screens/ProfileScreen.tsx +7 -10
  34. package/dist/templates/optional/auth-redux/screens/RegisterScreen.tsx +61 -11
  35. package/dist/templates/optional/auth-redux/screens/SettingsScreen.tsx +6 -9
  36. package/dist/templates/optional/auth-redux/screens/WelcomeScreen.tsx +174 -0
  37. package/dist/templates/optional/auth-zustand/components/layout/ScreenLayout.tsx +46 -0
  38. package/dist/templates/optional/auth-zustand/navigation/ProtectedStack.tsx +34 -6
  39. package/dist/templates/optional/auth-zustand/screens/HomeScreen.tsx +4 -3
  40. package/dist/templates/optional/auth-zustand/screens/LoginScreen.tsx +41 -6
  41. package/dist/templates/optional/auth-zustand/screens/ProfileScreen.tsx +4 -3
  42. package/dist/templates/optional/auth-zustand/screens/RegisterScreen.tsx +41 -6
  43. package/dist/templates/optional/auth-zustand/screens/SettingsScreen.tsx +4 -3
  44. package/dist/templates/optional/auth-zustand/screens/WelcomeScreen.tsx +174 -0
  45. package/dist/templates/optional/ci/.github/workflows/ci.yml +32 -0
  46. package/dist/templates/optional/error-boundary/components/ErrorBoundary.tsx +83 -0
  47. package/dist/templates/optional/formik/components/formik/FormikInput.tsx +45 -0
  48. package/dist/templates/optional/formik/components/formik/LoginForm.tsx +60 -0
  49. package/dist/templates/optional/formik/schemas/auth.schema.ts +17 -0
  50. package/dist/templates/optional/i18n/src/i18n/hooks/useAppTranslation.ts +28 -0
  51. package/dist/templates/optional/i18n/src/i18n/i18n.ts +30 -0
  52. package/dist/templates/optional/i18n/src/i18n/locales/en.json +32 -0
  53. package/dist/templates/optional/i18n/src/i18n/locales/es.json +32 -0
  54. package/dist/templates/optional/maestro/.maestro/flows/01_welcome.yaml +5 -0
  55. package/dist/templates/optional/maestro-auth/.maestro/flows/01_welcome.yaml +5 -0
  56. package/dist/templates/optional/maestro-auth/.maestro/flows/02_login.yaml +13 -0
  57. package/dist/templates/optional/maestro-auth/.maestro/flows/03_logout.yaml +16 -0
  58. package/dist/templates/optional/mmkv/utils/storage.ts +17 -0
  59. package/dist/templates/optional/react-hook-form/components/rhf/LoginForm.tsx +63 -0
  60. package/dist/templates/optional/react-hook-form/components/rhf/RHFInput.tsx +50 -0
  61. package/dist/templates/optional/react-hook-form/schemas/auth.schema.ts +29 -0
  62. package/dist/templates/optional/react-query/hooks/useAppMutation.ts +16 -0
  63. package/dist/templates/optional/react-query/hooks/useAppQuery.ts +12 -0
  64. package/dist/templates/optional/react-query/services/queryClient.ts +14 -0
  65. package/dist/templates/optional/redux/store/hooks.ts +6 -0
  66. package/dist/templates/optional/redux/store/store.ts +11 -0
  67. package/dist/templates/optional/sentry/src/utils/sentry.ts +24 -0
  68. package/dist/templates/optional/swr/hooks/useSWRFetch.ts +14 -0
  69. package/dist/templates/optional/swr/providers/SWRProvider.tsx +21 -0
  70. package/dist/templates/optional/tsconfig.json +17 -0
  71. package/dist/templates/optional/zustand/store/appStore.ts +13 -0
  72. package/package.json +40 -5
  73. package/dist/templates/expo-base/components/ui/collapsible.tsx +0 -45
  74. package/dist/templates/expo-base/components/ui/icon-symbol.ios.tsx +0 -32
  75. package/dist/templates/expo-base/components/ui/icon-symbol.tsx +0 -41
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ibukun Agboola
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -0,0 +1,268 @@
1
+ # RNStarterKit
2
+
3
+ **A CLI that scaffolds production-ready React Native apps with clean architecture and sensible defaults.**
4
+
5
+ Not a replacement for Expo or React Native CLI — it sits on top of them and structures your project correctly from day one.
6
+
7
+ ```bash
8
+ npx @highbeek/create-rnstarterkit myApp
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Why This Exists
14
+
15
+ Every serious React Native project ends up making the same decisions:
16
+
17
+ - Where do API calls live?
18
+ - How is auth state managed and persisted?
19
+ - What's the folder structure when the app scales?
20
+ - How are environment variables typed?
21
+
22
+ This tool answers those questions upfront. You get a complete, typed, linted project with your chosen stack — ready to build on, not fight against.
23
+
24
+ ---
25
+
26
+ ## Quick Start
27
+
28
+ ```bash
29
+ npx @highbeek/create-rnstarterkit myApp
30
+
31
+ # Skip prompts with a preset
32
+ npx @highbeek/create-rnstarterkit myApp --preset fintech
33
+ npx @highbeek/create-rnstarterkit myApp --preset social
34
+ npx @highbeek/create-rnstarterkit myApp --preset indie
35
+ npx @highbeek/create-rnstarterkit myApp --preset minimal
36
+ ```
37
+
38
+ ### After scaffolding — React Native CLI projects
39
+
40
+ Native dependencies need linking via CocoaPods before you can run on iOS:
41
+
42
+ ```bash
43
+ cd myApp
44
+ npm install
45
+ cd ios && pod install && cd ..
46
+
47
+ # Then run
48
+ npx react-native run-ios
49
+ npx react-native run-android
50
+ ```
51
+
52
+ ### After scaffolding — Expo projects
53
+
54
+ ```bash
55
+ cd myApp
56
+ npx expo start
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Presets
62
+
63
+ Presets bundle the most common stack combinations so you can skip all prompts:
64
+
65
+ | Preset | Platform | State | Auth | Data | Storage | Extras |
66
+ |---|---|---|---|---|---|---|
67
+ | `minimal` | Expo | None | No | None | None | Bare structure |
68
+ | `indie` | Expo | Context API | Yes | React Query | AsyncStorage | Husky |
69
+ | `social` | Expo | Zustand | Yes | SWR | AsyncStorage | Sentry, i18n, CI |
70
+ | `fintech` | RN CLI | Redux Toolkit | Yes | React Query | MMKV | Sentry, i18n, Maestro, CI |
71
+
72
+ ---
73
+
74
+ ## Interactive Setup
75
+
76
+ When run without a preset, the CLI walks you through your stack:
77
+
78
+ ```
79
+ ? Project name:
80
+ ? Platform: Expo / React Native CLI
81
+ ? TypeScript: Yes / No
82
+ ? Absolute imports (@/*): Yes / No
83
+ ? State management: None / Context API / Redux Toolkit / Zustand
84
+ ? Data fetching: None / React Query / SWR
85
+ ? Validation: Formik / React Hook Form / Yup
86
+ ? Storage: AsyncStorage / MMKV / None
87
+ ? Auth scaffold: Yes / No
88
+ ? API client: Yes / No → Fetch / Axios
89
+ ? Sentry: Yes / No
90
+ ? i18n (i18next): Yes / No
91
+ ? Maestro E2E flows: Yes / No
92
+ ? NativeWind (Tailwind): Yes / No
93
+ ? CI (GitHub Actions): Yes / No
94
+ ? Husky pre-commit hooks: Yes / No
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Generator Commands
100
+
101
+ After scaffolding, use the `generate` subcommand to add code that matches your project's setup:
102
+
103
+ ```bash
104
+ npx @highbeek/create-rnstarterkit generate screen Dashboard
105
+ npx @highbeek/create-rnstarterkit generate component Avatar
106
+ npx @highbeek/create-rnstarterkit generate hook useDebounce
107
+ npx @highbeek/create-rnstarterkit generate slice cart
108
+ ```
109
+
110
+ Generators are context-aware — they read your project's config from `package.json` and produce files with the correct imports for your state manager, platform, and auth setup.
111
+
112
+ | Command | Output |
113
+ |---|---|
114
+ | `generate screen Name` | `src/screens/NameScreen.tsx` with correct state hook |
115
+ | `generate component Name` | `src/components/Name.tsx` with typed props + StyleSheet |
116
+ | `generate hook useName` | `src/hooks/useName.ts` with loading/error boilerplate |
117
+ | `generate slice name` | `src/store/nameSlice.ts` + auto-patches `store.ts` |
118
+
119
+ > `generate slice` only works in Redux Toolkit projects. It automatically adds the reducer to your store.
120
+
121
+ ---
122
+
123
+ ## What Gets Generated
124
+
125
+ ```
126
+ <project>/
127
+ ├── src/
128
+ │ ├── api/ # API client + endpoints
129
+ │ ├── components/ # Shared UI components + ErrorBoundary
130
+ │ ├── config/ # Typed env config
131
+ │ ├── context/ # Context API providers (if selected)
132
+ │ ├── hooks/ # Shared custom hooks
133
+ │ ├── i18n/ # i18next config + locales (if selected)
134
+ │ ├── navigation/ # Navigation setup (RN CLI)
135
+ │ ├── providers/ # App-level providers
136
+ │ ├── screens/ # App screens
137
+ │ ├── services/ # Query clients, external services
138
+ │ ├── store/ # Redux / Zustand store (if selected)
139
+ │ ├── theme/ # Color tokens, spacing, typography
140
+ │ ├── types/ # Shared TypeScript types
141
+ │ └── utils/ # Shared utilities + Sentry (if selected)
142
+ ├── .env.example # Environment variable template
143
+ ├── .husky/ # Pre-commit hooks (if selected)
144
+ ├── .maestro/ # E2E flows (if selected)
145
+ ├── .vscode/ # Editor config + extension recommendations
146
+ └── .github/workflows/ # CI pipeline (if selected)
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Optional Modules
152
+
153
+ ### Auth Scaffold
154
+
155
+ Three implementations — all include Login/Register screens, protected routes, token persistence, and a ScreenLayout wrapper:
156
+
157
+ | Option | State | Best For |
158
+ |---|---|---|
159
+ | Context API | Built-in React | Most apps |
160
+ | Redux Toolkit | `@reduxjs/toolkit` | Complex global state |
161
+ | Zustand | `zustand` | Minimal, no boilerplate |
162
+
163
+ ### API Client
164
+
165
+ | Option | Description |
166
+ |---|---|
167
+ | Fetch | Zero deps — typed wrapper with request/response interceptors |
168
+ | Axios | Full Axios instance with error normalisation + typed helpers |
169
+
170
+ ### Data Fetching
171
+
172
+ | Option | Setup |
173
+ |---|---|
174
+ | React Query | QueryClient pre-configured with retry + stale time defaults |
175
+ | SWR | SWRConfig provider + typed `useSWRFetch` hook |
176
+
177
+ ### Storage
178
+
179
+ | Option | Description |
180
+ |---|---|
181
+ | AsyncStorage | Default — async token persistence |
182
+ | MMKV | Synchronous, 10x faster, drop-in replacement |
183
+
184
+ ### NativeWind (Tailwind CSS)
185
+
186
+ Adds `nativewind` + `tailwindcss` and wires everything up for your platform:
187
+
188
+ | Step | Expo | React Native CLI |
189
+ |---|---|---|
190
+ | `tailwind.config.js` | content globs for `app/` + `src/` | content globs for `App.tsx` + `src/` |
191
+ | `global.css` | created, imported in `app/_layout.tsx` | created, imported in `index.js` |
192
+ | `metro.config.js` | wrapped with `withNativeWind` (expo/metro-config) | wrapped with `withNativeWind` (@react-native/metro-config) |
193
+ | `babel.config.js` | `jsxImportSource: 'nativewind'` in babel-preset-expo | `nativewind/babel` plugin added |
194
+ | `tsconfig.json` | `"nativewind/types"` added to types | same |
195
+
196
+ After generation, use `className` props directly on React Native components — no extra setup needed.
197
+
198
+ ### Sentry
199
+
200
+ Adds `@sentry/react-native`, initialises in the app entry point, and exports `captureException` + `addBreadcrumb` helpers. The DSN is read from your environment variables — set `EXPO_PUBLIC_SENTRY_DSN` (Expo) or `SENTRY_DSN` (RN CLI) in your `.env` file.
201
+
202
+ ### i18n
203
+
204
+ Adds `i18next` + `react-i18next` with:
205
+ - Language auto-detection via `react-native-localize`
206
+ - English + Spanish locale files as a starting point
207
+ - Typed `useAppTranslation` hook
208
+
209
+ ### Maestro E2E
210
+
211
+ Generates `.maestro/flows/` with welcome, login, and logout flows. Automatically adds a Maestro job to the CI workflow when both are selected.
212
+
213
+ ### CI
214
+
215
+ GitHub Actions workflow with lint and test jobs on push/PR to `main` and `develop`.
216
+
217
+ ---
218
+
219
+ ## Tooling (Always Included)
220
+
221
+ | Tool | Config |
222
+ |---|---|
223
+ | TypeScript | `strict: true`, platform-native tsconfig |
224
+ | ESLint | `@react-native` ruleset, `--max-warnings 0` |
225
+ | Prettier | `format` script wired to `package.json` |
226
+ | Jest | RTL setup, `transformIgnorePatterns` for RN modules |
227
+ | VSCode | `extensions.json` + `settings.json` (format on save) |
228
+ | `.env.example` | Environment variable template + `.gitignore` entry |
229
+ | Theme | `colors.ts`, `spacing.ts`, `typography.ts` tokens |
230
+ | ErrorBoundary | Class component with reset, ready to wrap your app |
231
+ | Navigation types | `AuthStackParamList`, `MainTabParamList` (RN CLI + auth) |
232
+
233
+ ---
234
+
235
+ ## Architecture
236
+
237
+ **Typed from the start.** TypeScript strict mode on by default. API responses, environment variables, and navigation params are all typed.
238
+
239
+ **Thin screens, fat hooks.** Screens handle UI. Business logic, API calls, and state live in hooks and services. Screens don't call APIs directly.
240
+
241
+ **Non-destructive generation.** `writeIfMissing` is used throughout — the generator never overwrites files you've already customised.
242
+
243
+ ---
244
+
245
+ ## Contributing
246
+
247
+ ```bash
248
+ git clone https://github.com/highbeek/rnstarterkit
249
+ cd rnstarterkit
250
+ npm install
251
+ npm run build
252
+ ```
253
+
254
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full development workflow, testing, and PR guidelines.
255
+
256
+ ---
257
+
258
+ ## Status
259
+
260
+ Current version: `1.0.0`
261
+
262
+ Core scaffolding, presets, and generators are stable. Feedback and contributions welcome.
263
+
264
+ ---
265
+
266
+ ## License
267
+
268
+ MIT
@@ -4,15 +4,95 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
7
8
  const inquirer_1 = __importDefault(require("inquirer"));
8
9
  const appGenerator_1 = require("../src/generators/appGenerator");
9
- async function main() {
10
- console.log("🚀 Welcome to RNStarterKit!");
10
+ const codeGenerator_1 = require("../src/generators/codeGenerator");
11
+ // ---------------------------------------------------------------------------
12
+ // Presets — opinionated bundles that skip the 12-question prompt
13
+ // ---------------------------------------------------------------------------
14
+ const PRESETS = {
15
+ minimal: {
16
+ typescript: true,
17
+ absoluteImports: false,
18
+ state: "None",
19
+ auth: false,
20
+ dataFetching: "None",
21
+ validation: [],
22
+ storage: "None",
23
+ apiClient: false,
24
+ ci: false,
25
+ husky: false,
26
+ sentry: false,
27
+ i18n: false,
28
+ maestro: false,
29
+ nativewind: false,
30
+ },
31
+ fintech: {
32
+ platform: "React Native CLI",
33
+ typescript: true,
34
+ absoluteImports: true,
35
+ state: "Redux Toolkit",
36
+ auth: true,
37
+ dataFetching: "React Query",
38
+ validation: [],
39
+ storage: "MMKV",
40
+ apiClient: true,
41
+ apiClientType: "Fetch",
42
+ ci: true,
43
+ husky: true,
44
+ sentry: true,
45
+ i18n: true,
46
+ maestro: true,
47
+ nativewind: false,
48
+ },
49
+ social: {
50
+ platform: "Expo",
51
+ typescript: true,
52
+ absoluteImports: true,
53
+ state: "Zustand",
54
+ auth: true,
55
+ dataFetching: "SWR",
56
+ validation: [],
57
+ storage: "AsyncStorage",
58
+ apiClient: true,
59
+ apiClientType: "Fetch",
60
+ ci: true,
61
+ husky: true,
62
+ sentry: true,
63
+ i18n: true,
64
+ maestro: false,
65
+ nativewind: false,
66
+ },
67
+ indie: {
68
+ platform: "Expo",
69
+ typescript: true,
70
+ absoluteImports: true,
71
+ state: "Context API",
72
+ auth: true,
73
+ dataFetching: "React Query",
74
+ validation: [],
75
+ storage: "AsyncStorage",
76
+ apiClient: true,
77
+ apiClientType: "Fetch",
78
+ ci: false,
79
+ husky: true,
80
+ sentry: false,
81
+ i18n: false,
82
+ maestro: false,
83
+ nativewind: false,
84
+ },
85
+ };
86
+ // ---------------------------------------------------------------------------
87
+ // Interactive prompt
88
+ // ---------------------------------------------------------------------------
89
+ async function runInteractivePrompt(prefilledName, presetDefaults) {
11
90
  const answers = await inquirer_1.default.prompt([
12
91
  {
13
92
  type: "input",
14
93
  name: "projectName",
15
94
  message: "Project name:",
95
+ when: () => !prefilledName,
16
96
  validate: (input) => /^[A-Za-z0-9]+$/.test(input)
17
97
  ? true
18
98
  : "Project name must be alphanumeric",
@@ -22,18 +102,21 @@ async function main() {
22
102
  name: "platform",
23
103
  message: "Choose platform",
24
104
  choices: ["Expo", "React Native CLI"],
105
+ when: () => !presetDefaults?.platform,
25
106
  },
26
107
  {
27
108
  type: "confirm",
28
109
  name: "typescript",
29
110
  message: "Use TypeScript?",
30
111
  default: true,
112
+ when: () => presetDefaults?.typescript === undefined,
31
113
  },
32
114
  {
33
115
  type: "confirm",
34
116
  name: "absoluteImports",
35
117
  message: "Use absolute import alias (@/*)?",
36
118
  default: true,
119
+ when: () => presetDefaults?.absoluteImports === undefined,
37
120
  },
38
121
  {
39
122
  type: "rawlist",
@@ -41,6 +124,7 @@ async function main() {
41
124
  message: "State management?",
42
125
  choices: ["None", "Context API", "Redux Toolkit", "Zustand"],
43
126
  default: "Context API",
127
+ when: () => !presetDefaults?.state,
44
128
  },
45
129
  {
46
130
  type: "rawlist",
@@ -48,6 +132,7 @@ async function main() {
48
132
  message: "Data fetching library?",
49
133
  choices: ["None", "React Query", "SWR"],
50
134
  default: "None",
135
+ when: () => !presetDefaults?.dataFetching,
51
136
  },
52
137
  {
53
138
  type: "checkbox",
@@ -55,6 +140,7 @@ async function main() {
55
140
  message: "Validation libraries?",
56
141
  choices: ["Formik", "React Hook Form", "Yup"],
57
142
  default: [],
143
+ when: () => !presetDefaults?.validation?.length,
58
144
  validate: (selected) => {
59
145
  if (selected.includes("Formik") && !selected.includes("Yup")) {
60
146
  return "Formik requires Yup in this starter. Select Yup as well.";
@@ -68,18 +154,21 @@ async function main() {
68
154
  message: "Storage method?",
69
155
  choices: ["AsyncStorage", "MMKV", "None"],
70
156
  default: "AsyncStorage",
157
+ when: () => !presetDefaults?.storage,
71
158
  },
72
159
  {
73
160
  type: "confirm",
74
161
  name: "auth",
75
162
  message: "Include auth scaffold?",
76
163
  default: false,
164
+ when: () => presetDefaults?.auth === undefined,
77
165
  },
78
166
  {
79
167
  type: "confirm",
80
168
  name: "apiClient",
81
169
  message: "Include API client scaffold?",
82
170
  default: true,
171
+ when: () => presetDefaults?.apiClient === undefined,
83
172
  },
84
173
  {
85
174
  type: "rawlist",
@@ -87,19 +176,128 @@ async function main() {
87
176
  message: "API client transport?",
88
177
  choices: ["Fetch", "Axios"],
89
178
  default: "Fetch",
90
- when: (answers) => answers.apiClient,
179
+ when: (a) => presetDefaults?.apiClientType === undefined && a.apiClient,
180
+ },
181
+ {
182
+ type: "confirm",
183
+ name: "sentry",
184
+ message: "Include Sentry crash reporting?",
185
+ default: false,
186
+ when: () => presetDefaults?.sentry === undefined,
187
+ },
188
+ {
189
+ type: "confirm",
190
+ name: "i18n",
191
+ message: "Include i18n (i18next + react-i18next)?",
192
+ default: false,
193
+ when: () => presetDefaults?.i18n === undefined,
194
+ },
195
+ {
196
+ type: "confirm",
197
+ name: "maestro",
198
+ message: "Include Maestro E2E test flows?",
199
+ default: false,
200
+ when: () => presetDefaults?.maestro === undefined,
201
+ },
202
+ {
203
+ type: "confirm",
204
+ name: "nativewind",
205
+ message: "Use NativeWind (Tailwind CSS for React Native)?",
206
+ default: false,
207
+ when: () => presetDefaults?.nativewind === undefined,
91
208
  },
92
209
  {
93
210
  type: "confirm",
94
211
  name: "ci",
95
- message: "Include CI setup?",
212
+ message: "Include CI setup (GitHub Actions)?",
96
213
  default: false,
214
+ when: () => presetDefaults?.ci === undefined,
215
+ },
216
+ {
217
+ type: "confirm",
218
+ name: "husky",
219
+ message: "Set up Husky pre-commit hooks (lint-staged)?",
220
+ default: true,
221
+ when: () => presetDefaults?.husky === undefined,
97
222
  },
98
223
  ]);
99
- await (0, appGenerator_1.generateApp)(answers);
224
+ return {
225
+ projectName: prefilledName ?? answers.projectName,
226
+ platform: answers.platform ?? presetDefaults?.platform ?? "Expo",
227
+ typescript: answers.typescript ?? presetDefaults?.typescript ?? true,
228
+ absoluteImports: answers.absoluteImports ?? presetDefaults?.absoluteImports ?? true,
229
+ state: answers.state ?? presetDefaults?.state ?? "None",
230
+ auth: answers.auth ?? presetDefaults?.auth ?? false,
231
+ dataFetching: answers.dataFetching ?? presetDefaults?.dataFetching ?? "None",
232
+ validation: answers.validation ?? presetDefaults?.validation ?? [],
233
+ storage: answers.storage ?? presetDefaults?.storage ?? "AsyncStorage",
234
+ apiClient: answers.apiClient ?? presetDefaults?.apiClient ?? false,
235
+ apiClientType: answers.apiClientType ?? presetDefaults?.apiClientType,
236
+ sentry: answers.sentry ?? presetDefaults?.sentry ?? false,
237
+ i18n: answers.i18n ?? presetDefaults?.i18n ?? false,
238
+ maestro: answers.maestro ?? presetDefaults?.maestro ?? false,
239
+ nativewind: answers.nativewind ?? presetDefaults?.nativewind ?? false,
240
+ ci: answers.ci ?? presetDefaults?.ci ?? false,
241
+ husky: answers.husky ?? presetDefaults?.husky ?? true,
242
+ };
100
243
  }
101
- main().catch((error) => {
102
- console.error("❌ Failed to create project.");
244
+ // ---------------------------------------------------------------------------
245
+ // CLI wiring
246
+ // ---------------------------------------------------------------------------
247
+ const program = new commander_1.Command();
248
+ program
249
+ .name("create-rnstarterkit")
250
+ .description("Scaffold a production-ready React Native app")
251
+ .version("1.0.0")
252
+ .argument("[projectName]", "Name of the project (alphanumeric)")
253
+ .option("--preset <name>", "Skip prompts with a preset: minimal | fintech | social | indie")
254
+ .action(async (projectName, cmdOptions) => {
255
+ console.log("🚀 Welcome to RNStarterKit!");
256
+ const presetKey = cmdOptions.preset;
257
+ if (presetKey && !(presetKey in PRESETS)) {
258
+ console.error(`❌ Unknown preset "${presetKey}". Available: ${Object.keys(PRESETS).join(", ")}`);
259
+ process.exit(1);
260
+ }
261
+ const presetDefaults = presetKey ? PRESETS[presetKey] : undefined;
262
+ // Preset + name supplied → fully non-interactive
263
+ if (presetDefaults && projectName) {
264
+ const options = {
265
+ projectName,
266
+ platform: presetDefaults.platform ?? "Expo",
267
+ typescript: presetDefaults.typescript ?? true,
268
+ absoluteImports: presetDefaults.absoluteImports ?? true,
269
+ state: presetDefaults.state ?? "None",
270
+ auth: presetDefaults.auth ?? false,
271
+ dataFetching: presetDefaults.dataFetching ?? "None",
272
+ validation: presetDefaults.validation ?? [],
273
+ storage: presetDefaults.storage ?? "AsyncStorage",
274
+ apiClient: presetDefaults.apiClient ?? false,
275
+ apiClientType: presetDefaults.apiClientType,
276
+ sentry: presetDefaults.sentry ?? false,
277
+ i18n: presetDefaults.i18n ?? false,
278
+ maestro: presetDefaults.maestro ?? false,
279
+ nativewind: presetDefaults.nativewind ?? false,
280
+ ci: presetDefaults.ci ?? false,
281
+ husky: presetDefaults.husky ?? true,
282
+ };
283
+ console.log(`⚡ Using preset: ${presetKey}`);
284
+ await (0, appGenerator_1.generateApp)(options);
285
+ }
286
+ else {
287
+ const options = await runInteractivePrompt(projectName, presetDefaults);
288
+ await (0, appGenerator_1.generateApp)(options);
289
+ }
290
+ });
291
+ // generate subcommand
292
+ program
293
+ .command("generate <type> <name>")
294
+ .description("Generate code inside an existing rnstarterkit project\n" +
295
+ " Types: screen | component | hook | slice")
296
+ .action(async (type, name) => {
297
+ await (0, codeGenerator_1.generateCode)(type, name);
298
+ });
299
+ program.parseAsync(process.argv).catch((error) => {
300
+ console.error("❌ Failed.");
103
301
  console.error(error);
104
302
  process.exit(1);
105
303
  });