create-twinbloc-app 0.1.2 → 0.1.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.
- package/README.md +77 -10
- package/bin/cli.js +26 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# Create Twinbloc App
|
|
2
2
|
|
|
3
|
-
Create a React Native app
|
|
3
|
+
Create a React Native app with the Twinbloc starter template. This CLI scaffolds a production-ready Expo SDK 54 project with Expo Router, TypeScript, native modules, and a curated set of defaults for state, API, UI, and localization.
|
|
4
|
+
|
|
5
|
+
## What You Get
|
|
6
|
+
|
|
7
|
+
- Expo SDK 54 project with Expo Router and typed routes
|
|
8
|
+
- React Native 0.81 + React 19 setup
|
|
9
|
+
- TypeScript-first structure with strict TS config
|
|
10
|
+
- NativeWind + Tailwind utilities, themed UI primitives, and tokens
|
|
11
|
+
- Zustand store examples with persisted auth state
|
|
12
|
+
- TanStack Query with MMKV persistence
|
|
13
|
+
- Axios API client with auth-aware helpers
|
|
14
|
+
- i18next localization with English, Spanish, French, and Arabic samples
|
|
15
|
+
- Reanimated-ready bottom sheet provider and gesture handler setup
|
|
16
|
+
- Toasts, haptics, blur, image, and icon tooling prewired
|
|
17
|
+
- Environment-driven app config for name, slug, scheme, bundle IDs
|
|
4
18
|
|
|
5
19
|
## Requirements
|
|
6
20
|
|
|
@@ -8,7 +22,7 @@ Create a React Native app from the Twinbloc starter template (Expo + Expo Router
|
|
|
8
22
|
- npm, yarn, pnpm, or bun
|
|
9
23
|
- Xcode for iOS (macOS) and/or Android Studio for Android
|
|
10
24
|
|
|
11
|
-
## Quick Start
|
|
25
|
+
## Quick Start (npm)
|
|
12
26
|
|
|
13
27
|
```bash
|
|
14
28
|
npx create-twinbloc-app@latest MyApp
|
|
@@ -16,6 +30,13 @@ cd MyApp
|
|
|
16
30
|
npm run start
|
|
17
31
|
```
|
|
18
32
|
|
|
33
|
+
Then build and open a dev client:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx expo run:ios
|
|
37
|
+
npx expo run:android
|
|
38
|
+
```
|
|
39
|
+
|
|
19
40
|
## Using Other Package Managers
|
|
20
41
|
|
|
21
42
|
```bash
|
|
@@ -24,7 +45,7 @@ pnpm create twinbloc-app MyApp
|
|
|
24
45
|
bun create twinbloc-app MyApp
|
|
25
46
|
```
|
|
26
47
|
|
|
27
|
-
## Options
|
|
48
|
+
## CLI Options
|
|
28
49
|
|
|
29
50
|
```bash
|
|
30
51
|
npx create-twinbloc-app@latest MyApp --example base
|
|
@@ -35,16 +56,23 @@ npx create-twinbloc-app@latest MyApp --pm pnpm
|
|
|
35
56
|
npx create-twinbloc-app@latest MyApp --pm bun
|
|
36
57
|
```
|
|
37
58
|
|
|
38
|
-
|
|
59
|
+
### Example Variants
|
|
39
60
|
|
|
40
|
-
|
|
61
|
+
Only the base template ships in this repo. Use:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx create-twinbloc-app@latest MyApp --example base
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Running the App
|
|
68
|
+
|
|
69
|
+
This starter includes native modules, so you should use a development build instead of Expo Go.
|
|
41
70
|
|
|
42
71
|
```bash
|
|
43
|
-
cd MyApp
|
|
44
72
|
npm run start
|
|
45
73
|
```
|
|
46
74
|
|
|
47
|
-
Then
|
|
75
|
+
Then run a dev client when you need native modules:
|
|
48
76
|
|
|
49
77
|
```bash
|
|
50
78
|
npx expo run:ios
|
|
@@ -57,19 +85,58 @@ npx expo run:android
|
|
|
57
85
|
MyApp/
|
|
58
86
|
app/ # Expo Router routes
|
|
59
87
|
assets/ # Images and icons
|
|
60
|
-
src/
|
|
61
|
-
|
|
88
|
+
src/
|
|
89
|
+
api/ # Axios client and React Query helpers
|
|
90
|
+
components/ # UI components and providers
|
|
91
|
+
hooks/ # App hooks
|
|
92
|
+
lib/ # i18n, env, storage, utilities
|
|
93
|
+
store/ # Zustand stores
|
|
94
|
+
translations/ # i18n resource files
|
|
95
|
+
app.config.ts # Env-driven Expo config
|
|
96
|
+
global.css # NativeWind globals
|
|
62
97
|
package.json # Scripts and dependencies
|
|
63
98
|
tsconfig.json # TypeScript config
|
|
64
99
|
```
|
|
65
100
|
|
|
101
|
+
## Environment Configuration
|
|
102
|
+
|
|
103
|
+
The template loads environment values from `.env.{APP_ENV}` and `.env` with `APP_ENV=development|staging|production`.
|
|
104
|
+
|
|
105
|
+
Required by default:
|
|
106
|
+
|
|
107
|
+
- `EXPO_PUBLIC_API_URL` for your API base URL
|
|
108
|
+
|
|
109
|
+
Optional overrides:
|
|
110
|
+
|
|
111
|
+
- `APP_NAME`, `APP_SLUG`, `APP_SCHEME`
|
|
112
|
+
- `APP_BUNDLE_ID_DEVELOPMENT`, `APP_BUNDLE_ID_STAGING`, `APP_BUNDLE_ID_PRODUCTION`
|
|
113
|
+
- `APP_PACKAGE_DEVELOPMENT`, `APP_PACKAGE_STAGING`, `APP_PACKAGE_PRODUCTION`
|
|
114
|
+
|
|
115
|
+
Values are validated on startup. If you update `.env` files and still see errors, restart the dev server with `-c` to clear cache.
|
|
116
|
+
|
|
117
|
+
## Scripts
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm run start # Expo dev server
|
|
121
|
+
npm run ios # Build iOS dev client
|
|
122
|
+
npm run android # Build Android dev client
|
|
123
|
+
npm run web # Run web build
|
|
124
|
+
npm run lint # Lint project
|
|
125
|
+
npm run test # Jest tests
|
|
126
|
+
npm run reset-project # Move starter to app-example and create a fresh app
|
|
127
|
+
```
|
|
128
|
+
|
|
66
129
|
## Template Details
|
|
67
130
|
|
|
68
131
|
- Location: `template/react-native-starter`
|
|
69
132
|
- Framework: Expo SDK 54 + Expo Router
|
|
70
|
-
-
|
|
133
|
+
- Styling: NativeWind + Tailwind utilities
|
|
134
|
+
- State: Zustand stores in `src/store`
|
|
135
|
+
- Data: TanStack Query helpers in `src/api`
|
|
136
|
+
- i18n: `src/lib/i18n` and `src/translations`
|
|
71
137
|
|
|
72
138
|
## Troubleshooting
|
|
73
139
|
|
|
74
140
|
- If install fails, retry with `--pm` to force a package manager.
|
|
75
141
|
- If the folder is not empty, choose a new directory name.
|
|
142
|
+
- If your app launches but native modules crash, rebuild the dev client.
|
package/bin/cli.js
CHANGED
|
@@ -68,6 +68,11 @@ const runInstall = (dir, pm) => {
|
|
|
68
68
|
|
|
69
69
|
const brandPrimary = chalk.hex("#6CABDD");
|
|
70
70
|
const brandDeep = chalk.hex("#1C2C5B");
|
|
71
|
+
const rnBlue = chalk.hex("#61DAFB");
|
|
72
|
+
const uiMuted = chalk.gray;
|
|
73
|
+
const uiAccent = chalk.hex("#9FD7F2");
|
|
74
|
+
const uiActive = chalk.hex("#88C6FF");
|
|
75
|
+
const uiSelected = chalk.hex("#6EDC91");
|
|
71
76
|
|
|
72
77
|
const printBanner = () => {
|
|
73
78
|
const lines = [
|
|
@@ -83,6 +88,7 @@ const printBanner = () => {
|
|
|
83
88
|
console.log(paint(line));
|
|
84
89
|
});
|
|
85
90
|
console.log(brandPrimary("React Native Starter"));
|
|
91
|
+
console.log(rnBlue(" ⚛ React Native • Expo + Router • TypeScript • Zustand"));
|
|
86
92
|
console.log("");
|
|
87
93
|
};
|
|
88
94
|
|
|
@@ -113,9 +119,9 @@ const getExistingAgentKeys = async (rootDir) => {
|
|
|
113
119
|
const renderAgentSelection = (choices, selectedKeys, activeIndex) => {
|
|
114
120
|
output.write("\x1b[2J\x1b[0;0H");
|
|
115
121
|
printBanner();
|
|
116
|
-
console.log(brandPrimary.bold("Select agent configs to include
|
|
117
|
-
console.log(
|
|
118
|
-
console.log(
|
|
122
|
+
console.log(brandPrimary.bold("Select agent configs to include"));
|
|
123
|
+
console.log(uiMuted("Use ↑/↓ to move, space to toggle, enter to confirm."));
|
|
124
|
+
console.log(uiMuted("Press enter with nothing selected for none."));
|
|
119
125
|
console.log("");
|
|
120
126
|
|
|
121
127
|
choices.forEach((choice, index) => {
|
|
@@ -124,7 +130,15 @@ const renderAgentSelection = (choices, selectedKeys, activeIndex) => {
|
|
|
124
130
|
const cursor = isActive ? ">" : " ";
|
|
125
131
|
const mark = isSelected ? "x" : " ";
|
|
126
132
|
const line = `${cursor} [${mark}] ${choice.label}`;
|
|
127
|
-
|
|
133
|
+
if (isActive) {
|
|
134
|
+
console.log(uiActive.bold(line));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (isSelected) {
|
|
138
|
+
console.log(uiSelected(line));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.log(uiAccent(line));
|
|
128
142
|
});
|
|
129
143
|
};
|
|
130
144
|
|
|
@@ -230,16 +244,16 @@ const main = async () => {
|
|
|
230
244
|
[
|
|
231
245
|
brandPrimary.bold("Project created successfully."),
|
|
232
246
|
"",
|
|
233
|
-
|
|
247
|
+
uiMuted(`Agent configs: ${selectedLabels.length > 0 ? selectedLabels.join(", ") : "none"}`),
|
|
234
248
|
"",
|
|
235
|
-
brandPrimary("Next steps
|
|
236
|
-
|
|
237
|
-
options.skipInstall ?
|
|
238
|
-
|
|
249
|
+
brandPrimary("Next steps"),
|
|
250
|
+
uiAccent(` cd ${appName}`),
|
|
251
|
+
options.skipInstall ? uiAccent(" npm install") : null,
|
|
252
|
+
uiAccent(" npm run start"),
|
|
239
253
|
"",
|
|
240
|
-
brandPrimary("Then
|
|
241
|
-
|
|
242
|
-
|
|
254
|
+
brandPrimary("Then"),
|
|
255
|
+
uiMuted(" - Build a dev client (if you haven’t): npx expo run:ios / npx expo run:android"),
|
|
256
|
+
uiMuted(" - Open the app from the dev client on your device or simulator"),
|
|
243
257
|
]
|
|
244
258
|
.filter(Boolean)
|
|
245
259
|
.join("\n"),
|