create-ern-boilerplate 0.0.40 → 0.0.42

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/create.js CHANGED
@@ -14,12 +14,11 @@ const __dirname = path.dirname(__filename);
14
14
  async function main() {
15
15
  console.log(chalk.bold.cyan("\nšŸš€ Create Expo React Native Boilerplate\n"));
16
16
 
17
- // Ambil argumen CLI
17
+ // === Read CLI arguments ===
18
18
  const args = process.argv.slice(2);
19
19
  const hasY = args.includes("-y") || args.includes("--yes");
20
- const hasInstall = args.includes("--install"); // āœ… tambahkan flag install
20
+ const hasInstall = args.includes("--install");
21
21
 
22
- // Ambil nilai argumen manual
23
22
  const nameArg = args.find((a) => !a.startsWith("-"));
24
23
  const descIndex = args.findIndex((a) => a === "--desc");
25
24
  const templateIndex = args.findIndex((a) => a === "--template");
@@ -28,34 +27,34 @@ async function main() {
28
27
  const templateArg = templateIndex !== -1 ? args[templateIndex + 1] : "";
29
28
 
30
29
  let projectName = nameArg || "";
31
- let description = descArg || "Proyek React Native dengan Expo";
32
- let templateName = templateArg || "minimal"; // default template
30
+ let description = descArg || "Expo React Native project";
31
+ let templateName = templateArg || "minimal";
33
32
  let autoInstall = false;
34
33
 
35
- // === Mode interaktif ===
34
+ // === Interactive mode ===
36
35
  if (!hasY) {
37
36
  const answers = await inquirer.prompt([
38
37
  {
39
38
  name: "projectName",
40
- message: "Nama project kamu:",
39
+ message: "Project name:",
41
40
  default: projectName || "my-expo-app",
42
41
  },
43
42
  {
44
43
  name: "description",
45
- message: "Deskripsi singkat:",
44
+ message: "Short description:",
46
45
  default: description,
47
46
  },
48
47
  {
49
48
  type: "list",
50
49
  name: "template",
51
- message: "Pilih template:",
50
+ message: "Choose a template:",
52
51
  choices: await getTemplateChoices(),
53
52
  default: templateName,
54
53
  },
55
54
  {
56
55
  type: "confirm",
57
56
  name: "autoInstall",
58
- message: "Langsung install dependencies setelah membuat project?",
57
+ message: "Install dependencies automatically after creating the project?",
59
58
  default: false,
60
59
  },
61
60
  ]);
@@ -65,20 +64,23 @@ async function main() {
65
64
  templateName = answers.template;
66
65
  autoInstall = answers.autoInstall;
67
66
  } else {
68
- // === Mode cepat (-y) ===
67
+ // === Fast mode ===
69
68
  if (!projectName) {
70
- console.log(chalk.red("āŒ Kamu harus kasih nama project di mode -y"));
71
- console.log(chalk.yellow("Contoh:"));
69
+ console.log(chalk.red("āŒ You must provide a project name when using -y mode."));
70
+ console.log(chalk.yellow("Example:"));
72
71
  console.log(
73
- chalk.cyan(" npx create-ern-boilerplate my-app -y --desc 'My App' --template redux")
72
+ chalk.cyan(
73
+ " npx create-ern-boilerplate my-app -y --desc 'My App' --template redux"
74
+ )
74
75
  );
75
76
  process.exit(1);
76
77
  }
77
78
 
78
- autoInstall = hasInstall; // āœ… install hanya kalau ada flag --install
79
+ autoInstall = hasInstall;
79
80
  console.log(
80
81
  chalk.green(
81
- `šŸ“¦ Membuat proyek ${projectName}... (mode cepat${autoInstall ? " + install dependencies" : ""
82
+ `šŸ“¦ Creating project ${projectName}... (fast mode${
83
+ autoInstall ? " + installing dependencies" : ""
82
84
  })`
83
85
  )
84
86
  );
@@ -88,22 +90,22 @@ async function main() {
88
90
  const templateDir = path.resolve(__dirname, "templates", templateName);
89
91
 
90
92
  if (!(await fs.pathExists(templateDir))) {
91
- console.log(chalk.red(`āŒ Template "${templateName}" tidak ditemukan.`));
93
+ console.log(chalk.red(`āŒ Template "${templateName}" not found.`));
92
94
  process.exit(1);
93
95
  }
94
96
 
95
97
  if (fs.existsSync(targetDir)) {
96
- console.log(chalk.red(`āŒ Folder "${projectName}" sudah ada.`));
98
+ console.log(chalk.red(`āŒ Folder "${projectName}" already exists.`));
97
99
  process.exit(1);
98
100
  }
99
101
 
100
102
  // === Copy template ===
101
- const spinner = ora(`šŸ“ Menyalin template "${templateName}"...`).start();
103
+ const spinner = ora(`šŸ“ Copying template "${templateName}"...`).start();
102
104
 
103
105
  try {
104
106
  await fs.copy(templateDir, targetDir);
105
107
 
106
- // === Rename .gitignore-template ke .gitignore ===
108
+ // === Rename .gitignore-template → .gitignore ===
107
109
  const gitignoreTemplatePath = path.join(targetDir, ".gitignore-template");
108
110
  const gitignorePath = path.join(targetDir, ".gitignore");
109
111
 
@@ -121,121 +123,112 @@ async function main() {
121
123
  }
122
124
 
123
125
  // === Update app.json ===
124
- const appJsonPath = path.join(targetDir, "app.json");
125
- if (fs.existsSync(appJsonPath)) {
126
- const appJson = await fs.readJson(appJsonPath);
127
- appJson.expo = appJson.expo || {};
128
- appJson.expo.name = projectName;
129
- appJson.expo.slug = projectName.toLowerCase().replace(/\s+/g, "-");
130
- appJson.expo.ios = appJson.expo.ios || {};
131
- appJson.expo.android = appJson.expo.android || {};
132
- appJson.expo.ios.bundleIdentifier = `com.${projectName.toLowerCase()}.app`;
133
- appJson.expo.android.package = `com.${projectName.toLowerCase()}.app`;
134
- appJson.expo.scheme = projectName.toLowerCase();
135
- if (appJson.expo.extra && appJson.expo.extra.eas) {
136
- appJson.expo.extra.eas.projectId = "";
137
- }
138
- await fs.writeJson(appJsonPath, appJson, { spaces: 2 });
139
- }
126
+ const updateAppJson = async (file, env = null) => {
127
+ const filePath = path.join(targetDir, file);
128
+ if (!fs.existsSync(filePath)) return;
129
+
130
+ const json = await fs.readJson(filePath);
131
+
132
+ json.expo = json.expo || {};
133
+ json.expo.name = projectName;
134
+ json.expo.slug = projectName.toLowerCase().replace(/\s+/g, "-");
135
+
136
+ json.expo.ios = json.expo.ios || {};
137
+ json.expo.android = json.expo.android || {};
138
+
139
+ json.expo.ios.bundleIdentifier = `com.${projectName.toLowerCase()}.app`;
140
+ json.expo.android.package = `com.${projectName.toLowerCase()}.app`;
140
141
 
141
- // === Update app.staging.json ===
142
- const appJsonPathStaging = path.join(targetDir, "app.staging.json");
143
- if (fs.existsSync(appJsonPathStaging)) {
144
- const appJsonStaging = await fs.readJson(appJsonPathStaging);
145
- appJsonStaging.expo = appJsonStaging.expo || {};
146
- appJsonStaging.expo.name = projectName;
147
- appJsonStaging.expo.slug = projectName.toLowerCase().replace(/\s+/g, "-");
148
- appJsonStaging.expo.ios = appJsonStaging.expo.ios || {};
149
- appJsonStaging.expo.android = appJsonStaging.expo.android || {};
150
- appJsonStaging.expo.ios.bundleIdentifier = `com.${projectName.toLowerCase()}.app`;
151
- appJsonStaging.expo.android.package = `com.${projectName.toLowerCase()}.app`;
152
- appJsonStaging.expo.scheme = projectName.toLowerCase();
153
- if (appJsonStaging.expo.extra && appJsonStaging.expo.extra.eas) {
154
- appJsonStaging.expo.extra.eas.projectId = "";
142
+ json.expo.scheme = projectName.toLowerCase();
143
+
144
+ if (json.expo.extra && json.expo.extra.eas) {
145
+ json.expo.extra.eas.projectId = "";
155
146
  }
156
- appJsonStaging.expo.extra.ENV = "staging";
157
- appJsonStaging.expo.extra.BASE_URL = "";
158
- await fs.writeJson(appJsonPathStaging, appJsonStaging, { spaces: 2 });
159
- }
160
147
 
161
- // === Update app.prod.json ===
162
- const appJsonPathProd = path.join(targetDir, "app.prod.json");
163
- if (fs.existsSync(appJsonPathProd)) {
164
- const appJsonProd = await fs.readJson(appJsonPathProd);
165
- appJsonProd.expo = appJsonProd.expo || {};
166
- appJsonProd.expo.name = projectName;
167
- appJsonProd.expo.slug = projectName.toLowerCase().replace(/\s+/g, "-");
168
- appJsonProd.expo.ios = appJsonProd.expo.ios || {};
169
- appJsonProd.expo.android = appJsonProd.expo.android || {};
170
- appJsonProd.expo.ios.bundleIdentifier = `com.${projectName.toLowerCase()}.app`;
171
- appJsonProd.expo.android.package = `com.${projectName.toLowerCase()}.app`;
172
- appJsonProd.expo.scheme = projectName.toLowerCase();
173
- if (appJsonProd.expo.extra && appJsonProd.expo.extra.eas) {
174
- appJsonProd.expo.extra.eas.projectId = "";
148
+ if (env) {
149
+ json.expo.extra = json.expo.extra || {};
150
+ json.expo.extra.ENV = env;
151
+ json.expo.extra.BASE_URL = "";
175
152
  }
176
- appJsonProd.expo.extra.ENV = "production";
177
- appJsonProd.expo.extra.BASE_URL = "";
178
- await fs.writeJson(appJsonPathProd, appJsonProd, { spaces: 2 });
179
- }
180
153
 
181
- spinner.succeed(`āœ… Template "${templateName}" berhasil dibuat!`);
154
+ await fs.writeJson(filePath, json, { spaces: 2 });
155
+ };
156
+
157
+ await updateAppJson("app.json");
158
+ await updateAppJson("app.staging.json", "staging");
159
+ await updateAppJson("app.prod.json", "production");
160
+
161
+ spinner.succeed(`āœ… Template "${templateName}" has been created!`);
182
162
  } catch (err) {
183
- spinner.fail("āŒ Terjadi kesalahan saat membuat project.");
163
+ spinner.fail("āŒ An error occurred while creating the project.");
184
164
  console.error(err);
185
165
  process.exit(1);
186
166
  }
187
167
 
188
- // === Auto install kalau dipilih ===
168
+ // === Auto install dependencies ===
189
169
  if (autoInstall) {
190
- const installSpinner = ora("šŸ“¦ Menginstall dependencies...").start();
170
+ const installSpinner = ora("šŸ“¦ Installing dependencies...").start();
191
171
  try {
192
172
  execSync("npm install", { cwd: targetDir, stdio: "inherit" });
193
- installSpinner.succeed("āœ… Dependencies berhasil diinstall!");
173
+ installSpinner.succeed("āœ… Dependencies installed successfully!");
194
174
  } catch (err) {
195
- installSpinner.fail("āŒ Gagal menginstall dependencies.");
175
+ installSpinner.fail("āŒ Failed to install dependencies.");
196
176
  console.error(err);
197
177
  }
198
178
  }
199
179
 
180
+ // === Final output ===
200
181
  console.log(`
201
- ${chalk.green("šŸŽ‰ Selesai!")}
202
- Boilerplate berhasil dibuat untuk project: ${chalk.cyan(projectName)}
182
+ ${chalk.green("šŸŽ‰ Done!")}
183
+ The boilerplate has been successfully created for project: ${chalk.cyan(projectName)}
184
+
185
+ ${
186
+ autoInstall
187
+ ? chalk.dim("šŸ“¦ Dependencies have been installed automatically. āœ…")
188
+ : chalk.yellow(
189
+ "šŸ“¦ Before starting, review the dependencies in package.json to ensure they match your project requirements."
190
+ )
191
+ }
203
192
 
204
- ${autoInstall
205
- ? chalk.dim("šŸ“¦ Dependencies sudah terinstall otomatis āœ…")
206
- : chalk.yellow("šŸ“¦ Jalankan 'npm install' sebelum mulai coding")
207
- }
193
+ ${chalk.cyan(
194
+ "Before building any features, it is highly recommended to follow the reading order below to fully understand the project structure and AI workflow."
195
+ )}
196
+
197
+ ${chalk.bold("\nšŸ“– RECOMMENDED READING ORDER:\n")}
198
+
199
+ 1. ⭐ ${chalk.dim("# START HERE — Choose template variant")}
200
+ ${chalk.dim("TEMPLATE_VARIANTS.md")}
201
+
202
+ 2. ⭐ ${chalk.dim("# Complete AI onboarding guide & AI workflow explanation")}
203
+ ${chalk.dim("AI_GUIDE.md")}
204
+
205
+ 3. šŸ“ ${chalk.dim("# Path mappings & AI context configuration")}
206
+ ${chalk.dim("ai-context.json")}
207
+
208
+ 4. šŸ“‹ ${chalk.dim("# Code generation patterns and rules")}
209
+ ${chalk.dim("GENERATE_RULES.md")}
208
210
 
209
- ${chalk.cyan("Sebelum mulai mengembangkan fitur, sangat disarankan mengikuti urutan bacaan berikut untuk memahami alur project dan AI workflow.")}
210
-
211
- ${chalk.bold("\nšŸ“– READING ORDER (Urutan Baca yang Direkomendasikan):\n")}
212
-
213
- 1. ⭐ TEMPLATE_VARIANTS.md
214
- ${chalk.dim("# START HERE — Choose template variant")}
215
- 2. ⭐ AI_GUIDE.md
216
- ${chalk.dim("# Complete AI onboarding guide & cara kerja AI")}
217
- 3. šŸ“ ai-context.json
218
- ${chalk.dim("# Path mappings & context")}
219
- 4. šŸ“‹ GENERATE_RULES.md
220
- ${chalk.dim("# Pola pembuatan kode")}
221
- 5. šŸ“ CONVENTIONS.md
222
- ${chalk.dim("# Standar coding")}
223
- 6. šŸ“‹ SERVER_GUIDE.md
224
- ${chalk.dim("# Mock API & dummy data guide")}
225
- 7. šŸ’” examples/
226
- ${chalk.dim("# Contoh kode siap pakai")}
227
- 8. šŸ“– README.md
228
- ${chalk.dim("# Gambaran umum project")}
229
-
230
- ${chalk.bold("\nšŸ“– Pastikan untuk selalu menjaga stabilitas integrasi, skalabilitas arsitektur, maintainability, testability, konsistensi codebase, serta zero error.\n")}
211
+ 5. šŸ“ ${chalk.dim("# Coding standards and conventions")}
212
+ ${chalk.dim("CONVENTIONS.md")}
213
+
214
+ 6. šŸ“‹ ${chalk.dim("# Mock API usage & dummy data guide")}
215
+ ${chalk.dim("SERVER_GUIDE.md")}
216
+
217
+ 7. šŸ’” ${chalk.dim("# Ready-to-use examples")}
218
+ ${chalk.dim("examples/")}
219
+
220
+ 8. šŸ“– ${chalk.dim("# General overview of the project")}
221
+ ${chalk.dim("README.md")}
222
+
223
+ ${chalk.bold(
224
+ "\nšŸ“– Always maintain integration stability, architectural scalability, maintainability, testability, codebase consistency, and zero errors.\n"
225
+ )}
231
226
 
232
227
  ${chalk.green("Happy coding! šŸ’»")}
233
228
  `);
234
229
  }
235
230
 
236
-
237
-
238
- // === Helper untuk menampilkan daftar template ===
231
+ // === Helper: list available templates ===
239
232
  async function getTemplateChoices() {
240
233
  const templatesDir = path.join(__dirname, "templates");
241
234
  const folders = await fs.readdir(templatesDir);
@@ -245,6 +238,6 @@ async function getTemplateChoices() {
245
238
  }
246
239
 
247
240
  main().catch((err) => {
248
- console.error(chalk.red("Terjadi kesalahan:"), err);
241
+ console.error(chalk.red("An unexpected error occurred:"), err);
249
242
  process.exit(1);
250
243
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ern-boilerplate",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "description": "Expo React Native boilerplate generator",
5
5
  "bin": {
6
6
  "create-ern-boilerplate": "./create.js",
@@ -12,7 +12,7 @@
12
12
  import React, { useState, useEffect } from 'react';
13
13
  import { View, Text, FlatList, RefreshControl, ActivityIndicator } from 'react-native';
14
14
  import { SafeAreaView } from 'react-native-safe-area-context';
15
- import { useThemeStore } from '@/store/themeStore';
15
+ import { useTheme } from '@/hooks/useTheme';
16
16
  import { Card } from '@/components/common/Card';
17
17
  import { Button } from '@/components/common/Button';
18
18
 
@@ -25,8 +25,7 @@ interface NewsItem {
25
25
  }
26
26
 
27
27
  export default function NewsListScreen() {
28
- const colorScheme = useThemeStore((state) => state.colorScheme);
29
- const isDark = colorScheme === 'dark';
28
+ const { colors, isDark } = useTheme();
30
29
 
31
30
  // State management
32
31
  const [news, setNews] = useState<NewsItem[]>([]);
@@ -28,6 +28,7 @@ import {
28
28
  import { useTheme } from '@/hooks/useTheme';
29
29
  import { useRouter, useLocalSearchParams } from 'expo-router';
30
30
  import Toast from 'react-native-toast-message';
31
+ import { SafeAreaView } from 'react-native-safe-area-context';
31
32
 
32
33
  // Example: Product interface
33
34
  interface Product {
@@ -184,7 +185,7 @@ export default function ProductDetailScreen() {
184
185
 
185
186
  // Main content
186
187
  return (
187
- <View className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
188
+ <SafeAreaView className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
188
189
  <ScrollView>
189
190
  {/* Product Image */}
190
191
  {product.imageUrl && (
@@ -282,6 +283,6 @@ export default function ProductDetailScreen() {
282
283
  </View>
283
284
  </View>
284
285
  </ScrollView>
285
- </View>
286
+ </SafeAreaView>
286
287
  );
287
288
  }
@@ -26,6 +26,7 @@ import {
26
26
  } from 'react-native';
27
27
  import { useTheme } from '@/hooks/useTheme';
28
28
  import { useRouter } from 'expo-router';
29
+ import { SafeAreaView } from 'react-native-safe-area-context';
29
30
 
30
31
  // Example: News interface
31
32
  interface NewsItem {
@@ -142,7 +143,7 @@ export default function NewsListScreen() {
142
143
 
143
144
  // Main content
144
145
  return (
145
- <View className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
146
+ <SafeAreaView className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
146
147
  <FlatList
147
148
  data={news}
148
149
  keyExtractor={(item) => item.id}
@@ -189,6 +190,6 @@ export default function NewsListScreen() {
189
190
  )}
190
191
  contentContainerStyle={{ paddingBottom: 16 }}
191
192
  />
192
- </View>
193
+ </SafeAreaView>
193
194
  );
194
195
  }
@@ -27,6 +27,7 @@ import {
27
27
  import { useTheme } from '@/hooks/useTheme';
28
28
  import { useAuthStore } from '@/store/authStore';
29
29
  import { useRouter } from 'expo-router';
30
+ import { SafeAreaView } from 'react-native-safe-area-context';
30
31
 
31
32
  // Example: User's task interface
32
33
  interface Task {
@@ -190,7 +191,7 @@ export default function TaskListScreen() {
190
191
 
191
192
  // Main content
192
193
  return (
193
- <View className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
194
+ <SafeAreaView className="flex-1" style={{ backgroundColor: isDark ? '#000' : '#F9FAFB' }}>
194
195
  {/* Header with user info */}
195
196
  <View className={`px-4 py-4 ${isDark ? 'bg-gray-800' : 'bg-white'}`}>
196
197
  <Text className={`text-lg font-bold ${isDark ? 'text-white' : 'text-gray-900'}`}>
@@ -245,6 +246,6 @@ export default function TaskListScreen() {
245
246
  )}
246
247
  contentContainerStyle={{ paddingBottom: 16 }}
247
248
  />
248
- </View>
249
+ </SafeAreaView>
249
250
  );
250
251
  }