@highbeek/create-rnstarterkit 1.0.2-beta.7 → 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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +270 -0
  3. package/dist/bin/create-rnstarterkit.js +205 -7
  4. package/dist/src/generators/appGenerator.js +1949 -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 +8 -5
  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,270 @@
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
+ # or
31
+ yarn dlx @highbeek/create-rnstarterkit myApp
32
+
33
+ # Skip prompts with a preset
34
+ npx @highbeek/create-rnstarterkit myApp --preset fintech
35
+ npx @highbeek/create-rnstarterkit myApp --preset social
36
+ npx @highbeek/create-rnstarterkit myApp --preset indie
37
+ npx @highbeek/create-rnstarterkit myApp --preset minimal
38
+ ```
39
+
40
+ ### After scaffolding — React Native CLI projects
41
+
42
+ Native dependencies need linking via CocoaPods before you can run on iOS:
43
+
44
+ ```bash
45
+ cd myApp
46
+ npm install
47
+ cd ios && pod install && cd ..
48
+
49
+ # Then run
50
+ npx react-native run-ios
51
+ npx react-native run-android
52
+ ```
53
+
54
+ ### After scaffolding — Expo projects
55
+
56
+ ```bash
57
+ cd myApp
58
+ npx expo start
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Presets
64
+
65
+ Presets bundle the most common stack combinations so you can skip all prompts:
66
+
67
+ | Preset | Platform | State | Auth | Data | Storage | Extras |
68
+ |---|---|---|---|---|---|---|
69
+ | `minimal` | Expo | None | No | None | None | Bare structure |
70
+ | `indie` | Expo | Context API | Yes | React Query | AsyncStorage | Husky |
71
+ | `social` | Expo | Zustand | Yes | SWR | AsyncStorage | Sentry, i18n, CI |
72
+ | `fintech` | RN CLI | Redux Toolkit | Yes | React Query | MMKV | Sentry, i18n, Maestro, CI |
73
+
74
+ ---
75
+
76
+ ## Interactive Setup
77
+
78
+ When run without a preset, the CLI walks you through your stack:
79
+
80
+ ```
81
+ ? Project name:
82
+ ? Platform: Expo / React Native CLI
83
+ ? TypeScript: Yes / No
84
+ ? Absolute imports (@/*): Yes / No
85
+ ? State management: None / Context API / Redux Toolkit / Zustand
86
+ ? Data fetching: None / React Query / SWR
87
+ ? Validation: Formik / React Hook Form / Yup
88
+ ? Storage: AsyncStorage / MMKV / None
89
+ ? Auth scaffold: Yes / No
90
+ ? API client: Yes / No → Fetch / Axios
91
+ ? Sentry: Yes / No
92
+ ? i18n (i18next): Yes / No
93
+ ? Maestro E2E flows: Yes / No
94
+ ? NativeWind (Tailwind): Yes / No
95
+ ? CI (GitHub Actions): Yes / No
96
+ ? Husky pre-commit hooks: Yes / No
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Generator Commands
102
+
103
+ After scaffolding, use the `generate` subcommand to add code that matches your project's setup:
104
+
105
+ ```bash
106
+ npx @highbeek/create-rnstarterkit generate screen Dashboard
107
+ npx @highbeek/create-rnstarterkit generate component Avatar
108
+ npx @highbeek/create-rnstarterkit generate hook useDebounce
109
+ npx @highbeek/create-rnstarterkit generate slice cart
110
+ ```
111
+
112
+ 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.
113
+
114
+ | Command | Output |
115
+ |---|---|
116
+ | `generate screen Name` | `src/screens/NameScreen.tsx` with correct state hook |
117
+ | `generate component Name` | `src/components/Name.tsx` with typed props + StyleSheet |
118
+ | `generate hook useName` | `src/hooks/useName.ts` with loading/error boilerplate |
119
+ | `generate slice name` | `src/store/nameSlice.ts` + auto-patches `store.ts` |
120
+
121
+ > `generate slice` only works in Redux Toolkit projects. It automatically adds the reducer to your store.
122
+
123
+ ---
124
+
125
+ ## What Gets Generated
126
+
127
+ ```
128
+ <project>/
129
+ ├── src/
130
+ │ ├── api/ # API client + endpoints
131
+ │ ├── components/ # Shared UI components + ErrorBoundary
132
+ │ ├── config/ # Typed env config
133
+ │ ├── context/ # Context API providers (if selected)
134
+ │ ├── hooks/ # Shared custom hooks
135
+ │ ├── i18n/ # i18next config + locales (if selected)
136
+ │ ├── navigation/ # Navigation setup (RN CLI)
137
+ │ ├── providers/ # App-level providers
138
+ │ ├── screens/ # App screens
139
+ │ ├── services/ # Query clients, external services
140
+ │ ├── store/ # Redux / Zustand store (if selected)
141
+ │ ├── theme/ # Color tokens, spacing, typography
142
+ │ ├── types/ # Shared TypeScript types
143
+ │ └── utils/ # Shared utilities + Sentry (if selected)
144
+ ├── .env.example # Environment variable template
145
+ ├── .husky/ # Pre-commit hooks (if selected)
146
+ ├── .maestro/ # E2E flows (if selected)
147
+ ├── .vscode/ # Editor config + extension recommendations
148
+ └── .github/workflows/ # CI pipeline (if selected)
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Optional Modules
154
+
155
+ ### Auth Scaffold
156
+
157
+ Three implementations — all include Login/Register screens, protected routes, token persistence, and a ScreenLayout wrapper:
158
+
159
+ | Option | State | Best For |
160
+ |---|---|---|
161
+ | Context API | Built-in React | Most apps |
162
+ | Redux Toolkit | `@reduxjs/toolkit` | Complex global state |
163
+ | Zustand | `zustand` | Minimal, no boilerplate |
164
+
165
+ ### API Client
166
+
167
+ | Option | Description |
168
+ |---|---|
169
+ | Fetch | Zero deps — typed wrapper with request/response interceptors |
170
+ | Axios | Full Axios instance with error normalisation + typed helpers |
171
+
172
+ ### Data Fetching
173
+
174
+ | Option | Setup |
175
+ |---|---|
176
+ | React Query | QueryClient pre-configured with retry + stale time defaults |
177
+ | SWR | SWRConfig provider + typed `useSWRFetch` hook |
178
+
179
+ ### Storage
180
+
181
+ | Option | Description |
182
+ |---|---|
183
+ | AsyncStorage | Default — async token persistence |
184
+ | MMKV | Synchronous, 10x faster, drop-in replacement |
185
+
186
+ ### NativeWind (Tailwind CSS)
187
+
188
+ Adds `nativewind` + `tailwindcss` and wires everything up for your platform:
189
+
190
+ | Step | Expo | React Native CLI |
191
+ |---|---|---|
192
+ | `tailwind.config.js` | content globs for `app/` + `src/` | content globs for `App.tsx` + `src/` |
193
+ | `global.css` | created, imported in `app/_layout.tsx` | created, imported in `index.js` |
194
+ | `metro.config.js` | wrapped with `withNativeWind` (expo/metro-config) | wrapped with `withNativeWind` (@react-native/metro-config) |
195
+ | `babel.config.js` | `jsxImportSource: 'nativewind'` in babel-preset-expo | `nativewind/babel` plugin added |
196
+ | `tsconfig.json` | `"nativewind/types"` added to types | same |
197
+
198
+ After generation, use `className` props directly on React Native components — no extra setup needed.
199
+
200
+ ### Sentry
201
+
202
+ 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.
203
+
204
+ ### i18n
205
+
206
+ Adds `i18next` + `react-i18next` with:
207
+ - Language auto-detection via `react-native-localize`
208
+ - English + Spanish locale files as a starting point
209
+ - Typed `useAppTranslation` hook
210
+
211
+ ### Maestro E2E
212
+
213
+ Generates `.maestro/flows/` with welcome, login, and logout flows. Automatically adds a Maestro job to the CI workflow when both are selected.
214
+
215
+ ### CI
216
+
217
+ GitHub Actions workflow with lint and test jobs on push/PR to `main` and `develop`.
218
+
219
+ ---
220
+
221
+ ## Tooling (Always Included)
222
+
223
+ | Tool | Config |
224
+ |---|---|
225
+ | TypeScript | `strict: true`, platform-native tsconfig |
226
+ | ESLint | `@react-native` ruleset, `--max-warnings 0` |
227
+ | Prettier | `format` script wired to `package.json` |
228
+ | Jest | RTL setup, `transformIgnorePatterns` for RN modules |
229
+ | VSCode | `extensions.json` + `settings.json` (format on save) |
230
+ | `.env.example` | Environment variable template + `.gitignore` entry |
231
+ | Theme | `colors.ts`, `spacing.ts`, `typography.ts` tokens |
232
+ | ErrorBoundary | Class component with reset, ready to wrap your app |
233
+ | Navigation types | `AuthStackParamList`, `MainTabParamList` (RN CLI + auth) |
234
+
235
+ ---
236
+
237
+ ## Architecture
238
+
239
+ **Typed from the start.** TypeScript strict mode on by default. API responses, environment variables, and navigation params are all typed.
240
+
241
+ **Thin screens, fat hooks.** Screens handle UI. Business logic, API calls, and state live in hooks and services. Screens don't call APIs directly.
242
+
243
+ **Non-destructive generation.** `writeIfMissing` is used throughout — the generator never overwrites files you've already customised.
244
+
245
+ ---
246
+
247
+ ## Contributing
248
+
249
+ ```bash
250
+ git clone https://github.com/highbeek/rnstarterkit
251
+ cd rnstarterkit
252
+ npm install
253
+ npm run build
254
+ ```
255
+
256
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full development workflow, testing, and PR guidelines.
257
+
258
+ ---
259
+
260
+ ## Status
261
+
262
+ Current version: `1.1.0`
263
+
264
+ Core scaffolding, presets, and generators are stable. Feedback and contributions welcome.
265
+
266
+ ---
267
+
268
+ ## License
269
+
270
+ 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
  });