create-ern-boilerplate 0.0.2

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 ADDED
@@ -0,0 +1,56 @@
1
+ # šŸš€ create-ern-boilerplate
2
+
3
+ CLI untuk membuat proyek **React Native Expo** dengan cepat menggunakan berbagai template yang sudah disiapkan.
4
+
5
+ ---
6
+
7
+ ## šŸ“¦ Instalasi
8
+
9
+ Kamu tidak perlu menginstall package ini secara global.
10
+ Cukup jalankan menggunakan **npx**:
11
+
12
+ ```bash
13
+ npx create-ern-boilerplate
14
+
15
+
16
+
17
+ 🧠 Contoh Penggunaan
18
+ 🟢 1. Mode Interaktif (Default)
19
+ npx create-ern-boilerplate
20
+
21
+
22
+ Kamu akan ditanya:
23
+
24
+ Nama project
25
+
26
+ Deskripsi
27
+
28
+ Template yang ingin digunakan
29
+
30
+ Apakah ingin langsung install dependencies
31
+
32
+ Contoh:
33
+
34
+ ? Nama project kamu: my-expo-app
35
+ ? Deskripsi singkat: Aplikasi keren dengan Expo
36
+ ? Pilih template: redux
37
+ ? Langsung install dependencies setelah membuat project? Yes
38
+
39
+ 🟔 2. Mode Cepat (Tanpa Prompt)
40
+
41
+ Langsung buat project baru tanpa pertanyaan:
42
+
43
+ npx create-ern-boilerplate my-app -y
44
+
45
+
46
+ šŸ”¹ Otomatis pakai template default minimal
47
+ šŸ”¹ Langsung install dependencies
48
+
49
+ šŸ”µ 3. Mode Cepat + Custom Deskripsi + Template
50
+
51
+ Kamu bisa langsung menentukan deskripsi dan template:
52
+
53
+ npx create-ern-boilerplate my-app -y --desc "Boilerplate lengkap" --template default
54
+
55
+
56
+ Hasilnya langsung generate project my-app pakai template redux, isi deskripsi, dan auto install dependencies.
package/create.js ADDED
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ import chalk from "chalk";
7
+ import inquirer from "inquirer";
8
+ import ora from "ora";
9
+ import { execSync } from "child_process";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ async function main() {
15
+ console.log(chalk.bold.cyan("\nšŸš€ Create Expo React Native Boilerplate\n"));
16
+
17
+ // Ambil argumen CLI
18
+ const args = process.argv.slice(2);
19
+ const hasY = args.includes("-y") || args.includes("--yes");
20
+
21
+ // Ambil nilai argumen manual
22
+ const nameArg = args.find((a) => !a.startsWith("-"));
23
+ const descIndex = args.findIndex((a) => a === "--desc");
24
+ const templateIndex = args.findIndex((a) => a === "--template");
25
+
26
+ const descArg = descIndex !== -1 ? args[descIndex + 1] : "";
27
+ const templateArg = templateIndex !== -1 ? args[templateIndex + 1] : "";
28
+
29
+ let projectName = nameArg || "";
30
+ let description = descArg || "Proyek React Native dengan Expo";
31
+ let templateName = templateArg || "minimal"; // default template
32
+ let autoInstall = false;
33
+
34
+ // === Mode interaktif ===
35
+ if (!hasY) {
36
+ const answers = await inquirer.prompt([
37
+ {
38
+ name: "projectName",
39
+ message: "Nama project kamu:",
40
+ default: projectName || "my-expo-app",
41
+ },
42
+ {
43
+ name: "description",
44
+ message: "Deskripsi singkat:",
45
+ default: description,
46
+ },
47
+ {
48
+ type: "list",
49
+ name: "template",
50
+ message: "Pilih template:",
51
+ choices: await getTemplateChoices(),
52
+ default: templateName,
53
+ },
54
+ {
55
+ type: "confirm",
56
+ name: "autoInstall",
57
+ message: "Langsung install dependencies setelah membuat project?",
58
+ default: false,
59
+ },
60
+ ]);
61
+
62
+ projectName = answers.projectName;
63
+ description = answers.description;
64
+ templateName = answers.template;
65
+ autoInstall = answers.autoInstall;
66
+ } else {
67
+ // === Mode cepat (-y) ===
68
+ if (!projectName) {
69
+ console.log(chalk.red("āŒ Kamu harus kasih nama project di mode -y"));
70
+ console.log(chalk.yellow("Contoh: npx create-ern-boilerplate my-app -y --desc 'My App' --template redux"));
71
+ process.exit(1);
72
+ }
73
+ autoInstall = true; // kalau -y, langsung install
74
+ console.log(chalk.green(`šŸ“¦ Membuat proyek ${projectName}... (mode cepat)`));
75
+ }
76
+
77
+ const targetDir = path.resolve(process.cwd(), projectName);
78
+ const templateDir = path.resolve(__dirname, "templates", templateName);
79
+
80
+ if (!(await fs.pathExists(templateDir))) {
81
+ console.log(chalk.red(`āŒ Template "${templateName}" tidak ditemukan.`));
82
+ process.exit(1);
83
+ }
84
+
85
+ if (fs.existsSync(targetDir)) {
86
+ console.log(chalk.red(`āŒ Folder "${projectName}" sudah ada.`));
87
+ process.exit(1);
88
+ }
89
+
90
+ // === Copy template ===
91
+ const spinner = ora(`šŸ“ Menyalin template "${templateName}"...`).start();
92
+
93
+ try {
94
+ await fs.copy(templateDir, targetDir);
95
+
96
+ // === Update package.json ===
97
+ const pkgPath = path.join(targetDir, "package.json");
98
+ if (fs.existsSync(pkgPath)) {
99
+ const pkg = await fs.readJson(pkgPath);
100
+ pkg.name = projectName;
101
+ pkg.description = description;
102
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
103
+ }
104
+
105
+ // === Update app.json ===
106
+ // === Update app.json ===
107
+ const appJsonPath = path.join(targetDir, "app.json");
108
+ if (fs.existsSync(appJsonPath)) {
109
+ const appJson = await fs.readJson(appJsonPath);
110
+ appJson.expo = appJson.expo || {};
111
+ appJson.expo.name = projectName;
112
+ appJson.expo.slug = projectName.toLowerCase().replace(/\s+/g, "-");
113
+ appJson.expo.ios.bundleIdentifier = `com.${projectName.toLowerCase()}.app`;
114
+ appJson.expo.android.package = `com.${projectName.toLowerCase()}.app`;
115
+ appJson.expo.scheme = projectName.toLowerCase();
116
+ appJson.expo.extra.eas.projectId = ""; // reset biar gak nabrak project kamu
117
+ await fs.writeJson(appJsonPath, appJson, { spaces: 2 });
118
+ }
119
+
120
+ spinner.succeed(`āœ… Template "${templateName}" berhasil dibuat!`);
121
+ } catch (err) {
122
+ spinner.fail("āŒ Terjadi kesalahan saat membuat project.");
123
+ console.error(err);
124
+ process.exit(1);
125
+ }
126
+
127
+ // === Auto install kalau dipilih ===
128
+ if (autoInstall) {
129
+ const installSpinner = ora("šŸ“¦ Menginstall dependencies...").start();
130
+ try {
131
+ execSync("npm install", { cwd: targetDir, stdio: "inherit" });
132
+ installSpinner.succeed("āœ… Dependencies berhasil diinstall!");
133
+ } catch (err) {
134
+ installSpinner.fail("āŒ Gagal menginstall dependencies.");
135
+ console.error(err);
136
+ }
137
+ }
138
+
139
+ console.log(`
140
+ ${chalk.green("šŸŽ‰ Selesai!")}
141
+ Langkah selanjutnya:
142
+ ${chalk.cyan(`cd ${projectName}`)}
143
+ ${autoInstall ? chalk.dim("(dependencies sudah terinstall āœ…)") : chalk.cyan("npm install")}
144
+ ${chalk.cyan("npm run start")}
145
+
146
+ Happy coding! šŸ’»
147
+ `);
148
+ }
149
+
150
+ // === Helper untuk menampilkan daftar template ===
151
+ async function getTemplateChoices() {
152
+ const templatesDir = path.join(__dirname, "templates");
153
+ const folders = await fs.readdir(templatesDir);
154
+ return folders.filter((f) =>
155
+ fs.statSync(path.join(templatesDir, f)).isDirectory()
156
+ );
157
+ }
158
+
159
+ main().catch((err) => {
160
+ console.error(chalk.red("Terjadi kesalahan:"), err);
161
+ process.exit(1);
162
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "create-ern-boilerplate",
3
+ "version": "0.0.2",
4
+ "description": "Expo React Native boilerplate generator",
5
+ "bin": {
6
+ "create-ern-boilerplate": "./create.js",
7
+ "ern-boilerplate": "./create.js"
8
+ },
9
+ "type": "module",
10
+ "license": "MIT",
11
+ "author": {
12
+ "name": "defazr",
13
+ "email": "ulfar.far@gmail.com"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/"
21
+ },
22
+ "homepage": "https://github.com/",
23
+ "keywords": [
24
+ "expo",
25
+ "react-native",
26
+ "boilerplate",
27
+ "template",
28
+ "create"
29
+ ],
30
+ "dependencies": {
31
+ "chalk": "^5.3.0",
32
+ "fs-extra": "^11.2.0",
33
+ "inquirer": "^9.2.12",
34
+ "ora": "^8.0.1"
35
+ }
36
+ }
@@ -0,0 +1,278 @@
1
+ # Expo React Native Boilerplate
2
+
3
+ A comprehensive boilerplate for building React Native applications with Expo, featuring authentication, theming, and a clean architecture.
4
+
5
+ ## Features
6
+
7
+ - āœ… **Expo Router** - File-based routing
8
+ - āœ… **TypeScript** - Type-safe development
9
+ - āœ… **NativeWind (Tailwind CSS)** - Utility-first styling
10
+ - āœ… **Authentication Context** - Global auth state management
11
+ - āœ… **Theme Support** - Light/Dark mode with system preference
12
+ - āœ… **Mock API Server** - JSON Server for development
13
+ - āœ… **Secure Storage** - Expo SecureStore for sensitive data
14
+ - āœ… **Path Aliases** - Clean imports with `@` prefix
15
+ - āœ… **Form Validation** - Built-in validation utilities
16
+ - āœ… **Reusable Components** - Button, Input, Card, Loading, etc.
17
+
18
+ ## Project Structure
19
+
20
+ ```
21
+ .
22
+ ā”œā”€ā”€ app
23
+ │ ā”œā”€ā”€ (admin) # Halaman & route khusus admin
24
+ │ ā”œā”€ā”€ (auth) # Halaman auth (login, register, dsb)
25
+ │ ā”œā”€ā”€ (protected) # Halaman yang membutuhkan autentikasi
26
+ │ └── _layout.tsx # Root layout untuk Expo Router
27
+ │
28
+ ā”œā”€ā”€ server
29
+ │ ā”œā”€ā”€ db.json # Mock data untuk JSON Server
30
+ │ ā”œā”€ā”€ middleware.js # Custom middleware untuk API
31
+ │ └── routes.json # Routing konfigurasi JSON Server
32
+ │
33
+ ā”œā”€ā”€ src
34
+ │ ā”œā”€ā”€ assets # Gambar, font, dan aset statis
35
+ │ ā”œā”€ā”€ components # Komponen UI reusable
36
+ │ ā”œā”€ā”€ contexts # Context API (Auth, Theme, dsb)
37
+ │ ā”œā”€ā”€ hooks # Custom React hooks
38
+ │ ā”œā”€ā”€ services # API service (Axios, interceptors, dll)
39
+ │ ā”œā”€ā”€ theme # Konfigurasi tema (warna, dark mode)
40
+ │ ā”œā”€ā”€ types # TypeScript type definitions
41
+ │ └── utils # Helper functions & constants
42
+ │
43
+ ā”œā”€ā”€ app.json # Konfigurasi Expo project
44
+ ā”œā”€ā”€ babel.config.js # Konfigurasi Babel
45
+ ā”œā”€ā”€ expo-env.d.ts # Type definitions untuk Expo environment
46
+ ā”œā”€ā”€ global.css # Global style untuk Tailwind / NativeWind
47
+ ā”œā”€ā”€ index.ts # Entry point utama aplikasi
48
+ ā”œā”€ā”€ LICENSE # Lisensi project
49
+ ā”œā”€ā”€ metro.config.js # Konfigurasi Metro bundler
50
+ ā”œā”€ā”€ nativewind-env.d.ts # Type support untuk NativeWind
51
+ ā”œā”€ā”€ package.json # Dependency & script project
52
+ ā”œā”€ā”€ package-lock.json # Lock file npm
53
+ ā”œā”€ā”€ README.md # Dokumentasi project
54
+ ā”œā”€ā”€ structure.txt # Struktur folder (auto-generated)
55
+ ā”œā”€ā”€ tailwind.config.js # Konfigurasi TailwindCSS
56
+ └── tsconfig.json # Konfigurasi TypeScript
57
+
58
+ ```
59
+
60
+ ## Getting Started
61
+
62
+ ### Prerequisites
63
+
64
+ - Node.js 18+
65
+ - npm or yarn
66
+ - Expo CLI
67
+
68
+ ### Installation
69
+
70
+ 1. Clone the repository:
71
+ ```bash
72
+ git clone <your-repo-url>
73
+ cd expo-boilerplate
74
+ ```
75
+
76
+ 2. Install dependencies:
77
+ ```bash
78
+ npm install
79
+ ```
80
+
81
+ 3. Start the development server:
82
+ ```bash
83
+ npm run dev
84
+ ```
85
+
86
+ This will start both the Expo development server and the mock API server.
87
+
88
+ ### Available Scripts
89
+
90
+ - `npm start` - Start Expo development server
91
+ - `npm run android` - Run on Android
92
+ - `npm run ios` - Run on iOS
93
+ - `npm run web` - Run on web
94
+ - `npm run server` - Start mock API server
95
+ - `npm run dev` - Start both Expo and API server
96
+ - `npm run lint` - Run ESLint
97
+ - `npm run type-check` - Run TypeScript type checking
98
+
99
+ ## Configuration
100
+
101
+ ### API Configuration
102
+
103
+ Edit `src/utils/constants.ts` to configure your API endpoints:
104
+
105
+ ```typescript
106
+ export const API_CONFIG = {
107
+ BASE_URL: __DEV__ ? 'http://localhost:3001' : 'https://api.yourapp.com',
108
+ TIMEOUT: 10000,
109
+ };
110
+ ```
111
+
112
+ ### Theme Configuration
113
+
114
+ Customize colors in `src/theme/colors.ts`:
115
+
116
+ ```typescript
117
+ export const colors = {
118
+ light: {
119
+ primary: '#3b82f6',
120
+ // ... other colors
121
+ },
122
+ dark: {
123
+ primary: '#3b82f6',
124
+ // ... other colors
125
+ },
126
+ };
127
+ ```
128
+
129
+ ## Authentication
130
+
131
+ The boilerplate includes a complete authentication flow:
132
+
133
+ ### Default Credentials
134
+
135
+ - **Admin**: admin@example.com / admin123
136
+ - **User**: user@example.com / user123
137
+
138
+ ### Using Authentication
139
+
140
+ ```typescript
141
+ import { useAuth } from '@hooks/useAuth';
142
+
143
+ function MyComponent() {
144
+ const { user, login, logout, isAuthenticated } = useAuth();
145
+
146
+ // Use authentication methods
147
+ }
148
+ ```
149
+
150
+ ## Theming
151
+
152
+ Switch between light and dark themes:
153
+
154
+ ```typescript
155
+ import { useTheme } from '@hooks/useTheme';
156
+
157
+ function MyComponent() {
158
+ const { theme, colors, setThemeMode } = useTheme();
159
+
160
+ // theme: 'light' | 'dark'
161
+ // colors: current theme colors
162
+ // setThemeMode: 'light' | 'dark' | 'auto'
163
+ }
164
+ ```
165
+
166
+ ## API Services
167
+
168
+ ### Making API Calls
169
+
170
+ ```typescript
171
+ import { newsService } from '@services/newsService';
172
+
173
+ // Get all news
174
+ const news = await newsService.getNews({ limit: 10 });
175
+
176
+ // Get single news item
177
+ const newsItem = await newsService.getNewsById('1');
178
+
179
+ // Create news
180
+ const newNews = await newsService.createNews({
181
+ title: 'New Article',
182
+ content: 'Content here',
183
+ // ...
184
+ });
185
+ ```
186
+
187
+ ## Custom Hooks
188
+
189
+ ### useAuth
190
+
191
+ Access authentication state and methods.
192
+
193
+ ### useTheme
194
+
195
+ Access theme configuration and toggle themes.
196
+
197
+ ## Components
198
+
199
+ ### Button
200
+
201
+ ```typescript
202
+ <Button
203
+ title="Click Me"
204
+ variant="primary"
205
+ size="md"
206
+ onPress={() => {}}
207
+ loading={false}
208
+ fullWidth
209
+ />
210
+ ```
211
+
212
+ ### Input
213
+
214
+ ```typescript
215
+ <Input
216
+ label="Email"
217
+ placeholder="Enter email"
218
+ value={email}
219
+ onChangeText={setEmail}
220
+ error={emailError}
221
+ secureTextEntry
222
+ />
223
+ ```
224
+
225
+ ### Card
226
+
227
+ ```typescript
228
+ <Card className="mb-4">
229
+ <Text>Card content</Text>
230
+ </Card>
231
+ ```
232
+
233
+ ### Loading
234
+
235
+ ```typescript
236
+ <Loading text="Loading..." fullScreen />
237
+ ```
238
+
239
+ ## Path Aliases
240
+
241
+ Use clean imports with configured path aliases:
242
+
243
+ ```typescript
244
+ import { Button } from '@components/common/Button';
245
+ import { useAuth } from '@hooks/useAuth';
246
+ import { newsService } from '@services/newsService';
247
+ ```
248
+
249
+ ## Deployment
250
+
251
+ ### Building for Production
252
+
253
+ ```bash
254
+ # For Android
255
+ eas build --platform android
256
+
257
+ # For iOS
258
+ eas build --platform ios
259
+
260
+ # For both
261
+ eas build --platform all
262
+ ```
263
+
264
+ ## Contributing
265
+
266
+ 1. Fork the repository
267
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
268
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
269
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
270
+ 5. Open a Pull Request
271
+
272
+ ## License
273
+
274
+ MIT License - feel free to use this boilerplate for your projects!
275
+
276
+ ## Support
277
+
278
+ For issues and questions, please open an issue on GitHub.
@@ -0,0 +1,58 @@
1
+ {
2
+ "expo": {
3
+ "name": "APP_NAME",
4
+ "slug": "APP_SLUG",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "icon": "./src/assets/images/icon.png",
8
+ "scheme": "myapp",
9
+ "userInterfaceStyle": "automatic",
10
+ "splash": {
11
+ "image": "./src/assets/images/splash-icon.png",
12
+ "resizeMode": "contain",
13
+ "backgroundColor": "#ffffff"
14
+ },
15
+ "assetBundlePatterns": ["**/*"],
16
+ "ios": {
17
+ "supportsTablet": true,
18
+ "bundleIdentifier": "com.yourname.APP_SLUG"
19
+ },
20
+ "android": {
21
+ "adaptiveIcon": {
22
+ "foregroundImage": "./src/assets/images/adaptive-icon.png",
23
+ "backgroundColor": "#ffffff"
24
+ },
25
+ "package": "com.yourname.APP_SLUG",
26
+ "versionCode": 1
27
+ },
28
+ "web": {
29
+ "bundler": "metro",
30
+ "output": "static",
31
+ "favicon": "./src/assets/images/favicon.png"
32
+ },
33
+ "plugins": [
34
+ "expo-router",
35
+ [
36
+ "expo-secure-store",
37
+ {
38
+ "requireFullDiskAccess": false
39
+ }
40
+ ]
41
+ ],
42
+ "experiments": {
43
+ "typedRoutes": true
44
+ },
45
+ "extra": {
46
+ "eas": {
47
+ "projectId": ""
48
+ }
49
+ },
50
+ "updates": {
51
+ "enabled": true,
52
+ "checkAutomatically": "ON_LOAD"
53
+ },
54
+ "runtimeVersion": {
55
+ "policy": "sdkVersion"
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "expo-boilerplate",
3
+ "version": "1.0.0",
4
+ "main": "expo-router/entry",
5
+ "scripts": {
6
+ "start": "expo start --clear",
7
+ "android": "expo start --android",
8
+ "ios": "expo start --ios",
9
+ "web": "expo start --web",
10
+ "server": "json-server --watch server/db.json --host 10.1.59.172 --port 3001 --middlewares server/middleware.js --routes server/routes.json",
11
+ "dev": "concurrently -n expo,server -c blue,green \"expo start --no-interactive --clear\" \"npm run server\"",
12
+ "lint": "eslint .",
13
+ "type-check": "tsc --noEmit"
14
+ },
15
+ "dependencies": {
16
+ "axios": "^1.7.2",
17
+ "babel-preset-expo": "^54.0.5",
18
+ "expo": "~54.0.0",
19
+ "expo-constants": "~18.0.9",
20
+ "expo-linking": "~8.0.8",
21
+ "expo-router": "^6.0.12",
22
+ "expo-secure-store": "~15.0.7",
23
+ "expo-status-bar": "~3.0.8",
24
+ "lucide-react-native": "^0.546.0",
25
+ "nativewind": "^4.0.1",
26
+ "react": "19.1.0",
27
+ "react-native": "^0.81.4",
28
+ "react-native-gesture-handler": "~2.28.0",
29
+ "react-native-reanimated": "~4.1.1",
30
+ "react-native-safe-area-context": "~5.6.0",
31
+ "react-native-screens": "~4.16.0",
32
+ "react-native-worklets": "0.5.1",
33
+ "react-native-svg": "15.12.1",
34
+ "expo-linear-gradient": "~15.0.7"
35
+ },
36
+ "devDependencies": {
37
+ "@babel/core": "^7.26.0",
38
+ "@types/react": "~19.1.10",
39
+ "babel-plugin-module-resolver": "^5.0.2",
40
+ "concurrently": "^8.2.0",
41
+ "eslint": "^9.12.0",
42
+ "eslint-config-prettier": "^9.1.0",
43
+ "json-server": "^0.17.4",
44
+ "prettier": "^3.3.3",
45
+ "tailwindcss": "^3.4.13",
46
+ "typescript": "~5.9.2"
47
+ },
48
+ "private": true,
49
+ "expo": {
50
+ "install": {
51
+ "exclude": ["react", "expo", "expo-constants", "react-native"]
52
+ }
53
+ }
54
+ }